using System; using Stateless; using VM.PlatformSDKCS; using VM.Core; namespace HkVisionPro.App.Automation { /// /// 自动装配流程状态枚举。 /// public enum AutoAssemblyState { /// /// 空闲状态,尚未启动自动流程。 /// Idle, /// /// A 层检测中。 /// LayerAInspecting, /// /// B 层检测中。 /// LayerBInspecting, /// /// C 层检测中。 /// LayerCInspecting, /// /// 单件产品 A/B/C 全部通过。 /// ProductCompleted, /// /// 自动流程已停止。 /// Stopped, /// /// 自动流程故障状态。 /// Faulted, } /// /// 自动装配流程状态通知事件参数。 /// public sealed class AutoAssemblyStatusChangedEventArgs : EventArgs { /// /// 当前状态。 /// public AutoAssemblyState State { get; } /// /// 状态说明文本。 /// public string Message { get; } /// /// 当前产品序号(从 1 开始)。 /// public int ProductIndex { get; } /// /// 当前激活流程名称。 /// public string ActiveProcedureName { get; } /// /// 当前稳定计时已累计毫秒数。 /// public int StableElapsedMilliseconds { get; } /// /// 稳定计时目标毫秒数。 /// public int StableTargetMilliseconds { get; } /// /// 状态变化事件参数构造函数。 /// /// 当前状态。 /// 状态说明。 /// 当前产品序号(从 1 开始)。 /// 当前流程名称。 /// 当前稳定计时累计毫秒数。 /// 稳定计时目标毫秒数。 public AutoAssemblyStatusChangedEventArgs( AutoAssemblyState state, string message, int productIndex = 0, string activeProcedureName = "", int stableElapsedMilliseconds = 0, int stableTargetMilliseconds = 0) { State = state; Message = message ?? string.Empty; ProductIndex = productIndex < 0 ? 0 : productIndex; ActiveProcedureName = activeProcedureName ?? string.Empty; StableElapsedMilliseconds = stableElapsedMilliseconds < 0 ? 0 : stableElapsedMilliseconds; StableTargetMilliseconds = stableTargetMilliseconds < 0 ? 0 : stableTargetMilliseconds; } } /// /// 自动装配流程配置参数。 /// public sealed class AutoAssemblyOptions { /// /// A 层流程名称。 /// public string ProcedureAName { get; set; } /// /// B 层流程名称。 /// public string ProcedureBName { get; set; } /// /// C 层流程名称。 /// public string ProcedureCName { get; set; } /// /// 判定 OK 的整型输出名。 /// public string OkIntOutputName { get; set; } /// /// 判定 OK 的第二个整型输出名(当配置后,需与第一个整型输出同时满足目标值才判定为 OK)。 /// public string OkIntOutputName2 { get; set; } /// /// 判定 OK 的整型目标值。 /// public int OkIntValue { get; set; } /// /// OK 持续稳定时间(毫秒)。 /// public int StableOkMilliseconds { get; set; } /// /// 校验配置参数有效性。 /// public void Validate() { if (string.IsNullOrWhiteSpace(ProcedureAName) || string.IsNullOrWhiteSpace(ProcedureBName) || string.IsNullOrWhiteSpace(ProcedureCName)) { throw new ArgumentException("自动装配流程名称配置无效,请检查 A/B/C 流程名称。", nameof(AutoAssemblyOptions)); } if (string.IsNullOrWhiteSpace(OkIntOutputName) || string.IsNullOrWhiteSpace(OkIntOutputName2)) { throw new ArgumentException("自动装配判定输出配置无效,请检查 Result1/Result2 输出名称。", nameof(AutoAssemblyOptions)); } if (StableOkMilliseconds <= 0) { throw new ArgumentOutOfRangeException(nameof(StableOkMilliseconds), "稳定判定时长必须大于 0。"); } } } /// /// 自动装配流程状态机触发器。 /// internal enum AutoAssemblyTrigger { /// /// 启动自动流程。 /// Start, /// /// 当前层流程结果持续 OK 达标。 /// StableOkReached, /// /// 停止自动流程。 /// Stop, /// /// 单件产品完成后进入下一件。 /// NextProduct, /// /// 自动流程异常。 /// Fault, } /// /// 自动装配流程控制器: /// 基于 Stateless 状态机实现 A -> B -> C 顺序检测,并以“连续 OK 达标时长”驱动层间切换。 /// public sealed class AutoAssemblyWorkflowController : IDisposable { /// /// 并发互斥锁。 /// private readonly object _syncRoot = new object(); /// /// 自动流程配置。 /// private readonly AutoAssemblyOptions _options; /// /// 日志输出委托。 /// private readonly Action _logger; /// /// 外部渲染回调(可选)。 /// private readonly Action _renderResultAction; /// /// Stateless 状态机。 /// private readonly StateMachine _stateMachine; /// /// 当前激活流程。 /// private VmProcedure _activeProcedure; /// /// 当前流程首次出现 OK 的起始时间(UTC)。 /// private DateTime _okBeginUtc; /// /// 当前是否处于 OK 稳定计时中。 /// private bool _isOkTiming; /// /// 控制器是否处于运行中。 /// private bool _isRunning; /// /// 控制器是否已释放。 /// private bool _isDisposed; /// /// 最近一次“输出缺失”日志时间(用于限流)。 /// private DateTime _lastMissingOutputLogUtc; /// /// 最近一次稳定计时进度上报时间(用于限流)。 /// private DateTime _lastStableProgressReportUtc; /// /// VisionMaster: 模块已处于连续执行中的错误码(IMVS_EC_MODULE_CONTINUE_EXECUTE)。 /// private const int VmErrorCodeModuleContinueExecute = -536870127; /// /// 当前产品序号(从 1 开始)。 /// private int _currentProductIndex; /// /// 状态变化事件。 /// public event EventHandler StatusChanged; /// /// 当前状态。 /// public AutoAssemblyState CurrentState => _stateMachine.State; /// /// 是否正在自动运行。 /// public bool IsRunning => _isRunning; /// /// 自动装配流程控制器构造函数。 /// /// 自动流程参数。 /// 日志输出委托(可为空)。 /// 渲染回调(可为空)。 public AutoAssemblyWorkflowController( AutoAssemblyOptions options, Action logger, Action renderResultAction) { _options = options ?? throw new ArgumentNullException(nameof(options)); _options.Validate(); _logger = logger; _renderResultAction = renderResultAction; _okBeginUtc = DateTime.MinValue; _lastMissingOutputLogUtc = DateTime.MinValue; _lastStableProgressReportUtc = DateTime.MinValue; _currentProductIndex = 1; _stateMachine = new StateMachine(AutoAssemblyState.Idle); ConfigureStateMachine(); } /// /// 启动自动装配流程。 /// public void Start() { lock (_syncRoot) { ThrowIfDisposed(); if (_isRunning) { return; } _currentProductIndex = 1; _okBeginUtc = DateTime.MinValue; _isOkTiming = false; _lastStableProgressReportUtc = DateTime.MinValue; _isRunning = true; try { FireIfPermitted(AutoAssemblyTrigger.Start); } catch { // 启动阶段若状态机进入失败,需回滚运行状态,避免外部误判“已运行”。 _isRunning = false; DeactivateCurrentProcedure("启动失败回滚"); throw; } } } /// /// 停止自动装配流程。 /// /// 停止原因。 public void Stop(string reason) { lock (_syncRoot) { if (_isDisposed) { return; } if (!_isRunning) { return; } FireIfPermitted(AutoAssemblyTrigger.Stop); _isRunning = false; PublishStatus(AutoAssemblyState.Stopped, $"自动流程停止:{reason}"); } } /// /// 释放控制器资源。 /// public void Dispose() { lock (_syncRoot) { if (_isDisposed) { return; } try { Stop("控制器释放"); DeactivateCurrentProcedure("控制器释放"); } finally { _isDisposed = true; } } } /// /// 配置状态机。 /// private void ConfigureStateMachine() { _stateMachine.Configure(AutoAssemblyState.Idle) .Permit(AutoAssemblyTrigger.Start, AutoAssemblyState.LayerAInspecting) .Permit(AutoAssemblyTrigger.Stop, AutoAssemblyState.Stopped); ConfigureLayerState(AutoAssemblyState.LayerAInspecting, _options.ProcedureAName, "A层", AutoAssemblyState.LayerBInspecting); ConfigureLayerState(AutoAssemblyState.LayerBInspecting, _options.ProcedureBName, "B层", AutoAssemblyState.LayerCInspecting); ConfigureLayerState(AutoAssemblyState.LayerCInspecting, _options.ProcedureCName, "C层", AutoAssemblyState.ProductCompleted); _stateMachine.Configure(AutoAssemblyState.ProductCompleted) .OnEntry(() => { PublishStatus(AutoAssemblyState.ProductCompleted, "整机检测结果:OK,进入下一产品。"); _currentProductIndex++; if (_isRunning) { FireIfPermitted(AutoAssemblyTrigger.NextProduct); } }) .Permit(AutoAssemblyTrigger.NextProduct, AutoAssemblyState.LayerAInspecting) .Permit(AutoAssemblyTrigger.Stop, AutoAssemblyState.Stopped) .Permit(AutoAssemblyTrigger.Fault, AutoAssemblyState.Faulted); _stateMachine.Configure(AutoAssemblyState.Stopped) .OnEntry(() => { _isRunning = false; DeactivateCurrentProcedure("状态机停止"); }) .Permit(AutoAssemblyTrigger.Start, AutoAssemblyState.LayerAInspecting); _stateMachine.Configure(AutoAssemblyState.Faulted) .OnEntry(() => { _isRunning = false; DeactivateCurrentProcedure("状态机故障"); PublishStatus(AutoAssemblyState.Faulted, "自动流程发生异常,已停止。"); }) .Permit(AutoAssemblyTrigger.Start, AutoAssemblyState.LayerAInspecting) .Permit(AutoAssemblyTrigger.Stop, AutoAssemblyState.Stopped); } /// /// 配置层级检测状态。 /// /// 状态机状态。 /// 流程名称。 /// 层级显示名。 /// 连续 OK 达标后的下一状态。 private void ConfigureLayerState( AutoAssemblyState state, string procedureName, string layerDisplayName, AutoAssemblyState nextState) { _stateMachine.Configure(state) .OnEntry(() => ActivateLayerProcedure(state, procedureName, layerDisplayName)) .OnExit(() => DeactivateCurrentProcedure($"离开{layerDisplayName}检测")) .Permit(AutoAssemblyTrigger.StableOkReached, nextState) .Permit(AutoAssemblyTrigger.Stop, AutoAssemblyState.Stopped) .Permit(AutoAssemblyTrigger.Fault, AutoAssemblyState.Faulted); } /// /// 进入层级检测状态时激活对应流程。 /// /// 目标状态。 /// 流程名称。 /// 层级显示名。 private void ActivateLayerProcedure(AutoAssemblyState state, string procedureName, string layerDisplayName) { if (string.IsNullOrWhiteSpace(procedureName)) { throw new InvalidOperationException($"{layerDisplayName}流程名称为空,无法启动自动检测。"); } DeactivateCurrentProcedure($"切换到{layerDisplayName}"); var procedure = VmSolution.Instance[procedureName] as VmProcedure; if (procedure == null) { throw new InvalidOperationException($"未找到自动流程:{procedureName}({layerDisplayName})。"); } _activeProcedure = procedure; _okBeginUtc = DateTime.MinValue; _isOkTiming = false; _lastStableProgressReportUtc = DateTime.MinValue; _activeProcedure.OnWorkEndStatusCallBack -= ActiveProcedure_OnWorkEndStatusCallBack; _activeProcedure.OnWorkEndStatusCallBack += ActiveProcedure_OnWorkEndStatusCallBack; _activeProcedure.ContinuousRunEnable = true; try { _activeProcedure.Run(); } catch (Exception ex) { if (IsModuleContinueExecuteVmException(ex)) { WriteLog($"自动流程[{procedureName}]已处于连续执行状态,沿用现有执行链路。\n若需重启请先停止当前连续执行。"); } else { throw; } } PublishStatus(state, $"{layerDisplayName}检测中(连续OK保持{_options.StableOkMilliseconds}ms后切换)。"); WriteLog($"自动流程切换到{layerDisplayName}:{procedureName}"); } /// /// 关闭当前激活流程并解绑回调。 /// /// 关闭原因。 private void DeactivateCurrentProcedure(string reason) { if (_activeProcedure == null) { _okBeginUtc = DateTime.MinValue; _isOkTiming = false; _lastStableProgressReportUtc = DateTime.MinValue; return; } try { _activeProcedure.OnWorkEndStatusCallBack -= ActiveProcedure_OnWorkEndStatusCallBack; _activeProcedure.ContinuousRunEnable = false; WriteLog($"自动流程停用:{_activeProcedure.Name}({reason})"); } catch (Exception ex) { WriteLog($"停用自动流程异常:{ex.Message}"); } finally { _activeProcedure = null; _okBeginUtc = DateTime.MinValue; _isOkTiming = false; _lastStableProgressReportUtc = DateTime.MinValue; } } /// /// 流程执行结束回调:用于判定 OK 稳定时长,并驱动状态机切换。 /// /// 回调事件源。 /// 标准事件参数。 private void ActiveProcedure_OnWorkEndStatusCallBack(object sender, EventArgs e) { lock (_syncRoot) { if (_isDisposed || !_isRunning) { return; } var procedure = sender as VmProcedure; if (procedure == null || !ReferenceEquals(procedure, _activeProcedure)) { return; } try { _renderResultAction?.Invoke(procedure, $"自动流程[{GetStateDisplayText(_stateMachine.State)}]"); var isOk = EvaluateProcedureResultIsOk(procedure, out var sourceName, out var sourceValue); if (!isOk) { if (_isOkTiming) { _isOkTiming = false; _okBeginUtc = DateTime.MinValue; _lastStableProgressReportUtc = DateTime.MinValue; PublishStatus(_stateMachine.State, $"{GetStateDisplayText(_stateMachine.State)}检测 NG({sourceName}={sourceValue}),继续等待。"); } return; } if (!_isOkTiming) { _isOkTiming = true; _okBeginUtc = DateTime.UtcNow; _lastStableProgressReportUtc = DateTime.UtcNow; PublishStatus(_stateMachine.State, $"{GetStateDisplayText(_stateMachine.State)}检测 OK,开始稳定计时:0/{_options.StableOkMilliseconds}ms。"); return; } var elapsedMs = (DateTime.UtcNow - _okBeginUtc).TotalMilliseconds; if (elapsedMs < _options.StableOkMilliseconds) { if ((DateTime.UtcNow - _lastStableProgressReportUtc).TotalMilliseconds >= 500) { _lastStableProgressReportUtc = DateTime.UtcNow; var currentMs = (int)Math.Max(0, elapsedMs); PublishStatus( _stateMachine.State, $"{GetStateDisplayText(_stateMachine.State)}检测 OK,稳定计时:{currentMs}/{_options.StableOkMilliseconds}ms。", false); } return; } _isOkTiming = false; _okBeginUtc = DateTime.MinValue; _lastStableProgressReportUtc = DateTime.MinValue; PublishStatus(_stateMachine.State, $"{GetStateDisplayText(_stateMachine.State)}检测连续 OK 达标,准备切换下一层。"); FireIfPermitted(AutoAssemblyTrigger.StableOkReached); } catch (Exception ex) { WriteLog($"自动流程回调异常:{ex.Message}"); FireIfPermitted(AutoAssemblyTrigger.Fault); } } } /// /// 判定当前流程结果是否为 OK。 /// 判定规则:Result1 和 Result2 两个整型输出都等于目标值,才判定为 OK。 /// /// 流程对象。 /// 命中的输出名。 /// 命中的输出值。 /// 是否判定为 OK。 private bool EvaluateProcedureResultIsOk(VmProcedure procedure, out string sourceName, out string sourceValue) { sourceName = string.Empty; sourceValue = string.Empty; if (procedure == null) { return false; } var hasFirstInt = TryReadOutputInt(procedure, _options.OkIntOutputName, out var firstIntValue); var secondIntValue = 0; var hasSecondInt = TryReadOutputInt(procedure, _options.OkIntOutputName2, out secondIntValue); sourceName = $"{_options.OkIntOutputName}/{_options.OkIntOutputName2}"; sourceValue = $"{(hasFirstInt ? firstIntValue.ToString() : "")}/{(hasSecondInt ? secondIntValue.ToString() : "")}"; if (hasFirstInt && hasSecondInt) { return firstIntValue == _options.OkIntValue && secondIntValue == _options.OkIntValue; } if ((DateTime.UtcNow - _lastMissingOutputLogUtc).TotalSeconds >= 5) { _lastMissingOutputLogUtc = DateTime.UtcNow; WriteLog($"自动流程未读到判定输出,已尝试 Int[{_options.OkIntOutputName}/{_options.OkIntOutputName2}]。"); } return false; } /// /// 尝试读取整型输出。 /// /// 流程对象。 /// 输出名。 /// 输出值。 /// 读取是否成功。 private static bool TryReadOutputInt(VmProcedure procedure, string outputName, out int value) { value = 0; if (procedure == null || string.IsNullOrWhiteSpace(outputName)) { return false; } try { var output = procedure.ModuResult.GetOutputInt(outputName); if (output.pIntVal == null || output.pIntVal.Length == 0) { return false; } value = output.pIntVal[0]; return true; } catch { return false; } } /// /// 判断异常是否为“模块已处于连续执行”错误。 /// /// 异常对象。 /// 若是连续执行中错误则返回 true。 private static bool IsModuleContinueExecuteVmException(Exception ex) { if (ex == null) { return false; } var vmException = VmExceptionTool.GetVmException(ex); return vmException != null && vmException.errorCode == VmErrorCodeModuleContinueExecute; } /// /// 触发状态更新事件。 /// /// 状态。 /// 消息。 /// 是否写入日志。 private void PublishStatus(AutoAssemblyState state, string message, bool writeLog = true) { var elapsedMs = 0; if (_isOkTiming && _okBeginUtc != DateTime.MinValue) { elapsedMs = (int)Math.Max(0, (DateTime.UtcNow - _okBeginUtc).TotalMilliseconds); if (_options.StableOkMilliseconds > 0) { elapsedMs = Math.Min(elapsedMs, _options.StableOkMilliseconds); } } var statusArgs = new AutoAssemblyStatusChangedEventArgs( state, message, _currentProductIndex, _activeProcedure?.Name ?? string.Empty, elapsedMs, _options.StableOkMilliseconds); StatusChanged?.Invoke(this, statusArgs); if (writeLog) { WriteLog(message); } } /// /// 依据状态返回中文显示文本。 /// /// 状态枚举。 /// 中文描述。 private static string GetStateDisplayText(AutoAssemblyState state) { switch (state) { case AutoAssemblyState.LayerAInspecting: return "A层"; case AutoAssemblyState.LayerBInspecting: return "B层"; case AutoAssemblyState.LayerCInspecting: return "C层"; case AutoAssemblyState.ProductCompleted: return "整机"; case AutoAssemblyState.Faulted: return "故障"; case AutoAssemblyState.Stopped: return "已停止"; default: return "空闲"; } } /// /// 若当前状态允许指定触发器,则触发状态机。 /// /// 触发器。 private void FireIfPermitted(AutoAssemblyTrigger trigger) { if (_stateMachine.CanFire(trigger)) { _stateMachine.Fire(trigger); } } /// /// 输出日志(允许日志委托为空)。 /// /// 日志内容。 private void WriteLog(string message) { _logger?.Invoke(message); } /// /// 已释放保护。 /// private void ThrowIfDisposed() { if (_isDisposed) { throw new ObjectDisposedException(nameof(AutoAssemblyWorkflowController)); } } } }