@aircast-4g/mavlink 1.1.7 → 1.1.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.
- package/dist/dialects/ardupilotmega/index.d.ts +1538 -1513
- package/dist/dialects/ardupilotmega/index.js +3520 -2605
- package/dist/dialects/ardupilotmega/index.js.map +1 -1
- package/dist/dialects/common/index.d.ts +1248 -1223
- package/dist/dialects/common/index.js +2876 -2060
- package/dist/dialects/common/index.js.map +1 -1
- package/dist/dialects/minimal/index.d.ts +34 -9
- package/dist/dialects/minimal/index.js +441 -15
- package/dist/dialects/minimal/index.js.map +1 -1
- package/dist/dialects/paparazzi/index.d.ts +1256 -1231
- package/dist/dialects/paparazzi/index.js +2898 -2077
- package/dist/dialects/paparazzi/index.js.map +1 -1
- package/dist/dialects/python_array_test/index.d.ts +1277 -1252
- package/dist/dialects/python_array_test/index.js +2939 -2115
- package/dist/dialects/python_array_test/index.js.map +1 -1
- package/dist/dialects/standard/index.d.ts +39 -14
- package/dist/dialects/standard/index.js +446 -20
- package/dist/dialects/standard/index.js.map +1 -1
- package/dist/dialects/test/index.d.ts +44 -19
- package/dist/dialects/test/index.js +477 -52
- package/dist/dialects/test/index.js.map +1 -1
- package/package.json +11 -3
|
@@ -262,26 +262,26 @@ declare enum MAV_COMPONENTEnum {
|
|
|
262
262
|
MAV_COMP_ID_SYSTEM_CONTROL = 250
|
|
263
263
|
}
|
|
264
264
|
type MAV_COMPONENT = MAV_COMPONENTEnum;
|
|
265
|
-
declare enum
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
declare enum MAV_BOOLEnum {
|
|
266
|
+
MAV_BOOL_FALSE = 0,
|
|
267
|
+
MAV_BOOL_TRUE = 1
|
|
268
268
|
}
|
|
269
|
-
type
|
|
269
|
+
type MAV_BOOL = MAV_BOOLEnum;
|
|
270
270
|
|
|
271
271
|
interface MessageHeartbeat {
|
|
272
|
+
custom_mode: number;
|
|
272
273
|
type: MAV_TYPE;
|
|
273
274
|
autopilot: MAV_AUTOPILOT;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
mavlinkVersion: number;
|
|
275
|
+
base_mode: MAV_MODE_FLAG;
|
|
276
|
+
system_status: MAV_STATE;
|
|
277
|
+
mavlink_version: number;
|
|
278
278
|
}
|
|
279
279
|
interface MessageProtocolVersion {
|
|
280
280
|
version: number;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
281
|
+
min_version: number;
|
|
282
|
+
max_version: number;
|
|
283
|
+
spec_version_hash: number[];
|
|
284
|
+
library_version_hash: number[];
|
|
285
285
|
}
|
|
286
286
|
interface MessageTypeMap {
|
|
287
287
|
HEARTBEAT: MessageHeartbeat;
|
|
@@ -353,12 +353,37 @@ declare abstract class DialectParser {
|
|
|
353
353
|
getSupportedMessageIds(): number[];
|
|
354
354
|
getDialectName(): string;
|
|
355
355
|
supportsMessage(messageId: number): boolean;
|
|
356
|
+
serializeMessage(message: Record<string, unknown> & {
|
|
357
|
+
message_name: string;
|
|
358
|
+
}): Uint8Array;
|
|
359
|
+
private extractMessageFields;
|
|
360
|
+
private completeMessageWithDefaults;
|
|
361
|
+
private getDefaultValueForField;
|
|
362
|
+
private serializePayload;
|
|
363
|
+
private serializeField;
|
|
364
|
+
private serializeSingleValue;
|
|
365
|
+
private getFieldSize;
|
|
366
|
+
private getSingleFieldSize;
|
|
367
|
+
private getDefaultValueForType;
|
|
368
|
+
private createMAVLinkFrame;
|
|
356
369
|
}
|
|
357
370
|
declare class StandardParser extends DialectParser {
|
|
358
371
|
constructor();
|
|
359
372
|
loadDefinitions(): Promise<void>;
|
|
360
373
|
private loadDefinitionsSync;
|
|
361
374
|
}
|
|
375
|
+
declare class StandardSerializer {
|
|
376
|
+
private parser;
|
|
377
|
+
constructor();
|
|
378
|
+
serialize(message: Record<string, unknown> & {
|
|
379
|
+
message_name: string;
|
|
380
|
+
}): Uint8Array;
|
|
381
|
+
completeMessage(message: Record<string, unknown> & {
|
|
382
|
+
message_name: string;
|
|
383
|
+
}): Record<string, unknown>;
|
|
384
|
+
getSupportedMessages(): string[];
|
|
385
|
+
supportsMessage(messageName: string): boolean;
|
|
386
|
+
}
|
|
362
387
|
|
|
363
|
-
export {
|
|
364
|
-
export type { AnyMessage,
|
|
388
|
+
export { MAV_AUTOPILOTEnum, MAV_BOOLEnum, MAV_COMPONENTEnum, MAV_MODE_FLAGEnum, MAV_MODE_FLAG_DECODE_POSITIONEnum, MAV_STATEEnum, MAV_TYPEEnum, StandardParser, StandardSerializer, isHeartbeat, isProtocolVersion };
|
|
389
|
+
export type { AnyMessage, MAV_AUTOPILOT, MAV_BOOL, MAV_COMPONENT, MAV_MODE_FLAG, MAV_MODE_FLAG_DECODE_POSITION, MAV_STATE, MAV_TYPE, MessageHeartbeat, MessageProtocolVersion, MessageTypeMap, ParsedMAVLinkMessage$1 as ParsedMAVLinkMessage };
|
|
@@ -494,13 +494,13 @@ var MAV_COMPONENTEnum;
|
|
|
494
494
|
MAV_COMPONENTEnum[MAV_COMPONENTEnum["MAV_COMP_ID_SYSTEM_CONTROL"] = 250] = "MAV_COMP_ID_SYSTEM_CONTROL";
|
|
495
495
|
})(MAV_COMPONENTEnum || (MAV_COMPONENTEnum = {}));
|
|
496
496
|
// Enum used to indicate true or false (also: success or failure, enabled or disabled, active or inactive).
|
|
497
|
-
var
|
|
498
|
-
(function (
|
|
497
|
+
var MAV_BOOLEnum;
|
|
498
|
+
(function (MAV_BOOLEnum) {
|
|
499
499
|
// False.
|
|
500
|
-
|
|
500
|
+
MAV_BOOLEnum[MAV_BOOLEnum["MAV_BOOL_FALSE"] = 0] = "MAV_BOOL_FALSE";
|
|
501
501
|
// True.
|
|
502
|
-
|
|
503
|
-
})(
|
|
502
|
+
MAV_BOOLEnum[MAV_BOOLEnum["MAV_BOOL_TRUE"] = 1] = "MAV_BOOL_TRUE";
|
|
503
|
+
})(MAV_BOOLEnum || (MAV_BOOLEnum = {}));
|
|
504
504
|
|
|
505
505
|
// Auto-generated TypeScript message interfaces for standard dialect
|
|
506
506
|
// Type guard functions
|
|
@@ -513,6 +513,35 @@ function isProtocolVersion(msg) {
|
|
|
513
513
|
|
|
514
514
|
// Auto-generated decoder and parser for standard dialect
|
|
515
515
|
// Generated from MAVLink XML definitions
|
|
516
|
+
// Embedded MAVLink CRC implementation
|
|
517
|
+
const CRC_EXTRA = {
|
|
518
|
+
0: 50,
|
|
519
|
+
300: 0
|
|
520
|
+
};
|
|
521
|
+
class MAVLinkCRC {
|
|
522
|
+
static calculate(data, crcExtra) {
|
|
523
|
+
let crc = 0xffff;
|
|
524
|
+
// Process all message bytes using MCRF4XX algorithm
|
|
525
|
+
for (let i = 0; i < data.length; i++) {
|
|
526
|
+
let tmp = data[i] ^ (crc & 0xff);
|
|
527
|
+
tmp = (tmp ^ (tmp << 4)) & 0xff;
|
|
528
|
+
crc = ((crc >> 8) ^ (tmp << 8) ^ (tmp << 3) ^ (tmp >> 4)) & 0xffff;
|
|
529
|
+
}
|
|
530
|
+
// Add CRC_EXTRA byte using the same algorithm
|
|
531
|
+
let tmp = crcExtra ^ (crc & 0xff);
|
|
532
|
+
tmp = (tmp ^ (tmp << 4)) & 0xff;
|
|
533
|
+
crc = ((crc >> 8) ^ (tmp << 8) ^ (tmp << 3) ^ (tmp >> 4)) & 0xffff;
|
|
534
|
+
return crc;
|
|
535
|
+
}
|
|
536
|
+
static validate(data, messageId, receivedChecksum) {
|
|
537
|
+
const crcExtra = CRC_EXTRA[messageId];
|
|
538
|
+
if (crcExtra === undefined) {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
const calculatedChecksum = this.calculate(data, crcExtra);
|
|
542
|
+
return calculatedChecksum === receivedChecksum;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
516
545
|
// Base dialect parser class
|
|
517
546
|
class DialectParser {
|
|
518
547
|
constructor(dialectName) {
|
|
@@ -600,7 +629,9 @@ class DialectParser {
|
|
|
600
629
|
frameOffset += 13;
|
|
601
630
|
}
|
|
602
631
|
}
|
|
603
|
-
|
|
632
|
+
// Validate CRC using proper MAVLink algorithm
|
|
633
|
+
const headerAndPayload = data.slice(offset + 1, offset + frameOffset - offset - 2);
|
|
634
|
+
frame.crc_ok = MAVLinkCRC.validate(headerAndPayload, frame.message_id, frame.checksum);
|
|
604
635
|
frame.protocol_version = isV2 ? 2 : 1;
|
|
605
636
|
return { frame: frame, bytesConsumed: frameOffset - offset };
|
|
606
637
|
}
|
|
@@ -691,13 +722,30 @@ class DialectParser {
|
|
|
691
722
|
const isArray = field.arrayLength !== undefined;
|
|
692
723
|
const arrayLength = field.arrayLength || 1;
|
|
693
724
|
if (isArray && arrayLength > 1) {
|
|
694
|
-
const values = [];
|
|
695
|
-
let totalBytes = 0;
|
|
696
725
|
// Strip array notation from type to avoid double processing
|
|
697
726
|
let baseType = field.type;
|
|
698
727
|
if (baseType.includes('[') && baseType.includes(']')) {
|
|
699
728
|
baseType = baseType.substring(0, baseType.indexOf('['));
|
|
700
729
|
}
|
|
730
|
+
// Special handling for char arrays - return as string
|
|
731
|
+
if (baseType === 'char') {
|
|
732
|
+
const chars = [];
|
|
733
|
+
let totalBytes = 0;
|
|
734
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
735
|
+
if (offset + totalBytes >= view.byteLength)
|
|
736
|
+
break;
|
|
737
|
+
const charCode = view.getUint8(offset + totalBytes);
|
|
738
|
+
if (charCode === 0)
|
|
739
|
+
break; // Null terminator
|
|
740
|
+
chars.push(String.fromCharCode(charCode));
|
|
741
|
+
totalBytes += 1;
|
|
742
|
+
}
|
|
743
|
+
// Return string value and total bytes for the array
|
|
744
|
+
return { value: chars.join(''), bytesRead: arrayLength }; // Always consume full array size
|
|
745
|
+
}
|
|
746
|
+
// Handle other array types
|
|
747
|
+
const values = [];
|
|
748
|
+
let totalBytes = 0;
|
|
701
749
|
for (let i = 0; i < arrayLength; i++) {
|
|
702
750
|
if (offset + totalBytes >= view.byteLength)
|
|
703
751
|
break;
|
|
@@ -787,12 +835,357 @@ class DialectParser {
|
|
|
787
835
|
supportsMessage(messageId) {
|
|
788
836
|
return this.messageDefinitions.has(messageId);
|
|
789
837
|
}
|
|
838
|
+
// Serialization methods for outgoing commands
|
|
839
|
+
serializeMessage(message) {
|
|
840
|
+
const messageDef = Array.from(this.messageDefinitions.values())
|
|
841
|
+
.find(def => def.name === message.message_name);
|
|
842
|
+
if (!messageDef) {
|
|
843
|
+
throw new Error(`Unknown message type: ${message.message_name}`);
|
|
844
|
+
}
|
|
845
|
+
// Extract fields from either flat structure or payload structure
|
|
846
|
+
const messageFields = this.extractMessageFields(message, messageDef.fields);
|
|
847
|
+
// Complete the message with all defined fields (including extension fields with defaults)
|
|
848
|
+
const completeMessage = this.completeMessageWithDefaults(messageFields, messageDef.fields);
|
|
849
|
+
const payload = this.serializePayload(completeMessage, messageDef.fields);
|
|
850
|
+
return this.createMAVLinkFrame(message, messageDef.id, payload);
|
|
851
|
+
}
|
|
852
|
+
// Extract message fields from payload structure (payload format required)
|
|
853
|
+
extractMessageFields(message, fieldDefinitions) {
|
|
854
|
+
// Require payload structure
|
|
855
|
+
if (!message.payload || typeof message.payload !== 'object') {
|
|
856
|
+
throw new Error(`Message must have a 'payload' object containing the message fields. Expected format: { message_name: '...', system_id: 1, component_id: 1, sequence: 0, payload: { ...fields } }`);
|
|
857
|
+
}
|
|
858
|
+
return message.payload;
|
|
859
|
+
}
|
|
860
|
+
// Helper method to complete message with all defined fields
|
|
861
|
+
completeMessageWithDefaults(message, fields) {
|
|
862
|
+
const completeMessage = { ...message };
|
|
863
|
+
for (const field of fields) {
|
|
864
|
+
if (completeMessage[field.name] === undefined) {
|
|
865
|
+
completeMessage[field.name] = this.getDefaultValueForField(field);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return completeMessage;
|
|
869
|
+
}
|
|
870
|
+
// Get default value for a field based on its definition
|
|
871
|
+
getDefaultValueForField(field) {
|
|
872
|
+
const isArray = field.arrayLength !== undefined && field.arrayLength > 1;
|
|
873
|
+
if (isArray) {
|
|
874
|
+
return [];
|
|
875
|
+
}
|
|
876
|
+
let baseType = field.type;
|
|
877
|
+
if (baseType.includes('[') && baseType.includes(']')) {
|
|
878
|
+
baseType = baseType.substring(0, baseType.indexOf('['));
|
|
879
|
+
}
|
|
880
|
+
switch (baseType) {
|
|
881
|
+
case 'uint8_t':
|
|
882
|
+
case 'int8_t':
|
|
883
|
+
case 'uint16_t':
|
|
884
|
+
case 'int16_t':
|
|
885
|
+
case 'uint32_t':
|
|
886
|
+
case 'int32_t':
|
|
887
|
+
case 'float':
|
|
888
|
+
case 'double':
|
|
889
|
+
return 0;
|
|
890
|
+
case 'uint64_t':
|
|
891
|
+
case 'int64_t':
|
|
892
|
+
return 0n;
|
|
893
|
+
case 'char':
|
|
894
|
+
return field.type.includes('[') ? '' : '\0';
|
|
895
|
+
default:
|
|
896
|
+
return 0;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
serializePayload(message, fields) {
|
|
900
|
+
// Calculate total payload size
|
|
901
|
+
let totalSize = 0;
|
|
902
|
+
for (const field of fields) {
|
|
903
|
+
totalSize += this.getFieldSize(field);
|
|
904
|
+
}
|
|
905
|
+
const buffer = new ArrayBuffer(totalSize);
|
|
906
|
+
const view = new DataView(buffer);
|
|
907
|
+
let offset = 0;
|
|
908
|
+
for (const field of fields) {
|
|
909
|
+
const value = message[field.name];
|
|
910
|
+
const bytesWritten = this.serializeField(view, offset, field, value);
|
|
911
|
+
offset += bytesWritten;
|
|
912
|
+
}
|
|
913
|
+
// Implement MAVLink payload trimming: remove trailing zero bytes from extension fields only
|
|
914
|
+
// This is required for proper handling of extension fields
|
|
915
|
+
const fullPayload = new Uint8Array(buffer);
|
|
916
|
+
// Calculate minimum payload size (core fields only)
|
|
917
|
+
let corePayloadSize = 0;
|
|
918
|
+
let extensionStartOffset = 0;
|
|
919
|
+
let hasExtensions = false;
|
|
920
|
+
message.message_name;
|
|
921
|
+
for (const field of fields) {
|
|
922
|
+
const fieldSize = this.getFieldSize(field);
|
|
923
|
+
// Check if this is an extension field using proper XML-based detection
|
|
924
|
+
const isExtensionField = field.extension === true;
|
|
925
|
+
if (isExtensionField) {
|
|
926
|
+
if (!hasExtensions) {
|
|
927
|
+
extensionStartOffset = corePayloadSize;
|
|
928
|
+
hasExtensions = true;
|
|
929
|
+
}
|
|
930
|
+
// Don't add extension field sizes to core payload size
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
// This is a core field - always add to core payload size
|
|
934
|
+
corePayloadSize += fieldSize;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
// If there are no extension fields, don't trim at all
|
|
938
|
+
if (!hasExtensions) {
|
|
939
|
+
return fullPayload;
|
|
940
|
+
}
|
|
941
|
+
// Find the last non-zero byte in extension fields only
|
|
942
|
+
let trimmedLength = fullPayload.length;
|
|
943
|
+
// If we have extensions, check if they contain any non-zero bytes
|
|
944
|
+
if (hasExtensions && extensionStartOffset < fullPayload.length) {
|
|
945
|
+
// Look for non-zero bytes in the extension section
|
|
946
|
+
let hasNonZeroExtensions = false;
|
|
947
|
+
for (let i = extensionStartOffset; i < fullPayload.length; i++) {
|
|
948
|
+
if (fullPayload[i] !== 0) {
|
|
949
|
+
hasNonZeroExtensions = true;
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
if (!hasNonZeroExtensions) {
|
|
954
|
+
// All extension fields are zero, trim them all
|
|
955
|
+
trimmedLength = corePayloadSize;
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
// Find the last non-zero byte in extension fields
|
|
959
|
+
for (let i = fullPayload.length - 1; i >= extensionStartOffset; i--) {
|
|
960
|
+
if (fullPayload[i] !== 0) {
|
|
961
|
+
trimmedLength = i + 1;
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
// Never trim below the core payload size
|
|
968
|
+
if (trimmedLength < corePayloadSize) {
|
|
969
|
+
trimmedLength = corePayloadSize;
|
|
970
|
+
}
|
|
971
|
+
// Return trimmed payload if it's shorter than the original
|
|
972
|
+
if (trimmedLength < fullPayload.length) {
|
|
973
|
+
return fullPayload.slice(0, trimmedLength);
|
|
974
|
+
}
|
|
975
|
+
return fullPayload;
|
|
976
|
+
}
|
|
977
|
+
serializeField(view, offset, field, value) {
|
|
978
|
+
const isArray = field.arrayLength !== undefined;
|
|
979
|
+
const arrayLength = field.arrayLength || 1;
|
|
980
|
+
if (isArray && arrayLength > 1) {
|
|
981
|
+
let totalBytes = 0;
|
|
982
|
+
let baseType = field.type;
|
|
983
|
+
if (baseType.includes('[') && baseType.includes(']')) {
|
|
984
|
+
baseType = baseType.substring(0, baseType.indexOf('['));
|
|
985
|
+
}
|
|
986
|
+
// Special handling for char arrays - treat string as char array
|
|
987
|
+
if (baseType === 'char' && typeof value === 'string') {
|
|
988
|
+
const str = value;
|
|
989
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
990
|
+
const charCode = i < str.length ? str.charCodeAt(i) : 0;
|
|
991
|
+
view.setUint8(offset + totalBytes, charCode);
|
|
992
|
+
totalBytes += 1;
|
|
993
|
+
}
|
|
994
|
+
return totalBytes;
|
|
995
|
+
}
|
|
996
|
+
// Handle other array types
|
|
997
|
+
const arrayValue = Array.isArray(value) ? value : [value];
|
|
998
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
999
|
+
const itemValue = i < arrayValue.length ? arrayValue[i] : this.getDefaultValueForType(baseType);
|
|
1000
|
+
const bytesWritten = this.serializeSingleValue(view, offset + totalBytes, baseType, itemValue);
|
|
1001
|
+
totalBytes += bytesWritten;
|
|
1002
|
+
}
|
|
1003
|
+
return totalBytes;
|
|
1004
|
+
}
|
|
1005
|
+
else {
|
|
1006
|
+
return this.serializeSingleValue(view, offset, field.type, value);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
serializeSingleValue(view, offset, type, value) {
|
|
1010
|
+
const actualValue = value ?? this.getDefaultValueForType(type);
|
|
1011
|
+
switch (type) {
|
|
1012
|
+
case 'uint8_t':
|
|
1013
|
+
view.setUint8(offset, Number(actualValue));
|
|
1014
|
+
return 1;
|
|
1015
|
+
case 'int8_t':
|
|
1016
|
+
view.setInt8(offset, Number(actualValue));
|
|
1017
|
+
return 1;
|
|
1018
|
+
case 'uint16_t':
|
|
1019
|
+
view.setUint16(offset, Number(actualValue), true);
|
|
1020
|
+
return 2;
|
|
1021
|
+
case 'int16_t':
|
|
1022
|
+
view.setInt16(offset, Number(actualValue), true);
|
|
1023
|
+
return 2;
|
|
1024
|
+
case 'uint32_t':
|
|
1025
|
+
view.setUint32(offset, Number(actualValue), true);
|
|
1026
|
+
return 4;
|
|
1027
|
+
case 'int32_t':
|
|
1028
|
+
view.setInt32(offset, Number(actualValue), true);
|
|
1029
|
+
return 4;
|
|
1030
|
+
case 'uint64_t':
|
|
1031
|
+
view.setBigUint64(offset, typeof actualValue === 'bigint' ? actualValue : BigInt(Number(actualValue) || 0), true);
|
|
1032
|
+
return 8;
|
|
1033
|
+
case 'int64_t':
|
|
1034
|
+
view.setBigInt64(offset, typeof actualValue === 'bigint' ? actualValue : BigInt(Number(actualValue) || 0), true);
|
|
1035
|
+
return 8;
|
|
1036
|
+
case 'float':
|
|
1037
|
+
view.setFloat32(offset, Number(actualValue), true);
|
|
1038
|
+
return 4;
|
|
1039
|
+
case 'double':
|
|
1040
|
+
view.setFloat64(offset, Number(actualValue), true);
|
|
1041
|
+
return 8;
|
|
1042
|
+
case 'char':
|
|
1043
|
+
view.setUint8(offset, typeof actualValue === 'string' ? actualValue.charCodeAt(0) : Number(actualValue));
|
|
1044
|
+
return 1;
|
|
1045
|
+
default:
|
|
1046
|
+
if (type.startsWith('char[') && type.endsWith(']')) {
|
|
1047
|
+
const length = parseInt(type.slice(5, -1));
|
|
1048
|
+
const str = String(actualValue);
|
|
1049
|
+
for (let i = 0; i < length; i++) {
|
|
1050
|
+
const charCode = i < str.length ? str.charCodeAt(i) : 0;
|
|
1051
|
+
view.setUint8(offset + i, charCode);
|
|
1052
|
+
}
|
|
1053
|
+
return length;
|
|
1054
|
+
}
|
|
1055
|
+
view.setUint8(offset, Number(actualValue));
|
|
1056
|
+
return 1;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
getFieldSize(field) {
|
|
1060
|
+
if (typeof field === 'string') {
|
|
1061
|
+
// Legacy support for type string
|
|
1062
|
+
if (field.includes('[') && field.includes(']')) {
|
|
1063
|
+
const baseType = field.substring(0, field.indexOf('['));
|
|
1064
|
+
const arrayLength = parseInt(field.substring(field.indexOf('[') + 1, field.indexOf(']')));
|
|
1065
|
+
return this.getSingleFieldSize(baseType) * arrayLength;
|
|
1066
|
+
}
|
|
1067
|
+
return this.getSingleFieldSize(field);
|
|
1068
|
+
}
|
|
1069
|
+
// Handle FieldDefinition object
|
|
1070
|
+
const type = field.type;
|
|
1071
|
+
const arrayLength = field.arrayLength;
|
|
1072
|
+
if (arrayLength && arrayLength > 1) {
|
|
1073
|
+
return this.getSingleFieldSize(type) * arrayLength;
|
|
1074
|
+
}
|
|
1075
|
+
if (type.includes('[') && type.includes(']')) {
|
|
1076
|
+
const baseType = type.substring(0, type.indexOf('['));
|
|
1077
|
+
const parsedArrayLength = parseInt(type.substring(type.indexOf('[') + 1, type.indexOf(']')));
|
|
1078
|
+
return this.getSingleFieldSize(baseType) * parsedArrayLength;
|
|
1079
|
+
}
|
|
1080
|
+
return this.getSingleFieldSize(type);
|
|
1081
|
+
}
|
|
1082
|
+
getSingleFieldSize(type) {
|
|
1083
|
+
switch (type) {
|
|
1084
|
+
case 'uint8_t':
|
|
1085
|
+
case 'int8_t':
|
|
1086
|
+
case 'char':
|
|
1087
|
+
return 1;
|
|
1088
|
+
case 'uint16_t':
|
|
1089
|
+
case 'int16_t':
|
|
1090
|
+
return 2;
|
|
1091
|
+
case 'uint32_t':
|
|
1092
|
+
case 'int32_t':
|
|
1093
|
+
case 'float':
|
|
1094
|
+
return 4;
|
|
1095
|
+
case 'uint64_t':
|
|
1096
|
+
case 'int64_t':
|
|
1097
|
+
case 'double':
|
|
1098
|
+
return 8;
|
|
1099
|
+
default:
|
|
1100
|
+
return 1;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
getDefaultValueForType(type) {
|
|
1104
|
+
switch (type) {
|
|
1105
|
+
case 'uint8_t':
|
|
1106
|
+
case 'int8_t':
|
|
1107
|
+
case 'uint16_t':
|
|
1108
|
+
case 'int16_t':
|
|
1109
|
+
case 'uint32_t':
|
|
1110
|
+
case 'int32_t':
|
|
1111
|
+
case 'float':
|
|
1112
|
+
case 'double':
|
|
1113
|
+
return 0;
|
|
1114
|
+
case 'uint64_t':
|
|
1115
|
+
case 'int64_t':
|
|
1116
|
+
return 0n;
|
|
1117
|
+
case 'char':
|
|
1118
|
+
return 0;
|
|
1119
|
+
default:
|
|
1120
|
+
return 0;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
createMAVLinkFrame(message, messageId, payload) {
|
|
1124
|
+
const systemId = typeof message.system_id === 'number' ? message.system_id : 1;
|
|
1125
|
+
const componentId = typeof message.component_id === 'number' ? message.component_id : 1;
|
|
1126
|
+
const sequence = typeof message.sequence === 'number' ? message.sequence : 0;
|
|
1127
|
+
// Automatically determine protocol version based on message requirements
|
|
1128
|
+
const needsV2 = messageId > 255; // v1 only supports 8-bit message IDs (0-255)
|
|
1129
|
+
const userSpecifiedVersion = typeof message.protocol_version === 'number' ? message.protocol_version : null;
|
|
1130
|
+
// Use user-specified version if provided, otherwise auto-detect
|
|
1131
|
+
const protocolVersion = userSpecifiedVersion !== null ? userSpecifiedVersion : (needsV2 ? 2 : 1);
|
|
1132
|
+
const isV2 = protocolVersion === 2;
|
|
1133
|
+
const magic = isV2 ? 0xFD : 0xFE;
|
|
1134
|
+
// Calculate frame size based on protocol version
|
|
1135
|
+
const headerSize = isV2 ? 10 : 6; // v2 has extra fields
|
|
1136
|
+
const frameSize = headerSize + payload.length + 2;
|
|
1137
|
+
const buffer = new ArrayBuffer(frameSize);
|
|
1138
|
+
const view = new DataView(buffer);
|
|
1139
|
+
let offset = 0;
|
|
1140
|
+
// Header
|
|
1141
|
+
view.setUint8(offset++, magic);
|
|
1142
|
+
view.setUint8(offset++, payload.length);
|
|
1143
|
+
if (isV2) {
|
|
1144
|
+
// MAVLink v2: magic(1) + len(1) + incompat_flags(1) + compat_flags(1) + seq(1) + sysid(1) + compid(1) + msgid(3) + payload + checksum(2)
|
|
1145
|
+
view.setUint8(offset++, 0); // incompat_flags
|
|
1146
|
+
view.setUint8(offset++, 0); // compat_flags
|
|
1147
|
+
view.setUint8(offset++, sequence);
|
|
1148
|
+
view.setUint8(offset++, systemId);
|
|
1149
|
+
view.setUint8(offset++, componentId);
|
|
1150
|
+
// 24-bit message ID in v2
|
|
1151
|
+
view.setUint8(offset++, messageId & 0xFF);
|
|
1152
|
+
view.setUint8(offset++, (messageId >> 8) & 0xFF);
|
|
1153
|
+
view.setUint8(offset++, (messageId >> 16) & 0xFF);
|
|
1154
|
+
}
|
|
1155
|
+
else {
|
|
1156
|
+
// MAVLink v1: magic(1) + len(1) + seq(1) + sysid(1) + compid(1) + msgid(1) + payload + checksum(2)
|
|
1157
|
+
view.setUint8(offset++, sequence);
|
|
1158
|
+
view.setUint8(offset++, systemId);
|
|
1159
|
+
view.setUint8(offset++, componentId);
|
|
1160
|
+
view.setUint8(offset++, messageId & 0xFF); // 8-bit message ID in v1
|
|
1161
|
+
}
|
|
1162
|
+
// Payload
|
|
1163
|
+
const payloadView = new Uint8Array(buffer, offset, payload.length);
|
|
1164
|
+
payloadView.set(payload);
|
|
1165
|
+
offset += payload.length;
|
|
1166
|
+
// Calculate proper MAVLink CRC with CRC_EXTRA
|
|
1167
|
+
const crcExtra = CRC_EXTRA[messageId];
|
|
1168
|
+
if (crcExtra === undefined) {
|
|
1169
|
+
throw new Error("No CRC_EXTRA defined for message ID " + messageId);
|
|
1170
|
+
}
|
|
1171
|
+
// Get message data (exclude start byte and checksum)
|
|
1172
|
+
const messageData = new Uint8Array(buffer, 1, offset - 1);
|
|
1173
|
+
const checksum = MAVLinkCRC.calculate(messageData, crcExtra);
|
|
1174
|
+
// Checksum (little endian)
|
|
1175
|
+
view.setUint8(offset++, checksum & 0xFF);
|
|
1176
|
+
view.setUint8(offset++, (checksum >> 8) & 0xFF);
|
|
1177
|
+
return new Uint8Array(buffer);
|
|
1178
|
+
}
|
|
790
1179
|
}
|
|
791
1180
|
const MESSAGE_DEFINITIONS = [
|
|
792
1181
|
{
|
|
793
1182
|
id: 0,
|
|
794
1183
|
name: 'HEARTBEAT',
|
|
795
1184
|
fields: [
|
|
1185
|
+
{
|
|
1186
|
+
name: 'custom_mode',
|
|
1187
|
+
type: 'uint32_t',
|
|
1188
|
+
},
|
|
796
1189
|
{
|
|
797
1190
|
name: 'type',
|
|
798
1191
|
type: 'uint8_t',
|
|
@@ -802,19 +1195,15 @@ const MESSAGE_DEFINITIONS = [
|
|
|
802
1195
|
type: 'uint8_t',
|
|
803
1196
|
},
|
|
804
1197
|
{
|
|
805
|
-
name: '
|
|
1198
|
+
name: 'base_mode',
|
|
806
1199
|
type: 'uint8_t',
|
|
807
1200
|
},
|
|
808
1201
|
{
|
|
809
|
-
name: '
|
|
810
|
-
type: 'uint32_t',
|
|
811
|
-
},
|
|
812
|
-
{
|
|
813
|
-
name: 'systemStatus',
|
|
1202
|
+
name: 'system_status',
|
|
814
1203
|
type: 'uint8_t',
|
|
815
1204
|
},
|
|
816
1205
|
{
|
|
817
|
-
name: '
|
|
1206
|
+
name: 'mavlink_version',
|
|
818
1207
|
type: 'uint8_t_mavlink_version',
|
|
819
1208
|
},
|
|
820
1209
|
]
|
|
@@ -828,20 +1217,20 @@ const MESSAGE_DEFINITIONS = [
|
|
|
828
1217
|
type: 'uint16_t',
|
|
829
1218
|
},
|
|
830
1219
|
{
|
|
831
|
-
name: '
|
|
1220
|
+
name: 'min_version',
|
|
832
1221
|
type: 'uint16_t',
|
|
833
1222
|
},
|
|
834
1223
|
{
|
|
835
|
-
name: '
|
|
1224
|
+
name: 'max_version',
|
|
836
1225
|
type: 'uint16_t',
|
|
837
1226
|
},
|
|
838
1227
|
{
|
|
839
|
-
name: '
|
|
1228
|
+
name: 'spec_version_hash',
|
|
840
1229
|
type: 'uint8_t',
|
|
841
1230
|
arrayLength: 8,
|
|
842
1231
|
},
|
|
843
1232
|
{
|
|
844
|
-
name: '
|
|
1233
|
+
name: 'library_version_hash',
|
|
845
1234
|
type: 'uint8_t',
|
|
846
1235
|
arrayLength: 8,
|
|
847
1236
|
},
|
|
@@ -866,6 +1255,43 @@ class StandardParser extends DialectParser {
|
|
|
866
1255
|
}
|
|
867
1256
|
}
|
|
868
1257
|
}
|
|
1258
|
+
// Dialect-specific serializer
|
|
1259
|
+
class StandardSerializer {
|
|
1260
|
+
constructor() {
|
|
1261
|
+
this.parser = new StandardParser();
|
|
1262
|
+
}
|
|
1263
|
+
// Serialize a message to MAVLink bytes
|
|
1264
|
+
serialize(message) {
|
|
1265
|
+
return this.parser.serializeMessage(message);
|
|
1266
|
+
}
|
|
1267
|
+
// Complete a message with all defined fields (including extension fields with defaults)
|
|
1268
|
+
// This is useful to see what the serializer would send without actually serializing
|
|
1269
|
+
// Requires payload structure format
|
|
1270
|
+
completeMessage(message) {
|
|
1271
|
+
const messageDef = Array.from(this.parser['messageDefinitions'].values())
|
|
1272
|
+
.find(def => def.name === message.message_name);
|
|
1273
|
+
if (!messageDef) {
|
|
1274
|
+
throw new Error(`Unknown message type: ${message.message_name}`);
|
|
1275
|
+
}
|
|
1276
|
+
// Extract fields from payload structure (throws error if not payload format)
|
|
1277
|
+
const messageFields = this.parser['extractMessageFields'](message, messageDef.fields);
|
|
1278
|
+
// Complete the message with defaults
|
|
1279
|
+
const completedFields = this.parser['completeMessageWithDefaults'](messageFields, messageDef.fields);
|
|
1280
|
+
// Return in the payload structure format
|
|
1281
|
+
return {
|
|
1282
|
+
...message,
|
|
1283
|
+
payload: completedFields
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
// Get supported message names for this dialect
|
|
1287
|
+
getSupportedMessages() {
|
|
1288
|
+
return Array.from(this.parser['messageDefinitions'].values()).map(def => def.name);
|
|
1289
|
+
}
|
|
1290
|
+
// Check if a message is supported by this dialect
|
|
1291
|
+
supportsMessage(messageName) {
|
|
1292
|
+
return Array.from(this.parser['messageDefinitions'].values()).some(def => def.name === messageName);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
869
1295
|
|
|
870
|
-
export {
|
|
1296
|
+
export { MAV_AUTOPILOTEnum, MAV_BOOLEnum, MAV_COMPONENTEnum, MAV_MODE_FLAGEnum, MAV_MODE_FLAG_DECODE_POSITIONEnum, MAV_STATEEnum, MAV_TYPEEnum, StandardParser, StandardSerializer, isHeartbeat, isProtocolVersion };
|
|
871
1297
|
//# sourceMappingURL=index.js.map
|