signalk-vessels-to-ais 1.1.5 → 1.2.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.
Files changed (4) hide show
  1. package/README.md +3 -0
  2. package/index.js +250 -248
  3. package/package.json +1 -1
  4. package/.eslintrc.js +0 -24
package/README.md CHANGED
@@ -9,6 +9,9 @@ User can configure:
9
9
  - Own data can be added to AIS sending
10
10
 
11
11
  New:
12
+ - v1.2.2, fix if own position is not available
13
+ - v1.2.1, fix own vessel sending
14
+ - v1.2.0, updated fetch method, no need for NODE_TLS_REJECT_UNAUTHORIZED=0 anymore
12
15
  - v1.1.5, updated vessels within selected timeframe are sent out, radius filtering around own vessel and tag-block option added
13
16
  - v1.1.4, small fix
14
17
  - v1.1.3, add: own vessel data and sending interval modified
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-bitwise */
1
2
  /*
2
3
  MIT License
3
4
 
@@ -22,7 +23,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
23
  SOFTWARE.
23
24
  */
24
25
 
25
- const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
26
+ const fetchNew = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
27
+ const https = require('https');
26
28
  const AisEncode = require('ggencoder').AisEncode;
27
29
  const moment = require('moment');
28
30
  const haversine = require('haversine-distance');
@@ -38,22 +40,29 @@ module.exports = function createPlugin(app) {
38
40
  let sendOwn;
39
41
  let url;
40
42
  let intervalRun;
41
- let readInfo;
42
43
  const setStatus = app.setPluginStatus || app.setProviderStatus;
43
44
 
44
- let position_update;
45
45
  let useTag;
46
46
 
47
- plugin.start = function (options, restartPlugin) {
48
- position_update = options.position_update * 60;
47
+ const httpsAgent = new https.Agent({
48
+ rejectUnauthorized: false,
49
+ });
50
+
51
+ let getParam;
52
+
53
+ plugin.start = function (options) {
49
54
  useTag = options.useTag;
50
55
 
51
- let port = options.port || 3000;
52
- let portSec = options.portSec || 3443;
56
+ positionUpdate = options.position_update * 60;
57
+ distance = options.distance;
58
+ sendOwn = options.sendOwn;
59
+
60
+ const port = options.port || 3000;
61
+ const portSec = options.portSec || 3443;
53
62
 
54
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
55
- url = 'https://localhost:' + portSec + '/signalk/v1/api/vessels';
56
- fetch(url, { method: 'GET' })
63
+ url = `https://localhost:${portSec}/signalk/v1/api/vessels`;
64
+ getParam = { method: 'GET', agent: httpsAgent };
65
+ fetchNew(url, getParam)
57
66
  .then((res) => {
58
67
  console.log(`${plugin.id}: SSL enabled, using https`);
59
68
  if (!res.ok) {
@@ -62,26 +71,23 @@ module.exports = function createPlugin(app) {
62
71
  }
63
72
  })
64
73
  .catch(() => {
65
- url = 'http://localhost:' + port + '/signalk/v1/api/vessels';
66
- fetch(url, { method: 'GET' })
74
+ url = `http://localhost:${port}/signalk/v1/api/vessels`;
75
+ getParam = { method: 'GET' };
76
+ fetchNew(url, getParam)
67
77
  .then((res) => {
68
78
  console.log(`${plugin.id}: SSL disabled, using http`);
69
79
  if (!res.ok) {
70
80
  console.error(`${plugin.id}: SSL disabled, but error accessing server. Check 'Allow Readonly Access' and enable it.`);
71
81
  setStatus("Error accessing server. Check 'Allow Readonly Access' and enable it");
72
82
  }
73
- })
74
- .catch(() => {
75
83
  });
84
+ })
85
+ .finally(() => {
86
+ // eslint-disable-next-line no-use-before-define
87
+ intervalRun = setInterval(readData, (positionUpdate * 1000), getParam);
76
88
  });
77
89
 
78
- positionUpdate = options.position_update;
79
- distance = options.distance;
80
- sendOwn = options.sendOwn;
81
-
82
90
  app.debug('Plugin started');
83
-
84
- intervalRun = setInterval(readInfo, (positionUpdate * 60000));
85
91
  };
86
92
 
87
93
  //----------------------------------------------------------------------------
@@ -124,15 +130,16 @@ module.exports = function createPlugin(app) {
124
130
  const sentence = enc.nmea;
125
131
  let taggString = '';
126
132
  if (useTag) {
127
- taggString = createTagBlock(aisTime)
128
- }
133
+ // eslint-disable-next-line no-use-before-define
134
+ taggString = createTagBlock(aisTime);
135
+ }
129
136
  if (sentence && sentence.length > 0) {
130
- app.debug(taggString+sentence);
131
- app.emit('nmea0183out', taggString+sentence);
137
+ app.debug(taggString + sentence);
138
+ app.emit('nmea0183out', taggString + sentence);
132
139
  }
133
140
  }
134
141
 
135
- const m_hex = [
142
+ const mHex = [
136
143
  '0',
137
144
  '1',
138
145
  '2',
@@ -148,245 +155,240 @@ module.exports = function createPlugin(app) {
148
155
  'C',
149
156
  'D',
150
157
  'E',
151
- 'F'
152
- ]
153
-
154
- function toHexString (v) {
155
- let msn = (v >> 4) & 0x0f
156
- let lsn = (v >> 0) & 0x0f
157
- return m_hex[msn] + m_hex[lsn]
158
+ 'F',
159
+ ];
160
+
161
+ function toHexString(v) {
162
+ const msn = (v >> 4) & 0x0f;
163
+ const lsn = (v >> 0) & 0x0f;
164
+ return mHex[msn] + mHex[lsn];
158
165
  }
159
166
 
160
- function createTagBlock (aisTime) {
161
- let tagBlock = ''
162
- tagBlock += 's:SK0001,'
163
- //tagBlock += 'c:' + aisTime + ','
164
- tagBlock += 'c:' + Date.now(aisTime) + ','
165
- tagBlock = tagBlock.slice(0, - 1)
166
- let tagBlockChecksum = 0
167
+ function createTagBlock(aisTime) {
168
+ let tagBlock = '';
169
+ tagBlock += 's:SK0001,';
170
+ // tagBlock += 'c:' + aisTime + ','
171
+ tagBlock += `c:${Date.now(aisTime)},`;
172
+ tagBlock = tagBlock.slice(0, -1);
173
+ let tagBlockChecksum = 0;
167
174
  for (let i = 0; i < tagBlock.length; i++) {
168
- tagBlockChecksum ^= tagBlock.charCodeAt(i)
175
+ tagBlockChecksum ^= tagBlock.charCodeAt(i);
169
176
  }
170
- return `\\${tagBlock}*` + toHexString(tagBlockChecksum) + `\\`
177
+ return `\\${tagBlock}*${toHexString(tagBlockChecksum)}\\`;
171
178
  }
172
179
 
173
180
  //----------------------------------------------------------------------------
174
181
  // Read and parse AIS data
175
182
 
176
- readInfo = function readData(options) {
177
- let i, mmsi, aisTime, aisDelay, shipName, lat, lon, sog, cog, rot, navStat, hdg, dst, callSign, imo, id, type;
183
+ // eslint-disable-next-line no-shadow
184
+ function readData(getParam) {
185
+ let i, mmsi, aisTime, aisDelay, shipName, lat, lon, sog, cog, rot;
186
+ let navStat, hdg, dst, callSign, imo, id, type;
178
187
  let draftCur, length, beam, ais, encMsg3, encMsg5, encMsg18, encMsg240, encMsg241, own;
179
- let ownLat = app.getSelfPath('navigation.position.value.latitude');
180
- let ownLon = app.getSelfPath('navigation.position.value.longitude');
181
- fetch(url, { method: 'GET' })
182
- .then((res) => res.json())
183
- .then((json) => {
184
- const jsonContent = JSON.parse(JSON.stringify(json));
185
- const numberAIS = Object.keys(jsonContent).length;
186
- for (i = 0; i < numberAIS; i++) {
187
- const jsonKey = Object.keys(jsonContent)[i];
188
-
189
- try {
190
- aisTime = jsonContent[jsonKey].sensors.ais.class.timestamp;
191
- if ((parseFloat((moment(new Date(Date.now())).diff(aisTime)/1000).toFixed(3))) < position_update) {
192
- aisDelay = true;
193
- } else {
194
- aisDelay = false;
188
+ const ownLat = app.getSelfPath('navigation.position.value.latitude');
189
+ const ownLon = app.getSelfPath('navigation.position.value.longitude');
190
+ if (typeof ownLat !== "undefined" && typeof ownLon !== "undefined") {
191
+ fetchNew(url, getParam)
192
+ .then((res) => res.json())
193
+ .then((json) => {
194
+ const jsonContent = JSON.parse(JSON.stringify(json));
195
+ const numberAIS = Object.keys(jsonContent).length;
196
+ for (i = 0; i < numberAIS; i++) {
197
+ const jsonKey = Object.keys(jsonContent)[i];
198
+
199
+ try {
200
+ aisTime = jsonContent[jsonKey].sensors.ais.class.timestamp;
201
+ } catch (error) {
202
+ if (i === 0) {
203
+ aisTime = jsonContent[jsonKey].navigation.position.timestamp;
204
+ } else {
205
+ aisTime = null;
206
+ }
195
207
  }
196
- } catch (error) {
197
- if (i === 0) {
198
- aisTime = new Date(Date.now()).toISOString();
199
- aisDelay = true;
200
- } else {
201
- aisTime = null;
202
- aisDelay = false;
208
+
209
+ aisDelay = (parseFloat((moment(new Date(Date.now()))
210
+ .diff(aisTime) / 1000).toFixed(3))) < positionUpdate;
211
+
212
+ try {
213
+ mmsi = jsonContent[jsonKey].mmsi;
214
+ } catch (error) { mmsi = null; }
215
+ try {
216
+ shipName = jsonContent[jsonKey].name;
217
+ } catch (error) { shipName = ''; }
218
+ try {
219
+ lat = jsonContent[jsonKey].navigation.position.value.latitude;
220
+ } catch (error) { lat = null; }
221
+ try {
222
+ lon = jsonContent[jsonKey].navigation.position.value.longitude;
223
+ } catch (error) { lon = null; }
224
+ try {
225
+ sog = msToKnots(jsonContent[jsonKey].navigation.speedOverGround.value);
226
+ } catch (error) { sog = null; }
227
+ try {
228
+ cog = radToDegrees(jsonContent[jsonKey].navigation.courseOverGroundTrue.value);
229
+ } catch (error) { cog = null; }
230
+ try {
231
+ rot = radToDegrees(jsonContent[jsonKey].navigation.rateOfTurn.value);
232
+ } catch (error) { rot = null; }
233
+ try {
234
+ navStat = stateMapping[jsonContent[jsonKey].navigation.state.value];
235
+ } catch (error) { navStat = ''; }
236
+ try {
237
+ hdg = radToDegrees(jsonContent[jsonKey].navigation.headingTrue.value);
238
+ } catch (error) { hdg = null; }
239
+ try {
240
+ dst = jsonContent[jsonKey].navigation.destination.commonName.value;
241
+ } catch (error) { dst = ''; }
242
+ try {
243
+ if (i === 0) {
244
+ callSign = jsonContent[jsonKey].communication.callsignVhf;
245
+ } else {
246
+ callSign = jsonContent[jsonKey].communication.value.callsignVhf;
247
+ }
248
+ } catch (error) { callSign = ''; }
249
+ try {
250
+ imo = (jsonContent[jsonKey].registrations.value.imo).substring(4, 20);
251
+ } catch (error) { imo = null; }
252
+ try {
253
+ id = jsonContent[jsonKey].design.aisShipType.value.id;
254
+ } catch (error) { id = null; }
255
+ try {
256
+ type = jsonContent[jsonKey].design.aisShipType.value.name;
257
+ } catch (error) { type = ''; }
258
+ try {
259
+ draftCur = (jsonContent[jsonKey].design.draft.value.current) / 10;
260
+ } catch (error) { draftCur = null; }
261
+ try {
262
+ length = jsonContent[jsonKey].design.length.value.overall;
263
+ } catch (error) { length = null; }
264
+ try {
265
+ beam = (jsonContent[jsonKey].design.beam.value) / 2;
266
+ } catch (error) { beam = null; }
267
+ try {
268
+ ais = jsonContent[jsonKey].sensors.ais.class.value;
269
+ } catch (error) { ais = null; }
270
+
271
+ if (shipName % 1 === 0) {
272
+ shipName = '';
203
273
  }
204
- }
205
- try {
206
- mmsi = jsonContent[jsonKey].mmsi;
207
- } catch (error) { mmsi = null; }
208
- try {
209
- shipName = jsonContent[jsonKey].name;
210
- } catch (error) { shipName = ''; }
211
- try {
212
- lat = jsonContent[jsonKey].navigation.position.value.latitude;
213
- } catch (error) { lat = null; }
214
- try {
215
- lon = jsonContent[jsonKey].navigation.position.value.longitude;
216
- } catch (error) { lon = null; }
217
- try {
218
- sog = msToKnots(jsonContent[jsonKey].navigation.speedOverGround.value);
219
- } catch (error) { sog = null; }
220
- try {
221
- cog = radToDegrees(jsonContent[jsonKey].navigation.courseOverGroundTrue.value);
222
- } catch (error) { cog = null; }
223
- try {
224
- rot = radToDegrees(jsonContent[jsonKey].navigation.rateOfTurn.value);
225
- } catch (error) { rot = null; }
226
- try {
227
- navStat = stateMapping[jsonContent[jsonKey].navigation.state.value];
228
- } catch (error) { navStat = ''; }
229
- try {
230
- hdg = radToDegrees(jsonContent[jsonKey].navigation.headingTrue.value);
231
- } catch (error) { hdg = null; }
232
- try {
233
- dst = jsonContent[jsonKey].navigation.destination.commonName.value;
234
- } catch (error) { dst = ''; }
235
- try {
236
- if (i === 0) {
237
- callSign = jsonContent[jsonKey].communication.callsignVhf;
238
- } else {
239
- callSign = jsonContent[jsonKey].communication.value.callsignVhf;
274
+ if (dst % 1 === 0) {
275
+ dst = '';
276
+ }
277
+ if (callSign % 1 === 0) {
278
+ callSign = '';
279
+ }
280
+ if (type % 1 === 0) {
281
+ type = '';
240
282
  }
241
- } catch (error) { callSign = ''; }
242
- try {
243
- imo = (jsonContent[jsonKey].registrations.value.imo).substring(4, 20);
244
- } catch (error) { imo = null; }
245
- try {
246
- id = jsonContent[jsonKey].design.aisShipType.value.id;
247
- } catch (error) { id = null; }
248
- try {
249
- type = jsonContent[jsonKey].design.aisShipType.value.name;
250
- } catch (error) { type = ''; }
251
- try {
252
- draftCur = (jsonContent[jsonKey].design.draft.value.current) / 10;
253
- } catch (error) { draftCur = null; }
254
- try {
255
- length = jsonContent[jsonKey].design.length.value.overall;
256
- } catch (error) { length = null; }
257
- try {
258
- beam = (jsonContent[jsonKey].design.beam.value) / 2;
259
- } catch (error) { beam = null; }
260
- try {
261
- ais = jsonContent[jsonKey].sensors.ais.class.value;
262
- } catch (error) { ais = null; }
263
-
264
- if (shipName % 1 === 0) {
265
- shipName = '';
266
- }
267
- if (dst % 1 === 0) {
268
- dst = '';
269
- }
270
- if (callSign % 1 === 0) {
271
- callSign = '';
272
- }
273
- if (type % 1 === 0) {
274
- type = '';
275
- }
276
283
 
277
- if (i === 0) {
278
- own = true;
279
- if (sendOwn) {
280
- ais = 'A';
284
+ if (i === 0) {
285
+ own = true;
286
+ if (sendOwn) {
287
+ ais = 'A';
288
+ } else {
289
+ ais = '';
290
+ }
281
291
  } else {
282
- ais = '';
292
+ own = false;
283
293
  }
284
- } else {
285
- own = false;
286
- }
287
294
 
288
- const a = { lat: ownLat, lon: ownLon }
289
- const b = { lat: lat, lon: lon }
290
- let dist = (haversine(a, b)/1000).toFixed(2);
291
-
292
- if (dist <= distance) {
293
-
294
- encMsg3 = {
295
- own,
296
- aistype: 3, // class A position report
297
- repeat: 0,
298
- mmsi,
299
- navstatus: navStat,
300
- sog,
301
- lon,
302
- lat,
303
- cog,
304
- hdg,
305
- rot,
306
- };
307
-
308
- encMsg5 = {
309
- own,
310
- aistype: 5, // class A static
311
- repeat: 0,
312
- mmsi,
313
- imo,
314
- cargo: id,
315
- callsign: callSign,
316
- shipname: shipName,
317
- draught: draftCur,
318
- destination: dst,
319
- dimA: 0,
320
- dimB: length,
321
- dimC: beam,
322
- dimD: beam,
323
- };
324
-
325
- encMsg18 = {
326
- own,
327
- aistype: 18, // class B position report
328
- repeat: 0,
329
- mmsi,
330
- sog,
331
- accuracy: 0,
332
- lon,
333
- lat,
334
- cog,
335
- hdg,
336
- };
337
-
338
- encMsg240 = {
339
- own,
340
- aistype: 24, // class B static
341
- repeat: 0,
342
- part: 0,
343
- mmsi,
344
- shipname: shipName,
345
- };
346
-
347
- encMsg241 = {
348
- own,
349
- aistype: 24, // class B static
350
- repeat: 0,
351
- part: 1,
352
- mmsi,
353
- cargo: id,
354
- callsign: callSign,
355
- dimA: 0,
356
- dimB: length,
357
- dimC: beam,
358
- dimD: beam,
359
- };
360
-
361
- if (aisDelay && (ais === 'A' || ais === 'B')) {
362
- app.debug("Distance range: " + distance + "km, AIS target distance: " + dist + "km" + ", Class " + ais + " Vessel" + ", MMSI:" + mmsi)
363
- if (ais === 'A') {
364
- app.debug(`class A, ${i}, time: ${aisTime}`);
365
- aisOut(encMsg3, aisTime);
366
- aisOut(encMsg5, aisTime);
367
- }
368
- if (ais === 'B') {
369
- app.debug(`class B, ${i}, time: ${aisTime}`);
370
- aisOut(encMsg18, aisTime);
371
- aisOut(encMsg240, aisTime);
372
- aisOut(encMsg241, aisTime);
295
+ const a = { lat: ownLat, lon: ownLon };
296
+ const b = { lat, lon };
297
+ const dist = (haversine(a, b) / 1000).toFixed(2);
298
+
299
+ if (dist <= distance) {
300
+ encMsg3 = {
301
+ own,
302
+ aistype: 3, // class A position report
303
+ repeat: 0,
304
+ mmsi,
305
+ navstatus: navStat,
306
+ sog,
307
+ lon,
308
+ lat,
309
+ cog,
310
+ hdg,
311
+ rot,
312
+ };
313
+
314
+ encMsg5 = {
315
+ own,
316
+ aistype: 5, // class A static
317
+ repeat: 0,
318
+ mmsi,
319
+ imo,
320
+ cargo: id,
321
+ callsign: callSign,
322
+ shipname: shipName,
323
+ draught: draftCur,
324
+ destination: dst,
325
+ dimA: 0,
326
+ dimB: length,
327
+ dimC: beam,
328
+ dimD: beam,
329
+ };
330
+
331
+ encMsg18 = {
332
+ own,
333
+ aistype: 18, // class B position report
334
+ repeat: 0,
335
+ mmsi,
336
+ sog,
337
+ accuracy: 0,
338
+ lon,
339
+ lat,
340
+ cog,
341
+ hdg,
342
+ };
343
+
344
+ encMsg240 = {
345
+ own,
346
+ aistype: 24, // class B static
347
+ repeat: 0,
348
+ part: 0,
349
+ mmsi,
350
+ shipname: shipName,
351
+ };
352
+
353
+ encMsg241 = {
354
+ own,
355
+ aistype: 24, // class B static
356
+ repeat: 0,
357
+ part: 1,
358
+ mmsi,
359
+ cargo: id,
360
+ callsign: callSign,
361
+ dimA: 0,
362
+ dimB: length,
363
+ dimC: beam,
364
+ dimD: beam,
365
+ };
366
+
367
+ if (aisDelay && (ais === 'A' || ais === 'B')) {
368
+ // eslint-disable-next-line no-useless-concat
369
+ app.debug(`Distance range: ${distance}km, AIS target distance: ${dist}km` + `, Class ${ais} Vessel` + `, MMSI:${mmsi}`);
370
+ if (ais === 'A') {
371
+ app.debug(`class A, ${i}, time: ${aisTime}`);
372
+ aisOut(encMsg3, aisTime);
373
+ aisOut(encMsg5, aisTime);
374
+ }
375
+ if (ais === 'B') {
376
+ app.debug(`class B, ${i}, time: ${aisTime}`);
377
+ aisOut(encMsg18, aisTime);
378
+ aisOut(encMsg240, aisTime);
379
+ aisOut(encMsg241, aisTime);
380
+ }
381
+ app.debug('--------------------------------------------------------');
373
382
  }
374
- app.debug("--------------------------------------------------------");
375
-
376
383
  }
377
384
  }
378
- }
379
- const dateobj = new Date(Date.now());
380
- const date = dateobj.toISOString();
381
- app.handleMessage(plugin.id, {
382
- context: `vessels.${app.selfId}`,
383
- updates: [
384
- ],
385
- });
386
- setStatus(`Number of AIS targets sent: ${numberAIS - 1} (${date})`);
387
- })
388
- .catch((err) => console.error(err));
389
- };
385
+ const dateobj = new Date(Date.now());
386
+ const date = dateobj.toISOString();
387
+ setStatus(`AIS NMEA message send: ${date}`);
388
+ })
389
+ .catch((err) => console.error(err));
390
+ }
391
+ }
390
392
 
391
393
  //----------------------------------------------------------------------------
392
394
 
@@ -406,22 +408,22 @@ module.exports = function createPlugin(app) {
406
408
  port: {
407
409
  type: 'number',
408
410
  title: 'HTTP port',
409
- default: 3000
411
+ default: 3000,
410
412
  },
411
413
  portSec: {
412
414
  type: 'number',
413
415
  title: 'HTTPS port',
414
- default: 3443
416
+ default: 3443,
415
417
  },
416
418
  sendOwn: {
417
419
  type: 'boolean',
418
420
  title: 'Send own AIS data, VDO',
419
- default: true
421
+ default: true,
420
422
  },
421
423
  useTag: {
422
424
  type: 'boolean',
423
425
  title: 'Add Tag-block',
424
- default: false
426
+ default: false,
425
427
  },
426
428
  distance: {
427
429
  type: 'integer',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-vessels-to-ais",
3
- "version": "1.1.5",
3
+ "version": "1.2.2",
4
4
  "description": "SignalK server plugin to convert other vessel data to NMEA0183 AIS format and forward it out to 3rd party applications",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/.eslintrc.js DELETED
@@ -1,24 +0,0 @@
1
- module.exports = {
2
- env: {
3
- commonjs: true,
4
- es2021: true,
5
- node: true,
6
- browser: true,
7
- jquery: true,
8
- },
9
- extends: [
10
- 'airbnb-base',
11
- ],
12
- parserOptions: {
13
- ecmaVersion: 12,
14
- },
15
- rules: {
16
- 'linebreak-style': 0,
17
- 'no-console': 0,
18
- 'func-names': 0,
19
- 'prefer-destructuring': 0,
20
- 'one-var-declaration-per-line': 0,
21
- 'one-var': 0,
22
- 'no-plusplus': 0,
23
- },
24
- };