node-mavlink 1.0.12 → 1.1.1

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.
@@ -1,689 +1 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.sendSigned = exports.send = exports.createMavLinkStream = exports.MavLinkPacketParser = exports.MavLinkPacketSplitter = exports.MavLinkPacket = exports.MavLinkPacketSignature = exports.MavLinkProtocolV2 = exports.MavLinkProtocolV1 = exports.MavLinkProtocol = exports.MavLinkPacketHeader = void 0;
13
- const stream_1 = require("stream");
14
- const crypto_1 = require("crypto");
15
- const mavlink_mappings_1 = require("mavlink-mappings");
16
- const mavlink_mappings_2 = require("mavlink-mappings");
17
- const utils_1 = require("./utils");
18
- const logger_1 = require("./logger");
19
- const serialization_1 = require("./serialization");
20
- /**
21
- * Header definition of the MavLink packet
22
- */
23
- class MavLinkPacketHeader {
24
- constructor() {
25
- this.magic = 0;
26
- this.payloadLength = 0;
27
- this.incompatibilityFlags = 0;
28
- this.compatibilityFlags = 0;
29
- this.seq = 0;
30
- this.sysid = 0;
31
- this.compid = 0;
32
- this.msgid = 0;
33
- }
34
- }
35
- exports.MavLinkPacketHeader = MavLinkPacketHeader;
36
- /**
37
- * Base class for protocols
38
- *
39
- * Implements common functionality like getting the CRC and deserializing
40
- * data classes from the given payload buffer
41
- */
42
- class MavLinkProtocol {
43
- constructor() {
44
- this.log = logger_1.Logger.getLogger(this);
45
- }
46
- /**
47
- * Deserialize payload into actual data class
48
- */
49
- data(payload, clazz) {
50
- this.log.trace('Deserializing', clazz.MSG_NAME, 'with payload of size', payload.length);
51
- const instance = new clazz();
52
- clazz.FIELDS.forEach(field => {
53
- const deserialize = serialization_1.DESERIALIZERS[field.type];
54
- if (!deserialize) {
55
- throw new Error(`Unknown field type ${field.type}`);
56
- }
57
- instance[field.name] = deserialize(payload, field.offset, field.length);
58
- });
59
- return instance;
60
- }
61
- }
62
- exports.MavLinkProtocol = MavLinkProtocol;
63
- MavLinkProtocol.NAME = 'unknown';
64
- MavLinkProtocol.START_BYTE = 0;
65
- MavLinkProtocol.PAYLOAD_OFFSET = 0;
66
- MavLinkProtocol.CHECKSUM_LENGTH = 2;
67
- MavLinkProtocol.SYS_ID = 254;
68
- MavLinkProtocol.COMP_ID = 1;
69
- /**
70
- * MavLink Protocol V1
71
- */
72
- class MavLinkProtocolV1 extends MavLinkProtocol {
73
- constructor(sysid = MavLinkProtocol.SYS_ID, compid = MavLinkProtocol.COMP_ID) {
74
- super();
75
- this.sysid = sysid;
76
- this.compid = compid;
77
- }
78
- serialize(message, seq) {
79
- this.log.trace('Serializing message (seq:', seq, ')');
80
- const definition = message.constructor;
81
- const buffer = Buffer.from(new Uint8Array(MavLinkProtocolV1.PAYLOAD_OFFSET + definition.PAYLOAD_LENGTH + MavLinkProtocol.CHECKSUM_LENGTH));
82
- // serialize header
83
- buffer.writeUInt8(MavLinkProtocolV1.START_BYTE, 0);
84
- buffer.writeUInt8(definition.PAYLOAD_LENGTH, 1);
85
- buffer.writeUInt8(seq, 2);
86
- buffer.writeUInt8(this.sysid, 3);
87
- buffer.writeUInt8(this.compid, 4);
88
- buffer.writeUInt8(definition.MSG_ID, 5);
89
- // serialize fields
90
- definition.FIELDS.forEach(field => {
91
- const serialize = serialization_1.SERIALIZERS[field.type];
92
- if (!serialize)
93
- throw new Error(`Unknown field type ${field.type}: serializer not found`);
94
- serialize(message[field.name], buffer, field.offset + MavLinkProtocolV1.PAYLOAD_OFFSET, field.length);
95
- });
96
- // serialize checksum
97
- const crc = (0, mavlink_mappings_1.x25crc)(buffer, 1, 2, definition.MAGIC_NUMBER);
98
- buffer.writeUInt16LE(crc, buffer.length - 2);
99
- return buffer;
100
- }
101
- header(buffer) {
102
- this.log.trace('Reading header from buffer (len:', buffer.length, ')');
103
- const startByte = buffer.readUInt8(0);
104
- if (startByte !== MavLinkProtocolV1.START_BYTE) {
105
- throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV1.START_BYTE}, got ${startByte})`);
106
- }
107
- const result = new MavLinkPacketHeader();
108
- result.magic = startByte;
109
- result.payloadLength = buffer.readUInt8(1);
110
- result.seq = buffer.readUInt8(2);
111
- result.sysid = buffer.readUInt8(3);
112
- result.compid = buffer.readUInt8(4);
113
- result.msgid = buffer.readUInt8(5);
114
- return result;
115
- }
116
- /**
117
- * Deserialize packet checksum
118
- */
119
- crc(buffer) {
120
- this.log.trace('Reading crc from buffer (len:', buffer.length, ')');
121
- const plen = buffer.readUInt8(1);
122
- return buffer.readUInt16LE(MavLinkProtocolV1.PAYLOAD_OFFSET + plen);
123
- }
124
- payload(buffer) {
125
- this.log.trace('Reading payload from buffer (len:', buffer.length, ')');
126
- const plen = buffer.readUInt8(1);
127
- const payload = buffer.slice(MavLinkProtocolV1.PAYLOAD_OFFSET, MavLinkProtocolV1.PAYLOAD_OFFSET + plen);
128
- const padding = Buffer.from(new Uint8Array(255 - payload.length));
129
- return Buffer.concat([payload, padding]);
130
- }
131
- }
132
- exports.MavLinkProtocolV1 = MavLinkProtocolV1;
133
- MavLinkProtocolV1.NAME = 'MAV_V1';
134
- MavLinkProtocolV1.START_BYTE = 0xFE;
135
- MavLinkProtocolV1.PAYLOAD_OFFSET = 6;
136
- /**
137
- * MavLink Protocol V2
138
- */
139
- class MavLinkProtocolV2 extends MavLinkProtocol {
140
- constructor(sysid = MavLinkProtocol.SYS_ID, compid = MavLinkProtocol.COMP_ID, incompatibilityFlags = MavLinkProtocolV2.INCOMPATIBILITY_FLAGS, compatibilityFlags = MavLinkProtocolV2.COMPATIBILITY_FLAGS) {
141
- super();
142
- this.sysid = sysid;
143
- this.compid = compid;
144
- this.incompatibilityFlags = incompatibilityFlags;
145
- this.compatibilityFlags = compatibilityFlags;
146
- }
147
- serialize(message, seq) {
148
- this.log.trace('Serializing message (seq:', seq, ')');
149
- const definition = message.constructor;
150
- const buffer = Buffer.from(new Uint8Array(MavLinkProtocolV2.PAYLOAD_OFFSET + definition.PAYLOAD_LENGTH + MavLinkProtocol.CHECKSUM_LENGTH));
151
- buffer.writeUInt8(MavLinkProtocolV2.START_BYTE, 0);
152
- buffer.writeUInt8(this.incompatibilityFlags, 2);
153
- buffer.writeUInt8(this.compatibilityFlags, 3);
154
- buffer.writeUInt8(seq, 4);
155
- buffer.writeUInt8(this.sysid, 5);
156
- buffer.writeUInt8(this.compid, 6);
157
- buffer.writeUIntLE(definition.MSG_ID, 7, 3);
158
- definition.FIELDS.forEach(field => {
159
- const serialize = serialization_1.SERIALIZERS[field.type];
160
- if (!serialize)
161
- throw new Error(`Unknown field type ${field.type}: serializer not found`);
162
- serialize(message[field.name], buffer, field.offset + MavLinkProtocolV2.PAYLOAD_OFFSET, field.length);
163
- });
164
- // calculate actual truncated payload length
165
- const payloadLength = this.calculateTruncatedPayloadLength(buffer);
166
- buffer.writeUInt8(payloadLength, 1);
167
- // slice out the message buffer
168
- const result = buffer.slice(0, MavLinkProtocolV2.PAYLOAD_OFFSET + payloadLength + MavLinkProtocol.CHECKSUM_LENGTH);
169
- const crc = (0, mavlink_mappings_1.x25crc)(result, 1, 2, definition.MAGIC_NUMBER);
170
- result.writeUInt16LE(crc, result.length - MavLinkProtocol.CHECKSUM_LENGTH);
171
- return result;
172
- }
173
- /**
174
- * Create a signed package buffer
175
- *
176
- * @param buffer buffer with the original, unsigned package
177
- * @param linkId id of the link
178
- * @param key key to sign the package with
179
- * @param timestamp optional timestamp for packet signing (default: Date.now())
180
- * @returns signed package
181
- */
182
- sign(buffer, linkId, key, timestamp = Date.now()) {
183
- this.log.trace('Signing message');
184
- const result = Buffer.concat([
185
- buffer,
186
- Buffer.from(new Uint8Array(MavLinkPacketSignature.SIGNATURE_LENGTH))
187
- ]);
188
- const signer = new MavLinkPacketSignature(result);
189
- signer.linkId = linkId;
190
- signer.timestamp = timestamp;
191
- signer.signature = signer.calculate(key);
192
- return result;
193
- }
194
- calculateTruncatedPayloadLength(buffer) {
195
- let result = buffer.length;
196
- for (let i = buffer.length - MavLinkProtocol.CHECKSUM_LENGTH - 1; i >= MavLinkProtocolV2.PAYLOAD_OFFSET; i--) {
197
- result = i;
198
- if (buffer[i] !== 0) {
199
- result++;
200
- break;
201
- }
202
- }
203
- return result - MavLinkProtocolV2.PAYLOAD_OFFSET;
204
- }
205
- header(buffer) {
206
- this.log.trace('Reading header from buffer (len:', buffer.length, ')');
207
- const startByte = buffer.readUInt8(0);
208
- if (startByte !== MavLinkProtocolV2.START_BYTE) {
209
- throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV2.START_BYTE}, got ${startByte})`);
210
- }
211
- const result = new MavLinkPacketHeader();
212
- result.magic = startByte;
213
- result.payloadLength = buffer.readUInt8(1);
214
- result.incompatibilityFlags = buffer.readUInt8(2);
215
- result.compatibilityFlags = buffer.readUInt8(3);
216
- result.seq = buffer.readUInt8(4);
217
- result.sysid = buffer.readUInt8(5);
218
- result.compid = buffer.readUInt8(6);
219
- result.msgid = buffer.readUIntLE(7, 3);
220
- return result;
221
- }
222
- /**
223
- * Deserialize packet checksum
224
- */
225
- crc(buffer) {
226
- this.log.trace('Reading crc from buffer (len:', buffer.length, ')');
227
- const plen = buffer.readUInt8(1);
228
- return buffer.readUInt16LE(MavLinkProtocolV2.PAYLOAD_OFFSET + plen);
229
- }
230
- payload(buffer) {
231
- this.log.trace('Reading payload from buffer (len:', buffer.length, ')');
232
- const plen = buffer.readUInt8(1);
233
- const payload = buffer.slice(MavLinkProtocolV2.PAYLOAD_OFFSET, MavLinkProtocolV2.PAYLOAD_OFFSET + plen);
234
- const padding = Buffer.from(new Uint8Array(255 - payload.length));
235
- return Buffer.concat([payload, padding]);
236
- }
237
- signature(buffer, header) {
238
- this.log.trace('Reading signature from buffer (len:', buffer.length, ')');
239
- if (header.incompatibilityFlags & MavLinkProtocolV2.IFLAG_SIGNED) {
240
- return new MavLinkPacketSignature(buffer);
241
- }
242
- else {
243
- return null;
244
- }
245
- }
246
- }
247
- exports.MavLinkProtocolV2 = MavLinkProtocolV2;
248
- MavLinkProtocolV2.NAME = 'MAV_V2';
249
- MavLinkProtocolV2.START_BYTE = 0xFD;
250
- MavLinkProtocolV2.PAYLOAD_OFFSET = 10;
251
- MavLinkProtocolV2.INCOMPATIBILITY_FLAGS = 0;
252
- MavLinkProtocolV2.COMPATIBILITY_FLAGS = 0;
253
- MavLinkProtocolV2.IFLAG_SIGNED = 0x01;
254
- /**
255
- * Registry of known protocols by STX
256
- */
257
- const KNOWN_PROTOCOLS_BY_STX = {
258
- [MavLinkProtocolV1.START_BYTE]: MavLinkProtocolV1,
259
- [MavLinkProtocolV2.START_BYTE]: MavLinkProtocolV2,
260
- };
261
- /**
262
- * MavLink packet signature definition
263
- */
264
- class MavLinkPacketSignature {
265
- constructor(buffer) {
266
- this.buffer = buffer;
267
- }
268
- /**
269
- * Calculate key based on secret passphrase
270
- *
271
- * @param passphrase secret to generate the key
272
- * @returns key as a buffer
273
- */
274
- static key(passphrase) {
275
- return (0, crypto_1.createHash)('sha256')
276
- .update(passphrase)
277
- .digest();
278
- }
279
- get offset() {
280
- return this.buffer.length - MavLinkPacketSignature.SIGNATURE_LENGTH;
281
- }
282
- /**
283
- * Get the linkId from signature
284
- */
285
- get linkId() {
286
- return this.buffer.readUInt8(this.offset);
287
- }
288
- /**
289
- * Set the linkId in signature
290
- */
291
- set linkId(value) {
292
- this.buffer.writeUInt8(this.offset);
293
- }
294
- /**
295
- * Get the timestamp from signature
296
- */
297
- get timestamp() {
298
- return this.buffer.readUIntLE(this.offset + 1, 6);
299
- }
300
- /**
301
- * Set the linkId in signature
302
- */
303
- set timestamp(value) {
304
- this.buffer.writeUIntLE(value, this.offset + 1, 6);
305
- }
306
- /**
307
- * Get the signature from signature
308
- */
309
- get signature() {
310
- return this.buffer.slice(this.offset + 7, this.offset + 7 + 6).toString('hex');
311
- }
312
- /**
313
- * Set the signature in signature
314
- */
315
- set signature(value) {
316
- this.buffer.write(value, this.offset + 7, 'hex');
317
- }
318
- /**
319
- * Calculates signature of the packet buffer using the provided secret.
320
- * The secret is converted to a hash using the sha256 algorithm which matches
321
- * the way Mission Planner creates keys.
322
- *
323
- * @param key the secret key (Buffer)
324
- * @returns calculated signature value
325
- */
326
- calculate(key) {
327
- const hash = (0, crypto_1.createHash)('sha256')
328
- .update(key)
329
- .update(this.buffer.slice(0, this.buffer.length - 6))
330
- .digest('hex')
331
- .substr(0, 12);
332
- return hash;
333
- }
334
- /**
335
- * Checks the signature of the packet buffer against a given secret
336
- * The secret is converted to a hash using the sha256 algorithm which matches
337
- * the way Mission Planner creates keys.
338
- *
339
- * @param key key
340
- * @returns true if the signature matches, false otherwise
341
- */
342
- matches(key) {
343
- return this.calculate(key) === this.signature;
344
- }
345
- toString() {
346
- return `linkid: ${this.linkId}, timestamp ${this.timestamp}, signature ${this.signature}`;
347
- }
348
- }
349
- exports.MavLinkPacketSignature = MavLinkPacketSignature;
350
- MavLinkPacketSignature.SIGNATURE_LENGTH = 13;
351
- /**
352
- * MavLink packet definition
353
- */
354
- class MavLinkPacket {
355
- constructor(buffer, header = new MavLinkPacketHeader(), payload = Buffer.from(new Uint8Array(255)), crc = 0, protocol = new MavLinkProtocolV1(), signature = null) {
356
- this.buffer = buffer;
357
- this.header = header;
358
- this.payload = payload;
359
- this.crc = crc;
360
- this.protocol = protocol;
361
- this.signature = signature;
362
- }
363
- /**
364
- * Debug information about the packet
365
- *
366
- * @returns string representing debug information about a packet
367
- */
368
- debug() {
369
- return 'Packet ('
370
- + `proto: ${this.protocol.constructor['NAME']}, `
371
- + `sysid: ${this.header.sysid}, `
372
- + `compid: ${this.header.compid}, `
373
- + `msgid: ${this.header.msgid}, `
374
- + `seq: ${this.header.seq}, `
375
- + `plen: ${this.header.payloadLength}, `
376
- + `magic: ${mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[this.header.msgid]} (${(0, utils_1.hex)(mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[this.header.msgid])}), `
377
- + `crc: ${(0, utils_1.hex)(this.crc, 4)}`
378
- + this.signatureToString(this.signature)
379
- + ')';
380
- }
381
- signatureToString(signature) {
382
- return signature ? `, ${signature.toString()}` : '';
383
- }
384
- }
385
- exports.MavLinkPacket = MavLinkPacket;
386
- /**
387
- * This enum describes the different ways validation of a buffer can end
388
- */
389
- var PacketValidationResult;
390
- (function (PacketValidationResult) {
391
- PacketValidationResult[PacketValidationResult["VALID"] = 0] = "VALID";
392
- PacketValidationResult[PacketValidationResult["INVALID"] = 1] = "INVALID";
393
- PacketValidationResult[PacketValidationResult["UNKNOWN"] = 2] = "UNKNOWN";
394
- })(PacketValidationResult || (PacketValidationResult = {}));
395
- /**
396
- * A transform stream that splits the incomming data stream into chunks containing full MavLink messages
397
- */
398
- class MavLinkPacketSplitter extends stream_1.Transform {
399
- /**
400
- * @param opts options to pass on to the Transform constructor
401
- * @param verbose print diagnostic information
402
- * @param onCrcError callback executed if there is a CRC error (mostly for debugging)
403
- */
404
- constructor(opts = {}, onCrcError = () => { }) {
405
- super(opts);
406
- this.log = logger_1.Logger.getLogger(this);
407
- this.buffer = Buffer.from([]);
408
- this.onCrcError = null;
409
- this._validPackagesCount = 0;
410
- this._unknownPackagesCount = 0;
411
- this._invalidPackagesCount = 0;
412
- this.onCrcError = onCrcError;
413
- }
414
- _transform(chunk, encoding, callback) {
415
- this.buffer = Buffer.concat([this.buffer, chunk]);
416
- while (this.buffer.byteLength > 0) {
417
- const offset = this.findStartOfPacket(this.buffer);
418
- if (offset === null) {
419
- // start of the package was not found - need more data
420
- break;
421
- }
422
- // fast-forward the buffer to the first start byte
423
- if (offset > 0) {
424
- this.buffer = this.buffer.slice(offset);
425
- }
426
- this.log.debug('Found potential packet start at', offset);
427
- // get protocol this buffer is encoded with
428
- const Protocol = this.getPacketProtocol(this.buffer);
429
- this.log.debug('Packet protocol is', Protocol.NAME);
430
- // check if the buffer contains at least the minumum size of data
431
- if (this.buffer.length < Protocol.PAYLOAD_OFFSET + MavLinkProtocol.CHECKSUM_LENGTH) {
432
- // current buffer shorter than the shortest message - skipping
433
- this.log.debug('Current buffer shorter than the shortest message - skipping');
434
- break;
435
- }
436
- // check if the current buffer contains the entire message
437
- const expectedBufferLength = this.readPacketLength(this.buffer, Protocol);
438
- this.log.debug('Expected buffer length:', expectedBufferLength, `(${(0, utils_1.hex)(expectedBufferLength)})`);
439
- if (this.buffer.length < expectedBufferLength) {
440
- // current buffer is not fully retrieved yet - skipping
441
- this.log.debug('Current buffer is not fully retrieved yet - skipping');
442
- break;
443
- }
444
- else {
445
- this.log.debug('Current buffer length:', this.buffer.length, `(${(0, utils_1.hex)(this.buffer.length, 4)})`);
446
- }
447
- // retrieve the buffer based on payload size
448
- const buffer = this.buffer.slice(0, expectedBufferLength);
449
- this.log.debug('Recognized buffer length:', buffer.length, `(${(0, utils_1.hex)(buffer.length, 2)})`);
450
- switch (this.validatePacket(buffer, Protocol)) {
451
- case PacketValidationResult.VALID:
452
- this.log.debug('Found a valid packet');
453
- this._validPackagesCount++;
454
- this.push(buffer);
455
- // truncate the buffer to remove the current message
456
- this.buffer = this.buffer.slice(expectedBufferLength);
457
- break;
458
- case PacketValidationResult.INVALID:
459
- this.log.debug('Found an invalid packet - skipping');
460
- this._invalidPackagesCount++;
461
- // truncate the buffer to remove the wrongly identified STX
462
- this.buffer = this.buffer.slice(1);
463
- break;
464
- case PacketValidationResult.UNKNOWN:
465
- this.log.debug('Found an unknown packet - skipping');
466
- this._unknownPackagesCount++;
467
- // truncate the buffer to remove the current message
468
- this.buffer = this.buffer.slice(expectedBufferLength);
469
- break;
470
- }
471
- }
472
- callback(null);
473
- }
474
- findStartOfPacket(buffer) {
475
- const stxv1 = buffer.indexOf(MavLinkProtocolV1.START_BYTE);
476
- const stxv2 = buffer.indexOf(MavLinkProtocolV2.START_BYTE);
477
- if (stxv1 >= 0 && stxv2 >= 0) {
478
- // in the current buffer both STX v1 and v2 are found - get the first one
479
- if (stxv1 < stxv2) {
480
- return stxv1;
481
- }
482
- else {
483
- return stxv2;
484
- }
485
- }
486
- else if (stxv1 >= 0) {
487
- // in the current buffer STX v1 is found
488
- return stxv1;
489
- }
490
- else if (stxv2 >= 0) {
491
- // in the current buffer STX v2 is found
492
- return stxv2;
493
- }
494
- else {
495
- // no STX found
496
- return null;
497
- }
498
- }
499
- getPacketProtocol(buffer) {
500
- return KNOWN_PROTOCOLS_BY_STX[buffer.readUInt8(0)] || null;
501
- }
502
- readPacketLength(buffer, Protocol) {
503
- // check if the current buffer contains the entire message
504
- const payloadLength = buffer.readUInt8(1);
505
- return Protocol.PAYLOAD_OFFSET
506
- + payloadLength
507
- + MavLinkProtocol.CHECKSUM_LENGTH
508
- + (this.isV2Signed(buffer) ? MavLinkPacketSignature.SIGNATURE_LENGTH : 0);
509
- }
510
- validatePacket(buffer, Protocol) {
511
- const protocol = new Protocol();
512
- const header = protocol.header(buffer);
513
- const magic = mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[header.msgid];
514
- if (magic) {
515
- const crc = protocol.crc(buffer);
516
- const trim = this.isV2Signed(buffer)
517
- ? MavLinkPacketSignature.SIGNATURE_LENGTH + MavLinkProtocol.CHECKSUM_LENGTH
518
- : MavLinkProtocol.CHECKSUM_LENGTH;
519
- const crc2 = (0, mavlink_mappings_1.x25crc)(buffer, 1, trim, magic);
520
- if (crc === crc2) {
521
- // this is a proper message that is known and has been validated for corrupted data
522
- return PacketValidationResult.VALID;
523
- }
524
- else {
525
- // CRC mismatch
526
- const message = [
527
- `CRC error; expected: ${crc2} (${(0, utils_1.hex)(crc2, 4)}), got ${crc} (${(0, utils_1.hex)(crc, 4)});`,
528
- `msgid: ${header.msgid} (${(0, utils_1.hex)(header.msgid)}),`,
529
- `seq: ${header.seq} (${(0, utils_1.hex)(header.seq)}),`,
530
- `plen: ${header.payloadLength} (${(0, utils_1.hex)(header.payloadLength)}),`,
531
- `magic: ${magic} (${(0, utils_1.hex)(magic)})`,
532
- ];
533
- this.log.warn(message.join(' '));
534
- this.onCrcError(buffer);
535
- return PacketValidationResult.INVALID;
536
- }
537
- }
538
- else {
539
- // unknown message (as in not generated from the XML sources)
540
- this.log.debug(`Unknown message with id ${header.msgid} (magic number not found) - skipping`);
541
- return PacketValidationResult.UNKNOWN;
542
- }
543
- }
544
- /**
545
- * Checks if the buffer contains the entire message with signature
546
- *
547
- * @param buffer buffer with the message
548
- */
549
- isV2Signed(buffer) {
550
- const protocol = buffer.readUInt8(0);
551
- if (protocol === MavLinkProtocolV2.START_BYTE) {
552
- const flags = buffer.readUInt8(2);
553
- return !!(flags & MavLinkProtocolV2.IFLAG_SIGNED);
554
- }
555
- }
556
- /**
557
- * Number of invalid packages
558
- */
559
- get validPackages() {
560
- return this._validPackagesCount;
561
- }
562
- /**
563
- * Reset the number of valid packages
564
- */
565
- resetValidPackagesCount() {
566
- this._validPackagesCount = 0;
567
- }
568
- /**
569
- * Number of invalid packages
570
- */
571
- get invalidPackages() {
572
- return this._invalidPackagesCount;
573
- }
574
- /**
575
- * Reset the number of invalid packages
576
- */
577
- resetInvalidPackagesCount() {
578
- this._invalidPackagesCount = 0;
579
- }
580
- /**
581
- * Number of invalid packages
582
- */
583
- get unknownPackagesCount() {
584
- return this._unknownPackagesCount;
585
- }
586
- /**
587
- * Reset the number of invalid packages
588
- */
589
- resetUnknownPackagesCount() {
590
- this._unknownPackagesCount = 0;
591
- }
592
- }
593
- exports.MavLinkPacketSplitter = MavLinkPacketSplitter;
594
- /**
595
- * A transform stream that takes a buffer with data and converts it to MavLinkPacket object
596
- */
597
- class MavLinkPacketParser extends stream_1.Transform {
598
- constructor(opts = {}) {
599
- super(Object.assign(Object.assign({}, opts), { objectMode: true }));
600
- this.log = logger_1.Logger.getLogger(this);
601
- }
602
- getProtocol(buffer) {
603
- const startByte = buffer.readUInt8(0);
604
- switch (startByte) {
605
- case MavLinkProtocolV1.START_BYTE:
606
- return new MavLinkProtocolV1();
607
- case MavLinkProtocolV2.START_BYTE:
608
- return new MavLinkProtocolV2();
609
- default:
610
- throw new Error(`Unknown protocol '${(0, utils_1.hex)(startByte)}'`);
611
- }
612
- }
613
- _transform(chunk, encoding, callback) {
614
- const protocol = this.getProtocol(chunk);
615
- const header = protocol.header(chunk);
616
- const payload = protocol.payload(chunk);
617
- const crc = protocol.crc(chunk);
618
- const signature = protocol instanceof MavLinkProtocolV2
619
- ? protocol.signature(chunk, header)
620
- : null;
621
- const packet = new MavLinkPacket(chunk, header, payload, crc, protocol, signature);
622
- callback(null, packet);
623
- }
624
- }
625
- exports.MavLinkPacketParser = MavLinkPacketParser;
626
- /**
627
- * Creates a MavLink packet stream reader that is reading packets from the given input
628
- *
629
- * @param input input stream to read from
630
- */
631
- function createMavLinkStream(input, onCrcError) {
632
- return input
633
- .pipe(new MavLinkPacketSplitter({}, onCrcError))
634
- .pipe(new MavLinkPacketParser());
635
- }
636
- exports.createMavLinkStream = createMavLinkStream;
637
- let seq = 0;
638
- /**
639
- * Send a packet to the stream
640
- *
641
- * @param stream Stream to send the data to
642
- * @param msg message to serialize and send
643
- * @param protocol protocol to use (default: MavLinkProtocolV1)
644
- * @returns number of bytes sent
645
- */
646
- function send(stream, msg, protocol = new MavLinkProtocolV1()) {
647
- return __awaiter(this, void 0, void 0, function* () {
648
- return new Promise((resolve, reject) => {
649
- const buffer = protocol.serialize(msg, seq++);
650
- seq &= 255;
651
- stream.write(buffer, err => {
652
- if (err)
653
- reject(err);
654
- else
655
- resolve(buffer.length);
656
- });
657
- });
658
- });
659
- }
660
- exports.send = send;
661
- /**
662
- * Send a signed packet to the stream. Signed packets are always V2 protocol
663
- *
664
- * @param stream Stream to send the data to
665
- * @param msg message to serialize and send
666
- * @param key key to sign the message with
667
- * @param linkId link id for the signature
668
- * @param sysid system id
669
- * @param compid component id
670
- * @param timestamp optional timestamp for packet signing (default: Date.now())
671
- * @returns number of bytes sent
672
- */
673
- function sendSigned(stream, msg, key, linkId = 1, sysid = MavLinkProtocol.SYS_ID, compid = MavLinkProtocol.COMP_ID, timestamp = Date.now()) {
674
- return __awaiter(this, void 0, void 0, function* () {
675
- return new Promise((resolve, reject) => {
676
- const protocol = new MavLinkProtocolV2(sysid, compid, MavLinkProtocolV2.IFLAG_SIGNED);
677
- const b1 = protocol.serialize(msg, seq++);
678
- seq &= 255;
679
- const b2 = protocol.sign(b1, linkId, key, timestamp);
680
- stream.write(b2, err => {
681
- if (err)
682
- reject(err);
683
- else
684
- resolve(b2.length);
685
- });
686
- });
687
- });
688
- }
689
- exports.sendSigned = sendSigned;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.sendSigned=exports.send=exports.createMavLinkStream=exports.MavLinkPacketParser=exports.MavLinkPacketSplitter=exports.MavLinkPacket=exports.MavLinkPacketSignature=exports.MavLinkProtocolV2=exports.MavLinkProtocolV1=exports.MavLinkProtocol=exports.MavLinkPacketHeader=void 0;const stream_1=require("stream"),crypto_1=require("crypto"),mavlink_mappings_1=require("mavlink-mappings"),mavlink_mappings_2=require("mavlink-mappings"),utils_1=require("./utils"),logger_1=require("./logger"),serialization_1=require("./serialization");class MavLinkPacketHeader{constructor(){this.magic=0,this.payloadLength=0,this.incompatibilityFlags=0,this.compatibilityFlags=0,this.seq=0,this.sysid=0,this.compid=0,this.msgid=0}}exports.MavLinkPacketHeader=MavLinkPacketHeader;class MavLinkProtocol{constructor(){this.log=logger_1.Logger.getLogger(this)}data(t,n){this.log.trace("Deserializing",n.MSG_NAME,"with payload of size",t.length);const e=new n;return n.FIELDS.forEach(s=>{const i=serialization_1.DESERIALIZERS[s.type];if(!i)throw new Error(`Unknown field type ${s.type}`);e[s.name]=i(t,s.offset,s.length)}),e}}exports.MavLinkProtocol=MavLinkProtocol,MavLinkProtocol.NAME="unknown",MavLinkProtocol.START_BYTE=0,MavLinkProtocol.PAYLOAD_OFFSET=0,MavLinkProtocol.CHECKSUM_LENGTH=2,MavLinkProtocol.SYS_ID=254,MavLinkProtocol.COMP_ID=1;class MavLinkProtocolV1 extends MavLinkProtocol{constructor(t=MavLinkProtocol.SYS_ID,n=MavLinkProtocol.COMP_ID){super(),this.sysid=t,this.compid=n}serialize(t,n){this.log.trace("Serializing message (seq:",n,")");const e=t.constructor,s=Buffer.from(new Uint8Array(MavLinkProtocolV1.PAYLOAD_OFFSET+e.PAYLOAD_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH));s.writeUInt8(MavLinkProtocolV1.START_BYTE,0),s.writeUInt8(e.PAYLOAD_LENGTH,1),s.writeUInt8(n,2),s.writeUInt8(this.sysid,3),s.writeUInt8(this.compid,4),s.writeUInt8(e.MSG_ID,5),e.FIELDS.forEach(r=>{const o=serialization_1.SERIALIZERS[r.type];if(!o)throw new Error(`Unknown field type ${r.type}: serializer not found`);o(t[r.name],s,r.offset+MavLinkProtocolV1.PAYLOAD_OFFSET,r.length)});const i=(0,mavlink_mappings_1.x25crc)(s,1,2,e.MAGIC_NUMBER);return s.writeUInt16LE(i,s.length-2),s}header(t){this.log.trace("Reading header from buffer (len:",t.length,")");const n=t.readUInt8(0);if(n!==MavLinkProtocolV1.START_BYTE)throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV1.START_BYTE}, got ${n})`);const e=new MavLinkPacketHeader;return e.magic=n,e.payloadLength=t.readUInt8(1),e.seq=t.readUInt8(2),e.sysid=t.readUInt8(3),e.compid=t.readUInt8(4),e.msgid=t.readUInt8(5),e}crc(t){this.log.trace("Reading crc from buffer (len:",t.length,")");const n=t.readUInt8(1);return t.readUInt16LE(MavLinkProtocolV1.PAYLOAD_OFFSET+n)}payload(t){this.log.trace("Reading payload from buffer (len:",t.length,")");const n=t.readUInt8(1),e=t.slice(MavLinkProtocolV1.PAYLOAD_OFFSET,MavLinkProtocolV1.PAYLOAD_OFFSET+n),s=Buffer.from(new Uint8Array(255-e.length));return Buffer.concat([e,s])}}exports.MavLinkProtocolV1=MavLinkProtocolV1,MavLinkProtocolV1.NAME="MAV_V1",MavLinkProtocolV1.START_BYTE=254,MavLinkProtocolV1.PAYLOAD_OFFSET=6;class MavLinkProtocolV2 extends MavLinkProtocol{constructor(t=MavLinkProtocol.SYS_ID,n=MavLinkProtocol.COMP_ID,e=MavLinkProtocolV2.INCOMPATIBILITY_FLAGS,s=MavLinkProtocolV2.COMPATIBILITY_FLAGS){super(),this.sysid=t,this.compid=n,this.incompatibilityFlags=e,this.compatibilityFlags=s}serialize(t,n){this.log.trace("Serializing message (seq:",n,")");const e=t.constructor,s=Buffer.from(new Uint8Array(MavLinkProtocolV2.PAYLOAD_OFFSET+e.PAYLOAD_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH));s.writeUInt8(MavLinkProtocolV2.START_BYTE,0),s.writeUInt8(this.incompatibilityFlags,2),s.writeUInt8(this.compatibilityFlags,3),s.writeUInt8(n,4),s.writeUInt8(this.sysid,5),s.writeUInt8(this.compid,6),s.writeUIntLE(e.MSG_ID,7,3),e.FIELDS.forEach(c=>{const h=serialization_1.SERIALIZERS[c.type];if(!h)throw new Error(`Unknown field type ${c.type}: serializer not found`);h(t[c.name],s,c.offset+MavLinkProtocolV2.PAYLOAD_OFFSET,c.length)});const i=this.calculateTruncatedPayloadLength(s);s.writeUInt8(i,1);const r=s.slice(0,MavLinkProtocolV2.PAYLOAD_OFFSET+i+MavLinkProtocol.CHECKSUM_LENGTH),o=(0,mavlink_mappings_1.x25crc)(r,1,2,e.MAGIC_NUMBER);return r.writeUInt16LE(o,r.length-MavLinkProtocol.CHECKSUM_LENGTH),r}sign(t,n,e,s=Date.now()){this.log.trace("Signing message");const i=Buffer.concat([t,Buffer.from(new Uint8Array(MavLinkPacketSignature.SIGNATURE_LENGTH))]),r=new MavLinkPacketSignature(i);return r.linkId=n,r.timestamp=s,r.signature=r.calculate(e),i}calculateTruncatedPayloadLength(t){let n=t.length;for(let e=t.length-MavLinkProtocol.CHECKSUM_LENGTH-1;e>=MavLinkProtocolV2.PAYLOAD_OFFSET;e--)if(n=e,t[e]!==0){n++;break}return n-MavLinkProtocolV2.PAYLOAD_OFFSET}header(t){this.log.trace("Reading header from buffer (len:",t.length,")");const n=t.readUInt8(0);if(n!==MavLinkProtocolV2.START_BYTE)throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV2.START_BYTE}, got ${n})`);const e=new MavLinkPacketHeader;return e.magic=n,e.payloadLength=t.readUInt8(1),e.incompatibilityFlags=t.readUInt8(2),e.compatibilityFlags=t.readUInt8(3),e.seq=t.readUInt8(4),e.sysid=t.readUInt8(5),e.compid=t.readUInt8(6),e.msgid=t.readUIntLE(7,3),e}crc(t){this.log.trace("Reading crc from buffer (len:",t.length,")");const n=t.readUInt8(1);return t.readUInt16LE(MavLinkProtocolV2.PAYLOAD_OFFSET+n)}payload(t){this.log.trace("Reading payload from buffer (len:",t.length,")");const n=t.readUInt8(1),e=t.slice(MavLinkProtocolV2.PAYLOAD_OFFSET,MavLinkProtocolV2.PAYLOAD_OFFSET+n),s=Buffer.from(new Uint8Array(255-e.length));return Buffer.concat([e,s])}signature(t,n){return this.log.trace("Reading signature from buffer (len:",t.length,")"),n.incompatibilityFlags&MavLinkProtocolV2.IFLAG_SIGNED?new MavLinkPacketSignature(t):null}}exports.MavLinkProtocolV2=MavLinkProtocolV2,MavLinkProtocolV2.NAME="MAV_V2",MavLinkProtocolV2.START_BYTE=253,MavLinkProtocolV2.PAYLOAD_OFFSET=10,MavLinkProtocolV2.INCOMPATIBILITY_FLAGS=0,MavLinkProtocolV2.COMPATIBILITY_FLAGS=0,MavLinkProtocolV2.IFLAG_SIGNED=1;const KNOWN_PROTOCOLS_BY_STX={[MavLinkProtocolV1.START_BYTE]:MavLinkProtocolV1,[MavLinkProtocolV2.START_BYTE]:MavLinkProtocolV2};class MavLinkPacketSignature{constructor(t){this.buffer=t}static key(t){return(0,crypto_1.createHash)("sha256").update(t).digest()}get offset(){return this.buffer.length-MavLinkPacketSignature.SIGNATURE_LENGTH}get linkId(){return this.buffer.readUInt8(this.offset)}set linkId(t){this.buffer.writeUInt8(this.offset)}get timestamp(){return this.buffer.readUIntLE(this.offset+1,6)}set timestamp(t){this.buffer.writeUIntLE(t,this.offset+1,6)}get signature(){return this.buffer.slice(this.offset+7,this.offset+7+6).toString("hex")}set signature(t){this.buffer.write(t,this.offset+7,"hex")}calculate(t){return(0,crypto_1.createHash)("sha256").update(t).update(this.buffer.slice(0,this.buffer.length-6)).digest("hex").substr(0,12)}matches(t){return this.calculate(t)===this.signature}toString(){return`linkid: ${this.linkId}, timestamp ${this.timestamp}, signature ${this.signature}`}}exports.MavLinkPacketSignature=MavLinkPacketSignature,MavLinkPacketSignature.SIGNATURE_LENGTH=13;class MavLinkPacket{constructor(t,n=new MavLinkPacketHeader,e=Buffer.from(new Uint8Array(255)),s=0,i=new MavLinkProtocolV1,r=null){this.buffer=t,this.header=n,this.payload=e,this.crc=s,this.protocol=i,this.signature=r}debug(){return`Packet (proto: ${this.protocol.constructor.NAME}, sysid: ${this.header.sysid}, compid: ${this.header.compid}, msgid: ${this.header.msgid}, seq: ${this.header.seq}, plen: ${this.header.payloadLength}, magic: ${mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[this.header.msgid]} (${(0,utils_1.hex)(mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[this.header.msgid])}), crc: ${(0,utils_1.hex)(this.crc,4)}`+this.signatureToString(this.signature)+")"}signatureToString(t){return t?`, ${t.toString()}`:""}}exports.MavLinkPacket=MavLinkPacket;var PacketValidationResult;(function(a){a[a.VALID=0]="VALID",a[a.INVALID=1]="INVALID",a[a.UNKNOWN=2]="UNKNOWN"})(PacketValidationResult||(PacketValidationResult={}));class MavLinkPacketSplitter extends stream_1.Transform{constructor(t={},n=()=>{}){super(t),this.log=logger_1.Logger.getLogger(this),this.buffer=Buffer.from([]),this.onCrcError=null,this._validPackagesCount=0,this._unknownPackagesCount=0,this._invalidPackagesCount=0,this.onCrcError=n}_transform(t,n,e){for(this.buffer=Buffer.concat([this.buffer,t]);this.buffer.byteLength>0;){const s=this.findStartOfPacket(this.buffer);if(s===null)break;s>0&&(this.buffer=this.buffer.slice(s)),this.log.debug("Found potential packet start at",s);const i=this.getPacketProtocol(this.buffer);if(this.log.debug("Packet protocol is",i.NAME),this.buffer.length<i.PAYLOAD_OFFSET+MavLinkProtocol.CHECKSUM_LENGTH){this.log.debug("Current buffer shorter than the shortest message - skipping");break}const r=this.readPacketLength(this.buffer,i);if(this.log.debug("Expected buffer length:",r,`(${(0,utils_1.hex)(r)})`),this.buffer.length<r){this.log.debug("Current buffer is not fully retrieved yet - skipping");break}else this.log.debug("Current buffer length:",this.buffer.length,`(${(0,utils_1.hex)(this.buffer.length,4)})`);const o=this.buffer.slice(0,r);switch(this.log.debug("Recognized buffer length:",o.length,`(${(0,utils_1.hex)(o.length,2)})`),this.validatePacket(o,i)){case PacketValidationResult.VALID:this.log.debug("Found a valid packet"),this._validPackagesCount++,this.push(o),this.buffer=this.buffer.slice(r);break;case PacketValidationResult.INVALID:this.log.debug("Found an invalid packet - skipping"),this._invalidPackagesCount++,this.buffer=this.buffer.slice(1);break;case PacketValidationResult.UNKNOWN:this.log.debug("Found an unknown packet - skipping"),this._unknownPackagesCount++,this.buffer=this.buffer.slice(r);break}}e(null)}findStartOfPacket(t){const n=t.indexOf(MavLinkProtocolV1.START_BYTE),e=t.indexOf(MavLinkProtocolV2.START_BYTE);return n>=0&&e>=0?n<e?n:e:n>=0?n:e>=0?e:null}getPacketProtocol(t){return KNOWN_PROTOCOLS_BY_STX[t.readUInt8(0)]||null}readPacketLength(t,n){const e=t.readUInt8(1);return n.PAYLOAD_OFFSET+e+MavLinkProtocol.CHECKSUM_LENGTH+(this.isV2Signed(t)?MavLinkPacketSignature.SIGNATURE_LENGTH:0)}validatePacket(t,n){const e=new n,s=e.header(t),i=mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[s.msgid];if(i){const r=e.crc(t),o=this.isV2Signed(t)?MavLinkPacketSignature.SIGNATURE_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH:MavLinkProtocol.CHECKSUM_LENGTH,c=(0,mavlink_mappings_1.x25crc)(t,1,o,i);if(r===c)return PacketValidationResult.VALID;{const h=[`CRC error; expected: ${c} (${(0,utils_1.hex)(c,4)}), got ${r} (${(0,utils_1.hex)(r,4)});`,`msgid: ${s.msgid} (${(0,utils_1.hex)(s.msgid)}),`,`seq: ${s.seq} (${(0,utils_1.hex)(s.seq)}),`,`plen: ${s.payloadLength} (${(0,utils_1.hex)(s.payloadLength)}),`,`magic: ${i} (${(0,utils_1.hex)(i)})`];return this.log.warn(h.join(" ")),this.onCrcError(t),PacketValidationResult.INVALID}}else return this.log.debug(`Unknown message with id ${s.msgid} (magic number not found) - skipping`),PacketValidationResult.UNKNOWN}isV2Signed(t){if(t.readUInt8(0)===MavLinkProtocolV2.START_BYTE)return!!(t.readUInt8(2)&MavLinkProtocolV2.IFLAG_SIGNED)}get validPackages(){return this._validPackagesCount}resetValidPackagesCount(){this._validPackagesCount=0}get invalidPackages(){return this._invalidPackagesCount}resetInvalidPackagesCount(){this._invalidPackagesCount=0}get unknownPackagesCount(){return this._unknownPackagesCount}resetUnknownPackagesCount(){this._unknownPackagesCount=0}}exports.MavLinkPacketSplitter=MavLinkPacketSplitter;class MavLinkPacketParser extends stream_1.Transform{constructor(t={}){super({...t,objectMode:!0}),this.log=logger_1.Logger.getLogger(this)}getProtocol(t){const n=t.readUInt8(0);switch(n){case MavLinkProtocolV1.START_BYTE:return new MavLinkProtocolV1;case MavLinkProtocolV2.START_BYTE:return new MavLinkProtocolV2;default:throw new Error(`Unknown protocol '${(0,utils_1.hex)(n)}'`)}}_transform(t,n,e){const s=this.getProtocol(t),i=s.header(t),r=s.payload(t),o=s.crc(t),c=s instanceof MavLinkProtocolV2?s.signature(t,i):null,h=new MavLinkPacket(t,i,r,o,s,c);e(null,h)}}exports.MavLinkPacketParser=MavLinkPacketParser;function createMavLinkStream(a,t){return a.pipe(new MavLinkPacketSplitter({},t)).pipe(new MavLinkPacketParser)}exports.createMavLinkStream=createMavLinkStream;let seq=0;async function send(a,t,n=new MavLinkProtocolV1){return new Promise((e,s)=>{const i=n.serialize(t,seq++);seq&=255,a.write(i,r=>{r?s(r):e(i.length)})})}exports.send=send;async function sendSigned(a,t,n,e=1,s=MavLinkProtocol.SYS_ID,i=MavLinkProtocol.COMP_ID,r=Date.now()){return new Promise((o,c)=>{const h=new MavLinkProtocolV2(s,i,MavLinkProtocolV2.IFLAG_SIGNED),d=h.serialize(t,seq++);seq&=255;const g=h.sign(d,e,n,r);a.write(g,l=>{l?c(l):o(g.length)})})}exports.sendSigned=sendSigned;