@canboat/canboatjs 3.19.0-beta.1 → 3.19.0-beta.2
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.
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/n2kDevice.d.ts.map +1 -1
- package/dist/n2kDevice.js +58 -5
- package/dist/n2kDevice.js.map +1 -1
- package/dist/n2kDevice.test.d.ts +2 -0
- package/dist/n2kDevice.test.d.ts.map +1 -0
- package/dist/n2kDevice.test.js +154 -0
- package/dist/n2kDevice.test.js.map +1 -0
- package/dist/n2kIpGateway.d.ts +40 -0
- package/dist/n2kIpGateway.d.ts.map +1 -0
- package/dist/n2kIpGateway.js +464 -0
- package/dist/n2kIpGateway.js.map +1 -0
- package/dist/n2kIpGateway.test.d.ts +2 -0
- package/dist/n2kIpGateway.test.d.ts.map +1 -0
- package/dist/n2kIpGateway.test.js +349 -0
- package/dist/n2kIpGateway.test.js.map +1 -0
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const events_1 = require("events");
|
|
4
|
+
// A minimal stand-in for net.Socket. The constructor (called via `new
|
|
5
|
+
// net.Socket()` inside the gateway) hands the test a reference via the global
|
|
6
|
+
// hook below so the test can drive its data events and inspect everything
|
|
7
|
+
// that's written to it.
|
|
8
|
+
class MockSocket extends events_1.EventEmitter {
|
|
9
|
+
written = [];
|
|
10
|
+
connectedTo;
|
|
11
|
+
destroyed = false;
|
|
12
|
+
connect(port, host, cb) {
|
|
13
|
+
this.connectedTo = { port, host };
|
|
14
|
+
// Defer to the next tick so listeners attached after .connect() still see
|
|
15
|
+
// subsequent events.
|
|
16
|
+
setImmediate(cb);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
write(chunk) {
|
|
20
|
+
this.written.push(typeof chunk === 'string' ? chunk : chunk.toString('utf8'));
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
end() { }
|
|
24
|
+
destroy() {
|
|
25
|
+
this.destroyed = true;
|
|
26
|
+
}
|
|
27
|
+
// Test helpers
|
|
28
|
+
feed(data) {
|
|
29
|
+
this.emit('data', Buffer.from(data, 'utf8'));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
let lastSocket;
|
|
33
|
+
jest.mock('net', () => ({
|
|
34
|
+
Socket: jest.fn().mockImplementation(() => {
|
|
35
|
+
lastSocket = new MockSocket();
|
|
36
|
+
return lastSocket;
|
|
37
|
+
})
|
|
38
|
+
}));
|
|
39
|
+
// Avoid touching the real on-disk persisted unique number when CanDevice
|
|
40
|
+
// starts up — it writes/reads from process cwd which we don't want in tests.
|
|
41
|
+
jest.mock('./persist', () => ({
|
|
42
|
+
getPersistedData: jest.fn(() => undefined),
|
|
43
|
+
savePersistedData: jest.fn()
|
|
44
|
+
}));
|
|
45
|
+
const n2kIpGateway_1 = require("./n2kIpGateway");
|
|
46
|
+
function makeApp() {
|
|
47
|
+
const app = new events_1.EventEmitter();
|
|
48
|
+
app.setProviderStatus = jest.fn();
|
|
49
|
+
app.setProviderError = jest.fn();
|
|
50
|
+
return app;
|
|
51
|
+
}
|
|
52
|
+
async function waitForConnect() {
|
|
53
|
+
// setImmediate inside MockSocket.connect — flush microtasks + one macrotask.
|
|
54
|
+
await new Promise((r) => setImmediate(r));
|
|
55
|
+
}
|
|
56
|
+
describe('N2kIpGateway', () => {
|
|
57
|
+
const created = [];
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
lastSocket = undefined;
|
|
60
|
+
created.length = 0;
|
|
61
|
+
});
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
// Tear down any gateways created during the test so their CanDevice's
|
|
64
|
+
// setTimeout(0) for address-claim and any reconnect timers don't keep
|
|
65
|
+
// the Jest event loop alive.
|
|
66
|
+
while (created.length) {
|
|
67
|
+
const gw = created.pop();
|
|
68
|
+
try {
|
|
69
|
+
gw.end();
|
|
70
|
+
}
|
|
71
|
+
catch (_e) {
|
|
72
|
+
// ignore
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
function newGateway(opts) {
|
|
77
|
+
const gw = new n2kIpGateway_1.N2kIpGateway(opts);
|
|
78
|
+
created.push(gw);
|
|
79
|
+
return gw;
|
|
80
|
+
}
|
|
81
|
+
test('requires options.host', () => {
|
|
82
|
+
expect(() => new n2kIpGateway_1.N2kIpGateway({})).toThrow(/host/);
|
|
83
|
+
});
|
|
84
|
+
test('rejects unknown format', () => {
|
|
85
|
+
expect(() => new n2kIpGateway_1.N2kIpGateway({
|
|
86
|
+
app: makeApp(),
|
|
87
|
+
host: 'h',
|
|
88
|
+
format: 'totally-fake',
|
|
89
|
+
actAsCanDevice: false
|
|
90
|
+
})).toThrow(/unsupported format/);
|
|
91
|
+
});
|
|
92
|
+
test('connects to default port 2599', async () => {
|
|
93
|
+
newGateway({
|
|
94
|
+
app: makeApp(),
|
|
95
|
+
host: 'gw.local',
|
|
96
|
+
actAsCanDevice: false
|
|
97
|
+
});
|
|
98
|
+
await waitForConnect();
|
|
99
|
+
expect(lastSocket.connectedTo).toEqual({ port: 2599, host: 'gw.local' });
|
|
100
|
+
});
|
|
101
|
+
test('honors explicit port', async () => {
|
|
102
|
+
newGateway({
|
|
103
|
+
app: makeApp(),
|
|
104
|
+
host: 'gw.local',
|
|
105
|
+
port: 9999,
|
|
106
|
+
actAsCanDevice: false
|
|
107
|
+
});
|
|
108
|
+
await waitForConnect();
|
|
109
|
+
expect(lastSocket.connectedTo).toEqual({ port: 9999, host: 'gw.local' });
|
|
110
|
+
});
|
|
111
|
+
test('RX candump3: parses a line and pushes a frame downstream', async () => {
|
|
112
|
+
const gw = newGateway({
|
|
113
|
+
app: makeApp(),
|
|
114
|
+
host: 'gw.local',
|
|
115
|
+
format: 'candump3',
|
|
116
|
+
actAsCanDevice: false
|
|
117
|
+
});
|
|
118
|
+
await waitForConnect();
|
|
119
|
+
const pushed = [];
|
|
120
|
+
gw.on('data', (frame) => pushed.push(frame));
|
|
121
|
+
// CAN ID 09F11274 = prio 2, PGN 127250, src 116, dst (PDU2) implied 255.
|
|
122
|
+
lastSocket.feed('(1502979132.106111) can0 09F11274#0001020304050607\n');
|
|
123
|
+
expect(pushed).toHaveLength(1);
|
|
124
|
+
expect(pushed[0].pgn.pgn).toBe(127250);
|
|
125
|
+
expect(pushed[0].pgn.src).toBe(0x74);
|
|
126
|
+
expect(pushed[0].pgn.prio).toBe(2);
|
|
127
|
+
expect(pushed[0].data.length).toBe(8);
|
|
128
|
+
expect(pushed[0].data.toString('hex')).toBe('0001020304050607');
|
|
129
|
+
});
|
|
130
|
+
test('RX candump3: rewrites the raw "(sec.usec)" timestamp to an ISO date', async () => {
|
|
131
|
+
const gw = newGateway({
|
|
132
|
+
app: makeApp(),
|
|
133
|
+
host: 'gw.local',
|
|
134
|
+
format: 'candump3',
|
|
135
|
+
actAsCanDevice: false
|
|
136
|
+
});
|
|
137
|
+
await waitForConnect();
|
|
138
|
+
const pushed = [];
|
|
139
|
+
gw.on('data', (frame) => pushed.push(frame));
|
|
140
|
+
// 1502979132.106111 → 2017-08-17T14:12:12.106Z
|
|
141
|
+
lastSocket.feed('(1502979132.106111) can0 09F11274#0001020304050607\n');
|
|
142
|
+
expect(pushed).toHaveLength(1);
|
|
143
|
+
expect(pushed[0].pgn.timestamp).toBe('2017-08-17T14:12:12.106Z');
|
|
144
|
+
expect(new Date(pushed[0].pgn.timestamp).getTime()).not.toBeNaN();
|
|
145
|
+
});
|
|
146
|
+
test('RX candump3: drops timestamps from devices with unsynced clocks (pre-2000)', async () => {
|
|
147
|
+
const gw = newGateway({
|
|
148
|
+
app: makeApp(),
|
|
149
|
+
host: 'gw.local',
|
|
150
|
+
format: 'candump3',
|
|
151
|
+
actAsCanDevice: false
|
|
152
|
+
});
|
|
153
|
+
await waitForConnect();
|
|
154
|
+
const pushed = [];
|
|
155
|
+
gw.on('data', (frame) => pushed.push(frame));
|
|
156
|
+
// Epoch 56702 = uptime-derived timestamp (1970-01-01 + ~15h45m), which
|
|
157
|
+
// is what the SensESP gateway emits before NTP has synced. We drop it
|
|
158
|
+
// so downstream code falls back to the server's own clock.
|
|
159
|
+
lastSocket.feed('(56702.123456) can0 09F11274#0001020304050607\n');
|
|
160
|
+
expect(pushed).toHaveLength(1);
|
|
161
|
+
expect(pushed[0].pgn.timestamp).toBeUndefined();
|
|
162
|
+
});
|
|
163
|
+
test('RX candump3: drops the timestamp field when it cannot be parsed', async () => {
|
|
164
|
+
const gw = newGateway({
|
|
165
|
+
app: makeApp(),
|
|
166
|
+
host: 'gw.local',
|
|
167
|
+
format: 'candump3',
|
|
168
|
+
actAsCanDevice: false
|
|
169
|
+
});
|
|
170
|
+
await waitForConnect();
|
|
171
|
+
const pushed = [];
|
|
172
|
+
gw.on('data', (frame) => pushed.push(frame));
|
|
173
|
+
// Pathological line: "(invalid)" — the parser will still split it, but
|
|
174
|
+
// the timestamp should be dropped (not left as Invalid Date) so the
|
|
175
|
+
// analyzer can fall back to "now".
|
|
176
|
+
lastSocket.feed('(invalid) can0 09F11274#0001020304050607\n');
|
|
177
|
+
expect(pushed).toHaveLength(1);
|
|
178
|
+
expect(pushed[0].pgn.timestamp).toBeUndefined();
|
|
179
|
+
});
|
|
180
|
+
test('RX candump3: re-buffers partial lines across socket chunks', async () => {
|
|
181
|
+
const gw = newGateway({
|
|
182
|
+
app: makeApp(),
|
|
183
|
+
host: 'gw.local',
|
|
184
|
+
format: 'candump3',
|
|
185
|
+
actAsCanDevice: false
|
|
186
|
+
});
|
|
187
|
+
await waitForConnect();
|
|
188
|
+
const pushed = [];
|
|
189
|
+
gw.on('data', (frame) => pushed.push(frame));
|
|
190
|
+
lastSocket.feed('(1502979132.106111) can0 09F1127');
|
|
191
|
+
expect(pushed).toHaveLength(0);
|
|
192
|
+
lastSocket.feed('4#0001020304050607\n');
|
|
193
|
+
expect(pushed).toHaveLength(1);
|
|
194
|
+
});
|
|
195
|
+
test('TX candump3: encodes outbound PGN to a candump line on the socket', async () => {
|
|
196
|
+
const app = makeApp();
|
|
197
|
+
const gw = newGateway({
|
|
198
|
+
app,
|
|
199
|
+
host: 'gw.local',
|
|
200
|
+
format: 'candump3',
|
|
201
|
+
actAsCanDevice: false
|
|
202
|
+
});
|
|
203
|
+
await waitForConnect();
|
|
204
|
+
// Drive the sendPGN path through a raw Buffer in `data` so this test
|
|
205
|
+
// doesn't depend on field encoding for any specific PGN definition.
|
|
206
|
+
gw.sendPGN({
|
|
207
|
+
pgn: 127245,
|
|
208
|
+
prio: 2,
|
|
209
|
+
src: 17,
|
|
210
|
+
dst: 255,
|
|
211
|
+
data: Buffer.from('0001020304050607', 'hex')
|
|
212
|
+
});
|
|
213
|
+
expect(lastSocket.written.length).toBeGreaterThanOrEqual(1);
|
|
214
|
+
const line = lastSocket.written[0];
|
|
215
|
+
// Shape: "(<ts>) <iface> <8 hex CAN ID>#<hex data>\n". The CAN ID is
|
|
216
|
+
// lower-case (canIdString), the hex bytes are upper-case (encodeCandump3).
|
|
217
|
+
expect(line).toMatch(/^\([0-9.]+\) \S+ [0-9a-f]{8}#[0-9A-Fa-f]+\n$/);
|
|
218
|
+
});
|
|
219
|
+
test('TX candump3: splits fast-packet payloads into multiple lines', async () => {
|
|
220
|
+
const gw = newGateway({
|
|
221
|
+
app: makeApp(),
|
|
222
|
+
host: 'gw.local',
|
|
223
|
+
format: 'candump3',
|
|
224
|
+
actAsCanDevice: false
|
|
225
|
+
});
|
|
226
|
+
await waitForConnect();
|
|
227
|
+
// 12-byte raw payload — > 8 bytes triggers the fast-packet split.
|
|
228
|
+
gw.sendPGN({
|
|
229
|
+
pgn: 129029,
|
|
230
|
+
prio: 3,
|
|
231
|
+
src: 17,
|
|
232
|
+
dst: 255,
|
|
233
|
+
data: Buffer.from('000102030405060708090a0b', 'hex')
|
|
234
|
+
});
|
|
235
|
+
expect(lastSocket.written.length).toBeGreaterThan(1);
|
|
236
|
+
lastSocket.written.forEach((line) => {
|
|
237
|
+
expect(line).toMatch(/^\([0-9.]+\) \S+ [0-9a-f]{8}#[0-9A-Fa-f]+\n$/);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
test('actAsCanDevice: true creates a CanDevice and emits address claim', async () => {
|
|
241
|
+
jest.useFakeTimers({ doNotFake: ['setImmediate'] });
|
|
242
|
+
try {
|
|
243
|
+
const app = makeApp();
|
|
244
|
+
const gw = newGateway({
|
|
245
|
+
app,
|
|
246
|
+
providerId: 't',
|
|
247
|
+
host: 'gw.local',
|
|
248
|
+
format: 'candump3',
|
|
249
|
+
actAsCanDevice: true,
|
|
250
|
+
manufacturerCode: 999,
|
|
251
|
+
uniqueNumber: 12345,
|
|
252
|
+
preferredAddress: 100
|
|
253
|
+
});
|
|
254
|
+
await waitForConnect();
|
|
255
|
+
// n2kDevice.start() schedules sendAddressClaim() via setTimeout(1000);
|
|
256
|
+
// advance fake timers to fire it.
|
|
257
|
+
jest.advanceTimersByTime(1100);
|
|
258
|
+
expect(gw.candevice).toBeDefined();
|
|
259
|
+
// Address claim is PGN 60928. In the encoded CAN ID it appears as
|
|
260
|
+
// PDU1 form `prio EE dst src`, e.g. "18eeff64" (dst=255, src=100).
|
|
261
|
+
const seenClaim = lastSocket.written.some((line) => /\s[0-9a-f]{2}ee[0-9a-f]{2}64#/i.test(line));
|
|
262
|
+
expect(seenClaim).toBe(true);
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
jest.useRealTimers();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
test('actAsCanDevice: false skips CanDevice and still encodes outbound PGNs', async () => {
|
|
269
|
+
const gw = newGateway({
|
|
270
|
+
app: makeApp(),
|
|
271
|
+
host: 'gw.local',
|
|
272
|
+
format: 'candump3',
|
|
273
|
+
actAsCanDevice: false
|
|
274
|
+
});
|
|
275
|
+
await waitForConnect();
|
|
276
|
+
expect(gw.candevice).toBeUndefined();
|
|
277
|
+
gw.sendPGN({
|
|
278
|
+
pgn: 127245,
|
|
279
|
+
prio: 2,
|
|
280
|
+
src: 17,
|
|
281
|
+
dst: 255,
|
|
282
|
+
data: Buffer.from('0001020304050607', 'hex')
|
|
283
|
+
});
|
|
284
|
+
expect(lastSocket.written.length).toBeGreaterThanOrEqual(1);
|
|
285
|
+
});
|
|
286
|
+
test('format selection: ydraw uses YDRAW encoder', async () => {
|
|
287
|
+
const gw = newGateway({
|
|
288
|
+
app: makeApp(),
|
|
289
|
+
host: 'gw.local',
|
|
290
|
+
format: 'ydraw',
|
|
291
|
+
actAsCanDevice: false
|
|
292
|
+
});
|
|
293
|
+
await waitForConnect();
|
|
294
|
+
gw.sendPGN({
|
|
295
|
+
pgn: 127245,
|
|
296
|
+
prio: 2,
|
|
297
|
+
src: 17,
|
|
298
|
+
dst: 255,
|
|
299
|
+
data: Buffer.from('0001020304050607', 'hex')
|
|
300
|
+
});
|
|
301
|
+
// YDRAW: "<CANID> <hex bytes space-separated>\r\n", no leading "(".
|
|
302
|
+
expect(lastSocket.written[0]).not.toMatch(/^\(/);
|
|
303
|
+
expect(lastSocket.written[0]).toMatch(/^[0-9a-f]{8} ([0-9A-Fa-f]{2} ?)+\r\n$/);
|
|
304
|
+
});
|
|
305
|
+
test('TX self-PGN forwarded to analyzer is split per CAN frame, never > 8 bytes', async () => {
|
|
306
|
+
// Regression: a 134-byte PGN 126996 (Product Info) had been pushed
|
|
307
|
+
// downstream as a single chunk with length=134, which flipped the
|
|
308
|
+
// canboatjs FromPgn instance into FORMAT_COALESCED for the rest of
|
|
309
|
+
// the session and corrupted fast-packet reassembly for unrelated
|
|
310
|
+
// PGNs (e.g. AIS 129038/129039 — producing phantom vessels with
|
|
311
|
+
// misaligned MMSI bytes).
|
|
312
|
+
const gw = newGateway({
|
|
313
|
+
app: makeApp(),
|
|
314
|
+
host: 'gw.local',
|
|
315
|
+
format: 'candump3',
|
|
316
|
+
actAsCanDevice: false
|
|
317
|
+
});
|
|
318
|
+
await waitForConnect();
|
|
319
|
+
const pushed = [];
|
|
320
|
+
gw.on('data', (frame) => pushed.push(frame));
|
|
321
|
+
// 32-byte raw "Product Info" payload — over 8 bytes so it must split.
|
|
322
|
+
const raw = Buffer.from('0102030405060708090a0b0c0d0e0f1011121314151617181920212223', 'hex');
|
|
323
|
+
gw.sendPGN({
|
|
324
|
+
pgn: 126996,
|
|
325
|
+
prio: 6,
|
|
326
|
+
src: 17,
|
|
327
|
+
dst: 255,
|
|
328
|
+
data: raw
|
|
329
|
+
});
|
|
330
|
+
expect(pushed.length).toBeGreaterThan(1); // multi-frame
|
|
331
|
+
pushed.forEach((frame) => {
|
|
332
|
+
expect(frame.length).toBeLessThanOrEqual(8);
|
|
333
|
+
expect(frame.data.length).toBeLessThanOrEqual(8);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
test('end() closes socket', async () => {
|
|
337
|
+
const gw = newGateway({
|
|
338
|
+
app: makeApp(),
|
|
339
|
+
host: 'gw.local',
|
|
340
|
+
format: 'candump3',
|
|
341
|
+
actAsCanDevice: false
|
|
342
|
+
});
|
|
343
|
+
await waitForConnect();
|
|
344
|
+
const sock = lastSocket;
|
|
345
|
+
gw.end();
|
|
346
|
+
expect(sock.destroyed).toBe(true);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
//# sourceMappingURL=n2kIpGateway.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n2kIpGateway.test.js","sourceRoot":"","sources":["../lib/n2kIpGateway.test.ts"],"names":[],"mappings":"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"}
|