SDK 加载方式
将 HTML 上传到“画面文件”并从运行菜单打开,Runtime 会自动加载 SDK,无需安装 npm 包或添加 script。调用一次 ready(),后续复用返回的 api。
JavaScript JavaScript 复制代码
HQVISU_CUSTOM_PAGE . ready (). then ( async ( api ) => {
// Use the examples below here. Reuse this api object.
}). catch ( error => console. error (error.message));
完成标志 原生 HTML 将代码放在 body 末尾的 <script> 中,页面元素放在前面。按需选取下文片段放进同一 async 回调,复用 api。独立 Vite 服务器、file:// 和其它网站不提供此运行环境。
声明页面使用的 Tag
把这段声明放进入口 HTML 的 head。将 <device-code>.<tag-code> 替换成实际完整编码;只读页面把 writableTags 改成 []。Vue 也写在 index.html 中。
HTML HTML 复制代码
< script >window. HQVISU_PAGE_META = { layoutMode: "native" };</ script >
< script type = "application/json" id = "hqvisu-custom-page" >
{
"readableTags": ["<device-code>.<tag-code>"],
"writableTags": ["<device-code>.<tag-code>"]
}
</ script >
完成标志 layoutMode: "native" 让普通 HTML/Vue 按窗口自适应布局。readableTags 是读范围,writableTags 是写范围;声明不会授予账号原本没有的控制权限。
读取一个 Tag
添加 <output id="value"></output>,用 ReadTagDouble 读数值,null 指定不可用时的返回值。整数、布尔和文本分别用 ReadTagInteger、ReadTagBoolean、ReadTagString。
JavaScript JavaScript 复制代码
const value = api. ReadTagDouble ( "<device-code>.<tag-code>" , null );
document. getElementById ( "value" ).textContent = value ?? "—" ;
完成标志 读取同步返回已采集的当前值,不会每次调用都去读 PLC。null 显示为“—”,不能当成真实 0。
值变化时自动刷新
在 HTML 放一个 <output id="value"></output>,用 subscribe 通知页面刷新。无需 setInterval 反复查询。
JavaScript JavaScript 复制代码
const tag = "<device-code>.<tag-code>" ;
function renderValue () {
const value = api. ReadTagDouble (tag, null );
document. getElementById ( "value" ).textContent = value ?? "—" ;
}
renderValue ();
const stop = api. subscribe ([tag], renderValue);
// Call stop() when this view is removed.
完成标志 首次进入立即显示当前值,后续变化自动更新。组件退出时调用 stop()。
点击按钮写入一次 Tag
添加 <button id="write" disabled>写入</button> 和 <pre id="result"></pre>。下面写入一次 12.5;换成项目允许的目标值。目标需声明为可写、启用 Tag 控制,并由具有 tag:write 权限的账号操作。
JavaScript JavaScript 复制代码
const button = document. getElementById ( "write" );
const output = document. getElementById ( "result" );
button.disabled = ! (api. supports ( "tag.write" ) && await api.auth. hasPermission ( "tag:write" ));
button. onclick = async () => {
if (button.disabled) return ;
const tag = "<device-code>.<tag-code>" ;
if ( ! api. ReadTagQualityGood (tag)) { output.textContent = "Tag unavailable" ; return ; }
if ( ! confirm ( "Write 12.5?" )) return ;
button.disabled = true ;
try {
const result = await api. WriteTagDouble (tag, 12.5 , { reason: "Operator setpoint update" });
const done = result?.accepted === true && result?.status === "done"
&& result?.outcomeUncertain !== true && result?.operatorVerificationRequired !== true
&& result?.transportOnly !== true ;
output.textContent = (done ? "Command completed; verify collected value: " : "Result unconfirmed; verify before another write: " )
+ JSON . stringify (result ?? null );
} catch (error) {
output.textContent = "Result unconfirmed: " + error.message + " " + (error.commandId || error.result?.commandId || "" );
}
// Reconcile the result before another write. Never retry automatically.
};
完成标志 整数、布尔用 WriteTagInteger、WriteTagBoolean。本例每页实例仅提交一次,随后禁用按钮;done 仍须核对后续正常采集值。刷新会重置按钮,下次操作前须核对上次结果;需跨刷新阻断时,保存待核对状态。
查询当前报警(一次)
在 HTML 添加 <div id="alarms"></div> 和 <p id="alarm-status"></p>。getActive 查询当前报警,renderTable 把 items 显示成表格。
JavaScript JavaScript 复制代码
const alarms = await api.alarms. getActive ({ limit: 100 });
api. renderTable ( "#alarms" , alarms.items);
document. getElementById ( "alarm-status" ).textContent = alarms.hasMore ? "More records available" : "" ;
完成标志 本例仅查询一次。实时列表用 alarms.subscribe 通知重新查询,不直接追加回调数据;退出时取消订阅。空 items 表示当前范围无报警,hasMore=true 表示仍有记录。其它设备或分组须配置报警范围。
查看最近一小时的历史报警
添加 <div id="alarm-history"></div> 和 <p id="history-status"></p>。历史报警用 alarms.queryHistory 查询,显示在独立表格中。
JavaScript JavaScript 复制代码
const alarmEnd = new Date ();
const alarmHistory = await api.alarms. queryHistory ({
start: new Date (alarmEnd. getTime () - 3600000 ). toISOString (),
end: alarmEnd. toISOString (),
limit: 100
});
api. renderTable ( "#alarm-history" , alarmHistory.items);
document. getElementById ( "history-status" ).textContent = alarmHistory.hasMore ? "More records available" : "" ;
查询趋势数据
先为 Tag 启用趋势归档,添加 <div id="trend"></div> 和 <p id="trend-status"></p>。下面把最近一小时的分钟平均值显示成表格;将同一份 points 交给图表组件即可画曲线。
JavaScript JavaScript 复制代码
const trendEnd = new Date ();
const trend = await api.history. query ({
tags: [ "<device-code>.<tag-code>" ],
start: new Date (trendEnd. getTime () - 3600000 ). toISOString (),
end: trendEnd. toISOString (), interval: "1m" , aggregate: "avg"
});
const points = (trend.items[ 0 ]?.points || []). map ( point => ({
time: point.t, value: point.quality === "good" ? point.value : null
}));
api. renderTable ( "#trend" , points);
document. getElementById ( "trend-status" ).textContent = trend.effectiveInterval;
完成标志 无归档时返回空数据;异常质量用 null 保留曲线缺口。effectiveInterval 是实际采样间隔。
先在“弹窗模板”保存模板,添加 <button id="open-popup">打开设备</button>,再绑定点击。替换设备编码、模板编码和已声明的 Tag。
JavaScript JavaScript 复制代码
document. getElementById ( "open-popup" ). onclick = () => {
try {
api.dialog. openDevicePopup ({
plc: "<device-code>" ,
templateCode: "<popup-template-code>" ,
focusTagNames: [ "<device-code>.<tag-code>" ]
});
} catch (error) { console. error (error.message); }
};
完成标志 弹窗中的显示、读写、报警和趋势由模板配置决定。调用返回不代表弹窗内写入已完成;实际核对弹窗与目标值。
跳转到其它页面
按目标选择下面一个调用,放在按钮的点击处理函数中。相对路径以当前画面为起点;在 assets 根内可以跳到上级、下级和其它目录。
JavaScript JavaScript 复制代码
// Use one of these calls in your button's click handler:
api.navigation. gotoPage ( "details/equipment.html" ); // Child page
api.navigation. gotoPage ( "../overview.html" ); // Parent page
api.navigation. gotoPage ( "/assets/<folder>/index.html" ); // Another folder
api.navigation. openUrl ( "https://<server-host>/manual" , { openInNewWindow: true });
跟随系统切换中英文
系统顶栏 LANG 已负责切换,不必再加语言按钮。给标题加 id="title",读取当前语言并订阅变化。
JavaScript JavaScript 复制代码
function renderText () {
const zh = api.language. current (). startsWith ( "zh" );
document. getElementById ( "title" ).textContent = zh ? "设备总览" : "Equipment overview" ;
}
renderText ();
const stopLanguage = api.language. onChange (renderText);
// Call stopLanguage() when this view is removed.
在 Vue 中使用同一套 API
Vue 3 项目在 index.html 中添加布局和 Tag 声明,再保存并挂载以下完整只读组件。onMounted 后取得 api;发布到 Runtime 后自动加载 SDK。
Vue Vue 复制代码
< script setup >
import { ref, onMounted, onUnmounted } from "vue" ;
const value = ref ( null ), error = ref ( "" );
let stop, disposed = false ;
onMounted ( async () => {
try {
const api = await window. HQVISU_CUSTOM_PAGE . ready ();
if (disposed) return ;
const tag = "<device-code>.<tag-code>" ;
const refresh = () => { value.value = api. ReadTagDouble (tag, null ); };
refresh ();
stop = api. subscribe ([tag], refresh);
} catch (err) { if ( ! disposed) error.value = err.message; }
});
onUnmounted (() => { disposed = true ; stop ?.(); });
</ script >
< template >
< p v-if = "error" >{{ error }}</ p >
< output v-else >{{ value ?? "—" }}</ output >
</ template >
完成标志 设置 Vite base: "./",按原目录上传 dist 内容。卸载只取消组件订阅,不调用 api.destroy()。本例 api 位于 onMounted 内;按钮调用须在组件作用域保存 api,见完整 Vue 写入示例。
完整 HTML:替换点位后发布
需要从零开始时复制这个完整只读页,替换两处 Tag 编码。它使用 Runtime 自动提供的 SDK。
展开完整源代码 HTML HTML 复制代码
<! doctype html >
< html lang = "en" >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< title >Mingcedar live value</ title >
< script type = "application/json" id = "hqvisu-custom-page" >
{"readableTags":["<device-code>.<process-tag-code>"],"writableTags":[]}
</ script >
< script >window. HQVISU_PAGE_META = { layoutMode: "native" };</ script >
< h1 >Live value</ h1 >
< p id = "status" role = "status" >Connecting…</ p >
< output id = "value" >Waiting for data</ output >
< script >
( async () => {
const tag = "<device-code>.<process-tag-code>" ;
const status = document. getElementById ( "status" );
const output = document. getElementById ( "value" );
const api = await HQVISU_CUSTOM_PAGE . ready ({timeoutMs: 10000 });
function refresh () {
const quality = api. ReadTagQuality (tag);
const value = api. ReadTagDouble (tag, null );
status.textContent = "Connected — quality: " + quality;
output.textContent = quality === "good" && value !== null
? String (value) : "Unavailable" ;
}
const stop = api. subscribe ([tag], refresh);
refresh ();
window. addEventListener ( "pagehide" , event => {
if ( ! event.persisted) { stop (); api. destroy (); }
});
})(). catch ( error => {
document. getElementById ( "status" ).textContent = error.message;
});
</ script >
</ html >
发布到运行页面
配置中心 → 画面文件:上传 HTML;Vue 上传构建后 dist 的内容并保留目录。
配置中心 → 菜单:选择“页面(直接打开 HTML)”,把入口指向刚上传的 HTML。
从运行菜单打开。先确认 Tag 值,再验证需要的操作;修改后覆盖发布并刷新。
常见问题与排查
HQVISU_CUSTOM_PAGE 未定义:确认从当前 Runtime 的运行菜单打开;独立网页不会自动提供 SDK。
读到 null:核对静态声明、完整 Tag 编码及数据质量。
写入不可用:核对 writableTags、账号权限和 Tag 控制标志;未知结果先核对,不重发。
其他数据类型和选项见 API 参考。
页面交付信息
开发和验收自写页面时,准备实际 Tag 编码与类型、允许的写入值、页面功能及目标运行菜单。入口 HTML 中的声明须与页面代码一致。
记录页面使用的 Tag、报警和历史范围;写入点同时明确账号权限、控制许可和设备允许值。
从运行菜单打开页面,核对 SDK 初始化、GOOD 质量、系统 LANG 切换和组件卸载后的订阅清理。
验证写入回执与后续正常采集值;结果未确认时保留命令标识,下一次操作前先核对。
缺少实际编码、类型或授权范围时,先补全配置再发布。