@alexgyver/serial 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/serial.js +25 -20
- package/serial.min.js +1 -1
- package/test/script.js +9 -7
- package/test_dev/script.js +9 -7
package/package.json
CHANGED
package/serial.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { sleep, ShiftBuffer, StreamSplitter } from "@alexgyver/utils";
|
|
2
2
|
|
|
3
|
-
const States = {
|
|
4
|
-
Open: 1,
|
|
5
|
-
Closing: 2,
|
|
6
|
-
Closed: 3,
|
|
7
|
-
};
|
|
8
|
-
|
|
9
3
|
export default class SerialJS {
|
|
4
|
+
static State = {
|
|
5
|
+
Closed: 'closed',
|
|
6
|
+
Opening: 'opening',
|
|
7
|
+
Open: 'open',
|
|
8
|
+
Closing: 'closing',
|
|
9
|
+
};
|
|
10
|
+
|
|
10
11
|
//#region handlers
|
|
11
12
|
onbin = null;
|
|
12
13
|
ontext = null;
|
|
@@ -31,18 +32,18 @@ export default class SerialJS {
|
|
|
31
32
|
this.splitter.ontext = (t) => this.ontext(t);
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
//#region methods
|
|
34
36
|
config(params = {}) {
|
|
35
37
|
this.cfg = { ...this.cfg, ...params };
|
|
36
38
|
this.splitter.eol = this.cfg.eol;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
//#region methods
|
|
40
41
|
static supported() {
|
|
41
42
|
return 'serial' in navigator;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
opened() {
|
|
45
|
-
return this._state ==
|
|
46
|
+
return this._state == SerialJS.State.Open;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
selected() {
|
|
@@ -85,6 +86,7 @@ export default class SerialJS {
|
|
|
85
86
|
}
|
|
86
87
|
async _open() {
|
|
87
88
|
if (this.opened()) return;
|
|
89
|
+
this._change(SerialJS.State.Opening);
|
|
88
90
|
|
|
89
91
|
try {
|
|
90
92
|
await this._close();
|
|
@@ -93,10 +95,9 @@ export default class SerialJS {
|
|
|
93
95
|
this.writer = this._port.writable.getWriter();
|
|
94
96
|
this.reader = this._port.readable.getReader();
|
|
95
97
|
this._buffer.clear();
|
|
96
|
-
this.
|
|
97
|
-
this._change(true);
|
|
98
|
+
this._change(SerialJS.State.Open);
|
|
98
99
|
|
|
99
|
-
while (this._state ==
|
|
100
|
+
while (this._state == SerialJS.State.Open) {
|
|
100
101
|
const { value, done } = await this.reader.read();
|
|
101
102
|
if (done) break;
|
|
102
103
|
if (value) {
|
|
@@ -106,6 +107,7 @@ export default class SerialJS {
|
|
|
106
107
|
}
|
|
107
108
|
} catch (e) {
|
|
108
109
|
this._error(e);
|
|
110
|
+
this._change(SerialJS.State.Closed);
|
|
109
111
|
if (this.retry) setTimeout(() => this._open(), this.cfg.reconnect);
|
|
110
112
|
}
|
|
111
113
|
|
|
@@ -113,11 +115,10 @@ export default class SerialJS {
|
|
|
113
115
|
if (this.writer) this.writer.releaseLock();
|
|
114
116
|
this.reader = null;
|
|
115
117
|
this.writer = null;
|
|
116
|
-
this._state = States.Closed;
|
|
117
118
|
|
|
118
119
|
try {
|
|
119
120
|
await this._port.close();
|
|
120
|
-
this._change(
|
|
121
|
+
this._change(SerialJS.State.Closed);
|
|
121
122
|
} catch (e) { }
|
|
122
123
|
}
|
|
123
124
|
|
|
@@ -127,19 +128,19 @@ export default class SerialJS {
|
|
|
127
128
|
}
|
|
128
129
|
async _close() {
|
|
129
130
|
switch (this._state) {
|
|
130
|
-
case
|
|
131
|
-
case
|
|
131
|
+
case SerialJS.State.Closed: return;
|
|
132
|
+
case SerialJS.State.Open:
|
|
132
133
|
if (this.reader) await this.reader.cancel();
|
|
133
|
-
this.
|
|
134
|
+
this._change(SerialJS.State.Closing);
|
|
134
135
|
break;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
let i = 0;
|
|
138
|
-
while (this._state ==
|
|
139
|
+
while (this._state == SerialJS.State.Closing) {
|
|
139
140
|
await sleep(10);
|
|
140
141
|
if (++i > 200) {
|
|
141
142
|
this._error('Close timeout');
|
|
142
|
-
this.
|
|
143
|
+
this._change(SerialJS.State.Closed);
|
|
143
144
|
break;
|
|
144
145
|
}
|
|
145
146
|
}
|
|
@@ -157,7 +158,7 @@ export default class SerialJS {
|
|
|
157
158
|
|
|
158
159
|
//#region private
|
|
159
160
|
_port = null;
|
|
160
|
-
_state =
|
|
161
|
+
_state = SerialJS.State.Closed;
|
|
161
162
|
_buffer = new ShiftBuffer();
|
|
162
163
|
_decoder = new TextDecoder();
|
|
163
164
|
|
|
@@ -181,7 +182,11 @@ export default class SerialJS {
|
|
|
181
182
|
this.onerror('[SerialJS] ' + e);
|
|
182
183
|
}
|
|
183
184
|
_change(s) {
|
|
185
|
+
this._state = s;
|
|
184
186
|
this.onchange(s);
|
|
185
|
-
|
|
187
|
+
switch (s) {
|
|
188
|
+
case SerialJS.State.Open: this.onopen(); break;
|
|
189
|
+
case SerialJS.State.Closed: this.onclose(); break;
|
|
190
|
+
}
|
|
186
191
|
}
|
|
187
192
|
}
|
package/serial.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const t=t=>new Promise(e=>setTimeout(e,t));class e{ontext=null;constructor(t=/\r?\n/,e=!1){this.eol=t,this.skip=e}reset(){this._buf=""}write(t){if(!this.ontext)return;if(!this.eol)return void this.ontext(t);this._buf+=t;let e=this._buf.split(this.eol);1!=e.length&&(e[e.length-1].length?this._buf=e.pop():(this._buf="",e.pop()),this.skip&&(e.shift(),this.skip=!1),e.forEach(t=>this.ontext(t)))}_buf=""}class s{constructor(){this.chunks=[],this.length=0}clear(){this.chunks=[]}push(t){return this.chunks.push(t),this.length+=t.length,!0}shift(t){t>this.length&&(t=this.length);const e=new Uint8Array(t);let s=0;for(;t>0;){const i=this.chunks[0];t>=i.length?(e.set(i,s),s+=i.length,t-=i.length,this.chunks.shift(),this.length-=i.length):(e.set(i.subarray(0,t),s),this.chunks[0]=i.subarray(t),this.length-=t,t=0)}return e}shiftAll(){return this.shift(this.length)}}class i{onbin=null;ontext=null;onopen(){}onclose(){}onchange(t){}onselect(t){}onerror(t){}constructor(t={}){this.cfg={eol:/\r?\n/,baud:115200,reconnect:1e3,...t},this._setLastPort().then(()=>this.onselect(this.getName())),this.splitter=new e(this.cfg.eol),this.splitter.ontext=t=>this.ontext(t)}config(t={}){this.cfg={...this.cfg,...t},this.splitter.eol=this.cfg.eol}static supported(){return"serial"in navigator}opened(){return
|
|
1
|
+
const t=t=>new Promise(e=>setTimeout(e,t));class e{ontext=null;constructor(t=/\r?\n/,e=!1){this.eol=t,this.skip=e}reset(){this._buf=""}write(t){if(!this.ontext)return;if(!this.eol)return void this.ontext(t);this._buf+=t;let e=this._buf.split(this.eol);1!=e.length&&(e[e.length-1].length?this._buf=e.pop():(this._buf="",e.pop()),this.skip&&(e.shift(),this.skip=!1),e.forEach(t=>this.ontext(t)))}_buf=""}class s{constructor(){this.chunks=[],this.length=0}clear(){this.chunks=[]}push(t){return this.chunks.push(t),this.length+=t.length,!0}shift(t){t>this.length&&(t=this.length);const e=new Uint8Array(t);let s=0;for(;t>0;){const i=this.chunks[0];t>=i.length?(e.set(i,s),s+=i.length,t-=i.length,this.chunks.shift(),this.length-=i.length):(e.set(i.subarray(0,t),s),this.chunks[0]=i.subarray(t),this.length-=t,t=0)}return e}shiftAll(){return this.shift(this.length)}}class i{static State={Closed:"closed",Opening:"opening",Open:"open",Closing:"closing"};onbin=null;ontext=null;onopen(){}onclose(){}onchange(t){}onselect(t){}onerror(t){}constructor(t={}){this.cfg={eol:/\r?\n/,baud:115200,reconnect:1e3,...t},this._setLastPort().then(()=>this.onselect(this.getName())),this.splitter=new e(this.cfg.eol),this.splitter.ontext=t=>this.ontext(t)}config(t={}){this.cfg={...this.cfg,...t},this.splitter.eol=this.cfg.eol}static supported(){return"serial"in navigator}opened(){return this._state==i.State.Open}selected(){return!!this._port}getName(){if(!this._port)return null;switch(this._port.getInfo().usbProductId){case 21971:return"CH343";case 30084:return"CH340S";case 29986:case 29987:return"CH340";case 21778:case 21795:case 21892:return"CH341";case 1026:case 1027:case 1028:case 1029:case 24577:case 1538:case 24592:return"FT232";case 38144:case 258:case 1281:case 32937:case 6e4:case 60001:case 60003:return"CP210x"}return"Unknown"}async select(){try{await this.close(),this._port=null;let e=await navigator.serial.getPorts();for(let t of e)await t.forget();await t(50),await navigator.serial.requestPort(),await this._setLastPort()}catch(t){this._error(t)}return this.onselect(this.getName()),this.selected()}async open(){this.cfg.reconnect&&(this.retry=!0),await this._open()}async _open(){if(!this.opened()){this._change(i.State.Opening);try{for(await this._close(),await this._setLastPort(),await this._port.open({baudRate:this.cfg.baud}),this.writer=this._port.writable.getWriter(),this.reader=this._port.readable.getReader(),this._buffer.clear(),this._change(i.State.Open);this._state==i.State.Open;){const{value:t,done:e}=await this.reader.read();if(e)break;t&&(this.onbin&&this.onbin(t),this.ontext&&this.splitter.write(this._decoder.decode(t,{stream:!0})))}}catch(t){this._error(t),this._change(i.State.Closed),this.retry&&setTimeout(()=>this._open(),this.cfg.reconnect)}this.reader&&this.reader.releaseLock(),this.writer&&this.writer.releaseLock(),this.reader=null,this.writer=null;try{await this._port.close(),this._change(i.State.Closed)}catch(t){}}}async close(){this.retry=!1,await this._close()}async _close(){switch(this._state){case i.State.Closed:return;case i.State.Open:this.reader&&await this.reader.cancel(),this._change(i.State.Closing)}let e=0;for(;this._state==i.State.Closing;)if(await t(10),++e>200){this._error("Close timeout"),this._change(i.State.Closed);break}}async sendText(t){await this.sendBin((new TextEncoder).encode(t))}async sendBin(t){this._buffer.push(t),this._send()}_port=null;_state=i.State.Closed;_buffer=new s;_decoder=new TextDecoder;async _send(){if(!this._busy){for(this._busy=!0;this._buffer.length;){let t=this._buffer.shiftAll();try{this.writer&&await this.writer.write(t)}catch(t){}}this._busy=!1}}async _setLastPort(){let t=await navigator.serial.getPorts();this._port=t.length?t[0]:null}_error(t){this.onerror("[SerialJS] "+t)}_change(t){switch(this._state=t,this.onchange(t),t){case i.State.Open:this.onopen();break;case i.State.Closed:this.onclose()}}}export{i as default};
|
package/test/script.js
CHANGED
|
@@ -12,18 +12,20 @@ send_b.onclick = () => ser.sendText('Hello ' + i++);
|
|
|
12
12
|
// read
|
|
13
13
|
// ser.onbin = b => console.log(b);
|
|
14
14
|
ser.ontext = t => console.log(t);
|
|
15
|
-
// ser.online = t => console.log(t);
|
|
16
15
|
|
|
17
16
|
// state
|
|
18
|
-
ser.onopen = () => {
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
ser.onclose = () => {
|
|
22
|
-
|
|
17
|
+
// ser.onopen = () => {
|
|
18
|
+
// console.log('Opened', ser.getName());
|
|
19
|
+
// }
|
|
20
|
+
// ser.onclose = () => {
|
|
21
|
+
// console.log('Closed');
|
|
22
|
+
// }
|
|
23
|
+
ser.onchange = s => {
|
|
24
|
+
console.log(s);
|
|
23
25
|
}
|
|
24
26
|
ser.onerror = e => {
|
|
25
27
|
console.log(e);
|
|
26
28
|
}
|
|
27
29
|
ser.onselect = (port) => {
|
|
28
|
-
console.log('
|
|
30
|
+
console.log('select', port);
|
|
29
31
|
}
|
package/test_dev/script.js
CHANGED
|
@@ -12,18 +12,20 @@ send_b.onclick = () => ser.sendText('Hello ' + i++);
|
|
|
12
12
|
// read
|
|
13
13
|
// ser.onbin = b => console.log(b);
|
|
14
14
|
ser.ontext = t => console.log(t);
|
|
15
|
-
// ser.online = t => console.log(t);
|
|
16
15
|
|
|
17
16
|
// state
|
|
18
|
-
ser.onopen = () => {
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
ser.onclose = () => {
|
|
22
|
-
|
|
17
|
+
// ser.onopen = () => {
|
|
18
|
+
// console.log('Opened', ser.getName());
|
|
19
|
+
// }
|
|
20
|
+
// ser.onclose = () => {
|
|
21
|
+
// console.log('Closed');
|
|
22
|
+
// }
|
|
23
|
+
ser.onchange = s => {
|
|
24
|
+
console.log(s);
|
|
23
25
|
}
|
|
24
26
|
ser.onerror = e => {
|
|
25
27
|
console.log(e);
|
|
26
28
|
}
|
|
27
29
|
ser.onselect = (port) => {
|
|
28
|
-
console.log('
|
|
30
|
+
console.log('select', port);
|
|
29
31
|
}
|