@canboat/canboatjs 3.3.2 → 3.3.4
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/dist/bin/actisense-file.js +2 -0
- package/dist/bin/actisense-file.js.map +1 -1
- package/dist/bin/actisense-n2k-tcp.js +2 -0
- package/dist/bin/actisense-n2k-tcp.js.map +1 -1
- package/dist/bin/actisense-serialjs.js +2 -0
- package/dist/bin/actisense-serialjs.js.map +1 -1
- package/dist/bin/analyzerjs.js +35 -8
- package/dist/bin/analyzerjs.js.map +1 -1
- package/dist/bin/candumpjs.js +7 -4
- package/dist/bin/candumpjs.js.map +1 -1
- package/dist/bin/cansend.js +2 -0
- package/dist/bin/cansend.js.map +1 -1
- package/dist/bin/to-pgn.js +2 -0
- package/dist/bin/to-pgn.js.map +1 -1
- package/dist/bin/utils.d.ts +4 -0
- package/dist/bin/utils.d.ts.map +1 -0
- package/dist/bin/utils.js +15 -0
- package/dist/bin/utils.js.map +1 -0
- package/dist/bin/ydvr-file.d.ts +3 -0
- package/dist/bin/ydvr-file.d.ts.map +1 -0
- package/dist/bin/ydvr-file.js +31 -0
- package/dist/bin/ydvr-file.js.map +1 -0
- package/dist/fromPgn.d.ts.map +1 -1
- package/dist/fromPgn.js +1 -0
- package/dist/fromPgn.js.map +1 -1
- package/dist/stringMsg.d.ts +2 -0
- package/dist/stringMsg.d.ts.map +1 -1
- package/dist/stringMsg.js +18 -1
- package/dist/stringMsg.js.map +1 -1
- package/dist/stringMsg.test.js +15 -1
- package/dist/stringMsg.test.js.map +1 -1
- package/package.json +3 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/.github/workflows/publish.yml +0 -32
- package/.github/workflows/release_on_tag.yml +0 -27
- package/.github/workflows/require_pr_label.yml +0 -13
- package/.github/workflows/test.yml +0 -28
- package/.github/workflows/test_canboat_changes.yml +0 -92
- package/examples/signalk-device-emulator/index.js +0 -1
- package/examples/signalk-device-emulator/package.json +0 -24
- package/examples/simpleCan.js +0 -42
- package/ios.js +0 -67
- package/lib/actisense-serial.ts +0 -644
- package/lib/bin/actisense-file.ts +0 -53
- package/lib/bin/actisense-n2k-tcp.ts +0 -50
- package/lib/bin/actisense-serialjs.ts +0 -55
- package/lib/bin/analyzerjs.ts +0 -66
- package/lib/bin/candumpjs.ts +0 -100
- package/lib/bin/cansend.ts +0 -131
- package/lib/bin/ikonvert-serial.ts +0 -44
- package/lib/bin/to-pgn.ts +0 -65
- package/lib/bin/ydvr-file +0 -33
- package/lib/canId.test.js +0 -61
- package/lib/canId.ts +0 -84
- package/lib/canbus.ts +0 -293
- package/lib/candevice.ts +0 -41
- package/lib/codes.ts +0 -21
- package/lib/discovery.ts +0 -118
- package/lib/fromPgn.ts +0 -1217
- package/lib/fromPgnStream.ts +0 -54
- package/lib/ikonvert.ts +0 -250
- package/lib/index.ts +0 -48
- package/lib/n2k-actisense.test.js +0 -58
- package/lib/n2k-actisense.ts +0 -152
- package/lib/n2kDevice.ts +0 -509
- package/lib/pgns.test.ts +0 -12
- package/lib/pgns.ts +0 -191
- package/lib/simpleCan.ts +0 -140
- package/lib/stringMsg.test.js +0 -273
- package/lib/stringMsg.ts +0 -464
- package/lib/toPgn.ts +0 -601
- package/lib/utilities.test.js +0 -8
- package/lib/utilities.ts +0 -169
- package/lib/venus-mqtt.js +0 -118
- package/lib/venus.js +0 -88
- package/lib/w2k01.ts +0 -142
- package/lib/yddevice.ts +0 -48
- package/lib/ydgw02.ts +0 -197
- package/lib/ydvr.js +0 -138
package/lib/n2kDevice.ts
DELETED
|
@@ -1,509 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright 2025 Scott Bender (scott@scottbender.net)
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
PGN,
|
|
19
|
-
PGN_60928,
|
|
20
|
-
PGN_59904,
|
|
21
|
-
PGN_126208_NmeaRequestGroupFunction,
|
|
22
|
-
PGN_126208_NmeaCommandGroupFunction,
|
|
23
|
-
PGN_126208_NmeaAcknowledgeGroupFunction,
|
|
24
|
-
PGN_126996,
|
|
25
|
-
PGN_126993,
|
|
26
|
-
PGN_59392,
|
|
27
|
-
PGN_126464,
|
|
28
|
-
PgnListFunction,
|
|
29
|
-
PgnErrorCode,
|
|
30
|
-
TransmissionInterval,
|
|
31
|
-
ControllerState,
|
|
32
|
-
IsoControl
|
|
33
|
-
} from '@canboat/ts-pgns'
|
|
34
|
-
import { EventEmitter } from 'node:events'
|
|
35
|
-
import _ from 'lodash'
|
|
36
|
-
import { Uint64LE } from 'int64-buffer'
|
|
37
|
-
import { defaultTransmitPGNs } from './codes'
|
|
38
|
-
import { toPgn } from './toPgn'
|
|
39
|
-
import packageJson from '../package.json'
|
|
40
|
-
import { getPersistedData, savePersistedData } from './utilities'
|
|
41
|
-
import { createDebug } from './utilities'
|
|
42
|
-
|
|
43
|
-
const deviceTransmitPGNs = [60928, 59904, 126996, 126464]
|
|
44
|
-
|
|
45
|
-
export class N2kDevice extends EventEmitter {
|
|
46
|
-
addressClaim: any
|
|
47
|
-
productInfo: any
|
|
48
|
-
configurationInfo: any
|
|
49
|
-
options: any
|
|
50
|
-
address: number
|
|
51
|
-
cansend: boolean
|
|
52
|
-
foundConflict: boolean
|
|
53
|
-
heartbeatCounter: number
|
|
54
|
-
devices: any
|
|
55
|
-
sentAvailable: boolean
|
|
56
|
-
addressClaimDetectionTime: number
|
|
57
|
-
transmitPGNs: number[]
|
|
58
|
-
addressClaimSentAt?: number
|
|
59
|
-
addressClaimChecker?: any
|
|
60
|
-
heartbeatInterval?: any
|
|
61
|
-
debug: any
|
|
62
|
-
|
|
63
|
-
constructor(options: any, debugName: string) {
|
|
64
|
-
super()
|
|
65
|
-
|
|
66
|
-
this.options = options === undefined ? {} : options
|
|
67
|
-
this.debug = createDebug(debugName, options)
|
|
68
|
-
|
|
69
|
-
let uniqueNumber: number
|
|
70
|
-
if (options.uniqueNumber !== undefined) {
|
|
71
|
-
uniqueNumber = options.uniqueNumber
|
|
72
|
-
} else {
|
|
73
|
-
uniqueNumber = this.getPersistedData('uniqueNumber')
|
|
74
|
-
if (uniqueNumber === undefined) {
|
|
75
|
-
uniqueNumber = Math.floor(Math.random() * Math.floor(2097151))
|
|
76
|
-
this.savePersistedData('uniqueNumber', uniqueNumber)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (options.addressClaim) {
|
|
81
|
-
this.addressClaim = options.addressClaim
|
|
82
|
-
this.addressClaim.pgn = 60928
|
|
83
|
-
this.addressClaim.dst = 255
|
|
84
|
-
this.addressClaim.prio = 6
|
|
85
|
-
} else {
|
|
86
|
-
this.addressClaim = {
|
|
87
|
-
pgn: 60928,
|
|
88
|
-
dst: 255,
|
|
89
|
-
prio: 6,
|
|
90
|
-
'Manufacturer Code': 999,
|
|
91
|
-
'Device Function': 130, // PC gateway
|
|
92
|
-
'Device Class': 25, // Inter/Intranetwork Device
|
|
93
|
-
'Device Instance Lower': 0,
|
|
94
|
-
'Device Instance Upper': 0,
|
|
95
|
-
'System Instance': 0,
|
|
96
|
-
'Industry Group': 4, // Marine
|
|
97
|
-
Reserved1: 1,
|
|
98
|
-
Reserved2: 2
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
this.addressClaim['Unique Number'] = uniqueNumber
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const version = packageJson ? packageJson.version : '1.0'
|
|
105
|
-
|
|
106
|
-
if (options.productInfo) {
|
|
107
|
-
this.productInfo = options.productInfo
|
|
108
|
-
this.productInfo.pgn = 126996
|
|
109
|
-
this.productInfo.dst = 255
|
|
110
|
-
} else {
|
|
111
|
-
this.productInfo = {
|
|
112
|
-
pgn: 126996,
|
|
113
|
-
dst: 255,
|
|
114
|
-
'NMEA 2000 Version': 1300,
|
|
115
|
-
'Product Code': 667, // Just made up..
|
|
116
|
-
'Model ID': 'Signal K',
|
|
117
|
-
'Model Version': getModelVersion(options),
|
|
118
|
-
'Model Serial Code': uniqueNumber.toString(),
|
|
119
|
-
'Certification Level': 0,
|
|
120
|
-
'Load Equivalency': 1
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
this.productInfo['Software Version Code'] = version
|
|
125
|
-
|
|
126
|
-
if (options.serverVersion && options.serverUrl) {
|
|
127
|
-
this.configurationInfo = {
|
|
128
|
-
pgn: 126998,
|
|
129
|
-
dst: 255,
|
|
130
|
-
'Installation Description #1': options.serverUrl,
|
|
131
|
-
'Installation Description #2': options.serverDescription,
|
|
132
|
-
'Manufacturer Information': options.serverVersion
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
let address: number | undefined = undefined
|
|
137
|
-
|
|
138
|
-
address = this.getPersistedData('lastAddress')
|
|
139
|
-
|
|
140
|
-
if (address === undefined) {
|
|
141
|
-
address = _.isUndefined(options.preferredAddress)
|
|
142
|
-
? 100
|
|
143
|
-
: options.preferredAddress
|
|
144
|
-
}
|
|
145
|
-
this.address = address!
|
|
146
|
-
this.cansend = false
|
|
147
|
-
this.foundConflict = false
|
|
148
|
-
this.heartbeatCounter = 0
|
|
149
|
-
this.devices = {}
|
|
150
|
-
this.sentAvailable = false
|
|
151
|
-
this.addressClaimDetectionTime =
|
|
152
|
-
options.addressClaimDetectionTime !== undefined
|
|
153
|
-
? options.addressClaimDetectionTime
|
|
154
|
-
: 5000
|
|
155
|
-
|
|
156
|
-
if (!options.disableDefaultTransmitPGNs) {
|
|
157
|
-
this.transmitPGNs = _.union(deviceTransmitPGNs, defaultTransmitPGNs)
|
|
158
|
-
} else {
|
|
159
|
-
this.transmitPGNs = [...deviceTransmitPGNs]
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (this.options.transmitPGNs) {
|
|
163
|
-
this.transmitPGNs = _.union(this.transmitPGNs, this.options.transmitPGNs)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
start() {
|
|
168
|
-
sendISORequest(this, 60928, 254)
|
|
169
|
-
setTimeout(() => {
|
|
170
|
-
sendAddressClaim(this)
|
|
171
|
-
}, 1000)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
getPersistedData(key: string) {
|
|
175
|
-
try {
|
|
176
|
-
return getPersistedData(this.options, this.options.providerId, key)
|
|
177
|
-
} catch (err: any) {
|
|
178
|
-
this.debug('reading persisted data %o', err)
|
|
179
|
-
if (err.code !== 'ENOENT') {
|
|
180
|
-
console.error(err)
|
|
181
|
-
this.setError(err.message)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
savePersistedData(key: string, value: any) {
|
|
187
|
-
try {
|
|
188
|
-
savePersistedData(this.options, this.options.providerId, key, value)
|
|
189
|
-
} catch (err: any) {
|
|
190
|
-
console.error(err)
|
|
191
|
-
this.setError(err.message)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
setStatus(msg: string) {
|
|
196
|
-
if (this.options.app && this.options.app.setPluginStatus) {
|
|
197
|
-
this.options.app.setProviderStatus(this.options.providerId, msg)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
setError(msg: string) {
|
|
202
|
-
if (this.options.app && this.options.app.setPluginStatus) {
|
|
203
|
-
this.options.app.setProviderError(this.options.providerId, msg)
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
n2kMessage(pgn: PGN) {
|
|
208
|
-
if (pgn.dst == 255 || pgn.dst == this.address) {
|
|
209
|
-
try {
|
|
210
|
-
if (pgn.pgn == 59904) {
|
|
211
|
-
handleISORequest(this, pgn)
|
|
212
|
-
} else if (pgn.pgn == 126208) {
|
|
213
|
-
handleGroupFunction(this, pgn as PGN_126208_NmeaRequestGroupFunction)
|
|
214
|
-
} else if (pgn.pgn == 60928) {
|
|
215
|
-
handleISOAddressClaim(this, pgn as PGN_60928)
|
|
216
|
-
} else if (pgn.pgn == 126996) {
|
|
217
|
-
handleProductInformation(this, pgn)
|
|
218
|
-
}
|
|
219
|
-
} catch (err) {
|
|
220
|
-
console.error(err)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/*
|
|
224
|
-
var handler = this.handlers[pgn.pgn.toString()]
|
|
225
|
-
if ( pgn.dst == this.address )
|
|
226
|
-
debug(`handler ${handler}`)
|
|
227
|
-
if ( _.isFunction(handler) ) {
|
|
228
|
-
debug(`got handled PGN %j ${handled}`, pgn)
|
|
229
|
-
handler(pgn)
|
|
230
|
-
}
|
|
231
|
-
*/
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
sendPGN(_pgn: PGN, _src: number | undefined = undefined) {}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function getModelVersion(options: any) {
|
|
239
|
-
if (options.app?.config?.getExternalHostname !== undefined) {
|
|
240
|
-
return `${options.app.config.ssl ? 'https' : 'http'}://${options.app.config.getExternalHostname()}:${options.app.config.getExternalPort()}`
|
|
241
|
-
} else {
|
|
242
|
-
return 'canboatjs'
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function handleISORequest(device: N2kDevice, n2kMsg: PGN_59904) {
|
|
247
|
-
device.debug('handleISORequest %j', n2kMsg)
|
|
248
|
-
|
|
249
|
-
const PGN = Number(n2kMsg.fields.pgn)
|
|
250
|
-
|
|
251
|
-
switch (PGN) {
|
|
252
|
-
case 126996: // Product Information request
|
|
253
|
-
sendProductInformation(device)
|
|
254
|
-
break
|
|
255
|
-
case 126998: // Config Information request
|
|
256
|
-
sendConfigInformation(device)
|
|
257
|
-
break
|
|
258
|
-
case 60928: // ISO address claim request
|
|
259
|
-
device.debug('sending address claim %j', device.addressClaim)
|
|
260
|
-
device.sendPGN(device.addressClaim as PGN)
|
|
261
|
-
break
|
|
262
|
-
case 126464:
|
|
263
|
-
sendPGNList(device, n2kMsg.src!)
|
|
264
|
-
break
|
|
265
|
-
default:
|
|
266
|
-
if (!device.options.disableNAKs) {
|
|
267
|
-
device.debug(`Got unsupported ISO request for PGN ${PGN}. Sending NAK.`)
|
|
268
|
-
sendNAKAcknowledgement(device, n2kMsg.src!, PGN)
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function handleGroupFunction(
|
|
274
|
-
device: N2kDevice,
|
|
275
|
-
n2kMsg: PGN_126208_NmeaRequestGroupFunction
|
|
276
|
-
) {
|
|
277
|
-
device.debug('handleGroupFunction %j', n2kMsg)
|
|
278
|
-
const functionCode = n2kMsg.fields.functionCode
|
|
279
|
-
if (functionCode === 'Request') {
|
|
280
|
-
handleRequestGroupFunction(device, n2kMsg)
|
|
281
|
-
} else if (functionCode === 'Command') {
|
|
282
|
-
handleCommandGroupFunction(device, n2kMsg)
|
|
283
|
-
} else {
|
|
284
|
-
device.debug('Got unsupported Group Function PGN: %j', n2kMsg)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function handleRequestGroupFunction(
|
|
288
|
-
device: N2kDevice,
|
|
289
|
-
n2kMsg: PGN_126208_NmeaRequestGroupFunction
|
|
290
|
-
) {
|
|
291
|
-
if (!device.options.disableNAKs) {
|
|
292
|
-
// We really don't support group function requests for any PGNs yet -> always respond with pgnErrorCode 1 = "PGN not supported"
|
|
293
|
-
|
|
294
|
-
const PGN = n2kMsg.fields.pgn
|
|
295
|
-
|
|
296
|
-
device.debug(
|
|
297
|
-
"Sending 'PGN Not Supported' Group Function response for requested PGN",
|
|
298
|
-
PGN
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
const acknowledgement = new PGN_126208_NmeaAcknowledgeGroupFunction(
|
|
302
|
-
{
|
|
303
|
-
pgn: PGN,
|
|
304
|
-
pgnErrorCode: PgnErrorCode.NotSupported,
|
|
305
|
-
transmissionIntervalPriorityErrorCode:
|
|
306
|
-
TransmissionInterval.Acknowledge,
|
|
307
|
-
numberOfParameters: 0,
|
|
308
|
-
list: []
|
|
309
|
-
},
|
|
310
|
-
n2kMsg.src!
|
|
311
|
-
)
|
|
312
|
-
device.sendPGN(acknowledgement)
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function handleCommandGroupFunction(
|
|
317
|
-
device: N2kDevice,
|
|
318
|
-
n2kMsg: PGN_126208_NmeaCommandGroupFunction
|
|
319
|
-
) {
|
|
320
|
-
if (!device.options.disableNAKs) {
|
|
321
|
-
// We really don't support group function commands for any PGNs yet -> always respond with pgnErrorCode 1 = "PGN not supported"
|
|
322
|
-
|
|
323
|
-
const PGN = n2kMsg.fields.pgn
|
|
324
|
-
|
|
325
|
-
device.debug(
|
|
326
|
-
"Sending 'PGN Not Supported' Group Function response for commanded PGN",
|
|
327
|
-
PGN
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
const acknowledgement = new PGN_126208_NmeaAcknowledgeGroupFunction(
|
|
331
|
-
{
|
|
332
|
-
pgn: PGN,
|
|
333
|
-
pgnErrorCode: PgnErrorCode.NotSupported,
|
|
334
|
-
transmissionIntervalPriorityErrorCode:
|
|
335
|
-
TransmissionInterval.Acknowledge,
|
|
336
|
-
numberOfParameters: 0,
|
|
337
|
-
list: []
|
|
338
|
-
},
|
|
339
|
-
n2kMsg.src!
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
device.sendPGN(acknowledgement)
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function handleISOAddressClaim(device: N2kDevice, n2kMsg: PGN_60928) {
|
|
348
|
-
if (n2kMsg.src != device.address) {
|
|
349
|
-
if (!device.devices[n2kMsg.src!]) {
|
|
350
|
-
device.debug(`registering device ${n2kMsg.src}`)
|
|
351
|
-
device.devices[n2kMsg.src!] = { addressClaim: n2kMsg }
|
|
352
|
-
if (device.cansend) {
|
|
353
|
-
//sendISORequest(device, 126996, undefined, n2kMsg.src)
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
return
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
device.debug('Checking ISO address claim. %j', n2kMsg)
|
|
360
|
-
|
|
361
|
-
const uint64ValueFromReceivedClaim = getISOAddressClaimAsUint64(n2kMsg)
|
|
362
|
-
const uint64ValueFromOurOwnClaim = getISOAddressClaimAsUint64(
|
|
363
|
-
device.addressClaim
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
if (uint64ValueFromOurOwnClaim < uint64ValueFromReceivedClaim) {
|
|
367
|
-
device.debug(
|
|
368
|
-
`Address conflict detected! Kept our address as ${device.address}.`
|
|
369
|
-
)
|
|
370
|
-
sendAddressClaim(device) // We have smaller address claim data -> we can keep our address -> re-claim it
|
|
371
|
-
} else if (uint64ValueFromOurOwnClaim > uint64ValueFromReceivedClaim) {
|
|
372
|
-
device.foundConflict = true
|
|
373
|
-
increaseOwnAddress(device) // We have bigger address claim data -> we have to change our address
|
|
374
|
-
device.debug(
|
|
375
|
-
`Address conflict detected! trying address ${device.address}.`
|
|
376
|
-
)
|
|
377
|
-
sendAddressClaim(device)
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
function increaseOwnAddress(device: N2kDevice) {
|
|
382
|
-
const start = device.address
|
|
383
|
-
do {
|
|
384
|
-
device.address = (device.address + 1) % 253
|
|
385
|
-
} while (device.address != start && device.devices[device.address])
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function handleProductInformation(device: N2kDevice, n2kMsg: PGN_126996) {
|
|
389
|
-
if (!device.devices[n2kMsg.src!]) {
|
|
390
|
-
device.devices[n2kMsg.src!] = {}
|
|
391
|
-
}
|
|
392
|
-
device.debug('got product information %j', n2kMsg)
|
|
393
|
-
device.devices[n2kMsg.src!].productInformation = n2kMsg
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function sendHeartbeat(device: N2kDevice) {
|
|
397
|
-
device.heartbeatCounter = device.heartbeatCounter + 1
|
|
398
|
-
if (device.heartbeatCounter > 252) {
|
|
399
|
-
device.heartbeatCounter = 0
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const hb = new PGN_126993({
|
|
403
|
-
dataTransmitOffset: 60,
|
|
404
|
-
sequenceCounter: device.heartbeatCounter,
|
|
405
|
-
controller1State: ControllerState.ErrorActive
|
|
406
|
-
})
|
|
407
|
-
|
|
408
|
-
device.sendPGN(hb)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function sendAddressClaim(device: N2kDevice) {
|
|
412
|
-
if (device.devices[device.address]) {
|
|
413
|
-
//someone already has this address, so find a free one
|
|
414
|
-
increaseOwnAddress(device)
|
|
415
|
-
}
|
|
416
|
-
device.debug(`Sending address claim ${device.address}`)
|
|
417
|
-
device.sendPGN(device.addressClaim)
|
|
418
|
-
device.setStatus(`Claimed address ${device.address}`)
|
|
419
|
-
device.addressClaimSentAt = Date.now()
|
|
420
|
-
if (device.addressClaimChecker) {
|
|
421
|
-
clearTimeout(device.addressClaimChecker)
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
device.addressClaimChecker = setTimeout(() => {
|
|
425
|
-
//if ( Date.now() - device.addressClaimSentAt > 1000 ) {
|
|
426
|
-
//device.addressClaimChecker = null
|
|
427
|
-
device.debug('claimed address %d', device.address)
|
|
428
|
-
|
|
429
|
-
device.savePersistedData('lastAddress', device.address)
|
|
430
|
-
|
|
431
|
-
device.cansend = true
|
|
432
|
-
if (!device.sentAvailable) {
|
|
433
|
-
if (device.options.app) {
|
|
434
|
-
device.options.app.emit('nmea2000OutAvailable')
|
|
435
|
-
}
|
|
436
|
-
device.emit('nmea2000OutAvailable')
|
|
437
|
-
device.sentAvailable = true
|
|
438
|
-
}
|
|
439
|
-
sendISORequest(device, 126996)
|
|
440
|
-
if (!device.heartbeatInterval) {
|
|
441
|
-
device.heartbeatInterval = setInterval(() => {
|
|
442
|
-
sendHeartbeat(device)
|
|
443
|
-
}, 60 * 1000)
|
|
444
|
-
}
|
|
445
|
-
//}
|
|
446
|
-
}, device.addressClaimDetectionTime)
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
function sendISORequest(
|
|
450
|
-
device: N2kDevice,
|
|
451
|
-
pgn: number,
|
|
452
|
-
src: number | undefined = undefined,
|
|
453
|
-
dst = 255
|
|
454
|
-
) {
|
|
455
|
-
device.debug(`Sending iso request for ${pgn} to ${dst}`)
|
|
456
|
-
|
|
457
|
-
const isoRequest = new PGN_59904({ pgn })
|
|
458
|
-
|
|
459
|
-
device.sendPGN(isoRequest, src)
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function sendProductInformation(device: N2kDevice) {
|
|
463
|
-
device.debug('Sending product info %j', device.productInfo)
|
|
464
|
-
|
|
465
|
-
device.sendPGN(device.productInfo)
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
function sendConfigInformation(device: N2kDevice) {
|
|
469
|
-
if (device.configurationInfo) {
|
|
470
|
-
device.debug('Sending config info..')
|
|
471
|
-
device.sendPGN(device.configurationInfo)
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
function sendNAKAcknowledgement(
|
|
476
|
-
device: N2kDevice,
|
|
477
|
-
src: number,
|
|
478
|
-
requestedPGN: number
|
|
479
|
-
) {
|
|
480
|
-
const acknowledgement = new PGN_59392(
|
|
481
|
-
{
|
|
482
|
-
control: IsoControl.Ack,
|
|
483
|
-
groupFunction: 255,
|
|
484
|
-
pgn: requestedPGN
|
|
485
|
-
},
|
|
486
|
-
src
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
device.sendPGN(acknowledgement)
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function sendPGNList(device: N2kDevice, dst: number) {
|
|
493
|
-
//FIXME: for now, adding everything that signalk-to-nmea2000 supports
|
|
494
|
-
//need a way for plugins, etc. to register the pgns they provide
|
|
495
|
-
const pgnList = new PGN_126464(
|
|
496
|
-
{
|
|
497
|
-
functionCode: PgnListFunction.TransmitPgnList,
|
|
498
|
-
list: device.transmitPGNs.map((num: number) => {
|
|
499
|
-
return { pgn: num }
|
|
500
|
-
})
|
|
501
|
-
},
|
|
502
|
-
dst
|
|
503
|
-
)
|
|
504
|
-
device.sendPGN(pgnList)
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function getISOAddressClaimAsUint64(pgn: any) {
|
|
508
|
-
return new Uint64LE(toPgn(pgn)!)
|
|
509
|
-
}
|
package/lib/pgns.test.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { getPgn } from './pgns'
|
|
2
|
-
|
|
3
|
-
describe('getPgn', () => {
|
|
4
|
-
test('Return info array about a pgn number', () => {
|
|
5
|
-
const pgns = getPgn(60928)
|
|
6
|
-
expect(pgns !== undefined)
|
|
7
|
-
if (pgns !== undefined) {
|
|
8
|
-
expect(pgns.length > 0)
|
|
9
|
-
expect(pgns[0].Description).toBe('ISO Address Claim')
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
})
|
package/lib/pgns.ts
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getPGNWithNumber,
|
|
3
|
-
EnumBase,
|
|
4
|
-
Enumeration,
|
|
5
|
-
BitEnumeration,
|
|
6
|
-
FieldTypeEnumeration,
|
|
7
|
-
getEnumerations,
|
|
8
|
-
getFieldTypeEnumerations,
|
|
9
|
-
getBitEnumerations
|
|
10
|
-
} from '@canboat/ts-pgns'
|
|
11
|
-
import _ from 'lodash'
|
|
12
|
-
import { createDebug } from './utilities'
|
|
13
|
-
|
|
14
|
-
const debug = createDebug('canboatjs:pgns')
|
|
15
|
-
|
|
16
|
-
//const enumValues : {[key:string]: {[key:number]: string}} = {}
|
|
17
|
-
//const enumNames : {[key:string]: {[key:string]: number}} = {}
|
|
18
|
-
|
|
19
|
-
const enumValues: any = {}
|
|
20
|
-
const enumNames: any = {}
|
|
21
|
-
|
|
22
|
-
function getEnumeration(name: string) {
|
|
23
|
-
const enumeration: Enumeration = lookupEnumerations[name] as Enumeration
|
|
24
|
-
if (enumeration) {
|
|
25
|
-
let vmap: any = enumValues[name]
|
|
26
|
-
if (!vmap) {
|
|
27
|
-
vmap = {}
|
|
28
|
-
enumValues[name] = vmap
|
|
29
|
-
enumeration.EnumValues.forEach((enumPair) => {
|
|
30
|
-
vmap[enumPair.Value] = enumPair.Name
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const nmap: any = {}
|
|
34
|
-
enumNames[name] = nmap
|
|
35
|
-
enumeration.EnumValues.forEach((enumPair) => {
|
|
36
|
-
nmap[enumPair.Name] = enumPair.Value
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return enumeration
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const ftEnumValues: any = {}
|
|
44
|
-
const ftEnumNames: any = {}
|
|
45
|
-
const ftEnumBits: any = {}
|
|
46
|
-
|
|
47
|
-
function getFieldTypeEnumeration(name: string) {
|
|
48
|
-
const enumeration = lookupFieldTypeEnumerations[name] as FieldTypeEnumeration
|
|
49
|
-
if (enumeration) {
|
|
50
|
-
let vmap = ftEnumValues[name]
|
|
51
|
-
if (!vmap) {
|
|
52
|
-
vmap = {}
|
|
53
|
-
ftEnumValues[name] = vmap
|
|
54
|
-
enumeration.EnumFieldTypeValues.forEach((enumPair) => {
|
|
55
|
-
vmap[enumPair.value] = enumPair.name
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
const nmap: any = {}
|
|
59
|
-
ftEnumNames[name] = nmap
|
|
60
|
-
enumeration.EnumFieldTypeValues.forEach((enumPair) => {
|
|
61
|
-
nmap[enumPair.name] = enumPair.value
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const bmap: any = {}
|
|
65
|
-
ftEnumBits[name] = bmap
|
|
66
|
-
enumeration.EnumFieldTypeValues.forEach((enumPair) => {
|
|
67
|
-
bmap[enumPair.value] = Number(enumPair.Bits)
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return enumeration
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const bEnumValues: any = {}
|
|
75
|
-
const bEnumNames: any = {}
|
|
76
|
-
|
|
77
|
-
function getBitEnumeration(name: string) {
|
|
78
|
-
const enumeration = lookupBitEnumerations[name] as BitEnumeration
|
|
79
|
-
if (enumeration) {
|
|
80
|
-
let nmap = bEnumNames[name]
|
|
81
|
-
if (!nmap) {
|
|
82
|
-
nmap = {}
|
|
83
|
-
bEnumNames[name] = nmap
|
|
84
|
-
enumeration.EnumBitValues.forEach((enumPair) => {
|
|
85
|
-
nmap[enumPair.Bit] = enumPair.Name
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
const vmap: any = {}
|
|
89
|
-
bEnumValues[name] = vmap
|
|
90
|
-
enumeration.EnumBitValues.forEach((enumPair) => {
|
|
91
|
-
vmap[enumPair.Name] = Number(enumPair.Bit)
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return enumeration
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function lookupEnumerationName(enumName: string, value: number) {
|
|
99
|
-
const enumeration = getEnumeration(enumName)
|
|
100
|
-
return enumeration && enumValues[enumName][value]
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function lookupEnumerationValue(enumName: string, name: string) {
|
|
104
|
-
const enumeration = getEnumeration(enumName)
|
|
105
|
-
return enumeration && enumNames[enumName][name]
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function lookupFieldTypeEnumerationName(
|
|
109
|
-
enumName: string,
|
|
110
|
-
value: number
|
|
111
|
-
) {
|
|
112
|
-
const enumeration = getFieldTypeEnumeration(enumName)
|
|
113
|
-
return enumeration && ftEnumValues[enumName][value]
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function lookupFieldTypeEnumerationBits(
|
|
117
|
-
enumName: string,
|
|
118
|
-
value: number
|
|
119
|
-
) {
|
|
120
|
-
const enumeration = getFieldTypeEnumeration(enumName)
|
|
121
|
-
return enumeration && ftEnumBits[enumName][value]
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function lookupFieldTypeEnumerationValue(
|
|
125
|
-
enumName: string,
|
|
126
|
-
name: string
|
|
127
|
-
) {
|
|
128
|
-
const enumeration = getFieldTypeEnumeration(enumName)
|
|
129
|
-
return enumeration && ftEnumNames[enumName][name]
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function lookupBitEnumerationName(enumName: string, value: number) {
|
|
133
|
-
const enumeration = getBitEnumeration(enumName)
|
|
134
|
-
return enumeration && bEnumNames[enumName][value]
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function lookupBitEnumerationValue(enumName: string, name: number) {
|
|
138
|
-
const enumeration = getBitEnumeration(enumName)
|
|
139
|
-
return enumeration && bEnumValues[enumName][name]
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function organizeEnumerations(enums: EnumBase[]): { [key: string]: EnumBase } {
|
|
143
|
-
const map: any = {}
|
|
144
|
-
enums.forEach((e) => {
|
|
145
|
-
map[e.Name] = e
|
|
146
|
-
})
|
|
147
|
-
return map
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export const lookupEnumerations = organizeEnumerations(getEnumerations())
|
|
151
|
-
export const lookupFieldTypeEnumerations = organizeEnumerations(
|
|
152
|
-
getFieldTypeEnumerations()
|
|
153
|
-
)
|
|
154
|
-
export const lookupBitEnumerations = organizeEnumerations(getBitEnumerations())
|
|
155
|
-
export const getPgn = getPGNWithNumber
|
|
156
|
-
export const customPgns: any = {}
|
|
157
|
-
|
|
158
|
-
export const addCustomPgns = (pgns: any, setter: any) => {
|
|
159
|
-
pgns.PGNs.forEach((pgn: any) => {
|
|
160
|
-
if (!customPgns[pgn.PGN]) {
|
|
161
|
-
customPgns[pgn.PGN] = {
|
|
162
|
-
definitions: [],
|
|
163
|
-
callbacks: []
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
customPgns[pgn.PGN].definitions.push(pgn)
|
|
168
|
-
|
|
169
|
-
/*
|
|
170
|
-
if ( pgn.calllback ) {
|
|
171
|
-
customPgns[pgn.PGN].callbacks.push()
|
|
172
|
-
}
|
|
173
|
-
*/
|
|
174
|
-
|
|
175
|
-
debug('registered custom pgn %d by %s', pgn.PGN, setter)
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
if (pgns.LookupEnumerations) {
|
|
179
|
-
pgns.LookupEnumerations.forEach((e: any) => {
|
|
180
|
-
if (!lookupEnumerations[e.Name]) {
|
|
181
|
-
lookupEnumerations[e.Name] = e
|
|
182
|
-
} else {
|
|
183
|
-
debug(`enumeration ${e.Name} already exists`)
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export const getCustomPgn = (pgnNum: number) => {
|
|
190
|
-
return customPgns[pgnNum]
|
|
191
|
-
}
|