node-red-contrib-ntrip 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,9 +19,9 @@ jobs:
19
19
  # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
20
20
 
21
21
  steps:
22
- - uses: actions/checkout@v4
22
+ - uses: actions/checkout@v7
23
23
  - name: Use Node.js ${{ matrix.node-version }}
24
- uses: actions/setup-node@v4
24
+ uses: actions/setup-node@v6
25
25
  with:
26
26
  node-version: ${{ matrix.node-version }}
27
27
  cache: 'npm'
@@ -23,9 +23,9 @@ jobs:
23
23
  matrix:
24
24
  node-version: [18.x, 20.x, 22.x]
25
25
  steps:
26
- - uses: actions/checkout@v4
26
+ - uses: actions/checkout@v7
27
27
  - name: Use Node.js ${{ matrix.node-version }}
28
- uses: actions/setup-node@v4
28
+ uses: actions/setup-node@v6
29
29
  with:
30
30
  node-version: ${{ matrix.node-version }}
31
31
  cache: 'npm'
@@ -38,8 +38,8 @@ jobs:
38
38
  needs: test
39
39
  runs-on: ubuntu-latest
40
40
  steps:
41
- - uses: actions/checkout@v4
42
- - uses: actions/setup-node@v4
41
+ - uses: actions/checkout@v7
42
+ - uses: actions/setup-node@v6
43
43
  with:
44
44
  node-version: 20
45
45
  registry-url: 'https://registry.npmjs.org/'
@@ -54,11 +54,11 @@ jobs:
54
54
  permissions:
55
55
  contents: write
56
56
  steps:
57
- - uses: actions/checkout@v4
57
+ - uses: actions/checkout@v7
58
58
  with:
59
59
  fetch-depth: 0
60
60
  - name: Create GitHub Release
61
- uses: softprops/action-gh-release@v2
61
+ uses: softprops/action-gh-release@v3
62
62
  with:
63
63
  tag_name: ${{ github.ref_name }}
64
64
  name: ${{ github.ref_name }}
package/CHANGELOG.md CHANGED
@@ -1,6 +1,26 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [0.2.9]
5
+ ### NtripClient
6
+ - Status badge now shows the inbound data rate alongside the Rx/Tx message counters (e.g. `2.4 kbps Rx 123 Tx 4`). The rate is sampled once per second and formatted as `bps`, `kbps`, or `Mbps` depending on magnitude.
7
+
8
+ ### Internal — dependency bumps (dev tree, no runtime changes)
9
+ - `node-red` 4.1.11 → 5.0.1 (via the sigstore/node-red bundle).
10
+ - `eslint` 10.4.1 → 10.6.0.
11
+ - `prettier` 3.8.3 → 3.9.3.
12
+ - `mocha` 11.7.5 → 11.7.6.
13
+ - `globals` 17.6.0 → 17.7.0.
14
+ - `actions/checkout` v6 → v7 in the CI + release workflows.
15
+
16
+ ## [0.2.8]
17
+ ### Added RTCM Encoder node
18
+ - New `RtcmEncoder` node — inverse of `RtcmDecoder`. Accepts an `RtcmMessage` instance (or `msg.payload.message` from the decoder) and emits the encoded RTCM 3 frame as `msg.payload.rtcmMessage` (Buffer).
19
+ - Designed for round-trip flows: decode, optionally mutate, then re-encode for forwarding.
20
+ - Does not construct messages from plain fields (unlike the NMEA encoder) — there are 187 RTCM message types in `@gnss/rtcm` and per-type constructors are rarely needed for real flows.
21
+ - New example flow `examples/rtcm-encode.json` demonstrating the decode → encode round-trip.
22
+ - 6 new specs in `test/rtcm-encoder.spec.js` covering: round-trip from decoder instance, decoder-output-shape input, non-`RtcmMessage` rejected to error output, empty payload rejected, no `node.error` flood on bad input, fixture sanity.
23
+
4
24
  ## [0.2.7]
5
25
  ### Internal — tooling, tests, docs (no runtime changes)
6
26
  - Added a mocha test suite (14 specs across `NmeaDecoder`, `NmeaEncoder`, `RtcmDecoder`) using `node-red-node-test-helper`. Regression specs are tagged so they can be re-run with `mocha -g regression`.
package/README.md CHANGED
@@ -155,6 +155,46 @@ msg.payload =
155
155
  };
156
156
  ```
157
157
 
158
+ # RTCM Encoder Node
159
+ Encodes an `RtcmMessage` instance back into a binary RTCM 3 frame using the
160
+ `@gnss/rtcm` library. The inverse of the RTCM Decoder — useful for re-emitting
161
+ a decoded frame after modification (e.g. rewriting a station ID before
162
+ forwarding) or for synthesising test fixtures.
163
+
164
+ The node has **two outputs**: encoded frames go to the first, failures to the
165
+ second.
166
+
167
+ This encoder does **not** construct an `RtcmMessage` from plain fields. The
168
+ input must already be an instance — typically `msg.payload.message` from the
169
+ RTCM Decoder.
170
+
171
+ Input shape:
172
+ ```javascript
173
+ // Direct: pass an RtcmMessage instance
174
+ msg.payload = <RtcmMessage instance>;
175
+
176
+ // Or pipe the decoder's success output straight through:
177
+ msg.payload = {
178
+ rtcm, // number — echoed back
179
+ messageType, // string — echoed back
180
+ message, // RtcmMessage instance — what gets encoded
181
+ input // ignored
182
+ };
183
+ ```
184
+
185
+ Successful output:
186
+ ```javascript
187
+ msg.payload =
188
+ {
189
+ rtcmMessage, // Buffer — the encoded RTCM 3 frame, ready to forward
190
+ rtcm, // number — the RTCM message type identifier
191
+ messageType, // string — the constructor name
192
+ input, // the original input as supplied
193
+ };
194
+ ```
195
+
196
+ See also example flow [**RTCM encode flow**](examples/rtcm-encode.json).
197
+
158
198
  # NMEA Decoder Node
159
199
  This node accepts NMEA 0183 sentences as a string or `Buffer`. Multiple
160
200
  sentences in a single input (delimited by `\r\n`) are split and decoded
@@ -252,8 +292,10 @@ A few things that are easy to get wrong:
252
292
  interleaved with bytes received from the caster. Downstream nodes will see
253
293
  both streams on the same wire — distinguish them with a tag in a function
254
294
  node if you need to.
255
- - **NTRIP Client: status badge.** `Rx N Tx M` counts inbound and outbound
256
- messages separately. Handshake replies do not bump the inbound count.
295
+ - **NTRIP Client: status badge.** `<rate> Rx N Tx M` shows the inbound data
296
+ rate (sampled over a 1-second window; formatted as `bps`, `kbps`, or `Mbps`)
297
+ followed by the inbound and outbound message counts. Handshake replies do
298
+ not bump the inbound count.
257
299
  - **NTRIP Client: CR/LF in credentials.** `mountpoint`, `username`, and
258
300
  `password` are interpolated into the handshake string. CR/LF in these fields
259
301
  is rejected at startup to prevent header injection.
@@ -0,0 +1 @@
1
+ [{"id":"rtcm-encode-tab","type":"tab","label":"RTCM encode round-trip","disabled":false,"info":"Decode a known RTCM 1005 frame, then re-encode it via the RTCM Encoder node.\nWatch the two debug nodes — the encoded output should match the original bytes.\n"},{"id":"inj-1","type":"inject","z":"rtcm-encode-tab","name":"Trigger","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":140,"wires":[["fn-load-rtcm"]]},{"id":"fn-load-rtcm","type":"function","z":"rtcm-encode-tab","name":"Load RTCM 1005 sample","func":"// A real RTCM 3 type 1005 (Stationary RTK Reference Station ARP) frame.\nmsg.payload = Buffer.from('D300133ED7D30202980EDEEF34B4BD62AC0941986F33360B98', 'hex');\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":140,"wires":[["decoder","dbg-raw"]]},{"id":"dbg-raw","type":"debug","z":"rtcm-encode-tab","name":"Raw RTCM bytes","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":540,"y":80,"wires":[]},{"id":"decoder","type":"RtcmDecoder","z":"rtcm-encode-tab","description":"","x":540,"y":140,"wires":[["encoder","dbg-decoded"],["dbg-err"]]},{"id":"dbg-decoded","type":"debug","z":"rtcm-encode-tab","name":"Decoded frame","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":740,"y":80,"wires":[]},{"id":"encoder","type":"RtcmEncoder","z":"rtcm-encode-tab","description":"","x":740,"y":140,"wires":[["dbg-encoded"],["dbg-err"]]},{"id":"dbg-encoded","type":"debug","z":"rtcm-encode-tab","name":"Re-encoded RTCM","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":940,"y":140,"wires":[]},{"id":"dbg-err","type":"debug","z":"rtcm-encode-tab","name":"decode/encode error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":740,"y":220,"wires":[]}]
@@ -209,8 +209,11 @@
209
209
  <h3>Status</h3>
210
210
  <p>The node shows <code>connecting…</code> until the caster acknowledges the
211
211
  handshake with <code>ICY 200 OK</code>. After that it shows the running
212
- <code>Rx</code> / <code>Tx</code> counters. A red badge indicates a rejected
213
- connection, a missing mountpoint, or a write failure.</p>
212
+ <code>Rx</code> / <code>Tx</code> message counters plus the inbound data
213
+ rate in <code>bps</code> / <code>kbps</code> / <code>Mbps</code>, sampled
214
+ once per second over a 1-second window
215
+ (e.g. <code>2.4 kbps Rx 123 Tx 4</code>). A red badge indicates a
216
+ rejected connection, a missing mountpoint, or a write failure.</p>
214
217
 
215
218
  <h3>Details</h3>
216
219
  <p>The first <code>ICY 200 OK</code>, <code>ICY 406</code>, or
@@ -301,6 +304,88 @@
301
304
 
302
305
  <!-- ------------------------------------------------------------------------------------------ -->
303
306
 
307
+ <script type="text/javascript">
308
+ RED.nodes.registerType('RtcmEncoder', {
309
+ category: 'GNSS',
310
+ color: '#C0DEED',
311
+ defaults: {
312
+ description: { value:"" },
313
+ },
314
+ inputs: 1,
315
+ outputs: 2,
316
+ icon: "function.png",
317
+ paletteLabel: "RTCM Encoder",
318
+ label: function () {
319
+ return this.description || "RTCM Encoder";
320
+ },
321
+ labelStyle: function() {
322
+ return this.description?"node_label_italic":"";
323
+ }
324
+ });
325
+
326
+ </script>
327
+
328
+ <script type="text/x-red" data-template-name="RtcmEncoder">
329
+ <div class="form-row">
330
+ <label for="node-input-description"><i class="fa fa-comment"></i> Description</label>
331
+ <input type="text" id="node-input-description" placeholder="The description of the node (optional)">
332
+ </div>
333
+ </script>
334
+
335
+ <script type="text/x-red" data-help-name="RtcmEncoder">
336
+ <p>Encodes an <code>RtcmMessage</code> instance back into a binary RTCM 3
337
+ frame using the <code>@gnss/rtcm</code> library. The inverse of the
338
+ <i>RTCM Decoder</i> — useful for re-emitting a decoded frame after
339
+ modification (e.g. rewriting a station ID before forwarding upstream).</p>
340
+ <p>This encoder does not construct an <code>RtcmMessage</code> from plain
341
+ fields. The input must already be an instance — typically the
342
+ <code>msg.payload.message</code> emitted by the decoder.</p>
343
+
344
+ <h3>Configuration</h3>
345
+ <dl class="message-properties">
346
+ <dt>Description <span class="property-type">string</span></dt>
347
+ <dd>Optional label shown in the flow editor.</dd>
348
+ </dl>
349
+
350
+ <h3>Inputs</h3>
351
+ <dl class="message-properties">
352
+ <dt>payload <span class="property-type">RtcmMessage</span></dt>
353
+ <dd>An <code>RtcmMessage</code> instance, encoded directly.</dd>
354
+ <dt>payload.message <span class="property-type">RtcmMessage</span></dt>
355
+ <dd>Round-trip shape from the <i>RTCM Decoder</i> — the encoder picks
356
+ up <code>msg.payload.message</code> and encodes that.</dd>
357
+ </dl>
358
+
359
+ <h3>Outputs</h3>
360
+ <p>Two outputs: successfully encoded frames go to the first; failures go to the second.</p>
361
+ <ol class="node-ports">
362
+ <li>Encoded
363
+ <dl class="message-properties">
364
+ <dt>payload.rtcmMessage <span class="property-type">Buffer</span></dt>
365
+ <dd>The encoded RTCM 3 frame, ready to forward to an NTRIP
366
+ uploader or a TCP sink.</dd>
367
+ <dt>payload.rtcm <span class="property-type">number</span></dt>
368
+ <dd>The RTCM message type identifier (e.g. <code>1005</code>).</dd>
369
+ <dt>payload.messageType <span class="property-type">string</span></dt>
370
+ <dd>The constructor name (e.g. <code>StationArp</code>).</dd>
371
+ <dt>payload.input <span class="property-type">any</span></dt>
372
+ <dd>The original input as supplied.</dd>
373
+ </dl>
374
+ </li>
375
+ <li>Error
376
+ <dl class="message-properties">
377
+ <dt>payload.error <span class="property-type">Error | string</span></dt>
378
+ <dd>The exception thrown by the encoder, or a string describing
379
+ invalid input.</dd>
380
+ <dt>payload.input <span class="property-type">any</span></dt>
381
+ <dd>The offending input.</dd>
382
+ </dl>
383
+ </li>
384
+ </ol>
385
+ </script>
386
+
387
+ <!-- ------------------------------------------------------------------------------------------ -->
388
+
304
389
  <script type="text/javascript">
305
390
  RED.nodes.registerType('NmeaDecoder', {
306
391
  category: 'GNSS',
package/ntrip/99-ntrip.js CHANGED
@@ -19,6 +19,9 @@ module.exports = function (RED) {
19
19
  const RtcmDecoderNode = require('./nodes/rtcm-decoder-node.js')(RED);
20
20
  RED.nodes.registerType('RtcmDecoder', RtcmDecoderNode);
21
21
 
22
+ const RtcmEncoderNode = require('./nodes/rtcm-encoder-node.js')(RED);
23
+ RED.nodes.registerType('RtcmEncoder', RtcmEncoderNode);
24
+
22
25
  const NmeaDecoderNode = require('./nodes/nmea-decoder-node.js')(RED);
23
26
  RED.nodes.registerType('NmeaDecoder', NmeaDecoderNode);
24
27
 
@@ -7,11 +7,23 @@ module.exports = function (RED) {
7
7
  const NtripServerMissingMountpoint = 'SOURCETABLE 200 OK';
8
8
  const HandshakePrefixBytes = 64;
9
9
 
10
+ function formatBps(bps) {
11
+ if (bps < 1000) {
12
+ return Math.round(bps) + ' bps';
13
+ }
14
+ if (bps < 1000000) {
15
+ return (bps / 1000).toFixed(1) + ' kbps';
16
+ }
17
+ return (bps / 1000000).toFixed(2) + ' Mbps';
18
+ }
19
+
10
20
  function NtripClientNode(config) {
11
21
  RED.nodes.createNode(this, config);
12
22
  let node = this;
13
23
  node.messagesReceived = 0;
14
24
  node.messagesSent = 0;
25
+ node.bytesAccum = 0;
26
+ node.currentBps = 0;
15
27
  node.passthrough = config.passthrough || false;
16
28
 
17
29
  let host = typeof config.host === 'string' ? config.host.trim() : '';
@@ -70,11 +82,21 @@ module.exports = function (RED) {
70
82
  node.status({
71
83
  fill: 'green',
72
84
  shape: 'ring',
73
- text: 'Rx ' + node.messagesReceived + ' Tx ' + node.messagesSent,
85
+ text: formatBps(node.currentBps) + ' Rx ' + node.messagesReceived + ' Tx ' + node.messagesSent,
74
86
  });
75
87
  }
76
88
 
89
+ // Sample inbound byte counter once per second to compute the bps shown in the
90
+ // status badge. Counter is reset each tick — currentBps reflects the most
91
+ // recent 1-second window.
92
+ const bpsSampler = setInterval(() => {
93
+ node.currentBps = node.bytesAccum * 8;
94
+ node.bytesAccum = 0;
95
+ updateStatus();
96
+ }, 1000);
97
+
77
98
  client.on('data', (data) => {
99
+ node.bytesAccum += data.length;
78
100
  if (!connected) {
79
101
  let prefix = data.toString('utf8', 0, Math.min(data.length, HandshakePrefixBytes));
80
102
  if (prefix.startsWith(NtripServerOkReply)) {
@@ -163,6 +185,7 @@ module.exports = function (RED) {
163
185
  });
164
186
 
165
187
  this.on('close', function (done) {
188
+ clearInterval(bpsSampler);
166
189
  try {
167
190
  client.removeAllListeners();
168
191
  client.close();
@@ -0,0 +1,90 @@
1
+ module.exports = function (RED) {
2
+ 'use strict';
3
+
4
+ const { RtcmTransport, RtcmMessage } = require('@gnss/rtcm');
5
+
6
+ // Maximum RTCM 3 frame size — 3-byte header + 1023-byte payload + 3-byte CRC.
7
+ const MaxFrameBytes = 1029;
8
+
9
+ function RtcmEncoderNode(config) {
10
+ RED.nodes.createNode(this, config);
11
+ let node = this;
12
+ node.rtcmMessagesReceived = 0;
13
+ node.invalidMessagesReceived = 0;
14
+
15
+ function updateStatus(ok) {
16
+ node.status({
17
+ fill: ok ? 'green' : 'red',
18
+ shape: 'ring',
19
+ text: 'RTCM: ' + node.rtcmMessagesReceived + ' Invalid: ' + node.invalidMessagesReceived,
20
+ });
21
+ }
22
+
23
+ this.on('input', function (msg) {
24
+ let payload = msg.payload;
25
+
26
+ // Accept either an RtcmMessage instance directly as msg.payload, or the
27
+ // shape emitted by the RtcmDecoder (msg.payload.message). Anything else
28
+ // routes to the error output.
29
+ let message;
30
+ if (payload instanceof RtcmMessage) {
31
+ message = payload;
32
+ } else if (payload != null && payload.message instanceof RtcmMessage) {
33
+ message = payload.message;
34
+ } else {
35
+ node.send([
36
+ null,
37
+ {
38
+ payload: {
39
+ error: 'Invalid input. Expected an RtcmMessage instance as msg.payload, or msg.payload.message.',
40
+ input: payload,
41
+ },
42
+ },
43
+ ]);
44
+ node.invalidMessagesReceived++;
45
+ updateStatus(false);
46
+ return;
47
+ }
48
+
49
+ try {
50
+ let buffer = Buffer.alloc(MaxFrameBytes);
51
+ let length = RtcmTransport.encode(message, buffer);
52
+ let encoded = buffer.slice(0, length);
53
+ let messageType = message.constructor.name.replace('RtcmMessage', '');
54
+
55
+ node.send([
56
+ {
57
+ payload: {
58
+ rtcmMessage: encoded,
59
+ rtcm: message.messageType,
60
+ messageType: messageType,
61
+ input: payload,
62
+ },
63
+ },
64
+ null,
65
+ ]);
66
+ node.rtcmMessagesReceived++;
67
+ updateStatus(true);
68
+ } catch (ex) {
69
+ node.send([
70
+ null,
71
+ {
72
+ payload: {
73
+ error: ex,
74
+ input: payload,
75
+ },
76
+ },
77
+ ]);
78
+ node.invalidMessagesReceived++;
79
+ updateStatus(false);
80
+ }
81
+ });
82
+
83
+ this.on('close', function (done) {
84
+ node.status({});
85
+ done();
86
+ });
87
+ }
88
+
89
+ return RtcmEncoderNode;
90
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-ntrip",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Node for ntrip and rtcm handling.",
5
5
  "node-red": {
6
6
  "version": ">=0.1.0",
@@ -43,7 +43,7 @@
43
43
  "eslint-config-prettier": "^10.1.8",
44
44
  "globals": "^17.6.0",
45
45
  "mocha": "^11.7.5",
46
- "node-red": "^4.1.10",
46
+ "node-red": "^5.0.1",
47
47
  "node-red-node-test-helper": "^0.3.6",
48
48
  "prettier": "^3.8.3"
49
49
  }
@@ -0,0 +1,144 @@
1
+ 'use strict';
2
+
3
+ const helper = require('node-red-node-test-helper');
4
+ const { expect } = require('chai');
5
+ const { RtcmTransport, RtcmMessage } = require('@gnss/rtcm');
6
+ const ntripModule = require('../ntrip/99-ntrip.js');
7
+
8
+ helper.init(require.resolve('node-red'));
9
+
10
+ // A real RTCM 3 message type 1005 (Stationary RTK Reference Station ARP).
11
+ const RTCM_1005 = Buffer.from('D300133ED7D30202980EDEEF34B4BD62AC0941986F33360B98', 'hex');
12
+
13
+ function flow() {
14
+ return [
15
+ { id: 'n1', type: 'RtcmEncoder', wires: [['ok'], ['err']] },
16
+ { id: 'ok', type: 'helper' },
17
+ { id: 'err', type: 'helper' },
18
+ ];
19
+ }
20
+
21
+ describe('RtcmEncoder', function () {
22
+ before(() => helper.startServer());
23
+ after(() => helper.stopServer());
24
+ afterEach(() => helper.unload());
25
+
26
+ it('encodes an RtcmMessage instance round-tripped from the decoder', function () {
27
+ return new Promise((resolve, reject) => {
28
+ helper.load(ntripModule, flow(), () => {
29
+ const n1 = helper.getNode('n1');
30
+ const ok = helper.getNode('ok');
31
+ const [instance] = RtcmTransport.decode(RTCM_1005);
32
+ ok.on('input', (msg) => {
33
+ try {
34
+ expect(msg.payload.rtcm).to.equal(1005);
35
+ expect(msg.payload.messageType).to.be.a('string');
36
+ expect(Buffer.isBuffer(msg.payload.rtcmMessage)).to.equal(true);
37
+ expect(msg.payload.rtcmMessage).to.deep.equal(RTCM_1005);
38
+ resolve();
39
+ } catch (e) {
40
+ reject(e);
41
+ }
42
+ });
43
+ n1.receive({ payload: instance });
44
+ });
45
+ });
46
+ });
47
+
48
+ it('accepts the decoder output shape (msg.payload.message)', function () {
49
+ return new Promise((resolve, reject) => {
50
+ helper.load(ntripModule, flow(), () => {
51
+ const n1 = helper.getNode('n1');
52
+ const ok = helper.getNode('ok');
53
+ const [instance, length] = RtcmTransport.decode(RTCM_1005);
54
+ ok.on('input', (msg) => {
55
+ try {
56
+ expect(msg.payload.rtcm).to.equal(1005);
57
+ expect(Buffer.isBuffer(msg.payload.rtcmMessage)).to.equal(true);
58
+ expect(msg.payload.rtcmMessage.length).to.equal(length);
59
+ resolve();
60
+ } catch (e) {
61
+ reject(e);
62
+ }
63
+ });
64
+ // Mimic the RtcmDecoder's success payload shape.
65
+ n1.receive({
66
+ payload: {
67
+ rtcm: instance.messageType,
68
+ messageType: 'StationArp',
69
+ message: instance,
70
+ input: RTCM_1005,
71
+ },
72
+ });
73
+ });
74
+ });
75
+ });
76
+
77
+ it('routes a non-RtcmMessage payload to the error output', function () {
78
+ return new Promise((resolve, reject) => {
79
+ helper.load(ntripModule, flow(), () => {
80
+ const n1 = helper.getNode('n1');
81
+ const ok = helper.getNode('ok');
82
+ const err = helper.getNode('err');
83
+ ok.on('input', () => reject(new Error('unexpected success output')));
84
+ err.on('input', (msg) => {
85
+ try {
86
+ expect(msg.payload.error).to.be.a('string');
87
+ expect(msg.payload.error.toLowerCase()).to.include('invalid');
88
+ resolve();
89
+ } catch (e) {
90
+ reject(e);
91
+ }
92
+ });
93
+ n1.receive({ payload: { foo: 'bar' } });
94
+ });
95
+ });
96
+ });
97
+
98
+ it('routes empty payload to the error output', function () {
99
+ return new Promise((resolve, reject) => {
100
+ helper.load(ntripModule, flow(), () => {
101
+ const n1 = helper.getNode('n1');
102
+ const err = helper.getNode('err');
103
+ err.on('input', (msg) => {
104
+ try {
105
+ expect(msg.payload.error).to.exist;
106
+ resolve();
107
+ } catch (e) {
108
+ reject(e);
109
+ }
110
+ });
111
+ n1.receive({ payload: null });
112
+ });
113
+ });
114
+ });
115
+
116
+ it('does not call node.error on decode-shape failure (regression: log flood)', function () {
117
+ return new Promise((resolve, reject) => {
118
+ helper.load(ntripModule, flow(), () => {
119
+ const n1 = helper.getNode('n1');
120
+ let errorCalled = false;
121
+ const originalError = n1.error.bind(n1);
122
+ n1.error = function (...args) {
123
+ errorCalled = true;
124
+ return originalError(...args);
125
+ };
126
+ n1.receive({ payload: 'not-an-rtcm-message' });
127
+ setTimeout(() => {
128
+ try {
129
+ expect(errorCalled).to.equal(false);
130
+ resolve();
131
+ } catch (e) {
132
+ reject(e);
133
+ }
134
+ }, 50);
135
+ });
136
+ });
137
+ });
138
+
139
+ // Sanity check: RtcmMessage is exported and the test fixture decodes to an instance.
140
+ it('test fixture sanity: decoded 1005 frame is an RtcmMessage instance', function () {
141
+ const [instance] = RtcmTransport.decode(RTCM_1005);
142
+ expect(instance).to.be.instanceOf(RtcmMessage);
143
+ });
144
+ });