secs4js 0.1.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.
- package/LICENSE.md +20 -0
- package/README.md +442 -0
- package/lib/core/AbstractSecsCommunicator.d.ts +76 -0
- package/lib/core/AbstractSecsCommunicator.d.ts.map +1 -0
- package/lib/core/AbstractSecsCommunicator.js +85 -0
- package/lib/core/AbstractSecsCommunicator.js.map +1 -0
- package/lib/core/AbstractSecsMessage.d.ts +36 -0
- package/lib/core/AbstractSecsMessage.d.ts.map +1 -0
- package/lib/core/AbstractSecsMessage.js +66 -0
- package/lib/core/AbstractSecsMessage.js.map +1 -0
- package/lib/core/enums/HsmsSsControlType.d.ts +59 -0
- package/lib/core/enums/HsmsSsControlType.d.ts.map +1 -0
- package/lib/core/enums/HsmsSsControlType.js +105 -0
- package/lib/core/enums/HsmsSsControlType.js.map +1 -0
- package/lib/core/enums/RejectReason.d.ts +11 -0
- package/lib/core/enums/RejectReason.d.ts.map +1 -0
- package/lib/core/enums/RejectReason.js +13 -0
- package/lib/core/enums/RejectReason.js.map +1 -0
- package/lib/core/enums/SecsItemType.d.ts +33 -0
- package/lib/core/enums/SecsItemType.d.ts.map +1 -0
- package/lib/core/enums/SecsItemType.js +22 -0
- package/lib/core/enums/SecsItemType.js.map +1 -0
- package/lib/core/enums/SelectStatus.d.ts +11 -0
- package/lib/core/enums/SelectStatus.d.ts.map +1 -0
- package/lib/core/enums/SelectStatus.js +13 -0
- package/lib/core/enums/SelectStatus.js.map +1 -0
- package/lib/core/secs2item/AbstractSecs2Item.d.ts +34 -0
- package/lib/core/secs2item/AbstractSecs2Item.d.ts.map +1 -0
- package/lib/core/secs2item/AbstractSecs2Item.js +59 -0
- package/lib/core/secs2item/AbstractSecs2Item.js.map +1 -0
- package/lib/core/secs2item/Secs2ItemAscii.d.ts +18 -0
- package/lib/core/secs2item/Secs2ItemAscii.d.ts.map +1 -0
- package/lib/core/secs2item/Secs2ItemAscii.js +27 -0
- package/lib/core/secs2item/Secs2ItemAscii.js.map +1 -0
- package/lib/core/secs2item/Secs2ItemBinary.d.ts +11 -0
- package/lib/core/secs2item/Secs2ItemBinary.d.ts.map +1 -0
- package/lib/core/secs2item/Secs2ItemBinary.js +22 -0
- package/lib/core/secs2item/Secs2ItemBinary.js.map +1 -0
- package/lib/core/secs2item/Secs2ItemBoolean.d.ts +12 -0
- package/lib/core/secs2item/Secs2ItemBoolean.d.ts.map +1 -0
- package/lib/core/secs2item/Secs2ItemBoolean.js +27 -0
- package/lib/core/secs2item/Secs2ItemBoolean.js.map +1 -0
- package/lib/core/secs2item/Secs2ItemFactory.d.ts +27 -0
- package/lib/core/secs2item/Secs2ItemFactory.d.ts.map +1 -0
- package/lib/core/secs2item/Secs2ItemFactory.js +56 -0
- package/lib/core/secs2item/Secs2ItemFactory.js.map +1 -0
- package/lib/core/secs2item/Secs2ItemList.d.ts +11 -0
- package/lib/core/secs2item/Secs2ItemList.d.ts.map +1 -0
- package/lib/core/secs2item/Secs2ItemList.js +26 -0
- package/lib/core/secs2item/Secs2ItemList.js.map +1 -0
- package/lib/core/secs2item/Secs2ItemNumeric.d.ts +14 -0
- package/lib/core/secs2item/Secs2ItemNumeric.d.ts.map +1 -0
- package/lib/core/secs2item/Secs2ItemNumeric.js +128 -0
- package/lib/core/secs2item/Secs2ItemNumeric.js.map +1 -0
- package/lib/core/secs2item/Secs2ItemParser.d.ts +12 -0
- package/lib/core/secs2item/Secs2ItemParser.d.ts.map +1 -0
- package/lib/core/secs2item/Secs2ItemParser.js +74 -0
- package/lib/core/secs2item/Secs2ItemParser.js.map +1 -0
- package/lib/gem/Clock.d.ts +21 -0
- package/lib/gem/Clock.d.ts.map +1 -0
- package/lib/gem/Clock.js +65 -0
- package/lib/gem/Clock.js.map +1 -0
- package/lib/gem/Gem.d.ts +55 -0
- package/lib/gem/Gem.d.ts.map +1 -0
- package/lib/gem/Gem.js +137 -0
- package/lib/gem/Gem.js.map +1 -0
- package/lib/helper/Secs2ItemHelper.d.ts +96 -0
- package/lib/helper/Secs2ItemHelper.d.ts.map +1 -0
- package/lib/helper/Secs2ItemHelper.js +135 -0
- package/lib/helper/Secs2ItemHelper.js.map +1 -0
- package/lib/hsms/HsmsActiveCommunicator.d.ts +29 -0
- package/lib/hsms/HsmsActiveCommunicator.d.ts.map +1 -0
- package/lib/hsms/HsmsActiveCommunicator.js +123 -0
- package/lib/hsms/HsmsActiveCommunicator.js.map +1 -0
- package/lib/hsms/HsmsCommunicator.d.ts +70 -0
- package/lib/hsms/HsmsCommunicator.d.ts.map +1 -0
- package/lib/hsms/HsmsCommunicator.js +322 -0
- package/lib/hsms/HsmsCommunicator.js.map +1 -0
- package/lib/hsms/HsmsMessage.d.ts +92 -0
- package/lib/hsms/HsmsMessage.d.ts.map +1 -0
- package/lib/hsms/HsmsMessage.js +163 -0
- package/lib/hsms/HsmsMessage.js.map +1 -0
- package/lib/hsms/HsmsPassiveCommunicator.d.ts +48 -0
- package/lib/hsms/HsmsPassiveCommunicator.d.ts.map +1 -0
- package/lib/hsms/HsmsPassiveCommunicator.js +304 -0
- package/lib/hsms/HsmsPassiveCommunicator.js.map +1 -0
- package/lib/hsms/enums/HsmsControlType.d.ts +15 -0
- package/lib/hsms/enums/HsmsControlType.d.ts.map +1 -0
- package/lib/hsms/enums/HsmsControlType.js +17 -0
- package/lib/hsms/enums/HsmsControlType.js.map +1 -0
- package/lib/hsms/enums/RejectReason.d.ts +11 -0
- package/lib/hsms/enums/RejectReason.d.ts.map +1 -0
- package/lib/hsms/enums/RejectReason.js +13 -0
- package/lib/hsms/enums/RejectReason.js.map +1 -0
- package/lib/hsms/enums/SelectStatus.d.ts +11 -0
- package/lib/hsms/enums/SelectStatus.d.ts.map +1 -0
- package/lib/hsms/enums/SelectStatus.js +13 -0
- package/lib/hsms/enums/SelectStatus.js.map +1 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.js +29 -0
- package/lib/secs1/Secs1Communicator.d.ts +51 -0
- package/lib/secs1/Secs1Communicator.d.ts.map +1 -0
- package/lib/secs1/Secs1Communicator.js +342 -0
- package/lib/secs1/Secs1Communicator.js.map +1 -0
- package/lib/secs1/Secs1Message.d.ts +21 -0
- package/lib/secs1/Secs1Message.d.ts.map +1 -0
- package/lib/secs1/Secs1Message.js +77 -0
- package/lib/secs1/Secs1Message.js.map +1 -0
- package/lib/secs1/Secs1MessageBlock.d.ts +23 -0
- package/lib/secs1/Secs1MessageBlock.d.ts.map +1 -0
- package/lib/secs1/Secs1MessageBlock.js +86 -0
- package/lib/secs1/Secs1MessageBlock.js.map +1 -0
- package/lib/secs1/Secs1OnTcpIpActiveCommunicator.d.ts +23 -0
- package/lib/secs1/Secs1OnTcpIpActiveCommunicator.d.ts.map +1 -0
- package/lib/secs1/Secs1OnTcpIpActiveCommunicator.js +82 -0
- package/lib/secs1/Secs1OnTcpIpActiveCommunicator.js.map +1 -0
- package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.d.ts +19 -0
- package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.d.ts.map +1 -0
- package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.js +49 -0
- package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.js.map +1 -0
- package/lib/secs1/Secs1SerialCommunicator.d.ts +18 -0
- package/lib/secs1/Secs1SerialCommunicator.d.ts.map +1 -0
- package/lib/secs1/Secs1SerialCommunicator.js +59 -0
- package/lib/secs1/Secs1SerialCommunicator.js.map +1 -0
- package/lib/sml/SmlParser.d.ts +27 -0
- package/lib/sml/SmlParser.d.ts.map +1 -0
- package/lib/sml/SmlParser.js +184 -0
- package/lib/sml/SmlParser.js.map +1 -0
- package/package.json +53 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +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).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { AbstractSecs2Item } from "./secs2item/AbstractSecs2Item.js";
|
|
2
|
+
import { SecsMessage } from "./AbstractSecsMessage.js";
|
|
3
|
+
import { EventEmitter } from "events";
|
|
4
|
+
|
|
5
|
+
//#region src/core/AbstractSecsCommunicator.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param message The message received.
|
|
9
|
+
* @param error The error occurred.
|
|
10
|
+
* @param connected The connection is established.
|
|
11
|
+
* @param disconnected The connection is closed.
|
|
12
|
+
*/
|
|
13
|
+
interface SecsCommunicatorEvents {
|
|
14
|
+
message: [SecsMessage];
|
|
15
|
+
error: [Error];
|
|
16
|
+
connected: [];
|
|
17
|
+
disconnected: [];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @param deviceId The device ID.
|
|
21
|
+
* @param isEquip Whether the device is an equip.
|
|
22
|
+
* @param name The name of the connection.
|
|
23
|
+
* @param timeoutT1 Only SECS-I supports this parameter, ENQ retransmission interval.
|
|
24
|
+
* @param timeoutT2 Only SECS-I supports this parameter, ENQ wait-for-response timeout.
|
|
25
|
+
* @param timeoutT3 Support SECS-I and SECS-II, W-Bit timeout.
|
|
26
|
+
* @param timeoutT4 Only SECS-I supports this parameter, data block retransmission interval.
|
|
27
|
+
* @param timeoutT5 Support SECS-I and SECS-II, establishment of communication session timeout.
|
|
28
|
+
* @param timeoutT6 Only HSMS supports this parameter, reply control message timeout.
|
|
29
|
+
* @param timeoutT7 Only HSMS supports this parameter, create connection timeout.
|
|
30
|
+
* @param timeoutT8 Only HSMS supports this parameter, the maximum time interval between message bytes when receiving a message
|
|
31
|
+
*/
|
|
32
|
+
interface SecsCommunicatorConfig {
|
|
33
|
+
deviceId: number;
|
|
34
|
+
isEquip: boolean;
|
|
35
|
+
name?: string;
|
|
36
|
+
timeoutT1?: number;
|
|
37
|
+
timeoutT2?: number;
|
|
38
|
+
timeoutT3?: number;
|
|
39
|
+
timeoutT4?: number;
|
|
40
|
+
timeoutT5?: number;
|
|
41
|
+
timeoutT6?: number;
|
|
42
|
+
timeoutT7?: number;
|
|
43
|
+
timeoutT8?: number;
|
|
44
|
+
}
|
|
45
|
+
declare abstract class AbstractSecsCommunicator<Events extends Record<keyof Events, unknown[]> & SecsCommunicatorEvents = SecsCommunicatorEvents> extends EventEmitter {
|
|
46
|
+
readonly deviceId: number;
|
|
47
|
+
readonly isEquip: boolean;
|
|
48
|
+
readonly name: string;
|
|
49
|
+
timeoutT1: number;
|
|
50
|
+
timeoutT2: number;
|
|
51
|
+
timeoutT3: number;
|
|
52
|
+
timeoutT4: number;
|
|
53
|
+
timeoutT5: number;
|
|
54
|
+
timeoutT6: number;
|
|
55
|
+
timeoutT7: number;
|
|
56
|
+
timeoutT8: number;
|
|
57
|
+
protected _systemBytesCounter: number;
|
|
58
|
+
protected _transactions: Map<number, {
|
|
59
|
+
resolve: (msg: SecsMessage) => void;
|
|
60
|
+
reject: (err: Error) => void;
|
|
61
|
+
timer: NodeJS.Timeout;
|
|
62
|
+
}>;
|
|
63
|
+
constructor(config: SecsCommunicatorConfig);
|
|
64
|
+
abstract open(): Promise<void>;
|
|
65
|
+
abstract close(): Promise<void>;
|
|
66
|
+
protected emitInternal(eventName: string | symbol, ...args: unknown[]): boolean;
|
|
67
|
+
protected abstract sendBuffer(buffer: Buffer): Promise<void>;
|
|
68
|
+
protected abstract createMessage(stream: number, func: number, wBit: boolean, body: AbstractSecs2Item | null, systemBytes: number): SecsMessage;
|
|
69
|
+
send(stream: number, func: number, wBit: boolean, body?: AbstractSecs2Item | null): Promise<SecsMessage | null>;
|
|
70
|
+
reply(primaryMsg: SecsMessage, stream: number, func: number, body?: AbstractSecs2Item | null): Promise<void>;
|
|
71
|
+
protected getNextSystemBytes(): number;
|
|
72
|
+
protected handleMessage(msg: SecsMessage): void;
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { AbstractSecsCommunicator, SecsCommunicatorConfig, SecsCommunicatorEvents };
|
|
76
|
+
//# sourceMappingURL=AbstractSecsCommunicator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AbstractSecsCommunicator.d.ts","names":[],"sources":["../../src/core/AbstractSecsCommunicator.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAUA;AAoBA;AAcA;;AACgB,UAnCC,sBAAA,CAmCD;EAAkC,OAAA,EAAA,CAlCvC,WAkCuC,CAAA;EAChD,KAAA,EAAA,CAlCO,KAkCP,CAAA;EAmBgB,SAAA,EAAA,EAAA;EACD,YAAA,EAAA,EAAA;;;;;;;;;;;;;;;AAwFb,UA5Ha,sBAAA,CA4Hb;EAkB0B,QAAA,EAAA,MAAA;EA7HpB,OAAA,EAAA,OAAA;EAAY,IAAA,CAAA,EAAA,MAAA;;;;;;;;;;uBAHA,wCACN,aAAa,qBAAqB,yBAChD,gCACQ,YAAA;;;;;;;;;;;;;2BAec;mBAGN;kBACD;WACP,MAAA,CAAO;;sBAII;mBAeH;oBACC;;wCAgBoB,SAAS;sFAMxC,gDAEJ;2DAMI,2BACJ,QAAQ;oBAgCE,kDAGN,2BACJ;;+BAkB0B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
|
|
3
|
+
//#region src/core/AbstractSecsCommunicator.ts
|
|
4
|
+
var AbstractSecsCommunicator = class extends EventEmitter {
|
|
5
|
+
deviceId;
|
|
6
|
+
isEquip;
|
|
7
|
+
name;
|
|
8
|
+
timeoutT1 = 1;
|
|
9
|
+
timeoutT2 = 15;
|
|
10
|
+
timeoutT3 = 45;
|
|
11
|
+
timeoutT4 = 45;
|
|
12
|
+
timeoutT5 = 10;
|
|
13
|
+
timeoutT6 = 5;
|
|
14
|
+
timeoutT7 = 10;
|
|
15
|
+
timeoutT8 = 5;
|
|
16
|
+
_systemBytesCounter = 0;
|
|
17
|
+
_transactions = /* @__PURE__ */ new Map();
|
|
18
|
+
constructor(config) {
|
|
19
|
+
super();
|
|
20
|
+
this.deviceId = config.deviceId;
|
|
21
|
+
this.isEquip = config.isEquip;
|
|
22
|
+
this.name = config.name ?? "SecsCommunicator";
|
|
23
|
+
if (config.timeoutT1 !== void 0) this.timeoutT1 = config.timeoutT1;
|
|
24
|
+
if (config.timeoutT2 !== void 0) this.timeoutT2 = config.timeoutT2;
|
|
25
|
+
if (config.timeoutT3 !== void 0) this.timeoutT3 = config.timeoutT3;
|
|
26
|
+
if (config.timeoutT4 !== void 0) this.timeoutT4 = config.timeoutT4;
|
|
27
|
+
if (config.timeoutT5 !== void 0) this.timeoutT5 = config.timeoutT5;
|
|
28
|
+
if (config.timeoutT6 !== void 0) this.timeoutT6 = config.timeoutT6;
|
|
29
|
+
if (config.timeoutT7 !== void 0) this.timeoutT7 = config.timeoutT7;
|
|
30
|
+
if (config.timeoutT8 !== void 0) this.timeoutT8 = config.timeoutT8;
|
|
31
|
+
}
|
|
32
|
+
emitInternal(eventName, ...args) {
|
|
33
|
+
return super.emit.call(this, eventName, ...args);
|
|
34
|
+
}
|
|
35
|
+
async send(stream, func, wBit, body = null) {
|
|
36
|
+
const systemBytes = this.getNextSystemBytes();
|
|
37
|
+
const msg = this.createMessage(stream, func, wBit, body, systemBytes);
|
|
38
|
+
if (wBit) return new Promise((resolve, reject) => {
|
|
39
|
+
const timer = setTimeout(() => {
|
|
40
|
+
if (this._transactions.has(systemBytes)) {
|
|
41
|
+
this._transactions.delete(systemBytes);
|
|
42
|
+
reject(/* @__PURE__ */ new Error(`T3 Timeout waiting for reply to S${String(stream)}F${String(func)} [SysBytes=${String(systemBytes)}]`));
|
|
43
|
+
}
|
|
44
|
+
}, this.timeoutT3 * 1e3);
|
|
45
|
+
this._transactions.set(systemBytes, {
|
|
46
|
+
resolve,
|
|
47
|
+
reject,
|
|
48
|
+
timer
|
|
49
|
+
});
|
|
50
|
+
this.sendBuffer(msg.toBuffer()).catch((err) => {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
this._transactions.delete(systemBytes);
|
|
53
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
else {
|
|
57
|
+
await this.sendBuffer(msg.toBuffer());
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async reply(primaryMsg, stream, func, body = null) {
|
|
62
|
+
const msg = this.createMessage(stream, func, false, body, primaryMsg.systemBytes);
|
|
63
|
+
console.log("Reply Buffer:", msg.toBuffer());
|
|
64
|
+
console.log(`Reply: ${msg.toSml()}`);
|
|
65
|
+
await this.sendBuffer(msg.toBuffer());
|
|
66
|
+
}
|
|
67
|
+
getNextSystemBytes() {
|
|
68
|
+
this._systemBytesCounter = this._systemBytesCounter + 1 >>> 0;
|
|
69
|
+
return this._systemBytesCounter;
|
|
70
|
+
}
|
|
71
|
+
handleMessage(msg) {
|
|
72
|
+
const tx = this._transactions.get(msg.systemBytes);
|
|
73
|
+
if (tx) {
|
|
74
|
+
clearTimeout(tx.timer);
|
|
75
|
+
this._transactions.delete(msg.systemBytes);
|
|
76
|
+
tx.resolve(msg);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.emitInternal("message", msg);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
export { AbstractSecsCommunicator };
|
|
85
|
+
//# sourceMappingURL=AbstractSecsCommunicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AbstractSecsCommunicator.js","names":[],"sources":["../../src/core/AbstractSecsCommunicator.ts"],"sourcesContent":["import { EventEmitter } from \"events\";\nimport { SecsMessage } from \"./AbstractSecsMessage.js\";\nimport { AbstractSecs2Item } from \"./secs2item/AbstractSecs2Item.js\";\n\n/**\n * @param message The message received.\n * @param error The error occurred.\n * @param connected The connection is established.\n * @param disconnected The connection is closed.\n */\nexport interface SecsCommunicatorEvents {\n\tmessage: [SecsMessage];\n\terror: [Error];\n\tconnected: [];\n\tdisconnected: [];\n}\n\n/**\n * @param deviceId The device ID.\n * @param isEquip Whether the device is an equip.\n * @param name The name of the connection.\n * @param timeoutT1 Only SECS-I supports this parameter, ENQ retransmission interval.\n * @param timeoutT2 Only SECS-I supports this parameter, ENQ wait-for-response timeout.\n * @param timeoutT3 Support SECS-I and SECS-II, W-Bit timeout.\n * @param timeoutT4 Only SECS-I supports this parameter, data block retransmission interval.\n * @param timeoutT5 Support SECS-I and SECS-II, establishment of communication session timeout.\n * @param timeoutT6 Only HSMS supports this parameter, reply control message timeout.\n * @param timeoutT7 Only HSMS supports this parameter, create connection timeout.\n * @param timeoutT8 Only HSMS supports this parameter, the maximum time interval between message bytes when receiving a message\n */\nexport interface SecsCommunicatorConfig {\n\tdeviceId: number;\n\tisEquip: boolean;\n\tname?: string;\n\ttimeoutT1?: number;\n\ttimeoutT2?: number;\n\ttimeoutT3?: number;\n\ttimeoutT4?: number;\n\ttimeoutT5?: number;\n\ttimeoutT6?: number;\n\ttimeoutT7?: number;\n\ttimeoutT8?: number;\n}\n\nexport abstract class AbstractSecsCommunicator<\n\tEvents extends Record<keyof Events, unknown[]> & SecsCommunicatorEvents =\n\t\tSecsCommunicatorEvents,\n> extends EventEmitter {\n\tpublic readonly deviceId: number;\n\tpublic readonly isEquip: boolean;\n\tpublic readonly name: string;\n\n\tpublic timeoutT1 = 1;\n\tpublic timeoutT2 = 15;\n\tpublic timeoutT3 = 45;\n\tpublic timeoutT4 = 45;\n\tpublic timeoutT5 = 10;\n\tpublic timeoutT6 = 5;\n\tpublic timeoutT7 = 10;\n\tpublic timeoutT8 = 5;\n\n\tprotected _systemBytesCounter = 0;\n\tprotected _transactions = new Map<\n\t\tnumber,\n\t\t{\n\t\t\tresolve: (msg: SecsMessage) => void;\n\t\t\treject: (err: Error) => void;\n\t\t\ttimer: NodeJS.Timeout;\n\t\t}\n\t>();\n\n\tconstructor(config: SecsCommunicatorConfig) {\n\t\tsuper();\n\t\tthis.deviceId = config.deviceId;\n\t\tthis.isEquip = config.isEquip;\n\t\tthis.name = config.name ?? \"SecsCommunicator\";\n\t\tif (config.timeoutT1 !== undefined) this.timeoutT1 = config.timeoutT1;\n\t\tif (config.timeoutT2 !== undefined) this.timeoutT2 = config.timeoutT2;\n\t\tif (config.timeoutT3 !== undefined) this.timeoutT3 = config.timeoutT3;\n\t\tif (config.timeoutT4 !== undefined) this.timeoutT4 = config.timeoutT4;\n\t\tif (config.timeoutT5 !== undefined) this.timeoutT5 = config.timeoutT5;\n\t\tif (config.timeoutT6 !== undefined) this.timeoutT6 = config.timeoutT6;\n\t\tif (config.timeoutT7 !== undefined) this.timeoutT7 = config.timeoutT7;\n\t\tif (config.timeoutT8 !== undefined) this.timeoutT8 = config.timeoutT8;\n\t}\n\n\tabstract open(): Promise<void>;\n\tabstract close(): Promise<void>;\n\n\tprotected emitInternal(\n\t\teventName: string | symbol,\n\t\t...args: unknown[]\n\t): boolean {\n\t\treturn (\n\t\t\tsuper.emit as (\n\t\t\t\tthis: AbstractSecsCommunicator<Events>,\n\t\t\t\teventName: string | symbol,\n\t\t\t\t...args: unknown[]\n\t\t\t) => boolean\n\t\t).call(this, eventName, ...args);\n\t}\n\n\t// This method sends the message bytes. To be implemented by subclasses.\n\tprotected abstract sendBuffer(buffer: Buffer): Promise<void>;\n\n\tprotected abstract createMessage(\n\t\tstream: number,\n\t\tfunc: number,\n\t\twBit: boolean,\n\t\tbody: AbstractSecs2Item | null,\n\t\tsystemBytes: number,\n\t): SecsMessage;\n\n\tasync send(\n\t\tstream: number,\n\t\tfunc: number,\n\t\twBit: boolean,\n\t\tbody: AbstractSecs2Item | null = null,\n\t): Promise<SecsMessage | null> {\n\t\tconst systemBytes = this.getNextSystemBytes();\n\t\tconst msg = this.createMessage(stream, func, wBit, body, systemBytes);\n\n\t\tif (wBit) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tif (this._transactions.has(systemBytes)) {\n\t\t\t\t\t\tthis._transactions.delete(systemBytes);\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t`T3 Timeout waiting for reply to S${String(stream)}F${String(func)} [SysBytes=${String(systemBytes)}]`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}, this.timeoutT3 * 1000);\n\n\t\t\t\tthis._transactions.set(systemBytes, { resolve, reject, timer });\n\n\t\t\t\tthis.sendBuffer(msg.toBuffer()).catch((err: unknown) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tthis._transactions.delete(systemBytes);\n\t\t\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\t\t});\n\t\t\t});\n\t\t} else {\n\t\t\tawait this.sendBuffer(msg.toBuffer());\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync reply(\n\t\tprimaryMsg: SecsMessage,\n\t\tstream: number,\n\t\tfunc: number,\n\t\tbody: AbstractSecs2Item | null = null,\n\t): Promise<void> {\n\t\tconst msg = this.createMessage(\n\t\t\tstream,\n\t\t\tfunc,\n\t\t\tfalse,\n\t\t\tbody,\n\t\t\tprimaryMsg.systemBytes,\n\t\t);\n\t\tconsole.log(\"Reply Buffer:\", msg.toBuffer());\n\t\tconsole.log(`Reply: ${msg.toSml()}`);\n\t\tawait this.sendBuffer(msg.toBuffer());\n\t}\n\n\tprotected getNextSystemBytes(): number {\n\t\tthis._systemBytesCounter = (this._systemBytesCounter + 1) >>> 0; // Ensure 32-bit unsigned\n\t\treturn this._systemBytesCounter;\n\t}\n\n\tprotected handleMessage(msg: SecsMessage) {\n\t\t// Check if it's a reply\n\t\t// Usually, if we have a transaction waiting for this SystemBytes, we treat it as a reply.\n\t\t// However, Stream 0 (HSMS Control) messages are never replies to SECS-II messages.\n\t\t// SECS-II replies usually have the same SystemBytes as the primary.\n\n\t\tconst tx = this._transactions.get(msg.systemBytes);\n\t\tif (tx) {\n\t\t\tclearTimeout(tx.timer);\n\t\t\tthis._transactions.delete(msg.systemBytes);\n\t\t\ttx.resolve(msg);\n\t\t\treturn;\n\t\t}\n\n\t\t// If not a reply, emit event\n\t\tthis.emitInternal(\"message\", msg);\n\t}\n}\n"],"mappings":";;;AA4CA,IAAsB,2BAAtB,cAGU,aAAa;CACtB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,AAAO,YAAY;CACnB,AAAO,YAAY;CACnB,AAAO,YAAY;CACnB,AAAO,YAAY;CACnB,AAAO,YAAY;CACnB,AAAO,YAAY;CACnB,AAAO,YAAY;CACnB,AAAO,YAAY;CAEnB,AAAU,sBAAsB;CAChC,AAAU,gCAAgB,IAAI,KAO3B;CAEH,YAAY,QAAgC;AAC3C,SAAO;AACP,OAAK,WAAW,OAAO;AACvB,OAAK,UAAU,OAAO;AACtB,OAAK,OAAO,OAAO,QAAQ;AAC3B,MAAI,OAAO,cAAc,OAAW,MAAK,YAAY,OAAO;AAC5D,MAAI,OAAO,cAAc,OAAW,MAAK,YAAY,OAAO;AAC5D,MAAI,OAAO,cAAc,OAAW,MAAK,YAAY,OAAO;AAC5D,MAAI,OAAO,cAAc,OAAW,MAAK,YAAY,OAAO;AAC5D,MAAI,OAAO,cAAc,OAAW,MAAK,YAAY,OAAO;AAC5D,MAAI,OAAO,cAAc,OAAW,MAAK,YAAY,OAAO;AAC5D,MAAI,OAAO,cAAc,OAAW,MAAK,YAAY,OAAO;AAC5D,MAAI,OAAO,cAAc,OAAW,MAAK,YAAY,OAAO;;CAM7D,AAAU,aACT,WACA,GAAG,MACO;AACV,SACC,MAAM,KAKL,KAAK,MAAM,WAAW,GAAG,KAAK;;CAcjC,MAAM,KACL,QACA,MACA,MACA,OAAiC,MACH;EAC9B,MAAM,cAAc,KAAK,oBAAoB;EAC7C,MAAM,MAAM,KAAK,cAAc,QAAQ,MAAM,MAAM,MAAM,YAAY;AAErE,MAAI,KACH,QAAO,IAAI,SAAS,SAAS,WAAW;GACvC,MAAM,QAAQ,iBAAiB;AAC9B,QAAI,KAAK,cAAc,IAAI,YAAY,EAAE;AACxC,UAAK,cAAc,OAAO,YAAY;AACtC,4BACC,IAAI,MACH,oCAAoC,OAAO,OAAO,CAAC,GAAG,OAAO,KAAK,CAAC,aAAa,OAAO,YAAY,CAAC,GACpG,CACD;;MAEA,KAAK,YAAY,IAAK;AAEzB,QAAK,cAAc,IAAI,aAAa;IAAE;IAAS;IAAQ;IAAO,CAAC;AAE/D,QAAK,WAAW,IAAI,UAAU,CAAC,CAAC,OAAO,QAAiB;AACvD,iBAAa,MAAM;AACnB,SAAK,cAAc,OAAO,YAAY;AACtC,WAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,CAAC;KAC1D;IACD;OACI;AACN,SAAM,KAAK,WAAW,IAAI,UAAU,CAAC;AACrC,UAAO;;;CAIT,MAAM,MACL,YACA,QACA,MACA,OAAiC,MACjB;EAChB,MAAM,MAAM,KAAK,cAChB,QACA,MACA,OACA,MACA,WAAW,YACX;AACD,UAAQ,IAAI,iBAAiB,IAAI,UAAU,CAAC;AAC5C,UAAQ,IAAI,UAAU,IAAI,OAAO,GAAG;AACpC,QAAM,KAAK,WAAW,IAAI,UAAU,CAAC;;CAGtC,AAAU,qBAA6B;AACtC,OAAK,sBAAuB,KAAK,sBAAsB,MAAO;AAC9D,SAAO,KAAK;;CAGb,AAAU,cAAc,KAAkB;EAMzC,MAAM,KAAK,KAAK,cAAc,IAAI,IAAI,YAAY;AAClD,MAAI,IAAI;AACP,gBAAa,GAAG,MAAM;AACtB,QAAK,cAAc,OAAO,IAAI,YAAY;AAC1C,MAAG,QAAQ,IAAI;AACf;;AAID,OAAK,aAAa,WAAW,IAAI"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AbstractSecs2Item } from "./secs2item/AbstractSecs2Item.js";
|
|
2
|
+
|
|
3
|
+
//#region src/core/AbstractSecsMessage.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param stream The stream number.
|
|
7
|
+
* @param func The function number.
|
|
8
|
+
* @param wBit Whether the message is a request.
|
|
9
|
+
* @param body The body of the message.
|
|
10
|
+
* @param systemBytes The system bytes.
|
|
11
|
+
* @param deviceId The device ID.
|
|
12
|
+
*/
|
|
13
|
+
declare class SecsMessage {
|
|
14
|
+
readonly stream: number;
|
|
15
|
+
readonly func: number;
|
|
16
|
+
readonly wBit: boolean;
|
|
17
|
+
readonly body: AbstractSecs2Item | null;
|
|
18
|
+
readonly systemBytes: number;
|
|
19
|
+
readonly deviceId: number;
|
|
20
|
+
constructor(stream: number, func: number, wBit: boolean, body?: AbstractSecs2Item | null, systemBytes?: number,
|
|
21
|
+
// Typically 4 bytes, treated as integer for convenience if it fits, or Buffer
|
|
22
|
+
deviceId?: number);
|
|
23
|
+
/**
|
|
24
|
+
* @description Returns the SML representation of the message.
|
|
25
|
+
* @returns The SML representation.
|
|
26
|
+
*/
|
|
27
|
+
toSml(): string;
|
|
28
|
+
/**
|
|
29
|
+
* @description Returns the binary representation of the message.
|
|
30
|
+
* @returns The binary representation.
|
|
31
|
+
*/
|
|
32
|
+
toBuffer(): Buffer;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { SecsMessage };
|
|
36
|
+
//# sourceMappingURL=AbstractSecsMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AbstractSecsMessage.d.ts","names":[],"sources":["../../src/core/AbstractSecsMessage.ts"],"sourcesContent":[],"mappings":";;;;;;AAUA;;;;;;cAAa,WAAA;;;;iBAKW;;;kEAAA;;;;;;;;;;;;cAyBX"}
|