工业数据采集网关架构设计
一套 C++17 插件化工业数据采集网关的设计思路与关键实现 版本 v1.0
一、为什么用插件化架构
工业现场的设备协议极多:Modbus TCP、OPC UA、串口私有协议、UDP 报文、HTTP/JSON…… 如果每接一种协议就改一次内核,项目会迅速失控。
本网关的做法是:内核只负责\”点位数据\”这件事,协议全部下沉为插件。
内核定义了一套稳定的接口(IPlugin / IDriver / IChannel / IDevice), 任何新协议只要实现这套接口、编译成一个 .so,放进 plugins/ 目录即可被加载, 不需要重新编译内核。
带来的直接好处:
| 隔离 | 某个驱动崩溃不影响内核与其他驱动 |
| 可裁剪 | 现场不需要的驱动直接不部署,减少攻击面与体积 |
| 可扩展 | 私有协议对接只写一个插件,不动主干代码 |
| 可替换 | 同一份点位表可以在不同协议驱动间迁移 |
二、整体架构
系统按职责分为四层:
┌─────────────────────────────────────────────────────────────┐
│ 表现层 Web 组态 / 上位机(浏览器访问) │
│ 实时画面 · 设备配置 · 点位配置 · 历史查询 · 权限 │
└───────────────────────────┬─────────────────────────────────┘
│ REST / WebSocket
┌───────────────────────────┴─────────────────────────────────┐
│ 服务层 shuweb(Web 服务)· shuservice(工程管理服务) │
└───────────────────────────┬─────────────────────────────────┘
│ RPC(自定义二进制协议)
┌───────────────────────────┴─────────────────────────────────┐
│ 内核层 shucore │
│ · 点位管理(DataManager) · 设备管理(DeviceManager) │
│ · 工程加载(ProjectManager)· 事件(Event)· 搜索(DataSearch)│
│ · 冗余(Redundency)· 授权(License)· 日志(Logger) │
└───────────────────────────┬─────────────────────────────────┘
│ 统一接口 IChannel / IDevice
┌───────────────────────────┴─────────────────────────────────┐
│ 驱动层 采集插件(全部以 .so 形式动态加载) │
│ Modbus TCP · OPC UA · 串口 · UDP · JSON · … │
└─────────────────────────────────────────────────────────────┘
另有守护进程 shudaemon 负责内核异常退出后的拉起, shuupdate 负责版本升级,shuhwinfo 负责读取硬件指纹(授权用)。
三、插件模型
3.1 三种容器
插件管理器把插件按 ContainerType 分为三类,加载顺序与生命周期不同:
| 内核容器 | CONTAINER_CORE | DataManager、DeviceManager、Logger | 系统必需,最先加载 |
| 扩展容器 | CONTAINER_EXTENDED | Event、DataSearch、Redundency | 增强功能,可裁剪 |
| 驱动容器 | CONTAINER_DRIVER | Modbus、OPC UA、串口 | 按现场需要加载 |
3.2 插件生命周期
每个插件都实现统一的生命周期钩子:
class IPlugin {
public:
virtual const char* pluginName() = 0; // 唯一名称,如 \”c.modbus.tcp\”
virtual int install(int argc, char** argv) = 0; // 安装(生成属性定义文件)
virtual int uninstall() = 0; // 卸载
virtual int init(int argc, char** argv) = 0; // 初始化:读取配置、注册能力
virtual int uninit() = 0; // 反初始化:释放资源
virtual int update() = 0; // 周期回调
virtual int getPluginId() const = 0; // 内核内固定 ID
virtu
