增加日志
This commit is contained in:
@ -9,6 +9,9 @@ namespace DH.Commons.Base
|
||||
{
|
||||
public class CameraBase : NotifyProperty
|
||||
{
|
||||
|
||||
// public LoggerHelper LoggerHelper { get; set; } = new LoggerHelper();
|
||||
|
||||
// 私有字段 + 带通知的属性(与DetectionLabel风格一致)
|
||||
private bool _isEnabled = false;
|
||||
private bool _isallPicEnabled = true;//默认全画幅
|
||||
|
@ -191,18 +191,14 @@ namespace DH.Commons.Base
|
||||
}
|
||||
public class DetectStationResult
|
||||
{
|
||||
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
public string Pid { get; set; }
|
||||
public string Pid { get; set; }
|
||||
|
||||
|
||||
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
public string TempPid { get; set; }
|
||||
public string TempPid { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 检测工位名称
|
||||
/// </summary>
|
||||
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
public string DetectName { get; set; }
|
||||
|
||||
|
||||
@ -212,8 +208,11 @@ namespace DH.Commons.Base
|
||||
/// </summary>
|
||||
public List<DetectionResultDetail> DetectDetails = new List<DetectionResultDetail>();
|
||||
public List<IShapeElement> DetectionResultShapes = new List<IShapeElement>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 视觉测量结果集合
|
||||
/// </summary>
|
||||
|
||||
public List<IndexedSpec> realSpecs { get; set; } = new List<IndexedSpec>();
|
||||
/// <summary>
|
||||
/// 工位检测结果
|
||||
/// </summary>
|
||||
@ -249,9 +248,9 @@ namespace DH.Commons.Base
|
||||
public int StationDetectElapsed { get; set; }
|
||||
public static string NormalizeAndClean(string input)
|
||||
{
|
||||
#pragma warning disable CS8603 // 可能返回 null 引用。
|
||||
|
||||
if (input == null) return null;
|
||||
#pragma warning restore CS8603 // 可能返回 null 引用。
|
||||
|
||||
|
||||
// Step 1: 标准化字符编码为 Form C (规范组合)
|
||||
string normalizedString = input.Normalize(NormalizationForm.FormC);
|
||||
|
389
DH.Commons/Base/DeviceBase.cs
Normal file
389
DH.Commons/Base/DeviceBase.cs
Normal file
@ -0,0 +1,389 @@
|
||||
|
||||
using DH.Commons.Enums;
|
||||
using OpenCvSharp.Internal;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static DH.Commons.Enums.EnumHelper;
|
||||
using Timer = System.Threading.Timer;
|
||||
|
||||
namespace DH.Commons.Base
|
||||
{
|
||||
public abstract class DeviceBase : IDisposable
|
||||
{
|
||||
#region Event
|
||||
[JsonIgnore]
|
||||
[Browsable(false)]
|
||||
public Action<DateTime, Exception> OnExceptionOccured { get; set; }
|
||||
//public event Action<DateTime, LogLevel, string> OnLog;
|
||||
public event Action<LogMsg> OnLog;
|
||||
// public event Action<IDevice, DeviceState> OnDeviceStateChanged;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
#endregion
|
||||
|
||||
#region field
|
||||
int RetryTime = 3;
|
||||
/// <summary>
|
||||
/// 和设备暂停状态关联的信号量
|
||||
/// </summary>
|
||||
private readonly ManualResetEvent pauseHandle = new ManualResetEvent(true);
|
||||
readonly Timer stateChangedTimer;
|
||||
#endregion
|
||||
|
||||
#region Property
|
||||
#region State
|
||||
private EnumHelper.DeviceState _currentStateToBe = EnumHelper.DeviceState.DSUninit;
|
||||
/// <summary>
|
||||
/// 当前设备状态
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
internal EnumHelper.DeviceState CurrentStateToBe
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentStateToBe;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _currentStateToBe)
|
||||
{
|
||||
var initialState = _currentStateToBe;
|
||||
_currentStateToBe = value;
|
||||
|
||||
if (_currentStateToBe != EnumHelper.DeviceState.DSExcept)
|
||||
{
|
||||
// OnStateChanged(initialState);
|
||||
}
|
||||
else
|
||||
{
|
||||
stateChangedTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//private EnumHelper.DeviceState initialState = EnumHelper.DeviceState.DSUninit;
|
||||
private EnumHelper.DeviceState _currentState = EnumHelper.DeviceState.DSUninit;
|
||||
public EnumHelper.DeviceState CurrentState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentState;
|
||||
}
|
||||
set
|
||||
{
|
||||
_currentState = value;
|
||||
|
||||
if (value != EnumHelper.DeviceState.TBD)
|
||||
{
|
||||
// OnDeviceStateChanged?.Invoke(this, _currentState);
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentState"));
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// initialState = _currentState;
|
||||
//}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 设备标识符 从数据库获取
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称 从数据库获取
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
//private IInitialConfig initialConfig = null;
|
||||
///// <summary>
|
||||
///// 设备初始化配置 从数据库获取
|
||||
///// </summary>
|
||||
//public virtual IInitialConfig InitialConfig
|
||||
//{
|
||||
// get => initialConfig;
|
||||
// set
|
||||
// {
|
||||
// initialConfig = value;
|
||||
// Id = initialConfig.Id;
|
||||
// Name = initialConfig.Name;
|
||||
|
||||
// LoggerHelper.LogPath = initialConfig.LogPath;
|
||||
// LoggerHelper.LogPrefix = initialConfig.Name;
|
||||
// }
|
||||
//}
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
public DeviceBase()
|
||||
{
|
||||
RegisterFileWriterException();
|
||||
// stateChangedTimer = new Timer(new TimerCallback(CheckDeviceOpTimeOut), null, Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 设备抽象方法
|
||||
protected virtual void Init()
|
||||
{
|
||||
LogAsync(DateTime.Now, LogLevel.Information, $"{Name}初始化完成");
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
LogAsync(DateTime.Now, LogLevel.Information, $"{Name}启动");
|
||||
}
|
||||
|
||||
protected virtual void Stop()
|
||||
{
|
||||
LogAsync(DateTime.Now, LogLevel.Information, $"{Name}停止");
|
||||
}
|
||||
|
||||
//public abstract void AttachToProcess(IProcess process);
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 常用操作封装方法
|
||||
/// </summary>
|
||||
/// <param name="opConfig"></param>
|
||||
/// <returns></returns>
|
||||
//public virtual ResponseMessage RunWrap(IOperationConfig opConfig)
|
||||
//{
|
||||
// ResponseMessage msg = new ResponseMessage();
|
||||
// msg.Message = "设备基类默认操作";
|
||||
// return msg;
|
||||
//}
|
||||
|
||||
#region 状态切换
|
||||
//[DeviceExceptionAspect]
|
||||
//public void StateChange(EnumHelper.DeviceState stateToBe)
|
||||
//{
|
||||
// if (CurrentState == stateToBe)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (!stateToBe.CheckPreStateValid((int)CurrentStateToBe))
|
||||
// {
|
||||
// string currentStateStr = CurrentStateToBe.GetEnumDescription();
|
||||
// string stateToBeStr = stateToBe.GetEnumDescription();
|
||||
// throw new ProcessException($"{InitialConfig.Name}设备的当前状态为{currentStateStr},无法切换至{stateToBeStr}");
|
||||
// }
|
||||
|
||||
// CurrentState = EnumHelper.DeviceState.TBD;
|
||||
// CurrentStateToBe = stateToBe;
|
||||
//}
|
||||
|
||||
//[DeviceExceptionAspect]
|
||||
//private void OnStateChanged(EnumHelper.DeviceState initialState)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// if (CurrentStateToBe != EnumHelper.DeviceState.DSExcept)
|
||||
// {
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (CurrentState == EnumHelper.DeviceState.DSExcept)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// throw new ProcessException($"{InitialConfig.Name}设备操作超时");
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (RetryTime >= 0)
|
||||
// {
|
||||
// if (initialState == CurrentStateToBe)
|
||||
// {
|
||||
// CurrentState = CurrentStateToBe;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// #region 状态切换操作
|
||||
// switch (CurrentStateToBe)
|
||||
// {
|
||||
// case EnumHelper.DeviceState.DSInit:
|
||||
// if (initialState == EnumHelper.DeviceState.DSOpen)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Init();
|
||||
// }
|
||||
// break;
|
||||
// case EnumHelper.DeviceState.DSOpen:
|
||||
// if (initialState == EnumHelper.DeviceState.DSInit)
|
||||
// {
|
||||
// Start();
|
||||
// }
|
||||
// else if (initialState == EnumHelper.DeviceState.DSPause)
|
||||
// {
|
||||
// Resume();
|
||||
// pauseHandle.Set();
|
||||
// }
|
||||
// break;
|
||||
// case EnumHelper.DeviceState.DSPause:
|
||||
// pauseHandle.Reset();
|
||||
// Pause();
|
||||
// break;
|
||||
// case EnumHelper.DeviceState.DSClose:
|
||||
// if (initialState != DeviceState.DSUninit)
|
||||
// {
|
||||
// Stop();
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
// RetryTime = 3;
|
||||
// CurrentState = CurrentStateToBe;
|
||||
// #endregion
|
||||
// }
|
||||
|
||||
// stateChangedTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// RetryTime--;
|
||||
// if (RetryTime > 0)
|
||||
// {
|
||||
// OnStateChanged(initialState);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (CurrentState != EnumHelper.DeviceState.DSExcept)
|
||||
// {
|
||||
// RetryTime = 3;
|
||||
// throw new ProcessException($"设备{InitialConfig.Name}的{CurrentStateToBe.GetEnumDescription()}操作重复3次失败", ex, ExceptionLevel.Warning);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void CheckDeviceOpTimeOut(object state)
|
||||
//{
|
||||
// stateChangedTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
// if (CurrentState != EnumHelper.DeviceState.DSExcept)
|
||||
// {
|
||||
// StateChange(EnumHelper.DeviceState.DSExcept);
|
||||
// }
|
||||
//}
|
||||
#endregion
|
||||
|
||||
#region 日志处理
|
||||
private void RegisterFileWriterException()
|
||||
{
|
||||
LoggerHelper.OnLogExceptionRaised -= LoggerHelper_OnLogExceptionRaised;
|
||||
// CSVHelper.OnCSVExceptionRaised -= LoggerHelper_OnLogExceptionRaised;
|
||||
|
||||
LoggerHelper.OnLogExceptionRaised += LoggerHelper_OnLogExceptionRaised;
|
||||
// CSVHelper.OnCSVExceptionRaised += LoggerHelper_OnLogExceptionRaised;
|
||||
}
|
||||
// public CSVHelper CSVHelper { get; set; } = new CSVHelper();
|
||||
|
||||
public LoggerHelper LoggerHelper { get; set; } = new LoggerHelper();
|
||||
|
||||
public virtual void LogAsync(LogMsg msg)
|
||||
{
|
||||
msg.MsgSource = Name;
|
||||
msg.ThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
//OnLog?.BeginInvoke(msg, null, null);
|
||||
OnLog?.Invoke(msg);
|
||||
|
||||
//if (InitialConfig.IsEnableLog)
|
||||
//{
|
||||
// LoggerHelper.LogAsync(msg);
|
||||
//}
|
||||
}
|
||||
|
||||
public virtual void LogAsync(DateTime dt, LogLevel logLevel, string msg)
|
||||
{
|
||||
LogAsync(new LogMsg(dt, logLevel, msg));
|
||||
}
|
||||
private void LoggerHelper_OnLogExceptionRaised(DateTime dt, string msg)
|
||||
{
|
||||
OnLog?.Invoke(new LogMsg(dt, LogLevel.Error, msg));
|
||||
}
|
||||
/// <summary>
|
||||
/// CSV异步数据输出
|
||||
/// </summary>
|
||||
/// <param name="csvFile">CSV输出文件的文件全路径</param>
|
||||
/// <param name="csvData">CSV输出数据</param>
|
||||
/// <param name="csvHead">CSV文件表头</param>
|
||||
public virtual void CSVRecordAsync(string csvFile, string csvData, string csvHead = "")
|
||||
{
|
||||
// CSVHelper.CSVOutputAsync(csvFile, csvData, csvHead);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 报警记录
|
||||
//object _alarmLock = new object();
|
||||
//protected virtual async void SaveAlarmCSVAsync(DateTime now, string deviceName, IWarningSet ws)
|
||||
//{
|
||||
// await Task.Run(() =>
|
||||
// {
|
||||
// LogAsync(now, LogLevel.Warning, $"{ws.WarningCode}-{ws.WarningDescription} {(ws.CurrentStatus ? "发生" : "取消")}");
|
||||
|
||||
// if (string.IsNullOrWhiteSpace(this.InitialConfig.LogPath) || !InitialConfig.IsEnableCSV)
|
||||
// return;
|
||||
|
||||
// string path = Path.Combine(InitialConfig.LogPath, $"Alarm_{Name}_{now.ToString("yyyyMMdd")}.csv");
|
||||
// string head = "Time,Source,AlarmCode,AlarmDescription,AlarmStatus";
|
||||
// string data = $"{now.ToString("HH:mm:ss.fff")},{deviceName},{ws.WarningCode},{ws.WarningDescription},{(ws.CurrentStatus ? "报警发生" : "报警取消")}";
|
||||
// CSVRecordAsync(path, data, head);
|
||||
// });
|
||||
//}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // 要检测冗余调用
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
//释放托管状态(托管对象)。
|
||||
stateChangedTimer?.Dispose();
|
||||
pauseHandle?.Dispose();
|
||||
}
|
||||
|
||||
// TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
|
||||
// TODO: 将大型字段设置为 null。
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
|
||||
// ~DeviceBase()
|
||||
// {
|
||||
// // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
|
||||
// Dispose(false);
|
||||
// }
|
||||
|
||||
// 添加此代码以正确实现可处置模式。
|
||||
public void Dispose()
|
||||
{
|
||||
// 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
|
||||
Dispose(true);
|
||||
// TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
|
||||
// GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ namespace DH.Commons.Base
|
||||
{
|
||||
public class PLCBase : NotifyProperty
|
||||
{
|
||||
|
||||
// 私有字段
|
||||
private bool _enable;
|
||||
private bool _connected;
|
||||
|
@ -18,7 +18,7 @@ namespace DH.Commons.Base
|
||||
/// Object Detection 目标检测:寻找图像中的物体并进行定位;
|
||||
/// Instance Segmentation 实例分割:定位图中每个物体,并进行像素级标注,区分不同个体;
|
||||
/// </summary>
|
||||
public abstract class VisionEngineBase
|
||||
public abstract class VisionEngineBase : DeviceBase
|
||||
{
|
||||
public List<DetectionConfig> DetectionConfigs = new List<DetectionConfig>();
|
||||
#region event
|
||||
@ -66,118 +66,8 @@ namespace DH.Commons.Base
|
||||
}
|
||||
}
|
||||
}
|
||||
public class CamModuleXY
|
||||
{
|
||||
[Category("图片行")]
|
||||
[DisplayName("行")]
|
||||
[Description("行")]
|
||||
// [TypeConverter(typeof(DeviceIdSelectorConverter<CameraBase>))]
|
||||
//[TypeConverter(typeof(CollectionCountConvert))]
|
||||
public int PicRows { get; set; } = 1;
|
||||
|
||||
[Category("图片列")]
|
||||
[DisplayName("列")]
|
||||
[Description("列")]
|
||||
// [TypeConverter(typeof(DeviceIdSelectorConverter<CameraBase>))]
|
||||
//[TypeConverter(typeof(CollectionCountConvert))]
|
||||
public int PicCols { get; set; } = 1;
|
||||
|
||||
public string GetDisplayText()
|
||||
{
|
||||
return "行:" + PicRows.ToString() + "列:" + PicCols.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
//public class RelatedCamera
|
||||
//{
|
||||
|
||||
// [Category("关联相机")]
|
||||
// [DisplayName("关联相机")]
|
||||
// [Description("关联相机描述")]
|
||||
// //[TypeConverter(typeof(DeviceIdSelectorConverter<CameraBase>))]
|
||||
// //[TypeConverter(typeof(CollectionCountConvert))]
|
||||
// public string CameraSourceId { get; set; } = "";
|
||||
|
||||
|
||||
// //public string GetDisplayText()
|
||||
// //{
|
||||
// // using (var scope = GlobalVar.Container.BeginLifetimeScope())
|
||||
// // {
|
||||
// // List<IDevice> deviceList = scope.Resolve<List<IDevice>>();
|
||||
// // IDevice CameraDevice = deviceList.FirstOrDefault(dev => dev.Id.Equals(CameraSourceId));
|
||||
|
||||
// // if (CameraDevice != null && CameraDevice is CameraBase)
|
||||
// // {
|
||||
// // return CameraDevice.Name;
|
||||
// // }
|
||||
|
||||
// // }
|
||||
// // return CameraSourceId;
|
||||
// //}
|
||||
//}
|
||||
public class VisionEngineInitialConfigBase //: InitialConfigBase
|
||||
{
|
||||
[Category("深度学习检测配置")]
|
||||
[DisplayName("检测配置集合")]
|
||||
[Description("检测配置集合")]
|
||||
//[TypeConverter(typeof(CollectionCountConvert))]
|
||||
//[Editor(typeof(ComplexCollectionEditor<DetectionConfig>), typeof(UITypeEditor))]
|
||||
public List<DetectionConfig> DetectionConfigs { get; set; } = new List<DetectionConfig>();
|
||||
|
||||
[Category("深度学习检测配置")]
|
||||
[DisplayName("标签分类")]
|
||||
[Description("标签分类,A_NG,B_TBD...")]
|
||||
// [TypeConverter(typeof(CollectionCountConvert))]
|
||||
// [Editor(typeof(ComplexCollectionEditor<RecongnitionLabelCategory>), typeof(UITypeEditor))]
|
||||
public List<RecongnitionLabelCategory> RecongnitionLabelCategoryList { get; set; } = new List<RecongnitionLabelCategory>();
|
||||
|
||||
[Category("深度学习检测配置")]
|
||||
[DisplayName("检测标签定义集合")]
|
||||
[Description("定义检测标签的集合,例如:Seg/Detection模式:断裂、油污、划伤...;Class模式:ok、ng、上面、下面、套环、正常...")]
|
||||
// [TypeConverter(typeof(CollectionCountConvert))]
|
||||
// [Editor(typeof(ComplexCollectionEditor<RecongnitionLabel>), typeof(UITypeEditor))]
|
||||
public List<RecongnitionLabel> RecongnitionLabelList { get; set; } = new List<RecongnitionLabel>();
|
||||
|
||||
[Category("深度学习检测配置")]
|
||||
[DisplayName("标签置信度")]
|
||||
[Description("标签置信度,过滤小于改置信度的标签,大于该设置的标签才能识别")]
|
||||
public float Score { get; set; } = 0.5f;
|
||||
|
||||
[Category("深度学习检测配置")]
|
||||
[DisplayName("CPU线程数量")]
|
||||
[Description("用于深度学习的CPU线程数量,不要设置太大,会单独占用线程,影响其他程序运行")]
|
||||
public int CPUNums { get; set; } = 1;
|
||||
|
||||
//[Category("深度学习检测配置")]
|
||||
//[DisplayName("检测项GPU指定")]
|
||||
//[Description("将检测项指定到GPU")]
|
||||
// [TypeConverter(typeof(CollectionCountConvert))]
|
||||
// [Editor(typeof(ComplexCollectionEditor<DetectionGPUConfig>), typeof(UITypeEditor))]
|
||||
// public List<DetectionGPUConfig> DetectionGPUList { get; set; } = new List<DetectionGPUConfig>();
|
||||
|
||||
// [Category("数据保存配置")]
|
||||
//[DisplayName("是否保存检测明细CSV")]
|
||||
//[Description("是否保存 检测明细CSV")]
|
||||
//public override bool IsEnableCSV { get; set; } = true;
|
||||
|
||||
//[Category("数据保存配置")]
|
||||
//[DisplayName("是否保存检测图片")]
|
||||
//[Description("是否保存 检测图片,总开关")]
|
||||
//public bool IsSaveImage { get; set; } = true;
|
||||
|
||||
//[Category("数据保存配置")]
|
||||
//[Description("检测图片 保存文件夹")]
|
||||
//[DisplayName("检测图片保存文件夹")]
|
||||
//[Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))]
|
||||
//public string ImageSaveDirectory { get; set; } = "D:\\PROJECTS\\X017\\Images";
|
||||
|
||||
//[Category("数据保存配置")]
|
||||
//[Description("检测明细CSV文件夹")]
|
||||
//[DisplayName("检测明细CSV文件夹")]
|
||||
//[Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))]
|
||||
//public string CSVDataPath { get; set; } = "D:\\PROJECTS\\X017\\Images";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user