Plugin介绍
Plugin 是 driver-box 架构的核心,通过标准化的接口设计实现设备协议的无限扩展能力。本文档详细介绍插件系统的设计原理、接口定义和使用方法。
driver-box 采用微内核+插件的架构设计:
- 微内核:提供基础功能(设备影子、核心缓存、事件总线)
- 插件层:实现具体协议逻辑(Modbus、MQTT、HTTP等)
- 松耦合:插件之间独立,通过标准接口与内核交互
sequenceDiagram
autonumber
participant App as 应用层
participant PluginMgr as 插件管理器<br/>driverbox/plugin.go
participant Plugin as 协议插件<br/>(Modbus/MQTT)
participant Connector as 连接器<br/>(Connector)
participant CoreCache as 核心缓存<br/>cache
participant Shadow as 设备影子<br/>shadow
participant EventBus as 事件总线<br/>export
participant Device as 设备
rect rgb(200, 240, 255)
Note over App,Device: <b>启动阶段</b>
end
App->>PluginMgr: EnablePlugin(name, plugin)
PluginMgr->>PluginMgr: plugins.register(name, plugin)
PluginMgr->>CoreCache: InitCoreCache(plugins)
CoreCache-->>PluginMgr: 加载配置完成
PluginMgr->>Plugin: Initialize(config)
Plugin->>Connector: 创建连接池
Plugin-->>PluginMgr: 初始化完成
PluginMgr->>Shadow: 添加设备到影子
PluginMgr->>EventBus: 触发 DeviceAdded 事件
rect rgb(220, 255, 220)
Note over App,Device: <b>运行阶段 - 数据采集</b>
end
Connector->>Device: 建立连接
Device-->>Connector: 返回原始数据
Connector->>Connector: 编码/解码数据
Connector->>Shadow: UpdateDeviceData()
Shadow-->>PluginMgr: 数据更新完成
Shadow->>EventBus: 触发数据变更事件
rect rgb(220, 255, 220)
Note over App,Device: <b>运行阶段 - 写操作</b>
end
App->>PluginMgr: 请求写入数据
PluginMgr->>Plugin: Connector(deviceId)
Plugin-->>PluginMgr: 返回连接器
PluginMgr->>Connector: Encode(WriteMode, values)
Connector-->>PluginMgr: 返回编码数据
PluginMgr->>Connector: Send(encodedData)
Connector->>Device: 发送控制指令
Device-->>Connector: 操作结果
rect rgb(255, 240, 200)
Note over App,Device: <b>销毁阶段</b>
end
App->>PluginMgr: Destroy()
PluginMgr->>Plugin: Destroy()
Plugin->>Connector: Release()
Connector->>Device: 关闭连接
Plugin-->>PluginMgr: 资源释放完成
插件生命周期
Section titled “插件生命周期”插件从注册到销毁经历完整的生命周期:
stateDiagram-v2
[*] --> 注册: EnablePlugin()
注册 --> 初始化: Initialize(config)
初始化 --> 运行: 建立连接/启动采集
运行 --> 停止: Destroy()
停止 --> [*]
运行 --> 热重载: ReloadPlugin()
热重载 --> 运行
生命周期详解
Section titled “生命周期详解”- 注册阶段:通过
EnablePlugin()向插件管理器注册 - 初始化阶段:系统调用
Initialize(config)加载配置 - 运行阶段:插件建立连接、启动数据采集任务
- 热重载:配置变更时调用
ReloadPlugin()无缝重启 - 销毁阶段:调用
Destroy()释放所有资源
核心接口设计
Section titled “核心接口设计”Plugin 接口
Section titled “Plugin 接口”所有插件必须实现 Plugin 接口 (driverbox/plugin/plugin.go:26):
type Plugin interface { // Initialize 初始化插件 // 参数 c: 设备配置信息,包含连接参数、设备列表等 Initialize(c config.DeviceConfig)
// Connector 获取指定设备的连接器 // 参数 deviceId: 设备唯一标识符 // 返回: 连接器实例,不支持时返回 NotSupportGetConnector 错误 Connector(deviceId string) (connector Connector, err error)
// Destroy 销毁插件并释放资源 // 返回: 销毁过程中的错误 Destroy() error}Connector 接口
Section titled “Connector 接口”连接器负责与单个设备进行实际的通信操作 (driverbox/plugin/plugin.go:64):
type Connector interface { // Encode 编码设备操作指令 // 参数 deviceId: 设备ID, mode: 读/写模式, values: 点位数据 // 返回: 编码后的数据,不支持时返回 NotSupportEncode 错误 Encode(deviceId string, mode EncodeMode, values ...PointData) ( res interface{}, err error)
// Send 发送编码后的数据到设备 // 参数 data: 通过 Encode 方法编码后的数据 // 返回: 发送过程中的错误 Send(data interface{}) (err error)
// Release 释放连接器占用的资源 // 返回: 释放过程中的错误 Release() (err error)}EncodeMode 编码模式 (driverbox/plugin/model.go:11):
type EncodeMode string
const ( ReadMode EncodeMode = "read" // 读模式,从设备读取数据 WriteMode EncodeMode = "write" // 写模式,向设备写入数据)PointData 点位数据 (driverbox/plugin/model.go:26):
type PointData struct { PointName string `json:"name"` // 点位名称 Value interface{} `json:"value"` // 点位值}DeviceData 设备数据 (driverbox/plugin/model.go:36):
type DeviceData struct { ID string `json:"id"` // 设备唯一标识 Values []PointData `json:"values"` // 点位值数组 Events []event.Data `json:"events"` // 事件数组 ExportType ExportType `json:"exportType"` // 导出类型}ExportType 导出类型:
type ExportType string
const ( RealTimeExport ExportType = "realTimeExport" // 实时上报类型)插件管理器实现
Section titled “插件管理器实现”插件管理器位于 driverbox/plugin.go,负责插件的注册、加载和销毁。
// EnablePlugin 注册插件到系统// name: 插件名称(建议使用协议名称)// plugin: 实现了 Plugin 接口的插件实例func EnablePlugin(name string, plugin plugin.Plugin) { plugins.register(name, plugin)}使用示例(参考 plugins/modbus/plugin.go):
package modbus
import ( "github.com/ibuilding-x/driver-box/driverbox" "github.com/ibuilding-x/driver-box/plugins/modbus/internal")
func EnablePlugin() { driverbox.EnablePlugin(internal.ProtocolName, new(internal.Plugin))}loadPlugins() 函数负责加载并启动所有已注册的插件:
func loadPlugins() error { // 1. 卸载旧的驱动库 library.Driver().UnloadDeviceDrivers() library.Protocol().UnloadDeviceDrivers()
// 2. 初始化核心缓存 _, err := cache.InitCoreCache(plugins.plugins) if err != nil { return err }
// 3. 初始化设备影子服务 initDeviceShadow()
// 4. 启动所有插件 for key, p := range plugins.plugins { p.Initialize(cache.GetConfig(key)) }
// 5. 触发设备添加事件 for _, device := range cache.Get().Devices() { export.TriggerEvents(event.DeviceAdded, device.ID, nil) }
return nil}func destroyPlugins() { for key, p := range plugins.plugins { err := p.Destroy() if err != nil { Log().Error("stop plugin error", zap.String("plugin", key), zap.Error(err)) } }}// ReloadPlugin 重启指定名称的插件func ReloadPlugin(pluginName string) { cfg := cache.GetConfig(pluginName) p := plugins.plugins[pluginName] p.Destroy() p.Initialize(cfg)}
// ReloadPlugins 重启所有插件func ReloadPlugins() { for name := range plugins.plugins { ReloadPlugin(name) }}插件实现示例
Section titled “插件实现示例”Modbus 插件
Section titled “Modbus 插件”Modbus 插件是典型的协议插件实现 (plugins/modbus/internal/plugin.go):
type Plugin struct { connPool map[string]*connector // 连接器池 config config.DeviceConfig // 配置信息}
// Initialize 初始化插件func (p *Plugin) Initialize(c config.DeviceConfig) { p.config = c p.initNetworks(c) // 初始化连接池和采集任务}
// Connector 获取设备连接器func (p *Plugin) Connector(deviceId string) (conn plugin.Connector, err error) { device, ok := driverbox.CoreCache().GetDevice(deviceId) if !ok { return nil, errors.New("not found device") } c, ok := p.connPool[device.ConnectionKey] if !ok { return nil, errors.New("not found connection") } return c, nil}
// Destroy 销毁插件func (p *Plugin) Destroy() error { for _, conn := range p.connPool { conn.Close() } return nil}Modbus 连接器 (plugins/modbus/internal/connector.go):
type connector struct { config *ConnectionConfig plugin *Plugin client *modbus.ModbusClient devices map[uint8]*slaveDevice collectTask *crontab.Future // ... 其他字段}
// Encode 编码点位数据为 Modbus 协议帧func (c *connector) Encode(deviceId string, mode plugin.EncodeMode, values ...plugin.PointData) (interface{}, error) { // 实现 Modbus 协议编码逻辑}
// Send 发送编码后的数据func (c *connector) Send(data interface{}) error { // 实现 Modbus 协议发送逻辑}
// Release 释放连接器资源func (c *connector) Release() error { // 关闭连接、停止采集任务等}MQTT 插件
Section titled “MQTT 插件”MQTT 插件实现了发布/订阅协议 (plugins/mqtt/internal/plugin.go):
type Plugin struct { connectors map[string]*connector}
// Initialize 初始化连接池func (p *Plugin) Initialize(c config.DeviceConfig) { go func() { p.initConnPool(c) }()}
// Connector 获取 MQTT 连接器func (p *Plugin) Connector(deviceId string) (plugin.Connector, error) { device, ok := driverbox.CoreCache().GetDevice(deviceId) if !ok { return nil, errors.New("not found device") } c, ok := p.connectors[device.ConnectionKey] if !ok { return nil, errors.New("not found connection") } return c, nil}
// Destroy 销毁插件func (p *Plugin) Destroy() error { for _, conn := range p.connectors { go conn.client.Disconnect(0) } return nil}配置加载流程
Section titled “配置加载流程”插件配置的加载过程如下:
flowchart TD
JSONConfig[JSON配置文件] --> CoreCache[核心缓存]
CoreCache --> PluginManager[插件管理器]
PluginManager --> PluginInitialize[插件初始化]
PluginInitialize --> ConnectorPool[连接器池]
ConnectorPool --> DeviceShadow[设备影子]
DeviceShadow --> EventBus[事件总线]
配置加载的关键步骤:
- 配置扫描:扫描
res/driver/目录下的所有配置文件 - 核心缓存:解析配置并存储到核心缓存中
- 插件初始化:调用插件的
Initialize()方法传递配置 - 连接器创建:根据配置创建连接器实例
- 设备影子注册:将设备注册到影子服务中
内置插件列表
Section titled “内置插件列表”| 插件名称 | 协议类型 | 状态 | 描述 | 目录 |
|---|---|---|---|---|
| modbus | 工业协议 | ✅ 稳定 | Modbus RTU/TCP | plugins/modbus/ |
| mqtt | 物联网协议 | ✅ 稳定 | MQTT 3.1.1/5.0 | plugins/mqtt/ |
| httpclient | Web协议 | ✅ 稳定 | HTTP客户端 | plugins/httpclient/ |
| httpserver | Web协议 | ✅ 稳定 | HTTP服务端 | plugins/httpserver/ |
| websocket | Web协议 | ✅ 稳定 | WebSocket | plugins/websocket/ |
| tcpserver | 网络协议 | ✅ 稳定 | TCP服务端 | plugins/tcpserver/ |
| bacnet | 楼控协议 | ✅ 稳定 | BACnet | plugins/bacnet/ |
| dlt645 | 电表协议 | ✅ 稳定 | DL/T645 | plugins/dlt645/ |
插件接口定义了标准错误类型 (driverbox/plugin/plugin.go:12):
var ( NotSupportGetConnector = errors.New("the protocol does not support getting connector") NotSupportEncode = errors.New("the protocol adapter does not support encode functions") NotSupportDecode = errors.New("the protocol adapter does not support decode functions"))错误处理最佳实践:
// 1. 检查是否支持连接器conn, err := plugin.Connector(deviceId)if errors.Is(err, plugin.NotSupportGetConnector) { log.Info("协议不支持连接器模式")} else if err != nil { log.Error("获取连接器失败", zap.Error(err)) return}
// 2. 检查是否支持编码data, err := conn.Encode(deviceId, plugin.ReadMode, points...)if errors.Is(err, plugin.NotSupportEncode) { log.Info("协议不支持编码")} else if err != nil { log.Error("编码失败", zap.Error(err)) return}