index.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.SpacePacketParser = void 0;
  4. const stream_1 = require("stream");
  5. const utils_1 = require("./utils");
  6. /**
  7. * A Transform stream that accepts a stream of octet data and converts it into an object
  8. * representation of a CCSDS Space Packet. See https://public.ccsds.org/Pubs/133x0b2e1.pdf for a
  9. * description of the Space Packet format.
  10. */
  11. class SpacePacketParser extends stream_1.Transform {
  12. timeCodeFieldLength;
  13. ancillaryDataFieldLength;
  14. dataBuffer;
  15. headerBuffer;
  16. dataLength;
  17. expectingHeader;
  18. dataSlice;
  19. header;
  20. /**
  21. * A Transform stream that accepts a stream of octet data and emits object representations of
  22. * CCSDS Space Packets once a packet has been completely received.
  23. * @param {Object} [options] Configuration options for the stream
  24. * @param {Number} options.timeCodeFieldLength The length of the time code field within the data
  25. * @param {Number} options.ancillaryDataFieldLength The length of the ancillary data field within the data
  26. */
  27. constructor(options = {}) {
  28. super({ ...options, objectMode: true });
  29. // Set the constants for this Space Packet Connection; these will help us parse incoming data
  30. // fields:
  31. this.timeCodeFieldLength = options.timeCodeFieldLength || 0;
  32. this.ancillaryDataFieldLength = options.ancillaryDataFieldLength || 0;
  33. this.dataSlice = this.timeCodeFieldLength + this.ancillaryDataFieldLength;
  34. // These are stateful based on the current packet being received:
  35. this.dataBuffer = Buffer.alloc(0);
  36. this.headerBuffer = Buffer.alloc(0);
  37. this.dataLength = 0;
  38. this.expectingHeader = true;
  39. }
  40. /**
  41. * Bundle the header, secondary header if present, and the data into a JavaScript object to emit.
  42. * If more data has been received past the current packet, begin the process of parsing the next
  43. * packet(s).
  44. */
  45. pushCompletedPacket() {
  46. if (!this.header) {
  47. throw new Error('Missing header');
  48. }
  49. const timeCode = Buffer.from(this.dataBuffer.slice(0, this.timeCodeFieldLength));
  50. const ancillaryData = Buffer.from(this.dataBuffer.slice(this.timeCodeFieldLength, this.timeCodeFieldLength + this.ancillaryDataFieldLength));
  51. const data = Buffer.from(this.dataBuffer.slice(this.dataSlice, this.dataLength));
  52. const completedPacket = {
  53. header: { ...this.header },
  54. data: data.toString(),
  55. };
  56. if (timeCode.length > 0 || ancillaryData.length > 0) {
  57. completedPacket.secondaryHeader = {};
  58. if (timeCode.length) {
  59. completedPacket.secondaryHeader.timeCode = timeCode.toString();
  60. }
  61. if (ancillaryData.length) {
  62. completedPacket.secondaryHeader.ancillaryData = ancillaryData.toString();
  63. }
  64. }
  65. this.push(completedPacket);
  66. // If there is an overflow (i.e. we have more data than the packet we just pushed) begin parsing
  67. // the next packet.
  68. const nextChunk = Buffer.from(this.dataBuffer.slice(this.dataLength));
  69. if (nextChunk.length >= utils_1.HEADER_LENGTH) {
  70. this.extractHeader(nextChunk);
  71. }
  72. else {
  73. this.headerBuffer = nextChunk;
  74. this.dataBuffer = Buffer.alloc(0);
  75. this.expectingHeader = true;
  76. this.dataLength = 0;
  77. this.header = undefined;
  78. }
  79. }
  80. /**
  81. * Build the Stream's headerBuffer property from the received Buffer chunk; extract data from it
  82. * if it's complete. If there's more to the chunk than just the header, initiate handling the
  83. * packet data.
  84. * @param chunk - Build the Stream's headerBuffer property from
  85. */
  86. extractHeader(chunk) {
  87. const headerAsBuffer = Buffer.concat([this.headerBuffer, chunk]);
  88. const startOfDataBuffer = headerAsBuffer.slice(utils_1.HEADER_LENGTH);
  89. if (headerAsBuffer.length >= utils_1.HEADER_LENGTH) {
  90. this.header = (0, utils_1.convertHeaderBufferToObj)(headerAsBuffer);
  91. this.dataLength = this.header.dataLength;
  92. this.headerBuffer = Buffer.alloc(0);
  93. this.expectingHeader = false;
  94. }
  95. else {
  96. this.headerBuffer = headerAsBuffer;
  97. }
  98. if (startOfDataBuffer.length > 0) {
  99. this.dataBuffer = Buffer.from(startOfDataBuffer);
  100. if (this.dataBuffer.length >= this.dataLength) {
  101. this.pushCompletedPacket();
  102. }
  103. }
  104. }
  105. _transform(chunk, encoding, cb) {
  106. if (this.expectingHeader) {
  107. this.extractHeader(chunk);
  108. }
  109. else {
  110. this.dataBuffer = Buffer.concat([this.dataBuffer, chunk]);
  111. if (this.dataBuffer.length >= this.dataLength) {
  112. this.pushCompletedPacket();
  113. }
  114. }
  115. cb();
  116. }
  117. _flush(cb) {
  118. const remaining = Buffer.concat([this.headerBuffer, this.dataBuffer]);
  119. const remainingArray = Array.from(remaining);
  120. this.push(remainingArray);
  121. cb();
  122. }
  123. }
  124. exports.SpacePacketParser = SpacePacketParser;