signalk-vessels-to-ais 1.2.0 → 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.
- package/README.md +2 -0
- package/index.js +70 -75
- package/package.json +1 -1
- package/.eslintrc.js +0 -24
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ 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
|
|
12
14
|
- v1.2.0, updated fetch method, no need for NODE_TLS_REJECT_UNAUTHORIZED=0 anymore
|
|
13
15
|
- v1.1.5, updated vessels within selected timeframe are sent out, radius filtering around own vessel and tag-block option added
|
|
14
16
|
- v1.1.4, small fix
|
package/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-bitwise */
|
|
1
2
|
/*
|
|
2
3
|
MIT License
|
|
3
4
|
|
|
@@ -22,7 +23,7 @@ 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
|
|
26
|
+
const fetchNew = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
|
|
26
27
|
const https = require('https');
|
|
27
28
|
const AisEncode = require('ggencoder').AisEncode;
|
|
28
29
|
const moment = require('moment');
|
|
@@ -41,7 +42,6 @@ module.exports = function createPlugin(app) {
|
|
|
41
42
|
let intervalRun;
|
|
42
43
|
const setStatus = app.setPluginStatus || app.setProviderStatus;
|
|
43
44
|
|
|
44
|
-
let position_update;
|
|
45
45
|
let useTag;
|
|
46
46
|
|
|
47
47
|
const httpsAgent = new https.Agent({
|
|
@@ -50,20 +50,19 @@ module.exports = function createPlugin(app) {
|
|
|
50
50
|
|
|
51
51
|
let getParam;
|
|
52
52
|
|
|
53
|
-
plugin.start = function (options
|
|
54
|
-
position_update = options.position_update * 60;
|
|
53
|
+
plugin.start = function (options) {
|
|
55
54
|
useTag = options.useTag;
|
|
56
55
|
|
|
57
|
-
positionUpdate = options.position_update;
|
|
56
|
+
positionUpdate = options.position_update * 60;
|
|
58
57
|
distance = options.distance;
|
|
59
58
|
sendOwn = options.sendOwn;
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
const port = options.port || 3000;
|
|
61
|
+
const portSec = options.portSec || 3443;
|
|
63
62
|
|
|
64
|
-
url =
|
|
63
|
+
url = `https://localhost:${portSec}/signalk/v1/api/vessels`;
|
|
65
64
|
getParam = { method: 'GET', agent: httpsAgent };
|
|
66
|
-
|
|
65
|
+
fetchNew(url, getParam)
|
|
67
66
|
.then((res) => {
|
|
68
67
|
console.log(`${plugin.id}: SSL enabled, using https`);
|
|
69
68
|
if (!res.ok) {
|
|
@@ -72,23 +71,23 @@ module.exports = function createPlugin(app) {
|
|
|
72
71
|
}
|
|
73
72
|
})
|
|
74
73
|
.catch(() => {
|
|
75
|
-
url =
|
|
74
|
+
url = `http://localhost:${port}/signalk/v1/api/vessels`;
|
|
76
75
|
getParam = { method: 'GET' };
|
|
77
|
-
|
|
76
|
+
fetchNew(url, getParam)
|
|
78
77
|
.then((res) => {
|
|
79
78
|
console.log(`${plugin.id}: SSL disabled, using http`);
|
|
80
79
|
if (!res.ok) {
|
|
81
80
|
console.error(`${plugin.id}: SSL disabled, but error accessing server. Check 'Allow Readonly Access' and enable it.`);
|
|
82
81
|
setStatus("Error accessing server. Check 'Allow Readonly Access' and enable it");
|
|
83
82
|
}
|
|
84
|
-
})
|
|
83
|
+
});
|
|
85
84
|
})
|
|
86
85
|
.finally(() => {
|
|
87
|
-
|
|
86
|
+
// eslint-disable-next-line no-use-before-define
|
|
87
|
+
intervalRun = setInterval(readData, (positionUpdate * 1000), getParam);
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
app.debug('Plugin started');
|
|
91
|
-
|
|
92
91
|
};
|
|
93
92
|
|
|
94
93
|
//----------------------------------------------------------------------------
|
|
@@ -131,15 +130,16 @@ module.exports = function createPlugin(app) {
|
|
|
131
130
|
const sentence = enc.nmea;
|
|
132
131
|
let taggString = '';
|
|
133
132
|
if (useTag) {
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
// eslint-disable-next-line no-use-before-define
|
|
134
|
+
taggString = createTagBlock(aisTime);
|
|
135
|
+
}
|
|
136
136
|
if (sentence && sentence.length > 0) {
|
|
137
|
-
app.debug(taggString+sentence);
|
|
138
|
-
app.emit('nmea0183out', taggString+sentence);
|
|
137
|
+
app.debug(taggString + sentence);
|
|
138
|
+
app.emit('nmea0183out', taggString + sentence);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const
|
|
142
|
+
const mHex = [
|
|
143
143
|
'0',
|
|
144
144
|
'1',
|
|
145
145
|
'2',
|
|
@@ -155,60 +155,60 @@ module.exports = function createPlugin(app) {
|
|
|
155
155
|
'C',
|
|
156
156
|
'D',
|
|
157
157
|
'E',
|
|
158
|
-
'F'
|
|
159
|
-
]
|
|
160
|
-
|
|
161
|
-
function toHexString
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return
|
|
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];
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
function createTagBlock
|
|
168
|
-
let tagBlock = ''
|
|
169
|
-
tagBlock += 's:SK0001,'
|
|
170
|
-
//tagBlock += 'c:' + aisTime + ','
|
|
171
|
-
tagBlock +=
|
|
172
|
-
tagBlock = tagBlock.slice(0, -
|
|
173
|
-
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;
|
|
174
174
|
for (let i = 0; i < tagBlock.length; i++) {
|
|
175
|
-
tagBlockChecksum ^= tagBlock.charCodeAt(i)
|
|
175
|
+
tagBlockChecksum ^= tagBlock.charCodeAt(i);
|
|
176
176
|
}
|
|
177
|
-
return `\\${tagBlock}
|
|
177
|
+
return `\\${tagBlock}*${toHexString(tagBlockChecksum)}\\`;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
//----------------------------------------------------------------------------
|
|
181
181
|
// Read and parse AIS data
|
|
182
182
|
|
|
183
|
+
// eslint-disable-next-line no-shadow
|
|
183
184
|
function readData(getParam) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
let i, mmsi, aisTime, aisDelay, shipName, lat, lon, sog, cog, rot;
|
|
186
|
+
let navStat, hdg, dst, callSign, imo, id, type;
|
|
187
|
+
let draftCur, length, beam, ais, encMsg3, encMsg5, encMsg18, encMsg240, encMsg241, own;
|
|
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)
|
|
189
192
|
.then((res) => res.json())
|
|
190
193
|
.then((json) => {
|
|
191
194
|
const jsonContent = JSON.parse(JSON.stringify(json));
|
|
192
195
|
const numberAIS = Object.keys(jsonContent).length;
|
|
193
196
|
for (i = 0; i < numberAIS; i++) {
|
|
194
197
|
const jsonKey = Object.keys(jsonContent)[i];
|
|
195
|
-
|
|
198
|
+
|
|
196
199
|
try {
|
|
197
200
|
aisTime = jsonContent[jsonKey].sensors.ais.class.timestamp;
|
|
198
|
-
if ((parseFloat((moment(new Date(Date.now())).diff(aisTime)/1000).toFixed(3))) < position_update) {
|
|
199
|
-
aisDelay = true;
|
|
200
|
-
} else {
|
|
201
|
-
aisDelay = false;
|
|
202
|
-
}
|
|
203
201
|
} catch (error) {
|
|
204
202
|
if (i === 0) {
|
|
205
|
-
aisTime =
|
|
206
|
-
aisDelay = true;
|
|
203
|
+
aisTime = jsonContent[jsonKey].navigation.position.timestamp;
|
|
207
204
|
} else {
|
|
208
205
|
aisTime = null;
|
|
209
|
-
aisDelay = false;
|
|
210
206
|
}
|
|
211
207
|
}
|
|
208
|
+
|
|
209
|
+
aisDelay = (parseFloat((moment(new Date(Date.now()))
|
|
210
|
+
.diff(aisTime) / 1000).toFixed(3))) < positionUpdate;
|
|
211
|
+
|
|
212
212
|
try {
|
|
213
213
|
mmsi = jsonContent[jsonKey].mmsi;
|
|
214
214
|
} catch (error) { mmsi = null; }
|
|
@@ -267,7 +267,7 @@ module.exports = function createPlugin(app) {
|
|
|
267
267
|
try {
|
|
268
268
|
ais = jsonContent[jsonKey].sensors.ais.class.value;
|
|
269
269
|
} catch (error) { ais = null; }
|
|
270
|
-
|
|
270
|
+
|
|
271
271
|
if (shipName % 1 === 0) {
|
|
272
272
|
shipName = '';
|
|
273
273
|
}
|
|
@@ -280,7 +280,7 @@ module.exports = function createPlugin(app) {
|
|
|
280
280
|
if (type % 1 === 0) {
|
|
281
281
|
type = '';
|
|
282
282
|
}
|
|
283
|
-
|
|
283
|
+
|
|
284
284
|
if (i === 0) {
|
|
285
285
|
own = true;
|
|
286
286
|
if (sendOwn) {
|
|
@@ -291,13 +291,12 @@ module.exports = function createPlugin(app) {
|
|
|
291
291
|
} else {
|
|
292
292
|
own = false;
|
|
293
293
|
}
|
|
294
|
-
|
|
295
|
-
const a = { lat: ownLat, lon: ownLon }
|
|
296
|
-
const b = { lat
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
|
|
295
|
+
const a = { lat: ownLat, lon: ownLon };
|
|
296
|
+
const b = { lat, lon };
|
|
297
|
+
const dist = (haversine(a, b) / 1000).toFixed(2);
|
|
298
|
+
|
|
299
299
|
if (dist <= distance) {
|
|
300
|
-
|
|
301
300
|
encMsg3 = {
|
|
302
301
|
own,
|
|
303
302
|
aistype: 3, // class A position report
|
|
@@ -311,7 +310,7 @@ module.exports = function createPlugin(app) {
|
|
|
311
310
|
hdg,
|
|
312
311
|
rot,
|
|
313
312
|
};
|
|
314
|
-
|
|
313
|
+
|
|
315
314
|
encMsg5 = {
|
|
316
315
|
own,
|
|
317
316
|
aistype: 5, // class A static
|
|
@@ -328,7 +327,7 @@ module.exports = function createPlugin(app) {
|
|
|
328
327
|
dimC: beam,
|
|
329
328
|
dimD: beam,
|
|
330
329
|
};
|
|
331
|
-
|
|
330
|
+
|
|
332
331
|
encMsg18 = {
|
|
333
332
|
own,
|
|
334
333
|
aistype: 18, // class B position report
|
|
@@ -341,7 +340,7 @@ module.exports = function createPlugin(app) {
|
|
|
341
340
|
cog,
|
|
342
341
|
hdg,
|
|
343
342
|
};
|
|
344
|
-
|
|
343
|
+
|
|
345
344
|
encMsg240 = {
|
|
346
345
|
own,
|
|
347
346
|
aistype: 24, // class B static
|
|
@@ -350,7 +349,7 @@ module.exports = function createPlugin(app) {
|
|
|
350
349
|
mmsi,
|
|
351
350
|
shipname: shipName,
|
|
352
351
|
};
|
|
353
|
-
|
|
352
|
+
|
|
354
353
|
encMsg241 = {
|
|
355
354
|
own,
|
|
356
355
|
aistype: 24, // class B static
|
|
@@ -364,9 +363,10 @@ module.exports = function createPlugin(app) {
|
|
|
364
363
|
dimC: beam,
|
|
365
364
|
dimD: beam,
|
|
366
365
|
};
|
|
367
|
-
|
|
366
|
+
|
|
368
367
|
if (aisDelay && (ais === 'A' || ais === 'B')) {
|
|
369
|
-
|
|
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
370
|
if (ais === 'A') {
|
|
371
371
|
app.debug(`class A, ${i}, time: ${aisTime}`);
|
|
372
372
|
aisOut(encMsg3, aisTime);
|
|
@@ -378,22 +378,17 @@ module.exports = function createPlugin(app) {
|
|
|
378
378
|
aisOut(encMsg240, aisTime);
|
|
379
379
|
aisOut(encMsg241, aisTime);
|
|
380
380
|
}
|
|
381
|
-
app.debug(
|
|
382
|
-
|
|
381
|
+
app.debug('--------------------------------------------------------');
|
|
383
382
|
}
|
|
384
383
|
}
|
|
385
384
|
}
|
|
386
385
|
const dateobj = new Date(Date.now());
|
|
387
386
|
const date = dateobj.toISOString();
|
|
388
|
-
app.handleMessage(plugin.id, {
|
|
389
|
-
context: `vessels.${app.selfId}`,
|
|
390
|
-
updates: [
|
|
391
|
-
],
|
|
392
|
-
});
|
|
393
387
|
setStatus(`AIS NMEA message send: ${date}`);
|
|
394
388
|
})
|
|
395
389
|
.catch((err) => console.error(err));
|
|
396
|
-
|
|
390
|
+
}
|
|
391
|
+
}
|
|
397
392
|
|
|
398
393
|
//----------------------------------------------------------------------------
|
|
399
394
|
|
|
@@ -413,22 +408,22 @@ module.exports = function createPlugin(app) {
|
|
|
413
408
|
port: {
|
|
414
409
|
type: 'number',
|
|
415
410
|
title: 'HTTP port',
|
|
416
|
-
default: 3000
|
|
411
|
+
default: 3000,
|
|
417
412
|
},
|
|
418
413
|
portSec: {
|
|
419
414
|
type: 'number',
|
|
420
415
|
title: 'HTTPS port',
|
|
421
|
-
default: 3443
|
|
416
|
+
default: 3443,
|
|
422
417
|
},
|
|
423
418
|
sendOwn: {
|
|
424
419
|
type: 'boolean',
|
|
425
420
|
title: 'Send own AIS data, VDO',
|
|
426
|
-
default: true
|
|
421
|
+
default: true,
|
|
427
422
|
},
|
|
428
423
|
useTag: {
|
|
429
424
|
type: 'boolean',
|
|
430
425
|
title: 'Add Tag-block',
|
|
431
|
-
default: false
|
|
426
|
+
default: false,
|
|
432
427
|
},
|
|
433
428
|
distance: {
|
|
434
429
|
type: 'integer',
|
package/package.json
CHANGED
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
|
-
};
|