跳到主要内容

变量绑定基础

Darra HMI 的核心是 darra 全局 JS 对象 (定义在 /static/darra-plc.js), 封装了 WebSocket 通信、变量订阅、双向写入、元数据查询、连接生命周期事件。所有控件、脚本都必须通过 darra.* 访问 PLC 数据, 严禁直接操作 WebSocket

PLC 变量地址语法 (IEC 61131-3)

语法类型示例
M<byte>.<bit>标志位 (BOOL)M0.0 M0.7 M100.3
MB<offset>标志字节 (BYTE)MB10
MW<offset>标志字 (WORD / INT16)MW100 MW200
MD<offset>标志双字 (DWORD / INT32 / REAL)MD100 MD200
I<byte>.<bit>输入I0.0
IW<offset>输入字IW0
Q<byte>.<bit>输出Q0.0
QW<offset>输出字QW4
DB<n>.<name>数据块字段DB1.Temperature
DB<n>.<name>.<member>嵌套结构DB1.Motor.Speed

MD100MW100 / MW102 共享存储; 程序内注意避免别名冲突。

darra.init(config) — 初始化

darra.init({
updateRate: 100, // 变量推送节流 ms (服务端)
reconnect: true, // 自动重连
reconnectInterval: 3000, // 重连间隔 ms
locale: 'zh-CN' // 区域 ('zh-CN' | 'en-US')
})

默认已在 HmiLayoutRenderer<body x-init> 中调用, 多数情况无需手动 init。只有需要修改默认配置时才调用。

darra.bind(varName, callback) — 绑定单变量

darra.bind('DB1.Temperature', (value, meta) => {
document.getElementById('temp').textContent = value.toFixed(2) + ' ℃'
})
参数说明
varName变量地址 (DB1.Temperature, M0.0, MW100)
callback(value, meta)值变化回调
meta.timestamp服务端采集时间 (ms since epoch)
meta.qualitygood / bad / cached
meta.type'number' / 'boolean' / 'string'

行为:

  • 订阅会自动发送到 Service; 断线重连后会重新订阅
  • 如果已有缓存值, 立即回调一次 (quality='cached')
  • 可多次 bind 同一变量, 每个 callback 都会触发

darra.bindDB(dbName, callback) — 绑定整个 DB

darra.bindDB('DB1', (values) => {
// values = { Temperature: 25.3, Pressure: 1.2, Level: 80.5, ... }
console.log('DB1 更新:', values)
})

整个 DB 块任一字段变化都触发一次 callback, 参数是增量字段的对象 (只含变化的字段)。

darra.bindGroup(vars[], callback) — 绑定多变量组

darra.bindGroup(['DB1.T1', 'DB1.T2', 'M0.0'], (values) => {
// values = { 'DB1.T1': 25, 'DB1.T2': 26, 'M0.0': true }
console.log('任一变化:', values)
})

任一变量变化时整组回调, values 包含所有组内变量的最新值 (不只是变化的)。

darra.write(varName, value) — 单变量写入

darra.write('M0.0', true)
darra.write('DB2.Setpoint', 75.5)
darra.write('MW100', 1234)

行为:

  • 发送到 Service, Service 校验 WriteWhitelist, 未通过返回 error 事件
  • 异步: 不等响应; 关心结果需要监听 error 事件
  • 写入后, 下一次推送会带新值, 绑定回调自动触发

darra.writeGroup(values) — 批量写入

darra.writeGroup({
'DB2.Setpoint': 75.5,
'M0.1': false,
'MW100': 1234
})

一次 WebSocket 帧完成所有写入, 性能优于多次 write

darra.get(varName) — 取当前缓存值

const t = darra.get('DB1.Temperature')
if (t !== undefined && t > 80) { /* 告警 */ }

从本地缓存读 (不走 WebSocket), 未订阅的变量返回 undefined

darra.describeDB(dbName) — 查询 DB 结构

const fields = await darra.describeDB('DB1')
// fields = [
// { name: 'Temperature', type: 'REAL', address: 'DB1.Temperature', comment: '反应釜温度' },
// { name: 'Pressure', type: 'REAL', address: 'DB1.Pressure', comment: '' },
// ...
// ]

发起 HTTP 请求 GET /api/hmi/db/DB1, 返回字段列表。常用于 <darra-dx-form> 自动生成表单。

darra.on(event, callback) — 事件监听

darra.on('ready',   ()    => console.log('WebSocket 已连接'))
darra.on('offline', () => console.warn('连接断开'))
darra.on('error', err => console.error(err))
darra.on('alarm', alarm => { /* {level,source,message,timestamp} */ })
darra.on('themeChanged', name => { /* 'modern' | 'industrial' */ })
事件名触发时机参数
readyWebSocket 首次连上 或 重连成功
offlineWebSocket 断开
error写入失败 / 订阅失败{ type, var, error }
alarmService 推送报警{ level, source, message, timestamp }
auth_ok登录成功{ user, role, token }
themeChangedsetTheme 调用后'modern' / 'industrial'

darra.setTheme / getTheme

darra.setTheme('industrial')
console.log(darra.getTheme()) // 'industrial'

切换 <body class="theme-industrial"> 并存入 localStorage['darra-hmi-theme']

WebSocket 连接生命周期

                      ┌───────────────────┐
│ 页面加载 │
└─────────┬─────────┘

darra.init() ← 由 Alpine x-init 自动触发

darra._autoConnect() → ws://host/ws

┌──────────────────────┴──────────────────┐
↓ ↓
ready 事件 error → offline
│ │
↓ ↓
重新订阅 _subscribedVars scheduleReconnect
│ │
↓ ↓
update 消息 3s 后重新 connect
↓ │
_handleUpdate ↓
↓ (循环)
触发 bind/bindDB/bindGroup 回调

关键点:

  • 断线后所有 bind/bindDB/bindGroup 不会丢失, 重连后自动重新订阅
  • 右下角 <div id="darra-conn-status"> 永远显示当前连接状态 (不可删除)
  • Service 默认 100ms 节流 (合并多个变量的更新为一条 update 消息)

写入白名单 (WriteWhitelist)

HMIProjectDef.WriteWhitelist 是 Service 端的硬安全门:

{
"WriteWhitelist": [
"M0.*", // 允许写 M0 字节所有位
"DB2.Setpoint*", // 允许写 DB2 下以 Setpoint 开头的字段
"MW200" // 精确允许
]
}

不在白名单的写入会被 Service 拒绝并返回:

{ "type": "write_ack", "ok": false, "var": "M100.0", "error": "not_in_whitelist" }

darra.on('error', ...) 会收到, 建议 UI 提示 "无权限写入"。

最佳实践

  • 能用控件就别手写 bind: <darra-numeric var="DB1.T">darra.bind('DB1.T', ...) 更简洁
  • 大批量用 bindGroup: 10+ 变量一起绑比 10 次 bind 效率高
  • 写入防抖: 滑块拖动时 debounce(300ms) 后再 write, 避免洪泛
  • 始终监听 offline: 断开时禁用所有写入按钮, 避免用户误以为写成功
  • 错误友好提示: darra.on('error', ...) 用 toast 展示而非静默 log
  • 描述 DB 不要频繁调: describeDB 是 HTTP, 放 Alpine init 里调一次, 结果缓存

排错表

现象原因排查
bind 回调不触发变量未订阅成功F12 Network → WS → 看 subscribe 帧是否发出
写入无效白名单未通过控制台看 error 事件, 检查 WriteWhitelist
右下角显示"断开"Service 未启动确认 18823 端口监听, 防火墙未拦
重连后值是老值缓存未失效Service 重启时应清 HMI 侧缓存, 按 Ctrl+F5
describeDB 404DB 名拼写错/api/hmi/db/DB1 试, 区分大小写

相关文档