IO 监控
IOMonitor 提供实时 IO 状态观察和变更通知功能,帮助工程师在线监控所有 IO 信号的当前值、变化历史和通讯健康状况。
架构
实时状态显示
IO 状态面板以表格形式显示所有 IO 点位的实时状态:
| 列 | 说明 |
|---|---|
| 地址 | IEC 地址 (%IX0.0) |
| 名称 | 用户定义名称 |
| 当前值 | 实时值 (带颜色指示) |
| 类型 | DI / DO / AI / AO |
| 协议 | EtherCAT / Modbus / Virtual 等 |
| 强制 | 是否处于强制状态 |
| 通讯 | 通讯状态 (正常/超时/断开) |
颜色编码
| 状态 | DI/DO 颜色 | AI/AO 显示 |
|---|---|---|
| OFF / 0 | 灰色 | 数值 + 单位 |
| ON / 非零 | 绿色 | 数值 + 条形图 |
| 强制值 | 黄色边框 | 黄色边框 |
| 通讯故障 | 红色 | 红色 |
变更通知
IOMonitor 在信号状态变化时触发回调事件:
var monitor = new IOMonitor();
monitor.OnValueChanged += (sender, e) =>
{
Console.WriteLine($"[{e.Timestamp:HH:mm:ss.fff}] " +
$"{e.Address} ({e.Name}): {e.OldValue} → {e.NewValue}");
};
// 启用监控
monitor.StartMonitoring();
事件过滤
支持按条件过滤变更通知,避免高频信号产生过多事件:
| 过滤条件 | 说明 |
|---|---|
| 地址范围 | 仅监控指定地址范围 |
| 信号类型 | 仅监控 DI / DO / AI / AO |
| 变化阈值 | AI/AO 变化超过阈值才通知 |
| 最小间隔 | 同一信号两次通知的最小间隔 |
monitor.AddFilter(new IOFilter {
AddressPattern = "%IX0.*", // 仅监控 %IX0 开头的地址
MinInterval = 100, // 最小通知间隔 100ms
AnalogThreshold = 0.5 // 模拟量变化 > 0.5 才通知
});
边沿检测
IOMonitor 集成 IOManager 的边沿检测功能,自动识别数字信号的上升沿和下降沿:
| 检测类型 | IEC 功能块 | 触发条件 |
|---|---|---|
| 上升沿 | R_TRIG | OFF → ON |
| 下降沿 | F_TRIG | ON → OFF |
| 双沿 | — | 任意变化 |
monitor.OnRisingEdge += (sender, e) =>
{
Console.WriteLine($"上升沿: {e.Address} at {e.Timestamp:HH:mm:ss.fff}");
};
monitor.OnFallingEdge += (sender, e) =>
{
Console.WriteLine($"下降沿: {e.Address} at {e.Timestamp:HH:mm:ss.fff}");
};
消抖处理
对于机械触点等存在抖动的信号,IOMonitor 使用可配置的消抖滤波:
| 参数 | 说明 | 默认值 |
|---|---|---|
| 消抖时间 | 信号稳定持续时间 | 10ms |
| 消抖模式 | 上升沿/下降沿/双向 | 双向 |
消抖时间选择
- 光电传感器: 1-5ms
- 接近开关: 5-10ms
- 机械按钮: 10-50ms
- 继电器触点: 5-20ms
变更日志
IOMonitor 自动记录所有 IO 变更到数据缓存,支持回溯查看:
// 查询最近 100 条变更记录
var history = monitor.GetHistory(count: 100);
// 查询指定地址的变更历史
var addrHistory = monitor.GetHistory(address: "%IX0.0",
from: DateTime.Now.AddMinutes(-5));
趋势集成
模拟量信号可以直接添加到趋势面板进行实时曲线显示:
// 将模拟输入添加到趋势显示
trendPanel.AddChannel(new TrendChannel {
Address = "%IW100",
Name = "温度传感器",
Unit = "°C",
ScaleFactor = 0.1,
Color = Colors.Red
});
监视表
除了全 IO 概览, IDE 还支持"监视表" — 按需选择关心的变量/IO 集中显示:
项目树 → 监视表 → 新建
→ 添加 %IX0.0 (工件到位)
→ 添加 %QX0.0 (夹爪)
→ 添加 %IW100 (温度, scale 0.1, unit °C)
→ 添加 MW500 (生产计数)
→ 保存
监视表随项目保存, 下次打开恢复。每个表可独立配置:
- 刷新频率 (50 ms / 200 ms / 1 s)
- 显示格式 (十进制 / 十六进制 / 二进制)
- 排序方式 (按地址 / 按名称 / 按修改时间)
在线强制值
监视表里可以强制 IO 值:
- 右键某一行 → 强制 → 输入新值
- 值被锁定, 程序写入和硬件输入都被忽略
- 该行背景变黄提示
- 右键 → 解除强制 恢复正常
用于临时绕过传感器故障或调试逻辑分支。
强制输出
强制 %QX* (输出) 会实际驱动硬件, 可能导致设备误动作。仅在有安全防护 (如急停拍下) 时使用。
变更事件流
IOMonitor 的事件流可以导出:
monitor.StartLogging(file: "C:/logs/io.csv");
// ... 一段时间 ...
monitor.StopLogging();
CSV 格式:
Timestamp,Address,Name,OldValue,NewValue,TriggerSource
2026-04-17 10:00:01.234,%IX0.0,工件到位,false,true,Hardware
2026-04-17 10:00:01.456,%QX0.0,夹爪开,false,true,PLC
2026-04-17 10:00:02.123,%IW100,温度,85.3,85.5,Hardware
用于:
- 事后回溯故障时序
- 统计 IO 频率分布
- 校验 PLC 逻辑
高频信号显示
对 > 100 Hz 变化的信号, UI 刷新跟不上, 但不遗漏统计:
- UI 刷新: 25 Hz (40 ms)
- 后台采样: 每扫描周期
- 统计: 1 秒内的脉冲数, 最大值, 最小值
这样快速开合的传感器不会被 UI 丢失, 用户看到的是"1 秒内翻转 5 次"而不是静态的某个值。
分组显示
大量 IO 时, 按设备/区域分组:
├─ EtherCAT (32 points)
│ ├─ Slave 1 (EL1008, 8 DI)
│ ├─ Slave 2 (EL2008, 8 DO)
│ └─ Slave 3 (EL3064, 4 AI)
├─ Modbus (16 points)
│ ├─ 变频器 A (4 WORD)
│ ├─ 变频器 B (4 WORD)
│ └─ 温控 1 (8 WORD)
└─ 内部变量 (50+)
分组折叠节省屏幕空间, 每组右侧显示故障点数。
颜色主题
| 主题 | 背景 | OFF | ON | 故障 |
|---|---|---|---|---|
| 默认 | 白 | 灰 | 绿 | 红 |
| 深色 | 黑 | 暗灰 | 亮绿 | 亮红 |
| 高对比 | 纯黑 | 灰 | 黄 | 红 |
| 色盲友好 | 白 | 蓝色虚线 | 蓝色实心 | 橙 |
设置 → 外观 → IO 监视颜色 里配置。
过滤表达式
监视表支持过滤表达式显示特定条件的点:
显示所有值为 TRUE 的 DI:
value == true && type == "DI"
显示温度 > 80 的 AI:
address.startsWith("%IW1") && value > 80
显示最近 5 分钟有变化的:
lastChangeTime > now - 5min
过滤项动态更新, 符合条件的行实时进出。
性能计数
每个 IO 设备的统计信息:
| 指标 | 说明 |
|---|---|
| 周期成功率 | 最近 1000 周期的成功百分比 |
| 平均延迟 | 从发请求到收响应的 ms |
| 错误计数 (分类) | CRC / Timeout / Nack / Other |
| 连续错误 | 当前连续错误数, 持续增长 = 实际掉线 |
| 总请求 / 总错误 | 自启动以来累计 |
与报警集成
监视窗口可以按条件自动触发报警:
项目树 → 报警 → 新建报警
条件: %IW100 > 95
名称: 温度过高
级别: Warning
确认方式: Manual
触发后报警面板显示, 可推送 MQTT / HTTP webhook。
排错
| 症状 | 处理 |
|---|---|
| 监视表刷新慢 | 减少监视点数或降低刷新频率 |
| 值显示错位 | 检查地址和数据类型是否一致 |
| 强制值不生效 | 检查是否正在在线模式; 某些 IO 可能被硬件旁路 |
| CSV 日志文件过大 | 加过滤条件或缩短日志时间 |
| 多实例冲突 | 同一项目不能在多个 IDE 里同时开强制值 |