secs4js 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE.md +20 -20
  2. package/README.md +442 -442
  3. package/lib/core/AbstractSecsCommunicator.d.ts +4 -0
  4. package/lib/core/AbstractSecsCommunicator.d.ts.map +1 -1
  5. package/lib/core/AbstractSecsCommunicator.js +70 -5
  6. package/lib/core/AbstractSecsCommunicator.js.map +1 -1
  7. package/lib/core/AbstractSecsMessage.js.map +1 -1
  8. package/lib/core/enums/HsmsSsControlType.js.map +1 -1
  9. package/lib/core/enums/RejectReason.js.map +1 -1
  10. package/lib/core/enums/SecsItemType.js.map +1 -1
  11. package/lib/core/enums/SelectStatus.js.map +1 -1
  12. package/lib/core/secs2item/AbstractSecs2Item.js.map +1 -1
  13. package/lib/core/secs2item/Secs2ItemAscii.js.map +1 -1
  14. package/lib/core/secs2item/Secs2ItemBinary.js.map +1 -1
  15. package/lib/core/secs2item/Secs2ItemBoolean.js.map +1 -1
  16. package/lib/core/secs2item/Secs2ItemFactory.js.map +1 -1
  17. package/lib/core/secs2item/Secs2ItemList.js.map +1 -1
  18. package/lib/core/secs2item/Secs2ItemNumeric.js.map +1 -1
  19. package/lib/core/secs2item/Secs2ItemParser.js.map +1 -1
  20. package/lib/gem/Clock.js.map +1 -1
  21. package/lib/gem/Gem.js.map +1 -1
  22. package/lib/helper/Secs2ItemHelper.js.map +1 -1
  23. package/lib/hsms/HsmsActiveCommunicator.d.ts.map +1 -1
  24. package/lib/hsms/HsmsActiveCommunicator.js +21 -5
  25. package/lib/hsms/HsmsActiveCommunicator.js.map +1 -1
  26. package/lib/hsms/HsmsCommunicator.d.ts +1 -0
  27. package/lib/hsms/HsmsCommunicator.d.ts.map +1 -1
  28. package/lib/hsms/HsmsCommunicator.js +72 -7
  29. package/lib/hsms/HsmsCommunicator.js.map +1 -1
  30. package/lib/hsms/HsmsMessage.js.map +1 -1
  31. package/lib/hsms/HsmsPassiveCommunicator.js.map +1 -1
  32. package/lib/hsms/enums/HsmsControlType.js.map +1 -1
  33. package/lib/hsms/enums/RejectReason.js.map +1 -1
  34. package/lib/hsms/enums/SelectStatus.js.map +1 -1
  35. package/lib/index.d.ts +2 -1
  36. package/lib/index.js +2 -1
  37. package/lib/logging/SecsLogger.d.ts +36 -0
  38. package/lib/logging/SecsLogger.d.ts.map +1 -0
  39. package/lib/logging/SecsLogger.js +415 -0
  40. package/lib/logging/SecsLogger.js.map +1 -0
  41. package/lib/secs1/Secs1Communicator.d.ts +1 -0
  42. package/lib/secs1/Secs1Communicator.d.ts.map +1 -1
  43. package/lib/secs1/Secs1Communicator.js +45 -8
  44. package/lib/secs1/Secs1Communicator.js.map +1 -1
  45. package/lib/secs1/Secs1Message.js.map +1 -1
  46. package/lib/secs1/Secs1MessageBlock.js.map +1 -1
  47. package/lib/secs1/Secs1OnTcpIpActiveCommunicator.d.ts.map +1 -1
  48. package/lib/secs1/Secs1OnTcpIpActiveCommunicator.js +13 -2
  49. package/lib/secs1/Secs1OnTcpIpActiveCommunicator.js.map +1 -1
  50. package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.d.ts.map +1 -1
  51. package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.js +10 -2
  52. package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.js.map +1 -1
  53. package/lib/secs1/Secs1SerialCommunicator.js.map +1 -1
  54. package/lib/sml/SmlParser.js.map +1 -1
  55. package/package.json +16 -2
package/README.md CHANGED
@@ -1,442 +1,442 @@
1
- <h1 align="center">Secs4js</h1>
2
-
3
- <p align="center">A simple, efficient, and user-friendly SECS/GEM protocol library implemented in TypeScript.</p>
4
-
5
- ## Introduction
6
-
7
- This project is a TypeScript implementation of the SECS/GEM protocol, inspired by [pysemisecs](https://github.com/kenta-shimizu/pysemisecs).
8
-
9
- A special thanks to the author **kenta-shimizu** for their open-source contribution.
10
-
11
- Secs4js is a simple, efficient, and user-friendly SECS/GEM protocol library implemented in TypeScript. It provides a straightforward way to communicate with SECS/GEM devices, enabling you to easily read and write data using the SECS/GEM protocol.
12
-
13
- ## Supported Features
14
-
15
- - SECS-I (SEMI-E4)
16
- - SECS-I Virtual Serial Port (SECS-I on TCP/IP)
17
- - SECS-II (SEMI-E5)
18
- - GEM (SEMI-E30)
19
- - HSMS-SS (SEMI-E37.1)
20
- - **No HSMS-GS (SEMI-E37.2)**
21
-
22
- ## Installation
23
-
24
- ```shell
25
- npm i secs4js
26
-
27
- pnpm add secs4js
28
-
29
- yarn add secs4js
30
-
31
- bun add secs4js
32
- ```
33
-
34
- ## Getting Started from Source
35
-
36
- If you want to run some examples, they can be found in the `examples` directory.
37
-
38
- Run the following commands to start these examples:
39
-
40
- ```shell
41
- pnpm dlx tsx examples/gem_example.ts
42
-
43
- # ...
44
- pnpm dlx tsx examples/<example_file_name>.ts
45
- ```
46
-
47
- ## Usage
48
-
49
- ### 1. Creating SECS-II Messages
50
-
51
- I provide a concise, clear, and efficient way to create SECS-II message types. You can use the following code to import the required items:
52
-
53
- ```ts
54
- import { B, U1, U2, U4, U8, I1, I2, I4, I8, F4, F8, A, L } from "secs4js";
55
- ```
56
-
57
- Using these items, you can easily create SECS-II message types. For example, to create a message containing L, A, and U1 items, you can use the following code:
58
-
59
- ```ts
60
- import { L, A, U1, SecsMessage } from "secs4js";
61
-
62
- const body: AbstractSecs2Item = L(A("Hello, SECS/GEM!"), U1(123));
63
- ```
64
-
65
- Doesn't this highly resemble SML text syntax?
66
-
67
- All SECS-II messages are derived from the `AbstractSecs2Item` class, so you can use it to declare any SECS-II message of unknown type.
68
-
69
- If you don't like this approach, you can also use SML text syntax or the factory methods we provide to create SECS-II messages.
70
-
71
- Factory methods:
72
-
73
- ```ts
74
- import { Secs2ItemFactory } from "secs4js";
75
-
76
- // Create a message containing L, A, and U1 items
77
- const newMsg = Secs2ItemFactory.createListItem(
78
- Secs2ItemFactory.createAsciiItem("Hello World"),
79
- Secs2ItemFactory.createU1Item(123),
80
- );
81
- ```
82
-
83
- SML Conversion Support:
84
-
85
- You can use the `toSml` method of the `AbstractSecs2Item` class to convert SECS-II messages to SML text. For example:
86
-
87
- ```ts
88
- console.log(newMsg.toSml());
89
-
90
- // Output:
91
- // <L
92
- // <A "Hello World">
93
- // <U1 123>
94
- // >.
95
- ```
96
-
97
- ### 2. Creating SECS Messages
98
-
99
- We provide two ways to create SECS-II messages:
100
-
101
- 1. Use the `SecsMessage` class to create SECS-II messages.
102
- 2. Create SECS-II messages by parsing SML syntax text. You can use the `SmlParser` static class to parse SML text and create corresponding SECS-II messages.
103
-
104
- #### new SecsMessage(...)
105
-
106
- > You can use the `SecsMessage` class to create SECS-II messages. The class constructor accepts the following parameters:
107
- >
108
- > - `stream`: Stream number, one byte, range 0-255.
109
- > - `function`: Function number, one byte, range 0-255.
110
- > - `wBit`: W-bit, a boolean indicating whether to enable W-bit (i.e., whether a reply is required).
111
- > - `body`: SECS-II message body, an `AbstractSecs2Item` instance.
112
- > - We will automatically generate the message's `length` and `systemBytes`, so you don't need to manage them manually.
113
-
114
- ```ts
115
- import { SecsMessage } from "secs4js";
116
-
117
- const newMsg = new SecsMessage(1, 13, true, L(L(A("Hello World"), U1(123))));
118
- ```
119
-
120
- #### SmlParser
121
-
122
- ```ts
123
- import { SmlParser } from "secs4js";
124
-
125
- // Complete SML text
126
- const sml = `
127
- S1F13 W
128
- <L
129
- <B 0x20>
130
- <A "Hello World">
131
- >.
132
- `;
133
-
134
- // SML text containing only the message body
135
- const smlBody = `
136
- <L
137
- <B 0x20>
138
- <A "Hello World">
139
- >.
140
- `;
141
-
142
- // Parse complete SML text into a SecsMessage instance using the parse method
143
- const parsedMessage = SmlParser.parse(sml);
144
- const firstBodyItem =
145
- parsedMessage.body instanceof Secs2ItemList
146
- ? parsedMessage.body.value[0]
147
- : null;
148
- const bytes =
149
- firstBodyItem instanceof Secs2ItemBinary ? firstBodyItem.value : null;
150
- console.log(
151
- parsedMessage.stream,
152
- parsedMessage.func,
153
- parsedMessage.wBit,
154
- bytes,
155
- );
156
-
157
- // Parse SML text containing only the message body into an AbstractSecs2Item instance using the parseBody method
158
- const parsedBody = SmlParser.parseBody(smlBody);
159
- console.log(parsedBody?.toSml());
160
- ```
161
-
162
- ## HSMS-SS
163
-
164
- For HSMS-SS protocol support, you can act as the passive end (Equipment) or the active end (HOST/EAP).
165
-
166
- ### Active
167
-
168
- Quick start:
169
-
170
- ```ts
171
- const active = new HsmsActiveCommunicator({
172
- ip: "127.0.0.1",
173
- port: 5000,
174
- deviceId: 10,
175
- isEquip: false,
176
- // If you need to customize the timeout values, you can add additional parameters
177
- // timeoutT1: 10,
178
- // ...
179
- });
180
-
181
- active.on("connected", () => console.log("Active TCP Connected"));
182
- active.on("disconnected", () => console.log("Active Disconnected"));
183
- active.on("selected", () => console.log("Active Selected (HSMS Ready)"));
184
-
185
- await active.open();
186
- console.log("Active opened");
187
-
188
- // Active will automatically send SelectReq and start heartbeat
189
-
190
- await active.untilConnected(); // Wait for Select success
191
-
192
- // When you need to receive and process messages, you can listen for the "message" event
193
- active.on("message", (msg: SecsMessage) => {
194
- void (async () => {
195
- console.log(`Active received: ${msg.toSml()}`);
196
- if (msg.stream === 1 && msg.func === 1) {
197
- await active.reply(msg, 1, 2, L(A("MDLN-A"), A("SOFTREV-1")));
198
- }
199
- if (msg.stream === 1 && msg.func === 13) {
200
- await active.reply(msg, 1, 14, L(A("ACK")));
201
- }
202
- })();
203
- });
204
-
205
- const reply = await active.send(1, 1, true);
206
- console.log(`Active received reply: ${reply?.toSml()}`);
207
-
208
- // Interaction results with the simulator
209
- // Our reply message:
210
- // 2025-12-30 01:26:44.866:onReceivedEvent[TOOL] DeviceID=[10] SB=[6110]
211
- // S1F2
212
- // <L[2/1]
213
- // <A[6/1] "MDLN-A">
214
- // <A[9/1] "SOFTREV-1">
215
- // >.
216
-
217
- // Message actively sent by the simulator:
218
- // 2025-12-30 01:26:44.864:OnSent[TOOL] DeviceID=[1] SB=[6110]
219
-
220
- // S1F1 W.
221
- // 2025-12-30 01:26:44.864:Send the Message successfully.
222
-
223
- // Message replied by the simulator:
224
- // 2025-12-30 01:26:40.449:OnSent[TOOL] DeviceID=[10] SB=[2]
225
- // S1F2
226
- // <L[0/1]>.
227
-
228
- // Message we actively sent:
229
- // 2025-12-30 01:26:40.445:Do not find Tool in QutoReply List by Tool[TOOL] SFName=[S1F1]
230
- // 2025-12-30 01:26:40.444:onReceivedEvent[TOOL] DeviceID=[10] SB=[2]
231
- // S1F1 W.
232
- ```
233
-
234
- ### Passive
235
-
236
- ```ts
237
- import {
238
- HsmsPassiveCommunicator,
239
- SecsMessage,
240
- CommAck,
241
- OnlAck,
242
- Gem,
243
- } from "secs4js";
244
-
245
- // 1. Set up Equipment side (Passive)
246
- const equipComm = new HsmsPassiveCommunicator({
247
- ip: "127.0.0.1",
248
- port: 5000,
249
- deviceId: 1,
250
- isEquip: true,
251
- name: "Equipment",
252
- });
253
-
254
- // Use the GEM helper class (optional)
255
- const equipGem = new Gem(equipComm);
256
- equipGem.mdln = "MyEquip";
257
- equipGem.softrev = "1.0.0";
258
-
259
- // Handle received messages
260
- equipComm.on("message", (msg: SecsMessage) => {
261
- void (async () => {
262
- try {
263
- // S1F13: Establish Communications Request
264
- if (msg.stream === 1 && msg.func === 13) {
265
- console.log("[Equip] Received S1F13, replying S1F14...");
266
- await equipGem.s1f14(msg, CommAck.OK);
267
- }
268
- // S1F17: Request ON-LINE
269
- else if (msg.stream === 1 && msg.func === 17) {
270
- console.log("[Equip] Received S1F17, replying S1F18...");
271
- await equipGem.s1f18(msg, OnlAck.OK);
272
- }
273
- // S2F17: Date and Time Request
274
- else if (msg.stream === 2 && msg.func === 17) {
275
- console.log("[Equip] Received S2F17, replying S2F18...");
276
- await equipGem.s2f18Now(msg);
277
- } else {
278
- console.log(
279
- `[Equip] Received unhandled message S${msg.stream}F${msg.func}`,
280
- );
281
- }
282
- } catch (err) {
283
- console.error("[Equip] Error handling message:", err);
284
- }
285
- })();
286
- });
287
-
288
- await equipComm.open();
289
- console.log("Passive opened and listening");
290
- ```
291
-
292
- ## SECS-I Serial
293
-
294
- Supports SECS-I communication via serial port.
295
-
296
- **Note**:
297
-
298
- - Serial port communication needs to be tested on devices that support the SECS-I protocol.
299
- - Ensure the serial port path and baud rate match your device configuration.
300
- - If you want to test locally first, we recommend using a **virtual serial port tool** to simulate serial port communication.
301
-
302
- ### Active
303
-
304
- ```ts
305
- import { A, L, Secs1SerialCommunicator, SecsMessage } from "secs4js";
306
-
307
- async function SerialActive() {
308
- const active = new Secs1SerialCommunicator({
309
- path: "COM5", // Serial port path
310
- baudRate: 9600, // Baud rate
311
- deviceId: 10,
312
- isEquip: false, // Whether it is equipment
313
- });
314
-
315
- active.on("message", (msg: SecsMessage) => {
316
- void (async () => {
317
- console.log(`Active received: ${msg.toSml()}`);
318
- if (msg.stream === 1 && msg.func === 1) {
319
- await active.reply(msg, 1, 2, L(A("MDLN-A"), A("SOFTREV-1")));
320
- }
321
- })();
322
- });
323
-
324
- active.on("connected", () => {
325
- console.log("Active connected");
326
- });
327
-
328
- await active.open();
329
- console.log("Active opened");
330
- }
331
-
332
- SerialActive().catch((err) => console.error(err));
333
-
334
- // Communication results with the simulator
335
- // Our reply message:
336
- // 2025-12-30 01:35:40.187:onReceivedEvent[SERIAL_EQP] DeviceID=[10] SB=[5985]
337
- // S1F2
338
- // <L[2/1]
339
- // <A[6/1] "MDLN-A">
340
- // <A[9/1] "SOFTREV-1">
341
- // >.
342
-
343
- // Message actively sent by the simulator:
344
- // 2025-12-30 01:35:40.155:OnSent[SERIAL_EQP] DeviceID=[1] SB=[5985]
345
-
346
- // S1F1 W.
347
- // 2025-12-30 01:35:40.095:Send the Message successfully.
348
- ```
349
-
350
- ### Passive
351
-
352
- ```ts
353
- import { Secs1SerialCommunicator, SecsMessage, L, A } from "secs4js";
354
-
355
- async function SerialPassive() {
356
- const passive = new Secs1SerialCommunicator({
357
- path: "COM5",
358
- baudRate: 9600,
359
- deviceId: 10,
360
- isEquip: true,
361
- });
362
-
363
- passive.on("message", (msg: SecsMessage) => {
364
- void (async () => {
365
- if (msg.stream === 1 && msg.func === 1) {
366
- await passive.reply(msg, 1, 2, L(A("MDLN-A"), A("SOFTREV-1")));
367
- }
368
- console.log(`Passive received: ${msg.toSml()}`);
369
- })();
370
- });
371
-
372
- await passive.open();
373
- console.log("Passive opened");
374
- }
375
-
376
- SerialPassive().catch((err) => console.error(err));
377
- ```
378
-
379
- ## SECS-I On TCP/IP
380
-
381
- Supports SECS-I serial communication via TCP/IP (usually used for testing or connecting through a terminal server).
382
-
383
- ### Active
384
-
385
- ```ts
386
- import { Secs1OnTcpIpActiveCommunicator, SecsMessage, L, A } from "secs4js";
387
-
388
- async function TcpActive() {
389
- const active = new Secs1OnTcpIpActiveCommunicator({
390
- ip: "127.0.0.1",
391
- port: 5000,
392
- deviceId: 10,
393
- isEquip: false,
394
- });
395
-
396
- active.on("message", (msg: SecsMessage) => {
397
- void (async () => {
398
- console.log(`Active received: ${msg.toSml()}`);
399
- // Handle message...
400
- })();
401
- });
402
-
403
- active.on("connected", () => {
404
- console.log("Active connected");
405
- });
406
-
407
- await active.open();
408
- }
409
- ```
410
-
411
- ### Passive
412
-
413
- ```ts
414
- import { Secs1OnTcpIpPassiveCommunicator, SecsMessage, L, A } from "secs4js";
415
-
416
- async function TcpPassive() {
417
- const passive = new Secs1OnTcpIpPassiveCommunicator({
418
- ip: "0.0.0.0",
419
- port: 5000,
420
- deviceId: 10,
421
- isEquip: true,
422
- });
423
-
424
- passive.on("message", (msg: SecsMessage) => {
425
- void (async () => {
426
- console.log(`Passive received: ${msg.toSml()}`);
427
- // Process message and reply...
428
- })();
429
- });
430
-
431
- await passive.open();
432
- console.log("Passive server started");
433
- }
434
- ```
435
-
436
- ## Development
437
-
438
- If you are interested in this project, welcome to contribute your code!
439
-
440
- Thank you for your contribution! 💖
441
-
442
- > 💝 This project was generated using [`create-typescript-app`](https://github.com/JoshuaKGoldberg/create-typescript-app) and the [Bingo framework](https://create.bingo).
1
+ <h1 align="center">Secs4js</h1>
2
+
3
+ <p align="center">A simple, efficient, and user-friendly SECS/GEM protocol library implemented in TypeScript.</p>
4
+
5
+ ## Introduction
6
+
7
+ This project is a TypeScript implementation of the SECS/GEM protocol, inspired by [pysemisecs](https://github.com/kenta-shimizu/pysemisecs).
8
+
9
+ A special thanks to the author **kenta-shimizu** for their open-source contribution.
10
+
11
+ Secs4js is a simple, efficient, and user-friendly SECS/GEM protocol library implemented in TypeScript. It provides a straightforward way to communicate with SECS/GEM devices, enabling you to easily read and write data using the SECS/GEM protocol.
12
+
13
+ ## Supported Features
14
+
15
+ - SECS-I (SEMI-E4)
16
+ - SECS-I Virtual Serial Port (SECS-I on TCP/IP)
17
+ - SECS-II (SEMI-E5)
18
+ - GEM (SEMI-E30)
19
+ - HSMS-SS (SEMI-E37.1)
20
+ - **No HSMS-GS (SEMI-E37.2)**
21
+
22
+ ## Installation
23
+
24
+ ```shell
25
+ npm i secs4js
26
+
27
+ pnpm add secs4js
28
+
29
+ yarn add secs4js
30
+
31
+ bun add secs4js
32
+ ```
33
+
34
+ ## Getting Started from Source
35
+
36
+ If you want to run some examples, they can be found in the `examples` directory.
37
+
38
+ Run the following commands to start these examples:
39
+
40
+ ```shell
41
+ pnpm dlx tsx examples/gem_example.ts
42
+
43
+ # ...
44
+ pnpm dlx tsx examples/<example_file_name>.ts
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ### 1. Creating SECS-II Messages
50
+
51
+ I provide a concise, clear, and efficient way to create SECS-II message types. You can use the following code to import the required items:
52
+
53
+ ```ts
54
+ import { B, U1, U2, U4, U8, I1, I2, I4, I8, F4, F8, A, L } from "secs4js";
55
+ ```
56
+
57
+ Using these items, you can easily create SECS-II message types. For example, to create a message containing L, A, and U1 items, you can use the following code:
58
+
59
+ ```ts
60
+ import { L, A, U1, SecsMessage } from "secs4js";
61
+
62
+ const body: AbstractSecs2Item = L(A("Hello, SECS/GEM!"), U1(123));
63
+ ```
64
+
65
+ Doesn't this highly resemble SML text syntax?
66
+
67
+ All SECS-II messages are derived from the `AbstractSecs2Item` class, so you can use it to declare any SECS-II message of unknown type.
68
+
69
+ If you don't like this approach, you can also use SML text syntax or the factory methods we provide to create SECS-II messages.
70
+
71
+ Factory methods:
72
+
73
+ ```ts
74
+ import { Secs2ItemFactory } from "secs4js";
75
+
76
+ // Create a message containing L, A, and U1 items
77
+ const newMsg = Secs2ItemFactory.createListItem(
78
+ Secs2ItemFactory.createAsciiItem("Hello World"),
79
+ Secs2ItemFactory.createU1Item(123),
80
+ );
81
+ ```
82
+
83
+ SML Conversion Support:
84
+
85
+ You can use the `toSml` method of the `AbstractSecs2Item` class to convert SECS-II messages to SML text. For example:
86
+
87
+ ```ts
88
+ console.log(newMsg.toSml());
89
+
90
+ // Output:
91
+ // <L
92
+ // <A "Hello World">
93
+ // <U1 123>
94
+ // >.
95
+ ```
96
+
97
+ ### 2. Creating SECS Messages
98
+
99
+ We provide two ways to create SECS-II messages:
100
+
101
+ 1. Use the `SecsMessage` class to create SECS-II messages.
102
+ 2. Create SECS-II messages by parsing SML syntax text. You can use the `SmlParser` static class to parse SML text and create corresponding SECS-II messages.
103
+
104
+ #### new SecsMessage(...)
105
+
106
+ > You can use the `SecsMessage` class to create SECS-II messages. The class constructor accepts the following parameters:
107
+ >
108
+ > - `stream`: Stream number, one byte, range 0-255.
109
+ > - `function`: Function number, one byte, range 0-255.
110
+ > - `wBit`: W-bit, a boolean indicating whether to enable W-bit (i.e., whether a reply is required).
111
+ > - `body`: SECS-II message body, an `AbstractSecs2Item` instance.
112
+ > - We will automatically generate the message's `length` and `systemBytes`, so you don't need to manage them manually.
113
+
114
+ ```ts
115
+ import { SecsMessage } from "secs4js";
116
+
117
+ const newMsg = new SecsMessage(1, 13, true, L(L(A("Hello World"), U1(123))));
118
+ ```
119
+
120
+ #### SmlParser
121
+
122
+ ```ts
123
+ import { SmlParser } from "secs4js";
124
+
125
+ // Complete SML text
126
+ const sml = `
127
+ S1F13 W
128
+ <L
129
+ <B 0x20>
130
+ <A "Hello World">
131
+ >.
132
+ `;
133
+
134
+ // SML text containing only the message body
135
+ const smlBody = `
136
+ <L
137
+ <B 0x20>
138
+ <A "Hello World">
139
+ >.
140
+ `;
141
+
142
+ // Parse complete SML text into a SecsMessage instance using the parse method
143
+ const parsedMessage = SmlParser.parse(sml);
144
+ const firstBodyItem =
145
+ parsedMessage.body instanceof Secs2ItemList
146
+ ? parsedMessage.body.value[0]
147
+ : null;
148
+ const bytes =
149
+ firstBodyItem instanceof Secs2ItemBinary ? firstBodyItem.value : null;
150
+ console.log(
151
+ parsedMessage.stream,
152
+ parsedMessage.func,
153
+ parsedMessage.wBit,
154
+ bytes,
155
+ );
156
+
157
+ // Parse SML text containing only the message body into an AbstractSecs2Item instance using the parseBody method
158
+ const parsedBody = SmlParser.parseBody(smlBody);
159
+ console.log(parsedBody?.toSml());
160
+ ```
161
+
162
+ ## HSMS-SS
163
+
164
+ For HSMS-SS protocol support, you can act as the passive end (Equipment) or the active end (HOST/EAP).
165
+
166
+ ### Active
167
+
168
+ Quick start:
169
+
170
+ ```ts
171
+ const active = new HsmsActiveCommunicator({
172
+ ip: "127.0.0.1",
173
+ port: 5000,
174
+ deviceId: 10,
175
+ isEquip: false,
176
+ // If you need to customize the timeout values, you can add additional parameters
177
+ // timeoutT1: 10,
178
+ // ...
179
+ });
180
+
181
+ active.on("connected", () => console.log("Active TCP Connected"));
182
+ active.on("disconnected", () => console.log("Active Disconnected"));
183
+ active.on("selected", () => console.log("Active Selected (HSMS Ready)"));
184
+
185
+ await active.open();
186
+ console.log("Active opened");
187
+
188
+ // Active will automatically send SelectReq and start heartbeat
189
+
190
+ await active.untilConnected(); // Wait for Select success
191
+
192
+ // When you need to receive and process messages, you can listen for the "message" event
193
+ active.on("message", (msg: SecsMessage) => {
194
+ void (async () => {
195
+ console.log(`Active received: ${msg.toSml()}`);
196
+ if (msg.stream === 1 && msg.func === 1) {
197
+ await active.reply(msg, 1, 2, L(A("MDLN-A"), A("SOFTREV-1")));
198
+ }
199
+ if (msg.stream === 1 && msg.func === 13) {
200
+ await active.reply(msg, 1, 14, L(A("ACK")));
201
+ }
202
+ })();
203
+ });
204
+
205
+ const reply = await active.send(1, 1, true);
206
+ console.log(`Active received reply: ${reply?.toSml()}`);
207
+
208
+ // Interaction results with the simulator
209
+ // Our reply message:
210
+ // 2025-12-30 01:26:44.866:onReceivedEvent[TOOL] DeviceID=[10] SB=[6110]
211
+ // S1F2
212
+ // <L[2/1]
213
+ // <A[6/1] "MDLN-A">
214
+ // <A[9/1] "SOFTREV-1">
215
+ // >.
216
+
217
+ // Message actively sent by the simulator:
218
+ // 2025-12-30 01:26:44.864:OnSent[TOOL] DeviceID=[1] SB=[6110]
219
+
220
+ // S1F1 W.
221
+ // 2025-12-30 01:26:44.864:Send the Message successfully.
222
+
223
+ // Message replied by the simulator:
224
+ // 2025-12-30 01:26:40.449:OnSent[TOOL] DeviceID=[10] SB=[2]
225
+ // S1F2
226
+ // <L[0/1]>.
227
+
228
+ // Message we actively sent:
229
+ // 2025-12-30 01:26:40.445:Do not find Tool in QutoReply List by Tool[TOOL] SFName=[S1F1]
230
+ // 2025-12-30 01:26:40.444:onReceivedEvent[TOOL] DeviceID=[10] SB=[2]
231
+ // S1F1 W.
232
+ ```
233
+
234
+ ### Passive
235
+
236
+ ```ts
237
+ import {
238
+ HsmsPassiveCommunicator,
239
+ SecsMessage,
240
+ CommAck,
241
+ OnlAck,
242
+ Gem,
243
+ } from "secs4js";
244
+
245
+ // 1. Set up Equipment side (Passive)
246
+ const equipComm = new HsmsPassiveCommunicator({
247
+ ip: "127.0.0.1",
248
+ port: 5000,
249
+ deviceId: 1,
250
+ isEquip: true,
251
+ name: "Equipment",
252
+ });
253
+
254
+ // Use the GEM helper class (optional)
255
+ const equipGem = new Gem(equipComm);
256
+ equipGem.mdln = "MyEquip";
257
+ equipGem.softrev = "1.0.0";
258
+
259
+ // Handle received messages
260
+ equipComm.on("message", (msg: SecsMessage) => {
261
+ void (async () => {
262
+ try {
263
+ // S1F13: Establish Communications Request
264
+ if (msg.stream === 1 && msg.func === 13) {
265
+ console.log("[Equip] Received S1F13, replying S1F14...");
266
+ await equipGem.s1f14(msg, CommAck.OK);
267
+ }
268
+ // S1F17: Request ON-LINE
269
+ else if (msg.stream === 1 && msg.func === 17) {
270
+ console.log("[Equip] Received S1F17, replying S1F18...");
271
+ await equipGem.s1f18(msg, OnlAck.OK);
272
+ }
273
+ // S2F17: Date and Time Request
274
+ else if (msg.stream === 2 && msg.func === 17) {
275
+ console.log("[Equip] Received S2F17, replying S2F18...");
276
+ await equipGem.s2f18Now(msg);
277
+ } else {
278
+ console.log(
279
+ `[Equip] Received unhandled message S${msg.stream}F${msg.func}`,
280
+ );
281
+ }
282
+ } catch (err) {
283
+ console.error("[Equip] Error handling message:", err);
284
+ }
285
+ })();
286
+ });
287
+
288
+ await equipComm.open();
289
+ console.log("Passive opened and listening");
290
+ ```
291
+
292
+ ## SECS-I Serial
293
+
294
+ Supports SECS-I communication via serial port.
295
+
296
+ **Note**:
297
+
298
+ - Serial port communication needs to be tested on devices that support the SECS-I protocol.
299
+ - Ensure the serial port path and baud rate match your device configuration.
300
+ - If you want to test locally first, we recommend using a **virtual serial port tool** to simulate serial port communication.
301
+
302
+ ### Active
303
+
304
+ ```ts
305
+ import { A, L, Secs1SerialCommunicator, SecsMessage } from "secs4js";
306
+
307
+ async function SerialActive() {
308
+ const active = new Secs1SerialCommunicator({
309
+ path: "COM5", // Serial port path
310
+ baudRate: 9600, // Baud rate
311
+ deviceId: 10,
312
+ isEquip: false, // Whether it is equipment
313
+ });
314
+
315
+ active.on("message", (msg: SecsMessage) => {
316
+ void (async () => {
317
+ console.log(`Active received: ${msg.toSml()}`);
318
+ if (msg.stream === 1 && msg.func === 1) {
319
+ await active.reply(msg, 1, 2, L(A("MDLN-A"), A("SOFTREV-1")));
320
+ }
321
+ })();
322
+ });
323
+
324
+ active.on("connected", () => {
325
+ console.log("Active connected");
326
+ });
327
+
328
+ await active.open();
329
+ console.log("Active opened");
330
+ }
331
+
332
+ SerialActive().catch((err) => console.error(err));
333
+
334
+ // Communication results with the simulator
335
+ // Our reply message:
336
+ // 2025-12-30 01:35:40.187:onReceivedEvent[SERIAL_EQP] DeviceID=[10] SB=[5985]
337
+ // S1F2
338
+ // <L[2/1]
339
+ // <A[6/1] "MDLN-A">
340
+ // <A[9/1] "SOFTREV-1">
341
+ // >.
342
+
343
+ // Message actively sent by the simulator:
344
+ // 2025-12-30 01:35:40.155:OnSent[SERIAL_EQP] DeviceID=[1] SB=[5985]
345
+
346
+ // S1F1 W.
347
+ // 2025-12-30 01:35:40.095:Send the Message successfully.
348
+ ```
349
+
350
+ ### Passive
351
+
352
+ ```ts
353
+ import { Secs1SerialCommunicator, SecsMessage, L, A } from "secs4js";
354
+
355
+ async function SerialPassive() {
356
+ const passive = new Secs1SerialCommunicator({
357
+ path: "COM5",
358
+ baudRate: 9600,
359
+ deviceId: 10,
360
+ isEquip: true,
361
+ });
362
+
363
+ passive.on("message", (msg: SecsMessage) => {
364
+ void (async () => {
365
+ if (msg.stream === 1 && msg.func === 1) {
366
+ await passive.reply(msg, 1, 2, L(A("MDLN-A"), A("SOFTREV-1")));
367
+ }
368
+ console.log(`Passive received: ${msg.toSml()}`);
369
+ })();
370
+ });
371
+
372
+ await passive.open();
373
+ console.log("Passive opened");
374
+ }
375
+
376
+ SerialPassive().catch((err) => console.error(err));
377
+ ```
378
+
379
+ ## SECS-I On TCP/IP
380
+
381
+ Supports SECS-I serial communication via TCP/IP (usually used for testing or connecting through a terminal server).
382
+
383
+ ### Active
384
+
385
+ ```ts
386
+ import { Secs1OnTcpIpActiveCommunicator, SecsMessage, L, A } from "secs4js";
387
+
388
+ async function TcpActive() {
389
+ const active = new Secs1OnTcpIpActiveCommunicator({
390
+ ip: "127.0.0.1",
391
+ port: 5000,
392
+ deviceId: 10,
393
+ isEquip: false,
394
+ });
395
+
396
+ active.on("message", (msg: SecsMessage) => {
397
+ void (async () => {
398
+ console.log(`Active received: ${msg.toSml()}`);
399
+ // Handle message...
400
+ })();
401
+ });
402
+
403
+ active.on("connected", () => {
404
+ console.log("Active connected");
405
+ });
406
+
407
+ await active.open();
408
+ }
409
+ ```
410
+
411
+ ### Passive
412
+
413
+ ```ts
414
+ import { Secs1OnTcpIpPassiveCommunicator, SecsMessage, L, A } from "secs4js";
415
+
416
+ async function TcpPassive() {
417
+ const passive = new Secs1OnTcpIpPassiveCommunicator({
418
+ ip: "0.0.0.0",
419
+ port: 5000,
420
+ deviceId: 10,
421
+ isEquip: true,
422
+ });
423
+
424
+ passive.on("message", (msg: SecsMessage) => {
425
+ void (async () => {
426
+ console.log(`Passive received: ${msg.toSml()}`);
427
+ // Process message and reply...
428
+ })();
429
+ });
430
+
431
+ await passive.open();
432
+ console.log("Passive server started");
433
+ }
434
+ ```
435
+
436
+ ## Development
437
+
438
+ If you are interested in this project, welcome to contribute your code!
439
+
440
+ Thank you for your contribution! 💖
441
+
442
+ > 💝 This project was generated using [`create-typescript-app`](https://github.com/JoshuaKGoldberg/create-typescript-app) and the [Bingo framework](https://create.bingo).