同UCanCode一起释放 Visual C++, C# 的巨大能量!
UC UCanCodeSCADA / DCS / HMI
第 4 章

Web WASM 部署方案

ucchmiserver 数据网关 + WebPlayer 浏览器运行站:零安装、零插件、同一内核

Web 运行站解决的是工业软件交付环节中一个长期痛点:客户端部署。传统方案要么安装客户端软件,要么安装浏览器插件(ActiveX/NPAPI),在浏览器持续收紧安全策略的今天越来越难以为继。本平台的做法是把同一套运行内核编译为 WebAssembly,在标准浏览器中运行完整的运行站,并通过 ucchmiserver 网关收敛全部工业通信。

4.1 总体架构与设计约束

图 12  Web WASM 运行站(WebPlayer)运行时结构
图 12 Web WASM 运行站(WebPlayer)运行时结构

Web 运行站由两个角色组成:浏览器内的 WebPlayer(WASM 运行站)与服务端的 ucchmiserver(数据网关)。二者职责边界非常清晰:

角色职责明确不做的事
WebPlayer(WASM)画面渲染与动画、实时/历史曲线、报警与 SOE 查询与呈现、报表、3D 渲染、脚本(受限沙箱)、工程本地缓存、运维 HUD不打开任何工业协议 socket;不持有本地权威数据库;不做设备采集
ucchmiserverWebSocket/WSS 服务、实时数据订阅与轮询、站级 Historian 只读查询代理、报警命令转发、工程与媒体分发、客户端在线名册与运维接口不替代运行站做实时数据评估;不产生独立的报警权威数据

4.1.1 三条核心设计约束

  • 浏览器不是可信环境。 因此所有工业通信、协议解析、权限判定都在服务端完成;浏览器只接收已授权的数据,且脚本执行与危险方法调用均受白名单限制。
  • WebAssembly 内存不能无限增长。 WebPlayer 使用固定的 512 MiB 线性堆(ALLOW_MEMORY_GROWTH=0),所有缓存、队列、缓冲都必须有明确预算与回收策略。
  • 浏览器不能阻塞。 任何同步等待(例如历史查询)都必须走异步回调,禁止嵌套事件循环,否则会冻结整个运行时。
  • 注
    这三条约束不是「Web 版的妥协」,而是把 Web 端当作一个独立形态的运行站来认真设计的结果。也正因为如此,WebPlayer 提供的不是「看板」,而是完整的运行站功能集。

    4.2 ucchmiserver 数据网关

    ucchmiserver 是纯服务端组件,提供数据网关、工程分发、运维控制与数据源集成四类能力,并以系统托盘程序的形式常驻运行,提供主界面、工具栏服务控制、事件日志、Web 客户端在线名册与运行状态提示。

    4.2.1 四类能力

    能力组成说明
    数据网关WebSocket / WSS 服务(默认端口 8030)、UDP 接收(实时 45452 / 告警 45454)、实时数据取数(本地或远程实时库)、Historian 只读桥(默认 9100)把站端实时与历史数据以统一帧协议提供给浏览器端
    工程分发HTTP 测试与分发服务、工程目录与资源目录管理、可选 SHA256 清单校验让浏览器端无需安装即可获取工程文件与媒体资源
    运维控制实时库服务启停与配置对话框、实时库运维控制台、实时库诊断面板、事件日志查看、HTTP 测试服务开关把实时库与网关的运维动作可视化,降低现场维护门槛
    数据源集成SQLite / MySQL 数据源、MQTT 消息总线(自带 MQTT 源码编译)、配置持久化(Windows GBK / 信创 Linux UTF-8)与既有信息系统和历史数据源对接

    4.2.2 实时库服务与远程取数

    网关对实时库的支持有两种形态:本地进程内服务与远程客户端连接。网关可作为实时库的客户端连接到站端运行站的实时库(默认端口 6389),并在启动初期自动补连,保证首批实时请求不被丢弃。

  • 8990 通道(远程实时 JSON):通过实时库 MGET 批量取键值,返回 JSON 帧,适合变量数量中等、需要可读性的场景。
  • 8989 通道(紧凑二进制):远程 rt/<id> 键值以紧凑二进制打包(可选压缩),适合高频、大批量、带宽敏感的场景。
  • 连接独占与防饿死:同一连接的取数请求不会互相排队等待,而是保留「每连接最新一次轮询」(8989 优先于 8990),锁释放后立即投递最新结果。
  • 线程池异步:取数在工作线程完成,结果再回到主线程投递 WebSocket 帧,保证网关自身的响应性。
  • 4.2.3 站级 Historian 桥

    历史数据的权威源是站级 Historian,Web 端不存在本地历史库回退。网关提供只读桥接,查询失败时显式返回 remote_fail:* 错误,而不是返回空结果——这一点对工业场景至关重要:「没有数据」和「查询失败」必须是两种不同的状态。

    帧类型对应业务说明
    'b' (8970)历史日窗曲线按分辨率(小时/分钟/曲线)查询指定日期的曲线数据
    'c' (8971)历史报警分页按时间范围、确认状态、等级、区域、点名过滤分页查询
    'd' (8972)SOE / 历史事件按日期与关键字查询事件顺序记录
    8973站级命令转发报警确认、全确认、重同步、搁置等操作下发站端执行
    8974查询游标取消超时或发起新查询时取消网关侧游标,cursorId=0 表示最近一次
    8975运维指标快照获取网关与站端运维指标
    8976通道诊断快照获取站端运行站写入的通道诊断 JSON
    注
    游标与取消机制是 Web 历史查询的关键:浏览器端可能在用户快速翻页或切换画面时发起新查询。若不取消旧查询,过期结果可能覆盖新结果,造成「曲线显示错位」。平台通过游标 ID + owner 归属实现精确取消——例如报表刷新不会打断曲线或报警的查询。

    4.3 WebPlayer 的 WebAssembly 技术实现

    4.3.1 构建配置

    构建要素配置原因
    目标平台Emscripten / WebAssembly,定义 HMI_WASM_BUILD同一份源码条件编译,避免维护两套代码
    模块裁剪剔除 sql、concurrent、serialbus 模块浏览器内无本地数据库、无线程与串口总线
    语言标准C++11 / gnu++11 构建标志匹配 Emscripten 工具链与 Qt for WebAssembly 的约束
    JS 互操作Embind(wasmbridge.cpp)把 C++ 运行站能力以受控接口暴露给宿主 JS
    虚拟文件系统-lidbfs.js(IDBFS,基于 IndexedDB)持久化工程文件与运行缓存,实现二次冷启动加速
    内存模型固定 512 MiB 线性堆 + ALLOW_MEMORY_GROWTH=0避免内存无限增长导致浏览器标签页崩溃,强制资源预算化
    同步语义Asyncify(用于必须同步的既有代码路径)在不重写全部业务代码的前提下支持同步调用语义
    工程预载预载 RunApp 运行文件减少首次加载的往返次数

    4.3.2 内存治理:HmiMem 框架

    浏览器端的内存治理是本方案最具工程含量的部分。平台提供统一的 HmiMem 内存策略层,把「桌面宽松、Web 严格」的差异收敛为一组编译期常量,并在运行期以周期性整理(housekeep)+ 压力分级(Normal/High/Critical)两级机制执行回收。

    图 13  桌面运行站与 Web(WASM) 运行站的资源预算对比
    图 13 桌面运行站与 Web(WASM) 运行站的资源预算对比
    预算项桌面运行站Web 运行站回收策略
    线性堆上限不限制(操作系统管理)512 MiB(固定)压力分级 + 强制回收
    实时曲线最大点数100 0004 000隐藏页面丢弃实时缓冲
    历史曲线最大点数20 0008 000隐藏页面释放历史曲线缓冲
    历史报警单页行数5 000500分页拉取
    SOE 查询上限10 0002 000结果集上限
    页面缓存数162Critical 时逐出隐藏页面缓存
    脚本缓存条目25648超限淘汰
    媒体缓存64 MiB8 MiB按键淘汰最旧条目
    查询缓存16 MiB4 MiB按键淘汰最旧条目
    解码图片条目12824LRU 淘汰
    Pixmap 缓存100 000 KB8 192 KB按节拍刷新,High 压力时立即刷新
    事件持久化(JSONL)开启关闭避免写放大
    隐藏曲线采样开启关闭隐藏页面不做无谓计算
    注
    分级回收策略:HmiMem::housekeep() 以固定周期(桌面 30 s / Web 15 s)执行;隐藏页面释放历史/3D/GIF/实时缓冲,可见页面压缩曲线点数并触发脚本 GC;Pixmap 缓存按节拍刷新;进入 Critical 压力时进一步逐出隐藏页面与图片缓存。整个过程在 UI 线程安全执行,不产生阻塞。

    4.3.3 二次冷启动与持久化

  • IDBFS 持久化:工程文件、媒体资源与运行缓存写入浏览器持久化文件系统。
  • Service Worker:承担离线缓存与二次冷启动的快速装载,使第二次进入接近秒级。
  • 工程同步(RunApp 同步):启动阶段同步 HTTP 服务端的分发目录,先取 manifest.json,再按清单拉取各文件;支持可选 SHA256 完整性校验;同步过程向宿主 JS 报告运行阶段(便于 HUD 显示进度与错误)。
  • 失败可解释:同步失败、校验失败、网络失败均以明确的阶段与原因上报,而不是停留在白屏。
  • 4.4 通信协议与帧字典

    图 14  ucchmiserver 与 WebPlayer 通信协议栈及二进制帧字典
    图 14 ucchmiserver 与 WebPlayer 通信协议栈及二进制帧字典

    WebSocket 通道分为文本控制面与二进制数据面两部分:

    平面内容用途
    文本帧(控制面)心跳(heartbeat)、会话握手(session hello)、运行状态(runtime status)、运维 HUD 指令(HMIOPS1/HMISESS1)连接保活、会话建立、状态上报与运维交互
    二进制帧(数据面)首字节为类型标识,其后为负载实时数据、历史曲线、报警、SOE、工程与媒体文件、SQL 结果等

    表 4-1 二进制帧类型字典

    类型字节业务语义说明
    '1'8998 变量/标签数据标签数据下行
    '2'UDP 实时桥接 / 二进制回显运行站 UDP 实时帧转发给浏览器;也用于回显
    '3'9000 工程文件 / 1102 媒体二进制工程与媒体的大块二进制传输
    '4'8997 媒体 JSON媒体资源元数据
    '5'1103 文件数据 JSON文件数据描述
    '6'1101 画面文件包画面定义的打包传输
    '7'告警 UDP 帧告警事件转发
    '8'8990 远程实时 JSON实时库 MGET 结果
    '9'8996 SQL JSONSQL 查询结果的兼容帧
    'a'8989 远程实时紧凑二进制高频批量实时数据(可选压缩)
    'b'8970 历史日窗曲线历史曲线查询响应
    'c'8971 历史报警分页报警历史查询响应
    'd'8972 SOE / 历史事件SOE 查询响应
    注
    帧上限:桌面形态 32 MiB、Web 形态 8 MiB;待发送二进制缓冲桌面 16 MiB、Web 4 MiB。超限的连接会被明确拒绝而不是静默截断,避免「数据看起来传过去了但内容是残缺的」这类难以定位的问题。

    4.5 安全模型

    Web 端直接暴露在 IT 网络中,安全设计的核心思路是「收敛能力 + 显式授权 + 可验证完整性」。

    安全层面机制默认值
    传输安全WS / WSS(TLS),支持配置证书、私钥与 CA;对端校验策略可选 none / query / require对端校验可选,生产环境建议 require
    宿主脚本任意脚本执行默认关闭,仅可通过接口在操作员登录后开启;实验环境可通过编译开关 HMI_ALLOW_HOST_SCRIPT=1 放开关闭
    危险方法调用myObjInvoke / 宿主路径调用受方法白名单限制白名单机制
    写入操作写操作要求操作员已登录(可配置为强制)强制登录
    工程完整性工程清单 SHA256 校验;未提供期望哈希时跳过校验支持且可强制
    数据平面浏览器端不打开工业协议 socket,所有协议通信经网关代理架构保证
    历史数据权威性历史/报警数据一律回源站级 Historian,不提供本地回退架构保证
    运维可见提供安全策略 JSON 查询接口与运维 HUD,策略状态对运维透明内置
    注
    「默认关闭」原则:宿主脚本、危险方法、写入操作三者的默认状态都是「关闭或受限」,必须显式开启。这与工业现场的安全要求一致——默认配置应当是安全的,而不是需要额外加固才安全的。

    4.6 部署实施

    图 15  典型部署拓扑与端口规划
    图 15 典型部署拓扑与端口规划

    4.6.1 部署步骤

  • 部署站端运行站:按第 3 章的运行站部署方式安装 HMIPlayer,确认实时库、时序库、报警与采集正常运行。
  • 部署网关:在服务区/DMZ 主机上部署 ucchmiserver;配置数据源(SQLite/MySQL)、实时库端点(本地或远程 6389)、站级 Historian 端点(默认 9100)、WebSocket 监听地址与端口(默认 8030)、WSS 证书与对端校验策略。
  • 配置工程分发:把工程发布目录配置为 HTTP 分发根目录,启用可选的 SHA256 清单校验。
  • 部署 WebPlayer 静态资源:把 WASM 构建产物(.js / .wasm / .data 与静态页面)部署到 Web 服务器,或由网关的 HTTP 服务直接分发。
  • 网络与防火墙:按端口清单放行(见下表);OT/IT 之间建议单向或白名单方式隔离。
  • 验收:浏览器访问 → 观察工程同步进度 → 进入画面 → 验证实时刷新、历史曲线、报警查询、报警确认、报表导出与 3D 渲染。
  • 运维接入:配置运维 HUD 与指标抓取(运维指标快照 8975、通道诊断快照 8976),纳入既有监控体系。
  • 4.6.2 端口与协议清单

    表 4-2 默认端口与用途

    端口协议用途方向
    8030WebSocket / WSS网关数据服务(可配置)浏览器 → 网关
    80 / 8080HTTP工程与媒体分发、HTTP 测试服务浏览器 → 网关
    9100TCP(CTSP)站级 Historian 只读查询桥网关 → 站端
    6389TCP(RESP 兼容)实时库命令服务与复制内部 / 网关 → 运行站
    45452UDP实时数据转发站端 → 网关
    45454UDP告警转发站端 → 网关
    1883 / 8883MQTT / MQTTS消息总线(可选)双向
    4840OPC UA(opc.tcp)OPC UA 服务端(可选)双向
    —设备协议端口Modbus TCP、S7、IEC104、DNP3 等(按现场配置)运行站 → 现场设备

    4.6.3 运行与升级

  • 工程热更新:工程变更后无需重新安装任何客户端,浏览器刷新或按提示重载即可获得新版本;SHA256 校验保证新旧版本混用不会产生静默错误。
  • 网关服务控制:主界面工具栏可一键启停网络服务、实时库服务,并可查看运行状态提示与事件日志。
  • 在线名册:网关维护 Web 客户端在线名册(可只看在线),并记录历史接入端点,便于运维掌握终端分布。
  • 诊断:运行时状态、事件、崩溃报告、安全策略、宿主脚本控制均通过对外 API 暴露,可接入第三方运维平台。
  • 4.6.4 容量与带宽估算方法

    Web 运行站与桌面运行站的最大差别是资源边界是硬的:浏览器侧是固定的 512 MiB 线性堆,网关侧是有限的连接与线程预算。因此容量规划不能靠「先上再看」,而应在设计阶段按下面的口径估算。

    容量维度估算方法 / 口径工程建议
    下行带宽带宽 ≈ 可见测点数 × 变化率 × 单点负载字节 × 刷新频率;紧凑二进制帧的单点负载约 8~16 B(含质量码与时标),文本 JSON 帧约为其 2~4 倍优先让网关使用紧凑二进制通道(8989);文本通道(8990)用于低频或调试场景
    网关并发单会话在 1 Hz 刷新、数千可见点下约为数十 KB/s 量级;取数经线程池异步执行并按连接独占排队按实测压测确定单网关会话上限;超出后横向扩展网关或按区域划分
    单画面测点画面内可见测点全部需要订阅与刷新建议单画面可见测点控制在数千级;用弹出画面、分组刷新与死区降低瞬时负载
    曲线点数实时曲线上限 4 000 点,历史曲线上限 8 000 点(代码常量)长区间查询用抽点间隔,避免在浏览器侧做超大点数渲染
    历史查询日窗查询支持小时/分钟/曲线粒度;跨日区间用抽点避免「大区间 + 高密度」同时出现;批量取数用异步并支持取消
    浏览器内存固定 512 MiB;页面缓存 2 个、媒体缓存 8 MiB、报警队列 64、待发缓冲 4 MiB页面与 3D 场景按预算设计;隐藏页面会被回收,不应把状态寄托在隐藏页面上
    3D 场景模型与纹理计入 8 MiB 媒体预算建议单画面一个 3D 视图;Web 端优先使用低多边形模型与压缩纹理
    工程体积工程文件经 HTTP 分发并缓存于 IDBFS拆分工程资源、按需加载;启用 SHA256 校验以保证版本一致
    注
    口径声明:上表给出的是估算方法与设计边界,不是性能承诺;实际容量必须在目标硬件与目标工程规模上压测确认。好在平台把关键边界都做成了可读的代码常量与可统计的指标,压测结果可以直接与常量对照,而不是靠猜。

    4.7 技术指标

    表 4-3 Web 运行站关键指标

    类别指标数值 / 说明
    运行时内存模型固定 512 MiB 线性堆,ALLOW_MEMORY_GROWTH=0
    持久化IDBFS(IndexedDB)+ Service Worker 二次冷启动
    工程装载HTTP manifest + 分文件同步 + 可选 SHA256 校验
    渲染画面矢量图元 + 动画 + 多页面/弹出页面(并发上限按预算控制)
    实时曲线最大 4 000 点
    历史曲线最大 8 000 点
    3D支持(assimp 模型 + 媒体缓存 8 MiB)
    数据实时数据文本 JSON 帧(8990)与紧凑二进制帧(8989)双通道
    历史查询日窗(小时/分钟/曲线)+ 跨日区间 + 抽点间隔;异步 + 可取消
    报警历史报警分页(单页 500 行)、SOE(上限 2 000 条)、确认/全确认/搁置/重同步
    报表周期报表经时序库/Historian 通道(8970)
    通信帧上限单帧 8 MiB;待发缓冲 4 MiB
    保活文本帧心跳 + 会话握手 + 运行状态上报
    重连带退避策略的重连,会话恢复后按需重同步
    安全传输WS/WSS + 证书 + 对端校验策略
    脚本宿主脚本默认关闭、方法白名单、写操作要求登录
    完整性工程清单 SHA256 校验
    运维接口运行状态、事件、崩溃报告、安全策略查询
    指标运维指标快照(8975)、通道诊断快照(8976)
    兼容浏览器Chrome / Edge / Firefox 及主流国产浏览器(需支持 WASM)
    运行看护浏览器侧无本地自愈能力(浏览器沙箱不允许);运行健康由站端 HMICare 统一负责(见第 5 章)
    网关侧网关作为普通 Windows/Linux 服务进程,可纳入 HMICare 或既有 IT 监控
    对外可见运行状态、事件、崩溃报告与运维指标快照对外暴露,便于统一采集

    4.8 适应领域

    场景为什么适合 Web 运行站
    集团/厂级调度大屏免安装、集中发布、任意终端打开,运维成本最低
    工程师与管理人员远程查看不受办公终端操作系统与权限限制
    多终端分散监视车间、值班室、移动终端统一入口,无需逐台安装
    巡检与移动作业平板/手机浏览器直接访问,支持响应式画面
    演示与培训环境临时电脑、会议室设备即可作为运行站使用
    承包商/第三方协同授予只读或受限权限,不暴露工程与站端网络
    边缘/无人值守站点站端运行 + 远程 Web 监视,减少现场值守成本
    项目交付与验收业主无需安装任何软件即可验收,缩短交付周期
    国产化办公终端国产 OS + 国产浏览器环境下无需额外适配客户端

    4.9 技术领先性对比

    对比项传统 Web 方案本方案(WASM 运行站)
    技术路线服务端渲染 HTML 看板;或 ActiveX / NPAPI 插件;或远程桌面推送同一 C++ 运行内核编译为 WebAssembly,浏览器内原生执行
    功能完整性通常是桌面功能的子集(曲线、报警、报表常被简化或缺失)完整运行站:曲线、报警、SOE、报表、3D、脚本(受限沙箱)
    插件依赖需要安装插件或在特定浏览器版本运行标准浏览器直接运行,无插件、无本地安装
    与桌面的一致性两套代码两套行为,需分别验证同一份工程、同一套内核,行为一致(仅按内存预算裁剪上限值)
    安全模型插件通常拥有较高本地权限,风险面大浏览器沙箱 + 服务端代理通信 + 默认关闭宿主脚本 + 方法白名单
    内存可控性前端长跑易内存泄漏,只能靠刷新页面缓解固定 512 MiB 堆 + 显式预算 + 分级回收,支持长时间稳定运行
    历史数据权威性部分方案在浏览器侧缓存历史,产生「双源」歧义一律回源站级 Historian,失败显式报错,不静默返回空
    部署成本需为每个终端安装/配置,版本升级需逐台处理零安装、集中发布、刷新即升级
    可观测性前端黑盒,问题难定位运行状态/事件/崩溃报告/安全策略/运维指标对外可见
    运行看护浏览器端无法自愈,且缺少「数据是否还在更新」的功能级判定浏览器侧明确不做本地自愈;运行与采集的健康由站端 HMICare 统一看护(含历史库静默停写、通道设备在线率判定)
    注
    与其他 Web 化路线的本质区别:多数 SCADA 的 Web 化是重写一个前端,结果是「两套产品、功能不等、验证翻倍」。本方案是把运行内核搬到浏览器,结果是「一套工程、两种形态、行为一致」。代价是必须认真解决内存、异步与安全三个难题——这正是本章 §4.3/§4.5/§4.7 的内容。