上个月刚把上海工厂那套多块大屏的电子看板项目验收掉从需求确认到最后一块屏点亮前后折腾了一个多月。这类项目在制造业里越来越常见——产线数据要实时可见管理层要看综合指标各个车间又希望数据能同步刷新不能出现走廊屏和车间屏各说各话的情况。这篇就把整个项目的架构选型、多屏同步机制、调试落地过程和踩过的坑完整整理出来给正在做或者准备做工厂可视化大屏的朋友一个参考。1. 需求拆解与整体方案选型1.1 这个项目到底要解决什么问题这个项目的基础需求听起来很简单工厂里有十几块大屏分布在车间、走廊、会议室和前台每块屏显示不同的数据组合但关键数据要一致。比如车间屏显示当班产量、设备状态、不良率会议室屏显示月度达成率、OEE、能耗趋势前台屏显示欢迎信息和整体产能概况。但真正做起来就会发现难点不在“显示什么”而在“怎么保证多块屏在同一时刻看到的是同一份数据”。车间里一台设备刚刚报了一次故障三块屏上对应的状态卡应该同时翻转延迟不能超过两三秒。更麻烦的是工厂网络环境不稳定有线和无线混用偶尔还有电磁干扰导致个别屏断线重连断线期间的脏数据不能在上线后把已经同步好的数据搞乱。所以一开始就要把项目定性为“一个数据实时分发与多端一致渲染的系统”而不是“做一个网页放到大屏上”。定调不对后面所有设计都会跑偏。1.2 为什么选B/S架构而不是老牌的C/S方案工厂里很多老系统还在用C/S架构比如上位机软件、组态软件安装部署麻烦升级还要一台台机器去跑。这次十几块屏分布在不同物理位置如果每块屏都要装客户端光维护就能把人逼疯。我选了B/S架构浏览器直接打开URL访问看板页面。核心好处有三个升级零成本后端一发布所有屏刷新即最新版本硬件选型自由只要能跑浏览器的设备都行哪怕是淘汰下来的工控机调试方便开发机上改完就能预览不用先部署到目标机。前端框架用的Vue 3加ECharts后端用Node.js做数据聚合和推送服务。选Node不是因为性能有多强而是因为它做WebSocket推送和JSON数据处理太顺手了和前端共用一套JS生态联调时沟通成本低很多。数据量也不大车间每分钟几千条事件Node完全扛得住。1.3 数据链路整体设计整个数据链路长这样设备/PLC/MES系统 - 数据采集服务 - 内存缓存 Redis - 推送服务(WebSocket) - 大屏前端数据采集服务负责对接MES数据库和部分PLC点位定时轮询增量数据标准化后写入Redis缓存并发布事件。推送服务订阅Redis事件组装成带版本号的消息体通过WebSocket广播给所有已连接的大屏客户端。这里Redis的作用不只是缓存更重要的是做一个轻量级的发布订阅通道。采集服务和推送服务解耦万一采集服务重启推送服务不会跟着挂大屏还能显示最后一次缓存的数据。多块屏之间的一致性也依赖Redis这个中间层来统一消息序列。有个细节是关于刷新策略的。不同屏对实时性的要求不一样车间屏要求高设备状态变化要1秒内反映会议室大屏看的是趋势和汇总5秒刷新足够。所以不要所有屏一刀切。我在前端做了可配置的刷新策略按照看板类型区分推送频率。这个后面细说。2. 多屏同步显示的核心机制2.1 先搞清楚“同步”到底同步什么做多屏同步之前我先把“同步”这个词拆开了。很多客户以为同步就是“每块屏显示一模一样的画面”其实不是。车间的A屏和走廊的B屏布局、内容都不一样它们需要同步的是“同一时刻看到的同一份数据”也就是数据快照的一致性。比如当班产量是1357件A屏的产量卡片和B屏的产量数字必须都是1357不能因为推送先后顺序不一样一块显示1356另一块显示1357。要做到这一点不能靠前端各自去拉数据——各自拉数据的时间差会导致永远对不上。必须由服务端统一生成带版本号的数据快照推给所有前端前端校验版本号后决定是否丢弃过期数据。2.2 推送方案对比轮询、WebSocket还是MQTT做实时数据推送业内常用方案就那几种我简单对比一下方案实时性实现复杂度适用场景短轮询差有固定延迟低数据变化不频繁的非关键看板长轮询中等中后端不好上WebSocket时的过渡方案WebSocket高毫秒级中车间级实时看板主力方案MQTT高轻量中高需额外Broker设备传感器海量点位、大规模组网工厂场景我最终选了WebSocket为主、MQTT备用的组合。主链路直接用WebSocket因为前端浏览器原生支持不需要额外装客户端一条TCP长连接顶得住几千条消息。备用的MQTT留给后续扩展——如果工厂将来要接入几百台设备直接上报点位数据再用MQTT做设备侧接入推送服务订阅后继续统一分发。2.3 服务端如何保证全屏数据一致性这是整个项目的核心。我的做法是这样的推送服务内部维护一个自增的批次号batchId每聚合完一次数据快照就给快照打上batchId和生成时间戳然后广播给所有WebSocket客户端。前端拿到消息后先判断batchId是否大于本地已渲染的批次号是则渲染否则直接丢弃。这里有个关键点batchId必须是单调递增的不能依赖服务器本地时间。因为多台服务器时间可能偏差几十毫秒时间戳不一定能准确排序。自增序列放在Redis里每次生成快照时INCR一下拿值可以保证全局唯一且有序。前端渲染的时候还会做一次去重。如果同一batchId的消息因为网络重推被收到两次第二次直接忽略。这个去重逻辑单独抽了一个公共模块所有看板页面共用不用每块屏单独写一套。2.4 前端多实例的渲染架构每块大屏对应一个URL比如/screen/workshop-a、/screen/meeting-room但页面骨架是同一套。前端按URL参数动态加载对应的布局配置和数据订阅频道。数据订阅这块我封装了一个socket-manager职责有三建立和维护WebSocket连接自动重连按screenId订阅服务端对应的消息频道在收到数据后解析batchId并触发对应Vue组件的更新。ECharts图表组件单独封装了一层监听数据变化后调用setOption更新。这里有个性能细节setOption不要传整个option对象只传要变更的data部分渲染开销会小很多实测大屏在数据高频变化时CPU占用降低明显。3. 关键参数设计与调优3.1 刷新频率怎么定才合理这个听起来简单实际是个需要权衡的活。刷新太快前端渲染开销大CPU占用上去了浏览器还可能因为硬件加速问题出现画面撕裂刷新太慢数据跟不上现场节奏车间主管会来骂人。我最后定的参数是这样看板类型数据推送频率刷新内容设计理由车间产线屏1秒产量、设备状态、报警设备开停和报警需要秒级响应仓储物流屏3秒出入库、库存变化物流节拍相对稳定3秒足够会议室综合屏5秒OEE、能耗、趋势图表趋势数据变化慢刷新太快反而抖动难看前台欢迎屏30秒欢迎词、综合产能静态为主低频刷新省资源这个参数不是拍脑袋拍的。我写了个简单的压力测试脚本模拟同一时间推送50条消息给19块屏观察前端帧率和CPU占用。在1秒刷新档位下单块屏CPU占用大概15%-20%内存占用稳定在300MB以内基本可接受。3.2 消息体格式设计前后端消息格式统一这是我刻意规范的方便后续加字段不会手忙脚乱。标准格式如下{ batchId: 10231, ts: 1695852000000, screenType: workshop_a, data: { output: { value: 1357, trend: up }, deviceStatus: [ { id: MC001, status: running } ], defectRate: 0.012 } }batchId用于前端排序和去重ts是服务端生成时间戳screenType告诉前端这条消息是否与当前屏相关data才是真正要渲染的数据。这里有个我踩过的坑如果data里的字段每次类型不一致比如产量数字第一次是整数第二次变成字符串Vue的响应式系统虽然能更新但ECharts在某些情况下会曲线错乱。所以后端组装数据时一定要做类型强校验整数就是整数浮点就是浮点别偷懒。3.3 前端渲染的防抖与节流短时间高频推送也会带来问题。车间屏1秒一推每推一次就触发所有组件的更新渲染如果组件多性能压力不小。我在关键组件上做了统一处理数值型组件用requestAnimationFrame加数据标记做聚合同一帧内多次数据变更只渲染一次图表组件用setOption的notMerge参数只merge变化的series滚动列表组件用虚拟滚动只渲染可视区域的行。实测下来原本高频数据下图表会出现卡顿感优化后基本平滑。这个优化建议在项目早期就做进去别等上线后再回来补补的时候要到处找性能瓶颈痛苦得多。4. 调试与落地全过程4.1 大屏硬件的网络规划工厂环境跟办公室不一样网络拓扑复杂设备多IP规划稍有不慎就会冲突。这次大屏设备的IP我全部规划在专用网段比如192.168.50.0/24与办公网、设备网物理隔离或VLAN隔离。每块大屏主机设置固定IP同时在交换机上做DHCP保留防止设备重启后因为DHCP租约问题拿到不同IP导致服务端失联。开机自启动浏览器并进入全屏模式我是通过Windows计划任务实现的开机延迟30秒后自动启动Chrome的--kiosk模式这样访问不了其他页面也不会被误操作退出。这里还有个容易被忽略的点大屏主机的节能设置要把“关闭显示器”和“休眠”都设为从不。工厂夜班可能没人注意屏幕但如果主机休眠了第二天早班重启浏览器来不及看板黑着好几小时。4.2 WebSocket连接调试前期联调时最头疼的就是WebSocket连接不稳定。浏览器DevTools里的Network标签页可以查看WS帧能看到消息收发但不够精细。我写了一个本地的Node调试脚本模拟大屏客户端连接WebSocket服务器打印每次收到消息的batchId和时间差用来验证推送延迟和消息顺序。脚本核心就几行const WebSocket require(ws); const ws new WebSocket(ws://192.168.50.10:8080/ws?screenIdworkshop_a); ws.on(message, (data) { const msg JSON.parse(data.toString()); const delay Date.now() - msg.ts; console.log(batch ${msg.batchId} delay ${delay}ms); });这个脚本最大的价值是能直接验证“消息是否乱序、是否重复、延迟多高”。我靠它发现了一个后端bug采集服务并发写入Redis时部分消息的batchId顺序反了导致前端丢弃了本该渲染的新数据。后来在后端加了序列号检查才解决。4.3 时间同步问题多块大屏显示的时间和数据时间戳如果不一致车间主管对比几块屏时会觉得“数据有问题”。实际上数据没问题是各台主机的时间差了十几秒。每台大屏主机加入域或配置NTP时间同步统一对准车间交换机上的NTP服务器。同时在前端显示时钟组件时间源统一用服务端在消息体里携带的ts字段不读取本地系统时间。这样即使某台主机时间走偏大屏右上角显示的时间依然是服务端时间全场一致。4.4 上线前的灰度发布大屏项目最怕一次性全量上线出问题。我的做法是先挑车间里一块最显眼的屏做试点连着跑两天确认推送稳定、无内存泄漏、无断线重连异常然后再扩展到走廊屏、会议室屏和前台屏。灰度期间我每天到车间拍几块屏的实时照片比对数据是否一致。人工巡检虽然土但对现场环境的感知比任何监控脚本都直观——你会发现某块屏挂在阳光直射位置导致屏幕亮度不够某块屏离路由器太远导致WiFi频繁掉线这些是纯技术测试发现不了的。5. 常见问题与排查技巧实录5.1 症状个别屏总是比其他屏慢1-2秒几块屏放在一起对比有一块屏的产量数字总是慢半拍。排查发现那块屏的浏览器标签页处于后台状态Chrome对后台标签页有定时器节流机制WebSocket消息被延迟处理了。解决办法把大屏浏览器启动参数加上--disable-background-timer-throttling同时在代码里用Visibility API检测页面被隐藏时提示运维人员检查。另外让Chrome以--app模式运行页面始终处于前台节流问题基本绝迹。5.2 症状长时间运行后画面撕裂或白屏一块屏运行两三天后偶发白屏刷新页面又恢复。用DevTools的Performance监控发现JS堆内存持续增长典型的DOM节点或监听器泄漏。定位到是一个ECharts实例在组件销毁时没有调用dispose导致图表实例累积。修复就是规范的组件生命周期管理组件卸载时统一执行chart.dispose()全局事件监听用$off解绑WebSocket的onmessage回调不做匿名函数注册避免重复绑定。修完后跑了一周内存曲线平稳。5.3 症状断网重连后数据显示异常车间某块屏WiFi信号弱断网重连后数据能收到但显示的值跳变剧烈。原因是断网期间服务端持续推送前端离线时没有缓存最近一条合法快照重连后先渲染了重新发送的历史数据然后又跳到实时数据。解决思路是前端维护一个ArrayBuffer容量1条存最近一次渲染成功的batchId对应的完整快照。重连后先把缓存快照渲染出来等新batchId到来再覆盖。视觉效果上就是断网恢复后画面平滑跳变不会闪一下旧数据再跳新数据。5.4 症状ECharts图表数字在大屏上模糊不清这个几乎是所有工厂大屏的通病。ECharts默认字号12px在2米外的大屏上看就是一片糊。我把图表里的关键数字字号提到24-28px标题字号20px同时改用深色主题提高对比度。更隐蔽的是字重问题。ECharts默认字重normal在大屏上细体字会被抗锯齿糊掉。把所有关键数字的fontWeight改成bold开启textStyle.textShadowColor配合阴影清晰度立刻上一个台阶。5.5 快速排查问题清单我整理了一份随手查的清单现场出问题先过一遍排查项检查方法常见原因数据不更新DevTools网络面板看WS帧是否持续收到服务端推送挂了、Redis订阅断掉单块屏不更新其他屏正常检查该屏网络连接、浏览器是否被最小化WiFi掉线、浏览器标签被节流数据更新但画面没变检查batchId是否递增看console有无报错前端丢弃了过期数据、JS报错中断渲染图表曲线异常对比后端数据原始值和前端渲染值字段类型不一致、精度丢失CPU占用过高Performance面板录制看热点ECharts动画未关闭、高频setOption6. 实操心得与后续扩展整个项目做完我最深的体会是工厂大屏看板表面上是个前端展示项目实际上是个数据工程和现场工程。前端渲染只是最后一段更花精力的是后端数据一致性、网络稳定性、设备长时间运行的可靠性。有个经验想单独提一下大屏调试阶段一定要准备一台和现场同配置的测试机不要用开发笔记本代替。开发机性能好、屏幕分辨率高、散热强很多性能问题在开发机上根本复现不出来。我这次就吃过亏——开发机上测试流畅到现场工控机上跑才暴露出渲染卡顿又回头优化了一遍。后续如果继续扩展我计划做两个方向一是把移动端集成进来主管在手机上看相同数据复用同一套推送服务二是做历史数据回溯大屏上点个按钮就能回看过去一周的产量趋势方便交接班复盘。这两个方向扩展时前端的socket-manager和后端的推送服务都能直接复用不需要推倒重来。