当前开发阶段只有一个有效工程格式:1.0。代码直接读写本文描述的结构,不为早期开发草稿增加同版本默认值、字段推断、迁移分支或双写逻辑。缺少必填字段、字段类型错误、引用不存在或领域校验失败时,加载必须失败且不得替换当前工程。
{
"formatVersion": "1.0",
"id": "project-1",
"name": "包装线",
"hmiPages": [],
"initialHmiPageId": "",
"alarmDefinitions": [],
"registerComments": [],
"controlLogics": []
}
所有顶层字段必填。空工程允许 hmiPages、alarmDefinitions、registerComments 和 controlLogics 为空,此时 initialHmiPageId 必须为空。存在页面时,initialHmiPageId 必须引用 hmiPages 中的页面 ID。
hmiPages 与 controlLogics 都是有序数组。页面顺序用于工程树显示;控制逻辑顺序是离线扫描顺序。对象 ID 稳定且唯一,名称可修改且在同类对象中唯一。
编辑器的当前页面 ID 和当前逻辑 ID 是主窗口会话状态,不进入工程 JSON。每次进入运行态时,HMI 从 initialHmiPageId 重新启动。
{
"id": "page-1",
"name": "主操作页面",
"width": 800,
"height": 480,
"controls": []
}
每个控件都要求 id、type、bounds、text、binding 和 properties。bounds 包含整数 x、y、width、height,矩形必须完整位于页面内。binding 为寄存器对象或 JSON null。
properties 保存控件外观扩展属性。当前保留以下 HMI 外观属性名:
| 属性名 | 值格式 | 说明 |
|---|---|---|
textColor |
#RRGGBB 字符串 |
控件普通文字颜色,缺失时使用控件默认颜色 |
fontSize |
6~72 的十进制数字字符串 |
控件文字字号 |
fontBold |
true 或 false 字符串 |
是否使用粗体 |
fontItalic |
true 或 false 字符串 |
是否使用斜体 |
字体和颜色属于静态显示属性,不参与 M/D 绑定、PLC 轮询和离线控制逻辑。未知扩展属性仍按字符串保留。
type |
绑定 | 额外必填字段 |
|---|---|---|
button |
M 地址或 null 草稿 |
buttonOperation |
indicator |
M 地址或 null 草稿 |
无 |
numericDisplay |
D 地址或 null 草稿 |
无 |
numericInput |
D 地址或 null 草稿 |
无 |
label |
必须为 null |
无 |
pageJump |
必须为 null |
targetPageId |
alarmList |
必须为 null |
无 |
progressBar |
D 地址或 null 草稿 |
minimumValue、maximumValue、showValue |
按钮操作字符串为 setOn、setOff、toggle 或 momentaryOn。PageJump 使用目标页面稳定 ID,不使用名称或数组位置:
{
"id": "page-jump-1",
"type": "pageJump",
"bounds": {"x": 20, "y": 20, "width": 120, "height": 40},
"text": "参数设置",
"binding": null,
"properties": {},
"targetPageId": "page-2"
}
编辑草稿允许 targetPageId 为空字符串,但进入运行态前必须配置为现存页面 ID。任何页面被 PageJump 引用时不得删除。
AlarmList 只负责显示项目级当前报警,不保存独立触发逻辑,也不直接绑定单个寄存器。多个页面放置 AlarmList 时共享同一组当前报警和确认状态。运行态没有报警时控件隐藏;报警恢复后对应记录立即移除,最后一条报警恢复后控件再次隐藏。
ProgressBar 使用绑定 D 地址的有符号 16 位字值,并将其限制在 minimumValue 到
maximumValue 的范围后映射为百分比。minimumValue 和 maximumValue 必须是有符号 16 位整数,
且最小值严格小于最大值;showValue 为布尔值,决定是否在进度条上显示百分比。ProgressBar
是只读控件,不产生 PLC 写请求。
{
"id": "alarm-1",
"address": {"area": "M", "index": 0},
"condition": "mOn",
"threshold": 0,
"message": "急停已按下"
}
alarmDefinitions 是项目级有序数组,报警 ID 必须唯一。触发条件只包含当前业务需要的三种:
condition |
地址区域 | 触发规则 |
|---|---|---|
mOn |
M | 位值为 ON |
dHigh |
D | 字值大于等于 threshold |
dLow |
D | 字值小于等于 threshold |
threshold 使用有符号 16 位范围;M 报警仍保留该必填字段但不参与判断。报警文本不能为空。发生时间、活动状态、确认状态和清除时间属于运行会话,不进入工程 JSON。
registerComments 是工程级 M/D 地址元数据数组。每个元素必须包含有效的 address 和非空 text,同一地址只能出现一次:
{
"address": {"area": "M", "index": 0},
"text": "启动按钮"
}
注释不保存寄存器当前值,也不自动把地址加入 Modbus 轮询。网络注释属于 LadderRung,与网络 ID、名称、条件和输出并列保存:
{
"id": "rung-1",
"name": "启动网络",
"comment": "启动按钮接通后延时启动",
"condition": null,
"output": null
}
{
"id": "logic-1",
"name": "主控制逻辑",
"enabled": true,
"rungs": []
}
enabled 决定离线执行器是否扫描该逻辑。所有启用逻辑按 controlLogics 数组顺序执行;禁用逻辑仍要求 ID、名称、网络和表达式结构合法,但允许保留未完成节点作为草稿。
网络、Node / Wire / Series / Parallel 条件表达式、触点、边沿、T/C 触点、比较、线圈和输出指令字段沿用当前结构化梯形图模型。横线保存为恒真的 Wire 叶子:
{"id": "wire-1", "kind": "wire", "columnSpan": 2}
columnSpan 是必填整数,范围为 1~10。它表示横线占用的条件区逻辑网格列数,不是像素长度。单个网络前 10 列是条件区,第 11 列固定放输出指令。竖线不保存为独立对象,而是由 Parallel 表达式的支路边界派生。画布像素坐标、自由连线和运行轨迹是 UI 或运行会话投影,不进入 JSON;横线不绑定寄存器,也不产生 M/D 轮询地址。节点配置的类型和值域如下:
{"type": "edgeContact", "address": {"area": "M", "index": 0}, "mode": "rising"}
{"type": "edgeContact", "address": {"area": "M", "index": 0}, "mode": "falling"}
{"type": "timerContact", "timer": {"index": 0}, "mode": "normallyOpen"}
{"type": "ton", "timer": {"index": 0}, "presetMs": 1000}
{"type": "counterContact", "counter": {"index": 0}, "mode": "normallyOpen"}
{"type": "counter", "counter": {"index": 0}, "mode": "up", "currentValueAddress": {"area": "D", "index": 10}, "preset": {"kind": "constant", "value": 10}, "resetAddress": {"area": "M", "index": 0}}
{"type": "move", "source": {"kind": "register", "address": {"area": "D", "index": 1}}, "destination": {"area": "D", "index": 2}}
{"type": "arithmetic", "operation": "add", "left": {"kind": "register", "address": {"area": "D", "index": 2}}, "right": {"kind": "constant", "value": 1}, "destination": {"area": "D", "index": 2}}
T/C 资源地址范围均为 0~4000,TON presetMs 范围为 1~86400000。C 仅标识离线计数器实例,计数当前值必须绑定 D 地址,复位必须绑定 M 地址,PV 可为有符号 16 位常量或 D 地址。CTU/CTD 按条件上升沿分别递增/递减;CTU 复位将 CV 置 0,CTD 复位将 CV 装载 PV,CV 始终限制在 0~32767,C 触点读取完成状态。上升沿和下降沿是一次扫描脉冲;TON 是非保持接通延时,输入断开时 ET/Q 立即清零。T/C 不参与 Modbus,也不写入 M/D 仓库。
MOVE、ADD 和 SUB 只能把结果写入 D 地址,源操作数和算术左右操作数可以是有符号 16 位常量或 D 地址。指令仅在所在网络条件成立时执行;ADD/SUB 使用更宽中间类型计算,结果超出 -32768~32767 时饱和到对应边界,并在离线运行轨迹中记录 overflow: true,不会中止扫描。
保存顺序为领域校验、JSON 序列化、QSaveFile 原子提交。加载顺序为 JSON 语法、formatVersion、必填字段和类型、领域关系校验,全部成功后才替换当前工程。
工程保存允许未绑定的寄存器控件和未完成梯形图草稿。进入离线运行还必须通过运行校验:所有 HMI 寄存器控件完成绑定、所有 PageJump 目标有效、所有报警定义合法、所有启用逻辑包含输出指令;禁用草稿逻辑不阻止运行。只有输出而没有条件的网络表示左侧电源线直接接通,运行时按恒真条件执行。