@alexgyver/serial 1.0.5 → 1.0.7

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.
Files changed (2) hide show
  1. package/package.json +6 -6
  2. package/serial.js +131 -52
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexgyver/serial",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
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.105.1",
18
- "webpack-cli": "^6.0.1",
19
- "webpack-dev-server": "^5.2.3",
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.3"
21
+ "css-loader": "^7.1.4"
22
22
  },
23
23
  "dependencies": {
24
- "@alexgyver/utils": "^1.2.4"
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, ShiftBuffer, StreamSplitter } from "@alexgyver/utils";
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) => this.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 == SerialJS.State.Open;
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
- let ports = await navigator.serial.getPorts();
73
- for (let p of ports) await p.forget();
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
- const ports = await navigator.serial.getPorts();
87
- if (!ports.length) return;
88
-
89
- if (this.cfg.reconnect) this.retry = true;
90
- await this._open();
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
- this._buffer.clear();
127
+
128
+ this._sender.reset();
103
129
  this._change(SerialJS.State.Open);
104
130
 
105
- while (this._state == SerialJS.State.Open) {
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
- if (this.onbin) this.onbin(value);
110
- if (this.ontext) this.splitter.write(this._decoder.decode(value, { stream: true }));
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
- if (this.reader) this.reader.releaseLock();
120
- if (this.writer) this.writer.releaseLock();
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.retry = false;
132
- await this._close();
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: return;
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
- while (this._state == SerialJS.State.Closing) {
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
- await this.sendBin((new TextEncoder()).encode(text));
226
+ return this.sendBin(new TextEncoder().encode(text));
157
227
  }
158
228
 
159
229
  async sendBin(data) {
160
- this._buffer.push(data);
161
- this._send();
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
- _buffer = new ShiftBuffer();
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
- let ports = await navigator.serial.getPorts();
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: this.onopen(); break;
194
- case SerialJS.State.Closed: this.onclose(); break;
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
  }