serial-core 0.1.0 → 0.2.0-dev.2
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/LICENSE.md +73 -73
- package/README.md +91 -119
- package/dist/SerialService.d.cts +64 -0
- package/dist/SerialService.d.ts +64 -0
- package/dist/core/PortRegistry.d.cts +24 -0
- package/dist/core/PortRegistry.d.ts +24 -0
- package/dist/core/PortScanner.d.cts +11 -0
- package/dist/core/PortScanner.d.ts +11 -0
- package/dist/core/QueueManager.d.cts +33 -0
- package/dist/core/QueueManager.d.ts +33 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1 -0
- package/dist/types.d.cts +39 -0
- package/dist/types.d.ts +39 -0
- package/dist/utils/buffers.d.cts +27 -0
- package/dist/utils/buffers.d.ts +27 -0
- package/package.json +69 -55
- package/dist/serial-core.cjs +0 -1
- package/dist/serial-core.d.cts +0 -140
- package/dist/serial-core.d.mts +0 -140
- package/dist/serial-core.mjs +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QueueManager acts as an intermediate buffer.
|
|
3
|
+
* It prevents serial port saturation and guarantees messages
|
|
4
|
+
* leave in the order they arrived (FIFO).
|
|
5
|
+
*/
|
|
6
|
+
export declare class QueueManager {
|
|
7
|
+
private queue;
|
|
8
|
+
private isProcessing;
|
|
9
|
+
private _currentAlias;
|
|
10
|
+
private writeHandler;
|
|
11
|
+
constructor(writeHandler: (data: Buffer | string) => Promise<void>);
|
|
12
|
+
/**
|
|
13
|
+
* Adds a command to the queue and returns a promise
|
|
14
|
+
* that resolves when the command has been drained to the port.
|
|
15
|
+
*/
|
|
16
|
+
add(data: Buffer | string, options?: {
|
|
17
|
+
alias?: string;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Getter for the alias of the command currently processing (or just processed).
|
|
22
|
+
* Useful for correlating responses.
|
|
23
|
+
*/
|
|
24
|
+
get currentAlias(): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Clears the queue (useful on abrupt disconnections)
|
|
27
|
+
*/
|
|
28
|
+
clear(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Recursive processing loop
|
|
31
|
+
*/
|
|
32
|
+
private process;
|
|
33
|
+
}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=s(require(`serialport`)),l=s(require(`events`)),u={DISCONNECTED:`DISCONNECTED`,SCANNING:`SCANNING`,CONNECTING:`CONNECTING`,CONNECTED:`CONNECTED`,RECONNECTING:`RECONNECTING`};var d=class{queue=[];isProcessing=!1;_currentAlias=void 0;writeHandler;constructor(e){this.writeHandler=e}add(e,t){return new Promise((n,r)=>{this.queue.push({data:e,resolve:n,reject:r,alias:t?.alias,timeout:t?.timeout}),this.process()})}get currentAlias(){return this._currentAlias}clear(){this.queue.forEach(e=>e.reject(Error(`Queue cleared due to disconnection`))),this.queue=[],this.isProcessing=!1}async process(){if(this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();if(!e){this.isProcessing=!1;return}this._currentAlias=e.alias;let t=null,n=!1;try{e.timeout&&e.timeout>0&&(t=setTimeout(()=>{n=!0,e.reject(Error(`Command timeout after ${e.timeout}ms`)),this.isProcessing=!1,this.process()},e.timeout)),await this.writeHandler(e.data),n||(t&&clearTimeout(t),e.resolve())}catch(r){n||(t&&clearTimeout(t),e.reject(r instanceof Error?r:Error(String(r))))}finally{n||(this.isProcessing=!1,this.process())}}},f=class{static async findPort(e,t){if(!e&&!t)throw Error(`VendorID or ProductID is required for automatic scanning.`);try{let n=await c.SerialPort.list(),r=n.find(n=>{let r=n.vendorId?.toLowerCase()||``,i=n.productId?.toLowerCase()||``,a=(e||``).toLowerCase().replace(`0x`,``),o=(t||``).toLowerCase().replace(`0x`,``),s=e?r.includes(a):!0,c=t?i.includes(o):!0;return s&&c});return r?r.path:null}catch(e){return console.error(`Error scanning ports:`,e),null}}},p=class e{static instance;lockedPorts=new Set;constructor(){}static getInstance(){return e.instance||=new e,e.instance}register(e){return this.lockedPorts.has(e)?!1:(this.lockedPorts.add(e),!0)}unregister(e){this.lockedPorts.delete(e)}isLocked(e){return this.lockedPorts.has(e)}},m=class extends l.EventEmitter{port=null;queue;_status=u.DISCONNECTED;config;intentionalDisconnect=!1;reconnectTimer=null;constructor(e){super(),this.config=e,this.queue=new d(async e=>this.performWrite(e)),this.config.autoConnect&&this.connect()}get status(){return this._status}setStatus(e){this._status!==e&&(this._status=e,this.emit(`status`,e))}async connect(){if(this._status===u.CONNECTED||this._status===u.CONNECTING)return;this.intentionalDisconnect=!1,this.setStatus(u.SCANNING);let e=this.config.path;if(!e)try{let t=await f.findPort(this.config.vendorId,this.config.productId);if(t)e=t;else{this.handleConnectionFailure(`Device not found in scan`);return}}catch(e){this.handleConnectionFailure(`Error scanning: ${e}`);return}if(p.getInstance().isLocked(e)){this.handleConnectionFailure(`Port ${e} occupied by another internal instance`);return}if(!p.getInstance().register(e)){this.handleConnectionFailure(`Could not lock port ${e}`);return}this.setStatus(u.CONNECTING),this.openPort(e)}openPort(e){if(this.port=new c.SerialPort({path:e,baudRate:this.config.baudRate,autoOpen:!1}),this.port.open(t=>{if(t){this.handleConnectionFailure(t.message);return}this.config.handshake?this.performHandshake():(this.setStatus(u.CONNECTED),this.emit(`connected`,{path:e,baudRate:this.config.baudRate}))}),this.config.parser){let e=this.port.pipe(this.config.parser);e.on(`data`,e=>this.emit(`data`,e,this.queue.currentAlias)),e.on(`error`,e=>this.emit(`error`,e))}else this.port.on(`data`,e=>this.emit(`data`,e,this.queue.currentAlias));this.port.on(`error`,e=>{this.emit(`error`,e)}),this.port.on(`close`,()=>{this.cleanup(),this.emit(`disconnected`,this.intentionalDisconnect?`Manual`:`Unexpected`),this.intentionalDisconnect?this.setStatus(u.DISCONNECTED):(this.setStatus(u.RECONNECTING),this.scheduleReconnect())})}async disconnect(){if(this.intentionalDisconnect=!0,this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.port&&this.port.isOpen)return new Promise(e=>{this.port?.close(()=>e())})}send(e,t){return this._status===u.CONNECTED?this.queue.add(e,t):Promise.reject(Error(`Port not connected`))}performWrite(e){return new Promise((t,n)=>{if(!this.port||!this.port.isOpen)return n(Error(`Port closed during write`));let r=!this.port.write(e,e=>{if(e)return n(e);r||t()});r&&this.port.once(`drain`,t)})}handleConnectionFailure(e){this.emit(`error`,Error(`Connection failure: ${e}`)),this.cleanup(),this.intentionalDisconnect||(this.setStatus(u.RECONNECTING),this.scheduleReconnect())}scheduleReconnect(){this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=setTimeout(()=>{this.connect()},this.config.reconnectInterval)}cleanup(){this.port&&this.port.path&&p.getInstance().unregister(this.port.path),this.port&&(this.port.removeAllListeners(),this.port=null),this.config.parser&&(this.config.parser.removeAllListeners(`data`),this.config.parser.removeAllListeners(`error`)),this.intentionalDisconnect&&this.queue.clear()}performHandshake(){if(!this.port||!this.port.isOpen||!this.config.handshake)return;let{command:e,pattern:t,timeout:n}=this.config.handshake,r,i=e=>{let n=``;n=Buffer.isBuffer(e)?e.toString():typeof e==`string`?e:String(e);let r=typeof t==`string`?new RegExp(t):t;r.test(n)&&(a(),this.setStatus(u.CONNECTED),this.emit(`connected`,{path:this.port.path,baudRate:this.config.baudRate}))},a=()=>{clearTimeout(r),this.removeListener(`data`,o)},o=e=>i(e);this.on(`data`,o),r=setTimeout(()=>{a(),this.handleConnectionFailure(`Handshake timeout (pattern: ${t})`)},n),this.performWrite(e).catch(e=>{a(),this.handleConnectionFailure(`Error writing handshake: ${e.message}`)})}};function h(e){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e.buffer,e.byteOffset,e.byteLength):(Array.isArray(e),Buffer.from(e))}function g(e){return e}function _(e,t=`utf8`){return e.toString(t)}function v(e){return e.toString(`ascii`)}function y(e){return[...e]}exports.PortScanner=f,exports.SerialService=m,exports.SerialStatus=u,exports.toArray=y,exports.toAscii=v,exports.toBuffer=h,exports.toString=_,exports.toUint8Array=g;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license GPL-3.0 serial-core
|
|
3
|
+
*
|
|
4
|
+
* Created by (c) Danidoble.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the GPL-3.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
export { SerialService } from './SerialService.ts';
|
|
10
|
+
export { SerialStatus } from './types.ts';
|
|
11
|
+
export type { SerialConfig, SerialEvents } from './types.ts';
|
|
12
|
+
export * from './utils/buffers.ts';
|
|
13
|
+
export { PortScanner } from './core/PortScanner.ts';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license GPL-3.0 serial-core
|
|
3
|
+
*
|
|
4
|
+
* Created by (c) Danidoble.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the GPL-3.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
export { SerialService } from './SerialService.ts';
|
|
10
|
+
export { SerialStatus } from './types.ts';
|
|
11
|
+
export type { SerialConfig, SerialEvents } from './types.ts';
|
|
12
|
+
export * from './utils/buffers.ts';
|
|
13
|
+
export { PortScanner } from './core/PortScanner.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{SerialPort as e}from"serialport";import{EventEmitter as t}from"events";const n={DISCONNECTED:`DISCONNECTED`,SCANNING:`SCANNING`,CONNECTING:`CONNECTING`,CONNECTED:`CONNECTED`,RECONNECTING:`RECONNECTING`};var r=class{queue=[];isProcessing=!1;_currentAlias=void 0;writeHandler;constructor(e){this.writeHandler=e}add(e,t){return new Promise((n,r)=>{this.queue.push({data:e,resolve:n,reject:r,alias:t?.alias,timeout:t?.timeout}),this.process()})}get currentAlias(){return this._currentAlias}clear(){this.queue.forEach(e=>e.reject(Error(`Queue cleared due to disconnection`))),this.queue=[],this.isProcessing=!1}async process(){if(this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();if(!e){this.isProcessing=!1;return}this._currentAlias=e.alias;let t=null,n=!1;try{e.timeout&&e.timeout>0&&(t=setTimeout(()=>{n=!0,e.reject(Error(`Command timeout after ${e.timeout}ms`)),this.isProcessing=!1,this.process()},e.timeout)),await this.writeHandler(e.data),n||(t&&clearTimeout(t),e.resolve())}catch(r){n||(t&&clearTimeout(t),e.reject(r instanceof Error?r:Error(String(r))))}finally{n||(this.isProcessing=!1,this.process())}}},i=class{static async findPort(t,n){if(!t&&!n)throw Error(`VendorID or ProductID is required for automatic scanning.`);try{let r=await e.list(),i=r.find(e=>{let r=e.vendorId?.toLowerCase()||``,i=e.productId?.toLowerCase()||``,a=(t||``).toLowerCase().replace(`0x`,``),o=(n||``).toLowerCase().replace(`0x`,``),s=t?r.includes(a):!0,c=n?i.includes(o):!0;return s&&c});return i?i.path:null}catch(e){return console.error(`Error scanning ports:`,e),null}}},a=class e{static instance;lockedPorts=new Set;constructor(){}static getInstance(){return e.instance||=new e,e.instance}register(e){return this.lockedPorts.has(e)?!1:(this.lockedPorts.add(e),!0)}unregister(e){this.lockedPorts.delete(e)}isLocked(e){return this.lockedPorts.has(e)}},o=class extends t{port=null;queue;_status=n.DISCONNECTED;config;intentionalDisconnect=!1;reconnectTimer=null;constructor(e){super(),this.config=e,this.queue=new r(async e=>this.performWrite(e)),this.config.autoConnect&&this.connect()}get status(){return this._status}setStatus(e){this._status!==e&&(this._status=e,this.emit(`status`,e))}async connect(){if(this._status===n.CONNECTED||this._status===n.CONNECTING)return;this.intentionalDisconnect=!1,this.setStatus(n.SCANNING);let e=this.config.path;if(!e)try{let t=await i.findPort(this.config.vendorId,this.config.productId);if(t)e=t;else{this.handleConnectionFailure(`Device not found in scan`);return}}catch(e){this.handleConnectionFailure(`Error scanning: ${e}`);return}if(a.getInstance().isLocked(e)){this.handleConnectionFailure(`Port ${e} occupied by another internal instance`);return}if(!a.getInstance().register(e)){this.handleConnectionFailure(`Could not lock port ${e}`);return}this.setStatus(n.CONNECTING),this.openPort(e)}openPort(t){if(this.port=new e({path:t,baudRate:this.config.baudRate,autoOpen:!1}),this.port.open(e=>{if(e){this.handleConnectionFailure(e.message);return}this.config.handshake?this.performHandshake():(this.setStatus(n.CONNECTED),this.emit(`connected`,{path:t,baudRate:this.config.baudRate}))}),this.config.parser){let e=this.port.pipe(this.config.parser);e.on(`data`,e=>this.emit(`data`,e,this.queue.currentAlias)),e.on(`error`,e=>this.emit(`error`,e))}else this.port.on(`data`,e=>this.emit(`data`,e,this.queue.currentAlias));this.port.on(`error`,e=>{this.emit(`error`,e)}),this.port.on(`close`,()=>{this.cleanup(),this.emit(`disconnected`,this.intentionalDisconnect?`Manual`:`Unexpected`),this.intentionalDisconnect?this.setStatus(n.DISCONNECTED):(this.setStatus(n.RECONNECTING),this.scheduleReconnect())})}async disconnect(){if(this.intentionalDisconnect=!0,this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.port&&this.port.isOpen)return new Promise(e=>{this.port?.close(()=>e())})}send(e,t){return this._status===n.CONNECTED?this.queue.add(e,t):Promise.reject(Error(`Port not connected`))}performWrite(e){return new Promise((t,n)=>{if(!this.port||!this.port.isOpen)return n(Error(`Port closed during write`));let r=!this.port.write(e,e=>{if(e)return n(e);r||t()});r&&this.port.once(`drain`,t)})}handleConnectionFailure(e){this.emit(`error`,Error(`Connection failure: ${e}`)),this.cleanup(),this.intentionalDisconnect||(this.setStatus(n.RECONNECTING),this.scheduleReconnect())}scheduleReconnect(){this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=setTimeout(()=>{this.connect()},this.config.reconnectInterval)}cleanup(){this.port&&this.port.path&&a.getInstance().unregister(this.port.path),this.port&&(this.port.removeAllListeners(),this.port=null),this.config.parser&&(this.config.parser.removeAllListeners(`data`),this.config.parser.removeAllListeners(`error`)),this.intentionalDisconnect&&this.queue.clear()}performHandshake(){if(!this.port||!this.port.isOpen||!this.config.handshake)return;let{command:e,pattern:t,timeout:r}=this.config.handshake,i,a=e=>{let r=``;r=Buffer.isBuffer(e)?e.toString():typeof e==`string`?e:String(e);let i=typeof t==`string`?new RegExp(t):t;i.test(r)&&(o(),this.setStatus(n.CONNECTED),this.emit(`connected`,{path:this.port.path,baudRate:this.config.baudRate}))},o=()=>{clearTimeout(i),this.removeListener(`data`,s)},s=e=>a(e);this.on(`data`,s),i=setTimeout(()=>{o(),this.handleConnectionFailure(`Handshake timeout (pattern: ${t})`)},r),this.performWrite(e).catch(e=>{o(),this.handleConnectionFailure(`Error writing handshake: ${e.message}`)})}};function s(e){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e.buffer,e.byteOffset,e.byteLength):(Array.isArray(e),Buffer.from(e))}function c(e){return e}function l(e,t=`utf8`){return e.toString(t)}function u(e){return e.toString(`ascii`)}function d(e){return[...e]}export{i as PortScanner,o as SerialService,n as SerialStatus,d as toArray,u as toAscii,s as toBuffer,l as toString,c as toUint8Array};
|
package/dist/types.d.cts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare const SerialStatus: {
|
|
2
|
+
readonly DISCONNECTED: "DISCONNECTED";
|
|
3
|
+
readonly SCANNING: "SCANNING";
|
|
4
|
+
readonly CONNECTING: "CONNECTING";
|
|
5
|
+
readonly CONNECTED: "CONNECTED";
|
|
6
|
+
readonly RECONNECTING: "RECONNECTING";
|
|
7
|
+
};
|
|
8
|
+
export type SerialStatus = (typeof SerialStatus)[keyof typeof SerialStatus];
|
|
9
|
+
export interface SerialConfig {
|
|
10
|
+
baudRate: number;
|
|
11
|
+
path?: string;
|
|
12
|
+
vendorId?: string;
|
|
13
|
+
productId?: string;
|
|
14
|
+
reconnectInterval: number;
|
|
15
|
+
autoConnect: boolean;
|
|
16
|
+
parser?: NodeJS.ReadWriteStream;
|
|
17
|
+
handshake?: {
|
|
18
|
+
command: string | Buffer;
|
|
19
|
+
pattern: string | RegExp;
|
|
20
|
+
timeout: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface QueueItem {
|
|
24
|
+
data: Buffer | string;
|
|
25
|
+
alias?: string;
|
|
26
|
+
timeout?: number;
|
|
27
|
+
resolve: () => void;
|
|
28
|
+
reject: (err: Error) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface SerialEvents {
|
|
31
|
+
status: (status: SerialStatus) => void;
|
|
32
|
+
data: (data: Buffer | string, alias?: string) => void;
|
|
33
|
+
error: (err: Error) => void;
|
|
34
|
+
connected: (info: {
|
|
35
|
+
path: string;
|
|
36
|
+
baudRate: number;
|
|
37
|
+
}) => void;
|
|
38
|
+
disconnected: (reason: string) => void;
|
|
39
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare const SerialStatus: {
|
|
2
|
+
readonly DISCONNECTED: "DISCONNECTED";
|
|
3
|
+
readonly SCANNING: "SCANNING";
|
|
4
|
+
readonly CONNECTING: "CONNECTING";
|
|
5
|
+
readonly CONNECTED: "CONNECTED";
|
|
6
|
+
readonly RECONNECTING: "RECONNECTING";
|
|
7
|
+
};
|
|
8
|
+
export type SerialStatus = (typeof SerialStatus)[keyof typeof SerialStatus];
|
|
9
|
+
export interface SerialConfig {
|
|
10
|
+
baudRate: number;
|
|
11
|
+
path?: string;
|
|
12
|
+
vendorId?: string;
|
|
13
|
+
productId?: string;
|
|
14
|
+
reconnectInterval: number;
|
|
15
|
+
autoConnect: boolean;
|
|
16
|
+
parser?: NodeJS.ReadWriteStream;
|
|
17
|
+
handshake?: {
|
|
18
|
+
command: string | Buffer;
|
|
19
|
+
pattern: string | RegExp;
|
|
20
|
+
timeout: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface QueueItem {
|
|
24
|
+
data: Buffer | string;
|
|
25
|
+
alias?: string;
|
|
26
|
+
timeout?: number;
|
|
27
|
+
resolve: () => void;
|
|
28
|
+
reject: (err: Error) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface SerialEvents {
|
|
31
|
+
status: (status: SerialStatus) => void;
|
|
32
|
+
data: (data: Buffer | string, alias?: string) => void;
|
|
33
|
+
error: (err: Error) => void;
|
|
34
|
+
connected: (info: {
|
|
35
|
+
path: string;
|
|
36
|
+
baudRate: number;
|
|
37
|
+
}) => void;
|
|
38
|
+
disconnected: (reason: string) => void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimized helpers for Buffer operations.
|
|
3
|
+
* Designed to minimize unnecessary copies and validations where possible.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Efficiently converts various input types to Buffer.
|
|
7
|
+
* If input is already a Buffer, returns it as is (zero-copy).
|
|
8
|
+
*/
|
|
9
|
+
export declare function toBuffer(data: string | number[] | Uint8Array | Buffer): Buffer;
|
|
10
|
+
/**
|
|
11
|
+
* Converts a Buffer to Uint8Array.
|
|
12
|
+
* In Node.js, Buffers ARE Uint8Arrays, so this is ideally zero-copy.
|
|
13
|
+
*/
|
|
14
|
+
export declare function toUint8Array(buf: Buffer): Uint8Array;
|
|
15
|
+
/**
|
|
16
|
+
* Converts a Buffer to string with optional encoding (default utf8).
|
|
17
|
+
* Wrapper alias for toString().
|
|
18
|
+
*/
|
|
19
|
+
export declare function toString(buf: Buffer, encoding?: BufferEncoding): string;
|
|
20
|
+
/**
|
|
21
|
+
* Optimally converts to ASCII.
|
|
22
|
+
*/
|
|
23
|
+
export declare function toAscii(buf: Buffer): string;
|
|
24
|
+
/**
|
|
25
|
+
* Converts a Buffer to an Array of numbers (bytes).
|
|
26
|
+
*/
|
|
27
|
+
export declare function toArray(buf: Buffer): number[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimized helpers for Buffer operations.
|
|
3
|
+
* Designed to minimize unnecessary copies and validations where possible.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Efficiently converts various input types to Buffer.
|
|
7
|
+
* If input is already a Buffer, returns it as is (zero-copy).
|
|
8
|
+
*/
|
|
9
|
+
export declare function toBuffer(data: string | number[] | Uint8Array | Buffer): Buffer;
|
|
10
|
+
/**
|
|
11
|
+
* Converts a Buffer to Uint8Array.
|
|
12
|
+
* In Node.js, Buffers ARE Uint8Arrays, so this is ideally zero-copy.
|
|
13
|
+
*/
|
|
14
|
+
export declare function toUint8Array(buf: Buffer): Uint8Array;
|
|
15
|
+
/**
|
|
16
|
+
* Converts a Buffer to string with optional encoding (default utf8).
|
|
17
|
+
* Wrapper alias for toString().
|
|
18
|
+
*/
|
|
19
|
+
export declare function toString(buf: Buffer, encoding?: BufferEncoding): string;
|
|
20
|
+
/**
|
|
21
|
+
* Optimally converts to ASCII.
|
|
22
|
+
*/
|
|
23
|
+
export declare function toAscii(buf: Buffer): string;
|
|
24
|
+
/**
|
|
25
|
+
* Converts a Buffer to an Array of numbers (bytes).
|
|
26
|
+
*/
|
|
27
|
+
export declare function toArray(buf: Buffer): number[];
|
package/package.json
CHANGED
|
@@ -1,58 +1,72 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"module": "./dist/serial-core.mjs",
|
|
25
|
-
"types": "./dist/serial-core.d.cts",
|
|
26
|
-
"files": [
|
|
27
|
-
"dist"
|
|
28
|
-
],
|
|
29
|
-
"engines": {
|
|
30
|
-
"node": ">=22"
|
|
31
|
-
},
|
|
32
|
-
"scripts": {
|
|
33
|
-
"build": "prettier --write ./lib/ && tsdown",
|
|
34
|
-
"dev": "tsdown --watch",
|
|
35
|
-
"test": "vitest",
|
|
36
|
-
"typecheck": "tsc --noEmit",
|
|
37
|
-
"prepublishOnly": "npm run build",
|
|
38
|
-
"clean": "rm -rf ./dist/",
|
|
39
|
-
"format": "prettier --write ./lib/"
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@danidoble/webserial-vending-commands": "^1.0.3",
|
|
43
|
-
"@serialport/binding-mock": "^10.2.2",
|
|
44
|
-
"@serialport/parser-byte-length": "^13.0.0",
|
|
45
|
-
"@serialport/parser-delimiter": "^13.0.0",
|
|
46
|
-
"@serialport/parser-inter-byte-timeout": "^13.0.0",
|
|
47
|
-
"@types/node": "^25.0.3",
|
|
48
|
-
"bumpp": "^10.3.2",
|
|
49
|
-
"prettier": "3.7.4",
|
|
50
|
-
"serialport": "^13.0.0",
|
|
51
|
-
"tsdown": "^0.18.4",
|
|
52
|
-
"typescript": "^5.9.3",
|
|
53
|
-
"vitest": "^4.0.16"
|
|
54
|
-
},
|
|
55
|
-
"peerDependencies": {
|
|
56
|
-
"serialport": "^13.0.0"
|
|
2
|
+
"name": "serial-core",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.2.0-dev.2",
|
|
5
|
+
"description": "Resilient serial communication service with automatic reconnection and queues.",
|
|
6
|
+
"author": "Danidoble <danidoble@gmail.com>",
|
|
7
|
+
"license": "GPL-3.0-only",
|
|
8
|
+
"homepage": "https://github.com/danidoble/serial-core#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/danidoble/serial-core.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/danidoble/serial-core/issues"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs",
|
|
23
|
+
"types": "./dist/index.d.ts"
|
|
57
24
|
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"dev": "tsdown --watch",
|
|
31
|
+
"build": "prettier --write ./src/ && tsdown",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest",
|
|
34
|
+
"prepublishOnly": "npm run build",
|
|
35
|
+
"clean": "rm -rf ./dist/",
|
|
36
|
+
"format": "prettier --write ./src/",
|
|
37
|
+
"lint": "eslint ."
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"serialport",
|
|
41
|
+
"iot",
|
|
42
|
+
"resilience",
|
|
43
|
+
"typescript",
|
|
44
|
+
"queue",
|
|
45
|
+
"handshake",
|
|
46
|
+
"auto-discovery",
|
|
47
|
+
"parser",
|
|
48
|
+
"scanner",
|
|
49
|
+
"reconnection",
|
|
50
|
+
"event-driven"
|
|
51
|
+
],
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@eslint/js": "^9.39.2",
|
|
54
|
+
"@types/node": "^20.0.0",
|
|
55
|
+
"bumpp": "^10.4.0",
|
|
56
|
+
"eslint": "^9.39.2",
|
|
57
|
+
"globals": "^17.0.0",
|
|
58
|
+
"jiti": "^2.6.1",
|
|
59
|
+
"prettier": "^3.8.0",
|
|
60
|
+
"serialport": "^13.0.0",
|
|
61
|
+
"tsdown": "^0.2.17",
|
|
62
|
+
"typescript": "^5.9.3",
|
|
63
|
+
"typescript-eslint": "^8.53.0",
|
|
64
|
+
"vitest": "^4.0.17"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"serialport": "^13.0.0"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=18.0.0"
|
|
71
|
+
}
|
|
58
72
|
}
|
package/dist/serial-core.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
let e=require(`serialport`);var t=class extends CustomEvent{constructor(e,t){super(e,t)}},n=class extends EventTarget{_listeners={debug:!1};_debug=!1;_listenersCallbacks=[];dispatch(e,n={}){let r=new t(e,{detail:n});this.dispatchEvent(r),this._debug&&this.dispatchEvent(new t(`debug`,{detail:{type:e,data:n}}))}dispatchAsync(e,t={},n=100){setTimeout(()=>this.dispatch(e,t),n)}on(e,t){this._listeners[e]!==void 0&&!this._listeners[e]&&(this._listeners[e]=!0),this._listenersCallbacks.push({key:e,callback:t}),this.addEventListener(e,t)}off(e,t){this._listenersCallbacks=this._listenersCallbacks.filter(n=>!(n.key===e&&n.callback===t)),this.removeEventListener(e,t)}serialRegisterAvailableListener(e){this._listeners[e]||(this._listeners[e]=!1)}get availableListeners(){return Object.keys(this._listeners).sort().map(e=>({type:e,listening:this._listeners[e]}))}removeAllListeners(){for(let e of this._listenersCallbacks)[`internal:queue`].includes(e.key)||(this._listenersCallbacks=this._listenersCallbacks.filter(t=>!(t.key===e.key&&t.callback===e.callback)),this.removeEventListener(e.key,e.callback));for(let e of Object.keys(this._listeners))this._listeners[e]=!1}},r=class extends n{config={path:``,baudRate:9600,dataBits:8,lock:!0,stopBits:1,parity:`none`,rtscts:!1,xon:!1,xoff:!1,xany:!1,hupcl:!0};filters={};port=null;queue=[];connectionCmd=null;timeoutAfterWrite=500;timeoutInstance=null;lastAction=null;wasConnected=!1;channelNumber=1;isConnected=!1;constructor({port:e=null}){super(),this.port=e,this.initListeners()}setFilters({vendorId:e,productId:t}){return this.filters={...this.filters,vendorId:e||null},this.filters={...this.filters,productId:t||null},this}getFilters(){return this.filters}setConfig(e){return this.config={...this.config,...e},this}getConfig(){return this.config}async getAvailablePorts(){return(await e.SerialPort.list()).filter(e=>!(!e.path||!e.vendorId||!e.productId||this.filters.vendorId&&e.vendorId.toLowerCase()!==this.filters.vendorId.toLowerCase()||this.filters.productId&&e.productId.toLowerCase()!==this.filters.productId.toLowerCase()))}getConnectionCmd(){return this.connectionCmd}initListeners(){this.on(`internal:queue`,()=>this.runQueue())}async runQueue(){this.port&&this.port.isOpen&&this.queue.length!==0&&(this.timeoutInstance||await this.write())}async onTimeout(e,t){if(!this.wasConnected&&this.lastAction===`connection:start`){this.port?.close(),this.isConnected=!1,this.wasConnected=!1;return}this.timeoutInstance&&=(clearTimeout(this.timeoutInstance),null),this.dispatch(`internal:queue`),this.dispatch(`serial:timeout`,{message:`Operation timed out`,bytes:e,action:t}),console.error(`Timeout after write`,this.lastAction)}startListeningPort(){this.port&&(this.port.open(e=>{if(!this.port)return console.log(`Port is not defined`);if(e)return console.log(`Error opening port: `,e.message);if(!this.connectionCmd)throw Error(`Connection command is not defined`);this.lastAction=`connection:start`,this.timeoutInstance=setTimeout(()=>this.onTimeout(this.connectionCmd,this.lastAction),this.timeoutAfterWrite),this.port.write(this.connectionCmd)}),this.initParser())}onDataReceived(e){this.timeoutInstance&&=(clearTimeout(this.timeoutInstance),null),this.isConnected===!1&&(this.dispatch(`serial:connected`),this.wasConnected=!0,this.isConnected=!0),this.serialMessage(e),this.dispatch(`internal:queue`)}async write(){if(!this.port||this.queue.length===0)return;let e=this.queue[0];this.queue=this.queue.slice(1),this.lastAction=e.action||null,this.timeoutInstance=setTimeout(()=>this.onTimeout(e.bytes,this.lastAction),this.timeoutAfterWrite),this.port.write(e.bytes),this.dispatch(`serial:sent`,e)}async connect(){if(!this.port||!this.port.isOpen)return await this.openPort();if(!this.connectionCmd)throw Error(`Connection command is not defined`);this.appendToQueue(this.connectionCmd,this.port?.isOpen?`connect`:`connection:start`),this.dispatch(`internal:queue`)}async disconnect(){!this.port||!this.port.isOpen||(this.port.close(),this.isConnected=!1,this.wasConnected=!1,this.dispatch(`serial:disconnected`))}async appendToQueue(e,t){if([`connect`,`connection:start`].includes(t)&&(!this.port||!this.port.isOpen))return await this.openPort();this.queue.push({bytes:e,action:t}),this.dispatch(`internal:queue`)}async openPort(){if(this.port&&this.port.isOpen)return;if(this.port&&this.port instanceof e.SerialPortMock&&!this.port.isOpen){this.startListeningPort();return}let t=[],n=this.config;if(!this.config.path){if(t=await this.getAvailablePorts(),t.length===0)throw Error(`Port path is not defined`);t.length>1&&console.warn(`Multiple ports found, using the first one:`,t),n.path=t[0].path}this.port=new e.SerialPort({...n,autoOpen:!1}),this.startListeningPort()}stringToUint8Array(e){return console.warn(`stringToUint8Array is not recommended to use`),Buffer.from(e)}uint8ArrayToHexArray(e){return Array.from(e).map(e=>e.toString(16).toLowerCase().padStart(2,`0`))}stringToArrayHex(e){return Array.from(e).map(e=>e.charCodeAt(0).toString(16))}asciiToHex(e,t=``){let n=[];for(let t=0,r=e.length;t<r;t++){let r=Number(e.charCodeAt(t)).toString(16);n.push(r)}return n.join(t)}fixHexArray(e,t=2){return e.map(e=>typeof e==`string`?e.padStart(t,`0`).toLowerCase():e.toString(16).padStart(t,`0`).toLowerCase())}};exports.Core=r;
|
package/dist/serial-core.d.cts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { SerialPort, SerialPortMock } from "serialport";
|
|
2
|
-
|
|
3
|
-
//#region lib/types/events.d.ts
|
|
4
|
-
|
|
5
|
-
interface AvailableListener {
|
|
6
|
-
type: string;
|
|
7
|
-
listening: boolean;
|
|
8
|
-
}
|
|
9
|
-
type AvailableListeners = AvailableListener[];
|
|
10
|
-
type DataType = string | number | boolean | object | null;
|
|
11
|
-
// EventListenerOrEventListenerObject is not defined in node so we define it here
|
|
12
|
-
interface EventListener {
|
|
13
|
-
(evt: Event): void;
|
|
14
|
-
}
|
|
15
|
-
interface EventListenerObject {
|
|
16
|
-
handleEvent(object: Event): void;
|
|
17
|
-
}
|
|
18
|
-
type EventListenerOrEventListenerObject = EventListener | EventListenerObject | ((event: SerialEventListener) => void);
|
|
19
|
-
interface Dispatcher$1 {
|
|
20
|
-
dispatch(type: string, data?: DataType): void;
|
|
21
|
-
dispatchAsync(type: string, data?: DataType, ms?: number): void;
|
|
22
|
-
on(type: string, callback: EventListenerOrEventListenerObject): void;
|
|
23
|
-
off(type: string, callback: EventListenerOrEventListenerObject): void;
|
|
24
|
-
serialRegisterAvailableListener(type: string): void;
|
|
25
|
-
availableListeners: AvailableListeners;
|
|
26
|
-
}
|
|
27
|
-
interface Listeners {
|
|
28
|
-
[key: string]: boolean;
|
|
29
|
-
debug: boolean;
|
|
30
|
-
}
|
|
31
|
-
interface ListenersCallbacks {
|
|
32
|
-
key: string;
|
|
33
|
-
callback: EventListenerOrEventListenerObject;
|
|
34
|
-
}
|
|
35
|
-
//#endregion
|
|
36
|
-
//#region lib/events/dispatcher.d.ts
|
|
37
|
-
declare class Dispatcher extends EventTarget implements Dispatcher$1 {
|
|
38
|
-
protected _listeners: Listeners;
|
|
39
|
-
protected _debug: boolean;
|
|
40
|
-
protected _listenersCallbacks: ListenersCallbacks[];
|
|
41
|
-
dispatch(type: string, data?: DataType): void;
|
|
42
|
-
dispatchAsync(type: string, data?: DataType, ms?: number): void;
|
|
43
|
-
on(type: string, callback: EventListenerOrEventListenerObject): void;
|
|
44
|
-
off(type: string, callback: EventListenerOrEventListenerObject): void;
|
|
45
|
-
serialRegisterAvailableListener(type: string): void;
|
|
46
|
-
get availableListeners(): AvailableListeners;
|
|
47
|
-
removeAllListeners(): void;
|
|
48
|
-
}
|
|
49
|
-
//#endregion
|
|
50
|
-
//#region lib/types/core.d.ts
|
|
51
|
-
interface CoreConfig {
|
|
52
|
-
/** The system path of the serial port you want to open. For example, `/dev/tty.XXX` on Mac/Linux, or `COM1` on Windows */
|
|
53
|
-
path: string;
|
|
54
|
-
/**
|
|
55
|
-
* The baud rate of the port to be opened. This should match one of the commonly available baud rates, such as 110, 300, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, or 115200. Custom rates are supported best effort per platform. The device connected to the serial port is not guaranteed to support the requested baud rate, even if the port itself supports that baud rate.
|
|
56
|
-
*/
|
|
57
|
-
baudRate: number;
|
|
58
|
-
/** Must be one of these: 5, 6, 7, or 8 defaults to 8 */
|
|
59
|
-
dataBits?: 5 | 6 | 7 | 8;
|
|
60
|
-
/** Prevent other processes from opening the port. Windows does not currently support `false`. Defaults to true */
|
|
61
|
-
lock?: boolean;
|
|
62
|
-
/** Must be 1, 1.5 or 2 defaults to 1 */
|
|
63
|
-
stopBits?: 1 | 1.5 | 2;
|
|
64
|
-
parity?: 'none' | 'even' | 'odd' | undefined;
|
|
65
|
-
/** Flow control Setting. Defaults to false */
|
|
66
|
-
rtscts?: boolean;
|
|
67
|
-
/** Flow control Setting. Defaults to false */
|
|
68
|
-
xon?: boolean;
|
|
69
|
-
/** Flow control Setting. Defaults to false */
|
|
70
|
-
xoff?: boolean;
|
|
71
|
-
/** Flow control Setting defaults to false*/
|
|
72
|
-
xany?: boolean;
|
|
73
|
-
/** drop DTR on close. Defaults to true */
|
|
74
|
-
hupcl?: boolean;
|
|
75
|
-
}
|
|
76
|
-
interface PortFilter {
|
|
77
|
-
vendorId?: string | null;
|
|
78
|
-
productId?: string | null;
|
|
79
|
-
}
|
|
80
|
-
interface PortInfo {
|
|
81
|
-
path: string;
|
|
82
|
-
manufacturer?: string;
|
|
83
|
-
serialNumber?: string;
|
|
84
|
-
pnpId?: string;
|
|
85
|
-
locationId?: string;
|
|
86
|
-
vendorId?: string;
|
|
87
|
-
productId?: string;
|
|
88
|
-
}
|
|
89
|
-
interface QueueItem {
|
|
90
|
-
action?: string;
|
|
91
|
-
bytes?: Uint8Array;
|
|
92
|
-
}
|
|
93
|
-
//#endregion
|
|
94
|
-
//#region lib/serial/core.d.ts
|
|
95
|
-
declare abstract class Core extends Dispatcher {
|
|
96
|
-
protected config: CoreConfig;
|
|
97
|
-
protected filters: PortFilter;
|
|
98
|
-
protected port: SerialPort | SerialPortMock | null;
|
|
99
|
-
protected queue: QueueItem[];
|
|
100
|
-
protected connectionCmd: Uint8Array | null;
|
|
101
|
-
protected timeoutAfterWrite: number;
|
|
102
|
-
protected timeoutInstance: ReturnType<typeof setTimeout> | null;
|
|
103
|
-
protected lastAction: null | string;
|
|
104
|
-
protected wasConnected: boolean;
|
|
105
|
-
channelNumber: number;
|
|
106
|
-
protected isConnected: boolean;
|
|
107
|
-
constructor({
|
|
108
|
-
port
|
|
109
|
-
}: {
|
|
110
|
-
port: SerialPort | SerialPortMock | null;
|
|
111
|
-
});
|
|
112
|
-
setFilters({
|
|
113
|
-
vendorId,
|
|
114
|
-
productId
|
|
115
|
-
}: PortFilter): Core;
|
|
116
|
-
getFilters(): PortFilter;
|
|
117
|
-
setConfig(config: CoreConfig): Core;
|
|
118
|
-
getConfig(): CoreConfig;
|
|
119
|
-
protected getAvailablePorts(): Promise<PortInfo[]>;
|
|
120
|
-
getConnectionCmd(): Uint8Array<ArrayBufferLike> | null;
|
|
121
|
-
protected initListeners(): void;
|
|
122
|
-
protected runQueue(): Promise<void>;
|
|
123
|
-
protected onTimeout(bytes: Uint8Array, event: string | null): Promise<void>;
|
|
124
|
-
protected startListeningPort(): void;
|
|
125
|
-
protected abstract initParser(): void;
|
|
126
|
-
protected onDataReceived(buffer: Buffer): void;
|
|
127
|
-
protected abstract serialMessage(buffer: Buffer): void;
|
|
128
|
-
protected write(): Promise<void>;
|
|
129
|
-
connect(): Promise<void>;
|
|
130
|
-
disconnect(): Promise<void>;
|
|
131
|
-
protected appendToQueue(bytes: Uint8Array, action: string): Promise<void>;
|
|
132
|
-
protected openPort(): Promise<void>;
|
|
133
|
-
stringToUint8Array(str: string): Uint8Array;
|
|
134
|
-
uint8ArrayToHexArray(array: Uint8Array): Array<string>;
|
|
135
|
-
stringToArrayHex(string: string): string[];
|
|
136
|
-
asciiToHex(asciiString: string, joinWith?: string): string;
|
|
137
|
-
fixHexArray(array: Array<string | number>, size?: number): string[];
|
|
138
|
-
}
|
|
139
|
-
//#endregion
|
|
140
|
-
export { Core };
|
package/dist/serial-core.d.mts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { SerialPort, SerialPortMock } from "serialport";
|
|
2
|
-
|
|
3
|
-
//#region lib/types/events.d.ts
|
|
4
|
-
|
|
5
|
-
interface AvailableListener {
|
|
6
|
-
type: string;
|
|
7
|
-
listening: boolean;
|
|
8
|
-
}
|
|
9
|
-
type AvailableListeners = AvailableListener[];
|
|
10
|
-
type DataType = string | number | boolean | object | null;
|
|
11
|
-
// EventListenerOrEventListenerObject is not defined in node so we define it here
|
|
12
|
-
interface EventListener {
|
|
13
|
-
(evt: Event): void;
|
|
14
|
-
}
|
|
15
|
-
interface EventListenerObject {
|
|
16
|
-
handleEvent(object: Event): void;
|
|
17
|
-
}
|
|
18
|
-
type EventListenerOrEventListenerObject = EventListener | EventListenerObject | ((event: SerialEventListener) => void);
|
|
19
|
-
interface Dispatcher$1 {
|
|
20
|
-
dispatch(type: string, data?: DataType): void;
|
|
21
|
-
dispatchAsync(type: string, data?: DataType, ms?: number): void;
|
|
22
|
-
on(type: string, callback: EventListenerOrEventListenerObject): void;
|
|
23
|
-
off(type: string, callback: EventListenerOrEventListenerObject): void;
|
|
24
|
-
serialRegisterAvailableListener(type: string): void;
|
|
25
|
-
availableListeners: AvailableListeners;
|
|
26
|
-
}
|
|
27
|
-
interface Listeners {
|
|
28
|
-
[key: string]: boolean;
|
|
29
|
-
debug: boolean;
|
|
30
|
-
}
|
|
31
|
-
interface ListenersCallbacks {
|
|
32
|
-
key: string;
|
|
33
|
-
callback: EventListenerOrEventListenerObject;
|
|
34
|
-
}
|
|
35
|
-
//#endregion
|
|
36
|
-
//#region lib/events/dispatcher.d.ts
|
|
37
|
-
declare class Dispatcher extends EventTarget implements Dispatcher$1 {
|
|
38
|
-
protected _listeners: Listeners;
|
|
39
|
-
protected _debug: boolean;
|
|
40
|
-
protected _listenersCallbacks: ListenersCallbacks[];
|
|
41
|
-
dispatch(type: string, data?: DataType): void;
|
|
42
|
-
dispatchAsync(type: string, data?: DataType, ms?: number): void;
|
|
43
|
-
on(type: string, callback: EventListenerOrEventListenerObject): void;
|
|
44
|
-
off(type: string, callback: EventListenerOrEventListenerObject): void;
|
|
45
|
-
serialRegisterAvailableListener(type: string): void;
|
|
46
|
-
get availableListeners(): AvailableListeners;
|
|
47
|
-
removeAllListeners(): void;
|
|
48
|
-
}
|
|
49
|
-
//#endregion
|
|
50
|
-
//#region lib/types/core.d.ts
|
|
51
|
-
interface CoreConfig {
|
|
52
|
-
/** The system path of the serial port you want to open. For example, `/dev/tty.XXX` on Mac/Linux, or `COM1` on Windows */
|
|
53
|
-
path: string;
|
|
54
|
-
/**
|
|
55
|
-
* The baud rate of the port to be opened. This should match one of the commonly available baud rates, such as 110, 300, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, or 115200. Custom rates are supported best effort per platform. The device connected to the serial port is not guaranteed to support the requested baud rate, even if the port itself supports that baud rate.
|
|
56
|
-
*/
|
|
57
|
-
baudRate: number;
|
|
58
|
-
/** Must be one of these: 5, 6, 7, or 8 defaults to 8 */
|
|
59
|
-
dataBits?: 5 | 6 | 7 | 8;
|
|
60
|
-
/** Prevent other processes from opening the port. Windows does not currently support `false`. Defaults to true */
|
|
61
|
-
lock?: boolean;
|
|
62
|
-
/** Must be 1, 1.5 or 2 defaults to 1 */
|
|
63
|
-
stopBits?: 1 | 1.5 | 2;
|
|
64
|
-
parity?: 'none' | 'even' | 'odd' | undefined;
|
|
65
|
-
/** Flow control Setting. Defaults to false */
|
|
66
|
-
rtscts?: boolean;
|
|
67
|
-
/** Flow control Setting. Defaults to false */
|
|
68
|
-
xon?: boolean;
|
|
69
|
-
/** Flow control Setting. Defaults to false */
|
|
70
|
-
xoff?: boolean;
|
|
71
|
-
/** Flow control Setting defaults to false*/
|
|
72
|
-
xany?: boolean;
|
|
73
|
-
/** drop DTR on close. Defaults to true */
|
|
74
|
-
hupcl?: boolean;
|
|
75
|
-
}
|
|
76
|
-
interface PortFilter {
|
|
77
|
-
vendorId?: string | null;
|
|
78
|
-
productId?: string | null;
|
|
79
|
-
}
|
|
80
|
-
interface PortInfo {
|
|
81
|
-
path: string;
|
|
82
|
-
manufacturer?: string;
|
|
83
|
-
serialNumber?: string;
|
|
84
|
-
pnpId?: string;
|
|
85
|
-
locationId?: string;
|
|
86
|
-
vendorId?: string;
|
|
87
|
-
productId?: string;
|
|
88
|
-
}
|
|
89
|
-
interface QueueItem {
|
|
90
|
-
action?: string;
|
|
91
|
-
bytes?: Uint8Array;
|
|
92
|
-
}
|
|
93
|
-
//#endregion
|
|
94
|
-
//#region lib/serial/core.d.ts
|
|
95
|
-
declare abstract class Core extends Dispatcher {
|
|
96
|
-
protected config: CoreConfig;
|
|
97
|
-
protected filters: PortFilter;
|
|
98
|
-
protected port: SerialPort | SerialPortMock | null;
|
|
99
|
-
protected queue: QueueItem[];
|
|
100
|
-
protected connectionCmd: Uint8Array | null;
|
|
101
|
-
protected timeoutAfterWrite: number;
|
|
102
|
-
protected timeoutInstance: ReturnType<typeof setTimeout> | null;
|
|
103
|
-
protected lastAction: null | string;
|
|
104
|
-
protected wasConnected: boolean;
|
|
105
|
-
channelNumber: number;
|
|
106
|
-
protected isConnected: boolean;
|
|
107
|
-
constructor({
|
|
108
|
-
port
|
|
109
|
-
}: {
|
|
110
|
-
port: SerialPort | SerialPortMock | null;
|
|
111
|
-
});
|
|
112
|
-
setFilters({
|
|
113
|
-
vendorId,
|
|
114
|
-
productId
|
|
115
|
-
}: PortFilter): Core;
|
|
116
|
-
getFilters(): PortFilter;
|
|
117
|
-
setConfig(config: CoreConfig): Core;
|
|
118
|
-
getConfig(): CoreConfig;
|
|
119
|
-
protected getAvailablePorts(): Promise<PortInfo[]>;
|
|
120
|
-
getConnectionCmd(): Uint8Array<ArrayBufferLike> | null;
|
|
121
|
-
protected initListeners(): void;
|
|
122
|
-
protected runQueue(): Promise<void>;
|
|
123
|
-
protected onTimeout(bytes: Uint8Array, event: string | null): Promise<void>;
|
|
124
|
-
protected startListeningPort(): void;
|
|
125
|
-
protected abstract initParser(): void;
|
|
126
|
-
protected onDataReceived(buffer: Buffer): void;
|
|
127
|
-
protected abstract serialMessage(buffer: Buffer): void;
|
|
128
|
-
protected write(): Promise<void>;
|
|
129
|
-
connect(): Promise<void>;
|
|
130
|
-
disconnect(): Promise<void>;
|
|
131
|
-
protected appendToQueue(bytes: Uint8Array, action: string): Promise<void>;
|
|
132
|
-
protected openPort(): Promise<void>;
|
|
133
|
-
stringToUint8Array(str: string): Uint8Array;
|
|
134
|
-
uint8ArrayToHexArray(array: Uint8Array): Array<string>;
|
|
135
|
-
stringToArrayHex(string: string): string[];
|
|
136
|
-
asciiToHex(asciiString: string, joinWith?: string): string;
|
|
137
|
-
fixHexArray(array: Array<string | number>, size?: number): string[];
|
|
138
|
-
}
|
|
139
|
-
//#endregion
|
|
140
|
-
export { Core };
|
package/dist/serial-core.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{SerialPort as e,SerialPortMock as t}from"serialport";var n=class extends CustomEvent{constructor(e,t){super(e,t)}},r=class extends EventTarget{_listeners={debug:!1};_debug=!1;_listenersCallbacks=[];dispatch(e,t={}){let r=new n(e,{detail:t});this.dispatchEvent(r),this._debug&&this.dispatchEvent(new n(`debug`,{detail:{type:e,data:t}}))}dispatchAsync(e,t={},n=100){setTimeout(()=>this.dispatch(e,t),n)}on(e,t){this._listeners[e]!==void 0&&!this._listeners[e]&&(this._listeners[e]=!0),this._listenersCallbacks.push({key:e,callback:t}),this.addEventListener(e,t)}off(e,t){this._listenersCallbacks=this._listenersCallbacks.filter(n=>!(n.key===e&&n.callback===t)),this.removeEventListener(e,t)}serialRegisterAvailableListener(e){this._listeners[e]||(this._listeners[e]=!1)}get availableListeners(){return Object.keys(this._listeners).sort().map(e=>({type:e,listening:this._listeners[e]}))}removeAllListeners(){for(let e of this._listenersCallbacks)[`internal:queue`].includes(e.key)||(this._listenersCallbacks=this._listenersCallbacks.filter(t=>!(t.key===e.key&&t.callback===e.callback)),this.removeEventListener(e.key,e.callback));for(let e of Object.keys(this._listeners))this._listeners[e]=!1}},i=class extends r{config={path:``,baudRate:9600,dataBits:8,lock:!0,stopBits:1,parity:`none`,rtscts:!1,xon:!1,xoff:!1,xany:!1,hupcl:!0};filters={};port=null;queue=[];connectionCmd=null;timeoutAfterWrite=500;timeoutInstance=null;lastAction=null;wasConnected=!1;channelNumber=1;isConnected=!1;constructor({port:e=null}){super(),this.port=e,this.initListeners()}setFilters({vendorId:e,productId:t}){return this.filters={...this.filters,vendorId:e||null},this.filters={...this.filters,productId:t||null},this}getFilters(){return this.filters}setConfig(e){return this.config={...this.config,...e},this}getConfig(){return this.config}async getAvailablePorts(){return(await e.list()).filter(e=>!(!e.path||!e.vendorId||!e.productId||this.filters.vendorId&&e.vendorId.toLowerCase()!==this.filters.vendorId.toLowerCase()||this.filters.productId&&e.productId.toLowerCase()!==this.filters.productId.toLowerCase()))}getConnectionCmd(){return this.connectionCmd}initListeners(){this.on(`internal:queue`,()=>this.runQueue())}async runQueue(){this.port&&this.port.isOpen&&this.queue.length!==0&&(this.timeoutInstance||await this.write())}async onTimeout(e,t){if(!this.wasConnected&&this.lastAction===`connection:start`){this.port?.close(),this.isConnected=!1,this.wasConnected=!1;return}this.timeoutInstance&&=(clearTimeout(this.timeoutInstance),null),this.dispatch(`internal:queue`),this.dispatch(`serial:timeout`,{message:`Operation timed out`,bytes:e,action:t}),console.error(`Timeout after write`,this.lastAction)}startListeningPort(){this.port&&(this.port.open(e=>{if(!this.port)return console.log(`Port is not defined`);if(e)return console.log(`Error opening port: `,e.message);if(!this.connectionCmd)throw Error(`Connection command is not defined`);this.lastAction=`connection:start`,this.timeoutInstance=setTimeout(()=>this.onTimeout(this.connectionCmd,this.lastAction),this.timeoutAfterWrite),this.port.write(this.connectionCmd)}),this.initParser())}onDataReceived(e){this.timeoutInstance&&=(clearTimeout(this.timeoutInstance),null),this.isConnected===!1&&(this.dispatch(`serial:connected`),this.wasConnected=!0,this.isConnected=!0),this.serialMessage(e),this.dispatch(`internal:queue`)}async write(){if(!this.port||this.queue.length===0)return;let e=this.queue[0];this.queue=this.queue.slice(1),this.lastAction=e.action||null,this.timeoutInstance=setTimeout(()=>this.onTimeout(e.bytes,this.lastAction),this.timeoutAfterWrite),this.port.write(e.bytes),this.dispatch(`serial:sent`,e)}async connect(){if(!this.port||!this.port.isOpen)return await this.openPort();if(!this.connectionCmd)throw Error(`Connection command is not defined`);this.appendToQueue(this.connectionCmd,this.port?.isOpen?`connect`:`connection:start`),this.dispatch(`internal:queue`)}async disconnect(){!this.port||!this.port.isOpen||(this.port.close(),this.isConnected=!1,this.wasConnected=!1,this.dispatch(`serial:disconnected`))}async appendToQueue(e,t){if([`connect`,`connection:start`].includes(t)&&(!this.port||!this.port.isOpen))return await this.openPort();this.queue.push({bytes:e,action:t}),this.dispatch(`internal:queue`)}async openPort(){if(this.port&&this.port.isOpen)return;if(this.port&&this.port instanceof t&&!this.port.isOpen){this.startListeningPort();return}let n=[],r=this.config;if(!this.config.path){if(n=await this.getAvailablePorts(),n.length===0)throw Error(`Port path is not defined`);n.length>1&&console.warn(`Multiple ports found, using the first one:`,n),r.path=n[0].path}this.port=new e({...r,autoOpen:!1}),this.startListeningPort()}stringToUint8Array(e){return console.warn(`stringToUint8Array is not recommended to use`),Buffer.from(e)}uint8ArrayToHexArray(e){return Array.from(e).map(e=>e.toString(16).toLowerCase().padStart(2,`0`))}stringToArrayHex(e){return Array.from(e).map(e=>e.charCodeAt(0).toString(16))}asciiToHex(e,t=``){let n=[];for(let t=0,r=e.length;t<r;t++){let r=Number(e.charCodeAt(t)).toString(16);n.push(r)}return n.join(t)}fixHexArray(e,t=2){return e.map(e=>typeof e==`string`?e.padStart(t,`0`).toLowerCase():e.toString(16).padStart(t,`0`).toLowerCase())}};export{i as Core};
|