@aircast-4g/mavlink 1.1.7 → 1.1.8

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.
@@ -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 BOOLEnum {
266
- BOOL_FALSE = 0,
267
- BOOL_TRUE = 1
265
+ declare enum MAV_BOOLEnum {
266
+ MAV_BOOL_FALSE = 0,
267
+ MAV_BOOL_TRUE = 1
268
268
  }
269
- type BOOL = BOOLEnum;
269
+ type MAV_BOOL = MAV_BOOLEnum;
270
270
 
271
271
  interface MessageHeartbeat {
272
272
  type: MAV_TYPE;
273
273
  autopilot: MAV_AUTOPILOT;
274
- baseMode: MAV_MODE_FLAG;
275
- customMode: number;
276
- systemStatus: MAV_STATE;
277
- mavlinkVersion: number;
274
+ base_mode: MAV_MODE_FLAG;
275
+ custom_mode: number;
276
+ system_status: MAV_STATE;
277
+ mavlink_version: number;
278
278
  }
279
279
  interface MessageProtocolVersion {
280
280
  version: number;
281
- minVersion: number;
282
- maxVersion: number;
283
- specVersionHash: number[];
284
- libraryVersionHash: number[];
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 { BOOLEnum, MAV_AUTOPILOTEnum, MAV_COMPONENTEnum, MAV_MODE_FLAGEnum, MAV_MODE_FLAG_DECODE_POSITIONEnum, MAV_STATEEnum, MAV_TYPEEnum, StandardParser, isHeartbeat, isProtocolVersion };
364
- export type { AnyMessage, BOOL, MAV_AUTOPILOT, MAV_COMPONENT, MAV_MODE_FLAG, MAV_MODE_FLAG_DECODE_POSITION, MAV_STATE, MAV_TYPE, MessageHeartbeat, MessageProtocolVersion, MessageTypeMap, ParsedMAVLinkMessage$1 as ParsedMAVLinkMessage };
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 BOOLEnum;
498
- (function (BOOLEnum) {
497
+ var MAV_BOOLEnum;
498
+ (function (MAV_BOOLEnum) {
499
499
  // False.
500
- BOOLEnum[BOOLEnum["BOOL_FALSE"] = 0] = "BOOL_FALSE";
500
+ MAV_BOOLEnum[MAV_BOOLEnum["MAV_BOOL_FALSE"] = 0] = "MAV_BOOL_FALSE";
501
501
  // True.
502
- BOOLEnum[BOOLEnum["BOOL_TRUE"] = 1] = "BOOL_TRUE";
503
- })(BOOLEnum || (BOOLEnum = {}));
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
- frame.crc_ok = true; // Simplified - not doing CRC validation
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;
@@ -721,7 +769,7 @@ class DialectParser {
721
769
  case 'int8_t':
722
770
  return { value: view.getInt8(offset), bytesRead: 1 };
723
771
  case 'uint16_t':
724
- return { value: view.getUint16(offset, true), bytesRead: 2 };
772
+ return { value: view.getUint16(offset, false), bytesRead: 2 };
725
773
  case 'int16_t':
726
774
  return { value: view.getInt16(offset, true), bytesRead: 2 };
727
775
  case 'uint32_t':
@@ -787,6 +835,347 @@ 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), false);
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
  {
@@ -802,19 +1191,19 @@ const MESSAGE_DEFINITIONS = [
802
1191
  type: 'uint8_t',
803
1192
  },
804
1193
  {
805
- name: 'baseMode',
1194
+ name: 'base_mode',
806
1195
  type: 'uint8_t',
807
1196
  },
808
1197
  {
809
- name: 'customMode',
1198
+ name: 'custom_mode',
810
1199
  type: 'uint32_t',
811
1200
  },
812
1201
  {
813
- name: 'systemStatus',
1202
+ name: 'system_status',
814
1203
  type: 'uint8_t',
815
1204
  },
816
1205
  {
817
- name: 'mavlinkVersion',
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: 'minVersion',
1220
+ name: 'min_version',
832
1221
  type: 'uint16_t',
833
1222
  },
834
1223
  {
835
- name: 'maxVersion',
1224
+ name: 'max_version',
836
1225
  type: 'uint16_t',
837
1226
  },
838
1227
  {
839
- name: 'specVersionHash',
1228
+ name: 'spec_version_hash',
840
1229
  type: 'uint8_t',
841
1230
  arrayLength: 8,
842
1231
  },
843
1232
  {
844
- name: 'libraryVersionHash',
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 { BOOLEnum, MAV_AUTOPILOTEnum, MAV_COMPONENTEnum, MAV_MODE_FLAGEnum, MAV_MODE_FLAG_DECODE_POSITIONEnum, MAV_STATEEnum, MAV_TYPEEnum, StandardParser, isHeartbeat, isProtocolVersion };
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