20 · 程序集速查表
相关:README 的四层架构图 · 第 03 章 我该从哪一章开始看
这一章是什么
一页看全 15 个程序集。 查不到东西的时候翻这里。
每条包含:在哪一层、依赖谁、核心类型、对应的章。
一、四层总览
第 4 层 宿主 / 集成层 ── 已经装好的那套东西
Hmi.Shell(WPF 壳) Hmi.HostBridge(消息路由)
Hmi.Mcp.Host Hmi.RuntimeCore.Host Hmi.Supervisor
↑ 依赖
第 3 层 组合层 ── 把下面的拼成完整能力
Hmi.Automation(组态 API) Hmi.Backend(运行引擎) Hmi.ScriptRuntime(脚本宿主)
↑ 依赖
第 2 层 领域库层 ── 各自管一块
Hmi.Project Hmi.Drivers Hmi.Scripting
Hmi.Historian Hmi.Alarms Hmi.Security
↑ 依赖
第 1 层 内核层 ── 最底下的地基
Hmi.Core(实时库 + 点模型 + 变化分发)
规则:上层能用下层,下层不认识上层。所以 Hmi.Core 不知道什么是"工程"。
二、15 个程序集逐条
第 1 层 · 内核层
Hmi.Core
| 一句话 | 实时库 + 点模型 + 变化分发。所有东西的地基 |
| 依赖 | 无(最底层) |
| 目标框架 | net8.0 |
| 章 | 04 · 05 |
核心类型(15 个公开类型):
| 类型 | 干什么 |
|---|---|
RtdbCore |
实时库 —— 内存里的点值表。按 Id 读写,订阅变化 |
Tag |
一个点的运行时形态(值 + 质量 + 时间戳) |
TagValue |
值(联合体,能装 double/int/bool/string) |
Quality |
质量戳(良好/坏/不确定) |
TagDataType |
数据类型枚举 |
TagClassification |
点的分类(采集点 / 内部变量) |
DeliveryMode |
投递车道:Droppable(界面用)/ Lossless(报警历史用) |
ITagSubscriber |
订阅者接口,OnTagChanged(batch) |
ChangeDispatcher |
变化分发器 |
SubscriptionRegistry |
订阅登记 |
ByteOrderCodec / ScalingHelper / DeadbandHelper |
字节序 / 量程换算 / 死区 |
UiStateStore |
界面状态 |
Branding |
品牌信息 |
⭐
DeliveryMode是理解实时库的关键:界面订阅用Droppable(堵了就丢,保最新); 报警和历史用Lossless(一条都不能少)。两条车道分开,互不影响。
第 2 层 · 领域库层
Hmi.Project
| 一句话 | 工程模型 + 磁盘读写。工程文件长什么样就是它定的 |
| 依赖 | Hmi.Core、Drivers.Abstractions |
| 公开类型数 | 108(最多的一个) |
| 章 | 06 |
核心类型:
| 类型 | 干什么 |
|---|---|
ProjectLoader |
加载/保存工程目录。Load(dir) / Save(proj, dir) |
LoadedProject |
内存里的完整工程(Project + Screens + Tags + Channels + …) |
ProjectModel |
工程元信息(名/版本/启动画面) |
ScreenModel |
一个画面 |
ElementModel |
元素基类(30 多个派生:Button / Bar / Trend / Tank / Valve …) |
TagDef |
点的定义(不是值) |
ChannelDef / DeviceDef |
通道 / 设备定义 |
RecipeModel / RecipeItem |
配方 |
AlarmConfigModel / HistoryConfigModel |
报警配置 / 历史配置 |
ProjectScripts / ScreenScripts / ElementEvents |
三个层级的脚本 |
ScriptModule |
脚本模块 |
ReportModel |
报表定义 |
LanguageTable / TranslationEntry |
多语言 |
ProjectArchive / ProjectPackage |
.hmiproj 归档 / .hmipkg 封包 |
RtdbBridge |
把 TagDef[] 建成 Tag[] 给实时库 |
CustomButtonSkin |
自定义按钮皮肤 |
NotifyConfig / SmtpMailSender |
邮件通知 |
Hmi.Drivers
| 一句话 | 采集框架 —— 采集循环、块读规划、重连、插件加载 |
| 依赖 | Hmi.Core、Drivers.Abstractions |
| 章 | 07 · 08 · 09 |
核心类型:
| 类型 | 干什么 |
|---|---|
ChannelRuntime |
一个通道的运行时(连设备、跑采集循环) |
ChannelScheduler |
调度多个通道 |
BlockPlanner / BlockPlan |
块读规划 —— 把散点合并成连续块一次读 |
BackoffPolicy |
重连退避策略 |
ProtocolRegistry |
协议登记表 |
ProtocolPluginLoader / PluginManifest |
插件加载 / plugin.json 清单 |
SimulationDriver / SimulationPackage |
内置模拟驱动 |
Unpacker |
拆包 |
⚠
Hmi.Drivers只是框架。真正的协议实现在Drivers.*子项目里(见第四节)。
Drivers.Abstractions
| 一句话 | 写驱动要实现的接口都在这 |
| 依赖 | Hmi.Core |
| 章 | 07 · 09 |
核心类型:
| 类型 | 干什么 |
|---|---|
IProtocolDriver |
驱动主接口:连接、读块、写、断开 |
IProtocolPackage |
插件包接口:声明自己提供哪些驱动 |
IAddressParser |
地址解析(把 "40001+2" 解析成寄存器号) |
IConnectionSchemaProvider |
声明支持哪些连接方式(TCP/串口)及参数 |
ProtocolLimits |
协议限制(一次最多读多少寄存器/位) |
ProtocolAbi |
ABI 版本 —— Version = 1。版本不匹配的插件拒载 |
DeviceConfig / ChannelConfig |
设备/通道配置 |
ConnectionKind / ConnectionParam / TransportSchema |
连接方式与参数 |
RegisterWidth / ByteOrderCodec |
寄存器宽度 / 字节序 |
WriteSource |
写来源(区分谁写的) |
⭐
ProtocolAbi.Version是插件兼容性的关键。宿主 ABI 版本变了, 老插件会被拒绝加载(而不是崩溃)。
Hmi.Scripting
| 一句话 | 脚本引擎 —— 编译脚本、拦住危险操作、给脚本提供函数库 |
| 依赖 | Hmi.Core |
| 公开类型数 | 36 |
| 章 | 11 · 13 |
核心类型:
| 类型 | 干什么 |
|---|---|
ScriptEngine |
编译 + 执行脚本(Roslyn CSharpScript) |
ScriptSafetyAnalyzer |
安全检查 —— 黑名单拦危险 API |
ScriptGlobals |
脚本的全局上下文 |
ScriptCompileResult / ScriptRunResult / ScriptRunOutcome |
编译/运行结果(5 种结果之一) |
ScriptDiagnostic / ScriptDiagnosticSeverity |
编译诊断 |
ScriptApiUnavailableException |
"宿主没接上这个能力"(不是脚本写错了) |
| 16 个函数库门面 | TagApi LogApi SysApi Math2Api StringApi BitApi FileApi CsvApi TimerApi AlarmApi HistoryApi UiApi DbApi CommApi RecipeApi ReportApi |
| 对应的服务接口 | IAlarmService IHistoryService IUiService IDbService ICommService IRecipeService IReportService ITagGateway |
TagMeta |
点的元数据(单位/量程/可写) |
⭐
ScriptApiUnavailableException的语义很重要:它表示宿主没接上能力, 不是你的脚本写错了。看到这个异常要去查宿主装配,不要去改脚本。
Hmi.Historian
| 一句话 | 历史库 —— 把值记到 SQLite,按时间查回来 |
| 依赖 | Hmi.Core |
| 章 | 14 |
核心类型:
| 类型 | 干什么 |
|---|---|
IHistorian |
历史库接口(写记录 / 查范围 / 聚合) |
SqliteHistorian |
SQLite 实现。表 hist(tag_id, ts_utc, val, q),WAL 模式。攒批在这里:BatchSize=1000 / FlushIntervalMs=1000 |
HistorianRecorder |
规则判定器(RTDB 订阅者):按每点 TagHistoryRule 决定这条要不要落,落就交给 IHistorian。只做轻量判定 + 入队,重活在写线程 |
HistoryRecordMode |
记录模式枚举 |
TagHistoryRule |
记录规则:OnChange / Timed / Both |
AggFunc |
聚合函数:Avg / Max / Min / Sum / Count |
RetentionSweeper |
过期数据清理(按保留天数) |
IHistorianMaintenance |
维护接口 |
⭐ 攒批在
SqliteHistorian里,不在HistorianRecorder—— 两者职责别混:HistorianRecorder只判定"这条该不该记"(死区/定时),SqliteHistorian才负责攒够再刷盘。 一条一条写 SQLite 会慢死,所以攒到 1000 条或 1 秒批量写。
Hmi.Alarms
| 一句话 | 报警引擎 —— 判超限、管状态流转、记报警记录 |
| 依赖 | Hmi.Core |
| 章 | 14 |
核心类型:
| 类型 | 干什么 |
|---|---|
AlarmEngine |
报警引擎主体 |
AlarmEvaluationTicker |
周期评估(定时检查所有点) |
AlarmState |
状态机:Normal → ActiveUnacked → ActiveAcked → ClearedUnacked → Normal |
AlarmLevel |
级别 |
AnalogLimit / DigitalAlarm |
模拟量限值 / 开关量报警 |
TagAlarmConfig / AlarmConfigModel |
按点的报警配置 |
AlarmRecord / AlarmItem |
报警记录 |
IAlarmStore / InMemoryAlarmStore / SqliteAlarmStore |
存储(内存/持久) |
IClock / SystemClock |
时钟抽象(测试能拨快时间) |
AlarmConfigCsv |
配置 CSV 导入导出 |
Hmi.Security
| 一句话 | 账号、角色、权限。不依赖任何 Hmi.*,能单独摘出去用 |
| 依赖 | 无 |
| 章 | 19 |
核心类型(9 个,全在这里了):
| 类型 | 干什么 |
|---|---|
AuthService |
主入口:登录/登出/权限判定/账号管理 |
Permission |
8 个权限位的位标志枚举 |
Role |
角色(名 + 等级 + 权限位) |
UserAccount |
账号(带口令哈希,别整个序列化发前端) |
LoginCandidate |
登录器候选(只有 3 个字段,结构上不可能泄漏哈希) |
PasswordHasher |
PBKDF2-HMAC-SHA256,10 万次迭代 |
IUserStore / SqliteUserStore |
账号库存储 |
AuthResult |
结果(和 AutomationResult 同口径) |
第 3 层 · 组合层
Hmi.Automation
| 一句话 | 组态 + 感知的统一 API。界面/MCP/外部程序共用同一个入口 |
| 依赖 | Hmi.Core、Hmi.Project、Hmi.Drivers + 三个驱动实现 |
| 章 | 15 · 16 |
核心类型:
| 类型 | 干什么 |
|---|---|
ProjectAutomation |
实现类。Open(dir) 静态工厂 |
IProjectAutomation |
132 个方法的接口 |
AutomationResult |
统一返回 {Ok, Message, Data},失败不抛异常 |
AuditEntry |
审计记录(每个写操作一条) |
IScriptCompiler |
脚本编译校验器接口 |
ScriptFunctionCatalog |
脚本函数目录:15 库 / 176 函数 |
LibraryEntry / FunctionEntry |
目录的条目类型 |
⚠ 这个程序集刻意不引用
Hmi.Scripting—— 编译校验通过IScriptCompiler接口注入,避免循环依赖。
Hmi.Backend
| 一句话 | 运行数据引擎 —— 把工程真正跑起来 |
| 依赖 | Hmi.Core、Hmi.Project、Hmi.Drivers + 驱动实现 |
| 公开类型数 | 只有 3 个 |
| 章 | 10 |
核心类型(全在这了):
| 类型 | 干什么 |
|---|---|
RuntimeHost |
运行宿主:建实时库、起采集循环、写点、查在线 |
DriverFactory |
按协议名造驱动实例 |
IBackendDataService |
后端数据服务接口 |
⭐ 公开类型只有 3 个,但这是整个"让工程跑起来"的核心。
RuntimeHost的关键成员:Rtdb(实时库)、Start()/StopAsync()、WriteTag(name, value)、IsDeviceOnline(deviceName)、ReconnectChannel(name)、CycleOverrun事件。
Hmi.ScriptRuntime
| 一句话 | 运行时脚本宿主 —— 把脚本挂到工程上,周期跑、事件跑 |
| 依赖 | Hmi.Core、Hmi.Scripting、Hmi.Historian、Hmi.Alarms、Hmi.Project、Hmi.Automation |
| 公开类型数 | 29 |
| 章 | 12 |
核心类型:
| 类型 | 干什么 |
|---|---|
ScriptRuntimeHost |
脚本运行时宿主(起停、调度) |
ScriptRuntimeBuilder |
装配器 |
ScriptDispatcher |
脚本派发 |
RuntimeDataServices |
Compose(...) —— 一键叠上历史 + 报警 + 脚本 |
RoslynScriptChecker |
IScriptCompiler 的实现(给 AttachCompiler 用) |
RuntimeWriteGate |
⭐ 写门禁:脚本只放行内部变量,采集点写入抛异常 |
RtdbTagGateway |
脚本读写实时库的通道 |
LinkedVariableMirror |
连接变量镜像 |
HistorianHistoryService / AlarmEngineService |
给脚本用的历史/报警服务 |
ScriptUiService |
给脚本用的界面服务 |
SqliteDbService |
给脚本用的数据库服务 |
CommService / RecipeService / RecipeControlService |
通信/配方服务 |
AlarmNotifier |
报警往外发通知(邮件) |
ReportGenerator / ReportTable |
报表生成 |
ScriptModuleCompiler / ScriptValidator / ScriptBuildReport |
模块编译与校验 |
UiReferenceRewriter |
界面引用改写 |
ScriptLogSink |
日志转发 |
第 4 层 · 宿主 / 集成层
Hmi.HostBridge
| 一句话 | 前后端消息路由 —— 前端发来的 JSON 在这里分派给处理者 |
| 依赖 | Hmi.Automation、Hmi.Backend、Hmi.Core、Hmi.Historian、Hmi.Project、Hmi.Security |
| 公开类型数 | 33 |
| 章 | 17 |
核心类型:
| 类型 | 干什么 |
|---|---|
WebMessageRouter |
路由器:HandleInbound(json) 是唯一入口 |
WebMessage / WebJson |
消息信封 + 两套序列化配置 |
IWebMessageSink |
出站唯一出口(Post(json)) |
AutomationRpcDispatcher |
约 100 个组态 RPC |
AuthRpcDispatcher |
15 个认证/账号 RPC |
AiRpcDispatcher |
8 个 AI 助手 RPC |
McpControlRpcDispatcher |
5 个 MCP 控制 RPC |
TagSubscriptionBridge |
实时值订阅推送(tag-update) |
HistoryQueryBridge |
历史查询(query_history / aggregate_history) |
UiOverrideBridge |
界面覆盖配置 |
RuntimeLogWire |
运行态日志回流(##RTLOG## 哨兵) |
McpServerConfigStore / IMcpServerProcess / SystemMcpServerProcess |
MCP 子进程管理 |
UiLog |
前端日志落盘(ui.log,4MB 轮转) |
AiProviderStore / AiConversationStore / LlmClient |
AI 助手(多模型、会话、调用) |
AiToolCatalog |
AI 可用的工具目录 |
AiContextCompactor |
上下文压缩 |
Hmi.Shell
| 一句话 | WPF 窗口壳 —— 一个窗口 + 一个 WebView2 |
| 依赖 | Hmi.HostBridge、Hmi.Automation、Hmi.Project、Hmi.Backend、Hmi.ScriptRuntime |
| 目标框架 | ⚠ net8.0-windows(唯一一个,因为要 WPF) |
| 章 | 17 |
核心类型:
| 类型 | 干什么 |
|---|---|
App |
入口。解析命令行(--runtime)、读环境变量 |
MainWindow |
主窗口:嵌 WebView2、虚拟域名映射、消息收发 |
WebView2Sink |
IWebMessageSink 的实现 |
AutoSaveCoordinator |
自动保存(防抖 1.5 秒) |
UiZoomStore |
界面缩放记忆(ui-zoom.json) |
ShellLog |
壳日志(exe 同目录 shell.log) |
Hmi.Mcp.Host
| 一句话 | 给外部 AI 的 MCP 服务器(独立 exe) |
| 依赖 | Hmi.Automation、Hmi.ScriptRuntime |
| 章 | 18 |
核心类型:
| 类型 | 干什么 |
|---|---|
Program |
入口:解析参数、选传输方式、起服务 |
McpArgs |
命令行参数解析 |
McpProjectHost |
打开工程 + 挂编译器 |
ProjectMcpTools |
84 个工具(36 感知 + 48 组态) |
McpToolFilter |
工具过滤(readOnly / 白名单) |
AlarmLimitArg / AlarmDigitalArg |
报警配置的参数形状 |
Hmi.RuntimeCore.Host
| 一句话 | 运行内核进程(独立 exe)。客户机上可以只装它 |
| 依赖 | Hmi.Core、Hmi.Project、Hmi.Backend、Hmi.ScriptRuntime |
| 公开类型数 | 0(只有一个 Program) |
| 章 | 17 |
命令行:Hmi.RuntimeCore.Host.exe <工程目录> [--crash-after-ready <ms>]
通信:stdio。就绪打 RUNTIMECORE READY,之后每秒 HEARTBEAT <n>。
退出码:0 干净停机 / 1 装配异常 / 2 缺参数。
Hmi.Supervisor
| 一句话 | 进程监督 —— 拉起子进程、看它活着没、崩了重启。不依赖任何 Hmi.* |
| 依赖 | 无 |
| 公开类型数 | 3 |
| 章 | 17 |
核心类型(全在这了):
| 类型 | 干什么 |
|---|---|
ProcessSupervisor |
管一群进程:StartSessionAsync / StopSessionAsync |
ManagedProcess |
管一个进程:启动、看是否卡死、停止 |
ServiceSpec |
描述一个服务(exe 路径/参数/就绪标记/重启策略) |
三、依赖速查表
| 程序集 | 依赖谁 |
|---|---|
Hmi.Core |
— |
Hmi.Security |
— |
Hmi.Supervisor |
— |
Hmi.Project |
Core, Drivers.Abstractions |
Hmi.Scripting |
Core |
Hmi.Historian |
Core |
Hmi.Alarms |
Core |
Hmi.Drivers |
Core, Drivers.Abstractions |
Drivers.Abstractions |
Core |
Hmi.Automation |
Core, Project, Drivers, 三个驱动实现 |
Hmi.Backend |
Core, Project, Drivers, 三个驱动实现 |
Hmi.ScriptRuntime |
Core, Scripting, Historian, Alarms, Project, Automation |
Hmi.HostBridge |
Automation, Backend, Core, Historian, Project, Security |
Hmi.Shell |
HostBridge, Automation, Project, Backend, ScriptRuntime |
Hmi.Mcp.Host |
Automation, ScriptRuntime |
Hmi.RuntimeCore.Host |
Core, Project, Backend, ScriptRuntime |
三个"独立户"(零依赖,能单独摘出去用):
Hmi.Core—— 要一个内存点值表就能用Hmi.Security—— 要账号权限就能用Hmi.Supervisor—— 要进程监督就能用
四、驱动子项目(在 Hmi.Drivers/ 下)
Hmi.Drivers 只是框架。真正的协议实现是并列的子项目,每个都能单独发布成插件包:
| 子项目 | 提供什么 |
|---|---|
Drivers.Abstractions |
接口定义(不是实现) |
Drivers.ModbusTcp |
Modbus TCP |
Drivers.IoTClientPlc |
国产 IoTClient 系(西门子 200SMART / 300/400 / 1200/1500、三菱 MC、欧姆龙 FINS) |
Drivers.PanasonicMewtocol |
松下 Mewtocol |
Drivers.S7SmartClient |
西门子 S7(独立实现) |
Hmi.Drivers |
框架本身(采集循环、块读规划、插件加载) |
这 5 个子项目都只依赖
Hmi.Core+Drivers.Abstractions—— 所以第三方写插件时,只要引这两个就能编出插件包。
五、内置 8 种协议对照
| 协议名(驱动名) | 走哪个子项目 |
|---|---|
Simulation |
Hmi.Drivers 内置 |
ModbusTcp |
Drivers.ModbusTcp |
S7-200SMART |
Drivers.IoTClientPlc |
S7-300/400 |
Drivers.IoTClientPlc |
S7-1200/1500 |
Drivers.IoTClientPlc |
Mitsubishi-MC |
Drivers.IoTClientPlc |
Omron-FINS |
Drivers.IoTClientPlc |
Panasonic-Mewtocol |
Drivers.PanasonicMewtocol |
⚠ 协议清单有两份手抄(后端
BuiltInRegistry+ 前端 mockMOCK_PROTOCOLS), 有测试跨语言钉死。改必须两边一起改。
六、快速定位:我想干 X,该看哪个程序集
| 我想… | 程序集 | 章 |
|---|---|---|
| 读一个点的实时值 | Hmi.Core(RtdbCore) |
04 |
| 读/写工程文件 | Hmi.Project(ProjectLoader) |
06 |
| 把工程跑起来连 PLC | Hmi.Backend(RuntimeHost) |
10 |
| 支持一个新协议 | Drivers.Abstractions + Hmi.Drivers |
09 |
| 让脚本能编译、拦住危险操作 | Hmi.Scripting |
11 |
| 给脚本加一个新函数 | Hmi.Scripting(函数库门面)+ Hmi.Automation(目录) |
13 |
| 记历史数据 | Hmi.Historian |
14 |
| 做报警 | Hmi.Alarms |
14 |
| 做账号权限 | Hmi.Security |
19 |
| 程序化改工程 | Hmi.Automation |
15 |
| 给前端加一个 RPC | Hmi.HostBridge |
17 |
| 让 AI 连进来 | Hmi.Mcp.Host |
18 |
| 管子进程生死 | Hmi.Supervisor |
17 |
七、⭐ 改能力时的"四处"清单
加一个能力(比如"给元素加一个新属性"),通常不止改一处。以 describe_element_props 为例:
| # | 位置 | 改什么 |
|---|---|---|
| 1 | Hmi.Project |
属性定义本身(propSchema 的真源在前端) |
| 2 | Hmi.Automation |
IProjectAutomation + 实现 |
| 3 | Hmi.HostBridge |
AutomationRpcDispatcher 的 RPC 名 |
| 4 | Hmi.Mcp.Host |
ProjectMcpTools 的工具方法(工具级 + 参数级 [Description] 都要写) |
| 5 | 前端 | propSchema.js + mock 的 ELEMENT_TYPES |
| 6 | 文档 | 本手册 + docs/ 下相关文档 |
⭐ "同一能力有第二出口(MCP/CLI/脚本)时,形状必须对齐"。 漏改一处不会报错,只会静默不生效 —— 这是这个项目最常见的坑。
本章类型清单以
src/下各.csproj与源码为准。程序集数量变了记得同步改这里。