package m9z import ( "fmt" "github.com/towgo/towgo/errors/terror" "tgk-touch/internal/global" "tgk-touch/internal/module/maincontrollerClient" ) const ( FrameHeader = 0xEE FrameFooter = 0xFF CmdRead = 0x01 CmdWrite = 0x02 ResponseFlag = 0x80 // D7位掩码 MaxPacketSize = 256 ) // 读取命令结构 type ReadCommand struct { Instruction byte Index byte } // 写入命令结构 type WriteCommand struct { Instruction byte Index byte Data []byte IsUpdate bool } // 读取响应结构 type ReadResponse struct { Instruction byte Index byte Data []byte } // 写入响应结构 type WriteResponse struct { Instruction byte Index byte Success bool } // 校验和计算 func calCheckSum(data []byte) byte { sum := byte(0x33) for _, b := range data { sum += b } return sum } // 构建读取命令帧 func BuildReadCommand(cmd ReadCommand) []byte { frame := []byte{FrameHeader} payload := []byte{cmd.Instruction, CmdRead, cmd.Index} frame = append(frame, payload...) frame = append(frame, calCheckSum(payload)) frame = append(frame, FrameFooter) return frame } // 构建写入命令帧 func BuildWriteCommand(cmd WriteCommand) []byte { frame := []byte{FrameHeader} payload := []byte{cmd.Instruction, CmdWrite, cmd.Index} if len(cmd.Data) > 0 && !cmd.IsUpdate { payload = append(payload, byte(len(cmd.Data))) } payload = append(payload, cmd.Data...) frame = append(frame, payload...) frame = append(frame, calCheckSum(payload)) frame = append(frame, FrameFooter) return frame } // 解析接收帧 func ParseFrame(data []byte) (interface{}, error) { g.Log().Debugf("ParseFrame:[% X]", data) s := "" for i, d := range data { if i == 0 { } s += fmt.Sprintf("0x%X,", d) } //g.Log().Debugf("\n ParseFrame:{%s} ", s) // 基本长度检查 if len(data) < 5 { return nil, NewReadError("frame too short") } // 验证帧头帧尾 if data[0] != FrameHeader || data[len(data)-1] != FrameFooter { return nil, NewReadError("invalid frame header or footer") } // 提取有效载荷和校验和 payload := data[1 : len(data)-2] receivedCS := data[len(data)-2] // 计算校验和 if calCheckSum(payload) != receivedCS { return nil, NewReadError("checksum mismatch") } // 解析指令类型 if len(payload) < 2 { return nil, NewReadError("invalid payload length") } instruction := payload[0] cmdType := payload[1] // 判断是否是响应帧(D7位是否置1) isResponse := (instruction & ResponseFlag) != 0 switch { case isResponse && cmdType == CmdRead: // 特殊处理实际通断情况查询指令 (0x19) 的响应 // 该指令返回的数据结构较为特殊,可能包含多个字段,需要专门的解析逻辑 // 或者复用 parseReadResponse,但在解析 Data 时进行特定处理 return parseReadResponse(payload) case isResponse && cmdType == CmdWrite: return parseWriteResponse(payload) default: return nil, terror.New("unsupported command type") } } // 解析读取响应 func parseReadResponse(payload []byte) (*ReadResponse, error) { if len(payload) < 4 { return nil, terror.New("invalid read response format") } index := payload[2] dataLength := payload[3] if len(payload) < 4+int(dataLength) { return nil, terror.New("data length mismatch") } return &ReadResponse{ Instruction: payload[0], Index: index, Data: payload[4 : 4+dataLength], }, nil } func parseReadBlockResponse(payload []byte) (*ReadResponse, error) { if len(payload) < 4 { return nil, terror.New("invalid read response format") } index := payload[2] dataLength := payload[3:5] le := BytesToUint16LE(dataLength) if len(payload) < 4+int(le) { return nil, terror.New("data length mismatch") } return &ReadResponse{ Instruction: payload[0], Index: index, Data: payload[5 : 5+le], }, nil } // 解析写入响应 func parseWriteResponse(payload []byte) (*WriteResponse, error) { if len(payload) < 5 { return nil, terror.New("invalid write response format") } index := payload[2] dataLength := payload[3] if dataLength != 1 { return nil, terror.New("invalid data length in write response") } success := payload[4] == 0x01 return &WriteResponse{ Instruction: payload[0], Index: index, Success: success, }, nil } // 辅助函数:将数据转换为小端格式的uint16 func BytesToUint16LE(b []byte) uint16 { if len(b) < 2 { return 0 } return uint16(b[0]) | uint16(b[1])<<8 } // 辅助函数:将uint16转换为小端格式的字节切片 func Uint16ToBytesLE(n uint16) []byte { return []byte{byte(n), byte(n >> 8)} } /* func pushCommand(cmd []byte) ([]byte, error) { push.Lock() defer push.Unlock() fmt.Printf("Push Command :% X\n", cmd) conn, err := net.Dial("tcp", "10.10.100.254:8899") if err != nil { g.Log().Infof("%+v\n", err) return nil, err } defer conn.Close() _, err = conn.Write(cmd) if err != nil { g.Log().Infof("%+v\n", err) return nil, err } buf := make([]byte, 128) n, err := conn.Read(buf) if err != nil { g.Log().Infof("%+v\n", err) return nil, err } return buf[:n], nil } */ func pushCommand(deviceId string, cmd []byte, detail string) ([]byte, error) { var result []byte g.Log().Debugf("push cmd:[% X] ", cmd) err := maincontrollerClient.DeviceCallByDeviceID( deviceId, "/lampControl", "", cmd, &result, func(deviceID, method string, params, result interface{}) (deviceType, cmdDetail, cmdDisplay, resultDisplay string) { deviceType = "tgk" cmdDetail = detail return }) if err != nil { return nil, terror.Wrapf(err, "单灯控制命令 deviceID={%v} cmd={% X} 下发失败", deviceId, cmd) } return result, nil } func ReadCmd(deviceId string, cmd M9zCtrl, detail string, idx ...uint) (*ReadResponse, error) { var idxEnd byte idxEnd = 0x00 if len(idx) > 0 { idxEnd = byte(idx[0]) } readCmd := ReadCommand{Instruction: byte(cmd), Index: idxEnd} readFrame := BuildReadCommand(readCmd) cmdRespData, err := pushCommand(deviceId, readFrame, detail) if err != nil { return nil, err } frame, err := ParseFrame(cmdRespData) if err != nil { return nil, err } return frame.(*ReadResponse), err } // 定义专属的标记常量(根据业务场景选择合适的值,这里用0xff对应原逻辑的-1) 标记是升级 const UpdateMarker byte = 0xff // 特殊标记值,替代原逻辑中的-1 func WriteCmd(deviceId string, cmd M9zCtrl, data []byte, detail string, idx ...uint8) (*WriteResponse, error) { isUpdate := false // 4. 构建写入命令 wIdx := byte(0x00) if len(idx) > 0 { wIdx = idx[0] // 准确判断是否是特殊标记值(类型完全匹配,无隐式转换) if wIdx == UpdateMarker { isUpdate = true // 这里添加标记值的处理逻辑(根据你的业务需求定制) // 示例:重置为默认值、执行特定业务逻辑等 wIdx = 0x00 // 示例操作:将标记值重置为默认值 } } writeCmd := WriteCommand{ Instruction: byte(cmd), Index: wIdx, Data: data, IsUpdate: isUpdate, } writeFrame := BuildWriteCommand(writeCmd) cmdRespData, err := pushCommand(deviceId, writeFrame, detail) if err != nil { return nil, err } resp, err := ParseFrame(cmdRespData) if err != nil { return nil, err } return resp.(*WriteResponse), nil } func percentToHex(percent int) byte { if percent < 0 { return 0x00 // 低于0%视为0% } if percent > 100 { return 0x64 // 超过100%视为100% } return byte(percent) // 直接转换 } func hexToPercent(hexValue byte) int { if hexValue > 0x64 { err := fmt.Errorf("非法十六进制值: 0x%02X(超过0x64)", hexValue) g.Log().Infof("%+v", err) return 0 } return int(hexValue) }