@2702rebels/ntcore 1.0.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 +28 -0
- package/README.md +3 -0
- package/dist/index.cjs +817 -0
- package/dist/index.d.cts +428 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +428 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +816 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +54 -0
- package/src/NT4Client.ts +935 -0
- package/src/NT4Spec.ts +291 -0
- package/src/NT4Subscription.ts +19 -0
- package/src/NT4Topic.ts +74 -0
- package/src/NT4Types.ts +70 -0
- package/src/NT4WebSocketAsync.ts +145 -0
- package/src/index.ts +4 -0
package/src/NT4Spec.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import type { SubscriptionOptions, TopicProperties } from "./NT4Types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Network constants.
|
|
5
|
+
*/
|
|
6
|
+
export const NetworkSpecs = {
|
|
7
|
+
/** NT4 port for unsecure (ws://) connections */
|
|
8
|
+
portUnsecure: 5810,
|
|
9
|
+
|
|
10
|
+
/** NT4 port for secure (wss://) connections */
|
|
11
|
+
portSecure: 5811,
|
|
12
|
+
|
|
13
|
+
/** Subprotocol v4.1 */
|
|
14
|
+
protocol41: "v4.1.networktables.first.wpi.edu",
|
|
15
|
+
|
|
16
|
+
/** Subprotocol v4.0 */
|
|
17
|
+
protocol40: "networktables.first.wpi.edu",
|
|
18
|
+
|
|
19
|
+
/** Subprotocol RTT */
|
|
20
|
+
protocolRTT: "rtt.networktables.first.wpi.edu",
|
|
21
|
+
|
|
22
|
+
/** v4.0 ping period in milliseconds */
|
|
23
|
+
pingPeriod40: 1000,
|
|
24
|
+
|
|
25
|
+
/** v4.0 ping timeout in milliseconds */
|
|
26
|
+
pingTimeout40: 3000,
|
|
27
|
+
|
|
28
|
+
/** v4.1 ping period in milliseconds */
|
|
29
|
+
pingPeriod41: 250,
|
|
30
|
+
|
|
31
|
+
/** v4.1 ping timeout in milliseconds */
|
|
32
|
+
pingTimeout41: 1000,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Supported data types mapped to protocol identifiers.
|
|
37
|
+
*
|
|
38
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#supported-data-types
|
|
39
|
+
*/
|
|
40
|
+
export const DataTypeCodes = {
|
|
41
|
+
boolean: 0,
|
|
42
|
+
double: 1,
|
|
43
|
+
int: 2,
|
|
44
|
+
float: 3,
|
|
45
|
+
string: 4,
|
|
46
|
+
json: 4,
|
|
47
|
+
raw: 5,
|
|
48
|
+
rpc: 5,
|
|
49
|
+
msgpack: 5,
|
|
50
|
+
protobuf: 5,
|
|
51
|
+
"boolean[]": 16,
|
|
52
|
+
"double[]": 17,
|
|
53
|
+
"int[]": 18,
|
|
54
|
+
"float[]": 19,
|
|
55
|
+
"string[]": 20,
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
58
|
+
export type DataType = keyof typeof DataTypeCodes;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Publish Request Message (`publish`)
|
|
62
|
+
*
|
|
63
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#publish-request-message-publish
|
|
64
|
+
*/
|
|
65
|
+
export type PublishRequestMessage = {
|
|
66
|
+
/**
|
|
67
|
+
* **Publish name**
|
|
68
|
+
*
|
|
69
|
+
* The topic name being published.
|
|
70
|
+
*/
|
|
71
|
+
name: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* **Published UID**
|
|
75
|
+
*
|
|
76
|
+
* A client-generated unique identifier for this publisher. Use the same UID later to unpublish.
|
|
77
|
+
* This is also the identifier that the client will use in MessagePack messages for this topic.
|
|
78
|
+
*/
|
|
79
|
+
pubuid: number;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* **Type of data**
|
|
83
|
+
*
|
|
84
|
+
* The requested data type (as a string).
|
|
85
|
+
* If the topic is newly created (e.g. there are no other publishers) this sets the value type.
|
|
86
|
+
* If the topic was previously published, this is ignored.
|
|
87
|
+
*/
|
|
88
|
+
type: string;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* **Properties**
|
|
92
|
+
*
|
|
93
|
+
* Initial topic properties.
|
|
94
|
+
* If the topic is newly created (e.g. there are no other publishers) this sets the topic properties.
|
|
95
|
+
* If the topic was previously published, this is ignored.
|
|
96
|
+
*/
|
|
97
|
+
properties: TopicProperties;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Publish Release Message (`unpublish`)
|
|
102
|
+
*
|
|
103
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#publish-release-message-unpublish
|
|
104
|
+
*/
|
|
105
|
+
export type PublishReleaseMessage = {
|
|
106
|
+
/**
|
|
107
|
+
* **Publisher UID**
|
|
108
|
+
*
|
|
109
|
+
* The same unique identifier passed to the `publish` message.
|
|
110
|
+
*/
|
|
111
|
+
pubuid: number;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Set Properties Message (`setproperties`)
|
|
116
|
+
*
|
|
117
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#set-properties-message-setproperties
|
|
118
|
+
*/
|
|
119
|
+
export type SetPropertiesMessage = {
|
|
120
|
+
/**
|
|
121
|
+
* **Topic name**
|
|
122
|
+
*/
|
|
123
|
+
name: string;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* **Properties to update**
|
|
127
|
+
*/
|
|
128
|
+
update: TopicProperties;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Subscribe Message (`subscribe`)
|
|
133
|
+
*
|
|
134
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#subscribe-message-subscribe
|
|
135
|
+
*/
|
|
136
|
+
export type SubscribeMessage = {
|
|
137
|
+
/**
|
|
138
|
+
* **Array of topic names or prefixes**
|
|
139
|
+
*
|
|
140
|
+
* One or more topic names or prefixes (if the `prefix` option is true) to start receiving messages for.
|
|
141
|
+
*/
|
|
142
|
+
topics: Array<string>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* **Subscription UID**
|
|
146
|
+
*
|
|
147
|
+
* A client-generated unique identifier for this subscription. Use the same UID later to unsubscribe.
|
|
148
|
+
*/
|
|
149
|
+
subuid: number;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* **Options**
|
|
153
|
+
*/
|
|
154
|
+
options: SubscriptionOptions;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Unsubscribe Message (`unsubscribe`)
|
|
159
|
+
*
|
|
160
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#unsubscribe-message-unsubscribe
|
|
161
|
+
*/
|
|
162
|
+
export type UnsubscribeMessage = {
|
|
163
|
+
/**
|
|
164
|
+
* **Subscription UID**
|
|
165
|
+
*
|
|
166
|
+
* The same unique identifier passed to the `subscribe` message.
|
|
167
|
+
*/
|
|
168
|
+
subuid: number;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Topic Announcement Message (`announce`)
|
|
173
|
+
*
|
|
174
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#topic-announcement-message-announce
|
|
175
|
+
*/
|
|
176
|
+
export type TopicAnnouncementMessage = {
|
|
177
|
+
/**
|
|
178
|
+
* **Topic name**
|
|
179
|
+
*/
|
|
180
|
+
name: string;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* **Topic ID**
|
|
184
|
+
*
|
|
185
|
+
* The identifier that the server will use in MessagePack messages for this topic.
|
|
186
|
+
*/
|
|
187
|
+
id: number;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* **Data type**
|
|
191
|
+
*
|
|
192
|
+
* The data type for the topic (as a string).
|
|
193
|
+
*/
|
|
194
|
+
type: string;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* **Publisher UID**
|
|
198
|
+
*
|
|
199
|
+
* If this message was sent in response to a publish message,
|
|
200
|
+
* the Publisher UID provided in that message. Otherwise absent.
|
|
201
|
+
*/
|
|
202
|
+
pubuid?: number;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* **Properties**
|
|
206
|
+
*/
|
|
207
|
+
properties: TopicProperties;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Topic Removed Message (`unannounce`)
|
|
212
|
+
*
|
|
213
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#topic-removed-message-unannounce
|
|
214
|
+
*/
|
|
215
|
+
export type TopicRemovedMessage = {
|
|
216
|
+
/**
|
|
217
|
+
* **Topic name**
|
|
218
|
+
*/
|
|
219
|
+
name: string;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* **Topic ID**
|
|
223
|
+
*
|
|
224
|
+
* The identifier that the server was using for value updates.
|
|
225
|
+
*/
|
|
226
|
+
id: number;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Properties Update Message (`properties`)
|
|
231
|
+
*
|
|
232
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#properties-update-message-properties
|
|
233
|
+
*/
|
|
234
|
+
export type PropertiesUpdateMessage = {
|
|
235
|
+
/**
|
|
236
|
+
* **Topic name**
|
|
237
|
+
*/
|
|
238
|
+
name: string;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* **Acknowledgement**
|
|
242
|
+
*
|
|
243
|
+
* `True` if this message is in response to a `setproperties` message from the same client.
|
|
244
|
+
* Otherwise absent.
|
|
245
|
+
*/
|
|
246
|
+
ack: boolean;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* **Properties to set**
|
|
250
|
+
*/
|
|
251
|
+
update: TopicProperties;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Supported text data frames.
|
|
256
|
+
*
|
|
257
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#text-data-frames
|
|
258
|
+
*/
|
|
259
|
+
export type TextDataFrame =
|
|
260
|
+
| {
|
|
261
|
+
method: "publish";
|
|
262
|
+
params: PublishRequestMessage;
|
|
263
|
+
}
|
|
264
|
+
| {
|
|
265
|
+
method: "unpublish";
|
|
266
|
+
params: PublishReleaseMessage;
|
|
267
|
+
}
|
|
268
|
+
| {
|
|
269
|
+
method: "setproperties";
|
|
270
|
+
params: SetPropertiesMessage;
|
|
271
|
+
}
|
|
272
|
+
| {
|
|
273
|
+
method: "subscribe";
|
|
274
|
+
params: SubscribeMessage;
|
|
275
|
+
}
|
|
276
|
+
| {
|
|
277
|
+
method: "unsubscribe";
|
|
278
|
+
params: UnsubscribeMessage;
|
|
279
|
+
}
|
|
280
|
+
| {
|
|
281
|
+
method: "announce";
|
|
282
|
+
params: TopicAnnouncementMessage;
|
|
283
|
+
}
|
|
284
|
+
| {
|
|
285
|
+
method: "unannounce";
|
|
286
|
+
params: TopicRemovedMessage;
|
|
287
|
+
}
|
|
288
|
+
| {
|
|
289
|
+
method: "properties";
|
|
290
|
+
params: PropertiesUpdateMessage;
|
|
291
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SubscriptionOptions } from "./NT4Types";
|
|
2
|
+
|
|
3
|
+
/** Contains information about NT4 subscription. */
|
|
4
|
+
export class NT4Subscription {
|
|
5
|
+
/** Subscription UID. */
|
|
6
|
+
public readonly uid: number;
|
|
7
|
+
|
|
8
|
+
/** Subscription options. */
|
|
9
|
+
public readonly options: SubscriptionOptions;
|
|
10
|
+
|
|
11
|
+
/** Subscribed topic names or prefixes. */
|
|
12
|
+
public readonly topics: Set<string>;
|
|
13
|
+
|
|
14
|
+
constructor(uid: number, topics: Iterable<string>, options?: SubscriptionOptions) {
|
|
15
|
+
this.uid = uid;
|
|
16
|
+
this.topics = new Set(typeof topics === "string" ? [topics] : topics);
|
|
17
|
+
this.options = options ?? {};
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/NT4Topic.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { DataTypeCodes } from "./NT4Spec";
|
|
2
|
+
|
|
3
|
+
import type { DataType } from "./NT4Spec";
|
|
4
|
+
import type { TopicProperties } from "./NT4Types";
|
|
5
|
+
|
|
6
|
+
/** Contains information about NT4 topic. */
|
|
7
|
+
export class NT4Topic {
|
|
8
|
+
/** Topic UID. */
|
|
9
|
+
public readonly uid: number;
|
|
10
|
+
|
|
11
|
+
/** Topic name. */
|
|
12
|
+
public readonly name: string;
|
|
13
|
+
|
|
14
|
+
/** Topic data type. */
|
|
15
|
+
public readonly type: string;
|
|
16
|
+
|
|
17
|
+
/** Topic properties. */
|
|
18
|
+
public readonly properties: TopicProperties;
|
|
19
|
+
|
|
20
|
+
/** Publisher UID. */
|
|
21
|
+
public pubuid?: number;
|
|
22
|
+
|
|
23
|
+
/** Last retained value. */
|
|
24
|
+
public retainedValue?: unknown;
|
|
25
|
+
|
|
26
|
+
/** Timestamp of {@link retainedValue}. */
|
|
27
|
+
public retainedTimestamp?: number;
|
|
28
|
+
|
|
29
|
+
constructor(uid: number, name: string, type: string, properties?: TopicProperties, pubuid?: number) {
|
|
30
|
+
this.uid = uid;
|
|
31
|
+
this.name = name;
|
|
32
|
+
this.type = type;
|
|
33
|
+
this.properties = properties ?? {};
|
|
34
|
+
this.pubuid = pubuid;
|
|
35
|
+
this.typeCode = DataTypeCodes[type as DataType] ?? 5; // defaults to binary
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Merges properties with this topic. */
|
|
39
|
+
public mergeProperties(properties: TopicProperties) {
|
|
40
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
41
|
+
if (value === null) {
|
|
42
|
+
delete this.properties[key];
|
|
43
|
+
} else {
|
|
44
|
+
this.properties[key] = value;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Updates retained value if the incoming timestamp is newer (greater-than or equal-to). */
|
|
50
|
+
public updateRetainedValue(value: unknown, timestamp: number) {
|
|
51
|
+
if (this.retainedTimestamp == null || timestamp >= this.retainedTimestamp) {
|
|
52
|
+
this.retainedValue = value;
|
|
53
|
+
this.retainedTimestamp = timestamp;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Removes retained value except for "weak" default value. */
|
|
58
|
+
public removeRetainedValue() {
|
|
59
|
+
if (this.retainedTimestamp != 0) {
|
|
60
|
+
this.retainedValue = undefined;
|
|
61
|
+
this.retainedTimestamp = undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Resets retained value timestamp except for "weak" default value. */
|
|
66
|
+
public resetRetainedTimestamp() {
|
|
67
|
+
if (this.retainedTimestamp != null && this.retainedTimestamp > 1) {
|
|
68
|
+
this.retainedTimestamp = 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Topic type code. For internal use only. */
|
|
73
|
+
public readonly typeCode: number;
|
|
74
|
+
}
|
package/src/NT4Types.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topic properties, including well-known properties
|
|
3
|
+
*
|
|
4
|
+
* https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#properties
|
|
5
|
+
*/
|
|
6
|
+
export type TopicProperties = Record<string, unknown> & {
|
|
7
|
+
/**
|
|
8
|
+
* **Persistent flag**
|
|
9
|
+
*
|
|
10
|
+
* If `true`, the last set value will be periodically saved to persistent storage on the server
|
|
11
|
+
* and be restored during server startup. Topics with this property set to `true` will not
|
|
12
|
+
* be deleted by the server when the last publisher stops publishing.
|
|
13
|
+
*/
|
|
14
|
+
persistent?: boolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* **Retained flag**
|
|
18
|
+
*
|
|
19
|
+
* Topics with this property set to `true` will not be deleted by the server when
|
|
20
|
+
* the last publisher stops publishing.
|
|
21
|
+
*/
|
|
22
|
+
retained?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* **Cached flag**
|
|
26
|
+
*
|
|
27
|
+
* If `false`, the server and clients will not store the value of the topic.
|
|
28
|
+
* This means that only value updates will be available for the topic.
|
|
29
|
+
*/
|
|
30
|
+
cached?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Subscription options, including well-known options
|
|
35
|
+
*/
|
|
36
|
+
export type SubscriptionOptions = Record<string, unknown> & {
|
|
37
|
+
/**
|
|
38
|
+
* **Periodic sweep time (in seconds)**
|
|
39
|
+
*
|
|
40
|
+
* How frequently the server should send changes. The server may send more frequently than this
|
|
41
|
+
* (e.g. use a combined minimum period for all values) or apply a restricted range to this value
|
|
42
|
+
* The default if unspecified is 100 ms (same as NT 3.0).
|
|
43
|
+
*/
|
|
44
|
+
periodic?: number;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* **All changes flag**
|
|
48
|
+
*
|
|
49
|
+
* If `true`, the server should send all value changes over the wire. If `false`, only
|
|
50
|
+
* the most recent value is sent (same as NT 3.0 behavior). If not specified, defaults to `false`.
|
|
51
|
+
*/
|
|
52
|
+
all?: boolean;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* **No value changes flag**
|
|
56
|
+
*
|
|
57
|
+
* If `true`, the server should not send any value changes over the wire regardless of other options.
|
|
58
|
+
* This is useful for only getting topic announcements. If `false`, value changes are sent in accordance
|
|
59
|
+
* with other options. If not specified, defaults to `false`.
|
|
60
|
+
*/
|
|
61
|
+
topicsonly?: boolean;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* **Prefix flag**
|
|
65
|
+
*
|
|
66
|
+
* If `true`, any topic starting with the name in the subscription topics list is subscribed to,
|
|
67
|
+
* not just exact matches. If not specified, defaults to `false`.
|
|
68
|
+
*/
|
|
69
|
+
prefix?: boolean;
|
|
70
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { NetworkSpecs } from "./NT4Spec";
|
|
2
|
+
|
|
3
|
+
import type { TextDataFrame } from "./NT4Spec";
|
|
4
|
+
|
|
5
|
+
// version 4.1 -- v4.1.networktables.first.wpi.edu
|
|
6
|
+
// version 4.0 -- networktables.first.wpi.edu
|
|
7
|
+
const PROTOCOL_DATA = [NetworkSpecs.protocol41, NetworkSpecs.protocol40];
|
|
8
|
+
|
|
9
|
+
// https://github.com/wpilibsuite/allwpilib/blob/main/ntcore/doc/networktables4.adoc#rtt-subprotocol
|
|
10
|
+
const PROTOCOL_RTT = [NetworkSpecs.protocolRTT];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A wrapper for `WebSocket` that implements async `connect` and `close` methods.
|
|
14
|
+
*
|
|
15
|
+
* This class manages the lifecycle of the raw websocket internally, creating and
|
|
16
|
+
* destroying the websocket as necessary when establishing and closing the connection.
|
|
17
|
+
*/
|
|
18
|
+
export class NT4WebSocketAsync {
|
|
19
|
+
private url: string;
|
|
20
|
+
private rtt: boolean;
|
|
21
|
+
private socket: WebSocket | null = null;
|
|
22
|
+
private onMessage?: (_: MessageEvent) => void;
|
|
23
|
+
private onClose?: (_: CloseEvent) => void;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a `WebSocket` wrapper to be used for NT4 communication channel.
|
|
27
|
+
* This constructor does not initiate the connection, see `connect`.
|
|
28
|
+
*
|
|
29
|
+
* @param address server network address
|
|
30
|
+
* @param port sever port
|
|
31
|
+
* @param clientId client identifier
|
|
32
|
+
* @param options connection options
|
|
33
|
+
*/
|
|
34
|
+
constructor(
|
|
35
|
+
address: string,
|
|
36
|
+
port: number,
|
|
37
|
+
clientId: string,
|
|
38
|
+
options: {
|
|
39
|
+
/** Callback invoked when socket receives message. */
|
|
40
|
+
onMessage?: (ev: MessageEvent) => void;
|
|
41
|
+
/** Callback invoked when socket closes. */
|
|
42
|
+
onClose?: (ev: CloseEvent) => void;
|
|
43
|
+
/** `true` to use secure protocol/port; `false` to use unsecure protocol/port (default). */
|
|
44
|
+
secure?: boolean;
|
|
45
|
+
/** `true` to create RTT subprotocol channel; `false` to create default data channel (default). */
|
|
46
|
+
rtt?: boolean;
|
|
47
|
+
}
|
|
48
|
+
) {
|
|
49
|
+
const schema = options.secure ? "wss" : "ws";
|
|
50
|
+
|
|
51
|
+
this.url = `${schema}://${address}:${port}/nt/${clientId}`;
|
|
52
|
+
this.rtt = options.rtt ?? false;
|
|
53
|
+
this.onMessage = options.onMessage;
|
|
54
|
+
this.onClose = options.onClose;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private onMessageLocal(ev: MessageEvent, source: WebSocket) {
|
|
58
|
+
if (this.onMessage && this.socket === source) {
|
|
59
|
+
this.onMessage(ev);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private onCloseLocal(ev: CloseEvent, source: WebSocket) {
|
|
64
|
+
if (this.onClose && this.socket === source) {
|
|
65
|
+
this.onClose(ev);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Returns `true` if the socket is in the connected state. */
|
|
70
|
+
public get connected() {
|
|
71
|
+
return this.socket != null && this.socket.readyState === WebSocket.OPEN;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Returns `true` if the socket is in the connecting state. */
|
|
75
|
+
public get connecting() {
|
|
76
|
+
return this.socket != null && this.socket.readyState === WebSocket.CONNECTING;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Returns the subprotocol negotiated with the server. */
|
|
80
|
+
public get protocol() {
|
|
81
|
+
return this.socket?.protocol;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Sends text data frame (JSON serialized). */
|
|
85
|
+
public sendText(data: TextDataFrame | Array<TextDataFrame>) {
|
|
86
|
+
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
87
|
+
this.socket.send(JSON.stringify(Array.isArray(data) ? data : [data]));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Sends binary data frame. */
|
|
92
|
+
public sendBinary(data: Uint8Array) {
|
|
93
|
+
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
94
|
+
this.socket.send(data);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Asynchronously terminates the connection.
|
|
100
|
+
*
|
|
101
|
+
* @param code optional code
|
|
102
|
+
* @param reason optional reason
|
|
103
|
+
*/
|
|
104
|
+
public async close(code?: number, reason?: string): Promise<void> {
|
|
105
|
+
if (this.socket) {
|
|
106
|
+
const socket = this.socket;
|
|
107
|
+
this.socket = null;
|
|
108
|
+
|
|
109
|
+
// websocket is closing asynchronously, we need to wait
|
|
110
|
+
// for the corresponding onclose to fire:
|
|
111
|
+
// attach another listener here and promisify
|
|
112
|
+
return new Promise((resolve) => {
|
|
113
|
+
socket.addEventListener("close", () => {
|
|
114
|
+
resolve();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
socket.close(code, reason);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return Promise.resolve();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Asynchronously initiates the connection. */
|
|
125
|
+
public async connect(): Promise<void> {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const socket = new WebSocket(this.url, this.rtt ? PROTOCOL_RTT : PROTOCOL_DATA);
|
|
128
|
+
|
|
129
|
+
socket.binaryType = "arraybuffer";
|
|
130
|
+
|
|
131
|
+
this.socket = socket;
|
|
132
|
+
|
|
133
|
+
socket.addEventListener("message", (_) => this.onMessageLocal(_, socket));
|
|
134
|
+
socket.addEventListener("close", (_) => this.onCloseLocal(_, socket));
|
|
135
|
+
|
|
136
|
+
socket.onopen = () => {
|
|
137
|
+
resolve();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
socket.onerror = (event) => {
|
|
141
|
+
reject(event);
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
package/src/index.ts
ADDED