HMI 脚本 API
Darra HMI 在运行时向页面注入全局对象 window.Darra(定义在 /static/darra-plc.js),封装了 WebSocket 通信、变量订阅、页面导航、系统信息查询等全部运行时能力。所有自定义 JS 脚本必须通过 Darra.* 访问 PLC 数据,严禁直接操作 WebSocket。
Darra与darra是同一个全局对象。文档中Darra.xxx()与darra.xxx()等价。推荐使用大写Darra以区别于 Alpine.js 的$data约定。
一、变量读写 API
Darra.readVariable(varName) — 读取单变量
const value = Darra.readVariable('DB1.Temperature')
console.log(value) // 25.3
从本地缓存同步读取(不走 WebSocket),未订阅的变量返回 undefined。
| 参数 | 类型 | 说明 |
|---|---|---|
varName | string | PLC 变量地址,IEC 61131-3 语法 |
| 返回值 | 说明 |
|---|---|
number | boolean | string | undefined | 变量当前缓存值 |
Darra.writeVariable(varName, value, options?) — 写入单变量
// 基础写入
Darra.writeVariable('M0.0', true)
Darra.writeVariable('DB2.Setpoint', 75.5)
// 带选项
Darra.writeVariable('DB2.Setpoint', 80.0, {
reason: '操作员调整',
requireSignature: false
})
异步写入,不等待响应。关心结果请监听 error 事件。
| 参数 | 类型 | 说明 |
|---|---|---|
varName | string | 变量地址 |
value | number | boolean | string | 写入值 |
options.reason | string | 写入原因(审计日志用) |
options.requireSignature | boolean | 是否需要电子签名 |
行为:
- 发送到 Service,Service 校验
WriteWhitelist,未通过返回error事件 - 写入后下一次推送会带新值,绑定的回调自动触发
- 若
options.requireSignature === true且当前用户未签名,Service 会先弹电子签名对话框
Darra.writeGroup(values) — 批量写入
Darra.writeGroup({
'DB2.Setpoint': 75.5,
'M0.1': false,
'MW100': 1234
})
一次 WebSocket 帧完成所有写入,性能优于多次 writeVariable。返回值与单变量写入相同。
Darra.subscribeVariable(varName, callback) — 订阅变量变化
const unsubscribe = Darra.subscribeVariable('DB1.Temperature', (value, meta) => {
console.log(`温度: ${value} ℃, 质量: ${meta.quality}`)
})
// 取消订阅
unsubscribe()
| 参数 | 类型 | 说明 |
|---|---|---|
varName | string | 变量地址 |
callback(value, meta) | function | 值变化回调 |
meta 属性 | 类型 | 说明 |
|---|---|---|
meta.timestamp | number | 服务端采集时间 (ms since epoch) |
meta.quality | 'good' | 'bad' | 'cached' | 数据质量 |
meta.type | 'number' | 'boolean' | 'string' | 数据类型 |
行为:
- 订阅自动发送到 Service;断线重连后自动重新订阅
- 如果已有缓存值,立即回调一次(
quality='cached') - 返回
unsubscribe函数,调用后停止接收更新 - 同一变量可多次订阅,每个 callback 独立触发
Darra.subscribeGroup(vars, callback) — 订阅变量组
Darra.subscribeGroup(['DB1.T1', 'DB1.T2', 'M0.0'], (values) => {
// values = { 'DB1.T1': 25, 'DB1.T2': 26, 'M0.0': true }
console.log('任一变化:', values)
})
任一变量变化时整组回调。values 包含所有组内变量的最新值(不只是变化的)。
Darra.subscribeDB(dbName, callback) — 订阅整个 DB
Darra.subscribeDB('DB1', (values) => {
// values = { Temperature: 25.3, Pressure: 1.2, Level: 80.5 }
})
整个 DB 块任一字段变化触发一次 callback,参数是增量字段的对象(只含变化的字段)。
Darra.unsubscribeAll() — 取消所有订阅
Darra.unsubscribeAll()
清除当前页面的所有变量订阅。常用于页面销毁时的清理。
二、页面管理 API
Darra.navigateTo(route) — 页面导航
Darra.navigateTo('/reactor')
Darra.navigateTo('/alarm')
Darra.navigateTo('/') // 回到首页
切换到指定路由的 HMI 页面。
| 参数 | 类型 | 说明 |
|---|---|---|
route | string | 目标页面路由,如 /reactor |
行为:
- 向 Service 发送导航请求,Service 返回新页面的 HTML/CSS/JS
- 当前页面触发
pageLeave事件,新页面触发pageEnter事件 - 如果目标页面已加载过,默认走
Suspend/Resume而非重新渲染
Darra.showPopup(html, options?) — 显示弹出窗口
Darra.showPopup('<h2>确认停车?</h2><button onclick="Darra.closePopup(true)">确认</button>', {
width: 400,
height: 300,
modal: true,
title: '停车确认'
})
在当前页面之上弹出一个模态或非模态窗口。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
html | string | — | 弹出窗口的 HTML 内容 |
options.width | number | auto | 窗口宽度 (px) |
options.height | number | auto | 窗口高度 (px) |
options.modal | boolean | true | 是否模态(阻止背景交互) |
options.title | string | '' | 标题栏文字 |
options.closeOnEsc | boolean | true | 是否允许 ESC 关闭 |
options.onClose | function | — | 关闭时的回调 |
返回值:弹出窗口的引用,可用于编程关闭。
Darra.closePopup(result?) — 关闭弹出窗口
Darra.closePopup(true) // 关闭当前最上层弹出窗口
Darra.closePopup() // 不带返回值关闭
Darra.getCurrentRoute() — 获取当前路由
const route = Darra.getCurrentRoute()
console.log(route) // '/reactor'
三、系统 API
Darra.getSystemInfo() — 获取系统信息
const info = Darra.getSystemInfo()
// {
// "version": "1.0.5",
// "runtimeMode": "Production",
// "uptime": 3600,
// "projectName": "ReactorControl",
// "plcCycleTime": 5,
// "memoryUsage": 45,
// "cpuLoad": 32
// }
| 返回值 | 类型 | 说明 |
|---|---|---|
version | string | Service 版本号 |
runtimeMode | 'Dev' | 'Test' | 'Production' | 当前运行模式 |
uptime | number | Service 已运行秒数 |
projectName | string | 当前加载的项目名称 |
plcCycleTime | number | PLC 扫描周期 (ms) |
memoryUsage | number | 内存使用百分比 |
cpuLoad | number | CPU 负载百分比 |
Darra.getAlarms(filter?) — 获取报警列表
// 获取所有报警
const allAlarms = Darra.getAlarms()
// 获取未确认的报警
const activeAlarms = Darra.getAlarms({ acknowledged: false })
// 按严重度筛选
const criticalAlarms = Darra.getAlarms({ severity: 'Critical' })
| 参数 | 类型 | 说明 |
|---|---|---|
filter.acknowledged | boolean | true 只返回已确认,false 只返回未确认 |
filter.severity | string | 'Critical' | 'Warning' | 'Info' |
filter.source | string | 按来源筛选(如 'DB1') |
filter.limit | number | 最大返回条数,默认 100 |
| 返回值 | 说明 |
|---|---|
Array<{ id, severity, source, message, timestamp, acknowledged, ackUser }> | 报警数组 |
Darra.acknowledgeAlarm(alarmId, reason?) — 确认报警
Darra.acknowledgeAlarm('alarm-001', '已确认,安排处理')
| 参数 | 类型 | 说明 |
|---|---|---|
alarmId | string | 报警 ID |
reason | string | 确认原因(可选,审计用) |
Darra.getUserInfo() — 获取当前用户信息
const user = Darra.getUserInfo()
// {
// "username": "wang",
// "role": "engineer",
// "displayName": "王工",
// "permissions": ["read", "write", "recipe", "alarm_ack"]
// }
| 返回值 | 类型 | 说明 |
|---|---|---|
username | string | 登录用户名 |
role | 'admin' | 'engineer' | 'operator' | 'viewer' | 当前角色 |
displayName | string | 显示名称 |
permissions | string[] | 权限列表 |
Darra.logout() — 登出
Darra.logout()
清除当前登录会话,页面跳转到登录页。
四、数据绑定 API
Darra.bindElement(element, varName) — 绑定 DOM 元素
const el = document.getElementById('temp-display')
Darra.bindElement(el, 'DB1.Temperature')
// 元素 textContent 会自动更新为变量值
将 DOM 元素的 textContent 自动绑定到 PLC 变量。元素值随变量更新自动变化,无需手动写回调。
| 参数 | 类型 | 说明 |
|---|---|---|
element | HTMLElement | 要绑定的 DOM 元素 |
varName | string | 变量地址 |
Darra.bindElementAttribute(element, varName, attribute) — 绑定元素属性
const led = document.getElementById('status-led')
Darra.bindElementAttribute(led, 'M0.0', 'class')
// M0.0 = true → led.className = 'on'
// M0.0 = false → led.className = 'off'
将 DOM 元素的任意属性绑定到 PLC 变量。
| 参数 | 类型 | 说明 |
|---|---|---|
element | HTMLElement | 要绑定的 DOM 元素 |
varName | string | 变量地址 |
attribute | string | 要更新的属性名(如 'class', 'style', 'src') |
变量值到属性值的映射:
boolean→ 自动转为'true'/'false'number→ 自动转为字符串string→ 直接使用
Darra.bindElementStyle(element, varName, cssProperty, thresholds?) — 绑定样式
const bar = document.getElementById('level-bar')
Darra.bindElementStyle(bar, 'DB1.Level', 'width', {
min: 0,
max: 100,
unit: '%'
})
// DB1.Level = 75 → bar.style.width = '75%'
带阈值映射的样式绑定。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
element | HTMLElement | — | DOM 元素 |
varName | string | — | 变量地址 |
cssProperty | string | — | CSS 属性名(如 'width', 'opacity', 'background') |
thresholds.min | number | 0 | 变量最小值 |
thresholds.max | number | 100 | 变量最大值 |
thresholds.unit | string | '' | 单位后缀(如 '%', 'px') |
Darra.unbindElement(element) — 解除元素绑定
Darra.unbindElement(document.getElementById('temp-display'))
解除之前通过 bindElement / bindElementAttribute / bindElementStyle 建立的绑定。
五、事件与生命周期 API
Darra.on(event, callback) — 事件监听
Darra.on('ready', () => console.log('WebSocket 已连接'))
Darra.on('offline', () => console.warn('连接断开'))
Darra.on('error', (err) => console.error(err.type, err.var, err.error))
Darra.on('alarm', (alarm) => {
if (alarm.severity === 'Critical') {
showAlarmPopup(alarm)
}
})
| 事件名 | 触发时机 | 参数 |
|---|---|---|
ready | WebSocket 首次连上或重连成功 | — |
offline | WebSocket 断开 | — |
error | 写入失败 / 订阅失败 | { type, var, error } |
alarm | Service 推送报警 | { id, severity, source, message, timestamp } |
auth_ok | 登录成功 | { user, role, token } |
pageEnter | 页面激活(首次加载或 Resume) | { route, prevRoute } |
pageLeave | 页面离开(被 Suspend 或关闭) | { route, nextRoute } |
themeChanged | setTheme 调用后 | 'modern' | 'industrial' |
connectionQuality | 连接质量变化 | { rtt, quality } |
Darra.off(event, callback) — 移除事件监听
const handler = () => console.log('ready')
Darra.on('ready', handler)
// ...
Darra.off('ready', handler)
Darra.getConnectionStatus() — 获取连接状态
const status = Darra.getConnectionStatus()
// 'connected' | 'connecting' | 'offline' | 'reconnecting'
六、主题 API
Darra.setTheme(name) — 切换主题
Darra.setTheme('industrial')
Darra.setTheme('brandRed') // 自定义皮肤
切换 <body class="theme-xxx"> 并存入 localStorage['darra-hmi-theme']。详见主题系统。
Darra.getTheme() — 获取当前主题
const current = Darra.getTheme()
console.log(current) // 'industrial'
七、工具函数
Darra.formatNumber(value, format) — 数字格式化
Darra.formatNumber(25.333, '0.00') // '25.33'
Darra.formatNumber(1234.5, '#,##0') // '1,235'
Darra.formatNumber(0.85, '0%') // '85%'
底层使用 Chart.js 的格式化引擎,支持所有 Chart.js 格式模式。
Darra.formatTimestamp(ms, format?) — 时间戳格式化
Darra.formatTimestamp(Date.now()) // '2026-07-26 14:30:00'
Darra.formatTimestamp(Date.now(), 'HH:mm:ss') // '14:30:00'
Darra.formatTimestamp(Date.now(), 'MM-dd') // '07-26'
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ms | number | — | 毫秒时间戳 |
format | string | 'YYYY-MM-DD HH:mm:ss' | 输出格式 |
Darra.showToast(message, options?) — 显示 Toast 通知
Darra.showToast('配方加载成功', {
type: 'success', // 'success' | 'warning' | 'error' | 'info'
duration: 3000, // 自动关闭时间 (ms),0 为不自动关闭
position: 'bottom-right' // 'top' | 'bottom' | 'top-right' | 'bottom-right'
})
在页面角落显示短暂的通知消息,不打断用户操作流。适用于操作反馈而非报警。
八、完整 API 函数签名表
| 函数 | 签名 | 说明 |
|---|---|---|
readVariable | (varName: string) => value | 读取缓存值 |
writeVariable | (varName: string, value, options?) => void | 写入变量 |
writeGroup | (values: Record<string, any>) => void | 批量写入 |
subscribeVariable | (varName: string, cb: (value, meta) => void) => unsubscribe | 订阅变量 |
subscribeGroup | (vars: string[], cb: (values) => void) => unsubscribe | 订阅变量组 |
subscribeDB | (dbName: string, cb: (values) => void) => unsubscribe | 订阅 DB |
unsubscribeAll | () => void | 取消所有订阅 |
navigateTo | (route: string) => void | 页面导航 |
showPopup | (html: string, options?) => popupRef | 显示弹出窗口 |
closePopup | (result?: any) => void | 关闭弹出窗口 |
getCurrentRoute | () => string | 获取当前路由 |
getSystemInfo | () => SystemInfo | 获取系统信息 |
getAlarms | (filter?: AlarmFilter) => Alarm[] | 获取报警列表 |
acknowledgeAlarm | (alarmId: string, reason?: string) => void | 确认报警 |
getUserInfo | () => UserInfo | 获取当前用户 |
logout | () => void | 登出 |
bindElement | (el: HTMLElement, varName: string) => void | 绑定 DOM 元素 |
bindElementAttribute | (el: HTMLElement, varName: string, attr: string) => void | 绑定元素属性 |
bindElementStyle | (el: HTMLElement, varName: string, prop: string, thresholds?) => void | 绑定样式 |
unbindElement | (el: HTMLElement) => void | 解除元素绑定 |
on | (event: string, cb: Function) => void | 事件监听 |
off | (event: string, cb: Function) => void | 移除监听 |
getConnectionStatus | () => string | 获取连接状态 |
setTheme | (name: string) => void | 切换主题 |
getTheme | () => string | 获取当前主题 |
formatNumber | (value: number, format: string) => string | 数字格式化 |
formatTimestamp | (ms: number, format?: string) => string | 时间戳格式化 |
showToast | (message: string, options?) => void | Toast 通知 |
最佳实践
- 优先使用
<darra-*>控件:控件内置了bind/write/ 主题适配,比手写 API 更简洁 - 页面销毁时清理订阅:在
pageLeave事件中调用unsubscribeAll(),防止内存泄漏 - 写入操作加防抖:滑块拖动时
debounce(300ms)后再调用writeVariable,避免洪泛 - 始终监听
offline:断开时禁用所有写入按钮,避免用户误以为写成功 bindElementStyle配合 CSS transition:给绑定元素加transition: width 0.3s即可实现平滑动画- 错误友好提示:
on('error', ...)用showToast展示而非静默 log
排错表
| 现象 | 原因 | 排查 |
|---|---|---|
subscribeVariable 回调不触发 | 变量未订阅成功 | F12 Network → WS → 看 subscribe 帧是否发出 |
| 写入无效 | 白名单未通过 | 监听 error 事件,检查 WriteWhitelist |
getAlarms 返回空 | 当前无报警或筛选条件过严 | 去掉 filter 参数重试 |
| 页面导航后 JS 状态丢失 | 未在 pageEnter 中恢复状态 | 将初始化逻辑放在 pageEnter 而非页面加载时 |
bindElementStyle 不生效 | CSS 属性名拼写错误 | 检查 CSS 属性名(backgroundColor 而非 background-color) |