网关协议驱动开发
开发预览
本页依据提交 b1a16cbc 的驱动源码与项目驱动开发指南整理。驱动框架与多个驱动实现真实存在,但当前应用层不会把配置的驱动注册到 TagEngine(见第 9 节),新驱动联调需先解除该接线阻塞。
1. 插件化驱动架构
所有工业协议通过统一的 IProtocolDriver 接口实现,支持两种接入方式:
- 静态注册:驱动编译进主程序,启动时自动注册。
- 动态加载:驱动编译为
.so/.dll共享库,运行时通过dlopen/LoadLibrary热加载。
class IProtocolDriver {
public:
virtual ~IProtocolDriver() = default;
/// 初始化驱动(建立连接、加载配置)
virtual bool init(const DriverConfig& config) = 0;
/// 批量读取 Tag
virtual ReadResult readTags(const std::vector<TagConfig>& tags) = 0;
/// 写入单个 Tag
virtual WriteResult writeTag(const TagConfig& tag, const TagValue& value) = 0;
/// 获取协议名称(如 "modbus", "opcua")
virtual std::string getProtocolName() const = 0;
/// 获取驱动版本号
virtual std::string getVersion() const = 0;
/// 当前是否已连接
virtual bool isConnected() const = 0;
/// 断开连接,释放资源
virtual void disconnect() = 0;
};| 方法 | 调用时机 | 说明 |
|---|---|---|
init() | 网关启动 / 驱动加载时 | 建立与现场设备的连接,初始化内部状态 |
readTags() | TagEngine 定时轮询 | 批量读取一组 Tag 的当前值 |
writeTag() | 云端指令下发 / 手动调试 | 向设备写入单个 Tag 值 |
getProtocolName() | 注册时 | 返回协议标识符(小写,如 "modbus") |
isConnected() | 状态检查 | 返回设备连接状态 |
disconnect() | 网关停止 / 驱动卸载 | 释放所有资源 |
DriverConfig 承载连接参数:TCP(host/port)或 RTU 串口(serialDevice/baudRate/parity/dataBits/stopBits),以及通用的 timeoutMs(默认 3000)、retryCount(3)、pollIntervalMs(1000)和驱动特定扩展 extra 键值表。TagValue 为 variant(bool/int64/uint64/double/string/bytes),附时间戳与质量码(0=good,详见配置第 7 节)。
2. DriverFactory 注册机制
DriverFactory 是线程安全的单例工厂,提供 registerDriver() / loadFromFile() / create() / listDrivers() 等能力。
方式一:静态注册宏(推荐)——在驱动 .cpp 中使用宏,程序启动时自动注册:
FASTBEE_REGISTER_DRIVER("myprotocol", MyProtocolDriver)方式二:动态加载(热加载)——运行时从 .so 加载,动态库必须导出两个 C 函数:
extern "C" {
IProtocolDriver* createDriver();
void destroyDriver(IProtocolDriver* driver);
}3. 添加新驱动的步骤
- 创建
src/drivers/myprotocol/MyProtocolDriver.h/.cpp,实现IProtocolDriver接口。 - 导出
extern "C" createDriver() / destroyDriver()工厂函数。 - 使用
FASTBEE_REGISTER_DRIVER("myprotocol", MyProtocolDriver)宏注册。 - 在
CMakeLists.txt的FASTBEE_BUILD_DRIVERS区域添加驱动编译规则(add_library(myprotocol_driver SHARED ...))。 - 在
gateway.json的drivers数组中添加驱动配置,并添加设备和 Tag。 - 在
test/unit/编写单元测试并通过调试接口验证。
驱动实现注意事项:
- 线程安全:
readTags()和writeTag()可能在不同线程中被调用,驱动内部需做好同步。 - 异常处理:驱动内部应捕获所有异常,不要向调用方抛出。
- 连接管理:驱动应自行处理断线重连逻辑,并遵守
timeoutMs设置。 - 资源释放:
disconnect()必须释放所有资源(关闭 socket、释放内存等)。
4. Modbus 驱动
最完整的参考实现(src/drivers/modbus/),基于 libmodbus,支持 RTU 与 TCP。
支持的功能码:
| 功能码 | 说明 | 读/写 |
|---|---|---|
| 01 | 读线圈 (Coil) | 读 |
| 02 | 读离散输入 (Discrete Input) | 读 |
| 03 | 读保持寄存器 (Holding Register) | 读 |
| 04 | 读输入寄存器 (Input Register) | 读 |
| 05 | 写单个线圈 | 写 |
| 06 | 写单个寄存器 | 写 |
| 15 | 写多个线圈 | 写 |
| 16 | 写多个寄存器 | 写 |
数据类型映射(设计):
| dataType | Modbus 寄存器数 | C++ 类型 |
|---|---|---|
bool | 1 bit | bool(线圈/离散输入) |
int16 / uint16 | 1 | int16_t / uint16_t |
int32 / uint32 | 2 | int32_t / uint32_t |
float | 2 | float(IEEE 754 单精度) |
double | 4 | double(IEEE 754 双精度) |
字节序设计支持 ABCD(大端)/ CDAB(字交换)/ BADC / DCBA。注意当前 TagConfig::fromJson() 不解析 dataType、functionCode、byteOrder 字段(字节序恒为大端默认值),Tag 字段以运行时解析器为准(见配置第 5 节)。
5. OPC UA 驱动
双模式实现:FASTBEE_ENABLE_OPCUA=ON 且找到 open62541 时使用真实 Client 读写;默认(OFF)为 Stub 模式,根据 nodeId 哈希生成模拟数据。
- Node ID 格式:
ns=<命名空间>;<类型>=<标识符>,如ns=2;s=Temperature(字符串)、ns=0;i=2258(数字)。Tag 的nodeId字段存储该字符串。 - 端点:默认
opc.tcp://<host>:<port>,可用extra.endpointUrl覆盖;extra.securityMode选择安全模式。 - 类型映射:Boolean→bool、Int16/32/64→int64、UInt16/32/64→uint64、Float/Double→double、String→string。
- 读取后应用
scale/offset缩放;读取失败的 Tag 标记BAD质量码并继续处理其余 Tag。
6. BACnet 驱动
当前为 Stub 实现,接口已定义,待集成 BACnet Stack。设计要点:
- 以「对象类型 + 实例号 + 属性 ID」标识数据点:
objectType(0=analog-input、3=binary-input 等)、instanceId、propertyId(85=present-value 最常用)。 - 支持 BIP(BACnet/IP,默认端口 47808)与 MS/TP(RS485 串口)两种链路。
- 设备发现走 Who-Is / I-Am;读取走 ReadProperty 请求-响应。
7. Siemens S7 驱动
支持 S7-200/300/1200/1500 系列 PLC 以太网通信(默认端口 102)。双模式:定义 HAS_SNAP7 并链接 snap7 为真实通信,默认 Stub 模拟。
地址格式:
| 格式 | 说明 | 示例 |
|---|---|---|
DB<n>.DBW<offset> | 数据块字(2 字节) | DB1.DBW0 |
DB<n>.DBD<offset> | 数据块双字(4 字节) | DB1.DBD4 |
DB<n>.DBX<byte>.<bit> | 数据块位 | DB1.DBX0.0 |
MB<n> / M<byte>.<bit> | 标志字节/位 | MB0 / M0.0 |
I<byte>.<bit> / Q<byte>.<bit> | 输入/输出位 | I0.0 / Q0.0 |
8. DLT645 电表驱动
支持 DLT645-1997 与 DLT645-2007 两个版本的中国电表协议,自研帧格式实现(68H + 地址(6B BCD) + 68H + 控制码 + 长度 + 数据(+0x33) + 校验 + 16H)。
常用数据项:
| Tag ID | 数据项 | 2007 编码 | 单位 | 缩放 |
|---|---|---|---|---|
FE_total | 正向有功总电能 | 0x00010000 | kWh | 0.01 |
V_A | A 相电压 | 0x02010000 | V | 0.1 |
I_A | A 相电流 | 0x02020000 | A | 0.001 |
P_total | 瞬时有功功率 | 0x02030000 | kW | 0.0001 |
串口参数默认 2400 波特率,extra.meterAddress 配置电表地址,extra.version 选择协议版本。串口真实链路联调中,默认按 Stub 边界看待。
9. IEC 60870-5-104 驱动
电力远动协议,基于 TCP(端口 2404),事件驱动(总召唤 + 突发上报)而非轮询。当前为 Stub:帧格式与 28 个典型变电站数据点完整,待集成 lib60870(HAS_LIB60870 宏启用真实模式)。
支持的类型标识(部分):
| TypeID | 名称 | 说明 |
|---|---|---|
| M_SP_NA_1 (1) | 单点遥信 | 开关位置、告警信号 |
| M_ME_NC_1 (13) | 短浮点遥测 | 电压/电流/功率 |
| M_IT_NA_1 (15) | 累计量 | 电能表读数 |
| C_SC_NA_1 (45) | 单点遥控 | 开关分/合 |
| C_IC_NA_1 (100) | 总召唤 | 获取全量数据 |
Tag ID 命名规则:yx_NNNN 遥信、yc_NNNN 遥测、ym_NNNN 遥脉、yk_NNNN 遥控(NNNN 为 IOA 信息对象地址)。
10. 已实现驱动清单与当前接线现状
| 驱动 | 目录 | 状态 |
|---|---|---|
| Modbus RTU/TCP | src/drivers/modbus/ | 完整:libmodbus 全功能码、批量读取优化 |
| OPC UA | src/drivers/opcua/ | 双模式:open62541 真实实现 / Stub |
| Siemens S7 | src/drivers/s7/ | 双模式:Snap7 真实实现 / Stub |
| DLT645 | src/drivers/dlt645/ | 帧格式完整,串口链路联调中 |
| IEC 60870-5-104 | src/drivers/iec104/ | Stub:帧格式 + 28 个数据点 |
| BACnet IP | src/drivers/bacnet/ | Stub:接口已定义 |
| GPIO | src/hal/GpioManager | 完整:DI/DO/AI,基于 libgpiod |
当前接线现状(对驱动开发者最重要的一条):GatewayApp::initDrivers() 只加载并记录驱动配置数量,没有调用 DriverFactory::create() 创建实例,也没有 tagEngine_->registerDriver() 注册。因此新驱动即使编译注册成功,也不会被采集引擎调用——联调前需要先在应用层完成"读配置 → 创建驱动 → init → 注册到 TagEngine"的接线,这正是本项目现阶段的核心开发任务之一。
11. 驱动健康监控与重连
所有驱动由 DriverHealthMonitor 统一管理健康状态,6 态状态机 DISCONNECTED → CONNECTING → CONNECTED → RECONNECTING → FAILED → ISOLATED:连续错误达到 maxConsecutiveErrors(默认 10)进入 FAILED,持续超时进入 ISOLATED,之后按 autoRecoverMs(默认 60 秒)自动尝试恢复。健康摘要经 GET /api/drivers/health 暴露。
ExponentialBackoff 提供标准化重连延迟(基准 1s、上限 60s、×2 退避、±20% 抖动),MQTT 客户端与驱动重连共用。
12. Tag 聚合与工程值转换
- TagAggregator:对 Tag 做滑动窗口聚合(MAX/MIN/AVG/TOTAL/DELTA/RATE),结果经
GET /api/aggregator/rules/:id/result查询;当前无创建规则的 API 入口。 - TagScaling:原始值 ↔ 工程值转换。
LINEAR(工程值 = 原始值 × scale + offset)支持反向转换(控制下发时工程值 → 原始值);SQRT(√原始值 × sqrtFactor)用于差压流量计场景。
13. 单元测试
新驱动测试放在 test/unit/,使用 GoogleTest;不依赖真实硬件时通过模拟后端/Stub 注入数据:
cd build
cmake .. -DFASTBEE_BUILD_TESTS=ON -DFASTBEE_BUILD_DRIVERS=ON
cmake --build . -j"$(nproc)"
ctest --output-on-failure
# 运行指定用例
./bin/gateway_unit_tests --gtest_filter="MyProtocolDriver*"项目当前包含 585 个测试用例(555 单元 + 30 集成),新增驱动不应引入回归。

