阅读时间:约5分钟
适用人群:自动化测试与测量程序的开发者,需要在多个测试用例、多个循环之间复用同一个前面板波形图,并希望了解数据传递机制与程序架构设计原则的LabVIEW使用者
一、背景与问题现象
在自动化测试程序中,经常需要根据用户选择的测试类型执行不同的测量流程,并在同一个仪器前面板上展示测量结果。以常见的电流-电压特性曲线测量为例,用户可能从多种设备或多种测试条件中选择其一,程序随后进入对应的测量循环,对被测对象逐点扫描并得到一系列数据点。此时一个很自然的问题是:不同测试用例产生的数据,能否汇入同一个波形图控件进行显示?
直觉的实现方式是为每个测试用例单独放置一个波形图,在各自的用例分支内完成绘制。这种做法虽然简单直接,却会带来明显的弊端:随着测试类型增多,前面板上会出现大量内容重复的图表,界面空间被严重浪费;每个图表各自维护坐标范围、显示外观与曲线样式,统一管理十分困难;更重要的是,各图表之间相互孤立,数据散落在不同控件中,无法横向对比,后续处理也相当不便。
当被测对象本身需要连续多次测量、测量程序已经处于循环之中时,问题会进一步加剧。测量循环每迭代一次就要刷新一次图表,而用户切换测试用例又发生在不同时刻,如何在循环不断迭代、用例不断切换的情况下,让同一个图表持续正确地更新,成为设计上的关键难点。
二、根因分析
之所以会出现"每个用例一张图"的困境,根源在于把显示控件错误地当成了用例内部的局部资源。波形图控件位于前面板,本质上是程序与用户之间共享的资源,一旦被放进某个用例分支内部,它就成了该分支私有的显示出口,其它用例自然无法访问,只能各自复制一份。
更深层的原因在于对数据传递机制的理解不足。波形图在循环中需要持续更新,而数据产生的位置(测量循环内部)与显示的位置(前面板控件)之间隔着循环边界与用例分支。跨过这条边界传递数据,必须借助LabVIEW专门的数据流机制,例如反馈节点、移位寄存器、队列或局部变量。如果对这些机制缺乏认识,就容易退回到"复制控件"这种最笨拙的方案。
此外,很多程序在架构上把所有逻辑堆在一个循环里,把界面控件当作全局变量使用,既容易引发读写竞态,也使数据流向变得混乱,最终导致共享显示无从谈起。要解决这个问题,关键是把"测量产生数据"与"图表显示数据"两个职责分离开来。
三、共享显示的几种实现方案
针对"多个用例共享一个图表"的需求,有以下几种经过实践验证的方案。
方案一是独立显示循环配合队列。在程序中单独开辟一个循环专门负责更新波形图,各测试序列通过队列把测量数据发送给这个显示循环。显示循环不断从队列中取出数据并更新图表,与测量循环互不干扰。这种方案把采集与显示彻底解耦,测量循环可以任意切换用例,只要数据送入队列,图表就能持续更新,不会因某个用例的阻塞而中断。
方案二是利用反馈节点在测量循环内累积数据。反馈节点会在本次迭代结束时把输入值暂存起来,并在下一次迭代开始时把暂存值送回上一次连线的位置,从而在循环内形成数据的自我引用与历史累积。在测量循环中,每次完成一次扫描后,把新测量的波形数据与反馈节点输出的历史数据合并,再送入波形图更新显示。如此每迭代一次,图表就追加一个数据点,所有用例分支都可以把各自的测量结果汇入同一条反馈通道。需要注意,反馈节点的初始化端应连接到空数组常量,以保证首次迭代从空数据开始,避免图表第一帧出现异常。
方案三是移位寄存器配合事件结构。将测量数据保存在移位寄存器中,当检测到数值变化事件时,再把最新数据写入波形图。事件结构会在相关控件值发生变化时触发,此时将寄存器中的最新测量结果推送到图表。这种方案同样实现了"采集"与"显示"的解耦:数据随循环流动,显示仅在需要刷新时进行,避免高频采集下图表控件的无谓刷新。
方案四是将多个图表组成数组或簇数组,通过切换显示索引来展示对应测试的数据。该方法把若干图表控件放入数组,每个用例对应一个索引,用户选择测试时即切换当前显示的那个图表。这种方案适合曲线数量固定、每个测试的数据结构差异较大的场景,界面仍能保持整洁。
四、程序整体架构的选择
上述方案解决了数据传递问题,但要支撑"用户选择多个测试、数据全部进入同一张图"的完整需求,还必须在整体架构上作出安排。实践中常见三种架构,改造工作量由小到大排列。
状态机架构以程序状态作为主要控制信息,每个状态对应一类操作。可以在空闲状态中放置事件结构处理界面操作,在其它状态中执行测量。状态集合推荐使用类型定义的枚举常量进行管理,后续新增测试类型时只需扩展枚举并添加对应分支,代码的模块化程度会显著提高。状态机的实现形式有直接式、数组式、队列式和事件式之分,其中直接式结构最简单,通常已能满足需求,必要时可以平滑迁移到其它形式。
事件驱动架构以事件结构为主控制器,在超时分支或用户事件分支中执行测量,适合界面交互密集的应用。生产者消费者架构则把采集与显示分离到两个不同循环中,采集循环负责测量并写入队列,显示循环从队列取出数据更新图表,两个循环通过队列解耦、速率彼此独立。三者之中生产者消费者架构的改造量最大,但灵活性与健壮性也最好,尤其适合高数据率场景。
三种架构有一条共同的核心原则:主循环中的状态与数据都应通过移位寄存器传递,而不是借助大量的前面板控件或局部变量。这样既能避免不必要的控件读写与数据副本,也能让数据流清晰直观,便于维护和扩展。
五、易错点与常见误区
共享显示的实现过程中,有几个易错点需要特别注意。
第一,把波形图控件直接放进用例分支内部,这会使图表随用例数量成倍增加,是首要避免的做法。第二,忘记给反馈节点连接初始化端,导致首次迭代读到的历史数据是默认值,图表第一帧出现异常。第三,多个循环同时读写同一个前面板控件,容易引发竞态条件,数据更新可能出现丢失或覆盖。第四,滥用局部变量和全局变量传递测量数据,虽然局部变量可以跨循环读取控件值,但它引入的隐式数据流难以追踪,是程序难以维护的常见原因。第五,高频采集时在每次迭代中都强制刷新图表,可能拖慢测量循环,正确做法是仅在数据发生变化时刷新,或在独立的显示循环中更新。
六、实践建议与小结
在实际项目中,可以遵循以下建议。第一,优先把波形图从用例分支中移出,放到主循环或独立的显示循环中,让每个用例只负责产生数据,而不是负责绘制。第二,若测量速率较高,优先采用生产者消费者架构,通过队列在采集与显示之间传递数据,即使测量循环短暂阻塞,显示也不会丢帧。第三,状态集合使用类型定义枚举管理,新增测试类型只需扩展枚举并添加对应分支,无需改动原有逻辑。第四,尽量把状态与数据保存在主循环的移位寄存器中,减少对控件的读写,保持数据通路清晰。
从根本上看,波形图控件的数量不应随测试类型增长,而应保持唯一,由数据通路决定显示内容。把"测量逻辑"与"显示逻辑"分离,用队列、移位寄存器或反馈节点这类数据机制完成跨用例的数据汇聚,才能得到界面简洁、扩展方便、易于维护的测试程序。