serial-core 0.1.0 → 0.2.0-dev.1
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
package/LICENSE.md
CHANGED
|
@@ -7,11 +7,11 @@ the Free Software Foundation, either version 3 of the License, or
|
|
|
7
7
|
|
|
8
8
|
This program is distributed in the hope that it will be useful,
|
|
9
9
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11
11
|
GNU General Public License for more details.
|
|
12
12
|
|
|
13
13
|
You should have received a copy of the GNU General Public License
|
|
14
|
-
along with this program.
|
|
14
|
+
along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
15
|
|
|
16
16
|
# GNU GENERAL PUBLIC LICENSE
|
|
17
17
|
|
|
@@ -232,23 +232,23 @@ produce it from the Program, in the form of source code under the
|
|
|
232
232
|
terms of section 4, provided that you also meet all of these
|
|
233
233
|
conditions:
|
|
234
234
|
|
|
235
|
-
-
|
|
236
|
-
|
|
237
|
-
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
235
|
+
- a) The work must carry prominent notices stating that you modified
|
|
236
|
+
it, and giving a relevant date.
|
|
237
|
+
- b) The work must carry prominent notices stating that it is
|
|
238
|
+
released under this License and any conditions added under
|
|
239
|
+
section 7. This requirement modifies the requirement in section 4
|
|
240
|
+
to "keep intact all notices".
|
|
241
|
+
- c) You must license the entire work, as a whole, under this
|
|
242
|
+
License to anyone who comes into possession of a copy. This
|
|
243
|
+
License will therefore apply, along with any applicable section 7
|
|
244
|
+
additional terms, to the whole of the work, and all its parts,
|
|
245
|
+
regardless of how they are packaged. This License gives no
|
|
246
|
+
permission to license the work in any other way, but it does not
|
|
247
|
+
invalidate such permission if you have separately received it.
|
|
248
|
+
- d) If the work has interactive user interfaces, each must display
|
|
249
|
+
Appropriate Legal Notices; however, if the Program has interactive
|
|
250
|
+
interfaces that do not display Appropriate Legal Notices, your
|
|
251
|
+
work need not make them do so.
|
|
252
252
|
|
|
253
253
|
A compilation of a covered work with other separate and independent
|
|
254
254
|
works, which are not by their nature extensions of the covered work,
|
|
@@ -267,42 +267,42 @@ sections 4 and 5, provided that you also convey the machine-readable
|
|
|
267
267
|
Corresponding Source under the terms of this License, in one of these
|
|
268
268
|
ways:
|
|
269
269
|
|
|
270
|
-
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
270
|
+
- a) Convey the object code in, or embodied in, a physical product
|
|
271
|
+
(including a physical distribution medium), accompanied by the
|
|
272
|
+
Corresponding Source fixed on a durable physical medium
|
|
273
|
+
customarily used for software interchange.
|
|
274
|
+
- b) Convey the object code in, or embodied in, a physical product
|
|
275
|
+
(including a physical distribution medium), accompanied by a
|
|
276
|
+
written offer, valid for at least three years and valid for as
|
|
277
|
+
long as you offer spare parts or customer support for that product
|
|
278
|
+
model, to give anyone who possesses the object code either (1) a
|
|
279
|
+
copy of the Corresponding Source for all the software in the
|
|
280
|
+
product that is covered by this License, on a durable physical
|
|
281
|
+
medium customarily used for software interchange, for a price no
|
|
282
|
+
more than your reasonable cost of physically performing this
|
|
283
|
+
conveying of source, or (2) access to copy the Corresponding
|
|
284
|
+
Source from a network server at no charge.
|
|
285
|
+
- c) Convey individual copies of the object code with a copy of the
|
|
286
|
+
written offer to provide the Corresponding Source. This
|
|
287
|
+
alternative is allowed only occasionally and noncommercially, and
|
|
288
|
+
only if you received the object code with such an offer, in accord
|
|
289
|
+
with subsection 6b.
|
|
290
|
+
- d) Convey the object code by offering access from a designated
|
|
291
|
+
place (gratis or for a charge), and offer equivalent access to the
|
|
292
|
+
Corresponding Source in the same way through the same place at no
|
|
293
|
+
further charge. You need not require recipients to copy the
|
|
294
|
+
Corresponding Source along with the object code. If the place to
|
|
295
|
+
copy the object code is a network server, the Corresponding Source
|
|
296
|
+
may be on a different server (operated by you or a third party)
|
|
297
|
+
that supports equivalent copying facilities, provided you maintain
|
|
298
|
+
clear directions next to the object code saying where to find the
|
|
299
|
+
Corresponding Source. Regardless of what server hosts the
|
|
300
|
+
Corresponding Source, you remain obligated to ensure that it is
|
|
301
|
+
available for as long as needed to satisfy these requirements.
|
|
302
|
+
- e) Convey the object code using peer-to-peer transmission,
|
|
303
|
+
provided you inform other peers where the object code and
|
|
304
|
+
Corresponding Source of the work are being offered to the general
|
|
305
|
+
public at no charge under subsection 6d.
|
|
306
306
|
|
|
307
307
|
A separable portion of the object code, whose source code is excluded
|
|
308
308
|
from the Corresponding Source as a System Library, need not be
|
|
@@ -378,23 +378,23 @@ Notwithstanding any other provision of this License, for material you
|
|
|
378
378
|
add to a covered work, you may (if authorized by the copyright holders
|
|
379
379
|
of that material) supplement the terms of this License with terms:
|
|
380
380
|
|
|
381
|
-
-
|
|
382
|
-
|
|
383
|
-
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
-
|
|
390
|
-
|
|
391
|
-
-
|
|
392
|
-
|
|
393
|
-
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
381
|
+
- a) Disclaiming warranty or limiting liability differently from the
|
|
382
|
+
terms of sections 15 and 16 of this License; or
|
|
383
|
+
- b) Requiring preservation of specified reasonable legal notices or
|
|
384
|
+
author attributions in that material or in the Appropriate Legal
|
|
385
|
+
Notices displayed by works containing it; or
|
|
386
|
+
- c) Prohibiting misrepresentation of the origin of that material,
|
|
387
|
+
or requiring that modified versions of such material be marked in
|
|
388
|
+
reasonable ways as different from the original version; or
|
|
389
|
+
- d) Limiting the use for publicity purposes of names of licensors
|
|
390
|
+
or authors of the material; or
|
|
391
|
+
- e) Declining to grant rights under trademark law for use of some
|
|
392
|
+
trade names, trademarks, or service marks; or
|
|
393
|
+
- f) Requiring indemnification of licensors and authors of that
|
|
394
|
+
material by anyone who conveys the material (or modified versions
|
|
395
|
+
of it) with contractual assumptions of liability to the recipient,
|
|
396
|
+
for any liability that these contractual assumptions directly
|
|
397
|
+
impose on those licensors and authors.
|
|
398
398
|
|
|
399
399
|
All other non-permissive additional terms are considered "further
|
|
400
400
|
restrictions" within the meaning of section 10. If the Program as you
|
|
@@ -687,4 +687,4 @@ program into proprietary programs. If your program is a subroutine
|
|
|
687
687
|
library, you may consider it more useful to permit linking proprietary
|
|
688
688
|
applications with the library. If this is what you want to do, use the
|
|
689
689
|
GNU Lesser General Public License instead of this License. But first,
|
|
690
|
-
please read <https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
690
|
+
please read <https://www.gnu.org/licenses/why-not-lgpl.html>.
|
package/README.md
CHANGED
|
@@ -1,154 +1,126 @@
|
|
|
1
1
|
# serial-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Resilient serial communication service with automatic reconnection and queues.
|
|
4
4
|
|
|
5
|
-
## Development
|
|
6
5
|
|
|
7
|
-
-
|
|
6
|
+
`serial-core` is a robust TypeScript library designed to manage serial port communications in Node.js applications. It provides a resilient architecture with built-in features for automatic reconnection, message queuing, and device scanning.
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```
|
|
8
|
+
> [!WARNING]
|
|
9
|
+
> This library is currently in active development. The API is subject to change at any time before the first stable release (v1.0.0).
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
## Features
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
- **Resilience:** Automatically handles connection drops and attempts to reconnect.
|
|
14
|
+
- **Message Queuing:** Internal queue manager ensures data is sent sequentially and handles backpressure.
|
|
15
|
+
- **Auto-Discovery:** Can scan for devices by Vendor ID (VID) and Product ID (PID) if a specific path is not provided.
|
|
16
|
+
- **Handshake Support:** Built-in mechanism to verify device connection with a handshake command and response pattern.
|
|
17
|
+
- **TypeScript Support:** Written in TypeScript with full type definitions included.
|
|
18
|
+
- **Event-Driven:** Extends `EventEmitter` to provide real-time updates on connection status and incoming data.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
## Installation
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
|
-
npm
|
|
23
|
+
npm install serial-core serialport
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
### Using the Core Abstract Class
|
|
28
|
-
|
|
29
|
-
The `Core` class is an abstract base class that provides the foundation for working with serial ports. To use it, you need to create a class that extends `Core` and implement the abstract methods.
|
|
30
|
-
|
|
31
|
-
#### Basic Setup
|
|
32
|
-
|
|
33
|
-
1. **Create a class that extends Core:**
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
import { Core } from './lib/serial/core';
|
|
37
|
-
import { SerialPort } from 'serialport';
|
|
38
|
-
|
|
39
|
-
class MySerialDevice extends Core {
|
|
40
|
-
constructor() {
|
|
41
|
-
super({ port: null });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Implement abstract methods
|
|
45
|
-
protected initParser(): void {
|
|
46
|
-
// Initialize your data parser here
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
protected serialMessage(buffer: Buffer): void {
|
|
50
|
-
// Handle incoming serial data here
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
```
|
|
26
|
+
*Note: `serialport` is a peer dependency.*
|
|
54
27
|
|
|
55
|
-
|
|
28
|
+
## Usage
|
|
56
29
|
|
|
57
|
-
|
|
30
|
+
Here is a basic example of how to use `SerialService`:
|
|
58
31
|
|
|
59
32
|
```typescript
|
|
60
|
-
|
|
33
|
+
import { SerialService, SerialStatus } from 'serial-core';
|
|
61
34
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
35
|
+
// Configuration
|
|
36
|
+
const config = {
|
|
37
|
+
// Option 1: Direct path
|
|
38
|
+
path: '/dev/ttyUSB0',
|
|
39
|
+
|
|
40
|
+
// Option 2: Auto-discovery (used if path is not set)
|
|
41
|
+
// vendorId: '1234',
|
|
42
|
+
// productId: '5678',
|
|
67
43
|
|
|
68
|
-
// Set port configuration
|
|
69
|
-
device.setConfig({
|
|
70
|
-
path: '/dev/ttyUSB0', // Port path (optional if using filters)
|
|
71
44
|
baudRate: 9600,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
45
|
+
autoConnect: true,
|
|
46
|
+
reconnectInterval: 5000,
|
|
47
|
+
|
|
48
|
+
// Optional: Verify connection
|
|
49
|
+
// handshake: {
|
|
50
|
+
// command: 'PING\n',
|
|
51
|
+
// pattern: 'PONG',
|
|
52
|
+
// timeout: 2000
|
|
53
|
+
// }
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Initialize Service
|
|
57
|
+
const serial = new SerialService(config);
|
|
58
|
+
|
|
59
|
+
// Listen to Status Changes
|
|
60
|
+
serial.on('status', (status: SerialStatus) => {
|
|
61
|
+
console.log(`Serial Status: ${status}`);
|
|
62
|
+
// Possible values: DISCONNECTED, SCANNING, CONNECTING, CONNECTED, RECONNECTING
|
|
75
63
|
});
|
|
76
|
-
```
|
|
77
64
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- `disconnect()`: Closes the serial port and disconnects
|
|
83
|
-
|
|
84
|
-
**Configuration:**
|
|
85
|
-
- `setConfig(config)`: Set port configuration options
|
|
86
|
-
- `getConfig()`: Get current port configuration
|
|
87
|
-
- `setFilters(filters)`: Set vendor/product ID filters for auto-discovery
|
|
88
|
-
- `getFilters()`: Get current filters
|
|
89
|
-
|
|
90
|
-
**Data Transmission:**
|
|
91
|
-
- `appendToQueue(bytes, action)`: Add data to the send queue
|
|
92
|
-
- `write()`: Send queued data
|
|
93
|
-
|
|
94
|
-
**Utility Methods:**
|
|
95
|
-
- `uint8ArrayToHexArray(array)`: Convert Uint8Array to hex array
|
|
96
|
-
- `stringToArrayHex(string)`: Convert string to hex array
|
|
97
|
-
- `asciiToHex(asciiString, joinWith)`: Convert ASCII to hex
|
|
98
|
-
- `fixHexArray(array, size)`: Normalize hex array values
|
|
99
|
-
- `getConnectionCmd()`: Get the connection command bytes
|
|
100
|
-
|
|
101
|
-
#### Example Usage
|
|
65
|
+
// Listen to Incoming Data
|
|
66
|
+
serial.on('data', (data: any) => {
|
|
67
|
+
console.log('Received:', data.toString());
|
|
68
|
+
});
|
|
102
69
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
70
|
+
// Listen to Errors
|
|
71
|
+
serial.on('error', (err: Error) => {
|
|
72
|
+
console.error('Serial Error:', err.message);
|
|
73
|
+
});
|
|
108
74
|
|
|
109
|
-
|
|
110
|
-
|
|
75
|
+
// Send Data
|
|
76
|
+
async function sendCommand() {
|
|
77
|
+
try {
|
|
78
|
+
// Sends data using the internal queue
|
|
79
|
+
await serial.send('HELLO_WORLD\n');
|
|
80
|
+
console.log('Message sent successfully');
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Failed to send:', error);
|
|
111
83
|
}
|
|
112
84
|
}
|
|
113
85
|
|
|
114
|
-
|
|
115
|
-
|
|
86
|
+
// Manual Control
|
|
87
|
+
// serial.connect();
|
|
88
|
+
// serial.disconnect();
|
|
89
|
+
```
|
|
116
90
|
|
|
117
|
-
|
|
118
|
-
arduino.setFilters({
|
|
119
|
-
vendorId: 'arduino_vendor_id'
|
|
120
|
-
});
|
|
91
|
+
## API Reference
|
|
121
92
|
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
await arduino.connect();
|
|
125
|
-
console.log('Connected!');
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.error('Failed to connect:', error);
|
|
128
|
-
}
|
|
93
|
+
### `SerialService`
|
|
129
94
|
|
|
130
|
-
|
|
131
|
-
arduino.on('serial:connected', () => {
|
|
132
|
-
console.log('Device connected');
|
|
133
|
-
});
|
|
95
|
+
The main class for managing the serial connection.
|
|
134
96
|
|
|
135
|
-
|
|
136
|
-
console.log('Data sent:', data);
|
|
137
|
-
});
|
|
97
|
+
#### Configuration (`SerialConfig`)
|
|
138
98
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
99
|
+
| Property | Type | Description |
|
|
100
|
+
|----------|------|-------------|
|
|
101
|
+
| `path?` | `string` | The system path to the serial port (e.g., `/dev/ttyUSB0`). |
|
|
102
|
+
| `vendorId?` | `string` | USB Vendor ID for auto-discovery (if `path` is omitted). |
|
|
103
|
+
| `productId?` | `string` | USB Product ID for auto-discovery. |
|
|
104
|
+
| `baudRate` | `number` | Serial baud rate (default: `9600`). |
|
|
105
|
+
| `autoConnect` | `boolean` | Whether to connect automatically on instantiation. |
|
|
106
|
+
| `reconnectInterval` | `number` | Time in ms to wait before retrying connection. |
|
|
107
|
+
| `handshake?` | `object` | Optional handshake configuration to verify device identity. |
|
|
142
108
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
109
|
+
#### Methods
|
|
110
|
+
|
|
111
|
+
- **`connect(): Promise<void>`**: Initiates the connection process (scanning -> connecting -> handshake -> connected).
|
|
112
|
+
- **`disconnect(): Promise<void>`**: Manually closes the connection and stops reconnection attempts.
|
|
113
|
+
- **`send(data: string | Buffer, options?): Promise<void>`**: Queues data to be sent to the device.
|
|
114
|
+
- **`status`: `SerialStatus`**: Getter for the current connection state.
|
|
147
115
|
|
|
148
116
|
#### Events
|
|
149
117
|
|
|
150
|
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
118
|
+
- **`status`**: Emitted when the connection status changes.
|
|
119
|
+
- **`connected`**: Emitted when a connection is successfully established (and handshake passes).
|
|
120
|
+
- **`disconnected`**: Emitted when the connection is lost or closed.
|
|
121
|
+
- **`data`**: Emitted when data is received from the device.
|
|
122
|
+
- **`error`**: Emitted when an error occurs.
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
GPL-3.0-only
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { SerialPort } from 'serialport';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import type { SerialStatus as SerialStatusType, SerialConfig } from './types.ts';
|
|
4
|
+
import { SerialStatus } from './types.ts';
|
|
5
|
+
import { QueueManager } from './core/QueueManager.ts';
|
|
6
|
+
/**
|
|
7
|
+
* Main service. Implements a robust state machine.
|
|
8
|
+
* Extends EventEmitter to notify the rest of the app about changes.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SerialService extends EventEmitter {
|
|
11
|
+
protected port: SerialPort | null;
|
|
12
|
+
protected queue: QueueManager;
|
|
13
|
+
protected _status: SerialStatusType;
|
|
14
|
+
protected config: SerialConfig;
|
|
15
|
+
protected intentionalDisconnect: boolean;
|
|
16
|
+
protected reconnectTimer: NodeJS.Timeout | null;
|
|
17
|
+
constructor(config: SerialConfig);
|
|
18
|
+
get status(): SerialStatus;
|
|
19
|
+
/**
|
|
20
|
+
* Updates status and emits the corresponding event.
|
|
21
|
+
*/
|
|
22
|
+
protected setStatus(newStatus: SerialStatus): void;
|
|
23
|
+
/**
|
|
24
|
+
* Starts the connection process.
|
|
25
|
+
* Handles both direct connection (known path) and scanning (VID/PID).
|
|
26
|
+
*/
|
|
27
|
+
connect(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Physically opens the port and configures listeners.
|
|
30
|
+
*/
|
|
31
|
+
protected openPort(path: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Manual disconnection.
|
|
34
|
+
*/
|
|
35
|
+
disconnect(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Public method to send data. Uses internal queue.
|
|
38
|
+
*/
|
|
39
|
+
send(data: string | Buffer, options?: {
|
|
40
|
+
alias?: string;
|
|
41
|
+
timeout?: number;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Low-level write logic, handled by QueueManager.
|
|
45
|
+
* Handles backpressure (drain).
|
|
46
|
+
*/
|
|
47
|
+
protected performWrite(data: string | Buffer): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Centralized connection failure handling.
|
|
50
|
+
*/
|
|
51
|
+
protected handleConnectionFailure(reason: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Schedules next connection attempt with simple backoff.
|
|
54
|
+
*/
|
|
55
|
+
protected scheduleReconnect(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Resource cleanup.
|
|
58
|
+
*/
|
|
59
|
+
protected cleanup(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Executes connection handshake.
|
|
62
|
+
*/
|
|
63
|
+
protected performHandshake(): void;
|
|
64
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { SerialPort } from 'serialport';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import type { SerialStatus as SerialStatusType, SerialConfig } from './types.ts';
|
|
4
|
+
import { SerialStatus } from './types.ts';
|
|
5
|
+
import { QueueManager } from './core/QueueManager.ts';
|
|
6
|
+
/**
|
|
7
|
+
* Main service. Implements a robust state machine.
|
|
8
|
+
* Extends EventEmitter to notify the rest of the app about changes.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SerialService extends EventEmitter {
|
|
11
|
+
protected port: SerialPort | null;
|
|
12
|
+
protected queue: QueueManager;
|
|
13
|
+
protected _status: SerialStatusType;
|
|
14
|
+
protected config: SerialConfig;
|
|
15
|
+
protected intentionalDisconnect: boolean;
|
|
16
|
+
protected reconnectTimer: NodeJS.Timeout | null;
|
|
17
|
+
constructor(config: SerialConfig);
|
|
18
|
+
get status(): SerialStatus;
|
|
19
|
+
/**
|
|
20
|
+
* Updates status and emits the corresponding event.
|
|
21
|
+
*/
|
|
22
|
+
protected setStatus(newStatus: SerialStatus): void;
|
|
23
|
+
/**
|
|
24
|
+
* Starts the connection process.
|
|
25
|
+
* Handles both direct connection (known path) and scanning (VID/PID).
|
|
26
|
+
*/
|
|
27
|
+
connect(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Physically opens the port and configures listeners.
|
|
30
|
+
*/
|
|
31
|
+
protected openPort(path: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Manual disconnection.
|
|
34
|
+
*/
|
|
35
|
+
disconnect(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Public method to send data. Uses internal queue.
|
|
38
|
+
*/
|
|
39
|
+
send(data: string | Buffer, options?: {
|
|
40
|
+
alias?: string;
|
|
41
|
+
timeout?: number;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Low-level write logic, handled by QueueManager.
|
|
45
|
+
* Handles backpressure (drain).
|
|
46
|
+
*/
|
|
47
|
+
protected performWrite(data: string | Buffer): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Centralized connection failure handling.
|
|
50
|
+
*/
|
|
51
|
+
protected handleConnectionFailure(reason: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Schedules next connection attempt with simple backoff.
|
|
54
|
+
*/
|
|
55
|
+
protected scheduleReconnect(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Resource cleanup.
|
|
58
|
+
*/
|
|
59
|
+
protected cleanup(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Executes connection handshake.
|
|
62
|
+
*/
|
|
63
|
+
protected performHandshake(): void;
|
|
64
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singleton that keeps track of which serial ports are in use
|
|
3
|
+
* by this application. Prevents conflicts if multiple SerialServices
|
|
4
|
+
* are instantiated for the same port.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PortRegistry {
|
|
7
|
+
private static instance;
|
|
8
|
+
private lockedPorts;
|
|
9
|
+
private constructor();
|
|
10
|
+
static getInstance(): PortRegistry;
|
|
11
|
+
/**
|
|
12
|
+
* Attempts to register (lock) exclusive use of a port.
|
|
13
|
+
* Returns true if registered successfully, false if already occupied.
|
|
14
|
+
*/
|
|
15
|
+
register(path: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Releases the port so it can be used again.
|
|
18
|
+
*/
|
|
19
|
+
unregister(path: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a port is currently in use by the library.
|
|
22
|
+
*/
|
|
23
|
+
isLocked(path: string): boolean;
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singleton that keeps track of which serial ports are in use
|
|
3
|
+
* by this application. Prevents conflicts if multiple SerialServices
|
|
4
|
+
* are instantiated for the same port.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PortRegistry {
|
|
7
|
+
private static instance;
|
|
8
|
+
private lockedPorts;
|
|
9
|
+
private constructor();
|
|
10
|
+
static getInstance(): PortRegistry;
|
|
11
|
+
/**
|
|
12
|
+
* Attempts to register (lock) exclusive use of a port.
|
|
13
|
+
* Returns true if registered successfully, false if already occupied.
|
|
14
|
+
*/
|
|
15
|
+
register(path: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Releases the port so it can be used again.
|
|
18
|
+
*/
|
|
19
|
+
unregister(path: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a port is currently in use by the library.
|
|
22
|
+
*/
|
|
23
|
+
isLocked(path: string): boolean;
|
|
24
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility static class to find devices.
|
|
3
|
+
* Solves the problem of "on which COM/tty is my Arduino?".
|
|
4
|
+
*/
|
|
5
|
+
export declare class PortScanner {
|
|
6
|
+
/**
|
|
7
|
+
* Search for a port matching the provided VID and/or PID.
|
|
8
|
+
* Normalizes strings to avoid case sensitivity issues.
|
|
9
|
+
*/
|
|
10
|
+
static findPort(vendorId?: string, productId?: string): Promise<string | null>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility static class to find devices.
|
|
3
|
+
* Solves the problem of "on which COM/tty is my Arduino?".
|
|
4
|
+
*/
|
|
5
|
+
export declare class PortScanner {
|
|
6
|
+
/**
|
|
7
|
+
* Search for a port matching the provided VID and/or PID.
|
|
8
|
+
* Normalizes strings to avoid case sensitivity issues.
|
|
9
|
+
*/
|
|
10
|
+
static findPort(vendorId?: string, productId?: string): Promise<string | null>;
|
|
11
|
+
}
|
|
@@ -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
|
+
}
|