devices-update.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. <!-- 蓝牙平台限制 todo https://uniapp.dcloud.io/api/system/bluetooth?id=stopbluetoothdevicesdiscovery -->
  2. <template>
  3. <view>
  4. <uni-nav-bar id="nav-bar" status-bar="true" @clickLeft="onTipBack()" title="设备升级" color="#000000" fixed="true"
  5. :border="false">
  6. <view slot="left">
  7. <view class=" flex align-center margin-left">
  8. <image class="p-left-arrow" src="../../../static/p-left-arrow.png"></image>
  9. </view>
  10. </view>
  11. </uni-nav-bar>
  12. <view class="card-view padding-top padding-bottom" v-for="(item,index) in devicesList" :key="index"
  13. :class="item.bRatio?'hardware-border':''">
  14. <view class="flex justify-between align-center">
  15. <image style="margin-left: 30rpx; width: 200rpx;height: 120rpx;" :src="item.icon" mode="aspectFit">
  16. </image>
  17. <view style="width: 250rpx;">
  18. <view style="font-weight: bold; font-size: 18px; color: #565656; margin: 20rpx 0;">{{item.cname}}
  19. </view>
  20. <view v-if="send_index<=0&&item.currentVersion !== ''">
  21. <view style="margin: 20rpx 0; font-size: 12px; color: grey;">固件版本号:{{item.currentVersion}}
  22. </view>
  23. <!-- <view style="margin: 20rpx; font-size: 12px;" class="make-text-bPurple">更新版本号:{{item.latestVersion}}</view> -->
  24. </view>
  25. <!-- <view class="flex">
  26. <view style="margin: 20rpx; font-size: 12px; color: grey;">硬件rom:{{item.currentRomVersion}}</view>
  27. <view style="margin: 20rpx; font-size: 12px;" class="make-text-bPurple">更新版本rom:{{item.latestRomVersion}}</view>
  28. </view> -->
  29. <!-- <view class="flex">
  30. <view style="margin: 20rpx; font-size: 12px; color: grey;">Bytes:{{bin_offset}} 总 {{bin_byteLength}}</view>
  31. </view> -->
  32. <view v-if="send_index>0" class="flex justify-between align-center" style="width: 210rpx;">
  33. <view class="cu-progress round" style=" width: 140rpx; height: 15rpx;">
  34. <view class="bg-green" :style="[{ width:percentage+'%'}]"></view>
  35. </view>
  36. <view class="" style=" font-size: 12px; color: grey;">{{percentage}}%</view>
  37. <!-- <view>
  38. <progress :percent="percentage" stroke-width="3" />
  39. </view> -->
  40. <!-- ,total {{bin_total}} send:{{send_index}} -->
  41. <!-- <view style=" font-size: 12px; color: grey;"> {{percentage}}% </view> -->
  42. </view>
  43. </view>
  44. <view v-if="!bStart" style="width: 180rpx;margin-right: 30rpx;">
  45. <view v-if="send_index<=0&&item.latestVersion !== ''">
  46. <view v-if="bCanUpdate&&downLoadOTA!==''" class="text-11px make-text-bPurple "
  47. style="text-decoration:underline" @click="onSendConnect"> 请更新版本{{item.latestVersion}}
  48. </view>
  49. <view v-else class="text-11px text-grey">最新版本{{item.latestVersion}}</view>
  50. <!-- <button v-else class="text-11px make-bg-bPurple text-white" @click="onCheckDeviceFiles">检查更新</button> -->
  51. <!-- <button v-if="item.currentVersion !== ''" class="text-11px margin" @click="onSendConnect">开始</button> -->
  52. <!-- <button v-if="item.currentVersion !== ''" class="text-11px margin" @click="onStop">停止</button> -->
  53. </view>
  54. <view v-if="send_index<=0&&item.latestVersion === ''" class="text-26px text-grey text-center"><text
  55. class="cuIcon-loading2 cuIconfont-spin"></text></view>
  56. </view>
  57. <view v-else style="width: 180rpx;margin-right: 30rpx;">
  58. <view v-if="send_index<=0&&!sending_file" class="text-11px text-grey">{{writeTip}}</view>
  59. <view v-else class="text-11px text-grey">{{writeTip}}</view>
  60. </view>
  61. </view>
  62. <!-- <view class="flex">
  63. <view style="margin: 20rpx; font-size: 12px; color: grey;">发送和接收的次数:send:{{send_index}}</view>
  64. <view style="margin: 20rpx; font-size: 12px;" class="make-text-bPurple">time:{{send_index/100}}</view>
  65. </view> -->
  66. <!-- <scroll-view class="text-box" scroll-y="true">
  67. <scroll-view scroll-x="true">
  68. <text selectable="true">{{text}}</text>
  69. </scroll-view>
  70. </scroll-view> -->
  71. </view>
  72. </view>
  73. </template>
  74. <script>
  75. import config from '@/common/config.js'
  76. import reqUtil from '@/util/util-js/requstUtil.js';
  77. import {
  78. mapState,
  79. mapMutations
  80. } from 'vuex';
  81. export default {
  82. computed: mapState(['bOpenBluetooth', 'bOpenSuccess', 'bListenAdapterStateChange', 'bListenDeviceFound',
  83. 'BLEConnectDevice', 'cIndex'
  84. ]),
  85. data() {
  86. return {
  87. /**
  88. * OTA config
  89. */
  90. UUID_OTA_SERVICE: "f000ffc0-0451-4000-b000-000000000000", //OTA 服务对应的服务的 UUID
  91. UUID_IDENTFY: "f000ffc1-0451-4000-b000-000000000000", //OTA 版本特征值的UUID,读取版本就是写向这个特征值写入:0x00
  92. UUID_BLOCK: "f000ffc2-0451-4000-b000-000000000000", //OTA 写bin文件特征值UUID,发送bin文件就是写这个特征值
  93. /*****/
  94. boxingList: [{
  95. cname: "蓝牙手柄",
  96. ename: "BLEHandle",
  97. icon: "/static/devicesIcon/handle.png",
  98. mIcon: "/static/devicesIcon/handle.png",
  99. bRatio: false,
  100. limitType: 'noRebound', //app处理蓝牙发送的数据
  101. deviceType: 'BLEHandle', //指的是外部蓝牙,目前定义为BLEHandle
  102. deviceName: 'PBox', //连接的设备名称
  103. limitDis: 100, //
  104. primaryUUID: 'f000ffc0', //OTA升级主服务id
  105. currentVersion: '', //当前固件版本
  106. latestVersion: '', //服务器最新固件版本
  107. currentRomVersion: '',
  108. latestRomVersion: '',
  109. UUID_OTA_SERVICE: "f000ffc0-0451-4000-b000-000000000000", //OTA 服务对应的服务的 UUID
  110. UUID_IDENTFY: "f000ffc1-0451-4000-b000-000000000000", //OTA 版本特征值的UUID,读取版本就是写向这个特征值写入:0x00
  111. UUID_BLOCK: "f000ffc2-0451-4000-b000-000000000000", //OTA 写bin文件特征值UUID,发送bin文件就是写这个特征值
  112. }],
  113. devicesList: [],
  114. downLoadOTA: '',
  115. //文件数据
  116. bin_buffer: [],
  117. bin_file_len: 0,
  118. bin_byteLength: 0,
  119. bin_total: 0,
  120. bin_offset: 0,
  121. //当前发送的下标
  122. send_index: 0,
  123. sending_file: false,
  124. // 当前进度
  125. percentage: 0,
  126. //是否可以发送
  127. bSend: true,
  128. //是否可以更新
  129. bCanUpdate: false,
  130. //是否开始
  131. bStart: false,
  132. bListenValueChange: false,
  133. //测试
  134. text: '',
  135. extraLine: [],
  136. //timeout 对象
  137. writeHeadTimeout: null,
  138. backTimeout: null,
  139. sendFileHeaderTimeout: null,
  140. writeTip: '准备写入数据..',
  141. bBacking: false,
  142. }
  143. },
  144. onLoad(option) {
  145. uni.$on('callbackCloseBLE', this.updateCallbackCloseBLE);
  146. this.devicesList = this.boxingList;
  147. //比如现在BLEHandle 对应的 1,
  148. //获得游戏列表
  149. reqUtil
  150. .requestData(config.URL.GETOTA, {
  151. bleType: 1, //目前只有一种设备,就是 BLEHandle,todo 后面如果设备多了需要设备多种ota
  152. })
  153. .then(
  154. res => {
  155. console.log('GETOTA =====', res);
  156. if (res.code == 0) {
  157. //设置对应的下载地址
  158. this.downLoadOTA = res.data.otaDownload;
  159. //读取需要更新的设备,存储在数据库
  160. this.onCheckServerFiles();
  161. this.onCheckDeviceFiles();
  162. uni.showToast({
  163. title: '获取信息...',
  164. icon: 'loading',
  165. duration: 20000
  166. })
  167. }
  168. },
  169. e => {
  170. console.log(e);
  171. }
  172. );
  173. },
  174. onUnload() {
  175. uni.$off('callbackCloseBLE', this.updateCallbackCloseBLE);
  176. //如果是获取版本指令timeout
  177. if (this.writeHeadTimeout) {
  178. clearTimeout(this.writeHeadTimeout);
  179. this.writeHeadTimeout = null;
  180. }
  181. //回退timeout
  182. if (this.backTimeout) {
  183. clearTimeout(this.backTimeout);
  184. this.backTimeout = null;
  185. }
  186. //发送头指令timeout
  187. if (this.sendFileHeaderTimeout) {
  188. clearTimeout(this.sendFileHeaderTimeout);
  189. this.sendFileHeaderTimeout = null;
  190. }
  191. this.onBack();
  192. uni.$off("OTAValueChange", this.onOTAValueChange);
  193. },
  194. onShow() {
  195. },
  196. onHide() {
  197. },
  198. methods: {
  199. ...mapMutations(['initAdapter', 'onCloseBLEConnection',
  200. 'onGetRSSITransDistance', 'B_NotifyBLECharacteristicValueChange'
  201. ]),
  202. updateCallbackCloseBLE() {
  203. this.onBack(true);
  204. },
  205. onTipBack() {
  206. if (this.send_index != 0) {
  207. uni.showModal({
  208. title: '提示',
  209. content: '退出会停止更新,是否退出。',
  210. confirmText: '是',
  211. cancelText: '否',
  212. success: (res) => {
  213. if (res.confirm) {
  214. this.onBack();
  215. }
  216. }
  217. })
  218. } else {
  219. this.onBack();
  220. }
  221. },
  222. onBack(isReset) {
  223. // if(this.bStart){
  224. // }
  225. if (this.bBacking) return;
  226. this.bBacking = true;
  227. //停止发送数据
  228. this.onStop();
  229. console.log('onBack:', this.BLEConnectDevice);
  230. //关闭当前的蓝牙连接
  231. // this.onCloseBLEConnection({
  232. // getSuccess: () => {
  233. // console.log("close ble");
  234. // this.$store.state.bConnection = false;
  235. // }
  236. // });
  237. // this.$store.state.cIndex = -1;
  238. // this.$store.state.BLEConnectDevice = null;
  239. // this.$store.state.BLEGetServices = null;
  240. // this.$store.state.bOpenBluetooth = false;
  241. if (isReset) {
  242. this.onBackClose();
  243. }
  244. uni.navigateBack({
  245. delta: 1
  246. })
  247. },
  248. onBackClose() {
  249. console.log('onBackClose');
  250. //关闭当前的蓝牙连接
  251. this.onCloseBLEConnection({
  252. getSuccess: () => {
  253. console.log("close ble");
  254. this.$store.state.bConnection = false;
  255. }
  256. });
  257. this.$store.state.cIndex = -1;
  258. this.$store.state.BLEConnectDevice = null;
  259. this.$store.state.BLEGetServices = null;
  260. this.$store.state.bOpenBluetooth = false;
  261. },
  262. //获取版本
  263. onCheckDeviceFiles() {
  264. // console.log('onCheckDeviceFiles:', this.BLEConnectDevice);
  265. //直接连接对应服务
  266. this.notifyBLECharacteristicValueChange({
  267. deviceId: this.BLEConnectDevice.deviceId,
  268. serviceId: this.UUID_OTA_SERVICE,
  269. characteristicId: this.UUID_IDENTFY
  270. });
  271. },
  272. //开始写数据
  273. onSendConnect() {
  274. uni.showModal({
  275. title: '更新提示',
  276. content: '设备更新成功后会自动关闭,是否继续更新?',
  277. success: (resValue) => {
  278. if (resValue.confirm) {
  279. //直接连接对应服务
  280. this.notifyBLECharacteristicValueChange({
  281. deviceId: this.BLEConnectDevice.deviceId,
  282. serviceId: this.UUID_OTA_SERVICE,
  283. characteristicId: this.UUID_BLOCK
  284. });
  285. uni.showToast({
  286. title: '请求硬件更新数据...',
  287. icon: 'none',
  288. duration: 3000,
  289. mask: true
  290. })
  291. //发送头指令timeout
  292. if (this.sendFileHeaderTimeout) {
  293. clearTimeout(this.sendFileHeaderTimeout);
  294. this.sendFileHeaderTimeout = null;
  295. }
  296. this.sendFileHeaderTimeout = setTimeout(() => {
  297. let retryCount = 3;
  298. this.sendFileHeader(retryCount);
  299. // this.send_index = -1;
  300. }, 3000)
  301. this.bStart = true;
  302. }
  303. }
  304. })
  305. },
  306. //启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。
  307. //注意:必须设备的特征值支持notify或者indicate才可以成功调用,具体参照 characteristic 的 properties 属性
  308. notifyBLECharacteristicValueChange(context) {
  309. let _self = this;
  310. let {
  311. deviceId,
  312. serviceId,
  313. characteristicId
  314. } = context;
  315. // 启用notify功能
  316. uni.notifyBLECharacteristicValueChange({
  317. state: true,
  318. deviceId: deviceId,
  319. serviceId: serviceId,
  320. characteristicId: characteristicId,
  321. success: function(res) {
  322. if (!_self.bListenValueChange) {
  323. // _self.onBLECharacteristicValueChange(); //监听特征值变化
  324. uni.$on("OTAValueChange", _self.onOTAValueChange);
  325. _self.bListenValueChange = true;
  326. }
  327. if (characteristicId == _self.UUID_IDENTFY) {
  328. //重发3次
  329. _self.onGetDeviceVersion(context, 3);
  330. }
  331. },
  332. fail: function(res) {
  333. uni.showToast({
  334. title: 'notify启动失败',
  335. icon: "none",
  336. mask: true
  337. });
  338. }
  339. })
  340. },
  341. onGetDeviceVersion(context, retryCount) {
  342. let _self = this;
  343. let {
  344. deviceId,
  345. serviceId,
  346. characteristicId
  347. } = context;
  348. //如果是获取版本,延迟写一个指令
  349. if (_self.writeHeadTimeout) {
  350. clearTimeout(_self.writeHeadTimeout);
  351. _self.writeHeadTimeout = null;
  352. }
  353. _self.writeHeadTimeout = setTimeout(() => {
  354. // 向蓝牙设备发送一个0x00的16进制数据
  355. let bufferDevice = new ArrayBuffer(1)
  356. let dataViewDevice = new DataView(bufferDevice)
  357. dataViewDevice.setUint8(0, 0)
  358. uni.writeBLECharacteristicValue({
  359. deviceId: deviceId,
  360. serviceId: serviceId,
  361. characteristicId: characteristicId,
  362. value: bufferDevice,
  363. success: (res) => {
  364. console.log('获取版本:writeBLECharacteristicValue success', res.errMsg, " == ",
  365. retryCount)
  366. },
  367. fail: (fail) => {
  368. console.error('获取版本:writeBLECharacteristicValue fail', fail.errMsg, " == ",
  369. retryCount)
  370. if (retryCount > 0) {
  371. retryCount--;
  372. setTimeout(() => {
  373. _self.onGetDeviceVersion(context, retryCount);
  374. }, 300);
  375. return;
  376. }
  377. uni.showModal({
  378. title: '获取固件版本',
  379. content: '获取不成功,是否重新获取',
  380. success: (resValue) => {
  381. if (resValue.confirm) {
  382. _self.onGetDeviceVersion(context, retryCount);
  383. }
  384. }
  385. })
  386. }
  387. })
  388. }, 3000)
  389. },
  390. onOTAValueChange(res) {
  391. console.log(
  392. `update onOTAValueChange ${res.serviceId} characteristic ${res.characteristicId} has changed, now is ${res.value}`
  393. );
  394. let _self = this;
  395. if (res.characteristicId.toLocaleLowerCase() === _self.UUID_IDENTFY) {
  396. //如果硬件的版本获取版本
  397. var resValue = ab2hext(res.value); //16进制字符串
  398. // console.log("Device 版本:", resValue.substr(0, 4), "Rom 版本号:", resValue.substr(16, 4));
  399. _self.devicesList[0].currentRomVersion = resValue.substr(16, 4);
  400. _self.devicesList[0].currentVersion = resValue.substr(0, 4);
  401. //读取到硬件版本,判断是否需要更新
  402. if (_self.devicesList[0].currentVersion === _self.devicesList[0].latestVersion && _self
  403. .devicesList[0].currentRomVersion ===
  404. _self.devicesList[0].latestRomVersion) {
  405. uni.showToast({
  406. title: '版本一致,无需升级',
  407. icon: 'none',
  408. mask: true,
  409. })
  410. _self.bCanUpdate = false;
  411. return;
  412. }
  413. uni.showToast({
  414. title: '获取信息成功',
  415. icon: 'success',
  416. mask: true,
  417. })
  418. //设置状态
  419. _self.bCanUpdate = true;
  420. } else {
  421. uni.hideToast();
  422. //成功发送头文件会走一个回调
  423. let uint8_buf = new DataView(res.value);
  424. _self.send_index = ((uint8_buf.getUint8(1) << 8) | uint8_buf.getUint8(0));
  425. // console.log("val===>> data:" + ab2hext(res.value) + ",rv index:" + _self.send_index + _self.sending_file)
  426. if (_self.sending_file == false) {
  427. _self.sending_file = true;
  428. // console.log("SendBlePkg");
  429. _self.writeTip = "正在写入设备...";
  430. _self.SendBlePkg();
  431. }
  432. }
  433. },
  434. //监听低功耗蓝牙设备的特征值变化。必须先启用notify接口才能接收到设备推送的notification。
  435. onBLECharacteristicValueChange() {
  436. var _self = this;
  437. // ArrayBuffer转16进制字符串示例
  438. // console.log("onBLECharacteristicValueChange");
  439. uni.onBLECharacteristicValueChange(function(res) {
  440. console.log(
  441. `update ${res.serviceId} characteristic ${res.characteristicId} has changed, now is ${res.value}`
  442. );
  443. if (res.characteristicId.toLocaleLowerCase() === _self.UUID_IDENTFY) {
  444. //如果硬件的版本获取版本
  445. var resValue = ab2hext(res.value); //16进制字符串
  446. // console.log("Device 版本:", resValue.substr(0, 4), "Rom 版本号:", resValue.substr(16, 4));
  447. _self.devicesList[0].currentRomVersion = resValue.substr(16, 4);
  448. _self.devicesList[0].currentVersion = resValue.substr(0, 4);
  449. //读取到硬件版本,判断是否需要更新
  450. if (_self.devicesList[0].currentVersion === _self.devicesList[0].latestVersion && _self
  451. .devicesList[0].currentRomVersion ===
  452. _self.devicesList[0].latestRomVersion) {
  453. uni.showToast({
  454. title: '版本一致,无需升级',
  455. icon: 'none',
  456. mask: true,
  457. })
  458. _self.bCanUpdate = false;
  459. return;
  460. }
  461. uni.showToast({
  462. title: '获取信息成功',
  463. icon: 'success',
  464. mask: true,
  465. })
  466. //设置状态
  467. _self.bCanUpdate = true;
  468. } else {
  469. uni.hideToast();
  470. //成功发送头文件会走一个回调
  471. let uint8_buf = new DataView(res.value);
  472. _self.send_index = ((uint8_buf.getUint8(1) << 8) | uint8_buf.getUint8(0));
  473. // console.log("val===>> data:" + ab2hext(res.value) + ",rv index:" + _self.send_index + _self.sending_file)
  474. if (_self.sending_file == false) {
  475. _self.sending_file = true;
  476. // console.log("SendBlePkg");
  477. _self.writeTip = "正在写入设备...";
  478. _self.SendBlePkg();
  479. }
  480. }
  481. });
  482. },
  483. //下载后读取
  484. onCheckServerFiles() {
  485. this.bin_buffer = [];
  486. this.bin_offset = 0;
  487. this.bin_total = 0;
  488. this.reset();
  489. uni.request({
  490. url: this.downLoadOTA,
  491. method: "GET",
  492. responseType: "arraybuffer",
  493. success: (res) => {
  494. //记录二进制数据
  495. this.bin_buffer = res.data;
  496. let binData = new Uint8Array(this.bin_buffer, 0,
  497. 16); //new Uint8Array(this.bin_buffer);
  498. this.bin_file_len = (binData[7] << 8) | binData[6]
  499. this.bin_total = (this.bin_file_len) / 4
  500. this.bin_byteLength = this.bin_buffer.byteLength;
  501. function uint8_to_hex_str(data) {
  502. var v = data.toString(16).slice(-2) + ''
  503. if (v == '0') {
  504. v = '00'
  505. }
  506. if (v.length == 1) {
  507. v = '0' + v
  508. }
  509. return v
  510. }
  511. // console.log("ver2:", uint8_to_hex_str(binData[4]) + uint8_to_hex_str(binData[5]), ",rom:", uint8_to_hex_str(
  512. // binData[14]) +
  513. // uint8_to_hex_str(binData[15]));
  514. //显示信息
  515. this.devicesList[0].latestRomVersion = uint8_to_hex_str(binData[14]) +
  516. uint8_to_hex_str(binData[15]); //str.substr(28, 2);
  517. this.devicesList[0].latestVersion = uint8_to_hex_str(binData[4]) + uint8_to_hex_str(
  518. binData[5]); //str.substr(8, 2);
  519. },
  520. fail: (fail) => {
  521. uni.showToast({
  522. title: '下载固件失败..',
  523. icon: 'none'
  524. });
  525. }
  526. });
  527. },
  528. //发送文件头信息(配置信息)
  529. sendFileHeader(retryCount) {
  530. console.log('---------- sendFileHeader --------------')
  531. let _self = this
  532. let buffer = new ArrayBuffer(16)
  533. let dataView = new DataView(buffer)
  534. var b = new Uint8Array(_self.bin_buffer, 0, 16);
  535. for (var i = 0; i < 16; i++) {
  536. dataView.setUint8(i, b[i])
  537. }
  538. console.log('send header:' + ab2hext(buffer))
  539. // return;
  540. uni.writeBLECharacteristicValue({
  541. deviceId: _self.BLEConnectDevice.deviceId,
  542. serviceId: _self.UUID_OTA_SERVICE.toLocaleUpperCase(),
  543. characteristicId: _self.UUID_IDENTFY.toLocaleUpperCase(),
  544. value: buffer,
  545. success: function(res) {
  546. // _self.send_index = -1
  547. // _self.sending_file = false
  548. uni.showToast({
  549. title: '准备更新数据...',
  550. icon: 'none',
  551. duration: 3000,
  552. mask: true
  553. })
  554. console.log('开始发送文件success', _self.sending_file)
  555. // _self.SendBlePkg()
  556. },
  557. fail: function(err) {
  558. console.log('read version fail! ' + JSON.stringify(err) + retryCount)
  559. if (retryCount > 0) {
  560. retryCount--;
  561. setTimeout(() => {
  562. _self.sendFileHeader(retryCount);
  563. }, 300);
  564. return;
  565. }
  566. uni.showModal({
  567. title: '请求更新',
  568. content: '请求更新失败,是否重新尝试继续。',
  569. success: (resValue) => {
  570. if (resValue.confirm) {
  571. _self.sendFileHeader(retryCount);
  572. } else if (resValue.cancel) {
  573. _self.bStart = false;
  574. }
  575. }
  576. })
  577. }
  578. })
  579. },
  580. //发送一包数据
  581. SendBlePkg() {
  582. var _self = this;
  583. if (!_self.bSend) return;
  584. if (_self.send_index == -1) {
  585. return
  586. }
  587. if (_self.send_index >= _self.bin_total) {
  588. _self.percentage = 100;
  589. uni.showToast({
  590. title: "更新完成",
  591. duration: 2000,
  592. mask: true
  593. })
  594. //回退timeout
  595. if (this.backTimeout) {
  596. clearTimeout(this.backTimeout);
  597. this.backTimeout = null;
  598. }
  599. this.backTimeout = setTimeout(() => {
  600. //更新成功后设备会断开,此时回退出此页面
  601. uni.hideToast();
  602. _self.onBack(true);
  603. }, 1500)
  604. return;
  605. }
  606. _self.percentage = parseInt(_self.send_index / _self.bin_total * 100);
  607. //分包长度,16字节有效数据(总数据/16=次数)
  608. let length = 16;
  609. //发送每包数据的结构:2个字节的序号 + 16个字节的有效数据
  610. //设置数据包序号
  611. let lo = getLow8(_self.send_index);
  612. let hi = getHeight8(_self.send_index);
  613. //最要一个18字节的包
  614. let send_ab = new ArrayBuffer(18);
  615. let a = new Uint8Array(send_ab);
  616. //2个字节的序号
  617. a[0] = lo;
  618. a[1] = hi;
  619. if (_self.bin_offset + length > _self.bin_buffer.byteLength) {
  620. length = _self.bin_buffer.byteLength - _self.bin_offset;
  621. }
  622. var b = new Uint8Array(_self.bin_buffer, _self.bin_offset, length);
  623. a.set(b, 2);
  624. uni.writeBLECharacteristicValue({
  625. deviceId: _self.BLEConnectDevice.deviceId,
  626. serviceId: _self.UUID_OTA_SERVICE, // _deviceId,
  627. characteristicId: _self.UUID_BLOCK,
  628. value: send_ab,
  629. fail: function(res) {
  630. console.log('write BLEChar fail')
  631. _self.SendBlePkg();
  632. },
  633. success: function(res) {
  634. _self.send_index++;
  635. //发送的有效字节数
  636. _self.bin_offset += length;
  637. // _self.sending_file = false;
  638. _self.SendBlePkg();
  639. // console.log('success')
  640. },
  641. })
  642. },
  643. reset() {
  644. //发送字节的下标
  645. this.send_index = 0;
  646. //发送的有效字节数
  647. this.bin_offset = 0;
  648. this.percentage = 0;
  649. this.bSend = true;
  650. },
  651. onStop() {
  652. this.bSend = false;
  653. },
  654. onReadFile(url) {
  655. //游戏列表
  656. // firmwareList: [{
  657. // name: "24_LYY_3431Q_H2_S3_bk3435_ble_app_oad.bin",
  658. // downloadUrl: "https://www.yuyekeji.cn/Cocos/bin_version/24_LYY_3431Q_H2_S3_bk3435_ble_app_oad.bin",
  659. // fileName: "bin_version",
  660. // suffix: "bin",
  661. // type: "bin",
  662. // task: null,
  663. // progressVal: 0,
  664. // bFileExists: false,
  665. // bDownload: false,
  666. // }],
  667. // data URIs to buffer
  668. function dataURL2Buffer(dataURI) {
  669. var byteStr
  670. var intArray
  671. var ab
  672. var i
  673. var mimetype
  674. var parts
  675. parts = dataURI.split(',')
  676. parts[1] = parts[1].replace(/\s/g, '')
  677. // console.log(parts);
  678. if (~parts[0].indexOf('base64')) {
  679. byteStr = atob(parts[1])
  680. } else {
  681. byteStr = decodeURIComponent(parts[1])
  682. }
  683. // // ab = new ArrayBuffer(byteStr.length)
  684. // intArray = new Uint8Array(byteStr.length)
  685. // for (i = 0; i < byteStr.length; i++) {
  686. // intArray[i] = byteStr.charCodeAt(i)
  687. // }
  688. // console.log(intArray.length, intArray.byteLength);
  689. return byteStr;
  690. }
  691. let item = this.firmwareList[0];
  692. console.log("url:" + JSON.stringify(url));
  693. plus.io.resolveLocalFileSystemURL(url, function(entry) {
  694. entry.file(function(file) {
  695. var fileReader = new plus.io.FileReader();
  696. fileReader.readAsDataURL(file);
  697. fileReader.onloadend = function(evt) {
  698. // console.log("_buffer1:" + JSON.stringify(evt.target.result));
  699. // let _arrayBuffer = dataURL2Buffer(evt.target.result)
  700. let parts = evt.target.result.split(',')
  701. let _arrayBuffer = parts[1].replace(/\s/g, '')
  702. console.log("_buffer1:" + JSON.stringify(_arrayBuffer));
  703. let _buffer = uni.base64ToArrayBuffer(_arrayBuffer);
  704. console.log("_buffer2:", JSON.stringify(_buffer));
  705. console.log("_buffer:", _buffer.length, _buffer.byteLength, _buffer
  706. .byteOffset);
  707. }
  708. });
  709. }, function(e) {
  710. console.log("Resolve file URL failed: " + e.message);
  711. });
  712. },
  713. //下载固件
  714. downLoadBin() {
  715. let _self = this;
  716. let item = this.firmwareList[0];
  717. let downloadUrl = item.downloadUrl;
  718. let _filename = '_doc/' + item.name; //+'/'+item.name
  719. console.log(_filename, downloadUrl);
  720. item.task = plus.downloader.createDownload(downloadUrl, {
  721. filename: _filename
  722. });
  723. item.task.addEventListener("statechanged", (download, status) => {
  724. // console.log("status", status);
  725. //连接服务器后
  726. // no default
  727. switch (download.state) {
  728. case 1:
  729. console.log('开始下载');
  730. // item.bDownload = true;
  731. break;
  732. case 2:
  733. console.log('链接到服务器...');
  734. item.bDownload = true;
  735. break;
  736. case 3:
  737. if (status !== 200) {
  738. uni.showToast({
  739. title: 'status=' + status
  740. })
  741. return;
  742. }
  743. item.progressVal = Math.ceil(download.downloadedSize / download.totalSize * 100);
  744. // _this.precent = progressVal;
  745. console.log(item.progressVal);
  746. break;
  747. case 4:
  748. // 下载完成
  749. console.log("监听下载 success: " + download.getFileName());
  750. setTimeout(() => {
  751. item.bDownload = false;
  752. }, 500)
  753. break;
  754. }
  755. }, false);
  756. item.task.start();
  757. },
  758. add: function(data) {
  759. // console.log(data)
  760. if (this.extraLine.length > 500) {
  761. this.extraLine = [];
  762. this.text = this.extraLine.join('\n');
  763. }
  764. this.extraLine.push(data);
  765. this.text = this.extraLine.join('\n');
  766. },
  767. remove: function(e) {
  768. if (this.extraLine.length > 0) {
  769. // this.extraLine.pop();
  770. this.extraLine = [];
  771. this.text = this.extraLine.join('\n');
  772. }
  773. },
  774. }
  775. }
  776. function ab2hext(buffer) {
  777. var hexArr = Array.prototype.map.call(
  778. new Uint8Array(buffer),
  779. function(bit) {
  780. return ('00' + bit.toString(16)).slice(-2)
  781. }
  782. )
  783. return hexArr.join('');
  784. }
  785. function getHeight8(data) { //获取高8位
  786. let height;
  787. height = (data >> 8);
  788. return height;
  789. }
  790. function getLow8(data) { //获取低8位
  791. let low;
  792. low = (data & 0xFF);
  793. return low;
  794. }
  795. </script>
  796. <style>
  797. .hardware-border {
  798. border: 1rpx solid #9898FF;
  799. box-sizing: border-box;
  800. }
  801. .text-box {
  802. margin: 20rpx;
  803. display: flex;
  804. width: 95%;
  805. min-height: 300rpx;
  806. max-height: 600rpx;
  807. background-color: #EEEEEE;
  808. justify-content: center;
  809. align-items: center;
  810. text-align: center;
  811. font-size: 30upx;
  812. color: #353535;
  813. line-height: 1.8;
  814. border: 1rpx solid #555555;
  815. overflow-y: hidden;
  816. }
  817. </style>