using Bro.Common.Helper;
|
using Bro.Common.Interface;
|
using Newtonsoft.Json;
|
using System;
|
using System.ComponentModel;
|
using System.Threading;
|
using static Bro.Common.Helper.EnumHelper;
|
|
namespace Bro.Common.Base
|
{
|
public abstract class DeviceBase : IDevice
|
{
|
#region Event
|
[JsonIgnore]
|
[Browsable(false)]
|
public Action<DateTime, Exception> OnExceptionOccured { get; set; }
|
public event Action<DateTime, string, string> 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()
|
{
|
stateChangedTimer = new Timer(new TimerCallback(CheckDeviceOpTimeOut), null, Timeout.Infinite, Timeout.Infinite);
|
}
|
#endregion
|
|
#region 设备抽象方法
|
protected abstract void Init();
|
|
protected abstract void Pause();
|
|
protected abstract void Resume();
|
|
protected abstract void Start();
|
|
protected abstract void Stop();
|
|
//public abstract void AttachToProcess(IProcess process);
|
#endregion
|
|
#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)
|
{
|
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 日志处理
|
public LoggerHelper LoggerHelper { get; set; } = new LoggerHelper();
|
public virtual void LogAsync(DateTime dt, string prefix, string msg)
|
{
|
OnLog?.BeginInvoke(dt, $"{Name}_{ prefix}", msg, null, null);
|
|
if (InitialConfig.IsEnableLog)
|
{
|
LoggerHelper.LogAsync(dt, prefix, msg);
|
}
|
}
|
#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
|
}
|
}
|