Commit fdfb03b7 authored by Skye's avatar Skye

feat: send schedule info api

parent 55ef423c
import { Jensen as Jen } from "./src/jensen"; import { Jensen as Jen } from './src/jensen';
import { Logger } from "./src/utils/utils"; import { Logger } from './src/utils/utils';
export type DeviceInfo = { export type DeviceInfo = {
sn: string; sn: string;
...@@ -8,7 +8,7 @@ export type DeviceInfo = { ...@@ -8,7 +8,7 @@ export type DeviceInfo = {
}; };
export type ReturnStruct = { export type ReturnStruct = {
common: { result: "failed" | "success" }; common: { result: 'failed' | 'success' };
}; };
export type FileInfo = { export type FileInfo = {
...@@ -24,10 +24,8 @@ export type FileInfo = { ...@@ -24,10 +24,8 @@ export type FileInfo = {
export type ScheduleInfo = { export type ScheduleInfo = {
startDate: Date; startDate: Date;
endDate: Date; endDate: Date;
keyAnswer?: string; os: 'Windows' | 'Linux' | 'Mac';
keyHangup?: string; platform: string;
keyReject?: string;
keyMicMute?: string;
}; };
declare class JensenType { declare class JensenType {
...@@ -39,33 +37,20 @@ declare class JensenType { ...@@ -39,33 +37,20 @@ declare class JensenType {
getDeviceInfo: (time?: number) => Promise<DeviceInfo>; getDeviceInfo: (time?: number) => Promise<DeviceInfo>;
listFiles: (time?: number) => Promise<FileInfo[]>; listFiles: (time?: number) => Promise<FileInfo[]>;
tryconnect: (disableOnConnect?: boolean) => Promise<boolean>; tryconnect: (disableOnConnect?: boolean) => Promise<boolean>;
getFile: ( getFile: (fileName: string, length: number, on?: (msg: Uint8Array | 'fail') => void, onprogress?: (size: number) => void) => void;
fileName: string, getFileBlock: (fileName: string, length: number, on?: (msg: Uint8Array | 'fail') => void) => Promise<ReturnStruct['common']>;
length: number,
on?: (msg: Uint8Array | "fail") => void,
onprogress?: (size: number) => void,
) => void;
getFileBlock: (
fileName: string,
length: number,
on?: (msg: Uint8Array | "fail") => void,
) => Promise<ReturnStruct["common"]>;
requestFirmwareUpgrade: ( requestFirmwareUpgrade: (
vn: number, vn: number,
length: number, length: number,
time?: number, time?: number
) => Promise<{ ) => Promise<{
result: "accepted" | "fail"; result: 'accepted' | 'fail';
}>; }>;
beginBNC: (time?: number) => Promise<ReturnStruct["common"]>; beginBNC: (time?: number) => Promise<ReturnStruct['common']>;
endBNC: (time?: number) => Promise<ReturnStruct["common"]>; endBNC: (time?: number) => Promise<ReturnStruct['common']>;
setTime: (date: Date, timeout?: number) => Promise<ReturnStruct["common"]>; setTime: (date: Date, timeout?: number) => Promise<ReturnStruct['common']>;
deleteFile: (fileName: string) => Promise<{ result: string }>; deleteFile: (fileName: string) => Promise<{ result: string }>;
uploadFirmware: ( uploadFirmware: (data: number[], seconds?: number, onProgress?: (cur: number, total: number) => void) => Promise<ReturnStruct['common']>;
data: number[],
seconds?: number,
onProgress?: (cur: number, total: number) => void,
) => Promise<ReturnStruct["common"]>;
getTime: (time?: number) => Promise<{ getTime: (time?: number) => Promise<{
time: string; time: string;
}>; }>;
...@@ -74,19 +59,10 @@ declare class JensenType { ...@@ -74,19 +59,10 @@ declare class JensenType {
autoPlay: boolean; autoPlay: boolean;
notification?: boolean; notification?: boolean;
} | null>; } | null>;
setAutoRecord: ( setAutoRecord: (enable: boolean, time?: number) => Promise<ReturnStruct['common']>;
enable: boolean, setAutoPlay: (enable: boolean, time?: number) => Promise<ReturnStruct['common']>;
time?: number,
) => Promise<ReturnStruct["common"]>;
setAutoPlay: (
enable: boolean,
time?: number,
) => Promise<ReturnStruct["common"]>;
isConnected: () => boolean; isConnected: () => boolean;
setNotification: ( setNotification: (state: boolean, time?: number) => Promise<ReturnStruct['common']>;
state: boolean,
time?: number,
) => Promise<ReturnStruct["common"]>;
ondisconnect?: Function; ondisconnect?: Function;
isStopConnectionCheck: boolean; isStopConnectionCheck: boolean;
getRecordingFile: () => Promise<{ getRecordingFile: () => Promise<{
...@@ -94,29 +70,18 @@ declare class JensenType { ...@@ -94,29 +70,18 @@ declare class JensenType {
createTime: string; createTime: string;
createDate: string; createDate: string;
}>; }>;
getCardInfo: ( getCardInfo: (seconds?: number) => Promise<{ used: number; capacity: number; status: string }>;
seconds?: number, formatCard: (seconds?: number) => Promise<ReturnStruct['common']>;
) => Promise<{ used: number; capacity: number; status: string }>; factoryReset: (seconds?: number) => Promise<ReturnStruct['common']>;
formatCard: (seconds?: number) => Promise<ReturnStruct["common"]>; restoreFactorySettings: (seconds?: number) => Promise<ReturnStruct['common']>;
factoryReset: (seconds?: number) => Promise<ReturnStruct["common"]>;
restoreFactorySettings: (seconds?: number) => Promise<ReturnStruct["common"]>;
getModel: () => string; getModel: () => string;
getFileCount: (seconds?: number) => Promise<{ count: number } | null>; getFileCount: (seconds?: number) => Promise<{ count: number } | null>;
recordTestStart: ( recordTestStart: (type: number, seconds?: number) => Promise<ReturnStruct['common']>;
type: number, recordTestEnd: (type: number, seconds?: number) => Promise<ReturnStruct['common']>;
seconds?: number, test: (seconds?: number) => Promise<ReturnStruct['common']>;
) => Promise<ReturnStruct["common"]>; setBluetoothPromptPlay: (state: boolean, seconds?: number) => Promise<ReturnStruct['common']>;
recordTestEnd: ( writeSerialNumber: (sn: string) => Promise<ReturnStruct['common']>;
type: number, sendScheduleInfo: (info: ScheduleInfo) => Promise<ReturnStruct['common']>;
seconds?: number,
) => Promise<ReturnStruct["common"]>;
test: (seconds?: number) => Promise<ReturnStruct["common"]>;
setBluetoothPromptPlay: (
state: boolean,
seconds?: number,
) => Promise<ReturnStruct["common"]>;
writeSerialNumber: (sn: string) => Promise<ReturnStruct["common"]>;
sendScheduleInfo: (info: ScheduleInfo) => Promise<ReturnStruct["common"]>;
} }
//@ts-ignore //@ts-ignore
......
{ {
"name": "jensen", "name": "jensen",
"version": "1.1.3", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "jensen", "name": "jensen",
"version": "1.1.3", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"prettier": "^3.3.3" "prettier": "^3.3.3"
......
import { Logger, Logger as internalLogger, getTimeStr } from './utils/utils'; import { Logger, Logger as internalLogger, getTimeStr, shortcutKeys } from './utils/utils';
const QUERY_DEVICE_INFO = 0x01; const QUERY_DEVICE_INFO = 0x01;
const QUERY_DEVICE_TIME = 0x02; const QUERY_DEVICE_TIME = 0x02;
...@@ -25,200 +25,196 @@ const TEST_SN_WRITE = 0xf007; ...@@ -25,200 +25,196 @@ const TEST_SN_WRITE = 0xf007;
const RECORD_TEST_START = 0xf008; // 录音测试开始 const RECORD_TEST_START = 0xf008; // 录音测试开始
const RECORD_TEST_END = 0xf009; // 录音测试结束 const RECORD_TEST_END = 0xf009; // 录音测试结束
const COMMAND_NAMES = [ const COMMAND_NAMES = [
'invalid-0', 'invalid-0',
'get-device-info', 'get-device-info',
'get-device-time', 'get-device-time',
'set-device-time', 'set-device-time',
'get-file-list', 'get-file-list',
'transfer-file', 'transfer-file',
'get-file-count', 'get-file-count',
'delete-file', 'delete-file',
'request-firmware-upgrade', 'request-firmware-upgrade',
'firmware-upload', 'firmware-upload',
'read card info', 'read card info',
'format card', 'format card',
'get recording file', 'get recording file',
'restore factory settings', 'restore factory settings',
'device msg test', 'device msg test',
'bnc demo test', 'bnc demo test',
'get-settings', 'get-settings',
'set-settings', 'set-settings',
'get file block', 'get file block',
'factory reset', 'factory reset'
]; ];
function Jensen(log) { function Jensen(log) {
const Logger = log || internalLogger const Logger = log || internalLogger;
let device = null; let device = null;
let actions = {}; let actions = {};
let buffer = []; let buffer = [];
let blocks = []; let blocks = [];
let sequence = 0; let sequence = 0;
let current = null; let current = null;
let commands = []; let commands = [];
let handlers = []; let handlers = [];
let statusTimeout = null; let statusTimeout = null;
let recv = false; let recv = false;
let ready = false; let ready = false;
let totalBytes = 0; let totalBytes = 0;
let self = this; let self = this;
this.data = {}; this.data = {};
// 消息 // 消息
this.decodeTimeout = 0; this.decodeTimeout = 0;
this.timewait = 1; this.timewait = 1;
this.ondisconnect = null; this.ondisconnect = null;
this.isStopConnectionCheck = false; this.isStopConnectionCheck = false;
this.onconnect = null; this.onconnect = null;
this.onreceive = null; this.onreceive = null;
const RECV_BUFF_SIZE = 51200; const RECV_BUFF_SIZE = 51200;
const _check_conn_status = () => { const _check_conn_status = () => {
if (device?.opened === false) { if (device?.opened === false) {
try { try {
clearTimeout(statusTimeout); clearTimeout(statusTimeout);
const audio = document.getElementById('test_audio'); const audio = document.getElementById('test_audio');
audio && audio.pause() && audio.remove(); audio && audio.pause() && audio.remove();
if (this.ondisconnect && !this.isStopConnectionCheck) this.ondisconnect(); if (this.ondisconnect && !this.isStopConnectionCheck) this.ondisconnect();
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
} }
statusTimeout = setTimeout(() => { statusTimeout = setTimeout(() => {
_check_conn_status(); _check_conn_status();
}, 100); }, 100);
}; };
const crash = async function (procedure, error) { const crash = async function (procedure, error) {
Logger.error('jensen', procedure, String(error)); Logger.error('jensen', procedure, String(error));
self.versionCode = null; self.versionCode = null;
self.versionNumber = null; self.versionNumber = null;
}; };
const setup = async function (disableOnConnect) { const setup = async function (disableOnConnect) {
self.versionCode = null; self.versionCode = null;
self.versionNumber = null; self.versionNumber = null;
commands.length = 0; commands.length = 0;
try { try {
await device.selectConfiguration(1); await device.selectConfiguration(1);
await device.claimInterface(0); await device.claimInterface(0);
await device.selectAlternateInterface(0, 0); await device.selectAlternateInterface(0, 0);
self.model = device.productId == 45069 ? 'hidock-h1e' : 'hidock-h1'; self.model = device.productId == 45069 ? 'hidock-h1e' : 'hidock-h1';
} catch (e) { } catch (e) {
Logger.error('jensen', 'setup', String(e)); Logger.error('jensen', 'setup', String(e));
} }
if (!disableOnConnect) _check_conn_status(); if (!disableOnConnect) _check_conn_status();
current = null; current = null;
recv = false; recv = false;
ready = true; ready = true;
Logger.debug('jensen', 'setup', 'setup webusb connection'); Logger.debug('jensen', 'setup', 'setup webusb connection');
try { try {
if (!disableOnConnect && !self.isStopConnectionCheck) self.onconnect?.(); if (!disableOnConnect && !self.isStopConnectionCheck) self.onconnect?.();
} catch (err) { } catch (err) {
Logger.error('jensen', 'setup', err); Logger.error('jensen', 'setup', err);
} }
}; };
this.connect = async function () { this.connect = async function () {
Logger.debug('jensen', 'connect', 'connect'); Logger.debug('jensen', 'connect', 'connect');
let r = await self.tryconnect(); let r = await self.tryconnect();
if (r) return; if (r) return;
let conn = await navigator.usb.requestDevice({
filters: [{ vendorId: 0x10d6 }],
});
await conn.open();
self.model = conn.productId == 45069 ? 'hidock-h1e' : 'hidock-h1';
device = conn;
await setup();
};
this.getModel = function () { let conn = await navigator.usb.requestDevice({
return this.model; filters: [{ vendorId: 0x10d6 }]
}; });
await conn.open();
self.model = conn.productId == 45069 ? 'hidock-h1e' : 'hidock-h1';
device = conn;
await setup();
};
this.init = async function _init() { this.getModel = function () {
if (!navigator.usb) { return this.model;
Logger.error('jensen', 'init', 'webusb not supported'); };
return;
}
navigator.usb.onconnect = function (e) {
self.tryconnect();
};
await self.connect();
};
this.tryconnect = async function (disableOnConnect) { this.init = async function _init() {
await this.disconnect(); if (!navigator.usb) {
let devices = await navigator.usb.getDevices(); Logger.error('jensen', 'init', 'webusb not supported');
for (let i = 0; i < devices.length; i++) { return;
let item = devices[i]; }
if (item.productName.indexOf('HiDock') > -1) { navigator.usb.onconnect = function (e) {
Logger.debug('jensen', 'tryconnect', 'detected: ' + item.productName); self.tryconnect();
await item.open();
device = item;
await setup(disableOnConnect);
return true;
}
}
Logger.debug('jensen', 'tryconnect', 'no HiDock found');
return false;
}; };
await self.connect();
};
this.isConnected = function () { this.tryconnect = async function (disableOnConnect) {
return device != null; await this.disconnect();
}; let devices = await navigator.usb.getDevices();
for (let i = 0; i < devices.length; i++) {
let item = devices[i];
if (item.productName.indexOf('HiDock') > -1) {
Logger.debug('jensen', 'tryconnect', 'detected: ' + item.productName);
await item.open();
device = item;
await setup(disableOnConnect);
return true;
}
}
Logger.debug('jensen', 'tryconnect', 'no HiDock found');
return false;
};
this.disconnect = async function () { this.isConnected = function () {
Logger.info('jensen', 'disconnect', 'disconnect'); return device != null;
try { };
await device?.close();
} catch (e) {
}
};
this.send = function (cmd, seconds, onprogress) { this.disconnect = async function () {
cmd.sequence(sequence++); Logger.info('jensen', 'disconnect', 'disconnect');
cmd.onprogress = onprogress; try {
if (seconds) cmd.expireAfter(seconds); await device?.close();
commands.push(cmd); } catch (e) {}
sendNext(); };
return register(cmd, seconds);
};
const sendNext = async function () { this.send = function (cmd, seconds, onprogress) {
if (current) { cmd.sequence(sequence++);
// Logger.info('jensen', 'sendNext', 'return cuz current is: ' + current); cmd.onprogress = onprogress;
return; if (seconds) cmd.expireAfter(seconds);
} commands.push(cmd);
sendNext();
return register(cmd, seconds);
};
let cmd = null; const sendNext = async function () {
let now = new Date().getTime(); if (current) {
while (true) { // Logger.info('jensen', 'sendNext', 'return cuz current is: ' + current);
if (commands.length == 0) return; return;
cmd = commands.shift(); }
if (cmd.expireTime > 0 && cmd.expireTime < now) {
Logger.info('jensen', 'sendNext', 'expired: cmd-' + cmd.command + '-' + cmd.index + ', ' + COMMAND_NAMES[cmd.command]); let cmd = null;
continue; let now = new Date().getTime();
} while (true) {
break; if (commands.length == 0) return;
} cmd = commands.shift();
if (cmd.expireTime > 0 && cmd.expireTime < now) {
Logger.info('jensen', 'sendNext', 'expired: cmd-' + cmd.command + '-' + cmd.index + ', ' + COMMAND_NAMES[cmd.command]);
continue;
}
break;
}
let data = cmd.make(); let data = cmd.make();
current = 'cmd-' + cmd.command + '-' + cmd.index; current = 'cmd-' + cmd.command + '-' + cmd.index;
Logger.debug('jensen', 'sendNext', 'command: ' + COMMAND_NAMES[cmd.command] + ', data bytes: ' + data.byteLength); Logger.debug('jensen', 'sendNext', 'command: ' + COMMAND_NAMES[cmd.command] + ', data bytes: ' + data.byteLength);
self.timewait = cmd.command == TRANSFER_FILE ? 1000 : 10; self.timewait = cmd.command == TRANSFER_FILE ? 1000 : 10;
await device.transferOut(1, data).catch((e) => crash('sendNext', e)); await device.transferOut(1, data).catch((e) => crash('sendNext', e));
if (cmd.onprogress) cmd.onprogress(1, 1); if (cmd.onprogress) cmd.onprogress(1, 1);
/* /*
if (data.byteLength < 1000) await device.transferOut(1, data).catch((e) => crash('sendNext', e)); if (data.byteLength < 1000) await device.transferOut(1, data).catch((e) => crash('sendNext', e));
else else
{ {
...@@ -240,321 +236,309 @@ function Jensen(log) { ...@@ -240,321 +236,309 @@ function Jensen(log) {
} }
} }
*/ */
totalBytes = 0; totalBytes = 0;
if (recv == false) tryReceive(); if (recv == false) tryReceive();
else recv = true; else recv = true;
}; };
const register = function (cmd, seconds) { const register = function (cmd, seconds) {
let tag = 'cmd-' + cmd.command + '-' + cmd.index; let tag = 'cmd-' + cmd.command + '-' + cmd.index;
let t = seconds let t = seconds
? setTimeout(() => { ? setTimeout(() => {
timeout(tag); timeout(tag);
}, seconds * 1000) }, seconds * 1000)
: null; : null;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
actions[tag] = { actions[tag] = {
tag: tag, tag: tag,
resolve: resolve, resolve: resolve,
reject: reject, reject: reject,
timeout: t, timeout: t
}; };
}); });
}; };
const trigger = function (resp, msgid) { const trigger = function (resp, msgid) {
if (current == null) return; if (current == null) return;
Logger.debug('jensen', 'trigger', 'trigger - ' + current.substring(0, current.lastIndexOf('-')) + ' <---> cmd-' + msgid); Logger.debug('jensen', 'trigger', 'trigger - ' + current.substring(0, current.lastIndexOf('-')) + ' <---> cmd-' + msgid);
if (current.substring(0, current.lastIndexOf('-')) != 'cmd-' + msgid) { if (current.substring(0, current.lastIndexOf('-')) != 'cmd-' + msgid) {
current = null; current = null;
return; return;
} }
if (current in actions == false) { if (current in actions == false) {
Logger.debug('jensen', 'trigger', 'no action registered'); Logger.debug('jensen', 'trigger', 'no action registered');
return; return;
} }
let p = actions[current]; let p = actions[current];
if (p.timeout) clearTimeout(p.timeout); if (p.timeout) clearTimeout(p.timeout);
p.resolve(resp); p.resolve(resp);
delete actions[current]; delete actions[current];
current = null; current = null;
}; };
const timeout = function (tag) { const timeout = function (tag) {
Logger.debug('jensen', 'timeout', 'timeout ' + tag); Logger.debug('jensen', 'timeout', 'timeout ' + tag);
let p = actions[tag]; let p = actions[tag];
p.resolve(null); p.resolve(null);
delete actions[tag]; delete actions[tag];
}; };
this.dump = function () { this.dump = function () {
console.log('actions', actions); console.log('actions', actions);
console.log('pending commands', commands); console.log('pending commands', commands);
console.log('current', current); console.log('current', current);
console.log('device', device); console.log('device', device);
}; };
const tryReceive = function () { const tryReceive = function () {
if (device) if (device)
device.transferIn(2, RECV_BUFF_SIZE).then((r) => { device.transferIn(2, RECV_BUFF_SIZE).then((r) => {
Logger.save?.('jensen', 'tryReceive', r?.data); Logger.save?.('jensen', 'tryReceive', r?.data);
receive(r); receive(r);
}); });
}; };
const read_int = function (a, b, c, d) { const read_int = function (a, b, c, d) {
if (arguments.length === 2) { if (arguments.length === 2) {
return ((a & 0xff) << 8) | (b & 0xff); return ((a & 0xff) << 8) | (b & 0xff);
} else if (arguments.length === 4) { } else if (arguments.length === 4) {
return ((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff); return ((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff);
} }
}; };
const receive = function (result) { const receive = function (result) {
totalBytes += result.data.byteLength; totalBytes += result.data.byteLength;
// 做一个回调,怎么样找到它呢? // 做一个回调,怎么样找到它呢?
blocks.push(result.data); blocks.push(result.data);
tryReceive(); tryReceive();
tryDecode(); tryDecode();
if (self.onreceive) { if (self.onreceive) {
try { try {
self.onreceive(totalBytes); self.onreceive(totalBytes);
} catch (e) { } catch (e) {}
} } else {
} else { // if (self.decodeTimeout) clearTimeout(self.decodeTimeout);
// if (self.decodeTimeout) clearTimeout(self.decodeTimeout); // self.decodeTimeout = setTimeout(function () {
// self.decodeTimeout = setTimeout(function () { // tryDecode();
// tryDecode(); // }, self.timewait);
// }, self.timewait); }
} };
};
// 持续解码
const continueDecode = (block) => {
const buff = new ArrayBuffer(RECV_BUFF_SIZE * 2);
let bview = new Uint8Array(buff);
for (let k = 0; k < block.byteLength; k++) {
bview[k] = block.getInt8(k);
}
let result = null;
try {
result = decodeMessage(bview, 0, block.byteLength);
} catch (err) {
console.error('ci decode', err);
}
sendNext();
const msg = result.message;
return result && msg.id === TRANSFER_FILE ? msg.body : null;
};
// 持续解码 const tryDecode = function () {
const continueDecode = (block) => { // 一个容器,比单独任意一个小包要大一点儿,好像还差一点儿
const buff = new ArrayBuffer(RECV_BUFF_SIZE * 2); let stime = new Date();
let bview = new Uint8Array(buff); let buff = new ArrayBuffer(RECV_BUFF_SIZE * 2);
for (let k = 0; k < block.byteLength; k++) { let bview = new Uint8Array(buff);
bview[k] = block.getInt8(k); // buff中实际使用的字节数,有效字节数
let buffLength = 0;
let crash = false;
for (let i = 0, l = blocks.length; i < l; i++) {
let block = blocks.shift();
// 把block加到buff的最末尾
for (let k = 0; k < block.byteLength; k++) {
bview[k + buffLength] = block.getInt8(k);
}
buffLength += block.byteLength;
let startIndex = 0;
while (true) {
let rst = null;
try {
rst = decodeMessage(bview, startIndex, buffLength);
} catch (e) {
crash = true;
break;
}
if (rst == null) {
break;
}
startIndex += rst.length;
let msg = rst.message;
// WARN: 接下来怎么整
let cname = msg.id === FACTORY_RESET ? 'factory-reset' : COMMAND_NAMES[msg.id];
let heading = [];
for (let x = 0; x < msg.body?.byteLength && x < 32; x++) {
heading.push('0' + (msg.body[x] & 0xff).toString(16).replace(/^0(\w{2})$/gi, '$1'));
} }
let result = null; if (msg.id !== TRANSFER_FILE)
Logger.debug(
'jensen',
'receive',
'recv: ' + cname + ', seq: ' + msg.sequence + ', data bytes: ' + msg.body?.byteLength + ', data: ' + heading.join(' ')
);
try { try {
result = decodeMessage(bview, 0, block.byteLength); let handler = Jensen.handlers[msg.id];
} catch (err) { let r = handler(msg, self);
console.error('ci decode', err); if (r) trigger(r, msg.id);
} catch (e) {
trigger(e);
Logger.error('jensen', 'receive', 'recv: ' + COMMAND_NAMES[msg.id] + ', seq: ' + msg.sequence + ', error: ' + String(e));
} }
sendNext(); sendNext();
const msg = result.message; }
return result && msg.id === TRANSFER_FILE ? msg.body : null; // 是否已经崩溃了?
} if (crash) {
// cmd-5-42
const tryDecode = function () { // 5是msgid
// 一个容器,比单独任意一个小包要大一点儿,好像还差一点儿 let msgid = parseInt(current.replace(/^cmd-(\d+)-(\d+)$/gi, '$1'));
let stime = new Date(); try {
let buff = new ArrayBuffer(RECV_BUFF_SIZE * 2); let handler = Jensen.handlers[msgid];
let bview = new Uint8Array(buff); handler(null, self);
// buff中实际使用的字节数,有效字节数 } catch (e) {
let buffLength = 0; trigger(e);
let crash = false; Logger.error('jensen', 'decode', 'decode error: ' + String(e));
for (let i = 0, l = blocks.length; i < l; i++) {
let block = blocks.shift();
// 把block加到buff的最末尾
for (let k = 0; k < block.byteLength; k++) {
bview[k + buffLength] = block.getInt8(k);
}
buffLength += block.byteLength;
let startIndex = 0;
while (true) {
let rst = null;
try {
rst = decodeMessage(bview, startIndex, buffLength);
} catch (e) {
crash = true;
break;
}
if (rst == null) {
break;
}
startIndex += rst.length;
let msg = rst.message;
// WARN: 接下来怎么整
let cname = msg.id === FACTORY_RESET ? 'factory-reset' : COMMAND_NAMES[msg.id];
let heading = [];
for (let x = 0; x < msg.body?.byteLength && x < 32; x++) {
heading.push('0' + (msg.body[x] & 0xff).toString(16).replace(/^0(\w{2})$/gi, '$1'));
}
if (msg.id !== TRANSFER_FILE)
Logger.debug(
'jensen',
'receive',
'recv: ' +
cname +
', seq: ' +
msg.sequence +
', data bytes: ' +
msg.body?.byteLength +
', data: ' +
heading.join(' '),
);
try {
let handler = Jensen.handlers[msg.id];
let r = handler(msg, self);
if (r) trigger(r, msg.id);
} catch (e) {
trigger(e);
Logger.error(
'jensen',
'receive',
'recv: ' + COMMAND_NAMES[msg.id] + ', seq: ' + msg.sequence + ', error: ' + String(e),
);
}
sendNext();
}
// 是否已经崩溃了?
if (crash) {
// cmd-5-42
// 5是msgid
let msgid = parseInt(current.replace(/^cmd-(\d+)-(\d+)$/gi, '$1'));
try {
let handler = Jensen.handlers[msgid];
handler(null, self);
} catch (e) {
trigger(e);
Logger.error('jensen', 'decode', 'decode error: ' + String(e));
}
trigger(null, msgid);
blocks.length = 0;
break;
}
// WARN: 需要把剩余的字节数挪到最前面去
// startIndex已经抵达最后的位置,startIndex -> buffLength就是需要移到最前面去的字节内容了
for (let k = 0, bs = buffLength - startIndex; k < bs; k++) {
bview[k] = bview[k + startIndex];
}
buffLength = buffLength - startIndex;
} }
console.error('Decode: ' + (new Date().getTime() - stime.getTime()) + 'ms'); trigger(null, msgid);
}; blocks.length = 0;
break;
}
// WARN: 需要把剩余的字节数挪到最前面去
// startIndex已经抵达最后的位置,startIndex -> buffLength就是需要移到最前面去的字节内容了
for (let k = 0, bs = buffLength - startIndex; k < bs; k++) {
bview[k] = bview[k + startIndex];
}
buffLength = buffLength - startIndex;
}
console.error('Decode: ' + (new Date().getTime() - stime.getTime()) + 'ms');
};
const decodeMessage = function (dataView, startIndex, buffLength) { const decodeMessage = function (dataView, startIndex, buffLength) {
let dataLen = buffLength - startIndex; let dataLen = buffLength - startIndex;
if (dataLen < 12) return null; 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) throw new Error('invalid header');
// 2 字节的指令id // 2 字节的指令id
let idx = 2; let idx = 2;
// let cmdid = this.nextShort(idx); // let cmdid = this.nextShort(idx);
let cmdid = read_int(dataView[startIndex + idx], dataView[startIndex + idx + 1]); let cmdid = read_int(dataView[startIndex + idx], dataView[startIndex + idx + 1]);
idx += 2; idx += 2;
let sequeue = read_int( let sequeue = read_int(
dataView[startIndex + idx + 0], dataView[startIndex + idx + 0],
dataView[startIndex + idx + 1], dataView[startIndex + idx + 1],
dataView[startIndex + idx + 2], dataView[startIndex + idx + 2],
dataView[startIndex + idx + 3], dataView[startIndex + idx + 3]
); );
idx += 4; idx += 4;
let len = read_int( let len = read_int(
dataView[startIndex + idx + 0], dataView[startIndex + idx + 0],
dataView[startIndex + idx + 1], dataView[startIndex + idx + 1],
dataView[startIndex + idx + 2], dataView[startIndex + idx + 2],
dataView[startIndex + idx + 3], dataView[startIndex + idx + 3]
); );
let padding = (len >> 24) & 0xff; let padding = (len >> 24) & 0xff;
len = len & 0xffffff; len = len & 0xffffff;
idx += 4; idx += 4;
// 需要去除的字节数 // 需要去除的字节数
var cutLen = 0; var cutLen = 0;
// 数据还没有完全准备好 // 数据还没有完全准备好
if (dataLen < 12 + len + padding) return null; if (dataLen < 12 + len + padding) return null;
// 去掉header部分 // 去掉header部分
// 下面这一行做什么用的? // 下面这一行做什么用的?
// for (let i = 0; i < 12; i++) this.buffer[i + cutLen]; // for (let i = 0; i < 12; i++) this.buffer[i + cutLen];
cutLen += 12; cutLen += 12;
// 取走body部分 // 取走body部分
// 数据体部分 // 数据体部分
// let body = new Uint8Array(new ArrayBuffer(len)); // let body = new Uint8Array(new ArrayBuffer(len));
// for (let i = 0; i < len; i++) body.push(this.buffer[i + cutLen]); // for (let i = 0; i < len; i++) body.push(this.buffer[i + cutLen]);
let body = dataView.slice(startIndex + cutLen, startIndex + cutLen + len); let body = dataView.slice(startIndex + cutLen, startIndex + cutLen + len);
cutLen += len; cutLen += len;
// 干掉补上来的数据 // 干掉补上来的数据
// 下面这一行做什么用的? // 下面这一行做什么用的?
// for (let i = 0; i < padding; i++) this.buffer[i + cutLen]; // for (let i = 0; i < padding; i++) this.buffer[i + cutLen];
cutLen += padding; cutLen += padding;
// self.buffer = self.buffer.slice(cutLen); // self.buffer = self.buffer.slice(cutLen);
return { message: new Message(cmdid, sequeue, body), length: cutLen }; return { message: new Message(cmdid, sequeue, body), length: cutLen };
}; };
this.to_bcd = function (str) { this.to_bcd = function (str) {
let x = []; let x = [];
for (let i = 0; i < str.length; i += 2) { for (let i = 0; i < str.length; i += 2) {
let h = (str.charCodeAt(i) - 48) & 0xff; let h = (str.charCodeAt(i) - 48) & 0xff;
let l = (str.charCodeAt(i + 1) - 48) & 0xff; let l = (str.charCodeAt(i + 1) - 48) & 0xff;
x.push((h << 4) | l); x.push((h << 4) | l);
} }
return x; return x;
}; };
this.from_bcd = function () { this.from_bcd = function () {
let str = ''; let str = '';
for (let i = 0; i < arguments.length; i++) { for (let i = 0; i < arguments.length; i++) {
let v = arguments[i] & 0xff; let v = arguments[i] & 0xff;
str += (v >> 4) & 0x0f; str += (v >> 4) & 0x0f;
str += v & 0x0f; str += v & 0x0f;
} }
return str; return str;
}; };
} }
function Command(id) { function Command(id) {
this.command = id; this.command = id;
this.msgBody = []; this.msgBody = [];
this.index = 0; this.index = 0;
this.expireTime = 0; this.expireTime = 0;
this.timeout = 0; this.timeout = 0;
this.body = function (data) { this.body = function (data) {
this.msgBody = data; this.msgBody = data;
return this; return this;
}; };
this.expireAfter = function (seconds) { this.expireAfter = function (seconds) {
this.expireTime = new Date().getTime() + seconds * 1000; this.expireTime = new Date().getTime() + seconds * 1000;
}; };
this.sequence = function (seq) { this.sequence = function (seq) {
this.index = seq; this.index = seq;
return this; return this;
}; };
this.make = function () { this.make = function () {
let msg = new Uint8Array(2 + 2 + 4 + 4 + this.msgBody.length); let msg = new Uint8Array(2 + 2 + 4 + 4 + this.msgBody.length);
let idx = 0; let idx = 0;
// msg header // msg header
msg[idx++] = 0x12; msg[idx++] = 0x12;
msg[idx++] = 0x34; msg[idx++] = 0x34;
// msg command id // msg command id
msg[idx++] = (this.command >> 8) & 0xff; msg[idx++] = (this.command >> 8) & 0xff;
msg[idx++] = (this.command >> 0) & 0xff; msg[idx++] = (this.command >> 0) & 0xff;
// msg sequence id // msg sequence id
msg[idx++] = (this.index >> 24) & 0xff; msg[idx++] = (this.index >> 24) & 0xff;
msg[idx++] = (this.index >> 16) & 0xff; msg[idx++] = (this.index >> 16) & 0xff;
msg[idx++] = (this.index >> 8) & 0xff; msg[idx++] = (this.index >> 8) & 0xff;
msg[idx++] = (this.index >> 0) & 0xff; msg[idx++] = (this.index >> 0) & 0xff;
// msg body length // msg body length
let len = this.msgBody.length; let len = this.msgBody.length;
msg[idx++] = (len >> 24) & 0xff; msg[idx++] = (len >> 24) & 0xff;
msg[idx++] = (len >> 16) & 0xff; msg[idx++] = (len >> 16) & 0xff;
msg[idx++] = (len >> 8) & 0xff; msg[idx++] = (len >> 8) & 0xff;
msg[idx++] = (len >> 0) & 0xff; msg[idx++] = (len >> 0) & 0xff;
// msg body // msg body
for (let i = 0; i < this.msgBody.length; i++) msg[idx++] = this.msgBody[i] & 0xff; for (let i = 0; i < this.msgBody.length; i++) msg[idx++] = this.msgBody[i] & 0xff;
return msg; return msg;
}; };
} }
function Message(id, sequence, body) { function Message(id, sequence, body) {
this.id = id; this.id = id;
this.sequence = sequence; this.sequence = sequence;
this.body = body; this.body = body;
} }
/** /**
...@@ -562,496 +546,500 @@ function Message(id, sequence, body) { ...@@ -562,496 +546,500 @@ function Message(id, sequence, body) {
* @param {*} handler callback function for response processing, return `non-undefined` means command ended * @param {*} handler callback function for response processing, return `non-undefined` means command ended
*/ */
Jensen.registerHandler = function (cmdid, handler) { Jensen.registerHandler = function (cmdid, handler) {
if (typeof Jensen.handlers == 'undefined') Jensen.handlers = {}; if (typeof Jensen.handlers == 'undefined') Jensen.handlers = {};
Jensen.handlers[cmdid] = handler; Jensen.handlers[cmdid] = handler;
}; };
Jensen.prototype.getDeviceInfo = async function (seconds) { Jensen.prototype.getDeviceInfo = async function (seconds) {
return this.send(new Command(QUERY_DEVICE_INFO), seconds); return this.send(new Command(QUERY_DEVICE_INFO), seconds);
}; };
Jensen.prototype.getTime = async function (seconds) { Jensen.prototype.getTime = async function (seconds) {
return this.send(new Command(QUERY_DEVICE_TIME), seconds); return this.send(new Command(QUERY_DEVICE_TIME), seconds);
}; };
Jensen.prototype.getFileCount = async function (seconds) { Jensen.prototype.getFileCount = async function (seconds) {
return this.send(new Command(QUERY_FILE_COUNT), seconds); return this.send(new Command(QUERY_FILE_COUNT), seconds);
}; };
Jensen.prototype.factoryReset = async function (seconds) { Jensen.prototype.factoryReset = async function (seconds) {
if (this.versionNumber < 327705) return null; if (this.versionNumber < 327705) return null;
return this.send(new Command(FACTORY_RESET), seconds); return this.send(new Command(FACTORY_RESET), seconds);
}; };
Jensen.prototype.restoreFactorySettings = async function (seconds) { Jensen.prototype.restoreFactorySettings = async function (seconds) {
if (this.model === 'hidock-h1e' && this.versionNumber < 393476) return null; if (this.model === 'hidock-h1e' && this.versionNumber < 393476) return null;
if (this.model === 'hidock-h1' && this.versionNumber < 327944) return null; if (this.model === 'hidock-h1' && this.versionNumber < 327944) return null;
return this.send(new Command(RESTORE_FACTORY_SETTINGS).body([0x01, 0x02, 0x03, 0x04]), seconds); return this.send(new Command(RESTORE_FACTORY_SETTINGS).body([0x01, 0x02, 0x03, 0x04]), seconds);
}; };
Jensen.prototype.listFiles = async function () { Jensen.prototype.listFiles = async function () {
let tag = 'filelist'; let tag = 'filelist';
if (this[tag] != null) return null; if (this[tag] != null) return null;
let fc = null; let fc = null;
if (typeof this.versionNumber == 'undefined' || this.versionNumber <= 327722) { if (typeof this.versionNumber == 'undefined' || this.versionNumber <= 327722) {
fc = await this.getFileCount(5); fc = await this.getFileCount(5);
if (fc == null) return null; if (fc == null) return null;
}
if (fc && fc.count == 0) return null;
let self = this;
// let tag = 'data_' + new Date().getTime();
this[tag] = [];
Jensen.registerHandler(QUERY_FILE_LIST, (msg, jensen) => {
if (msg.body.length == 0) {
jensen[tag] = null;
return [];
} }
jensen[tag].push(msg.body);
let data = [];
let files = [];
let fcount = -1;
let start = 0;
for (let i = 0; i < jensen[tag].length; i++) {
for (let k = 0; k < jensen[tag][i].length; k++) data.push(jensen[tag][i][k]);
}
if ((data[0] & 0xff) == 0xff && (data[1] & 0xff) == 0xff) {
fcount = ((data[2] & 0xff) << 24) | ((data[3] & 0xff) << 16) | ((data[4] & 0xff) << 8) | (data[5] & 0xff);
start += 6;
}
let fnpad = function (v) {
return v > 9 ? v : '0' + v;
};
for (let i = start; i < data.length; ) {
let len = 0;
let fname = [];
if (i + 4 >= data.length) break;
let ver = data[i++] & 0xff;
let nameLen = ((data[i++] & 0xff) << 16) | ((data[i++] & 0xff) << 8) | (data[i++] & 0xff);
for (let k = 0; k < nameLen && i < data.length; k++) {
let c = data[i++] & 0xff;
if (c > 0) fname.push(String.fromCharCode(c));
}
// 4 + nameLen + 4 + 6 + 16
if (i + 4 + 6 + 16 > data.length) {
break;
}
let flen = ((data[i++] & 0xff) << 24) | ((data[i++] & 0xff) << 16) | ((data[i++] & 0xff) << 8) | (data[i++] & 0xff);
i += 6;
let sign = [];
for (let k = 0; k < 16; k++, i++) {
let h = (data[i] & 0xff).toString(16);
sign.push(h.length == 1 ? '0' + h : h);
}
let ftime = fname.join('');
let duration = 0;
if (ftime.match(/^\d{14}REC\d+\.wav$/gi)) {
ftime = ftime.replace(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})REC.*$/gi, '$1-$2-$3 $4:$5:$6');
ftime = new Date(ftime);
duration = flen / 32;
} else if (ftime.match(/^(\d{2})?(\d{2})(\w{3})(\d{2})-(\d{2})(\d{2})(\d{2})-.*\.hda$/gi)) {
// 2024Mar19-110932-Rec00.hda
ftime = ftime.replace(/^(\d{2})?(\d{2})(\w{3})(\d{2})-(\d{2})(\d{2})(\d{2})-.*\.hda$/gi, '20$2 $3 $4 $5:$6:$7');
ftime = new Date(ftime);
duration = (flen / 32) * 4;
} else {
ftime = null;
}
if (ver == 1) {
duration = duration * 2;
}
let createDate = '';
let createTime = '';
if (ftime) {
createDate = ftime.getFullYear() + '/' + fnpad(ftime.getMonth() + 1) + '/' + fnpad(ftime.getDate());
createTime = fnpad(ftime.getHours()) + ':' + fnpad(ftime.getMinutes()) + ':' + fnpad(ftime.getSeconds());
}
files.push({
name: fname.join(''),
createDate: createDate,
createTime: createTime,
time: ftime,
duration: duration,
length: flen,
signature: sign.join('')
});
}
// if (fcount == -1 && (fc))
// 如果没有判断数量的依据
if (fcount == -1) {
// return [];
}
if ((fc && files.length >= fc.count) || (fcount > -1 && files.length >= fcount)) {
// delete jensen[tag];
jensen[tag] = null;
return files.filter((f) => {
return Boolean(f.time);
});
}
});
if (fc && fc.count == 0) return null; return this.send(new Command(QUERY_FILE_LIST));
let self = this;
// let tag = 'data_' + new Date().getTime();
this[tag] = [];
Jensen.registerHandler(QUERY_FILE_LIST, (msg, jensen) => {
if (msg.body.length == 0) {
jensen[tag] = null;
return [];
}
jensen[tag].push(msg.body);
let data = [];
let files = [];
let fcount = -1;
let start = 0;
for (let i = 0; i < jensen[tag].length; i++) {
for (let k = 0; k < jensen[tag][i].length; k++) data.push(jensen[tag][i][k]);
}
if ((data[0] & 0xff) == 0xff && (data[1] & 0xff) == 0xff) {
fcount = ((data[2] & 0xff) << 24) | ((data[3] & 0xff) << 16) | ((data[4] & 0xff) << 8) | (data[5] & 0xff);
start += 6;
}
let fnpad = function (v) {
return v > 9 ? v : '0' + v;
};
for (let i = start; i < data.length;) {
let len = 0;
let fname = [];
if (i + 4 >= data.length) break;
let ver = data[i++] & 0xff;
let nameLen = ((data[i++] & 0xff) << 16) | ((data[i++] & 0xff) << 8) | (data[i++] & 0xff);
for (let k = 0; k < nameLen && i < data.length; k++) {
let c = data[i++] & 0xff;
if (c > 0) fname.push(String.fromCharCode(c));
}
// 4 + nameLen + 4 + 6 + 16
if (i + 4 + 6 + 16 > data.length) {
break;
}
let flen = ((data[i++] & 0xff) << 24) | ((data[i++] & 0xff) << 16) | ((data[i++] & 0xff) << 8) | (data[i++] & 0xff);
i += 6;
let sign = [];
for (let k = 0; k < 16; k++, i++) {
let h = (data[i] & 0xff).toString(16);
sign.push(h.length == 1 ? '0' + h : h);
}
let ftime = fname.join('');
let duration = 0;
if (ftime.match(/^\d{14}REC\d+\.wav$/gi)) {
ftime = ftime.replace(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})REC.*$/gi, '$1-$2-$3 $4:$5:$6');
ftime = new Date(ftime);
duration = flen / 32;
} else if (ftime.match(/^(\d{2})?(\d{2})(\w{3})(\d{2})-(\d{2})(\d{2})(\d{2})-.*\.hda$/gi)) {
// 2024Mar19-110932-Rec00.hda
ftime = ftime.replace(/^(\d{2})?(\d{2})(\w{3})(\d{2})-(\d{2})(\d{2})(\d{2})-.*\.hda$/gi, '20$2 $3 $4 $5:$6:$7');
ftime = new Date(ftime);
duration = (flen / 32) * 4;
} else {
ftime = null;
}
if (ver == 1) {
duration = duration * 2;
}
let createDate = '';
let createTime = '';
if (ftime) {
createDate = ftime.getFullYear() + '/' + fnpad(ftime.getMonth() + 1) + '/' + fnpad(ftime.getDate());
createTime = fnpad(ftime.getHours()) + ':' + fnpad(ftime.getMinutes()) + ':' + fnpad(ftime.getSeconds());
}
files.push({
name: fname.join(''),
createDate: createDate,
createTime: createTime,
time: ftime,
duration: duration,
length: flen,
signature: sign.join(''),
});
}
// if (fcount == -1 && (fc))
// 如果没有判断数量的依据
if (fcount == -1) {
// return [];
}
if ((fc && files.length >= fc.count) || (fcount > -1 && files.length >= fcount)) {
// delete jensen[tag];
jensen[tag] = null;
return files.filter((f) => {
return Boolean(f.time);
});
}
});
return this.send(new Command(QUERY_FILE_LIST));
}; };
Jensen.prototype.deleteFile = async function (filename, seconds) { Jensen.prototype.deleteFile = async function (filename, seconds) {
let fname = []; let fname = [];
for (let i = 0; i < filename.length; i++) fname.push(filename.charCodeAt(i)); for (let i = 0; i < filename.length; i++) fname.push(filename.charCodeAt(i));
return this.send(new Command(DELETE_FILE).body(fname), seconds); return this.send(new Command(DELETE_FILE).body(fname), seconds);
}; };
Jensen.prototype.setTime = async function (time, seconds) { Jensen.prototype.setTime = async function (time, seconds) {
let str = let str =
time.getFullYear() + time.getFullYear() +
'-0' + '-0' +
(time.getMonth() + 1) + (time.getMonth() + 1) +
'-0' + '-0' +
time.getDate() + time.getDate() +
'-0' + '-0' +
time.getHours() + time.getHours() +
'-0' + '-0' +
time.getMinutes() + time.getMinutes() +
'-0' + '-0' +
time.getSeconds(); time.getSeconds();
str = str.replace(/(\d{4})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})/gi, '$1$2$3$4$5$6'); str = str.replace(/(\d{4})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})/gi, '$1$2$3$4$5$6');
return this.send(new Command(SET_DEVICE_TIME).body(this.to_bcd(str)), seconds); return this.send(new Command(SET_DEVICE_TIME).body(this.to_bcd(str)), seconds);
}; };
Jensen.prototype.getFile = async function (filename, length, ondata, onprogress) { Jensen.prototype.getFile = async function (filename, length, ondata, onprogress) {
if (typeof length != 'number') throw new Error('parameter `length` required'); if (typeof length != 'number') throw new Error('parameter `length` required');
if (length <= 0) throw new Error('parameter `length` must greater than zero'); if (length <= 0) throw new Error('parameter `length` must greater than zero');
Logger.info('jensen', 'getFile', `file download start. filename: ${filename}, length: ${length} `); Logger.info('jensen', 'getFile', `file download start. filename: ${filename}, length: ${length} `);
let fname = []; let fname = [];
for (let i = 0; i < filename.length; i++) fname.push(filename.charCodeAt(i)); for (let i = 0; i < filename.length; i++) fname.push(filename.charCodeAt(i));
let wakeLock = null; let wakeLock = null;
function visibilitychange() { function visibilitychange() {
let audio = document.getElementById('test_audio'); let audio = document.getElementById('test_audio');
if (!audio) { if (!audio) {
audio = document.createElement('audio'); audio = document.createElement('audio');
audio.id = 'test_audio' audio.id = 'test_audio';
audio.src = 'https://audionotes.hidock.com/test/test.aac'; audio.src = 'https://audionotes.hidock.com/test/test.aac';
audio.loop = true; audio.loop = true;
audio.controls = false; audio.controls = false;
audio.autoplay = true; audio.autoplay = true;
audio.muted = true; audio.muted = true;
document.body.appendChild(audio); document.body.appendChild(audio);
}
if (document.visibilityState === 'hidden') {
console.log('Page is hidden');
audio.play();
} else {
audio.pause();
console.log('Page is visible');
wakeLock && wakeLock.release();
}
} }
if (document.visibilityState === 'hidden') {
if ('wakeLock' in navigator) { console.log('Page is hidden');
try { audio.play();
wakeLock = await navigator.wakeLock.request('screen'); } else {
console.log('Wake lock activated'); audio.pause();
} catch (err) { console.log('Page is visible');
console.error('Failed to acquire wake lock:', err); wakeLock && wakeLock.release();
}
} }
}
function clearEventAndTask() {
const audio = document.getElementById('test_audio'); if ('wakeLock' in navigator) {
audio && audio.remove(); try {
wakeLock && wakeLock.release(); wakeLock = await navigator.wakeLock.request('screen');
console.log('Clear event and task.'); console.log('Wake lock activated');
} catch (err) {
console.error('Failed to acquire wake lock:', err);
} }
}
visibilitychange();
function clearEventAndTask() {
document.addEventListener('visibilitychange', visibilitychange); const audio = document.getElementById('test_audio');
audio && audio.remove();
let flen = 0; wakeLock && wakeLock.release();
let handler = (msg) => { console.log('Clear event and task.');
if (msg != null) { }
flen += msg.body.length || msg.body.byteLength;
ondata(msg.body); visibilitychange();
Logger.info('jensen', 'getFile length', `${length} ${flen}`)
if (flen >= length) { document.addEventListener('visibilitychange', visibilitychange);
document.removeEventListener('visibilitychange', visibilitychange);
Logger.info('jensen', 'getFile', 'file download finish.') let flen = 0;
clearEventAndTask(); let handler = (msg) => {
// return OK indicates all file blocks received if (msg != null) {
return 'OK'; flen += msg.body.length || msg.body.byteLength;
} ondata(msg.body);
} else { Logger.info('jensen', 'getFile length', `${length} ${flen}`);
document.removeEventListener('visibilitychange', visibilitychange); if (flen >= length) {
clearEventAndTask(); document.removeEventListener('visibilitychange', visibilitychange);
Logger.info('jensen', 'getFile', 'file download fail.') Logger.info('jensen', 'getFile', 'file download finish.');
ondata('fail'); clearEventAndTask();
} // return OK indicates all file blocks received
}; return 'OK';
this.onreceive = onprogress; }
Jensen.registerHandler(TRANSFER_FILE, handler); } else {
this.send(new Command(TRANSFER_FILE).body(fname)); document.removeEventListener('visibilitychange', visibilitychange);
clearEventAndTask();
Logger.info('jensen', 'getFile', 'file download fail.');
ondata('fail');
}
};
this.onreceive = onprogress;
Jensen.registerHandler(TRANSFER_FILE, handler);
this.send(new Command(TRANSFER_FILE).body(fname));
}; };
Jensen.prototype.requestFirmwareUpgrade = async function (versionNumber, fileSize, seconds) { Jensen.prototype.requestFirmwareUpgrade = async function (versionNumber, fileSize, seconds) {
let data = []; let data = [];
data[0] = (versionNumber >> 24) & 0xff; data[0] = (versionNumber >> 24) & 0xff;
data[1] = (versionNumber >> 16) & 0xff; data[1] = (versionNumber >> 16) & 0xff;
data[2] = (versionNumber >> 8) & 0xff; data[2] = (versionNumber >> 8) & 0xff;
data[3] = (versionNumber >> 0) & 0xff; data[3] = (versionNumber >> 0) & 0xff;
data[4] = (fileSize >> 24) & 0xff; data[4] = (fileSize >> 24) & 0xff;
data[5] = (fileSize >> 16) & 0xff; data[5] = (fileSize >> 16) & 0xff;
data[6] = (fileSize >> 8) & 0xff; data[6] = (fileSize >> 8) & 0xff;
data[7] = (fileSize >> 0) & 0xff; data[7] = (fileSize >> 0) & 0xff;
return this.send(new Command(REQUEST_FIRMWARE_UPGRADE).body(data), seconds); return this.send(new Command(REQUEST_FIRMWARE_UPGRADE).body(data), seconds);
}; };
Jensen.prototype.uploadFirmware = async function (data, seconds, onprogress) { Jensen.prototype.uploadFirmware = async function (data, seconds, onprogress) {
return this.send(new Command(FIRMWARE_UPLOAD).body(data), seconds, onprogress); return this.send(new Command(FIRMWARE_UPLOAD).body(data), seconds, onprogress);
}; };
Jensen.prototype.beginBNC = async function (seconds) { Jensen.prototype.beginBNC = async function (seconds) {
return this.send(new Command(BNC_DEMO_TEST).body([0x01]), seconds); return this.send(new Command(BNC_DEMO_TEST).body([0x01]), seconds);
}; };
Jensen.prototype.endBNC = async function (seconds) { Jensen.prototype.endBNC = async function (seconds) {
return this.send(new Command(BNC_DEMO_TEST).body([0x00]), seconds); return this.send(new Command(BNC_DEMO_TEST).body([0x00]), seconds);
}; };
Jensen.prototype.getSettings = async function (seconds) { Jensen.prototype.getSettings = async function (seconds) {
if (this.versionNumber < 327714) { if (this.versionNumber < 327714) {
return { autoRecord: false, autoPlay: false }; return { autoRecord: false, autoPlay: false };
} }
return this.send(new Command(GET_SETTINGS), seconds); return this.send(new Command(GET_SETTINGS), seconds);
}; };
Jensen.prototype.setAutoRecord = function (enable, seconds) { Jensen.prototype.setAutoRecord = function (enable, seconds) {
if (this.versionNumber < 327714) return { result: false }; if (this.versionNumber < 327714) return { result: false };
return this.send(new Command(SET_SETTINGS).body([0, 0, 0, enable ? 1 : 2]), seconds); return this.send(new Command(SET_SETTINGS).body([0, 0, 0, enable ? 1 : 2]), seconds);
}; };
Jensen.prototype.setAutoPlay = function (enable, seconds) { Jensen.prototype.setAutoPlay = function (enable, seconds) {
if (this.versionNumber < 327714) return { result: false }; if (this.versionNumber < 327714) return { result: false };
return this.send(new Command(SET_SETTINGS).body([0, 0, 0, 0, 0, 0, 0, enable ? 1 : 2,]), seconds); return this.send(new Command(SET_SETTINGS).body([0, 0, 0, 0, 0, 0, 0, enable ? 1 : 2]), seconds);
}; };
Jensen.prototype.setNotification = function (enable, seconds) { Jensen.prototype.setNotification = function (enable, seconds) {
if (this.versionNumber < 327714) return { result: false }; if (this.versionNumber < 327714) return { result: false };
return this.send(new Command(SET_SETTINGS).body([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, enable ? 1 : 2]), seconds); return this.send(new Command(SET_SETTINGS).body([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, enable ? 1 : 2]), seconds);
}; };
Jensen.prototype.setBluetoothPromptPlay = function (enable, seconds) { Jensen.prototype.setBluetoothPromptPlay = function (enable, seconds) {
// h1e 6.1.4 393476 // h1e 6.1.4 393476
// h1 5.1.4 327940 // h1 5.1.4 327940
if (this.model === 'hidock-h1e' && this.versionNumber < 393476) return { result: false } if (this.model === 'hidock-h1e' && this.versionNumber < 393476) return { result: false };
if (this.model === 'hidock-h1' && this.versionNumber < 327940) return { result: false } if (this.model === 'hidock-h1' && this.versionNumber < 327940) return { result: false };
return this.send(new Command(SET_SETTINGS).body([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, enable ? 2 : 1]), seconds); return this.send(new Command(SET_SETTINGS).body([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, enable ? 2 : 1]), seconds);
} };
Jensen.prototype.getCardInfo = function (seconds) { Jensen.prototype.getCardInfo = function (seconds) {
if (this.versionNumber < 327733) return null; if (this.versionNumber < 327733) return null;
return this.send(new Command(READ_CARD_INFO), seconds); return this.send(new Command(READ_CARD_INFO), seconds);
}; };
Jensen.prototype.formatCard = function (seconds) { Jensen.prototype.formatCard = function (seconds) {
if (this.versionNumber < 327733) return null; if (this.versionNumber < 327733) return null;
return this.send(new Command(FORMAT_CARD).body([0x01, 0x02, 0x03, 0x04]), seconds); return this.send(new Command(FORMAT_CARD).body([0x01, 0x02, 0x03, 0x04]), seconds);
}; };
Jensen.prototype.getRecordingFile = function (seconds) { Jensen.prototype.getRecordingFile = function (seconds) {
if (this.versionNumber < 327733) return null; if (this.versionNumber < 327733) return null;
return this.send(new Command(GET_RECORDING_FILE), seconds); return this.send(new Command(GET_RECORDING_FILE), seconds);
}; };
Jensen.prototype.recordTestStart = async function (type, seconds) { Jensen.prototype.recordTestStart = async function (type, seconds) {
return this.send(new Command(0xf008).body([type]), seconds); return this.send(new Command(0xf008).body([type]), seconds);
}; };
Jensen.prototype.recordTestEnd = async function (type, seconds) { Jensen.prototype.recordTestEnd = async function (type, seconds) {
return this.send(new Command(0xf009).body([type]), seconds); return this.send(new Command(0xf009).body([type]), seconds);
}; };
Jensen.prototype.test = async function (seconds) { Jensen.prototype.test = async function (seconds) {
return this.send(new Command(DEVICE_MSG_TEST), seconds); return this.send(new Command(DEVICE_MSG_TEST), seconds);
}; };
Jensen.prototype.getFileBlock = async function (filename, length, ondata) { Jensen.prototype.getFileBlock = async function (filename, length, ondata) {
if (typeof (length) != 'number') throw new Error('parameter `length` required'); if (typeof length != 'number') throw new Error('parameter `length` required');
if (length <= 0) throw new Error('parameter `length` must greater than zero'); if (length <= 0) throw new Error('parameter `length` must greater than zero');
if (this.fileLength > 0) return null; if (this.fileLength > 0) return null;
let data = []; let data = [];
data.push((length >> 24) & 0xff); data.push((length >> 24) & 0xff);
data.push((length >> 16) & 0xff); data.push((length >> 16) & 0xff);
data.push((length >> 8) & 0xff); data.push((length >> 8) & 0xff);
data.push((length >> 0) & 0xff); data.push((length >> 0) & 0xff);
for (let i = 0; i < filename.length; i++) data.push(filename.charCodeAt(i)); for (let i = 0; i < filename.length; i++) data.push(filename.charCodeAt(i));
this.onFileRecvHandler = ondata; this.onFileRecvHandler = ondata;
this.fileLength = length; this.fileLength = length;
this.fileReadBytes = 0; this.fileReadBytes = 0;
return this.send(new Command(GET_FILE_BLOCK).body(data)); return this.send(new Command(GET_FILE_BLOCK).body(data));
} };
Jensen.prototype.writeSerialNumber = async function (sn) { Jensen.prototype.writeSerialNumber = async function (sn) {
let data = []; let data = [];
for (let i = 0; i < sn.length; i++) { for (let i = 0; i < sn.length; i++) {
data.push(sn.charCodeAt(i)); data.push(sn.charCodeAt(i));
} }
return this.send(new Command(TEST_SN_WRITE).body(data)); return this.send(new Command(TEST_SN_WRITE).body(data));
} };
const commonMessageParser = (msg) => { const commonMessageParser = (msg) => {
return { result: msg.body[0] === 0x00 ? 'success' : 'failed' }; return { result: msg.body[0] === 0x00 ? 'success' : 'failed' };
}; };
Jensen.prototype.sendScheduleInfo = function (info) { Jensen.prototype.sendScheduleInfo = function (info) {
const startDate = this.to_bcd(getTimeStr(info.startDate)); let codes = new Array(33).fill(0);
const endDate = this.to_bcd(getTimeStr(info.endDate)); if (shortcutKeys[info.platform] && shortcutKeys[info.platform][info.os]) {
const keyAnswer = [3, 7, 4, 0, 0, 0, 0, 0]; codes = shortcutKeys[info.platform][info.os];
const keyHangup = [3, 7, 5, 0, 0, 0, 0, 0]; }
const keyReject = [3, 7, 6, 0, 0, 0, 0, 0]; let startDate = new Array(7).fill(0);
const keyMicMute = [3, 7, 7, 0, 0, 0, 0, 0]; let endDate = new Array(7).fill(0);
const body = [...startDate, ...endDate, 0, 0, ...keyAnswer, ...keyHangup, ...keyReject, ...keyMicMute]; if (info.startDate && info.endDate) {
console.log('send schedule info', info, body); startDate = this.to_bcd(getTimeStr(info.startDate));
return this.send(new Command(SCHEDULE_INFO).body(body)); endDate = this.to_bcd(getTimeStr(info.endDate));
}; }
const reserved = 0x00; // 预留
const body = [...startDate, ...endDate, 0x00, ...codes];
console.log('send schedule info', info, body);
return this.send(new Command(SCHEDULE_INFO).body(body));
};
Jensen.registerHandler(SET_DEVICE_TIME, commonMessageParser); Jensen.registerHandler(SET_DEVICE_TIME, commonMessageParser);
Jensen.registerHandler(BNC_DEMO_TEST, commonMessageParser); Jensen.registerHandler(BNC_DEMO_TEST, commonMessageParser);
Jensen.registerHandler(DELETE_FILE, (msg) => { Jensen.registerHandler(DELETE_FILE, (msg) => {
let rst = 'failed'; let rst = 'failed';
if (msg.body[0] === 0x00) rst = 'success'; if (msg.body[0] === 0x00) rst = 'success';
else if (msg.body[0] === 0x01) rst = 'not-exists'; else if (msg.body[0] === 0x01) rst = 'not-exists';
else if (msg.body[0] === 0x02) rst = 'failed'; else if (msg.body[0] === 0x02) rst = 'failed';
return { result: rst }; return { result: rst };
}); });
Jensen.registerHandler(QUERY_DEVICE_INFO, (msg, jensen) => { Jensen.registerHandler(QUERY_DEVICE_INFO, (msg, jensen) => {
let vc = [], let vc = [],
vn = 0; vn = 0;
let sn = []; let sn = [];
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
let b = msg.body[i] & 0xff; let b = msg.body[i] & 0xff;
if (i > 0) { if (i > 0) {
vc.push(String(b)); vc.push(String(b));
}
vn = vn | (b << ((4 - i - 1) * 8));
}
for (let i = 0; i < 16; i++) {
let chr = msg.body[i + 4];
if (chr > 0) sn.push(String.fromCharCode(chr));
} }
jensen.versionCode = vc.join('.'); vn = vn | (b << ((4 - i - 1) * 8));
jensen.versionNumber = vn; }
sn = sn.join(''); for (let i = 0; i < 16; i++) {
jensen.serialNumber = sn; let chr = msg.body[i + 4];
return { if (chr > 0) sn.push(String.fromCharCode(chr));
versionCode: vc.join('.'), }
versionNumber: vn, jensen.versionCode = vc.join('.');
sn: sn, jensen.versionNumber = vn;
}; sn = sn.join('');
jensen.serialNumber = sn;
return {
versionCode: vc.join('.'),
versionNumber: vn,
sn: sn
};
}); });
Jensen.registerHandler(QUERY_DEVICE_TIME, (msg, jensen) => { Jensen.registerHandler(QUERY_DEVICE_TIME, (msg, jensen) => {
let time = jensen.from_bcd( let time = jensen.from_bcd(
msg.body[0] & 0xff, msg.body[0] & 0xff,
msg.body[1] & 0xff, msg.body[1] & 0xff,
msg.body[2] & 0xff, msg.body[2] & 0xff,
msg.body[3] & 0xff, msg.body[3] & 0xff,
msg.body[4] & 0xff, msg.body[4] & 0xff,
msg.body[5] & 0xff, msg.body[5] & 0xff,
msg.body[6] & 0xff, msg.body[6] & 0xff
); );
return { return {
time: time === '00000000000000' ? 'unknown' : time.replace(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/gi, '$1-$2-$3 $4:$5:$6'), time: time === '00000000000000' ? 'unknown' : time.replace(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/gi, '$1-$2-$3 $4:$5:$6')
}; };
}); });
Jensen.registerHandler(QUERY_FILE_COUNT, (msg) => { Jensen.registerHandler(QUERY_FILE_COUNT, (msg) => {
if (msg.body.length === 0) return { count: 0 }; if (msg.body.length === 0) return { count: 0 };
let c = 0; let c = 0;
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
let b = msg.body[i] & 0xff; let b = msg.body[i] & 0xff;
c = c | (b << ((4 - i - 1) * 8)); c = c | (b << ((4 - i - 1) * 8));
} }
return { count: c }; return { count: c };
}); });
Jensen.registerHandler(GET_SETTINGS, (msg) => { Jensen.registerHandler(GET_SETTINGS, (msg) => {
let r1 = msg.body[3]; let r1 = msg.body[3];
let r2 = msg.body[7]; let r2 = msg.body[7];
let r4 = msg.body[15]; let r4 = msg.body[15];
let rst = { autoRecord: r1 === 1, autoPlay: r2 === 1, bluetoothTone: !(r4 === 1) }; let rst = { autoRecord: r1 === 1, autoPlay: r2 === 1, bluetoothTone: !(r4 === 1) };
if (msg.body.length >= 12) { if (msg.body.length >= 12) {
let r3 = msg.body[11] === 1; let r3 = msg.body[11] === 1;
rst['notification'] = r3; rst['notification'] = r3;
} }
return rst; return rst;
}); });
Jensen.registerHandler(SET_SETTINGS, commonMessageParser); Jensen.registerHandler(SET_SETTINGS, commonMessageParser);
Jensen.registerHandler(FACTORY_RESET, commonMessageParser); Jensen.registerHandler(FACTORY_RESET, commonMessageParser);
Jensen.registerHandler(RESTORE_FACTORY_SETTINGS, commonMessageParser); Jensen.registerHandler(RESTORE_FACTORY_SETTINGS, commonMessageParser);
Jensen.registerHandler(REQUEST_FIRMWARE_UPGRADE, (msg) => { Jensen.registerHandler(REQUEST_FIRMWARE_UPGRADE, (msg) => {
let rst = ''; let rst = '';
let c = msg.body[0]; let c = msg.body[0];
if (c === 0x00) rst = 'accepted'; if (c === 0x00) rst = 'accepted';
if (c === 0x01) rst = 'wrong-version'; if (c === 0x01) rst = 'wrong-version';
if (c === 0x02) rst = 'busy'; if (c === 0x02) rst = 'busy';
if (c === 0x03) return 'unknown'; if (c === 0x03) return 'unknown';
return { result: rst }; return { result: rst };
}); });
Jensen.registerHandler(FIRMWARE_UPLOAD, (msg) => { Jensen.registerHandler(FIRMWARE_UPLOAD, (msg) => {
let c = msg.body[0]; let c = msg.body[0];
return { result: c === 0x00 ? 'success' : 'failed' }; return { result: c === 0x00 ? 'success' : 'failed' };
}); });
Jensen.registerHandler(READ_CARD_INFO, (msg) => { Jensen.registerHandler(READ_CARD_INFO, (msg) => {
let i = 0; let i = 0;
let used = ((msg.body[i++] & 0xff) << 24) | ((msg.body[i++] & 0xff) << 16) | ((msg.body[i++] & 0xff) << 8) | (msg.body[i++] & 0xff); let used = ((msg.body[i++] & 0xff) << 24) | ((msg.body[i++] & 0xff) << 16) | ((msg.body[i++] & 0xff) << 8) | (msg.body[i++] & 0xff);
let capacity = ((msg.body[i++] & 0xff) << 24) | ((msg.body[i++] & 0xff) << 16) | ((msg.body[i++] & 0xff) << 8) | (msg.body[i++] & 0xff); let capacity = ((msg.body[i++] & 0xff) << 24) | ((msg.body[i++] & 0xff) << 16) | ((msg.body[i++] & 0xff) << 8) | (msg.body[i++] & 0xff);
let status = ((msg.body[i++] & 0xff) << 24) | ((msg.body[i++] & 0xff) << 16) | ((msg.body[i++] & 0xff) << 8) | (msg.body[i++] & 0xff); let status = ((msg.body[i++] & 0xff) << 24) | ((msg.body[i++] & 0xff) << 16) | ((msg.body[i++] & 0xff) << 8) | (msg.body[i++] & 0xff);
return { used: used, capacity: capacity, status: status.toString(16) }; return { used: used, capacity: capacity, status: status.toString(16) };
}); });
Jensen.registerHandler(FORMAT_CARD, commonMessageParser); Jensen.registerHandler(FORMAT_CARD, commonMessageParser);
Jensen.registerHandler(GET_RECORDING_FILE, (msg) => { Jensen.registerHandler(GET_RECORDING_FILE, (msg) => {
if (msg.body == null || msg.body.length === 0) return { recording: null }; if (msg.body == null || msg.body.length === 0) return { recording: null };
else { else {
let fname = []; let fname = [];
for (var i = 0; i < msg.body.length; i++) { for (var i = 0; i < msg.body.length; i++) {
fname.push(String.fromCharCode(msg.body[i])); fname.push(String.fromCharCode(msg.body[i]));
}
let fnpad = function (v) {
return v > 9 ? v : '0' + v;
};
let ftime = fname.join('');
if (ftime.match(/^\d{14}REC\d+\.wav$/gi)) {
ftime = ftime.replace(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})REC.*$/gi, '$1-$2-$3 $4:$5:$6');
ftime = new Date(ftime);
} else if (ftime.match(/^(\d{2})?(\d{2})(\w{3})(\d{2})-(\d{2})(\d{2})(\d{2})-.*\.hda$/gi)) {
// 2024Mar19-110932-Rec00.hda
ftime = ftime.replace(/^(\d{2})?(\d{2})(\w{3})(\d{2})-(\d{2})(\d{2})(\d{2})-.*\.hda$/gi, '20$2 $3 $4 $5:$6:$7');
ftime = new Date(ftime);
} else {
ftime = null;
}
let createDate = '';
let createTime = '';
if (ftime) {
createDate = ftime.getFullYear() + '/' + fnpad(ftime.getMonth() + 1) + '/' + fnpad(ftime.getDate());
createTime = fnpad(ftime.getHours()) + ':' + fnpad(ftime.getMinutes()) + ':' + fnpad(ftime.getSeconds());
}
return {
recording: fname.join(''),
name: fname.join(''),
createDate: createDate,
createTime: createTime,
time: ftime,
duration: 0,
length: 0,
signature: '0'.repeat(32),
};
} }
let fnpad = function (v) {
return v > 9 ? v : '0' + v;
};
let ftime = fname.join('');
if (ftime.match(/^\d{14}REC\d+\.wav$/gi)) {
ftime = ftime.replace(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})REC.*$/gi, '$1-$2-$3 $4:$5:$6');
ftime = new Date(ftime);
} else if (ftime.match(/^(\d{2})?(\d{2})(\w{3})(\d{2})-(\d{2})(\d{2})(\d{2})-.*\.hda$/gi)) {
// 2024Mar19-110932-Rec00.hda
ftime = ftime.replace(/^(\d{2})?(\d{2})(\w{3})(\d{2})-(\d{2})(\d{2})(\d{2})-.*\.hda$/gi, '20$2 $3 $4 $5:$6:$7');
ftime = new Date(ftime);
} else {
ftime = null;
}
let createDate = '';
let createTime = '';
if (ftime) {
createDate = ftime.getFullYear() + '/' + fnpad(ftime.getMonth() + 1) + '/' + fnpad(ftime.getDate());
createTime = fnpad(ftime.getHours()) + ':' + fnpad(ftime.getMinutes()) + ':' + fnpad(ftime.getSeconds());
}
return {
recording: fname.join(''),
name: fname.join(''),
createDate: createDate,
createTime: createTime,
time: ftime,
duration: 0,
length: 0,
signature: '0'.repeat(32)
};
}
}); });
Jensen.registerHandler(RECORD_TEST_START, commonMessageParser); Jensen.registerHandler(RECORD_TEST_START, commonMessageParser);
Jensen.registerHandler(RECORD_TEST_END, commonMessageParser); Jensen.registerHandler(RECORD_TEST_END, commonMessageParser);
Jensen.registerHandler(DEVICE_MSG_TEST, commonMessageParser); Jensen.registerHandler(DEVICE_MSG_TEST, commonMessageParser);
Jensen.registerHandler(GET_FILE_BLOCK, commonMessageParser); Jensen.registerHandler(GET_FILE_BLOCK, commonMessageParser);
Jensen.registerHandler(TEST_SN_WRITE, commonMessageParser) Jensen.registerHandler(TEST_SN_WRITE, commonMessageParser);
Jensen.registerHandler(SCHEDULE_INFO, commonMessageParser) Jensen.registerHandler(SCHEDULE_INFO, commonMessageParser);
export { Jensen }; export { Jensen };
const Level = { const Level = {
debug: "debug", debug: 'debug',
info: "info", info: 'info',
error: "error", error: 'error'
}; };
export const Logger = { export const Logger = {
...@@ -22,7 +22,7 @@ export const Logger = { ...@@ -22,7 +22,7 @@ export const Logger = {
module, module,
procedure, procedure,
message: String(message), message: String(message),
time: new Date().getTime(), time: new Date().getTime()
}; };
this.messages.push(log); this.messages.push(log);
if (this.consoleOutput) { if (this.consoleOutput) {
...@@ -33,22 +33,20 @@ export const Logger = { ...@@ -33,22 +33,20 @@ export const Logger = {
_print(log) { _print(log) {
let time = new Date(log.time); let time = new Date(log.time);
console.info( console.info(
"[" + '[' +
(log.level === "error" ? "x" : "*") + (log.level === 'error' ? 'x' : '*') +
"][" + '][' +
time.toLocaleString() + time.toLocaleString() +
"](" + '](' +
log.module + log.module +
" - " + ' - ' +
log.procedure + log.procedure +
") " + ') ' +
log.message, log.message
); );
}, },
filter(module, procedure) { filter(module, procedure) {
return this.messages.filter( return this.messages.filter((i) => i.module === module && i.procedure === procedure);
(i) => i.module === module && i.procedure === procedure,
);
}, },
enableConsoleOutput() { enableConsoleOutput() {
this.consoleOutput = true; this.consoleOutput = true;
...@@ -71,25 +69,250 @@ export const Logger = { ...@@ -71,25 +69,250 @@ export const Logger = {
} }
return true; return true;
}); });
}, }
}; };
export const getTimeStr = (date: Date) => { export const getTimeStr = (date: Date) => {
let str = let str =
date.getFullYear() + date.getFullYear() +
"-0" + '-0' +
(date.getMonth() + 1) + (date.getMonth() + 1) +
"-0" + '-0' +
date.getDate() + date.getDate() +
"-0" + '-0' +
date.getHours() + date.getHours() +
"-0" + '-0' +
date.getMinutes() + date.getMinutes() +
"-0" + '-0' +
date.getSeconds(); date.getSeconds();
str = str.replace( str = str.replace(/(\d{4})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})/gi, '$1$2$3$4$5$6');
/(\d{4})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})\-0*(\d{2})/gi,
"$1$2$3$4$5$6",
);
return str; return str;
}; };
const keyMap: { [key: string]: number } = {
A: 0x04,
B: 0x05,
C: 0x06,
D: 0x07,
E: 0x08,
F: 0x09,
G: 0x0a,
H: 0x0b,
I: 0x0c,
J: 0x0d,
K: 0x0e,
L: 0x0f,
M: 0x10,
N: 0x11,
O: 0x12,
P: 0x13,
Q: 0x14,
R: 0x15,
S: 0x16,
T: 0x17,
U: 0x18,
V: 0x19,
W: 0x1a,
X: 0x1b,
Y: 0x1c,
Z: 0x1b,
ENTER: 0x28,
ESCAPE: 0x29,
SPACE: 0x2c
};
const HotKeyBuilder = {
control: false,
shift: false,
alt: false,
guiKey: false,
keys: [],
withControl: () => {
HotKeyBuilder.control = true;
return HotKeyBuilder;
},
withShift: () => {
HotKeyBuilder.shift = true;
return HotKeyBuilder;
},
withAlt: () => {
HotKeyBuilder.alt = true;
return HotKeyBuilder;
},
withGuiKey: () => {
HotKeyBuilder.guiKey = true;
return HotKeyBuilder;
},
withKey: (key: string) => {
if (HotKeyBuilder.keys.length >= 2) throw new Error('exceed max key bindings');
HotKeyBuilder.keys.push(HotKeyBuilder.__mapping(key));
return HotKeyBuilder;
},
__mapping: (key: string) => {
return keyMap[key];
},
build: () => {
let key1 = 0;
if (HotKeyBuilder.control) key1 |= 0x01 << 0;
if (HotKeyBuilder.shift) key1 |= 0x01 << 1;
if (HotKeyBuilder.alt) key1 |= 0x01 << 2;
if (HotKeyBuilder.guiKey) key1 |= 0x01 << 3;
let codes = [
0x03,
key1,
HotKeyBuilder.keys.length ? HotKeyBuilder.keys[0] : 0x00,
HotKeyBuilder.keys.length > 1 ? HotKeyBuilder.keys[1] : 0x00,
0x00,
0x00,
0x00,
0x00
];
HotKeyBuilder.control = false;
HotKeyBuilder.shift = false;
HotKeyBuilder.alt = false;
HotKeyBuilder.guiKey = false;
HotKeyBuilder.keys = [];
return codes;
}
};
const enterKeyCode = (answer: 0 | 1 = 0, hangup: 0 | 1 = 0, reject: 0 | 1 = 0, micMute: 0 | 1 = 0) => {
let code = 0;
if (answer) code |= 0x01 << 0;
if (hangup) code |= 0x01 << 1;
if (reject) code |= 0x01 << 2;
if (micMute) code |= 0x01 << 3;
return code;
};
const emptyCodes = [0, 0, 0, 0, 0, 0, 0, 0];
export const shortcutKeys = {
Zoom: {
Windows: [
enterKeyCode(0, 1),
...emptyCodes,
HotKeyBuilder.withAlt().withKey('Q').build(),
...emptyCodes,
HotKeyBuilder.withAlt().withKey('A').build()
],
Mac: [
enterKeyCode(0, 1),
...emptyCodes,
HotKeyBuilder.withGuiKey().withKey('W').build(),
...emptyCodes,
HotKeyBuilder.withGuiKey().withShift().withKey('A').build()
],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
Teams: {
Windows: [
enterKeyCode(),
HotKeyBuilder.withControl().withShift().withKey('A').build(),
HotKeyBuilder.withControl().withShift().withKey('H').build(),
HotKeyBuilder.withControl().withShift().withKey('D').build(),
HotKeyBuilder.withControl().withShift().withKey('M').build()
],
Mac: [
enterKeyCode(),
HotKeyBuilder.withGuiKey().withShift().withKey('A').build(),
HotKeyBuilder.withGuiKey().withShift().withKey('H').build(),
HotKeyBuilder.withGuiKey().withShift().withKey('D').build(),
HotKeyBuilder.withGuiKey().withShift().withKey('M').build()
],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
'Google Meetings': {
Windows: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, HotKeyBuilder.withControl().withKey('D').build()],
Mac: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, HotKeyBuilder.withGuiKey().withKey('D').build()],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
Webex: {
Windows: [
enterKeyCode(),
HotKeyBuilder.withControl().withShift().withKey('C').build(),
HotKeyBuilder.withControl().withKey('L').build(),
HotKeyBuilder.withControl().withKey('D').build(),
HotKeyBuilder.withControl().withKey('M').build()
],
Mac: [
enterKeyCode(),
HotKeyBuilder.withControl().withShift().withKey('C').build(),
HotKeyBuilder.withGuiKey().withKey('L').build(),
HotKeyBuilder.withGuiKey().withShift().withKey('D').build(),
HotKeyBuilder.withGuiKey().withShift().withKey('M').build()
],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
Feishu: {
Windows: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, HotKeyBuilder.withControl().withShift().withKey('D').build()],
Mac: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, HotKeyBuilder.withGuiKey().withShift().withKey('D').build()],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
Lark: {
Windows: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, HotKeyBuilder.withControl().withShift().withKey('D').build()],
Mac: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, HotKeyBuilder.withGuiKey().withShift().withKey('D').build()],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
WeChat: {
Windows: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes],
Mac: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
Line: {
Windows: [
enterKeyCode(0, 1, 1),
...emptyCodes,
HotKeyBuilder.withKey('ESCAPE').build(),
HotKeyBuilder.withKey('ESCAPE').build(),
HotKeyBuilder.withControl().withShift().withKey('A').build()
],
Mac: [
enterKeyCode(0, 1, 1),
...emptyCodes,
HotKeyBuilder.withKey('ESCAPE').build(),
HotKeyBuilder.withKey('ESCAPE').build(),
HotKeyBuilder.withGuiKey().withShift().withKey('A').build()
],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
WhatsApp: {
Windows: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes],
Mac: [
enterKeyCode(),
...emptyCodes,
HotKeyBuilder.withGuiKey().withKey('W').build(),
HotKeyBuilder.withGuiKey().withKey('W').build(),
HotKeyBuilder.withGuiKey().withShift().withKey('M').build()
],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
Slack: {
Windows: [
enterKeyCode(),
...emptyCodes,
...emptyCodes,
...emptyCodes,
HotKeyBuilder.withControl().withShift().withKey('SPACE').build()
],
Mac: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, HotKeyBuilder.withGuiKey().withShift().withKey('SPACE').build()],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
},
Discord: {
Windows: [
enterKeyCode(),
HotKeyBuilder.withControl().withKey('ENTER').build(),
...emptyCodes,
HotKeyBuilder.withKey('ESCAPE').build(),
HotKeyBuilder.withControl().withShift().withKey('M').build()
],
Mac: [
enterKeyCode(),
HotKeyBuilder.withGuiKey().withKey('ENTER').build(),
...emptyCodes,
HotKeyBuilder.withGuiKey().withKey('ESCAPE').build(),
HotKeyBuilder.withGuiKey().withShift().withKey('M').build()
],
Linux: [enterKeyCode(), ...emptyCodes, ...emptyCodes, ...emptyCodes, ...emptyCodes]
}
};
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