sen-ether-client 0.1.6 → 0.2.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/API.md +75 -7
- package/README.md +54 -2
- package/index.js +26 -0
- package/lib/bus.js +261 -1
- package/lib/client.js +1002 -96
- package/lib/discovery.js +42 -1
- package/lib/sen.js +101 -13
- package/lib/values.js +14 -0
- package/package.json +2 -2
package/API.md
CHANGED
|
@@ -8,7 +8,7 @@ import { Sen, SenInterest, SenRemoteObject } from 'sen-ether-client';
|
|
|
8
8
|
|
|
9
9
|
## Compatibility
|
|
10
10
|
|
|
11
|
-
`sen-ether-client@0.1.x`
|
|
11
|
+
`sen-ether-client@0.1.x` and `sen-ether-client@0.2.x` support:
|
|
12
12
|
|
|
13
13
|
- kernel protocol `9`
|
|
14
14
|
- ether protocol `2`
|
|
@@ -39,9 +39,25 @@ Connection options:
|
|
|
39
39
|
- `interfaceAddress`: local interface address or interface name for multicast
|
|
40
40
|
discovery.
|
|
41
41
|
- `tcpHub`: optional SEN TCP discovery hub as `host:port`. If omitted,
|
|
42
|
-
multicast discovery is used.
|
|
42
|
+
multicast discovery is used. When combined with `session`, the client opens
|
|
43
|
+
a local Ether listener, sends presence beams to the hub and connects to
|
|
44
|
+
compatible peers announced by the hub.
|
|
43
45
|
- `session`: optional SEN session name. If omitted, `Sen` can use queries for
|
|
44
|
-
different sessions and connects to each one on demand.
|
|
46
|
+
different sessions and connects to each one on demand. When provided, the
|
|
47
|
+
client also acts as an active Ether process and announces itself through the
|
|
48
|
+
selected discovery transport.
|
|
49
|
+
- `multicastDiscovery`: enable active multicast presence beaming when no
|
|
50
|
+
`tcpHub` is configured. Defaults to `true`.
|
|
51
|
+
- `group`: multicast discovery group. Defaults to `239.255.0.44`.
|
|
52
|
+
- `bindAddress`: optional multicast discovery bind address.
|
|
53
|
+
- `listen`: enable the local Ether TCP listener. Defaults to `true` for active
|
|
54
|
+
hub sessions.
|
|
55
|
+
- `listenHost`: host/interface for the local Ether listener. Defaults to
|
|
56
|
+
`0.0.0.0`.
|
|
57
|
+
- `listenPort`: local Ether listener port. Defaults to `0` so the OS picks one.
|
|
58
|
+
- `advertisedHost`: host advertised in discovery beams. Defaults to the
|
|
59
|
+
selected interface address.
|
|
60
|
+
- `beamPeriodMs`: active discovery beam period. Defaults to `1000`.
|
|
45
61
|
- `timeout`: discovery and operation timeout in ms.
|
|
46
62
|
- `discoverySettleMs`: discovery settle time after the first process is found.
|
|
47
63
|
Defaults to `100`.
|
|
@@ -67,7 +83,12 @@ variable as its multicast default:
|
|
|
67
83
|
- `SEN_ETHER_DISCOVERY_PORT`
|
|
68
84
|
|
|
69
85
|
Multicast group, bind address and interface selection are explicit `sen-ether-client`
|
|
70
|
-
options, not SEN environment variables.
|
|
86
|
+
options, not SEN environment variables. When no `interfaceAddress` is provided,
|
|
87
|
+
multicast discovery joins every local IPv4 interface visible to Node.js. If a
|
|
88
|
+
SEN producer on the same host sends discovery through a physical interface that
|
|
89
|
+
does not loop multicast packets back locally, discovery can still return no
|
|
90
|
+
processes; in that case run the producer discovery on `lo`, pass the matching
|
|
91
|
+
`interfaceAddress`, or use SEN TCP discovery.
|
|
71
92
|
|
|
72
93
|
Preferred multi-session usage:
|
|
73
94
|
|
|
@@ -81,9 +102,30 @@ const world = await sen.interest('SELECT * FROM world1.environment');
|
|
|
81
102
|
TCP discovery hub usage:
|
|
82
103
|
|
|
83
104
|
```js
|
|
84
|
-
const sen = await Sen.connect({
|
|
105
|
+
const sen = await Sen.connect({
|
|
106
|
+
session: 'hmi',
|
|
107
|
+
tcpHub: '127.0.0.1:65222'
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The TCP discovery hub forwards fixed-size presence beams only. Bus messages are
|
|
112
|
+
sent over direct process TCP connections between peers, so Node.js producers
|
|
113
|
+
and consumers must advertise reachable `listenHost`/`advertisedHost` endpoints.
|
|
114
|
+
|
|
115
|
+
Multicast discovery usage:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
const sen = await Sen.connect({
|
|
119
|
+
session: 'hmi',
|
|
120
|
+
interfaceAddress: '127.0.0.1',
|
|
121
|
+
listenHost: '127.0.0.1',
|
|
122
|
+
advertisedHost: '127.0.0.1'
|
|
123
|
+
});
|
|
85
124
|
```
|
|
86
125
|
|
|
126
|
+
On multi-interface machines, set `interfaceAddress` so multicast beams are sent
|
|
127
|
+
and received on the intended network device.
|
|
128
|
+
|
|
87
129
|
Explicit single-session usage is still supported:
|
|
88
130
|
|
|
89
131
|
```js
|
|
@@ -110,6 +152,8 @@ Main methods:
|
|
|
110
152
|
|
|
111
153
|
- `await sen.connect(options)`
|
|
112
154
|
- `await sen.interest(query, options)`
|
|
155
|
+
- `await sen.publishObjects(busName, objects, options)`
|
|
156
|
+
- `await sen.removePublishedObjects(busName, objects, options)`
|
|
113
157
|
- `await sen.session(name)`
|
|
114
158
|
- `await sen.discoverBuses(options)`
|
|
115
159
|
- `sen.listSessions()`
|
|
@@ -140,6 +184,30 @@ const diagnostics = await sen.session('hmi').then(hmi => hmi.bus('diagnostics'))
|
|
|
140
184
|
does open a lightweight process connection per discovered session, because SEN
|
|
141
185
|
presence beams announce sessions/processes but not the bus list.
|
|
142
186
|
|
|
187
|
+
Publishing local objects:
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
const sen = await Sen.connect({
|
|
191
|
+
session: 'session',
|
|
192
|
+
tcpHub: '127.0.0.1:65222'
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await sen.publishObjects('session.bus', [{
|
|
196
|
+
name: 'demo-counter',
|
|
197
|
+
className: 'demo.Counter',
|
|
198
|
+
properties: {
|
|
199
|
+
label: 'Demo Counter',
|
|
200
|
+
count: 1,
|
|
201
|
+
running: true
|
|
202
|
+
}
|
|
203
|
+
}]);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
When no `spec` is provided, `sen-ether-client` infers a simple ClassTypeSpec
|
|
207
|
+
from scalar `properties`. For objects with nested structs, sequences, enums or
|
|
208
|
+
aliases, pass the exact SEN `spec` and dependent `types` so consumers can decode
|
|
209
|
+
the state with the same model as native SEN producers.
|
|
210
|
+
|
|
143
211
|
Main events:
|
|
144
212
|
|
|
145
213
|
- `connect`
|
|
@@ -183,8 +251,8 @@ For browser gateways or high-frequency telemetry, request only the properties
|
|
|
183
251
|
you need and emit batches instead of one JS event per property update:
|
|
184
252
|
|
|
185
253
|
```js
|
|
186
|
-
const tracks = await sen.interest('SELECT
|
|
187
|
-
properties: ['latitude', 'longitude', 'altitude', '
|
|
254
|
+
const tracks = await sen.interest('SELECT bus.Track FROM hmi.loadtest', {
|
|
255
|
+
properties: ['latitude', 'longitude', 'altitude', 'heading'],
|
|
188
256
|
changeMode: 'batch',
|
|
189
257
|
batchIntervalMs: 16,
|
|
190
258
|
batchMaxSize: 1000,
|
package/README.md
CHANGED
|
@@ -39,6 +39,7 @@ versions, not to a specific SEN release name.
|
|
|
39
39
|
| sen-ether-client | Kernel protocol | Ether protocol |
|
|
40
40
|
| --- | ---: | ---: |
|
|
41
41
|
| 0.1.x | 9 | 2 |
|
|
42
|
+
| 0.2.x | 9 | 2 |
|
|
42
43
|
|
|
43
44
|
If a remote kernel reports another kernel or ether protocol version during the
|
|
44
45
|
SEN handshake, `sen-ether-client` treats it as incompatible until that protocol version
|
|
@@ -61,6 +62,10 @@ visible SEN processes:
|
|
|
61
62
|
const sen = await Sen.connect();
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
When a `session` is provided, the client also behaves as an active Ether
|
|
66
|
+
process: it opens a TCP listener, announces its presence through multicast
|
|
67
|
+
discovery, and connects to compatible peers announced on the same session.
|
|
68
|
+
|
|
64
69
|
If your SEN ether discovery port is configured through SEN's environment,
|
|
65
70
|
`sen-ether-client` reads the same variable:
|
|
66
71
|
|
|
@@ -80,10 +85,28 @@ explicitly:
|
|
|
80
85
|
|
|
81
86
|
```js
|
|
82
87
|
const sen = await Sen.connect({
|
|
88
|
+
session: 'hmi',
|
|
83
89
|
tcpHub: '127.0.0.1:65222'
|
|
84
90
|
});
|
|
85
91
|
```
|
|
86
92
|
|
|
93
|
+
With a `session` and `tcpHub`, `sen-ether-client` acts as an active Ether
|
|
94
|
+
process: it opens a TCP listener, announces a SEN presence beam to the hub, and
|
|
95
|
+
connects to compatible peers announced by the hub. This allows Node.js
|
|
96
|
+
producers and consumers to discover each other without a native SEN process
|
|
97
|
+
brokering their bus messages.
|
|
98
|
+
|
|
99
|
+
For local multicast tests, select loopback explicitly:
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const sen = await Sen.connect({
|
|
103
|
+
session: 'hmi',
|
|
104
|
+
interfaceAddress: '127.0.0.1',
|
|
105
|
+
listenHost: '127.0.0.1',
|
|
106
|
+
advertisedHost: '127.0.0.1'
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
87
110
|
Connected sessions are monitored through SEN ether presence beams. If the
|
|
88
111
|
remote process stops announcing itself for `presenceTimeoutMs` milliseconds
|
|
89
112
|
(default `5000`), the client closes the stale connection and restarts the
|
|
@@ -152,8 +175,8 @@ For browser gateways or high-frequency telemetry, batch changes and decode only
|
|
|
152
175
|
the properties needed by the UI:
|
|
153
176
|
|
|
154
177
|
```js
|
|
155
|
-
const tracks = await sen.interest('SELECT
|
|
156
|
-
properties: ['latitude', 'longitude', 'altitude', '
|
|
178
|
+
const tracks = await sen.interest('SELECT bus.Track FROM hmi.loadtest', {
|
|
179
|
+
properties: ['latitude', 'longitude', 'altitude', 'heading'],
|
|
157
180
|
changeMode: 'batch',
|
|
158
181
|
coalesce: true
|
|
159
182
|
});
|
|
@@ -178,6 +201,35 @@ const firstAircraft = await tracks.waitFor(
|
|
|
178
201
|
);
|
|
179
202
|
```
|
|
180
203
|
|
|
204
|
+
## Publish objects
|
|
205
|
+
|
|
206
|
+
`sen-ether-client` can also act as a lightweight producer. The producer joins
|
|
207
|
+
the bus, publishes local objects to remote interests, and answers type/state
|
|
208
|
+
requests for those objects.
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
import { Sen } from 'sen-ether-client';
|
|
212
|
+
|
|
213
|
+
const sen = await Sen.connect({
|
|
214
|
+
session: 'session',
|
|
215
|
+
tcpHub: '127.0.0.1:65222'
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
await sen.publishObjects('session.bus', {
|
|
219
|
+
name: 'demo-counter',
|
|
220
|
+
className: 'demo.Counter',
|
|
221
|
+
properties: {
|
|
222
|
+
label: 'Demo Counter',
|
|
223
|
+
count: 1,
|
|
224
|
+
running: true
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
For exact SEN typing, pass a `spec` on each object and any dependent custom
|
|
230
|
+
types through `types`. This is required for structured values such as structs,
|
|
231
|
+
sequences, enums and variants.
|
|
232
|
+
|
|
181
233
|
## Objects
|
|
182
234
|
|
|
183
235
|
Read and write properties:
|
package/index.js
CHANGED
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
* @property {string} [tcpHub] Optional SEN TCP discovery hub as `host:port`. If omitted, multicast discovery is used.
|
|
32
32
|
* @property {string} [session] Optional SEN session name. Omit it to let
|
|
33
33
|
* `interest(query)` connect to the session named in the query.
|
|
34
|
+
* @property {boolean} [multicastDiscovery=true] Enable active multicast presence beaming when no TCP hub is configured.
|
|
35
|
+
* @property {string} [group='239.255.0.44'] Multicast discovery group.
|
|
36
|
+
* @property {string} [bindAddress] Optional multicast discovery bind address.
|
|
34
37
|
* @property {string} [app] Remote process appName substring filter.
|
|
35
38
|
* @property {number} [timeout=3000] Discovery and operation timeout in ms.
|
|
36
39
|
* @property {number} [discoverySettleMs=100] Discovery settle time after the first process is found.
|
|
@@ -45,6 +48,11 @@
|
|
|
45
48
|
* @property {number} [presenceTimeoutMs=5000] Close and reconnect when the connected SEN process stops announcing presence beams. `0` disables it.
|
|
46
49
|
* @property {number} [presenceCheckIntervalMs=1000] Presence watchdog check interval in ms.
|
|
47
50
|
* @property {string} [interfaceAddress] Local interface address or interface name for multicast discovery.
|
|
51
|
+
* @property {boolean} [listen=true] Enable the local Ether TCP listener for active discovery.
|
|
52
|
+
* @property {string} [listenHost='0.0.0.0'] Local host/interface for the Ether listener.
|
|
53
|
+
* @property {number} [listenPort=0] Local Ether listener port. `0` lets the OS choose.
|
|
54
|
+
* @property {string} [advertisedHost] Host advertised in TCP discovery beams.
|
|
55
|
+
* @property {number} [beamPeriodMs=1000] Active discovery beam period in ms.
|
|
48
56
|
* @property {object} [target] Already discovered/direct SEN target.
|
|
49
57
|
*/
|
|
50
58
|
|
|
@@ -62,6 +70,24 @@
|
|
|
62
70
|
* @property {boolean} [coalesce=false] Keep only latest queued change per object/property.
|
|
63
71
|
*/
|
|
64
72
|
|
|
73
|
+
/**
|
|
74
|
+
* @typedef {object} SenPublishedObject
|
|
75
|
+
* @property {string} name SEN object name.
|
|
76
|
+
* @property {string} className SEN class name.
|
|
77
|
+
* @property {number} [id] Optional stable object id. Defaults to CRC32(name).
|
|
78
|
+
* @property {number} [typeHash] Optional class hash. Defaults to CRC32(className).
|
|
79
|
+
* @property {object} [properties] Current object property values.
|
|
80
|
+
* @property {object} [snapshot] Alias for properties.
|
|
81
|
+
* @property {object} [spec] Optional SEN ClassTypeSpec. If omitted, a simple class spec is inferred from properties.
|
|
82
|
+
* @property {bigint|number|string} [timestamp] Optional SEN timestamp in ns.
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @typedef {object} SenPublishOptions
|
|
87
|
+
* @property {Map<string, object>|Record<string, object>|object[]} [types] Extra SEN type specs required by object properties.
|
|
88
|
+
* @property {number} [participantId] Optional local participant id for a newly joined bus.
|
|
89
|
+
*/
|
|
90
|
+
|
|
65
91
|
/**
|
|
66
92
|
* @typedef {object} SenListBusesOptions
|
|
67
93
|
* @property {boolean} [qualified=false] Return session-qualified bus names.
|
package/lib/bus.js
CHANGED
|
@@ -67,9 +67,36 @@ function readU32List(reader) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function writeU32List(writer, values = []) {
|
|
70
|
+
writer.writeUInt32(values.length);
|
|
71
|
+
for (const value of values) {
|
|
72
|
+
writer.writeUInt32(value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function enumKey(values, value, label) {
|
|
77
|
+
const index = values.indexOf(value);
|
|
78
|
+
if (index < 0) {
|
|
79
|
+
throw new RangeError(`unknown SEN ${label} value: ${value}`);
|
|
80
|
+
}
|
|
81
|
+
return index;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function writeSequence(writer, values = [], writeItem) {
|
|
70
85
|
writer.writeUInt32(values.length);
|
|
71
86
|
for (const value of values) {
|
|
72
|
-
writer
|
|
87
|
+
writeItem(writer, value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function writeStringList(writer, values = []) {
|
|
92
|
+
writeSequence(writer, values, (itemWriter, value) => itemWriter.writeString(value));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function writeOptional(writer, value, writeValue) {
|
|
96
|
+
const present = value !== null && value !== undefined;
|
|
97
|
+
writer.writeBool(present);
|
|
98
|
+
if (present) {
|
|
99
|
+
writeValue(writer, value);
|
|
73
100
|
}
|
|
74
101
|
}
|
|
75
102
|
|
|
@@ -333,6 +360,224 @@ function readTypeSpecResponse(reader) {
|
|
|
333
360
|
}
|
|
334
361
|
}
|
|
335
362
|
|
|
363
|
+
function writeEnumeratorSpec(writer, item = {}) {
|
|
364
|
+
writer.writeString(item.name ?? '');
|
|
365
|
+
writer.writeUInt32(item.key ?? 0);
|
|
366
|
+
writer.writeString(item.description ?? '');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function writeEnumTypeSpec(writer, value = {}) {
|
|
370
|
+
writeSequence(writer, value.enums ?? [], writeEnumeratorSpec);
|
|
371
|
+
writer.writeUInt8(enumKey(INTEGRAL_TYPE, value.storageType ?? 'uint32Type', 'IntegralType'));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function writeNumericType(writer, value = {}) {
|
|
375
|
+
const type = value.type ?? 'RealType';
|
|
376
|
+
writer.writeUInt32(enumKey(NUMERIC_TYPE, type, 'NumericType'));
|
|
377
|
+
if (type === 'IntegralType') {
|
|
378
|
+
writer.writeUInt8(enumKey(INTEGRAL_TYPE, value.value ?? 'int32Type', 'IntegralType'));
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (type === 'RealType') {
|
|
382
|
+
writer.writeUInt8(enumKey(REAL_TYPE, value.value ?? 'float64Type', 'RealType'));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
throw new TypeError(`unhandled SEN NumericType alternative: ${type}`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function writeUnitInfo(writer, value = {}) {
|
|
389
|
+
writer.writeString(value.name ?? '');
|
|
390
|
+
writer.writeString(value.abbreviation ?? '');
|
|
391
|
+
writer.writeUInt8(enumKey(UNIT_CATEGORY, value.category ?? 'length', 'UnitCat'));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function writeQuantityTypeSpec(writer, value = {}) {
|
|
395
|
+
writeNumericType(writer, value.elementType ?? { type: 'RealType', value: 'float64Type' });
|
|
396
|
+
writeUnitInfo(writer, value.unit ?? {});
|
|
397
|
+
writeOptional(writer, value.minValue, (itemWriter, item) => itemWriter.writeFloat64(Number(item)));
|
|
398
|
+
writeOptional(writer, value.maxValue, (itemWriter, item) => itemWriter.writeFloat64(Number(item)));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function writeSequenceTypeSpec(writer, value = {}) {
|
|
402
|
+
writer.writeString(value.elementType ?? '');
|
|
403
|
+
writeOptional(writer, value.maxSize, (itemWriter, item) => itemWriter.writeUInt64(item));
|
|
404
|
+
writer.writeBool(Boolean(value.fixedSize));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function writeStructTypeFieldSpec(writer, item = {}) {
|
|
408
|
+
writer.writeString(item.name ?? '');
|
|
409
|
+
writer.writeString(item.description ?? '');
|
|
410
|
+
writer.writeString(item.type ?? '');
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function writeStructTypeSpec(writer, value = {}) {
|
|
414
|
+
writeSequence(writer, value.fields ?? [], writeStructTypeFieldSpec);
|
|
415
|
+
writer.writeString(value.parent ?? '');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function writeVariantTypeFieldSpec(writer, item = {}) {
|
|
419
|
+
writer.writeUInt32(item.key ?? 0);
|
|
420
|
+
writer.writeString(item.description ?? '');
|
|
421
|
+
writer.writeString(item.type ?? '');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function writeVariantTypeSpec(writer, value = {}) {
|
|
425
|
+
writeSequence(writer, value.fields ?? [], writeVariantTypeFieldSpec);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function writeAliasTypeSpec(writer, value = {}) {
|
|
429
|
+
writer.writeString(value.aliasedType ?? '');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function writeOptionalTypeSpec(writer, value = {}) {
|
|
433
|
+
writer.writeString(value.type ?? '');
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function writeArgSpec(writer, item = {}) {
|
|
437
|
+
writer.writeString(item.name ?? '');
|
|
438
|
+
writer.writeString(item.description ?? '');
|
|
439
|
+
writer.writeString(item.type ?? '');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function writeEventSpec(writer, item = {}) {
|
|
443
|
+
writer.writeString(item.name ?? '');
|
|
444
|
+
writer.writeString(item.description ?? '');
|
|
445
|
+
writeSequence(writer, item.args ?? [], writeArgSpec);
|
|
446
|
+
writer.writeUInt8(enumKey(TRANSPORT_MODE, item.transportMode ?? 'confirmed', 'TransportModeSpec'));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function writeMethodSpec(writer, item = {}) {
|
|
450
|
+
writer.writeString(item.name ?? '');
|
|
451
|
+
writer.writeString(item.description ?? '');
|
|
452
|
+
writeSequence(writer, item.args ?? [], writeArgSpec);
|
|
453
|
+
writer.writeUInt8(enumKey(TRANSPORT_MODE, item.transportMode ?? 'confirmed', 'TransportModeSpec'));
|
|
454
|
+
writer.writeUInt8(enumKey(METHOD_CONSTNESS, item.constness ?? 'nonConstant', 'MethodConstnessSpec'));
|
|
455
|
+
writer.writeBool(Boolean(item.deferred));
|
|
456
|
+
writer.writeString(item.returnType ?? '');
|
|
457
|
+
writer.writeUInt8(enumKey(PROPERTY_RELATION, item.propertyRelation ?? 'nonPropertyRelated', 'PropertyRelationSpec'));
|
|
458
|
+
writer.writeBool(Boolean(item.localOnly));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function writePropertySpec(writer, item = {}) {
|
|
462
|
+
writer.writeString(item.name ?? '');
|
|
463
|
+
writer.writeString(item.description ?? '');
|
|
464
|
+
writer.writeUInt8(enumKey(PROPERTY_CATEGORY, item.category ?? 'dynamicRO', 'PropertyCategorySpec'));
|
|
465
|
+
writer.writeString(item.type ?? '');
|
|
466
|
+
writer.writeUInt8(enumKey(TRANSPORT_MODE, item.transportMode ?? 'confirmed', 'TransportModeSpec'));
|
|
467
|
+
writeStringList(writer, item.tags ?? []);
|
|
468
|
+
writer.writeBool(Boolean(item.checkedSet));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function writeClassTypeSpec(writer, value = {}) {
|
|
472
|
+
writeSequence(writer, value.properties ?? [], writePropertySpec);
|
|
473
|
+
writeSequence(writer, value.methods ?? [], writeMethodSpec);
|
|
474
|
+
writeSequence(writer, value.events ?? [], writeEventSpec);
|
|
475
|
+
writeMethodSpec(writer, value.constructor ?? { name: '', returnType: '' });
|
|
476
|
+
writeStringList(writer, value.parents ?? []);
|
|
477
|
+
writer.writeBool(Boolean(value.isInterface));
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function writeCustomTypeData(writer, data = {}) {
|
|
481
|
+
const type = data.type ?? 'StructTypeSpec';
|
|
482
|
+
const value = data.value ?? {};
|
|
483
|
+
writer.writeUInt32(enumKey(CUSTOM_TYPE_DATA, type, 'CustomTypeData'));
|
|
484
|
+
|
|
485
|
+
switch (type) {
|
|
486
|
+
case 'EnumTypeSpec':
|
|
487
|
+
writeEnumTypeSpec(writer, value);
|
|
488
|
+
break;
|
|
489
|
+
case 'QuantityTypeSpec':
|
|
490
|
+
writeQuantityTypeSpec(writer, value);
|
|
491
|
+
break;
|
|
492
|
+
case 'SequenceTypeSpec':
|
|
493
|
+
writeSequenceTypeSpec(writer, value);
|
|
494
|
+
break;
|
|
495
|
+
case 'StructTypeSpec':
|
|
496
|
+
writeStructTypeSpec(writer, value);
|
|
497
|
+
break;
|
|
498
|
+
case 'VariantTypeSpec':
|
|
499
|
+
writeVariantTypeSpec(writer, value);
|
|
500
|
+
break;
|
|
501
|
+
case 'AliasTypeSpec':
|
|
502
|
+
writeAliasTypeSpec(writer, value);
|
|
503
|
+
break;
|
|
504
|
+
case 'OptionalTypeSpec':
|
|
505
|
+
writeOptionalTypeSpec(writer, value);
|
|
506
|
+
break;
|
|
507
|
+
case 'ClassTypeSpec':
|
|
508
|
+
writeClassTypeSpec(writer, value);
|
|
509
|
+
break;
|
|
510
|
+
default:
|
|
511
|
+
throw new TypeError(`unhandled SEN CustomTypeData alternative: ${type}`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function writeCustomTypeSpec(writer, spec = {}) {
|
|
516
|
+
writer.writeString(spec.name ?? '');
|
|
517
|
+
writer.writeString(spec.qualifiedName ?? spec.name ?? '');
|
|
518
|
+
writer.writeString(spec.description ?? '');
|
|
519
|
+
writeCustomTypeData(writer, spec.data);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function writeTypeSpecResponse(writer, item = {}) {
|
|
523
|
+
writer.writeUInt32(enumKey(TYPE_SPEC_RESPONSE, item.type ?? 'NonClassSpecResponse', 'TypeSpecResponse'));
|
|
524
|
+
if ((item.type ?? 'NonClassSpecResponse') === 'ClassSpecResponse') {
|
|
525
|
+
writer.writeUInt32(item.classHash ?? crc32(item.spec?.qualifiedName ?? item.spec?.name ?? ''));
|
|
526
|
+
writeCustomTypeSpec(writer, item.spec);
|
|
527
|
+
writeU32List(writer, item.dependentTypes ?? []);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
writeCustomTypeSpec(writer, item.spec);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function writeObjectAdded(writer, item = {}) {
|
|
534
|
+
writer.writeString(item.className ?? '');
|
|
535
|
+
writer.writeUInt32(item.typeHash ?? crc32(item.className ?? ''));
|
|
536
|
+
writer.writeString(item.name ?? '');
|
|
537
|
+
writer.writeUInt32(item.id ?? crc32(item.name ?? ''));
|
|
538
|
+
writer.writeBuffer(item.state ?? Buffer.alloc(0));
|
|
539
|
+
writer.writeInt64(item.time ?? 0n);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function writeInterestDiscovery(writer, item = {}) {
|
|
543
|
+
writer.writeUInt32(item.interestId ?? 0);
|
|
544
|
+
writeSequence(writer, item.objects ?? [], writeObjectAdded);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function writeObjectsPublished(writer, value = {}) {
|
|
548
|
+
writer.writeUInt32(value.ownerId ?? 0);
|
|
549
|
+
writeSequence(writer, value.discoveries ?? [], writeInterestDiscovery);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function writeObjectsRemoved(writer, value = {}) {
|
|
553
|
+
writeSequence(writer, value.removals ?? [], (itemWriter, item) => {
|
|
554
|
+
itemWriter.writeUInt32(item.interestId ?? 0);
|
|
555
|
+
writeU32List(itemWriter, item.ids ?? []);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function writeObjectsStateResponse(writer, value = {}) {
|
|
560
|
+
writer.writeUInt32(value.ownerId ?? 0);
|
|
561
|
+
writeSequence(writer, value.responses ?? [], (itemWriter, response) => {
|
|
562
|
+
itemWriter.writeUInt32(response.interestId ?? 0);
|
|
563
|
+
writeSequence(itemWriter, response.objectStates ?? [], (stateWriter, state) => {
|
|
564
|
+
stateWriter.writeUInt32(state.id ?? 0);
|
|
565
|
+
stateWriter.writeInt64(state.timestamp ?? 0n);
|
|
566
|
+
stateWriter.writeBuffer(state.state ?? Buffer.alloc(0));
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function writeTypesInfoResponse(writer, value = {}) {
|
|
572
|
+
writer.writeUInt32(value.ownerId ?? 0);
|
|
573
|
+
writeSequence(writer, value.types ?? [], writeTypeSpecResponse);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function writeTypesInfoRejection(writer, value = {}) {
|
|
577
|
+
writer.writeUInt32(value.ownerId ?? 0);
|
|
578
|
+
writeStringList(writer, value.rejections ?? []);
|
|
579
|
+
}
|
|
580
|
+
|
|
336
581
|
function readTypesInfoResponse(reader) {
|
|
337
582
|
return {
|
|
338
583
|
ownerId: reader.readUInt32(),
|
|
@@ -637,6 +882,21 @@ export function encodeKernelControlMessage(message) {
|
|
|
637
882
|
writer.writeUInt32(value.ownerId);
|
|
638
883
|
writeU32List(writer, value.requests);
|
|
639
884
|
break;
|
|
885
|
+
case 'ObjectsPublished':
|
|
886
|
+
writeObjectsPublished(writer, value);
|
|
887
|
+
break;
|
|
888
|
+
case 'ObjectsRemoved':
|
|
889
|
+
writeObjectsRemoved(writer, value);
|
|
890
|
+
break;
|
|
891
|
+
case 'ObjectsStateResponse':
|
|
892
|
+
writeObjectsStateResponse(writer, value);
|
|
893
|
+
break;
|
|
894
|
+
case 'TypesInfoResponse':
|
|
895
|
+
writeTypesInfoResponse(writer, value);
|
|
896
|
+
break;
|
|
897
|
+
case 'TypesInfoRejection':
|
|
898
|
+
writeTypesInfoRejection(writer, value);
|
|
899
|
+
break;
|
|
640
900
|
default:
|
|
641
901
|
throw new TypeError(`encoding SEN kernel ControlMessage ${type} is not implemented`);
|
|
642
902
|
}
|