蓝牙调试页面初步完成

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

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) };
}