@aircast-4g/mavlink 1.1.5 → 1.1.7

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/README.md CHANGED
@@ -1,51 +1,46 @@
1
1
  # Aircast MAVLink
2
2
 
3
- A comprehensive Node.js tool for working with MAVLink protocols, providing both TypeScript type generation from XML dialect files and real-time MAVLink message parsing.
3
+ A comprehensive TypeScript/JavaScript library for working with MAVLink protocols, providing both TypeScript type generation from XML dialect files and robust real-time MAVLink message parsing with built-in frame parsing and buffering.
4
4
 
5
5
  ## Features
6
6
 
7
7
  ### Type Generation
8
8
  - Generate TypeScript interfaces from MAVLink XML dialects
9
- - Support for all MAVLink data types and enums
9
+ - Support for all MAVLink data types and enums
10
10
  - Type-safe enum definitions with numeric values (not string literals)
11
11
  - Batch processing of multiple dialects
12
12
  - CLI interface for easy integration
13
13
 
14
- ### MAVLink Parser
15
- - Real-time MAVLink v1 and v2 message parsing
16
- - CRC validation for data integrity
17
- - Streaming data support with buffer management
18
- - Browser and Node.js compatibility
19
- - Web Worker and WebRTC integration examples
20
- - TCP/UDP connection handling
14
+ ### MAVLink Parser & Decoder
15
+ - **Complete message parsing** - Built-in frame parsing with `parseBytes()` API
16
+ - **Protocol support** - MAVLink v1 and v2 message parsing
17
+ - **Robust decoding** - Fixed array handling, proper field ordering, graceful error handling
18
+ - **Buffering** - Automatic buffer management for partial messages
19
+ - **Browser & Node.js** - Universal compatibility with Web Workers
20
+ - **Dialect-specific parsers** - Generated parsers for each dialect (Common, ArduPilot, etc.)
21
21
 
22
22
  ## Installation
23
23
 
24
- ### From GitHub Packages
25
-
26
- Since this is a public repository, you can install the package directly without authentication:
27
-
28
24
  ```bash
29
- # Global installation
30
- npm install -g @pavliha/aircast-mavlink --registry=https://npm.pkg.github.com
25
+ # Install the package
26
+ npm install @aircast-4g/mavlink
31
27
 
32
- # Local installation
33
- npm install @pavliha/aircast-mavlink --registry=https://npm.pkg.github.com
28
+ # Or with yarn
29
+ yarn add @aircast-4g/mavlink
34
30
  ```
35
31
 
36
- **Alternative: Configure npm scope (one-time setup)**
37
- ```bash
38
- # Configure npm to use GitHub Packages for @pavliha scope
39
- npm config set @pavliha:registry https://npm.pkg.github.com
40
-
41
- # Then install normally
42
- npm install @pavliha/aircast-mavlink
43
- ```
32
+ ### CDN Usage (Browser)
44
33
 
45
- ### Global Installation (Alternative)
34
+ ```html
35
+ <!-- ES modules -->
36
+ <script type="module">
37
+ import { CommonParser } from 'https://esm.sh/@aircast-4g/mavlink@1.1.6/dist/dialects/common/index.js';
38
+ </script>
46
39
 
47
- ```bash
48
- npm install -g aircast-mavlink
40
+ <!-- Or for specific dialects -->
41
+ <script type="module">
42
+ import { ArdupilotmegaParser } from 'https://esm.sh/@aircast-4g/mavlink@1.1.6/dist/dialects/ardupilotmega/index.js';
43
+ </script>
49
44
  ```
50
45
 
51
46
  ### Local Development
@@ -136,12 +131,151 @@ node dist/cli.js list
136
131
  node dist/cli.js generate -i ./my-dialect.xml -o ./output/custom
137
132
  ```
138
133
 
134
+ ## Quick Start
135
+
136
+ ### Basic Message Parsing
137
+
138
+ ```typescript
139
+ import { CommonParser } from '@aircast-4g/mavlink/dialects/common';
140
+
141
+ // Create parser instance
142
+ const parser = new CommonParser();
143
+
144
+ // Parse raw MAVLink bytes
145
+ const rawBytes = new Uint8Array([/* MAVLink frame data */]);
146
+ const messages = parser.parseBytes(rawBytes);
147
+
148
+ // Process messages
149
+ messages.forEach(message => {
150
+ console.log(`Received ${message.message_name} from ${message.system_id}:${message.component_id}`);
151
+ console.log('Payload:', message.payload);
152
+ });
153
+ ```
154
+
155
+ ### Web Worker Integration
156
+
157
+ ```typescript
158
+ // worker.js
159
+ import { ArdupilotmegaParser } from '@aircast-4g/mavlink/dialects/ardupilotmega';
160
+
161
+ const parser = new ArdupilotmegaParser();
162
+
163
+ self.onmessage = (event) => {
164
+ if (event.data.type === 'PARSE_MAVLINK') {
165
+ const messages = parser.parseBytes(event.data.data);
166
+ self.postMessage({ type: 'MESSAGES', messages });
167
+ }
168
+ };
169
+ ```
170
+
171
+ ### Real-time Stream Processing
172
+
173
+ ```typescript
174
+ import { CommonParser } from '@aircast-4g/mavlink/dialects/common';
175
+
176
+ const parser = new CommonParser();
177
+
178
+ // WebSocket example
179
+ websocket.onmessage = (event) => {
180
+ const data = new Uint8Array(event.data);
181
+ const messages = parser.parseBytes(data);
182
+
183
+ messages.forEach(msg => {
184
+ switch (msg.message_name) {
185
+ case 'HEARTBEAT':
186
+ updateSystemStatus(msg.payload);
187
+ break;
188
+ case 'GPS_RAW_INT':
189
+ updatePosition(msg.payload);
190
+ break;
191
+ }
192
+ });
193
+ };
194
+ ```
195
+
196
+ ## API Reference
197
+
198
+ ### Dialect Parsers
199
+
200
+ Each dialect has its own parser class with the same interface:
201
+
202
+ ```typescript
203
+ // Available parsers
204
+ import { CommonParser } from '@aircast-4g/mavlink/dialects/common';
205
+ import { ArdupilotmegaParser } from '@aircast-4g/mavlink/dialects/ardupilotmega';
206
+ import { MinimalParser } from '@aircast-4g/mavlink/dialects/minimal';
207
+ import { StandardParser } from '@aircast-4g/mavlink/dialects/standard';
208
+ import { TestParser } from '@aircast-4g/mavlink/dialects/test';
209
+
210
+ // Parser interface
211
+ class DialectParser {
212
+ // Parse raw bytes into messages (handles buffering internally)
213
+ parseBytes(data: Uint8Array): ParsedMAVLinkMessage[];
214
+
215
+ // Decode a single frame
216
+ decode(frame: MAVLinkFrame): ParsedMAVLinkMessage;
217
+
218
+ // Get supported message IDs
219
+ getSupportedMessageIds(): number[];
220
+
221
+ // Check if message ID is supported
222
+ supportsMessage(messageId: number): boolean;
223
+
224
+ // Get message definition
225
+ getMessageDefinition(id: number): MessageDefinition | undefined;
226
+
227
+ // Get dialect name
228
+ getDialectName(): string;
229
+
230
+ // Reset internal buffer
231
+ resetBuffer(): void;
232
+ }
233
+ ```
234
+
235
+ ### ParsedMAVLinkMessage
236
+
237
+ ```typescript
238
+ interface ParsedMAVLinkMessage {
239
+ timestamp: number; // Parse timestamp
240
+ system_id: number; // MAVLink system ID
241
+ component_id: number; // MAVLink component ID
242
+ message_id: number; // Message type ID
243
+ message_name: string; // Human-readable message name
244
+ sequence: number; // MAVLink sequence number
245
+ payload: Record<string, any>; // Decoded message fields
246
+ protocol_version: 1 | 2; // MAVLink protocol version
247
+ checksum: number; // Frame checksum
248
+ crc_ok: boolean; // CRC validation result
249
+ signature?: Uint8Array; // MAVLink v2 signature (if present)
250
+ dialect?: string; // Dialect name
251
+ }
252
+ ```
253
+
254
+ ### Key Benefits of parseBytes()
255
+
256
+ 1. **Automatic Buffering** - Handles partial frames across multiple calls
257
+ 2. **Frame Synchronization** - Finds valid MAVLink frames in noisy data
258
+ 3. **Protocol Detection** - Automatically detects MAVLink v1 vs v2
259
+ 4. **Robust Parsing** - Gracefully handles malformed data
260
+ 5. **Zero Configuration** - No setup required, just call parseBytes()
261
+
262
+ ```typescript
263
+ const parser = new CommonParser();
264
+
265
+ // Handle partial data - parser buffers automatically
266
+ const part1 = new Uint8Array([0xFE, 0x09, 0x00]); // Partial frame
267
+ const part2 = new Uint8Array([0x01, 0x01, 0x00, /* rest of frame */]);
268
+
269
+ const messages1 = parser.parseBytes(part1); // [] - no complete messages
270
+ const messages2 = parser.parseBytes(part2); // [message] - complete message
271
+ ```
272
+
139
273
  ### Programmatic Usage
140
274
 
141
275
  #### Type Generation
142
276
 
143
277
  ```typescript
144
- import { MAVLinkGenerator, generateTypesFromXML } from 'aircast-mavlink';
278
+ import { MAVLinkGenerator, generateTypesFromXML } from '@aircast-4g/mavlink';
145
279
 
146
280
  // Generate from XML string
147
281
  const files = await generateTypesFromXML(xmlContent, {
@@ -170,93 +304,100 @@ await generator.generateFromURL(
170
304
  includeTypeGuards: true
171
305
  }
172
306
  );
173
-
174
- // From local file
175
- await generator.generateFromFile('./dialect.xml', './output', {
176
- dialectName: 'custom',
177
- outputFormat: 'single',
178
- includeEnums: true,
179
- includeTypeGuards: false
180
- });
181
307
  ```
182
308
 
183
- #### MAVLink Parsing
309
+ ## Examples & Integration Patterns
310
+
311
+ ### Real-time Telemetry Processing
184
312
 
185
313
  ```typescript
186
- import {
187
- MAVLinkParser,
188
- MAVLinkFrameParser,
189
- MAVLinkMessageDecoder,
190
- CRCCalculator
191
- } from 'aircast-mavlink';
192
-
193
- // Import pre-generated dialect types separately
194
- import * as CommonTypes from 'aircast-mavlink/types/common';
195
- import * as MinimalTypes from 'aircast-mavlink/types/minimal';
196
- import * as ArduPilotMegaTypes from 'aircast-mavlink/types/ardupilotmega';
197
- import * as StandardTypes from 'aircast-mavlink/types/standard';
314
+ import { CommonParser } from '@aircast-4g/mavlink/dialects/common';
198
315
 
199
- // Basic message parsing
200
- const parser = new MAVLinkParser({ validateCRC: true });
316
+ const parser = new CommonParser();
201
317
 
202
- // Parse incoming data (Buffer or Uint8Array)
203
- const messages = parser.parseBytes(incomingData);
204
- messages.forEach(message => {
205
- console.log(`Message: ${message.message_name}`);
206
- console.log(`From: ${message.system_id}:${message.component_id}`);
207
- console.log(`Payload:`, message.payload);
208
- });
318
+ // WebRTC data channel
319
+ dataChannel.onmessage = (event) => {
320
+ const messages = parser.parseBytes(new Uint8Array(event.data));
321
+ messages.forEach(processMessage);
322
+ };
209
323
 
210
- // Advanced frame-by-frame parsing
211
- const frameParser = new MAVLinkFrameParser();
212
- const decoder = new MAVLinkMessageDecoder();
324
+ // WebSocket connection
325
+ websocket.onmessage = (event) => {
326
+ const messages = parser.parseBytes(new Uint8Array(event.data));
327
+ messages.forEach(processMessage);
328
+ };
213
329
 
214
- frameParser.on('frame', (frame) => {
215
- const message = decoder.decode(frame);
216
- if (message) {
217
- console.log('Decoded message:', message);
218
- }
330
+ // TCP/UDP streams (Node.js)
331
+ socket.on('data', (data) => {
332
+ const messages = parser.parseBytes(data);
333
+ messages.forEach(processMessage);
219
334
  });
335
+ ```
220
336
 
221
- // Process streaming data
222
- frameParser.process(dataBuffer);
223
-
224
- // WebSocket integration example
225
- websocket.onmessage = (event) => {
226
- const data = new Uint8Array(event.data);
227
- const messages = parser.parseBytes(data);
228
-
229
- messages.forEach(msg => {
230
- switch (msg.message_name) {
231
- case 'HEARTBEAT':
232
- updateConnectionStatus(msg.payload);
233
- break;
234
- case 'GPS_RAW_INT':
235
- updatePosition(msg.payload);
236
- break;
237
- case 'ATTITUDE':
238
- updateAttitude(msg.payload);
239
- break;
240
- }
241
- });
242
- };
337
+ ### Message Filtering and Routing
243
338
 
244
- // Using pre-generated types with parser
245
- function processMessage(msg: MAVLinkMessage) {
339
+ ```typescript
340
+ function processMessage(msg: ParsedMAVLinkMessage) {
246
341
  switch (msg.message_name) {
247
342
  case 'HEARTBEAT':
248
- // Type-safe access with CommonTypes
249
- const heartbeat = msg.payload as CommonTypes.MessageHeartbeat;
250
- console.log(`System type: ${heartbeat.type}`);
343
+ updateSystemStatus(msg.system_id, msg.payload);
251
344
  break;
252
345
  case 'GPS_RAW_INT':
253
- const gps = msg.payload as CommonTypes.MessageGpsRawInt;
254
- console.log(`Lat: ${gps.lat / 1e7}, Lon: ${gps.lon / 1e7}`);
346
+ updatePosition(msg.payload.lat / 1e7, msg.payload.lon / 1e7);
347
+ break;
348
+ case 'ATTITUDE':
349
+ updateAttitude(msg.payload.roll, msg.payload.pitch, msg.payload.yaw);
350
+ break;
351
+ case 'VFR_HUD':
352
+ updateHUD(msg.payload);
353
+ break;
354
+ case 'STATUSTEXT':
355
+ displayStatusMessage(msg.payload.text, msg.payload.severity);
255
356
  break;
256
357
  }
257
358
  }
258
359
  ```
259
360
 
361
+ ### Error Handling and Recovery
362
+
363
+ ```typescript
364
+ try {
365
+ const messages = parser.parseBytes(incomingData);
366
+ messages.forEach(processMessage);
367
+ } catch (error) {
368
+ console.error('Parse error:', error.message);
369
+
370
+ // Reset parser buffer if needed
371
+ parser.resetBuffer();
372
+
373
+ // Implement reconnection logic
374
+ scheduleReconnect();
375
+ }
376
+ ```
377
+
378
+ ## Robustness & Features
379
+
380
+ ### Parser Robustness
381
+ - **Fixed array decoding** - Proper handling of `uint8_t[8]` arrays without double nesting
382
+ - **Graceful degradation** - Missing fields get sensible default values
383
+ - **Protocol version detection** - Automatic MAVLink v1/v2 detection
384
+ - **Frame synchronization** - Finds valid frames in noisy data streams
385
+ - **Buffer management** - Handles partial frames across data chunks
386
+ - **Memory efficient** - Reuses buffers, minimal allocations
387
+
388
+ ### Decoder Features
389
+ - **All MAVLink types** - Support for `uint8_t`, `int32_t`, `float`, `double`, `char[N]`, arrays
390
+ - **Little-endian parsing** - Correct byte order handling
391
+ - **Unknown message handling** - Gracefully processes unsupported message types
392
+ - **Field validation** - Bounds checking and safe defaults
393
+ - **CRC validation** - Optional checksum verification (simplified implementation)
394
+
395
+ ### Testing & Quality
396
+ - **Comprehensive tests** - 20+ test cases covering edge cases
397
+ - **Generated decoder tests** - Validates HEARTBEAT, PROTOCOL_VERSION, arrays, partial payloads
398
+ - **Frame parsing tests** - Tests v1/v2 protocols, multi-message buffers, invalid data
399
+ - **CI/CD integration** - Automated testing on builds
400
+
260
401
  ## Pre-generated Types
261
402
 
262
403
  The package includes pre-generated TypeScript types for common MAVLink dialects:
@@ -337,8 +478,14 @@ interface ParserOptions {
337
478
  class MAVLinkParser {
338
479
  constructor(options?: ParserOptions);
339
480
 
481
+ // Initialize dialect parsers (call before parsing)
482
+ initialize(): Promise<void>;
483
+
340
484
  // Parse bytes and return complete messages
341
- parseBytes(data: Buffer | Uint8Array): MAVLinkMessage[];
485
+ parseBytes(data: Buffer | Uint8Array): Promise<MAVLinkMessage[]>;
486
+
487
+ // Parse single message
488
+ parseMessage(data: Uint8Array): Promise<MAVLinkMessage | null>;
342
489
 
343
490
  // Reset parser state
344
491
  reset(): void;
@@ -365,14 +512,20 @@ class MAVLinkFrameParser extends EventEmitter {
365
512
  }
366
513
  ```
367
514
 
368
- ### MAVLinkMessageDecoder
515
+ ### DialectParserFactory
369
516
 
370
- Decodes frames into structured messages.
517
+ Creates and manages dialect-specific parsers.
371
518
 
372
519
  ```typescript
373
- class MAVLinkMessageDecoder {
374
- // Decode a frame into a message
375
- decode(frame: MAVLinkFrame): MAVLinkMessage | null;
520
+ class DialectParserFactory {
521
+ // Create single dialect parser
522
+ static createParser(dialectName: SupportedDialects): Promise<DialectParser>;
523
+
524
+ // Create multi-dialect parser
525
+ static createMultipleDialectParser(dialectNames: SupportedDialects[]): Promise<MultiDialectParser>;
526
+
527
+ // Get list of supported dialects
528
+ static getSupportedDialects(): SupportedDialects[];
376
529
  }
377
530
  ```
378
531
 
@@ -432,20 +585,20 @@ python3 -m http.server 8000
432
585
  const parser = new MAVLinkParser({ validateCRC: true });
433
586
 
434
587
  // WebRTC data channel
435
- dataChannel.onmessage = (event) => {
436
- const messages = parser.parseBytes(new Uint8Array(event.data));
588
+ dataChannel.onmessage = async (event) => {
589
+ const messages = await parser.parseBytes(new Uint8Array(event.data));
437
590
  messages.forEach(processMessage);
438
591
  };
439
592
 
440
593
  // WebSocket connection
441
- websocket.onmessage = (event) => {
442
- const messages = parser.parseBytes(new Uint8Array(event.data));
594
+ websocket.onmessage = async (event) => {
595
+ const messages = await parser.parseBytes(new Uint8Array(event.data));
443
596
  messages.forEach(processMessage);
444
597
  };
445
598
 
446
599
  // TCP/UDP streams (Node.js)
447
- socket.on('data', (data) => {
448
- const messages = parser.parseBytes(data);
600
+ socket.on('data', async (data) => {
601
+ const messages = await parser.parseBytes(data);
449
602
  messages.forEach(processMessage);
450
603
  });
451
604
  ```
@@ -476,7 +629,7 @@ function processMessage(msg: MAVLinkMessage) {
476
629
  #### Error Handling and Recovery
477
630
  ```typescript
478
631
  try {
479
- const messages = parser.parseBytes(incomingData);
632
+ const messages = await parser.parseBytes(incomingData);
480
633
  messages.forEach(processMessage);
481
634
  } catch (error) {
482
635
  console.error('Parse error:', error.message);
@@ -605,28 +758,36 @@ node dist/cli.js generate -i https://raw.githubusercontent.com/mavlink/mavlink/m
605
758
  │ ├── types.ts # Shared type definitions
606
759
  │ ├── generator/ # Type generation components
607
760
  │ │ ├── generator.ts # Main generator class
608
- │ │ ├── template-engine.ts # Handlebars template engine
761
+ │ │ ├── template-engine.ts # Handlebars template engine with decoder
609
762
  │ │ ├── type-converter.ts # XML to TypeScript conversion
610
763
  │ │ ├── xml-parser.ts # MAVLink XML parser
611
764
  │ │ └── batch-processor.ts # Batch processing utilities
612
- │ └── parser/ # MAVLink parsing components
613
- ├── index.ts # Parser exports
614
- ├── mavlink-parser.ts # Main parser class
615
- ├── frame-parser.ts # Frame-level parsing
616
- ├── message-decoder.ts # Message decoding
617
- ├── crc.ts # CRC calculation utilities
618
- └── types.ts # Parser type definitions
619
- ├── examples/ # Usage examples and demos
620
- │ ├── README.md # Examples documentation
621
- │ ├── basic-parser.js # Basic Node.js usage
622
- │ ├── nodejs-stream.js # Advanced streaming patterns
623
- │ ├── web-worker-example.js # Web Worker integration
624
- │ ├── webrtc-integration.html # Complete web demo
625
- │ └── test-web.html # Simple browser test
765
+ │ └── generated/ # Generated dialect parsers
766
+ └── dialects/ # Generated parsers for each dialect
767
+ ├── common/ # Common dialect parser + types
768
+ ├── ardupilotmega/ # ArduPilot dialect parser + types
769
+ ├── minimal/ # Minimal dialect parser + types
770
+ ├── standard/ # Standard dialect parser + types
771
+ └── test/ # Test dialect parser + types
626
772
  ├── tests/ # Jest test files
627
- └── dist/ # Compiled JavaScript output
773
+ │ ├── decoder.test.ts # Decoder functionality tests
774
+ │ ├── template-engine.test.ts # Template generation tests
775
+ │ ├── xml-parser.test.ts # XML parsing tests
776
+ │ └── generator.test.ts # End-to-end generation tests
777
+ ├── dist/ # Compiled JavaScript output
778
+ │ └── dialects/ # Built dialect parsers for distribution
779
+ └── examples/ # Usage examples and demos
628
780
  ```
629
781
 
782
+ ### Generated Files Per Dialect
783
+
784
+ Each dialect directory contains:
785
+ - `decoder.ts` - Parser class with parseBytes() and decode() methods
786
+ - `types.ts` - Base types and ParsedMAVLinkMessage interface
787
+ - `enums.ts` - Enum definitions and type aliases
788
+ - `messages.ts` - Message interfaces and type guards
789
+ - `index.ts` - Main export file
790
+
630
791
  ## License
631
792
 
632
793
  MIT