You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

477 lines
13 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import Taro from "@tarojs/taro";
import classnames from "classnames";
import { Component, PropsWithChildren, useEffect, useState } from "react";
import { Block, View, Text, PageMeta } from "@tarojs/components";
import { Popup, Progress } from "@antmjs/vantui";
import { connect } from "react-redux";
import {
msg,
back,
showModal,
go,
loading,
getStorageSync,
} from "@/utils/traoAPI";
import { InstrumentInfo } from "@/utils/Interface";
import {
notifyBLECharacteristicValueChange,
writeBLECharacteristicValue,
sendCommand,
} from "@/utils/bluetoothWXAPI";
import {
ab2hex,
ccrc8,
filterBleData,
hex2int,
string2buffer,
} from "@/utils/util";
import {
Stringkit,
TResponseFromDevice,
} from "@flossom-npm/iot-translater-we100";
// 通过设备类型判断给 deviceToolKitInstance 赋值
import { DeviceToolKit as DeviceToolKitWE100 } from "@flossom-npm/iot-translater-we100";
import { DeviceToolKit as DeviceToolKitFR200 } from "@flossom-npm/iot-translater";
import OtaDeviceTypeEnum from "../OtaDeviceTypeEnum";
// 临时替代
var log = console;
let deviceToolKitInstance: any = null;
const deviceToolKitInstanceFR200 = new DeviceToolKitFR200("FR200");
deviceToolKitInstanceFR200.setDebug(true);
import "./index.less";
class UpdateIotFR200 extends Component<any, any> {
constructor(props) {
super(props);
this.state = {
name: "UpdateIotFR200",
progressbar: 1, // 进度条
currentDevice: {},
hadStartOta: false, // 是否开始ota
};
}
$instance = Taro.getCurrentInstance();
$checkTimer: any = null;
// 与页面渲染无关的数据
$otaDataGroup: any = {
// 包大小面罩设置为100主机设置为240
packageSize: 240, //每个包的大小上限(单位是字节)
otaBin: null, //固件包buffer形式从16进制字符串转换成
otaHexString: "", //固件包的16进制字符串从后端接口返回
packageLength: 0, //升级包的总长度(单位是字节)
packageCount: 0, //分包后的总包数
otaData: [], //分包数组
packageCrc8: "", //总包的校验码
versionNo: "", //包的版本号
otaCurrentPackageIndex: 0, //现在正在传输的分包索引
countDown: 7,
};
async onLoad() {
if (this.$checkTimer) clearInterval(this.$checkTimer);
}
componentDidMount() {
console.log("componentDidMount");
this.initData();
}
componentDidHide() {
console.log("componentDidHide");
this.errorFun();
}
async initData() {
console.log("UpdateIotWL200");
deviceToolKitInstance = deviceToolKitInstanceFR200;
let objStr = getStorageSync("instrument_detail");
if (objStr) {
this.setState({
currentDevice: objStr,
});
}
setTimeout(() => {
this.notifyBLECharacteristicValueChange();
this.getDeviceUpgrade();
});
}
/**
* 拆分升级包
*/
async getUpgradeData() {
//解析获取升级包更新数据
const { otaHexString, packageSize } = this.$otaDataGroup;
if (!otaHexString?.length) {
return [];
}
let arr: any = [];
for (let i = 0, len = otaHexString.length; i < len; i += packageSize * 2) {
let index = i + packageSize * 2;
let value = otaHexString.slice(i, index);
arr.push(value);
}
return arr;
}
/**
* 重置OTA状态
*/
resetOta() {
this.$otaDataGroup = {
...this.$otaDataGroup,
otaCurrentPackageIndex: 0,
otaData: [],
};
}
/**
* 启动ota
*/
startOTA() {
this.resetOta();
let jsoncmd = {
commandType: "OTAStart",
otaDeviceType: "FR200",
versionNo: this.$otaDataGroup.versionNo,
};
console.log("startOTA jsoncmd", jsoncmd);
//@ts-ignore
const value = deviceToolKitInstance.toBleCommand(jsoncmd as any);
console.log(jsoncmd);
sendCommand({ value }).then((res) => {
this.setState({
hadStartOta: true,
});
this.createTimer();
});
}
/**
* 准备ota
*/
preOTA() {
const { packageCount, packageLength, packageCrc8, versionNo } =
this.$otaDataGroup;
let jsoncmd = {
commandType: "OTAPrepare",
packageCount,
packageLength,
packageCrc8,
versionNo,
};
console.info("OTAPrepare json => ", jsoncmd);
//@ts-ignore
const value = deviceToolKitInstance.toBleCommand(jsoncmd as any);
sendCommand({ value });
}
/**
* ota升级
*/
processOTA(i, data) {
const { packageCount, packageLength } = this.$otaDataGroup;
if (!packageLength) {
this.quitOTA();
}
let jsoncmd = {
commandType: "OTAUpdating",
packageCount,
packageLength,
currentPackageNo: i + 1,
currentPackageLength: data.length / 2,
currentPackageContent: data,
};
console.info("OTAUpdating json => ", jsoncmd);
//@ts-ignore
const value = deviceToolKitInstance.toBleCommand(jsoncmd as any);
sendCommand({ value });
}
/**
* ota升级完成
*/
async finishedOTA() {
let jsoncmd = {
commandType: "OTAUpdateFinish",
versionNo: this.$otaDataGroup.versionNo,
};
clearInterval(this.$checkTimer);
log.info("ota成功日志, 清除计时器");
//@ts-ignore
const value = deviceToolKitInstance.toBleCommand(jsoncmd as any);
sendCommand({ value }).then((res) => {
this.resetOta();
});
}
/**
* ota升级强制退出
*/
quitOTA(errorString = "") {
// log.info('ota失败日志', errorString);
console.log("ota失败日志", errorString);
const errorMap = {
packageError: "总包错误",
packageNumError: "包号错误",
checkedError: "校验错误",
dataLengthError: "数据总长度错误",
dataCRCError: "数据总数CRC错误",
updateTimeOut: "数据更新超时",
};
let jsoncmd = {
commandType: "OTAQuit",
};
//@ts-ignore
const value = deviceToolKitInstance.toBleCommand(jsoncmd as any);
sendCommand({ value })
.then((res) => {
this.resetOta();
// this.showError(errorMap[errorString] || tipMap[errorString] || '发生错误')
})
.catch((err) => {
if (!this.state.hadShowError) {
this.setState({
hadShowError: true,
});
// log.info('ota失败日志', '发送日志OTAQuit失败', err);
console.info("ota失败日志", "发送日志OTAQuit失败", err);
if (err.errCode === 10000) {
let isDisconnect = getStorageSync("isDisconnectUpdate");
if (isDisconnect) {
if (this.$checkTimer) clearInterval(this.$checkTimer);
}
}
}
});
setTimeout(() => {
this.errorFun();
}, 1000);
}
/**
* 处理返回的数据 OTA用
*/
async parseData(json) {
console.log("parseData json.commandType", json.commandType);
if (!json || !json.commandType) return;
switch (json.commandType) {
case "OTAStart":
if (json.responseStatus == "OK") {
const otaData = await this.getUpgradeData();
this.$otaDataGroup = {
...this.$otaDataGroup,
otaData,
};
this.$otaDataGroup.otaCurrentPackageIndex = 0;
this.preOTA();
} else {
this.quitOTA(json.responseStatus);
}
break;
case "OTAPrepare":
if (json.responseStatus == "OK") {
this.processOTA(0, this.$otaDataGroup.otaData[0]);
} else {
this.quitOTA(json.responseStatus);
}
break;
case "OTAUpdating":
if (json.responseStatus == "OK") {
let ota = this.$otaDataGroup;
let progressbar =
(ota.otaCurrentPackageIndex / ota.packageCount) * 100;
this.setState({
progressbar: (progressbar <= 1 ? 1 : progressbar).toFixed(0),
});
if (!json.currentPackageNo) {
return;
}
console.log(
"json.currentPackageNo",
json.currentPackageNo,
this.$otaDataGroup.packageCount
);
if (json.currentPackageNo < this.$otaDataGroup.packageCount) {
this.$otaDataGroup.otaCurrentPackageIndex++;
this.processOTA(
this.$otaDataGroup.otaCurrentPackageIndex,
this.$otaDataGroup.otaData[
this.$otaDataGroup.otaCurrentPackageIndex
]
);
} else {
this.finishedOTA();
}
} else {
this.quitOTA(json.responseStatus);
}
break;
case "OTAUpdateFinish":
console.log("完成OTA传输");
this.setState({
progressbar: 100,
});
this.finishFun();
// quitOTA()
break;
default:
break;
}
}
/**
* 从后端拉取固件包并初始化ota流程的所需对象
*/
getDeviceUpgrade = async () => {
let res: any = await InstrumentInfo.getUpgrade({
instrumentId: this.state.currentDevice.id,
isWe200: false,
});
if (res.data.data == "解析失败") {
// this.showError('文件解析失败');
return;
}
if (res.data.code !== 200) {
return;
}
const otaHexString = res.data.data;
const otaBin = string2buffer(res.data.data);
const packageLength = otaBin?.byteLength || 0;
const packageSize = this.$otaDataGroup.packageSize;
const packageCrc8 = Stringkit.getCrc8CodeByString(otaHexString);
const packageCount = Math.ceil(packageLength / packageSize);
let { iotVersion: versionNo } = this.state.currentDevice;
this.$otaDataGroup = {
...this.$otaDataGroup,
otaHexString,
otaBin,
packageLength,
packageSize,
packageCrc8,
packageCount,
versionNo,
};
console.log("this.$otaDataGroup ", this.$otaDataGroup);
};
notifyBLECharacteristicValueChange = () => {
const bluetoothInfo = this.props.bluetoothInfo;
notifyBLECharacteristicValueChange({
deviceId: bluetoothInfo.deviceId,
servicesuuid: bluetoothInfo.servicesuuid,
characteristicsuuid1: bluetoothInfo.characteristicsuuid1,
characteristicsuuid0: bluetoothInfo.characteristicsuuid0,
}).then((res) => {
Taro.onBLECharacteristicValueChange((value) => {
// let str = ab2hex(value.value); //转为16进制字符串
// console.log('返回',str)
const jsonStatus: any = deviceToolKitInstance.toJsonStatus(value.value);
log.info("OTA页面设备响应数据打印==》", JSON.stringify(jsonStatus));
console.info("onBLECharacteristicValueChange json => ", jsonStatus);
this.$otaDataGroup.currentPackageNo = jsonStatus.currentPackageNo;
this.$otaDataGroup.countDown = 7;
this.parseData(jsonStatus);
});
setTimeout(() => {
this.startOTA();
}, 500);
});
};
createTimer() {
log.info("设备ota计时器创建成功");
this.$checkTimer = setInterval(() => {
// 超过7s没有回包, 直接断开
if (this.$otaDataGroup.countDown <= 0 && this.state.hadStartOta) {
this.quitOTA("shutDownMaskTip");
} else {
this.$otaDataGroup.countDown--;
}
console.log(this.$otaDataGroup.countDown);
}, 1000);
}
//升级销毁页面时关闭小程序蓝牙
onUnload() {
console.log("onUnload");
if (this.$checkTimer) clearInterval(this.$checkTimer);
Taro.closeBluetoothAdapter();
}
onPullDownRefresh() {
Taro.stopPullDownRefresh();
}
onClickStop = (e) => {
e.stopPropagation();
};
// 错误关闭回调
errorFun = () => {
this.resetOta();
this.props.errorFun();
};
// 升级完成回调
finishFun = () => {
this.props.finishFun();
};
render() {
let { isShow } = this.props;
let { progressbar } = this.state;
return (
<Block>
<PageMeta pageStyle={isShow ? "overflow: hidden;" : ""} />
<Popup
show={isShow}
closeOnClickOverlay={false}
round
overlayStyle="width: 100vw;padding: 0;"
onClick={this.onClickStop}
>
<View
className={classnames(
"common-box",
"common-progress-box",
"common-large"
)}
catchMove
>
<View className={classnames("common-popup-title", "margin-samll")}>
</View>
<View className="common-popup-content-box">
<View className={classnames("common-popup-content", "text-left")}>
<View className="progress">
<Progress
percentage={progressbar}
strokeWidth="12"
color="linearGradient(to right, #eecda1, #ffe9c7) !important"
/>
<View className="percent"> {progressbar}%</View>
</View>
<View className="progress-tips"></View>
<View className="progress-tips">1.</View>
<View className="progress-tips">2.</View>
</View>
</View>
</View>
</Popup>
</Block>
);
}
}
const mapStateToProps = (state) => ({
bluetoothInfo: state.deviceInfo.bluetoothInfo,
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(mapStateToProps, mapDispatchToProps)(UpdateIotFR200);