Files
APP/pages/BlueTooth/ModeSetting/HBY670V1.vue
2025-08-27 11:06:49 +08:00

2837 lines
69 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<!-- 蓝牙设备列表页 -->
<view v-if="currentPage === 'deviceList'">
<view class="title">蓝牙设备列表</view>
<view class="status-bar">
<text>蓝牙状态: {{ bluetoothStatus }}</text>
<button class="btn" :disabled="isLoading" @click="toggleBluetooth">
{{ isBluetoothOpen ? '关闭蓝牙' : '开启蓝牙' }}
</button>
</view>
<view class="search-area">
<button class="btn search-btn" :disabled="!isBluetoothOpen || isSearching" @click="startSearch">
{{ isSearching ? '正在搜索...' : '搜索设备' }}
</button>
<text class="tips">{{ searchTips }}</text>
</view>
<view class="devices-list" v-if="deviceList.length > 0">
<view class="device-item" v-for="(device, index) in deviceList" :key="index"
:class="{'active': device.name === targetDeviceName}"
@click="connectDevice(device.deviceId, device.name)">
<text class="device-name">{{ device.name || '未知设备' }}</text>
<text class="device-rssi">{{ device.RSSI }}dBm</text>
<text class="device-status" v-if="device.name === targetDeviceName">已连接</text>
</view>
</view>
</view>
<!-- 主控制页面 -->
<view v-if="currentPage === 'controlPanel'">
<view class="status-bar">
<text>已连接: {{ targetDeviceName }}</text>
<button class="btn" @click="disconnectDevice(null)">断开连接</button>
</view>
<view class="status-bar">
<text>当前模式: {{ netMode=='ble'?'蓝牙模式':netMode=='mqtt'?'MQ模式':'Http模式' }}</text>
<button class="btn" @click="toggleNetMode()">模式切换</button>
</view>
<view class="receivLog">
<view>数据更新时间{{receiveData.date}}</view>
<view class="w50 fleft">Mac:{{receiveData.macAddress}}</view>
<view class="w50 fleft">IMEI:{{receiveData.IMEI}}</view>
<view class="w50 fleft">经度:{{receiveData.Lon}}</view>
<view class="w50 fleft">纬度:{{receiveData.Lat}}</view>
<view class="w50 fleft">静电探测档位:{{receiveData.staticLevel}}</view>
<view class="w50 fleft">照明档位:{{receiveData.lightingLevel}}</view>
<view class="w50 fleft">剩余照明时间:{{receiveData.lightingTime}}</view>
<view class="w50 fleft">电池电量:{{receiveData.batteryLevel}}</view>
<view class="w50 fleft">预警级别:{{receiveData.warnLevel}}</view>
<view class="w50 fleft">静止报警状态:{{receiveData.staticWarn}}</view>
<view class="w50 fleft">4G信号强度:{{receiveData.fourGStrenth}}</view>
<view class="w50 fleft">SOS模式:{{receiveData.SOS}}</view>
<view class="clear"></view>
</view>
<view>重发包序号{{reSendNumber}}</view>
<view class="sending-progress" v-if="isSending">
<view class="progress-bar">
<view class="progress-fill" :style="{ width: progress + '%' }"></view>
</view>
<text>正在发送: {{ progress }}% ({{ currentPacket }}/{{ totalPackets }})</text>
</view>
<view v-show="currentTab === 'splash' || currentTab === 'text'">
<text>发送间隔:</text>
<input type="number" v-model="inteval" class="txt" />
</view>
<view class="function-tabs">
<button class="tab-btn" :class="{'active': currentTab === 'mode'}"
@click="currentTab = 'mode'">检测模式</button>
<button class="tab-btn" :class="{'active': currentTab === 'brightness'}"
@click="currentTab = 'brightness'">照明模式</button>
<button class="tab-btn" :class="{'active': currentTab === 'sos'}"
@click="currentTab = 'sos'">SOS</button>
<button class="tab-btn" :class="{'active': currentTab === 'splash'}"
@click="currentTab = 'splash'">开机画面</button>
<button class="tab-btn" :class="{'active': currentTab === 'flash'}" @click="gotoSendVideo">开机动画</button>
<button class="tab-btn" :class="{'active': currentTab === 'text'}"
@click="currentTab = 'text'">文字设置</button>
<button class="tab-btn" :class="{'active': currentTab === 'urgent'}"
@click="currentTab = 'urgent'">紧急通知</button>
<button class="tab-btn" :class="{'active': currentTab === 'warn'}"
@click="currentTab = 'warn'">强制报警</button>
</view>
<!-- 模式设置选项卡 -->
<view v-if="currentTab === 'mode'">
<view class="modes-area">
<view class="mode-title">选择静电探测档位</view>
<view class="modes-grid">
<view class="mode-item" :class="{'selected': currentMode === 'high'}" @click="setMode('high')">
<text class="mode-name">高档模式</text>
</view>
<view class="mode-item" :class="{'selected': currentMode === 'center'}"
@click="setMode('center')">
<text class="mode-name">中档模式</text>
</view>
<view class="mode-item" :class="{'selected': currentMode === 'low'}" @click="setMode('low')">
<text class="mode-name">低档模式</text>
</view>
<view class="mode-item" :class="{'selected': currentMode === 'close'}"
@click="setMode('close')">
<text class="mode-name">关闭</text>
</view>
</view>
</view>
</view>
<!-- 强制报警选项卡 -->
<view v-if="currentTab === 'warn'">
<view class="brightness-area">
<button class="tab-btn" :class="{'active': brightnessStatu==='qiang'}"
@click="MandatoryWarn('open')">开启报警</button>
<button class="tab-btn" :class="{'active': brightnessStatu==='ruo'}"
@click="MandatoryWarn('close')">关闭报警</button>
</view>
</view>
<!-- 亮度调节选项卡 -->
<view v-if="currentTab === 'brightness'">
<view class="brightness-area">
<view class="brightness-title">照明模式</view>
<button class="tab-btn" :class="{'active': brightnessStatu==='qiang'}"
@click="setBrightness('qiang')">强光</button>
<button class="tab-btn" :class="{'active': brightnessStatu==='ruo'}"
@click="setBrightness('ruo')">弱光</button>
<button class="tab-btn" :class="{'active': brightnessStatu==='close'}"
@click="setBrightness('close')">关闭</button>
</view>
</view>
<view v-if="currentTab === 'sos'">
<view class="modes-area">
<view class="mode-title">选择模式</view>
<view class="modes-grid">
<view class="mode-item" :class="{'selected': currentSOS === 'sos'}" @click="setSOS('sos')">
<text class="mode-name">红蓝模式</text>
</view>
<view class="mode-item" :class="{'selected': currentSOS === 'warn'}" @click="setSOS('warn')">
<text class="mode-name">爆闪模式</text>
</view>
<view class="mode-item" :class="{'selected': currentSOS === 'close'}" @click="setSOS('close')">
<text class="mode-name">关闭SOS</text>
</view>
</view>
</view>
</view>
<!-- 开机画面选项卡 -->
<view v-if="currentTab === 'splash'">
<canvas canvas-id="splashCanvas" style="width: 160px; height: 80px; z-index: 9999;position: fixed; "
:style="{top:canvasTop,left:canvasLeft}"></canvas>
<view class="splash-controls">
<button class="btn" @click="importImage">导入图片</button>
<button class="btn" :disabled="!hasImage || isSending" @click="sendSplashImage">确认发送</button>
</view>
<view class="splash-preview">
<view class="splash-frame">
<image v-if="tempImagePath" :src="tempImagePath" mode="aspectFit"></image>
<view v-else class="empty-frame">点击"导入图片"选择开机画面</view>
</view>
</view>
</view>
<!-- 文字设置选项卡 -->
<view v-if="currentTab === 'text'">
<view class="text-inputs">
<view class="text-line" v-for="(line, index) in textLines" :key="index">
<text>{{ index + 1 }}:</text>
<input v-model="textLines[index]" placeholder="请输入文字" maxlength="30"
@confirm="onTextChanged(index)" />
</view>
</view>
<button class="btn send-text-btn" @click="sendText()">发送</button>
<view class="sending-progress" v-if="isSendingText">
<view class="progress-bar">
<view class="progress-fill" :style="{ width: textProgress + '%' }"></view>
</view>
<text>正在发送: {{ textProgress }}% ({{ currentTextPacket }}/{{ totalTextPackets }})</text>
</view>
</view>
<!-- 紧急通知 -->
<view v-if="currentTab === 'urgent'">
<view class="text-inputs">
<view class="text-line" v-for="(line, index) in warnLines" :key="index">
<text>{{ index + 1 }}:</text>
<input v-model="warnLines[index]" placeholder="请输入文字" maxlength="30" />
</view>
</view>
<button class="btn send-text-btn" @click="sendUrgent()">发送文本</button>
</view>
</view>
</view>
</template>
<script>
import gbk from '@/utils/gbk.js';
import MqttClient from '@/utils/mqtt.js';
import {
request,
baseURL
} from '@/utils/request.js';
var videoChannel=null;
var mqttClient = null;
export default {
data() {
return {
rgb565Data: null,
netMode: 'ble',
canvasTop: '-1000px',
canvasLeft: '-1000px',
inteval: 20,
isLoading: false,
isBluetoothOpen: false,
isSearching: false,
isConnected: false,
isSending: false,
isSendingText: false,
bluetoothStatus: '未初始化',
searchTips: '',
deviceList: [],
connectedDeviceId: '',
serviceId: '0xFFE1',
writeCharacteristicId: '0xFFE1',
notifyCharacteristicId: '0xFFE2',
currentMode: '',
logList: [],
packetCount: 1,
currentPage: 'deviceList',
targetDeviceName: ['JQZM-EF4651', 'XLCX-68455E'],
currentTab: 'mode',
brightness: 50,
brightnessTimer: null,
tempImagePath: '',
hasImage: false,
scale: 1,
translateX: 0,
translateY: 0,
lastX: 0,
lastY: 0,
isDragging: false,
imageWidth: 0,
imageHeight: 0,
textLines: ['你好中国', '你好中国', '你好中国', '你好中国'],
textBytes: ['', '', ''],
warnLines: ['现场危险,停止救援,', '紧急撤离至安全区域!'],
progress: 0,
currentPacket: 0,
totalPackets: 100,
textProgress: 0,
currentTextPacket: 0,
totalTextPackets: 4,
brightnessStatu: false,
receiveData: {
date: "",
staticLevel: "",
lightingLevel: "",
lightingTime: "",
batteryLevel: "",
macAddress: "",
warnLevel: "",
staticWarn: "",
fourGStrenth: "",
IMEI: "",
Lon: "",
Lat: "",
SOS: ""
},
subscits: [],
videoPath: "",
videoWidth: "",
videoHeight: "",
videoDuration: "",
currentSOS: "",
reSendNumber:null
}
},
computed: {
},
onLoad() {
this.initBluetoothAdapter();
},
methods: {
MandatoryWarn(flag) { //强制报警
let buffer = null;
let dic = {
ble: {
open: 0x70,
close: 0x71
},
mqtt: {
open: 1,
close: 0
}
};
this.currentTab = 'warn';
if (this.netMode == 'ble') {
dic[flag]
buffer = new ArrayBuffer(6);
let dataView = new DataView(buffer);
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x06); // 帧类型
dataView.setUint8(2, 0x01); // 包序号
dataView.setUint8(3, 0x00); // 数据长度
dataView.setUint8(4, 0x01); // 数据长度
dataView.setUint8(5, dic.ble[flag]); // 数据
} else {
buffer = {
ins_ShakeBit: [dic.mqtt[flag]]
}
}
// 发送数据
this.sendData(buffer, '/app/xinghan/device/ShakeBitSettings');
},
parseDataMQ(json) {
let staticLevelText = "";
let dic = {
"3": "高档",
"2": "中档",
"1": "低档",
"0": "关闭",
};
staticLevelText = dic[json.sta_DetectGrade];
let lightingLevelText = json.sta_LightGrade ===
1 ? '强光' :
json.sta_LightGrade ===
2 ?
'弱光' :
'关闭';
let warn = "";
dic = {
"0": "无预警",
"1": "弱预警",
"2": "中预警",
"3": "强预警",
"4": "非常强预警",
};
warn = dic[json.sta_DetectResult];
let sosText = json.sta_SOSGrade === 2 ? '红蓝模式' :
json.sta_SOSGrade === 1 ? "爆闪模式" : "关闭"
let staticWarn = json.sta_ShakeBit === 1 ? '正在报警' : '未报警';
this.receiveData.staticLevel = staticLevelText;
this.receiveData.lightingLevel = lightingLevelText;
this.receiveData.lightingTime = json.sta_PowerTime + "分钟";
this.receiveData.batteryLevel = json.sta_PowerPercent + "%";
this.receiveData.SOS = sosText;
this.receiveData.warnLevel = warn;
this.receiveData.staticWarn = staticWarn;
this.receiveData.fourGStrenth = json.sta_4gSinal;
this.receiveData.IMEI = json.sta_imei;
this.receiveData.Lon = json.sta_longitude;
this.receiveData.Lat = json.sta_latitude;
},
parseData(receive) {
var bytesToHexString =
function(bytes) {
return bytes.map(
byte =>
byte
.toString(
16)
.padStart(
2, '0')
).join(' ');
}
var todo = (
bytes) => {
let date =
new Date();
this.receiveData
.date = date
.getHours() +
':' + date
.getMinutes() +
':' + date
.getSeconds();
if (bytes[0] ==
0x55) {
try {
// 跳过帧头(第一个字节),从第二个字节开始解析
let staticLevelByte =
bytes[
1];
let staticLevelText =
'未知';
switch (
staticLevelByte
) {
case 0x65:
staticLevelText
=
'高档';
break
case 0x66:
staticLevelText
=
'中档';
break
case 0x67:
staticLevelText
=
'低档';
break
case 0x68:
staticLevelText
=
'关闭';
break
}
// 解析照明档位
let lightingLevelByte =
bytes[
2];
let lightingLevelText = lightingLevelByte ===
0x6d ? '强光' :
lightingLevelByte ===
0x6e ?
'弱光' :
'关闭';
// 解析剩余照明时间(第三和第四字节,小端序)
let lightingTime =
(bytes[
3
] <<
8
) |
bytes[
4];
// 解析剩余电量 // 电量百分比范围检查
let batteryLevelByte =
bytes[
5];
let batteryLevel =
Math
.max(0,
Math
.min(
100,
batteryLevelByte
)
);
let warn =
bytes[
6];
if (warn ==
0x00) {
warn =
'无预警';
} else if (
warn ==
0x01) {
warn =
'弱预警';
} else if (
warn ==
0x02) {
warn =
'中预警';
} else if (
warn ==
0x03) {
warn =
'强预警';
} else if (
warn ==
0x04) {
warn =
'非常强预警';
}
let staticWarn =
bytes[
7
] ==
0x01 ?
'静止报警' :
'无静止报警';
let fourGStrenth =
bytes[
8
]; //4g信号强度
let sos = bytes[9] == 0x00 ? '关闭' : bytes[9] == 0x01 ? '爆闪模式' : '红蓝模式';
this.receiveData
.staticLevel =
staticLevelText;
this.receiveData
.lightingLevel =
lightingLevelText;
this.receiveData
.lightingTime =
lightingTime +
'分钟';
this.receiveData
.batteryLevel =
batteryLevel +
'%';
this.receiveData
.warnLevel =
warn;
this.receiveData
.staticWarn =
staticWarn;
this.receiveData
.fourGStrenth =
fourGStrenth;
this.receiveData.SOS = sos;
} catch (
error) {
console
.log(
'数据解析错误:',
error
);
}
} else {
try {
let uint8Array =
new Uint8Array(
receive
.value
);
let str =
'';
for (let i =
0; i <
uint8Array
.length; i++
) {
// 将每个字节转换为对应的字符
str +=
String
.fromCharCode(
uint8Array[
i
]
);
}
if (str
.indexOf(
'mac address:'
) ==
0) {
this.receiveData.macAddress = str.split(':').slice(1).join(":")
console
.log(
'收到mac地址:',
+
this
.receiveData
.macAddress
);
} else if (
str
.indexOf(
'imei:'
) ==
0) {
this.receiveData
.IMEI =
str
.split(
':'
)[
1
];
console
.log(
'收到IEMI:',
+
this
.receiveData
.macAddress
);
this
.initMQ();
} else if (
str
.indexOf(
'longitude:'
) ==
0) {
this.receiveData
.Lon =
str
.split(
':'
)[
1
];
console
.log(
'收到经度:',
+
this
.receiveData
.macAddress
);
} else if (
str
.indexOf(
'latitude:'
) ==
0) {
this.receiveData
.Lat =
str
.split(
':'
)[
1
];
console
.log(
'收到纬度:',
+
this
.receiveData
.macAddress
);
} else {
try {
let json=JSON.parse(str);
if("staBlue_picture" in json){
//重发图片
console.log("收到重新发送图片的命令");
this.reSendNumber=json.staBlue_picture;
setTimeout(()=>{
this.sendImagePackets(this.rgb565Data,json.staBlue_picture);
},0);
return ;
}
else if("staBlue_text" in json){
//重发文本
console.log("收到重新发送文本的命令");
this.reSendNumber=json.staBlue_text;
setTimeout(()=>{
this.sendText(null,json.staBlue_text);
},0)
return ;
}
else if("staBlue_vidio" in json){
//重发视频
console.log("收到重新发送视频的命令");
videoChannel.emit("ReSendVideo", {
videoNo:json.staBlue_vidio
});
return ;
}
else if("staBlue" in json){
if(json.staBlue=="finish"){
console.log("收到设备回复,全部传输完成");
}
return ;
}
else{
console.log("无法解析该文本");
}
} catch (error) {
console.log("文本解析失败")
}
}
} catch (ex) {
console
.log(
'将数据转文本失败',
ex
);
}
}
}
let dataView =
new DataView(receive
.value);
// 转换为字节数组
let bytes = [];
for (let i = 0; i <
dataView
.byteLength; i++) {
bytes.push(dataView
.getUint8(i));
}
// 保存原始数据用于调试
this.receivedData = bytes;
console.log('接收到原始数据:',
bytesToHexString(
bytes));
todo(bytes);
},
toggleNetMode() {
if (this.netMode == 'ble') {
if (!this.receiveData.IMEI) {
uni.showModal({
title: "提示",
content: "蓝牙设备未上报IMEI,无法开启4G模式"
});
return;
}
this.netMode = 'mqtt';
return;
}
if (this.netMode == 'mqtt') {
this.netMode = 'http';
} else {
this.netMode = 'ble';
}
},
setBrightness(flag) {
if (this.brightnessStatu === flag) {
return;
}
this.brightnessStatu = flag;
let dic = {
ble: {
qiang: 0x6D,
ruo: 0x6E,
close: 0x6F
},
mqtt: {
qiang: 1,
ruo: 2,
close: 0
}
}
let buffer = null;
if (this.netMode == 'ble') {
buffer = new ArrayBuffer(6);
let dataView = new DataView(buffer);
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x01); // 帧类型
dataView.setUint8(2, 0x01); // 包序号
dataView.setUint8(3, 0x00); // 数据长度
dataView.setUint8(4, 0x01); // 数据长度
dataView.setUint8(5, dic.ble[flag]); // 数据
} else {
buffer = {
ins_LightGrade: [dic.mqtt[flag]]
};
}
// 发送数据
this.sendData(buffer, '/app/xinghan/device/LightGradeSettings');
},
setSOS(flag) {
if (this.currentSOS === flag) {
return;
}
this.currentSOS = flag;
let dic = {
ble: {
sos: 0x68,
warn: 0x69,
close: 0x6A
},
mqtt: {
sos: 2,
warn: 1,
close: 0
}
}
setTimeout(() => {
let buffer = null;
if (this.netMode == 'ble') {
buffer = new ArrayBuffer(6);
let dataView = new DataView(buffer);
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x05); // 帧类型
dataView.setUint8(2, 0x01); // 包序号
dataView.setUint8(3, 0x00); // 数据长度
dataView.setUint8(4, 0x01); // 数据长度
dataView.setUint8(5, dic.ble[flag]); // 数据
} else {
buffer = {
ins_SOSGrade: [dic.mqtt[flag]]
};
}
// 发送数据
this.sendData(buffer, '/app/xinghan/device/SOSGradeSettings');
}, 0);
},
// 蓝牙初始化
initBluetoothAdapter() {
console.log('开始初始化蓝牙适配器');
uni.openBluetoothAdapter({
success: (res) => {
this.isBluetoothOpen = true;
this.bluetoothStatus = '已开启';
console.log('蓝牙适配器初始化成功');
this.getBluetoothAdapterState();
// 自动开始搜索设备
this.startSearch();
},
fail: (err) => {
this.bluetoothStatus = '初始化失败';
console.log(`蓝牙适配器初始化失败: ${err.errMsg}`);
if (err.errCode === 10001) {
uni.onBluetoothAdapterStateChange((res) => {
if (res.available) {
this.isBluetoothOpen = true;
this.bluetoothStatus = '已开启';
console.log('蓝牙适配器已开启');
this.startSearch();
}
});
}
}
});
},
// 获取蓝牙适配器状态
getBluetoothAdapterState() {
uni.getBluetoothAdapterState({
success: (res) => {
this.isBluetoothOpen = res.available;
this.bluetoothStatus = res.available ? '已开启' : '已关闭';
if (!res.available) {
console.log('蓝牙适配器未开启');
}
}
});
},
// 开关蓝牙
toggleBluetooth() {
if (this.isBluetoothOpen) {
this.closeBluetoothAdapter();
} else {
this.initBluetoothAdapter();
}
},
// 关闭蓝牙适配器
closeBluetoothAdapter() {
this.stopSearch();
this.disconnectDevice(() => {
uni.closeBluetoothAdapter({
success: () => {
console.log("蓝牙已关闭");
this.isBluetoothOpen = false;
this.isSearching = false;
this.isConnected = false;
this.bluetoothStatus = '已关闭';
this.deviceList = [];
this.connectedDeviceId = '';
this.currentMode = '';
this.currentPage = 'deviceList';
}
})
});
},
// 开始搜索蓝牙设备
startSearch() {
if (this.isSearching) return;
this.isSearching = true;
this.deviceList = [];
this.searchTips = '搜索中...';
console.log('开始搜索蓝牙设备');
uni.startBluetoothDevicesDiscovery({
services: ["0000FFE0-0000-1000-8000-00805F9B34FB"],
allowDuplicatesKey: false,
success: (res) => {
console.log('开始搜索蓝牙设备成功');
this.onDeviceFound();
},
fail: (err) => {
this.isSearching = false;
this.searchTips = '搜索失败';
console.log(`搜索蓝牙设备失败: ${err.errMsg}`);
}
});
},
// 停止搜索蓝牙设备
stopSearch() {
this.isSearching = false;
this.searchTips = this.deviceList.length > 0 ? '搜索完成' : '未发现蓝牙设备';
uni.stopBluetoothDevicesDiscovery({
success: () => {
console.log('停止搜索蓝牙设备');
}
});
},
// 监听发现新设备
onDeviceFound() {
uni.onBluetoothDeviceFound((res) => {
let device = res.devices[0];
if (!device.name && !device.localName) return;
let index = this.deviceList.findIndex(item => item.deviceId === device.deviceId);
if (index === -1) {
this.deviceList.push({
deviceId: device.deviceId,
name: device.name || device.localName,
RSSI: device.RSSI
});
} else {
// 更新信号强度
this.deviceList[index].RSSI = device.RSSI;
}
// }
// 如果找到目标设备,自动连接
if (this.targetDeviceName.indexOf(device.name || device.localName) > -1) {
console.log(`发现目标设备: ${JSON.stringify(device)}`);
this.connectDevice(device.deviceId, device.name);
}
});
},
// 连接蓝牙设备
connectDevice(deviceId, deviceName) {
if (this.isConnected && this.connectedDeviceId === deviceId) return;
this.isLoading = true;
console.log(`开始连接设备: ${deviceId}`);
var these = this;
uni.createBLEConnection({
deviceId: deviceId,
timeout: 20000,
success: (res) => {
this.stopSearch();
this.isConnected = true;
this.connectedDeviceId = deviceId;
this.targetDeviceName = deviceName;
console.log(`连接设备 ${deviceName} 成功`);
this.enableNotification(deviceId);
// 连接成功后切换到控制面板
this.currentPage = 'controlPanel';
// setTimeout(function() {
uni.setBLEMTU({
deviceId: deviceId,
mtu: 512,
success: (info) => {
console.log("设置mtu成功")
},
fail: (ex) => {
console.log("设置mtu失败" + JSON.stringify(ex));
},
complete() {
}
})
these.getDeviceServices(deviceId);
// }, 2000)
},
fail: (err) => {
console.log(`连接设备失败: ${JSON.stringify(err)}`);
uni.showToast({
title: '连接失败',
icon: 'none'
});
},
complete: () => {
this.isLoading = false;
}
});
},
// 断开蓝牙连接
disconnectDevice(callback) {
var these = this;
var promises = [];
for (var i = 0; i < these.subscits.length; i++) {
promises.push(new Promise((succ, err) => {
uni.notifyBLECharacteristicValueChange({
deviceId: these.connectedDeviceId,
serviceId: these.subscits[i].serviceId,
characteristicId: these.subscits[i].notifyId,
state: false,
success: () => {
console.log("取消订阅成功");
succ();
},
fail: () => {
err();
}
});
}));
}
these.subscits = [];
Promise.all(promises).then(() => {
if (!this.connectedDeviceId) {
this.isConnected = false;
this.connectedDeviceId = '';
this.currentMode = '';
this.currentPage = 'deviceList';
if (callback) {
callback();
}
return;
}
setTimeout(() => {
uni.closeBLEConnection({
deviceId: this.connectedDeviceId,
success: (res) => {
console.log('蓝牙连接已断开' + JSON.stringify(res));
},
fail: (ex) => {
let arr = [{
code: 0,
remark: '正常'
},
{
code: 10000,
remark: '未初始化蓝牙适配器'
},
{
code: 10001,
remark: '当前蓝牙适配器不可用'
},
{
code: 10002,
remark: '没有找到指定设备'
},
{
code: 10003,
remark: '连接失败'
},
{
code: 10004,
remark: '没有找到指定服务'
},
{
code: 10005,
remark: '没有找到指定特征值'
},
{
code: 10006,
remark: '当前连接已断开'
},
{
code: 10007,
remark: '当前特征值不支持此操作'
},
{
code: 10008,
remark: '其余所有系统上报的异常'
},
{
code: 10009,
remark: 'Android 系统特有,系统版本低于 4.3 不支持 BLE'
},
{
code: 10010,
remark: '已连接'
},
{
code: 10011,
remark: '配对设备需要配对码'
},
{
code: 10012,
remark: '连接超时'
},
{
code: 10013,
remark: '连接 deviceId 为空或者是格式不正确'
}
];
let f = arr.find(function(v) {
return v.code == ex.code;
})
console.log("无法断开蓝牙连接:" + f.remark);
uni.showToast({
title: "无法断开蓝牙连接:" + f.remark,
icon: 'fail'
});
},
complete: () => {
this.isConnected = false;
this.connectedDeviceId = '';
this.currentMode = '';
this.currentPage = 'deviceList';
if (callback) {
callback();
}
}
});
}, 200)
}).catch().finally();
},
// 获取设备服务
getDeviceServices(deviceId) {
console.log("deviceId=" + deviceId);
var these = this;
uni.getBLEDeviceServices({
deviceId: deviceId,
success: (res) => {
console.log("发现服务" + JSON.stringify(res.services))
if (res.services.length == 0) {
setTimeout(function() {
these.getDeviceServices(deviceId);
}, 200);
return;
}
// 查找目标服务
let targetService = res.services.find(service => service.uuid.indexOf('FFE0') > -1);
if (targetService) {
console.log(`找到目标服务: ${targetService.uuid}`);
this.serviceId = targetService.uuid;
this.getDeviceCharacteristics(deviceId, targetService.uuid);
}
},
fail: (err) => {
console.log(`获取设备服务失败: ${err.errMsg}`);
}
});
},
// 获取设备特征值
getDeviceCharacteristics(deviceId, serviceId) {
var these = this;
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) => {
console.log(`获取设备特征值成功,共发现 ${res.characteristics.length} 个特征值`);
console.log(`获取设备特征值成功${JSON.stringify(res.characteristics)}`);
// 查找可写特征值
let writeChar = res.characteristics.find(char =>
char.uuid.indexOf("FFE1") >= 1
);
// 查找通知特征值
let notifyChar = res.characteristics.find(char => {
return char.properties.notify;
}
);
if (writeChar) {
console.log(`找到可写特征值: ${writeChar.uuid}`);
this.writeCharacteristicId = writeChar.uuid;
} else {
console.log(`未找到可写特征值: ${this.writeCharacteristicId}`);
}
if (notifyChar) {
console.log(`找到通知特征值: ${notifyChar.uuid}`);
this.notifyCharacteristicId = notifyChar.uuid;
console.log("serviceId=" + serviceId);
console.log("characteristicId=" + notifyChar.uuid);
this.subscits.push({
deviceId: deviceId,
serviceId: serviceId,
notifyId: notifyChar.uuid
});
setTimeout(() => {
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: notifyChar.uuid,
state: true,
success: (info) => {
console.log('启用通知成功' + JSON.stringify(info));
uni.showToast({
title: "订阅消息成功",
icon: 'success'
});
// 监听特征值变化
setTimeout(() => {
uni.onBLECharacteristicValueChange((
receive) => {
these.parseData(receive);
});
}, 100)
},
fail: (err) => {
console.error('启用通知失败', err);
}
});
}, 500);
} else {
console.log(`未找到通知特征值: ${this.notifyCharacteristicId}`);
}
},
fail: (err) => {
console.log(`获取设备特征值失败: ${err.errMsg}`);
}
});
},
// 启用通知
enableNotification(deviceId) {
// uni.notifyBLECharacteristicValueChange({
// deviceId: deviceId,
// serviceId: this.serviceId,
// characteristicId: this.notifyCharacteristicId,
// state: true,
// success: () => {
// console.log('启用通知成功');
// // 监听通知
// uni.onBLECharacteristicValueChange((res) => {
// this.handleNotification(res.value);
// });
// },
// fail: (err) => {
// console.log(`启用通知失败: ${err.errMsg}`);
// }
// });
},
// 处理通知数据
handleNotification(buffer) {
console.log("收到设备通知数据")
// 解析从设备接收的数据
let dataView = new DataView(buffer);
let result = '';
for (let i = 0; i < dataView.byteLength; i++) {
result += String.fromCharCode(dataView.getUint8(i));
}
console.log(`收到设备数据: ${result}`);
},
// 设置静电检测模式
setMode(mode) {
if (!this.isConnected) {
uni.showToast({
title: '未连接设备',
icon: 'none'
});
return;
}
if (this.currentMode === mode) return;
this.currentMode = mode;
let dic = {
ble: {
high: 0x65,
center: 0x66,
low: 0x67,
close: 0x68
},
mqtt: {
high: 3,
center: 2,
low: 1,
close: 0
}
}
// 构建数据包
let buffer = null;
if (this.netMode == 'ble') {
buffer = new ArrayBuffer(6);
let dataView = new DataView(buffer);
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x00); // 帧类型
dataView.setUint8(2, 0x01); // 包序号
dataView.setUint8(3, 0x00); // 数据长度
dataView.setUint8(4, 0x01); // 数据长度
dataView.setUint8(5, dic.ble[mode]); // 数据
} else {
buffer = {
ins_DetectGrade: [dic.mqtt[mode]]
}
}
// 发送数据
this.sendData(buffer, '/app/xinghan/device/DetectGradeSettings');
},
// 亮度调节
onBrightnessChanging(e) {
this.brightness = e.detail.value;
// 清除之前的定时器
if (this.brightnessTimer) {
clearTimeout(this.brightnessTimer);
}
// 设置新的定时器,控制发送频率
this.brightnessTimer = setTimeout(() => {
this.sendBrightness();
}, 100);
},
onBrightnessChanged(e) {
this.brightness = e.detail.value;
//this.sendBrightness();
},
sendBrightness() {
if (!this.isConnected) return;
// 构建数据包
let buffer = new ArrayBuffer(6);
let dataView = new DataView(buffer);
let data = '0x' + parseInt(this.brightness).toString(16);
console.log("亮度:" + this.brightness + ',16进制:' + data);
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x01); // 帧类型:亮度调节
dataView.setUint8(2, 0x01); // 包序号
dataView.setUint8(3, 0x00); // 数据长度
dataView.setUint8(4, 0x01); // 数据长度
dataView.setUint8(5, data); // 数据
// 发送数据
this.sendData(buffer);
},
// 导入图片
importImage() {
var these = this;
uni.chooseImage({
count: 1,
sourceType: ['album'],
success: (res) => {
this.tempImagePath = res.tempFilePaths[0];
this.hasImage = true;
this.scale = 1;
this.translateX = 0;
this.translateY = 0;
// 获取图片尺寸
uni.getImageInfo({
src: this.tempImagePath,
success: (info) => {
this.imageWidth = info.width;
this.imageHeight = info.height;
uni.navigateTo({
url: "/pages/6155/ImgCrop",
events: {
ImgCutOver: function(data) {
console.log("111111");
console.log("data=", data.length);
these.rgb565Data = these.convertToRGB565(
data);
console.log("rgb565=", these.rgb565Data
.length);
},
ImgCutOver_Path: function(data) {
these.tempImagePath = data;
}
},
success(ev) {
ev.eventChannel.emit('checkImg', {
data: res.tempFilePaths[0]
});
}
});
console.log('图片导入成功,请调整图片位置和大小');
},
fail: (err) => {
console.log(`获取图片信息失败: ${err.errMsg}`);
}
});
},
fail: (err) => {
console.log(`选择图片失败: ${err.errMsg}`);
}
});
},
// 发送开机画面
sendSplashImage() {
var these = this;
if (!these.rgb565Data) {
uni.showToast({
title: '请先导入图片',
icon: 'none'
});
return;
}
if (this.netMode == 'http') {
this.sendImgHttp();
return;
}
uni.showLoading({
title: '处理图片中...',
mask: true
});
let res = {
data: these.rgb565Data
};
// 将图片转换为RGB565格式并发送
this.convertAndSendImage(res).then(() => {
if (this.netMode == 'ble') {
uni.hideLoading();
console.log('开机画面发送完成');
uni.showToast({
title: '开机画面发送完成',
icon: 'success'
});
} else {
this.isSending = true;
}
}).catch((error) => {
uni.hideLoading();
console.log(`图片发送失败: ${error}`);
uni.showToast({
title: '图片发送失败',
icon: 'none'
});
});
},
// 转换图片并发送
convertAndSendImage(res) {
return new Promise((resolve, reject) => {
try {
this.processAndSendImageData(res.data).then(
resolve).catch(reject);
} catch (ex) {
console.log("出现异常" + JSON.stringify(ex))
}
});
},
// 处理像素数据并发送
processAndSendImageData(pixels) {
return new Promise((resolve, reject) => {
// 创建RGB565格式的像素数据
// 分包发送
this.sendImagePackets(pixels).then(resolve).catch(reject);
});
},
// 将RGBA转换为RGB565
convertToRGB565(pixels) {
var rgbaToRgb565 = function(r, g, b, a = 255) {
// 忽略透明度(如果需要考虑透明度,请根据需求处理)
// 将8位颜色值压缩到RGB565对应的位数
let r5 = Math.round((r / 255) * 31); // 5位0-31
let g6 = Math.round((g / 255) * 63); // 6位0-63
let b5 = Math.round((b / 255) * 31); // 5位0-31
// 合并为16位整数
return (r5 << 11) | (g6 << 5) | b5;
}
let result = new Uint16Array(160 * 80);
let index = 0;
console.log("pixels.length=" + pixels.length);
for (let i = 0; i < pixels.length; i += 4) {
let r = pixels[i];
let g = pixels[i + 1];
let b = pixels[i + 2];
// 转换为RGB565 (R:5bit, G:6bit, B:5bit)
let rgb565 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
//let rgb565=rgbaToRgb565(r,g,b);
result[index++] = rgb565;
}
return result;
},
// 分包发送图片数据
sendImagePackets(imageData, ReSendNo) {
if (this.netMode == 'ble') {
return new Promise((resolve, reject) => {
this.isSending = true;
this.progress = 0;
this.currentPacket = 0;
// 总数据包数
let totalPackets = 52;
let currentPacket = 1;
if (ReSendNo) {
this.currentPacket = ReSendNo;
this.currentPacket = ReSendNo;
totalPackets = ReSendNo;
currentPacket=ReSendNo;
}
// 发送单个数据包
let sendNextPacket = () => {
if (currentPacket > totalPackets) {
this.isSending = false;
if (!ReSendNo) {
this.bleSendComplete();
}else{
// this.reSendNumber="";
}
resolve();
return;
}
// 计算当前包的数据
let packetSize = 250;
// if (currentPacket <= 51) {
// packetSize = 250; // 前51个包每个500字节
// } else {
// packetSize = 50; // 最后一个包100字节
// }
// 创建数据包
let startIndex = (currentPacket - 1) * 250;
let endIndex = Math.min(startIndex + packetSize, imageData.length);
if (startIndex > endIndex) {
return;
}
//console.log("imageData="+imageData.length);
let packetData = imageData.slice(startIndex,
endIndex);
// 构建数据包
let bufferSize = 505; // 5 + packetData.length * 2; // 头部5字节 + 数据部分
let buffer = new ArrayBuffer(bufferSize);
let dataView = new DataView(buffer);
// 填充头部
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x02); // 帧类型:开机画面
dataView.setUint8(2, '0x' + currentPacket.toString(16).padStart(2, '0')); // 包序号
// dataView.setUint8(3, 0x01);
// dataView.setUint8(4, 0xFF);
if (packetData.length == 250) {
dataView.setUint8(3, 0x01);
dataView.setUint8(4, 0xF4);
} else {
dataView.setUint8(3, 0x00);
dataView.setUint8(4, 0x64);
}
// 填充数据每个RGB565值占2字节
for (let i = 0; i < packetData.length; i++) {
dataView.setUint16(5 + i * 2, packetData[i], false); // 大端字节序
}
if (currentPacket == 52) {
for (var i = 105; i < bufferSize; i++) {
dataView.setUint8(i, 0xFF);
}
}
let inteval = parseInt(this.inteval ? this.inteval : 0);
//发送数据包
this.sendData(buffer).then(() => {
// 更新进度
this.currentPacket = currentPacket;
this.progress = Math.round((currentPacket / totalPackets) * 52);
console.log("发送第" + currentPacket + "包成功");
// 发送下一个包(添加延迟避免蓝牙缓冲区溢出)
currentPacket++;
setTimeout(sendNextPacket, inteval);
}).catch(err => {
if (err.code == '10007') {
console.log("发送第" + currentPacket + "包失败");
setTimeout(sendNextPacket, inteval);
} else {
this.isSending = false;
uni.hideLoading();
reject(err);
}
});
};
//牵着你的手
var HoldYouHand = () => {
var str = "picture transmit start"; //握手的协议字符串
// 1. 创建 ArrayBuffer 和 DataView
let buffer = new ArrayBuffer(str.length);
let dataView = new DataView(buffer);
// 2. 将字符串转换为 ASCII 码并写入 DataView
for (let i = 0; i < str.length; i++) {
dataView.setUint8(i, str.charCodeAt(i));
}
this.sendData(buffer).then(() => {
// 开始发送第一个包
console.log("握手成功了,等待200ms");
setTimeout(sendNextPacket, 120);
}).catch(err => {
console.log("握手没有成功");
reject(err);
});
}
if(ReSendNo){
sendNextPacket();
}else{
HoldYouHand();
}
});
} else {
return new Promise((resolve, reject) => {
let woshou = () => {
let buffer = {
ins_PicTrans: [0]
};
this.sendData(buffer).then(() => {
console.log("发送开机画面握手信息成功了")
resolve();
}).catch(ex => {
console.log("发送开机画面握手信息失败");
reject();
});
}
woshou();
});
}
},
// 文字输入变化
onTextChanged(index) {
console.log(`${index + 1}行文字已更新`);
},
sendUrgent(type) {
if (this.netMode !== 'ble') {
let cmd = {
"ins_BreakNews": [0]
}
this.sendData(cmd).catch((ex) => {
console.log("发送紧急通知握手失败", ex);
});
return;
}
let sendTxt = (text) => {
console.log("开始发送数据:" + text);
return new Promise((resolve, reject) => {
let gbkData = this.convertToGBK(text);
console.log("gbkData=", gbkData);
// 构建数据包
let bufferSize = gbkData.length / 2; // 头部4字节 + 数据部分
let buffer = new ArrayBuffer(bufferSize);
let dataView = new DataView(buffer);
let index = 0;
// 填充数据
let arr = [];
for (var i = 0; i < gbkData.length; i += 2) {
let value = parseInt(gbkData[i] + "" + gbkData[i + 1], 16);
console.log("value=" + value);
dataView.setUint8(index, value);
index++;
arr.push(value);
}
console.log("arr=", arr.join(" "));
this.sendData(buffer).then(() => {
resolve();
}).catch(err => {
reject(err);
});
});
}
let dic = ["the first line:" + this.warnLines[0], "the second line:" + this.warnLines[1]]
let promis = new Array();
for (var i = 0; i < dic.length; i++) {
promis.push(sendTxt(dic[i]));
}
Promise.allSettled(promis).then((results) => {
let cnt = 0;
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功结果:', result.value);
cnt += 1;
} else {
console.log('失败原因:', result.reason);
}
});
uni.showModal({
title: '提示',
content: "发送包数量:" + dic.length + ",成功数量:" + cnt
})
});
},
// 发送文字
sendText(type, ReSendNo) {
if (!this.isConnected) {
uni.showToast({
title: '未连接设备',
icon: 'none'
});
return;
}
this.isSendingText = true;
this.textProgress = 0;
this.currentTextPacket = 0;
// 总数据包数
let totalPackets = this.textLines.length;
let currentPacket = 1;
if (ReSendNo) {
this.textProgress = ReSendNo - 1;
this.currentTextPacket = ReSendNo - 1;
totalPackets = ReSendNo;
currentPacket = ReSendNo;
}
// 发送单个数据包
let sendNextPacket = () => {
if (currentPacket > totalPackets) {
this.isSendingText = false;
console.log('文字发送完成');
if (!ReSendNo) {
this.bleSendComplete();
}else{
// this.reSendNumber="";
}
uni.showToast({
title: '发送成功',
icon: 'success'
});
return;
}
// 获取当前行文字
let text = this.textLines[currentPacket - 1] || '';
// 将文字转换为GBK编码
let gbkData = this.convertToGBK(text);
// 构建数据包
let bufferSize = 5 + gbkData.length / 2; // 头部4字节 + 数据部分
let buffer = new ArrayBuffer(bufferSize);
let dataView = new DataView(buffer);
// 填充头部
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x03); // 帧类型:文字
dataView.setUint8(2, currentPacket.toString(16)); // 包序号
dataView.setUint16(3, (text.length * 2).toString(16)); // 数据长度
let index = 0;
for (var i = 0; i < gbkData.length; i += 2) {
let value = parseInt(gbkData[i] + "" + gbkData[i + 1], 16);
dataView.setUint8(5 + index, value);
index++;
}
// 发送数据包
this.sendData(buffer).then(() => {
// 更新进度
this.currentTextPacket = currentPacket;
this.textProgress = Math.round((currentPacket / totalPackets) * 100);
console.log(`发送文字数据包 ${currentPacket}/${totalPackets}: ${text}`);
// 发送下一个包
currentPacket++;
setTimeout(sendNextPacket, this.inteval ? this.inteval : 0);
}).catch(err => {
this.isSendingText = false;
uni.showToast({
title: '文字发送失败',
icon: 'none'
});
});
};
// 开始发送第一个包
//牵着你的手
var HoldYouHand = () => {
var str = "word transmit start"; //握手的协议字符串
let buffer = null;
if (this.netMode == 'ble') {
buffer = new ArrayBuffer(str.length);
let dataView = new DataView(buffer);
for (let i = 0; i < str.length; i++) {
dataView.setUint8(i, str.charCodeAt(i));
}
} else if (this.netMode == 'mqtt') {
buffer = {
ins_TexTrans: [0]
}
} else {
buffer = {
deviceId: '1942386314582163705',
deviceImei: '862419074249128',
name: this.textLines[0],
position: this.textLines[1],
unitName: this.textLines[2],
code: this.textLines[3]
};
this.sendTxtHttp(buffer, '/app/xinghan/device/registerPersonInfo').then((res) => {
console.log("res=", res);
if (res.code == 200) {
uni.showToast({
title: '成功',
icon: 'success'
});
} else {
console.log("失败了", res);
}
}).catch((ex) => {
console.log("出现异常", ex);
});
return;
}
this.sendData(buffer, '/app/xinghan/device/uploadLogo').then(() => {
// 开始发送第一个包
if (this.netMode == 'ble') {
console.log("蓝牙握手成功了,等待200ms");
setTimeout(sendNextPacket, 120);
}
}).catch(err => {
console.log("握手没有成功");
uni.showToast({
title: '握手没有成功',
icon: 'none'
});
});
}
if(!ReSendNo){
HoldYouHand();
}
else{
sendNextPacket();
}
},
bleSendComplete() {
var str = "transmit complete"; //握手的协议字符串
let buffer = new ArrayBuffer(str.length);
let dataView = new DataView(buffer);
for (let i = 0; i < str.length; i++) {
dataView.setUint8(i, str.charCodeAt(i));
}
setTimeout(()=>{
this.sendData(buffer).then(() => {
console.log("完成指令发送成功");
}).catch(err => {
console.log("完成指令发送失败");
});
},500)
},
// 将文字转换为GBK编码使用第三方库或API
convertToGBK(text) {
let arr = gbk.encode(text)
console.log("arr=", arr.join(" "));
let utf8Data = gbk.arr2hex(arr);
return utf8Data; // 实际项目中替换为GBK编码实现
},
// 添加日志
addLog(message) {
},
checkVideo: function() {
uni.chooseVideo({
sourceType: ['album', 'camera'],
compressed: true,
maxDuration: 2,
camera: 'back',
success: (res) => {
this.videoPath = res.tempFilePath;
this.videoWidth = res.width;
this.videoHeight = res.height;
this.videoDuration = res.duration;
}
})
},
sendVideo: function() {
uni.showLoading({
title: "正在处理视频"
});
setTimeout(() => {
this.$refs.compARef.shotVideoClick(0);
}, 0);
},
shotVideoClick: function(array) {
},
gotoSendVideo: function() {
this.currentTab = 'flash';
uni.navigateTo({
url: "/pages/BlueTooth/ModeSetting/VideoSend_670",
success: (res) => {
let channel = res.eventChannel;
videoChannel=channel;
channel.emit("receiveDevice", {
connectedDeviceId: this.connectedDeviceId,
serviceId: this.serviceId,
writeCharacteristicId: this.writeCharacteristicId,
notifyCharacteristicId: this.notifyCharacteristicId,
netMode: this.netMode,
IMEI: this.receiveData.IMEI
});
},
fail(ex) {
console.log("出现异常,",ex);
}
})
},
initMQ() {
return new Promise((resolve, reject) => {
if (mqttClient) {
resolve();
return;
}
mqttClient = new MqttClient();
mqttClient.connect(() => {
// 订阅来自设备的状态更新
const statusTopic = `A/${this.receiveData.IMEI}`;
mqttClient.subscribe(statusTopic, (payload, receive) => {
try {
let json = JSON.parse(payload);
let keys = Object.keys(json);
console.log("keys=", keys);
if (keys.indexOf('sta_DetectGrade') > -1) { //上报的报文
console.log("收到设备上报的数据", payload);
this.parseDataMQ(json);
} else if (keys.indexOf('sta_PicTrans') > -1) { //发送开机画面
console.log("收到开机画面答复", payload);
this.SendImgMQ(json);
} else if (keys.indexOf('sta_TexTrans') > -1) { //发送文本信息
console.log("收到文本回复", payload);
this.SendTxtMQ(json);
} else if (keys.indexOf('sta_BreakNews') > -1) { //紧急通知
console.log("收到紧急消息回复", payload);
this.sendUrgentMQ(json);
} else {
console.log("收到不能处理的数据", payload);
}
} catch (error) {
console.log("无法解析此消息", error);
}
});
resolve();
});
});
},
// 发送数据通用方法
sendData(buffer, url) {
if (this.netMode == 'ble') {
return this.sendBle(buffer);
} else if (this.netMode == 'mqtt') {
buffer = JSON.stringify(buffer);
return this.sendMQ(buffer);
} else {
return this.sendRequest(buffer, url);
}
},
sendBle(buffer) {
return new Promise(async (resolve, reject) => {
var promise = new Promise((succ, err) => {
uni.writeBLECharacteristicValue({
deviceId: this.connectedDeviceId,
serviceId: this.serviceId,
characteristicId: this.writeCharacteristicId,
value: buffer,
writeType: plus.os.name == 'iOS' ? 'write' : 'writeNoResponse',
success: () => {
//console.log("发送数据成功");
succ();
},
fail: (ex) => {
console.log("发送数据失败", ex);
err(ex);
},
complete: function() {
//console.log("123456");
}
});
});
if (plus.os.name == 'iOS') {
function timeout(ms) {
return new Promise((_, err) => {
setTimeout(() => {
err({
code: -1,
errMsg: '超时了'
})
}, ms);
});
}
Promise.race([promise, timeout(this.inteval ? this.inteval : 0)]).then(resolve)
.catch((
ex) => {
console.log("ex=", ex);
if (ex.code == -1) {
resolve();
} else {
reject(ex);
}
}).finally(() => {
console.log("完成了")
});
} else {
promise.then(resolve).catch(reject);
}
});
},
sendImgHttp(data, url) {
uni.showLoading({
title: "上传中"
})
return new Promise((resolve, reject) => {
const token = uni.getStorageSync('token');
const clientid = uni.getStorageSync('clientID');
let config = {
header: {}
};
if (token) {
config.header['Authorization'] = 'Bearer ' + token;
config.header['clientid'] = clientid;
}
uni.uploadFile({
url: baseURL + '/app/xinghan/device/uploadLogo',
filePath: this.tempImagePath,
name: 'file',
formData: {
deviceId: '1942386314582163705',
deviceImei: '862419074249128'
},
header: config.header,
timeout: 600000,
fail: (ex) => {
//console.log("上传视频失败" + JSON.stringify(ex));
uni.showToast({
title: "视频文件上传失败了,请检查网络连接",
icon: 'fail'
});
},
success: (res) => {
console.log("上传完成", res);
},
complete: () => {
uni.hideLoading();
}
});
});
},
sendTxtHttp(data, url) {
return new Promise((resolve, reject) => {
request({
url: url,
method: 'POST',
data: data
}).then((res) => {
console.log("res=", res);
if (res.code == 200) {
console.log("2000000")
resolve(res);
return;
} else {
console.log("22222222")
reject(res);
}
}).catch((ex) => {
console.log("ex=", ex);
reject(ex);
});
});
},
sendRequest(message, url) {
return new Promise((resolve, reject) => {
try {
var keys = Object.keys(message);
let data = message[keys[0]];
request({
url: url,
method: 'POST',
data: {
deviceId: '1942386314582163705',
deviceImei: '862419074249128',
instructValue: data[0]
}
}).then((res) => {
console.log("res=", res);
if (res.code == 200) {
resolve();
return;
}
reject(res);
}).catch((ex) => {
console.log("ex=", ex);
reject(ex);
});
} catch (ex) {
console.log("ex=", ex);
reject(ex)
}
});
},
sendMQ(message) {
const topic = `B/${this.receiveData.IMEI}`;
return new Promise((resolve, reject) => {
if (!mqttClient) {
reject("MQTT未连接");
return;
}
try {
let flag = mqttClient.publish(topic, message, {
qos: 1
});
if (flag) {
resolve();
return;
}
reject("MQTT未连接无法发布消息");
} catch (error) {
mqttClient.unsubscribe(`A/${this.receiveData.IMEI}`);
mqttClient.disconnect();
this.initMQ();
reject(error);
}
});
},
calCRC32(data) {
// 生成CRC32表
let crcTable = [];
for (let i = 0; i < 256; i++) {
let c = i;
for (let j = 0; j < 8; j++) {
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
}
crcTable[i] = c;
}
// 计算CRC32值
let crc = 0xFFFFFFFF;
for (let i = 0; i < data.length; i++) {
crc = (crc >>> 8) ^ crcTable[(crc ^ data[i]) & 0xFF];
}
crc ^= 0xFFFFFFFF; // 最终异或
return [
(crc >>> 24) & 0xFF, // 最高位字节
(crc >>> 16) & 0xFF,
(crc >>> 8) & 0xFF,
crc & 0xFF // 最低位字节
];
},
SendImgMQ(json) { //发送开机画面MQ版本
if (this.netMode != 'mqtt') {
return;
}
try {
let val = json.sta_PicTrans;
if (val >= 1 && val <= 100) {
let imageData = this.rgb565Data;
console.log("imageData.length=" + imageData.length);
let packetSize = 128;
let startIndex = (val - 1) * packetSize;
let endIndex = Math.min(startIndex + packetSize, imageData.length);
if (startIndex > endIndex) {
console.log("发送已完成了")
return;
}
let packetData = imageData.slice(startIndex, endIndex);
let arr = [];
let bufferSize = packetData.length * 2; // 头部5字节 + 数据部分
let buffer = new ArrayBuffer(bufferSize);
let dataView = new DataView(buffer);
for (let i = 0; i < packetData.length; i++) {
dataView.setUint16(i * 2, packetData[i], false); // 大端字节序
arr.push(dataView.getUint8(i * 2));
arr.push(dataView.getUint8(i * 2 + 1));
}
buffer = {
ins_PicTrans: [val]
};
console.log("arr=", arr.length);
let crc32 = this.calCRC32(arr);
arr = arr.concat(crc32);
buffer.ins_PicTrans = buffer.ins_PicTrans.concat((arr));
this.sendData(buffer).then(() => {
console.log("发送开机画面第" + val + "包成功");
this.currentPacket = val;
this.progress = val;
}).catch((ex) => {
console.log("发送开机画面第" + val + "包失败,正在重试,", ex);
// this.SendImgMQ(json);
});
} else if (val == 'great!') {
uni.showToast({
icon: 'success',
title: "发送成功"
});
uni.hideLoading();
this.isSending = false;
}
} catch (error) {
console.log("error=", error)
}
},
SendTxtMQ(json) { //发送文字MQ版本
if (this.netMode != 'mqtt') {
return;
}
let val = json.sta_TexTrans;
this.currentTextPacket = 4;
if (val === 1 || val === 2 || val === 3 || val === 4) {
this.textProgress = Math.round(((val - 1) / this.currentTextPacket) * 100);
let text = this.textLines[val - 1] || '';
let gbkData = this.convertToGBK(text);
let buffer = {
ins_TexTrans: [val]
}
for (var i = 0; i < gbkData.length; i += 2) {
let value = parseInt(gbkData[i] + "" + gbkData[i + 1], 16);
buffer.ins_TexTrans.push(value);
}
this.sendData(buffer).then(() => {
console.log("发送成功第" + val + "包文本数据");
}).catch((ex) => {
console.log("发送失败第" + val + "包文本数据,正在重试,", ex);
this.SendTxtMQ(json); //重试
});
return;
}
if (val === 'genius!') {
this.textProgress = 100;
uni.showToast({
icon: "success",
title: "发送成功"
})
console.log("全部文本数据发送成功");
}
},
sendUrgentMQ(json) { //发送紧急通知MQ版本
if (this.netMode != 'mqtt') {
return;
}
let val = json.sta_BreakNews;
if (val === 1 || val === 2) {
let text = this.warnLines[val - 1] || '';
let gbkData = this.convertToGBK(text);
let buffer = {
ins_BreakNews: [val]
}
for (var i = 0; i < gbkData.length; i += 2) {
let value = parseInt(gbkData[i] + "" + gbkData[i + 1], 16);
buffer.ins_BreakNews.push(value);
}
this.sendData(buffer).then(() => {
console.log("发送成功第" + val + "包数据成功");
}).catch((ex) => {
console.log("发送失败第" + val + "包,正在重试,", ex);
this.sendUrgentMQ(json); //重试
});
return;
}
if (val == 'cover!') {
uni.showToast({
icon: "success",
title: "发送成功"
})
console.log("紧急通知全部发送成功");
}
}
},
onUnload() {
this.closeBluetoothAdapter();
}
}
</script>
<style>
.receivLog {
padding: 20rpx;
width: 100%;
height: auto;
box-sizing: border-box;
border: 2rpx solid #e5e5e5;
border-radius: 20rpx;
font-size: 28rpx;
color: #2e2e2e;
}
.container {
padding: 20rpx;
background-color: #f8f8f8;
min-height: 100vh;
}
.title {
font-size: 36rpx;
font-weight: bold;
text-align: center;
padding: 20rpx 0;
color: #333;
}
.status-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15rpx 0;
border-bottom: 1rpx solid #eee;
}
.function-tabs {
display: flex;
margin: 20rpx 0;
}
.tab-btn {
flex: 1;
padding: 15rpx 0;
text-align: center;
font-size: 28rpx;
border-bottom: 2rpx solid #eee;
}
.tab-btn.active {
color: #409eff;
border-bottom: 2rpx solid #409eff;
}
/* 设备列表样式 */
.search-area {
padding: 20rpx 0;
}
.search-btn {
background-color: #409eff;
color: white;
}
.tips {
color: #999;
font-size: 24rpx;
margin-top: 10rpx;
}
.devices-list {
margin-top: 20rpx;
}
.device-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background-color: white;
border-radius: 10rpx;
margin-bottom: 15rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.device-item.active {
background-color: #e8f3ff;
border-left: 6rpx solid #409eff;
}
.device-name {
font-size: 28rpx;
max-width: 500rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.device-rssi {
color: #999;
font-size: 24rpx;
}
.device-status {
color: #409eff;
font-size: 24rpx;
}
/* 模式设置样式 */
.modes-area {
margin-top: 30rpx;
}
.mode-title {
font-size: 28rpx;
font-weight: bold;
margin-bottom: 20rpx;
color: #333;
}
.modes-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.mode-item {
width: 30%;
padding: 20rpx 0;
background-color: white;
border-radius: 10rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.mode-item.selected {
background-color: #e8f3ff;
border: 2rpx solid #409eff;
}
.mode-icon {
font-size: 40rpx;
margin-bottom: 10rpx;
}
.mode-name {
font-size: 26rpx;
}
/* 亮度调节样式 */
.brightness-area {
margin-top: 30rpx;
padding: 20rpx;
background-color: white;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.brightness-title {
font-size: 28rpx;
font-weight: bold;
margin-bottom: 20rpx;
color: #333;
}
.brightness-value {
font-size: 36rpx;
text-align: center;
margin-bottom: 30rpx;
color: #409eff;
}
.brightness-slider {
width: 100%;
}
/* 开机画面样式 */
.splash-controls {
display: flex;
justify-content: center;
gap: 20rpx;
margin: 20rpx 0;
}
.splash-preview {
display: flex;
flex-direction: column;
align-items: center;
}
.splash-frame {
height: 320rpx;
width: 640rpx;
border: 2rpx solid #409eff;
border-radius: 8rpx;
overflow: hidden;
position: relative;
background-color: #f0f0f0;
}
.splash-frame image {
width: 100%;
height: 100%;
}
.empty-frame {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 24rpx;
}
.frame-info {
margin-top: 15rpx;
color: #666;
font-size: 24rpx;
}
.sending-progress {
margin-top: 30rpx;
width: 100%;
}
.progress-bar {
height: 12rpx;
background-color: #e0e0e0;
border-radius: 6rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #409eff;
transition: width 0.3s;
}
/* 文字设置样式 */
.text-inputs {
margin-top: 30rpx;
background-color: white;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
padding: 20rpx;
}
.text-line {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.text-line text {
width: 80rpx;
font-size: 26rpx;
}
.text-line input {
flex: 1;
height: 60rpx;
padding: 0 15rpx;
border: 1rpx solid #eee;
border-radius: 8rpx;
font-size: 26rpx;
}
.send-text-btn {
margin-top: 20rpx;
background-color: #409eff;
color: white;
}
/* 日志区域样式 */
.log-area {
margin-top: 30rpx;
}
.log-title {
font-size: 28rpx;
font-weight: bold;
margin-bottom: 15rpx;
color: #333;
}
.log-content {
height: 200rpx;
background-color: white;
border-radius: 10rpx;
padding: 15rpx;
font-size: 24rpx;
color: #666;
}
.log-item {
margin-bottom: 8rpx;
}
.btn {
padding: 10rpx 20rpx;
border-radius: 8rpx;
font-size: 26rpx;
text-align: center;
}
.btn[disabled] {
opacity: 0.5;
}
.txt {
border: 1px solid #000000;
}
.w50 {
width: 50%;
color: #656363;
font-size: 26prx;
height: 60rpx;
}
.fleft {
float: left;
}
.clear {
clear: both;
}
</style>