事件与动作
Vision Studio 采用事件驱动模型,视觉程序在执行过程中触发事件,绑定的事件处理器自动执行相应动作。事件机制实现了视觉程序与 PLC 逻辑的松耦合联动。
事件模型
┌────────────────────────────────────────────┐
│ 视觉程序执行流程 │
│ │
│ 触发信号 ──→ 采集 ──→ 预处理 ──→ 推理 ──→ 输出 │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ OnTrigger OnFrame OnInference │
│ Completed │
│ │
│ 异常 ──→ OnFault │
│ 超时 ──→ OnTimeout │
└────────────────────────────────────────────┘
事件列表
OnTrigger (触发事件)
当视觉程序被触发时触发,发生在采集开始之前。
触发时机:收到触发信号(软件、硬件、PLC 变量或定时)的瞬间
事件参数:
| 参数 | 类型 | 说明 |
|---|---|---|
trigger_source | STRING | 触发源("PLC"/"HARDWARE"/"SOFTWARE"/"TIMER") |
trigger_timestamp | LWORD | 触发时间戳 |
trigger_count | DWORD | 触发累计次数 |
last_result_status | DWORD | 上次执行状态 |
示例用途:
- 重置累计计数器
- 记录触发时间
- 检查上次执行状态,决定是否继续
OnFrame (帧事件)
当相机采集到一帧图像时触发,发生在图像预处理之前。
触发时机:图像采集完成,图像数据就绪
事件参数:
| 参数 | 类型 | 说明 |
|---|---|---|
image | Image | 采集到的原始图像 |
acquisition_time_ms | REAL | 采集耗时 |
exposure_time | REAL | 实际曝光时间 (µs) |
gain | REAL | 实际增益 (dB) |
示例用途:
- 检查图像质量(亮度、对比度)
- 动态调整曝光参数
- 保存原始图像用于追溯
OnInferenceCompleted (推理完成事件)
当推理完成并得到结果时触发。
触发时机:模型推理完成,结果已生成
事件参数:
| 参数 | 类型 | 说明 |
|---|---|---|
result | DetectionResult | 检测结果 |
inference_time_ms | REAL | 推理耗时 |
model_name | STRING | 模型名称 |
input_size | STRING | 输入尺寸(如 "640x640") |
示例用途:
- 根据结果执行条件分支
- 触发二次确认流程
- 记录推理性能指标
OnFault (故障事件)
当视觉程序执行过程中发生故障时触发。
触发时机:相机断开、模型加载失败、推理异常等
事件参数:
| 参数 | 类型 | 说明 |
|---|---|---|
error_code | DWORD | 错误码 |
error_message | STRING | 错误描述 |
fault_source | STRING | 故障来源("CAMERA"/"MODEL"/"INFERENCE"/"SYSTEM") |
recoverable | BOOL | 是否可自动恢复 |
故障事件不触发 OnTimeout:OnFault 覆盖超时以外的所有异常
示例用途:
- 切换备用相机
- 记录故障日志
- 通知 PLC 进入安全状态
OnTimeout (超时事件)
当视觉程序执行超过配置的超时时间时触发。
触发时机:采集或推理超过 timeout_ms 配置值
事件参数:
| 参数 | 类型 | 说明 |
|---|---|---|
timeout_ms | DWORD | 配置的超时值 |
elapsed_ms | DWORD | 实际已耗时 |
stage | STRING | 超时阶段("ACQUISITION"/"INFERENCE") |
OnTimeout 与 OnFault 互斥:超时场景只触发 OnTimeout,不触发 OnFault
示例用途:
- 输出默认结果值
- 记录超时到诊断日志
- 触发降级处理流程
事件绑定
在卡片图编辑器中绑定
- 选中要绑定事件的卡片
- 在属性面板中选择 事件 标签页
- 点击事件旁边的 + 按钮添加动作
┌──────────────────────────────────────┐
│ 卡片: 目标检测 │
│ ┌──────┬─────────────────────────┐ │
│ │ 参数 │ 事件 │ │
│ ├──────┼─────────────────────────┤ │
│ │ │ ● OnInferenceCompleted │ │
│ │ │ ├─ 写入 PLC 变量 │ │
│ │ │ ├─ 保存图像 │ │
│ │ │ └─ 记录日志 │ │
│ │ │ │ │
│ │ │ ○ OnFault │ │
│ │ │ └─ 切换备用相机 │ │
│ │ │ │ │
│ │ │ ○ OnTimeout │ │
│ │ │ └─ 输出默认值 │ │
│ └──────┴─────────────────────────┘ │
│ │
│ [+ 添加动作] │
└──────────────────────────────────────┘
在 Vision SCL 中绑定
PROGRAM VisionWithEvents
VAR
cam : Camera;
img : Image;
result : DetectionResult;
END_VAR
// 注册事件处理器
EVENT_BIND(EVENT_ON_TRIGGER, "OnTriggerHandler");
EVENT_BIND(EVENT_ON_FRAME, "OnFrameHandler");
EVENT_BIND(EVENT_ON_INFERENCE_COMPLETED, "OnInferenceHandler");
EVENT_BIND(EVENT_ON_FAULT, "OnFaultHandler");
EVENT_BIND(EVENT_ON_TIMEOUT, "OnTimeoutHandler");
// 主流程
cam := CAM_Open("USB_CAM_1");
img := CAM_Capture(cam, TIMEOUT := 5000);
result := OBJ_Detect(img, MODEL := "model.onnx");
// 等待所有事件处理完成
EVENT_WAIT_ALL(TIMEOUT := 1000);
END_PROGRAM
// 事件处理器: 推理完成
ACTION OnInferenceHandler
VAR
i : INT;
END_VAR
VIS_Log(LOG_INFO, "推理完成, 检测到 " +
INT_TO_STRING(result.object_count) + " 个目标");
// 逐一输出结果
FOR i := 0 TO result.object_count - 1 DO
VIS_WriteVariable("VIS_OBJ_" + INT_TO_STRING(i) + "_CLASS",
STRING_TO_REAL(result.objects[i].class_name));
END_FOR;
END_ACTION
// 事件处理器: 故障处理
ACTION OnFaultHandler
VAR
alt_cam : Camera;
END_VAR
VIS_Log(LOG_ERROR, "视觉故障: " + error_message);
// 尝试切换备用相机
IF error_code = ERR_CAMERA_DISCONNECTED THEN
alt_cam := CAM_Open("USB_CAM_2");
IF alt_cam <> INVALID_CAMERA THEN
VIS_WriteVariable("VIS_CAM_SWITCHED", 1.0);
END_IF;
END_IF;
END_ACTION
// 事件处理器: 超时处理
ACTION OnTimeoutHandler
VIS_Log(LOG_WARN, "视觉超时: " + stage + " 已耗时 " +
INT_TO_STRING(elapsed_ms) + "ms");
// 输出默认值
VIS_WriteVariable("VIS_RESULT_COUNT", 0.0);
VIS_WriteVariable("VIS_JOB_TIMEOUT", 1.0);
END_ACTION
动作配置
可用动作类型
| 动作类型 | 说明 | 适用事件 |
|---|---|---|
| 写入 PLC 变量 | 将指定值写入 PLC 变量 | 全部 |
| 保存图像 | 将当前图像保存到文件 | OnFrame, OnInferenceCompleted |
| 记录日志 | 写入诊断日志 | 全部 |
| 发送通知 | 发送报警或通知 | OnFault, OnTimeout |
| 执行 SCL 脚本 | 执行一段 Vision SCL 代码 | 全部 |
| 触发另一视觉程序 | 启动另一个视觉 Job | OnInferenceCompleted |
| 切换相机 | 切换到备用相机 | OnFault |
| 输出默认值 | 输出预配置的默认结果 | OnTimeout |
动作执行顺序
同一事件可以绑定多个动作,按配置顺序执行:
- 同步动作(如写入 PLC 变量、记录日志)立即执行
- 异步动作(如保存图像、触发另一 Job)加入队列,不阻塞主流程
- 所有动作在
EVENT_WAIT_ALL超时时间内完成
动作条件
可以为动作设置执行条件,仅当条件满足时执行:
动作: 保存图像
条件: result.object_count > 0 // 仅当检测到目标时保存
| 条件类型 | 说明 | 示例 |
|---|---|---|
| 数值比较 | 比较事件参数 | result.confidence > 0.8 |
| 字符串匹配 | 匹配字符串参数 | trigger_source == "PLC" |
| 布尔表达式 | 组合条件 | pass AND count > 0 |
| 复合条件 | 多条件 AND/OR | (error_code <> 0) OR (elapsed_ms > 5000) |
事件优先级
当多个事件同时发生时,按优先级处理:
| 优先级 | 事件 | 说明 |
|---|---|---|
| 最高 | OnFault | 故障优先处理 |
| 高 | OnTimeout | 超时次之 |
| 中 | OnTrigger | 触发事件 |
| 低 | OnFrame | 帧事件 |
| 最低 | OnInferenceCompleted | 推理完成 |
最佳实践
- 事件处理器保持轻量:事件处理器中避免长时间操作,耗时操作应异步执行
- OnFault 必须处理:每个视觉程序都应绑定 OnFault,确保故障可追溯
- OnTimeout 设置默认值:超时时输出默认结果,避免 PLC 读到脏数据
- 避免事件循环:注意不要在事件处理器中触发同一事件,防止无限循环
- 日志分级:OnFault 用 ERROR 级别、OnTimeout 用 WARN 级别、正常事件用 INFO 级别