node-mavlink 1.0.8 → 1.0.9

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.
@@ -0,0 +1,35 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Convert a number to hexadecimal representation with a minumum
4
+ * number of characters and optional prefix (0x by default)
5
+ *
6
+ * @param n value to convert
7
+ * @param len length of the converted string (without prefix)
8
+ * @param prefix prefix to prepend the generated string with
9
+ */
10
+ export declare function hex(n: number, len?: number, prefix?: string): string;
11
+ /**
12
+ * Dump a buffer in a readable form
13
+ *
14
+ * @param buffer buffer to dump
15
+ * @param lineWidth width of the line, in bytes of buffer
16
+ */
17
+ export declare function dump(buffer: Buffer, lineWidth?: number): void;
18
+ /**
19
+ * Sleep for a given number of miliseconds
20
+ *
21
+ * @param ms number of miliseconds to sleep
22
+ */
23
+ export declare function sleep(ms: any): Promise<unknown>;
24
+ /**
25
+ * Execute a callback every <code>interval</code>ms and if it will not return
26
+ * a truthy value in the <code>timeout<code>ms then throw a Timeout exception.
27
+ * This is a very useful utility that will allow you to specify how often
28
+ * a particular expression should be evaluated and how long will it take to end
29
+ * the execution without success. Great for time-sensitive operations.
30
+ *
31
+ * @param cb callback to call every <code>interval</code>ms
32
+ * @param timeout number of miliseconds that need to pass before the Timeout exception is thrown
33
+ * @param interval number of miliseconds before re-running the callback
34
+ */
35
+ export declare function waitFor(cb: any, timeout?: number, interval?: number): Promise<unknown>;
@@ -0,0 +1,88 @@
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.waitFor = exports.sleep = exports.dump = exports.hex = void 0;
13
+ /**
14
+ * Convert a number to hexadecimal representation with a minumum
15
+ * number of characters and optional prefix (0x by default)
16
+ *
17
+ * @param n value to convert
18
+ * @param len length of the converted string (without prefix)
19
+ * @param prefix prefix to prepend the generated string with
20
+ */
21
+ function hex(n, len = 2, prefix = '0x') {
22
+ return `${prefix}${n.toString(16).padStart(len, '0')}`;
23
+ }
24
+ exports.hex = hex;
25
+ /**
26
+ * Dump a buffer in a readable form
27
+ *
28
+ * @param buffer buffer to dump
29
+ * @param lineWidth width of the line, in bytes of buffer
30
+ */
31
+ function dump(buffer, lineWidth = 16) {
32
+ const line = [];
33
+ let address = 0;
34
+ for (let i = 0; i < buffer.length; i++) {
35
+ line.push(hex(buffer[i], 2, '0x'));
36
+ if (line.length === lineWidth) {
37
+ console.log(hex(address, 4), '|', line.join(' '));
38
+ address += lineWidth;
39
+ line.length = 0;
40
+ }
41
+ }
42
+ if (line.length > 0) {
43
+ console.log(hex(address, 4), '|', line.join(' '));
44
+ }
45
+ }
46
+ exports.dump = dump;
47
+ /**
48
+ * Sleep for a given number of miliseconds
49
+ *
50
+ * @param ms number of miliseconds to sleep
51
+ */
52
+ function sleep(ms) {
53
+ return new Promise(resolve => setTimeout(resolve, ms));
54
+ }
55
+ exports.sleep = sleep;
56
+ /**
57
+ * Execute a callback every <code>interval</code>ms and if it will not return
58
+ * a truthy value in the <code>timeout<code>ms then throw a Timeout exception.
59
+ * This is a very useful utility that will allow you to specify how often
60
+ * a particular expression should be evaluated and how long will it take to end
61
+ * the execution without success. Great for time-sensitive operations.
62
+ *
63
+ * @param cb callback to call every <code>interval</code>ms
64
+ * @param timeout number of miliseconds that need to pass before the Timeout exception is thrown
65
+ * @param interval number of miliseconds before re-running the callback
66
+ */
67
+ function waitFor(cb, timeout = 10000, interval = 100) {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ return new Promise((resolve, reject) => {
70
+ const timeoutTimer = setTimeout(() => {
71
+ cleanup();
72
+ reject('Timeout');
73
+ }, timeout);
74
+ const intervalTimer = setInterval(() => {
75
+ const result = cb();
76
+ if (result) {
77
+ cleanup();
78
+ resolve(result);
79
+ }
80
+ });
81
+ const cleanup = () => {
82
+ clearTimeout(timeoutTimer);
83
+ clearTimeout(intervalTimer);
84
+ };
85
+ });
86
+ });
87
+ }
88
+ exports.waitFor = waitFor;
Binary file
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env -S npx ts-node
2
2
 
3
3
  import { createReadStream } from 'fs'
4
- import { MavLinkPacketSplitter, MavLinkPacketParser, MavLinkPacket } from '..'
4
+ import { MavLinkPacketSplitter, MavLinkPacketParser } from '..'
5
5
  import { minimal, common, ardupilotmega, uavionix, icarous } from '..'
6
6
 
7
- const file = createReadStream('./mavlink-v2-3412-packets.bin')
7
+ const file = createReadStream('./GH-5.bin')
8
8
 
9
- const splitter = new MavLinkPacketSplitter({}, true)
9
+ const splitter = new MavLinkPacketSplitter()
10
10
 
11
11
  const reader = file
12
12
  .pipe(splitter)
package/index.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from 'mavlink-mappings'
2
+ export * from './lib/utils'
3
+ export * from './lib/logger'
2
4
  export * from './lib/mavlink'
3
5
  export * from './lib/mavesp'
package/lib/logger.ts ADDED
@@ -0,0 +1,128 @@
1
+ import EventEmitter = require('events')
2
+
3
+ /**
4
+ * Level of the log entry
5
+ */
6
+ export enum LogLevel {
7
+ trace = 5,
8
+ debug = 4,
9
+ info = 3,
10
+ warn = 2,
11
+ error = 1,
12
+ fatal = 0,
13
+ }
14
+
15
+ type LoggerRegistry = { [x: string]: Logger }
16
+ type LoggerEvents = 'log'
17
+ type LoggerEventHandler = (context: string, level: LogLevel, message: any[]) => void
18
+
19
+ /**
20
+ * Simplified interface for logging facilities
21
+ */
22
+ export class Logger {
23
+ private static readonly events: EventEmitter = new EventEmitter()
24
+ private static registry: LoggerRegistry = {}
25
+
26
+ /**
27
+ * Gets a logger by name
28
+ *
29
+ * @param context logger context
30
+ */
31
+ static getLogger(context) {
32
+ let name = ''
33
+ if (typeof context === 'function') name = context.name
34
+ else if (typeof context === 'object') name = (<any>context).constructor.name
35
+ else if (typeof context === 'string') name = context
36
+ else throw new Error(`Do not know how to get logger for ${context} (${typeof context})`)
37
+
38
+ if (!Logger.registry[name]) Logger.registry[name] = new Logger(name)
39
+
40
+ return Logger.registry[name]
41
+ }
42
+
43
+ /**
44
+ * Binds an event handler
45
+ *
46
+ * @param event event to react to
47
+ * @param handler event handler
48
+ */
49
+ static on(event: LoggerEvents, handler: (context, level, message) => void) {
50
+ this.events.on(event, handler)
51
+ }
52
+
53
+ /**
54
+ * Removes an event handler
55
+ *
56
+ * @param event event to react to
57
+ * @param handler event handler
58
+ */
59
+ static off(event: LoggerEvents, handler: LoggerEventHandler) {
60
+ this.events.off(event, handler)
61
+ }
62
+
63
+ private context: string
64
+
65
+ /**
66
+ * Constructs a new logger instance
67
+ *
68
+ * @param context logger context
69
+ */
70
+ constructor(context: string) {
71
+ this.context = context
72
+ Logger.events.emit('logger-created', Logger.registry[context])
73
+ }
74
+
75
+ /**
76
+ * Sends a log message if the trace level is enabled for this logger
77
+ *
78
+ * @param args parameters for the log entry
79
+ */
80
+ trace(...args: any) {
81
+ Logger.events.emit('log', { context: this.context, level: LogLevel.trace, message: args })
82
+ }
83
+
84
+ /**
85
+ * Sends a log message if the debug level is enabled for this logger
86
+ *
87
+ * @param args parameters for the log entry
88
+ */
89
+ debug(...args: any) {
90
+ Logger.events.emit('log', { context: this.context, level: LogLevel.debug, message: args })
91
+ }
92
+
93
+ /**
94
+ * Sends a log message if the info level is enabled for this logger
95
+ *
96
+ * @param args parameters for the log entry
97
+ */
98
+ info(...args: any) {
99
+ Logger.events.emit('log', { context: this.context, level: LogLevel.info, message: args })
100
+ }
101
+
102
+ /**
103
+ * Sends a log message if the warn level is enabled for this logger
104
+ *
105
+ * @param args parameters for the log entry
106
+ */
107
+ warn(...args: any) {
108
+ Logger.events.emit('log', { context: this.context, level: LogLevel.warn, message: args })
109
+ }
110
+
111
+ /**
112
+ * Sends a log message if the error level is enabled for this logger
113
+ *
114
+ * @param args parameters for the log entry
115
+ */
116
+ error(...args: any) {
117
+ Logger.events.emit('log', { context: this.context, level: LogLevel.error, message: args })
118
+ }
119
+
120
+ /**
121
+ * Sends a log message if the fatal level is enabled for this logger
122
+ *
123
+ * @param args parameters for the log entry
124
+ */
125
+ fatal(...args: any) {
126
+ Logger.events.emit('log', { context: this.context, level: LogLevel.fatal, message: args })
127
+ }
128
+ }
package/lib/mavesp.ts CHANGED
@@ -4,7 +4,8 @@ import { Socket, createSocket } from 'dgram'
4
4
  import { Writable, PassThrough } from 'stream'
5
5
  import { MavLinkPacketSplitter, MavLinkPacketParser, MavLinkPacketSignature } from './mavlink'
6
6
  import { MavLinkProtocol, MavLinkProtocolV2 } from './mavlink'
7
- import { uint8_t, waitFor, MavLinkData } from 'mavlink-mappings'
7
+ import { waitFor } from './utils'
8
+ import { uint8_t, MavLinkData } from 'mavlink-mappings'
8
9
 
9
10
  /**
10
11
  * Encapsulation of communication with MavEsp8266
package/lib/mavlink.ts CHANGED
@@ -1,16 +1,17 @@
1
- import { Transform, TransformCallback, Writable } from 'stream'
1
+ import { Transform, TransformCallback, Readable, Writable } from 'stream'
2
2
  import { createHash } from 'crypto'
3
- import { uint8_t, uint16_t } from 'mavlink-mappings'
4
- import { x25crc, dump } from 'mavlink-mappings'
3
+ import { uint8_t, uint16_t, x25crc } from 'mavlink-mappings'
5
4
  import { MSG_ID_MAGIC_NUMBER } from 'mavlink-mappings'
6
5
  import { MavLinkData, MavLinkDataConstructor } from 'mavlink-mappings'
7
6
 
7
+ import { hex } from './utils'
8
+ import { Logger } from './logger'
8
9
  import { SERIALIZERS, DESERIALIZERS } from './serialization'
9
10
 
10
11
  /**
11
12
  * Header definition of the MavLink packet
12
13
  */
13
- export class MavLinkPacketHeader {
14
+ export class MavLinkPacketHeader {
14
15
  magic: number = 0
15
16
  payloadLength: uint8_t = 0
16
17
  incompatibilityFlags: uint8_t = 0
@@ -28,6 +29,8 @@ import { SERIALIZERS, DESERIALIZERS } from './serialization'
28
29
  * data classes from the given payload buffer
29
30
  */
30
31
  export abstract class MavLinkProtocol {
32
+ protected readonly log = Logger.getLogger(this)
33
+
31
34
  static NAME = 'unknown'
32
35
  static START_BYTE = 0
33
36
  static PAYLOAD_OFFSET = 0
@@ -64,6 +67,8 @@ export abstract class MavLinkProtocol {
64
67
  * Deserialize payload into actual data class
65
68
  */
66
69
  data<T extends MavLinkData>(payload: Buffer, clazz: MavLinkDataConstructor<T>): T {
70
+ this.log.trace('Deserializing', clazz.MSG_NAME, 'with payload of size', payload.length)
71
+
67
72
  const instance = new clazz()
68
73
  clazz.FIELDS.forEach(field => {
69
74
  const deserialize = DESERIALIZERS[field.type]
@@ -107,6 +112,8 @@ export class MavLinkProtocolV1 extends MavLinkProtocol {
107
112
  }
108
113
 
109
114
  serialize(message: MavLinkData, seq: number): Buffer {
115
+ this.log.trace('Serializing message (seq:', seq, ')')
116
+
110
117
  const definition: MavLinkDataConstructor<MavLinkData> = <any>message.constructor
111
118
  const buffer = Buffer.from(new Uint8Array(MavLinkProtocolV1.PAYLOAD_OFFSET + definition.PAYLOAD_LENGTH + MavLinkProtocol.CHECKSUM_LENGTH))
112
119
 
@@ -133,6 +140,8 @@ export class MavLinkProtocolV1 extends MavLinkProtocol {
133
140
  }
134
141
 
135
142
  header(buffer: Buffer): MavLinkPacketHeader {
143
+ this.log.trace('Reading header from buffer (len:', buffer.length, ')')
144
+
136
145
  const startByte = buffer.readUInt8(0)
137
146
  if (startByte !== MavLinkProtocolV1.START_BYTE) {
138
147
  throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV1.START_BYTE}, got ${startByte})`)
@@ -152,12 +161,16 @@ export class MavLinkProtocolV1 extends MavLinkProtocol {
152
161
  /**
153
162
  * Deserialize packet checksum
154
163
  */
155
- crc(buffer: Buffer): uint16_t {
164
+ crc(buffer: Buffer): uint16_t {
165
+ this.log.trace('Reading crc from buffer (len:', buffer.length, ')')
166
+
156
167
  const plen = buffer.readUInt8(1)
157
168
  return buffer.readUInt16LE(MavLinkProtocolV1.PAYLOAD_OFFSET + plen)
158
169
  }
159
170
 
160
171
  payload(buffer: Buffer): Buffer {
172
+ this.log.trace('Reading payload from buffer (len:', buffer.length, ')')
173
+
161
174
  const plen = buffer.readUInt8(1)
162
175
  const payload = buffer.slice(MavLinkProtocolV1.PAYLOAD_OFFSET, MavLinkProtocolV1.PAYLOAD_OFFSET + plen)
163
176
  const padding = Buffer.from(new Uint8Array(255 - payload.length))
@@ -188,6 +201,8 @@ export class MavLinkProtocolV2 extends MavLinkProtocol {
188
201
  }
189
202
 
190
203
  serialize(message: MavLinkData, seq: number): Buffer {
204
+ this.log.trace('Serializing message (seq:', seq, ')')
205
+
191
206
  const definition: MavLinkDataConstructor<MavLinkData> = <any>message.constructor
192
207
  const buffer = Buffer.from(new Uint8Array(MavLinkProtocolV2.PAYLOAD_OFFSET + definition.PAYLOAD_LENGTH + MavLinkProtocol.CHECKSUM_LENGTH))
193
208
 
@@ -224,9 +239,12 @@ export class MavLinkProtocolV2 extends MavLinkProtocol {
224
239
  * @param buffer buffer with the original, unsigned package
225
240
  * @param linkId id of the link
226
241
  * @param key key to sign the package with
242
+ * @param timestamp optional timestamp for packet signing (default: Date.now())
227
243
  * @returns signed package
228
244
  */
229
- sign(buffer: Buffer, linkId: number, key: Buffer) {
245
+ sign(buffer: Buffer, linkId: number, key: Buffer, timestamp = Date.now()) {
246
+ this.log.trace('Signing message')
247
+
230
248
  const result = Buffer.concat([
231
249
  buffer,
232
250
  Buffer.from(new Uint8Array(MavLinkPacketSignature.SIGNATURE_LENGTH))
@@ -234,7 +252,7 @@ export class MavLinkProtocolV2 extends MavLinkProtocol {
234
252
 
235
253
  const signer = new MavLinkPacketSignature(result)
236
254
  signer.linkId = linkId
237
- signer.timestamp = Date.now()
255
+ signer.timestamp = timestamp
238
256
  signer.signature = signer.calculate(key)
239
257
 
240
258
  return result
@@ -255,6 +273,8 @@ export class MavLinkProtocolV2 extends MavLinkProtocol {
255
273
  }
256
274
 
257
275
  header(buffer: Buffer): MavLinkPacketHeader {
276
+ this.log.trace('Reading header from buffer (len:', buffer.length, ')')
277
+
258
278
  const startByte = buffer.readUInt8(0)
259
279
  if (startByte !== MavLinkProtocolV2.START_BYTE) {
260
280
  throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV2.START_BYTE}, got ${startByte})`)
@@ -277,11 +297,15 @@ export class MavLinkProtocolV2 extends MavLinkProtocol {
277
297
  * Deserialize packet checksum
278
298
  */
279
299
  crc(buffer: Buffer): uint16_t {
300
+ this.log.trace('Reading crc from buffer (len:', buffer.length, ')')
301
+
280
302
  const plen = buffer.readUInt8(1)
281
303
  return buffer.readUInt16LE(MavLinkProtocolV2.PAYLOAD_OFFSET + plen)
282
304
  }
283
305
 
284
306
  payload(buffer: Buffer): Buffer {
307
+ this.log.trace('Reading payload from buffer (len:', buffer.length, ')')
308
+
285
309
  const plen = buffer.readUInt8(1)
286
310
  const payload = buffer.slice(MavLinkProtocolV2.PAYLOAD_OFFSET, MavLinkProtocolV2.PAYLOAD_OFFSET + plen)
287
311
  const padding = Buffer.from(new Uint8Array(255 - payload.length))
@@ -289,6 +313,8 @@ export class MavLinkProtocolV2 extends MavLinkProtocol {
289
313
  }
290
314
 
291
315
  signature(buffer: Buffer, header: MavLinkPacketHeader): MavLinkPacketSignature {
316
+ this.log.trace('Reading signature from buffer (len:', buffer.length, ')')
317
+
292
318
  if (header.incompatibilityFlags & MavLinkProtocolV2.IFLAG_SIGNED) {
293
319
  return new MavLinkPacketSignature(buffer)
294
320
  } else {
@@ -432,7 +458,8 @@ export class MavLinkPacket {
432
458
  + `msgid: ${this.header.msgid}, `
433
459
  + `seq: ${this.header.seq}, `
434
460
  + `plen: ${this.header.payloadLength}, `
435
- + `crc: ${this.crc.toString(16).padStart(2, '0')}`
461
+ + `magic: ${MSG_ID_MAGIC_NUMBER[this.header.msgid]} (${hex(MSG_ID_MAGIC_NUMBER[this.header.msgid])}), `
462
+ + `crc: ${hex(this.crc, 4)}`
436
463
  + this.signatureToString(this.signature)
437
464
  + ')'
438
465
  }
@@ -447,19 +474,28 @@ export class MavLinkPacket {
447
474
  */
448
475
  enum PacketValidationResult { VALID, INVALID, UNKNOWN }
449
476
 
477
+ type BufferCallback = (buffer: Buffer) => void
478
+
450
479
  /**
451
480
  * A transform stream that splits the incomming data stream into chunks containing full MavLink messages
452
481
  */
453
482
  export class MavLinkPacketSplitter extends Transform {
483
+ protected readonly log = Logger.getLogger(this)
484
+
454
485
  private buffer = Buffer.from([])
455
- private verbose = false
486
+ private onCrcError = null
456
487
  private _validPackagesCount = 0
457
488
  private _unknownPackagesCount = 0
458
489
  private _invalidPackagesCount = 0
459
490
 
460
- constructor(opts = {}, verbose = false) {
491
+ /**
492
+ * @param opts options to pass on to the Transform constructor
493
+ * @param verbose print diagnostic information
494
+ * @param onCrcError callback executed if there is a CRC error (mostly for debugging)
495
+ */
496
+ constructor(opts = {}, onCrcError: BufferCallback = () => {}) {
461
497
  super(opts)
462
- this.verbose = verbose
498
+ this.onCrcError = onCrcError
463
499
  }
464
500
 
465
501
  _transform(chunk: Buffer, encoding, callback: TransformCallback) {
@@ -477,38 +513,51 @@ export class MavLinkPacketSplitter extends Transform {
477
513
  this.buffer = this.buffer.slice(offset)
478
514
  }
479
515
 
516
+ this.log.debug('Found potential packet start at', offset)
517
+
480
518
  // get protocol this buffer is encoded with
481
519
  const Protocol = this.getPacketProtocol(this.buffer)
482
520
 
521
+ this.log.debug('Packet protocol is', Protocol.NAME)
522
+
483
523
  // check if the buffer contains at least the minumum size of data
484
524
  if (this.buffer.length < Protocol.PAYLOAD_OFFSET + MavLinkProtocol.CHECKSUM_LENGTH) {
485
525
  // current buffer shorter than the shortest message - skipping
526
+ this.log.debug('Current buffer shorter than the shortest message - skipping')
486
527
  break
487
528
  }
488
529
 
489
530
  // check if the current buffer contains the entire message
490
531
  const expectedBufferLength = this.readPacketLength(this.buffer, Protocol)
532
+ this.log.debug('Expected buffer length:', expectedBufferLength, `(${hex(expectedBufferLength)})`)
491
533
  if (this.buffer.length < expectedBufferLength) {
492
534
  // current buffer is not fully retrieved yet - skipping
535
+ this.log.debug('Current buffer is not fully retrieved yet - skipping')
493
536
  break
537
+ } else {
538
+ this.log.debug('Current buffer length:', this.buffer.length, `(${hex(this.buffer.length, 4)})`)
494
539
  }
495
540
 
496
541
  // retrieve the buffer based on payload size
497
542
  const buffer = this.buffer.slice(0, expectedBufferLength)
543
+ this.log.debug('Recognized buffer length:', buffer.length, `(${hex(buffer.length, 2)})`)
498
544
 
499
545
  switch (this.validatePacket(buffer, Protocol)) {
500
546
  case PacketValidationResult.VALID:
547
+ this.log.debug('Found a valid packet')
501
548
  this._validPackagesCount++
502
549
  this.push(buffer)
503
550
  // truncate the buffer to remove the current message
504
551
  this.buffer = this.buffer.slice(expectedBufferLength)
505
552
  break
506
553
  case PacketValidationResult.INVALID:
554
+ this.log.debug('Found an invalid packet - skipping')
507
555
  this._invalidPackagesCount++
508
556
  // truncate the buffer to remove the wrongly identified STX
509
557
  this.buffer = this.buffer.slice(1)
510
558
  break
511
559
  case PacketValidationResult.UNKNOWN:
560
+ this.log.debug('Found an unknown packet - skipping')
512
561
  this._unknownPackagesCount++
513
562
  // truncate the buffer to remove the current message
514
563
  this.buffer = this.buffer.slice(expectedBufferLength)
@@ -570,21 +619,22 @@ export class MavLinkPacketSplitter extends Transform {
570
619
  return PacketValidationResult.VALID
571
620
  } else {
572
621
  // CRC mismatch
573
- if (this.verbose) {
574
- console.error(
575
- 'CRC error; expected', crc2, `(0x${crc2.toString(16).padStart(4, '0')})`,
576
- 'got', crc, `(0x${crc.toString(16).padStart(4, '0')})`,
577
- '; msgid:', header.msgid, ', magic:', magic
578
- )
579
- dump(buffer)
580
- }
622
+ const message = [
623
+ `CRC error; expected: ${crc2} (${hex(crc2, 4)}), got ${crc} (${hex(crc, 4)});`,
624
+ `msgid: ${header.msgid} (${hex(header.msgid)}),`,
625
+ `seq: ${header.seq} (${hex(header.seq)}),`,
626
+ `plen: ${header.payloadLength} (${hex(header.payloadLength)}),`,
627
+ `magic: ${magic} (${hex(magic)})`,
628
+ ]
629
+ this.log.warn(message.join(' '))
630
+ this.onCrcError(buffer)
631
+
581
632
  return PacketValidationResult.INVALID
582
633
  }
583
634
  } else {
584
635
  // unknown message (as in not generated from the XML sources)
585
- if (this.verbose) {
586
- console.error(`Unknown message with id ${header.msgid} (magic number not found) - skipping`)
587
- }
636
+ this.log.debug(`Unknown message with id ${header.msgid} (magic number not found) - skipping`)
637
+
588
638
  return PacketValidationResult.UNKNOWN
589
639
  }
590
640
  }
@@ -613,7 +663,7 @@ export class MavLinkPacketSplitter extends Transform {
613
663
  * Reset the number of valid packages
614
664
  */
615
665
  resetValidPackagesCount() {
616
- this,this._validPackagesCount = 0
666
+ this._validPackagesCount = 0
617
667
  }
618
668
 
619
669
  /**
@@ -627,7 +677,7 @@ export class MavLinkPacketSplitter extends Transform {
627
677
  * Reset the number of invalid packages
628
678
  */
629
679
  resetInvalidPackagesCount() {
630
- this,this._invalidPackagesCount = 0
680
+ this._invalidPackagesCount = 0
631
681
  }
632
682
 
633
683
  /**
@@ -641,7 +691,7 @@ export class MavLinkPacketSplitter extends Transform {
641
691
  * Reset the number of invalid packages
642
692
  */
643
693
  resetUnknownPackagesCount() {
644
- this,this._unknownPackagesCount = 0
694
+ this._unknownPackagesCount = 0
645
695
  }
646
696
  }
647
697
 
@@ -649,6 +699,8 @@ export class MavLinkPacketSplitter extends Transform {
649
699
  * A transform stream that takes a buffer with data and converts it to MavLinkPacket object
650
700
  */
651
701
  export class MavLinkPacketParser extends Transform {
702
+ protected readonly log = Logger.getLogger(this)
703
+
652
704
  constructor(opts = {}) {
653
705
  super({ ...opts, objectMode: true })
654
706
  }
@@ -661,7 +713,7 @@ export class MavLinkPacketParser extends Transform {
661
713
  case MavLinkProtocolV2.START_BYTE:
662
714
  return new MavLinkProtocolV2()
663
715
  default:
664
- throw new Error(`Unknown protocol '${startByte.toString(16).padStart(2, '0')}'`)
716
+ throw new Error(`Unknown protocol '${hex(startByte)}'`)
665
717
  }
666
718
  }
667
719
 
@@ -680,6 +732,17 @@ export class MavLinkPacketParser extends Transform {
680
732
  }
681
733
  }
682
734
 
735
+ /**
736
+ * Creates a MavLink packet stream reader that is reading packets from the given input
737
+ *
738
+ * @param input input stream to read from
739
+ */
740
+ export function createMavLinkStream(input: Readable, onCrcError: BufferCallback) {
741
+ return input
742
+ .pipe(new MavLinkPacketSplitter({}, onCrcError))
743
+ .pipe(new MavLinkPacketParser())
744
+ }
745
+
683
746
  let seq = 0
684
747
 
685
748
  /**
@@ -710,14 +773,21 @@ export async function send(stream: Writable, msg: MavLinkData, protocol: MavLink
710
773
  * @param linkId link id for the signature
711
774
  * @param sysid system id
712
775
  * @param compid component id
776
+ * @param timestamp optional timestamp for packet signing (default: Date.now())
713
777
  * @returns number of bytes sent
714
778
  */
715
- export async function sendSigned(stream: Writable, msg: MavLinkData, key: Buffer, linkId: uint8_t = 1, sysid: uint8_t = MavLinkProtocol.SYS_ID, compid: uint8_t = MavLinkProtocol.COMP_ID) {
779
+ export async function sendSigned(
780
+ stream: Writable,
781
+ msg: MavLinkData,
782
+ key: Buffer,
783
+ linkId: uint8_t = 1, sysid: uint8_t = MavLinkProtocol.SYS_ID, compid: uint8_t = MavLinkProtocol.COMP_ID,
784
+ timestamp = Date.now()
785
+ ) {
716
786
  return new Promise((resolve, reject) => {
717
787
  const protocol = new MavLinkProtocolV2(sysid, compid, MavLinkProtocolV2.IFLAG_SIGNED)
718
788
  const b1 = protocol.serialize(msg, seq++)
719
789
  seq &= 255
720
- const b2 = protocol.sign(b1, linkId, key)
790
+ const b2 = protocol.sign(b1, linkId, key, timestamp)
721
791
  stream.write(b2, err => {
722
792
  if (err) reject(err)
723
793
  else resolve(b2.length)
@@ -165,7 +165,7 @@ export const DESERIALIZERS = {
165
165
  },
166
166
  'float[]': (buffer: Buffer, offset: number, length: number) => {
167
167
  const result = new Array<number>(length)
168
- for (let i = 0; i < length; i++) result[i] = buffer.readFloatLE(offset + i * 8)
168
+ for (let i = 0; i < length; i++) result[i] = buffer.readFloatLE(offset + i * 4)
169
169
  return result
170
170
  },
171
171
  'double[]': (buffer: Buffer, offset: number, length: number) => {