初步可以运行,未严格调试
This commit is contained in:
14
.windsurf/rules/pro1.md
Normal file
14
.windsurf/rules/pro1.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
trigger: always_on
|
||||||
|
---
|
||||||
|
## 我先开发一个视觉的检测系统,使用海康的硬件和算子二次开发
|
||||||
|
|
||||||
|
### 被检测的产品放到工位上然后工人进行组装,安装一些部件,此时调用A层(底板) 的VisionMaster解决方案的A流程进行实时检测(具体检测内容由A流程进行定义),工人所有部件安装完毕后并且A流程能检测到的结果是所有都安装OK时,视觉检测都OK的状态维持2秒,代表A层板部件安装和检测完毕,进行提示,程序切入VisionMaster解决方案的B流程(具体检测内容由B流程进行定义)进行实时检测,工人放盖板到B层继续部件安装,工人所有部件安装完毕后并且B流程能检测到的结果是所有都安装OK时,视觉检测都OK的状态维持2秒,代表B层板部件安装和检测完毕,进行提示,程序切入VisionMaster解决方案的C流程,依次类推,C层板部件安装和检测OK完毕,整个产品组装完毕,总结果提示OK,循环进入下一个产品的组装。
|
||||||
|
|
||||||
|
### 如果A流程的部件安装没有完成(那么视觉检测A流程的结果肯定也是NG),是不允许进入B流程的,即是上一个流程没完成不允许进入下一个流程,整个过程在不出现异常时是自动运行的
|
||||||
|
|
||||||
|
### 流程管理我使用Stateless状态机进行管理
|
||||||
|
|
||||||
|
### VisionMaster解决方案里面我会按照约定放置好A B C 三个流程,供程序调用
|
||||||
|
|
||||||
|
### 不要把逻辑放到Main界面中,界面里面很多的按钮代表手动的已经开发好了,自动的按照上面描述的流程进行开发
|
||||||
@@ -5,16 +5,28 @@
|
|||||||
<add key="StartServerByExe" value="0" />
|
<add key="StartServerByExe" value="0" />
|
||||||
<!--服务绝对路径,用于拉起服务(value为""时默认拉起系统服务,value为服务路径时为以进程方式启动)-->
|
<!--服务绝对路径,用于拉起服务(value为""时默认拉起系统服务,value为服务路径时为以进程方式启动)-->
|
||||||
<add key="ServerPath" value=""/>
|
<add key="ServerPath" value=""/>
|
||||||
<!--流程执行回调读取的字符串输出名称(默认 out0,来自流程输出设置)-->
|
<!--画面渲染使用的图像输出名称(默认 ImageData0,来自流程输出设置)-->
|
||||||
<add key="ProcedureOutputCodeName" value="out0" />
|
<add key="RenderImageOutputName" value="ImageData0" />
|
||||||
<!--流程执行回调读取的整数输出名称(默认 out,来自流程输出设置)-->
|
<!--画面渲染使用的主 ROI 输出名称(默认 InputR1,来自流程输出设置)-->
|
||||||
<add key="ProcedureOutputNumberName" value="out" />
|
<add key="RenderRoiOutputName" value="InputR1" />
|
||||||
<!--画面渲染使用的图像输出名称(默认 ImageData,来自流程输出设置)-->
|
<!--画面渲染使用的次 ROI 输出名称(默认 InputR2,来自流程输出设置)-->
|
||||||
<add key="RenderImageOutputName" value="ImageData" />
|
<add key="RenderRoiOutputName2" value="InputR2" />
|
||||||
<!--画面渲染使用的 ROI 输出名称(默认 InputROI,来自流程输出设置)-->
|
<!--画面渲染使用的第1个判定整型输出名称(默认 Result1)-->
|
||||||
<add key="RenderRoiOutputName" value="InputROI" />
|
<add key="RenderResultIntOutputName1" value="Result1" />
|
||||||
<!--画面渲染使用的文本输出名称(默认 out0)-->
|
<!--画面渲染使用的第2个判定整型输出名称(默认 Result2)-->
|
||||||
<add key="RenderCodeOutputName" value="out0" />
|
<add key="RenderResultIntOutputName2" value="Result2" />
|
||||||
|
<!--自动装配:方案加载后是否自动启动 A/B/C 状态机流程-->
|
||||||
|
<add key="AutoRunEnabledAfterSolutionLoaded" value="false" />
|
||||||
|
<!--自动装配:A/B/C 三层流程名称(需与 VisionMaster 方案中的流程名一致)-->
|
||||||
|
<add key="AutoRunProcedureAName" value="A-Layer" />
|
||||||
|
<add key="AutoRunProcedureBName" value="B-Layer" />
|
||||||
|
<add key="AutoRunProcedureCName" value="C-Layer" />
|
||||||
|
<!--自动装配:优先使用双整型输出做 OK 判定(Result1=1 且 Result2=1 才表示 OK)-->
|
||||||
|
<add key="AutoRunOkIntOutputName" value="Result1" />
|
||||||
|
<add key="AutoRunOkIntOutputName2" value="Result2" />
|
||||||
|
<add key="AutoRunOkIntValue" value="1" />
|
||||||
|
<!--自动装配:连续 OK 维持时长(毫秒),达到后切入下一层流程-->
|
||||||
|
<add key="AutoRunStableMilliseconds" value="2000" />
|
||||||
<!--<add key="ServerPath" value="E:\Program Files\VisionMaster\VisionMaster4.1.0\Applications\Server\VisionMasterServer.exe"/>-->
|
<!--<add key="ServerPath" value="E:\Program Files\VisionMaster\VisionMaster4.1.0\Applications\Server\VisionMasterServer.exe"/>-->
|
||||||
<add key="ClientSettingsProvider.ServiceUri" value="" />
|
<add key="ClientSettingsProvider.ServiceUri" value="" />
|
||||||
</appSettings>
|
</appSettings>
|
||||||
|
|||||||
805
HkVisionPro.App/Automation/AutoAssemblyWorkflowController.cs
Normal file
805
HkVisionPro.App/Automation/AutoAssemblyWorkflowController.cs
Normal file
@@ -0,0 +1,805 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -585,6 +585,7 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Automation\AutoAssemblyWorkflowController.cs" />
|
||||||
<Compile Include="MainForm.cs">
|
<Compile Include="MainForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
93
HkVisionPro.App/MainForm.Designer.cs
generated
93
HkVisionPro.App/MainForm.Designer.cs
generated
@@ -40,6 +40,7 @@
|
|||||||
this.label4 = new System.Windows.Forms.Label();
|
this.label4 = new System.Windows.Forms.Label();
|
||||||
this.vmProcedureConfigControl1 = new VMControls.Winform.Release.VmProcedureConfigControl();
|
this.vmProcedureConfigControl1 = new VMControls.Winform.Release.VmProcedureConfigControl();
|
||||||
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||||
|
this.btnAutoRun = new System.Windows.Forms.Button();
|
||||||
this.btnProExecStop = new System.Windows.Forms.Button();
|
this.btnProExecStop = new System.Windows.Forms.Button();
|
||||||
this.btnProExecAlway = new System.Windows.Forms.Button();
|
this.btnProExecAlway = new System.Windows.Forms.Button();
|
||||||
this.btnProExecOnce = new System.Windows.Forms.Button();
|
this.btnProExecOnce = new System.Windows.Forms.Button();
|
||||||
@@ -57,17 +58,20 @@
|
|||||||
this.pictureBox1 = new System.Windows.Forms.PictureBox();
|
this.pictureBox1 = new System.Windows.Forms.PictureBox();
|
||||||
this.groupBox6 = new System.Windows.Forms.GroupBox();
|
this.groupBox6 = new System.Windows.Forms.GroupBox();
|
||||||
this.vmParamsConfigControl1 = new VMControls.Winform.Release.VmParamsConfigControl();
|
this.vmParamsConfigControl1 = new VMControls.Winform.Release.VmParamsConfigControl();
|
||||||
|
this.groupBox5 = new System.Windows.Forms.GroupBox();
|
||||||
|
this.lblRunState = new System.Windows.Forms.Label();
|
||||||
this.groupBox1.SuspendLayout();
|
this.groupBox1.SuspendLayout();
|
||||||
this.groupBox2.SuspendLayout();
|
this.groupBox2.SuspendLayout();
|
||||||
this.groupBox3.SuspendLayout();
|
this.groupBox3.SuspendLayout();
|
||||||
this.groupBox4.SuspendLayout();
|
this.groupBox4.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
||||||
this.groupBox6.SuspendLayout();
|
this.groupBox6.SuspendLayout();
|
||||||
|
this.groupBox5.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// btnLoadSolution
|
// btnLoadSolution
|
||||||
//
|
//
|
||||||
this.btnLoadSolution.Location = new System.Drawing.Point(103, 93);
|
this.btnLoadSolution.Location = new System.Drawing.Point(114, 52);
|
||||||
this.btnLoadSolution.Name = "btnLoadSolution";
|
this.btnLoadSolution.Name = "btnLoadSolution";
|
||||||
this.btnLoadSolution.Size = new System.Drawing.Size(127, 38);
|
this.btnLoadSolution.Size = new System.Drawing.Size(127, 38);
|
||||||
this.btnLoadSolution.TabIndex = 0;
|
this.btnLoadSolution.TabIndex = 0;
|
||||||
@@ -77,24 +81,25 @@
|
|||||||
//
|
//
|
||||||
// txtSolutionAddress
|
// txtSolutionAddress
|
||||||
//
|
//
|
||||||
this.txtSolutionAddress.Location = new System.Drawing.Point(103, 20);
|
this.txtSolutionAddress.Location = new System.Drawing.Point(101, 23);
|
||||||
this.txtSolutionAddress.Name = "txtSolutionAddress";
|
this.txtSolutionAddress.Name = "txtSolutionAddress";
|
||||||
this.txtSolutionAddress.Size = new System.Drawing.Size(393, 23);
|
this.txtSolutionAddress.Size = new System.Drawing.Size(393, 23);
|
||||||
this.txtSolutionAddress.TabIndex = 2;
|
this.txtSolutionAddress.TabIndex = 2;
|
||||||
//
|
//
|
||||||
// btnSelctedSolution
|
// btnSelctedSolution
|
||||||
//
|
//
|
||||||
this.btnSelctedSolution.Location = new System.Drawing.Point(369, 49);
|
this.btnSelctedSolution.BackColor = System.Drawing.Color.Transparent;
|
||||||
|
this.btnSelctedSolution.Location = new System.Drawing.Point(10, 52);
|
||||||
this.btnSelctedSolution.Name = "btnSelctedSolution";
|
this.btnSelctedSolution.Name = "btnSelctedSolution";
|
||||||
this.btnSelctedSolution.Size = new System.Drawing.Size(127, 38);
|
this.btnSelctedSolution.Size = new System.Drawing.Size(100, 38);
|
||||||
this.btnSelctedSolution.TabIndex = 3;
|
this.btnSelctedSolution.TabIndex = 3;
|
||||||
this.btnSelctedSolution.Text = "选择文件方案";
|
this.btnSelctedSolution.Text = "选择文件方案";
|
||||||
this.btnSelctedSolution.UseVisualStyleBackColor = true;
|
this.btnSelctedSolution.UseVisualStyleBackColor = false;
|
||||||
this.btnSelctedSolution.Click += new System.EventHandler(this.btnSelctedSolution_Click);
|
this.btnSelctedSolution.Click += new System.EventHandler(this.btnSelctedSolution_Click);
|
||||||
//
|
//
|
||||||
// btnExecutSolution
|
// btnExecutSolution
|
||||||
//
|
//
|
||||||
this.btnExecutSolution.Location = new System.Drawing.Point(236, 93);
|
this.btnExecutSolution.Location = new System.Drawing.Point(244, 52);
|
||||||
this.btnExecutSolution.Name = "btnExecutSolution";
|
this.btnExecutSolution.Name = "btnExecutSolution";
|
||||||
this.btnExecutSolution.Size = new System.Drawing.Size(127, 38);
|
this.btnExecutSolution.Size = new System.Drawing.Size(127, 38);
|
||||||
this.btnExecutSolution.TabIndex = 4;
|
this.btnExecutSolution.TabIndex = 4;
|
||||||
@@ -104,7 +109,7 @@
|
|||||||
//
|
//
|
||||||
// btnSaveSolution
|
// btnSaveSolution
|
||||||
//
|
//
|
||||||
this.btnSaveSolution.Location = new System.Drawing.Point(369, 93);
|
this.btnSaveSolution.Location = new System.Drawing.Point(372, 52);
|
||||||
this.btnSaveSolution.Name = "btnSaveSolution";
|
this.btnSaveSolution.Name = "btnSaveSolution";
|
||||||
this.btnSaveSolution.Size = new System.Drawing.Size(127, 38);
|
this.btnSaveSolution.Size = new System.Drawing.Size(127, 38);
|
||||||
this.btnSaveSolution.TabIndex = 5;
|
this.btnSaveSolution.TabIndex = 5;
|
||||||
@@ -120,9 +125,9 @@
|
|||||||
this.groupBox1.Controls.Add(this.btnExecutSolution);
|
this.groupBox1.Controls.Add(this.btnExecutSolution);
|
||||||
this.groupBox1.Controls.Add(this.txtSolutionAddress);
|
this.groupBox1.Controls.Add(this.txtSolutionAddress);
|
||||||
this.groupBox1.Controls.Add(this.btnLoadSolution);
|
this.groupBox1.Controls.Add(this.btnLoadSolution);
|
||||||
this.groupBox1.Location = new System.Drawing.Point(882, 4);
|
this.groupBox1.Location = new System.Drawing.Point(882, 58);
|
||||||
this.groupBox1.Name = "groupBox1";
|
this.groupBox1.Name = "groupBox1";
|
||||||
this.groupBox1.Size = new System.Drawing.Size(505, 144);
|
this.groupBox1.Size = new System.Drawing.Size(505, 105);
|
||||||
this.groupBox1.TabIndex = 6;
|
this.groupBox1.TabIndex = 6;
|
||||||
this.groupBox1.TabStop = false;
|
this.groupBox1.TabStop = false;
|
||||||
this.groupBox1.Text = "方案操作";
|
this.groupBox1.Text = "方案操作";
|
||||||
@@ -130,7 +135,7 @@
|
|||||||
// label3
|
// label3
|
||||||
//
|
//
|
||||||
this.label3.AutoSize = true;
|
this.label3.AutoSize = true;
|
||||||
this.label3.Location = new System.Drawing.Point(30, 23);
|
this.label3.Location = new System.Drawing.Point(28, 26);
|
||||||
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||||
this.label3.Name = "label3";
|
this.label3.Name = "label3";
|
||||||
this.label3.Size = new System.Drawing.Size(68, 17);
|
this.label3.Size = new System.Drawing.Size(68, 17);
|
||||||
@@ -159,16 +164,16 @@
|
|||||||
//
|
//
|
||||||
// vmProcedureConfigControl1
|
// vmProcedureConfigControl1
|
||||||
//
|
//
|
||||||
this.vmProcedureConfigControl1.Dock = System.Windows.Forms.DockStyle.Left;
|
this.vmProcedureConfigControl1.Location = new System.Drawing.Point(20, 440);
|
||||||
this.vmProcedureConfigControl1.Location = new System.Drawing.Point(20, 60);
|
|
||||||
this.vmProcedureConfigControl1.Margin = new System.Windows.Forms.Padding(2);
|
this.vmProcedureConfigControl1.Margin = new System.Windows.Forms.Padding(2);
|
||||||
this.vmProcedureConfigControl1.Name = "vmProcedureConfigControl1";
|
this.vmProcedureConfigControl1.Name = "vmProcedureConfigControl1";
|
||||||
this.vmProcedureConfigControl1.Size = new System.Drawing.Size(439, 781);
|
this.vmProcedureConfigControl1.Size = new System.Drawing.Size(439, 440);
|
||||||
this.vmProcedureConfigControl1.TabIndex = 7;
|
this.vmProcedureConfigControl1.TabIndex = 7;
|
||||||
// TODO: “”的代码生成失败,原因是出现异常“无效的基元类型: System.IntPtr。请考虑使用 CodeObjectCreateExpression。”。
|
// TODO: “”的代码生成失败,原因是出现异常“无效的基元类型: System.IntPtr。请考虑使用 CodeObjectCreateExpression。”。
|
||||||
//
|
//
|
||||||
// groupBox2
|
// groupBox2
|
||||||
//
|
//
|
||||||
|
this.groupBox2.Controls.Add(this.btnAutoRun);
|
||||||
this.groupBox2.Controls.Add(this.cmbProcdure);
|
this.groupBox2.Controls.Add(this.cmbProcdure);
|
||||||
this.groupBox2.Controls.Add(this.label4);
|
this.groupBox2.Controls.Add(this.label4);
|
||||||
this.groupBox2.Controls.Add(this.btnProExecStop);
|
this.groupBox2.Controls.Add(this.btnProExecStop);
|
||||||
@@ -177,13 +182,23 @@
|
|||||||
this.groupBox2.Controls.Add(this.btnProDel);
|
this.groupBox2.Controls.Add(this.btnProDel);
|
||||||
this.groupBox2.Controls.Add(this.btnProExpo);
|
this.groupBox2.Controls.Add(this.btnProExpo);
|
||||||
this.groupBox2.Controls.Add(this.btnProImport);
|
this.groupBox2.Controls.Add(this.btnProImport);
|
||||||
this.groupBox2.Location = new System.Drawing.Point(882, 151);
|
this.groupBox2.Location = new System.Drawing.Point(881, 167);
|
||||||
this.groupBox2.Name = "groupBox2";
|
this.groupBox2.Name = "groupBox2";
|
||||||
this.groupBox2.Size = new System.Drawing.Size(505, 145);
|
this.groupBox2.Size = new System.Drawing.Size(505, 145);
|
||||||
this.groupBox2.TabIndex = 7;
|
this.groupBox2.TabIndex = 7;
|
||||||
this.groupBox2.TabStop = false;
|
this.groupBox2.TabStop = false;
|
||||||
this.groupBox2.Text = "流程操作";
|
this.groupBox2.Text = "流程操作";
|
||||||
//
|
//
|
||||||
|
// btnAutoRun
|
||||||
|
//
|
||||||
|
this.btnAutoRun.Location = new System.Drawing.Point(11, 53);
|
||||||
|
this.btnAutoRun.Name = "btnAutoRun";
|
||||||
|
this.btnAutoRun.Size = new System.Drawing.Size(75, 64);
|
||||||
|
this.btnAutoRun.TabIndex = 9;
|
||||||
|
this.btnAutoRun.Text = "自动运行";
|
||||||
|
this.btnAutoRun.UseVisualStyleBackColor = true;
|
||||||
|
this.btnAutoRun.Click += new System.EventHandler(this.btnAutoRun_Click);
|
||||||
|
//
|
||||||
// btnProExecStop
|
// btnProExecStop
|
||||||
//
|
//
|
||||||
this.btnProExecStop.Location = new System.Drawing.Point(372, 97);
|
this.btnProExecStop.Location = new System.Drawing.Point(372, 97);
|
||||||
@@ -251,7 +266,7 @@
|
|||||||
this.groupBox3.Controls.Add(this.btnModuleRenderResult);
|
this.groupBox3.Controls.Add(this.btnModuleRenderResult);
|
||||||
this.groupBox3.Controls.Add(this.btnModuleExec);
|
this.groupBox3.Controls.Add(this.btnModuleExec);
|
||||||
this.groupBox3.Controls.Add(this.btnModuleBindingPar);
|
this.groupBox3.Controls.Add(this.btnModuleBindingPar);
|
||||||
this.groupBox3.Location = new System.Drawing.Point(882, 302);
|
this.groupBox3.Location = new System.Drawing.Point(881, 314);
|
||||||
this.groupBox3.Name = "groupBox3";
|
this.groupBox3.Name = "groupBox3";
|
||||||
this.groupBox3.Size = new System.Drawing.Size(505, 112);
|
this.groupBox3.Size = new System.Drawing.Size(505, 112);
|
||||||
this.groupBox3.TabIndex = 9;
|
this.groupBox3.TabIndex = 9;
|
||||||
@@ -311,19 +326,19 @@
|
|||||||
//
|
//
|
||||||
this.listBox1.FormattingEnabled = true;
|
this.listBox1.FormattingEnabled = true;
|
||||||
this.listBox1.ItemHeight = 17;
|
this.listBox1.ItemHeight = 17;
|
||||||
this.listBox1.Location = new System.Drawing.Point(882, 422);
|
this.listBox1.Location = new System.Drawing.Point(882, 431);
|
||||||
this.listBox1.Name = "listBox1";
|
this.listBox1.Name = "listBox1";
|
||||||
this.listBox1.Size = new System.Drawing.Size(516, 429);
|
this.listBox1.Size = new System.Drawing.Size(516, 446);
|
||||||
this.listBox1.TabIndex = 10;
|
this.listBox1.TabIndex = 10;
|
||||||
//
|
//
|
||||||
// groupBox4
|
// groupBox4
|
||||||
//
|
//
|
||||||
this.groupBox4.Controls.Add(this.pictureBox1);
|
this.groupBox4.Controls.Add(this.pictureBox1);
|
||||||
this.groupBox4.Location = new System.Drawing.Point(463, 2);
|
this.groupBox4.Location = new System.Drawing.Point(463, 58);
|
||||||
this.groupBox4.Margin = new System.Windows.Forms.Padding(2);
|
this.groupBox4.Margin = new System.Windows.Forms.Padding(2);
|
||||||
this.groupBox4.Name = "groupBox4";
|
this.groupBox4.Name = "groupBox4";
|
||||||
this.groupBox4.Padding = new System.Windows.Forms.Padding(2);
|
this.groupBox4.Padding = new System.Windows.Forms.Padding(2);
|
||||||
this.groupBox4.Size = new System.Drawing.Size(414, 412);
|
this.groupBox4.Size = new System.Drawing.Size(414, 376);
|
||||||
this.groupBox4.TabIndex = 11;
|
this.groupBox4.TabIndex = 11;
|
||||||
this.groupBox4.TabStop = false;
|
this.groupBox4.TabStop = false;
|
||||||
this.groupBox4.Text = "视图窗口";
|
this.groupBox4.Text = "视图窗口";
|
||||||
@@ -333,18 +348,18 @@
|
|||||||
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.pictureBox1.Location = new System.Drawing.Point(2, 18);
|
this.pictureBox1.Location = new System.Drawing.Point(2, 18);
|
||||||
this.pictureBox1.Name = "pictureBox1";
|
this.pictureBox1.Name = "pictureBox1";
|
||||||
this.pictureBox1.Size = new System.Drawing.Size(410, 392);
|
this.pictureBox1.Size = new System.Drawing.Size(410, 356);
|
||||||
this.pictureBox1.TabIndex = 0;
|
this.pictureBox1.TabIndex = 0;
|
||||||
this.pictureBox1.TabStop = false;
|
this.pictureBox1.TabStop = false;
|
||||||
//
|
//
|
||||||
// groupBox6
|
// groupBox6
|
||||||
//
|
//
|
||||||
this.groupBox6.Controls.Add(this.vmParamsConfigControl1);
|
this.groupBox6.Controls.Add(this.vmParamsConfigControl1);
|
||||||
this.groupBox6.Location = new System.Drawing.Point(463, 422);
|
this.groupBox6.Location = new System.Drawing.Point(463, 432);
|
||||||
this.groupBox6.Margin = new System.Windows.Forms.Padding(2);
|
this.groupBox6.Margin = new System.Windows.Forms.Padding(2);
|
||||||
this.groupBox6.Name = "groupBox6";
|
this.groupBox6.Name = "groupBox6";
|
||||||
this.groupBox6.Padding = new System.Windows.Forms.Padding(2);
|
this.groupBox6.Padding = new System.Windows.Forms.Padding(2);
|
||||||
this.groupBox6.Size = new System.Drawing.Size(409, 439);
|
this.groupBox6.Size = new System.Drawing.Size(409, 447);
|
||||||
this.groupBox6.TabIndex = 12;
|
this.groupBox6.TabIndex = 12;
|
||||||
this.groupBox6.TabStop = false;
|
this.groupBox6.TabStop = false;
|
||||||
this.groupBox6.Text = "参数配置";
|
this.groupBox6.Text = "参数配置";
|
||||||
@@ -357,14 +372,35 @@
|
|||||||
this.vmParamsConfigControl1.ModuleSource = null;
|
this.vmParamsConfigControl1.ModuleSource = null;
|
||||||
this.vmParamsConfigControl1.Name = "vmParamsConfigControl1";
|
this.vmParamsConfigControl1.Name = "vmParamsConfigControl1";
|
||||||
this.vmParamsConfigControl1.ParamsConfig = null;
|
this.vmParamsConfigControl1.ParamsConfig = null;
|
||||||
this.vmParamsConfigControl1.Size = new System.Drawing.Size(405, 419);
|
this.vmParamsConfigControl1.Size = new System.Drawing.Size(405, 427);
|
||||||
this.vmParamsConfigControl1.TabIndex = 0;
|
this.vmParamsConfigControl1.TabIndex = 0;
|
||||||
//
|
//
|
||||||
|
// groupBox5
|
||||||
|
//
|
||||||
|
this.groupBox5.Controls.Add(this.lblRunState);
|
||||||
|
this.groupBox5.Location = new System.Drawing.Point(23, 58);
|
||||||
|
this.groupBox5.Name = "groupBox5";
|
||||||
|
this.groupBox5.Size = new System.Drawing.Size(435, 374);
|
||||||
|
this.groupBox5.TabIndex = 13;
|
||||||
|
this.groupBox5.TabStop = false;
|
||||||
|
this.groupBox5.Text = "自动运行结果";
|
||||||
|
//
|
||||||
|
// lblRunState
|
||||||
|
//
|
||||||
|
this.lblRunState.AutoSize = true;
|
||||||
|
this.lblRunState.Font = new System.Drawing.Font("微软雅黑", 21.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||||
|
this.lblRunState.Location = new System.Drawing.Point(32, 85);
|
||||||
|
this.lblRunState.Name = "lblRunState";
|
||||||
|
this.lblRunState.Size = new System.Drawing.Size(133, 39);
|
||||||
|
this.lblRunState.TabIndex = 0;
|
||||||
|
this.lblRunState.Text = "运行状态";
|
||||||
|
//
|
||||||
// MainForm
|
// MainForm
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 17F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 17F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(1400, 861);
|
this.ClientSize = new System.Drawing.Size(1400, 900);
|
||||||
|
this.Controls.Add(this.groupBox5);
|
||||||
this.Controls.Add(this.groupBox6);
|
this.Controls.Add(this.groupBox6);
|
||||||
this.Controls.Add(this.groupBox4);
|
this.Controls.Add(this.groupBox4);
|
||||||
this.Controls.Add(this.listBox1);
|
this.Controls.Add(this.listBox1);
|
||||||
@@ -376,7 +412,9 @@
|
|||||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||||
this.Margin = new System.Windows.Forms.Padding(4);
|
this.Margin = new System.Windows.Forms.Padding(4);
|
||||||
this.Name = "MainForm";
|
this.Name = "MainForm";
|
||||||
this.Text = "主窗体";
|
this.Style = ReaLTaiizor.Enum.Poison.ColorStyle.Black;
|
||||||
|
this.Text = "机器视觉系统ORPAON-v26.1";
|
||||||
|
this.Theme = ReaLTaiizor.Enum.Poison.ThemeStyle.Default;
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
|
||||||
this.Load += new System.EventHandler(this.MainForm_Load);
|
this.Load += new System.EventHandler(this.MainForm_Load);
|
||||||
this.groupBox1.ResumeLayout(false);
|
this.groupBox1.ResumeLayout(false);
|
||||||
@@ -388,6 +426,8 @@
|
|||||||
this.groupBox4.ResumeLayout(false);
|
this.groupBox4.ResumeLayout(false);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
|
||||||
this.groupBox6.ResumeLayout(false);
|
this.groupBox6.ResumeLayout(false);
|
||||||
|
this.groupBox5.ResumeLayout(false);
|
||||||
|
this.groupBox5.PerformLayout();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -422,6 +462,9 @@
|
|||||||
private System.Windows.Forms.ComboBox cmbProcdure;
|
private System.Windows.Forms.ComboBox cmbProcdure;
|
||||||
private System.Windows.Forms.Label label4;
|
private System.Windows.Forms.Label label4;
|
||||||
private System.Windows.Forms.PictureBox pictureBox1;
|
private System.Windows.Forms.PictureBox pictureBox1;
|
||||||
|
private System.Windows.Forms.GroupBox groupBox5;
|
||||||
|
private System.Windows.Forms.Label lblRunState;
|
||||||
|
private System.Windows.Forms.Button btnAutoRun;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Configuration;
|
using System.Configuration;
|
||||||
|
using HkVisionPro.App.Automation;
|
||||||
using ReaLTaiizor.Enum.Poison;
|
using ReaLTaiizor.Enum.Poison;
|
||||||
using ReaLTaiizor.Forms;
|
using ReaLTaiizor.Forms;
|
||||||
using ReaLTaiizor.Manager;
|
using ReaLTaiizor.Manager;
|
||||||
@@ -86,30 +87,30 @@ namespace HkVisionPro.App
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool _isClosingResourcesReleased;
|
private bool _isClosingResourcesReleased;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 流程字符串输出名称(支持配置,默认 out)。
|
|
||||||
/// </summary>
|
|
||||||
private readonly string _procedureOutputCodeName;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 流程整数输出名称(支持配置,默认 out0)。
|
|
||||||
/// </summary>
|
|
||||||
private readonly string _procedureOutputNumberName;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 渲染图像输出名称(支持配置,默认 Image)。
|
/// 渲染图像输出名称(支持配置,默认 Image)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly string _renderImageOutputName;
|
private readonly string _renderImageOutputName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 渲染 ROI 输出名称(支持配置,默认 ROI)。
|
/// 渲染主 ROI 输出名称(支持配置,默认 InputR1)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly string _renderRoiOutputName;
|
private readonly string _renderRoiOutputName1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 渲染文本输出名称(支持配置,默认 code)。
|
/// 渲染次 ROI 输出名称(支持配置,默认 InputR2)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly string _renderCodeOutputName;
|
private readonly string _renderRoiOutputName2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渲染判定结果1整型输出名称(支持配置,默认 Result1)。
|
||||||
|
/// </summary>
|
||||||
|
private readonly string _renderResultIntOutputName1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渲染判定结果2整型输出名称(支持配置,默认 Result2)。
|
||||||
|
/// </summary>
|
||||||
|
private readonly string _renderResultIntOutputName2;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// VisionMaster: 方案正在加载中的错误码(IMVS_EC_SOLUTION_LOADING)。
|
/// VisionMaster: 方案正在加载中的错误码(IMVS_EC_SOLUTION_LOADING)。
|
||||||
@@ -121,6 +122,21 @@ namespace HkVisionPro.App
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const int VmErrorCodeModuleContinueExecute = -536870127;
|
private const int VmErrorCodeModuleContinueExecute = -536870127;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动装配状态机配置。
|
||||||
|
/// </summary>
|
||||||
|
private readonly AutoAssemblyOptions _autoAssemblyOptions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动装配流程控制器(Stateless 状态机)。
|
||||||
|
/// </summary>
|
||||||
|
private readonly AutoAssemblyWorkflowController _autoAssemblyController;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方案加载完成后是否自动启动自动装配流程。
|
||||||
|
/// </summary>
|
||||||
|
private readonly bool _autoRunEnabledAfterSolutionLoaded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ReaLTaiizor Poison 样式管理器(用于统一暗色主题风格元数据)。
|
/// ReaLTaiizor Poison 样式管理器(用于统一暗色主题风格元数据)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -171,11 +187,20 @@ namespace HkVisionPro.App
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_poisonStyleManager = components == null ? new PoisonStyleManager() : new PoisonStyleManager(components);
|
_poisonStyleManager = components == null ? new PoisonStyleManager() : new PoisonStyleManager(components);
|
||||||
_solutionManager = new SolutionManager();
|
_solutionManager = new SolutionManager();
|
||||||
_procedureOutputCodeName = GetAppSettingOrDefault("ProcedureOutputCodeName", "out");
|
_renderImageOutputName = GetAppSettingOrDefault("RenderImageOutputName", "ImageData0");
|
||||||
_procedureOutputNumberName = GetAppSettingOrDefault("ProcedureOutputNumberName", "out0");
|
_renderRoiOutputName1 = GetAppSettingOrDefault("RenderRoiOutputName", "InputR1");
|
||||||
_renderImageOutputName = GetAppSettingOrDefault("RenderImageOutputName", "Image");
|
_renderRoiOutputName2 = GetAppSettingOrDefault("RenderRoiOutputName2", "InputR2");
|
||||||
_renderRoiOutputName = GetAppSettingOrDefault("RenderRoiOutputName", "ROI");
|
_renderResultIntOutputName1 = GetAppSettingOrDefault("RenderResultIntOutputName1", "Result1");
|
||||||
_renderCodeOutputName = GetAppSettingOrDefault("RenderCodeOutputName", "code");
|
_renderResultIntOutputName2 = GetAppSettingOrDefault("RenderResultIntOutputName2", "Result2");
|
||||||
|
_autoAssemblyOptions = BuildAutoAssemblyOptions();
|
||||||
|
_autoRunEnabledAfterSolutionLoaded = GetAppSettingBoolOrDefault("AutoRunEnabledAfterSolutionLoaded", true);
|
||||||
|
|
||||||
|
_autoAssemblyController = new AutoAssemblyWorkflowController(
|
||||||
|
_autoAssemblyOptions,
|
||||||
|
WriteLog,
|
||||||
|
(procedure, triggerSource) => RenderProcedureResultToPictureBox(procedure, triggerSource));
|
||||||
|
|
||||||
|
_autoAssemblyController.StatusChanged += AutoAssemblyController_StatusChanged;
|
||||||
RegisterSolutionCallbacks();
|
RegisterSolutionCallbacks();
|
||||||
RegisterVmSolutionCallbacks();
|
RegisterVmSolutionCallbacks();
|
||||||
|
|
||||||
@@ -187,11 +212,276 @@ namespace HkVisionPro.App
|
|||||||
pictureBox1.BackColor = Color.Black;
|
pictureBox1.BackColor = Color.Black;
|
||||||
|
|
||||||
InitializeDarkTheme();
|
InitializeDarkTheme();
|
||||||
|
InitializeAutoRunStatusPanel();
|
||||||
|
|
||||||
UpdateUiState(false);
|
UpdateUiState(false);
|
||||||
txtSolutionAddress.Text = string.Empty;
|
txtSolutionAddress.Text = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化自动运行结果显示面板。
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeAutoRunStatusPanel()
|
||||||
|
{
|
||||||
|
lblRunState.AutoSize = false;
|
||||||
|
lblRunState.Dock = DockStyle.Fill;
|
||||||
|
lblRunState.TextAlign = ContentAlignment.MiddleCenter;
|
||||||
|
|
||||||
|
var initMessage = _autoRunEnabledAfterSolutionLoaded
|
||||||
|
? "自动流程待启动(等待方案加载)"
|
||||||
|
: "自动流程已禁用(配置开关关闭)";
|
||||||
|
|
||||||
|
UpdateAutoRunResultDisplay(AutoAssemblyState.Idle, initMessage, 0, string.Empty, 0, _autoAssemblyOptions.StableOkMilliseconds);
|
||||||
|
SyncAutoRunButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动装配状态变化事件:刷新“自动运行结果”显示。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源对象。</param>
|
||||||
|
/// <param name="e">状态变化参数。</param>
|
||||||
|
private void AutoAssemblyController_StatusChanged(object sender, AutoAssemblyStatusChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncAutoRunButtonState();
|
||||||
|
|
||||||
|
UpdateAutoRunResultDisplay(
|
||||||
|
e.State,
|
||||||
|
e.Message,
|
||||||
|
e.ProductIndex,
|
||||||
|
e.ActiveProcedureName,
|
||||||
|
e.StableElapsedMilliseconds,
|
||||||
|
e.StableTargetMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
private void UpdateAutoRunResultDisplay(
|
||||||
|
AutoAssemblyState state,
|
||||||
|
string message,
|
||||||
|
int productIndex,
|
||||||
|
string activeProcedureName,
|
||||||
|
int stableElapsedMilliseconds,
|
||||||
|
int stableTargetMilliseconds)
|
||||||
|
{
|
||||||
|
if (!IsHandleCreated || IsDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateAction()
|
||||||
|
{
|
||||||
|
lblRunState.ForeColor = GetAutoRunStateColor(state);
|
||||||
|
lblRunState.Text = BuildAutoRunDisplayText(
|
||||||
|
state,
|
||||||
|
message,
|
||||||
|
productIndex,
|
||||||
|
activeProcedureName,
|
||||||
|
stableElapsedMilliseconds,
|
||||||
|
stableTargetMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lblRunState.InvokeRequired)
|
||||||
|
{
|
||||||
|
lblRunState.BeginInvoke(new Action(UpdateAction));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpdateAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造自动运行结果展示文本。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">自动流程状态。</param>
|
||||||
|
/// <param name="message">状态描述。</param>
|
||||||
|
/// <param name="productIndex">当前产品序号。</param>
|
||||||
|
/// <param name="activeProcedureName">当前流程名。</param>
|
||||||
|
/// <param name="stableElapsedMilliseconds">稳定计时累计毫秒数。</param>
|
||||||
|
/// <param name="stableTargetMilliseconds">稳定计时目标毫秒数。</param>
|
||||||
|
/// <returns>用于显示的多行文本。</returns>
|
||||||
|
private static string BuildAutoRunDisplayText(
|
||||||
|
AutoAssemblyState state,
|
||||||
|
string message,
|
||||||
|
int productIndex,
|
||||||
|
string activeProcedureName,
|
||||||
|
int stableElapsedMilliseconds,
|
||||||
|
int stableTargetMilliseconds)
|
||||||
|
{
|
||||||
|
var displayProductIndex = productIndex <= 0 ? 1 : productIndex;
|
||||||
|
var displayProcedure = string.IsNullOrWhiteSpace(activeProcedureName) ? "-" : activeProcedureName;
|
||||||
|
var elapsed = Math.Max(0, stableElapsedMilliseconds);
|
||||||
|
var target = Math.Max(0, stableTargetMilliseconds);
|
||||||
|
if (target > 0)
|
||||||
|
{
|
||||||
|
elapsed = Math.Min(elapsed, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{GetAutoRunStateTitle(state)}\r\n产品序号:#{displayProductIndex}\r\n当前流程:{displayProcedure}\r\n稳定计时:{elapsed}/{target} ms\r\n状态描述:{message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取自动状态标题文本。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">自动流程状态。</param>
|
||||||
|
/// <returns>状态标题。</returns>
|
||||||
|
private static string GetAutoRunStateTitle(AutoAssemblyState state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case AutoAssemblyState.LayerAInspecting:
|
||||||
|
return "A层检测中";
|
||||||
|
case AutoAssemblyState.LayerBInspecting:
|
||||||
|
return "B层检测中";
|
||||||
|
case AutoAssemblyState.LayerCInspecting:
|
||||||
|
return "C层检测中";
|
||||||
|
case AutoAssemblyState.ProductCompleted:
|
||||||
|
return "整机检测OK";
|
||||||
|
case AutoAssemblyState.Faulted:
|
||||||
|
return "自动流程异常";
|
||||||
|
case AutoAssemblyState.Stopped:
|
||||||
|
return "自动流程已停止";
|
||||||
|
default:
|
||||||
|
return "自动流程空闲";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取自动状态标题颜色。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">自动流程状态。</param>
|
||||||
|
/// <returns>状态颜色。</returns>
|
||||||
|
private static Color GetAutoRunStateColor(AutoAssemblyState state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case AutoAssemblyState.LayerAInspecting:
|
||||||
|
return Color.FromArgb(64, 196, 255);
|
||||||
|
case AutoAssemblyState.LayerBInspecting:
|
||||||
|
return Color.FromArgb(255, 179, 71);
|
||||||
|
case AutoAssemblyState.LayerCInspecting:
|
||||||
|
return Color.FromArgb(170, 132, 255);
|
||||||
|
case AutoAssemblyState.ProductCompleted:
|
||||||
|
return Color.FromArgb(120, 220, 130);
|
||||||
|
case AutoAssemblyState.Faulted:
|
||||||
|
return Color.FromArgb(255, 99, 99);
|
||||||
|
case AutoAssemblyState.Stopped:
|
||||||
|
return Color.FromArgb(200, 200, 200);
|
||||||
|
default:
|
||||||
|
return DarkThemeForeColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按配置构建自动装配流程参数。
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>自动流程配置对象。</returns>
|
||||||
|
private AutoAssemblyOptions BuildAutoAssemblyOptions()
|
||||||
|
{
|
||||||
|
return new AutoAssemblyOptions
|
||||||
|
{
|
||||||
|
ProcedureAName = GetAppSettingOrDefault("AutoRunProcedureAName", "A"),
|
||||||
|
ProcedureBName = GetAppSettingOrDefault("AutoRunProcedureBName", "B"),
|
||||||
|
ProcedureCName = GetAppSettingOrDefault("AutoRunProcedureCName", "C"),
|
||||||
|
OkIntOutputName = GetAppSettingOrDefault("AutoRunOkIntOutputName", _renderResultIntOutputName1),
|
||||||
|
OkIntOutputName2 = GetAppSettingOrDefault("AutoRunOkIntOutputName2", _renderResultIntOutputName2),
|
||||||
|
OkIntValue = GetAppSettingIntOrDefault("AutoRunOkIntValue", 1),
|
||||||
|
StableOkMilliseconds = GetAppSettingIntOrDefault("AutoRunStableMilliseconds", 2000),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方案加载成功后按配置启动自动装配流程。
|
||||||
|
/// </summary>
|
||||||
|
private void StartAutoAssemblyIfConfigured()
|
||||||
|
{
|
||||||
|
if (!_autoRunEnabledAfterSolutionLoaded)
|
||||||
|
{
|
||||||
|
UpdateAutoRunResultDisplay(
|
||||||
|
AutoAssemblyState.Idle,
|
||||||
|
"自动流程已禁用(AutoRunEnabledAfterSolutionLoaded=false)",
|
||||||
|
0,
|
||||||
|
string.Empty,
|
||||||
|
0,
|
||||||
|
_autoAssemblyOptions.StableOkMilliseconds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isSolutionLoaded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 切入自动模式前清理手动执行上下文,避免两套执行链路并发冲突。
|
||||||
|
StopContinuousRunIfNeeded("切换到自动装配流程");
|
||||||
|
UnbindProcedureWorkEndCallback();
|
||||||
|
_currentProcedure = null;
|
||||||
|
|
||||||
|
_autoAssemblyController.Start();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
HandleException("启动自动装配流程", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止自动装配流程(若运行中)。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reason">停止原因。</param>
|
||||||
|
private void StopAutoAssemblyIfRunning(string reason)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_autoAssemblyController.Stop(reason);
|
||||||
|
SyncAutoRunButtonState();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteLog($"停止自动装配流程异常:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步自动运行按钮文本(线程安全)。
|
||||||
|
/// 运行中显示“停止自动”,未运行显示“自动运行”。
|
||||||
|
/// </summary>
|
||||||
|
private void SyncAutoRunButtonState()
|
||||||
|
{
|
||||||
|
if (!IsHandleCreated || IsDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateAction()
|
||||||
|
{
|
||||||
|
btnAutoRun.Text = _autoAssemblyController.IsRunning ? "停止自动" : "自动运行";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnAutoRun.InvokeRequired)
|
||||||
|
{
|
||||||
|
btnAutoRun.BeginInvoke(new Action(UpdateAction));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpdateAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化 ReaLTaiizor 暗色主题,并将原生 WinForms 控件统一成暗色风格。
|
/// 初始化 ReaLTaiizor 暗色主题,并将原生 WinForms 控件统一成暗色风格。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -635,6 +925,10 @@ namespace HkVisionPro.App
|
|||||||
_activeBcrTool = null;
|
_activeBcrTool = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StopAutoAssemblyIfRunning("窗体关闭释放资源");
|
||||||
|
_autoAssemblyController.StatusChanged -= AutoAssemblyController_StatusChanged;
|
||||||
|
_autoAssemblyController.Dispose();
|
||||||
|
|
||||||
StopContinuousRunIfNeeded("窗体关闭释放资源");
|
StopContinuousRunIfNeeded("窗体关闭释放资源");
|
||||||
UnbindProcedureWorkEndCallback();
|
UnbindProcedureWorkEndCallback();
|
||||||
_currentProcedure = null;
|
_currentProcedure = null;
|
||||||
@@ -702,6 +996,7 @@ namespace HkVisionPro.App
|
|||||||
_isSolutionLoaded = true;
|
_isSolutionLoaded = true;
|
||||||
BindFirstModuleToRenderControl();
|
BindFirstModuleToRenderControl();
|
||||||
UpdateProcedureList();
|
UpdateProcedureList();
|
||||||
|
StartAutoAssemblyIfConfigured();
|
||||||
|
|
||||||
WriteLog($"方案加载成功:{_currentSolutionPath}");
|
WriteLog($"方案加载成功:{_currentSolutionPath}");
|
||||||
MessageBox.Show(this, "方案加载成功。", "加载方案", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
MessageBox.Show(this, "方案加载成功。", "加载方案", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
@@ -709,6 +1004,7 @@ namespace HkVisionPro.App
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_isSolutionLoaded = false;
|
_isSolutionLoaded = false;
|
||||||
|
StopAutoAssemblyIfRunning("方案加载失败");
|
||||||
ResetProcedureAndModuleState();
|
ResetProcedureAndModuleState();
|
||||||
MessageBox.Show(this, $"方案加载失败,状态码:{endInfo.nStatus}", "加载方案", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show(this, $"方案加载失败,状态码:{endInfo.nStatus}", "加载方案", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
@@ -716,6 +1012,7 @@ namespace HkVisionPro.App
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_isSolutionLoaded = false;
|
_isSolutionLoaded = false;
|
||||||
|
StopAutoAssemblyIfRunning("加载方案回调异常");
|
||||||
ResetProcedureAndModuleState();
|
ResetProcedureAndModuleState();
|
||||||
HandleException("加载方案回调处理", ex);
|
HandleException("加载方案回调处理", ex);
|
||||||
}
|
}
|
||||||
@@ -831,23 +1128,29 @@ namespace HkVisionPro.App
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 字符串读取仅尝试字符串候选名,避免对 int 输出调用 GetOutputString 引发 SDK 内部异常。
|
var result1 = GetProcedureOutputInt(procedure, _renderResultIntOutputName1);
|
||||||
var code = GetProcedureOutputString(procedure, _renderCodeOutputName, _procedureOutputCodeName, "out0", "code", "Code");
|
var result2 = GetProcedureOutputInt(procedure, _renderResultIntOutputName2);
|
||||||
// 整型读取仅尝试整型候选名。
|
var isResult1Ok = string.Equals(result1?.Trim(), "1", StringComparison.Ordinal);
|
||||||
var codeNum = GetProcedureOutputInt(procedure, _procedureOutputNumberName, "out");
|
var isResult2Ok = string.Equals(result2?.Trim(), "1", StringComparison.Ordinal);
|
||||||
var bitmap = GetProcedureOutputBitmap(procedure, _renderImageOutputName, "ImageData", "Image", "image");
|
var pairResult = isResult1Ok && isResult2Ok ? "OK" : "NG";
|
||||||
|
var overlayText = $"R1={result1}, R2={result2}, Total={pairResult}";
|
||||||
|
|
||||||
|
var bitmap = GetProcedureOutputBitmap(procedure, _renderImageOutputName);
|
||||||
if (bitmap == null)
|
if (bitmap == null)
|
||||||
{
|
{
|
||||||
WriteLog($"{triggerSource}未读取到可渲染图像,输出名候选:{_renderImageOutputName}/ImageData/Image/image");
|
WriteLog($"{triggerSource}未读取到可渲染图像,固定输出名:{_renderImageOutputName}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var roiList = GetProcedureOutputBoxList(procedure, _renderRoiOutputName, "InputROI", "rect", "outline", "ROI", "roi");
|
var primaryRoiList = GetProcedureOutputBoxList(procedure, _renderRoiOutputName1);
|
||||||
var renderBitmap = DrawROIOnBitmap(bitmap, roiList, Color.Cyan, 2, code);
|
var secondaryRoiList = GetProcedureOutputBoxList(procedure, _renderRoiOutputName2);
|
||||||
|
var mergedRoiList = MergeRoiLists(primaryRoiList, secondaryRoiList);
|
||||||
|
var roiColor = pairResult == "OK" ? Color.Lime : Color.Red;
|
||||||
|
var renderBitmap = DrawROIOnBitmap(bitmap, mergedRoiList, roiColor, 2, overlayText);
|
||||||
SetPictureBoxImage(renderBitmap);
|
SetPictureBoxImage(renderBitmap);
|
||||||
WriteLog($"{triggerSource}渲染完成:Code={code},CodeNumber={codeNum},ROI数={roiList.Count}");
|
WriteLog($"{triggerSource}渲染完成:Result1={result1},Result2={result2},Total={pairResult},ROI数={mergedRoiList.Count}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -930,6 +1233,32 @@ namespace HkVisionPro.App
|
|||||||
return new List<RectBox>();
|
return new List<RectBox>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并多组 ROI 列表,避免流程多目标输出时遗漏展示。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="roiGroups">待合并 ROI 列表集合。</param>
|
||||||
|
/// <returns>合并后的 ROI 列表。</returns>
|
||||||
|
private static List<RectBox> MergeRoiLists(params List<RectBox>[] roiGroups)
|
||||||
|
{
|
||||||
|
var merged = new List<RectBox>();
|
||||||
|
if (roiGroups == null || roiGroups.Length == 0)
|
||||||
|
{
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var roiGroup in roiGroups)
|
||||||
|
{
|
||||||
|
if (roiGroup == null || roiGroup.Count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.AddRange(roiGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在图像上绘制 ROI 轮廓和识别文本。
|
/// 在图像上绘制 ROI 轮廓和识别文本。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1148,11 +1477,13 @@ namespace HkVisionPro.App
|
|||||||
btnModuleBindingPar.Enabled = canOperateLoadedSolution;
|
btnModuleBindingPar.Enabled = canOperateLoadedSolution;
|
||||||
btnModuleExec.Enabled = canOperateLoadedSolution;
|
btnModuleExec.Enabled = canOperateLoadedSolution;
|
||||||
btnModuleRenderResult.Enabled = canOperateLoadedSolution;
|
btnModuleRenderResult.Enabled = canOperateLoadedSolution;
|
||||||
|
btnAutoRun.Enabled = canOperateLoadedSolution;
|
||||||
cmbProcdure.Enabled = canOperateLoadedSolution;
|
cmbProcdure.Enabled = canOperateLoadedSolution;
|
||||||
cmbModule.Enabled = canOperateLoadedSolution;
|
cmbModule.Enabled = canOperateLoadedSolution;
|
||||||
|
|
||||||
// 保持连续执行按钮文案与状态一致。
|
// 保持连续执行按钮文案与状态一致。
|
||||||
btnProExecAlway.Text = _isContinuousRunning ? "停止连续" : "连续执行";
|
btnProExecAlway.Text = _isContinuousRunning ? "停止连续" : "连续执行";
|
||||||
|
SyncAutoRunButtonState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1301,6 +1632,54 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 AppSettings 读取整型配置,失败时返回默认值。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">配置键名。</param>
|
||||||
|
/// <param name="defaultValue">默认值。</param>
|
||||||
|
/// <returns>解析后的整型值。</returns>
|
||||||
|
private static int GetAppSettingIntOrDefault(string key, int defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = ConfigurationManager.AppSettings[key];
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return int.TryParse(value.Trim(), out var parsedValue) ? parsedValue : defaultValue;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 AppSettings 读取布尔配置,失败时返回默认值。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">配置键名。</param>
|
||||||
|
/// <param name="defaultValue">默认值。</param>
|
||||||
|
/// <returns>解析后的布尔值。</returns>
|
||||||
|
private static bool GetAppSettingBoolOrDefault(string key, bool defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = ConfigurationManager.AppSettings[key];
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bool.TryParse(value.Trim(), out var parsedValue) ? parsedValue : defaultValue;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 尝试获取当前选中的流程对象。
|
/// 尝试获取当前选中的流程对象。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1411,6 +1790,7 @@ namespace HkVisionPro.App
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void ResetProcedureAndModuleState()
|
private void ResetProcedureAndModuleState()
|
||||||
{
|
{
|
||||||
|
StopAutoAssemblyIfRunning("重置流程与模块状态");
|
||||||
StopContinuousRunIfNeeded("重置流程与模块状态");
|
StopContinuousRunIfNeeded("重置流程与模块状态");
|
||||||
UnbindProcedureWorkEndCallback();
|
UnbindProcedureWorkEndCallback();
|
||||||
_currentProcedure = null;
|
_currentProcedure = null;
|
||||||
@@ -1427,40 +1807,6 @@ namespace HkVisionPro.App
|
|||||||
ClearPictureBoxImage();
|
ClearPictureBoxImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 读取流程字符串输出(多候选容错读取)。
|
|
||||||
/// </summary>
|
|
||||||
private string GetProcedureOutputString(VmProcedure procedure, params string[] outputNames)
|
|
||||||
{
|
|
||||||
if (procedure == null || outputNames == null || outputNames.Length == 0)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var outputName in outputNames)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(outputName))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var output = procedure.ModuResult.GetOutputString(outputName);
|
|
||||||
if (output.astStringVal != null && output.astStringVal.Length > 0)
|
|
||||||
{
|
|
||||||
return output.astStringVal[0].strValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// 忽略输出不存在等异常,保持流程主链路稳定。
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 读取流程整型输出(多候选容错读取)。
|
/// 读取流程整型输出(多候选容错读取)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1660,6 +2006,12 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导入流程按钮事件:从外部 <c>.prc</c> 文件导入流程到当前方案,并刷新流程列表。
|
||||||
|
/// 该方法包含完整的前置状态检查(忙碌态、方案是否已加载)与异常处理,避免 UI 重入导致的状态错乱。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(导入按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnProImport_Click(object sender, EventArgs e)
|
private void btnProImport_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -1693,6 +2045,7 @@ namespace HkVisionPro.App
|
|||||||
procedurePath = dialog.FileName;
|
procedurePath = dialog.FileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 通过 VM SDK 加载流程文件到当前方案上下文。
|
||||||
VmProcedure.Load(procedurePath);
|
VmProcedure.Load(procedurePath);
|
||||||
UpdateProcedureList();
|
UpdateProcedureList();
|
||||||
WriteLog($"流程导入成功:{procedurePath}");
|
WriteLog($"流程导入成功:{procedurePath}");
|
||||||
@@ -1708,6 +2061,11 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导出流程按钮事件:将当前选中的流程导出为独立 <c>.prc</c> 文件。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(导出按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnProExpo_Click(object sender, EventArgs e)
|
private void btnProExpo_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -1739,6 +2097,7 @@ namespace HkVisionPro.App
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
UpdateUiState(true);
|
UpdateUiState(true);
|
||||||
|
// 仅导出当前选中流程,不影响方案内其它流程。
|
||||||
procedure.SaveAs(dialog.FileName);
|
procedure.SaveAs(dialog.FileName);
|
||||||
WriteLog($"流程导出成功:{dialog.FileName}");
|
WriteLog($"流程导出成功:{dialog.FileName}");
|
||||||
MessageBox.Show(this, "流程导出成功。", "导出流程", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
MessageBox.Show(this, "流程导出成功。", "导出流程", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
@@ -1754,6 +2113,11 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除流程按钮事件:删除当前选中的流程,并同步清理界面绑定状态(模块列表、参数面板、渲染图像)。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(删除按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnProDel_Click(object sender, EventArgs e)
|
private void btnProDel_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -1781,6 +2145,7 @@ namespace HkVisionPro.App
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
UpdateUiState(true);
|
UpdateUiState(true);
|
||||||
|
// 从方案中删除流程,并清理当前流程回调绑定,避免悬挂引用。
|
||||||
VmSolution.Instance.DeleteOneProcedure(selectedProcedureName);
|
VmSolution.Instance.DeleteOneProcedure(selectedProcedureName);
|
||||||
UnbindProcedureWorkEndCallback();
|
UnbindProcedureWorkEndCallback();
|
||||||
_currentProcedure = null;
|
_currentProcedure = null;
|
||||||
@@ -1800,6 +2165,11 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行一次按钮事件:对当前选中流程执行单次运行,并在流程结束回调中刷新渲染结果。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(执行一次按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnProExecOnce_Click(object sender, EventArgs e)
|
private void btnProExecOnce_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -1820,6 +2190,7 @@ namespace HkVisionPro.App
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
UpdateUiState(true);
|
UpdateUiState(true);
|
||||||
|
// 执行单次前先停掉连续执行,保证流程运行模式互斥。
|
||||||
StopContinuousRunIfNeeded("执行单次流程");
|
StopContinuousRunIfNeeded("执行单次流程");
|
||||||
UnbindProcedureWorkEndCallback();
|
UnbindProcedureWorkEndCallback();
|
||||||
_currentProcedure = procedure;
|
_currentProcedure = procedure;
|
||||||
@@ -1838,6 +2209,13 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连续执行按钮事件(启停一体):
|
||||||
|
/// - 若当前未连续运行,则启动选中流程的连续执行;
|
||||||
|
/// - 若当前已连续运行,则先停止,再根据选择情况决定是否切换到新流程继续运行。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(连续执行按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnProExecAlway_Click(object sender, EventArgs e)
|
private void btnProExecAlway_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -1873,6 +2251,7 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显式开启 SDK 连续执行标记,并重新绑定流程结束回调。
|
||||||
procedure.ContinuousRunEnable = true;
|
procedure.ContinuousRunEnable = true;
|
||||||
UnbindProcedureWorkEndCallback();
|
UnbindProcedureWorkEndCallback();
|
||||||
_currentProcedure = procedure;
|
_currentProcedure = procedure;
|
||||||
@@ -1899,6 +2278,11 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止执行按钮事件:在连续执行模式下,主动停止当前流程连续运行并同步按钮状态。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(停止按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnProExecStop_Click(object sender, EventArgs e)
|
private void btnProExecStop_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -1917,10 +2301,16 @@ namespace HkVisionPro.App
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 仅处理连续执行停止,不触发新的执行请求。
|
||||||
StopContinuousRunIfNeeded("用户点击停止按钮");
|
StopContinuousRunIfNeeded("用户点击停止按钮");
|
||||||
UpdateUiState(false);
|
UpdateUiState(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定参数按钮事件:将当前模块绑定到参数配置控件,供用户查看或调整模块参数。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(绑定参数按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnModuleBindingPar_Click(object sender, EventArgs e)
|
private void btnModuleBindingPar_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -1940,6 +2330,7 @@ namespace HkVisionPro.App
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// 将模块对象注入参数控件,参数控件内部负责参数树加载与编辑。
|
||||||
vmParamsConfigControl1.ModuleSource = module;
|
vmParamsConfigControl1.ModuleSource = module;
|
||||||
WriteLog($"绑定模块参数成功:{cmbProcdure.Text}.{cmbModule.Text}");
|
WriteLog($"绑定模块参数成功:{cmbProcdure.Text}.{cmbModule.Text}");
|
||||||
}
|
}
|
||||||
@@ -1949,6 +2340,12 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行模块按钮事件:执行当前模块,并尝试刷新流程渲染结果;
|
||||||
|
/// 若为条码模块,则自动注册条码结果回调用于实时日志输出。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(执行模块按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnModuleExec_Click(object sender, EventArgs e)
|
private void btnModuleExec_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -1970,6 +2367,7 @@ namespace HkVisionPro.App
|
|||||||
{
|
{
|
||||||
UpdateUiState(true);
|
UpdateUiState(true);
|
||||||
|
|
||||||
|
// 先执行模块本体,再根据当前流程输出刷新界面渲染。
|
||||||
module.Run();
|
module.Run();
|
||||||
if (TryGetSelectedProcedure(out var procedure))
|
if (TryGetSelectedProcedure(out var procedure))
|
||||||
{
|
{
|
||||||
@@ -1983,6 +2381,7 @@ namespace HkVisionPro.App
|
|||||||
_activeBcrTool = null;
|
_activeBcrTool = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 若当前模块支持条码结果回调,启用回调并做去重绑定。
|
||||||
var bcrTool = module as IMVSBcrModuTool;
|
var bcrTool = module as IMVSBcrModuTool;
|
||||||
if (bcrTool != null)
|
if (bcrTool != null)
|
||||||
{
|
{
|
||||||
@@ -2007,6 +2406,11 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渲染结果按钮事件:不重新执行模块,仅根据当前流程最新输出重绘图像/ROI/识别结果。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(渲染结果按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void btnModuleRenderResult_Click(object sender, EventArgs e)
|
private void btnModuleRenderResult_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
@@ -2068,6 +2472,11 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模块下拉展开事件:在用户展开模块下拉框时动态刷新模块列表,确保显示与当前流程一致。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(模块下拉框)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void cmbModule_DropDown(object sender, EventArgs e)
|
private void cmbModule_DropDown(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy || !_isSolutionLoaded)
|
if (_isBusy || !_isSolutionLoaded)
|
||||||
@@ -2086,6 +2495,11 @@ namespace HkVisionPro.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 流程下拉展开事件:在用户展开流程下拉框时动态刷新流程列表,避免旧缓存造成显示不一致。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(流程下拉框)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void cmbProcdure_DropDown(object sender, EventArgs e)
|
private void cmbProcdure_DropDown(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_isBusy || !_isSolutionLoaded)
|
if (_isBusy || !_isSolutionLoaded)
|
||||||
@@ -2114,9 +2528,66 @@ namespace HkVisionPro.App
|
|||||||
ReleaseSdkResources();
|
ReleaseSdkResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主窗体加载事件。
|
||||||
|
/// 当前初始化逻辑已在构造函数中完成(主题、回调、UI 状态),
|
||||||
|
/// 因此此处暂不添加额外业务处理,预留后续扩展入口。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(主窗体)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
private void MainForm_Load(object sender, EventArgs e)
|
private void MainForm_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动运行按钮事件。
|
||||||
|
/// 点击后执行自动流程启停切换:
|
||||||
|
/// - 未运行:启动自动状态机;
|
||||||
|
/// - 运行中:停止自动状态机。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">事件源控件(自动运行按钮)。</param>
|
||||||
|
/// <param name="e">标准事件参数。</param>
|
||||||
|
private void btnAutoRun_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_isBusy)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EnsureSolutionLoaded("自动运行"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdateUiState(true);
|
||||||
|
|
||||||
|
if (_autoAssemblyController.IsRunning)
|
||||||
|
{
|
||||||
|
StopAutoAssemblyIfRunning("用户点击自动运行按钮停止");
|
||||||
|
WriteLog("自动装配流程已停止(按钮触发)。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动自动流程前清理手动流程执行上下文,确保执行链路互斥。
|
||||||
|
StopContinuousRunIfNeeded("点击自动运行按钮");
|
||||||
|
UnbindProcedureWorkEndCallback();
|
||||||
|
_currentProcedure = null;
|
||||||
|
|
||||||
|
_autoAssemblyController.Start();
|
||||||
|
SyncAutoRunButtonState();
|
||||||
|
WriteLog("自动装配流程已启动(按钮触发)。");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
HandleException("自动运行按钮操作", ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
UpdateUiState(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user