You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

676 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package m9z // 插件
import (
"encoding/binary"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"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, gerror.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<<i) != 0) {
task.Loops[i].Pwm = pwmVal
}
}
}
}
}
// 如果没有找到时间指令,返回错误
if !foundTimeInstruction {
return nil, fmt.Errorf("missing time instruction in task program %+v", program.Instructions)
}
/* // 过滤掉保持默认关闭状态的回路
activeLoops := make([]TaskLoop, 0, len(task.Loops))
for _, loop := range task.Loops {
if loop.TurnOn || loop.Pwm > 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, gerror.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)
}