4. IEC 61131-3数据建模
在PLCnext的数据采集项目中,真正决定后续云端对接效率的,往往不是通信协议选得多先进,而是变量建模是否清晰。IEC 61131-3不仅规定了编程语言,也提供了变量、数据类型、结构体(STRUCT)和数组(ARRAY)等建模手段。把这些手段用在产线设备上,就能把“现场一堆散点”整理成“可采集、可理解、可维护”的数据字典。
一、从设备对象出发,而不是从地址出发
很多初学者习惯先看PLC地址表:%I0.0是启动按钮,%Q0.1是电机输出,%IW64是温度值。这种按地址罗列的方式在采集端会带来两个问题:一是地址含义靠注释维护,注释一丢就不可读;二是同一台设备的状态量、模拟量、报警量分散在不同区域,云端无法按设备聚合。
更合理的做法是先识别产线中的设备对象。以一条典型装配线为例,可抽象出以下对象:输送电机、气缸、光电传感器、RFID读写器、称重模块、安全门。每个对象都有属性,属性就是需要采集的变量。例如输送电机有:运行状态、故障状态、当前速度、累计运行时间、启停命令。把这些属性用IEC 61131-3的数据类型表达出来,就形成了设备变量。
二、用基本数据类型锁定语义
IEC 61131-3的基本数据类型是建模的最小单元。选择类型时,不只看位数,还要看语义:
- BOOL:用于开关量,如电机运行、气缸到位、安全门关闭。
- INT / DINT:用于计数值、状态码、配方号。状态码建议用INT,便于后续映射枚举。
- REAL:用于速度、温度、压力、重量等模拟量。注意REAL是32位浮点,采集到云端后通常直接映射为float。
- TIME:用于累计运行时间、节拍时间。PLCnext中TIME通常为64位,单位毫秒或纳秒,采集时需明确单位。
- STRING:用于设备编号、批次号、RFID标签内容。STRING长度要显式限定,避免不定长带来的内存与通信问题。
一个常见错误是把所有模拟量都定义成REAL,却忽略了量程和工程单位。例如称重模块原始值为INT,量程0~20000对应0~5000g,那么建模时应同时保留原始值变量和工程值变量,并在数据字典中记录换算关系。这样云端既能拿到原始数据做诊断,也能直接使用工程值做展示。
三、结构体:把一台设备的属性打包
结构体(STRUCT)是IEC 61131-3中组织相关变量的核心工具。以输送电机为例,可以定义:
TYPE ST_Motor :
STRUCT
bRun : BOOL; // 运行状态
bFault : BOOL; // 故障状态
bEnable : BOOL; // 使能状态
rSpeed : REAL; // 当前速度,单位mm/s
nStatus : INT; // 状态码:0停止 1运行 2故障
tRunTime : TIME; // 累计运行时间
sDeviceID : STRING[16];// 设备编号
END_STRUCT
END_TYPE
定义好结构体后,在全局变量表中声明:stMotor1 : ST_Motor;。这样一台电机的所有采集点就聚合在一个命名空间下。云端采集时,只需按stMotor1.前缀批量读取,既减少通信请求数量,也避免变量名冲突。
结构体还可以嵌套。例如把整条产线定义为ST_Line,内部包含多个ST_Motor、ST_Cylinder和ST_Sensor。这种层级关系与云端设备模型天然对应:产线→设备→属性。
四、数组:处理同类设备的批量采集
当产线上有8个相同的气缸时,逐个定义stCylinder1到stCylinder8会非常冗长。此时用数组更合适:
TYPE ST_Cylinder :
STRUCT
bExtend : BOOL; // 伸出到位
bRetract : BOOL; // 缩回到位
bAlarm : BOOL; // 报警
nActionCount: DINT; // 动作次数
END_STRUCT
END_TYPE
VAR_GLOBAL
aCylinder : ARRAY[1..8] OF ST_Cylinder;
END_VAR
采集端可以用循环遍历aCylinder[i],把8个气缸的数据一次性打包上传。数组下标建议从1开始,符合IEC 61131-3习惯,也便于与现场编号对应。如果设备数量可能扩展,可以在数据字典中预留数组上限,例如ARRAY[1..16],实际使用前8个。
五、建立可采集的数据字典
变量定义完成后,还需要一份数据字典,把“PLC内部变量”翻译成“云端可理解的数据点”。数据字典至少应包含:变量路径、数据类型、工程单位、量程、采集周期、读写权限、业务含义。下面是一个示例片段:
| 变量路径 | 类型 | 单位 | 量程 | 周期 | 权限 | 含义 |
|---|---|---|---|---|---|---|
| stMotor1.bRun | BOOL | — | 0/1 | 100ms | 只读 | 1号电机运行状态 |
| stMotor1.rSpeed | REAL | mm/s | 0~1500 | 100ms | 只读 | 1号电机当前速度 |
| stMotor1.nStatus | INT | — | 0/1/2 | 100ms | 只读 | 0停止 1运行 2故障 |
| aCylinder[1].bExtend | BOOL | — | 0/1 | 200ms | 只读 | 1号气缸伸出到位 |
| aCylinder[1].nActionCount | DINT | 次 | 0~999999 | 1s | 只读 | 1号气缸动作次数 |
| stScale.rWeight | REAL | g | 0~5000 | 50ms | 只读 | 称重工程值 |
数据字典不是一次性文档,而是采集配置的来源。OPC UA服务器、MQTT主题、REST接口都可以从这份字典自动生成。例如MQTT主题可设计为line1/motor1/speed,与stMotor1.rSpeed一一对应。
六、建模时的几个实用判断
第一,状态量用BOOL还是INT?如果只有“是/否”,用BOOL;如果有多个状态(停止、运行、故障、维护),用INT状态码,并在数据字典中列出枚举映射。第二,模拟量是否保留原始值?建议保留。原始值可用于诊断传感器断线或量程溢出,工程值用于展示。第三,时间量用什么单位?PLCnext中TIME类型精度高,但云端通常只需要秒级。可以在采集端统一转换为DINT秒数,减少数据量。
第四,字符串长度要克制。RFID标签号如果固定为8字节,就定义STRING[8],不要用STRING[255]。第五,数组下标与设备编号的对应关系要写进字典。例如aCylinder[1]对应现场1号气缸,避免调试时靠猜。