@canboat/canboatjs 2.11.1 → 3.0.0-beta.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/.github/workflows/publish.yml +3 -3
- package/bin/analyzerjs +8 -2
- package/bin/to-pgn +3 -4
- package/index.js +0 -3
- package/ios_canboat.js/main.js +2 -0
- package/ios_canboat.js/main.js.LICENSE.txt +17 -0
- package/ios_canboat.js/main.js.map +1 -0
- package/lib/candevice.js +330 -8
- package/lib/fromPgn.js +32 -9
- package/lib/pgns.js +3 -2
- package/lib/simpleCan.js +1 -5
- package/lib/stringMsg.js +0 -12
- package/lib/toPgn.js +14 -13
- package/lib/ydgw02.js +2 -19
- package/package.json +3 -3
- package/examples/signalk-device-emulator/dist/index.d.ts +0 -11
- package/examples/signalk-device-emulator/dist/index.js +0 -129
- package/examples/signalk-device-emulator/index.js +0 -1
- package/examples/signalk-device-emulator/package.json +0 -24
- package/examples/signalk-device-emulator/signalk-device-emulator-1.0.0.tgz +0 -0
- package/lib/n2kDevice.js +0 -365
- package/lib/yddevice.js +0 -47
package/lib/candevice.js
CHANGED
|
@@ -20,9 +20,8 @@ const _ = require('lodash')
|
|
|
20
20
|
const Uint64LE = require('int64-buffer').Uint64LE
|
|
21
21
|
const { defaultTransmitPGNs, getIndustryCode, getManufacturerCode, getDeviceClassCode } = require('./codes')
|
|
22
22
|
const { toPgn } = require('./toPgn')
|
|
23
|
-
const N2kDevice = require('./n2kDevice')
|
|
24
|
-
|
|
25
23
|
let packageJson
|
|
24
|
+
|
|
26
25
|
try
|
|
27
26
|
{
|
|
28
27
|
packageJson = require('../' + 'package.json')
|
|
@@ -31,21 +30,344 @@ try
|
|
|
31
30
|
|
|
32
31
|
const deviceTransmitPGNs = [ 60928, 59904, 126996, 126464 ]
|
|
33
32
|
|
|
34
|
-
class CanDevice extends
|
|
33
|
+
class CanDevice extends EventEmitter {
|
|
35
34
|
constructor (canbus, options) {
|
|
36
|
-
super(
|
|
35
|
+
super()
|
|
36
|
+
|
|
37
|
+
if ( options.addressClaim ) {
|
|
38
|
+
this.addressClaim = options.addressClaim
|
|
39
|
+
this.addressClaim.pgn = 60928
|
|
40
|
+
this.addressClaim.dst = 255
|
|
41
|
+
this.addressClaim.prio = 6
|
|
42
|
+
} else {
|
|
43
|
+
this.addressClaim = {
|
|
44
|
+
pgn: 60928,
|
|
45
|
+
dst: 255,
|
|
46
|
+
prio:6,
|
|
47
|
+
"Unique Number": 1263,
|
|
48
|
+
"Manufacturer Code": 999,
|
|
49
|
+
"Device Function": 130, // PC gateway
|
|
50
|
+
"Device Class": 25, // Inter/Intranetwork Device
|
|
51
|
+
"Device Instance Lower": 0,
|
|
52
|
+
"Device Instance Upper": 0,
|
|
53
|
+
"System Instance": 0,
|
|
54
|
+
"Industry Group": 4, // Marine
|
|
55
|
+
"Reserved1": 1,
|
|
56
|
+
"Reserved2": 2
|
|
57
|
+
}
|
|
58
|
+
this.addressClaim["Unique Number"] = options.uniqueNumber || Math.floor(Math.random() * Math.floor(2097151))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let version = packageJson ? packageJson.version : "1.0"
|
|
62
|
+
|
|
63
|
+
if ( options.productInfo ) {
|
|
64
|
+
this.productInfo = options.productInfo
|
|
65
|
+
this.productInfo.pgn = 126996
|
|
66
|
+
this.productInfo.dst = 255
|
|
67
|
+
} else {
|
|
68
|
+
this.productInfo = {
|
|
69
|
+
pgn: 126996,
|
|
70
|
+
dst: 255,
|
|
71
|
+
"NMEA 2000 Version": 1300,
|
|
72
|
+
"Product Code": 667, // Just made up..
|
|
73
|
+
"Model ID": "Signal K",
|
|
74
|
+
"Model Version": "canboatjs",
|
|
75
|
+
"Model Serial Code": options.uniqueNumber ? options.uniqueNumber.toString() : "000001",
|
|
76
|
+
"Certification Level": 0,
|
|
77
|
+
"Load Equivalency": 1
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.productInfo["Software Version Code"] = version
|
|
82
|
+
|
|
83
|
+
if ( options.serverVersion && options.serverUrl ) {
|
|
84
|
+
this.configurationInfo = {
|
|
85
|
+
pgn: 126998,
|
|
86
|
+
dst: 255,
|
|
87
|
+
"Installation Description #1": options.serverUrl,
|
|
88
|
+
"Installation Description #2": options.serverDescription,
|
|
89
|
+
"Manufacturer Information": options.serverVersion
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
37
93
|
this.canbus = canbus
|
|
94
|
+
this.options = _.isUndefined(options) ? {} : options
|
|
95
|
+
|
|
96
|
+
this.address = _.isUndefined(options.preferredAddress) ? 100 : options.preferredAddress
|
|
97
|
+
this.cansend = false
|
|
98
|
+
this.foundConflict = false
|
|
99
|
+
this.heartbeatCounter = 0
|
|
100
|
+
this.devices = {}
|
|
101
|
+
|
|
102
|
+
if ( !options.disableDefaultTransmitPGNs ) {
|
|
103
|
+
this.transmitPGNs = _.union(deviceTransmitPGNs, defaultTransmitPGNs)
|
|
104
|
+
} else {
|
|
105
|
+
this.transmitPGNs = [...deviceTransmitPGNs]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if ( this.options.transmitPGNs ) {
|
|
109
|
+
this.transmitPGNs = _.union(this.transmitPGNs,
|
|
110
|
+
this.options.transmitPGNs)
|
|
111
|
+
}
|
|
38
112
|
|
|
39
113
|
if ( options.app ) {
|
|
40
114
|
options.app.on(options.analyzerOutEvent || 'N2KAnalyzerOut', this.n2kMessage.bind(this))
|
|
41
115
|
}
|
|
42
116
|
}
|
|
43
117
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
118
|
+
start() {
|
|
119
|
+
sendISORequest(this, 60928, 254)
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
sendAddressClaim(this)
|
|
122
|
+
}, 1000)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
n2kMessage(pgn) {
|
|
126
|
+
if ( pgn.dst == 255 || pgn.dst == this.address ) {
|
|
127
|
+
try {
|
|
128
|
+
if ( pgn.pgn == 59904 ) {
|
|
129
|
+
handleISORequest(this, pgn)
|
|
130
|
+
} else if ( pgn.pgn == 126208 ) {
|
|
131
|
+
handleGroupFunction(this, pgn)
|
|
132
|
+
} else if ( pgn.pgn == 60928 ) {
|
|
133
|
+
handleISOAddressClaim(this, pgn)
|
|
134
|
+
} else if ( pgn.pgn == 126996 ) {
|
|
135
|
+
handleProductInformation(this, pgn)
|
|
136
|
+
}
|
|
137
|
+
} catch ( err ) {
|
|
138
|
+
console.error(err)
|
|
139
|
+
console.error(err.stack)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/*
|
|
143
|
+
var handler = this.handlers[pgn.pgn.toString()]
|
|
144
|
+
if ( pgn.dst == this.address )
|
|
145
|
+
debug(`handler ${handler}`)
|
|
146
|
+
if ( _.isFunction(handler) ) {
|
|
147
|
+
debug(`got handled PGN %j ${handled}`, pgn)
|
|
148
|
+
handler(pgn)
|
|
149
|
+
}
|
|
150
|
+
*/
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function sendPGN(device, pgn, src, dest) {
|
|
156
|
+
pgn.src = src || device.address
|
|
157
|
+
debug('Sending PGN %j', pgn)
|
|
158
|
+
device.canbus.sendPGN(pgn)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function handleISORequest(device, n2kMsg) {
|
|
162
|
+
debug('handleISORequest %j', n2kMsg)
|
|
163
|
+
|
|
164
|
+
switch (n2kMsg.fields.PGN) {
|
|
165
|
+
case 126996: // Product Information request
|
|
166
|
+
sendProductInformation(device)
|
|
167
|
+
break;
|
|
168
|
+
case 126998: // Config Information request
|
|
169
|
+
sendConfigInformation(device)
|
|
170
|
+
break;
|
|
171
|
+
case 60928: // ISO address claim request
|
|
172
|
+
sendPGN(device, device.addressClaim)
|
|
173
|
+
break;
|
|
174
|
+
case 126464:
|
|
175
|
+
sendPGNList(device)
|
|
176
|
+
break;
|
|
177
|
+
default:
|
|
178
|
+
if ( !device.options.disableNAKs ) {
|
|
179
|
+
debug(`Got unsupported ISO request for PGN ${n2kMsg.fields.PGN}. Sending NAK.`)
|
|
180
|
+
sendNAKAcknowledgement(device, n2kMsg.src, n2kMsg.fields.PGN)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function handleGroupFunction(device, n2kMsg) {
|
|
186
|
+
debug('handleGroupFunction %j', n2kMsg)
|
|
187
|
+
if(n2kMsg.fields.functionCodeRequest) {
|
|
188
|
+
handleRequestGroupFunction(device, n2kMsg)
|
|
189
|
+
} else if(n2kMsg.fields.functionCodeCommand) {
|
|
190
|
+
handleCommandGroupFunction(device, n2kMsg)
|
|
191
|
+
} else {
|
|
192
|
+
debug('Got unsupported Group Function PGN: %j', n2kMsg)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function handleRequestGroupFunction(device, n2kMsg) {
|
|
196
|
+
if ( !device.options.disableNAKs ) {
|
|
197
|
+
// We really don't support group function requests for any PGNs yet -> always respond with pgnErrorCode 1 = "PGN not supported"
|
|
198
|
+
debug("Sending 'PGN Not Supported' Group Function response for requested PGN", n2kMsg.fields.PGN)
|
|
199
|
+
|
|
200
|
+
const acknowledgement = {
|
|
201
|
+
pgn: 126208,
|
|
202
|
+
dst: n2kMsg.src,
|
|
203
|
+
"Function Code": 2,
|
|
204
|
+
"PGN": n2kMsg.fields.PGN,
|
|
205
|
+
"PGN error code": 4,
|
|
206
|
+
"Transmission interval/Priority error code": 0,
|
|
207
|
+
"# of Parameters": 0
|
|
208
|
+
}
|
|
209
|
+
sendPGN(device, acknowledgement)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function handleCommandGroupFunction(device, n2kMsg) {
|
|
214
|
+
if ( !device.options.disableNAKs ) {
|
|
215
|
+
// We really don't support group function commands for any PGNs yet -> always respond with pgnErrorCode 1 = "PGN not supported"
|
|
216
|
+
debug("Sending 'PGN Not Supported' Group Function response for commanded PGN", n2kMsg.fields.PGN)
|
|
217
|
+
|
|
218
|
+
const acknowledgement = {
|
|
219
|
+
pgn: 126208,
|
|
220
|
+
dst: n2kMsg.src,
|
|
221
|
+
"Function Code": 2,
|
|
222
|
+
"PGN": n2kMsg.fields.PGN,
|
|
223
|
+
"PGN error code": 4,
|
|
224
|
+
"Transmission interval/Priority error code": 0,
|
|
225
|
+
"# of Parameters": 0
|
|
226
|
+
}
|
|
227
|
+
sendPGN(device, acknowledgement)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function handleISOAddressClaim(device, n2kMsg) {
|
|
233
|
+
if ( n2kMsg.src != device.address ) {
|
|
234
|
+
if ( !device.devices[n2kMsg.src] ) {
|
|
235
|
+
debug(`registering device ${n2kMsg.src}`)
|
|
236
|
+
device.devices[n2kMsg.src] = { addressClaim: n2kMsg }
|
|
237
|
+
if ( device.cansend ) {
|
|
238
|
+
//sendISORequest(device, 126996, undefined, n2kMsg.src)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
debug('Checking ISO address claim. %j', n2kMsg)
|
|
245
|
+
|
|
246
|
+
const uint64ValueFromReceivedClaim = getISOAddressClaimAsUint64(n2kMsg)
|
|
247
|
+
const uint64ValueFromOurOwnClaim = getISOAddressClaimAsUint64(device.addressClaim)
|
|
248
|
+
|
|
249
|
+
if(uint64ValueFromOurOwnClaim < uint64ValueFromReceivedClaim) {
|
|
250
|
+
debug(`Address conflict detected! Kept our address as ${device.address}.`)
|
|
251
|
+
sendAddressClaim(device) // We have smaller address claim data -> we can keep our address -> re-claim it
|
|
252
|
+
} else if(uint64ValueFromOurOwnClaim > uint64ValueFromReceivedClaim) {
|
|
253
|
+
this.foundConflict = true
|
|
254
|
+
increaseOwnAddress(device) // We have bigger address claim data -> we have to change our address
|
|
255
|
+
sendAddressClaim(device)
|
|
256
|
+
debug(`Address conflict detected! Changed our address to ${device.address}.`)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function increaseOwnAddress(device) {
|
|
261
|
+
var start = device.address
|
|
262
|
+
do {
|
|
263
|
+
device.address = (device.address + 1) % 253
|
|
264
|
+
} while ( device.address != start && device.devices[device.address] )
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function handleProductInformation(device, n2kMsg) {
|
|
268
|
+
if ( !device.devices[n2kMsg.src] ) {
|
|
269
|
+
device.devices[n2kMsg.src] = {}
|
|
270
|
+
}
|
|
271
|
+
debug('got production information %j', n2kMsg)
|
|
272
|
+
device.devices[n2kMsg.src].productInformation = n2kMsg
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function sendHeartbeat(device)
|
|
276
|
+
{
|
|
277
|
+
device.heartbeatCounter = device.heartbeatCounter + 1
|
|
278
|
+
if ( device.heartbeatCounter > 252 )
|
|
279
|
+
{
|
|
280
|
+
device.heartbeatCounter = 0
|
|
281
|
+
}
|
|
282
|
+
sendPGN(device,{
|
|
283
|
+
pgn: 126993,
|
|
284
|
+
dst: 255,
|
|
285
|
+
prio:7,
|
|
286
|
+
"Data transmit offset": "00:01:00",
|
|
287
|
+
"Sequence Counter": device.heartbeatCounter,
|
|
288
|
+
"Controller 1 State":"Error Active"
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
function sendAddressClaim(device) {
|
|
294
|
+
if ( device.devices[device.address] ) {
|
|
295
|
+
//someone already has this address, so find a free one
|
|
296
|
+
increaseOwnAddress(device)
|
|
297
|
+
}
|
|
298
|
+
debug(`Sending address claim ${device.address}`)
|
|
299
|
+
sendPGN(device, device.addressClaim)
|
|
300
|
+
setTimeout(() => {
|
|
301
|
+
if ( !device.foundConflict ) {
|
|
302
|
+
debug('no address conflics, enabling send')
|
|
303
|
+
device.cansend = true
|
|
304
|
+
if ( device.options.app ) {
|
|
305
|
+
device.options.app.emit('nmea2000OutAvailable')
|
|
306
|
+
}
|
|
307
|
+
sendISORequest(device, 126996)
|
|
308
|
+
device.heartbeatInterval = setInterval(() => {
|
|
309
|
+
sendHeartbeat(device)
|
|
310
|
+
}, 60*1000)
|
|
311
|
+
/*
|
|
312
|
+
_.keys(device.devices).forEach((address) => {
|
|
313
|
+
sendISORequest(device, 126996, undefined, address)
|
|
314
|
+
})
|
|
315
|
+
*/
|
|
316
|
+
|
|
317
|
+
}
|
|
318
|
+
}, 250)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function sendISORequest(device, pgn, src, dst=255) {
|
|
322
|
+
debug(`Sending iso request for ${pgn} to ${dst}`)
|
|
323
|
+
|
|
324
|
+
const isoRequest = {
|
|
325
|
+
pgn: 59904,
|
|
326
|
+
dst: dst,
|
|
327
|
+
"PGN": pgn
|
|
328
|
+
}
|
|
329
|
+
sendPGN(device, isoRequest, src)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
function sendProductInformation(device) {
|
|
334
|
+
debug("Sending product info..")
|
|
335
|
+
|
|
336
|
+
sendPGN(device, device.productInfo)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function sendConfigInformation(device) {
|
|
340
|
+
if ( device.configurationInfo ) {
|
|
341
|
+
debug("Sending config info..")
|
|
342
|
+
sendPGN(device, device.configurationInfo)
|
|
48
343
|
}
|
|
49
344
|
}
|
|
50
345
|
|
|
346
|
+
function sendNAKAcknowledgement(device, src, requestedPGN) {
|
|
347
|
+
const acknowledgement = {
|
|
348
|
+
pgn: 59392,
|
|
349
|
+
dst: src,
|
|
350
|
+
Control: 1,
|
|
351
|
+
"Group Function": 255,
|
|
352
|
+
PGN: requestedPGN
|
|
353
|
+
}
|
|
354
|
+
sendPGN(device, acknowledgement)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function sendPGNList(device, src) {
|
|
358
|
+
//FIXME: for now, adding everything that signalk-to-nmea2000 supports
|
|
359
|
+
//need a way for plugins, etc. to register the pgns they provide
|
|
360
|
+
const pgnList = {
|
|
361
|
+
pgn: 126464,
|
|
362
|
+
dst: src,
|
|
363
|
+
"Function Code": 0,
|
|
364
|
+
list: device.transmitPGNs
|
|
365
|
+
}
|
|
366
|
+
sendPGN(device, pgnList)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function getISOAddressClaimAsUint64(pgn) {
|
|
370
|
+
return new Uint64LE(toPgn(pgn))
|
|
371
|
+
}
|
|
372
|
+
|
|
51
373
|
module.exports = CanDevice
|
package/lib/fromPgn.js
CHANGED
|
@@ -62,6 +62,14 @@ class Parser extends EventEmitter {
|
|
|
62
62
|
if ( this.options.returnNulls === undefined ) {
|
|
63
63
|
this.options.returnNulls = false
|
|
64
64
|
}
|
|
65
|
+
|
|
66
|
+
if ( this.options.useCamel === undefined ) {
|
|
67
|
+
this.options.useCamel = false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if ( this.options.useCamelCompat === undefined ) {
|
|
71
|
+
this.options.useCamelCompat = false
|
|
72
|
+
}
|
|
65
73
|
|
|
66
74
|
this.name = pkg.name
|
|
67
75
|
this.version = pkg.version
|
|
@@ -204,6 +212,7 @@ class Parser extends EventEmitter {
|
|
|
204
212
|
let RepeatingFields = pgnData.RepeatingFieldSet1Size ? pgnData.RepeatingFieldSet1Size : 0
|
|
205
213
|
|
|
206
214
|
pgn.fields = {}
|
|
215
|
+
|
|
207
216
|
try {
|
|
208
217
|
var fields = pgnData.Fields
|
|
209
218
|
|
|
@@ -234,8 +243,8 @@ class Parser extends EventEmitter {
|
|
|
234
243
|
const ts = _.get(pgn, 'timestamp', new Date())
|
|
235
244
|
pgn.timestamp = _.isDate(ts) ? ts.toISOString() : ts
|
|
236
245
|
if ( !_.isUndefined(value) && (value != null ||
|
|
237
|
-
|
|
238
|
-
pgn.fields
|
|
246
|
+
this.options.returnNulls) ) {
|
|
247
|
+
this.setField(pgn.fields, field, value)
|
|
239
248
|
}
|
|
240
249
|
this.emit('pgn', pgn)
|
|
241
250
|
cb && cb(undefined, pgn)
|
|
@@ -255,7 +264,7 @@ class Parser extends EventEmitter {
|
|
|
255
264
|
|
|
256
265
|
if ( !_.isUndefined(value) && (value != null ||
|
|
257
266
|
this.options.returnNulls) ) {
|
|
258
|
-
pgn.fields
|
|
267
|
+
this.setField(pgn.fields, field, value)
|
|
259
268
|
}
|
|
260
269
|
}
|
|
261
270
|
if ( RepeatingFields > 0 ) {
|
|
@@ -265,7 +274,7 @@ class Parser extends EventEmitter {
|
|
|
265
274
|
let count
|
|
266
275
|
|
|
267
276
|
if ( pgnData.RepeatingFieldSet1CountField !== undefined ) {
|
|
268
|
-
count = pgn.fields[pgnData.Fields[pgnData.RepeatingFieldSet1CountField-1].Name]
|
|
277
|
+
count = pgn.fields[pgnData.Fields[pgnData.RepeatingFieldSet1CountField-1][this.options.useCamel ? 'Id' : 'Name']]
|
|
269
278
|
} else {
|
|
270
279
|
count = 2048
|
|
271
280
|
}
|
|
@@ -276,7 +285,7 @@ class Parser extends EventEmitter {
|
|
|
276
285
|
if ( bs.bitsLeft > 0 ) {
|
|
277
286
|
var value = readField(this.options, true, pgn, field, bs, fields)
|
|
278
287
|
if ( !_.isUndefined(value) && value != null ) {
|
|
279
|
-
group
|
|
288
|
+
this.setField(group, field, value)
|
|
280
289
|
}
|
|
281
290
|
}
|
|
282
291
|
})
|
|
@@ -306,6 +315,17 @@ class Parser extends EventEmitter {
|
|
|
306
315
|
}
|
|
307
316
|
}
|
|
308
317
|
|
|
318
|
+
setField(res, field, value) {
|
|
319
|
+
if ( this.options.useCamelCompat ) {
|
|
320
|
+
res[field.Id] = value
|
|
321
|
+
res[field.Name] = value
|
|
322
|
+
} else if ( this.options.useCamel ) {
|
|
323
|
+
res[field.Id] = value
|
|
324
|
+
} else {
|
|
325
|
+
res[field.Name] = value
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
309
329
|
findNonMatchPgn(pgnList) {
|
|
310
330
|
return pgnList.find(f => {
|
|
311
331
|
return !f.Fields.find(f => !_.isUndefined(f.Match))
|
|
@@ -616,8 +636,9 @@ function readField(options, runPostProcessor, pgn, field, bs, fields) {
|
|
|
616
636
|
if ( options.checkForInvalidFields !== false && max !== 'undefined' &&
|
|
617
637
|
field.FieldType !== 'LOOKUP' &&
|
|
618
638
|
field.FieldType !== 'FIELDTYPE_LOOKUP' &&
|
|
639
|
+
field.FieldType !== 'PGN' &&
|
|
619
640
|
field.BitLength > 1 &&
|
|
620
|
-
max - value
|
|
641
|
+
max - value < 0 ) {
|
|
621
642
|
//console.log(`Bad field ${field.Name} ${max - value}`)
|
|
622
643
|
value = null
|
|
623
644
|
} if ( field.Resolution && typeof value === 'number' ) {
|
|
@@ -724,9 +745,9 @@ function readValue(options, pgn, field, bs, fields, bitLength) {
|
|
|
724
745
|
var y = bs.readUint32()
|
|
725
746
|
|
|
726
747
|
if ( field.Signed ) {
|
|
727
|
-
value = x === 0xffffffff && y == 0x7fffffff
|
|
748
|
+
value = (x === 0xffffffff || x === 0xfffffffe) && y == 0x7fffffff ? null : new Int64LE(y,x)
|
|
728
749
|
} else {
|
|
729
|
-
value = x === 0xffffffff && y == 0xffffffff ? null : new Uint64LE(y,x)
|
|
750
|
+
value = (x === 0xffffffff || x === 0xfffffffe) && y == 0xffffffff ? null : new Uint64LE(y,x)
|
|
730
751
|
}
|
|
731
752
|
} else if ( bitLength <= 64 ) {
|
|
732
753
|
value = bs.readBits(bitLength, field.Signed)
|
|
@@ -930,7 +951,7 @@ function lookupKeyBitLength(data, fields)
|
|
|
930
951
|
{
|
|
931
952
|
let field = fields.find(field => (field.Name === 'Key'))
|
|
932
953
|
|
|
933
|
-
let val = data['Key']
|
|
954
|
+
let val = data['Key'] || data['key']
|
|
934
955
|
if ( typeof val === 'string' ) {
|
|
935
956
|
val = lookupFieldTypeEnumerationValue(field.LookupFieldTypeEnumeration, val)
|
|
936
957
|
}
|
|
@@ -967,6 +988,8 @@ fieldTypePostProcessors['TIME'] = (field, value) => {
|
|
|
967
988
|
return value
|
|
968
989
|
}
|
|
969
990
|
|
|
991
|
+
fieldTypePostProcessors['DURATION'] = fieldTypePostProcessors['TIME']
|
|
992
|
+
|
|
970
993
|
fieldTypePostProcessors['Pressure'] = (field, value) => {
|
|
971
994
|
if (field.Unit)
|
|
972
995
|
{
|
package/lib/pgns.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { flow, first, isArray, isEmpty, propertyOf } = require('lodash/fp')
|
|
2
|
-
const
|
|
2
|
+
const canboat = require('@canboat/pgns')
|
|
3
|
+
const pgns = canboat.pgns
|
|
3
4
|
const pgnsIK = { PGNs: [] } //require('@canboat/pgns/pgns-ik')
|
|
4
5
|
const pgnsNGT = { PGNs: [] } //require('@canboat/pgns/pgns-ngt')
|
|
5
6
|
const _ = require('lodash')
|
|
@@ -7,7 +8,7 @@ const debug = require('debug')('canboatjs:pgns')
|
|
|
7
8
|
|
|
8
9
|
function organizePGNs() {
|
|
9
10
|
const res = {}
|
|
10
|
-
const all = [...
|
|
11
|
+
const all = [...canboat.getPGNs(), ...pgnsIK.PGNs, ...pgnsNGT.PGNs]
|
|
11
12
|
all.forEach(pgn => {
|
|
12
13
|
if ( !res[pgn.PGN] ) {
|
|
13
14
|
res[pgn.PGN] = []
|
package/lib/simpleCan.js
CHANGED
|
@@ -39,7 +39,7 @@ SimpleCan.prototype.start = function () {
|
|
|
39
39
|
disableDefaultTransmitPGNs: true,
|
|
40
40
|
disableNAKs: true
|
|
41
41
|
},
|
|
42
|
-
|
|
42
|
+
)
|
|
43
43
|
this.candevice.start()
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -98,10 +98,6 @@ SimpleCan.prototype.sendPGN = function (msg) {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
SimpleCan.prototype.sendActisenseFormat = function (msg) {
|
|
102
|
-
this.sendPGN(msg)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
101
|
function binToActisense(pgn, data, length) {
|
|
106
102
|
return (
|
|
107
103
|
pgn.timestamp +
|
package/lib/stringMsg.js
CHANGED
|
@@ -8,7 +8,6 @@ const {
|
|
|
8
8
|
const {
|
|
9
9
|
buildCanId, encodeCanIdString, parseCanId, parseCanIdStr,
|
|
10
10
|
} = require('./canId')
|
|
11
|
-
const moment = require('moment')
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Helper function that helps merge canId fields with format, data, and others.
|
|
@@ -118,17 +117,6 @@ exports.encodeYDRAW = ({ data, ...canIdInfo }) => {
|
|
|
118
117
|
return pgns.map(buffer => canId + ' ' + byteString(buffer, ' '))
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
//16:29:27.082 R 19F51323 01 02<CR><LF>
|
|
122
|
-
exports.encodeYDRAWFull = ({ data, ...canIdInfo }) => {
|
|
123
|
-
const canId = encodeCanIdString(canIdInfo)
|
|
124
|
-
const pgns = data.length > 8 || canIdInfo.pgn == 126720 ? getPlainPGNs(data) : [ data ]
|
|
125
|
-
return pgns.map(buffer =>
|
|
126
|
-
moment().utc().format('hh:mm:ss.SSS')
|
|
127
|
-
+ ' R '
|
|
128
|
-
+ canId + ' '
|
|
129
|
-
+ byteString(buffer, ' '))
|
|
130
|
-
}
|
|
131
|
-
|
|
132
120
|
const get0183Sentence = (msg) => {
|
|
133
121
|
let sentence = msg
|
|
134
122
|
if (sentence.charAt(0) === '\\') {
|
package/lib/toPgn.js
CHANGED
|
@@ -21,7 +21,7 @@ const BitStream = require('bit-buffer').BitStream
|
|
|
21
21
|
const Int64LE = require('int64-buffer').Int64LE
|
|
22
22
|
const Uint64LE = require('int64-buffer').Uint64LE
|
|
23
23
|
const { getPlainPGNs } = require('./utilities')
|
|
24
|
-
const { encodeActisense, encodeActisenseN2KACSII, encodeYDRAW,
|
|
24
|
+
const { encodeActisense, encodeActisenseN2KACSII, encodeYDRAW, parseActisense, encodePCDIN, encodeMXPGN, encodePDGY } = require('./stringMsg')
|
|
25
25
|
const { encodeN2KActisense } = require('./n2k-actisense')
|
|
26
26
|
const { encodeCanId } = require('./canId')
|
|
27
27
|
const { getIndustryCode, getManufacturerCode } = require('./codes')
|
|
@@ -74,7 +74,7 @@ function toPgn(data) {
|
|
|
74
74
|
let RepeatingFields = pgnData.RepeatingFieldSet1Size ? pgnData.RepeatingFieldSet1Size : 0
|
|
75
75
|
for ( var index = 0; index < fields.length - RepeatingFields; index++ ) {
|
|
76
76
|
var field = fields[index]
|
|
77
|
-
var value = data[field.Name]
|
|
77
|
+
var value = data[field.Name] !== undefined ? data[field.Name] : data[field.Id]
|
|
78
78
|
|
|
79
79
|
if ( !_.isUndefined(field.Match) ) {
|
|
80
80
|
//console.log(`matching ${field.Name} ${field.Match} ${value} ${_.isString(value)}`)
|
|
@@ -98,7 +98,7 @@ function toPgn(data) {
|
|
|
98
98
|
data.list.forEach(repeat => {
|
|
99
99
|
for (var index = 0; index < RepeatingFields; index++ ) {
|
|
100
100
|
var field = fields[pgnData.Fields.length-RepeatingFields+index]
|
|
101
|
-
var value = repeat[field.Name]
|
|
101
|
+
var value = repeat[field.Name] !== undefined ? repeat[field.Name] : repeat[field.Id]
|
|
102
102
|
|
|
103
103
|
writeField(bs, pgn_number, field, data, value, fields)
|
|
104
104
|
}
|
|
@@ -171,7 +171,7 @@ function writeField(bs, pgn_number, field, data, value, fields, bitLength) {
|
|
|
171
171
|
}
|
|
172
172
|
} else {
|
|
173
173
|
let type = field.FieldType
|
|
174
|
-
if ( field.
|
|
174
|
+
if ( field.Id === 'industryCode' ) {
|
|
175
175
|
if ( _.isString(value) ) {
|
|
176
176
|
value = Number(getIndustryCode(value))
|
|
177
177
|
}
|
|
@@ -184,6 +184,10 @@ function writeField(bs, pgn_number, field, data, value, fields, bitLength) {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
if ( field.FieldType == "NUMBER" && _.isString(value) ) {
|
|
188
|
+
value = Number(value)
|
|
189
|
+
}
|
|
190
|
+
|
|
187
191
|
if (field.Resolution && typeof value === 'number' ) {
|
|
188
192
|
value = Number((value / field.Resolution).toFixed(0))
|
|
189
193
|
}
|
|
@@ -191,9 +195,11 @@ function writeField(bs, pgn_number, field, data, value, fields, bitLength) {
|
|
|
191
195
|
if ( field.FieldType && fieldTypeWriters[field.FieldType] ) {
|
|
192
196
|
fieldTypeWriters[field.FieldType](pgn_number, field, value, bs)
|
|
193
197
|
} else {
|
|
198
|
+
/*
|
|
194
199
|
if ( _.isString(value) && typeof bitLength !== 'undefined' && bitLength !== 0 ) {
|
|
195
200
|
value = Number(value)
|
|
196
|
-
|
|
201
|
+
}
|
|
202
|
+
*/
|
|
197
203
|
|
|
198
204
|
if ( field.Unit === "kWh" ) {
|
|
199
205
|
value /= 3.6e6; // 1 kWh = 3.6 MJ.
|
|
@@ -284,7 +290,7 @@ function lookupKeyBitLength(data, fields)
|
|
|
284
290
|
{
|
|
285
291
|
let field = fields.find(field => (field.Name === 'Key'))
|
|
286
292
|
|
|
287
|
-
let val = data['Key']
|
|
293
|
+
let val = data['Key'] || data['key']
|
|
288
294
|
if ( typeof val === 'string' ) {
|
|
289
295
|
val = lookupFieldTypeEnumerationValue(field.LookupFieldTypeEnumeration, val)
|
|
290
296
|
}
|
|
@@ -327,10 +333,6 @@ function pgnToYdgwRawFormat(info) {
|
|
|
327
333
|
return encodeYDRAW({ ...info, data: toPgn(info) })
|
|
328
334
|
}
|
|
329
335
|
|
|
330
|
-
function pgnToYdgwFullRawFormat(info) {
|
|
331
|
-
return encodeYDRAWFull({ ...info, data: toPgn(info) })
|
|
332
|
-
}
|
|
333
|
-
|
|
334
336
|
function pgnToPCDIN(info) {
|
|
335
337
|
return encodePCDIN({ ...info, data: toPgn(info) })
|
|
336
338
|
}
|
|
@@ -340,7 +342,6 @@ function pgnToMXPGN(info) {
|
|
|
340
342
|
}
|
|
341
343
|
|
|
342
344
|
const actisenseToYdgwRawFormat = _.flow(parseActisense, encodeYDRAW)
|
|
343
|
-
const actisenseToYdgwFullRawFormat = _.flow(parseActisense, encodeYDRAWFull)
|
|
344
345
|
const actisenseToPCDIN = _.flow(parseActisense, encodePCDIN)
|
|
345
346
|
const actisenseToMXPGN = _.flow(parseActisense, encodeMXPGN)
|
|
346
347
|
const actisenseToiKonvert = _.flow(parseActisense, encodePDGY)
|
|
@@ -460,6 +461,8 @@ fieldTypeMappers['TIME'] = (field, value) => {
|
|
|
460
461
|
return value
|
|
461
462
|
}
|
|
462
463
|
|
|
464
|
+
fieldTypeMappers['DURATION'] = fieldTypeMappers['TIME']
|
|
465
|
+
|
|
463
466
|
/*
|
|
464
467
|
fieldTypeMappers['Manufacturer code'] = (field, value) => {
|
|
465
468
|
if ( _.isString(value) ) {
|
|
@@ -495,9 +498,7 @@ module.exports.toPgn = toPgn
|
|
|
495
498
|
module.exports.toiKonvertSerialFormat = toiKonvertSerialFormat
|
|
496
499
|
module.exports.pgnToiKonvertSerialFormat = pgnToiKonvertSerialFormat
|
|
497
500
|
module.exports.pgnToYdgwRawFormat = pgnToYdgwRawFormat
|
|
498
|
-
module.exports.pgnToYdgwFullRawFormat = pgnToYdgwFullRawFormat
|
|
499
501
|
module.exports.actisenseToYdgwRawFormat = actisenseToYdgwRawFormat
|
|
500
|
-
module.exports.actisenseToYdgwFullRawFormat = actisenseToYdgwFullRawFormat
|
|
501
502
|
module.exports.pgnToActisenseSerialFormat = pgnToActisenseSerialFormat
|
|
502
503
|
module.exports.pgnToActisenseN2KAsciiFormat = pgnToActisenseN2KAsciiFormat
|
|
503
504
|
module.exports.pgnToN2KActisenseFormat = pgnToN2KActisenseFormat
|
package/lib/ydgw02.js
CHANGED
|
@@ -20,7 +20,7 @@ const FromPgn = require('./fromPgn').Parser
|
|
|
20
20
|
const Parser = require('./fromPgn').Parser
|
|
21
21
|
const _ = require('lodash')
|
|
22
22
|
const { defaultTransmitPGNs } = require('./codes')
|
|
23
|
-
const { pgnToYdgwRawFormat,
|
|
23
|
+
const { pgnToYdgwRawFormat, actisenseToYdgwRawFormat } = require('./toPgn')
|
|
24
24
|
|
|
25
25
|
const pgnsSent = {}
|
|
26
26
|
|
|
@@ -63,11 +63,6 @@ function Ydgw02Stream (options, type) {
|
|
|
63
63
|
options.app.emit('connectionwrite', { providerId: options.providerId })
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
options.app.on('ydFullRawOut', (msgs) => {
|
|
67
|
-
this.sendYdgwFullPGN(msgs)
|
|
68
|
-
options.app.emit('connectionwrite', { providerId: options.providerId })
|
|
69
|
-
})
|
|
70
|
-
|
|
71
66
|
//this.sendString('$PDGY,N2NET_OFFLINE')
|
|
72
67
|
|
|
73
68
|
if ( type === 'usb' ) {
|
|
@@ -87,24 +82,12 @@ Ydgw02Stream.prototype.sendString = function (msg) {
|
|
|
87
82
|
Ydgw02Stream.prototype.sendPGN = function (pgn) {
|
|
88
83
|
let now = Date.now()
|
|
89
84
|
let lastSent = pgnsSent[pgn.pgn]
|
|
90
|
-
|
|
91
|
-
if ( pgn.ydFullFormat === true ) {
|
|
92
|
-
msgs = pgnToYdgwFullRawFormat(pgn)
|
|
93
|
-
} else {
|
|
94
|
-
msgs = pgnToYdgwRawFormat(pgn)
|
|
95
|
-
}
|
|
96
|
-
msgs.forEach(raw => {
|
|
85
|
+
pgnToYdgwRawFormat(pgn).forEach(raw => {
|
|
97
86
|
this.sendString(raw + '\r\n')
|
|
98
87
|
})
|
|
99
88
|
pgnsSent[pgn.pgn] = now
|
|
100
89
|
}
|
|
101
90
|
|
|
102
|
-
Ydgw02Stream.prototype.sendYdgwFullPGN = function (msgs) {
|
|
103
|
-
msgs.forEach(raw => {
|
|
104
|
-
this.sendString(raw + '\r\n')
|
|
105
|
-
})
|
|
106
|
-
}
|
|
107
|
-
|
|
108
91
|
Ydgw02Stream.prototype.sendYdgwPGN = function (msg) {
|
|
109
92
|
|
|
110
93
|
actisenseToYdgwRawFormat(msg).forEach(raw => {
|