Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
J
jensen
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Skye Yu
jensen
Commits
7a1aaa43
Commit
7a1aaa43
authored
Sep 29, 2025
by
martin hou
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 整理测试页面UI,增加自动连接管理
parent
5ebd2b7a
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
794 additions
and
75 deletions
+794
-75
index.css
src/index.css
+41
-4
index.tsx
src/index.tsx
+99
-71
mgr.ts
src/utils/mgr.ts
+654
-0
No files found.
src/index.css
View file @
7a1aaa43
...
...
@@ -11,31 +11,68 @@ button {
outline
:
none
;
border
:
1px
solid
#ccc
;
}
#root
{
width
:
100%
;
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
}
button
:hover
{
cursor
:
pointer
;
border
:
solid
1px
#999
;
}
.
result
-container
{
.
btn
-container
{
display
:
flex
;
flex-direction
:
row
;
gap
:
16px
;
padding
:
16px
;
align-items
:
center
;
flex-wrap
:
wrap
;
/* 移除高度限制,保持自适应 */
}
/* 让#root成为flex容器,纵向排列,撑满body */
#root
{
width
:
100%
;
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
}
/* result-container占据剩余空间 */
.result-container
{
display
:
flex
;
flex-direction
:
row
;
flex
:
1
1
0%
;
/* 占据剩余空间 */
width
:
100%
;
min-height
:
0
;
/* 防止溢出 */
padding
:
20px
;
box-sizing
:
border-box
;
}
.list-container
,
.log-container
{
height
:
100%
;
}
.list-container
{
width
:
50%
;
height
:
100%
;
overflow
:
scroll
;
}
.log-container
{
width
:
50%
;
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
}
.log-container
textarea
{
width
:
100%
;
height
:
100%
;
min-height
:
800px
;
min-height
:
0
;
border
:
solid
1px
#ccc
;
border-radius
:
5px
;
outline
:
none
;
...
...
src/index.tsx
View file @
7a1aaa43
...
...
@@ -2,10 +2,13 @@ import { useEffect, useState } from 'react';
import
Jensen
,
{
BluetoothDevice
}
from
'..'
;
import
'./index.css'
;
import
{
Logger
}
from
'./Logger'
import
{
mgr
}
from
'./utils/mgr'
;
const
jensen
=
new
Jensen
(
Logger
);
const
firmwareVersions
=
[
{
model
:
'hidock-p1:mini'
,
version
:
'2.1.0'
,
url
:
'https://jensen.test.hidock.com/firmwares/mini-2.1.0.bin'
},
{
model
:
'hidock-h1'
,
version
:
'5.2.9'
,
url
:
'https://jensen.test.hidock.com/firmwares/hidock-h1-5.2.9.bin'
},
{
model
:
'hidock-h1e'
,
version
:
'6.2.9'
,
url
:
'https://jensen.test.hidock.com/firmwares/hidock-h1e-6.2.9.bin'
},
{
model
:
'hidock-p1'
,
version
:
'1.3.4'
,
url
:
'https://jensen.test.hidock.com/firmwares/hidock-p1-1.3.4.bin'
},
{
model
:
'hidock-p1'
,
version
:
'1.2.18'
,
url
:
'https://jensen.test.hidock.com/firmwares/eason-1.2.18.bin'
},
{
model
:
'hidock-p1'
,
version
:
'1.2.14'
,
url
:
'https://jensen.test.hidock.com/firmwares/eason-v1.2.14.bin'
}
];
...
...
@@ -17,8 +20,30 @@ export function Home() {
const
[
greeting
,
setGreeting
]
=
useState
<
string
|
null
>
(
null
);
const
[
firmwares
,
setFirmwares
]
=
useState
<
typeof
firmwareVersions
|
null
>
(
null
);
const
[
logs
,
setLogs
]
=
useState
<
string
[]
>
([]);
const
[
sn
,
setSn
]
=
useState
<
string
|
null
>
(
null
);
mgr
.
onconnectionstatechanged
((
state
,
dinfo
)
=>
{
console
.
log
(
'onconnectionstatechanged'
,
state
,
dinfo
);
// alert(JSON.stringify(dinfo));
});
mgr
.
onautoconnect
((
dinfo
)
=>
{
// console.log('onautoconnect', dinfo);
setSn
(
dinfo
.
sn
);
// alert(JSON.stringify(dinfo));
});
const
getJensen
=
()
=>
{
if
(
sn
==
null
)
return
alert
(
'Please connect to a device first'
),
null
;
return
mgr
.
getInstance
(
sn
);
}
useEffect
(()
=>
{
// console.log(mgr);
mgr
.
tryconnect
();
},
[]);
useEffect
(()
=>
{
/*
jensen.connect();
jensen.onconnect = () => {
console.log('connect successfully');
...
...
@@ -32,6 +57,7 @@ export function Home() {
console.log('getDeviceInfo error', e);
});
};
*/
// jensen.onerror = (e) => {
// console.log('onerror', e);
// alert('此设备已经在其它已打开的HiNotes网页上建立连接');
...
...
@@ -54,19 +80,20 @@ export function Home() {
};
},
[]);
const
getFilePart
=
()
=>
{
const
file
=
files
[
4
];
const
secondsLength
=
Math
.
ceil
(
file
.
length
/
file
.
duration
)
*
1000
;
console
.
time
(
'!!!time'
);
jensen
.
getFilePart
(
file
.
name
,
secondsLength
*
600
,
(
res
)
=>
{
if
(
res
instanceof
Uint8Array
)
{
console
.
timeEnd
(
'!!!time'
);
}
});
};
const
info
=
async
()
=>
{
// alert(sn);
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
// console.log('jensen -> ', jensen);
let
info
=
await
jensen
?.
getDeviceInfo
();
// alert(JSON.stringify(info));
Logger
.
info
(
'jensen'
,
'info'
,
'Device Info: '
+
JSON
.
stringify
(
info
));
}
let
bluetoothDevices
:
BluetoothDevice
[]
=
[];
const
bluetoothScan
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
setDevices
([]);
setGreeting
(
'scan started at: '
+
new
Date
().
toLocaleString
());
let
devices
=
await
jensen
.
scanDevices
();
...
...
@@ -74,32 +101,25 @@ export function Home() {
setDevices
(
devices
);
setGreeting
(
null
)
}
const
connecty
=
async
()
=>
{
const
usb
=
(
navigator
as
any
).
usb
;
let
conn
=
await
usb
.
requestDevice
({
filters
:
[{
vendorId
:
0x10d6
},
{
vendorId
:
0x3887
}]
});
if
(
conn
==
null
)
return
null
;
// if (conn.opened) throw new Error('device_already_opened');
await
conn
.
open
();
let
jensen
=
new
Jensen
(
Logger
,
conn
);
jensen
.
onerror
=
(
e
)
=>
{
console
.
log
(
'onerror'
,
e
);
alert
(
'This device has already been connected to another HiNotes webpage'
);
};
await
jensen
.
initialize
();
let
dinfo
=
await
jensen
.
getDeviceInfo
();
if
(
dinfo
)
console
.
log
(
'dinfo'
,
dinfo
);
return
null
;
}
const
connect
=
async
()
=>
{
await
jensen
.
connect
();
alert
(
jensen
.
getModel
()
+
' connected'
)
// await jensen.connect();
// alert(jensen.getModel() + ' connected')
let
info
=
await
mgr
.
connect
();
if
(
info
&&
info
.
sn
)
{
setSn
(
info
.
sn
);
Logger
.
info
(
'jensen'
,
'connect'
,
info
.
sn
+
' connected'
);
}
else
{
alert
(
'connect failed'
);
}
}
const
disconnectBTDevice
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
r
=
await
jensen
.
disconnectBTDevice
();
console
.
log
(
r
);
}
...
...
@@ -127,24 +147,23 @@ export function Home() {
}
const
doConnectBluetooth
=
async
(
mac
:
string
)
=>
{
// alert(mac);
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
rst
=
await
jensen
.
connectBTDevice
(
mac
,
10
);
alert
(
'connect: '
+
rst
.
result
);
}
const
getTime
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
time
=
await
jensen
.
getTime
();
// alert(JSON.stringify(time));
Logger
.
info
(
'jensen'
,
'getTime'
,
'Time: '
+
JSON
.
stringify
(
time
));
}
const
info
=
async
()
=>
{
let
info
=
await
jensen
.
getDeviceInfo
();
// alert(JSON.stringify(info));
Logger
.
info
(
'jensen'
,
'info'
,
'Device Info: '
+
JSON
.
stringify
(
info
));
}
const
listFiles
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
fc
=
await
jensen
.
getFileCount
();
// alert(fc?.count);
let
files
=
await
jensen
.
listFiles
();
...
...
@@ -153,11 +172,14 @@ export function Home() {
}
const
getBluetoothStatus
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
info
=
await
jensen
.
getBluetoothStatus
();
// alert(JSON.stringify(info));
Logger
.
info
(
'jensen'
,
'getBluetoothStatus'
,
'Bluetooth: '
+
JSON
.
stringify
(
info
));
}
/*
const readFilePartial = async() => {
if (files.length == 0) return alert('You don\'t have any recording files, or you haven\'t queried the file list');
let s0 = prompt('Please Input the File Index (Start from 0):', '0');
...
...
@@ -176,8 +198,11 @@ export function Home() {
let data = await jensen.readFile(file.name, offset, length);
console.log(data);
}
*/
const
updateDeviceTone
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
resp
=
await
jensen
.
requestToneUpdate
(
'826d9bac0b535e7babe02b389327a9f2'
,
1050688
);
if
(
resp
.
code
!=
0x00
)
return
alert
(
resp
.
result
);
...
...
@@ -197,7 +222,8 @@ export function Home() {
}
const
updateUAC
=
async
()
=>
{
// 92e66fd8cfd36f09c83fc61491899307 1024
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
resp
=
await
jensen
.
requestUACUpdate
(
'92e66fd8cfd36f09c83fc61491899307'
,
1024
);
if
(
resp
.
code
!=
0x00
)
return
alert
(
resp
.
result
);
...
...
@@ -217,6 +243,8 @@ export function Home() {
}
const
batteryStatus
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
status
=
await
jensen
.
getBatteryStatus
(
5
);
// alert(JSON.stringify(status));
Logger
.
info
(
'jensen'
,
'batteryStatus'
,
'Battery: '
+
JSON
.
stringify
(
status
));
...
...
@@ -239,6 +267,8 @@ export function Home() {
}
const
get_file
=
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
recvBytes
=
0
;
let
stime
=
new
Date
().
getTime
();
jensen
.
getFile
(
filename
,
filelength
,
function
(
data
)
{
...
...
@@ -254,64 +284,48 @@ export function Home() {
});
}
const
writeSN
=
async
()
=>
{
let
nsn
=
prompt
(
'Please Input the New SN:'
,
''
);
if
(
nsn
&&
nsn
.
match
(
/^HD
(
H1|1E|P1|PM
)\w{9}
$/gi
))
{
let
rst
=
await
jensen
.
writeSerialNumber
(
nsn
);
alert
(
rst
.
result
);
}
}
const
connectx
=
async
()
=>
{
const
usb
=
(
navigator
as
any
).
usb
;
usb
.
onconnect
=
(
evt
:
any
)
=>
{
console
.
log
(
evt
);
}
usb
.
getDevices
().
then
(
async
(
devices
:
any
[])
=>
{
for
(
let
i
=
0
;
i
<
devices
.
length
;
i
++
)
{
let
dev
=
devices
[
i
];
await
dev
.
open
();
let
jensen
=
new
Jensen
(
Logger
,
dev
);
await
jensen
.
initialize
();
let
rst
=
await
jensen
.
getDeviceInfo
();
console
.
log
(
rst
);
}
});
}
/*
const test = async () => {
await jensen.reconnect();
let rst = await jensen.getDeviceInfo();
if (rst) alert(rst.sn + ' reconnected...');
else alert('Something went wrong...');
}
*/
const
setWebUSBTimeout
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
ts
=
prompt
(
'Please Input the Timeout (ms):'
,
'10000'
);
if
(
ts
===
undefined
||
ts
===
null
)
return
;
let
timeout
=
parseInt
(
ts
);
if
(
isNaN
(
timeout
)
||
timeout
<=
0
)
return
alert
(
'Please Input the Correct Timeout'
);
await
jensen
.
setWebUSBTimeout
(
timeout
,
30
);
alert
(
'Set Timeout Success'
);
Logger
.
info
(
'jensen'
,
'setWebUSBTimeout'
,
'Set Timeout Success'
);
}
const
getWebUSBTimeout
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
rst
=
await
jensen
.
getWebUSBTimeout
(
5
);
alert
(
JSON
.
stringify
(
rst
));
// alert(JSON.stringify(rst));
Logger
.
info
(
'jensen'
,
'getWebUSBTimeout'
,
'WebUSB Timeout: '
+
JSON
.
stringify
(
rst
));
}
const
listFirmwares
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
let
model
=
jensen
.
getModel
();
let
versions
=
firmwareVersions
.
filter
((
item
)
=>
item
.
model
==
model
);
setFirmwares
(
versions
);
}
const
updateFirmware
=
async
(
version
:
string
,
url
:
string
)
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
// let rst = await jensen.requestFirmwareUpgrade(version, url);
// alert(JSON.stringify(rst));
alert
(
'ready to update firmware, do not close/refresh this page, and wait for the update to complete'
)
;
if
(
!
confirm
(
'Ready to update firmware, DO NOT close/refresh this page, and wait for the update to complete.
\
n
\
n Confirm to continue'
))
return
;
// 版本号类似于1.2.3,需要转换为0x010203
// 将类似于"1.2.3"的字符串转换为0x010203的数值
let
versionNumber
=
0
;
...
...
@@ -362,9 +376,21 @@ export function Home() {
xhr
.
send
();
}
const
turnPopupOn
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
// await jensen.();
}
const
turnPopupOff
=
async
()
=>
{
let
jensen
=
getJensen
();
if
(
jensen
==
null
)
return
;
// await jensen.turnPopupOff();
}
return
(
<>
<
div
style=
{
{
display
:
'flex'
,
flexDirection
:
'row'
,
gap
:
'16px'
,
padding
:
'16px'
,
alignItems
:
'center'
,
flexWrap
:
'wrap'
}
}
>
<
div
className=
"btn-container"
style=
{
{
display
:
'flex'
,
flexDirection
:
'row'
,
gap
:
'16px'
,
padding
:
'16px'
,
alignItems
:
'center'
,
flexWrap
:
'wrap'
}
}
>
<
button
onClick=
{
connect
}
>
Connect
</
button
>
<
button
onClick=
{
info
}
>
Info
</
button
>
<
button
onClick=
{
getTime
}
>
Get Time
</
button
>
...
...
@@ -379,6 +405,8 @@ export function Home() {
<
button
onClick=
{
setWebUSBTimeout
}
>
Set Timeout
</
button
>
<
button
onClick=
{
getWebUSBTimeout
}
>
Get Timeout
</
button
>
<
button
onClick=
{
listFirmwares
}
>
Firmewares
</
button
>
<
button
onClick=
{
turnPopupOn
}
>
Turn Popup On
</
button
>
<
button
onClick=
{
turnPopupOff
}
>
Turn Popup Off
</
button
>
</
div
>
<
div
className=
"result-container"
>
<
div
className=
"list-container"
>
...
...
src/utils/mgr.ts
0 → 100644
View file @
7a1aaa43
import
Jensen
,
{
DeviceInfo
,
ScheduleInfo
}
from
"../../"
;
import
{
Logger
}
from
"../Logger"
;
// 指令参数
export
type
CommandOptions
=
{
exclusive
?:
boolean
;
// 是否排它性的,在排它性的事务执行期间,其它所有的新事务都不再等待而是直接返回错误
expires
?:
number
;
// 超时时长
retry
?:
number
;
// 重试次数
wait
?:
number
;
// 等待前一个事务完结的最大时长
reconnect
?:
boolean
;
// 是否重连来确保事务的成功
params
?:
any
[];
// 原方法的参数
}
// 指令代理函数
export
type
CommandProxy
=
(
jensen
:
Jensen
|
null
)
=>
Promise
<
any
>
|
undefined
;
// 指令请求
export
type
CommandRequest
=
{
id
:
string
;
command
:
CommandProxy
;
options
:
CommandOptions
;
createTime
:
number
;
}
export
type
CommandPromise
=
{
id
:
string
;
resolve
:
(
value
:
any
)
=>
void
;
reject
:
(
error
:
any
)
=>
void
;
timeout
:
number
;
}
// 定时任务
export
type
ScheduledTask
=
{
interval
:
number
;
command
:
CommandProxy
;
onComplete
?:
(
data
:
any
)
=>
void
;
}
// 定时任务信息,增加了执行结果的记录
type
ScheduledTaskInfo
=
{
interval
:
number
;
lastExecuteTime
:
number
;
executedCount
:
number
;
command
:
CommandProxy
;
onComplete
?:
(
data
:
any
)
=>
void
;
}
type
AutoConnectEventHandler
=
(
dinfo
:
DeviceInfo
)
=>
void
;
type
ConnectionEventHandler
=
(
state
:
ConnectionStatus
,
dinfo
:
DeviceInfo
|
string
|
null
)
=>
void
;
// 连接状态枚举
export
type
ConnectionStatus
=
'connect-timeout'
|
'init'
|
'connected'
|
'reconnected'
|
'disconnected'
;
// exec('set-time', { exclusive: false, expires: 5, })
// setTime(false, true, new Date(), 5)
//
// 连接管理
export
class
ConnectionManager
{
private
logger
:
typeof
Logger
;
private
connections
:
Map
<
string
,
Jensen
>
;
private
onautoconnectEventListener
:
AutoConnectEventHandler
|
null
=
null
;
private
onconnectionstatechangedListener
:
ConnectionEventHandler
|
null
=
null
;
constructor
(
_logger
:
typeof
Logger
)
{
this
.
logger
=
_logger
;
this
.
connections
=
new
Map
<
string
,
Jensen
>
();
this
.
registerUSBEventListener
();
}
async
registerUSBEventListener
()
{
const
usb
=
(
navigator
as
any
).
usb
;
const
self
=
this
;
usb
.
onconnect
=
(
evt
:
any
)
=>
{
try
{
let
dev
=
evt
.
device
;
if
(
dev
.
vendorId
!=
0x10d6
&&
dev
.
vendorId
!=
0x3887
)
return
;
self
.
_connect
(
dev
);
}
catch
(
ex
)
{
this
.
logger
.
error
(
'jensen'
,
'onconnect'
,
String
(
ex
));
}
}
usb
.
ondisconnect
=
(
evt
:
any
)
=>
{
try
{
let
dev
=
evt
.
device
;
if
(
dev
.
vendorId
!=
0x10d6
&&
dev
.
vendorId
!=
0x3887
)
return
;
self
.
_disconnect
(
dev
);
}
catch
(
ex
)
{
this
.
logger
.
error
(
'jensen'
,
'ondisconnect'
,
String
(
ex
));
}
}
}
// 尝试连接,遍历所有的WebUSB设备,如果是HiDock设备,则直接建立连接,并且还要
async
tryconnect
()
{
const
usb
=
(
navigator
as
any
).
usb
;
const
self
=
this
;
usb
.
getDevices
().
then
(
async
(
devices
:
any
[])
=>
{
for
(
let
i
=
0
;
i
<
devices
.
length
;
i
++
)
{
let
dev
=
devices
[
i
];
if
(
dev
.
opend
)
continue
;
if
(
dev
.
vendorId
!=
0x10d6
&&
dev
.
vendorId
!=
0x3887
)
continue
;
// console.log('auto connect', dev);
self
.
logger
.
info
(
'jensen'
,
'tryconnect'
,
'VID: '
+
dev
.
vendorId
+
', PID: '
+
dev
.
productId
);
// 使用dev创建新的Jensen实例
try
{
self
.
_connect
(
dev
);
}
catch
(
ex
)
{
self
.
logger
.
error
(
'jensen'
,
'tryconnect'
,
String
(
ex
));
}
}
});
}
// 连接事件
private
async
_onstatechanged
(
state
:
ConnectionStatus
,
jensen
:
Jensen
)
{
}
// 当设备断开连接时触发,如果设备是之前已经连接过的,那还需要触发ondisconnect事件
private
async
_disconnect
(
dev
:
any
)
{
let
self
=
this
;
this
.
connections
.
forEach
((
j
,
k
)
=>
{
let
ud
=
j
.
getUSBDevice
();
if
(
ud
==
dev
)
{
// console.log(k + ' disconnected...');
self
.
logger
.
info
(
'jensen'
,
'ondisconnect'
,
k
+
' disconnected'
);
self
.
onconnectionstatechangedListener
?.(
'disconnected'
,
k
);
}
});
}
// 当设备连接时触发,如果设备是之前已经连接过的,那还需要触发onconnect事件
private
async
_connect
(
dev
:
any
)
{
await
dev
.
open
();
let
inst
=
new
Jensen
(
Logger
,
dev
);
this
.
logger
.
info
(
'jensen'
,
'auto-connect'
,
'initialize'
);
await
inst
.
initialize
();
this
.
onconnectionstatechangedListener
?.(
'init'
,
inst
.
getModel
());
let
dinfo
:
DeviceInfo
|
null
=
null
;
for
(
let
i
=
0
;
i
<
30
;
i
++
)
{
try
{
dinfo
=
await
inst
.
getDeviceInfo
(
5
);
if
(
dinfo
==
null
)
{
inst
.
reconnect
();
await
sleep
(
100
);
continue
;
}
break
;
}
catch
(
e
)
{
// ...
}
}
if
(
dinfo
==
null
)
{
console
.
log
(
'dinfo is null'
,
dev
);
}
if
(
dinfo
==
null
&&
dev
.
opened
)
{
this
.
onconnectionstatechangedListener
?.(
'connect-timeout'
,
inst
.
getModel
());
try
{
dev
.
close
();
dev
.
forget
();
}
catch
(
error
)
{
// do nothing
}
return
;
}
this
.
logger
.
info
(
'jensen'
,
'auto-connect'
,
JSON
.
stringify
(
dinfo
));
if
(
dinfo
)
{
// 如果是之前已连接的设备,那就需要触发onconnect事件
if
(
this
.
connections
.
has
(
dinfo
.
sn
))
{
this
.
connections
.
set
(
dinfo
.
sn
,
// new CommandManager(dinfo, inst)
inst
);
this
.
logger
.
info
(
'jensen'
,
'onconnect'
,
dinfo
.
sn
+
' reconnected'
);
// let jensen = this.connections.get(dinfo.sn);
// jensen?.setUSBDevice(dev);
this
.
onconnectionstatechangedListener
?.(
'reconnected'
,
dinfo
);
return
;
}
this
.
connections
.
set
(
dinfo
.
sn
,
// new CommandManager(dinfo, inst)
inst
);
try
{
this
.
onautoconnectEventListener
?.(
dinfo
);
this
.
onconnectionstatechangedListener
?.(
'connected'
,
dinfo
);
}
catch
(
err
)
{
this
.
logger
.
error
(
'jensen'
,
'autoconnect'
,
String
(
err
));
}
}
}
onautoconnect
(
eventListener
:
AutoConnectEventHandler
)
{
this
.
onautoconnectEventListener
=
eventListener
;
}
// 这个方法用于管理 连接/断开连接 的回调
onconnectionstatechanged
(
eventHandler
:
ConnectionEventHandler
)
{
this
.
onconnectionstatechangedListener
=
eventHandler
;
}
// 获取一个Jensen实例
getInstance
(
tag
:
string
)
{
return
this
.
connections
.
get
(
tag
);
}
// 设置一个Jensen实例
setInstance
(
tag
:
string
,
jensen
:
Jensen
,
oldTag
:
string
|
null
=
null
)
{
if
(
oldTag
)
this
.
connections
.
delete
(
oldTag
);
this
.
connections
.
set
(
tag
,
jensen
);
}
// 主动连接新设备,如果没有连接成功或是未连接,则直接返回null,如果用户选择的是已连接的设备,则直接抛出异常
// 但是异常的表现形式需要另外商量沟通
async
connect
()
{
const
usb
=
(
navigator
as
any
).
usb
;
let
self
=
this
;
let
conn
=
await
usb
.
requestDevice
({
filters
:
[{
vendorId
:
0x10d6
},
{
vendorId
:
0x3887
}]
});
if
(
conn
==
null
)
return
null
;
if
(
conn
.
opened
)
return
Logger
.
error
(
'jensen'
,
'connect'
,
'device already opened'
);
await
conn
.
open
();
let
jensen
=
new
Jensen
(
self
.
logger
,
conn
);
jensen
.
onerror
=
(
err
)
=>
{
self
.
logger
.
error
(
'jensen'
,
'connect error'
,
String
(
err
));
// myMessage.error(i18n.t('connection.error'), 10000);
}
await
jensen
.
initialize
();
let
dinfo
=
await
jensen
.
getDeviceInfo
();
// @ts-ignore
jensen
.
onerror
=
null
;
if
(
dinfo
)
{
self
.
connections
.
set
(
dinfo
.
sn
,
// new CommandManager(dinfo, inst)
jensen
);
return
dinfo
}
return
null
;
}
// 关闭连接
async
close
(
sn
:
string
)
{
let
jensen
=
this
.
getInstance
(
sn
);
if
(
jensen
)
jensen
.
disconnect
();
this
.
connections
.
delete
(
sn
);
}
}
// 指令管理/代理
class
CommandManager
{
// 设备信息
private
serialNumber
:
string
|
null
=
null
;
// 命令队列
private
commands
:
CommandRequest
[]
=
[];
// 指令Promise响应
private
directives
:
Map
<
string
,
CommandPromise
>
=
new
Map
<
string
,
CommandPromise
>
();
// 定时任务
private
tasks
:
ScheduledTaskInfo
[]
=
[];
// 是否暂停定时任务
private
suspendTimer
:
boolean
=
false
;
// Jensen实例
private
jensen
:
Jensen
|
null
=
null
;
// 当前事务
private
currentTask
:
String
|
null
=
null
;
constructor
(
dinfo
:
DeviceInfo
,
jensen
:
Jensen
)
{
// TODO: 要不要完全保存deviceInfo?
this
.
serialNumber
=
dinfo
.
sn
;
this
.
jensen
=
jensen
;
// 开启定时器
let
self
=
this
;
window
.
setInterval
(
function
()
{
self
.
_executeTimer
();
},
1000
);
}
dump
()
{
// TODO: 完成内部成员的状态输出,用于跟踪连接和事务状态
console
.
log
(
'###############################################################'
);
console
.
log
(
'SerialNumber'
,
this
.
serialNumber
);
console
.
log
(
'Tasks'
,
this
.
tasks
);
console
.
log
(
'Commands'
,
this
.
commands
);
console
.
log
(
'SuspendTimer'
,
this
.
suspendTimer
);
console
.
log
(
'Jensen Instance'
,
this
.
jensen
);
}
setup
()
{
// 在连接初次建立时需要做的初始化工作,是不是放在这里还没有想好
}
// onconnect事件如何触发?
// 是不是应该全局唯一触发?好像至少多少时间上就应该重新触发一次
// 或者是每次触发,然后可以被别的地方接管走
// 另外,还有一个问题,如果断开了连接再次连接上来,会话都不同了,还是要按SN做标识,否则多连接管理会出问题
// 增加一个新指令消息
// 怎么样触发新指令呢?
async
getDeviceInfo
(
secs
?:
number
)
{
// 当前是否可以执行?
// 我是不是要分一下组?
// 我是不是要加一个模式转换的方法?
return
this
.
jensen
?.
getDeviceInfo
(
secs
);
// return this.request((jensen) => jensen?.getDeviceInfo(), { reconnect: false });
}
getSerialNumber
()
{
return
this
.
serialNumber
;
}
// 更改模式,要不要超时时长?在超过多长时间后要销毁或者说是恢复过去?
// 1. 当前是什么模式
// 2. 请求指令的是来自于哪个模式?
// 3. 整一个事务控制吧
// 事务开始或结束,不传参数就是事务结束了
// 对于每一个事务而言,应该要尽量确保它执行的成功的,包括重连尝试
async
begin
(
secs
:
number
=
5
)
{
// 如果应用端尝试变更事务切换不过去,那就应该一直等
// 这里是不是要弄一个可以打断的?如果事务与当前事务一致,又应该怎么办?还是要阻止吧?
// 结束事务
if
(
this
.
currentTask
)
{
throw
new
Error
(
"pending task: "
+
this
.
currentTask
);
}
let
tid
=
this
.
uuid
();
this
.
currentTask
=
tid
;
return
tid
;
}
end
(
id
:
string
)
{
if
(
this
.
currentTask
!=
id
)
throw
new
Error
(
'invalid task: '
+
this
.
currentTask
+
' --> '
+
id
);
this
.
currentTask
=
null
;
}
// 开始/停止定时任务
// 但是如果停止的话,也只是停止下一轮的执行,要不要等待全部停止完成再说?
startTimer
()
{
this
.
suspendTimer
=
false
;
}
stopTimer
()
{
this
.
suspendTimer
=
true
;
}
private
async
_executeTimer
()
{
if
(
this
.
suspendTimer
)
return
;
// 遍历所有的定时器任务
for
(
let
i
=
0
;
i
<
this
.
tasks
.
length
;
i
++
)
{
let
item
=
this
.
tasks
[
i
];
let
now
=
new
Date
().
getTime
()
/
1000
;
if
(
now
-
item
.
lastExecuteTime
<
item
.
interval
)
continue
;
try
{
let
rst
=
await
item
.
command
.
apply
(
null
,
[
this
.
jensen
]);
item
.
onComplete
?.
apply
(
null
,
[
rst
]);
/*
item.command.apply(null, [this.jensen])?.then((rst) => {
item.onComplete?.apply(null, [ rst ]);
});
*/
}
catch
(
ex
)
{
console
.
error
(
ex
);
}
finally
{
item
.
lastExecuteTime
=
now
;
}
}
}
// 注册定时任务,返回值为它在列表中的索引位置,可能后面有要解除定时执行的需要,先预留一下
// 这里有个问题,我要如何完成回调呢?
async
registerTimer
(
task
:
ScheduledTask
)
{
let
len
=
this
.
tasks
.
length
;
this
.
tasks
.
push
({
interval
:
task
.
interval
,
command
:
task
.
command
,
lastExecuteTime
:
0
,
executedCount
:
0
});
return
len
;
}
// 指令分两种,有复杂结构的返回值的或是普通boolean类型的两种
async
request
(
func
:
CommandProxy
,
opts
:
CommandOptions
)
{
// TODO: 需要在这里注册一个Promise
let
self
=
this
;
let
rid
=
this
.
uuid
();
let
future
=
new
Promise
((
resolve
,
reject
)
=>
{
self
.
directives
.
set
(
rid
,
{
id
:
rid
,
resolve
:
resolve
,
reject
:
reject
,
timeout
:
opts
.
expires
||
5
});
});
this
.
commands
.
push
({
command
:
func
,
id
:
rid
,
options
:
opts
,
createTime
:
new
Date
().
getTime
()
});
return
future
;
}
// 需要怎么样持续的执行呢?
async
execute
(
func
:
CommandProxy
,
opts
:
CommandOptions
)
{
let
tid
=
await
this
.
begin
(
opts
.
wait
);
try
{
let
args
:
any
[]
|
undefined
=
opts
.
params
;
let
execCount
=
0
;
let
maxTries
=
(
opts
.
retry
||
0
)
+
1
;
do
{
execCount
+=
1
;
try
{
let
rst
:
any
=
await
func
(
this
.
jensen
);
if
(
rst
)
return
rst
;
if
(
opts
.
reconnect
)
{
// TODO: 连接重新建立
this
.
logger
.
info
(
'jensen'
,
'execute'
,
'try reconnect'
);
}
}
catch
(
ex
)
{
console
.
error
(
ex
);
}
}
while
(
execCount
<
maxTries
);
}
finally
{
this
.
end
(
tid
);
}
}
private
async
tryReconnect
()
{
try
{
// TODO: 需要准备一个新的reconnect方法,使用同一个WebUSB实例来close/open来实现重连,否则必然会乱掉
this
.
jensen
?.
reconnect
();
}
catch
(
ex
)
{
console
.
error
(
ex
);
}
}
async
getTime
(
timeout
?:
number
)
{
return
this
.
jensen
?.
getTime
(
timeout
);
}
async
setTime
(
date
:
Date
,
timeout
?:
number
)
{
return
this
.
jensen
?.
setTime
(
date
,
timeout
);
}
async
getRecordingFile
()
{
return
this
.
jensen
?.
getRecordingFile
();
}
async
getFileCount
(
timeout
?:
number
)
{
return
this
.
jensen
?.
getFileCount
(
timeout
);
}
async
listFiles
(
timeout
?:
number
)
{
return
this
.
jensen
?.
listFiles
(
timeout
);
}
async
readFile
(
fname
:
string
,
offset
:
number
,
length
:
number
)
{
return
this
.
jensen
?.
readFile
(
fname
,
offset
,
length
);
}
async
getFile
(
fileName
:
string
,
length
:
number
,
on
?:
(
msg
:
Uint8Array
|
'fail'
)
=>
void
,
onprogress
?:
(
size
:
number
)
=>
void
)
{
return
this
.
jensen
?.
getFile
(
fileName
,
length
,
on
,
onprogress
);
}
async
getFilePart
(
fileName
:
string
,
length
:
number
,
on
?:
(
msg
:
Uint8Array
|
'fail'
)
=>
void
,
onprogress
?:
(
size
:
number
)
=>
void
)
{
return
this
.
jensen
?.
getFilePart
(
fileName
,
length
,
on
,
onprogress
);
}
async
getFileBlock
(
fileName
:
string
,
length
:
number
,
on
?:
(
msg
:
Uint8Array
|
'fail'
)
=>
void
)
{
return
this
.
jensen
?.
getFileBlock
(
fileName
,
length
,
on
);
}
async
requestFirmwareUpgrade
(
vn
:
number
,
length
:
number
,
timeout
?:
number
)
{
return
this
.
jensen
?.
requestFirmwareUpgrade
(
vn
,
length
,
timeout
);
}
async
uploadFirmware
(
data
:
number
[],
timeout
?:
number
,
onProgress
?:
(
cur
:
number
,
total
:
number
)
=>
void
)
{
return
this
.
jensen
?.
uploadFirmware
(
data
,
timeout
,
onProgress
);
}
async
beginBNC
(
timeout
?:
number
)
{
return
this
.
jensen
?.
beginBNC
(
timeout
);
}
async
endBNC
(
timeout
?:
number
)
{
return
this
.
jensen
?.
endBNC
(
timeout
);
}
async
deleteFile
(
fileName
:
string
)
{
return
this
.
jensen
?.
deleteFile
(
fileName
);
}
async
getSettings
(
timeout
?:
number
)
{
return
this
.
jensen
?.
getSettings
(
timeout
);
}
async
setAutoRecord
(
enable
:
boolean
,
timeout
?:
number
)
{
return
this
.
jensen
?.
setAutoRecord
(
enable
,
timeout
);
}
async
setAutoPlay
(
enable
:
boolean
,
timeout
?:
number
)
{
return
this
.
jensen
?.
setAutoPlay
(
enable
,
timeout
);
}
async
setNotification
(
enable
:
boolean
,
timeout
?:
number
)
{
return
this
.
jensen
?.
setNotification
(
enable
,
timeout
);
}
async
setBluetoothPromptPlay
(
enable
:
boolean
,
timeout
?:
number
)
{
return
this
.
jensen
?.
setBluetoothPromptPlay
(
enable
,
timeout
);
}
async
getCardInfo
(
timeout
?:
number
)
{
return
this
.
jensen
?.
getCardInfo
(
timeout
);
}
async
formatCard
(
timeout
?:
number
)
{
return
this
.
jensen
?.
formatCard
(
timeout
);
}
async
factoryReset
(
timeout
?:
number
)
{
return
this
.
jensen
?.
factoryReset
(
timeout
);
}
async
restoreFactorySettings
(
timeout
?:
number
)
{
return
this
.
jensen
?.
restoreFactorySettings
(
timeout
);
}
async
recordTestStart
(
type
:
number
,
timeout
?:
number
)
{
return
this
.
jensen
?.
recordTestStart
(
type
,
timeout
);
}
async
recordTestEnd
(
type
:
number
,
timeout
?:
number
)
{
return
this
.
jensen
?.
recordTestEnd
(
type
,
timeout
);
}
async
test
(
timeout
?:
number
)
{
return
this
.
jensen
?.
test
(
timeout
);
}
async
writeSerialNumber
(
sn
:
string
)
{
return
this
.
jensen
?.
writeSerialNumber
(
sn
);
}
async
getRealtimeSettings
()
{
return
this
.
jensen
?.
getRealtimeSettings
();
}
async
startRealtime
()
{
return
this
.
jensen
?.
startRealtime
();
}
async
pauseRealtime
()
{
return
this
.
jensen
?.
pauseRealtime
();
}
async
stopRealtime
()
{
return
this
.
jensen
?.
stopRealtime
();
}
async
getRealtime
(
frames
:
number
)
{
return
this
.
jensen
?.
getRealtime
(
frames
);
}
async
scanDevices
(
timeout
?:
number
)
{
return
this
.
jensen
?.
scanDevices
(
timeout
);
}
async
connectBTDevice
(
mac
:
string
,
timeout
?:
number
)
{
return
this
.
jensen
?.
connectBTDevice
(
mac
,
timeout
);
}
async
disconnectBTDevice
(
timeout
?:
number
)
{
return
this
.
jensen
?.
disconnectBTDevice
(
timeout
);
}
async
getBluetoothStatus
(
timeout
?:
number
)
{
return
this
.
jensen
?.
getBluetoothStatus
(
timeout
);
}
async
requestToneUpdate
(
signature
:
string
,
size
:
number
,
timeout
?:
number
)
{
return
this
.
jensen
?.
requestToneUpdate
(
signature
,
size
,
timeout
);
}
async
updateTone
(
data
:
Uint8Array
,
timeout
?:
number
)
{
return
this
.
jensen
?.
updateTone
(
data
,
timeout
);
}
async
requestUACUpdate
(
signature
:
string
,
size
:
number
,
timeout
?:
number
)
{
return
this
.
jensen
?.
requestUACUpdate
(
signature
,
size
,
timeout
);
}
async
updateUAC
(
data
:
Uint8Array
,
timeout
?:
number
)
{
return
this
.
jensen
?.
updateUAC
(
data
,
timeout
);
}
async
sendScheduleInfo
(
info
:
ScheduleInfo
[])
{
return
this
.
jensen
?.
sendScheduleInfo
(
info
);
}
// 生成唯一id,暂时用随机数生成,后面最好用顺序递增的整数比较好
private
uuid
()
{
return
String
(
Math
.
floor
(
1000
_0000_0000_0000
+
Math
.
random
()
*
1000
_0000_0000_0000
));
}
}
function
sleep
(
ms
:
number
)
{
return
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
ms
));
}
export
const
mgr
=
new
ConnectionManager
(
Logger
);
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment