2837 lines
69 KiB
Vue
2837 lines
69 KiB
Vue
<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> |