|
|
package m9z
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
"fmt"
|
|
|
"github.com/towgo/towgo/errors/terror"
|
|
|
)
|
|
|
|
|
|
// 对应C的db_ble_extern_t结构体(简化版,使用bool型Enable)
|
|
|
type BluetoothConfig struct {
|
|
|
Enable bool // 离线监控使能(false=禁止,true=允许/0xAA)
|
|
|
OfflineMin uint8 // 离线时间(分钟数)
|
|
|
BleName string // 蓝牙名称(最大20字节) 不支持中文
|
|
|
}
|
|
|
|
|
|
func BluetoothRead(deviceId string) (*BluetoothConfig, error) {
|
|
|
// 1. 读取指令
|
|
|
cmd, err := ReadCmd(deviceId, bluetooth, "设备蓝牙配置读取")
|
|
|
if err != nil {
|
|
|
errMsg := fmt.Sprintf("读取设备%s蓝牙配置失败: %v", deviceId, err)
|
|
|
|
|
|
return nil, terror.New(errMsg)
|
|
|
}
|
|
|
|
|
|
data := cmd.Data
|
|
|
|
|
|
// 2. 校验基础长度(至少2字节:离线监控+离线时间)
|
|
|
const minDataLen = 2
|
|
|
if len(data) < minDataLen {
|
|
|
errMsg := fmt.Sprintf("蓝牙配置数据长度不足,至少需要%d字节,实际收到%d字节", minDataLen, len(data))
|
|
|
|
|
|
return nil, terror.New(errMsg)
|
|
|
}
|
|
|
|
|
|
// 3. 解析基础字段(固定2字节)
|
|
|
var bleConfig BluetoothConfig
|
|
|
bleConfig.Enable = data[0] == 0xAA // 0xAA对应true(允许),其他为false(禁止)
|
|
|
bleConfig.OfflineMin = data[1] // 离线时间
|
|
|
|
|
|
// 4. 解析蓝牙名称(动态长度,最多20字节)
|
|
|
nameBytes := data[2:] // 取基础字段后的所有字节
|
|
|
if len(nameBytes) > 20 {
|
|
|
nameBytes = nameBytes[:20] // 超过20字节则截断
|
|
|
}
|
|
|
// 去掉名称末尾的空字符(设备返回的结束符)
|
|
|
bleConfig.BleName = string(bytes.TrimRight(nameBytes, "\x00"))
|
|
|
return &bleConfig, nil
|
|
|
}
|
|
|
|
|
|
// BluetoothWrite 写入蓝牙配置到设备(修复所有核心问题)
|
|
|
func BluetoothWrite(deviceId string, bleConfig *BluetoothConfig) error {
|
|
|
// 1. 精准参数校验(拆分判断,提示语匹配)
|
|
|
if bleConfig == nil {
|
|
|
errMsg := "蓝牙配置结构体为nil,无法写入"
|
|
|
|
|
|
return terror.New(errMsg)
|
|
|
}
|
|
|
nameBytes := []byte(bleConfig.BleName)
|
|
|
if len(nameBytes) > 20 {
|
|
|
errMsg := fmt.Sprintf("蓝牙名称超长,最大支持20字节,实际传入%d字节", len(nameBytes))
|
|
|
|
|
|
return terror.New(errMsg)
|
|
|
}
|
|
|
// 校验1:是否包含中文/非ASCII字符
|
|
|
if hasChinese(bleConfig.BleName) {
|
|
|
errMsg := fmt.Sprintf("设备[%s]蓝牙名称包含中文/非ASCII字符,仅支持英文/数字/符号等ASCII字符", deviceId)
|
|
|
|
|
|
return terror.New(errMsg)
|
|
|
}
|
|
|
|
|
|
// 2. 构造缓冲区:2字节基础字段 + 动态长度蓝牙名称
|
|
|
buf := make([]byte, 2) // 先初始化基础字段缓冲区
|
|
|
// 编码离线监控使能(Byte0:0=禁止,0xAA=允许)
|
|
|
if bleConfig.Enable {
|
|
|
buf[0] = 0xAA
|
|
|
} else {
|
|
|
buf[0] = 0x00
|
|
|
}
|
|
|
// 编码离线时间(Byte1)
|
|
|
buf[1] = bleConfig.OfflineMin
|
|
|
|
|
|
// 追加蓝牙名称(仅追加一次,修复重复追加问题)
|
|
|
buf = append(buf, nameBytes...)
|
|
|
|
|
|
// 调试日志:打印最终缓冲区(长度+十六进制)
|
|
|
//g.Log().Debugf("Bluetooth Write raw data: len=%d , hex=% X", len(buf), buf)
|
|
|
|
|
|
// 3. 发送写入命令
|
|
|
writeResp, err := WriteCmd(deviceId, bluetooth, buf, "设备蓝牙配置写入")
|
|
|
if err != nil {
|
|
|
errMsg := fmt.Sprintf("设备[%s]蓝牙配置写入命令调用失败", deviceId)
|
|
|
return terror.Wrapf(err, errMsg)
|
|
|
}
|
|
|
|
|
|
// 4. 检查写入响应状态(修复err为nil的问题)
|
|
|
if !writeResp.Success {
|
|
|
errMsg := fmt.Sprintf("设备[%s]蓝牙配置写入失败,响应状态为失败", deviceId)
|
|
|
|
|
|
// 手动构造错误,避免Wrapf传入nil
|
|
|
return terror.New(errMsg)
|
|
|
}
|
|
|
|
|
|
// 5. 写入成功日志(修复切片越界问题:用buf[2:]而非固定22)
|
|
|
//bleNameStr := string(bytes.TrimRight(buf[2:], "\x00"))
|
|
|
//g.Log().Infof("设备%s蓝牙配置写入成功,离线监控使能:%v,离线时间:%d分钟,蓝牙名称:%s",
|
|
|
// deviceId, bleConfig.Enable, bleConfig.OfflineMin, bleNameStr)
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// hasChinese 辅助函数:判断字符串是否包含中文(或非ASCII字符)
|
|
|
func hasChinese(s string) bool {
|
|
|
for _, r := range s {
|
|
|
// 中文Unicode范围:0x4E00-0x9FFF,同时拦截所有>127的非ASCII字符
|
|
|
if r > 127 || (r >= 0x4E00 && r <= 0x9FFF) {
|
|
|
return true
|
|
|
}
|
|
|
}
|
|
|
return false
|
|
|
}
|