signalk-vessels-to-ais 1.1.1 → 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/.eslintrc.js +24 -0
- package/README.md +5 -0
- package/index.js +338 -175
- package/package.json +10 -3
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
};
|
package/README.md
CHANGED
|
@@ -6,8 +6,13 @@ SignalK server plugin to convert other vessel data to NMEA0183 AIS format and fo
|
|
|
6
6
|
|
|
7
7
|
User can configure:
|
|
8
8
|
- How often data is sent out
|
|
9
|
+
- Own data can be added to AIS sending
|
|
9
10
|
|
|
10
11
|
New:
|
|
12
|
+
- v1.1.5, updated vessels within selected timeframe are sent out, radius filtering around own vessel and tag-block option added
|
|
13
|
+
- v1.1.4, small fix
|
|
14
|
+
- v1.1.3, add: own vessel data and sending interval modified
|
|
15
|
+
- v1.1.2, fix: http/https url selection and better error info
|
|
11
16
|
- v1.1.1, fix: current status of the plugin updated
|
|
12
17
|
- v1.1.0, fix: numeric value test for text strings of AIS
|
|
13
18
|
- v1.0.0, v1 release
|
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 =
|
|
26
|
-
const AisEncode = require(
|
|
25
|
+
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
|
|
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 = {};
|
|
@@ -31,229 +33,365 @@ module.exports = function createPlugin(app) {
|
|
|
31
33
|
plugin.name = 'Other vessels data to AIS NMEA0183';
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
let positionUpdate = null;
|
|
37
|
+
let distance;
|
|
38
|
+
let sendOwn;
|
|
39
|
+
let url;
|
|
40
|
+
let intervalRun;
|
|
41
|
+
let readInfo;
|
|
39
42
|
const setStatus = app.setPluginStatus || app.setProviderStatus;
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
let position_update;
|
|
45
|
+
let useTag;
|
|
46
|
+
|
|
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
|
+
|
|
54
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
55
|
+
url = 'https://localhost:' + portSec + '/signalk/v1/api/vessels';
|
|
56
|
+
fetch(url, { method: 'GET' })
|
|
57
|
+
.then((res) => {
|
|
58
|
+
console.log(`${plugin.id}: SSL enabled, using https`);
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
console.error(`${plugin.id}: SSL enabled, but error accessing server. Check 'Allow Readonly Access' and enable it.`);
|
|
61
|
+
setStatus("Error accessing server. Check 'Allow Readonly Access' and enable it");
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
.catch(() => {
|
|
65
|
+
url = 'http://localhost:' + port + '/signalk/v1/api/vessels';
|
|
66
|
+
fetch(url, { method: 'GET' })
|
|
67
|
+
.then((res) => {
|
|
68
|
+
console.log(`${plugin.id}: SSL disabled, using http`);
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
console.error(`${plugin.id}: SSL disabled, but error accessing server. Check 'Allow Readonly Access' and enable it.`);
|
|
71
|
+
setStatus("Error accessing server. Check 'Allow Readonly Access' and enable it");
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
.catch(() => {
|
|
75
|
+
});
|
|
76
|
+
});
|
|
42
77
|
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
positionUpdate = options.position_update;
|
|
79
|
+
distance = options.distance;
|
|
80
|
+
sendOwn = options.sendOwn;
|
|
45
81
|
|
|
46
|
-
|
|
47
|
-
setTimeout(clear, 15000);
|
|
48
|
-
interval_id2 = setInterval(read_info,(position_update * 60000));
|
|
82
|
+
app.debug('Plugin started');
|
|
49
83
|
|
|
84
|
+
intervalRun = setInterval(readInfo, (positionUpdate * 60000));
|
|
50
85
|
};
|
|
51
86
|
|
|
52
|
-
//----------------------------------------------------------------------------
|
|
53
|
-
// State Mapping
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
//----------------------------------------------------------------------------
|
|
72
|
-
// Rad to Deg
|
|
73
|
-
function radians_to_degrees(radians)
|
|
74
|
-
{
|
|
75
|
-
var pi = Math.PI;
|
|
76
|
-
return ((radians * 180)/pi);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
//----------------------------------------------------------------------------
|
|
80
|
-
// m/s to knots
|
|
81
|
-
function ms_to_knots(speed)
|
|
82
|
-
{
|
|
83
|
-
return ((speed * 3.6) / 1.852);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
//----------------------------------------------------------------------------
|
|
87
|
-
// Clear start interval
|
|
88
|
-
|
|
89
|
-
function clear() {
|
|
90
|
-
clearInterval(interval_id1);
|
|
87
|
+
//----------------------------------------------------------------------------
|
|
88
|
+
// State Mapping
|
|
89
|
+
|
|
90
|
+
const stateMapping = {
|
|
91
|
+
motoring: 0,
|
|
92
|
+
anchored: 1,
|
|
93
|
+
'not under command': 2,
|
|
94
|
+
'restricted manouverability': 3,
|
|
95
|
+
'constrained by draft': 4,
|
|
96
|
+
moored: 5,
|
|
97
|
+
aground: 6,
|
|
98
|
+
fishing: 7,
|
|
99
|
+
sailing: 8,
|
|
100
|
+
'hazardous material high speed': 9,
|
|
101
|
+
'hazardous material wing in ground': 10,
|
|
102
|
+
'ais-sart': 14,
|
|
103
|
+
default: 15,
|
|
91
104
|
};
|
|
92
105
|
|
|
93
|
-
//----------------------------------------------------------------------------
|
|
94
|
-
//
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
//----------------------------------------------------------------------------
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
var enc= new AisEncode(enc_msg)
|
|
105
|
-
var sentence = enc.nmea
|
|
106
|
-
if ( sentence && sentence.length > 0 )
|
|
107
|
-
{
|
|
108
|
-
app.debug(sentence);
|
|
109
|
-
app.emit('nmea0183out', sentence);
|
|
106
|
+
//----------------------------------------------------------------------------
|
|
107
|
+
// Rad to Deg
|
|
108
|
+
function radToDegrees(radians) {
|
|
109
|
+
const pi = Math.PI;
|
|
110
|
+
return ((radians * 180) / pi);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//----------------------------------------------------------------------------
|
|
114
|
+
// m/s to knots
|
|
115
|
+
function msToKnots(speed) {
|
|
116
|
+
return ((speed * 3.6) / 1.852);
|
|
110
117
|
}
|
|
111
|
-
}
|
|
112
118
|
|
|
113
|
-
//----------------------------------------------------------------------------
|
|
114
|
-
//
|
|
119
|
+
//----------------------------------------------------------------------------
|
|
120
|
+
// nmea out
|
|
121
|
+
|
|
122
|
+
function aisOut(encMsg, aisTime) {
|
|
123
|
+
const enc = new AisEncode(encMsg);
|
|
124
|
+
const sentence = enc.nmea;
|
|
125
|
+
let taggString = '';
|
|
126
|
+
if (useTag) {
|
|
127
|
+
taggString = createTagBlock(aisTime)
|
|
128
|
+
}
|
|
129
|
+
if (sentence && sentence.length > 0) {
|
|
130
|
+
app.debug(taggString+sentence);
|
|
131
|
+
app.emit('nmea0183out', taggString+sentence);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
115
134
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
+
}
|
|
119
159
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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)
|
|
169
|
+
}
|
|
170
|
+
return `\\${tagBlock}*` + toHexString(tagBlockChecksum) + `\\`
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
//----------------------------------------------------------------------------
|
|
174
|
+
// Read and parse AIS data
|
|
175
|
+
|
|
176
|
+
readInfo = function readData(options) {
|
|
177
|
+
let i, mmsi, aisTime, aisDelay, shipName, lat, lon, sog, cog, rot, navStat, hdg, dst, callSign, imo, id, type;
|
|
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');
|
|
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;
|
|
154
195
|
}
|
|
155
|
-
|
|
156
|
-
|
|
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;
|
|
157
203
|
}
|
|
158
|
-
|
|
159
|
-
|
|
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;
|
|
160
240
|
}
|
|
161
|
-
|
|
162
|
-
|
|
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
|
+
|
|
277
|
+
if (i === 0) {
|
|
278
|
+
own = true;
|
|
279
|
+
if (sendOwn) {
|
|
280
|
+
ais = 'A';
|
|
281
|
+
} else {
|
|
282
|
+
ais = '';
|
|
163
283
|
}
|
|
284
|
+
} else {
|
|
285
|
+
own = false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const a = { lat: ownLat, lon: ownLon }
|
|
289
|
+
const b = { lat: lat, lon: lon }
|
|
290
|
+
let dist = (haversine(a, b)/1000).toFixed(2);
|
|
164
291
|
|
|
165
|
-
|
|
292
|
+
if (dist <= distance) {
|
|
293
|
+
|
|
294
|
+
encMsg3 = {
|
|
295
|
+
own,
|
|
166
296
|
aistype: 3, // class A position report
|
|
167
297
|
repeat: 0,
|
|
168
|
-
mmsi
|
|
298
|
+
mmsi,
|
|
169
299
|
navstatus: navStat,
|
|
170
|
-
sog
|
|
171
|
-
lon
|
|
172
|
-
lat
|
|
173
|
-
cog
|
|
174
|
-
hdg
|
|
175
|
-
rot
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
300
|
+
sog,
|
|
301
|
+
lon,
|
|
302
|
+
lat,
|
|
303
|
+
cog,
|
|
304
|
+
hdg,
|
|
305
|
+
rot,
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
encMsg5 = {
|
|
309
|
+
own,
|
|
310
|
+
aistype: 5, // class A static
|
|
180
311
|
repeat: 0,
|
|
181
|
-
mmsi
|
|
182
|
-
imo
|
|
312
|
+
mmsi,
|
|
313
|
+
imo,
|
|
183
314
|
cargo: id,
|
|
184
315
|
callsign: callSign,
|
|
185
|
-
shipname:
|
|
186
|
-
draught:
|
|
316
|
+
shipname: shipName,
|
|
317
|
+
draught: draftCur,
|
|
187
318
|
destination: dst,
|
|
188
319
|
dimA: 0,
|
|
189
320
|
dimB: length,
|
|
190
321
|
dimC: beam,
|
|
191
|
-
dimD: beam
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
322
|
+
dimD: beam,
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
encMsg18 = {
|
|
326
|
+
own,
|
|
195
327
|
aistype: 18, // class B position report
|
|
196
328
|
repeat: 0,
|
|
197
|
-
mmsi
|
|
198
|
-
sog
|
|
329
|
+
mmsi,
|
|
330
|
+
sog,
|
|
199
331
|
accuracy: 0,
|
|
200
|
-
lon
|
|
201
|
-
lat
|
|
202
|
-
cog
|
|
203
|
-
hdg
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
332
|
+
lon,
|
|
333
|
+
lat,
|
|
334
|
+
cog,
|
|
335
|
+
hdg,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
encMsg240 = {
|
|
339
|
+
own,
|
|
207
340
|
aistype: 24, // class B static
|
|
208
341
|
repeat: 0,
|
|
209
342
|
part: 0,
|
|
210
|
-
mmsi
|
|
211
|
-
shipname:
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
343
|
+
mmsi,
|
|
344
|
+
shipname: shipName,
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
encMsg241 = {
|
|
348
|
+
own,
|
|
215
349
|
aistype: 24, // class B static
|
|
216
350
|
repeat: 0,
|
|
217
351
|
part: 1,
|
|
218
|
-
mmsi
|
|
352
|
+
mmsi,
|
|
219
353
|
cargo: id,
|
|
220
354
|
callsign: callSign,
|
|
221
355
|
dimA: 0,
|
|
222
356
|
dimB: length,
|
|
223
357
|
dimC: beam,
|
|
224
|
-
dimD: beam
|
|
225
|
-
}
|
|
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("--------------------------------------------------------");
|
|
226
375
|
|
|
227
|
-
if (ais == "A") {
|
|
228
|
-
app.debug("class A " + i);
|
|
229
|
-
ais_out(enc_msg_3);
|
|
230
|
-
ais_out(enc_msg_5);
|
|
231
|
-
}
|
|
232
|
-
if (ais == "B") {
|
|
233
|
-
app.debug("class B " + i);
|
|
234
|
-
ais_out(enc_msg_18);
|
|
235
|
-
ais_out(enc_msg_24_0);
|
|
236
|
-
ais_out(enc_msg_24_1);
|
|
237
376
|
}
|
|
238
|
-
|
|
239
377
|
}
|
|
240
|
-
|
|
241
|
-
|
|
378
|
+
}
|
|
379
|
+
const dateobj = new Date(Date.now());
|
|
380
|
+
const date = dateobj.toISOString();
|
|
242
381
|
app.handleMessage(plugin.id, {
|
|
243
382
|
context: `vessels.${app.selfId}`,
|
|
244
383
|
updates: [
|
|
245
|
-
]
|
|
384
|
+
],
|
|
246
385
|
});
|
|
247
|
-
setStatus(`Number of AIS targets sent: ${numberAIS-1} (${date})`);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
386
|
+
setStatus(`Number of AIS targets sent: ${numberAIS - 1} (${date})`);
|
|
387
|
+
})
|
|
388
|
+
.catch((err) => console.error(err));
|
|
251
389
|
};
|
|
252
390
|
|
|
253
|
-
//----------------------------------------------------------------------------
|
|
391
|
+
//----------------------------------------------------------------------------
|
|
254
392
|
|
|
255
393
|
plugin.stop = function stop() {
|
|
256
|
-
clearInterval(
|
|
394
|
+
clearInterval(intervalRun);
|
|
257
395
|
app.debug('Stopped');
|
|
258
396
|
};
|
|
259
397
|
|
|
@@ -261,10 +399,35 @@ function ais_out(enc_msg) {
|
|
|
261
399
|
type: 'object',
|
|
262
400
|
properties: {
|
|
263
401
|
position_update: {
|
|
264
|
-
type: '
|
|
402
|
+
type: 'number',
|
|
265
403
|
default: 1,
|
|
266
|
-
title: 'How often AIS data is sent to NMEA0183 out (in minutes)',
|
|
267
|
-
}
|
|
404
|
+
title: 'How often AIS data is sent to NMEA0183 out (in minutes). E.g. 0.5 = 30s, 1 = 1min',
|
|
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
|
+
},
|
|
416
|
+
sendOwn: {
|
|
417
|
+
type: 'boolean',
|
|
418
|
+
title: 'Send own AIS data, VDO',
|
|
419
|
+
default: true
|
|
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
|
+
},
|
|
268
431
|
},
|
|
269
432
|
};
|
|
270
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,14 @@
|
|
|
13
13
|
"author": "Karl-Erik Gustafsson",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
16
|
+
"haversine-distance": "^1.2.1",
|
|
17
|
+
"moment": "^2.29.1",
|
|
18
|
+
"node-fetch": "^3.1.1",
|
|
19
|
+
"ggencoder": "^1.0.5"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"eslint": "^7.17.0",
|
|
23
|
+
"eslint-config-airbnb-base": "^14.2.1",
|
|
24
|
+
"eslint-plugin-import": "^2.22.1"
|
|
18
25
|
}
|
|
19
26
|
}
|