Skip to content

网关协议驱动开发 ​

开发预览

本页依据提交 b1a16cbc 的驱动源码与项目驱动开发指南整理。驱动框架与多个驱动实现真实存在,但当前应用层不会把配置的驱动注册到 TagEngine(见第 9 节),新驱动联调需先解除该接线阻塞。

1. 插件化驱动架构 ​

所有工业协议通过统一的 IProtocolDriver 接口实现,支持两种接入方式:

  • 静态注册:驱动编译进主程序,启动时自动注册。
  • 动态加载:驱动编译为 .so/.dll 共享库,运行时通过 dlopen/LoadLibrary 热加载。
cpp
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 中使用宏,程序启动时自动注册:

cpp
FASTBEE_REGISTER_DRIVER("myprotocol", MyProtocolDriver)

方式二:动态加载(热加载)——运行时从 .so 加载,动态库必须导出两个 C 函数:

cpp
extern "C" {
    IProtocolDriver* createDriver();
    void destroyDriver(IProtocolDriver* driver);
}

3. 添加新驱动的步骤 ​

  1. 创建 src/drivers/myprotocol/MyProtocolDriver.h/.cpp,实现 IProtocolDriver 接口。
  2. 导出 extern "C" createDriver() / destroyDriver() 工厂函数。
  3. 使用 FASTBEE_REGISTER_DRIVER("myprotocol", MyProtocolDriver) 宏注册。
  4. 在 CMakeLists.txt 的 FASTBEE_BUILD_DRIVERS 区域添加驱动编译规则(add_library(myprotocol_driver SHARED ...))。
  5. 在 gateway.json 的 drivers 数组中添加驱动配置,并添加设备和 Tag。
  6. 在 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写多个寄存器写

数据类型映射(设计):

dataTypeModbus 寄存器数C++ 类型
bool1 bitbool(线圈/离散输入)
int16 / uint161int16_t / uint16_t
int32 / uint322int32_t / uint32_t
float2float(IEEE 754 单精度)
double4double(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正向有功总电能0x00010000kWh0.01
V_AA 相电压0x02010000V0.1
I_AA 相电流0x02020000A0.001
P_total瞬时有功功率0x02030000kW0.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/TCPsrc/drivers/modbus/完整:libmodbus 全功能码、批量读取优化
OPC UAsrc/drivers/opcua/双模式:open62541 真实实现 / Stub
Siemens S7src/drivers/s7/双模式:Snap7 真实实现 / Stub
DLT645src/drivers/dlt645/帧格式完整,串口链路联调中
IEC 60870-5-104src/drivers/iec104/Stub:帧格式 + 28 个数据点
BACnet IPsrc/drivers/bacnet/Stub:接口已定义
GPIOsrc/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 注入数据:

bash
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 集成),新增驱动不应引入回归。

蜂信物联 | fastbee.cn