01 · 内核与工程模型 —— Hmi.Core / Hmi.Project
内核层。所有上层程序集都从这两个开始:Hmi.Core 管"运行时内存里的实时数据",Hmi.Project 管"磁盘上的工程文件"。
Hmi.Core —— 实时库 RTDB
职责:进程内实时数据库、点模型、发布/订阅变化分发。这是每个上层都要读写的内核。无任何 Hmi.* 依赖。
入口类型 RtdbCore : IDisposable
src/Hmi.Core/Rtdb/RtdbCore.cs(public sealed partial class)。构造:RtdbCore(IReadOnlyList<Tag> tags)。
常用方法:
| 方法 | 作用 |
|---|---|
bool TryGetId(string name, out int id) |
点名 → 内部整型 id(热路径都用 id 寻址) |
Tag GetTag(int id) |
取点定义 |
TagChange Read(int id) |
读当前值(seqlock 无锁乐观读) |
NonScalarSnapshot ReadNonScalar(int id) |
读非标量(字符串/数组/结构)旁路值 |
bool UpdateFromDriver(int id, TagValue v, Quality q, long ts) |
驱动侧写入 |
bool UpdateNonScalar(...) / bool SetQuality(int,Quality,long) |
写非标量 / 只改质量戳 |
IDisposable Subscribe(IEnumerable<int>, ITagSubscriber, DeliveryMode) |
订阅一批点 |
IDisposable SubscribeAll(...) / int Count |
订阅全部 / 点数 |
读用 seqlock 乐观读 + 分片锁;变化分发在独立线程上,分
Droppable(只要最新)/Lossless(绝不丢)两条投递车道。这就是"报警历史不丢、界面刷新不卡"的机制根源。
订阅契约 ITagSubscriber
src/Hmi.Core/Subscription/ITagSubscriber.cs —— void OnTagChanged(ReadOnlySpan<TagChange> batch)。这是 RTDB 唯一的消费者接口,TagSubscriptionBridge、HistorianRecorder、AlarmEngine、LinkedVariableMirror 都实现它。你想接一个新的"实时数据消费者",实现这个接口再 Subscribe 即可。
Hmi.Core.Branding:可变的ProductName/DefaultProductName,运行期品牌名切换用。
Hmi.Project —— 工程模型与读写
职责:磁盘工程模型 + JSON 加载/保存(点、通道、画面、脚本、报警/历史配置、配方、多语言、通知、报表、样式),外加绑定/动作抽象。依赖 Hmi.Core、Drivers.Abstractions。
聚合根 LoadedProject
src/Hmi.Project/Persistence/LoadedProject.cs —— 一个工程在内存里的全部:ProjectModel Project、List<TagDef> Tags、List<ChannelDef> Channels、Dictionary<string,ScreenModel> Screens、ProjectScripts、Dictionary<string,ScriptModule> Modules、List<RecipeModel> Recipes、LanguageTable Languages、NotifyConfig Notify、List<AlarmConfigModel> Alarms、List<HistoryConfigModel> History、List<ReportModel> Reports、List<CustomButtonSkin> ButtonSkins。
加载器 ProjectLoader(静态)
src/Hmi.Project/Persistence/ProjectLoader.cs:
LoadedProject Load(string projectDir)—— 从目录加载;缺project.json抛FileNotFoundException。LoadedProject Load(IProjectFileSource src)—— 从文件源加载。目录源DirectoryFileSource或内存 zip 源ZipFileSource;加密运行包解密成 ZipFileSource,明文永不落盘。void Save(LoadedProject, string projectDir)。
邮件密码故意不进
notify.json,由MailSecretStore(DPAPI)另存。
写路径抽象 IActionDispatcher
src/Hmi.Project/Binding/IActionDispatcher.cs —— WriteTag(name,value) / OpenScreen(name) / RunScript(name)。脚本、AI 共用这条写路径;运行态由 Hmi.Backend 提供实现。
小结
- 想操作内存实时值 →
RtdbCore;想接一个新消费者 → 实现ITagSubscriber。 - 想读写磁盘工程 →
ProjectLoader.Load/Save+LoadedProject。 - 大多数集成不会直接碰
LoadedProject的字段,而是通过第 06 章的Hmi.Automation间接改 —— 那一层带校验和审计。