537 lines
13 KiB
Vue
537 lines
13 KiB
Vue
![]() |
<template>
|
|||
|
<view class="content">
|
|||
|
<canvas canvas-id="flashCanvas"
|
|||
|
style="width: 160px; height: 80px; z-index: 9999;position: fixed; top:-9999px;left:-9999px;"></canvas>
|
|||
|
|
|||
|
<view>
|
|||
|
<view>
|
|||
|
选择的视频:{{videoPath}}</view>
|
|||
|
</view>
|
|||
|
<view>
|
|||
|
重发包序号:{{reSendNumber}}
|
|||
|
</view>
|
|||
|
<view>
|
|||
|
<text>发送间隔</text>
|
|||
|
<input type="text" v-model="inteval" />
|
|||
|
</view>
|
|||
|
<view>
|
|||
|
<button @click="checkVideo">选择视频</button>
|
|||
|
|
|||
|
<button @click="uploadVideo">发送</button>
|
|||
|
</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>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
import Common from '../../../utils/Common';
|
|||
|
var mqttClient=null;
|
|||
|
export default {
|
|||
|
data() {
|
|||
|
return {
|
|||
|
videoPath: '',
|
|||
|
inteval: 0,
|
|||
|
progress: 0,
|
|||
|
currentPacket: 0,
|
|||
|
totalPackets: 100,
|
|||
|
|
|||
|
connectedDeviceId: '',
|
|||
|
serviceId: '0xFFE1',
|
|||
|
writeCharacteristicId: '0xFFE1',
|
|||
|
notifyCharacteristicId: '0xFFE2',
|
|||
|
isSending: "",
|
|||
|
textProgress: "",
|
|||
|
netMode:"ble",
|
|||
|
IMEI:"",
|
|||
|
|
|||
|
|
|||
|
imgs: [],
|
|||
|
videoWidth: 320,
|
|||
|
videoHeight: 160,
|
|||
|
videoDuration: 2,
|
|||
|
|
|||
|
hexArray: [],
|
|||
|
reSendNumber:""
|
|||
|
}
|
|||
|
},
|
|||
|
onLoad() {
|
|||
|
const eventChannel = this.getOpenerEventChannel();
|
|||
|
|
|||
|
eventChannel.on('receiveDevice', (data) => {
|
|||
|
this.connectedDeviceId = data.connectedDeviceId;
|
|||
|
this.serviceId = data.serviceId;
|
|||
|
this.writeCharacteristicId = data.writeCharacteristicId;
|
|||
|
this.notifyCharacteristicId = data.notifyCharacteristicId;
|
|||
|
this.netMode=data.netMode;
|
|||
|
mqttClient=data.mqttClient;
|
|||
|
this.IMEI=data.IMEI;
|
|||
|
});
|
|||
|
|
|||
|
eventChannel.on('ReSendVideo',(data)=>{
|
|||
|
//重新发送某一包
|
|||
|
this.reSendNumber=data.videoNo;
|
|||
|
setTimeout(()=>{
|
|||
|
this.shotVideoClick(this.hexArray,'rgb565',data.videoNo);
|
|||
|
},0);
|
|||
|
|
|||
|
|
|||
|
});
|
|||
|
},
|
|||
|
methods: {
|
|||
|
checkVideo: function() {
|
|||
|
uni.chooseVideo({
|
|||
|
sourceType: ['album', 'camera'],
|
|||
|
compressed: false,
|
|||
|
maxDuration: 2,
|
|||
|
camera: 'back',
|
|||
|
success: (res) => {
|
|||
|
this.videoPath = res.tempFilePath;
|
|||
|
|
|||
|
this.imgs = [];
|
|||
|
this.hexArray = [];
|
|||
|
|
|||
|
this.videoWidth = res.width;
|
|||
|
this.videoHeight = res.height;
|
|||
|
this.videoDuration = res.duration;
|
|||
|
console.log("视频宽:" + res.width + ",视频高:" + res.height + ",视频时长:" + res.duration);
|
|||
|
}
|
|||
|
})
|
|||
|
},
|
|||
|
|
|||
|
uploadVideo: function() {
|
|||
|
|
|||
|
if (this.hexArray.length > 0) {
|
|||
|
console.log("开始处理,无需上传");
|
|||
|
this.shotVideoClick(this.hexArray, 'rgb565');
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
if(!this.videoPath){
|
|||
|
uni.showToast({
|
|||
|
title: "请选择视频",
|
|||
|
icon: 'fail'
|
|||
|
})
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
console.log("正在上传视频");
|
|||
|
uni.showLoading({
|
|||
|
title: "上传中"
|
|||
|
});
|
|||
|
|
|||
|
let p2=new Promise((resolve,reject)=>{
|
|||
|
let start = new Date();
|
|||
|
console.log("Common.baseURL="+Common.baseURL);
|
|||
|
uni.uploadFile({
|
|||
|
url:Common.baseURL+'video/upload',
|
|||
|
filePath: this.videoPath,
|
|||
|
name: 'file',
|
|||
|
header: {
|
|||
|
"Method": "POST",
|
|||
|
"Content-Type": "multipart/form-data"
|
|||
|
},
|
|||
|
timeout: 600000,
|
|||
|
fail: (ex) => {
|
|||
|
//console.log("上传视频失败" + JSON.stringify(ex));
|
|||
|
uni.showToast({
|
|||
|
title: "视频文件上传失败了,请检查网络连接",
|
|||
|
icon: 'fail'
|
|||
|
})
|
|||
|
uni.hideLoading();
|
|||
|
reject(ex);
|
|||
|
},
|
|||
|
success: (res) => {
|
|||
|
let end = new Date();
|
|||
|
var diff = (end.getTime() - start.getTime()) / 1000;
|
|||
|
let s = diff % 60;
|
|||
|
let m = (diff - s) / 60;
|
|||
|
console.log("上传完成,耗时:" + m + "分" + s + "秒");
|
|||
|
uni.hideLoading();
|
|||
|
resolve(res);
|
|||
|
}
|
|||
|
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
let p1=this.HoldYouHand();
|
|||
|
|
|||
|
Promise.all([p2,p1]).then((arr)=>{
|
|||
|
|
|||
|
if(arr[1]===true){
|
|||
|
let res=arr[0];
|
|||
|
res = JSON.parse(res.data);
|
|||
|
|
|||
|
if (res.data) {
|
|||
|
this.hexArray = res.data;
|
|||
|
uni.showLoading({
|
|||
|
title: "正在发送"
|
|||
|
});
|
|||
|
|
|||
|
setTimeout(() => {
|
|||
|
this.shotVideoClick(res.data, 'rgb565');
|
|||
|
}, 0)
|
|||
|
|
|||
|
} else {
|
|||
|
console.log("res")
|
|||
|
uni.showModal({
|
|||
|
content: "服务器未返回RGB565数据",
|
|||
|
title: '错误'
|
|||
|
})
|
|||
|
}
|
|||
|
}else{
|
|||
|
uni.showModal({
|
|||
|
content:"与设备握手失败了",
|
|||
|
title:"错误"
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
},
|
|||
|
|
|||
|
shotVideoClick: function(array, type,ReSendNo) {
|
|||
|
//console.log("处理视频完成", array);
|
|||
|
//console.log("type=" + type)
|
|||
|
//console.log("array=", array);
|
|||
|
this.imgs = array;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
var sendImagePackets = (imageData) => {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
|
|||
|
this.isSending = true;
|
|||
|
this.progress = 0;
|
|||
|
this.currentPacket = 0;
|
|||
|
|
|||
|
// 总数据包数
|
|||
|
var totalPackets = 1536;
|
|||
|
this.totalPackets = totalPackets;
|
|||
|
let currentPacket = 1;
|
|||
|
if(ReSendNo){
|
|||
|
this.progress = ReSendNo-1;
|
|||
|
this.currentPacket = ReSendNo-1;
|
|||
|
currentPacket=ReSendNo;
|
|||
|
totalPackets=ReSendNo;
|
|||
|
this.totalPackets=ReSendNo;
|
|||
|
}
|
|||
|
// 发送单个数据包
|
|||
|
const sendNextPacket = () => {
|
|||
|
////console.log("currentPacket="+currentPacket+",imageData.length="+imageData.length);
|
|||
|
if (currentPacket > totalPackets) {
|
|||
|
this.isSending = false;
|
|||
|
if(!ReSendNo){
|
|||
|
this.bleSendComplete();
|
|||
|
}else{
|
|||
|
// this.reSendNumber="";
|
|||
|
}
|
|||
|
|
|||
|
resolve();
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 计算当前包的数据
|
|||
|
let packetSize = 250;
|
|||
|
if (type == 'rgb565') {
|
|||
|
packetSize = 500;
|
|||
|
}
|
|||
|
|
|||
|
// 创建数据包
|
|||
|
const startIndex = (currentPacket - 1) * packetSize;
|
|||
|
const endIndex = Math.min(startIndex + packetSize, imageData.length);
|
|||
|
if (startIndex > endIndex) {
|
|||
|
resolve();
|
|||
|
return;
|
|||
|
}
|
|||
|
////console.log("111111");
|
|||
|
const packetData = imageData.slice(startIndex, endIndex);
|
|||
|
|
|||
|
// 构建数据包
|
|||
|
////console.log("packetData.length"+packetData.length);
|
|||
|
const bufferSize = 506; // 头部5字节 + 数据部分
|
|||
|
const buffer = new ArrayBuffer(bufferSize);
|
|||
|
const dataView = new DataView(buffer);
|
|||
|
|
|||
|
let sortNo = currentPacket.toString(16).padStart(4, '0');
|
|||
|
|
|||
|
// 填充头部
|
|||
|
dataView.setUint8(0, 0x55); // 帧头
|
|||
|
dataView.setUint8(1, 0x04); // 帧类型:开机画面
|
|||
|
dataView.setUint8(2, '0x' + sortNo.substring(0, 2)); // 包序号
|
|||
|
dataView.setUint8(3, '0x' + sortNo.substring(2, 4)); // 包序号
|
|||
|
|
|||
|
|
|||
|
dataView.setUint8(4, 0x01);
|
|||
|
dataView.setUint8(5, 0xF4);
|
|||
|
|
|||
|
|
|||
|
if (type == 'rgb565') {
|
|||
|
for (let i = 0; i < packetData.length; i++) {
|
|||
|
dataView.setUint8(6 + i, '0x' + packetData[i]);
|
|||
|
}
|
|||
|
} else {
|
|||
|
for (let i = 0; i < packetData.length; i++) {
|
|||
|
dataView.setUint16(6 + i * 2, packetData[i], false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
let inteval = parseInt(this.inteval ? this.inteval : 0);
|
|||
|
this.sendData(buffer).then(() => {
|
|||
|
// 更新进度
|
|||
|
this.currentPacket = currentPacket;
|
|||
|
this.progress = Math.round((currentPacket / totalPackets) *
|
|||
|
100);
|
|||
|
console.log(`发送数据包完成 ${currentPacket}/${totalPackets}`);
|
|||
|
|
|||
|
// 发送下一个包(添加延迟避免蓝牙缓冲区溢出)
|
|||
|
currentPacket++;
|
|||
|
|
|||
|
|
|||
|
setTimeout(sendNextPacket, inteval);
|
|||
|
}).catch(err => {
|
|||
|
|
|||
|
console.log(err.errMsg + ",发送失败了,正在补偿:" + currentPacket);
|
|||
|
setTimeout(sendNextPacket, inteval);
|
|||
|
// uni.showToast({
|
|||
|
// title:"发送失败"+JSON.stringify(err)
|
|||
|
// })
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
sendNextPacket();
|
|||
|
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (type == 'rgb565') {
|
|||
|
let start = new Date();
|
|||
|
console.log("开始发送");
|
|||
|
sendImagePackets(array).then(() => {
|
|||
|
console.log("发送完成");
|
|||
|
let end = new Date();
|
|||
|
var diff = (end.getTime() - start.getTime()) / 1000;
|
|||
|
let s = diff % 60;
|
|||
|
let m = (diff-s) / 60;
|
|||
|
console.log("发送完成,耗时:" + m + "分" + s + "秒");
|
|||
|
uni.showModal({
|
|||
|
content: "发送完成,耗时:" + m + "分" + s + "秒",
|
|||
|
title: 'success'
|
|||
|
});
|
|||
|
|
|||
|
}).catch((ex1) => {
|
|||
|
console.log("出现了异常", ex1)
|
|||
|
}).finally(() => {
|
|||
|
uni.hideLoading();
|
|||
|
});
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
},
|
|||
|
HoldYouHand() {
|
|||
|
|
|||
|
var promise=new Promise((resolve,reject)=>{
|
|||
|
try{
|
|||
|
let start=new Date();
|
|||
|
var str = "video transmit start"; //握手的协议字符串
|
|||
|
console.log("开始握手:"+str)
|
|||
|
|
|||
|
// 1. 创建 ArrayBuffer 和 DataView
|
|||
|
const buffer = new ArrayBuffer(str.length);
|
|||
|
const dataView = new DataView(buffer);
|
|||
|
|
|||
|
// 2. 将字符串转换为 ASCII 码并写入 DataView
|
|||
|
for (let i = 0; i < str.length; i++) {
|
|||
|
dataView.setUint8(i, str.charCodeAt(i));
|
|||
|
}
|
|||
|
//console.log("111111");
|
|||
|
this.sendData(buffer).then(() => {
|
|||
|
// 开始发送第一个包
|
|||
|
setTimeout(()=>{
|
|||
|
|
|||
|
let end = new Date();
|
|||
|
var diff = (end.getTime() - start.getTime()) / 1000;
|
|||
|
let s = diff % 60;
|
|||
|
let m = (diff - s) / 60;
|
|||
|
|
|||
|
console.log("握手成功并完成2200ms等待,耗时"+m+"分"+s+"秒");
|
|||
|
|
|||
|
resolve(true);
|
|||
|
}, 2200);
|
|||
|
|
|||
|
}).catch(err => {
|
|||
|
//console.log("握手没有成功");
|
|||
|
reject(err);
|
|||
|
});
|
|||
|
}catch(ex){
|
|||
|
reject(ex);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
return promise;
|
|||
|
|
|||
|
},
|
|||
|
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("完成指令发送失败");
|
|||
|
});
|
|||
|
},1500)
|
|||
|
|
|||
|
},
|
|||
|
sendData(buffer) {
|
|||
|
if(this.netMode=='ble'){
|
|||
|
return this.sendBle(buffer);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return this.sendMQ(buffer);
|
|||
|
},
|
|||
|
sendBle(buffer){
|
|||
|
return new Promise(async (resolve, reject) => {
|
|||
|
|
|||
|
// //console.log("开始发送数据,buffer");
|
|||
|
|
|||
|
|
|||
|
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);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
let inteval = parseInt(this.inteval ? this.inteval : 0);
|
|||
|
|
|||
|
Promise.race([promise, timeout(inteval)]).then(resolve).catch((ex) => {
|
|||
|
//console.log("ex=", ex);
|
|||
|
if (ex.code == -1) {
|
|||
|
resolve();
|
|||
|
} else {
|
|||
|
reject();
|
|||
|
}
|
|||
|
|
|||
|
}).finally(() => {
|
|||
|
//console.log("完成了")
|
|||
|
});
|
|||
|
} else {
|
|||
|
promise.then(resolve).catch(reject);
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
sendMQ(message) {
|
|||
|
|
|||
|
const topic = `B/${this.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) {
|
|||
|
reject(error);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
</script>
|
|||
|
|
|||
|
<style>
|
|||
|
.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;
|
|||
|
}
|
|||
|
|
|||
|
input {
|
|||
|
border: 2rpx solid #000000;
|
|||
|
}
|
|||
|
|
|||
|
button {
|
|||
|
height: 100rpx;
|
|||
|
line-height: 100rpx;
|
|||
|
font-size: 28rpx;
|
|||
|
float: left;
|
|||
|
}
|
|||
|
|
|||
|
.content {
|
|||
|
width: 100vw;
|
|||
|
height: 300px;
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
align-items: center;
|
|||
|
justify-content: center;
|
|||
|
}
|
|||
|
</style>
|