sen-ether-client 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/API.md +239 -0
- package/LICENSE +21 -0
- package/README.md +227 -0
- package/bin/node-sen-probe.js +426 -0
- package/bin/node-sen-scan.js +77 -0
- package/index.js +75 -0
- package/lib/bus.js +740 -0
- package/lib/client.js +634 -0
- package/lib/codec.js +501 -0
- package/lib/crc32.js +26 -0
- package/lib/discovery.js +439 -0
- package/lib/hash32.js +40 -0
- package/lib/protocol/generated.js +157 -0
- package/lib/sen.js +1346 -0
- package/lib/values.js +421 -0
- package/package.json +31 -0
- package/resources/protocol/ether/discovery.stl +19 -0
- package/resources/protocol/ether/runtime.stl +40 -0
- package/resources/protocol/kernel/basic_types.stl +274 -0
- package/resources/protocol/kernel/bus_protocol.stl +198 -0
- package/resources/protocol/kernel/type_specs.stl +554 -0
- package/resources/protocol/protocol.json +15 -0
- package/scripts/generate-protocol.mjs +111 -0
package/lib/bus.js
ADDED
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
import { SenBinaryReader, SenBinaryWriter } from './codec.js';
|
|
2
|
+
import { crc32 } from './crc32.js';
|
|
3
|
+
import { eventHash, methodHash, propertyHash } from './hash32.js';
|
|
4
|
+
import {
|
|
5
|
+
BASIC_TYPE,
|
|
6
|
+
BUILT_IN_TYPE,
|
|
7
|
+
CUSTOM_TYPE_DATA,
|
|
8
|
+
INTEGRAL_TYPE,
|
|
9
|
+
KERNEL_CONTROL_MESSAGE_KEY,
|
|
10
|
+
METHOD_CONSTNESS,
|
|
11
|
+
NUMERIC_TYPE,
|
|
12
|
+
PROPERTY_CATEGORY,
|
|
13
|
+
PROPERTY_RELATION,
|
|
14
|
+
REAL_TYPE,
|
|
15
|
+
TRANSPORT_MODE,
|
|
16
|
+
TYPE_SPEC_RESPONSE,
|
|
17
|
+
UNIT_CATEGORY
|
|
18
|
+
} from './protocol/generated.js';
|
|
19
|
+
|
|
20
|
+
export { KERNEL_CONTROL_MESSAGE_KEY };
|
|
21
|
+
|
|
22
|
+
export const BUS_MESSAGE_CATEGORY = Object.freeze({
|
|
23
|
+
controlMessage: 0,
|
|
24
|
+
runtimeObjectUpdate: 1,
|
|
25
|
+
runtimeMethodCallBestEffort: 2,
|
|
26
|
+
runtimeMethodCallConfirmed: 3,
|
|
27
|
+
runtimeMethodResponse: 4,
|
|
28
|
+
runtimeEvents: 5
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const REMOTE_CALL_RESULT = [
|
|
32
|
+
'success',
|
|
33
|
+
'objectNotFound',
|
|
34
|
+
'runtimeError',
|
|
35
|
+
'logicError',
|
|
36
|
+
'unknownException'
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
function enumName(values, code, label) {
|
|
40
|
+
const name = values[code];
|
|
41
|
+
if (name === undefined) {
|
|
42
|
+
throw new RangeError(`unknown SEN ${label} value: ${code}`);
|
|
43
|
+
}
|
|
44
|
+
return name;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function kernelControlTypeFromKey(key) {
|
|
48
|
+
for (const [type, value] of Object.entries(KERNEL_CONTROL_MESSAGE_KEY)) {
|
|
49
|
+
if (value === key) {
|
|
50
|
+
return type;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readSequence(reader, readItem) {
|
|
57
|
+
const count = reader.readUInt32();
|
|
58
|
+
const values = [];
|
|
59
|
+
for (let i = 0; i < count; i += 1) {
|
|
60
|
+
values.push(readItem(reader));
|
|
61
|
+
}
|
|
62
|
+
return values;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readU32List(reader) {
|
|
66
|
+
return readSequence(reader, itemReader => itemReader.readUInt32());
|
|
67
|
+
}
|
|
68
|
+
|
|
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 readStringList(reader) {
|
|
77
|
+
return readSequence(reader, itemReader => itemReader.readString());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function readOptional(reader, readValue) {
|
|
81
|
+
return reader.readBool() ? readValue(reader) : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function readUInt64Json(reader) {
|
|
85
|
+
const value = reader.readUInt64();
|
|
86
|
+
return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readIntegralType(reader) {
|
|
90
|
+
return enumName(INTEGRAL_TYPE, reader.readUInt8(), 'IntegralType');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function readRealType(reader) {
|
|
94
|
+
return enumName(REAL_TYPE, reader.readUInt8(), 'RealType');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function readNumericType(reader) {
|
|
98
|
+
const key = reader.readUInt32();
|
|
99
|
+
const type = enumName(NUMERIC_TYPE, key, 'NumericType');
|
|
100
|
+
switch (type) {
|
|
101
|
+
case 'IntegralType':
|
|
102
|
+
return { type: 'IntegralType', value: readIntegralType(reader) };
|
|
103
|
+
case 'RealType':
|
|
104
|
+
return { type: 'RealType', value: readRealType(reader) };
|
|
105
|
+
default:
|
|
106
|
+
throw new TypeError(`unhandled SEN NumericType alternative: ${type}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function readBasicType(reader) {
|
|
111
|
+
return enumName(BASIC_TYPE, reader.readUInt8(), 'BasicType');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function readBuiltInType(reader) {
|
|
115
|
+
const key = reader.readUInt32();
|
|
116
|
+
const type = enumName(BUILT_IN_TYPE, key, 'BuiltInType');
|
|
117
|
+
switch (type) {
|
|
118
|
+
case 'NumericType':
|
|
119
|
+
return { type: 'NumericType', value: readNumericType(reader) };
|
|
120
|
+
case 'BasicType':
|
|
121
|
+
return { type: 'BasicType', value: readBasicType(reader) };
|
|
122
|
+
default:
|
|
123
|
+
throw new TypeError(`unhandled SEN BuiltInType alternative: ${type}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function readUnitInfo(reader) {
|
|
128
|
+
return {
|
|
129
|
+
name: reader.readString(),
|
|
130
|
+
abbreviation: reader.readString(),
|
|
131
|
+
category: enumName(UNIT_CATEGORY, reader.readUInt8(), 'UnitCat')
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function readEnumeratorSpec(reader) {
|
|
136
|
+
return {
|
|
137
|
+
name: reader.readString(),
|
|
138
|
+
key: reader.readUInt32(),
|
|
139
|
+
description: reader.readString()
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function readEnumTypeSpec(reader) {
|
|
144
|
+
return {
|
|
145
|
+
enums: readSequence(reader, readEnumeratorSpec),
|
|
146
|
+
storageType: readIntegralType(reader)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function readQuantityTypeSpec(reader) {
|
|
151
|
+
return {
|
|
152
|
+
elementType: readNumericType(reader),
|
|
153
|
+
unit: readUnitInfo(reader),
|
|
154
|
+
minValue: readOptional(reader, itemReader => itemReader.readFloat64()),
|
|
155
|
+
maxValue: readOptional(reader, itemReader => itemReader.readFloat64())
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function readSequenceTypeSpec(reader) {
|
|
160
|
+
return {
|
|
161
|
+
elementType: reader.readString(),
|
|
162
|
+
maxSize: readOptional(reader, readUInt64Json),
|
|
163
|
+
fixedSize: reader.readBool()
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function readStructTypeFieldSpec(reader) {
|
|
168
|
+
return {
|
|
169
|
+
name: reader.readString(),
|
|
170
|
+
description: reader.readString(),
|
|
171
|
+
type: reader.readString()
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function readStructTypeSpec(reader) {
|
|
176
|
+
return {
|
|
177
|
+
fields: readSequence(reader, readStructTypeFieldSpec),
|
|
178
|
+
parent: reader.readString()
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function readVariantTypeFieldSpec(reader) {
|
|
183
|
+
return {
|
|
184
|
+
key: reader.readUInt32(),
|
|
185
|
+
description: reader.readString(),
|
|
186
|
+
type: reader.readString()
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function readVariantTypeSpec(reader) {
|
|
191
|
+
return {
|
|
192
|
+
fields: readSequence(reader, readVariantTypeFieldSpec)
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function readAliasTypeSpec(reader) {
|
|
197
|
+
return {
|
|
198
|
+
aliasedType: reader.readString()
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function readOptionalTypeSpec(reader) {
|
|
203
|
+
return {
|
|
204
|
+
type: reader.readString()
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function readArgSpec(reader) {
|
|
209
|
+
return {
|
|
210
|
+
name: reader.readString(),
|
|
211
|
+
description: reader.readString(),
|
|
212
|
+
type: reader.readString()
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function readEventSpec(reader) {
|
|
217
|
+
const name = reader.readString();
|
|
218
|
+
return {
|
|
219
|
+
id: eventHash(name),
|
|
220
|
+
name,
|
|
221
|
+
description: reader.readString(),
|
|
222
|
+
args: readSequence(reader, readArgSpec),
|
|
223
|
+
transportMode: enumName(TRANSPORT_MODE, reader.readUInt8(), 'TransportModeSpec')
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function readMethodSpec(reader) {
|
|
228
|
+
const name = reader.readString();
|
|
229
|
+
return {
|
|
230
|
+
id: methodHash(name),
|
|
231
|
+
name,
|
|
232
|
+
description: reader.readString(),
|
|
233
|
+
args: readSequence(reader, readArgSpec),
|
|
234
|
+
transportMode: enumName(TRANSPORT_MODE, reader.readUInt8(), 'TransportModeSpec'),
|
|
235
|
+
constness: enumName(METHOD_CONSTNESS, reader.readUInt8(), 'MethodConstnessSpec'),
|
|
236
|
+
deferred: reader.readBool(),
|
|
237
|
+
returnType: reader.readString(),
|
|
238
|
+
propertyRelation: enumName(PROPERTY_RELATION, reader.readUInt8(), 'PropertyRelationSpec'),
|
|
239
|
+
localOnly: reader.readBool()
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function readPropertySpec(reader) {
|
|
244
|
+
const name = reader.readString();
|
|
245
|
+
return {
|
|
246
|
+
id: propertyHash(name),
|
|
247
|
+
name,
|
|
248
|
+
description: reader.readString(),
|
|
249
|
+
category: enumName(PROPERTY_CATEGORY, reader.readUInt8(), 'PropertyCategorySpec'),
|
|
250
|
+
type: reader.readString(),
|
|
251
|
+
transportMode: enumName(TRANSPORT_MODE, reader.readUInt8(), 'TransportModeSpec'),
|
|
252
|
+
tags: readStringList(reader),
|
|
253
|
+
checkedSet: reader.readBool()
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function readClassTypeSpec(reader) {
|
|
258
|
+
return {
|
|
259
|
+
properties: readSequence(reader, readPropertySpec),
|
|
260
|
+
methods: readSequence(reader, readMethodSpec),
|
|
261
|
+
events: readSequence(reader, readEventSpec),
|
|
262
|
+
constructor: readMethodSpec(reader),
|
|
263
|
+
parents: readStringList(reader),
|
|
264
|
+
isInterface: reader.readBool()
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function readCustomTypeData(reader) {
|
|
269
|
+
const key = reader.readUInt32();
|
|
270
|
+
const type = enumName(CUSTOM_TYPE_DATA, key, 'CustomTypeData');
|
|
271
|
+
let value;
|
|
272
|
+
|
|
273
|
+
switch (type) {
|
|
274
|
+
case 'EnumTypeSpec':
|
|
275
|
+
value = readEnumTypeSpec(reader);
|
|
276
|
+
break;
|
|
277
|
+
case 'QuantityTypeSpec':
|
|
278
|
+
value = readQuantityTypeSpec(reader);
|
|
279
|
+
break;
|
|
280
|
+
case 'SequenceTypeSpec':
|
|
281
|
+
value = readSequenceTypeSpec(reader);
|
|
282
|
+
break;
|
|
283
|
+
case 'StructTypeSpec':
|
|
284
|
+
value = readStructTypeSpec(reader);
|
|
285
|
+
break;
|
|
286
|
+
case 'VariantTypeSpec':
|
|
287
|
+
value = readVariantTypeSpec(reader);
|
|
288
|
+
break;
|
|
289
|
+
case 'AliasTypeSpec':
|
|
290
|
+
value = readAliasTypeSpec(reader);
|
|
291
|
+
break;
|
|
292
|
+
case 'OptionalTypeSpec':
|
|
293
|
+
value = readOptionalTypeSpec(reader);
|
|
294
|
+
break;
|
|
295
|
+
case 'ClassTypeSpec':
|
|
296
|
+
value = readClassTypeSpec(reader);
|
|
297
|
+
break;
|
|
298
|
+
default:
|
|
299
|
+
throw new TypeError(`unhandled SEN CustomTypeData alternative: ${type}`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { type, value };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function readCustomTypeSpec(reader) {
|
|
306
|
+
return {
|
|
307
|
+
name: reader.readString(),
|
|
308
|
+
qualifiedName: reader.readString(),
|
|
309
|
+
description: reader.readString(),
|
|
310
|
+
data: readCustomTypeData(reader)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function readTypeSpecResponse(reader) {
|
|
315
|
+
const key = reader.readUInt32();
|
|
316
|
+
const type = enumName(TYPE_SPEC_RESPONSE, key, 'TypeSpecResponse');
|
|
317
|
+
|
|
318
|
+
switch (type) {
|
|
319
|
+
case 'ClassSpecResponse':
|
|
320
|
+
return {
|
|
321
|
+
type,
|
|
322
|
+
classHash: reader.readUInt32(),
|
|
323
|
+
spec: readCustomTypeSpec(reader),
|
|
324
|
+
dependentTypes: readU32List(reader)
|
|
325
|
+
};
|
|
326
|
+
case 'NonClassSpecResponse':
|
|
327
|
+
return {
|
|
328
|
+
type,
|
|
329
|
+
spec: readCustomTypeSpec(reader)
|
|
330
|
+
};
|
|
331
|
+
default:
|
|
332
|
+
throw new TypeError(`unhandled SEN TypeSpecResponse alternative: ${type}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function readTypesInfoResponse(reader) {
|
|
337
|
+
return {
|
|
338
|
+
ownerId: reader.readUInt32(),
|
|
339
|
+
types: readSequence(reader, readTypeSpecResponse)
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function readTypesInfoRejection(reader) {
|
|
344
|
+
return {
|
|
345
|
+
ownerId: reader.readUInt32(),
|
|
346
|
+
rejections: readStringList(reader)
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function readObjectAdded(reader) {
|
|
351
|
+
return {
|
|
352
|
+
className: reader.readString(),
|
|
353
|
+
typeHash: reader.readUInt32(),
|
|
354
|
+
name: reader.readString(),
|
|
355
|
+
id: reader.readUInt32(),
|
|
356
|
+
state: reader.readBuffer(),
|
|
357
|
+
time: reader.readInt64()
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function readInterestDiscovery(reader) {
|
|
362
|
+
const interestId = reader.readUInt32();
|
|
363
|
+
const objectCount = reader.readUInt32();
|
|
364
|
+
const objects = [];
|
|
365
|
+
for (let i = 0; i < objectCount; i += 1) {
|
|
366
|
+
objects.push(readObjectAdded(reader));
|
|
367
|
+
}
|
|
368
|
+
return { interestId, objects };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function readObjectsPublished(reader) {
|
|
372
|
+
const ownerId = reader.readUInt32();
|
|
373
|
+
const discoveryCount = reader.readUInt32();
|
|
374
|
+
const discoveries = [];
|
|
375
|
+
for (let i = 0; i < discoveryCount; i += 1) {
|
|
376
|
+
discoveries.push(readInterestDiscovery(reader));
|
|
377
|
+
}
|
|
378
|
+
return { ownerId, discoveries };
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function readObjectsRemoved(reader) {
|
|
382
|
+
const removalCount = reader.readUInt32();
|
|
383
|
+
const removals = [];
|
|
384
|
+
for (let i = 0; i < removalCount; i += 1) {
|
|
385
|
+
removals.push({
|
|
386
|
+
interestId: reader.readUInt32(),
|
|
387
|
+
ids: readU32List(reader)
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
return { removals };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function readObjectIdsByInterestList(reader) {
|
|
394
|
+
const count = reader.readUInt32();
|
|
395
|
+
const requests = [];
|
|
396
|
+
for (let i = 0; i < count; i += 1) {
|
|
397
|
+
requests.push({
|
|
398
|
+
interestId: reader.readUInt32(),
|
|
399
|
+
objectIds: readU32List(reader)
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
return requests;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function writeObjectIdsByInterestList(writer, requests = []) {
|
|
406
|
+
writer.writeUInt32(requests.length);
|
|
407
|
+
for (const request of requests) {
|
|
408
|
+
writer.writeUInt32(request.interestId);
|
|
409
|
+
writeU32List(writer, request.objectIds);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function readObjectState(reader) {
|
|
414
|
+
return {
|
|
415
|
+
id: reader.readUInt32(),
|
|
416
|
+
timestamp: reader.readInt64(),
|
|
417
|
+
state: reader.readBuffer()
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function readObjectsStateResponse(reader) {
|
|
422
|
+
const ownerId = reader.readUInt32();
|
|
423
|
+
const groupCount = reader.readUInt32();
|
|
424
|
+
const responses = [];
|
|
425
|
+
for (let i = 0; i < groupCount; i += 1) {
|
|
426
|
+
const interestId = reader.readUInt32();
|
|
427
|
+
const objectStateCount = reader.readUInt32();
|
|
428
|
+
const objectStates = [];
|
|
429
|
+
for (let j = 0; j < objectStateCount; j += 1) {
|
|
430
|
+
objectStates.push(readObjectState(reader));
|
|
431
|
+
}
|
|
432
|
+
responses.push({ interestId, objectStates });
|
|
433
|
+
}
|
|
434
|
+
return { ownerId, responses };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function decodePropertyUpdateBuffer(buffer) {
|
|
438
|
+
const reader = new SenBinaryReader(buffer);
|
|
439
|
+
const updates = [];
|
|
440
|
+
|
|
441
|
+
while (reader.remaining() > 0) {
|
|
442
|
+
const id = reader.readUInt32();
|
|
443
|
+
const size = reader.readUInt32();
|
|
444
|
+
reader.ensure(size);
|
|
445
|
+
const value = reader.buffer.subarray(reader.offset, reader.offset + size);
|
|
446
|
+
reader.offset += size;
|
|
447
|
+
updates.push({ id, size, value });
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return updates;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function readRuntimeObjectUpdate(reader) {
|
|
454
|
+
const objectId = reader.readUInt32();
|
|
455
|
+
const time = reader.readInt64();
|
|
456
|
+
const propertiesSize = reader.readUInt32();
|
|
457
|
+
reader.ensure(propertiesSize);
|
|
458
|
+
const properties = reader.buffer.subarray(reader.offset, reader.offset + propertiesSize);
|
|
459
|
+
reader.offset += propertiesSize;
|
|
460
|
+
return {
|
|
461
|
+
objectId,
|
|
462
|
+
time,
|
|
463
|
+
propertiesSize,
|
|
464
|
+
properties,
|
|
465
|
+
propertyUpdates: decodePropertyUpdateBuffer(properties)
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function readRuntimeMethodResponse(reader) {
|
|
470
|
+
const resultCode = reader.readUInt8();
|
|
471
|
+
const result = enumName(REMOTE_CALL_RESULT, resultCode, 'RemoteCallResult');
|
|
472
|
+
const objectId = reader.readUInt32();
|
|
473
|
+
const ticketId = reader.readUInt32();
|
|
474
|
+
|
|
475
|
+
if (result === 'success') {
|
|
476
|
+
const returnSize = reader.readUInt32();
|
|
477
|
+
reader.ensure(returnSize);
|
|
478
|
+
const returnValue = reader.buffer.subarray(reader.offset, reader.offset + returnSize);
|
|
479
|
+
reader.offset += returnSize;
|
|
480
|
+
return {
|
|
481
|
+
resultCode,
|
|
482
|
+
result,
|
|
483
|
+
objectId,
|
|
484
|
+
ticketId,
|
|
485
|
+
returnValue
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const error = result === 'runtimeError' || result === 'logicError'
|
|
490
|
+
? reader.readString()
|
|
491
|
+
: result;
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
resultCode,
|
|
495
|
+
result,
|
|
496
|
+
objectId,
|
|
497
|
+
ticketId,
|
|
498
|
+
error
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function readRuntimeEvents(reader) {
|
|
503
|
+
const events = [];
|
|
504
|
+
|
|
505
|
+
while (reader.remaining() > 0) {
|
|
506
|
+
const producerId = reader.readUInt32();
|
|
507
|
+
const eventId = reader.readUInt32();
|
|
508
|
+
const creationTime = reader.readInt64();
|
|
509
|
+
const argumentsSize = reader.readUInt32();
|
|
510
|
+
reader.ensure(argumentsSize);
|
|
511
|
+
const argumentsBuffer = reader.buffer.subarray(reader.offset, reader.offset + argumentsSize);
|
|
512
|
+
reader.offset += argumentsSize;
|
|
513
|
+
events.push({
|
|
514
|
+
producerId,
|
|
515
|
+
eventId,
|
|
516
|
+
creationTime,
|
|
517
|
+
argumentsSize,
|
|
518
|
+
argumentsBuffer
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return events;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Decode process-level bus payload from ProcessHandler TCP category `busMessage`.
|
|
527
|
+
*
|
|
528
|
+
* Payload layout:
|
|
529
|
+
* u32 to; u32 busId; bytes busMessage
|
|
530
|
+
*
|
|
531
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} buffer
|
|
532
|
+
*/
|
|
533
|
+
export function decodeConfirmedBusFrame(buffer) {
|
|
534
|
+
const reader = new SenBinaryReader(buffer);
|
|
535
|
+
const to = reader.readUInt32();
|
|
536
|
+
const busId = reader.readUInt32();
|
|
537
|
+
const message = reader.buffer.subarray(reader.offset);
|
|
538
|
+
return { to, busId, message };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Encode a confirmed process-level bus payload.
|
|
543
|
+
*
|
|
544
|
+
* @param {object} frame
|
|
545
|
+
* @param {number} frame.to
|
|
546
|
+
* @param {number} frame.busId
|
|
547
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} frame.message
|
|
548
|
+
*/
|
|
549
|
+
export function encodeConfirmedBusFrame(frame) {
|
|
550
|
+
const writer = new SenBinaryWriter();
|
|
551
|
+
writer.writeUInt32(frame.to);
|
|
552
|
+
writer.writeUInt32(frame.busId);
|
|
553
|
+
writer.chunks.push(Buffer.from(frame.message ?? []));
|
|
554
|
+
return writer.toBuffer();
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Decode sen::kernel::impl bus message envelope.
|
|
559
|
+
*
|
|
560
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} buffer
|
|
561
|
+
*/
|
|
562
|
+
export function decodeBusMessage(buffer) {
|
|
563
|
+
const reader = new SenBinaryReader(buffer);
|
|
564
|
+
const category = reader.readUInt8();
|
|
565
|
+
const payload = reader.buffer.subarray(reader.offset);
|
|
566
|
+
|
|
567
|
+
if (category === BUS_MESSAGE_CATEGORY.controlMessage) {
|
|
568
|
+
return {
|
|
569
|
+
category,
|
|
570
|
+
categoryName: 'controlMessage',
|
|
571
|
+
control: decodeKernelControlMessage(payload)
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (category === BUS_MESSAGE_CATEGORY.runtimeObjectUpdate) {
|
|
576
|
+
return {
|
|
577
|
+
category,
|
|
578
|
+
categoryName: 'runtimeObjectUpdate',
|
|
579
|
+
update: readRuntimeObjectUpdate(reader)
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (category === BUS_MESSAGE_CATEGORY.runtimeMethodResponse) {
|
|
584
|
+
return {
|
|
585
|
+
category,
|
|
586
|
+
categoryName: 'runtimeMethodResponse',
|
|
587
|
+
response: readRuntimeMethodResponse(reader)
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (category === BUS_MESSAGE_CATEGORY.runtimeEvents) {
|
|
592
|
+
return {
|
|
593
|
+
category,
|
|
594
|
+
categoryName: 'runtimeEvents',
|
|
595
|
+
events: readRuntimeEvents(reader),
|
|
596
|
+
payload
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return {
|
|
601
|
+
category,
|
|
602
|
+
categoryName: Object.entries(BUS_MESSAGE_CATEGORY).find(([, value]) => value === category)?.[0] ?? `unknown:${category}`,
|
|
603
|
+
payload
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* @param {{ type: string, value?: object }} message
|
|
609
|
+
*/
|
|
610
|
+
export function encodeKernelControlMessage(message) {
|
|
611
|
+
const type = message.type;
|
|
612
|
+
const value = message.value ?? {};
|
|
613
|
+
|
|
614
|
+
if (!(type in KERNEL_CONTROL_MESSAGE_KEY)) {
|
|
615
|
+
throw new TypeError(`unknown SEN kernel ControlMessage: ${type}`);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const writer = new SenBinaryWriter();
|
|
619
|
+
writer.writeUInt32(KERNEL_CONTROL_MESSAGE_KEY[type]);
|
|
620
|
+
|
|
621
|
+
switch (type) {
|
|
622
|
+
case 'RemoteParticipantReady':
|
|
623
|
+
writer.writeUInt32(value.id);
|
|
624
|
+
break;
|
|
625
|
+
case 'InterestStarted':
|
|
626
|
+
writer.writeString(value.query);
|
|
627
|
+
writer.writeUInt32(value.id ?? crc32(value.query));
|
|
628
|
+
break;
|
|
629
|
+
case 'InterestStopped':
|
|
630
|
+
writer.writeUInt32(value.id);
|
|
631
|
+
break;
|
|
632
|
+
case 'ObjectsStateRequest':
|
|
633
|
+
writer.writeUInt32(value.ownerId);
|
|
634
|
+
writeObjectIdsByInterestList(writer, value.requests);
|
|
635
|
+
break;
|
|
636
|
+
case 'TypesInfoRequest':
|
|
637
|
+
writer.writeUInt32(value.ownerId);
|
|
638
|
+
writeU32List(writer, value.requests);
|
|
639
|
+
break;
|
|
640
|
+
default:
|
|
641
|
+
throw new TypeError(`encoding SEN kernel ControlMessage ${type} is not implemented`);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
return writer.toBuffer();
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} buffer
|
|
649
|
+
*/
|
|
650
|
+
export function decodeKernelControlMessage(buffer) {
|
|
651
|
+
const reader = new SenBinaryReader(buffer);
|
|
652
|
+
const key = reader.readUInt32();
|
|
653
|
+
const type = kernelControlTypeFromKey(key);
|
|
654
|
+
|
|
655
|
+
if (!type) {
|
|
656
|
+
throw new RangeError(`unknown SEN kernel ControlMessage key: ${key}`);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
let value = {};
|
|
660
|
+
switch (type) {
|
|
661
|
+
case 'RemoteParticipantReady':
|
|
662
|
+
value = { id: reader.readUInt32() };
|
|
663
|
+
break;
|
|
664
|
+
case 'InterestStarted':
|
|
665
|
+
value = { query: reader.readString(), id: reader.readUInt32() };
|
|
666
|
+
break;
|
|
667
|
+
case 'InterestStopped':
|
|
668
|
+
value = { id: reader.readUInt32() };
|
|
669
|
+
break;
|
|
670
|
+
case 'ObjectsPublished':
|
|
671
|
+
value = readObjectsPublished(reader);
|
|
672
|
+
break;
|
|
673
|
+
case 'ObjectsRemoved':
|
|
674
|
+
value = readObjectsRemoved(reader);
|
|
675
|
+
break;
|
|
676
|
+
case 'ObjectsStateRequest':
|
|
677
|
+
value = { ownerId: reader.readUInt32(), requests: readObjectIdsByInterestList(reader) };
|
|
678
|
+
break;
|
|
679
|
+
case 'ObjectsStateResponse':
|
|
680
|
+
value = readObjectsStateResponse(reader);
|
|
681
|
+
break;
|
|
682
|
+
case 'TypesInfoRequest':
|
|
683
|
+
value = { ownerId: reader.readUInt32(), requests: readU32List(reader) };
|
|
684
|
+
break;
|
|
685
|
+
case 'TypesInfoResponse':
|
|
686
|
+
value = readTypesInfoResponse(reader);
|
|
687
|
+
break;
|
|
688
|
+
case 'TypesInfoRejection':
|
|
689
|
+
value = readTypesInfoRejection(reader);
|
|
690
|
+
break;
|
|
691
|
+
default:
|
|
692
|
+
value = { raw: reader.buffer.subarray(reader.offset) };
|
|
693
|
+
reader.offset = reader.buffer.length;
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return {
|
|
698
|
+
type,
|
|
699
|
+
value,
|
|
700
|
+
bytesRead: reader.offset
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export function encodeBusControlMessage(message) {
|
|
705
|
+
const writer = new SenBinaryWriter();
|
|
706
|
+
writer.writeUInt8(BUS_MESSAGE_CATEGORY.controlMessage);
|
|
707
|
+
writer.chunks.push(encodeKernelControlMessage(message));
|
|
708
|
+
return writer.toBuffer();
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Encode a SEN runtime method call bus message.
|
|
713
|
+
*
|
|
714
|
+
* Source implementation:
|
|
715
|
+
* libs/kernel/src/bus/remote_participant.cpp::makeMethodCallHeader
|
|
716
|
+
*
|
|
717
|
+
* @param {object} call
|
|
718
|
+
* @param {boolean} [call.confirmed]
|
|
719
|
+
* @param {number} call.ownerId Local participant id that should receive the response.
|
|
720
|
+
* @param {number} call.objectId Remote object id.
|
|
721
|
+
* @param {number} call.methodId SEN method member hash.
|
|
722
|
+
* @param {number} call.ticketId Local call id.
|
|
723
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} [call.argumentsBuffer]
|
|
724
|
+
*/
|
|
725
|
+
export function encodeRuntimeMethodCall(call) {
|
|
726
|
+
const args = Buffer.isBuffer(call.argumentsBuffer)
|
|
727
|
+
? call.argumentsBuffer
|
|
728
|
+
: Buffer.from(call.argumentsBuffer ?? []);
|
|
729
|
+
const writer = new SenBinaryWriter();
|
|
730
|
+
writer.writeUInt8(call.confirmed ? BUS_MESSAGE_CATEGORY.runtimeMethodCallConfirmed : BUS_MESSAGE_CATEGORY.runtimeMethodCallBestEffort);
|
|
731
|
+
writer.writeUInt32(call.ownerId);
|
|
732
|
+
writer.writeUInt32(call.objectId);
|
|
733
|
+
writer.writeUInt32(call.methodId);
|
|
734
|
+
writer.writeUInt32(call.ticketId);
|
|
735
|
+
writer.writeUInt32(args.length);
|
|
736
|
+
if (args.length) {
|
|
737
|
+
writer.chunks.push(args);
|
|
738
|
+
}
|
|
739
|
+
return writer.toBuffer();
|
|
740
|
+
}
|