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.

259 lines
6.7 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"
"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转为1true或0false
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. 填充备用字节1Byte1
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. 填充备用字节4Byte4
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 gerror.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
}