package m9z // 插件 import ( "encoding/binary" "fmt" "github.com/towgo/towgo/errors/terror" "sort" "tgk-touch/internal/global" "time" ) var ( DefaultStartTask ControlTask = ControlTask{ TimeType: 0, Loops: []TaskLoop{ { 0, true, 70, }, { 1, true, 70, }, { 2, true, 70, }, { 3, true, 70, }, { 4, true, 70, }, { 5, true, 70, }, { 6, true, 70, }, { 7, true, 70, }, { 8, true, 70, }, { 9, true, 70, }, }, } DefaultMiddleTask MiddleControlTask = MiddleControlTask{ []ControlTask{ ControlTask{ TimeType: 0, Loops: []TaskLoop{ { 0, true, 70, }, { 1, true, 70, }, { 2, true, 70, }, { 3, true, 70, }, { 4, true, 70, }, { 5, true, 70, }, { 6, true, 70, }, { 7, true, 70, }, { 8, true, 70, }, { 9, true, 70, }, }, }, }, } DefaultStopTask ControlTask = ControlTask{ TimeType: 0, Loops: []TaskLoop{ { 0, false, 70, }, { 1, false, 70, }, { 2, false, 70, }, { 3, false, 70, }, { 4, false, 70, }, { 5, false, 70, }, { 6, false, 70, }, { 7, false, 70, }, { 8, false, 70, }, { 9, false, 70, }, }, } ) func init() { parse, _ := time.ParseInLocation("2006-01-02 15:04:05", "2025-06-05 19:00:00", time.Local) DefaultStartTask.ExecTime = parse for i, c := range DefaultMiddleTask.ControlTaskList { p, _ := time.ParseInLocation("2006-01-02 15:04:05", "2025-06-05 20:00:00", time.Local) c.ExecTime = p DefaultMiddleTask.ControlTaskList[i] = c } parse, _ = time.ParseInLocation("2006-01-02 15:04:05", "2025-06-05 07:00:00", time.Local) DefaultStartTask.ExecTime = parse } func IsTaskInitRead(deviceId string) (bool, error) { read, err := ManualRead(deviceId) if err != nil { return false, err } return read.BackupByte1 == 1, nil } func SetIsTaskInitRead(deviceId string, b bool) error { read, err := ManualRead(deviceId) if err != nil { return err } if b { read.BackupByte1 = 1 } else { read.BackupByte1 = 0 } return ManualWrite(deviceId, read) } type TaskLoop struct { Index uint `json:"index"` // 0 D0 回路0 TurnOn bool `json:"turnOn"` Pwm uint8 `json:"pwm"` // 功率百分比 } // 控制任务配置 type ControlTask struct { ExecTime time.Time `json:"execTime"` // "2006-01-02 15:04:05" 格式时间 TimeType int `json:"timeType"` // 0 定时 1经纬 Loops []TaskLoop `json:"loops"` } type MiddleControlTask struct { ControlTaskList []ControlTask `json:"controlTaskList"` } func ControlTaskToTaskProgram(task ControlTask, idx uint8) (*TaskProgram, error) { if uint8(0) > idx || idx > uint8(2) { idx = 0 } // 1. 创建任务程序对象 program := &TaskProgram{ Index: idx, } // 2. 解析执行时间 eTime := task.ExecTime g.Log().Debugf("task:%+v\ntask.ExecTime %+v , eTime:%+v,hour:%d,min:%d", task, task.ExecTime, eTime, eTime.Hour(), eTime.Minute()) minValue := uint32(eTime.Hour()*60 + eTime.Minute()) var cmpInstruction TaskInstruction // 3. 添加时间比较指令 if task.TimeType == 0 { cmpInstruction = TaskInstruction{ Type: 0x01 << 28, // CMP指令类型 Param: 0x01 << 24, Value: minValue, } } else { cmpInstruction = TaskInstruction{ Type: 0x01 << 28, Param: 0x02 << 24, Value: minValue, } } program.Instructions = append(program.Instructions, cmpInstruction) // 开灯任务需要两条指令: // 1. 开启所有回路继电器 var openLoopValue uint32 var closeLoopValue uint32 for _, loop := range task.Loops { if loop.TurnOn { openLoopValue |= (0x1 << loop.Index) } else { closeLoopValue |= (0x1 << loop.Index) } if loop.Pwm > 100 { loop.Pwm = 100 } loopIndex := (0x1 << loop.Index) loopIndex |= (0x1 << loop.Index) // 修复类型不匹配问题 pwmValue := (uint32(loopIndex) << 12) | uint32(loop.Pwm) pwmInstruction := TaskInstruction{ Type: 0x4 << 28, // OUTPUT指令 Param: 0x03 << 24, Value: pwmValue, } program.Instructions = append(program.Instructions, pwmInstruction) } if openLoopValue != 0 { openLoopValue = (openLoopValue << 12) | 0x01 } if closeLoopValue != 0 { closeLoopValue = (closeLoopValue << 12) | 0x00 } openLoopIns := TaskInstruction{ Type: 0x04 << 28, // OUTPUT指令 Param: 0x01 << 24, Value: openLoopValue, } closeLoopIns := TaskInstruction{ Type: 0x04 << 28, // OUTPUT指令 Param: 0x01 << 24, Value: closeLoopValue, } program.Instructions = append(program.Instructions, openLoopIns, closeLoopIns) // 2. 设置PWM亮度 // 5. 设置任务大小(现在有3条指令) program.Size = uint8(len(program.Instructions)) return program, nil } func TaskProgramToControlTask(program *TaskProgram) (*ControlTask, error) { task := &ControlTask{ TimeType: 0, // 默认定时类型 // 初始化所有10个回路(0-9) Loops: make([]TaskLoop, 10), } // 初始化所有回路为关闭状态 for i := range task.Loops { task.Loops[i] = TaskLoop{ Index: uint(i), TurnOn: false, Pwm: 0, } } foundTimeInstruction := false // 解析所有指令 for _, ins := range program.Instructions { g.Log().Debugf("Found instruction: %+v,type %+v isTime %+v", ins, ins.Type, ins.Type == 0x01) switch ins.Type { case 0x01: // 时间命令 foundTimeInstruction = true minutes := ins.Value hour := minutes / 60 minute := minutes % 60 parse, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s %02d:%02d:00", time.Now().Format(time.DateOnly), hour, minute), time.Local) if err != nil { return nil, terror.Wrapf(err, "failed to parse time") } task.ExecTime = parse // 处理时间类型 switch ins.Param { case 0x01: task.TimeType = 0 // 定时 case 0x02: task.TimeType = 1 // 经纬 } case 0x04: // OUTPUT指令 paramType := ins.Param switch paramType { case 0x01: // 继电器状态命令 relayValue := ins.Value // 判断是开还是关 isOpen := (relayValue & 1) == 1 // 获取开启掩码(高12位) onMask := (relayValue >> 12) & 0xFFF // 颠倒位顺序 - 低位对应低索引回路 reversedOnMask := uint32(0) for i := 0; i < 12; i++ { originalBit := (onMask >> i) & 0x1 reversedOnMask |= (originalBit << (11 - i)) } str := fmt.Sprintf("%12b", reversedOnMask) for i := 0; i < len(str)-2; i++ { if str[i] == '1' { if isOpen { task.Loops[i].TurnOn = true g.Log().Debugf("isOpen %+v ,loop %+v ,on %q ,t :%+v", isOpen, i, str[i], true) } else { task.Loops[i].TurnOn = false g.Log().Debugf("isOpen %+v ,loop %+v ,on %q ,t :%+v", isOpen, i, str[i], false) } } } case 0x03: // PWM指令 pwmValue := ins.Value // 高12位:通道掩码,低12位:PWM值 channelMask := (pwmValue >> 12) & 0xFFF pwmVal := uint8(pwmValue & 0xFF) // 取低8位作为PWM值 // 处理每个回路的PWM值 for i := 0; i < 12; i++ { if i < len(task.Loops) && (channelMask&(1< 0 { activeLoops = append(activeLoops, loop) } } task.Loops = activeLoops*/ return task, nil } func MiddleControlTaskToTaskProgram(middle MiddleControlTask, idx uint8) (*TaskProgram, error) { sort.Slice(middle.ControlTaskList, func(i, j int) bool { t1 := middle.ControlTaskList[i].ExecTime t2 := middle.ControlTaskList[i].ExecTime return t1.Before(t2) }) if idx < 0 || idx > 2 { idx = 0 } program := &TaskProgram{ Index: idx, } fmt.Printf("middleControlTaskToTaskProgram middle:%+v\n", middle) for _, task := range middle.ControlTaskList { // 2. 解析执行时间 eTime := task.ExecTime fmt.Printf("task:%+v\ntask.ExecTime %+v , eTime:%+v,hour:%d,min:%d\n", task, task.ExecTime, eTime, eTime.Hour(), eTime.Minute()) minValue := uint32(eTime.Hour()*60 + eTime.Minute()) var cmpInstruction TaskInstruction // 3. 添加时间比较指令 if task.TimeType == 0 { cmpInstruction = TaskInstruction{ Type: 0x01 << 28, // CMP指令类型 Param: 0x5 << 24, Value: minValue, } } else { cmpInstruction = TaskInstruction{ Type: 0x01 << 28, Param: 0x04 << 24, Value: minValue, } } program.Instructions = append(program.Instructions, cmpInstruction) // 开灯任务需要两条指令: // 1. 开启所有回路继电器 var openLoopValue uint32 var closeLoopValue uint32 // 2. PWM指令(针对每个回路) pwmMap := make(map[uint8][]uint) // 按PWM值分组 for _, loop := range task.Loops { if loop.Pwm > 0 { pwmMap[loop.Pwm] = append(pwmMap[loop.Pwm], loop.Index) } } // 为每个PWM值创建指令 for pwmVal, indices := range pwmMap { channelMask := uint32(0) for _, idx2 := range indices { if idx2 < 12 { channelMask |= (1 << idx2) } } pwmInstruction := TaskInstruction{ Type: 0x4 << 28, // OUTPUT指令 Param: 0x03 << 24, // PWM类型 Value: (channelMask << 12) | uint32(pwmVal), } program.Instructions = append(program.Instructions, pwmInstruction) } for _, loop := range task.Loops { if loop.TurnOn { openLoopValue |= (0x1 << loop.Index) } else { closeLoopValue |= (0x1 << loop.Index) } } if openLoopValue != 0 { openLoopValue = (openLoopValue << 12) | 0x01 } if closeLoopValue != 0 { closeLoopValue = (closeLoopValue << 12) | 0x00 } openLoopIns := TaskInstruction{ Type: 0x04 << 28, // OUTPUT指令 Param: 0x01 << 24, Value: openLoopValue, } if openLoopValue != 0 { program.Instructions = append(program.Instructions, openLoopIns) } closeLoopIns := TaskInstruction{ Type: 0x04 << 28, // OUTPUT指令 Param: 0x01 << 24, Value: closeLoopValue, } if closeLoopValue != 0 { program.Instructions = append(program.Instructions, closeLoopIns) } } // 中间任务需要循环 loopIns := TaskInstruction{ Type: 0x2 << 28, Param: ((0x1 /*跳转使能*/) & 0xF) << 24, Value: (((0 /*跳转到第几步*/) & 0xFF) << 16) | (((0 /*最大次数*/) & 0xFF) << 8), } program.Instructions = append(program.Instructions, loopIns) program.Size = uint8(len(program.Instructions)) return program, nil } func TaskProgramToMiddleControlTask(program *TaskProgram) (*MiddleControlTask, error) { middle := &MiddleControlTask{ ControlTaskList: make([]ControlTask, 0), } // 存储当前正在解析的任务 var currentTask *ControlTask taskIndex := 0 for i, ins := range program.Instructions { insType := ins.Type paramType := ins.Param g.Log().Debugf("解析指令 %d: Type=0x%X, Param=0x%X, Value=0x%08X", i, insType, paramType, ins.Value) switch insType { case 0x01: // 时间比较指令 // 完成前一个任务(如果有) if currentTask != nil { middle.ControlTaskList = append(middle.ControlTaskList, *currentTask) taskIndex++ } // 创建新任务 currentTask = &ControlTask{ Loops: make([]TaskLoop, 10), // 10个回路的默认配置 } // 初始化所有回路状态 for j := range currentTask.Loops { currentTask.Loops[j] = TaskLoop{ Index: uint(j), TurnOn: false, Pwm: 0, } } // 解析时间 minutes := ins.Value hour := minutes / 60 minute := minutes % 60 parse, err := time.ParseInLocation("2006-01-02 15:04", fmt.Sprintf("%s %02d:%02d", time.Now().Format(time.DateOnly), hour, minute), time.Local) if err != nil { return nil, terror.Wrap(err, "parse time error") } currentTask.ExecTime = parse // 解析时间类型 switch paramType { case 0x5: currentTask.TimeType = 0 // 定时 case 0x4: currentTask.TimeType = 1 // 经纬 default: g.Log().Warnf("未知的时间类型: 0x%X", paramType) currentTask.TimeType = 0 } g.Log().Debugf("时间指令: %s, 时间类型: %d ,分钟数:%d", currentTask.ExecTime, currentTask.TimeType, minutes) case 0x04: // OUTPUT指令 if currentTask == nil { g.Log().Warnf("在时间指令前发现OUTPUT指令,忽略") continue } switch paramType { case 0x01: // 继电器状态命令 value := ins.Value // 判断是开还是关 isOpen := (value & 1) == 1 onMask := (value >> 12) & 0xFFF // 输出原始掩码值(二进制12位格式) g.Log().Debugf("开关命令%+v", isOpen) g.Log().Debugf("原始继电器掩码: %012b (onMask = 0x%03X)", onMask, onMask) // 颠倒位顺序 - 低位对应低索引回路 reversedOnMask := uint32(0) for j := 0; j < 12; j++ { originalBit := (onMask >> j) & 0x1 reversedOnMask |= (originalBit << (11 - j)) } // 输出反转后的掩码值 str := fmt.Sprintf("%012b", reversedOnMask) g.Log().Debugf("反转后继电器掩码: %s (reversedOnMask = 0x%03X)", str, reversedOnMask) // 逐个输出每个回路的位状态 g.Log().Debug("按位解析回路状态:") for j := 0; j < len(str)-2; j++ { if j >= len(currentTask.Loops) { break } if str[j] == '1' { if isOpen { currentTask.Loops[j].TurnOn = str[j] == '1' && isOpen g.Log().Debugf("loop %+v ,on %q ,t :%+v", j, str[j], true) } else { currentTask.Loops[j].TurnOn = false g.Log().Debugf("loop %+v ,on %q ,t :%+v", j, str[j], false) } } } case 0x03: // PWM指令 value := ins.Value channelMask := (value >> 12) & 0xFFF pwmVal := uint8(value & 0xFF) // 输出原始掩码值 g.Log().Debugf("原始PWM掩码: %012b (channelMask = 0x%03X), PWM值: %d", channelMask, channelMask, pwmVal) // 颠倒位顺序 - 低位对应低索引回路 reversedChannelMask := uint32(0) for j := 0; j < 12; j++ { originalBit := (channelMask >> j) & 0x1 reversedChannelMask |= (originalBit << (11 - j)) } // 输出反转后的掩码值 str := fmt.Sprintf("%012b", reversedChannelMask) g.Log().Debugf("反转后PWM掩码: %s (reversedChannelMask = 0x%03X)", str, reversedChannelMask) // 设置每个回路的PWM值 for j := 0; j < len(str); j++ { if j >= len(currentTask.Loops) { break } if str[j] == '1' { g.Log().Debugf(" 设置回路 %d PWM值: %d%%", j, pwmVal) currentTask.Loops[j].Pwm = pwmVal } } } case 0x02: // 循环指令 g.Log().Debug("检测到循环指令,跳过") default: g.Log().Warnf("未知的指令类型: 0x%X", insType) } } // 添加最后一个任务(如果有) if currentTask != nil { middle.ControlTaskList = append(middle.ControlTaskList, *currentTask) } g.Log().Debugf("成功解析 %d 个控制任务", len(middle.ControlTaskList)) return middle, nil } // 写入开始任务到设备 func StartTaskWriteByTaskCfg(deviceId string, idx uint8, task ControlTask) error { // 1. 将控制任务转换为任务程序 tp, err := ControlTaskToTaskProgram(task, idx) if err != nil { return err } // 2. 使用CMD 0x13写入开始任务 return StartTaskWrite(deviceId, tp) } // 写入任务程序到设备 func StartTaskWrite2(deviceId string, program *TaskProgram) error { // 1. 构造数据缓冲区 (1字节大小 + 每个指令4字节) buf := make([]byte, 1+4*len(program.Instructions)) buf[0] = program.Size offset := 1 for _, ins := range program.Instructions { // 2. 组合指令 (类型在高4位) instruction := uint32(ins.Type) << 28 /*// 3. 根据指令类型组合参数 switch len(ins.Params) { case 1: instruction |= ins.Param & 0x0FFFFFFF case 2: instruction |= (ins.Param & 0x0F) << 24 instruction |= ins.Value & 0x00FFFFFF case 3: instruction |= (ins.Param & 0x0F) << 24 instruction |= (ins.Value & 0xFFFF) << 8 instruction |= ins.Params[2] & 0xFF }*/ // 4. 写入小端序 binary.LittleEndian.PutUint32(buf[offset:offset+4], instruction) offset += 4 } // 5. 发送写入命令 return StartTaskWrite(deviceId, program) }