@alexgyver/serial 1.0.5 → 1.0.6
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 +6 -6
- package/serial.js +131 -52
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexgyver/serial",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Web Serial API wrapper",
|
|
5
5
|
"main": "./serial.js",
|
|
6
6
|
"module": "./serial.js",
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
"url": "git+https://github.com/GyverLibs/Serial.js.git"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"webpack": "^5.
|
|
18
|
-
"webpack-cli": "^
|
|
19
|
-
"webpack-dev-server": "^5.2.
|
|
17
|
+
"webpack": "^5.106.2",
|
|
18
|
+
"webpack-cli": "^7.0.2",
|
|
19
|
+
"webpack-dev-server": "^5.2.4",
|
|
20
20
|
"style-loader": "^4.0.0",
|
|
21
|
-
"css-loader": "^7.1.
|
|
21
|
+
"css-loader": "^7.1.4"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@alexgyver/utils": "^1.2.
|
|
24
|
+
"@alexgyver/utils": "^1.2.6"
|
|
25
25
|
},
|
|
26
26
|
"author": "AlexGyver <alex@alexgyver.ru>",
|
|
27
27
|
"license": "MIT"
|
package/serial.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sleep,
|
|
1
|
+
import { sleep, StreamSplitter, SerialExecutor } from "@alexgyver/utils";
|
|
2
2
|
|
|
3
3
|
export default class SerialJS {
|
|
4
4
|
static State = {
|
|
@@ -8,17 +8,14 @@ export default class SerialJS {
|
|
|
8
8
|
Closing: 'closing',
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
//#region handlers
|
|
12
|
-
onbin = null;
|
|
13
11
|
ontext = null;
|
|
14
|
-
|
|
12
|
+
onbin(b) { }
|
|
15
13
|
onopen() { }
|
|
16
14
|
onclose() { }
|
|
17
15
|
onchange(s) { }
|
|
18
16
|
onselect(name) { }
|
|
19
17
|
onerror(e) { }
|
|
20
18
|
|
|
21
|
-
//#region constructor
|
|
22
19
|
constructor(params = {}) {
|
|
23
20
|
const def = {
|
|
24
21
|
eol: /\r?\n/,
|
|
@@ -26,14 +23,21 @@ export default class SerialJS {
|
|
|
26
23
|
auto_open: false,
|
|
27
24
|
reconnect: 1000,
|
|
28
25
|
};
|
|
26
|
+
|
|
29
27
|
this.cfg = { ...def, ...params };
|
|
30
28
|
|
|
31
|
-
this._setLastPort().then(() => this.onselect(this.getName()));
|
|
32
29
|
this.splitter = new StreamSplitter(this.cfg.eol);
|
|
33
|
-
this.splitter.ontext = (t) =>
|
|
30
|
+
this.splitter.ontext = (t) => {
|
|
31
|
+
try {
|
|
32
|
+
if (this.ontext) this.ontext(t);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
this._error(e);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
this._setLastPort().then(() => this.onselect(this.getName()));
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
//#region methods
|
|
37
41
|
config(params = {}) {
|
|
38
42
|
this.cfg = { ...this.cfg, ...params };
|
|
39
43
|
this.splitter.eol = this.cfg.eol;
|
|
@@ -44,7 +48,7 @@ export default class SerialJS {
|
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
opened() {
|
|
47
|
-
return this._state
|
|
51
|
+
return this._state === SerialJS.State.Open;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
selected() {
|
|
@@ -62,136 +66,211 @@ export default class SerialJS {
|
|
|
62
66
|
case 0x0402: case 0x0403: case 0x0404: case 0x0405: case 0x6001: case 0x0602: case 0x6010: return 'FT232';
|
|
63
67
|
case 0x9500: case 0x0102: case 0x0501: case 0x80a9: case 0xea60: case 0xea61: case 0xea63: return 'CP210x';
|
|
64
68
|
}
|
|
69
|
+
|
|
65
70
|
return 'Unknown';
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
async select() {
|
|
69
74
|
try {
|
|
70
75
|
await this.close();
|
|
76
|
+
|
|
71
77
|
this._port = null;
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
|
|
79
|
+
const ports = await navigator.serial.getPorts();
|
|
80
|
+
for (const p of ports) await p.forget();
|
|
81
|
+
|
|
74
82
|
await sleep(50);
|
|
75
83
|
await navigator.serial.requestPort();
|
|
76
84
|
await this._setLastPort();
|
|
77
85
|
} catch (e) {
|
|
78
86
|
this._error(e);
|
|
79
87
|
}
|
|
88
|
+
|
|
80
89
|
this.onselect(this.getName());
|
|
90
|
+
|
|
81
91
|
if (this.cfg.auto_open) this.open();
|
|
92
|
+
|
|
82
93
|
return this.selected();
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
async open() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
return this._lifecycle.runNothrow(async () => {
|
|
98
|
+
const ports = await navigator.serial.getPorts();
|
|
99
|
+
if (!ports.length) return false;
|
|
100
|
+
|
|
101
|
+
if (this.cfg.reconnect) this.retry = true;
|
|
102
|
+
|
|
103
|
+
await this._open();
|
|
104
|
+
|
|
105
|
+
return this.opened();
|
|
106
|
+
});
|
|
91
107
|
}
|
|
108
|
+
|
|
92
109
|
async _open() {
|
|
93
|
-
if (this.opened()) return;
|
|
110
|
+
if (this.opened() || this._state === SerialJS.State.Opening) return;
|
|
111
|
+
|
|
94
112
|
this._change(SerialJS.State.Opening);
|
|
95
113
|
|
|
96
114
|
try {
|
|
97
115
|
await this._close();
|
|
98
116
|
await this._setLastPort();
|
|
117
|
+
|
|
118
|
+
if (!this._port) {
|
|
119
|
+
this._change(SerialJS.State.Closed);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
99
123
|
await this._port.open({ baudRate: this.cfg.baud });
|
|
124
|
+
|
|
100
125
|
this.writer = this._port.writable.getWriter();
|
|
101
126
|
this.reader = this._port.readable.getReader();
|
|
102
|
-
|
|
127
|
+
|
|
128
|
+
this._sender.reset();
|
|
103
129
|
this._change(SerialJS.State.Open);
|
|
104
130
|
|
|
105
|
-
while (this._state
|
|
131
|
+
while (this._state === SerialJS.State.Open) {
|
|
106
132
|
const { value, done } = await this.reader.read();
|
|
133
|
+
|
|
107
134
|
if (done) break;
|
|
135
|
+
|
|
108
136
|
if (value) {
|
|
109
|
-
|
|
110
|
-
|
|
137
|
+
try {
|
|
138
|
+
this.onbin(value);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
this._error(e);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (this.ontext) {
|
|
144
|
+
this.splitter.write(
|
|
145
|
+
this._decoder.decode(value, { stream: true })
|
|
146
|
+
);
|
|
147
|
+
}
|
|
111
148
|
}
|
|
112
149
|
}
|
|
113
150
|
} catch (e) {
|
|
114
151
|
this._error(e);
|
|
115
|
-
this._change(SerialJS.State.Closed);
|
|
116
|
-
if (this.retry) setTimeout(() => this._open(), this.cfg.reconnect);
|
|
117
152
|
}
|
|
118
153
|
|
|
119
|
-
|
|
120
|
-
|
|
154
|
+
this._sender.reset();
|
|
155
|
+
|
|
156
|
+
if (this._state !== SerialJS.State.Closing) {
|
|
157
|
+
this._change(SerialJS.State.Closing);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
if (this.reader) this.reader.releaseLock();
|
|
162
|
+
} catch (e) { }
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
if (this.writer) this.writer.releaseLock();
|
|
166
|
+
} catch (e) { }
|
|
167
|
+
|
|
121
168
|
this.reader = null;
|
|
122
169
|
this.writer = null;
|
|
123
170
|
|
|
124
171
|
try {
|
|
125
|
-
await this._port.close();
|
|
126
|
-
this._change(SerialJS.State.Closed);
|
|
172
|
+
if (this._port) await this._port.close();
|
|
127
173
|
} catch (e) { }
|
|
174
|
+
|
|
175
|
+
this._change(SerialJS.State.Closed);
|
|
176
|
+
|
|
177
|
+
if (this.retry) {
|
|
178
|
+
setTimeout(() => this._lifecycle.runNothrow(() => this._open()), this.cfg.reconnect);
|
|
179
|
+
}
|
|
128
180
|
}
|
|
129
181
|
|
|
130
182
|
async close() {
|
|
131
|
-
this.
|
|
132
|
-
|
|
183
|
+
return this._lifecycle.runNothrow(async () => {
|
|
184
|
+
this.retry = false;
|
|
185
|
+
this._sender.reset();
|
|
186
|
+
await this._close();
|
|
187
|
+
return true;
|
|
188
|
+
});
|
|
133
189
|
}
|
|
190
|
+
|
|
134
191
|
async _close() {
|
|
135
192
|
switch (this._state) {
|
|
136
|
-
case SerialJS.State.Closed:
|
|
193
|
+
case SerialJS.State.Closed:
|
|
194
|
+
return;
|
|
195
|
+
|
|
196
|
+
case SerialJS.State.Opening:
|
|
137
197
|
case SerialJS.State.Open:
|
|
138
|
-
if (this.reader) await this.reader.cancel();
|
|
139
198
|
this._change(SerialJS.State.Closing);
|
|
199
|
+
|
|
200
|
+
if (this.reader) {
|
|
201
|
+
try {
|
|
202
|
+
await this.reader.cancel();
|
|
203
|
+
} catch (e) { }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
break;
|
|
207
|
+
|
|
208
|
+
case SerialJS.State.Closing:
|
|
140
209
|
break;
|
|
141
210
|
}
|
|
142
211
|
|
|
143
212
|
let i = 0;
|
|
144
|
-
|
|
213
|
+
|
|
214
|
+
while (this._state === SerialJS.State.Closing) {
|
|
145
215
|
await sleep(10);
|
|
216
|
+
|
|
146
217
|
if (++i > 200) {
|
|
147
218
|
this._error('Close timeout');
|
|
148
219
|
this._change(SerialJS.State.Closed);
|
|
149
220
|
break;
|
|
150
221
|
}
|
|
151
222
|
}
|
|
152
|
-
|
|
153
223
|
}
|
|
154
224
|
|
|
155
225
|
async sendText(text) {
|
|
156
|
-
|
|
226
|
+
return this.sendBin(new TextEncoder().encode(text));
|
|
157
227
|
}
|
|
158
228
|
|
|
159
229
|
async sendBin(data) {
|
|
160
|
-
this.
|
|
161
|
-
|
|
230
|
+
if (!this.opened() || !this.writer) return false;
|
|
231
|
+
|
|
232
|
+
return this._sender.runNothrow(async () => {
|
|
233
|
+
if (!this.opened() || !this.writer) return false;
|
|
234
|
+
|
|
235
|
+
await this.writer.write(data);
|
|
236
|
+
return true;
|
|
237
|
+
});
|
|
162
238
|
}
|
|
163
239
|
|
|
164
|
-
//#region private
|
|
165
240
|
_port = null;
|
|
166
241
|
_state = SerialJS.State.Closed;
|
|
167
|
-
|
|
242
|
+
|
|
243
|
+
reader = null;
|
|
244
|
+
writer = null;
|
|
245
|
+
retry = false;
|
|
246
|
+
|
|
168
247
|
_decoder = new TextDecoder();
|
|
248
|
+
_sender = new SerialExecutor();
|
|
249
|
+
_lifecycle = new SerialExecutor();
|
|
169
250
|
|
|
170
|
-
async _send() {
|
|
171
|
-
if (this._busy) return;
|
|
172
|
-
this._busy = true;
|
|
173
|
-
while (this._buffer.length && this.writer) {
|
|
174
|
-
let d = this._buffer.shiftAll();
|
|
175
|
-
try {
|
|
176
|
-
await this.writer.write(d);
|
|
177
|
-
} catch (e) { }
|
|
178
|
-
}
|
|
179
|
-
this._busy = false;
|
|
180
|
-
}
|
|
181
251
|
async _setLastPort() {
|
|
182
|
-
|
|
252
|
+
const ports = await navigator.serial.getPorts();
|
|
183
253
|
this._port = ports.length ? ports[0] : null;
|
|
184
254
|
}
|
|
185
255
|
|
|
186
256
|
_error(e) {
|
|
187
257
|
this.onerror('[SerialJS] ' + e);
|
|
188
258
|
}
|
|
259
|
+
|
|
189
260
|
_change(s) {
|
|
261
|
+
if (this._state === s) return;
|
|
262
|
+
|
|
190
263
|
this._state = s;
|
|
191
264
|
this.onchange(s);
|
|
265
|
+
|
|
192
266
|
switch (s) {
|
|
193
|
-
case SerialJS.State.Open:
|
|
194
|
-
|
|
267
|
+
case SerialJS.State.Open:
|
|
268
|
+
this.onopen();
|
|
269
|
+
break;
|
|
270
|
+
|
|
271
|
+
case SerialJS.State.Closed:
|
|
272
|
+
this.onclose();
|
|
273
|
+
break;
|
|
195
274
|
}
|
|
196
275
|
}
|
|
197
276
|
}
|