package m9z // 手动控制指令 import ( "encoding/binary" "fmt" "github.com/towgo/towgo/errors/terror" "strings" "tgk-touch/internal/global" ) type ManualData struct { Able bool `json:"disable"` //是否是手动控制状态 BackupByte1 byte `json:"backup_byte1"` //备用字节下标1 Loops []Loop `json:"loops"` //回路数据 BackupByte4 byte `json:"backup_byte4"` //备用字节下标4 } type Loop struct { Index int `json:"index"` // 0 D0 回路0 Name string `json:"name"` // 0 #1 回路 IsOpen bool `json:"is_open"` Pwm int `json:"pwm"` // 功率百分比 } func BuildManualData(enable, isOpen bool, pwm int) *ManualData { md := &ManualData{ Able: enable, } loops := make([]Loop, 10) for i := range loops { loops[i].Index = i loops[i].IsOpen = isOpen loops[i].Pwm = pwm } md.Loops = loops return md } func (md *ManualData) Write(deviceId string) error { return ManualWrite(deviceId, md) } func (md *ManualData) Display() string { // 改用strings.Builder提升拼接性能(避免不可变字符串频繁创建临时对象) var sb strings.Builder // 遍历循环列表 for _, l := range md.Loops { // 将bool类型的IsOpen转为1(true)或0(false) isOpenNum := 0 if l.IsOpen { isOpenNum = 1 } // 拼接格式:索引-开关状态(1/0)-调光值, fmt.Fprintf(&sb, "%d-%d-%d,", l.Index, isOpenNum, l.Pwm) } // 转换为字符串并去除末尾多余的逗号(若字符串非空) result := sb.String() return fmt.Sprintf("%v-%s", md.Able, strings.TrimSuffix(result, ",")) } func ManualRead(deviceId string) (*ManualData, error) { manualResult := new(ManualData) readResp, err := ReadCmd(deviceId, manual, "读取手动控制参数") if err != nil { return nil, err } manualResult.Able = readResp.Data[0] == 01 manualResult.BackupByte1 = readResp.Data[1] manualResult.BackupByte4 = readResp.Data[4] // 解析10路的继电器动作状态 relayBits := BytesToUint16LE(readResp.Data[2:4]) g.Log().Debugf("继电器状态(二进制): %016b ,% X", relayBits, readResp.Data[2:4]) // 补零显示16位 loops := make([]Loop, 10) for i := 0; i < 10; i++ { var loop Loop bit := (relayBits >> i) & 0x01 // 取第i位的值 loop.IsOpen = bit == 1 loop.Index = i loop.Name = fmt.Sprintf("#%+v", i+1) loops[i] = loop } pwms := readResp.Data[5:len(readResp.Data)] // 确定要处理的元素数量,取两者中的较小值 processCount := len(pwms) if len(loops) < processCount { processCount = len(loops) } // 只循环到有效的索引范围 for index := 0; index < processCount; index++ { loops[index].Pwm = hexToPercent(pwms[index]) } manualResult.Loops = loops return manualResult, err } func ManualWrite(deviceId string, manualData *ManualData) error { // 1. 准备数据缓冲区(15字节:1+1+2+1+10) data := make([]byte, 15) // 2. 填充手动控制状态(Byte0) if manualData.Able { data[0] = 0x01 } else { data[0] = 0x00 } // 3. 填充备用字节1(Byte1) data[1] = manualData.BackupByte1 // 4. 计算继电器控制位(Byte2-3) var relayBits uint16 for i, loop := range manualData.Loops { if loop.IsOpen { relayBits |= (1 << i) // 设置对应位为1 } } binary.LittleEndian.PutUint16(data[2:4], relayBits) // 小端序写入 // 5. 填充备用字节4(Byte4) data[4] = manualData.BackupByte4 // 6. 填充回路PWM百分比(Byte5-14) for i, loop := range manualData.Loops { if loop.Pwm < 0 { loop.Pwm = 0 } else if loop.Pwm > 100 { loop.Pwm = 100 } data[5+i] = byte(loop.Pwm) // 直接转换百分比 } //EE 01 02 00 0F 01 00 64 64 00 3F 37 3C 3D 37 40 1C 37 40 1C 23 FF //EE 01 02 00 0F 01 01 FF 03 00 32 32 32 32 32 32 32 32 32 32 3D FF //g.Log().Debugf("manualData: % X", data) // 8. 发送命令 writeResp, err := WriteCmd(deviceId, manual, data, "写入手动模式") if err != nil { return err } if !writeResp.Success { return terror.New("设置失败") } return nil } // Enable 使能 func (md *ManualData) Enable(deviceId string) error { md.Able = true return ManualWrite(deviceId, md) } // Disable 禁用 func (md *ManualData) Disable(deviceId string) error { md.Able = false return ManualWrite(deviceId, md) } // StartLoops 启动指定回路(如果index为空则启动所有) func (md *ManualData) StartLoops(deviceId string, indexes ...int) error { if !md.Able { err := md.Enable(deviceId) if err != nil { return err } } // 如果没有指定index,则操作所有回路 if len(indexes) == 0 { for i := range md.Loops { md.Loops[i].IsOpen = true } } else { // 否则只操作指定回路 for _, idx := range indexes { if idx >= 0 && idx < len(md.Loops) { md.Loops[idx].IsOpen = true } else { return fmt.Errorf("无效的回路索引: %d", idx) } } } return ManualWrite(deviceId, md) } // StopLoops 停止指定回路(如果index为空则停止所有) func (md *ManualData) StopLoops(deviceId string, indexes ...int) error { if !md.Able { err := md.Enable(deviceId) if err != nil { return err } } // 如果没有指定index,则操作所有回路 if len(indexes) == 0 { for i := range md.Loops { md.Loops[i].IsOpen = false } } else { // 否则只操作指定回路 for _, idx := range indexes { if idx >= 0 && idx < len(md.Loops) { md.Loops[idx].IsOpen = false } else { return fmt.Errorf("无效的回路索引: %d", idx) } } } return ManualWrite(deviceId, md) } // SetLoopPWM 设置指定回路的功率百分比 // 参数: // // pwm: 功率百分比 (0-100) // indexes: 回路索引(可变参数,不传则设置所有回路) func (md *ManualData) SetLoopPWM(deviceId string, pwm int, indexes ...int) error { if !md.Able { err := md.Enable(deviceId) if err != nil { return err } } // 校验PWM值范围 if pwm < 0 || pwm > 100 { return fmt.Errorf("功率百分比必须在0-100之间,当前值: %d", pwm) } // 如果没有指定index,则操作所有回路 if len(indexes) == 0 { for i := range md.Loops { md.Loops[i].Pwm = pwm } } else { // 操作指定回路 for _, idx := range indexes { if idx >= 0 && idx < len(md.Loops) { md.Loops[idx].Pwm = pwm } else { return fmt.Errorf("无效的回路索引: %d (可用范围: 0-%d)", idx, len(md.Loops)-1) } } } // 自动提交修改到设备 return ManualWrite(deviceId, md) } // GetLoopPWM 获取指定回路的功率百分比 // 参数:index 回路索引(0-based) // 返回值:(百分比, 错误信息) func (md *ManualData) GetLoopPWM(index int) (int, error) { if index < 0 || index >= len(md.Loops) { return 0, fmt.Errorf("回路索引超出范围(可用:0-%d)", len(md.Loops)-1) } return md.Loops[index].Pwm, nil }