边缘网关驱动 SDK 开发

更新时间:2024-06-21 07:01:51下载pdf

本章节描述如何使用涂鸦 tuya-edge-driver-sdk-go 版本 SDK 进行边缘侧设备服务的开发流程。驱动开发前,建议您先部署好涂鸦 边缘计算网关

SDK 介绍

tuya-edge-driver-sdk-go 用于实现涂鸦边缘计算网关的南向设备服务。SDK 的功能主要包含以下几个部分:

  • 接收涂鸦发送的设备控制指令,包括读设备控制指令和写设备控制指令。
  • 异步接收设备发送的DP数据,并将接收到的数据推送到涂鸦边缘计算网关,用于后续数据处理。
  • 子设备元信息的同步。
  • 简单的设备定时任务,复杂的定时任务请使用涂鸦边缘计算网关的定时服务。

环境要求

获取驱动配置模板

您可以访问本地部署的涂鸦边缘计算节点 http://your-edge-ip:3000/driver/lib,下载驱动配置模板。

边缘网关驱动 SDK 开发

配置模板内容如下:

// config.json
{
	"deviceServer": {
	"driver": {

		}
	},
	// 其他配置
}

具体配置需要在 driver 字段中配置,格式为键值对(key-value)形式。例如,要开发 MQTT 相关的设备服务时,就可以指定 MQTT 服务相关的 IP 地址、端口、QoS 等配置。

示例如下:

// config.json
{
	"deviceServer": {
	"driver": {
			"MQTTBrokerIP": "0.0.0.0",
			"MQTTBrokerPort": "1883"
		}
	},
	// 其他配置
}

运行服务时,应用可以通过 SDK 提供的 func DriverConfigs() map[string]string 方法来获取驱动配置。

获取驱动项目模板

您需要使用SDK中自带的device-service-template进行相应的驱动服务开发。工程目录如下:

边缘网关驱动 SDK 开发

驱动的业务层代码可以在internal/driver目录下进行开发。开发完成后请对DockerfileMakefile按照实际情况进行修改,之后可以执行make build将驱动服务打包成 Docker 镜像。

开发驱动服务

初始化驱动

main 函数中调用 func Bootstrap(serviceName string, serviceVersion string, driver interface{}) 函数即可完成 SDK 初始化,其中 driver 字段为您需要实现的 SDK ProtocolDriver 接口。

// main.go
package main

import (
	"device-service-template/internal/driver"

	"github.com/tuya/tuya-edge-driver-sdk-go"
	"github.com/tuya/tuya-edge-driver-sdk-go/pkg/startup"
)

const (
	serviceName string = "device-service-template"
)

func main() {
	sd := driver.SimpleDriver{}
	startup.Bootstrap(serviceName, device.Version, &sd)
}

实现驱动接口

您需要实现 ProtocolDriver 接口,以实现初始化驱动服务、发送读设备控制、写设备控制指令和增、删、改设备通知等功能。接口定义如下:

type ProtocolDriver interface {
	Initialize(lc logger.LoggingClient, asyncCh chan<- *AsyncValues, deviceCh chan<- []DiscoveredDevice) error
	HandleReadCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []CommandRequest) ([]*CommandValue, error)
	HandleWriteCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []CommandRequest, params []*CommandValue) error
	Stop(force bool) error
	AddDevice(deviceName string, protocols map[string]models.ProtocolProperties, adminState models.AdminState) error
	UpdateDevice(deviceName string, protocols map[string]models.ProtocolProperties, adminState models.AdminState) error
	RemoveDevice(deviceName string, protocols map[string]models.ProtocolProperties) error
}

各函数的具体示例如下。

  1. 驱动初始化Initialize,比如设备连接使用的 MQTT Broker 或者 HTTP Server 等。

    函数:

    func (d *SimpleDriver) Initialize(lc logger.LoggingClient, asyncCh chan<- *AsyncValues, deviceCh chan<- []DiscoveredDevice) error`
    

    Go 示例:

    func (d *SimpleDriver) Initialize(lc logger.LoggingClient, asyncCh chan<- *dsModels.AsyncValues, deviceCh chan<- []dsModels.DiscoveredDevice) error {
    	d.lc = lc
    	d.asyncCh = asyncCh
    // code here
    // 初始化业务代码
    	return nil
    }
    
  2. 设备数据读取HandleReadCommands

    函数:

    func (d *SimpleDriver) HandleReadCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []CommandRequest) ([]*CommandValue, error)
    

    Go 示例:

    // 控制设备的读指令会通过该函数到达业务代码层,您需要在该函数内实现设备数据读取。
    func (d *SimpleDriver) HandleReadCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []dsModels.CommandRequest) (res []*dsModels.CommandValue, err error) {
    	rd := d.retrieveRandomDevice(deviceName)
    
    	res = make([]*dsModels.CommandValue, len(reqs))
    	now := time.Now().UnixNano()
    
    	for i, req := range reqs {
    		t := req.Type
    		v, err := rd.value(t)
    		if err != nil {
    			return nil, err
    		}
    		var cv *dsModels.CommandValue
    		switch t {
    		case contracts.ValueTypeInt8:
    			cv, _ = dsModels.NewInt8Value(req.DeviceResourceName, now, int8(v))
    		case contracts.ValueTypeInt16:
    			cv, _ = dsModels.NewInt16Value(req.DeviceResourceName, now, int16(v))
    		case contracts.ValueTypeInt32:
    			cv, _ = dsModels.NewInt32Value(req.DeviceResourceName, now, int32(v))
    		}
    		res[i] = cv
    	}
    
    	return res, nil
    }
    

    注意

    • 请求参数protocols中的内容与子设备添加时的协议选择相关,示例如下:
    • 本文展示的是添加一个标准Modbus协议的设备,由于Modbus驱动服务需要主动去连接Modbus设备,所以在添加该设备时需要提供访问该设备的一些参数。其他设备,根据具体的协议判断是否需要相关参数。
    • 当控制指令发送到设备驱动服务后,SDK在调用该函数时会将对应设备的参数存入protocols中,驱动使用这些参数去连接设备, 进行控制指令发送。
      • reqs:设备控制指令的请求
      • commandRequest.DeviceResourceName:此次控制请求的设备资源的名称,即物模型定义的DP。
      • commandRequest.Type:本次读取到的设备资源的类型。commandRequest.Attributes代表该设备资源的一些属性,此处再以Modbus设备举例,则该设备资源的属性可能有以下:
        以上表示读取该Modbus设备DP为15的数据时用到的读功能码为03,起始地址为0,读取的长度为1个字长,具体含义请参考标准Modbus协议。
    • SDK支持的数据类型在SDK的contracts包下可以找到,您可以调用SDK models包下的相关函数做数据转换。
  3. 写入设备数据HandleWriteCommands

    func (d *SimpleDriver) HandleWriteCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []CommandRequest, params []*CommandValue) error
    
    • params:写设备控制指令的参数,即为要写入数据的值。

    • params:在写入设备前需要做相应的数据类型转换,您可以调用SDK models包下的相关函数做数据转换。