signalk-vessels-to-ais 1.1.4 → 1.1.5
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 +1 -0
- package/index.js +193 -93
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ User can configure:
|
|
|
9
9
|
- Own data can be added to AIS sending
|
|
10
10
|
|
|
11
11
|
New:
|
|
12
|
+
- v1.1.5, updated vessels within selected timeframe are sent out, radius filtering around own vessel and tag-block option added
|
|
12
13
|
- v1.1.4, small fix
|
|
13
14
|
- v1.1.3, add: own vessel data and sending interval modified
|
|
14
15
|
- v1.1.2, fix: http/https url selection and better error info
|
package/index.js
CHANGED
|
@@ -22,8 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
22
22
|
SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
const fetch =
|
|
25
|
+
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
|
|
26
26
|
const AisEncode = require('ggencoder').AisEncode;
|
|
27
|
+
const moment = require('moment');
|
|
28
|
+
const haversine = require('haversine-distance');
|
|
27
29
|
|
|
28
30
|
module.exports = function createPlugin(app) {
|
|
29
31
|
const plugin = {};
|
|
@@ -32,16 +34,25 @@ module.exports = function createPlugin(app) {
|
|
|
32
34
|
plugin.description = 'SignalK server plugin to convert other vessel data to NMEA0183 AIS format and forward it out to 3rd party applications';
|
|
33
35
|
|
|
34
36
|
let positionUpdate = null;
|
|
37
|
+
let distance;
|
|
35
38
|
let sendOwn;
|
|
36
39
|
let url;
|
|
37
|
-
let intervalStart;
|
|
38
40
|
let intervalRun;
|
|
39
41
|
let readInfo;
|
|
40
42
|
const setStatus = app.setPluginStatus || app.setProviderStatus;
|
|
41
43
|
|
|
44
|
+
let position_update;
|
|
45
|
+
let useTag;
|
|
46
|
+
|
|
42
47
|
plugin.start = function (options, restartPlugin) {
|
|
48
|
+
position_update = options.position_update * 60;
|
|
49
|
+
useTag = options.useTag;
|
|
50
|
+
|
|
51
|
+
let port = options.port || 3000;
|
|
52
|
+
let portSec = options.portSec || 3443;
|
|
53
|
+
|
|
43
54
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
44
|
-
url = 'https://localhost:
|
|
55
|
+
url = 'https://localhost:' + portSec + '/signalk/v1/api/vessels';
|
|
45
56
|
fetch(url, { method: 'GET' })
|
|
46
57
|
.then((res) => {
|
|
47
58
|
console.log(`${plugin.id}: SSL enabled, using https`);
|
|
@@ -51,7 +62,7 @@ module.exports = function createPlugin(app) {
|
|
|
51
62
|
}
|
|
52
63
|
})
|
|
53
64
|
.catch(() => {
|
|
54
|
-
url = 'http://localhost:
|
|
65
|
+
url = 'http://localhost:' + port + '/signalk/v1/api/vessels';
|
|
55
66
|
fetch(url, { method: 'GET' })
|
|
56
67
|
.then((res) => {
|
|
57
68
|
console.log(`${plugin.id}: SSL disabled, using http`);
|
|
@@ -65,16 +76,11 @@ module.exports = function createPlugin(app) {
|
|
|
65
76
|
});
|
|
66
77
|
|
|
67
78
|
positionUpdate = options.position_update;
|
|
79
|
+
distance = options.distance;
|
|
68
80
|
sendOwn = options.sendOwn;
|
|
69
81
|
|
|
70
82
|
app.debug('Plugin started');
|
|
71
83
|
|
|
72
|
-
function clear() {
|
|
73
|
-
clearInterval(intervalStart);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
intervalStart = setInterval(readInfo, (15000));
|
|
77
|
-
setTimeout(clear, 15000);
|
|
78
84
|
intervalRun = setInterval(readInfo, (positionUpdate * 60000));
|
|
79
85
|
};
|
|
80
86
|
|
|
@@ -113,21 +119,65 @@ module.exports = function createPlugin(app) {
|
|
|
113
119
|
//----------------------------------------------------------------------------
|
|
114
120
|
// nmea out
|
|
115
121
|
|
|
116
|
-
function aisOut(encMsg) {
|
|
122
|
+
function aisOut(encMsg, aisTime) {
|
|
117
123
|
const enc = new AisEncode(encMsg);
|
|
118
124
|
const sentence = enc.nmea;
|
|
125
|
+
let taggString = '';
|
|
126
|
+
if (useTag) {
|
|
127
|
+
taggString = createTagBlock(aisTime)
|
|
128
|
+
}
|
|
119
129
|
if (sentence && sentence.length > 0) {
|
|
120
|
-
app.debug(sentence);
|
|
121
|
-
app.emit('nmea0183out', sentence);
|
|
130
|
+
app.debug(taggString+sentence);
|
|
131
|
+
app.emit('nmea0183out', taggString+sentence);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const m_hex = [
|
|
136
|
+
'0',
|
|
137
|
+
'1',
|
|
138
|
+
'2',
|
|
139
|
+
'3',
|
|
140
|
+
'4',
|
|
141
|
+
'5',
|
|
142
|
+
'6',
|
|
143
|
+
'7',
|
|
144
|
+
'8',
|
|
145
|
+
'9',
|
|
146
|
+
'A',
|
|
147
|
+
'B',
|
|
148
|
+
'C',
|
|
149
|
+
'D',
|
|
150
|
+
'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
|
+
}
|
|
159
|
+
|
|
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
|
+
for (let i = 0; i < tagBlock.length; i++) {
|
|
168
|
+
tagBlockChecksum ^= tagBlock.charCodeAt(i)
|
|
122
169
|
}
|
|
170
|
+
return `\\${tagBlock}*` + toHexString(tagBlockChecksum) + `\\`
|
|
123
171
|
}
|
|
124
172
|
|
|
125
173
|
//----------------------------------------------------------------------------
|
|
126
174
|
// Read and parse AIS data
|
|
127
175
|
|
|
128
176
|
readInfo = function readData(options) {
|
|
129
|
-
let i, mmsi, shipName, lat, lon, sog, cog, rot, navStat, hdg, dst, callSign, imo, id, type;
|
|
177
|
+
let i, mmsi, aisTime, aisDelay, shipName, lat, lon, sog, cog, rot, navStat, hdg, dst, callSign, imo, id, type;
|
|
130
178
|
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');
|
|
131
181
|
fetch(url, { method: 'GET' })
|
|
132
182
|
.then((res) => res.json())
|
|
133
183
|
.then((json) => {
|
|
@@ -136,6 +186,22 @@ module.exports = function createPlugin(app) {
|
|
|
136
186
|
for (i = 0; i < numberAIS; i++) {
|
|
137
187
|
const jsonKey = Object.keys(jsonContent)[i];
|
|
138
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;
|
|
195
|
+
}
|
|
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;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
139
205
|
try {
|
|
140
206
|
mmsi = jsonContent[jsonKey].mmsi;
|
|
141
207
|
} catch (error) { mmsi = null; }
|
|
@@ -174,7 +240,7 @@ module.exports = function createPlugin(app) {
|
|
|
174
240
|
}
|
|
175
241
|
} catch (error) { callSign = ''; }
|
|
176
242
|
try {
|
|
177
|
-
imo = (jsonContent[jsonKey].registrations.imo).substring(4, 20);
|
|
243
|
+
imo = (jsonContent[jsonKey].registrations.value.imo).substring(4, 20);
|
|
178
244
|
} catch (error) { imo = null; }
|
|
179
245
|
try {
|
|
180
246
|
id = jsonContent[jsonKey].design.aisShipType.value.id;
|
|
@@ -211,89 +277,103 @@ module.exports = function createPlugin(app) {
|
|
|
211
277
|
if (i === 0) {
|
|
212
278
|
own = true;
|
|
213
279
|
if (sendOwn) {
|
|
214
|
-
ais = '
|
|
280
|
+
ais = 'A';
|
|
281
|
+
} else {
|
|
282
|
+
ais = '';
|
|
215
283
|
}
|
|
216
284
|
} else {
|
|
217
285
|
own = false;
|
|
218
286
|
}
|
|
219
287
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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);
|
|
373
|
+
}
|
|
374
|
+
app.debug("--------------------------------------------------------");
|
|
375
|
+
|
|
376
|
+
}
|
|
297
377
|
}
|
|
298
378
|
}
|
|
299
379
|
const dateobj = new Date(Date.now());
|
|
@@ -323,11 +403,31 @@ module.exports = function createPlugin(app) {
|
|
|
323
403
|
default: 1,
|
|
324
404
|
title: 'How often AIS data is sent to NMEA0183 out (in minutes). E.g. 0.5 = 30s, 1 = 1min',
|
|
325
405
|
},
|
|
406
|
+
port: {
|
|
407
|
+
type: 'number',
|
|
408
|
+
title: 'HTTP port',
|
|
409
|
+
default: 3000
|
|
410
|
+
},
|
|
411
|
+
portSec: {
|
|
412
|
+
type: 'number',
|
|
413
|
+
title: 'HTTPS port',
|
|
414
|
+
default: 3443
|
|
415
|
+
},
|
|
326
416
|
sendOwn: {
|
|
327
417
|
type: 'boolean',
|
|
328
418
|
title: 'Send own AIS data, VDO',
|
|
329
419
|
default: true
|
|
330
420
|
},
|
|
421
|
+
useTag: {
|
|
422
|
+
type: 'boolean',
|
|
423
|
+
title: 'Add Tag-block',
|
|
424
|
+
default: false
|
|
425
|
+
},
|
|
426
|
+
distance: {
|
|
427
|
+
type: 'integer',
|
|
428
|
+
default: 100,
|
|
429
|
+
title: 'AIS target within range [km]',
|
|
430
|
+
},
|
|
331
431
|
},
|
|
332
432
|
};
|
|
333
433
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "signalk-vessels-to-ais",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
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": {
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"author": "Karl-Erik Gustafsson",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"
|
|
16
|
+
"haversine-distance": "^1.2.1",
|
|
17
|
+
"moment": "^2.29.1",
|
|
18
|
+
"node-fetch": "^3.1.1",
|
|
17
19
|
"ggencoder": "^1.0.5"
|
|
18
20
|
},
|
|
19
21
|
"devDependencies": {
|