@alexgyver/serial 1.0.4 → 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/README.md +5 -1
- package/package.json +7 -6
- package/serial.js +131 -49
- package/test/index.html +1 -2
- package/test/script.js +2 -1
- package/webpack.dev.config.js +12 -27
- package/test_dev/index.html +0 -18
- package/test_dev/script.js +0 -31
package/README.md
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
- Буферизация отправки
|
|
6
6
|
- Буферизация приёма, разделение текста по разделителю
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
[demo](https://gyverlibs.github.io/Serial.js/test/)
|
|
9
|
+
|
|
10
|
+
> **Browser**: https://gyverlibs.github.io/Serial.js/Serial.min.js
|
|
11
|
+
|
|
12
|
+
> **Node**: npm i @alexgyver/serial
|
|
9
13
|
|
|
10
14
|
## Дока
|
|
11
15
|
```js
|
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,13 +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.
|
|
20
|
-
"
|
|
17
|
+
"webpack": "^5.106.2",
|
|
18
|
+
"webpack-cli": "^7.0.2",
|
|
19
|
+
"webpack-dev-server": "^5.2.4",
|
|
20
|
+
"style-loader": "^4.0.0",
|
|
21
|
+
"css-loader": "^7.1.4"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@alexgyver/utils": "^1.2.
|
|
24
|
+
"@alexgyver/utils": "^1.2.6"
|
|
24
25
|
},
|
|
25
26
|
"author": "AlexGyver <alex@alexgyver.ru>",
|
|
26
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,133 +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
|
-
|
|
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
|
+
});
|
|
88
107
|
}
|
|
108
|
+
|
|
89
109
|
async _open() {
|
|
90
|
-
if (this.opened()) return;
|
|
110
|
+
if (this.opened() || this._state === SerialJS.State.Opening) return;
|
|
111
|
+
|
|
91
112
|
this._change(SerialJS.State.Opening);
|
|
92
113
|
|
|
93
114
|
try {
|
|
94
115
|
await this._close();
|
|
95
116
|
await this._setLastPort();
|
|
117
|
+
|
|
118
|
+
if (!this._port) {
|
|
119
|
+
this._change(SerialJS.State.Closed);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
96
123
|
await this._port.open({ baudRate: this.cfg.baud });
|
|
124
|
+
|
|
97
125
|
this.writer = this._port.writable.getWriter();
|
|
98
126
|
this.reader = this._port.readable.getReader();
|
|
99
|
-
|
|
127
|
+
|
|
128
|
+
this._sender.reset();
|
|
100
129
|
this._change(SerialJS.State.Open);
|
|
101
130
|
|
|
102
|
-
while (this._state
|
|
131
|
+
while (this._state === SerialJS.State.Open) {
|
|
103
132
|
const { value, done } = await this.reader.read();
|
|
133
|
+
|
|
104
134
|
if (done) break;
|
|
135
|
+
|
|
105
136
|
if (value) {
|
|
106
|
-
|
|
107
|
-
|
|
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
|
+
}
|
|
108
148
|
}
|
|
109
149
|
}
|
|
110
150
|
} catch (e) {
|
|
111
151
|
this._error(e);
|
|
112
|
-
this._change(SerialJS.State.Closed);
|
|
113
|
-
if (this.retry) setTimeout(() => this._open(), this.cfg.reconnect);
|
|
114
152
|
}
|
|
115
153
|
|
|
116
|
-
|
|
117
|
-
|
|
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
|
+
|
|
118
168
|
this.reader = null;
|
|
119
169
|
this.writer = null;
|
|
120
170
|
|
|
121
171
|
try {
|
|
122
|
-
await this._port.close();
|
|
123
|
-
this._change(SerialJS.State.Closed);
|
|
172
|
+
if (this._port) await this._port.close();
|
|
124
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
|
+
}
|
|
125
180
|
}
|
|
126
181
|
|
|
127
182
|
async close() {
|
|
128
|
-
this.
|
|
129
|
-
|
|
183
|
+
return this._lifecycle.runNothrow(async () => {
|
|
184
|
+
this.retry = false;
|
|
185
|
+
this._sender.reset();
|
|
186
|
+
await this._close();
|
|
187
|
+
return true;
|
|
188
|
+
});
|
|
130
189
|
}
|
|
190
|
+
|
|
131
191
|
async _close() {
|
|
132
192
|
switch (this._state) {
|
|
133
|
-
case SerialJS.State.Closed:
|
|
193
|
+
case SerialJS.State.Closed:
|
|
194
|
+
return;
|
|
195
|
+
|
|
196
|
+
case SerialJS.State.Opening:
|
|
134
197
|
case SerialJS.State.Open:
|
|
135
|
-
if (this.reader) await this.reader.cancel();
|
|
136
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:
|
|
137
209
|
break;
|
|
138
210
|
}
|
|
139
211
|
|
|
140
212
|
let i = 0;
|
|
141
|
-
|
|
213
|
+
|
|
214
|
+
while (this._state === SerialJS.State.Closing) {
|
|
142
215
|
await sleep(10);
|
|
216
|
+
|
|
143
217
|
if (++i > 200) {
|
|
144
218
|
this._error('Close timeout');
|
|
145
219
|
this._change(SerialJS.State.Closed);
|
|
146
220
|
break;
|
|
147
221
|
}
|
|
148
222
|
}
|
|
149
|
-
|
|
150
223
|
}
|
|
151
224
|
|
|
152
225
|
async sendText(text) {
|
|
153
|
-
|
|
226
|
+
return this.sendBin(new TextEncoder().encode(text));
|
|
154
227
|
}
|
|
155
228
|
|
|
156
229
|
async sendBin(data) {
|
|
157
|
-
this.
|
|
158
|
-
|
|
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
|
+
});
|
|
159
238
|
}
|
|
160
239
|
|
|
161
|
-
//#region private
|
|
162
240
|
_port = null;
|
|
163
241
|
_state = SerialJS.State.Closed;
|
|
164
|
-
|
|
242
|
+
|
|
243
|
+
reader = null;
|
|
244
|
+
writer = null;
|
|
245
|
+
retry = false;
|
|
246
|
+
|
|
165
247
|
_decoder = new TextDecoder();
|
|
248
|
+
_sender = new SerialExecutor();
|
|
249
|
+
_lifecycle = new SerialExecutor();
|
|
166
250
|
|
|
167
|
-
async _send() {
|
|
168
|
-
if (this._busy) return;
|
|
169
|
-
this._busy = true;
|
|
170
|
-
while (this._buffer.length && this.writer) {
|
|
171
|
-
let d = this._buffer.shiftAll();
|
|
172
|
-
try {
|
|
173
|
-
await this.writer.write(d);
|
|
174
|
-
} catch (e) { }
|
|
175
|
-
}
|
|
176
|
-
this._busy = false;
|
|
177
|
-
}
|
|
178
251
|
async _setLastPort() {
|
|
179
|
-
|
|
252
|
+
const ports = await navigator.serial.getPorts();
|
|
180
253
|
this._port = ports.length ? ports[0] : null;
|
|
181
254
|
}
|
|
182
255
|
|
|
183
256
|
_error(e) {
|
|
184
257
|
this.onerror('[SerialJS] ' + e);
|
|
185
258
|
}
|
|
259
|
+
|
|
186
260
|
_change(s) {
|
|
261
|
+
if (this._state === s) return;
|
|
262
|
+
|
|
187
263
|
this._state = s;
|
|
188
264
|
this.onchange(s);
|
|
265
|
+
|
|
189
266
|
switch (s) {
|
|
190
|
-
case SerialJS.State.Open:
|
|
191
|
-
|
|
267
|
+
case SerialJS.State.Open:
|
|
268
|
+
this.onopen();
|
|
269
|
+
break;
|
|
270
|
+
|
|
271
|
+
case SerialJS.State.Closed:
|
|
272
|
+
this.onclose();
|
|
273
|
+
break;
|
|
192
274
|
}
|
|
193
275
|
}
|
|
194
276
|
}
|
package/test/index.html
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8">
|
|
6
|
-
<
|
|
7
|
-
<script src="script.js" type="module" defer="defer"></script>
|
|
6
|
+
<script src="./script.js" type="module"></script>
|
|
8
7
|
<title>Serial test</title>
|
|
9
8
|
</head>
|
|
10
9
|
|
package/test/script.js
CHANGED
package/webpack.dev.config.js
CHANGED
|
@@ -1,39 +1,24 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const PACKAGE = require('./package.json');
|
|
3
|
-
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
4
2
|
|
|
5
3
|
module.exports = {
|
|
6
|
-
entry:
|
|
7
|
-
index: './test_dev/script.js',
|
|
8
|
-
},
|
|
9
|
-
|
|
4
|
+
entry: './test/script.js',
|
|
10
5
|
output: {
|
|
11
6
|
filename: 'script.js',
|
|
12
|
-
path: path.resolve(__dirname, '
|
|
13
|
-
clean:
|
|
7
|
+
path: path.resolve(__dirname, 'test'),
|
|
8
|
+
clean: false,
|
|
9
|
+
},
|
|
10
|
+
module: {
|
|
11
|
+
rules: [
|
|
12
|
+
{
|
|
13
|
+
test: /\.css$/,
|
|
14
|
+
use: ['style-loader', 'css-loader'],
|
|
15
|
+
}
|
|
16
|
+
]
|
|
14
17
|
},
|
|
15
|
-
|
|
16
|
-
plugins: [
|
|
17
|
-
new HtmlWebpackPlugin({
|
|
18
|
-
template: `./test_dev/index.html`,
|
|
19
|
-
filename: `index.html`,
|
|
20
|
-
inject: true,
|
|
21
|
-
minify: false,
|
|
22
|
-
version: PACKAGE.version,
|
|
23
|
-
title: PACKAGE.title,
|
|
24
|
-
}),
|
|
25
|
-
],
|
|
26
|
-
|
|
27
18
|
devServer: {
|
|
28
|
-
|
|
29
|
-
static: path.resolve(__dirname, './dev'),
|
|
19
|
+
static: path.resolve(__dirname, 'test'),
|
|
30
20
|
hot: true,
|
|
31
21
|
open: true,
|
|
32
22
|
},
|
|
33
|
-
|
|
34
|
-
watchOptions: {
|
|
35
|
-
poll: 1000,
|
|
36
|
-
},
|
|
37
|
-
|
|
38
23
|
mode: 'development',
|
|
39
24
|
};
|
package/test_dev/index.html
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
7
|
-
<script src="script.js" type="module" defer="defer"></script>
|
|
8
|
-
<title>Serial test</title>
|
|
9
|
-
</head>
|
|
10
|
-
|
|
11
|
-
<body>
|
|
12
|
-
<button id="select_b">select</button>
|
|
13
|
-
<button id="open_b">open</button>
|
|
14
|
-
<button id="close_b">close</button>
|
|
15
|
-
<button id="send_b">send</button>
|
|
16
|
-
</body>
|
|
17
|
-
|
|
18
|
-
</html>
|
package/test_dev/script.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import SerialJS from "../serial";
|
|
2
|
-
|
|
3
|
-
let i = 0;
|
|
4
|
-
let ser = new SerialJS();
|
|
5
|
-
|
|
6
|
-
// control
|
|
7
|
-
select_b.onclick = () => ser.select();
|
|
8
|
-
open_b.onclick = () => ser.open();
|
|
9
|
-
close_b.onclick = () => ser.close();
|
|
10
|
-
send_b.onclick = () => ser.sendText('Hello ' + i++);
|
|
11
|
-
|
|
12
|
-
// read
|
|
13
|
-
// ser.onbin = b => console.log(b);
|
|
14
|
-
ser.ontext = t => console.log(t);
|
|
15
|
-
|
|
16
|
-
// state
|
|
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);
|
|
25
|
-
}
|
|
26
|
-
ser.onerror = e => {
|
|
27
|
-
console.log(e);
|
|
28
|
-
}
|
|
29
|
-
ser.onselect = (port) => {
|
|
30
|
-
console.log('select', port);
|
|
31
|
-
}
|