这确实是我的第一次认真的设想一个软件框架。
列车 到站-开门-关门-离站 车辆之间的 LED 屏幕、 LCD 显示器 、 车门上的 DRMD 动态地图要显示具体的消息
我整体的实现架构类似这样:
backend ┊ frontend
PIS_BASE ──── PIS_GLOBAL
┃
PIS_XXX_BASE
┃
┣━━━━ PIS_XXX_DDS ────┬──── QT_QML_ADAPTOR
┗━━━━ PIS_XXX_CAN ────┼──── VIDEO_PLAYER
┗━━━━ PIS_XXX_XXX ────┴──── XXX
PIS_XXX_BASE 是具体的 PIS 组件,继承自 PIS_BASE ,又有各个具体的 PIS 组件的具体实现,比如基于 DDS 的 PIS_XXX_DDS ,基于 CAN 实现的 PIS_XXX_CAN 。
这里,三层继承关系分别定义的内容如下:
PIS_BASE : 定义组件切换的相关内容(虚方法,需要下层实现):
okay : 确认当前组件可用_onEnable : 当组件被启用时的动作_onDisable : 当组件被停用时的动作PIS_XXX_BASE :实现前面的虚方法,并且对前端的具体接口定义回调函数PIS_XXX_XXX : 实现具体的后端通信,调用上层的回调函数PIS_GLOBAL 维护一个带优先级的实例表:
0: instance_dds
1: instance_can
INT_MAX: instance_default
PIS_BASE 定义了 enable 和 disable 方法,先保存启用状态再调用子类实现的 _onEnable 和 _onDisable ,而 PIS_GLOBAL 则实现了 component 方法,通过 PIS_BASE::okay 和优先级启用正确的实例并返回。 PIS_XXX_BASE 则通过 PIS_BASE::okay 检查自身是否正被启用,这样设计就是为了绕过的可能,譬如在更新全局信息等等情况下可能允许违被启用的实例调用回调。
另外,我一般允许 instance_default 永远可用,它的作用就是调用前端显示设备故障。
所有回调都在主入口连接以确保灵活性,下面是伪代码:
QT_QML_ADAPTOR qt_qml_adaptor;
VIDEO_PLAYER video_player;
void connectAll(PIS_XXX_BASE& pis) {
pis.setCallback_OnTrainArrive([qt_qml_adaptor&, video_player&](int station){
qt_qml_adaptor.doTrainArrive(station);
video_player.doTrainArrive(station);
});
}
int main() {
PIS_XXX_DDS pis_xxx_dds;
PIS_XXX_CAN pis_xxx_can;
connectAll(pis_xxx_dds);
connectAll(pis_xxx_can);
while(true) process();
return 0;
}