蓝牙调试页面初步完成

This commit is contained in:
2026-01-16 12:00:49 +08:00
parent d9f8358191
commit bbc4f205a3
60 changed files with 2201 additions and 51331 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,8 @@ export const COMMANDS = {
SET_CONDITION_2: 0x09,
OTA_START: 0x0B,
ENABLE_BLE_LOG: 0x0C,
// 设置门磁/卫浴事件触发/释放参数
SET_DOOR_BATH_EVENT: 0x16,
RADAR_STATUS: 0x11,
TEST_KEYS: 0x13,
};
@@ -24,6 +26,22 @@ function ensureUint8Array(data) {
if (data instanceof Uint8Array) return data;
if (Array.isArray(data)) return Uint8Array.from(data.map(x => x & 0xFF));
if (typeof data === 'string') {
// 如果是十六进制字符串包含空格或连续十六进制则按hex解析
const hexOnly = data.trim();
if (/^[0-9a-fA-F\s]+$/.test(hexOnly)) {
const parts = hexOnly.split(/\s+/).filter(Boolean);
let bytes = [];
if (parts.length > 1) {
bytes = parts.map(p => parseInt(p, 16) & 0xFF);
} else {
const s = parts[0];
for (let i = 0; i < s.length; i += 2) {
const hex = s.substr(i, 2);
if (hex.length === 2) bytes.push(parseInt(hex, 16) & 0xFF);
}
}
return Uint8Array.from(bytes);
}
// 默认按ASCII编码如需GBK请先外部转换后传入字节数组
const arr = new Uint8Array(data.length);
for (let i = 0; i < data.length; i++) arr[i] = data.charCodeAt(i) & 0xFF;
@@ -85,7 +103,8 @@ export function buildPacket(opts) {
const framNum = (opts && opts.framNum) != null ? (opts.framNum >>> 0) : 1;
const type = (opts && opts.type) != null ? (opts.type & 0xFF) : 0x00;
const params = ensureUint8Array(opts && opts.params);
const crcType = (opts && opts.crcType) === 'MODBUS' ? 'MODBUS' : 'CCITT';
// 默认CRC方式改为 MODBUS可显式传 'CCITT' 使用CCITT-FALSE
const crcType = (opts && opts.crcType) === 'CCITT' ? 'CCITT' : 'MODBUS';
// 预分配:固定头(2) + 长度(2) + CRC(2) + Frame(2) + FramNum(2) + Type(1) + 参数
const fixedLen = 2 + 2 + 2 + 2 + 2 + 1;
@@ -149,7 +168,7 @@ export function buildCommand(cmd, payload, options = {}) {
framNum: options.framNum != null ? options.framNum : 1,
type: cmd & 0xFF,
params: payload,
crcType: options.crcType === 'MODBUS' ? 'MODBUS' : 'CCITT',
crcType: options.crcType === 'CCITT' ? 'CCITT' : 'MODBUS',
head: options.head || DEFAULT_HEAD,
});
}
@@ -161,3 +180,73 @@ export function buildCommand(cmd, payload, options = {}) {
export function buildReadVersion() {
return buildCommand(COMMANDS.READ_VERSION, [0x00]);
}
/**
* 构造:设置门磁开廊灯事件与卫浴雷达开卫浴灯事件 (Frame_Type=0x16)
* 参数布局P0..P8
* P0: 控制位 bit0=门磁事件启用, bit1=卫浴事件启用
* 门磁事件(当 bit0=1 启用)
* P1: 触发延迟数值
* P2: 时间单位(1=秒,2=分,3=时)
* P3: 释放延迟数值
* P4: 时间单位(1=秒,2=分,3=时)
* 卫浴事件(当 bit1=1 启用)
* P5: 触发延迟数值
* P6: 时间单位(1=秒,2=分,3=时)
* P7: 释放延迟数值
* P8: 时间单位(1=秒,2=分,3=时)
* 注意:当仅设置某一侧事件时,另一侧对应的参数必须填 0。
*
* @param {{
* door?: {triggerDelay?:number, triggerUnit?:number, releaseDelay?:number, releaseUnit?:number},
* bath?: {triggerDelay?:number, triggerUnit?:number, releaseDelay?:number, releaseUnit?:number}
* }} opts
* @param {{frame?:number, framNum?:number, crcType?:('CCITT'|'MODBUS'), head?:number[]}} [options]
*/
export function buildSetDoorBathEvent(opts = {}, options = {}) {
const door = opts.door || null;
const bath = opts.bath || null;
const doorEnabled = !!door;
const bathEnabled = !!bath;
const P0 = (doorEnabled ? 0x01 : 0x00) | (bathEnabled ? 0x02 : 0x00);
const p1 = doorEnabled ? (door.triggerDelay || 0) & 0xFF : 0x00;
const p2 = doorEnabled ? (door.triggerUnit || 0) & 0xFF : 0x00;
const p3 = doorEnabled ? (door.releaseDelay || 0) & 0xFF : 0x00;
const p4 = doorEnabled ? (door.releaseUnit || 0) & 0xFF : 0x00;
const p5 = bathEnabled ? (bath.triggerDelay || 0) & 0xFF : 0x00;
const p6 = bathEnabled ? (bath.triggerUnit || 0) & 0xFF : 0x00;
const p7 = bathEnabled ? (bath.releaseDelay || 0) & 0xFF : 0x00;
const p8 = bathEnabled ? (bath.releaseUnit || 0) & 0xFF : 0x00;
const payload = [P0, p1, p2, p3, p4, p5, p6, p7, p8];
return buildCommand(COMMANDS.SET_DOOR_BATH_EVENT, payload, options);
}
/**
* 验证十六进制字符串包并计算/写入 CRC默认 MODBUS返回完整包与CRC值
* @param {string} hexStr 如 'CC C0 0C 00 00 00 01 00 02 00 0C 08' 或连续hex
* @param {'MODBUS'|'CCITT'} [crcType='MODBUS']
* @returns {{crc:number, low:number, high:number, packetHex:string, packet:Array<number>}}
*/
export function verifyHexPacket(hexStr, crcType = 'MODBUS') {
if (typeof hexStr !== 'string') throw new TypeError('hexStr must be a string');
const params = ensureUint8Array(hexStr);
if (params.length < 6) throw new RangeError('Packet too short');
// 将输入视为完整包先把CRC位置清0再计算
const buf = new Uint8Array(params.length);
buf.set(params);
const crcPos = 4;
buf[crcPos] = 0x00;
buf[crcPos + 1] = 0x00;
const crc = computeCrc(buf, crcType, [0, buf.length]);
const crcLE = toUint16LE(crc);
buf[crcPos] = crcLE[0];
buf[crcPos + 1] = crcLE[1];
const packetHex = Array.from(buf).map(b => b.toString(16).toUpperCase().padStart(2, '0')).join(' ');
return { crc: crc & 0xFFFF, low: crcLE[0], high: crcLE[1], packetHex, packet: Array.from(buf) };
}