<template>
<view class="container">
<!-- 蓝牙连接状态(仅文档设备状态) -->
<view class="status-bar" :class="statusClass">
<text class="status-text">{{ currentStatus }}</text>
</view>
<!-- 蓝牙基础操作(仅文档关联功能) -->
<view class="bt-operation">
<button
class="btn-scan"
:disabled="currentStatus === BT_STATUS.CONNECTING"
@click="handleScanConnect"
>
{{ currentStatus === BT_STATUS.DISCONNECTED ? '扫描连接设备' : '重新扫描' }}
</button>
<button
class="btn-disconnect"
v-if="currentStatus === BT_STATUS.CONNECTED"
@click="handleDisconnect"
>
断开连接
</button>
</view>
<!-- 核心功能按钮(基于文档) -->
<view class="func-btns" v-if="currentStatus === BT_STATUS.CONNECTED">
<button class="func-btn" @click="showSamplingConfig = !showSamplingConfig">
{{ showSamplingConfig ? '隐藏参数配置' : '采样参数配置' }}
</button>
<button class="func-btn" @click="handleInspectionFeature">巡检特征值测量</button>
<button class="func-btn" @click="handleWaveform">波形</button>
<button class="func-btn" @click="handleRemoteTemperatureMeasurement">远距离测温</button>
</view>
<!-- 采样参数配置区域 -->
<view class="config-area" v-if="showSamplingConfig && currentStatus === BT_STATUS.CONNECTED">
<view class="config-title">采样参数配置</view>
<!-- 采样系数选择 -->
<view class="config-item">
<text class="config-label">采样系数:</text>
<picker @change="onSamplingFactorChange" :value="samplingFactorIndex" :range="samplingFactors">
<view class="picker">
{{ samplingFactors[samplingFactorIndex] }}
</view>
</picker>
</view>
<!-- 高通滤波系数选择 -->
<view class="config-item">
<text class="config-label">高通滤波系数:</text>
<picker @change="onHighPassFilterChange" :value="highPassFilterIndex" :range="highPassFilters">
<view class="picker">
{{ highPassFilters[highPassFilterIndex] }}
</view>
</picker>
</view>
<!-- 参数选择提示 -->
<view class="config-info">
<text class="info-text">当前配置:采样率 {{ currentSamplingRate }}Hz,截止频率 {{ currentCutoffFreq }}Hz</text>
</view>
</view>
<!-- 巡检特征值展示区(默认Z轴3个参数,文档READ通道推送) -->
<view class="inspection-area" v-if="showInspectionArea">
<view class="area-title">巡检特征值(设备采样完成后推送,默认Z轴)</view>
<!-- 交互状态提示(响应/采样/推送) -->
<view class="interact-status" :class="interactStatusClass">
<text>{{ interactStatusText }}</text>
</view>
<!-- 数据展示表格 -->
<view class="feature-table" v-if="inspectionData.length > 0">
<view class="table-header">
<view class="table-col">参数类型</view>
<view class="table-col">数值</view>
<view class="table-col">单位</view>
</view>
<view class="table-body">
<view class="table-row" v-for="(item, idx) in inspectionData" :key="idx">
<view class="table-col">{{ item.type }}</view>
<view class="table-col">
{{ item.value !== undefined && item.value !== null ? item.value.toFixed(4) :
(item.zValue !== null && item.zValue !== undefined && !isNaN(item.zValue) ? item.zValue.toFixed(4) : '-') }}
</view>
<view class="table-col">{{ item.unit }}</view>
</view>
</view>
</view>
<!-- 原始数据调试区域 -->
<view class="debug-area" v-if="rawData.length > 0">
<view class="debug-title">原始数据(调试用)</view>
<text class="debug-data">{{ rawDataText }}</text>
</view>
</view>
</view>
</template>
<script>
// 仅导入《蓝牙服务配置.docx》的参数常量,无其他依赖
import { ref, computed, onUnmounted } from 'vue';
import { BT_CONFIG, BT_STATUS } from '@/common/bt-config.js';
export default {
setup() {
// 1. 基础状态(仅关联《蓝牙服务配置.docx》设备)
const currentStatus = ref(BT_STATUS.DISCONNECTED);
const deviceList = ref([]);
const connectedDeviceId = ref('');
const showInspectionArea = ref(false); // 数据展示区开关
const showSamplingConfig = ref(false); // 采样参数配置区开关
// 采样参数配置
const samplingFactors = ref([2, 3, 4, 5, 6, 8, 10, 15, 18, 20, 26, 30, 35, 40, 50, 60, 80, 100, 101, 255]);
const highPassFilters = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 50, 77, 102, 128, 154, 179, 205, 230, 255]);
const samplingFactorIndex = ref(0);
const highPassFilterIndex = ref(3); // 默认值为4
// 采样参数映射表
const samplingConfigMap = {
2: { rate: 13333, maxPoints: 16384 },
3: { rate: 8888.67, maxPoints: 16384 },
4: { rate: 6666.5, maxPoints: 16384 },
5: { rate: 5333.2, maxPoints: 16384 },
6: { rate: 4444.33, maxPoints: 16384 },
8: { rate: 3333.25, maxPoints: 16384 },
10: { rate: 2666.6, maxPoints: 16384 },
15: { rate: 1777.73, maxPoints: 16384 },
18: { rate: 1481.44, maxPoints: 16384 },
20: { rate: 1333.3, maxPoints: 16384 },
26: { rate: 1025.62, maxPoints: 8192 },
30: { rate: 888.87, maxPoints: 8192 },
35: { rate: 761.89, maxPoints: 8192 },
40: { rate: 666.65, maxPoints: 8192 },
50: { rate: 533.32, maxPoints: 8192 },
60: { rate: 444.43, maxPoints: 4096 },
80: { rate: 333.33, maxPoints: 4096 },
100: { rate: 266.66, maxPoints: 4096 },
101: { rate: 264.09, maxPoints: 4096 },
255: { rate: 104.57, maxPoints: 4096 }
};
// 高通滤波系数映射表
const highPassFilterMap = {
1: 2.604,
2: 5.208,
3: 7.812,
4: 10.416,
5: 13.021,
6: 15.625,
7: 18.229,
8: 20.833,
9: 23.437,
10: 26.041,
12: 31.249,
15: 39.062,
20: 52.082,
25: 65.103,
50: 130.205,
77: 200.516,
102: 265.618,
128: 333.325,
154: 401.032,
179: 466.134,
205: 533.841,
230: 598.943,
255: 664.046
};
// 2. 设备交互状态(匹配“请求→响应→采样→推送”逻辑)
const interactStatusText = ref(''); // 交互状态提示(如“等待设备响应...”)
const interactStatusType = ref(''); // 状态类型:waiting/success/error
// 3. 测量数据存储
const inspectionData = ref([]);
// 存储当前请求序号(匹配设备响应/推送的序号,确保数据对应)
let currentReqSeq = 0;
// 存储当前测量类型
let currentMeasurementType = '';
// 存储原始数据(用于调试)
const rawData = ref([]);
// 存储实际连接的服务和特征值信息
let connectedServiceInfo = {
serviceId: '',
writeCharId: '',
readCharId: ''
};
// 原始数据文本显示
const rawDataText = computed(() => {
return rawData.value.length > 0 ?
Array.from(rawData.value).map(b => '0x' + b.toString(16).padStart(2, '0').toUpperCase()).join(', ') :
'';
});
// 当前采样率计算
const currentSamplingRate = computed(() => {
const factor = samplingFactors.value[samplingFactorIndex.value];
return samplingConfigMap[factor]?.rate || 13333;
});
// 当前截止频率计算
const currentCutoffFreq = computed(() => {
const filter = highPassFilters.value[highPassFilterIndex.value];
return highPassFilterMap[filter] || 10.416;
});
// 采样系数变更处理
const onSamplingFactorChange = (e) => {
samplingFactorIndex.value = e.detail.value;
uni.showToast({
title: `已设置采样系数: ${samplingFactors.value[samplingFactorIndex.value]}`,
icon: 'none'
});
};
// 高通滤波系数变更处理
const onHighPassFilterChange = (e) => {
highPassFilterIndex.value = e.detail.value;
uni.showToast({
title: `已设置高通滤波系数: ${highPassFilters.value[highPassFilterIndex.value]}`,
icon: 'none'
});
};
// 状态样式(仅UI区分)
const statusClass = computed(() => ({
'status-bar': true,
'status-disconnected': currentStatus.value === BT_STATUS.DISCONNECTED,
'status-connecting': currentStatus.value === BT_STATUS.CONNECTING,
'status-connected': currentStatus.value === BT_STATUS.CONNECTED
}));
// 交互状态样式(提示响应/采样/推送状态)
const interactStatusClass = computed(() => ({
'interact-status': true,
'status-waiting': interactStatusType.value === 'waiting',
'status-success': interactStatusType.value === 'success',
'status-error': interactStatusType.value === 'error',
'status-warning': interactStatusType.value === 'warning'
}));
// ---------------------- 基础蓝牙逻辑(严格文档参数) ----------------------
/**
* 扫描设备(仅匹配《蓝牙服务配置.docx》服务ID:0000fff0的设备)
*/
const handleScanConnect = async () => {
if (currentStatus.value === BT_STATUS.CONNECTING) return;
try {
// 打开蓝牙适配器(文档功能前置操作)
await uni.openBluetoothAdapter({
fail: (err) => {
uni.showToast({ title: err.errCode === 10001 ? '请开启蓝牙' : `初始化失败:${err.errMsg}`, icon: 'none' });
throw err;
}
});
// 扫描设备(过滤无效设备)
deviceList.value = [];
currentStatus.value = BT_STATUS.CONNECTING;
await uni.startBluetoothDevicesDiscovery({ allowDuplicatesKey: false });
// 监听扫描结果(仅保留有效设备)
uni.onBluetoothDeviceFound((res) => {
res.devices.forEach(device => {
if (device.deviceId && (device.name || device.localName)) {
!deviceList.value.some(item => item.deviceId === device.deviceId) && deviceList.value.push({
deviceId: device.deviceId,
name: device.name || device.localName
});
}
});
});
// 3秒后弹窗选择文档关联设备
setTimeout(async () => {
await uni.stopBluetoothDevicesDiscovery();
currentStatus.value = BT_STATUS.DISCONNECTED;
if (deviceList.value.length === 0) {
uni.showToast({ title: '未扫描到设备', icon: 'none' });
return;
}
// 原生弹窗选择设备(仅提示文档服务ID)
uni.showActionSheet({
title: `选择设备`,
itemList: deviceList.value.map(d => d.name),
success: (res) => selectDevice(deviceList.value[res.tapIndex])
});
}, 2000);
} catch (err) {
currentStatus.value = BT_STATUS.DISCONNECTED;
}
};
/**
* 连接设备(文档MTU=131,稳定连接后设置)
*/
const selectDevice = async (device) => {
if (!device.deviceId) return;
currentStatus.value = BT_STATUS.CONNECTING;
try {
// 发起连接(仅《蓝牙服务配置.docx》关联设备)
await uni.createBLEConnection({
deviceId: device.deviceId,
fail: (err) => {
uni.showToast({ title: `连接失败:${err.errMsg}`, icon: 'none' });
currentStatus.value = BT_STATUS.DISCONNECTED;
throw err;
}
});
const bleAdapter = {
// 存储当前注册的监听器
listeners: {},
// 注册特征值变化监听器
onCharacteristicValueChange(callback) {
// 保存监听器引用
this.listeners.valueChange = callback;
// 调用原生API
uni.onBLECharacteristicValueChange(callback);
},
// 移除特征值变化监听器
offCharacteristicValueChange() {
// 优先使用原生方法
if (typeof uni.offBLECharacteristicValueChange === 'function') {
uni.offBLECharacteristicValueChange();
} else {
// 不支持时,通过覆盖监听器实现"移除"效果
if (this.listeners.valueChange) {
uni.onBLECharacteristicValueChange(() => {}); // 覆盖为空函数
this.listeners.valueChange = null; // 清空引用
}
}
}
};
// 在代码中使用封装后的方法
// 注册监听器时
bleAdapter.onCharacteristicValueChange((res) => {
parseDeviceData(res.value);
});
// 需要移除监听器时
bleAdapter.offCharacteristicValueChange();
// 监听连接状态,稳定后设置MTU(文档最大值131)
const connectionListener = (res) => {
if (res.deviceId !== device.deviceId) return;
if (res.connected) {
connectedDeviceId.value = device.deviceId;
currentStatus.value = BT_STATUS.CONNECTED;
uni.showToast({ title: `已连接:${device.name}` });
// 设置MTU=131(严格遵循《蓝牙服务配置.docx》)
uni.setBLEMTU({
deviceId: device.deviceId,
mtu: BT_CONFIG.MAX_MTU,
success: () => console.log(`MTU=${BT_CONFIG.MAX_MTU}(文档要求)`),
fail: (err) => uni.showToast({ title: `MTU设置失败:${err.errMsg}`, icon: 'none' })
});
// 启用文档READ通道通知(接收设备响应/推送,ID:0000fff1)
enableReadNotify(device.deviceId);
uni.offBLEConnectionStateChange(connectionListener);
} else {
uni.showToast({ title: '设备断开连接', icon: 'none' });
currentStatus.value = BT_STATUS.DISCONNECTED;
uni.offBLEConnectionStateChange(connectionListener);
}
};
uni.onBLEConnectionStateChange(connectionListener);
} catch (err) {
currentStatus.value = BT_STATUS.DISCONNECTED;
}
};
/**
* 启用READ通道通知(文档READ_ID=0000fff2,接收响应/推送)
*/
const enableReadNotify = async (deviceId) => {
try {
// 获取设备所有服务并显示(调试信息)
const serviceRes = await uni.getBLEDeviceServices({ deviceId });
console.log('设备支持的所有服务ID:', serviceRes.services.map(s => s.uuid));
// 显示所有服务ID的弹窗提示(调试用)
uni.showModal({
title: '设备服务信息',
content: `发现 ${serviceRes.services.length} 个服务:\n${serviceRes.services.map(s => s.uuid).join('\n')}\n\n正在尝试连接文档服务...`,
showCancel: false
});
// 重置连接信息
connectedServiceInfo = {
serviceId: '',
writeCharId: '',
readCharId: ''
};
// 匹配《蓝牙服务配置.docx》服务ID:0000fff0
// 处理SERVICE_ID可能是数组的情况
let targetService = null;
if (Array.isArray(BT_CONFIG.SERVICE_ID)) {
// 如果是数组,查找数组中任一匹配的服务ID
for (const serviceId of BT_CONFIG.SERVICE_ID) {
targetService = serviceRes.services.find(
s => s.uuid.toLowerCase() === serviceId.toLowerCase()
);
if (targetService) break;
}
} else {
// 如果是字符串,直接查找匹配
targetService = serviceRes.services.find(
s => s.uuid.toLowerCase() === BT_CONFIG.SERVICE_ID.toLowerCase()
);
}
if (!targetService) {
// 不抛出错误,而是记录警告并尝试使用第一个可用服务
console.warn(`未找到文档服务ID:${BT_CONFIG.SERVICE_ID},尝试使用第一个可用服务`);
if (serviceRes.services.length > 0) {
// 使用第一个可用服务
const fallbackService = serviceRes.services[0];
console.log(`使用备选服务ID: ${fallbackService.uuid}`);
// 尝试获取该服务的所有特征值
const characteristics = await uni.getBLEDeviceCharacteristics({
deviceId,
serviceId: fallbackService.uuid
});
// 详细记录所有特征值及其属性
const charDetails = characteristics.characteristics.map(c => ({
uuid: c.uuid,
properties: c.properties
}));
console.log('备选服务的特征值详情:', charDetails);
// 查找可读和可写特征值
const readChar = findSuitableReadCharacteristic(characteristics.characteristics);
const writeChar = findSuitableWriteCharacteristic(characteristics.characteristics);
// 先启用读取特征值
if (readChar) {
await enableCharNotify(deviceId, fallbackService.uuid, readChar.uuid);
connectedServiceInfo.serviceId = fallbackService.uuid;
connectedServiceInfo.readCharId = readChar.uuid;
console.log(`已设置读取特征值: ${readChar.uuid}`);
}
// 设置写入特征值
if (writeChar) {
connectedServiceInfo.writeCharId = writeChar.uuid;
console.log(`已设置写入特征值: ${writeChar.uuid}`);
}
// 显示特征值信息
showCharacteristicInfo(fallbackService.uuid, charDetails);
return;
}
// 如果没有可用服务或特征值,显示警告但保持连接
uni.showToast({
title: `未找到文档服务ID,但设备已连接`,
icon: 'none',
duration: 3000
});
} else {
// 找到文档服务,获取所有特征值
const characteristics = await uni.getBLEDeviceCharacteristics({
deviceId,
serviceId: targetService.uuid
});
// 详细记录所有特征值及其属性
const charDetails = characteristics.characteristics.map(c => ({
uuid: c.uuid,
properties: c.properties
}));
console.log('文档服务的特征值详情:', charDetails);
// 查找可读和可写特征值
const readChar = findSuitableReadCharacteristic(characteristics.characteristics);
const writeChar = findSuitableWriteCharacteristic(characteristics.characteristics);
// 先启用读取特征值
if (readChar) {
await enableCharNotify(deviceId, targetService.uuid, readChar.uuid);
connectedServiceInfo.serviceId = targetService.uuid;
connectedServiceInfo.readCharId = readChar.uuid;
console.log(`已设置读取特征值: ${readChar.uuid}`);
}
// 设置写入特征值
if (writeChar) {
connectedServiceInfo.writeCharId = writeChar.uuid;
console.log(`已设置写入特征值: ${writeChar.uuid}`);
}
// 显示特征值信息
showCharacteristicInfo(targetService.uuid, charDetails);
}
} catch (err) {
console.error('启用READ通道失败:', err);
// 显示更详细的错误信息
const errorMsg = `启用READ通道失败:\n${err.message || '未知错误'}\n\n设备可能不支持通知功能,已设置基本数据监听。`;
// 记录详细错误
console.error('蓝牙通知错误详情:', {
errType: err.name,
errMsg: err.message,
error: err
});
// 显示错误信息但不中断连接
uni.showModal({
title: '蓝牙数据接收配置',
content: errorMsg,
showCancel: false,
success: () => {
// 即使失败也继续,用户可以尝试发送命令
console.log('用户已确认错误信息,继续操作');
}
});
}
};
/**
* 启用特定特征值的数据接收(通知或读取)
*/
const enableCharNotify = async (deviceId, serviceId, characteristicId) => {
try {
// 先尝试获取特征值的属性信息
const charInfo = await uni.getBLEDeviceCharacteristics({
deviceId,
serviceId,
characteristicId
});
console.log(`特征值信息:`, charInfo.characteristics[0]);
// 尝试多种数据接收方式
try {
// 方式1: 尝试启用通知(已在enableNotification中处理了描述符不支持的情况)
await enableNotification(deviceId, serviceId, characteristicId);
return;
} catch (notifyErr) {
console.warn('启用通知失败,尝试其他方式:', notifyErr);
// 方式2: 如果特征值支持读取,设置周期性读取
if (charInfo.characteristics[0].properties.read) {
console.log('特征值支持读取,设置周期性读取');
startPeriodicReading(deviceId, serviceId, characteristicId, charInfo.characteristics[0].properties);
return;
} else {
// 方式3: 注册数据变化监听器(即使通知失败,某些设备仍可能推送数据)
console.log('注册数据变化监听器作为备选方案');
registerDataChangeListener(deviceId, serviceId, characteristicId);
}
}
// 显示成功消息
uni.showToast({
title: `已设置数据接收通道`,
icon: 'success',
duration: 2000
});
} catch (err) {
console.error('启用数据接收失败:', err);
// 不抛出错误,而是设置基本的监听器
registerDataChangeListener(deviceId, serviceId, characteristicId);
throw new Error(`启用通知失败:${err.errMsg}`);
}
};
/**
* 尝试启用通知(方式1)
*/
const enableNotification = async (deviceId, serviceId, characteristicId) => {
try {
// 当前环境不支持获取描述符,直接尝试启用通知
// 跳过描述符查询,避免环境兼容性问题
console.log('当前环境不支持获取描述符,直接尝试启用通知');
// 尝试启用通知
await uni.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId,
state: true
});
console.log(`已启用通知 - 服务:${serviceId},特征值:${characteristicId}`);
// 注册数据变化监听器
registerDataChangeListener(deviceId, serviceId, characteristicId);
} catch (err) {
console.error('启用通知失败:', err);
throw err;
}
};
/**
* 设置周期性读取(方式2)
*/
let readTimer = null;
let isPeriodicReadingActive = false;
const startPeriodicReading = (deviceId, serviceId, characteristicId, charProperties = null) => {
// 清除之前的定时器
if (readTimer) {
clearInterval(readTimer);
}
console.log(`开始周期性读取 - 服务:${serviceId},特征值:${characteristicId}`);
// 检查是否需要获取特征值属性信息
const checkAndStartReading = async () => {
let actualProperties = charProperties;
// 如果没有提供属性信息,尝试获取
if (!actualProperties) {
try {
const charInfo = await uni.getBLEDeviceCharacteristics({
deviceId,
serviceId,
characteristicId
});
if (charInfo.characteristics && charInfo.characteristics.length > 0) {
actualProperties = charInfo.characteristics[0].properties;
console.log('获取到特征值属性:', actualProperties);
}
} catch (err) {
console.warn('获取特征值属性失败,将尝试其他方式:', err);
}
}
// 如果确认不支持读取操作,切换到通知模式
if (actualProperties && !actualProperties.read) {
console.log('特征值不支持读取操作,尝试切换到通知模式');
// 尝试直接启用通知(跳过描述符检查)
try {
await uni.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId,
state: true
});
console.log(`成功启用通知 - 服务:${serviceId},特征值:${characteristicId}`);
isPeriodicReadingActive = false;
// 注册数据变化监听器
registerDataChangeListener(deviceId, serviceId, characteristicId);
// 更新状态提示
if (interactStatusType.value === '' || interactStatusType.value === 'waiting') {
interactStatusType.value = 'info';
interactStatusText.value = '已启用数据通知模式';
}
return;
} catch (notifyErr) {
console.warn('启用通知也失败,将采用宽松的数据接收策略:', notifyErr);
}
}
// 如果支持读取或无法确认属性,继续使用周期性读取作为备选
isPeriodicReadingActive = true;
// 仅在等待状态时更新UI,避免覆盖重要状态提示
if (interactStatusType.value === '' || interactStatusType.value === 'waiting') {
interactStatusType.value = 'info';
interactStatusText.value = '已启用数据接收模式';
}
// 设置数据变化监听器作为主要数据接收方式
registerDataChangeListener(deviceId, serviceId, characteristicId);
// 设置定时器,减少读取频率,避免重复失败
readTimer = setInterval(() => {
if (isPeriodicReadingActive && currentStatus.value === BT_STATUS.CONNECTED) {
// 只有在非success状态或数据为空时才尝试读取
if (interactStatusType.value !== 'success' || inspectionData.value.length === 0) {
readCharacteristicValue(deviceId, serviceId, characteristicId);
}
}
}, 8000); // 增加间隔时间,减少失败尝试
};
// 启动异步检查和读取
checkAndStartReading();
};
const stopPeriodicReading = () => {
if (readTimer) {
clearInterval(readTimer);
readTimer = null;
isPeriodicReadingActive = false;
console.log('已停止周期性读取');
}
};
/**
* 读取特征值
*/
const readCharacteristicValue = async (deviceId, serviceId, characteristicId) => {
try {
// 添加详细的读取特征值信息日志
console.log(`尝试读取特征值: 设备=${deviceId}, 服务=${serviceId}, 特征值=${characteristicId}`);
// 避免在交互状态为success或error时打印过多日志
const logLevel = ['success', 'error'].includes(interactStatusType.value) ? 'debug' : 'info';
// 优化状态提示,避免一直显示"尝试主动读取特征值"
if (interactStatusType.value === '' || interactStatusType.value === 'waiting') {
interactStatusType.value = 'waiting';
interactStatusText.value = '正在读取设备数据...';
}
// 先检查特征值是否支持读取
const charRes = await uni.getBLEDeviceCharacteristics({
deviceId,
serviceId,
characteristicId
});
const char = charRes.characteristics[0];
if (!char) {
console.error('未找到指定的特征值');
// 更新状态提示
if (inspectionData.value.length === 0 && interactStatusType.value !== 'success') {
interactStatusType.value = 'warning';
interactStatusText.value = '特征值未找到,请检查通道设置';
}
return;
}
if (!char.properties.read) {
console.warn(`特征值 ${characteristicId} 不支持读取操作,属性:`, char.properties);
// 如果支持通知,提示用户
if (char.properties.notify) {
console.log('该特征值支持通知,尝试启用通知模式');
if (inspectionData.value.length === 0 && interactStatusType.value !== 'success') {
interactStatusType.value = 'warning';
interactStatusText.value = '特征值不支持读取,请尝试启用通知模式';
}
}
return;
}
console.log(`特征值支持读取操作: ${characteristicId}`);
// 执行读取操作
await uni.readBLECharacteristicValue({
deviceId,
serviceId,
characteristicId
});
// 读取命令发送成功后,记录日志
if (logLevel === 'info' && interactStatusType.value !== 'success') {
console.log('数据读取命令已发送,等待响应');
}
} catch (err) {
console.error('读取特征值失败:', err);
console.error('错误详情:', { errCode: err.errCode, errMsg: err.errMsg, deviceId, serviceId, characteristicId });
// 更详细的错误状态处理
if (inspectionData.value.length === 0 && interactStatusType.value !== 'success') {
interactStatusType.value = 'warning';
// 根据错误码提供更具体的提示
if (err.errCode === 10004) {
interactStatusText.value = '设备未连接,请重新连接';
} else if (err.errCode === 10009) {
interactStatusText.value = '特征值不存在,请检查通道设置';
} else {
interactStatusText.value = '数据读取中,请稍候...';
}
}
// 如果连接已断开,停止周期性读取
if (err.errCode === 10018 || err.errCode === 10004) {
stopPeriodicReading();
return;
}
// 3秒后尝试重新读取
setTimeout(() => {
if (currentStatus.value === BT_STATUS.CONNECTED && isPeriodicReadingActive) {
readCharacteristicValue(deviceId, serviceId, characteristicId);
}
}, 3000);
}
};
/**
* 注册数据变化监听器(通用)
*/
const registerDataChangeListener = (deviceId, serviceId, characteristicId) => {
// 先移除之前可能存在的监听器
uni.offBLECharacteristicValueChange();
// 注册新的监听器
uni.onBLECharacteristicValueChange((res) => {
console.log('收到特征值变化通知:', {
serviceId: res.serviceId,
characteristicId: res.characteristicId,
expectedCharId: characteristicId
});
// 处理所有收到的数据,不严格匹配特征值ID,提高兼容性
if (res.value) {
parseDeviceData(res.value);
}
});
};
/**
* 断开连接(释放文档设备资源)
*/
const handleDisconnect = async () => {
if (currentStatus.value !== BT_STATUS.CONNECTED || !connectedDeviceId.value) return;
try {
await uni.closeBLEConnection({ deviceId: connectedDeviceId.value });
currentStatus.value = BT_STATUS.DISCONNECTED;
connectedDeviceId.value = '';
showInspectionArea.value = false;
inspectionData.value = [];
uni.showToast({ title: '已断开连接' });
} catch (err) {
uni.showToast({ title: `断开失败:${err.errMsg}`, icon: 'none' });
}
};
// ---------------------- 核心:巡检特征值逻辑(匹配设备交互流程) ----------------------
/**
* 查找合适的读取特征值
*/
const findSuitableReadCharacteristic = (characteristics) => {
// 优先查找包含fff1的特征值(根据用户提供的信息,fff1是读特征值)
const fff1Char = characteristics.find(
c => c.uuid.toLowerCase().includes('fff1') && (c.properties.read || c.properties.notify)
);
if (fff1Char) {
console.log('找到fff1读取特征值:', fff1Char.uuid);
return fff1Char;
}
// 然后查找文档中指定的READ特征值(支持完整UUID格式)
const docReadChar = characteristics.find(
c => c.uuid.toLowerCase().includes(BT_CONFIG.CHARACTERISTIC_ID.READ.toLowerCase().split('-')[0])
);
if (docReadChar) return docReadChar;
// 查找支持通知或读取的特征值
const notifyChar = characteristics.find(c => c.properties.notify);
if (notifyChar) return notifyChar;
// 查找支持读取的特征值
const readChar = characteristics.find(c => c.properties.read);
if (readChar) return readChar;
// 返回第一个特征值作为备选
return characteristics[0] || null;
};
/**
* 为指定服务查找合适的读取特征值
*/
const findSuitableReadCharacteristicForService = async (serviceId) => {
try {
console.log(`尝试为服务 ${serviceId} 查找读取特征值`);
// 获取服务的所有特征值
const charRes = await uni.getBLEDeviceCharacteristics({
deviceId: connectedDeviceId.value,
serviceId
});
// 查找合适的读取特征值
const readChar = findSuitableReadCharacteristic(charRes.characteristics);
if (readChar) {
console.log(`找到读取特征值: ${readChar.uuid}`);
connectedServiceInfo.readCharId = readChar.uuid;
// 如果支持通知,尝试启用通知
if (readChar.properties.notify) {
console.log('尝试启用通知模式');
await uni.notifyBLECharacteristicValueChange({
deviceId: connectedDeviceId.value,
serviceId,
characteristicId: readChar.uuid,
state: true,
success: () => {
console.log('通知模式已启用');
interactStatusType.value = 'info';
interactStatusText.value = '已启用通知模式,等待数据推送';
},
fail: (err) => {
console.warn('启用通知模式失败:', err);
// 继续使用读取模式
readCharacteristicValue(connectedDeviceId.value, serviceId, readChar.uuid);
}
});
} else {
// 如果不支持通知,尝试读取
readCharacteristicValue(connectedDeviceId.value, serviceId, readChar.uuid);
}
} else {
console.warn('未找到合适的读取特征值');
interactStatusType.value = 'warning';
interactStatusText.value = '未找到可用的读取特征值';
}
} catch (err) {
console.error('查找读取特征值失败:', err);
}
};
/**
* 查找合适的写入特征值
*/
const findSuitableWriteCharacteristic = (characteristics) => {
// 优先查找包含fff2的特征值(根据用户提供的信息,fff2是写特征值)
const fff2Char = characteristics.find(
c => c.uuid.toLowerCase().includes('fff2') && (c.properties.write || c.properties.writeNoResponse)
);
if (fff2Char) {
console.log('找到fff2写入特征值:', fff2Char.uuid);
return fff2Char;
}
// 然后查找文档中指定的WRITE特征值(支持完整UUID格式)
const docWriteChar = characteristics.find(
c => c.uuid.toLowerCase().includes(BT_CONFIG.CHARACTERISTIC_ID.WRITE.toLowerCase().split('-')[0])
);
if (docWriteChar) return docWriteChar;
// 查找支持写入的特征值
const writeChar = characteristics.find(c => c.properties.write);
if (writeChar) return writeChar;
// 查找支持无响应写入的特征值
const writeNoRespChar = characteristics.find(c => c.properties.writeNoResponse);
if (writeNoRespChar) return writeNoRespChar;
// 返回第一个特征值作为备选
return characteristics[0] || null;
};
/**
* 显示特征值信息
*/
const showCharacteristicInfo = (serviceId, characteristics) => {
const charInfo = characteristics.map(c => {
const props = [];
if (c.properties.read) props.push('读');
if (c.properties.write) props.push('写');
if (c.properties.notify) props.push('通知');
if (c.properties.writeNoResponse) props.push('无响应写');
return `${c.uuid} [${props.join(', ')}]`;
}).join('\n');
uni.showModal({
title: '特征值信息',
content: `服务: ${serviceId}\n\n特征值详情:\n${charInfo}\n\n已选择:\n读: ${connectedServiceInfo.readCharId}\n写: ${connectedServiceInfo.writeCharId}`,
showCancel: false
});
};
/**
* 发送测量指令到设备
* @param {number} funcCode - 功能码
* @param {string} requestText - 请求提示文本
* @param {string} measurementType - 测量类型标识
*/
// 存储超时定时器ID
let responseTimeoutId = null;
// 清除超时定时器的函数
const clearResponseTimeout = () => {
if (responseTimeoutId) {
clearTimeout(responseTimeoutId);
responseTimeoutId = null;
}
};
// 设置响应超时处理
const setResponseTimeout = () => {
// 清除之前可能存在的定时器
clearResponseTimeout();
// 设置10秒超时(增加超时时间)
responseTimeoutId = setTimeout(() => {
interactStatusType.value = 'warning';
interactStatusText.value = '设备未响应,请检查连接或重试';
console.log('设备响应超时,请求序号:', currentReqSeq, '测量类型:', currentMeasurementType);
console.log('当前使用的服务和特征值:', connectedServiceInfo);
responseTimeoutId = null;
// 主动读取一次特征值,看是否能获取数据
if (connectedServiceInfo.serviceId && connectedServiceInfo.readCharId) {
console.log('超时后尝试主动读取特征值');
readCharacteristicValue(connectedDeviceId.value, connectedServiceInfo.serviceId, connectedServiceInfo.readCharId);
}
}, 10000);
};
const sendMeasurementCommand = async (funcCode, requestText, measurementType) => {
console.log("===== 开始发送测量指令 =====");
console.log("指令参数:", {
funcCode: '0x' + funcCode.toString(16).toUpperCase(),
requestText,
measurementType,
currentStatus: currentStatus.value,
deviceConnected: currentStatus.value === BT_STATUS.CONNECTED
});
if (currentStatus.value !== BT_STATUS.CONNECTED) {
console.error("发送失败: 设备未连接");
uni.showToast({ title: '请先连接设备', icon: 'none' });
return;
}
// 初始化交互状态+展示区
showInspectionArea.value = true;
interactStatusType.value = 'waiting';
interactStatusText.value = requestText;
inspectionData.value = [];
currentMeasurementType = measurementType;
// 生成请求序号(确保与设备响应/推送序号匹配)
currentReqSeq = (currentReqSeq + 1) % 256;
console.log(`生成请求序号: ${currentReqSeq}`);
try {
// 根据测量类型组装不同的指令
let fullCmd;
if (measurementType === 'vibration') {
console.log("构建振动测量指令");
// 振动测量时添加采样参数配置
const samplingFactor = samplingFactors.value[samplingFactorIndex.value];
const highPassFilter = highPassFilters.value[highPassFilterIndex.value];
// 功能掩码 - 设置bit6为1启用近距离测温(第7位,从0开始计数)
const featureMask = 0x40; // 0b01000000,bit6为1
// 组装带参数的指令:前导+长度+序号+功能码+功能掩码+采样系数+高通滤波系数+校验和
// 注意:长度需要增加1以包含功能掩码参数
const cmd = new Uint8Array([0x5A, 0x05, currentReqSeq, funcCode, featureMask, samplingFactor, highPassFilter]);
const checksum = cmd.reduce((a, b) => a + b, 0) & 0xFF; // 校验和(累加取低8位)
fullCmd = new Uint8Array([...cmd, checksum]);
console.log('功能掩码设置:', {
featureMask: '0x' + featureMask.toString(16).toUpperCase(),
bit6Enabled: (featureMask & 0x40) === 0x40 ? '是' : '否',
description: '近距离测温已启用'
});
console.log('发送带参数的振动测量指令:', {
samplingFactor,
highPassFilter,
expectedSamplingRate: currentSamplingRate.value,
expectedCutoffFreq: currentCutoffFreq.value
});
} else {
console.log("构建标准测量指令");
// 其他测量类型使用标准指令
const cmd = new Uint8Array([0x5A, 0x02, currentReqSeq, funcCode]);
const checksum = cmd.reduce((a, b) => a + b, 0) & 0xFF; // 校验和(累加取低8位)
fullCmd = new Uint8Array([...cmd, checksum]);
}
const cmdBuffer = fullCmd.buffer;
// 详细的指令发送日志
console.log('发送测量指令:', {
measurementType,
functionCode: '0x' + funcCode.toString(16).toUpperCase(),
sequence: currentReqSeq,
commandBytes: Array.from(fullCmd),
commandHex: Array.from(fullCmd).map(b => '0x' + b.toString(16).padStart(2, '0').toUpperCase()).join(', ')
});
// 优先使用已知的传感器服务和正确的特征值映射(fff1为读,fff2为写)
let serviceId = connectedServiceInfo.serviceId;
let writeCharId = connectedServiceInfo.writeCharId;
let readCharId = connectedServiceInfo.readCharId;
console.log("当前连接信息:", {
deviceId: connectedDeviceId.value,
serviceId: serviceId || '未设置',
writeCharId: writeCharId || '未设置',
readCharId: readCharId || '未设置'
});
// 改进的特征值组合查找逻辑
let foundSuitableChars = false;
// 首先检查现有配置是否合适
if (writeCharId && readCharId) {
const hasValidWriteChar = writeCharId.toLowerCase().includes('fff2');
const hasValidReadChar = readCharId.toLowerCase().includes('fff1');
if (hasValidWriteChar && hasValidReadChar) {
foundSuitableChars = true;
console.log("发现有效特征值组合,已包含fff2(写)和fff1(读)");
} else {
console.log("现有特征值组合不满足要求:", {
writeCharValid: hasValidWriteChar,
readCharValid: hasValidReadChar
});
}
}
// 如果未设置正确的读写特征值组合,尝试查找
if (!foundSuitableChars) {
console.log('未找到正确的读写特征值组合,尝试查找最佳匹配...');
// 记录查找前的状态
console.log("查找前的特征值状态:", {
hasServiceId: !!serviceId,
hasWriteCharId: !!writeCharId,
hasReadCharId: !!readCharId
});
// 调用优化后的查找函数
await findAndUseAlternativeWriteChar();
// 更新使用的ID
serviceId = connectedServiceInfo.serviceId;
writeCharId = connectedServiceInfo.writeCharId;
readCharId = connectedServiceInfo.readCharId;
console.log("查找后的特征值状态:", {
serviceId: serviceId || '未找到',
writeCharId: writeCharId || '未找到',
readCharId: readCharId || '未找到'
});
}
// 如果仍然没有找到合适的特征值,使用默认配置
console.log("应用默认配置检查...");
if (!serviceId) {
// 处理SERVICE_ID可能是数组的情况,优先使用第一个服务ID
serviceId = Array.isArray(BT_CONFIG.SERVICE_ID) ? BT_CONFIG.SERVICE_ID[0] : BT_CONFIG.SERVICE_ID;
console.log(`使用默认服务ID: ${serviceId}`);
}
if (!writeCharId) {
writeCharId = BT_CONFIG.CHARACTERISTIC_ID.WRITE;
console.log(`使用默认写入特征值: ${writeCharId}`);
}
if (!readCharId) {
readCharId = BT_CONFIG.CHARACTERISTIC_ID.READ;
console.log(`使用默认读取特征值: ${readCharId}`);
}
console.log(`最终使用的参数:`, {
deviceId: connectedDeviceId.value,
serviceId,
writeCharId,
readCharId,
fullCommand: Array.from(fullCmd).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ')
});
// 发送前先尝试获取特征值属性,确保可写
try {
console.log("开始检查特征值属性...");
const charRes = await uni.getBLEDeviceCharacteristics({
deviceId: connectedDeviceId.value,
serviceId,
characteristicId: writeCharId
});
if (charRes.characteristics && charRes.characteristics.length > 0) {
const char = charRes.characteristics[0];
console.log('写入特征值属性检查结果:', char.properties);
// 如果特征值不支持写入,尝试查找其他可写特征值
if (!char.properties.write && !char.properties.writeNoResponse) {
console.warn('当前特征值不支持写入,尝试查找其他可写特征值');
await findAndUseAlternativeWriteChar();
// 使用新的写入特征值
const newWriteCharId = connectedServiceInfo.writeCharId;
const newReadCharId = connectedServiceInfo.readCharId;
if (newWriteCharId) writeCharId = newWriteCharId;
if (newReadCharId) readCharId = newReadCharId;
console.log('更新后的特征值:', { newWriteCharId, newReadCharId });
}
} else {
console.error('未找到指定的特征值');
}
} catch (checkErr) {
console.warn('特征值属性检查失败:', checkErr);
// 继续尝试发送,不中断流程
}
// 先检查特征值是支持write还是writeNoResponse
try {
console.log("开始检查写入特征值属性类型...");
const charRes = await uni.getBLEDeviceCharacteristics({
deviceId: connectedDeviceId.value,
serviceId,
characteristicId: writeCharId
});
if (charRes.characteristics && charRes.characteristics.length > 0) {
const char = charRes.characteristics[0];
console.log('写入特征值可用属性:', char.properties);
// 确定写入类型
const writeType = char.properties.writeNoResponse ? 'noResponse' : 'write';
console.log(`确定使用写入类型: ${writeType}`);
// 发送指令到WRITE通道(优先使用fff2)
console.log("准备发送指令...");
await uni.writeBLECharacteristicValue({
deviceId: connectedDeviceId.value,
serviceId,
characteristicId: writeCharId,
value: cmdBuffer,
writeType: writeType,
success: () => {
console.log('✅ 指令发送成功,等待设备响应...');
// 设置超时处理
setResponseTimeout();
// 发送成功后立即尝试读取一次,看是否有即时响应
setTimeout(() => {
if (readCharId) {
console.log('尝试主动读取特征值(fff1),检查即时响应');
readCharacteristicValue(connectedDeviceId.value, serviceId, readCharId);
} else {
console.log('未设置读取特征值,尝试查找合适的读取特征值');
// 如果没有设置读取特征值,尝试查找一个
findSuitableReadCharacteristicForService(serviceId);
}
}, 300);
},
fail: (err) => {
console.error('❌ 指令发送失败(高级写入模式):', err);
// 处理失败情况
throw new Error(`高级写入模式失败: ${err.errMsg}`);
}
});
} else {
console.error('特征值不存在,使用默认写入方式');
throw new Error('特征值不存在');
}
} catch (checkErr) {
console.warn('特征值属性检查失败,降级使用默认写入方式:', checkErr.message);
// 如果检查失败,使用默认方式发送指令
console.log("尝试使用默认方式发送指令...");
await uni.writeBLECharacteristicValue({
deviceId: connectedDeviceId.value,
serviceId,
characteristicId: writeCharId,
value: cmdBuffer,
success: () => {
console.log('✅ 指令发送成功(默认写入方式),等待设备响应...');
setResponseTimeout();
setTimeout(() => {
if (readCharId) {
readCharacteristicValue(connectedDeviceId.value, serviceId, readCharId);
} else {
findSuitableReadCharacteristicForService(serviceId);
}
}, 300);
},
fail: async (err) => {
console.error('❌ 指令发送失败(默认方式):', JSON.stringify(err, null, 2));
// 更友好的错误提示和处理
let errorMessage = `发送失败:${err.errMsg || '未知错误'}`;
if (err.errCode === 10009 || err.errMsg?.includes('property not support')) {
// 特征值不存在或不支持写入,尝试查找正确的fff2写入特征值
console.warn('⚠️ 特征值不存在或不支持写入,尝试查找正确的fff2写入特征值');
await findAndUseAlternativeWriteChar();
// 强制更新连接信息
serviceId = connectedServiceInfo.serviceId;
writeCharId = connectedServiceInfo.writeCharId;
readCharId = connectedServiceInfo.readCharId;
console.log('更新后的特征值信息:', { serviceId, writeCharId, readCharId });
errorMessage = '特征值不支持写入,已尝试查找正确通道,请重试';
} else if (err.errCode === 10004) {
errorMessage = '设备未连接,请重新连接';
}
console.error(`最终错误信息: ${errorMessage}`);
throw new Error(errorMessage);
}
});
}
} catch (err) {
console.error('❌ 测量指令发送流程失败:', JSON.stringify(err, null, 2));
interactStatusType.value = 'warning';
interactStatusText.value = `请求发送失败:${err.message}`;
clearResponseTimeout();
// 如果发送失败且未使用备选方案,尝试查找替代写入特征值
if (!connectedServiceInfo.writeCharId) {
console.log('未找到写入特征值,尝试查找替代方案...');
findAndUseAlternativeWriteChar();
}
} finally {
console.log("===== 测量指令发送流程结束 =====");
}
};
/**
* 查找并使用替代的写入特征值和读取特征值
*/
const findAndUseAlternativeWriteChar = async () => {
try {
console.log('? 开始查找最佳特征值组合...');
// 确保设备已连接
if (!connectedDeviceId.value) {
console.error('❌ 设备未连接,无法查找特征值');
uni.showToast({
title: '设备未连接',
icon: 'none'
});
return;
}
// 清除之前的连接信息,确保重新查找
connectedServiceInfo.serviceId = null;
connectedServiceInfo.writeCharId = null;
connectedServiceInfo.readCharId = null;
console.log('✅ 已清除之前的连接信息,准备重新查找');
// 获取设备的所有服务
const serviceRes = await uni.getBLEDeviceServices({
deviceId: connectedDeviceId.value
});
console.log(`? 发现设备服务数量: ${serviceRes.services.length}`);
console.log('? 设备服务列表:', serviceRes.services.map(s => s.uuid));
// 优先遍历已知的传感器服务
let servicesToCheck = [];
// 确保SENSOR_SERVICE_IDS是数组
if (Array.isArray(BT_CONFIG.SENSOR_SERVICE_IDS)) {
servicesToCheck = [...BT_CONFIG.SENSOR_SERVICE_IDS];
} else {
servicesToCheck = [BT_CONFIG.SENSOR_SERVICE_IDS];
}
// 添加设备实际支持的所有服务(去重)
const deviceServices = serviceRes.services.map(s => s.uuid);
servicesToCheck = [...new Set([...servicesToCheck, ...deviceServices])];
// 优先检查文档中指定的服务(fff0)
const fff0Service = servicesToCheck.find(s => s.toLowerCase().includes('fff0'));
if (fff0Service) {
const index = servicesToCheck.indexOf(fff0Service);
servicesToCheck.splice(index, 1);
servicesToCheck.unshift(fff0Service); // 移到最前面优先检查
console.log('? 找到fff0服务,优先检查');
}
console.log('? 将检查的服务列表:', servicesToCheck);
// 记录找到的最佳组合
let bestWriteChar = null;
let bestReadChar = null;
let bestServiceId = null;
let bestScore = 0;
// 服务评分函数
const scoreCharacteristicPair = (writeChar, readChar, serviceId) => {
let score = 0;
// 基础分:有写入特征值+10,有读取特征值+10
if (writeChar) score += 10;
if (readChar) score += 10;
// 额外分:如果包含特定标识符
if (writeChar && writeChar.uuid.toLowerCase().includes('fff2')) score += 20;
if (readChar && readChar.uuid.toLowerCase().includes('fff1')) score += 20;
// 额外分:如果是fff0服务
if (serviceId.toLowerCase().includes('fff0')) score += 15;
// 额外分:支持更多属性
if (writeChar && writeChar.properties.writeNoResponse) score += 5;
if (readChar && readChar.properties.notify) score += 5;
return score;
};
// 为每个服务评分
const serviceScores = [];
for (const serviceId of servicesToCheck) {
try {
console.log(`? 正在检查服务: ${serviceId}`);
// 获取服务的所有特征值
const charRes = await uni.getBLEDeviceCharacteristics({
deviceId: connectedDeviceId.value,
serviceId
});
console.log(`? 服务 ${serviceId} 特征值数量: ${charRes.characteristics.length}`);
// 详细记录所有特征值信息
const charDetails = [];
charRes.characteristics.forEach((c, idx) => {
const props = [];
if (c.properties.read) props.push('read');
if (c.properties.write) props.push('write');
if (c.properties.notify) props.push('notify');
if (c.properties.writeNoResponse) props.push('writeNoResponse');
charDetails.push({
index: idx + 1,
uuid: c.uuid,
properties: props
});
console.log(` 特征值 ${idx + 1}:`, {
uuid: c.uuid,
properties: c.properties
});
});
// 全面查找读写特征值
let writeChar = null;
let readChar = null;
// 1. 优先使用专业查找函数
writeChar = findSuitableWriteCharacteristic(charRes.characteristics);
// 查找合适的读取特征值
readChar = charRes.characteristics.find(
c => c.uuid.toLowerCase().includes('fff1') && (c.properties.read || c.properties.notify)
) || charRes.characteristics.find(c => c.properties.read || c.properties.notify);
// 计算分数
const score = scoreCharacteristicPair(writeChar, readChar, serviceId);
serviceScores.push({
serviceId,
score,
writeChar: writeChar?.uuid,
readChar: readChar?.uuid
});
// 记录特征值找到情况
console.log(`? 服务 ${serviceId} 特征值匹配结果:`, {
writeCharFound: !!writeChar,
readCharFound: !!readChar,
writeCharId: writeChar?.uuid || '未找到',
readCharId: readChar?.uuid || '未找到',
score
});
// 更新最佳组合
if (score > bestScore) {
bestWriteChar = writeChar;
bestReadChar = readChar;
bestServiceId = serviceId;
bestScore = score;
console.log(`⭐ 找到更好的特征值组合 (分数: ${score}),更新最佳选择`);
}
// 如果找到完美组合,立即返回
if (writeChar && readChar &&
writeChar.uuid.toLowerCase().includes('fff2') &&
readChar.uuid.toLowerCase().includes('fff1')) {
console.log(`? 找到完美匹配的特征值组合,立即使用`);
bestWriteChar = writeChar;
bestReadChar = readChar;
bestServiceId = serviceId;
break; // 找到完美组合,立即返回
}
} catch (serviceError) {
console.warn(`⚠️ 服务 ${serviceId} 访问失败,跳过:`, serviceError);
}
}
// 打印所有服务的评分结果
console.log('? 所有服务评分结果:', serviceScores.sort((a, b) => b.score - a.score));
// 处理找到的最佳组合
if (bestWriteChar && bestReadChar) {
console.log(`✅ 找到最佳特征值组合: 服务=${bestServiceId} (分数: ${bestScore})`);
console.log(` 写入特征值: ${bestWriteChar.uuid} [${Object.keys(bestWriteChar.properties).filter(p => bestWriteChar.properties[p]).join(', ')}]`);
console.log(` 读取特征值: ${bestReadChar.uuid} [${Object.keys(bestReadChar.properties).filter(p => bestReadChar.properties[p]).join(', ')}]`);
// 更新连接信息
connectedServiceInfo.serviceId = bestServiceId;
connectedServiceInfo.writeCharId = bestWriteChar.uuid;
connectedServiceInfo.readCharId = bestReadChar.uuid;
// 通知用户
uni.showModal({
title: '✅ 已找到匹配的读写通道',
content: `已切换到最佳通道组合:\n服务: ${bestServiceId}\n写入特征值: ${bestWriteChar.uuid}\n读取特征值: ${bestReadChar.uuid}\n匹配分数: ${bestScore}\n\n请尝试发送测量指令`,
showCancel: false
});
// 尝试启用通知
if (bestReadChar.properties.notify) {
try {
console.log('? 尝试启用读取特征值的通知模式');
await uni.notifyBLECharacteristicValueChange({
deviceId: connectedDeviceId.value,
serviceId: bestServiceId,
characteristicId: bestReadChar.uuid,
state: true
});
console.log('✅ 通知模式已成功启用');
} catch (notifyErr) {
console.warn('⚠️ 启用通知模式失败:', notifyErr);
}
}
} else if (bestWriteChar) {
// 只有写入特征值的情况
console.log(`⚠️ 找到写入特征值: 服务=${bestServiceId}, 写入特征值=${bestWriteChar.uuid}`);
connectedServiceInfo.serviceId = bestServiceId;
connectedServiceInfo.writeCharId = bestWriteChar.uuid;
connectedServiceInfo.readCharId = bestReadChar?.uuid || null;
uni.showModal({
title: '⚠️ 已找到写入通道',
content: `已切换到替代通道:\n服务: ${bestServiceId}\n写入特征值: ${bestWriteChar.uuid}\n读取特征值: ${bestReadChar?.uuid || '未找到'}\n\n请尝试发送测量指令`,
showCancel: false
});
} else {
// 如果没有找到可写入的特征值
console.error('❌ 未找到任何可用的特征值');
// 尝试极端方案:遍历所有服务的所有特征值
console.log('? 尝试极端方案:遍历所有服务的所有特征值...');
for (const service of serviceRes.services) {
try {
const charRes = await uni.getBLEDeviceCharacteristics({
deviceId: connectedDeviceId.value,
serviceId: service.uuid
});
const anyWriteChar = charRes.characteristics.find(c => c.properties.write || c.properties.writeNoResponse);
cons
2***@qq.com
- 发布:2025-10-25 18:59
- 更新:2025-10-25 18:59
- 阅读:8
0 个回复