增加中文日志输出 优化导出日志功能
This commit is contained in:
@@ -84,8 +84,8 @@ PC -> MCU
|
|||||||
- 0: 不判断
|
- 0: 不判断
|
||||||
- 1: 触发
|
- 1: 触发
|
||||||
- 2: 释放
|
- 2: 释放
|
||||||
- 3: 关至开
|
- 3: 开至关
|
||||||
- 4: 开至关
|
- 4: 关至开
|
||||||
|
|
||||||
注意:条件组超时时间必须大于或等于条件判定时间(P11~P12 >= P3~P4),否则设备应判定为参数错误。
|
注意:条件组超时时间必须大于或等于条件判定时间(P11~P12 >= P3~P4),否则设备应判定为参数错误。
|
||||||
|
|
||||||
|
|||||||
6
app.json
6
app.json
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [
|
||||||
|
|
||||||
"pages/autho/index",
|
"pages/autho/index",
|
||||||
|
"pages/basics/BluetoothDebugging/B13page/B13page",
|
||||||
"pages/login/login",
|
"pages/login/login",
|
||||||
"pages/index/index",
|
"pages/index/index",
|
||||||
"pages/mycenter/index",
|
"pages/mycenter/index",
|
||||||
@@ -16,8 +17,7 @@
|
|||||||
"pages/basics/FacialDeviceBinding/FacialDeviceBinding",
|
"pages/basics/FacialDeviceBinding/FacialDeviceBinding",
|
||||||
"pages/basics/progress/progress",
|
"pages/basics/progress/progress",
|
||||||
"pages/basics/progress/RoomTypeControlLog/RoomTypeControlLog",
|
"pages/basics/progress/RoomTypeControlLog/RoomTypeControlLog",
|
||||||
"pages/basics/BluetoothDebugging/BluetoothDebugging",
|
"pages/basics/BluetoothDebugging/BluetoothDebugging"
|
||||||
"pages/basics/BluetoothDebugging/B13page/B13page"
|
|
||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"backgroundTextStyle": "light",
|
"backgroundTextStyle": "light",
|
||||||
|
|||||||
92
lib/gbkDecoder.js
Normal file
92
lib/gbkDecoder.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Lightweight GBK/GB18030 decoder helper
|
||||||
|
// Strategy:
|
||||||
|
// 1. Prefer native TextDecoder('gb18030'/'gbk') if available
|
||||||
|
// 2. Fallback to a reverse table built from utils/ecUnicodeToGBK.js (if present)
|
||||||
|
// 3. Final fallback: byte-by-byte ASCII/replace decoding
|
||||||
|
|
||||||
|
function toUint8Array(input) {
|
||||||
|
if (!input) return new Uint8Array(0)
|
||||||
|
if (input instanceof Uint8Array) return input
|
||||||
|
if (input.buffer && input.buffer instanceof ArrayBuffer) return new Uint8Array(input.buffer)
|
||||||
|
return new Uint8Array(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
let reverseMap = null
|
||||||
|
function buildReverseFromTable(table) {
|
||||||
|
try {
|
||||||
|
const rev = Object.create(null)
|
||||||
|
for (let i = 0; i < table.length; i++) {
|
||||||
|
const v = table[i]
|
||||||
|
if (v) rev[String(v).toUpperCase()] = Number(i)
|
||||||
|
}
|
||||||
|
return rev
|
||||||
|
} catch (e) { return null }
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeWithTable(u8, table) {
|
||||||
|
if (!table) return null
|
||||||
|
if (!reverseMap) reverseMap = buildReverseFromTable(table)
|
||||||
|
if (!reverseMap) return null
|
||||||
|
let out = ''
|
||||||
|
for (let i = 0; i < u8.length; i++) {
|
||||||
|
const b = u8[i]
|
||||||
|
if (b <= 0x7F) { out += String.fromCharCode(b); continue }
|
||||||
|
const b2 = u8[i + 1]
|
||||||
|
if (typeof b2 !== 'number') { out += '\uFFFD'; continue }
|
||||||
|
const hex = b.toString(16).padStart(2, '0').toUpperCase() + b2.toString(16).padStart(2, '0').toUpperCase()
|
||||||
|
const cp = reverseMap[hex]
|
||||||
|
if (cp != null) {
|
||||||
|
try { out += String.fromCodePoint(cp) } catch (e) { out += '\uFFFD' }
|
||||||
|
} else {
|
||||||
|
out += '\uFFFD'
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode(u8in) {
|
||||||
|
const u8 = toUint8Array(u8in)
|
||||||
|
// 1) Native TextDecoder if environment supports gb18030/gbk
|
||||||
|
try {
|
||||||
|
if (typeof TextDecoder === 'function') {
|
||||||
|
try { return new TextDecoder('gb18030').decode(u8) } catch (e) { /* ignore */ }
|
||||||
|
try { return new TextDecoder('gbk').decode(u8) } catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
|
||||||
|
// 2) Try a local Encoding lib if available (bundles may expose Encoding.convert)
|
||||||
|
try {
|
||||||
|
const enc = (typeof Encoding !== 'undefined') ? Encoding : (typeof window !== 'undefined' ? window.Encoding : null)
|
||||||
|
if (enc && typeof enc.convert === 'function') {
|
||||||
|
try { return enc.convert(u8, { to: 'UNICODE', from: 'GB18030', type: 'string' }) } catch (e) { /* ignore */ }
|
||||||
|
try { return enc.convert(u8, { to: 'UTF8', from: 'GB18030', type: 'string' }) } catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
|
||||||
|
// 3) Try building reverse map from project mapping utils (utils/ecUnicodeToGBK.js)
|
||||||
|
try {
|
||||||
|
const tableMod = require('../utils/ecUnicodeToGBK.js')
|
||||||
|
const table = tableMod && (tableMod.table || tableMod.t || tableMod)
|
||||||
|
if (table) {
|
||||||
|
const s = decodeWithTable(u8, table)
|
||||||
|
if (s && s.length) return s
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
|
||||||
|
// 4) Final fallback: best-effort ASCII/replace pass
|
||||||
|
try {
|
||||||
|
let out = ''
|
||||||
|
for (let i = 0; i < u8.length; i++) {
|
||||||
|
const b = u8[i]
|
||||||
|
if (b <= 0x7F) out += String.fromCharCode(b)
|
||||||
|
else {
|
||||||
|
const b2 = u8[i + 1]
|
||||||
|
if (typeof b2 === 'number') { out += '\uFFFD'; i += 1 } else { out += '\uFFFD' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
} catch (e) { return '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { decode }
|
||||||
11
lib/test_gbk.js
Normal file
11
lib/test_gbk.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Quick test for gbkDecoder
|
||||||
|
const path = require('path')
|
||||||
|
const { decode } = require('./gbkDecoder')
|
||||||
|
|
||||||
|
const hex = '2D D7 B4 CC AC 3A 32 CC F5 BC FE D7 E9 3A 31 C5 D0 B6 A8 32 2C 32 2C 32 2C 32 2C 32 2C 54 69 6D 65 3A 34 34 30 35 2D 34 34 30 35'
|
||||||
|
const bytes = hex.split(/\s+/).map(h => parseInt(h,16))
|
||||||
|
const u8 = Uint8Array.from(bytes)
|
||||||
|
|
||||||
|
const out = decode(u8)
|
||||||
|
console.log('DECODED:', out)
|
||||||
|
console.log('EXPECTED:', '- 状态:2 条件组:1 判定-2,2,2,2,2 Time:4405-4405')
|
||||||
@@ -1,6 +1,15 @@
|
|||||||
|
|
||||||
const { buildCommand, buildReadVersion, buildSetDoorBathEvent, COMMANDS, verifyHexPacket } = require('../../../../utils/w13Packet.js')
|
const { buildCommand, buildReadVersion, buildSetDoorBathEvent, COMMANDS, verifyHexPacket } = require('../../../../utils/w13Packet.js')
|
||||||
const FIXED_CONNECT_CMD = new Uint8Array([0xCC, 0xC0, 0x0C, 0x00, 0x4F, 0xC0, 0x01, 0x00, 0x02, 0x00, 0x0C, 0x08])
|
const FIXED_CONNECT_CMD = new Uint8Array([0xCC, 0xC0, 0x0C, 0x00, 0x4F, 0xC0, 0x01, 0x00, 0x02, 0x00, 0x0C, 0x08])
|
||||||
|
// Optional encoding library for robust GBK/GB18030 decoding
|
||||||
|
let EncodingLib = null
|
||||||
|
try {
|
||||||
|
EncodingLib = require('../../../../utils/ecUnicodeToGBK.js')
|
||||||
|
} catch (e) {
|
||||||
|
try { EncodingLib = (typeof Encoding !== 'undefined' ? Encoding : null) } catch (e2) { EncodingLib = null }
|
||||||
|
}
|
||||||
|
let GBKDecoder = null
|
||||||
|
try { GBKDecoder = require('../../../../lib/gbkDecoder.js') } catch (e) { GBKDecoder = null }
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
TabCur: 1, // 1: 蓝牙调试 2: 蓝牙升级
|
TabCur: 1, // 1: 蓝牙调试 2: 蓝牙升级
|
||||||
@@ -37,19 +46,19 @@ Page({
|
|||||||
],
|
],
|
||||||
// 条件“有无人标记”选项,参考截图:无人至有人/短暂离开/长时间离开/有人至无人
|
// 条件“有无人标记”选项,参考截图:无人至有人/短暂离开/长时间离开/有人至无人
|
||||||
tagOptions: ['无人至有人', '短暂离开', '长时间离开', '有人至无人'],
|
tagOptions: ['无人至有人', '短暂离开', '长时间离开', '有人至无人'],
|
||||||
stateOptions: ['不判断', '触发', '释放', '关至开', '开至关'],
|
stateOptions: ['不判断', '触发', '释放', '开至关', '关至开'],
|
||||||
// 门磁专用选项(与其他端口不同):0=不判断,1=关门,2=开门,3=关至开,4=开至关
|
// 门磁专用选项(与其他端口不同):0=不判断,1=关门,2=开门,3=开至关,4=关至开
|
||||||
doorMagOptions: ['不判断', '关门', '开门', '关至开', '开至关'],
|
doorMagOptions: ['不判断', '关门', '开门', '开至关', '关至开'],
|
||||||
// 默认条件:由用户指定的条件组与条件
|
// 默认条件:由用户指定的条件组与条件
|
||||||
conditions: [
|
conditions: [
|
||||||
// 组1: timeout 2秒,包含1个条件
|
// 组1: timeout 2秒,包含1个条件
|
||||||
{ group: 1, seq: 1, tag: 0, cardPower: 0, doorMag: 3, irHall: 0, bathRadar: 0, bathroomRadar: 0, judgeTime: 0, judgeUnit: 0, timeout: 2, timeoutUnit: 0 },
|
{ group: 1, seq: 1, tag: 0, cardPower: 0, doorMag: 4, irHall: 0, bathRadar: 0, bathroomRadar: 0, judgeTime: 0, judgeUnit: 0, timeout: 2, timeoutUnit: 0 },
|
||||||
// 组2: timeout 20秒,包含3个条件
|
// 组2: timeout 20秒,包含3个条件
|
||||||
{ group: 2, seq: 1, tag: 0, cardPower: 0, doorMag: 0, irHall: 1, bathRadar: 0, bathroomRadar: 0, judgeTime: 0, judgeUnit: 0, timeout: 20, timeoutUnit: 0 },
|
{ group: 2, seq: 1, tag: 0, cardPower: 0, doorMag: 0, irHall: 1, bathRadar: 0, bathroomRadar: 0, judgeTime: 0, judgeUnit: 0, timeout: 20, timeoutUnit: 0 },
|
||||||
{ group: 2, seq: 2, tag: 0, cardPower: 0, doorMag: 0, irHall: 0, bathRadar: 1, bathroomRadar: 0, judgeTime: 0, judgeUnit: 0, timeout: 20, timeoutUnit: 0 },
|
{ group: 2, seq: 2, tag: 0, cardPower: 0, doorMag: 0, irHall: 0, bathRadar: 1, bathroomRadar: 0, judgeTime: 0, judgeUnit: 0, timeout: 20, timeoutUnit: 0 },
|
||||||
{ group: 2, seq: 3, tag: 0, cardPower: 0, doorMag: 0, irHall: 0, bathRadar: 0, bathroomRadar: 1, judgeTime: 0, judgeUnit: 0, timeout: 20, timeoutUnit: 0 },
|
{ group: 2, seq: 3, tag: 0, cardPower: 0, doorMag: 0, irHall: 0, bathRadar: 0, bathroomRadar: 1, judgeTime: 0, judgeUnit: 0, timeout: 20, timeoutUnit: 0 },
|
||||||
// 组3: timeout 2秒,包含1个条件
|
// 组3: timeout 2秒,包含1个条件
|
||||||
{ group: 3, seq: 1, tag: 1, cardPower: 0, doorMag: 4, irHall: 0, bathRadar: 0, bathroomRadar: 0, judgeTime: 0, judgeUnit: 0, timeout: 2, timeoutUnit: 0 },
|
{ group: 3, seq: 1, tag: 1, cardPower: 0, doorMag:3, irHall: 0, bathRadar: 0, bathroomRadar: 0, judgeTime: 0, judgeUnit: 0, timeout: 2, timeoutUnit: 0 },
|
||||||
// 组4: timeout 10分,包含1个条件
|
// 组4: timeout 10分,包含1个条件
|
||||||
{ group: 4, seq: 1, tag: 1, cardPower: 0, doorMag: 1, irHall: 2, bathRadar: 2, bathroomRadar: 2, judgeTime: 2, judgeUnit: 1, timeout: 10, timeoutUnit: 1 },
|
{ group: 4, seq: 1, tag: 1, cardPower: 0, doorMag: 1, irHall: 2, bathRadar: 2, bathroomRadar: 2, judgeTime: 2, judgeUnit: 1, timeout: 10, timeoutUnit: 1 },
|
||||||
// 组5: timeout 10分,包含1个条件
|
// 组5: timeout 10分,包含1个条件
|
||||||
@@ -100,6 +109,8 @@ Page({
|
|||||||
bathTriggerIndex: 0,
|
bathTriggerIndex: 0,
|
||||||
bathReleaseIndex: 0,
|
bathReleaseIndex: 0,
|
||||||
pressedMask: 0,
|
pressedMask: 0,
|
||||||
|
// 是否将非协议数据按 GBK 解码为中文日志
|
||||||
|
showChineseLogs: false,
|
||||||
// 删除模态框状态
|
// 删除模态框状态
|
||||||
deleteDialogVisible: false,
|
deleteDialogVisible: false,
|
||||||
deleteDialogMode: '', // 'group' | 'condition'
|
deleteDialogMode: '', // 'group' | 'condition'
|
||||||
@@ -237,6 +248,21 @@ Page({
|
|||||||
onShow() {
|
onShow() {
|
||||||
// 页面显示时也打印一次,方便返回/二次进入场景
|
// 页面显示时也打印一次,方便返回/二次进入场景
|
||||||
this.logBleStatus()
|
this.logBleStatus()
|
||||||
|
// 尝试在页面恢复时重建 BLE 通道并恢复通知/订阅
|
||||||
|
try {
|
||||||
|
// 小延时以让系统先恢复蓝牙状态
|
||||||
|
setTimeout(() => {
|
||||||
|
this.ensureBleConnectedAndReconnect(() => {
|
||||||
|
// 确保找到 service/char 并开启 notify
|
||||||
|
this.ensureBleChannels(() => {
|
||||||
|
try { this.enableNotify() } catch (e) { /* ignore */ }
|
||||||
|
try { this.setupBleListener() } catch (e) { /* ignore */ }
|
||||||
|
// 重新启动雷达读取(若页面之前在读)
|
||||||
|
try { this.sendRadarStatusCommand(true) } catch (e) { /* ignore */ }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, 250)
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnload() {
|
onUnload() {
|
||||||
@@ -270,7 +296,16 @@ Page({
|
|||||||
// === 门磁控件事件处理 ===
|
// === 门磁控件事件处理 ===
|
||||||
onDoorFieldInput(e) {
|
onDoorFieldInput(e) {
|
||||||
const field = e.currentTarget.dataset.field
|
const field = e.currentTarget.dataset.field
|
||||||
let val = Number(e.detail.value || 0)
|
const raw = (e.detail && e.detail.value != null) ? String(e.detail.value).trim() : ''
|
||||||
|
// Allow empty input while user is editing (don't enforce min/max on empty)
|
||||||
|
if (raw === '') {
|
||||||
|
const updates = {}
|
||||||
|
if (field === 'triggerDelay') updates.doorTriggerDelay = ''
|
||||||
|
if (field === 'releaseDelay') updates.doorReleaseDelay = ''
|
||||||
|
this.setData(updates)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let val = Number(raw)
|
||||||
if (isNaN(val)) val = 0
|
if (isNaN(val)) val = 0
|
||||||
// 单位:index 0 => 秒 (max 60), index 1 => 分 (max 30)
|
// 单位:index 0 => 秒 (max 60), index 1 => 分 (max 30)
|
||||||
const unitIndex = (field === 'triggerDelay') ? (this.data.doorTriggerUnitIndex || 0) : (this.data.doorReleaseUnitIndex || 0)
|
const unitIndex = (field === 'triggerDelay') ? (this.data.doorTriggerUnitIndex || 0) : (this.data.doorReleaseUnitIndex || 0)
|
||||||
@@ -323,7 +358,15 @@ Page({
|
|||||||
// === 卫浴控件事件处理 ===
|
// === 卫浴控件事件处理 ===
|
||||||
onBathFieldInput(e) {
|
onBathFieldInput(e) {
|
||||||
const field = e.currentTarget.dataset.field
|
const field = e.currentTarget.dataset.field
|
||||||
let val = Number(e.detail.value || 0)
|
const raw = (e.detail && e.detail.value != null) ? String(e.detail.value).trim() : ''
|
||||||
|
if (raw === '') {
|
||||||
|
const updates = {}
|
||||||
|
if (field === 'triggerDelay') updates.bathTriggerDelay = ''
|
||||||
|
if (field === 'releaseDelay') updates.bathReleaseDelay = ''
|
||||||
|
this.setData(updates)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let val = Number(raw)
|
||||||
if (isNaN(val)) val = 0
|
if (isNaN(val)) val = 0
|
||||||
const unitIndex = (field === 'triggerDelay') ? (this.data.bathTriggerUnitIndex || 0) : (this.data.bathReleaseUnitIndex || 0)
|
const unitIndex = (field === 'triggerDelay') ? (this.data.bathTriggerUnitIndex || 0) : (this.data.bathReleaseUnitIndex || 0)
|
||||||
const maxAllowed = unitIndex === 0 ? 60 : 30
|
const maxAllowed = unitIndex === 0 ? 60 : 30
|
||||||
@@ -459,6 +502,100 @@ Page({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 导出日志:将当前 logList 文本
|
||||||
|
onExportLogs(){
|
||||||
|
const list = this.data.logList || []
|
||||||
|
if (!list || list.length === 0) {
|
||||||
|
// this.appendLog('WARN', '导出失败:无日志可导出')
|
||||||
|
wx.showToast({ title: '无日志可导出', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const txt = list.map(i => (i.time ? i.time + ' ' : '') + (i.text || '')).join('\n')
|
||||||
|
try {
|
||||||
|
if (typeof wx.getFileSystemManager !== 'function') {
|
||||||
|
this.appendLog('WARN', '环境不支持 wx.getFileSystemManager,回退为剪贴板')
|
||||||
|
if (typeof wx.setClipboardData === 'function') {
|
||||||
|
wx.setClipboardData({ data: txt, success: () => { wx.showToast({ title: '日志已复制到剪贴板', icon: 'none' }); this.appendLog('UI', 'clipboard fallback success') }, fail: () => { wx.showToast({ title: '导出失败', icon: 'none' }) } })
|
||||||
|
} else {
|
||||||
|
wx.showToast({ title: '当前环境不支持导出', icon: 'none' })
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const fsm = wx.getFileSystemManager()
|
||||||
|
const now = new Date()
|
||||||
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
|
const name = `comm_logs_${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}.txt`
|
||||||
|
const base = (wx.env && wx.env.USER_DATA_PATH) ? wx.env.USER_DATA_PATH : null
|
||||||
|
const filePath = base ? `${base}/${name}` : `/tmp/${name}`
|
||||||
|
fsm.writeFile({
|
||||||
|
filePath,
|
||||||
|
data: txt,
|
||||||
|
encoding: 'utf8',
|
||||||
|
success: () => {
|
||||||
|
this.appendLog('UI', `导出文件: ${filePath}`)
|
||||||
|
// 写入后弹出确认对话,用户点击“分享”视为手势再调用分享接口(不打开文档)
|
||||||
|
wx.showModal({
|
||||||
|
title: '导出完成',
|
||||||
|
content: '文件已导出,是否现在分享给微信联系人?',
|
||||||
|
confirmText: '分享',
|
||||||
|
cancelText: '取消',
|
||||||
|
success: (res) => {
|
||||||
|
if (res && res.confirm) {
|
||||||
|
try {
|
||||||
|
// this.appendLog('DEBUG', '用户确认分享,尝试调用分享接口')
|
||||||
|
if (typeof wx.shareFileMessage === 'function') {
|
||||||
|
wx.shareFileMessage({ filePath, success: () => { wx.showToast({ title: '已打开分享面板', icon: 'success' }); this.appendLog('UI', 'shareFileMessage success') }, fail: (e) => { this.appendLog('WARN', `shareFileMessage fail ${e && (e.errMsg||e)}`); if (typeof wx.setClipboardData === 'function') { wx.setClipboardData({ data: txt, success: () => wx.showToast({ title: '已复制到剪贴板,可粘贴发送', icon: 'none' }) }) } } })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof wx.shareFile === 'function') {
|
||||||
|
wx.shareFile({ filePath, success: () => { wx.showToast({ title: '已打开分享面板', icon: 'success' });
|
||||||
|
// this.appendLog('UI', 'shareFile success')
|
||||||
|
}, fail: (e) => { this.appendLog('WARN', `shareFile fail ${e && (e.errMsg||e)}`); if (typeof wx.setClipboardData === 'function') { wx.setClipboardData({ data: txt, success: () => wx.showToast({ title: '已复制到剪贴板,可粘贴发送', icon: 'none' }) }) } } })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 无分享接口,回退为剪贴板
|
||||||
|
if (typeof wx.setClipboardData === 'function') {
|
||||||
|
wx.setClipboardData({ data: txt, success: () => { wx.showToast({ title: '日志已复制到剪贴板,可在聊天中粘贴发送', icon: 'none' });
|
||||||
|
// this.appendLog('UI', 'clipboard fallback success')
|
||||||
|
} })
|
||||||
|
} else {
|
||||||
|
wx.showToast({ title: '导出完成,但无法直接分享', icon: 'none' })
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
this.appendLog('WARN', `用户触发分享时出错: ${ex && (ex.message||ex)}`)
|
||||||
|
if (typeof wx.setClipboardData === 'function') {
|
||||||
|
wx.setClipboardData({ data: txt, success: () => { wx.showToast({ title: '日志已复制到剪贴板', icon: 'none' });
|
||||||
|
// this.appendLog('UI', 'clipboard fallback success')
|
||||||
|
} })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.appendLog('UI', '用户取消分享,文件已保存')
|
||||||
|
wx.showToast({ title: '文件已保存', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (e) => {
|
||||||
|
this.appendLog('WARN', `showModal failed: ${e && (e.errMsg||e)}`)
|
||||||
|
// 弹窗失败时直接回退为剪贴板
|
||||||
|
if (typeof wx.setClipboardData === 'function') {
|
||||||
|
wx.setClipboardData({ data: txt, success: () => { wx.showToast({ title: '日志已复制到剪贴板', icon: 'none' }); this.appendLog('UI', 'clipboard fallback success after showModal fail') } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('writeFile fail', err)
|
||||||
|
wx.showToast({ title: '写入文件失败', icon: 'none' })
|
||||||
|
this.appendLog('WARN', `写入文件失败 ${err && (err.errMsg||err)}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
wx.showToast({ title: '导出失败', icon: 'none' })
|
||||||
|
this.appendLog('WARN', `导出异常 ${e && (e.message||e)}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 开始OTA升级(命令0x0B, P0=0x01)
|
// 开始OTA升级(命令0x0B, P0=0x01)
|
||||||
onStartOta() {
|
onStartOta() {
|
||||||
try {
|
try {
|
||||||
@@ -1078,6 +1215,20 @@ Page({
|
|||||||
// 按显示偏好记录日志(HEX 或原始文本)
|
// 按显示偏好记录日志(HEX 或原始文本)
|
||||||
const viewText = this.data.hexShow ? hex : `[${hex}]`
|
const viewText = this.data.hexShow ? hex : `[${hex}]`
|
||||||
this.appendLog('RX', viewText)
|
this.appendLog('RX', viewText)
|
||||||
|
|
||||||
|
// 若开启中文日志且数据不符合协议头,则尝试用 GBK 解码生成中文日志
|
||||||
|
try {
|
||||||
|
const isProtocol = u8 && u8.length >= 2 && u8[0] === 0xCC && u8[1] === 0xC0
|
||||||
|
if (!isProtocol && this.data.showChineseLogs) {
|
||||||
|
const decoded = this._tryDecodeGbk(u8)
|
||||||
|
if (decoded && decoded.trim()) {
|
||||||
|
this.appendLog('RX-CN', decoded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore decode errors
|
||||||
|
}
|
||||||
|
|
||||||
// 将规范化数据交给协议解析器,进行业务处理与 UI 更新
|
// 将规范化数据交给协议解析器,进行业务处理与 UI 更新
|
||||||
this.handleIncomingPacket(u8)
|
this.handleIncomingPacket(u8)
|
||||||
}
|
}
|
||||||
@@ -1101,6 +1252,46 @@ Page({
|
|||||||
wx.showModal({ title: '提示', content: '下载链接:' + url, showCancel: false });
|
wx.showModal({ title: '提示', content: '下载链接:' + url, showCancel: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Toggle Chinese logs: when enabled, non-protocol BLE data will be decoded via GBK
|
||||||
|
onToggleChineseLogs() {
|
||||||
|
const next = !this.data.showChineseLogs
|
||||||
|
this.setData({ showChineseLogs: next })
|
||||||
|
wx.showToast({ title: next ? '中文日志已开启' : '中文日志已关闭', icon: 'none' })
|
||||||
|
if (next) {
|
||||||
|
// 发送固定数据包以通知设备(要求:中文日志开启时发送)
|
||||||
|
const pkt = new Uint8Array([0xCC, 0xC0, 0x0C, 0x00, 0x4F, 0xC5, 0x01, 0x00, 0x02, 0x00, 0x0C, 0x04])
|
||||||
|
try {
|
||||||
|
this.appendLog('TX', `中文日志开关包: ${this.toHex(pkt)}`)
|
||||||
|
this.writeBleBytes(pkt, '中文日志开关')
|
||||||
|
} catch (e) {
|
||||||
|
this.appendLog('WARN', `发送中文日志开关包失败: ${e && (e.message || e)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 尝试用 GBK 解码 Uint8Array,若环境支持 TextDecoder('gbk') 则使用之,失败则回退到 utf-8 或简单字节->字符串
|
||||||
|
_tryDecodeGbk(u8) {
|
||||||
|
try {
|
||||||
|
if (GBKDecoder && typeof GBKDecoder.decode === 'function') {
|
||||||
|
const r = GBKDecoder.decode(u8)
|
||||||
|
if (r != null) return r
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
// Fallback: best-effort ASCII/pass-through
|
||||||
|
try {
|
||||||
|
let s = ''
|
||||||
|
for (let i = 0; i < u8.length; i++) {
|
||||||
|
const b = u8[i]
|
||||||
|
if (b <= 0x7F) s += String.fromCharCode(b)
|
||||||
|
else {
|
||||||
|
const b2 = u8[i + 1]
|
||||||
|
if (typeof b2 === 'number') { s += '\uFFFD'; i += 1 } else s += '\uFFFD'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
} catch (e) { return '' }
|
||||||
|
},
|
||||||
teardownBleListener() {
|
teardownBleListener() {
|
||||||
if (this._onBleChange && typeof wx.offBLECharacteristicValueChange === 'function') {
|
if (this._onBleChange && typeof wx.offBLECharacteristicValueChange === 'function') {
|
||||||
wx.offBLECharacteristicValueChange(this._onBleChange)
|
wx.offBLECharacteristicValueChange(this._onBleChange)
|
||||||
@@ -2494,11 +2685,15 @@ Page({
|
|||||||
const now = new Date()
|
const now = new Date()
|
||||||
const timeStr = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`
|
const timeStr = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`
|
||||||
const finalText = this.data.withTimestamp ? `[${timeStr}] ${direction}: ${text}` : `${direction}: ${text}`
|
const finalText = this.data.withTimestamp ? `[${timeStr}] ${direction}: ${text}` : `${direction}: ${text}`
|
||||||
try {
|
try {
|
||||||
// 同步输出到控制台,方便开发者在调试时查看
|
// 始终在控制台输出,便于调试;但若 direction 为 UI/DEBUG 则不加入页面日志列表以减少冗余
|
||||||
console.log(finalText)
|
console.log(finalText)
|
||||||
} catch (e) { /* ignore console errors */ }
|
} catch (e) { /* ignore console errors */ }
|
||||||
|
|
||||||
|
if (String(direction) === 'UI' || String(direction) === 'DEBUG') {
|
||||||
|
// 仅打印到控制台,不更新页面 logList
|
||||||
|
return
|
||||||
|
}
|
||||||
const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`
|
const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`
|
||||||
// 限制日志长度,避免无限增长导致 setData 负担和内存问题(保留最新200条)
|
// 限制日志长度,避免无限增长导致 setData 负担和内存问题(保留最新200条)
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
<view class="cu-item {{1==TabCur?'text-blue cur':''}}" bindtap="tabSelect" data-id="1">设备测试</view>
|
<view class="cu-item {{1==TabCur?'text-blue cur':''}}" bindtap="tabSelect" data-id="1">设备测试</view>
|
||||||
<view class="cu-item {{2==TabCur?'text-blue cur':''}}" bindtap="tabSelect" data-id="2">设备配置</view>
|
<view class="cu-item {{2==TabCur?'text-blue cur':''}}" bindtap="tabSelect" data-id="2">设备配置</view>
|
||||||
<view class="cu-item {{3==TabCur?'text-blue cur':''}}" bindtap="tabSelect" data-id="3">设备升级</view>
|
<view class="cu-item {{3==TabCur?'text-blue cur':''}}" bindtap="tabSelect" data-id="3">设备升级</view>
|
||||||
|
<view class="cu-item {{4==TabCur?'text-blue cur':''}}" bindtap="tabSelect" data-id="4">设备通信日志</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
@@ -25,12 +27,15 @@
|
|||||||
<button size="mini" type="primary" bindtap="onStartOta">OTA升级</button>
|
<button size="mini" type="primary" bindtap="onStartOta">OTA升级</button>
|
||||||
</view> -->
|
</view> -->
|
||||||
|
|
||||||
<!-- 顶部设备分类 -->
|
<!-- 顶部设备分类(含读雷达按钮) -->
|
||||||
<view class="grid">
|
<view class="grid-and-actions">
|
||||||
<view class="grid-item" wx:for="{{radarLights}}" wx:key="key">
|
<view class="grid">
|
||||||
<view class="circle {{item.colorClass}}"></view>
|
<view class="grid-item" wx:for="{{radarLights}}" wx:key="key">
|
||||||
<text class="label">{{item.label}}</text>
|
<view class="circle {{item.colorClass}}"></view>
|
||||||
|
<text class="label">{{item.label}}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 状态功能块:已改为测试按键按钮,点击发送测试按键命令 -->
|
<!-- 状态功能块:已改为测试按键按钮,点击发送测试按键命令 -->
|
||||||
@@ -75,7 +80,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 门磁/卫浴事件设置(替代原延时滑块) -->
|
<!-- 门磁/卫浴事件设置(替代原延时滑块) -->
|
||||||
<view class="cfg-card">
|
<view class="cfg-card tall-controls">
|
||||||
<view class="cfg-head head-row">
|
<view class="cfg-head head-row">
|
||||||
<text>门磁开廊灯事件设置</text>
|
<text>门磁开廊灯事件设置</text>
|
||||||
<view class="head-actions">
|
<view class="head-actions">
|
||||||
@@ -85,17 +90,13 @@
|
|||||||
<view class="form-row">
|
<view class="form-row">
|
||||||
<view class="form-inline">
|
<view class="form-inline">
|
||||||
<text class="label">开门延时</text>
|
<text class="label">开门延时</text>
|
||||||
<picker class="inline-picker" mode="selector" range="{{doorTriggerOptions}}" value="{{doorTriggerIndex}}" bindchange="onDoorTriggerPickerChange">
|
<input class="picker-text" placeholder="输入延时" value="{{doorTriggerDelay}}" type="number" data-field="triggerDelay" bindinput="onDoorFieldInput" />
|
||||||
<view class="picker-text">{{doorTriggerOptions[doorTriggerIndex]}}</view>
|
|
||||||
</picker>
|
|
||||||
<text class="label">单位</text>
|
<text class="label">单位</text>
|
||||||
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{doorTriggerUnitIndex}}" bindchange="onDoorUnitChange" data-field="triggerUnit">
|
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{doorTriggerUnitIndex}}" bindchange="onDoorUnitChange" data-field="triggerUnit">
|
||||||
<view class="picker-text">{{eventUnits[doorTriggerUnitIndex]}}</view>
|
<view class="picker-text">{{eventUnits[doorTriggerUnitIndex]}}</view>
|
||||||
</picker>
|
</picker>
|
||||||
<text class="label">关门延时</text>
|
<text class="label">关门延时</text>
|
||||||
<picker class="inline-picker" mode="selector" range="{{doorReleaseOptions}}" value="{{doorReleaseIndex}}" bindchange="onDoorReleasePickerChange">
|
<input class="picker-text" placeholder="输入延时" value="{{doorReleaseDelay}}" type="number" data-field="releaseDelay" bindinput="onDoorFieldInput" />
|
||||||
<view class="picker-text">{{doorReleaseOptions[doorReleaseIndex]}}</view>
|
|
||||||
</picker>
|
|
||||||
<text class="label">单位</text>
|
<text class="label">单位</text>
|
||||||
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{doorReleaseUnitIndex}}" bindchange="onDoorUnitChange" data-field="releaseUnit">
|
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{doorReleaseUnitIndex}}" bindchange="onDoorUnitChange" data-field="releaseUnit">
|
||||||
<view class="picker-text">{{eventUnits[doorReleaseUnitIndex]}}</view>
|
<view class="picker-text">{{eventUnits[doorReleaseUnitIndex]}}</view>
|
||||||
@@ -104,7 +105,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="cfg-card">
|
<view class="cfg-card tall-controls">
|
||||||
<view class="cfg-head head-row">
|
<view class="cfg-head head-row">
|
||||||
<text>卫浴雷达开卫浴灯事件设置</text>
|
<text>卫浴雷达开卫浴灯事件设置</text>
|
||||||
<view class="head-actions">
|
<view class="head-actions">
|
||||||
@@ -114,17 +115,13 @@
|
|||||||
<view class="form-row">
|
<view class="form-row">
|
||||||
<view class="form-inline">
|
<view class="form-inline">
|
||||||
<text class="label">触发延迟</text>
|
<text class="label">触发延迟</text>
|
||||||
<picker class="inline-picker" mode="selector" range="{{bathTriggerOptions}}" value="{{bathTriggerIndex}}" bindchange="onBathTriggerPickerChange">
|
<input class="picker-text" placeholder="输入延时" value="{{bathTriggerDelay}}" type="number" data-field="triggerDelay" bindinput="onBathFieldInput" />
|
||||||
<view class="picker-text">{{bathTriggerOptions[bathTriggerIndex]}}</view>
|
|
||||||
</picker>
|
|
||||||
<text class="label">单位</text>
|
<text class="label">单位</text>
|
||||||
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{bathTriggerUnitIndex}}" bindchange="onBathUnitChange" data-field="triggerUnit">
|
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{bathTriggerUnitIndex}}" bindchange="onBathUnitChange" data-field="triggerUnit">
|
||||||
<view class="picker-text">{{eventUnits[bathTriggerUnitIndex]}}</view>
|
<view class="picker-text">{{eventUnits[bathTriggerUnitIndex]}}</view>
|
||||||
</picker>
|
</picker>
|
||||||
<text class="label">释放延迟</text>
|
<text class="label">释放延迟</text>
|
||||||
<picker class="inline-picker" mode="selector" range="{{bathReleaseOptions}}" value="{{bathReleaseIndex}}" bindchange="onBathReleasePickerChange">
|
<input class="picker-text" placeholder="输入延时" value="{{bathReleaseDelay}}" type="number" data-field="releaseDelay" bindinput="onBathFieldInput" />
|
||||||
<view class="picker-text">{{bathReleaseOptions[bathReleaseIndex]}}</view>
|
|
||||||
</picker>
|
|
||||||
<text class="label">单位</text>
|
<text class="label">单位</text>
|
||||||
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{bathReleaseUnitIndex}}" bindchange="onBathUnitChange" data-field="releaseUnit">
|
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{bathReleaseUnitIndex}}" bindchange="onBathUnitChange" data-field="releaseUnit">
|
||||||
<view class="picker-text">{{eventUnits[bathReleaseUnitIndex]}}</view>
|
<view class="picker-text">{{eventUnits[bathReleaseUnitIndex]}}</view>
|
||||||
@@ -139,7 +136,7 @@
|
|||||||
<text class="title">通讯日志</text>
|
<text class="title">通讯日志</text>
|
||||||
<view class="log-actions">
|
<view class="log-actions">
|
||||||
<button class="radar-btn" size="mini" type="{{radarReading ? 'warn' : 'primary'}}" bindtap="toggleRadarRead">{{radarReading ? '停止读取' : '读雷达状态'}}</button>
|
<button class="radar-btn" size="mini" type="{{radarReading ? 'warn' : 'primary'}}" bindtap="toggleRadarRead">{{radarReading ? '停止读取' : '读雷达状态'}}</button>
|
||||||
<button class="clear-btn" size="mini" type="default" bindtap="onClearLogs">清空</button>
|
<button class="clear-btn" style="display:none;" size="mini" type="default" bindtap="onClearLogs">清空</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -163,7 +160,32 @@
|
|||||||
<button class="send-btn" size="mini" type="primary" bindtap="onSend">发送</button>
|
<button class="send-btn" size="mini" type="primary" bindtap="onSend">发送</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view class="log-scroll" scroll-y="true">
|
<!-- <scroll-view class="log-scroll" scroll-y="true">
|
||||||
|
<block wx:if="{{logList.length > 0}}">
|
||||||
|
<view class="log-item" wx:for="{{logList}}" wx:key="id">
|
||||||
|
<text class="log-time">{{item.time}}</text>
|
||||||
|
<text class="log-text">{{item.text}}</text>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
<view class="log-empty" wx:else>暂无日志记录</view>
|
||||||
|
</scroll-view> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- Tab: 设备通信日志(独立全屏日志视图) -->
|
||||||
|
<view wx:if="{{TabCur==4}}" class="content full-log-view">
|
||||||
|
<view class="cfg-card">
|
||||||
|
<view class="toolbar">
|
||||||
|
通信日志
|
||||||
|
<view class="toolbar-actions">
|
||||||
|
<button size="mini" type="default" bindtap="onExportLogs">导出日志</button>
|
||||||
|
<button size="mini" class="chinese-log-btn {{showChineseLogs ? 'on' : 'off'}}" bindtap="onToggleChineseLogs">{{showChineseLogs ? '中文日志*关' : '中文日志*开'}}</button>
|
||||||
|
<button size="mini" type="default" bindtap="onClearLogs">清空</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="log-card" style="display:flex; flex-direction:column; gap:8rpx; flex:1;">
|
||||||
|
<scroll-view class="log-scroll" scroll-y="true" style="flex:1;">
|
||||||
<block wx:if="{{logList.length > 0}}">
|
<block wx:if="{{logList.length > 0}}">
|
||||||
<view class="log-item" wx:for="{{logList}}" wx:key="id">
|
<view class="log-item" wx:for="{{logList}}" wx:key="id">
|
||||||
<text class="log-time">{{item.time}}</text>
|
<text class="log-time">{{item.time}}</text>
|
||||||
@@ -410,25 +432,42 @@
|
|||||||
|
|
||||||
<!-- Tab: 设备升级 -->
|
<!-- Tab: 设备升级 -->
|
||||||
<view wx:if="{{TabCur==3}}" class="content">
|
<view wx:if="{{TabCur==3}}" class="content">
|
||||||
<!-- OTA 升级卡片(顶部) -->
|
<!-- OTA 升级说明卡片 -->
|
||||||
<view class="cfg-card ota-card">
|
<view class="cfg-card ota-card">
|
||||||
<view class="cfg-head head-row">
|
<view class="cfg-head head-row">
|
||||||
<text>OTA 升级工具</text>
|
<text>设备升级说明</text>
|
||||||
<view class="head-actions">
|
<view class="head-actions">
|
||||||
<button size="mini" type="primary" bindtap="onStartOta">发送 OTA 升级命令</button>
|
<button size="mini" type="primary" bindtap="onStartOta">发送 OTA 升级命令</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="ota-steps">
|
|
||||||
<view class="step">1. 升级工具下载:
|
<view class="ota-article">
|
||||||
<button class="link-btn" size="mini" bindtap="onDownloadOtaTool">下载工具</button>
|
<view class="section">
|
||||||
|
<text class="section-title">前提条件</text>
|
||||||
|
<view class="section-body">
|
||||||
|
<text>1)手机已安装 “OTA升级工具”。</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<text class="section-title">操作步骤</text>
|
||||||
|
<view class="section-body steps">
|
||||||
|
<text>1. 在本页面点击“发送 OTA 升级命令”。</text>
|
||||||
|
<text>2. 打开已下载并安装的 OTA 升级工具。</text>
|
||||||
|
<text>3. 手机蓝牙扫描并连接名称为:<text class="mono ">OTAOTA_OTAOTA_OTA</text> 的设备。</text>
|
||||||
|
<text>4. 连接后依次点击 GETINFO → IMAGEA 。</text>
|
||||||
|
<text>5. 选择升级固件文件后,点击 START ,并选择芯片类型 “CH573” 开始升级。</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<text class="section-title">注意事项</text>
|
||||||
|
<view class="section-body">
|
||||||
|
<text>• 升级过程中请保持设备与手机的蓝牙连接稳定,勿中断电源。</text>
|
||||||
|
<text>• 升级失败后请重试一次,仍失败请联系技术支持并提供日志。</text>
|
||||||
|
<text>• 点击“下载工具”将复制下载链接到剪贴板,需在浏览器中打开并下载。</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="step">2. 安装软件</view>
|
|
||||||
<view class="step">3. 点击 OTA 按钮,发送 OTA 升级命令</view>
|
|
||||||
<view class="step">4. 打开下载的 【OTA 升级工具】</view>
|
|
||||||
<view class="step">5. 蓝牙连接名称:<text class="mono">OTAOTA_OTAOTA_OTA</text></view>
|
|
||||||
<view class="step">6. 连接后点击 <text class="mono">GETINFO</text>,再点击 <text class="mono">IMAGEA</text></view>
|
|
||||||
<view class="step">7. 选择升级固件后,点击最下面的 <text class="mono">START</text> 按钮,再选择芯片 “CH573” 开始升级</view>
|
|
||||||
<view class="note">下载链接: https://www.baidu.com/ (点击“下载工具”将复制链接)</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
.table.cond-item-content .tr { display:grid; grid-template-columns: 1.1fr 1.1fr 1.1fr 1fr 1fr 1.1fr; gap: 6rpx; padding: 6rpx 0; }
|
.table.cond-item-content .tr { display:grid; grid-template-columns: 1.1fr 1.1fr 1.1fr 1fr 1fr 1.1fr; gap: 6rpx; padding: 6rpx 0; }
|
||||||
/* pages/basics/BluetoothDebugging/B13page/B13page.wxss */
|
/* pages/basics/BluetoothDebugging/B13page/B13page.wxss */
|
||||||
.container { background: #f5f6f8; min-height: 100vh; display: flex; flex-direction: column; }
|
.container { background: #f5f6f8; min-height: 100vh; display: flex; flex-direction: column; }
|
||||||
|
.nav { padding: 4rpx 6rpx; }
|
||||||
|
.nav .flex { gap: 6rpx; }
|
||||||
|
.nav .cu-item { padding: 6rpx 8rpx; margin-right: 4rpx; font-size: 32rpx; }
|
||||||
.content { padding: 8rpx 10rpx 0rpx; box-sizing: border-box; display: flex; flex-direction: column; gap: 8rpx; flex: 1; }
|
.content { padding: 8rpx 10rpx 0rpx; box-sizing: border-box; display: flex; flex-direction: column; gap: 8rpx; flex: 1; }
|
||||||
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10rpx; margin-bottom: 12rpx; }
|
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10rpx; margin-bottom: 12rpx; }
|
||||||
.grid-item { background: #fff; border-radius: 14rpx; padding: 10rpx 0; display: flex; flex-direction: column; align-items: center; }
|
.grid-item { background: #fff; border-radius: 14rpx; padding: 10rpx 0; display: flex; flex-direction: column; align-items: center; }
|
||||||
@@ -44,11 +47,11 @@
|
|||||||
/* 日志头按钮组:读雷达 与 清空 按钮并列样式 */
|
/* 日志头按钮组:读雷达 与 清空 按钮并列样式 */
|
||||||
.log-head .log-actions { display:flex; align-items:center; gap: 8rpx; }
|
.log-head .log-actions { display:flex; align-items:center; gap: 8rpx; }
|
||||||
.log-head .log-actions .radar-btn {
|
.log-head .log-actions .radar-btn {
|
||||||
padding: 0 16rpx;
|
padding: 0 18rpx;
|
||||||
height: 48rpx;
|
height: 56rpx;
|
||||||
line-height: 48rpx;
|
line-height: 56rpx;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
font-size: 22rpx;
|
font-size: 26rpx;
|
||||||
}
|
}
|
||||||
.log-head .log-actions .clear-btn {
|
.log-head .log-actions .clear-btn {
|
||||||
padding: 0 16rpx;
|
padding: 0 16rpx;
|
||||||
@@ -215,6 +218,20 @@
|
|||||||
.cond-card .group-time .unit-picker .picker-text { width: 100rpx; height: 44rpx; padding: 0 8rpx; font-size: 22rpx; white-space: nowrap; }
|
.cond-card .group-time .unit-picker .picker-text { width: 100rpx; height: 44rpx; padding: 0 8rpx; font-size: 22rpx; white-space: nowrap; }
|
||||||
.cfg-actions { display:flex; justify-content:flex-end; margin-top: 10rpx; }
|
.cfg-actions { display:flex; justify-content:flex-end; margin-top: 10rpx; }
|
||||||
.action-row { display:flex; flex-wrap: wrap; gap: 12rpx; }
|
.action-row { display:flex; flex-wrap: wrap; gap: 12rpx; }
|
||||||
|
|
||||||
|
/* OTA 文案样式:用于设备升级的分段说明(前提条件 / 操作步骤 / 注意事项) */
|
||||||
|
.ota-article .section { margin-bottom: 12rpx; }
|
||||||
|
.ota-article .section-title { font-size: 36rpx; font-weight: 800; color:#222; margin-bottom: 10rpx; display: block; }
|
||||||
|
.ota-article .section-body { font-size: 32rpx; color: #444; padding-left: 8rpx; line-height: 46rpx; }
|
||||||
|
.ota-article .section-body text { display: block; margin-bottom: 10rpx; }
|
||||||
|
.ota-article .section-body .mono { font-family: monospace; background: #f5f6f8; padding: 2rpx 6rpx; border-radius: 6rpx; }
|
||||||
|
.ota-article .note { color: #d9534f; }
|
||||||
|
|
||||||
|
/* Full-screen log view for TabCur==4: ensure scroll area fills from toolbar bottom to screen bottom */
|
||||||
|
.full-log-view { display:flex; flex-direction: column; flex: 1; min-height: 0; }
|
||||||
|
.full-log-view .cfg-card { flex: none; }
|
||||||
|
.full-log-view .log-card { flex: 1; min-height: 0; }
|
||||||
|
.full-log-view .log-scroll { flex: 1; min-height: 0; max-height: none; }
|
||||||
.log-item { margin-bottom: 10rpx; }
|
.log-item { margin-bottom: 10rpx; }
|
||||||
.log-item:last-child { margin-bottom: 0; }
|
.log-item:last-child { margin-bottom: 0; }
|
||||||
.log-time { display: block; font-size: 22rpx; color: #9aa0a6; margin-bottom: 4rpx; }
|
.log-time { display: block; font-size: 22rpx; color: #9aa0a6; margin-bottom: 4rpx; }
|
||||||
@@ -251,3 +268,27 @@
|
|||||||
.inline-input { width: 90rpx; }
|
.inline-input { width: 90rpx; }
|
||||||
.inline-picker .picker-text { width: 80rpx; }
|
.inline-picker .picker-text { width: 80rpx; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 增大门磁/卫浴卡片中的控件高度,便于触控 */
|
||||||
|
.cfg-card.tall-controls .picker-text,
|
||||||
|
.cfg-card.tall-controls input.picker-text,
|
||||||
|
.cfg-card.tall-controls .inline-picker .picker-text,
|
||||||
|
.cfg-card.tall-controls .inline-input {
|
||||||
|
height: 56rpx !important;
|
||||||
|
line-height: 56rpx !important;
|
||||||
|
font-size: 26rpx !important;
|
||||||
|
padding: 0 12rpx !important;
|
||||||
|
}
|
||||||
|
.cfg-card.tall-controls .form-inline { gap: 14rpx; }
|
||||||
|
.cfg-card.tall-controls .label { font-size: 24rpx; }
|
||||||
|
|
||||||
|
/* 全屏日志视图:增大工具栏中“清空”按钮宽度,提升点击目标 */
|
||||||
|
.full-log-view .toolbar .toolbar-actions button:last-child {
|
||||||
|
min-width: 150rpx;
|
||||||
|
padding: 0 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中文日志开关按钮:开-绿色,关-红色 */
|
||||||
|
.chinese-log-btn { padding: 0 12rpx; height: 54rpx; line-height: 54rpx; border-radius: 10rpx; font-size: 24rpx; color: #ffffff; border: none; }
|
||||||
|
.chinese-log-btn.on { background: #ff3b30 !important; }
|
||||||
|
.chinese-log-btn.off { background: #21c161 !important; }
|
||||||
|
|||||||
@@ -141,27 +141,65 @@ Page({
|
|||||||
// 先取消旧的发现监听,避免多次注册造成干扰
|
// 先取消旧的发现监听,避免多次注册造成干扰
|
||||||
this.teardownDeviceFoundListener()
|
this.teardownDeviceFoundListener()
|
||||||
this._foundCount = 0
|
this._foundCount = 0
|
||||||
|
const now = Date.now()
|
||||||
|
// 防护:避免短时间内频繁触发扫描(系统限制)
|
||||||
|
if (this._lastScanAt && now - this._lastScanAt < 2000) {
|
||||||
|
try { this.appendLog && this.appendLog('WARN', 'skip startBluetoothDevicesDiscovery: throttled') } catch (e) {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._lastScanAt = now
|
||||||
console.info('[BLE] start scan, prefix:', prefix || 'ALL')
|
console.info('[BLE] start scan, prefix:', prefix || 'ALL')
|
||||||
// 先停止可能已有的搜索,待停止完成后再启动,避免竞态
|
|
||||||
wx.stopBluetoothDevicesDiscovery({
|
const doStart = () => {
|
||||||
complete: () => {
|
// 先停止可能已有的搜索,待停止完成后再启动,避免竞态
|
||||||
wx.startBluetoothDevicesDiscovery({
|
wx.stopBluetoothDevicesDiscovery({
|
||||||
allowDuplicatesKey: true, // 允许重复上报,提升二次搜索的发现率
|
complete: () => {
|
||||||
success: () => {
|
wx.startBluetoothDevicesDiscovery({
|
||||||
|
allowDuplicatesKey: true, // 允许重复上报,提升二次搜索的发现率
|
||||||
|
success: () => {
|
||||||
|
this.setupDeviceFoundListener(prefix)
|
||||||
|
// 定时停止,避免长时间占用
|
||||||
|
setTimeout(() => {
|
||||||
|
this.stopBluetoothDiscovery()
|
||||||
|
}, 6000)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('开始搜索蓝牙设备失败', err)
|
||||||
|
// 若为系统提示搜索过于频繁,可稍后重试一次
|
||||||
|
const code = err && (err.errCode || (err.errMsg && Number((err.errMsg.match(/\d+/)||[])[0])))
|
||||||
|
if (code === 10008) {
|
||||||
|
try { this.appendLog && this.appendLog('WARN', 'startBluetoothDevicesDiscovery failed: scanning too frequently, retrying shortly') } catch (e) {}
|
||||||
|
setTimeout(() => { try { doStart() } catch (e) {} }, 1500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wx.hideLoading()
|
||||||
|
wx.showToast({ title: '搜索失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先查询适配器状态,若系统正在扫描则直接注册监听并返回
|
||||||
|
if (typeof wx.getBluetoothAdapterState === 'function') {
|
||||||
|
wx.getBluetoothAdapterState({
|
||||||
|
success: (res) => {
|
||||||
|
if (res && res.discovering) {
|
||||||
|
try { this.appendLog && this.appendLog('CFG', 'adapter already discovering, attach listener') } catch (e) {}
|
||||||
this.setupDeviceFoundListener(prefix)
|
this.setupDeviceFoundListener(prefix)
|
||||||
// 定时停止,避免长时间占用
|
|
||||||
setTimeout(() => {
|
|
||||||
this.stopBluetoothDiscovery()
|
|
||||||
}, 6000)
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('开始搜索蓝牙设备失败', err)
|
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
wx.showToast({ title: '搜索失败', icon: 'none' })
|
wx.showToast({ title: '正在搜索中', icon: 'none' })
|
||||||
|
return
|
||||||
}
|
}
|
||||||
})
|
doStart()
|
||||||
}
|
},
|
||||||
})
|
fail: () => {
|
||||||
|
doStart()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
doStart()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setupDeviceFoundListener(prefix) {
|
setupDeviceFoundListener(prefix) {
|
||||||
@@ -262,6 +300,7 @@ Page({
|
|||||||
? `${base}&serviceId=${encodeURIComponent(svc)}&txCharId=${encodeURIComponent(tx)}&rxCharId=${encodeURIComponent(rx)}`
|
? `${base}&serviceId=${encodeURIComponent(svc)}&txCharId=${encodeURIComponent(tx)}&rxCharId=${encodeURIComponent(rx)}`
|
||||||
: base
|
: base
|
||||||
console.log('navigateTo:', withParams)
|
console.log('navigateTo:', withParams)
|
||||||
|
try { this._navigatingToB13 = true } catch (e) { /* ignore */ }
|
||||||
wx.navigateTo({ url: withParams })
|
wx.navigateTo({ url: withParams })
|
||||||
|
|
||||||
|
|
||||||
@@ -305,30 +344,49 @@ Page({
|
|||||||
}
|
}
|
||||||
|
|
||||||
wx.showLoading({ title: '连接中...', mask: true })
|
wx.showLoading({ title: '连接中...', mask: true })
|
||||||
// 使用 BLE 直连,不再使用模拟延迟
|
// 在尝试 createBLEConnection 前确保适配器已打开(处理息屏后 closeBluetoothAdapter 场景)
|
||||||
wx.createBLEConnection({
|
this.ensureBluetoothReady()
|
||||||
deviceId: device.id,
|
.then(() => {
|
||||||
success: () => {
|
// 使用 BLE 直连
|
||||||
const list = this.data.deviceList.map((d, i) => ({ ...d, connected: i === index }))
|
wx.createBLEConnection({
|
||||||
this.setData({ deviceList: list, currentDeviceId: device.id })
|
deviceId: device.id,
|
||||||
// 设置MTU为256,提升传输效率
|
success: () => {
|
||||||
if (typeof wx.setBLEMTU === 'function') {
|
const list = this.data.deviceList.map((d, i) => ({ ...d, connected: i === index }))
|
||||||
wx.setBLEMTU({
|
this.setData({ deviceList: list, currentDeviceId: device.id })
|
||||||
deviceId: device.id,
|
// 设置MTU为256,提升传输效率(若支持)
|
||||||
mtu: 500,
|
if (typeof wx.setBLEMTU === 'function') {
|
||||||
fail: () => console.warn('[BLE] set MTU 256 failed'),
|
wx.setBLEMTU({
|
||||||
success: () => console.info('[BLE] set MTU 256 success')
|
deviceId: device.id,
|
||||||
})
|
mtu: 500,
|
||||||
}
|
fail: () => console.warn('[BLE] set MTU 256 failed'),
|
||||||
// 连接成功后发现服务与特征
|
success: () => console.info('[BLE] set MTU 256 success')
|
||||||
this.discoverBleChannels(device)
|
})
|
||||||
},
|
}
|
||||||
fail: (err) => {
|
// 连接成功后发现服务与特征
|
||||||
|
this.discoverBleChannels(device)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
wx.hideLoading()
|
||||||
|
console.error('BLE 连接失败', err)
|
||||||
|
const errmsg = (err && (err.errMsg || err.message)) || ''
|
||||||
|
const isAlreadyConnected = errmsg.indexOf('already connect') >= 0 || errmsg.indexOf('already connected') >= 0 || (err && (err.errCode === -1 || err.errno === 1509007))
|
||||||
|
if (isAlreadyConnected) {
|
||||||
|
try { this.appendLog && this.appendLog('CFG', 'createBLEConnection: already connected, treating as connected') } catch (e) {}
|
||||||
|
const list = this.data.deviceList.map((d, i) => ({ ...d, connected: i === index }))
|
||||||
|
this.setData({ deviceList: list, currentDeviceId: device.id })
|
||||||
|
// 继续发现服务与特征以恢复页面状态
|
||||||
|
try { this.discoverBleChannels(device) } catch (e) {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wx.showToast({ title: '连接失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
console.error('BLE 连接失败', err)
|
console.error('蓝牙未初始化,无法连接', err)
|
||||||
wx.showToast({ title: '连接失败', icon: 'none' })
|
wx.showToast({ title: '蓝牙未初始化', icon: 'none' })
|
||||||
}
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 发现包含 FFE1(写) / FFE2(订阅) 的服务与特征,并启用 FFE2 通知
|
// 发现包含 FFE1(写) / FFE2(订阅) 的服务与特征,并启用 FFE2 通知
|
||||||
@@ -383,8 +441,9 @@ Page({
|
|||||||
if (this.data.activeTab === 'W13') {
|
if (this.data.activeTab === 'W13') {
|
||||||
const devName = device.name || 'W13设备'
|
const devName = device.name || 'W13设备'
|
||||||
const url = `/pages/basics/BluetoothDebugging/B13page/B13page?DevName=${encodeURIComponent(devName)}&mac=${encodeURIComponent(deviceId)}&serviceId=${encodeURIComponent(serviceId)}&txCharId=${encodeURIComponent(ffe1.uuid)}&rxCharId=${encodeURIComponent(ffe2.uuid)}`
|
const url = `/pages/basics/BluetoothDebugging/B13page/B13page?DevName=${encodeURIComponent(devName)}&mac=${encodeURIComponent(deviceId)}&serviceId=${encodeURIComponent(serviceId)}&txCharId=${encodeURIComponent(ffe1.uuid)}&rxCharId=${encodeURIComponent(ffe2.uuid)}`
|
||||||
console.log(url)
|
console.log(url)
|
||||||
wx.navigateTo({ url })
|
try { this._navigatingToB13 = true } catch (e) { /* ignore */ }
|
||||||
|
wx.navigateTo({ url })
|
||||||
// this.sendFixedCommand(deviceId, serviceId, ffe1.uuid)
|
// this.sendFixedCommand(deviceId, serviceId, ffe1.uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -482,6 +541,11 @@ Page({
|
|||||||
|
|
||||||
// 断开当前连接设备(如果有真实连接)
|
// 断开当前连接设备(如果有真实连接)
|
||||||
disconnectCurrentDevice() {
|
disconnectCurrentDevice() {
|
||||||
|
// 如果正在导航到 B13 页面,避免主动断开连接以保持会话
|
||||||
|
if (this._navigatingToB13) {
|
||||||
|
try { this.appendLog && this.appendLog('CFG', 'skip disconnectCurrentDevice during navigate to B13') } catch (e) {}
|
||||||
|
return
|
||||||
|
}
|
||||||
const idx = this.data.deviceList.findIndex(d => d.connected)
|
const idx = this.data.deviceList.findIndex(d => d.connected)
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
// 标记断开状态
|
// 标记断开状态
|
||||||
@@ -530,5 +594,105 @@ Page({
|
|||||||
this.loadDevicesByTab(this.data.activeTab)
|
this.loadDevicesByTab(this.data.activeTab)
|
||||||
// 同步执行一次蓝牙搜索(按 W13 过滤规则)
|
// 同步执行一次蓝牙搜索(按 W13 过滤规则)
|
||||||
this.searchBluetooth()
|
this.searchBluetooth()
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
// 返回到此页面时,清理导航标记
|
||||||
|
if (this._navigatingToB13) {
|
||||||
|
this._navigatingToB13 = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try { this.appendLog && this.appendLog('CFG', 'onShow: resume, ensuring adapter and forcing discovery') } catch (e) {}
|
||||||
|
// 息屏唤醒后可能适配器被关闭,先确保打开后短延迟再搜索一次
|
||||||
|
this.ensureBluetoothReady()
|
||||||
|
.then(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
this.startBluetoothDevicesDiscovery(this.data.activeTab)
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
}, 300)
|
||||||
|
|
||||||
|
// 尝试获取系统已连接设备,优先恢复之前连接的设备显示(防止已连接但不广播的设备无法被扫描到)
|
||||||
|
try {
|
||||||
|
const svc = this.data.currentServiceId
|
||||||
|
const isW13 = this.data.activeTab === 'W13'
|
||||||
|
const prefix = this.data.activeTab
|
||||||
|
const matchByTab = (name) => {
|
||||||
|
const n = name || ''
|
||||||
|
if (isW13) return /^BLV_(W13|C13)_.+$/i.test(n)
|
||||||
|
return prefix ? n.startsWith(prefix) : true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof wx.getConnectedBluetoothDevices === 'function' && svc) {
|
||||||
|
wx.getConnectedBluetoothDevices({ services: [svc], success: (res) => {
|
||||||
|
const devices = (res && res.devices) || []
|
||||||
|
if (devices.length) {
|
||||||
|
// 将已连接设备合并到列表并按过滤规则筛选
|
||||||
|
const list = [...this.data.deviceList]
|
||||||
|
devices.forEach(d => {
|
||||||
|
const name = d.name || d.localName || ''
|
||||||
|
if (!matchByTab(name)) return
|
||||||
|
const idx = list.findIndex(x => x.id === d.deviceId)
|
||||||
|
const mapped = { id: d.deviceId, name, mac: d.deviceId, connected: true, RSSI: d.RSSI || 0, serviceUUIDs: d.serviceUUIDs || [] }
|
||||||
|
if (idx >= 0) list[idx] = { ...list[idx], ...mapped }
|
||||||
|
else list.unshift(mapped)
|
||||||
|
})
|
||||||
|
this.setData({ deviceList: list })
|
||||||
|
try { this.appendLog && this.appendLog('CFG', 'restored connected devices from system') } catch (e) {}
|
||||||
|
}
|
||||||
|
}})
|
||||||
|
} else if (typeof wx.getBluetoothDevices === 'function') {
|
||||||
|
// 作为兜底,查询最近缓存的设备并按过滤规则合并
|
||||||
|
wx.getBluetoothDevices({ success: (res) => {
|
||||||
|
const devices = (res && res.devices) || []
|
||||||
|
if (devices.length) {
|
||||||
|
const list = [...this.data.deviceList]
|
||||||
|
devices.forEach(d => {
|
||||||
|
const name = d.name || d.localName || ''
|
||||||
|
if (!matchByTab(name)) return
|
||||||
|
const idx = list.findIndex(x => x.id === d.deviceId)
|
||||||
|
const mapped = { id: d.deviceId, name, mac: d.deviceId, connected: !!d.connected, RSSI: d.RSSI || 0, serviceUUIDs: d.serviceUUIDs || [] }
|
||||||
|
if (idx >= 0) list[idx] = { ...list[idx], ...mapped }
|
||||||
|
else list.push(mapped)
|
||||||
|
})
|
||||||
|
this.setData({ deviceList: list })
|
||||||
|
}
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
try { this.appendLog && this.appendLog('WARN', 'onShow ensureBluetoothReady failed') } catch (e) {}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onHide() {
|
||||||
|
// 如果正在导航到 B13 页面,跳过 onHide 的断开/重置流程,保留连接
|
||||||
|
if (this._navigatingToB13) {
|
||||||
|
try { this.appendLog && this.appendLog('CFG', 'onHide skipped due to navigation to B13') } catch (e) {}
|
||||||
|
// 清理标记,后续返回时 onShow 会再次执行
|
||||||
|
this._navigatingToB13 = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 停止发现,避免后台扫描
|
||||||
|
if (typeof wx.stopBluetoothDevicesDiscovery === 'function') {
|
||||||
|
wx.stopBluetoothDevicesDiscovery({ complete: () => { try { this.appendLog && this.appendLog('CFG', 'stopBluetoothDevicesDiscovery onHide') } catch (e) {} } })
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 断开当前连接(如果有)
|
||||||
|
this.disconnectCurrentDevice()
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 关闭蓝牙适配器以重置底层状态(部分 Android 机型息屏后需要此步骤)
|
||||||
|
if (typeof wx.closeBluetoothAdapter === 'function') {
|
||||||
|
wx.closeBluetoothAdapter({ complete: () => { try { this.appendLog && this.appendLog('CFG', 'closeBluetoothAdapter called onHide') } catch (e) {} } })
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
24075
utils/ecUnicodeToGBK.js
Normal file
24075
utils/ecUnicodeToGBK.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user