
1. 方案整体设计与思路拆解先说说我为什么折腾这件事。之前在做一个某水处理站的上位机监控界面组态王本身自带的XY曲线、温控曲线用起来总差点意思客户看到之后提了一嘴“能不能像网页上那种图表鼠标放上去能看数值、能缩放、能切换曲线”这就麻烦了组态王内置的历史趋势控件虽然也能看趋势但交互体验和颜值都赶不上现在Web端图表库。当时脑子里第一反应就是ECharts——这东西在Web圈用了很多年图表类型丰富、交互流畅而且完全开源免费。问题就剩一个怎么把ECharts塞进组态王画面里。其实组态王是支持ActiveX控件的ECharts本质上是个JS库没法直接作为ActiveX控件加载但组态王有一个“HTML控件”或者说WebBrowser控件能嵌入一个网页。那思路就清楚了把ECharts相关的HTML页面放在本地用组态王的HTML控件加载它再把组态王里的实时变量传给网页网页里的JS代码收到数据后更新图表。这个方案对既有项目侵入很小不用改PLC程序也不用额外装数据库纯上位机层面就能完成。这套方案能解决什么问题我认为最重要的是解决了两类痛点一是传统组态控件交互能力弱二是多系统数据展示割裂。以前想看个趋势曲线要么在组态王里拖个历史曲线控件配置起来繁琐要么干脆导到Excel里看时效性太差。用HTML控件嵌ECharts之后图表交互性、美观度、二次开发灵活性都上来了而且ECharts的图表类型足够丰富折线图、柱状图、饼图、仪表盘、散点图都有一套代码通吃。适合谁来参考如果你在用组态王做上位机开发或者类似组态软件比如力控、杰控、WinCC里做数据可视化那这篇文章的整个思路都能复用。哪怕你完全没接触过ECharts只要会写一点HTML和JavaScript跟着做也能搞定。我会从环境准备、控件配置、页面编写、变量传递几个环节一步步来并且把我踩过的坑也一并列出来。2. 环境准备与工具选型2.1 准备好三样基础物料在做这件事之前先把以下三样东西准备好组态王安装环境我这边用的版本是组态王6.55其他版本的操作逻辑大体相近ECharts库文件建议直接去官网下载我这里用到的是5.x版本的echarts.min.jsHTML编辑器手写记事本也行但推荐用Visual Studio Code写代码带高亮调格式方便组态王版本虽老但HTML控件在6.5x系列里已经比较成熟关键的坑不在版本而在控件属性和路径处理后面我会专门讲。2.2 ECharts库文件如何放置ECharts的echarts.min.js和你的HTML页面最好放在同一个目录下或者一个明确的子目录里。我把它们放在组态王工程目录下的一个新建文件夹中这里命名为ChartWeb路径大致是D:\Projects\WaterStation\ChartWeb里面放了两个文件一个是echarts.min.js一个是trend.html。trend.html里面通过相对路径引用echarts.min.js这样做的好处是如果整个工程拷到别的电脑上只要工程目录一起拷走路径不会出错。组态王工程本身就包含画面、变量、脚本等文件而HTML资源默认不会被组态王打包管理所以需要你手动维护这个文件夹最好在工程文件夹里单独建一个目录和组态王的文件放在一起这样整个工程拷贝时不会丢。2.3 为什么选ECharts而不是其他图表库这里多说一句选型问题。市面上常见的Web图表库有ECharts、Chart.js、Highcharts、D3.js等但要嵌入组态王这种老牌工控软件里稳定性、离线可用性、上手门槛是第一位的。ECharts完全支持离线运行不需要联网加载CDN资源对工业现场这种多数时候无法访问外网的场景非常友好。而且它的中文文档齐全遇到问题查起来快。Chart.js虽然轻量但图表类型少一些D3.js自由度最高但学习成本太高Highcharts商用授权对部分企业不友好。综合下来ECharts是做组态王图表展示的优选方案。3. 组态王HTML控件的配置与加载3.1 在画面上放置HTML控件打开组态王开发系统TouchExplorer在画面编辑器中从工具箱里找到“ActiveX控件”这一类双击或者拖拽到画面上。弹出的ActiveX控件列表里能找到HTML控件不同版本名称略有差异确认是“Microsoft Web Browser”或者类似描述即可。选定后会在画面上生成一个矩形区域那就是网页显示区。放上去之后右键点击控件进入控件属性设置。关键的属性是“URL”或“LocationURL”这个值决定控件加载哪个网页。这里有个非常重要的注意事项URL一定要写成完整路径不能用相对路径而且文件协议要写对。我最初写的是D:\Projects\WaterStation\ChartWeb\trend.html结果死活加载不出来后来改成file:///D:/Projects/WaterStation/ChartWeb/trend.html也就是把反斜杠换成正斜杠前面加上file协议这才能正常显示。如果你把网页放到组态王安装目录下路径写起来也差不多但建议还是统一按我上面的方式管理。有的版本里HTML控件加载时会调用系统默认浏览器内核如果组态王运行在旧版Windows Server上IE版本较老可能导致ECharts的某些渲染效果异常这个在后文常见问题里我再详细讲。3.2 画面打开时自动加载页面实现了控件加载网页后下一步要考虑的是加载时机。如果每次打开画面都要手动点一下刷新体验会很糟糕。所以我在画面的“显示时”命令语言里写了一段脚本主动刷新一下控件让页面在画面打开时立即加载。这里要说一下为什么需要主动刷新HTML控件在组态王启动时经常有一定概率不自动加载URL尤其是工程文件从其他电脑拷过来或者修改过路径后很容易出现白屏。手动刷新是最好的兜底方案。具体做法是打开画面的命令语言编辑器在“显示时”标签页里添加!Controls[HTML控件].Refresh();注意控件名称要和画面上实际放置的控件名称一致。如果你一开始没有给控件命名默认可能是“控件1”之类的最好在控件属性里手动给它一个明确的名字比如HTMLChart脚本里引用时既清晰又不容易错。3.3 运行时的免弹窗设置组态王运行系统在打开含有HTML控件的画面时默认可能会弹出脚本调试器或安全提示这是因为WebBrowser控件在加载本地页面时被IE安全策略拦截。解决方式有两种在画面编程环境里找到“系统配置”里的安全选项把控件脚本执行级别调低在HTML页面里加入兼容性声明也就是后面在HTML页面写法中提到的meta标签我这边是页面代码和系统配置两手抓。系统配置上如果你熟悉组态王的“系统配置”对话框找到“运行系统”相关设置勾选允许ActiveX脚本执行。这两步都做到运行画面时就不会有弹窗干扰了。4. HTML页面编写与ECharts图表实现4.1 页面基本框架与本地引用这一节是整个方案的核心我直接给出一个可用的HTML页面模板并逐段解释每部分的作用。!DOCTYPE html html head meta charsetUTF-8 meta http-equivX-UA-Compatible contentIEedge title实时趋势/title script srcecharts.min.js/script style html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: #1b2a3a; } #chart { width: 100%; height: 100%; } /style /head body div idchart/div script // 后续代码放这里 /script /body /html这里有几个细节需要留意。第一个meta标签是告诉IE内核使用最新模式渲染页面这在工控机上尤为重要因为工控机系统普遍老默认的IE兼容模式会导致ECharts显示异常或动画卡顿。第2个细节是样式里把html和body的宽高都设为100%并且去掉滚动条这样图表才能完整撑满整个HTML控件的显示区域。第3个细节是背景色我用了深色底这样整个画面和组态王本身的深色工业风格能融合如果你要嵌入亮色监控界面可以自行修改背景色。4.2 初始化图表并配置动态数据接下来是ECharts本体部分的代码。我先初始化一个折线图实例然后定义了图表的基本配置项。var chartDom document.getElementById(chart); var myChart echarts.init(chartDom); var option { backgroundColor: rgba(0,0,0,0), title: { text: 实时流量趋势, textStyle: { color: #ffffff } }, tooltip: { trigger: axis }, legend: { data: [瞬时流量], textStyle: { color: #ffffff } }, grid: { left: 3%, right: 4%, bottom: 3%, containLabel: true }, xAxis: { type: category, boundaryGap: false, data: [], axisLine: { lineStyle: { color: #ffffff } } }, yAxis: { type: value, axisLine: { lineStyle: { color: #ffffff } } }, series: [{ name: 瞬时流量, type: line, smooth: true, data: [], lineStyle: { width: 2 } }] }; myChart.setOption(option);这段代码定义了一个最基础的折线图坐标轴、图例、提示框都配置好了。现在x轴和y轴的数据都是空数组等组态王把数据传进来后我再更新这两个数组。对于一个初次接触ECharts的读者我建议先把这一套跑通再慢慢增加图例、平滑曲线、面积填充等效果。要特别注意tooltip的trigger设置成axis这样鼠标悬浮在图表上时会以十字线形式显示当前点的值这种交互在工业数据监控场景里非常实用。4.3 接收组态王变量的两种方式组态王和HTML页面之间传数据最简单的方式是通过URL参数。比如画面加载时URL是这样的file:///D:/Projects/WaterStation/ChartWeb/trend.html?value12.5trend.html页面在启动时用JavaScript解析URL里的value参数然后把这个值追加到图表的数据序列里。这种方式实现简单不需要额外写文件、不需要数据库适合数据量小、刷新频率不高的场景。但如果页面已经加载完成想继续更新数据靠URL跳转就不太合适了因为你不想让页面反复刷新否则图表会闪烁、动画会中断。第二种方式是组态王变量写入本地文件然后HTML页面定时读取文件内容。这种方式适合高频刷新和持续积累数据的场景但会引入文件读写延迟和磁盘占用问题而且需要额外的脚本参与。工程上最常见的做法还是第一种用组态王画面里的定时脚本或循环脚本周期性地向控件发出导航请求每请求一次就往URL参数里塞一个新的数值。页面收到新URL后不刷新整个页面而是通过Event或定时轮询的方式去读取最新参数更新图表。我实际采用的是定时轮询方式具体做法是在HTML页面里写一个setInterval定时器每3秒读取一次URL参数中的value然后更新图表的数据点。这样既避免了页面整体刷新也不会因为变量更新太快而频繁操作DOM。组态王侧只需要在画面中添加一个“每隔3000毫秒”执行的循环脚本脚本里用控件的Navigate方法给URL追加当前时间的随机参数和最新的变量值页面就会因为URL变化而触发更新。4.4 组态王脚本侧变量传递示例下面给出组态王一侧的脚本写法。假设组态王里已经定义好了“瞬时流量”这个变量画面上的HTML控件叫HTMLChart在画面的“循环脚本”里每3秒执行一次往控件发送带参数的URLSTRING strUrl; STRING strValue; STRING strTime; strValue value StrFromReal(瞬时流量, 2, f); strTime t StrFromInt(HTM_GetTickCount(), 10); // 用时间戳避免缓存 strUrl file:///D:/Projects/WaterStation/ChartWeb/trend.html? strValue strTime; !Controls[HTMLChart].Navigate(strUrl);这里有几个关键细节StrFromReal用来把实数变量转成字符串且保留两位小数比如12.34这样显示比较规整strTime是一个时间戳参数因为浏览器内核遇到完全相同的URL可能不触发加载时间戳能强制每次URL都是新的页面才会重新解析Navigate方法让WebBrowser控件跳转到新URL这样HTML页面里的JS能获取到最新的value值这个方案运行起来后你再打开组态王运行画面就能看到图表上每3秒多点一个点同时推进时间轴。如果希望数据点保持在最近N个点我就在HTML页面里维护一个固定长度的数组超出长度就把最早的点shift掉视觉上就是一个滚动更新的实时趋势图。很多工业监控场景不关心很久以前的数据只看最近几分钟或几小时的变化这个设计刚好满足需求。4.5 页面端JavaScript解析参数与更新图表trend.html里解析URL参数并更新图表的脚本我做了一个简单的封装function getQueryParam(name) { var reg new RegExp((^|) name ([^]*)(|$)); var r window.location.search.substr(1).match(reg); if (r ! null) { return decodeURIComponent(r[2]); } return null; } var maxPoints 60; var xData []; var yData []; function updateChart() { var val getQueryParam(value); if (val null || isNaN(parseFloat(val))) { return; } var num parseFloat(val); var now new Date(); var timeStr now.getHours() : now.getMinutes() : now.getSeconds(); xData.push(timeStr); yData.push(num); if (xData.length maxPoints) { xData.shift(); yData.shift(); } myChart.setOption({ xAxis: { data: xData }, series: [{ data: yData }] }); } setInterval(updateChart, 3000);这段代码看起来不长但里面有几个容易踩坑的地方。第一个坑是用window.location.search来获取参数如果URL中带中文可能解析不出来所以我在URL拼接时只传数字和时间戳从源头避免编码问题。第二个坑是setInterval的间隔时间和组态王脚本的循环时间要匹配或略小于组态王的刷频率否则页面还没来得及更新下一次导航又来了数据会堆积。我这里采用的是组态王3秒刷新、页面也3秒读取一次保持同步。第三个坑是ECharts在连续更新大量数据时如果每次都重建整个option会造成性能损耗所以只更新变化的xAxis数据和时间轴的data这是ECharts增量更新的推荐做法。4.6 历史曲线与多变量图表扩展上面讲的是实时曲线。实际工程中客户还经常要看历史趋势。组态王自带的历史数据库如果配置好你可以把某段时间范围内的历史数据通过ODBC或DDE方式导出但因为HTML控件装载的是本地页面直接访问组态王历史数据不太方便。所以我通常的做法是组态王侧在运行时按周期把需要的变量值写入一个文本文件HTML页面里通过JavaScript读取这个文件再渲染历史趋势。这里不展开文件读写的细节但思路是一致的组态王负责采集和存储HTML页面负责展示。多变量展示也很容易扩展你只需要在URL参数里同时传递多个变量值比如?v112.3v245.6v378.9然后在页面里解析三个值并分别推入三个数据系列。图表的legend里显示三个变量的名称就能在一个图表里看到多条曲线。实际做水处理项目时我把进水流量、出水流量、水箱液位都放在一张图上值班人员一眼就能看出波动关联工作效率提升非常明显。5. 实操过程与核心环节实现5.1 控件加载白屏问题一次解决我实际操作过程中最让人头疼的一步就是控件加载后白屏。第一次测试时画面运行起来HTML控件区域灰蒙蒙一片什么都没有。我当时第一反应是URL路径写错了检查了一下file协议没问题页面单独在浏览器里打开也正常。后来发现是ECharts库文件本身加载失败了。因为我直接把echarts.min.js放在了ChartWeb目录下但trend.html里写的引用路径是script srcjs/echarts.min.js/script我当时想当然地把JS文件放进了子目录但实际目录里根本没有js文件夹。这类问题很隐蔽因为页面本身能打开只是图表区域不渲染控制台又没弹错排查起来浪费时间。建议你写完HTML页面后先在这个页面上右键查看源代码确认script标签引用地址是否正确或者直接用浏览器打开本地页面按F12查看Console有没有404错误。这段经验让我后面写其他页面时都习惯性地检查资源路径很多类似问题都能提前避免。5.2 组态王与HTML页面变量联调页面代码写好之后下一步就是联调。先在组态王开发系统里定义一个简单的内存变量比如“瞬时流量”初始值设为10。然后在画面上放置一个按钮按钮的“弹起时”命令语言里写瞬时流量 瞬时流量 1;这样就有一个手动递增的数据源方便观察图表是否在更新。接着启动运行系统打开画面观察图表上是否每隔3秒多一个点。这里要特别留意一个点如果图表数据不更新不要急着怀疑ECharts代码先确认组态王这边有没有真的把变量值传出去。调试小技巧是在画面里再放一个文本显示框绑定同一个变量看看数值有没有变化。如果文本在变但图表不变问题出在页面JS解析上如果文本都不变问题就出在组态王变量刷新或路径传递上。这样逐环节定位很快就能找到症结。5.3 刷新频率和数据量权衡在实际项目中组态王变量数量多的时候每3秒刷新一个数据点没问题但如果画面上同时有好几个HTML控件每个控件都每3秒导航一次系统开销就会增加。我的建议是单个画面里HTML控件的数量控制在3个以内刷新频率不低于2秒否则整个运行画面可能会出现卡顿。对于特别高频率的数据更新比如1秒一次或更快的场景就不要用HTML控件实时刷新了可以改为组态王将数据写入内存共享区域HTML侧用更底层的WebSocket或定时读文件来获取但这已经超出本文范围就不展开了。5.4 图表样式与组态王风格统一最后一步是风格统一。组态王的画面通常都是深蓝色或灰色调的工业风格如果HTML页面是白底黑字嵌进去会特别突兀。我在页面里把背景色设为深色文字、坐标轴、图例都设为白色边框也处理成半透明这样整体看起来是一体的。ECharts默认的主题颜色偏亮在深色背景上显示效果也可以但如果你追求更高质感可以自己定义颜色列表甚至直接引入ECharts的暗色主题。具体方式是引入主题JS文件然后初始化时传入主题名称var myChart echarts.init(chartDom, dark);ECharts官方提供了dark等几个主题效果不错。不过引入主题文件也会增加本地资源看项目需求取舍。6. 常见问题与排查技巧实录6.1 HTML控件白屏的几大成因白屏这个问题我会单独列一个速查表因为遇到的概率太高现象可能原因处理方式控件区域灰白无内容URL路径格式错误使用file:///协议盘符为正斜杠页面能打开但图表空白echarts.min.js加载失败检查script路径和本地文件是否存在图表不更新URL未变化或页面缓存在URL后加时间戳参数图表部分功能异常IE兼容模式问题添加X-UA-Compatible为IEedge的meta标签控件尺寸不对HTML页面未适配100%宽高设置html和body宽高均为100%白屏的时候不要着急改代码先在电脑上用浏览器直接打开那个HTML文件验证页面本身是否正常。页面正常就一定是组态王和HTML控件之间的路径或缓存问题页面报错就先把页面调通再回到组态王环境里测试可以节省大量时间。6.2 变量值不更新的检查顺序如果变量值不更新按照以下顺序排查先检查组态王画面循环脚本是否在执行可以在脚本里临时加一个写日志动作确认脚本有没有被触发然后确认HTML页面里getQueryParam解析到的value值是否为空可以在图表上方临时加一个div显示解析到的值调试完再隐藏最后检查组态王波形刷新是否与脚本周期同步运行如果组态王运行系统的画面刷新频率设置过低也会导致变量在画面显示滞后。我因为这个原因吃过亏组态王默认画面刷新频率需要检查如果刷新频率调成“仅当改变时”一些变量变化频率高但脚本周期固定时画面数据会看起来像卡住了一样。6.3 旧版IE内核导致渲染异常工控机普遍装的还是Windows 7或者Windows Server 2008组态王运行系统调用的WebBrowser控件内核是IE7或IE8甚至更低。ECharts 5.x版本对旧浏览器的兼容性一般某些动画效果和半透明渲染可能在旧IE上表现不佳。解决办法有两个一是在页面head里加上X-UA-Compatible的meta标签让浏览器以Edge模式渲染二是如果工控机环境实在过于老旧把ECharts版本降级到3.x我当时在某个现场就是因为客户机器还在用老旧的IE内核ECharts 5版本图表完全无法显示换成3.x版本后恢复正常。如果你工控机的系统比较旧建议优先选择ECharts 3.x或者4.x稳定性更好。6.4 大屏展示场景的字体与缩放问题做大屏展示时客户通常会用1080P甚至更高分辨率的显示器如果HTML页面里的文字是固定像素大小在高分屏下会显得很小。解决办法是在ECharts配置里使用百分比和rem单位或者根据屏幕宽度动态计算字体大小。我提供一个简单的方式在初始化ECharts之前根据window.devicePixelRatio调整canvas的清晰度同时设置图表的字体为相对值myChart echarts.init(chartDom, null, { devicePixelRatio: window.devicePixelRatio || 1 });这样在高分屏上图表边缘会更清晰字体大小也可以配合CSS中的vw单位做自适应。不过说实话组态王画面本身的分辨率是固定的HTML控件的大小也是固定的所以字体问题的处理优先级并不高更多是细节打磨时做做调整。6.5 现场部署时资源文件丢失的坑最后一个常见问题是工程拷贝后HTML资源文件丢失。很多工程师会把组态王工程从开发电脑拷贝到现场工控机结果发现图表不显示原因往往就是ChartWeb这个文件夹没有跟着工程拷贝或者拷过去之后路径变了。这里强烈建议在组态王工程主目录下建立固定的资源子目录比如工程根目录下的ChartWeb然后URL使用相对路径形式file:///./ChartWeb/trend.html但组态王里的URL是写死字符串不好动态获得工程根目录。有个取巧的办法在组态王画面的“显示时”脚本里先获取当前工程路径再拼出完整URL。组态王提供了函数可以获得当前开发工程路径你可以用它拼接这样工程不管放到哪台电脑、哪个目录都能自动适配路径不会因为路径写死而找不到资源。这也是我后来养成的习惯所有外部资源都通过脚本动态拼接路径不会再出现拷机后白屏的问题。7. 进阶扩展从单张图表到数据驾驶舱当单张趋势图稳定运行后不少项目会提出更进一步的需求把多个图表聚合到一个画面里形成一个数据驾驶舱。比如水处理项目中除了实时流量趋势还要显示各水池液位、设备运行状态、告警信息等。这个需求用HTML控件可以继续扩展只要在同一个HTML页面里多放几个div分别初始化为不同的图表实例然后通过组态王侧传递不同的URL参数页面端就可以一次更新所有图表。我实际做的某跨平台系统中页面里放了4个图表分别显示流量趋势、压力趋势、液位柱状图和设备运行状态饼图URL参数一次传了6个变量。组态王侧每3秒导航一次页面端每3秒解析一次全部参数并分发给对应的图表实例。运行时整体流畅CPU占用也正常。不过这也意味着调试复杂度增加了万一某一个图表出问题整个页面都会受影响所以建议增加错误捕获机制比如把一个图表的数据更新单独包在try-catch里避免一个异常导致全页面卡死。如果你后续要做数据驾驶舱强烈建议把页面组织成一个单页应用所有图表的数据更新逻辑集中统一管理组态王侧只负责传数据页面端负责数据处理和展示。这样后期加图表、改布局都方便得多。8. 关于这个方案的真实体会我在实际项目里反复折腾过这个方案最大的感受是组态王加HTML控件这条路几乎把工控组态软件的封闭性打开了一个口子。以前想做复杂可视化总被组态软件自身的控件能力限制住现在网页端成熟的技术栈都能往里搬。ECharts只是其中之一你还可以用同样的方式内嵌地图、视频监控、3D模型原理都是一样的。我个人的实际体会是做这类集成时一定要有“最小可运行原型”的意识。先把最简单的折线图画出来确认组态王变量能传到页面里再逐步加多变量、加样式、完善交互。不要一上来就想着做得很复杂因为组态王环境里的调试手段比纯Web开发少得多出问题时定位问题的时间成本很高。最后再分享一个小技巧调试这类HTML页面时不要只盯着组态王运行画面看。你把trend.html在普通浏览器里打开通过手动修改URL参数的方式模拟组态王传值先把页面端调通再回到组态王环境里联调。这样两边隔离调试效率会提升很多。希望这篇文章能帮你少走一些弯路。