|
|
@@ -0,0 +1,327 @@
|
|
|
+using Stateless;
|
|
|
+using Serilog;
|
|
|
+using YZWater.Core.Models;
|
|
|
+
|
|
|
+namespace YZWater.Core.Services;
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// 设备状态机服务 - 使用 Stateless 管理设备状态转换
|
|
|
+/// </summary>
|
|
|
+public class DeviceStateMachine
|
|
|
+{
|
|
|
+ private readonly StateMachine<DeviceState, DeviceTrigger> _stateMachine;
|
|
|
+ private readonly string _deviceId;
|
|
|
+ private readonly string _deviceName;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设备状态枚举(用于状态机)
|
|
|
+ /// </summary>
|
|
|
+ public enum DeviceState
|
|
|
+ {
|
|
|
+ /// <summary>停止</summary>
|
|
|
+ Stopped,
|
|
|
+ /// <summary>运行中</summary>
|
|
|
+ Running,
|
|
|
+ /// <summary>故障</summary>
|
|
|
+ Fault,
|
|
|
+ /// <summary>维护中</summary>
|
|
|
+ Maintenance
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设备触发器枚举
|
|
|
+ /// </summary>
|
|
|
+ public enum DeviceTrigger
|
|
|
+ {
|
|
|
+ /// <summary>启动</summary>
|
|
|
+ Start,
|
|
|
+ /// <summary>停止</summary>
|
|
|
+ Stop,
|
|
|
+ /// <summary>发生故障</summary>
|
|
|
+ Fault,
|
|
|
+ /// <summary>故障恢复</summary>
|
|
|
+ Recover,
|
|
|
+ /// <summary>进入维护</summary>
|
|
|
+ BeginMaintenance,
|
|
|
+ /// <summary>维护完成</summary>
|
|
|
+ CompleteMaintenance
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 当前状态
|
|
|
+ /// </summary>
|
|
|
+ public DeviceState CurrentState => _stateMachine.State;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 状态变更事件
|
|
|
+ /// </summary>
|
|
|
+ public event Action<string, DeviceState, DeviceState>? StateChanged;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 进入状态事件(用于触发报警、日志等)
|
|
|
+ /// </summary>
|
|
|
+ public event Action<string, DeviceState>? StateEntered;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 离开状态事件
|
|
|
+ /// </summary>
|
|
|
+ public event Action<string, DeviceState>? StateExited;
|
|
|
+
|
|
|
+ public DeviceStateMachine(string deviceId, string deviceName, DeviceState initialState = DeviceState.Stopped)
|
|
|
+ {
|
|
|
+ _deviceId = deviceId;
|
|
|
+ _deviceName = deviceName;
|
|
|
+ _stateMachine = new StateMachine<DeviceState, DeviceTrigger>(initialState);
|
|
|
+
|
|
|
+ ConfigureStateMachine();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 配置状态机转换规则
|
|
|
+ /// </summary>
|
|
|
+ private void ConfigureStateMachine()
|
|
|
+ {
|
|
|
+ // 停止状态
|
|
|
+ _stateMachine.Configure(DeviceState.Stopped)
|
|
|
+ .Permit(DeviceTrigger.Start, DeviceState.Running)
|
|
|
+ .Permit(DeviceTrigger.Fault, DeviceState.Fault)
|
|
|
+ .Permit(DeviceTrigger.BeginMaintenance, DeviceState.Maintenance)
|
|
|
+ .OnEntry(() => OnStateEntered(DeviceState.Stopped))
|
|
|
+ .OnExit(() => OnStateExited(DeviceState.Stopped));
|
|
|
+
|
|
|
+ // 运行状态
|
|
|
+ _stateMachine.Configure(DeviceState.Running)
|
|
|
+ .Permit(DeviceTrigger.Stop, DeviceState.Stopped)
|
|
|
+ .Permit(DeviceTrigger.Fault, DeviceState.Fault)
|
|
|
+ .Permit(DeviceTrigger.BeginMaintenance, DeviceState.Maintenance)
|
|
|
+ .OnEntry(() => OnStateEntered(DeviceState.Running))
|
|
|
+ .OnExit(() => OnStateExited(DeviceState.Running));
|
|
|
+
|
|
|
+ // 故障状态
|
|
|
+ _stateMachine.Configure(DeviceState.Fault)
|
|
|
+ .Permit(DeviceTrigger.Recover, DeviceState.Stopped)
|
|
|
+ .Permit(DeviceTrigger.BeginMaintenance, DeviceState.Maintenance)
|
|
|
+ .OnEntry(() => OnStateEntered(DeviceState.Fault))
|
|
|
+ .OnExit(() => OnStateExited(DeviceState.Fault));
|
|
|
+
|
|
|
+ // 维护状态
|
|
|
+ _stateMachine.Configure(DeviceState.Maintenance)
|
|
|
+ .Permit(DeviceTrigger.CompleteMaintenance, DeviceState.Stopped)
|
|
|
+ .Permit(DeviceTrigger.Fault, DeviceState.Fault)
|
|
|
+ .OnEntry(() => OnStateEntered(DeviceState.Maintenance))
|
|
|
+ .OnExit(() => OnStateExited(DeviceState.Maintenance));
|
|
|
+
|
|
|
+ // 状态转换回调
|
|
|
+ _stateMachine.OnTransitioned(transition =>
|
|
|
+ {
|
|
|
+ Log.Debug("设备 [{DeviceId}:{DeviceName}] 状态转换: {Source} → {Destination} (触发器: {Trigger})",
|
|
|
+ _deviceId, _deviceName, transition.Source, transition.Destination, transition.Trigger);
|
|
|
+ StateChanged?.Invoke(_deviceId, transition.Source, transition.Destination);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 注: Stateless 5.x 无 OnTransitionException,异常通过 FireSafe 方法捕获
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 进入状态时的回调
|
|
|
+ /// </summary>
|
|
|
+ private void OnStateEntered(DeviceState state)
|
|
|
+ {
|
|
|
+ Log.Information("设备 [{DeviceId}:{DeviceName}] 进入状态: {State}",
|
|
|
+ _deviceId, _deviceName, state);
|
|
|
+ StateEntered?.Invoke(_deviceId, state);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 离开状态时的回调
|
|
|
+ /// </summary>
|
|
|
+ private void OnStateExited(DeviceState state)
|
|
|
+ {
|
|
|
+ Log.Debug("设备 [{DeviceId}:{DeviceName}] 离开状态: {State}",
|
|
|
+ _deviceId, _deviceName, state);
|
|
|
+ StateExited?.Invoke(_deviceId, state);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ─── 触发方法 ───
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 安全触发(捕获异常并记录)
|
|
|
+ /// </summary>
|
|
|
+ private void FireSafe(DeviceTrigger trigger)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _stateMachine.Fire(trigger);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ Log.Error(ex, "设备 [{DeviceId}:{DeviceName}] 状态转换异常: 触发器 {Trigger}, 当前状态 {State}",
|
|
|
+ _deviceId, _deviceName, trigger, CurrentState);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 启动设备
|
|
|
+ /// </summary>
|
|
|
+ public void Start() => FireSafe(DeviceTrigger.Start);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 停止设备
|
|
|
+ /// </summary>
|
|
|
+ public void Stop() => FireSafe(DeviceTrigger.Stop);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 报告故障
|
|
|
+ /// </summary>
|
|
|
+ public void ReportFault() => FireSafe(DeviceTrigger.Fault);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 故障恢复
|
|
|
+ /// </summary>
|
|
|
+ public void Recover() => FireSafe(DeviceTrigger.Recover);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 进入维护
|
|
|
+ /// </summary>
|
|
|
+ public void BeginMaintenance() => FireSafe(DeviceTrigger.BeginMaintenance);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 完成维护
|
|
|
+ /// </summary>
|
|
|
+ public void CompleteMaintenance() => FireSafe(DeviceTrigger.CompleteMaintenance);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 尝试触发(不抛异常)
|
|
|
+ /// </summary>
|
|
|
+ public bool TryFire(DeviceTrigger trigger)
|
|
|
+ {
|
|
|
+ if (_stateMachine.CanFire(trigger))
|
|
|
+ {
|
|
|
+ _stateMachine.Fire(trigger);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ Log.Warning("设备 [{DeviceId}:{DeviceName}] 无法触发 {Trigger}: 当前状态 {State}",
|
|
|
+ _deviceId, _deviceName, trigger, CurrentState);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 是否可以触发
|
|
|
+ /// </summary>
|
|
|
+ public bool CanFire(DeviceTrigger trigger) => _stateMachine.CanFire(trigger);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 将 DeviceState 转换为旧版 DeviceStatus 枚举(兼容)
|
|
|
+ /// </summary>
|
|
|
+ public DeviceStatus ToDeviceStatus() => CurrentState switch
|
|
|
+ {
|
|
|
+ DeviceState.Stopped => DeviceStatus.Stopped,
|
|
|
+ DeviceState.Running => DeviceStatus.Running,
|
|
|
+ DeviceState.Fault => DeviceStatus.Fault,
|
|
|
+ DeviceState.Maintenance => DeviceStatus.Maintenance,
|
|
|
+ _ => DeviceStatus.Stopped
|
|
|
+ };
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取允许的状态转换列表
|
|
|
+ /// </summary>
|
|
|
+ public IEnumerable<DeviceTrigger> GetPermittedTriggers() => _stateMachine.PermittedTriggers;
|
|
|
+}
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// 设备状态机管理器 - 管理所有设备的状态机实例
|
|
|
+/// </summary>
|
|
|
+public class DeviceStateMachineManager
|
|
|
+{
|
|
|
+ private static DeviceStateMachineManager? _instance;
|
|
|
+ private static readonly object _lock = new();
|
|
|
+ public static DeviceStateMachineManager Instance
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ lock (_lock)
|
|
|
+ {
|
|
|
+ return _instance ??= new DeviceStateMachineManager();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private readonly Dictionary<string, DeviceStateMachine> _machines = new();
|
|
|
+ private readonly object _machinesLock = new();
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取或创建设备状态机
|
|
|
+ /// </summary>
|
|
|
+ public DeviceStateMachine GetOrCreate(string deviceId, string deviceName, DeviceStateMachine.DeviceState initialState = DeviceStateMachine.DeviceState.Stopped)
|
|
|
+ {
|
|
|
+ lock (_machinesLock)
|
|
|
+ {
|
|
|
+ if (!_machines.ContainsKey(deviceId))
|
|
|
+ {
|
|
|
+ _machines[deviceId] = new DeviceStateMachine(deviceId, deviceName, initialState);
|
|
|
+ Log.Information("创建设备状态机: {DeviceId} ({DeviceName})", deviceId, deviceName);
|
|
|
+ }
|
|
|
+ return _machines[deviceId];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取设备状态机
|
|
|
+ /// </summary>
|
|
|
+ public DeviceStateMachine? Get(string deviceId)
|
|
|
+ {
|
|
|
+ lock (_machinesLock)
|
|
|
+ {
|
|
|
+ return _machines.GetValueOrDefault(deviceId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取所有设备状态机
|
|
|
+ /// </summary>
|
|
|
+ public IReadOnlyDictionary<string, DeviceStateMachine> GetAll()
|
|
|
+ {
|
|
|
+ lock (_machinesLock)
|
|
|
+ {
|
|
|
+ return new Dictionary<string, DeviceStateMachine>(_machines);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 移除设备状态机
|
|
|
+ /// </summary>
|
|
|
+ public bool Remove(string deviceId)
|
|
|
+ {
|
|
|
+ lock (_machinesLock)
|
|
|
+ {
|
|
|
+ return _machines.Remove(deviceId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 清除所有状态机
|
|
|
+ /// </summary>
|
|
|
+ public void Clear()
|
|
|
+ {
|
|
|
+ lock (_machinesLock)
|
|
|
+ {
|
|
|
+ _machines.Clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 初始化默认设备(5台泵 + 2台风机)
|
|
|
+ /// </summary>
|
|
|
+ public void InitializeDefaultDevices()
|
|
|
+ {
|
|
|
+ for (int i = 1; i <= 5; i++)
|
|
|
+ {
|
|
|
+ GetOrCreate($"P{i:D3}", $"泵{i}");
|
|
|
+ }
|
|
|
+ for (int i = 1; i <= 2; i++)
|
|
|
+ {
|
|
|
+ GetOrCreate($"F{i:D3}", $"风机{i}");
|
|
|
+ }
|
|
|
+ Log.Information("已初始化 {Count} 台默认设备状态机", _machines.Count);
|
|
|
+ }
|
|
|
+}
|