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.
- package/dist/lib/mavlink.d.ts +9 -4
- package/dist/lib/mavlink.js +1 -1
- package/examples/parse-tlog-file.ts +43 -0
- package/examples/vtol.tlog +0 -0
- package/lib/mavlink.ts +22 -12
- package/package.json +1 -1
- package/coverage/coverage-final.json +0 -1
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -101
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/serialization.ts.html +0 -613
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov.info +0 -0
package/dist/lib/mavlink.d.ts
CHANGED
@@ -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(
|
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
|
package/dist/lib/mavlink.js
CHANGED
@@ -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(
|
721
|
-
const protocol = this.getProtocol(
|
722
|
-
const header = protocol.header(
|
723
|
-
const payload = protocol.payload(
|
724
|
-
const crc = protocol.crc(
|
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(
|
736
|
+
? protocol.signature(buffer, header)
|
727
737
|
: null
|
728
738
|
|
729
|
-
const packet = new MavLinkPacket(
|
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 +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
|