@alexgyver/serial 1.0.2 → 1.0.4
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/README.md +1 -0
- package/package.json +2 -2
- package/serial.js +29 -22
- package/serial.min.js +1 -1
- package/test/script.js +9 -7
- package/test_dev/script.js +9 -7
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexgyver/serial",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Web Serial API wrapper",
|
|
5
5
|
"main": "./serial.js",
|
|
6
6
|
"module": "./serial.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"html-webpack-plugin": "^5.6.5"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@alexgyver/utils": "^1.2.
|
|
23
|
+
"@alexgyver/utils": "^1.2.4"
|
|
24
24
|
},
|
|
25
25
|
"author": "AlexGyver <alex@alexgyver.ru>",
|
|
26
26
|
"license": "MIT"
|
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;
|
|
@@ -22,6 +23,7 @@ export default class SerialJS {
|
|
|
22
23
|
const def = {
|
|
23
24
|
eol: /\r?\n/,
|
|
24
25
|
baud: 115200,
|
|
26
|
+
auto_open: false,
|
|
25
27
|
reconnect: 1000,
|
|
26
28
|
};
|
|
27
29
|
this.cfg = { ...def, ...params };
|
|
@@ -31,18 +33,18 @@ export default class SerialJS {
|
|
|
31
33
|
this.splitter.ontext = (t) => this.ontext(t);
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
//#region methods
|
|
34
37
|
config(params = {}) {
|
|
35
38
|
this.cfg = { ...this.cfg, ...params };
|
|
36
39
|
this.splitter.eol = this.cfg.eol;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
//#region methods
|
|
40
42
|
static supported() {
|
|
41
43
|
return 'serial' in navigator;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
opened() {
|
|
45
|
-
return this._state ==
|
|
47
|
+
return this._state == SerialJS.State.Open;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
selected() {
|
|
@@ -76,6 +78,7 @@ export default class SerialJS {
|
|
|
76
78
|
this._error(e);
|
|
77
79
|
}
|
|
78
80
|
this.onselect(this.getName());
|
|
81
|
+
if (this.cfg.auto_open) this.open();
|
|
79
82
|
return this.selected();
|
|
80
83
|
}
|
|
81
84
|
|
|
@@ -85,6 +88,7 @@ export default class SerialJS {
|
|
|
85
88
|
}
|
|
86
89
|
async _open() {
|
|
87
90
|
if (this.opened()) return;
|
|
91
|
+
this._change(SerialJS.State.Opening);
|
|
88
92
|
|
|
89
93
|
try {
|
|
90
94
|
await this._close();
|
|
@@ -93,10 +97,9 @@ export default class SerialJS {
|
|
|
93
97
|
this.writer = this._port.writable.getWriter();
|
|
94
98
|
this.reader = this._port.readable.getReader();
|
|
95
99
|
this._buffer.clear();
|
|
96
|
-
this.
|
|
97
|
-
this._change(true);
|
|
100
|
+
this._change(SerialJS.State.Open);
|
|
98
101
|
|
|
99
|
-
while (this._state ==
|
|
102
|
+
while (this._state == SerialJS.State.Open) {
|
|
100
103
|
const { value, done } = await this.reader.read();
|
|
101
104
|
if (done) break;
|
|
102
105
|
if (value) {
|
|
@@ -106,6 +109,7 @@ export default class SerialJS {
|
|
|
106
109
|
}
|
|
107
110
|
} catch (e) {
|
|
108
111
|
this._error(e);
|
|
112
|
+
this._change(SerialJS.State.Closed);
|
|
109
113
|
if (this.retry) setTimeout(() => this._open(), this.cfg.reconnect);
|
|
110
114
|
}
|
|
111
115
|
|
|
@@ -113,11 +117,10 @@ export default class SerialJS {
|
|
|
113
117
|
if (this.writer) this.writer.releaseLock();
|
|
114
118
|
this.reader = null;
|
|
115
119
|
this.writer = null;
|
|
116
|
-
this._state = States.Closed;
|
|
117
120
|
|
|
118
121
|
try {
|
|
119
122
|
await this._port.close();
|
|
120
|
-
this._change(
|
|
123
|
+
this._change(SerialJS.State.Closed);
|
|
121
124
|
} catch (e) { }
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -127,19 +130,19 @@ export default class SerialJS {
|
|
|
127
130
|
}
|
|
128
131
|
async _close() {
|
|
129
132
|
switch (this._state) {
|
|
130
|
-
case
|
|
131
|
-
case
|
|
133
|
+
case SerialJS.State.Closed: return;
|
|
134
|
+
case SerialJS.State.Open:
|
|
132
135
|
if (this.reader) await this.reader.cancel();
|
|
133
|
-
this.
|
|
136
|
+
this._change(SerialJS.State.Closing);
|
|
134
137
|
break;
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
let i = 0;
|
|
138
|
-
while (this._state ==
|
|
141
|
+
while (this._state == SerialJS.State.Closing) {
|
|
139
142
|
await sleep(10);
|
|
140
143
|
if (++i > 200) {
|
|
141
144
|
this._error('Close timeout');
|
|
142
|
-
this.
|
|
145
|
+
this._change(SerialJS.State.Closed);
|
|
143
146
|
break;
|
|
144
147
|
}
|
|
145
148
|
}
|
|
@@ -157,17 +160,17 @@ export default class SerialJS {
|
|
|
157
160
|
|
|
158
161
|
//#region private
|
|
159
162
|
_port = null;
|
|
160
|
-
_state =
|
|
163
|
+
_state = SerialJS.State.Closed;
|
|
161
164
|
_buffer = new ShiftBuffer();
|
|
162
165
|
_decoder = new TextDecoder();
|
|
163
166
|
|
|
164
167
|
async _send() {
|
|
165
168
|
if (this._busy) return;
|
|
166
169
|
this._busy = true;
|
|
167
|
-
while (this._buffer.length) {
|
|
170
|
+
while (this._buffer.length && this.writer) {
|
|
168
171
|
let d = this._buffer.shiftAll();
|
|
169
172
|
try {
|
|
170
|
-
|
|
173
|
+
await this.writer.write(d);
|
|
171
174
|
} catch (e) { }
|
|
172
175
|
}
|
|
173
176
|
this._busy = false;
|
|
@@ -181,7 +184,11 @@ export default class SerialJS {
|
|
|
181
184
|
this.onerror('[SerialJS] ' + e);
|
|
182
185
|
}
|
|
183
186
|
_change(s) {
|
|
187
|
+
this._state = s;
|
|
184
188
|
this.onchange(s);
|
|
185
|
-
|
|
189
|
+
switch (s) {
|
|
190
|
+
case SerialJS.State.Open: this.onopen(); break;
|
|
191
|
+
case SerialJS.State.Closed: this.onclose(); break;
|
|
192
|
+
}
|
|
186
193
|
}
|
|
187
194
|
}
|
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
|
|
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=[],this.length=0}push(t){return!(!t||0===t.length||(this.chunks.push(t),this.length+=t.length,0))}shift(t){if(!this.length||!t)return new Uint8Array(0);t>this.length&&(t=this.length);const e=new Uint8Array(t);let s=0;for(;s<t&&this.chunks.length>0;){const i=this.chunks[0];if(!i||0===i.length){this.chunks.shift();continue}const r=Math.min(i.length,t-s);e.set(i.subarray(0,r),s),s+=r,r===i.length?this.chunks.shift():this.chunks[0]=i.subarray(r),this.length-=r}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,auto_open:!1,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.cfg.auto_open&&this.open(),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&&this.writer;){let t=this._buffer.shiftAll();try{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
|
}
|