Commit dffea6f6 authored by Skye Yu's avatar Skye Yu

feat: get file by continue decode

parent 2c149765
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import Jensen from '..';
import './index.css';
const jensen = new Jensen();
export function Home() {
const [files, setFiles] = useState<Jensen.FileInfo[]>([]);
useEffect(() => {
jensen.connect();
jensen.onconnect = () => {
console.log('connect successfully');
jensen.listFiles(3).then((res) => {
console.log('files', res);
res && setFiles(res);
});
};
}, []);
return <button onClick={() => jensen.connect()}>Click to connect</button>;
const downloadFile = () => {
const file = files[0];
console.time('---finished');
jensen.getDeviceInfo();
jensen.getFile(file.name, file.length, () => {
console.timeEnd('---finished');
});
setTimeout(() => {
jensen.getDeviceInfo();
}, 1000);
};
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px', alignItems: 'center', justifyContent: 'center' }}>
<button style={{ width: '200px', height: '40px' }} onClick={() => jensen.connect()}>
Click to connect
</button>
<button style={{ width: '200px', height: '40px' }} onClick={downloadFile}>
get file
</button>
<button style={{ width: '200px', height: '40px' }} onClick={() => jensen.getDeviceInfo()}>
getDeviceInfo
</button>
</div>
);
}
import { Logger as internalLogger, formatTime, shortcutKeys, sliceTime } from './utils';
import { Logger as internalLogger, formatTime, shortcutKeys, sliceTime, TaskQueue } from './utils';
const INVAILD = 0x00;
const QUERY_DEVICE_INFO = 0x01;
......@@ -53,8 +53,12 @@ const COMMAND_NAMES = {
[RECORD_TEST_END]: 'record test end'
};
let Logger = null;
const taskQueue = new TaskQueue();
function Jensen(log) {
const Logger = log || internalLogger;
Logger = log || internalLogger;
let device = null;
let actions = {};
let buffer = [];
......@@ -79,8 +83,14 @@ function Jensen(log) {
this.onconnect = null;
this.onreceive = null;
this.streamingBegin = false;
const RECV_BUFF_SIZE = 51200;
let worker = null;
let streamingByteView = new Uint8Array(new ArrayBuffer(RECV_BUFF_SIZE * 2));
let streamingBuffLength = 0;
const _check_conn_status = () => {
if (device?.opened === false) {
try {
......@@ -128,6 +138,14 @@ function Jensen(log) {
};
this.connect = async function () {
if (window.Worker) {
worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
worker.onmessage = (data) => {
if (this.streamingBegin) {
taskQueue.addTask(() => continueDecode(data.data));
}
};
}
Logger.debug('jensen', 'connect', 'connect');
let r = await self.tryconnect();
if (r) return;
......@@ -299,6 +317,7 @@ function Jensen(log) {
if (device)
device.transferIn(2, RECV_BUFF_SIZE).then((r) => {
Logger.save?.('jensen', 'tryReceive', r?.data);
worker.postMessage(r.data);
receive(r);
});
};
......@@ -327,6 +346,44 @@ function Jensen(log) {
}
};
this.resetStreamingData = () => {
streamingByteView = new ArrayBuffer(RECV_BUFF_SIZE * 2);
streamingBuffLength = 0;
this.streamingBegin = false;
};
const continueDecode = (block) => {
for (let k = 0; k < block.byteLength; k++) {
streamingByteView[k + streamingBuffLength] = block.getInt8(k);
}
streamingBuffLength += block.byteLength;
let rst = null;
let _startIndex = 0;
while (true) {
try {
rst = decodeMessage(streamingByteView, _startIndex, streamingBuffLength);
} catch (e) {
let msgid = parseInt(current.replace(/^cmd-(\d+)-(\d+)$/gi, '$1'));
let handler = Jensen.handlers[msgid];
handler(null, self);
trigger(null, msgid);
break;
}
if (rst == null) {
break;
}
const handler = Jensen.handlers[rst.message.id];
const r = handler(rst.message, self);
r && trigger(r, rst.message.id);
_startIndex += rst.length;
}
for (let k = 0, bs = streamingBuffLength - _startIndex; k < bs; k++) {
streamingByteView[k] = streamingByteView[k + _startIndex];
}
streamingBuffLength = streamingBuffLength - _startIndex;
return 'finished';
};
const tryDecode = function () {
// 一个容器,比单独任意一个小包要大一点儿,好像还差一点儿
let stime = new Date();
......@@ -410,7 +467,10 @@ function Jensen(log) {
const decodeMessage = function (dataView, startIndex, buffLength) {
let dataLen = buffLength - startIndex;
if (dataLen < 12) return null;
if (dataView[startIndex + 0] !== 0x12 || dataView[startIndex + 1] !== 0x34) throw new Error('invalid header');
if (dataView[startIndex + 0] !== 0x12 || dataView[startIndex + 1] !== 0x34) {
console.log('0000000000000000000000', dataView);
throw new Error('invalid header');
}
// 2 字节的指令id
let idx = 2;
// let cmdid = this.nextShort(idx);
......@@ -435,7 +495,10 @@ function Jensen(log) {
// 需要去除的字节数
var cutLen = 0;
// 数据还没有完全准备好
if (dataLen < 12 + len + padding) return null;
if (dataLen < 12 + len + padding) {
console.log(dataLen, len, padding);
return null;
}
// 去掉header部分
// 下面这一行做什么用的?
// for (let i = 0; i < 12; i++) this.buffer[i + cutLen];
......@@ -777,6 +840,7 @@ Jensen.prototype.getFile = async function (filename, length, ondata, onprogress)
ondata(msg.body);
Logger.info('jensen', 'getFile length', `${length} ${flen}`);
if (flen >= length) {
this.resetStreamingData();
document.removeEventListener('visibilitychange', visibilitychange);
Logger.info('jensen', 'getFile', 'file download finish.');
clearEventAndTask();
......@@ -784,12 +848,14 @@ Jensen.prototype.getFile = async function (filename, length, ondata, onprogress)
return 'OK';
}
} else {
this.resetStreamingData();
document.removeEventListener('visibilitychange', visibilitychange);
clearEventAndTask();
Logger.info('jensen', 'getFile', 'file download fail.');
ondata('fail');
}
};
this.streamingBegin = true;
this.onreceive = onprogress;
Jensen.registerHandler(TRANSFER_FILE, handler);
this.send(new Command(TRANSFER_FILE).body(fname));
......@@ -870,6 +936,7 @@ Jensen.prototype.getRecordingFile = function (seconds) {
Jensen.prototype.recordTestStart = async function (type, seconds) {
return this.send(new Command(0xf008).body([type]), seconds);
};
Jensen.prototype.recordTestEnd = async function (type, seconds) {
return this.send(new Command(0xf009).body([type]), seconds);
};
......
......@@ -308,3 +308,13 @@ export const shortcutKeys = {
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
}
};
export class TaskQueue {
constructor() {
this.queue = Promise.resolve();
}
addTask(task) {
this.queue = this.queue.then(() => task());
}
}
// worker.js
self.onmessage = function (event) {
self.postMessage(event.data);
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment