diff --git a/api/100J/HBY100-J.js b/api/100J/HBY100-J.js index f470974..76f4448 100644 --- a/api/100J/HBY100-J.js +++ b/api/100J/HBY100-J.js @@ -163,7 +163,12 @@ class HBY100JProtocol { this.onNotifyCallback = callback; } - parseBleData(buffer) { + /** + * @param {Uint8Array|ArrayBuffer} buffer + * @param {{ skipSideEffects?: boolean }} [options] skipSideEffects=true:仅解析字段,不打日志、不触发 onNotify/文件回调(供 BleReceive 与设备页 bleValueNotify 双订阅时避免重复) + */ + parseBleData(buffer, options = {}) { + const skipSideEffects = !!options.skipSideEffects; const view = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer); if (view.length < 3) return null; @@ -175,8 +180,10 @@ class HBY100JProtocol { const macBytes = view.slice(1, 7); const macAddress = Array.from(macBytes).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(':'); const result = { type: 'mac', macAddress }; - console.log('[100J-蓝牙] 设备上报MAC:', macAddress, '原始:', Array.from(view).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ')); - if (this.onNotifyCallback) this.onNotifyCallback(result); + if (!skipSideEffects) { + console.log('[100J-蓝牙] 设备上报MAC:', macAddress, '原始:', Array.from(view).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ')); + if (this.onNotifyCallback) this.onNotifyCallback(result); + } return result; } @@ -206,15 +213,19 @@ class HBY100JProtocol { // 05: 文件更新响应 FB 05 [fileType] [status] FF,status: 1=成功 2=失败 if (data.length >= 1) result.fileType = data[0]; if (data.length >= 2) result.fileStatus = data[1]; // 1=Success, 2=Failure - if (this._fileResponseResolve) this._fileResponseResolve(result); + if (!skipSideEffects && this._fileResponseResolve) this._fileResponseResolve(result); break; case 0x04: - // 5.5 获取设备电源状态: 电池容量8B + 电压8B + 百分比1B + 车载电源1B + 续航时间2B(分钟) + // 5.5 获取设备电源状态: 容量8B + 电压8B + 百分比1B + 车载1B + 续航2B(分钟) + [充电状态1B] + FF + // 充电状态为固件新增,在续航之后;旧固件仅 20 字节 payload,不影响 [16..19] 字段 if (data.length >= 20) { result.batteryPercentage = data[16]; result.vehiclePower = data[17]; result.batteryRemainingTime = data[18] | (data[19] << 8); // 小端序,单位分钟 } + if (data.length >= 21) { + result.chargingStatus = data[20]; // 0未充电 1充电中 2已充满 + } break; case 0x06: // 06: 语音播报响应 @@ -261,10 +272,11 @@ class HBY100JProtocol { const funcNames = { 0x01: '复位', 0x02: '基础信息', 0x03: '位置', 0x04: '电源状态', 0x05: '文件更新', 0x06: '语音播报', 0x09: '音量', 0x0A: '爆闪模式', 0x0B: '爆闪频率', 0x0C: '强制报警', 0x0D: 'LED亮度', 0x0E: '工作方式' }; const name = funcNames[funcCode] || ('0x' + funcCode.toString(16)); - console.log('[100J-蓝牙] 设备响应 FB:', name, '解析:', JSON.stringify(result), '原始:', hexStr); - - if (this.onNotifyCallback) { - this.onNotifyCallback(result); + if (!skipSideEffects) { + console.log('[100J-蓝牙] 设备响应 FB:', name, '解析:', JSON.stringify(result), '原始:', hexStr); + if (this.onNotifyCallback) { + this.onNotifyCallback(result); + } } return result; } @@ -879,9 +891,9 @@ export function cache100JVoiceFileForBle(deviceId, voiceListId, filePath) { }); } -// 暴露给页面:解析蓝牙接收到的数据 -export function parseBleData(buffer) { - return protocolInstance.parseBleData(buffer); +// 暴露给页面:解析蓝牙接收到的数据(options 见类上 parseBleData 注释) +export function parseBleData(buffer, options) { + return protocolInstance.parseBleData(buffer, options); } // 暴露给页面:蓝牙连接后主动拉取电源状态(电量、续航) @@ -1121,37 +1133,47 @@ function execWithBleFirst(bleExec, httpExec, logName, onWaiting, opts = {}) { } catch (e) {} }; + // 协议层认为已连:仍可能被系统蓝牙关闭/底层已断而滞后,先校验适配器,避免先发蓝牙卡超时再回退 if (protocolInstance.isBleConnected && protocolInstance.bleDeviceId) { - console.log('[100J] 语音上传:协议层已连接,执行蓝牙传文件'); - return doBle().catch(onBleSendFail); + return getBleAdapterAvailable().then((adapterOk) => { + if (!adapterOk) { + protocolInstance.setBleConnectionStatus(false, ''); + return go4GOrReject('系统蓝牙已关闭,走4G'); + } + console.log('[100J]', logName || '指令', '协议层已连接,走蓝牙'); + return doBle().catch(onBleSendFail); + }); } - console.log('[100J] 语音上传:协议层未就绪,将等待重连或走 4G', { isBleConnected: protocolInstance.isBleConnected, hasBleDeviceId: !!protocolInstance.bleDeviceId }); - // 无 bleDeviceId:系统蓝牙关闭则立即 4G;开启则短时等页面扫描连上(不再白等 12s) + console.log('[100J]', logName || '指令', '协议层未就绪', { isBleConnected: protocolInstance.isBleConnected, hasBleDeviceId: !!protocolInstance.bleDeviceId }); + // 无 bleDeviceId:本地仅蓝牙场景仍弹「请稍候」并等扫描;可走 4G 时不弹 loading、不白等 5s,直接 4G if (!protocolInstance.bleDeviceId) { return getBleAdapterAvailable().then((adapterOk) => { if (!adapterOk) { + protocolInstance.setBleConnectionStatus(false, ''); return go4GOrReject('系统蓝牙未开启,走4G'); } - if (typeof onWaiting === 'function') onWaiting(); - else showWaitUi('请稍候…'); - return waitForBleConnection() - .then((connected) => { - return connected ? doBle().catch(onBleSendFail) : go4GOrReject('蓝牙未连接'); - }) - .finally(hideWaitUi); + if (no4G) { + if (typeof onWaiting === 'function') onWaiting(); + else showWaitUi('请稍候…'); + return waitForBleConnection() + .then((connected) => { + return connected ? doBle().catch(onBleSendFail) : go4GOrReject('蓝牙未连接'); + }) + .finally(hideWaitUi); + } + return waitForBleConnection(0, BLE_POLL_INTERVAL_MS).then((connected) => + connected ? doBle().catch(onBleSendFail) : go4GOrReject('无蓝牙连接,走4G') + ); }); } - // 有 bleDeviceId 但未连:系统蓝牙关则直接 4G,否则短时重连 + // 有 bleDeviceId 但未连:用户刚关蓝牙/超出范围时,不再弹「请稍候」等重连 ~2s,双通道在线时直接 4G return getBleAdapterAvailable().then((adapterOk) => { if (!adapterOk) { + protocolInstance.setBleConnectionStatus(false, ''); return go4GOrReject('系统蓝牙未开启,走4G'); } - if (typeof onWaiting !== 'function') showWaitUi('请稍候…'); - return tryReconnectBle() - .then((reconnected) => { - return reconnected ? doBle().catch(onBleSendFail) : go4GOrReject('蓝牙未连接'); - }) - .finally(hideWaitUi); + console.log('[100J]', logName || '指令', '蓝牙已断开,直接走4G(不阻塞重连)'); + return go4GOrReject(null); }); } diff --git a/pages/100J/HBY100-J.vue b/pages/100J/HBY100-J.vue index e05dab9..15576c7 100644 --- a/pages/100J/HBY100-J.vue +++ b/pages/100J/HBY100-J.vue @@ -173,10 +173,10 @@ 频率 - {{ formData.strobeFrequency }}HZ + {{ strobeFrequencySlider }}HZ - @@ -331,7 +331,7 @@ sta_VoiceType: '0', volume: 10, sta_LightType: '', - strobeFrequency: 0.5, + strobeFrequency: 1, lightBrightness: 10, sta_system: '', warnTime: 0, @@ -552,8 +552,9 @@ // 设备按键, app同步 } else if (funcType == '14') { // 调节亮度,音量,频率相关字段 - these.formData.strobeFrequency = led_strobe.frequency || - 0.5; //频率 + these.formData.strobeFrequency = these.normalizeStrobeFreq( + led_strobe.frequency != null ? led_strobe.frequency : 1 + ); these.formData.volume = volume || 10; //音量 these.formData.lightBrightness = brightness.red || 10; //亮度值 @@ -693,6 +694,10 @@ this.$nextTick(() => this.sync100JBleUiFromHelper()); }, computed: { + /** 与 slider min/max(1~10) 对齐;离线未拉到详情时避免 0.5 等非法值导致滑块渲染飞出 */ + strobeFrequencySlider() { + return this.normalizeStrobeFreq(this.formData.strobeFrequency); + }, getbleStatu() { if (this.formData.bleStatu === true) { return '已连接'; @@ -711,6 +716,15 @@ }, methods: { + /** 警示灯频率 UI:slider 为 1~10,与协议 0~12 取交集 */ + normalizeStrobeFreq(v) { + const n = Number(v); + if (!Number.isFinite(n)) return 1; + const r = Math.round(n); + if (r < 1) return 1; + if (r > 10) return 10; + return r; + }, /** 与 BleHelper 实际连接状态对齐(系统关蓝牙再开、从后台回前台等) */ sync100JBleUiFromHelper() { const mac = (this.device && this.device.deviceMac) || (this.deviceInfo && this.deviceInfo.deviceMac); @@ -796,6 +810,7 @@ }) ); Object.assign(this.formData, validData); + that.formData.strobeFrequency = that.normalizeStrobeFreq(that.formData.strobeFrequency); that.deviceInfo = res.data; that.$nextTick(() => that.sync100JBleUiFromHelper && that.sync100JBleUiFromHelper()); const strobeEnable = res.data.strobeEnable ?? 0; // 0=关闭,1=开启 @@ -1368,6 +1383,9 @@ if (parsedData.batteryRemainingTime !== undefined) { this.$set(this.deviceInfo, 'batteryRemainingTime', parsedData.batteryRemainingTime); } + if (parsedData.chargingStatus !== undefined) { + this.$set(this.deviceInfo, 'chargingStatus', parsedData.chargingStatus); + } @@ -1417,15 +1435,17 @@ else if (this.formData.sta_VoiceType === '7') this.formData.sta_VoiceType = '-1'; } if (parsedData.volume !== undefined) this.formData.volume = parsedData.volume; - if (parsedData.strobeFrequency !== undefined) this.formData.strobeFrequency = parsedData - .strobeFrequency; + if (parsedData.strobeFrequency !== undefined) { + this.formData.strobeFrequency = this.normalizeStrobeFreq(parsedData.strobeFrequency); + } if (parsedData.redBrightness !== undefined) this.formData.lightBrightness = parsedData .redBrightness; } // 0x09 音量、0x0D 亮度:单独响应时同步 if (fc === 0x09 && parsedData.volume !== undefined) this.formData.volume = parsedData.volume; - if (fc === 0x0B && parsedData.strobeFrequency !== undefined) this.formData.strobeFrequency = parsedData - .strobeFrequency; + if (fc === 0x0B && parsedData.strobeFrequency !== undefined) { + this.formData.strobeFrequency = this.normalizeStrobeFreq(parsedData.strobeFrequency); + } if (fc === 0x0D && parsedData.redBrightness !== undefined) this.formData.lightBrightness = parsedData .redBrightness; }, @@ -1932,6 +1952,9 @@ .slider-container { padding: 0px; + width: 100%; + overflow: hidden; + box-sizing: border-box; } .addIco { diff --git a/utils/BleHelper.js b/utils/BleHelper.js index 23f74fc..87c5e64 100644 --- a/utils/BleHelper.js +++ b/utils/BleHelper.js @@ -1728,28 +1728,18 @@ class BleHelper { return linkDevice(deviceId); }).then((res) => { - if (res) { //新连接 + if (res) { //新连接(含 createBLEConnection 刚成功) // console.log("11111111"); if (fIndex == -1) { // console.log("开始获取服务", targetServiceId) return this.getService(deviceId, targetServiceId, writeCharId, notifyCharId); //获取服务 } else { - if (f.wirteCharactId && f.notifyCharactId) { - if (!f.notifyState) { - // console.log("开始订阅特征"); - this.subScribe(deviceId, true); - } else { - console.log("不订阅消息"); - } - return Promise.resolve(true); - } else { - console.log("开始获取服务", targetServiceId) - return this.getService(deviceId, targetServiceId, writeCharId, - notifyCharId); - } - - + // 设备已在 LinkedList(例如缓存/重连):不能仅用缓存的 write/notify UUID 直接订阅。 + // 重连后须先 getBLEDeviceServices,否则部分机型 notifyBLECharacteristicValueChange 报 no service(10004),notify 收不到任何数据。 + console.log("已缓存设备重新连接,重新发现服务并订阅", deviceId); + return this.getService(deviceId, targetServiceId, writeCharId, + notifyCharId); } } else { //已连接过,直接订阅消息 // console.log("11111111"); diff --git a/utils/BleReceive.js b/utils/BleReceive.js index b6c1a87..a9175c7 100644 --- a/utils/BleReceive.js +++ b/utils/BleReceive.js @@ -694,7 +694,8 @@ class BleReceive { let receiveData = {}; try { if (!receive.bytes || receive.bytes.length < 3) return receiveData; - const parsed = parseBleData(receive.bytes); + // 与 HBY100-J 页 bleValueNotify 共用 notify,避免 parseBleData 执行两次:重复日志、FB05 双次 resolve、onNotify 双次 + const parsed = parseBleData(receive.bytes, { skipSideEffects: true }); if (!parsed) return receiveData; if (parsed.longitude !== undefined) receiveData.longitude = parsed.longitude; if (parsed.latitude !== undefined) receiveData.latitude = parsed.latitude;