简介:在工业数字化领域,AVEVA System Platform作为主流集成平台,其界面定制与开发教程面向工业软件实施工程师与二次开发人员,帮助读者掌握从架构认知、仪表板设计到自定义应用及系统集成的完整路径。资源包内含1个docx文档,大小约40KB,便于随时查阅和传播;文档系统梳理了平台主要组件、数据模型与统一框架,并结合实际案例说明定制前的准备、常用界面控件使用以及Python与PI服务器交互的代码示例。对于需要为AVEVA平台做界面优化、业务看板开发或外部系统对接的读者,可直接对照学习。目前已有86人学习下载,适合正在接触AVEVA系统平台、希望快速建立定制开发思路的初中级工业软件工程师。全文结构清晰,从基础概念到实践步骤循序渐进,能有效缩短上手时间并降低试错成本。
1. AVEVA系统平台界面定制到底在做什么:别再硬编码你的HMI
前阵子一个朋友的项目快验收了,甲方提了个不算过分的需求:某个设备状态框要从红色改成黄色,异常时再闪三下。他在 InTouch 画面里找到那个图元,改完动画链接,保存、发布、重启 ViewApp,结果第二天一看,换了另一个设备实例,颜色又不对了。问题出在他改的是“这一张画面”,而系统里跑的是同一套模板下的上百个对象实例。
AVEVA系统平台界面定制要解决的,就是把界面从一组硬编码的按钮、颜色、数据链接里解放出来:数据从 ArchestrA 对象来,画面只负责把对象的属性投影出来,改一处模板,所有实例跟着变。这不是单纯“美化界面”,而是把 HMI 的维护成本打下来的工程手段。这套东西适合三类人:做实施交付的工程师、系统集成商里的二次开发人员、甲方负责运维和深化应用的工程师。后面按我实际干活的顺序,从架构聊到代码,再讲翻车记录。
2. 定制先定架构:ArchestrA对象模型与界面解耦
界面定制失败的项目,多数不是不会画图,而是对象模型没立住。很多人第一次打开 AVEVA System Platform(原 Wonderware 的 System Platform),第一反应是“这不就是一个组态软件吗”,于是直奔 InTouch 画画面,等到后面要批量维护的时候,才发现每一张画面都是孤岛。真正能撑住后续定制的,是先理解 ArchestrA 这套对象模型,再用它把界面和数据解耦。
2.1 界面只是投影:模板、实例、属性三层拆解
ArchestrA 的核心运行环境叫 Galaxy,所有对象都跑在 Application Server 上。你先在 IDE 里定义模板(Template),模板规定了结构和默认行为,比如“离心泵模板”有运行状态、出口压力、远程启动这些属性;再由模板生成实例(Instance),实例是现场具体的设备,比如 P-101 泵;实例上真正变的,是属性(Property)的值。界面上的图元(Symbol)不存数据,它只是属性的投影。
很多人会把这三层混在一起。最常见的错误是:直接在画面上放一个静态文本框,把设备号写死,再把数据源指向某个 OPC 点。这样做单个画面确实快,但设备一多、点位一调整,维护成本直接爆炸。更合理的方式是:把画面里的图元做成模板,图元上用“当前实例”这种相对引用,运行时由平台自动解析到具体对象。这样新增一台设备,只需要新建实例,画面不用动。
点对点绑定和基于模板的绑定,差别很直观,我整理了个表:
| 对比项 | 点对点绑定(硬编码) | 基于模板绑定 |
|---|---|---|
| 数据来源 | 画面标记名直接指向具体设备点 | 图元引用当前实例的通用属性 |
| 新增设备 | 复制画面、重新链接、逐个改 | 新建对象实例,画面自动匹配 |
| 改样式或逻辑 | 每张画面逐张改 | 改模板一处,重新发布 |
| 运行期追踪 | 链路藏在几十个动画链接里 | Object Viewer 里一眼看到对象值 |
这个差别,就是我说的“界面只是投影”的含义。界面定制如果从架构上就选了硬编码,后面不管你脚本写得再好、控件做得再精致,维护成本都压不下来。所以做 AVEVA系统平台界面定制,第一步不是打开画面编辑器,而是先在 IDE 里把对象模型理清楚。
2.2 先建对象后画画面:Galaxy初始化与最小模板创建步骤
我一般会按下面这套最小操作建一个设备模板。装完 System Platform 后,先用 Galaxy Management Console 新建一个 Galaxy,这一步要定的主要是认证方式和运行用户。认证方式默认用 Windows 集成认证,对实施项目来说最省事;运行用户建议单独建一个服务账号,不要拿管理员账号跑运行环境,后面安全策略调整时你会感谢这个决定。
接着打开 ArchestrA IDE,连接到刚建的 Galaxy。在模型视图里新建一个设备模板,然后添加属性。属性是后面所有绑定的基础,我的习惯是把属性分三类:状态类(只读)、控制类(可读写)、统计类(只读)。以离心泵为例:
| 属性名 | 数据类型 | 读写权限 | 初始值 | 说明 |
|---|---|---|---|---|
| RunStatus | Boolean | ReadOnly | False | 运行状态,来自 OPC |
| RemoteStart | Boolean | ReadWrite | False | 远程启动命令,界面可写 |
| TotalRunHours | Int32 | ReadOnly | 0 | 累计运行时间 |
| LastAlarmTime | DateTime | ReadOnly | 空 | 最近报警时间 |
属性建好后,先导出模型做一次目检,再创建实例。不要一上来就建实例,模板结构错了,后面所有实例都要跟着返工。导出后你会看到类似下面的结构,字段按工程习惯梳理过,不是平台原始导出文件:
<!-- 模板对象导出的结构示意:重点核对属性名、数据类型、读写权限 --> <ObjectTemplate Name="DevicePump" Type="Device"> <Properties> <Property Name="RunStatus" DataType="Boolean" Access="ReadOnly" InitValue="False"/> <Property Name="RemoteStart" DataType="Boolean" Access="ReadWrite" InitValue="False"/> <Property Name="TotalRunHours" DataType="Int32" Access="ReadOnly" InitValue="0"/> <Property Name="LastAlarmTime" DataType="DateTime" Access="ReadOnly"/> </Properties> <Scripts> <!-- 这里放置对象端脚本:只放数据逻辑,不放界面显隐逻辑 --> </Scripts> </ObjectTemplate>这里逻辑说明一下:导出的 XML 里,DataType 和 Access 是最核心的两个字段。DataType 写错了,画面上显示的类型会截断数值;Access 写错了,按钮写属性会被运行时拒绝。平台里经常出现“按钮点了没反应”的问题,一半以上是这里 Access 设成了 ReadOnly。脚本区域的原则是:对象端脚本只做数据处理和跨系统通信,界面显隐、颜色变化这类视觉逻辑不要放进来,否则部署时会把一个纯数据对象变成界面耦合对象。
模型目检通过后,创建 P-101、P-102 两个实例,各自填初始值,然后 Deploy 到运行环境。这一步做完,你才有第一批可以被画面绑定的数据。
2.3 把图元绑定到对象:接入点、标记名和#引用的用法
对象部署完,接下来才是画面侧。在 System Platform 里,画面应用(InTouch 应用)通过“接入点”(Access Node)指向 Application Server。新手容易在这里漏一步:建了应用但不配接入点,画面里的数据源全是空的。接入点配置好后,在 InTouch 标记名数据库里新建标记,标记名对应“对象.属性”,比如 P101.RunStatus。
我习惯把标记名命名规范和对象属性名保持一致,不要另起一套中文别名,否则后期排查数据链路时对不上。画布上放图元后,动画链接里直接引用这个标记名。再进一步,如果做的是可复用模板图元,就不要写具体对象名,用 # 代表当前实例,比如 #.RunStatus。这个 # 引用是整个模板化画面的关键:
# 当前实例引用(模板图元标准写法) 数据源: #.RunStatus 闪烁条件: #.AlarmActive == TRUE 颜色条件: #.RunStatus == FALSE ? 灰色 : #.AlarmActive ? 红 : 绿 # 具体实例引用(单点画面才用) 数据源: P101.RunStatus参数说明在这里要讲清楚:模板图元里必须用 #,这样同一个图元放到 P-101 和 P-102 上,解析出来的是完全不同的两个数据源。如果你在模板图元里硬写了 P101,那 P-102 画面上永远显示的是 P101 的值。有 AVEVA 设计端经验的人应该眼熟——AVEVA PML 里做界面动作用的是 !! 和 % 引用符号,和这里 # 的定位类似,但语法完全不同,别把两套引用混着写。在 System Platform 里,模板化就是靠 # 撑起来的。
3. 用脚本把画面盘活:QuickScript关键写法与三个必调参数
对象和画面绑定打通后,界面能显示数据了,但还不会“动”。按钮点击、条件显隐、报警闪烁这些交互,靠脚本实现。这里最大的误区是:一上来就写复杂脚本,动不动几十行,把界面脚本当成业务逻辑仓库。脚本在 AVEVA系统平台界面定制里是调味料,不是主菜。主菜始终是对象模型和数据绑定,脚本只在需要表达交互意图时才出现。
3.1 QuickScript和C#脚本怎么选:超过50行就往下沉
经典 InTouch 环境里有两套脚本体系:QuickScript(类 VB 语法)和 C# 风格脚本(ArchestrA 图形里用的)。两者边界很清楚。按钮置数、颜色变化、窗口切换这类轻交互,用 QuickScript,它和标记名深度绑定,写起来最快;复杂算法、调用外部 SDK、批量数据整理,用 C# 脚本,或者干脆下沉到 ArchestrA 对象脚本里执行。
我给自己定了一条线:界面脚本超过 50 行,就要考虑把逻辑挪到对象方法里。道理很简单,界面脚本的生命周期和窗口绑定,窗口关了脚本就没了;而对象脚本跑在 Application Server 上,有完整的调度、重试和日志。把业务逻辑放在界面层,等于把系统的稳定性押在操作员会不会误关窗口上。
选型的时候可以参考这个表:
| 场景 | 推荐写法 | 理由 |
|---|---|---|
| 按钮写对象属性 | QuickScript | 两行搞定,和标记名绑定自然 |
| 窗口打开时初始化 | QuickScript | 生命周期短,适合一次性的初始化动作 |
| 跨系统 REST 调用 | C# 脚本或对象脚本 | 要处理连接池、超时、异常 |
| 批量修改多个对象属性 | ArchestrA 对象方法 | 可以在引擎端执行,不依赖界面 |
3.2 设备启停控制画面:按钮脚本、颜色动画和报警闪烁
拿刚才的 DevicePump 模板做一张操作画面。画面上放启动按钮、停止按钮、运行指示、报警指示。启动按钮的触发动作脚本写在 LeftMouseUp 事件里,QuickScript 写法:
' 启动按钮 LeftMouseUp 事件 ' P101.RemoteStart 是 InTouch 标记名,对应 ArchestrA 对象属性 P101.RemoteStart = TRUE LogMessage("ODS:ACTION", "操作员 " + $OperatorName + " 远程启动 P101", 1)停止按钮对应P101.RemoteStart = FALSE,逻辑对称。这里有个细节值得单独说:为什么用 LeftMouseUp 不用 LeftMouseDown。操作类按钮用 Up,允许操作员在按下的瞬间反悔划走,误触概率低;用 Down 响应快,但工业现场误触一次可能就是一条报警。确认类操作还要加一个二次确认弹窗,这是行业的常规要求。
动画绑定部分,运行指示用颜色动画,报警指示用闪烁动画。表达式设置在动画链接里:
颜色动画: P101.RunStatus == FALSE ? 灰色 : #.AlarmActive ? 红色 : 绿色 闪烁动画: #.AlarmActive == TRUE逻辑说明:这个写法把三种状态压缩在一个表达式里,减少动画链接数量。运行时,表达式的值变化会触发图元刷新,不需要额外脚本。这里参数的关键点是:动画表达式里属性名必须和标记名完全一致,大小写都不能差;模板模式下优先用 # 开头,避免表达式在实例复制后失效。
3.3 三个必调参数:触发事件、扫描周期和执行上下文
脚本能不能稳定跑,和业务逻辑关系不大,反而是三个参数决定生死。第一个是触发事件。按钮类选 LeftMouseUp 我已说过;数据变化类选“值变化触发”而不是时间触发,很多卡顿就是因为在用时间轮询的方式检查一个几小时才变一次的值。
第二个是扫描周期。如果是周期型脚本,默认 1 秒刷新通常太激进。页面图元多、客户机配置一般,我会把周期调整到 3 到 5 秒;只有需要实时反馈的数值(比如电流、温度曲线)才单独设 1 秒。全局脚本里尤其要克制,一个全局周期脚本每 1 秒扫描一遍全部对象,十几个画面全开,客户机 CPU 必然飙高。
第三个是执行上下文。QuickScript 分全局脚本、窗口脚本、标记事件脚本,作用域完全不同。新手常犯的错误是把窗口初始化逻辑放在全局脚本里,结果每个窗口打开都执行一遍,数据被反复覆盖。我的原则是:窗口脚本只干窗口自己的事,全局脚本只放跨窗口的公共逻辑,比如操作员登录状态同步。
3.4 脚本出问题先看日志:LogMessage定位三段链路
System Platform 的脚本调试能力比现代开发工具弱得多,QuickScript 没有单步调试器,脚本跑没跑、跑到哪一行,全靠日志。我在关键动作里都会写 LogMessage,这是最朴素的“打桩”。上面的启动脚本里那行 LogMessage 就是干这个用的。
日志查的时候分三段看:第一段在 InTouch 的日志查看器里看标记事件脚本有没有触发;第二段去 Galaxy Management 看对象属性有没有收到写入;第三段看 Application Server 日志里对象方法有没有抛异常。三段对上了,链路才通。
排查三段链路: 1. InTouch 日志: 脚本是否触发、标记名是否有效 2. 对象属性: Object Viewer 里 RemoteStart 是否变成 TRUE 3. 引擎日志: 对象端脚本是否正常执行如果第三段报了异常,拿异常堆栈去搜,比猜快得多。不要相信“重启一下就好了”,重启只能解决缓存类问题,解决不了逻辑错误。
4. 图元不够就上控件:.NET自定义控件从代码到发布
脚本解决交互,但界面上有些东西脚本画不出来:复杂表格、甘特图、专业趋势组件、自定义输入表单。默认图元库在这些场景下很吃力,硬用基础图元拼,界面卡、维护难。这时候就该写自定义控件。很多人把这个想得很玄,其实就是做一个小型 .NET 程序集,嵌入到系统平台的画面容器里,数据由平台喂给它,它只负责把数据呈现出来。
4.1 什么时候该写自定义控件:默认图元做不到的三种场景
我判断要不要写控件,就三条:默认图元做不出的、做出来交互太别扭的、同一个功能要用在五个以上画面的。典型如报警统计列表,默认图元只能一行一行摆,排序、筛选、分页全要手动做;用自定义表格控件,一次搞定。
技术选型上要区分平台版本。经典 InTouch 环境通过 ActiveX 容器嵌控件,用 WinForms UserControl 最稳妥;新版 AVEVA InTouch Unlimited、Operations Hub 这类 Web 端界面,则走 REST API 加自定义 HTML 组件。规划阶段先确认现场用的是哪一套,不要一上来就做 WPF 控件,WPF 在 ActiveX 容器里的互操作问题很多,资源加载、输入焦点都容易出幺蛾子。如果是新项目,我更倾向于先用平台原生能力,把自定义控件留到确实过不去的场景,因为控件是要维护的代码资产。
4.2 写一个报警摘要控件:WinForms骨架与运行参数
下面这个报警摘要控件的骨架,是从一个实际项目里抽出来的。它只做一件事:定时从数据源拉报警列表,显示在一个表格里。完整代码由你按实际数据接入方式补充,这里给的是稳定的骨架:
// AlarmSummaryControl.cs(WinForms UserControl 骨架) using System; using System.Windows.Forms; public partial class AlarmSummaryControl : UserControl { public string ObjectSource { get; set; } // 由嵌入方传入,如 "P101,P102" private Timer _refreshTimer; public AlarmSummaryControl() { InitializeComponent(); _refreshTimer = new Timer { Interval = 5000 }; _refreshTimer.Tick += (s, e) => RefreshAlarms(); _refreshTimer.Start(); } private void RefreshAlarms() { // 调用 AVEVA 历史库或实时库接口,拉取 ObjectSource 范围内的报警 // 填充到控件内的 DataGridView,只做展示,不做写操作 } }逻辑说明:控件里放一个 Timer 每 5 秒刷新一次,而不是让外界反复触发重绘。5 秒对报警列表足够,工业界面不需要秒级刷新;写操作一律不放在控件里,权限全部由平台的 ArchestrA 对象安全体系去管,控件只做只读展示,这是安全边界。ObjectSource 属性由嵌入方传入,是为了让同一个 DLL 可以复用在不同的画面上,不需要每换一组设备就重编一版。
4.3 注册和分发:DLL进画面要过的三道门
控件编译出来只是个 DLL,要进系统平台的画面,得过三道门。第一道是程序集签名,生成 DLL 时勾选强签名,否则目标机器加载 .NET 控件可能直接被拒。第二道是在目标机器上注册,经典 InTouch 环境下要用 RegAsm 注册成 COM 组件,命令如下:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe AlarmSummaryControl.dll /codebase /tlb参数说明:/codebase 告诉运行时从注册表里找当前路径,不用装到 GAC,方便项目期反复替换 DLL;/tlb 生成类型库,ActiveX 容器才能识别控件的接口。如果你想让这台机器上的所有工程通用这个控件,再把它装进 GAC,但我一般建议先 /codebase,等版本稳定了再进 GAC,省得升级时清不干净。
第三道门是分发。System Platform 支持通过 SMS 把控件部署到多台客户机,不要手动一台一台去注册。项目交付时,把 DLL、注册命令、使用说明打包成一份文档,现场照着执行,能少接很多电话。这里有个很容易踩的坑:开发机上控件运行正常,一发布到现场就红叉。绝大多数原因是开发机装过 Visual Studio,机器上 .NET Framework 组件齐全;现场机器没有,所以分发包里要把运行库组件带齐。
4.4 新版Web端控件集成:REST API与经典环境的差异
如果你做的项目是新版 Web 端界面,上面的 WinForms 路线不适用,改成 REST API 加前端组件。平台暴露接口后,前端用 fetch 直接拉数据,渲染交给组件库。写法和做前端开发教程里的常规做法类似,但认证逻辑要按平台的令牌体系来:
// 新版 AVEVA Web 界面里拉报警列表的示例 const res = await fetch(`/aveva/api/alarmlist?area=${areaId}&from=${fromTime}`, { headers: { "Authorization": `Bearer ${await getToken()}` } }); const list = await res.json(); renderTable(list);这个写法只适用于新版 Web 架构,经典 InTouch 环境里没有这个入口。做跨版本支持时,我习惯先问清楚现场的部署版本,再决定走 ActiveX 还是 REST,别拿新版方案套旧环境,那是翻车的重灾区。
5. 界面定制避坑:5个高频翻车点和排查路径
界面定制做得多了,会发现翻车来来回回就那么几类。我把高频问题整理成五条,每条按现象、原因、解决三段写,这是我自己排查时走的顺序。
5.1 画面显示旧值:部署成功但缓存和接入点没跟上
现象:IDE 里改了模板属性默认值,Deploy 成功,确认过没有报错,但打开 ViewApp 后画面显示的数值还是原来的。
原因:三个地方都有可能。一是对象运行时的缓存没有刷新;二是 InTouch 标记名的 ArchestrA 访问节点没有重连,画面上连的还是旧接入点;三是 ViewApp 的本地缓存没有清理,加载的还是旧画面包。
解决:先在 Object Viewer 里看对象属性运行时值,如果运行时值是对的,问题就在画面侧;再查 ViewApp 接入点配置,确认指向当前的 Application Server;最后做一次强制发布,清掉本地缓存再启动。我遇到过的案例里,接入点配置错误占比最高,因为换机器时最容易漏改这里。
5.2 .NET控件加载失败:十字叉、程序集路径和版本三连查
现象:画面能正常打开,但控件位置是红叉或者灰块,客户端事件日志报“无法加载程序集”。
原因:目标机器没注册控件、.NET Framework 版本不匹配、DLL 不在探测路径里。这三个原因经常同时存在。
解决:按顺序查。先确认目标机器装了 .NET Framework 4.x 而不是 .NET Core,这块平台不认;再用第 4 章的 RegAsm 命令重新注册,确认 /codebase 参数没漏;最后检查 DLL 是否放在 InTouch 程序的私有目录里,或者已注册到 GAC。我自己的习惯是,控件升级后第一时间被测试机验证,而不是直接上生产机,否则红叉会出现在用户眼前。
5.3 脚本把CPU拉满:周期脚本和写操作的隐形循环
现象:某窗口打开后客户机 CPU 持续飙高,其他画面切换都卡,关掉窗口后恢复正常。
原因:窗口脚本里放了周期执行逻辑,脚本里又写了对象属性;属性变化触发了另一个脚本,那个脚本又写回同一属性,形成隐形循环。这种循环在 IDE 里不看运行时监控很难发现。
解决:打开脚本编辑器,把时间触发的周期脚本改成数据变化触发;检查脚本里是否有对同一个属性反复写入的代码。界面脚本只读属性、不下发命令,这个原则能避开大部分循环。如果循环逻辑确实需要放在界面侧,至少加一个防抖标志位,值变化但结果相同时不继续执行。
5.4 对象被签出锁死:多人协同下的IDE排他机制
现象:A 工程师在 ArchestrA IDE 里改了模板,B 工程师在另一台电脑上发布时提示“对象被签出”,无法部署。
原因:ArchestrA IDE 的签出机制默认是排他的,A 改了模板没有签入,整个 Galaxy 里这个模板就被锁住了。
解决:联系 A 撤销签出,或者由管理员在 Galaxy Management Console 里强制释放对象锁。但强制释放有风险,未保存的修改会丢,所以规范做法是下班前把不需要改的对象全部签入,并在项目组里约定:模板对象由专人改,其他人只改实例参数。这个问题在项目冲刺阶段最容易爆,影响范围是整个发布流程。
5.5 换服务器绑定全断:Galaxy迁移时的接入点清单
现象:从开发环境把工程导入到生产机后,所有画面数据源变成问号,标记名全部失效。
原因:机器的服务名、IP、安全上下文都变了,画面里的接入点字符串还停留在开发机状态。Galaxy 导入导出工具不会自动帮你改接入点。
解决:迁移前导出模型,做一个接入点清单,列出哪个应用连哪个 Application Server;迁移后逐个更新接入点,再对画面标记名做批量替换。这里不要手动改,用脚本批量替换服务名,能少很多事。操作顺序上,先改接入点,再导入模型,最后发布,可以避免中间状态污染运行时环境。
6. 用数值追踪做端到端验收:一个数据源到底的核对技巧
界面开发完,怎么验收?别只看画面好不好看,要看一条数据从源头到画面每一步都在不在。我每次做界面定制交付前,都会做一次端到端数值核对,三步走。
第一步,在数据源头造一个测试值。模拟量就写一个斜波或者固定值,开关量就强制置位一次,用平台自带的 Simulation 对象最省事。第二步,打开 Object Viewer,看对象属性实时值,确认数据确实进到了 ArchestrA 对象这一层。第三步,回到画面,看图元上显示的值。三步一致性对了,数据链路才是通的。
| 核对环节 | 怎么查 | 常见异常信号 |
|---|---|---|
| 数据源头 | Simulator / OPC 客户端 | 值一直不动,源头就没数据 |
| ArchestrA 对象 | Object Viewer | 源头有值,属性没变,查通讯配置 |
| InTouch 标记名 | Tag Browser | 值变了但类型截断,查标记名类型 |
| 图元动画链接 | 打开动画绑定属性 | 绑定项写错属性名,查动画表达式 |
我有一个养成的习惯:在对象模板里加一个专门用于验收的 DiagnosticValue 属性,界面上留一个很小的调试文本显示它,交付验证时直接对比这个值和现场表计读数,验收完再隐藏。这个值平时不参与业务逻辑,只做链路校验,很多扯皮都能省掉。界面定制这件事,最后拼的不是画图技巧,是这一套从数据源头查到界面末端的耐心。希望帮到你。
本文还有配套的精品资源,点击获取