806 lines
28 KiB
C#
806 lines
28 KiB
C#
using System;
|
||
using Stateless;
|
||
using VM.PlatformSDKCS;
|
||
using VM.Core;
|
||
|
||
namespace HkVisionPro.App.Automation
|
||
{
|
||
/// <summary>
|
||
/// 自动装配流程状态枚举。
|
||
/// </summary>
|
||
public enum AutoAssemblyState
|
||
{
|
||
/// <summary>
|
||
/// 空闲状态,尚未启动自动流程。
|
||
/// </summary>
|
||
Idle,
|
||
|
||
/// <summary>
|
||
/// A 层检测中。
|
||
/// </summary>
|
||
LayerAInspecting,
|
||
|
||
/// <summary>
|
||
/// B 层检测中。
|
||
/// </summary>
|
||
LayerBInspecting,
|
||
|
||
/// <summary>
|
||
/// C 层检测中。
|
||
/// </summary>
|
||
LayerCInspecting,
|
||
|
||
/// <summary>
|
||
/// 单件产品 A/B/C 全部通过。
|
||
/// </summary>
|
||
ProductCompleted,
|
||
|
||
/// <summary>
|
||
/// 自动流程已停止。
|
||
/// </summary>
|
||
Stopped,
|
||
|
||
/// <summary>
|
||
/// 自动流程故障状态。
|
||
/// </summary>
|
||
Faulted,
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动装配流程状态通知事件参数。
|
||
/// </summary>
|
||
public sealed class AutoAssemblyStatusChangedEventArgs : EventArgs
|
||
{
|
||
/// <summary>
|
||
/// 当前状态。
|
||
/// </summary>
|
||
public AutoAssemblyState State { get; }
|
||
|
||
/// <summary>
|
||
/// 状态说明文本。
|
||
/// </summary>
|
||
public string Message { get; }
|
||
|
||
/// <summary>
|
||
/// 当前产品序号(从 1 开始)。
|
||
/// </summary>
|
||
public int ProductIndex { get; }
|
||
|
||
/// <summary>
|
||
/// 当前激活流程名称。
|
||
/// </summary>
|
||
public string ActiveProcedureName { get; }
|
||
|
||
/// <summary>
|
||
/// 当前稳定计时已累计毫秒数。
|
||
/// </summary>
|
||
public int StableElapsedMilliseconds { get; }
|
||
|
||
/// <summary>
|
||
/// 稳定计时目标毫秒数。
|
||
/// </summary>
|
||
public int StableTargetMilliseconds { get; }
|
||
|
||
/// <summary>
|
||
/// 状态变化事件参数构造函数。
|
||
/// </summary>
|
||
/// <param name="state">当前状态。</param>
|
||
/// <param name="message">状态说明。</param>
|
||
/// <param name="productIndex">当前产品序号(从 1 开始)。</param>
|
||
/// <param name="activeProcedureName">当前流程名称。</param>
|
||
/// <param name="stableElapsedMilliseconds">当前稳定计时累计毫秒数。</param>
|
||
/// <param name="stableTargetMilliseconds">稳定计时目标毫秒数。</param>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动装配流程配置参数。
|
||
/// </summary>
|
||
public sealed class AutoAssemblyOptions
|
||
{
|
||
/// <summary>
|
||
/// A 层流程名称。
|
||
/// </summary>
|
||
public string ProcedureAName { get; set; }
|
||
|
||
/// <summary>
|
||
/// B 层流程名称。
|
||
/// </summary>
|
||
public string ProcedureBName { get; set; }
|
||
|
||
/// <summary>
|
||
/// C 层流程名称。
|
||
/// </summary>
|
||
public string ProcedureCName { get; set; }
|
||
|
||
/// <summary>
|
||
/// 判定 OK 的整型输出名。
|
||
/// </summary>
|
||
public string OkIntOutputName { get; set; }
|
||
|
||
/// <summary>
|
||
/// 判定 OK 的第二个整型输出名(当配置后,需与第一个整型输出同时满足目标值才判定为 OK)。
|
||
/// </summary>
|
||
public string OkIntOutputName2 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 判定 OK 的整型目标值。
|
||
/// </summary>
|
||
public int OkIntValue { get; set; }
|
||
|
||
/// <summary>
|
||
/// OK 持续稳定时间(毫秒)。
|
||
/// </summary>
|
||
public int StableOkMilliseconds { get; set; }
|
||
|
||
/// <summary>
|
||
/// 校验配置参数有效性。
|
||
/// </summary>
|
||
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。");
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动装配流程状态机触发器。
|
||
/// </summary>
|
||
internal enum AutoAssemblyTrigger
|
||
{
|
||
/// <summary>
|
||
/// 启动自动流程。
|
||
/// </summary>
|
||
Start,
|
||
|
||
/// <summary>
|
||
/// 当前层流程结果持续 OK 达标。
|
||
/// </summary>
|
||
StableOkReached,
|
||
|
||
/// <summary>
|
||
/// 停止自动流程。
|
||
/// </summary>
|
||
Stop,
|
||
|
||
/// <summary>
|
||
/// 单件产品完成后进入下一件。
|
||
/// </summary>
|
||
NextProduct,
|
||
|
||
/// <summary>
|
||
/// 自动流程异常。
|
||
/// </summary>
|
||
Fault,
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动装配流程控制器:
|
||
/// 基于 Stateless 状态机实现 A -> B -> C 顺序检测,并以“连续 OK 达标时长”驱动层间切换。
|
||
/// </summary>
|
||
public sealed class AutoAssemblyWorkflowController : IDisposable
|
||
{
|
||
/// <summary>
|
||
/// 并发互斥锁。
|
||
/// </summary>
|
||
private readonly object _syncRoot = new object();
|
||
|
||
/// <summary>
|
||
/// 自动流程配置。
|
||
/// </summary>
|
||
private readonly AutoAssemblyOptions _options;
|
||
|
||
/// <summary>
|
||
/// 日志输出委托。
|
||
/// </summary>
|
||
private readonly Action<string> _logger;
|
||
|
||
/// <summary>
|
||
/// 外部渲染回调(可选)。
|
||
/// </summary>
|
||
private readonly Action<VmProcedure, string> _renderResultAction;
|
||
|
||
/// <summary>
|
||
/// Stateless 状态机。
|
||
/// </summary>
|
||
private readonly StateMachine<AutoAssemblyState, AutoAssemblyTrigger> _stateMachine;
|
||
|
||
/// <summary>
|
||
/// 当前激活流程。
|
||
/// </summary>
|
||
private VmProcedure _activeProcedure;
|
||
|
||
/// <summary>
|
||
/// 当前流程首次出现 OK 的起始时间(UTC)。
|
||
/// </summary>
|
||
private DateTime _okBeginUtc;
|
||
|
||
/// <summary>
|
||
/// 当前是否处于 OK 稳定计时中。
|
||
/// </summary>
|
||
private bool _isOkTiming;
|
||
|
||
/// <summary>
|
||
/// 控制器是否处于运行中。
|
||
/// </summary>
|
||
private bool _isRunning;
|
||
|
||
/// <summary>
|
||
/// 控制器是否已释放。
|
||
/// </summary>
|
||
private bool _isDisposed;
|
||
|
||
/// <summary>
|
||
/// 最近一次“输出缺失”日志时间(用于限流)。
|
||
/// </summary>
|
||
private DateTime _lastMissingOutputLogUtc;
|
||
|
||
/// <summary>
|
||
/// 最近一次稳定计时进度上报时间(用于限流)。
|
||
/// </summary>
|
||
private DateTime _lastStableProgressReportUtc;
|
||
|
||
/// <summary>
|
||
/// VisionMaster: 模块已处于连续执行中的错误码(IMVS_EC_MODULE_CONTINUE_EXECUTE)。
|
||
/// </summary>
|
||
private const int VmErrorCodeModuleContinueExecute = -536870127;
|
||
|
||
/// <summary>
|
||
/// 当前产品序号(从 1 开始)。
|
||
/// </summary>
|
||
private int _currentProductIndex;
|
||
|
||
/// <summary>
|
||
/// 状态变化事件。
|
||
/// </summary>
|
||
public event EventHandler<AutoAssemblyStatusChangedEventArgs> StatusChanged;
|
||
|
||
/// <summary>
|
||
/// 当前状态。
|
||
/// </summary>
|
||
public AutoAssemblyState CurrentState => _stateMachine.State;
|
||
|
||
/// <summary>
|
||
/// 是否正在自动运行。
|
||
/// </summary>
|
||
public bool IsRunning => _isRunning;
|
||
|
||
/// <summary>
|
||
/// 自动装配流程控制器构造函数。
|
||
/// </summary>
|
||
/// <param name="options">自动流程参数。</param>
|
||
/// <param name="logger">日志输出委托(可为空)。</param>
|
||
/// <param name="renderResultAction">渲染回调(可为空)。</param>
|
||
public AutoAssemblyWorkflowController(
|
||
AutoAssemblyOptions options,
|
||
Action<string> logger,
|
||
Action<VmProcedure, string> 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, AutoAssemblyTrigger>(AutoAssemblyState.Idle);
|
||
ConfigureStateMachine();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动自动装配流程。
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止自动装配流程。
|
||
/// </summary>
|
||
/// <param name="reason">停止原因。</param>
|
||
public void Stop(string reason)
|
||
{
|
||
lock (_syncRoot)
|
||
{
|
||
if (_isDisposed)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!_isRunning)
|
||
{
|
||
return;
|
||
}
|
||
|
||
FireIfPermitted(AutoAssemblyTrigger.Stop);
|
||
_isRunning = false;
|
||
PublishStatus(AutoAssemblyState.Stopped, $"自动流程停止:{reason}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放控制器资源。
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
lock (_syncRoot)
|
||
{
|
||
if (_isDisposed)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
Stop("控制器释放");
|
||
DeactivateCurrentProcedure("控制器释放");
|
||
}
|
||
finally
|
||
{
|
||
_isDisposed = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置状态机。
|
||
/// </summary>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置层级检测状态。
|
||
/// </summary>
|
||
/// <param name="state">状态机状态。</param>
|
||
/// <param name="procedureName">流程名称。</param>
|
||
/// <param name="layerDisplayName">层级显示名。</param>
|
||
/// <param name="nextState">连续 OK 达标后的下一状态。</param>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 进入层级检测状态时激活对应流程。
|
||
/// </summary>
|
||
/// <param name="state">目标状态。</param>
|
||
/// <param name="procedureName">流程名称。</param>
|
||
/// <param name="layerDisplayName">层级显示名。</param>
|
||
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}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭当前激活流程并解绑回调。
|
||
/// </summary>
|
||
/// <param name="reason">关闭原因。</param>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 流程执行结束回调:用于判定 OK 稳定时长,并驱动状态机切换。
|
||
/// </summary>
|
||
/// <param name="sender">回调事件源。</param>
|
||
/// <param name="e">标准事件参数。</param>
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判定当前流程结果是否为 OK。
|
||
/// 判定规则:Result1 和 Result2 两个整型输出都等于目标值,才判定为 OK。
|
||
/// </summary>
|
||
/// <param name="procedure">流程对象。</param>
|
||
/// <param name="sourceName">命中的输出名。</param>
|
||
/// <param name="sourceValue">命中的输出值。</param>
|
||
/// <returns>是否判定为 OK。</returns>
|
||
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() : "<missing>")}/{(hasSecondInt ? secondIntValue.ToString() : "<missing>")}";
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试读取整型输出。
|
||
/// </summary>
|
||
/// <param name="procedure">流程对象。</param>
|
||
/// <param name="outputName">输出名。</param>
|
||
/// <param name="value">输出值。</param>
|
||
/// <returns>读取是否成功。</returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断异常是否为“模块已处于连续执行”错误。
|
||
/// </summary>
|
||
/// <param name="ex">异常对象。</param>
|
||
/// <returns>若是连续执行中错误则返回 true。</returns>
|
||
private static bool IsModuleContinueExecuteVmException(Exception ex)
|
||
{
|
||
if (ex == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
var vmException = VmExceptionTool.GetVmException(ex);
|
||
return vmException != null && vmException.errorCode == VmErrorCodeModuleContinueExecute;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发状态更新事件。
|
||
/// </summary>
|
||
/// <param name="state">状态。</param>
|
||
/// <param name="message">消息。</param>
|
||
/// <param name="writeLog">是否写入日志。</param>
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 依据状态返回中文显示文本。
|
||
/// </summary>
|
||
/// <param name="state">状态枚举。</param>
|
||
/// <returns>中文描述。</returns>
|
||
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 "空闲";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 若当前状态允许指定触发器,则触发状态机。
|
||
/// </summary>
|
||
/// <param name="trigger">触发器。</param>
|
||
private void FireIfPermitted(AutoAssemblyTrigger trigger)
|
||
{
|
||
if (_stateMachine.CanFire(trigger))
|
||
{
|
||
_stateMachine.Fire(trigger);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 输出日志(允许日志委托为空)。
|
||
/// </summary>
|
||
/// <param name="message">日志内容。</param>
|
||
private void WriteLog(string message)
|
||
{
|
||
_logger?.Invoke(message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 已释放保护。
|
||
/// </summary>
|
||
private void ThrowIfDisposed()
|
||
{
|
||
if (_isDisposed)
|
||
{
|
||
throw new ObjectDisposedException(nameof(AutoAssemblyWorkflowController));
|
||
}
|
||
}
|
||
}
|
||
}
|