DHDHSoftware/DH.Commons/Base/DeviceBase.cs
2025-04-01 18:15:30 +08:00

390 lines
13 KiB
C#

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
}
}