@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 CHANGED
@@ -13,6 +13,7 @@ constructor(params = {});
13
13
  config(params = {});
14
14
  // eol: /\r?\n/
15
15
  // baud: 115200
16
+ // auto_open: false
16
17
  // reconnect: 1000
17
18
 
18
19
  onbin(b);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexgyver/serial",
3
- "version": "1.0.2",
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.3"
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 == States.Open;
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._state = States.Open;
97
- this._change(true);
100
+ this._change(SerialJS.State.Open);
98
101
 
99
- while (this._state == States.Open) {
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(false);
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 States.Closed: return;
131
- case States.Open:
133
+ case SerialJS.State.Closed: return;
134
+ case SerialJS.State.Open:
132
135
  if (this.reader) await this.reader.cancel();
133
- this._state = States.Closing;
136
+ this._change(SerialJS.State.Closing);
134
137
  break;
135
138
  }
136
139
 
137
140
  let i = 0;
138
- while (this._state == States.Closing) {
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._state = States.Closed;
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 = States.Closed;
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
- if (this.writer) await this.writer.write(d);
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
- s ? this.onopen() : this.onclose();
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 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==this._state}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()){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._state=1,this._change(!0);1==this._state;){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.retry&&setTimeout(()=>this._open(),this.cfg.reconnect)}this.reader&&this.reader.releaseLock(),this.writer&&this.writer.releaseLock(),this.reader=null,this.writer=null,this._state=3;try{await this._port.close(),this._change(!1)}catch(t){}}}async close(){this.retry=!1,await this._close()}async _close(){switch(this._state){case 3:return;case 1:this.reader&&await this.reader.cancel(),this._state=2}let e=0;for(;2==this._state;)if(await t(10),++e>200){this._error("Close timeout"),this._state=3;break}}async sendText(t){await this.sendBin((new TextEncoder).encode(t))}async sendBin(t){this._buffer.push(t),this._send()}_port=null;_state=3;_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){this.onchange(t),t?this.onopen():this.onclose()}}export{i as default};
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
- console.log('Opened', ser.getName());
20
- }
21
- ser.onclose = () => {
22
- console.log('Closed');
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('port change', port);
30
+ console.log('select', port);
29
31
  }
@@ -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
- console.log('Opened', ser.getName());
20
- }
21
- ser.onclose = () => {
22
- console.log('Closed');
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('port change', port);
30
+ console.log('select', port);
29
31
  }