using Bro.Common.Interface;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using Bro.Common.Helper;
|
using System.Reflection;
|
using Newtonsoft.Json;
|
using System.Threading;
|
using System.Diagnostics;
|
using System.Dynamic;
|
using System.Linq.Expressions;
|
//using BroCComn.PubSub;
|
using static Bro.Common.Helper.EnumHelper;
|
using System.ComponentModel;
|
|
namespace Bro.Common.Base
|
{
|
//public delegate void DeviceStateChangedDelegate(EnumHelper.DeviceState initialState);
|
public abstract class DeviceBase : IDevice, IDisposable
|
{
|
#region Event
|
[JsonIgnore]
|
public Action<IDevice, Exception> OnDeviceException { get; set; }
|
[JsonIgnore]
|
public Action<DateTime, IDevice, string> OnLog { get; set; }
|
[JsonIgnore]
|
public Action<EnumHelper.DeviceState> OnDeviceStateChanged { get; set; }
|
#endregion
|
|
|
int RetryTime = 3;
|
/// <summary>
|
/// 和设备暂停状态关联的信号量
|
/// </summary>
|
private ManualResetEvent pauseHandle = new ManualResetEvent(true);
|
|
Timer stateChangedTimer;
|
|
internal bool stateChangedFlag = false;
|
|
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(initialState);
|
OnDeviceStateChangedWithDeviceInfo?.Invoke(this, _currentState);
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentState"));
|
}
|
else
|
{
|
initialState = _currentState;
|
}
|
}
|
}
|
|
/// <summary>
|
/// 设备状态 编辑时或者运行时
|
/// </summary>
|
public EnumHelper.DeviceMode DeviceMode { get; set; } = EnumHelper.DeviceMode.Edit;
|
|
/// <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;
|
this.Id = initialConfig.ID;
|
this.Name = initialConfig.Name;
|
}
|
}
|
|
#region IPorcessId
|
/// <summary>
|
/// 设备当前执行工序编号
|
/// </summary>
|
[JsonIgnore]
|
public string ProcessId { get; set; }
|
|
/// <summary>
|
/// 设备当前执行工序名称
|
/// </summary>
|
[JsonIgnore]
|
public string ProcessName { get; set; }
|
#endregion
|
|
[JsonIgnore]
|
public Dictionary<DeviceOutputMethodAttribute, MethodInfo> OutputMethods { get; set; } = new Dictionary<DeviceOutputMethodAttribute, MethodInfo>();
|
#region 延迟加载方法字典方式 现转移到构造函数中实现
|
//{
|
// get
|
// {
|
// if (outputMethods == null)
|
// {
|
// this.GetType().GetMethods().ToList().ForEach(m =>
|
// {
|
// var attr = m.GetCustomAttribute<DeviceOutputMethodAttribute>();
|
// if (attr != null)
|
// {
|
// outputMethods[attr.MethodName] = m;
|
// }
|
// });
|
// }
|
|
// return outputMethods;
|
// }
|
// set
|
// {
|
// outputMethods = value;
|
// }
|
//}
|
#endregion
|
|
[JsonIgnore]
|
public Dictionary<DeviceInputMethodAttribute, MethodInfo> InputMethods { get; set; } = new Dictionary<DeviceInputMethodAttribute, MethodInfo>();
|
|
|
|
//public event DeviceStateChangedDelegate OnDeviceStateChanged;
|
|
public DeviceBase()
|
{
|
//预先获取所有设备输出方法
|
this.GetType().GetMethods().ToList().ForEach(m =>
|
{
|
var attrOut = m.GetCustomAttribute<DeviceOutputMethodAttribute>();
|
if (attrOut != null)
|
{
|
OutputMethods[attrOut] = m;
|
}
|
|
var attrIn = m.GetCustomAttribute<DeviceInputMethodAttribute>();
|
if (attrIn != null)
|
{
|
InputMethods[attrIn] = m;
|
}
|
});
|
|
|
stateChangedTimer = new Timer(new TimerCallback(CheckDeviceOpTimeOut), null, Timeout.Infinite, Timeout.Infinite);
|
|
//StateMachine.OnUserLogin = OnCurrentUserLogin;
|
}
|
|
protected abstract void Init();
|
|
protected abstract void Pause();
|
|
protected abstract void Resume();
|
|
protected abstract void Start();
|
|
protected abstract void Stop();
|
|
public virtual void Dispose()
|
{
|
|
}
|
|
object OpLock = new Object(); //操作锁,一个设备,一次只能做一个动作
|
|
public event DeviceStateChangedDelegate OnDeviceStateChangedWithDeviceInfo;
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
/// <summary>
|
/// 设备Run之前工序进入Running状态,设备Run完成之后工序进入Done状态
|
/// 发生异常时,设备和工序都进入异常状态
|
/// 考虑使用AOP切面实现前后状态变化
|
/// </summary>
|
/// <param name="config"></param>
|
/// <returns></returns>
|
[DeviceExceptionAspect]
|
[DeviceRunAspect]
|
public virtual IOperationConfig SetAndRun(IOperationConfig config)
|
{
|
//try
|
//{
|
//lock (OpLock)
|
//{
|
//if (string.IsNullOrWhiteSpace(config.ProcessId))
|
//{
|
// DeviceMode = EnumHelper.DeviceMode.Edit;
|
//}
|
//else
|
//{
|
// DeviceMode = EnumHelper.DeviceMode.Run;
|
|
// ProcessId = config.ProcessId;
|
// ProcessName = config.ProcessName;
|
//}
|
|
//pauseHandle.WaitOne();
|
//if (config.DeviceConfig != null)
|
//{
|
// DeviceSet(config.DeviceConfig);
|
//}
|
|
//bool inputFlag = false;
|
//do
|
//{
|
// pauseHandle.WaitOne();
|
// inputFlag = DeviceInput(config);
|
//} while (!inputFlag);
|
|
//if (CurrentState != EnumHelper.DeviceState.DSOpen)
|
//{
|
// return null;
|
//}
|
|
pauseHandle.WaitOne();
|
|
if (config.IsEnabled)
|
{
|
DeviceRun(config);
|
}
|
pauseHandle.WaitOne();
|
IOperationConfig output = DeviceOutput(config);
|
|
//config.InputConfig?.Dispose();
|
|
return output;
|
//}
|
//catch (Exception ex)
|
//{
|
// return null;
|
//}
|
//}
|
}
|
|
/// <summary>
|
/// 根据设备配置信息配置设备参数
|
/// </summary>
|
/// <param name="config"></param>
|
protected abstract void DeviceSet(IDeviceConfig config);
|
|
protected virtual bool DeviceInput(IOperationConfig config)
|
{
|
////如果需要设备输入配置判断
|
//if (config.InputConfig != null && config.InputConfig.IsInputNeed)
|
//{
|
// //config.InputConfig.BoundFlag = true;
|
// //等待输入配置信号量
|
// //config.InputConfig.InputHanlderDict[config.ProcessId]?.WaitOne();
|
// //while (config.InputConfig.InputFlagDict[config.ProcessId] == false && CurrentState == EnumHelper.DeviceState.DSOpen)
|
// //{
|
// // Thread.Sleep(10);
|
// //}
|
|
// //config.InputConfig.InputFlagDict[config.ProcessId] = false;
|
|
// config.InputConfig.InputHandle.WaitOne();
|
// //Debug.WriteLine(DateTime.Now.ToString("mm:ss.fff") + "\t输入完成,编号:" + config.InputConfig.ProcessId);
|
|
// if (CurrentState != EnumHelper.DeviceState.DSOpen)
|
// {
|
// return true;
|
// }
|
|
// //config.InputConfig.InputHanlderDict[config.ProcessId].Reset();
|
|
// if (!string.IsNullOrWhiteSpace(config.InputConfig.InputMethodName))
|
// {
|
// var attr = InputMethods.Keys.FirstOrDefault(u => u.MethodName == config.InputConfig.InputMethodName);
|
// if (attr != null)
|
// {
|
// return Convert.ToBoolean(InputMethods[attr].Invoke(this, new object[] { config }));
|
// }
|
// else
|
// {
|
// //throw new UserException(string.Format("未能获取{0}的方法信息!", config.InputConfig.InputMethodName), EnumHelper.LogType.Exception_Fatal);
|
// throw new Exception(string.Format("未能获取{0}的方法信息!", config.InputConfig.InputMethodName));
|
// }
|
// }
|
//}
|
|
return true;
|
}
|
|
/// <summary>
|
/// 设备运行
|
/// </summary>
|
/// <param name="config"></param>
|
protected abstract void DeviceRun(IOperationConfig config);
|
|
/// <summary>
|
/// 具体的输出方法返回IOperationConfig,必须包括ProcessId和ResultOutput
|
/// </summary>
|
/// <param name="config"></param>
|
/// <returns></returns>
|
protected virtual IOperationConfig DeviceOutput(IOperationConfig config)
|
{
|
//if (string.IsNullOrWhiteSpace(config.OutputMethodName))
|
//{
|
return null;
|
//}
|
//else
|
//{
|
// #region 实时获取输出方法 考虑到效率问题,将方法字典转移到构造函数中实现
|
// //var methodInfo = this.GetType().GetMethods().FirstOrDefault(m =>
|
// // {
|
// // var attr = m.GetCustomAttribute<DeviceOutputMethodAttribute>();
|
|
// // return attr != null && attr.MethodName == config.OutputMethodName;
|
// // });
|
|
// //if (methodInfo == null)
|
// //{
|
// // throw new UserException(string.Format("未能获取{0}的方法信息!", config.OutputMethodName));
|
// //}
|
// //else
|
// //{
|
// // return (methodInfo.Invoke(this, new object[] { config })) as IOperationConfig;
|
// //}
|
// #endregion
|
|
// var attr = OutputMethods.Keys.FirstOrDefault(u => u.MethodName == config.OutputMethodName);
|
// if (attr != null)
|
// {
|
// return (OutputMethods[attr].Invoke(this, new object[] { config })) as IOperationConfig;
|
// }
|
// else
|
// {
|
// //throw new UserException(string.Format("未能获取{0}的方法信息!", config.OutputMethodName), EnumHelper.LogType.Exception_Fatal);
|
// throw new Exception(string.Format("未能获取{0}的方法信息!", config.OutputMethodName));
|
// }
|
//}
|
}
|
|
[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}", null);
|
}
|
|
CurrentState = EnumHelper.DeviceState.TBD;
|
CurrentStateToBe = stateToBe;
|
}
|
|
[DeviceStateChangedAspect]
|
[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}设备操作超时", null);
|
}
|
}
|
|
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 (this.CurrentState != EnumHelper.DeviceState.DSExcept)
|
{
|
//this.CurrentState = EnumHelper.DeviceState.DSExcept;
|
//this.CurrentStateToBe = EnumHelper.DeviceState.DSExcept;
|
|
throw new ProcessException($"设备{InitialConfig.Name}的{CurrentStateToBe.GetEnumDescription()}操作重复3次失败", ex);
|
}
|
}
|
}
|
}
|
|
[DeviceExceptionAspect]
|
private void CheckDeviceOpTimeOut(object state)
|
{
|
stateChangedTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
|
if (CurrentState != EnumHelper.DeviceState.DSExcept)
|
{
|
StateChange(EnumHelper.DeviceState.DSExcept);
|
}
|
}
|
|
#region 用户信息
|
[JsonIgnore]
|
public string CurrentUserId { get; set; }
|
|
private void OnCurrentUserLogin(string obj)
|
{
|
CurrentUserId = obj;
|
}
|
#endregion
|
}
|
}
|