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

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

Enlace del blog de este artículo: http://blog.csdn.net/jdh99 , Autor: jdh, reimprimir, especifique.

Bienvenido al intercambio de la comunidad: Haiying Internet of Things Community

Introducción

RPC: Llamada a procedimiento remoto, llamada a procedimiento remoto. Con RPC, un programa en una computadora puede llamar a un programa en otra computadora.

RPC abstrae la comunicación de la red en llamadas a procedimientos remotos. Invocar procedimientos remotos es tan conveniente como llamar a subrutinas locales. Esto protege la complejidad de la comunicación y permite a los desarrolladores ahorrar más tiempo y tiempo sin prestar atención a los detalles de la programación de la red. Céntrese en la realización de propia lógica empresarial para mejorar la eficiencia del trabajo.

DCOM: Protocolo de comunicación de dispositivos (DCOM), protocolo de comunicación entre dispositivos. DCOM es un marco RPC desarrollado para escenarios de uso de IoT. Tiene las siguientes características:

  • La sobrecarga del protocolo es extremadamente pequeña y solo 4 bytes. Muchos escenarios de Internet de las cosas son marcos cortos de decenas de bytes. Si la sobrecarga del protocolo RPC en sí es demasiado grande, no se puede aplicar en estos escenarios.
  • Puede comunicarse en varios idiomas. El protocolo DCOM no tiene nada que ver con el lenguaje en el diseño, y DCOM se puede usar independientemente de C, Golang, Python, etc.
  • Puede comunicarse a través de los medios de comunicación. El protocolo DCOM puede funcionar en todos los medios de comunicación, como Ethernet, puerto serie, wifi, conexión inalámbrica pequeña, etc.

En Haiying Internet of Things, DCOM se utiliza para comunicarse entre nodos. Este artículo presenta cómo utilizar el paquete DCOM desarrollado por Go Language.

Fuente abierta

instalación

Recomendamos usar go mod: github.com/jdhxyy/dcom

Después de la instalación, se puede importar y utilizar en el proyecto:

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

Concepto basico

ID de recurso

El ID de recurso también se denomina número de servicio: ID de recurso o RID para abreviar. El nodo usa el número de servicio para abrir sus propias capacidades o recursos. El número de servicio tiene un valor de 1-1023 y un total de 1023 números.

Por ejemplo, un nodo es un enchufe inteligente, que abre dos capacidades:

  • Interruptor de enchufe
  • Estado actual del enchufe

Se pueden proporcionar dos servicios externamente:

Número de servicio Descripción
1 Interruptor de enchufe
2 Estado actual del enchufe

Numero de acuerdo

Protocolo de número de protocolo, cada protocolo se puede vincular a un conjunto de ID de recursos y se pueden repetir los ID de recursos en diferentes protocolos.

tubería

Pipa: pipa. Durante la comunicación DCOM, el flujo de datos se puede vincular a una determinada tubería y DCOM admite la comunicación simultánea de múltiples tuberías. Las tuberías pueden ser puertos UDP, puertos TCP, puertos serie, 4G, inalámbricos, etc. Cada tubería tiene devoluciones de llamada de envío y recepción independientes. El número de tubería no puede ser 0, es un número de 64 dígitos.

Modelo de comunicación

DCOM tiene principalmente dos modelos de comunicación, comunicación de respuesta y comunicación de no respuesta.

Comúnmente se utiliza la comunicación con una respuesta. Si no se recibe respuesta, DCOM intentará retransmitir durante la comunicación. Cada vez que se utiliza la función de llamada para comunicarse con el nodo de destino, se trata de una nueva sesión, y cada sesión tiene un valor de token único para garantizar que los datos no se crucen.

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)
  • Función auxiliar
// StructToBytes 结构体转字节流
func StructToBytes(s interface{
    
    }) ([]uint8, error)

// BytesToStruct 字节流转结构体 结构体中的元素首字母要求大写
// s是结构体指针,保存转换后的结构体
func BytesToStruct(data []uint8, s interface{
    
    }) error
  • estructura de datos
// 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)
  • Código de error del sistema
const (
	// 正确值
	SystemOK = 0
	// 接收超时
	SystemErrorRxTimeout = 0x10
	// 发送超时
	SystemErrorTxTimeout = 0x11
	// 内存不足
	SystemErrorNotEnoughMemory = 0x12
	// 没有对应的资源ID
	SystemErrorInvalidRid = 0x13
	// 块传输校验错误
	SystemErrorWrongBlockCheck = 0x14
	// 块传输偏移地址错误
	SystemErrorWrongBlockOffset = 0x15
	// 参数错误
	SystemErrorParamInvalid = 0x16
)

Carga: carga del módulo

Debe inicializarse antes de usar DCOM. DCOM admite la retransmisión, por lo que debe ingresar el intervalo de retransmisión y el número máximo de retransmisiones durante la inicialización.

DCOM no tiene nada que ver con el medio de comunicación, diferentes medios pueden definir diferentes números de tubería. La aplicación necesita escribir diferentes operaciones de canalización para permitir la función de envío (IsAllowSend) y la función de envío (Enviar).

  • Ejemplo: un nodo tiene dos canalizaciones
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)
	}
}

Los campos como protocolo y dstIA se procesan de acuerdo con los requisitos.

Recibir datos

El programa de aplicación recibe los datos y necesita llamar a la función Recibir para enviar los datos a DCOM.

  • Ejemplo: un nodo puede recibir ambos canales
func pipe1Receive(data []uint8) {
    
    
	dcom.Receive(0, 1, 0x2140000000000101, data)
}

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

El número de protocolo en el ejemplo de protocolo es 0 y debe completarse de acuerdo con el escenario real durante la aplicación.

Registro: registro de servicio

Los nodos pueden abrir sus capacidades a través de servicios de registro.

  • Ejemplo: suponiendo que el nodo 2140 :: 101 es un socket inteligente, proporciona dos servicios: control y lectura del estado del conmutador:
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
}

Llamada: llamada sincrónica

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

Las llamadas síncronas se bloquearán hasta que se obtenga el resultado. El nodo puede llamar a la función o al servicio del nodo de destino mediante una llamada síncrona. El campo de tiempo de espera es el período de tiempo de espera en milisegundos. Si el nodo de destino no responde después de un tiempo de espera, la llamada fallará. Si el período de tiempo de espera se llena con 0, significa que el nodo de destino no necesita responder.

  • Ejemplo: el nodo 2141 :: 102 controla el zócalo inteligente 2141 :: 101 El estado del interruptor está activado
resp, errCode := dcom.Call(0, 1, 0x2140000000000101, 3000, []uint8{
    
    1})
  • Ejemplo: el nodo 2141 :: 102 lee el estado del conmutador del zócalo inteligente 2141 :: 101
resp, errCode := dcom.Call(0, 2, 0x2140000000000101, 3000, nil)
if errCode == dcom.SystemOK {
    
    
	fmt.println("开关状态:", resp[0])
}

Formato de datos de solicitud y respuesta

Los flujos de datos enviados por ambas partes de la comunicación DCOM son binarios, y los tipos de datos de solicitud (req) y respuesta (resp) son [] uint8.

El binario no favorece el procesamiento de la aplicación, por lo que se convertirá a otros tipos de datos para su procesamiento. Hay tres de uso común:

  • Estructura
  • json
  • Cuerda

En Internet de las cosas, los recursos de los nodos de hardware son limitados y la mayoría de ellos están escritos en lenguaje C. Por lo que se recomienda utilizar la estructura del lenguaje C para comunicarse. La estructura acepta usar alineación de 1 byte, modo little-endian.

Por ejemplo, la estructura de datos proporcionada por el servicio ntp de Haiying Internet of Things:

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

DCOM proporciona funciones para estructura y conversión binaria:

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

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

Nota: La definición de estas estructuras en go requiere que los atributos se escriban con mayúscula; de lo contrario, la conversión a binario fallará.

  • Ejemplo: convertir una estructura de tiempo en binario
// 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)
  • Ejemplo: convertir binario en estructura
var t Time
err := BytesToStruct(data, &t)

Convención: si la comunicación binaria se utiliza directamente en la comunicación DCOM, se recomienda utilizar el modo big-endian. Si usa la comunicación estructurada, la codificación de la estructura es little-endian. Si la transmisión es mixta, hay binario y estructura, la parte binaria usa big endian y la parte de estructura usa little endian.

Supongo que te gusta

Origin blog.csdn.net/jdh99/article/details/115331198
Recomendado
Clasificación