Haiying Internet of Things Tutorial: Internet of Things RPC Framework Go DCOM

Haiying Internet of Things Tutorial: Internet of Things RPC Framework Go DCOM

This article blog link: http://blog.csdn.net/jdh99 , Author: jdh, reprint please specify.

Welcome to the community exchange: Haiying Internet of Things Community

Introduction

RPC: Remote Procedure Call, remote procedure call. Using RPC, a program on one computer can call a program on another computer.

RPC abstracts network communication into remote procedure calls. Invoking remote procedures is as convenient as calling local subroutines. This shields the complexity of communication and enables developers to save more time and effort without paying attention to the details of network programming. Focus on the realization of business logic itself to improve work efficiency.

DCOM: Device Communication Protocol (DCOM), communication protocol between devices. DCOM is an RPC framework developed for IoT usage scenarios. It has the following characteristics:

  • The protocol overhead is extremely small and only 4 bytes. Many scenarios of the Internet of Things are short frames of tens of bytes. If the overhead of the RPC protocol itself is too large, it cannot be applied in these scenarios.
  • Can communicate across languages. The DCOM protocol has nothing to do with language in design, and DCOM can be used regardless of C, Golang, Python, etc.
  • Can communicate across communication media. DCOM protocol can work on all communication media such as Ethernet, serial port, wifi, small wireless, etc.

In Haiying Internet of Things, DCOM is used to communicate between nodes. This article introduces how to use the DCOM package developed by Go language.

Open source

installation

Recommend to use go mod: github.com/jdhxyy/dcom

After installation, it can be imported and used in the project:

import "https://github.com/jdhxyy/dcom"

Basic concept

Resource ID

Resource ID is also called service number: Resource ID, or RID for short. The node uses the service number to open its own capabilities or resources. The service number has a value of 1-1023 and a total of 1023.

For example, a node is a smart socket, opening two capabilities:

  • Socket switch
  • Current state of the socket

Two services can be provided externally:

Service number Description
1 Socket switch
2 Current state of the socket

Agreement number

Protocol number protocol, each protocol can be bound to a set of resource IDs, and resource IDs under different protocols can be repeated.

pipeline

Pipe: pipe. During DCOM communication, the data stream can be bound to a certain pipe, and DCOM supports simultaneous communication of multiple pipes. Pipes can be UDP ports, TCP ports, serial ports, 4G, wireless, etc. Each pipe has independent sending and receiving callbacks. The pipe number cannot be 0, it is a 64-digit number.

Communication model

DCOM mainly has two communication models, response communication and non-response communication.

Commonly used is communication with a response. If no response is received, DCOM will try to retransmit during communication. Each time the Call function is used to communicate with the destination node, it is a new session, and each session has a unique token value to ensure that data will not crosstalk.

API

// Load 模块载入
func Load(param *LoadParam)

// Receive 接收数据
// 应用模块接收到数据后需调用本函数
// 本函数接收帧的格式为DCOM协议数据
func Receive(protocol int, pipe uint64, srcIA uint64, bytes []uint8)

// Register 注册服务回调函数
func Register(protocol int, rid int, callback CallbackFunc)

// Call RPC同步调用
// timeout是超时时间,单位:ms.为0表示不需要应答
// 返回值是应答字节流和错误码.错误码非SystemOK表示调用失败
func Call(protocol int, pipe uint64, dstIA uint64, rid int, timeout int, req []uint8) ([]uint8, int)
  • Auxiliary function
// StructToBytes 结构体转字节流
func StructToBytes(s interface{
    
    }) ([]uint8, error)

// BytesToStruct 字节流转结构体 结构体中的元素首字母要求大写
// s是结构体指针,保存转换后的结构体
func BytesToStruct(data []uint8, s interface{
    
    }) error
  • data structure
// LoadParam 载入参数
type LoadParam struct {
    
    
	// 块传输帧重试间隔.单位:ms
	BlockRetryInterval int
	// 块传输帧重试最大次数
	BlockRetryMaxNum int

	// API接口
	// 是否允许发送
	IsAllowSend IsAllowSendFuncByPipeFunc
	// 发送的是DCOM协议数据
	Send SendByPipeFunc
}

// IsAllowSendFuncByPipeFunc 某管道是否允许发送函数类型
type IsAllowSendFuncByPipeFunc func(pipe uint64) bool

// SendByPipeFunc 向指定端口发送函数类型
type SendByPipeFunc func(protocol int, pipe uint64, dstIA uint64, bytes []uint8)

// CallbackFunc 注册DCOM服务回调函数
// 返回值是应答和错误码.错误码为0表示回调成功,否则是错误码
type CallbackFunc func(pipe uint64, srcIA uint64, req []uint8) ([]uint8, int)
  • System error code
const (
	// 正确值
	SystemOK = 0
	// 接收超时
	SystemErrorRxTimeout = 0x10
	// 发送超时
	SystemErrorTxTimeout = 0x11
	// 内存不足
	SystemErrorNotEnoughMemory = 0x12
	// 没有对应的资源ID
	SystemErrorInvalidRid = 0x13
	// 块传输校验错误
	SystemErrorWrongBlockCheck = 0x14
	// 块传输偏移地址错误
	SystemErrorWrongBlockOffset = 0x15
	// 参数错误
	SystemErrorParamInvalid = 0x16
)

Load: Module loading

It must be initialized before using DCOM. DCOM supports retransmission, so you need to enter the retransmission interval and the maximum number of retransmissions during initialization.

DCOM has nothing to do with the communication medium. Different mediums can define different pipe numbers. The application needs to write different pipeline operations in whether to allow the sending function (IsAllowSend) and the sending function (Send).

  • Example: a node has two pipelines
func main() {
    
    
	var param dcom.LoadParam
	param.BlockRetryMaxNum = 5
	param.BlockRetryInterval = 1000
	param.IsAllowSend = isAllowSend
	param.Send = send
	dcom.Load(&param)
}

func isAllowSend(pipe uin64) bool {
    
    
	if pipe == 1 {
    
    
		return isPipe1AllowSend()
	} else {
    
    
		return isPipe2AllowSend()
	}
}

func send(protocol int, pipe uint64, dstIA uint64, data []uint8) {
    
    
	if pipe == 1 {
    
    
		pipe1Send(data)
	} else {
    
    
		pipe2Send(data)
	}
}

Fields such as protocol and dstIA are processed according to requirements.

Receive data

The application program receives the data and needs to call the Receive function to send the data to DCOM.

  • Example: A node can receive both channels
func pipe1Receive(data []uint8) {
    
    
	dcom.Receive(0, 1, 0x2140000000000101, data)
}

func pipe2Receive(data []uint8) {
    
    
	dcom.Receive(0, 2, 0x2140000000000101, data)
}

The protocol number in the protocol example is 0, and it should be filled in according to the actual scenario during application.

Register: Service registration

Nodes can open their capabilities through registration services.

  • Example: Assuming that the node 2140::101 is a smart socket, it provides two services: control and read switch status:
dcom.Register(0, 1, controlService)
dcom.Register(0, 2, getStateService)

// controlService 控制开关服务
// 返回值是应答和错误码.错误码为0表示回调成功,否则是错误码
func controlService(pipe uint64, srcIA uint64, req []uint8) ([]uint8, int) {
    
    
	if req[0] == 0 {
    
    
		off()
	} else {
    
    
		on()
	}
	return nil, dcom.SystemOK
}

// getStateService 读取开关状态服务
// 返回值是应答和错误码.错误码为0表示回调成功,否则是错误码
func getStateService(pipe uint64, srcIA uint64, req []uint8) ([]uint8, int) {
    
    
	return []uint8{
    
    state()}, dcom.SystemOK
}

Call: synchronous call

// Call RPC同步调用
// timeout是超时时间,单位:ms.为0表示不需要应答
// 返回值是应答字节流和错误码.错误码非SystemOK表示调用失败
func Call(protocol int, pipe uint64, dstIA uint64, rid int, timeout int, req []uint8) ([]uint8, int)

Synchronous calls will block until the result is obtained. The node can call the function or service of the target node through synchronous call. The timeout field is the timeout period in milliseconds. If the target node does not respond after a timeout, the call will fail. If the timeout period is filled with 0, it means that the target node does not need to reply.

  • Example: 2141::102 node controls the smart socket 2141::101 switch state is on
resp, errCode := dcom.Call(0, 1, 0x2140000000000101, 3000, []uint8{
    
    1})
  • Example: The 2141::102 node reads the switch status of the smart socket 2141::101
resp, errCode := dcom.Call(0, 2, 0x2140000000000101, 3000, nil)
if errCode == dcom.SystemOK {
    
    
	fmt.println("开关状态:", resp[0])
}

Request and response data format

The data streams sent by both parties of DCOM communication are binary, and the data types of request (req) and response (resp) are both []uint8.

Binary is not conducive to application processing, so it will be converted to other data types for processing. There are three commonly used:

  • Structure
  • json
  • String

In the Internet of Things, the resources of hardware nodes are limited, and most of them are written in C language. So it is recommended to use C language structure to communicate. The structure agrees to use 1-byte alignment, little-endian mode.

For example, the data structure provided by the ntp service of Haiying Internet of Things:

struct {
    
    
    // 时区
    uint8 TimeZone
    uint16 Year
    uint8 Month
    uint8 Day
    uint8 Hour
    uint8 Minute
    uint8 Second
    // 星期
    uint8 Weekday
}

DCOM provides functions for structure and binary conversion:

// StructToBytes 结构体转字节流
func StructToBytes(s interface{
    
    }) ([]uint8, error)

// BytesToStruct 字节流转结构体 结构体中的元素首字母要求大写
// s是结构体指针,保存转换后的结构体
func BytesToStruct(data []uint8, s interface{
    
    }) error

Note: The definition of these structures in go requires that the attributes must be capitalized, otherwise the conversion to binary will fail.

  • Example: Convert a time structure to binary
// ACK格式
type Time struct {
    
    
	// 时区
	TimeZone uint8
	Year     uint16
	Month    uint8
	Day      uint8
	Hour     uint8
	Minute   uint8
	Second   uint8
	// 星期
	Weekday uint8
}

t := Time{
    
    8, 2021, 4, 1, 7, 32, 1, 4}
data := StructToBytes(t)
  • Example: Convert binary to structure
var t Time
err := BytesToStruct(data, &t)

Convention: If binary communication is used directly in DCOM communication, it is recommended to use big-endian mode. If you use structured communication, the structure encoding is little-endian. If the transmission is mixed, there are both binary and structure. The binary part uses big endian and the structure part uses little endian.

Guess you like

Origin blog.csdn.net/jdh99/article/details/115331198