第三章 DMI软件架构与设计模式
各位好,我是老张。在轨道交通信号这行摸爬滚打了十几年,今天咱们聊聊DMI的软件架构。说实话,这部分内容看着抽象,但你要是真理解了,后面做开发会顺很多。
DMI不是个简单的显示设备。它要处理司机操作、接收ATP指令、管理界面状态,还得保证实时响应。我见过不少项目,前期架构没想清楚,后期改得想哭。所以这一章,咱们把地基打牢。
3.1 DMI软件分层架构
先说说分层。我个人习惯把DMI软件分成三层:应用层、中间层、驱动层。你想想看,这样分的好处是什么?
- 应用层:负责业务逻辑。比如速度表怎么画、报警怎么显示、按钮怎么响应。这一层只关心“做什么”,不关心“怎么做”。
- 中间层:负责数据转换和通信。把ATP发来的二进制报文,转成应用层能用的结构体。我管它叫“翻译官”。
- 驱动层:直接跟硬件打交道。屏幕刷新、触摸检测、蜂鸣器控制。这一层最底层,也最容易出问题。
关键原则:上层不能直接调用下层。每一层只通过接口跟相邻层通信。这样做的好处是,换屏幕驱动不用改应用代码。
我在项目中遇到过一件事。有个同事把触摸检测的逻辑直接写在了应用层里。后来换了触摸芯片,整个应用层都得重写。嗯,这就是不分层的代价。
3.2 MVC模式在DMI中的应用
说到设计模式,MVC(Model-View-Controller)在DMI里特别实用。说白了,就是把数据、显示、控制分开。
| 组件 | 职责 | DMI中的例子 |
|---|---|---|
| Model(模型) | 管理数据状态 | 当前速度值、目标距离、模式状态 |
| View(视图) | 负责界面显示 | 速度表盘、数字显示、图标 |
| Controller(控制器) | 处理用户输入 | 按钮点击、触摸滑动、旋钮操作 |
为什么会这样设计?我举个例子。司机按下“确认”按钮,Controller收到事件,通知Model更新状态(比如“确认报警”),Model变了,View自动刷新。你看,每个角色各司其职。
// 伪代码示例:MVC在DMI中的交互
class SpeedModel {
int currentSpeed;
void updateSpeed(int newSpeed) {
currentSpeed = newSpeed;
notifyView(); // 通知视图更新
}
}
class SpeedView {
void displaySpeed(int speed) {
// 绘制速度表盘
drawDial(speed);
}
}
class SpeedController {
void onSpeedChange(int rawData) {
int speed = convertRawToSpeed(rawData);
model.updateSpeed(speed);
}
}
我的建议:刚开始用MVC时,别把Controller写得太重。我见过有人把业务逻辑全塞进Controller,结果Controller比Model还复杂。记住,Controller只是“传话筒”。
3.3 状态机设计
DMI的状态管理,是很多新手头疼的地方。为什么?因为DMI的状态太多了。待机、启动、正常运行、故障、紧急制动...每个状态下的界面和响应都不一样。
我建议用有限状态机(FSM)来管理。说白了,就是定义好“状态”、“事件”、“转移”这三个要素。
- 状态:DMI当前处于什么模式。比如SB(待机)、FS(全监督)、OS(部分监督)等。
- 事件:触发状态变化的条件。比如收到ATP的“紧急制动”指令、司机按下“缓解”按钮。
- 转移:从一个状态到另一个状态的规则。比如从FS状态收到“紧急制动”事件,转移到EB(紧急制动)状态。
注意:状态机最怕“未定义转移”。我曾经在一个项目里,测试时发现DMI在某个特殊场景下卡死了。查了两天才发现,有个状态转移没定义,程序跑飞了。所以,一定要覆盖所有可能的转移路径。
// 状态机伪代码示例
enum DMIState {
SB, // 待机
FS, // 全监督
OS, // 部分监督
EB, // 紧急制动
TR // 故障
};
DMIState currentState = SB;
void handleEvent(Event event) {
switch (currentState) {
case SB:
if (event == STARTUP) {
currentState = FS;
showMainScreen();
}
break;
case FS:
if (event == EMERGENCY_BRAKE) {
currentState = EB;
showEmergencyWarning();
activateBrake();
}
break;
// ... 其他状态处理
}
}
嗯,这里要注意。状态机实现时,别用太多if-else嵌套。我习惯用状态转移表,清晰又好维护。你想想看,如果状态多了,if-else能把你绕晕。
避坑指南:我曾经犯过一个错,把状态机的“进入动作”和“持续动作”混在一起。比如进入EB状态时,我只触发了一次报警,但报警应该持续响。后来我改成:进入状态时执行一次动作,在状态内循环执行持续动作。这才对。
最后总结一下。DMI的软件架构,分层是基础,MVC是骨架,状态机是灵魂。这三样东西搞明白了,DMI开发就成功了一半。下一章咱们聊聊具体的界面布局设计,到时候见。