【LabVIEW】新手如何分析Actor Framework的范例反馈蒸发冷凝系统

这个例程很适合入门 Actor Framework(AF) ,因为它不是"纯教学骨架",而是一个能跑起来的完整小系统:有应用入口、有主 Actor、有子 Actor、有消息类、有 UI、还有"模拟/硬件"两套实现。

我先用新手视角 帮你建立一张"脑图",先看懂整体,再告诉你应该按什么顺序读

另外,我也给你放几个你现在最值得打开的项目链接:

  • Air Cooler Application.lvlib:Load App.vi
  • Air Cooler Application.lvlib:Air Cooler Application.lvclass:Actor Core.vi
  • Air Cooler Application.lvlib:Air Cooler Application.lvclass:Read Config File.vi
  • Timed Loop Actor.lvlib:Timed Loop Actor.lvclass:Actor Core.vi
  • Air Cooler Application.lvlib:Air Cooler Application.lvclass:Swap Controllers.vi

一句话先讲清:这个例程到底在演示什么?

这个例程演示的是:

核心点 这个例程怎么演示
Actor 启动与层级 应用 Actor 启动 Cooler 和 Controller 两类子 Actor
消息通信 Controller 发命令给 Cooler;Cooler 把状态回报给 Controller
动态替换模块 可以切换不同 Controller
继承与覆写 基类定义通用行为,子类实现具体逻辑
插件化 Cooler 可换成 SimulatedHW 实现
UI 与 Actor 解耦 UI Controller 自己是一个 Actor,通过消息与系统通信

先建立一个系统图 🧭

你可以把整个系统理解成下面这几个角色:

角色 你可以怎么理解
总控/应用壳 Air Cooler Application 系统老板,负责启动、连接、切换
被控对象 Cooler / Evaporative Cooler 真正执行"降温"的设备侧 Actor
控制器 Cooler Controller / Evaporative Cooler Controller 发控制命令、接收状态
UI 控制器 Live User Controller 人通过界面调温度、看状态
程序控制器 Programmatic Controller 不靠人,按预设步骤改温度
子系统 Dual Fan / Water Level 风扇、水位这些子设备
周期执行框架 Timed Loop Actor 给很多 Actor 提供"定时更新"的骨架

这个例程最重要的 AF 思想

1. Actor 不是"一个 while loop"那么简单

在 AF 里,一个 Actor 本质上是:

  • 一个类对象
  • 带一个私有状态数据
  • 有自己的消息队列
  • Actor Core.vi 里跑生命周期
  • 通过 Message 收命令

所以你看这个例程时,不要只盯着框图逻辑,要同时看三件事:

看什么 为什么
Actor Core.vi 看这个 Actor 怎么启动、怎么活着、怎么停
这个类的私有数据 看它"记住"了什么状态
消息类(Msg) 看别人怎么驱动它

2. 消息是 AF 的"函数调用替代品"

传统写法常是:

  • A 直接调用 B 的 VI

AF 更常见的是:

  • A 发消息给 B
  • B 在自己的线程/执行上下文里处理

这意味着:

传统调用 AF 消息
立即执行 异步排队执行
调用方控制节奏 接收方按自己的消息队列节奏处理
耦合更紧 耦合更松

这个例程里典型例子就是:

  • Controller → Cooler:写目标温度
  • Cooler → Controller:上报当前温度
  • Application → Controller:告诉它该控制哪个 Cooler
  • Application → Cooler:告诉它该把状态发给哪个 Controller

3. Application Actor 是"组装工厂"

这个例程里最关键的认知是:

Air Cooler Application 不负责直接做冷却控制逻辑,

它主要负责 启动对象、连接对象、切换对象

所以它更像:

  • 系统装配器
  • 路由器
  • 顶层协调者

推荐你按这个顺序读,不容易乱

第一阶段:只看"系统怎么启动"

先看这 3 个:

顺序 VI 作用
1 Load App.vi 应用入口
2 Read Config File.vi 读配置,决定加载什么
3 Actor Core.vi 真正启动 Cooler 和 Controller

你在这一阶段要回答 4 个问题:

问题 重点
谁是 root actor? Load App.vi 里会看到
配置文件决定了什么? 主要是 CoolerType
Cooler 是怎么被实例化的? 动态加载/默认类值/插件目录
Controller 是怎么被启动并接线的? Launch Nested Actor 和若干 Send ... Msg

第二阶段:只看"Actor 怎么周期运行"

这个例程里很多对象都站在 Timed Loop Actor 这个基类上。

先看:

  • Timed Loop Actor.lvlib:Timed Loop Actor.lvclass:Actor Core.vi
  • Timed Loop Actor.lvlib:Timed Loop Actor.lvclass:Update.vi
  • Timed Loop Actor.lvlib:Update Msg.lvclass:Do.vi

这一块你要理解的是:

概念 在这个例程里什么意思
Actor Core 建一个定时循环,不断给自己发 Update Msg
Update Msg 一个"发给自己"的内部消息
Update.vi 每个周期要执行的动作入口
Update Core.vi 子类覆写的核心业务逻辑

这个设计非常 AF:

定时机制业务逻辑 被分开了。

也就是说:

  • 父类负责"多久执行一次"
  • 子类负责"每次执行干什么"

这就是面向对象 + Actor 的味道。


第三阶段:只看"命令怎么走、状态怎么回"

这一步是新手最容易突然开窍的地方。

主命令链:Controller → Cooler

比如用户或程序想修改目标温度:

步骤 内容
1 Controller 侧触发"改目标温度"
2 Change Desired Temperature Msg 给自己或内部逻辑
3 Controller 再发 Write Desired Temperature Msg 给 Cooler
4 Cooler 更新自己的目标温度状态

状态回报链:Cooler → Controller

例如 Cooler 测到当前温度:

步骤 内容
1 Cooler 周期更新
2 读取当前温度
3 Update Current Temperature Msg 给 Controller
4 Controller 更新 UI 或内部逻辑

你要抓住一个关键:

命令与状态是双向分离的。

方向 典型消息
控制命令 Write Desired Temperature Msg
状态上报 Update Current Temperature MsgUpdate Fan Status MsgUpdate Water Level Msg

这在测试系统架构里很常见,也很有价值。


第四阶段:看"继承层次",别一开始就钻细节

这个项目的类层次比较清晰:

基类 子类 作用
Timed Loop Actor Level Controller、可能其他周期性 Actor 周期执行骨架
Level Controller Cooler、Water Level 带死区控制逻辑
Cooler Evaporative Cooler 一般冷却器 → 蒸发式冷却器
Cooler Controller Evaporative Cooler Controller 一般控制器 → 蒸发式控制器
Evaporative Cooler Controller Live User Controller 带 UI 的控制器
Cooler / Fan / Water Level Simulated / HW 子类 模拟实现或硬件实现

这个例程里几个特别值得你学的点 ⭐

A. 消息类分成两种:给自己、给别人

例如:

类型 例子 用途
内部消息 Update MsgSwap Controllers Msg Actor 自己调度自己
外部交互消息 Write Desired Temperature MsgUpdate Current Temperature Msg Actor 之间通信

这会让系统结构更清楚。


B. 抽象消息用于"回调式上报"

例如:

  • Dual Fan 有一个抽象的 Update Fan Status Msg
  • 调用它的上层 Actor 可以提供一个子类消息
  • 这样 Dual Fan 不需要知道调用者到底是谁

这其实是在做一种 面向对象的回调协议

这个思想非常 AF,也非常值钱。

好处 解释
低耦合 Fan 不需要依赖具体 caller 类型
可复用 同一个 Fan Actor 能服务不同上层
可扩展 新 caller 只要定义新的消息子类即可

C. Application 负责"交换控制权"

你项目里有:

  • Swap Controllers.vi
  • Air Cooler Application.lvlib:Swap Controllers Msg.lvclass:Do.vi

它展示了一个重要能力:

系统运行中,可以替换控制器,而不用重写 Cooler。

这就是 AF 特别擅长的地方之一:替换一个角色,而不是重写整个流程。


新手看这个例程,最容易误解的地方

误区 实际上应该怎么理解
Actor Core.vi 就是全部逻辑 不对,它更像生命周期入口和协调点
消息类只是"包装参数" 不止,消息类也是行为分发机制的一部分
父类没什么用,直接看子类 反而通常先看父类才知道子类在覆写什么
一个 Actor 只对应一个 VI 实际上是"一组类 + 私有数据 + 消息 + 覆写方法"
UI 是主程序 在 AF 里,UI 往往只是某个 Actor 的一种表现层

我建议你的阅读顺序(最适合新手)

第几步 看什么 目标
1 Load App.vi 看 root actor 怎么起来
2 Read Config File.vi 看插件/配置怎么决定系统组成
3 Air Cooler Application.Actor Core.vi 看顶层怎么 launch 和接线
4 Timed Loop Actor.Actor Core.vi 看"周期更新"这个骨架
5 Cooler Controller 相关消息 看命令怎么发到 Cooler
6 Cooler / Evaporative Cooler 看设备侧怎么更新和上报
7 Live User Controller 看 UI 如何与 Actor 结合
8 Swap Controllers.vi 看动态切换控制器

如果你现在只想"先看懂 60%",抓这几个关键词就够了

关键词 你先这样理解
Actor 一个有状态、有队列、能收消息的对象
Message 发给 Actor 的异步命令
Actor Core Actor 的主生命周期
Nested Actor 被另一个 Actor 启动的子 Actor
Enqueuer 给某 Actor 发消息的"投递口"
Caller Enqueuer / Self Enqueuer 发给调用者 / 发给自己
Override 子类替换父类默认行为
Abstract Message 为不同接收者定制的消息接口

你现在最值得先打开的 3 个文件

优先级 文件 为什么
1 Air Cooler Application.lvlib:Air Cooler Application.lvclass:Actor Core.vi 它是全局总装配点
2 Timed Loop Actor.lvlib:Timed Loop Actor.lvclass:Actor Core.vi 它解释周期型 Actor 的模式
3 Air Cooler Application.lvlib:Air Cooler Application.lvclass:Swap Controllers.vi 它能帮你理解 Actor 之间如何重新接线

我对你的建议:先别急着"全部看懂"

新手看 AF,最有效的方式不是逐个 VI 全看完,而是分三层:

先回答的问题
系统层 谁启动谁?谁管谁?
通信层 谁给谁发什么消息?
实现层 某个消息收到后具体做什么?

你先把前两层看懂,AF 就已经入门了。

Actor 启动时序图

下面这个时序图基于这个例程里的启动路径整理,重点看 Application Launcher → Load App → Air Cooler Application → Cooler / Controller 的启动关系。

1. 顶层启动时序

Controller Actor Cooler Actor Air Cooler Application Actor Actor Framework Read Config File.vi Load App.vi Splash Screen.vi 用户/EXE Controller Actor Cooler Actor Air Cooler Application Actor Actor Framework Read Config File.vi Load App.vi Splash Screen.vi 用户/EXE #mermaid-svg-nLxZWfXxnhcoUfov{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nLxZWfXxnhcoUfov .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nLxZWfXxnhcoUfov .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nLxZWfXxnhcoUfov .error-icon{fill:#552222;}#mermaid-svg-nLxZWfXxnhcoUfov .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nLxZWfXxnhcoUfov .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nLxZWfXxnhcoUfov .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nLxZWfXxnhcoUfov .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nLxZWfXxnhcoUfov .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nLxZWfXxnhcoUfov .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nLxZWfXxnhcoUfov .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nLxZWfXxnhcoUfov .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nLxZWfXxnhcoUfov .marker.cross{stroke:#333333;}#mermaid-svg-nLxZWfXxnhcoUfov svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nLxZWfXxnhcoUfov p{margin:0;}#mermaid-svg-nLxZWfXxnhcoUfov .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nLxZWfXxnhcoUfov text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-nLxZWfXxnhcoUfov .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-nLxZWfXxnhcoUfov .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-nLxZWfXxnhcoUfov .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-nLxZWfXxnhcoUfov .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-nLxZWfXxnhcoUfov #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-nLxZWfXxnhcoUfov .sequenceNumber{fill:white;}#mermaid-svg-nLxZWfXxnhcoUfov #sequencenumber{fill:#333;}#mermaid-svg-nLxZWfXxnhcoUfov #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-nLxZWfXxnhcoUfov .messageText{fill:#333;stroke:none;}#mermaid-svg-nLxZWfXxnhcoUfov .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nLxZWfXxnhcoUfov .labelText,#mermaid-svg-nLxZWfXxnhcoUfov .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-nLxZWfXxnhcoUfov .loopText,#mermaid-svg-nLxZWfXxnhcoUfov .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-nLxZWfXxnhcoUfov .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-nLxZWfXxnhcoUfov .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-nLxZWfXxnhcoUfov .noteText,#mermaid-svg-nLxZWfXxnhcoUfov .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-nLxZWfXxnhcoUfov .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nLxZWfXxnhcoUfov .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nLxZWfXxnhcoUfov .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nLxZWfXxnhcoUfov .actorPopupMenu{position:absolute;}#mermaid-svg-nLxZWfXxnhcoUfov .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-nLxZWfXxnhcoUfov .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nLxZWfXxnhcoUfov .actor-man circle,#mermaid-svg-nLxZWfXxnhcoUfov line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-nLxZWfXxnhcoUfov :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动程序调用 Load App.vi发送通知 "Reading Configuration"读取 Air Cooler App.ini返回配置好的 Air Cooler Application 对象Launch Root Actor(App)启动 Air Cooler Application.Actor Core.viLaunch Nested Actor(Cooler)启动 Cooler ActorLaunch Nested Actor(Controller)启动 Controller ActorSend Write Cooler EnqueuerSend Write Controller Enqueuer系统进入运行态

  1. 更贴近代码的启动细节
    这个图强调 谁创建谁、谁把 Enqueuer 传给谁。

Controller Actor Cooler Actor Air Cooler Application.Actor Core Launch Root Actor Air Cooler Application对象 Load App.vi Controller Actor Cooler Actor Air Cooler Application.Actor Core Launch Root Actor Air Cooler Application对象 Load App.vi #mermaid-svg-Fi6aRFkNX3ioMwbk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Fi6aRFkNX3ioMwbk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Fi6aRFkNX3ioMwbk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Fi6aRFkNX3ioMwbk .error-icon{fill:#552222;}#mermaid-svg-Fi6aRFkNX3ioMwbk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Fi6aRFkNX3ioMwbk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Fi6aRFkNX3ioMwbk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Fi6aRFkNX3ioMwbk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Fi6aRFkNX3ioMwbk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Fi6aRFkNX3ioMwbk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Fi6aRFkNX3ioMwbk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Fi6aRFkNX3ioMwbk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Fi6aRFkNX3ioMwbk .marker.cross{stroke:#333333;}#mermaid-svg-Fi6aRFkNX3ioMwbk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Fi6aRFkNX3ioMwbk p{margin:0;}#mermaid-svg-Fi6aRFkNX3ioMwbk .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fi6aRFkNX3ioMwbk text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Fi6aRFkNX3ioMwbk .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Fi6aRFkNX3ioMwbk .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Fi6aRFkNX3ioMwbk .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Fi6aRFkNX3ioMwbk .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Fi6aRFkNX3ioMwbk #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Fi6aRFkNX3ioMwbk .sequenceNumber{fill:white;}#mermaid-svg-Fi6aRFkNX3ioMwbk #sequencenumber{fill:#333;}#mermaid-svg-Fi6aRFkNX3ioMwbk #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Fi6aRFkNX3ioMwbk .messageText{fill:#333;stroke:none;}#mermaid-svg-Fi6aRFkNX3ioMwbk .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fi6aRFkNX3ioMwbk .labelText,#mermaid-svg-Fi6aRFkNX3ioMwbk .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Fi6aRFkNX3ioMwbk .loopText,#mermaid-svg-Fi6aRFkNX3ioMwbk .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Fi6aRFkNX3ioMwbk .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Fi6aRFkNX3ioMwbk .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Fi6aRFkNX3ioMwbk .noteText,#mermaid-svg-Fi6aRFkNX3ioMwbk .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Fi6aRFkNX3ioMwbk .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fi6aRFkNX3ioMwbk .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fi6aRFkNX3ioMwbk .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fi6aRFkNX3ioMwbk .actorPopupMenu{position:absolute;}#mermaid-svg-Fi6aRFkNX3ioMwbk .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Fi6aRFkNX3ioMwbk .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fi6aRFkNX3ioMwbk .actor-man circle,#mermaid-svg-Fi6aRFkNX3ioMwbk line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Fi6aRFkNX3ioMwbk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 从 ini 读取 CoolerType 等配置 应用先拿到自己的消息入口 Cooler 可能是 Simulated 或 HW 插件实例 默认可为 Live User Controller 或 Programmatic Controller 告诉 Controller "你该控制谁" 告诉 Cooler "状态该回报给谁" Read Config File.viLaunch Root Actor(AppCfg)调用 Actor Core.viRead Self EnqueuerLaunch Nested Actor(Cooler instance)Launch Nested Actor(Controller instance)Write Cooler Enqueuer MsgWrite Controller Enqueuer Msg保存 Controller Enqueuer / Cooler Enqueuer / SubPanel

  1. 如果启动的是带 UI 的 Controller
    这个图是 Live User Controller 启动时额外发生的事情。

Application SubPanel Load Into Sub Panel Msg Live User Controller Air Cooler Application Application SubPanel Load Into Sub Panel Msg Live User Controller Air Cooler Application #mermaid-svg-kSI9pQtWHEjPDhAo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kSI9pQtWHEjPDhAo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kSI9pQtWHEjPDhAo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kSI9pQtWHEjPDhAo .error-icon{fill:#552222;}#mermaid-svg-kSI9pQtWHEjPDhAo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kSI9pQtWHEjPDhAo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kSI9pQtWHEjPDhAo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kSI9pQtWHEjPDhAo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kSI9pQtWHEjPDhAo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kSI9pQtWHEjPDhAo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kSI9pQtWHEjPDhAo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kSI9pQtWHEjPDhAo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kSI9pQtWHEjPDhAo .marker.cross{stroke:#333333;}#mermaid-svg-kSI9pQtWHEjPDhAo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kSI9pQtWHEjPDhAo p{margin:0;}#mermaid-svg-kSI9pQtWHEjPDhAo .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kSI9pQtWHEjPDhAo text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-kSI9pQtWHEjPDhAo .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kSI9pQtWHEjPDhAo .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-kSI9pQtWHEjPDhAo .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-kSI9pQtWHEjPDhAo .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-kSI9pQtWHEjPDhAo #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-kSI9pQtWHEjPDhAo .sequenceNumber{fill:white;}#mermaid-svg-kSI9pQtWHEjPDhAo #sequencenumber{fill:#333;}#mermaid-svg-kSI9pQtWHEjPDhAo #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-kSI9pQtWHEjPDhAo .messageText{fill:#333;stroke:none;}#mermaid-svg-kSI9pQtWHEjPDhAo .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kSI9pQtWHEjPDhAo .labelText,#mermaid-svg-kSI9pQtWHEjPDhAo .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-kSI9pQtWHEjPDhAo .loopText,#mermaid-svg-kSI9pQtWHEjPDhAo .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-kSI9pQtWHEjPDhAo .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kSI9pQtWHEjPDhAo .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-kSI9pQtWHEjPDhAo .noteText,#mermaid-svg-kSI9pQtWHEjPDhAo .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-kSI9pQtWHEjPDhAo .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kSI9pQtWHEjPDhAo .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kSI9pQtWHEjPDhAo .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kSI9pQtWHEjPDhAo .actorPopupMenu{position:absolute;}#mermaid-svg-kSI9pQtWHEjPDhAo .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-kSI9pQtWHEjPDhAo .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kSI9pQtWHEjPDhAo .actor-man circle,#mermaid-svg-kSI9pQtWHEjPDhAo line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-kSI9pQtWHEjPDhAo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 把 Controller 的前面板嵌入到应用 SubPanel Launch Nested Actor(Live User Controller)Send Load Into Sub Panel Msg(VI Ref)Insert VI

  1. 启动后,周期型 Actor 怎么开始跑
    这个项目里很多 Actor 继承自 Timed Loop Actor。
    所以一个子 Actor 启动后,通常还会进入下面这种内部节奏:

Update.vi Update Msg Self Enqueuer Timed Loop Actor.Actor Core Actor Framework Update.vi Update Msg Self Enqueuer Timed Loop Actor.Actor Core Actor Framework #mermaid-svg-DDSIZZyZXKKCvY4d{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DDSIZZyZXKKCvY4d .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DDSIZZyZXKKCvY4d .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DDSIZZyZXKKCvY4d .error-icon{fill:#552222;}#mermaid-svg-DDSIZZyZXKKCvY4d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DDSIZZyZXKKCvY4d .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DDSIZZyZXKKCvY4d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DDSIZZyZXKKCvY4d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DDSIZZyZXKKCvY4d .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DDSIZZyZXKKCvY4d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DDSIZZyZXKKCvY4d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DDSIZZyZXKKCvY4d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DDSIZZyZXKKCvY4d .marker.cross{stroke:#333333;}#mermaid-svg-DDSIZZyZXKKCvY4d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DDSIZZyZXKKCvY4d p{margin:0;}#mermaid-svg-DDSIZZyZXKKCvY4d .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DDSIZZyZXKKCvY4d text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-DDSIZZyZXKKCvY4d .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-DDSIZZyZXKKCvY4d .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-DDSIZZyZXKKCvY4d .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-DDSIZZyZXKKCvY4d .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-DDSIZZyZXKKCvY4d #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-DDSIZZyZXKKCvY4d .sequenceNumber{fill:white;}#mermaid-svg-DDSIZZyZXKKCvY4d #sequencenumber{fill:#333;}#mermaid-svg-DDSIZZyZXKKCvY4d #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-DDSIZZyZXKKCvY4d .messageText{fill:#333;stroke:none;}#mermaid-svg-DDSIZZyZXKKCvY4d .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DDSIZZyZXKKCvY4d .labelText,#mermaid-svg-DDSIZZyZXKKCvY4d .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-DDSIZZyZXKKCvY4d .loopText,#mermaid-svg-DDSIZZyZXKKCvY4d .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-DDSIZZyZXKKCvY4d .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-DDSIZZyZXKKCvY4d .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-DDSIZZyZXKKCvY4d .noteText,#mermaid-svg-DDSIZZyZXKKCvY4d .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-DDSIZZyZXKKCvY4d .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DDSIZZyZXKKCvY4d .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DDSIZZyZXKKCvY4d .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DDSIZZyZXKKCvY4d .actorPopupMenu{position:absolute;}#mermaid-svg-DDSIZZyZXKKCvY4d .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-DDSIZZyZXKKCvY4d .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DDSIZZyZXKKCvY4d .actor-man circle,#mermaid-svg-DDSIZZyZXKKCvY4d line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-DDSIZZyZXKKCvY4d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop每个周期 启动某个 Timed Loop 子类 Actor读取 Poll RateSend Update Msg消息入队调用 Do.vi ->> Update.vi执行子类 Update Core 逻辑

你可以把启动过程记成 4 句话

步骤 发生了什么

1 Load App.vi 读取配置,并启动 Air Cooler Application 作为 Root Actor

2 Air Cooler Application.Actor Core.vi 启动 Cooler Actor

3 同一个 Actor Core.vi 再启动 Controller Actor

4 Application 把双方的 Enqueuer 互相下发,建立"控制命令"和"状态回报"的通信链路

这张图里最关键的 AF 知识点

概念 在本例中的体现

Root Actor Load App.vi 通过 Launch Root Actor 启动 Air Cooler Application

Nested Actor Air Cooler Application 再启动 Cooler 和 Controller

Enqueuer Actor 之间不是直接调用方法,而是交换消息入口

启动即接线 Actor 启动后还不够,必须把通信队列交给对方

UI 也是 Actor 的一部分 Live User Controller 启动后把自己的 VI 装进 SubPanel

建议你接下来按这个顺序对照源码看

Load App.vi

Read Config File.vi

Air Cooler Application.Actor Core.vi

Cooler Controller.Write Cooler Enqueuer Msg

Cooler.Write Controller Enqueuer Msg

Live User Controller.Actor Core.vi

Timed Loop Actor.Actor Core.vi

相关推荐
LabVIEW开发21 小时前
LabVIEW 通过以太网控制 Yokogawa WT3000 功率分析仪:tmctl.dll 与原始套接字方案
网络·labview·labview知识·labview功能·labview程序
LabVIEW开发4 天前
LabVIEW 如何把地下震源定位误差做到5米内
fpga开发·labview·labview知识·labview功能·labview程序
LabVIEW开发5 天前
LabVIEW 把伺服调参从编程工作变成了点鼠标
labview·labview知识·labview功能·labview程序
fanchenxinok6 天前
基于图莫斯的CAN UDS诊断上位机-LabVIEW版本(十九):TOOMOSS_SID2F_InputOutputControl.vi — 输入输出控制
labview·uds诊断
9稳13 天前
基于组态的双向人行道交通灯控制系统设计
开发语言·网络·数据库·labview·plc
LabVIEW开发13 天前
LabVIEW远程调试实战:从“被迫接受”到“高效真香”
labview
猎头南楼14 天前
新型电力系统下的储能控制仿真:从策略研发到工程落地的关键技术挑战
labview
fanchenxinok14 天前
基于图莫斯的CAN UDS诊断上位机-LabVIEW版本(十七):TOOMOSS_SID19_ReadDTCInformation.vi — 读取DTC信息
labview·dtc·sid19
神电测控14 天前
新品发布:32通道50MS/s同步并行14位采集模块PG-FMC9249(支持任意FPGA/ZYNQ,兼容LabVIEW FPGA软件开发)
fpga开发·labview·ni·神电测控·王电令·ad9249·32通道