Files

156 lines
4.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package protocol
import (
"encoding/binary"
"fmt"
)
const (
MainBasicInfo = byte(0x01)
MainRuntime = byte(0x20)
MainQuery = byte(0x30)
MainSetting = byte(0x40)
MainRealtime = byte(0x50)
MainHistorical = byte(0x60)
SubValve = byte(0x0A)
)
// DataBlock 是主标识下的一个子标识数据块。
type DataBlock struct {
SubID byte
Data []byte
}
type RealtimeData struct {
Sensors []SensorReading `json:"sensors,omitempty"`
Events []EventReading `json:"events,omitempty"`
IO []IOReading `json:"io,omitempty"`
Parameters []ParameterReading `json:"parameters,omitempty"`
}
type SensorReading struct {
Number uint32 `json:"number"`
SensorType byte `json:"sensor_type"`
Object uint16 `json:"object"`
Value int16 `json:"value"`
Unit byte `json:"unit"`
Decimal byte `json:"decimal"`
Status byte `json:"status"`
}
type EventReading struct {
Type uint16 `json:"type"`
Number uint32 `json:"number"`
Value int16 `json:"value"`
}
type IOReading struct {
Type uint16 `json:"type"`
Number uint32 `json:"number"`
Status byte `json:"status"`
}
type ParameterReading struct {
Code byte `json:"code"`
Value int16 `json:"value"`
}
// EncodePayload 按“主标识、块数量、子标识、定长数据”编码。
// 由于厂商协议没有携带块长度,本函数用于已知命令;上行解析由业务标识专用解析器完成。
func EncodePayload(mainID byte, blocks ...DataBlock) ([]byte, error) {
if len(blocks) > 255 {
return nil, fmt.Errorf("数据块数量超过 255")
}
result := []byte{mainID, byte(len(blocks))}
for _, block := range blocks {
result = append(result, block.SubID)
result = append(result, block.Data...)
}
result = append(result, 0x00)
return result, nil
}
// ValveCommand 编码 0x40/0x0A 电磁阀控制0x01 关闭0xA0 开启。
func ValveCommand(controller, loop, component byte, open bool) ([]byte, error) {
action := byte(0x01)
if open {
action = 0xA0
}
return EncodePayload(MainSetting, DataBlock{SubID: SubValve, Data: []byte{controller, loop, component, action}})
}
// DecodeRealtime 解析协议 0x50 的传感器、事件、IO 和 AI 阀参数定长数据块。
func DecodeRealtime(payload []byte) (RealtimeData, error) {
var result RealtimeData
if len(payload) < 3 || payload[0] != MainRealtime {
return result, fmt.Errorf("不是实时数据包")
}
offset := 2
for blockIndex := 0; blockIndex < int(payload[1]); blockIndex++ {
if offset >= len(payload) {
return result, fmt.Errorf("实时数据块被截断")
}
subID := payload[offset]
offset++
switch subID {
case 0x01:
count, next, err := readCount(payload, offset, 12)
if err != nil {
return result, err
}
offset = next
for range count {
item := payload[offset : offset+12]
result.Sensors = append(result.Sensors, SensorReading{Number: binary.BigEndian.Uint32(item[0:4]), SensorType: item[4], Object: binary.BigEndian.Uint16(item[5:7]), Value: int16(binary.BigEndian.Uint16(item[7:9])), Unit: item[9], Decimal: item[10], Status: item[11]})
offset += 12
}
case 0x02:
count, next, err := readCount(payload, offset, 8)
if err != nil {
return result, err
}
offset = next
for range count {
item := payload[offset : offset+8]
result.Events = append(result.Events, EventReading{Type: binary.BigEndian.Uint16(item[0:2]), Number: binary.BigEndian.Uint32(item[2:6]), Value: int16(binary.BigEndian.Uint16(item[6:8]))})
offset += 8
}
case 0x03:
count, next, err := readCount(payload, offset, 7)
if err != nil {
return result, err
}
offset = next
for range count {
item := payload[offset : offset+7]
result.IO = append(result.IO, IOReading{Type: binary.BigEndian.Uint16(item[0:2]), Number: binary.BigEndian.Uint32(item[2:6]), Status: item[6]})
offset += 7
}
case 0x04:
if offset >= len(payload) {
return result, fmt.Errorf("参数块被截断")
}
count := int(payload[offset])
offset++
if offset+count*3 > len(payload) {
return result, fmt.Errorf("参数块长度无效")
}
for range count {
result.Parameters = append(result.Parameters, ParameterReading{Code: payload[offset], Value: int16(binary.BigEndian.Uint16(payload[offset+1 : offset+3]))})
offset += 3
}
default:
return result, fmt.Errorf("未知实时数据子标识 0x%02X", subID)
}
}
return result, nil
}
func readCount(payload []byte, offset, recordSize int) (int, int, error) {
if offset+2 > len(payload) {
return 0, offset, fmt.Errorf("数据块数量被截断")
}
count := int(binary.BigEndian.Uint16(payload[offset : offset+2]))
offset += 2
if offset+count*recordSize > len(payload) {
return 0, offset, fmt.Errorf("数据块记录长度无效")
}
return count, offset, nil
}