signalk-vessels-to-ais 1.2.0 → 1.3.0
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 +3 -0
- package/index.js +85 -76
- package/package.json +1 -1
- 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.3.0, add: Navigational Status variations
|
|
13
|
+
- v1.2.2, fix: if own position is not available
|
|
14
|
+
- v1.2.1, fix: own vessel sending
|
|
12
15
|
- v1.2.0, updated fetch method, no need for NODE_TLS_REJECT_UNAUTHORIZED=0 anymore
|
|
13
16
|
- v1.1.5, updated vessels within selected timeframe are sent out, radius filtering around own vessel and tag-block option added
|
|
14
17
|
- 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
|
//----------------------------------------------------------------------------
|
|
@@ -96,18 +95,32 @@ module.exports = function createPlugin(app) {
|
|
|
96
95
|
|
|
97
96
|
const stateMapping = {
|
|
98
97
|
motoring: 0,
|
|
98
|
+
'UnderWayUsingEngine': 0,
|
|
99
|
+
'under way using engine': 0,
|
|
100
|
+
'underway using engine': 0,
|
|
99
101
|
anchored: 1,
|
|
102
|
+
'AtAnchor': 1,
|
|
103
|
+
'at anchor': 1,
|
|
100
104
|
'not under command': 2,
|
|
101
105
|
'restricted manouverability': 3,
|
|
102
106
|
'constrained by draft': 4,
|
|
107
|
+
'constrained by her draught': 4,
|
|
103
108
|
moored: 5,
|
|
109
|
+
'Moored': 5,
|
|
104
110
|
aground: 6,
|
|
105
111
|
fishing: 7,
|
|
112
|
+
'engaged in fishing': 7,
|
|
106
113
|
sailing: 8,
|
|
114
|
+
'UnderWaySailing': 8,
|
|
115
|
+
'under way sailing': 8,
|
|
116
|
+
'underway sailing': 8,
|
|
107
117
|
'hazardous material high speed': 9,
|
|
108
118
|
'hazardous material wing in ground': 10,
|
|
119
|
+
'reserved for future use': 13,
|
|
109
120
|
'ais-sart': 14,
|
|
110
121
|
default: 15,
|
|
122
|
+
'UnDefined': 15,
|
|
123
|
+
'undefined': 15,
|
|
111
124
|
};
|
|
112
125
|
|
|
113
126
|
//----------------------------------------------------------------------------
|
|
@@ -131,15 +144,16 @@ module.exports = function createPlugin(app) {
|
|
|
131
144
|
const sentence = enc.nmea;
|
|
132
145
|
let taggString = '';
|
|
133
146
|
if (useTag) {
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
// eslint-disable-next-line no-use-before-define
|
|
148
|
+
taggString = createTagBlock(aisTime);
|
|
149
|
+
}
|
|
136
150
|
if (sentence && sentence.length > 0) {
|
|
137
|
-
app.debug(taggString+sentence);
|
|
138
|
-
app.emit('nmea0183out', taggString+sentence);
|
|
151
|
+
app.debug(taggString + sentence);
|
|
152
|
+
app.emit('nmea0183out', taggString + sentence);
|
|
139
153
|
}
|
|
140
154
|
}
|
|
141
155
|
|
|
142
|
-
const
|
|
156
|
+
const mHex = [
|
|
143
157
|
'0',
|
|
144
158
|
'1',
|
|
145
159
|
'2',
|
|
@@ -155,60 +169,60 @@ module.exports = function createPlugin(app) {
|
|
|
155
169
|
'C',
|
|
156
170
|
'D',
|
|
157
171
|
'E',
|
|
158
|
-
'F'
|
|
159
|
-
]
|
|
160
|
-
|
|
161
|
-
function toHexString
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return
|
|
172
|
+
'F',
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
function toHexString(v) {
|
|
176
|
+
const msn = (v >> 4) & 0x0f;
|
|
177
|
+
const lsn = (v >> 0) & 0x0f;
|
|
178
|
+
return mHex[msn] + mHex[lsn];
|
|
165
179
|
}
|
|
166
180
|
|
|
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
|
|
181
|
+
function createTagBlock(aisTime) {
|
|
182
|
+
let tagBlock = '';
|
|
183
|
+
tagBlock += 's:SK0001,';
|
|
184
|
+
// tagBlock += 'c:' + aisTime + ','
|
|
185
|
+
tagBlock += `c:${Date.now(aisTime)},`;
|
|
186
|
+
tagBlock = tagBlock.slice(0, -1);
|
|
187
|
+
let tagBlockChecksum = 0;
|
|
174
188
|
for (let i = 0; i < tagBlock.length; i++) {
|
|
175
|
-
tagBlockChecksum ^= tagBlock.charCodeAt(i)
|
|
189
|
+
tagBlockChecksum ^= tagBlock.charCodeAt(i);
|
|
176
190
|
}
|
|
177
|
-
return `\\${tagBlock}
|
|
191
|
+
return `\\${tagBlock}*${toHexString(tagBlockChecksum)}\\`;
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
//----------------------------------------------------------------------------
|
|
181
195
|
// Read and parse AIS data
|
|
182
196
|
|
|
197
|
+
// eslint-disable-next-line no-shadow
|
|
183
198
|
function readData(getParam) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
let i, mmsi, aisTime, aisDelay, shipName, lat, lon, sog, cog, rot;
|
|
200
|
+
let navStat, hdg, dst, callSign, imo, id, type;
|
|
201
|
+
let draftCur, length, beam, ais, encMsg3, encMsg5, encMsg18, encMsg240, encMsg241, own;
|
|
202
|
+
const ownLat = app.getSelfPath('navigation.position.value.latitude');
|
|
203
|
+
const ownLon = app.getSelfPath('navigation.position.value.longitude');
|
|
204
|
+
if (typeof ownLat !== "undefined" && typeof ownLon !== "undefined") {
|
|
205
|
+
fetchNew(url, getParam)
|
|
189
206
|
.then((res) => res.json())
|
|
190
207
|
.then((json) => {
|
|
191
208
|
const jsonContent = JSON.parse(JSON.stringify(json));
|
|
192
209
|
const numberAIS = Object.keys(jsonContent).length;
|
|
193
210
|
for (i = 0; i < numberAIS; i++) {
|
|
194
211
|
const jsonKey = Object.keys(jsonContent)[i];
|
|
195
|
-
|
|
212
|
+
|
|
196
213
|
try {
|
|
197
214
|
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
215
|
} catch (error) {
|
|
204
216
|
if (i === 0) {
|
|
205
|
-
aisTime =
|
|
206
|
-
aisDelay = true;
|
|
217
|
+
aisTime = jsonContent[jsonKey].navigation.position.timestamp;
|
|
207
218
|
} else {
|
|
208
219
|
aisTime = null;
|
|
209
|
-
aisDelay = false;
|
|
210
220
|
}
|
|
211
221
|
}
|
|
222
|
+
|
|
223
|
+
aisDelay = (parseFloat((moment(new Date(Date.now()))
|
|
224
|
+
.diff(aisTime) / 1000).toFixed(3))) < positionUpdate;
|
|
225
|
+
|
|
212
226
|
try {
|
|
213
227
|
mmsi = jsonContent[jsonKey].mmsi;
|
|
214
228
|
} catch (error) { mmsi = null; }
|
|
@@ -267,7 +281,7 @@ module.exports = function createPlugin(app) {
|
|
|
267
281
|
try {
|
|
268
282
|
ais = jsonContent[jsonKey].sensors.ais.class.value;
|
|
269
283
|
} catch (error) { ais = null; }
|
|
270
|
-
|
|
284
|
+
|
|
271
285
|
if (shipName % 1 === 0) {
|
|
272
286
|
shipName = '';
|
|
273
287
|
}
|
|
@@ -280,7 +294,7 @@ module.exports = function createPlugin(app) {
|
|
|
280
294
|
if (type % 1 === 0) {
|
|
281
295
|
type = '';
|
|
282
296
|
}
|
|
283
|
-
|
|
297
|
+
|
|
284
298
|
if (i === 0) {
|
|
285
299
|
own = true;
|
|
286
300
|
if (sendOwn) {
|
|
@@ -291,13 +305,12 @@ module.exports = function createPlugin(app) {
|
|
|
291
305
|
} else {
|
|
292
306
|
own = false;
|
|
293
307
|
}
|
|
294
|
-
|
|
295
|
-
const a = { lat: ownLat, lon: ownLon }
|
|
296
|
-
const b = { lat
|
|
297
|
-
|
|
298
|
-
|
|
308
|
+
|
|
309
|
+
const a = { lat: ownLat, lon: ownLon };
|
|
310
|
+
const b = { lat, lon };
|
|
311
|
+
const dist = (haversine(a, b) / 1000).toFixed(2);
|
|
312
|
+
|
|
299
313
|
if (dist <= distance) {
|
|
300
|
-
|
|
301
314
|
encMsg3 = {
|
|
302
315
|
own,
|
|
303
316
|
aistype: 3, // class A position report
|
|
@@ -311,7 +324,7 @@ module.exports = function createPlugin(app) {
|
|
|
311
324
|
hdg,
|
|
312
325
|
rot,
|
|
313
326
|
};
|
|
314
|
-
|
|
327
|
+
|
|
315
328
|
encMsg5 = {
|
|
316
329
|
own,
|
|
317
330
|
aistype: 5, // class A static
|
|
@@ -328,7 +341,7 @@ module.exports = function createPlugin(app) {
|
|
|
328
341
|
dimC: beam,
|
|
329
342
|
dimD: beam,
|
|
330
343
|
};
|
|
331
|
-
|
|
344
|
+
|
|
332
345
|
encMsg18 = {
|
|
333
346
|
own,
|
|
334
347
|
aistype: 18, // class B position report
|
|
@@ -341,7 +354,7 @@ module.exports = function createPlugin(app) {
|
|
|
341
354
|
cog,
|
|
342
355
|
hdg,
|
|
343
356
|
};
|
|
344
|
-
|
|
357
|
+
|
|
345
358
|
encMsg240 = {
|
|
346
359
|
own,
|
|
347
360
|
aistype: 24, // class B static
|
|
@@ -350,7 +363,7 @@ module.exports = function createPlugin(app) {
|
|
|
350
363
|
mmsi,
|
|
351
364
|
shipname: shipName,
|
|
352
365
|
};
|
|
353
|
-
|
|
366
|
+
|
|
354
367
|
encMsg241 = {
|
|
355
368
|
own,
|
|
356
369
|
aistype: 24, // class B static
|
|
@@ -364,9 +377,10 @@ module.exports = function createPlugin(app) {
|
|
|
364
377
|
dimC: beam,
|
|
365
378
|
dimD: beam,
|
|
366
379
|
};
|
|
367
|
-
|
|
380
|
+
|
|
368
381
|
if (aisDelay && (ais === 'A' || ais === 'B')) {
|
|
369
|
-
|
|
382
|
+
// eslint-disable-next-line no-useless-concat
|
|
383
|
+
app.debug(`Distance range: ${distance}km, AIS target distance: ${dist}km` + `, Class ${ais} Vessel` + `, MMSI:${mmsi}`);
|
|
370
384
|
if (ais === 'A') {
|
|
371
385
|
app.debug(`class A, ${i}, time: ${aisTime}`);
|
|
372
386
|
aisOut(encMsg3, aisTime);
|
|
@@ -378,22 +392,17 @@ module.exports = function createPlugin(app) {
|
|
|
378
392
|
aisOut(encMsg240, aisTime);
|
|
379
393
|
aisOut(encMsg241, aisTime);
|
|
380
394
|
}
|
|
381
|
-
app.debug(
|
|
382
|
-
|
|
395
|
+
app.debug('--------------------------------------------------------');
|
|
383
396
|
}
|
|
384
397
|
}
|
|
385
398
|
}
|
|
386
399
|
const dateobj = new Date(Date.now());
|
|
387
400
|
const date = dateobj.toISOString();
|
|
388
|
-
|
|
389
|
-
context: `vessels.${app.selfId}`,
|
|
390
|
-
updates: [
|
|
391
|
-
],
|
|
392
|
-
});
|
|
393
|
-
setStatus(`AIS NMEA message send: ${date}`);
|
|
401
|
+
setStatus(`AIS NMEA message sent: ${date}`);
|
|
394
402
|
})
|
|
395
403
|
.catch((err) => console.error(err));
|
|
396
|
-
|
|
404
|
+
}
|
|
405
|
+
}
|
|
397
406
|
|
|
398
407
|
//----------------------------------------------------------------------------
|
|
399
408
|
|
|
@@ -413,22 +422,22 @@ module.exports = function createPlugin(app) {
|
|
|
413
422
|
port: {
|
|
414
423
|
type: 'number',
|
|
415
424
|
title: 'HTTP port',
|
|
416
|
-
default: 3000
|
|
425
|
+
default: 3000,
|
|
417
426
|
},
|
|
418
427
|
portSec: {
|
|
419
428
|
type: 'number',
|
|
420
429
|
title: 'HTTPS port',
|
|
421
|
-
default: 3443
|
|
430
|
+
default: 3443,
|
|
422
431
|
},
|
|
423
432
|
sendOwn: {
|
|
424
433
|
type: 'boolean',
|
|
425
434
|
title: 'Send own AIS data, VDO',
|
|
426
|
-
default: true
|
|
435
|
+
default: true,
|
|
427
436
|
},
|
|
428
437
|
useTag: {
|
|
429
438
|
type: 'boolean',
|
|
430
439
|
title: 'Add Tag-block',
|
|
431
|
-
default: false
|
|
440
|
+
default: false,
|
|
432
441
|
},
|
|
433
442
|
distance: {
|
|
434
443
|
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
|
-
};
|