yanghn2002

View My GitHub Profile

这确实是我的第一次认真的设想一个软件框架。

需求

  1. 地铁车辆 PIS 系统有不同的通信后端,默认是 DDS ,车辆以太网故障时,切换到 CAN+模拟信号
  2. 每个 PIS 组件有不同功能,我可以称之为前端,定义了不同的方法供后端调用,具体:

    列车 到站-开门-关门-离站 车辆之间的 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
这里,三层继承关系分别定义的内容如下:

  1. PIS_BASE : 定义组件切换的相关内容(虚方法,需要下层实现):
    • okay : 确认当前组件可用
    • _onEnable : 当组件被启用时的动作
    • _onDisable : 当组件被停用时的动作
  2. PIS_XXX_BASE :实现前面的虚方法,并且对前端的具体接口定义回调函数
  3. PIS_XXX_XXX : 实现具体的后端通信,调用上层的回调函数

PIS_GLOBAL 维护一个带优先级的实例表:

0: instance_dds
1: instance_can
INT_MAX: instance_default

PIS_BASE 定义了 enabledisable 方法,先保存启用状态再调用子类实现的 _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;
}