进程模式软网关自定义驱动开发
更新时间:2023-07-10
功能简介
进程模式使用了和容器模式不一样的数采上报架构,使用了软网关模块。该模块大大简化了自定义协议驱动开发难度,当有新增驱动协议时,用户只需要开发数据采集插件,由软网关模块来完成数据的清洗、转换、上报。且数采插件开发与语言无关,用户可以使用任意支持的语言进行插件开发(容器模式自定义协议驱动仅支持go语言)。架构图如下
操作指南
当前平台内置的协议包含modbus tcp、modbus rtu、bacnet、iec104、opcua、opcda,5种不同类型的协议。若用户有其余的协议设备接入需求,可以参考软网关自定义协议驱动开发文档。
软网关部署
用户创建产品、设备、接入模版后,可在节点的子设备管理界面,进行设备与节点的绑定。具体操作步骤可参考文档‘子设备绑定’。 相关信息填写完毕后,点击部署软网关,会向边缘节点下发baetyl-gateway-{nodename}应用。
软网关程序为主进程,主进程下会挂载针对不同协议插件的子进程。
自定义驱动开发
架构图
简介
1、baetyl-gateway采用go-plugin框架开发,宿主进程与插件进程间通过gRPC方式通信,插件进程只需实现指定接口即可。
2、插件实现语言无关,用户可自定选择熟悉的语言进行开发,只需要实现下述固定接口即可。
3、宿主进程与插件进程间接口列表
Plain Text
1type Driver interface {
2 GetDriverInfo(req *Request) (*Response, error)
3 SetConfig(req *Request) (*Response, error)
4 Setup(config *BackendConfig) (*Response, error)
5 Start(req *Request) (*Response, error)
6 Restart(req *Request) (*Response, error)
7 Stop(req *Request) (*Response, error)
8
9 Get(req *Request) (*Response, error)
10 Set(req *Request) (*Response, error)
11}
12
13type Report interface {
14 // 驱动 --> 宿主
15 Post(req *Request) (*Response, error)
16 State(req *Request) (*Response, error)
17}
开发指南
主进程中开启gRPC服务,监听宿主进程调用请求
Plain Text
1func main() {
2 if err := plugin.Serve(&plugin.ServeOpts{
3 // 工厂函数返回插件进程用于实现指定接口的结构体实例
4 FactoryFunc: modbus.NewDriver,
5 }); err != nil {
6 logger := hclog.New(&hclog.LoggerOptions{})
7 logger.Error("plugin modbus shutting down", "error", err)
8 os.Exit(1)
9 }
10}
11
12type Driver struct {
13 driverName string
14 configPath string
15 report plugin.Report
16 mds *Modbus
17 log hclog.Logger
18}
19
20func NewDriver(_ context.Context, cfg *plugin.BackendConfig) (plugin.Driver, error) {
21 b := &Driver{log: cfg.Log}
22 return b, nil
23}
插件实现指定接口
插件侧server 宿主侧client
Plain Text
1//
2GetDriverInfo
3获取驱动信息
4func (d *Driver) GetDriverInfo(req *plugin.Request) (*plugin.Response, error) {
5 return nil, nil
6}
7
8// SetConfig 配置驱动,目前只配置了驱动的配置文件路径
9func (d *Driver) SetConfig(req *plugin.Request) (*plugin.Response, error) {
10 d.configPath = req.Req
11 return &plugin.Response{Data: fmt.Sprintf("Plugin %s SetConfig success", d.driverName)}, nil
12}
13
14// Setup 宿主进程上报接口传递,必须调用下述逻辑,其余可用户自定义
15func (d *Driver) Setup(config *plugin.BackendConfig) (*plugin.Response, error) {
16 d.driverName = config.DriverName
17 d.report = config.ReportSvc
18 return &plugin.Response{Data: fmt.Sprintf("Plugin %s Setup success", d.driverName)}, nil
19}
20
21// Start 驱动采集启动,用户自定义实现
22func (d *Driver) Start(req *plugin.Request) (*plugin.Response, error) {
23 return nil, nil
24}
25
26// Restart 驱动重启,用户自定义实现
27func (d *Driver) Restart(req *plugin.Request) (*plugin.Response, error) {
28 return nil, nil
29}
30
31// Stop 驱动停止,用户自定义实现
32func (d *Driver) Stop(req *plugin.Request) (*plugin.Response, error) {
33 return nil, nil
34}
35
36// Get 召测,用户自定义实现
37func (d *Driver) Get(req *plugin.Request) (*plugin.Response, error) {
38 return nil, nil
39}
40
41// Set 置数,用户自定义实现
42func (d *Driver) Set(req *plugin.Request) (*plugin.Response, error) {
43 return nil, nil
44}
插件侧client 宿主侧server
Plain Text
1// 插件侧采集上报接口,指定消息类型 MessageDeviceReport
2func (m *ReportImpl) Post(req *plugin.Request) (*plugin.Response, error) {
3 msg := &v1.Message{}
4 err := json.Unmarshal([]byte(req.Req), msg)
5 if err != nil {
6 return nil, err
7 }
8 switch msg.Kind {
9 case v1.MessageDeviceReport:
10 select {
11 case m.msgCh <- msg:
12 default:
13 m.log.Error("failed to write device report message", log.Any("msg", msg))
14 }
15 default:
16 m.log.Error("message kind not supported", log.Any("type", msg.Kind))
17 }
18
19 return &plugin.Response{Data: fmt.Sprintf("msg kind: %s post success", msg.Kind)}, nil
20}
21
22// State 驱动状态上报接口,驱动调用,消息放入channel,指定消息类型 MessageDeviceLifecycleReport
23func (m *ReportImpl) State(req *plugin.Request) (*plugin.Response, error) {
24 msg := &v1.Message{}
25 err := json.Unmarshal([]byte(req.Req), msg)
26 if err != nil {
27 return nil, err
28 }
29 switch msg.Kind {
30 case v1.MessageDeviceLifecycleReport:
31 select {
32 case m.msgCh <- msg:
33 default:
34 m.log.Error("failed to write device state message", log.Any("msg", msg))
35 }
36 default:
37 m.log.Error("message kind not supported", log.Any("type", msg.Kind))
38 }
39 return &plugin.Response{Data: fmt.Sprintf("msg kind: %s state success", msg.Kind)}, nil
40}
41
42
43// 插件侧调用示例,插件侧setup后,上报接口实现传递至插件侧,插件只需调用即可
44res, err := w.driver.report.Post(&plugin.Request{Req: string(msgData)})
45
46res, err = s.driver.report.State(&plugin.Request{Req: string(msgData)})
消息类型
Plain Text
1// 采集上报
2MessageDeviceReport MessageKind = "deviceReport"
3
4// 状态上报
5MessageDeviceLifecycleReport MessageKind = "thing.lifecycle.post"
6
7// 置数消息
8MessageDeviceDelta MessageKind = "deviceDelta"
9
10// 召测消息
11MessageDevicePropertyGet MessageKind = "thing.property.get"
软网关主配置文件
- 软网关主配置文件,包含插件配置,apiserver配置及mqtt配置
- 插件配置包含插件名(需与二进制文件同名)、bin文件路径、配置文件路径
- apiserver为插件管理server
- mqtt配置,如果不配置,则默认连接bie的系统应用baetyl-broker
Plain Text
1plugin:
2 drivers:
3 - name: modbus
4 binPath: "etc/modbus"
5 configPath: "etc/modbus"
6server:
7 port: ":9889"
8mqttConfig:
9 address: mqtt://127.0.0.1:8963
10 cleansession: true
11 username: test
12 password: hahaha
插件配置文件
sub_devices.yml
『节点管理』-『子设备管理』中的驱动配置和子设备配置信息存放在sub_device.yml中
- devcies[i].accessConfig.custom为子设备的配置
- driver为驱动配置
Plain Text
1devices:
2- name: device1
3 version: 1663240313luvrs4
4 deviceModel: device-model
5 accessTemplate: device-access-tpl
6 deviceTopic:
7 delta:
8 qos: 1
9 topic: thing/device-model/device1/property/invoke
10 report:
11 qos: 1
12 topic: thing/device-model/device1/property/post
13 event:
14 qos: 1
15 topic: thing/device-model/device1/raw/c2d
16 get:
17 qos: 1
18 topic: $baetyl/device/device1/get
19 getResponse:
20 qos: 1
21 topic: $baetyl/device/device1/getResponse
22 eventReport:
23 qos: 1
24 topic: thing/device-model/device1/event/post
25 propertyGet:
26 qos: 1
27 topic: thing/device-model/device1/property/get
28 lifecycleReport:
29 qos: 1
30 topic: thing/device-model/device1/lifecycle/post
31 accessConfig:
32 custom: |-
33 channelId: test-chan-01
34 machineNumber: N001L01.101
35driver: |-
36 channels:
37 - name: test-chan-01
38 address: 192.168.0.1:23
39 interval: 30s
models.yml
『子设备管理』-『产品』中的产品测点信息存放在models.yml中
Plain Text
1device-model:
2- name: switch
3 type: bool
4 mode: rw
5- name: temperature
6 type: float32
7 mode: ro
8- name: humidity
9 type: float32
10 mode: ro
11- name: high-temperature-threshold
12 type: int32
13 mode: rw
14- name: high-temperature-alarm
15 type: bool
16 mode: ro
access_template.yml
『子设备管理』-『接入模板』中的设备点表和物模型点位映射信息存放在access_template.yml中
- device-access-tpl.properties[i].visitor.custom为设备点表信息中的采集配置
- device-access-tpl.properties[i].mapping为物模型点位映射信息
Plain Text
1device-access-tpl:
2 properties:
3 - name: 高温报警
4 id: "1"
5 type: bool
6 visitor:
7 custom: |-
8 start: 1
9 offset: 14
10 mappings:
11 - attribute: high-temperature-alarm
12 type: value
13 expression: x1