node-mavlink 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,7 @@ import { Logger } from './logger';
7
7
  * Header definition of the MavLink packet
8
8
  */
9
9
  export declare class MavLinkPacketHeader {
10
+ timestamp: BigInt;
10
11
  magic: number;
11
12
  payloadLength: uint8_t;
12
13
  incompatibilityFlags: uint8_t;
@@ -37,7 +38,7 @@ export declare abstract class MavLinkProtocol {
37
38
  /**
38
39
  * Deserialize packet header
39
40
  */
40
- abstract header(buffer: any): MavLinkPacketHeader;
41
+ abstract header(buffer: any, timestamp?: any): MavLinkPacketHeader;
41
42
  /**
42
43
  * Deserialize packet checksum
43
44
  */
@@ -66,7 +67,7 @@ export declare class MavLinkProtocolV1 extends MavLinkProtocol {
66
67
  static PAYLOAD_OFFSET: number;
67
68
  constructor(sysid?: uint8_t, compid?: uint8_t);
68
69
  serialize(message: MavLinkData, seq: number): Buffer;
69
- header(buffer: Buffer): MavLinkPacketHeader;
70
+ header(buffer: Buffer, timestamp?: any): MavLinkPacketHeader;
70
71
  /**
71
72
  * Deserialize packet checksum
72
73
  */
@@ -100,7 +101,7 @@ export declare class MavLinkProtocolV2 extends MavLinkProtocol {
100
101
  */
101
102
  sign(buffer: Buffer, linkId: number, key: Buffer, timestamp?: number): Buffer;
102
103
  private calculateTruncatedPayloadLength;
103
- header(buffer: Buffer): MavLinkPacketHeader;
104
+ header(buffer: Buffer, timestamp?: any): MavLinkPacketHeader;
104
105
  /**
105
106
  * Deserialize packet checksum
106
107
  */
@@ -194,6 +195,7 @@ export declare class MavLinkPacketSplitter extends Transform {
194
195
  protected readonly log: Logger;
195
196
  private buffer;
196
197
  private onCrcError;
198
+ private timestamp;
197
199
  private _validPackagesCount;
198
200
  private _unknownPackagesCount;
199
201
  private _invalidPackagesCount;
@@ -246,7 +248,10 @@ export declare class MavLinkPacketParser extends Transform {
246
248
  protected readonly log: Logger;
247
249
  constructor(opts?: {});
248
250
  private getProtocol;
249
- _transform(chunk: Buffer, encoding: any, callback: TransformCallback): void;
251
+ _transform({ buffer, timestamp, ...rest }: {
252
+ buffer?: Buffer;
253
+ timestamp?: any;
254
+ }, encoding: any, callback: TransformCallback): void;
250
255
  }
251
256
  /**
252
257
  * Creates a MavLink packet stream reader that is reading packets from the given input
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.sendSigned=exports.send=exports.createMavLinkStream=exports.MavLinkPacketParser=exports.MavLinkPacketSplitter=exports.MavLinkPacket=exports.MavLinkPacketSignature=exports.MavLinkProtocolV2=exports.MavLinkProtocolV1=exports.MavLinkProtocol=exports.MavLinkPacketHeader=void 0;const stream_1=require("stream"),crypto_1=require("crypto"),mavlink_mappings_1=require("mavlink-mappings"),mavlink_mappings_2=require("mavlink-mappings"),utils_1=require("./utils"),logger_1=require("./logger"),serialization_1=require("./serialization");class MavLinkPacketHeader{constructor(){this.magic=0,this.payloadLength=0,this.incompatibilityFlags=0,this.compatibilityFlags=0,this.seq=0,this.sysid=0,this.compid=0,this.msgid=0}}exports.MavLinkPacketHeader=MavLinkPacketHeader;class MavLinkProtocol{constructor(){this.log=logger_1.Logger.getLogger(this)}data(t,n){this.log.trace("Deserializing",n.MSG_NAME,"with payload of size",t.length);const e=new n;return n.FIELDS.forEach(s=>{const i=serialization_1.DESERIALIZERS[s.type];if(!i)throw new Error(`Unknown field type ${s.type}`);e[s.name]=i(t,s.offset,s.length)}),e}}exports.MavLinkProtocol=MavLinkProtocol,MavLinkProtocol.NAME="unknown",MavLinkProtocol.START_BYTE=0,MavLinkProtocol.PAYLOAD_OFFSET=0,MavLinkProtocol.CHECKSUM_LENGTH=2,MavLinkProtocol.SYS_ID=254,MavLinkProtocol.COMP_ID=1;class MavLinkProtocolV1 extends MavLinkProtocol{constructor(t=MavLinkProtocol.SYS_ID,n=MavLinkProtocol.COMP_ID){super(),this.sysid=t,this.compid=n}serialize(t,n){this.log.trace("Serializing message (seq:",n,")");const e=t.constructor,s=Buffer.from(new Uint8Array(MavLinkProtocolV1.PAYLOAD_OFFSET+e.PAYLOAD_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH));s.writeUInt8(MavLinkProtocolV1.START_BYTE,0),s.writeUInt8(e.PAYLOAD_LENGTH,1),s.writeUInt8(n,2),s.writeUInt8(this.sysid,3),s.writeUInt8(this.compid,4),s.writeUInt8(e.MSG_ID,5),e.FIELDS.forEach(r=>{const o=serialization_1.SERIALIZERS[r.type];if(!o)throw new Error(`Unknown field type ${r.type}: serializer not found`);o(t[r.name],s,r.offset+MavLinkProtocolV1.PAYLOAD_OFFSET,r.length)});const i=(0,mavlink_mappings_1.x25crc)(s,1,2,e.MAGIC_NUMBER);return s.writeUInt16LE(i,s.length-2),s}header(t){this.log.trace("Reading header from buffer (len:",t.length,")");const n=t.readUInt8(0);if(n!==MavLinkProtocolV1.START_BYTE)throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV1.START_BYTE}, got ${n})`);const e=new MavLinkPacketHeader;return e.magic=n,e.payloadLength=t.readUInt8(1),e.seq=t.readUInt8(2),e.sysid=t.readUInt8(3),e.compid=t.readUInt8(4),e.msgid=t.readUInt8(5),e}crc(t){this.log.trace("Reading crc from buffer (len:",t.length,")");const n=t.readUInt8(1);return t.readUInt16LE(MavLinkProtocolV1.PAYLOAD_OFFSET+n)}payload(t){this.log.trace("Reading payload from buffer (len:",t.length,")");const n=t.readUInt8(1),e=t.slice(MavLinkProtocolV1.PAYLOAD_OFFSET,MavLinkProtocolV1.PAYLOAD_OFFSET+n),s=Buffer.from(new Uint8Array(255-e.length));return Buffer.concat([e,s])}}exports.MavLinkProtocolV1=MavLinkProtocolV1,MavLinkProtocolV1.NAME="MAV_V1",MavLinkProtocolV1.START_BYTE=254,MavLinkProtocolV1.PAYLOAD_OFFSET=6;class MavLinkProtocolV2 extends MavLinkProtocol{constructor(t=MavLinkProtocol.SYS_ID,n=MavLinkProtocol.COMP_ID,e=MavLinkProtocolV2.INCOMPATIBILITY_FLAGS,s=MavLinkProtocolV2.COMPATIBILITY_FLAGS){super(),this.sysid=t,this.compid=n,this.incompatibilityFlags=e,this.compatibilityFlags=s}serialize(t,n){this.log.trace("Serializing message (seq:",n,")");const e=t.constructor,s=Buffer.from(new Uint8Array(MavLinkProtocolV2.PAYLOAD_OFFSET+e.PAYLOAD_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH));s.writeUInt8(MavLinkProtocolV2.START_BYTE,0),s.writeUInt8(this.incompatibilityFlags,2),s.writeUInt8(this.compatibilityFlags,3),s.writeUInt8(n,4),s.writeUInt8(this.sysid,5),s.writeUInt8(this.compid,6),s.writeUIntLE(e.MSG_ID,7,3),e.FIELDS.forEach(c=>{const h=serialization_1.SERIALIZERS[c.type];if(!h)throw new Error(`Unknown field type ${c.type}: serializer not found`);h(t[c.name],s,c.offset+MavLinkProtocolV2.PAYLOAD_OFFSET,c.length)});const i=this.calculateTruncatedPayloadLength(s);s.writeUInt8(i,1);const r=s.slice(0,MavLinkProtocolV2.PAYLOAD_OFFSET+i+MavLinkProtocol.CHECKSUM_LENGTH),o=(0,mavlink_mappings_1.x25crc)(r,1,2,e.MAGIC_NUMBER);return r.writeUInt16LE(o,r.length-MavLinkProtocol.CHECKSUM_LENGTH),r}sign(t,n,e,s=Date.now()){this.log.trace("Signing message");const i=Buffer.concat([t,Buffer.from(new Uint8Array(MavLinkPacketSignature.SIGNATURE_LENGTH))]),r=new MavLinkPacketSignature(i);return r.linkId=n,r.timestamp=s,r.signature=r.calculate(e),i}calculateTruncatedPayloadLength(t){let n=t.length;for(let e=t.length-MavLinkProtocol.CHECKSUM_LENGTH-1;e>=MavLinkProtocolV2.PAYLOAD_OFFSET;e--)if(n=e,t[e]!==0){n++;break}return n-MavLinkProtocolV2.PAYLOAD_OFFSET}header(t){this.log.trace("Reading header from buffer (len:",t.length,")");const n=t.readUInt8(0);if(n!==MavLinkProtocolV2.START_BYTE)throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV2.START_BYTE}, got ${n})`);const e=new MavLinkPacketHeader;return e.magic=n,e.payloadLength=t.readUInt8(1),e.incompatibilityFlags=t.readUInt8(2),e.compatibilityFlags=t.readUInt8(3),e.seq=t.readUInt8(4),e.sysid=t.readUInt8(5),e.compid=t.readUInt8(6),e.msgid=t.readUIntLE(7,3),e}crc(t){this.log.trace("Reading crc from buffer (len:",t.length,")");const n=t.readUInt8(1);return t.readUInt16LE(MavLinkProtocolV2.PAYLOAD_OFFSET+n)}payload(t){this.log.trace("Reading payload from buffer (len:",t.length,")");const n=t.readUInt8(1),e=t.slice(MavLinkProtocolV2.PAYLOAD_OFFSET,MavLinkProtocolV2.PAYLOAD_OFFSET+n),s=Buffer.from(new Uint8Array(255-e.length));return Buffer.concat([e,s])}signature(t,n){return this.log.trace("Reading signature from buffer (len:",t.length,")"),n.incompatibilityFlags&MavLinkProtocolV2.IFLAG_SIGNED?new MavLinkPacketSignature(t):null}}exports.MavLinkProtocolV2=MavLinkProtocolV2,MavLinkProtocolV2.NAME="MAV_V2",MavLinkProtocolV2.START_BYTE=253,MavLinkProtocolV2.PAYLOAD_OFFSET=10,MavLinkProtocolV2.INCOMPATIBILITY_FLAGS=0,MavLinkProtocolV2.COMPATIBILITY_FLAGS=0,MavLinkProtocolV2.IFLAG_SIGNED=1;const KNOWN_PROTOCOLS_BY_STX={[MavLinkProtocolV1.START_BYTE]:MavLinkProtocolV1,[MavLinkProtocolV2.START_BYTE]:MavLinkProtocolV2};class MavLinkPacketSignature{constructor(t){this.buffer=t}static key(t){return(0,crypto_1.createHash)("sha256").update(t).digest()}get offset(){return this.buffer.length-MavLinkPacketSignature.SIGNATURE_LENGTH}get linkId(){return this.buffer.readUInt8(this.offset)}set linkId(t){this.buffer.writeUInt8(this.offset)}get timestamp(){return this.buffer.readUIntLE(this.offset+1,6)}set timestamp(t){this.buffer.writeUIntLE(t,this.offset+1,6)}get signature(){return this.buffer.slice(this.offset+7,this.offset+7+6).toString("hex")}set signature(t){this.buffer.write(t,this.offset+7,"hex")}calculate(t){return(0,crypto_1.createHash)("sha256").update(t).update(this.buffer.slice(0,this.buffer.length-6)).digest("hex").substr(0,12)}matches(t){return this.calculate(t)===this.signature}toString(){return`linkid: ${this.linkId}, timestamp ${this.timestamp}, signature ${this.signature}`}}exports.MavLinkPacketSignature=MavLinkPacketSignature,MavLinkPacketSignature.SIGNATURE_LENGTH=13;class MavLinkPacket{constructor(t,n=new MavLinkPacketHeader,e=Buffer.from(new Uint8Array(255)),s=0,i=new MavLinkProtocolV1,r=null){this.buffer=t,this.header=n,this.payload=e,this.crc=s,this.protocol=i,this.signature=r}debug(){return`Packet (proto: ${this.protocol.constructor.NAME}, sysid: ${this.header.sysid}, compid: ${this.header.compid}, msgid: ${this.header.msgid}, seq: ${this.header.seq}, plen: ${this.header.payloadLength}, magic: ${mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[this.header.msgid]} (${(0,utils_1.hex)(mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[this.header.msgid])}), crc: ${(0,utils_1.hex)(this.crc,4)}`+this.signatureToString(this.signature)+")"}signatureToString(t){return t?`, ${t.toString()}`:""}}exports.MavLinkPacket=MavLinkPacket;var PacketValidationResult;(function(a){a[a.VALID=0]="VALID",a[a.INVALID=1]="INVALID",a[a.UNKNOWN=2]="UNKNOWN"})(PacketValidationResult||(PacketValidationResult={}));class MavLinkPacketSplitter extends stream_1.Transform{constructor(t={},n=()=>{}){super(t),this.log=logger_1.Logger.getLogger(this),this.buffer=Buffer.from([]),this.onCrcError=null,this._validPackagesCount=0,this._unknownPackagesCount=0,this._invalidPackagesCount=0,this.onCrcError=n}_transform(t,n,e){for(this.buffer=Buffer.concat([this.buffer,t]);this.buffer.byteLength>0;){const s=this.findStartOfPacket(this.buffer);if(s===null)break;s>0&&(this.buffer=this.buffer.slice(s)),this.log.debug("Found potential packet start at",s);const i=this.getPacketProtocol(this.buffer);if(this.log.debug("Packet protocol is",i.NAME),this.buffer.length<i.PAYLOAD_OFFSET+MavLinkProtocol.CHECKSUM_LENGTH){this.log.debug("Current buffer shorter than the shortest message - skipping");break}const r=this.readPacketLength(this.buffer,i);if(this.log.debug("Expected buffer length:",r,`(${(0,utils_1.hex)(r)})`),this.buffer.length<r){this.log.debug("Current buffer is not fully retrieved yet - skipping");break}else this.log.debug("Current buffer length:",this.buffer.length,`(${(0,utils_1.hex)(this.buffer.length,4)})`);const o=this.buffer.slice(0,r);switch(this.log.debug("Recognized buffer length:",o.length,`(${(0,utils_1.hex)(o.length,2)})`),this.validatePacket(o,i)){case PacketValidationResult.VALID:this.log.debug("Found a valid packet"),this._validPackagesCount++,this.push(o),this.buffer=this.buffer.slice(r);break;case PacketValidationResult.INVALID:this.log.debug("Found an invalid packet - skipping"),this._invalidPackagesCount++,this.buffer=this.buffer.slice(1);break;case PacketValidationResult.UNKNOWN:this.log.debug("Found an unknown packet - skipping"),this._unknownPackagesCount++,this.buffer=this.buffer.slice(r);break}}e(null)}findStartOfPacket(t){const n=t.indexOf(MavLinkProtocolV1.START_BYTE),e=t.indexOf(MavLinkProtocolV2.START_BYTE);return n>=0&&e>=0?n<e?n:e:n>=0?n:e>=0?e:null}getPacketProtocol(t){return KNOWN_PROTOCOLS_BY_STX[t.readUInt8(0)]||null}readPacketLength(t,n){const e=t.readUInt8(1);return n.PAYLOAD_OFFSET+e+MavLinkProtocol.CHECKSUM_LENGTH+(this.isV2Signed(t)?MavLinkPacketSignature.SIGNATURE_LENGTH:0)}validatePacket(t,n){const e=new n,s=e.header(t),i=mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[s.msgid];if(i){const r=e.crc(t),o=this.isV2Signed(t)?MavLinkPacketSignature.SIGNATURE_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH:MavLinkProtocol.CHECKSUM_LENGTH,c=(0,mavlink_mappings_1.x25crc)(t,1,o,i);if(r===c)return PacketValidationResult.VALID;{const h=[`CRC error; expected: ${c} (${(0,utils_1.hex)(c,4)}), got ${r} (${(0,utils_1.hex)(r,4)});`,`msgid: ${s.msgid} (${(0,utils_1.hex)(s.msgid)}),`,`seq: ${s.seq} (${(0,utils_1.hex)(s.seq)}),`,`plen: ${s.payloadLength} (${(0,utils_1.hex)(s.payloadLength)}),`,`magic: ${i} (${(0,utils_1.hex)(i)})`];return this.log.warn(h.join(" ")),this.onCrcError(t),PacketValidationResult.INVALID}}else return this.log.debug(`Unknown message with id ${s.msgid} (magic number not found) - skipping`),PacketValidationResult.UNKNOWN}isV2Signed(t){if(t.readUInt8(0)===MavLinkProtocolV2.START_BYTE)return!!(t.readUInt8(2)&MavLinkProtocolV2.IFLAG_SIGNED)}get validPackages(){return this._validPackagesCount}resetValidPackagesCount(){this._validPackagesCount=0}get invalidPackages(){return this._invalidPackagesCount}resetInvalidPackagesCount(){this._invalidPackagesCount=0}get unknownPackagesCount(){return this._unknownPackagesCount}resetUnknownPackagesCount(){this._unknownPackagesCount=0}}exports.MavLinkPacketSplitter=MavLinkPacketSplitter;class MavLinkPacketParser extends stream_1.Transform{constructor(t={}){super({...t,objectMode:!0}),this.log=logger_1.Logger.getLogger(this)}getProtocol(t){const n=t.readUInt8(0);switch(n){case MavLinkProtocolV1.START_BYTE:return new MavLinkProtocolV1;case MavLinkProtocolV2.START_BYTE:return new MavLinkProtocolV2;default:throw new Error(`Unknown protocol '${(0,utils_1.hex)(n)}'`)}}_transform(t,n,e){const s=this.getProtocol(t),i=s.header(t),r=s.payload(t),o=s.crc(t),c=s instanceof MavLinkProtocolV2?s.signature(t,i):null,h=new MavLinkPacket(t,i,r,o,s,c);e(null,h)}}exports.MavLinkPacketParser=MavLinkPacketParser;function createMavLinkStream(a,t){return a.pipe(new MavLinkPacketSplitter({},t)).pipe(new MavLinkPacketParser)}exports.createMavLinkStream=createMavLinkStream;let seq=0;async function send(a,t,n=new MavLinkProtocolV1){return new Promise((e,s)=>{const i=n.serialize(t,seq++);seq&=255,a.write(i,r=>{r?s(r):e(i.length)})})}exports.send=send;async function sendSigned(a,t,n,e=1,s=MavLinkProtocol.SYS_ID,i=MavLinkProtocol.COMP_ID,r=Date.now()){return new Promise((o,c)=>{const h=new MavLinkProtocolV2(s,i,MavLinkProtocolV2.IFLAG_SIGNED),d=h.serialize(t,seq++);seq&=255;const g=h.sign(d,e,n,r);a.write(g,l=>{l?c(l):o(g.length)})})}exports.sendSigned=sendSigned;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.sendSigned=exports.send=exports.createMavLinkStream=exports.MavLinkPacketParser=exports.MavLinkPacketSplitter=exports.MavLinkPacket=exports.MavLinkPacketSignature=exports.MavLinkProtocolV2=exports.MavLinkProtocolV1=exports.MavLinkProtocol=exports.MavLinkPacketHeader=void 0;const stream_1=require("stream"),crypto_1=require("crypto"),mavlink_mappings_1=require("mavlink-mappings"),mavlink_mappings_2=require("mavlink-mappings"),utils_1=require("./utils"),logger_1=require("./logger"),serialization_1=require("./serialization");class MavLinkPacketHeader{constructor(){this.timestamp=null,this.magic=0,this.payloadLength=0,this.incompatibilityFlags=0,this.compatibilityFlags=0,this.seq=0,this.sysid=0,this.compid=0,this.msgid=0}}exports.MavLinkPacketHeader=MavLinkPacketHeader;class MavLinkProtocol{constructor(){this.log=logger_1.Logger.getLogger(this)}data(t,s){this.log.trace("Deserializing",s.MSG_NAME,"with payload of size",t.length);const n=new s;return s.FIELDS.forEach(e=>{const i=serialization_1.DESERIALIZERS[e.type];if(!i)throw new Error(`Unknown field type ${e.type}`);n[e.name]=i(t,e.offset,e.length)}),n}}exports.MavLinkProtocol=MavLinkProtocol,MavLinkProtocol.NAME="unknown",MavLinkProtocol.START_BYTE=0,MavLinkProtocol.PAYLOAD_OFFSET=0,MavLinkProtocol.CHECKSUM_LENGTH=2,MavLinkProtocol.SYS_ID=254,MavLinkProtocol.COMP_ID=1;class MavLinkProtocolV1 extends MavLinkProtocol{constructor(t=MavLinkProtocol.SYS_ID,s=MavLinkProtocol.COMP_ID){super(),this.sysid=t,this.compid=s}serialize(t,s){this.log.trace("Serializing message (seq:",s,")");const n=t.constructor,e=Buffer.from(new Uint8Array(MavLinkProtocolV1.PAYLOAD_OFFSET+n.PAYLOAD_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH));e.writeUInt8(MavLinkProtocolV1.START_BYTE,0),e.writeUInt8(n.PAYLOAD_LENGTH,1),e.writeUInt8(s,2),e.writeUInt8(this.sysid,3),e.writeUInt8(this.compid,4),e.writeUInt8(n.MSG_ID,5),n.FIELDS.forEach(r=>{const o=serialization_1.SERIALIZERS[r.type];if(!o)throw new Error(`Unknown field type ${r.type}: serializer not found`);o(t[r.name],e,r.offset+MavLinkProtocolV1.PAYLOAD_OFFSET,r.length)});const i=(0,mavlink_mappings_1.x25crc)(e,1,2,n.MAGIC_NUMBER);return e.writeUInt16LE(i,e.length-2),e}header(t,s=null){this.log.trace("Reading header from buffer (len:",t.length,")");const n=t.readUInt8(0);if(n!==MavLinkProtocolV1.START_BYTE)throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV1.START_BYTE}, got ${n})`);const e=new MavLinkPacketHeader;return e.timestamp=s,e.magic=n,e.payloadLength=t.readUInt8(1),e.seq=t.readUInt8(2),e.sysid=t.readUInt8(3),e.compid=t.readUInt8(4),e.msgid=t.readUInt8(5),e}crc(t){this.log.trace("Reading crc from buffer (len:",t.length,")");const s=t.readUInt8(1);return t.readUInt16LE(MavLinkProtocolV1.PAYLOAD_OFFSET+s)}payload(t){this.log.trace("Reading payload from buffer (len:",t.length,")");const s=t.readUInt8(1),n=t.slice(MavLinkProtocolV1.PAYLOAD_OFFSET,MavLinkProtocolV1.PAYLOAD_OFFSET+s),e=Buffer.from(new Uint8Array(255-n.length));return Buffer.concat([n,e])}}exports.MavLinkProtocolV1=MavLinkProtocolV1,MavLinkProtocolV1.NAME="MAV_V1",MavLinkProtocolV1.START_BYTE=254,MavLinkProtocolV1.PAYLOAD_OFFSET=6;class MavLinkProtocolV2 extends MavLinkProtocol{constructor(t=MavLinkProtocol.SYS_ID,s=MavLinkProtocol.COMP_ID,n=MavLinkProtocolV2.INCOMPATIBILITY_FLAGS,e=MavLinkProtocolV2.COMPATIBILITY_FLAGS){super(),this.sysid=t,this.compid=s,this.incompatibilityFlags=n,this.compatibilityFlags=e}serialize(t,s){this.log.trace("Serializing message (seq:",s,")");const n=t.constructor,e=Buffer.from(new Uint8Array(MavLinkProtocolV2.PAYLOAD_OFFSET+n.PAYLOAD_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH));e.writeUInt8(MavLinkProtocolV2.START_BYTE,0),e.writeUInt8(this.incompatibilityFlags,2),e.writeUInt8(this.compatibilityFlags,3),e.writeUInt8(s,4),e.writeUInt8(this.sysid,5),e.writeUInt8(this.compid,6),e.writeUIntLE(n.MSG_ID,7,3),n.FIELDS.forEach(c=>{const h=serialization_1.SERIALIZERS[c.type];if(!h)throw new Error(`Unknown field type ${c.type}: serializer not found`);h(t[c.name],e,c.offset+MavLinkProtocolV2.PAYLOAD_OFFSET,c.length)});const i=this.calculateTruncatedPayloadLength(e);e.writeUInt8(i,1);const r=e.slice(0,MavLinkProtocolV2.PAYLOAD_OFFSET+i+MavLinkProtocol.CHECKSUM_LENGTH),o=(0,mavlink_mappings_1.x25crc)(r,1,2,n.MAGIC_NUMBER);return r.writeUInt16LE(o,r.length-MavLinkProtocol.CHECKSUM_LENGTH),r}sign(t,s,n,e=Date.now()){this.log.trace("Signing message");const i=Buffer.concat([t,Buffer.from(new Uint8Array(MavLinkPacketSignature.SIGNATURE_LENGTH))]),r=new MavLinkPacketSignature(i);return r.linkId=s,r.timestamp=e,r.signature=r.calculate(n),i}calculateTruncatedPayloadLength(t){let s=t.length;for(let n=t.length-MavLinkProtocol.CHECKSUM_LENGTH-1;n>=MavLinkProtocolV2.PAYLOAD_OFFSET;n--)if(s=n,t[n]!==0){s++;break}return s-MavLinkProtocolV2.PAYLOAD_OFFSET}header(t,s=null){this.log.trace("Reading header from buffer (len:",t.length,")");const n=t.readUInt8(0);if(n!==MavLinkProtocolV2.START_BYTE)throw new Error(`Invalid start byte (expected: ${MavLinkProtocolV2.START_BYTE}, got ${n})`);const e=new MavLinkPacketHeader;return e.timestamp=s,e.magic=n,e.payloadLength=t.readUInt8(1),e.incompatibilityFlags=t.readUInt8(2),e.compatibilityFlags=t.readUInt8(3),e.seq=t.readUInt8(4),e.sysid=t.readUInt8(5),e.compid=t.readUInt8(6),e.msgid=t.readUIntLE(7,3),e}crc(t){this.log.trace("Reading crc from buffer (len:",t.length,")");const s=t.readUInt8(1);return t.readUInt16LE(MavLinkProtocolV2.PAYLOAD_OFFSET+s)}payload(t){this.log.trace("Reading payload from buffer (len:",t.length,")");const s=t.readUInt8(1),n=t.slice(MavLinkProtocolV2.PAYLOAD_OFFSET,MavLinkProtocolV2.PAYLOAD_OFFSET+s),e=Buffer.from(new Uint8Array(255-n.length));return Buffer.concat([n,e])}signature(t,s){return this.log.trace("Reading signature from buffer (len:",t.length,")"),s.incompatibilityFlags&MavLinkProtocolV2.IFLAG_SIGNED?new MavLinkPacketSignature(t):null}}exports.MavLinkProtocolV2=MavLinkProtocolV2,MavLinkProtocolV2.NAME="MAV_V2",MavLinkProtocolV2.START_BYTE=253,MavLinkProtocolV2.PAYLOAD_OFFSET=10,MavLinkProtocolV2.INCOMPATIBILITY_FLAGS=0,MavLinkProtocolV2.COMPATIBILITY_FLAGS=0,MavLinkProtocolV2.IFLAG_SIGNED=1;const KNOWN_PROTOCOLS_BY_STX={[MavLinkProtocolV1.START_BYTE]:MavLinkProtocolV1,[MavLinkProtocolV2.START_BYTE]:MavLinkProtocolV2};class MavLinkPacketSignature{constructor(t){this.buffer=t}static key(t){return(0,crypto_1.createHash)("sha256").update(t).digest()}get offset(){return this.buffer.length-MavLinkPacketSignature.SIGNATURE_LENGTH}get linkId(){return this.buffer.readUInt8(this.offset)}set linkId(t){this.buffer.writeUInt8(this.offset)}get timestamp(){return this.buffer.readUIntLE(this.offset+1,6)}set timestamp(t){this.buffer.writeUIntLE(t,this.offset+1,6)}get signature(){return this.buffer.slice(this.offset+7,this.offset+7+6).toString("hex")}set signature(t){this.buffer.write(t,this.offset+7,"hex")}calculate(t){return(0,crypto_1.createHash)("sha256").update(t).update(this.buffer.slice(0,this.buffer.length-6)).digest("hex").substr(0,12)}matches(t){return this.calculate(t)===this.signature}toString(){return`linkid: ${this.linkId}, timestamp ${this.timestamp}, signature ${this.signature}`}}exports.MavLinkPacketSignature=MavLinkPacketSignature,MavLinkPacketSignature.SIGNATURE_LENGTH=13;class MavLinkPacket{constructor(t,s=new MavLinkPacketHeader,n=Buffer.from(new Uint8Array(255)),e=0,i=new MavLinkProtocolV1,r=null){this.buffer=t,this.header=s,this.payload=n,this.crc=e,this.protocol=i,this.signature=r}debug(){return`Packet (proto: ${this.protocol.constructor.NAME}, sysid: ${this.header.sysid}, compid: ${this.header.compid}, msgid: ${this.header.msgid}, seq: ${this.header.seq}, plen: ${this.header.payloadLength}, magic: ${mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[this.header.msgid]} (${(0,utils_1.hex)(mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[this.header.msgid])}), crc: ${(0,utils_1.hex)(this.crc,4)}`+this.signatureToString(this.signature)+")"}signatureToString(t){return t?`, ${t.toString()}`:""}}exports.MavLinkPacket=MavLinkPacket;var PacketValidationResult;(function(a){a[a.VALID=0]="VALID",a[a.INVALID=1]="INVALID",a[a.UNKNOWN=2]="UNKNOWN"})(PacketValidationResult||(PacketValidationResult={}));class MavLinkPacketSplitter extends stream_1.Transform{constructor(t={},s=()=>{}){super({...t,objectMode:!0}),this.log=logger_1.Logger.getLogger(this),this.buffer=Buffer.from([]),this.onCrcError=null,this.timestamp=null,this._validPackagesCount=0,this._unknownPackagesCount=0,this._invalidPackagesCount=0,this.onCrcError=s}_transform(t,s,n){for(this.buffer=Buffer.concat([this.buffer,t]);this.buffer.byteLength>0;){const e=this.findStartOfPacket(this.buffer);if(e===null)break;e===8?this.timestamp=Buffer.from(this.buffer,0,8).readBigUInt64BE()/1000n:this.timestamp=null,e>0&&(this.buffer=this.buffer.slice(e)),this.log.debug("Found potential packet start at",e);const i=this.getPacketProtocol(this.buffer);if(this.log.debug("Packet protocol is",i.NAME),this.buffer.length<i.PAYLOAD_OFFSET+MavLinkProtocol.CHECKSUM_LENGTH){this.log.debug("Current buffer shorter than the shortest message - skipping");break}const r=this.readPacketLength(this.buffer,i);if(this.log.debug("Expected buffer length:",r,`(${(0,utils_1.hex)(r)})`),this.buffer.length<r){this.log.debug("Current buffer is not fully retrieved yet - skipping");break}else this.log.debug("Current buffer length:",this.buffer.length,`(${(0,utils_1.hex)(this.buffer.length,4)})`);const o=this.buffer.slice(0,r);switch(this.log.debug("Recognized buffer length:",o.length,`(${(0,utils_1.hex)(o.length,2)})`),this.validatePacket(o,i)){case PacketValidationResult.VALID:this.log.debug("Found a valid packet"),this._validPackagesCount++,this.push({buffer:o,timestamp:this.timestamp}),this.buffer=this.buffer.slice(r);break;case PacketValidationResult.INVALID:this.log.debug("Found an invalid packet - skipping"),this._invalidPackagesCount++,this.buffer=this.buffer.slice(1);break;case PacketValidationResult.UNKNOWN:this.log.debug("Found an unknown packet - skipping"),this._unknownPackagesCount++,this.buffer=this.buffer.slice(r);break}}n(null)}findStartOfPacket(t){const s=t.indexOf(MavLinkProtocolV1.START_BYTE),n=t.indexOf(MavLinkProtocolV2.START_BYTE);return s>=0&&n>=0?s<n?s:n:s>=0?s:n>=0?n:null}getPacketProtocol(t){return KNOWN_PROTOCOLS_BY_STX[t.readUInt8(0)]||null}readPacketLength(t,s){const n=t.readUInt8(1);return s.PAYLOAD_OFFSET+n+MavLinkProtocol.CHECKSUM_LENGTH+(this.isV2Signed(t)?MavLinkPacketSignature.SIGNATURE_LENGTH:0)}validatePacket(t,s){const n=new s,e=n.header(t),i=mavlink_mappings_2.MSG_ID_MAGIC_NUMBER[e.msgid];if(i){const r=n.crc(t),o=this.isV2Signed(t)?MavLinkPacketSignature.SIGNATURE_LENGTH+MavLinkProtocol.CHECKSUM_LENGTH:MavLinkProtocol.CHECKSUM_LENGTH,c=(0,mavlink_mappings_1.x25crc)(t,1,o,i);if(r===c)return PacketValidationResult.VALID;{const h=[`CRC error; expected: ${c} (${(0,utils_1.hex)(c,4)}), got ${r} (${(0,utils_1.hex)(r,4)});`,`msgid: ${e.msgid} (${(0,utils_1.hex)(e.msgid)}),`,`seq: ${e.seq} (${(0,utils_1.hex)(e.seq)}),`,`plen: ${e.payloadLength} (${(0,utils_1.hex)(e.payloadLength)}),`,`magic: ${i} (${(0,utils_1.hex)(i)})`];return this.log.warn(h.join(" ")),this.onCrcError(t),PacketValidationResult.INVALID}}else return this.log.debug(`Unknown message with id ${e.msgid} (magic number not found) - skipping`),PacketValidationResult.UNKNOWN}isV2Signed(t){if(t.readUInt8(0)===MavLinkProtocolV2.START_BYTE)return!!(t.readUInt8(2)&MavLinkProtocolV2.IFLAG_SIGNED)}get validPackages(){return this._validPackagesCount}resetValidPackagesCount(){this._validPackagesCount=0}get invalidPackages(){return this._invalidPackagesCount}resetInvalidPackagesCount(){this._invalidPackagesCount=0}get unknownPackagesCount(){return this._unknownPackagesCount}resetUnknownPackagesCount(){this._unknownPackagesCount=0}}exports.MavLinkPacketSplitter=MavLinkPacketSplitter;class MavLinkPacketParser extends stream_1.Transform{constructor(t={}){super({...t,objectMode:!0}),this.log=logger_1.Logger.getLogger(this)}getProtocol(t){const s=t.readUInt8(0);switch(s){case MavLinkProtocolV1.START_BYTE:return new MavLinkProtocolV1;case MavLinkProtocolV2.START_BYTE:return new MavLinkProtocolV2;default:throw new Error(`Unknown protocol '${(0,utils_1.hex)(s)}'`)}}_transform({buffer:t=Buffer.from([]),timestamp:s=null,...n}={},e,i){const r=this.getProtocol(t),o=r.header(t,s),c=r.payload(t),h=r.crc(t),l=r instanceof MavLinkProtocolV2?r.signature(t,o):null,g=new MavLinkPacket(t,o,c,h,r,l);i(null,g)}}exports.MavLinkPacketParser=MavLinkPacketParser;function createMavLinkStream(a,t){return a.pipe(new MavLinkPacketSplitter({},t)).pipe(new MavLinkPacketParser)}exports.createMavLinkStream=createMavLinkStream;let seq=0;async function send(a,t,s=new MavLinkProtocolV1){return new Promise((n,e)=>{const i=s.serialize(t,seq++);seq&=255,a.write(i,r=>{r?e(r):n(i.length)})})}exports.send=send;async function sendSigned(a,t,s,n=1,e=MavLinkProtocol.SYS_ID,i=MavLinkProtocol.COMP_ID,r=Date.now()){return new Promise((o,c)=>{const h=new MavLinkProtocolV2(e,i,MavLinkProtocolV2.IFLAG_SIGNED),l=h.serialize(t,seq++);seq&=255;const g=h.sign(l,n,s,r);a.write(g,u=>{u?c(u):o(g.length)})})}exports.sendSigned=sendSigned;
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env -S npx ts-node
2
+
3
+ import { createReadStream } from 'fs'
4
+ import { MavLinkPacketSplitter, MavLinkPacketParser } from '..'
5
+ import {
6
+ minimal, common, ardupilotmega, uavionix, icarous,
7
+ asluav, development, ualberta,
8
+ } from '..'
9
+
10
+ const splitter = new MavLinkPacketSplitter()
11
+ const parser = new MavLinkPacketParser()
12
+ const file = createReadStream(__dirname + '/vtol.tlog')
13
+ const reader = file.pipe(splitter).pipe(parser)
14
+
15
+ // create a registry of mappings between a message id and a data class
16
+ const REGISTRY = {
17
+ ...minimal.REGISTRY,
18
+ ...common.REGISTRY,
19
+ ...ardupilotmega.REGISTRY,
20
+ ...uavionix.REGISTRY,
21
+ ...icarous.REGISTRY,
22
+ ...asluav.REGISTRY,
23
+ ...development.REGISTRY,
24
+ ...ualberta.REGISTRY,
25
+ }
26
+
27
+ reader.on('data', packet => {
28
+ const clazz = REGISTRY[packet.header.msgid]
29
+ if (clazz) {
30
+ const data = packet.protocol.data(packet.payload, clazz)
31
+ if (packet.header.timestamp) {
32
+ console.log(new Date(Number(packet.header.timestamp)).toISOString(), data)
33
+ } else {
34
+ console.log(data)
35
+ }
36
+ }
37
+ })
38
+
39
+ file.on('close', () => {
40
+ console.log('\n\nNumber of invalid packages:', splitter.invalidPackages)
41
+ console.log('Number of unknown packages:', splitter.unknownPackagesCount)
42
+ console.log('\nTotal number of consumed packets:', splitter.validPackages)
43
+ })
Binary file
package/lib/mavlink.ts CHANGED
@@ -12,6 +12,7 @@ import { SERIALIZERS, DESERIALIZERS } from './serialization'
12
12
  * Header definition of the MavLink packet
13
13
  */
14
14
  export class MavLinkPacketHeader {
15
+ timestamp: BigInt = null
15
16
  magic: number = 0
16
17
  payloadLength: uint8_t = 0
17
18
  incompatibilityFlags: uint8_t = 0
@@ -47,7 +48,7 @@ export abstract class MavLinkProtocol {
47
48
  /**
48
49
  * Deserialize packet header
49
50
  */
50
- abstract header(buffer): MavLinkPacketHeader
51
+ abstract header(buffer, timestamp?): MavLinkPacketHeader
51
52
 
52
53
  /**
53
54
  * Deserialize packet checksum
@@ -139,7 +140,7 @@ export class MavLinkProtocolV1 extends MavLinkProtocol {
139
140
  return buffer
140
141
  }
141
142
 
142
- header(buffer: Buffer): MavLinkPacketHeader {
143
+ header(buffer: Buffer, timestamp = null): MavLinkPacketHeader {
143
144
  this.log.trace('Reading header from buffer (len:', buffer.length, ')')
144
145
 
145
146
  const startByte = buffer.readUInt8(0)
@@ -148,6 +149,7 @@ export class MavLinkProtocolV1 extends MavLinkProtocol {
148
149
  }
149
150
 
150
151
  const result = new MavLinkPacketHeader()
152
+ result.timestamp = timestamp
151
153
  result.magic = startByte
152
154
  result.payloadLength = buffer.readUInt8(1)
153
155
  result.seq = buffer.readUInt8(2)
@@ -272,7 +274,7 @@ export class MavLinkProtocolV2 extends MavLinkProtocol {
272
274
  return result - MavLinkProtocolV2.PAYLOAD_OFFSET
273
275
  }
274
276
 
275
- header(buffer: Buffer): MavLinkPacketHeader {
277
+ header(buffer: Buffer, timestamp = null): MavLinkPacketHeader {
276
278
  this.log.trace('Reading header from buffer (len:', buffer.length, ')')
277
279
 
278
280
  const startByte = buffer.readUInt8(0)
@@ -281,6 +283,7 @@ export class MavLinkProtocolV2 extends MavLinkProtocol {
281
283
  }
282
284
 
283
285
  const result = new MavLinkPacketHeader()
286
+ result.timestamp = timestamp
284
287
  result.magic = startByte
285
288
  result.payloadLength = buffer.readUInt8(1)
286
289
  result.incompatibilityFlags = buffer.readUInt8(2)
@@ -484,6 +487,7 @@ export class MavLinkPacketSplitter extends Transform {
484
487
 
485
488
  private buffer = Buffer.from([])
486
489
  private onCrcError = null
490
+ private timestamp = null
487
491
  private _validPackagesCount = 0
488
492
  private _unknownPackagesCount = 0
489
493
  private _invalidPackagesCount = 0
@@ -494,7 +498,7 @@ export class MavLinkPacketSplitter extends Transform {
494
498
  * @param onCrcError callback executed if there is a CRC error (mostly for debugging)
495
499
  */
496
500
  constructor(opts = {}, onCrcError: BufferCallback = () => {}) {
497
- super(opts)
501
+ super({ ...opts, objectMode: true })
498
502
  this.onCrcError = onCrcError
499
503
  }
500
504
 
@@ -508,6 +512,12 @@ export class MavLinkPacketSplitter extends Transform {
508
512
  break
509
513
  }
510
514
 
515
+ // if the current offset is exactly the size of the timestamp field from tlog then read it.
516
+ if (offset === 8) {
517
+ this.timestamp = Buffer.from(this.buffer, 0, 8).readBigUInt64BE() / 1000n
518
+ } else {
519
+ this.timestamp = null
520
+ }
511
521
  // fast-forward the buffer to the first start byte
512
522
  if (offset > 0) {
513
523
  this.buffer = this.buffer.slice(offset)
@@ -546,7 +556,7 @@ export class MavLinkPacketSplitter extends Transform {
546
556
  case PacketValidationResult.VALID:
547
557
  this.log.debug('Found a valid packet')
548
558
  this._validPackagesCount++
549
- this.push(buffer)
559
+ this.push({ buffer, timestamp: this.timestamp })
550
560
  // truncate the buffer to remove the current message
551
561
  this.buffer = this.buffer.slice(expectedBufferLength)
552
562
  break
@@ -717,16 +727,16 @@ export class MavLinkPacketParser extends Transform {
717
727
  }
718
728
  }
719
729
 
720
- _transform(chunk: Buffer, encoding, callback: TransformCallback) {
721
- const protocol = this.getProtocol(chunk)
722
- const header = protocol.header(chunk)
723
- const payload = protocol.payload(chunk)
724
- const crc = protocol.crc(chunk)
730
+ _transform({ buffer = Buffer.from([]), timestamp = null, ...rest } = {}, encoding, callback: TransformCallback) {
731
+ const protocol = this.getProtocol(buffer)
732
+ const header = protocol.header(buffer, timestamp)
733
+ const payload = protocol.payload(buffer)
734
+ const crc = protocol.crc(buffer)
725
735
  const signature = protocol instanceof MavLinkProtocolV2
726
- ? protocol.signature(chunk, header)
736
+ ? protocol.signature(buffer, header)
727
737
  : null
728
738
 
729
- const packet = new MavLinkPacket(chunk, header, payload, crc, protocol, signature)
739
+ const packet = new MavLinkPacket(buffer, header, payload, crc, protocol, signature)
730
740
 
731
741
  callback(null, packet)
732
742
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mavlink",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "author": "Matthias Hryniszak <padcom@gmail.com>",
5
5
  "license": "LGPL",
6
6
  "description": "MavLink definitions and parsing library",
@@ -1 +0,0 @@
1
- {}
@@ -1,224 +0,0 @@
1
- body, html {
2
- margin:0; padding: 0;
3
- height: 100%;
4
- }
5
- body {
6
- font-family: Helvetica Neue, Helvetica, Arial;
7
- font-size: 14px;
8
- color:#333;
9
- }
10
- .small { font-size: 12px; }
11
- *, *:after, *:before {
12
- -webkit-box-sizing:border-box;
13
- -moz-box-sizing:border-box;
14
- box-sizing:border-box;
15
- }
16
- h1 { font-size: 20px; margin: 0;}
17
- h2 { font-size: 14px; }
18
- pre {
19
- font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20
- margin: 0;
21
- padding: 0;
22
- -moz-tab-size: 2;
23
- -o-tab-size: 2;
24
- tab-size: 2;
25
- }
26
- a { color:#0074D9; text-decoration:none; }
27
- a:hover { text-decoration:underline; }
28
- .strong { font-weight: bold; }
29
- .space-top1 { padding: 10px 0 0 0; }
30
- .pad2y { padding: 20px 0; }
31
- .pad1y { padding: 10px 0; }
32
- .pad2x { padding: 0 20px; }
33
- .pad2 { padding: 20px; }
34
- .pad1 { padding: 10px; }
35
- .space-left2 { padding-left:55px; }
36
- .space-right2 { padding-right:20px; }
37
- .center { text-align:center; }
38
- .clearfix { display:block; }
39
- .clearfix:after {
40
- content:'';
41
- display:block;
42
- height:0;
43
- clear:both;
44
- visibility:hidden;
45
- }
46
- .fl { float: left; }
47
- @media only screen and (max-width:640px) {
48
- .col3 { width:100%; max-width:100%; }
49
- .hide-mobile { display:none!important; }
50
- }
51
-
52
- .quiet {
53
- color: #7f7f7f;
54
- color: rgba(0,0,0,0.5);
55
- }
56
- .quiet a { opacity: 0.7; }
57
-
58
- .fraction {
59
- font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60
- font-size: 10px;
61
- color: #555;
62
- background: #E8E8E8;
63
- padding: 4px 5px;
64
- border-radius: 3px;
65
- vertical-align: middle;
66
- }
67
-
68
- div.path a:link, div.path a:visited { color: #333; }
69
- table.coverage {
70
- border-collapse: collapse;
71
- margin: 10px 0 0 0;
72
- padding: 0;
73
- }
74
-
75
- table.coverage td {
76
- margin: 0;
77
- padding: 0;
78
- vertical-align: top;
79
- }
80
- table.coverage td.line-count {
81
- text-align: right;
82
- padding: 0 5px 0 20px;
83
- }
84
- table.coverage td.line-coverage {
85
- text-align: right;
86
- padding-right: 10px;
87
- min-width:20px;
88
- }
89
-
90
- table.coverage td span.cline-any {
91
- display: inline-block;
92
- padding: 0 5px;
93
- width: 100%;
94
- }
95
- .missing-if-branch {
96
- display: inline-block;
97
- margin-right: 5px;
98
- border-radius: 3px;
99
- position: relative;
100
- padding: 0 4px;
101
- background: #333;
102
- color: yellow;
103
- }
104
-
105
- .skip-if-branch {
106
- display: none;
107
- margin-right: 10px;
108
- position: relative;
109
- padding: 0 4px;
110
- background: #ccc;
111
- color: white;
112
- }
113
- .missing-if-branch .typ, .skip-if-branch .typ {
114
- color: inherit !important;
115
- }
116
- .coverage-summary {
117
- border-collapse: collapse;
118
- width: 100%;
119
- }
120
- .coverage-summary tr { border-bottom: 1px solid #bbb; }
121
- .keyline-all { border: 1px solid #ddd; }
122
- .coverage-summary td, .coverage-summary th { padding: 10px; }
123
- .coverage-summary tbody { border: 1px solid #bbb; }
124
- .coverage-summary td { border-right: 1px solid #bbb; }
125
- .coverage-summary td:last-child { border-right: none; }
126
- .coverage-summary th {
127
- text-align: left;
128
- font-weight: normal;
129
- white-space: nowrap;
130
- }
131
- .coverage-summary th.file { border-right: none !important; }
132
- .coverage-summary th.pct { }
133
- .coverage-summary th.pic,
134
- .coverage-summary th.abs,
135
- .coverage-summary td.pct,
136
- .coverage-summary td.abs { text-align: right; }
137
- .coverage-summary td.file { white-space: nowrap; }
138
- .coverage-summary td.pic { min-width: 120px !important; }
139
- .coverage-summary tfoot td { }
140
-
141
- .coverage-summary .sorter {
142
- height: 10px;
143
- width: 7px;
144
- display: inline-block;
145
- margin-left: 0.5em;
146
- background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147
- }
148
- .coverage-summary .sorted .sorter {
149
- background-position: 0 -20px;
150
- }
151
- .coverage-summary .sorted-desc .sorter {
152
- background-position: 0 -10px;
153
- }
154
- .status-line { height: 10px; }
155
- /* yellow */
156
- .cbranch-no { background: yellow !important; color: #111; }
157
- /* dark red */
158
- .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159
- .low .chart { border:1px solid #C21F39 }
160
- .highlighted,
161
- .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162
- background: #C21F39 !important;
163
- }
164
- /* medium red */
165
- .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166
- /* light red */
167
- .low, .cline-no { background:#FCE1E5 }
168
- /* light green */
169
- .high, .cline-yes { background:rgb(230,245,208) }
170
- /* medium green */
171
- .cstat-yes { background:rgb(161,215,106) }
172
- /* dark green */
173
- .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174
- .high .chart { border:1px solid rgb(77,146,33) }
175
- /* dark yellow (gold) */
176
- .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
177
- .medium .chart { border:1px solid #f9cd0b; }
178
- /* light yellow */
179
- .medium { background: #fff4c2; }
180
-
181
- .cstat-skip { background: #ddd; color: #111; }
182
- .fstat-skip { background: #ddd; color: #111 !important; }
183
- .cbranch-skip { background: #ddd !important; color: #111; }
184
-
185
- span.cline-neutral { background: #eaeaea; }
186
-
187
- .coverage-summary td.empty {
188
- opacity: .5;
189
- padding-top: 4px;
190
- padding-bottom: 4px;
191
- line-height: 1;
192
- color: #888;
193
- }
194
-
195
- .cover-fill, .cover-empty {
196
- display:inline-block;
197
- height: 12px;
198
- }
199
- .chart {
200
- line-height: 0;
201
- }
202
- .cover-empty {
203
- background: white;
204
- }
205
- .cover-full {
206
- border-right: none !important;
207
- }
208
- pre.prettyprint {
209
- border: none !important;
210
- padding: 0 !important;
211
- margin: 0 !important;
212
- }
213
- .com { color: #999 !important; }
214
- .ignore-none { color: #999; font-weight: normal; }
215
-
216
- .wrapper {
217
- min-height: 100%;
218
- height: auto !important;
219
- height: 100%;
220
- margin: 0 auto -48px;
221
- }
222
- .footer, .push {
223
- height: 48px;
224
- }
@@ -1,87 +0,0 @@
1
- /* eslint-disable */
2
- var jumpToCode = (function init() {
3
- // Classes of code we would like to highlight in the file view
4
- var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
5
-
6
- // Elements to highlight in the file listing view
7
- var fileListingElements = ['td.pct.low'];
8
-
9
- // We don't want to select elements that are direct descendants of another match
10
- var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
11
-
12
- // Selecter that finds elements on the page to which we can jump
13
- var selector =
14
- fileListingElements.join(', ') +
15
- ', ' +
16
- notSelector +
17
- missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
18
-
19
- // The NodeList of matching elements
20
- var missingCoverageElements = document.querySelectorAll(selector);
21
-
22
- var currentIndex;
23
-
24
- function toggleClass(index) {
25
- missingCoverageElements
26
- .item(currentIndex)
27
- .classList.remove('highlighted');
28
- missingCoverageElements.item(index).classList.add('highlighted');
29
- }
30
-
31
- function makeCurrent(index) {
32
- toggleClass(index);
33
- currentIndex = index;
34
- missingCoverageElements.item(index).scrollIntoView({
35
- behavior: 'smooth',
36
- block: 'center',
37
- inline: 'center'
38
- });
39
- }
40
-
41
- function goToPrevious() {
42
- var nextIndex = 0;
43
- if (typeof currentIndex !== 'number' || currentIndex === 0) {
44
- nextIndex = missingCoverageElements.length - 1;
45
- } else if (missingCoverageElements.length > 1) {
46
- nextIndex = currentIndex - 1;
47
- }
48
-
49
- makeCurrent(nextIndex);
50
- }
51
-
52
- function goToNext() {
53
- var nextIndex = 0;
54
-
55
- if (
56
- typeof currentIndex === 'number' &&
57
- currentIndex < missingCoverageElements.length - 1
58
- ) {
59
- nextIndex = currentIndex + 1;
60
- }
61
-
62
- makeCurrent(nextIndex);
63
- }
64
-
65
- return function jump(event) {
66
- if (
67
- document.getElementById('fileSearch') === document.activeElement &&
68
- document.activeElement != null
69
- ) {
70
- // if we're currently focused on the search input, we don't want to navigate
71
- return;
72
- }
73
-
74
- switch (event.which) {
75
- case 78: // n
76
- case 74: // j
77
- goToNext();
78
- break;
79
- case 66: // b
80
- case 75: // k
81
- case 80: // p
82
- goToPrevious();
83
- break;
84
- }
85
- };
86
- })();
87
- window.addEventListener('keydown', jumpToCode);
Binary file