@canboat/canboatjs 1.21.1 → 1.22.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/README.md +2 -2
- package/lib/candevice.js +2 -2
- package/lib/codes.test.js +2 -0
- package/lib/codesMfgs.json +1 -0
- package/lib/fromPgn.js +87 -41
- package/lib/simpleCan.js +2 -2
- package/lib/stringMsg.js +16 -7
- package/lib/stringMsg.test.js +2 -1
- package/lib/ydgw02.js +1 -1
- package/package.json +1 -1
- package/ios_canboat.js +0 -26
package/README.md
CHANGED
|
@@ -205,9 +205,9 @@ Output: `!PDGY,127245,255,/Pj/f/9///8=`
|
|
|
205
205
|
## Generate YDGW-02 format from canboat json
|
|
206
206
|
|
|
207
207
|
```js
|
|
208
|
-
const
|
|
208
|
+
const pgnToYdgwRawFormat = require("./index").pgnToYdgwRawFormat;
|
|
209
209
|
|
|
210
|
-
const array =
|
|
210
|
+
const array = pgnToYdgwRawFormat({
|
|
211
211
|
src: 127,
|
|
212
212
|
prio: 3,
|
|
213
213
|
dst: 255,
|
package/lib/candevice.js
CHANGED
|
@@ -177,7 +177,7 @@ function handleGroupFunction(device, n2kMsg) {
|
|
|
177
177
|
const acknowledgement = {
|
|
178
178
|
pgn: 126208,
|
|
179
179
|
dst: n2kMsg.src,
|
|
180
|
-
"Function Code":
|
|
180
|
+
"Function Code": 2,
|
|
181
181
|
"PGN": n2kMsg.fields.PGN,
|
|
182
182
|
"PGN error code": 4,
|
|
183
183
|
"Transmission interval/Priority error code": 0,
|
|
@@ -195,7 +195,7 @@ function handleGroupFunction(device, n2kMsg) {
|
|
|
195
195
|
const acknowledgement = {
|
|
196
196
|
pgn: 126208,
|
|
197
197
|
dst: n2kMsg.src,
|
|
198
|
-
"Function Code":
|
|
198
|
+
"Function Code": 2,
|
|
199
199
|
"PGN": n2kMsg.fields.PGN,
|
|
200
200
|
"PGN error code": 4,
|
|
201
201
|
"Transmission interval/Priority error code": 0,
|
package/lib/codes.test.js
CHANGED
|
@@ -4,6 +4,7 @@ describe('getManufacturerCode', () => {
|
|
|
4
4
|
test('Return mfg number from name string', () => {
|
|
5
5
|
expect(getManufacturerCode('Furuno')).toBe(1855)
|
|
6
6
|
expect(getManufacturerCode('Yacht Devices')).toBe(717)
|
|
7
|
+
expect(getManufacturerCode('TJC Micro')).toBe(963)
|
|
7
8
|
})
|
|
8
9
|
})
|
|
9
10
|
|
|
@@ -11,5 +12,6 @@ describe('getManufacturerName', () => {
|
|
|
11
12
|
test('Return name string from mfg number', () => {
|
|
12
13
|
expect(getManufacturerName(1855)).toBe('Furuno')
|
|
13
14
|
expect(getManufacturerName(717)).toBe('Yacht Devices')
|
|
15
|
+
expect(getManufacturerName(963)).toBe('TJC Micro')
|
|
14
16
|
})
|
|
15
17
|
})
|
package/lib/codesMfgs.json
CHANGED
package/lib/fromPgn.js
CHANGED
|
@@ -39,7 +39,7 @@ const maxUint64 = new Int64LE(0xffffffff, 0xffffffff)
|
|
|
39
39
|
const maxInt64 = new Int64LE(0x7fffffff, 0xffffffff)
|
|
40
40
|
|
|
41
41
|
const FORMAT_PLAIN = 0
|
|
42
|
-
const
|
|
42
|
+
const FORMAT_COALESCED = 1
|
|
43
43
|
|
|
44
44
|
const FASTPACKET_INDEX = 0
|
|
45
45
|
const FASTPACKET_SIZE = 1
|
|
@@ -99,7 +99,17 @@ class Parser extends EventEmitter {
|
|
|
99
99
|
pgnList = [ ...customPgns.definitions, ...(pgnList||[]) ]
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
let pgnData
|
|
102
|
+
let pgnData
|
|
103
|
+
let origPGNList = pgnList
|
|
104
|
+
|
|
105
|
+
if ( pgnList.length > 1 ) {
|
|
106
|
+
pgnData = this.findMatchPgn(pgnList)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if ( !pgnData ) {
|
|
110
|
+
pgnData = pgnList[0]
|
|
111
|
+
}
|
|
112
|
+
|
|
103
113
|
let couldBeMulti = false;
|
|
104
114
|
|
|
105
115
|
if ( pgnList.length > 0 && len == 8 ) {
|
|
@@ -111,14 +121,13 @@ class Parser extends EventEmitter {
|
|
|
111
121
|
}
|
|
112
122
|
|
|
113
123
|
trace(`${pgn.pgn} ${len} ${pgnData.Length} ${pgnData.RepeatingFields} ${couldBeMulti}`)
|
|
114
|
-
if ( len > 0x8 || (this.format ==
|
|
115
|
-
|
|
116
|
-
this.format = FORMAT_FAST
|
|
124
|
+
if ( coalesced || len > 0x8 || (this.format == FORMAT_COALESCED && !this.mixedFormat) ) {
|
|
125
|
+
this.format = FORMAT_COALESCED
|
|
117
126
|
if ( sourceString ) {
|
|
118
127
|
pgn.input = [ sourceString ]
|
|
119
128
|
}
|
|
120
|
-
} else if ( pgnData.Length > 0x8 || (len == 0x8 && (pgnData.RepeatingFields || couldBeMulti))) {
|
|
121
|
-
|
|
129
|
+
//} else if ( pgnData.Length > 0x8 || (len == 0x8 && (pgnData.RepeatingFields || couldBeMulti))) {
|
|
130
|
+
} else if ( pgnData.Type === 'Fast' ) {
|
|
122
131
|
//partial packet
|
|
123
132
|
this.format = FORMAT_PLAIN
|
|
124
133
|
|
|
@@ -138,7 +147,6 @@ class Parser extends EventEmitter {
|
|
|
138
147
|
var start = bs.byteIndex
|
|
139
148
|
var packetIndex = bs.view.buffer.readUInt8(FASTPACKET_INDEX)
|
|
140
149
|
var bucket = packetIndex & FASTPACKET_MAX_INDEX;
|
|
141
|
-
var maybeBad = false
|
|
142
150
|
|
|
143
151
|
trace(`${pgn.pgn} partial ${packetIndex} ${bucket} ${packet.size}`)
|
|
144
152
|
|
|
@@ -156,14 +164,12 @@ class Parser extends EventEmitter {
|
|
|
156
164
|
bs.view.buffer.copy(packet.buffer, 0, FASTPACKET_BUCKET_0_OFFSET, 8)
|
|
157
165
|
trace(`${pgn.pgn} targetStart: 0 sourceStart: ${FASTPACKET_BUCKET_0_OFFSET}`)
|
|
158
166
|
} else {
|
|
159
|
-
if ( sourceString ) {
|
|
160
|
-
packet.src.push(sourceString)
|
|
161
|
-
}
|
|
162
167
|
if (packet.lastPacket + 1 != packetIndex) {
|
|
163
168
|
debug(`PGN ${pgn.pgn} malformed packet for ${pgn.src} received; expected ${packet.lastPacket+1} but got ${packetIndex}`)
|
|
164
169
|
cb && cb(`Could not parse ${JSON.stringify(pgn)}`)
|
|
165
170
|
bs.byteIndex = start
|
|
166
|
-
|
|
171
|
+
delete this.devices[pgn.src][pgn.pgn]
|
|
172
|
+
return
|
|
167
173
|
} else {
|
|
168
174
|
trace(`${pgn.pgn} targetStart: ${FASTPACKET_BUCKET_0_SIZE + FASTPACKET_BUCKET_N_SIZE * (bucket-1)} sourceStart: ${FASTPACKET_BUCKET_N_OFFSET} sourceEned: ${FASTPACKET_BUCKET_N_SIZE}`)
|
|
169
175
|
bs.view.buffer.copy(
|
|
@@ -173,20 +179,18 @@ class Parser extends EventEmitter {
|
|
|
173
179
|
)
|
|
174
180
|
}
|
|
175
181
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
var view = new BitView(packet.buffer)
|
|
185
|
-
bs = new BitStream(view)
|
|
186
|
-
trace(`${pgn.pgn} done`)
|
|
187
|
-
pgn.input = packet.src
|
|
188
|
-
delete this.devices[pgn.src][pgn.pgn]
|
|
182
|
+
packet.lastPacket = packetIndex;
|
|
183
|
+
if (FASTPACKET_BUCKET_0_SIZE + FASTPACKET_BUCKET_N_SIZE * bucket < packet.size)
|
|
184
|
+
{
|
|
185
|
+
// Packet is not complete yet
|
|
186
|
+
trace(`${pgn.pgn} not complete`)
|
|
187
|
+
return;
|
|
189
188
|
}
|
|
189
|
+
var view = new BitView(packet.buffer)
|
|
190
|
+
bs = new BitStream(view)
|
|
191
|
+
trace(`${pgn.pgn} done`)
|
|
192
|
+
pgn.input = packet.src
|
|
193
|
+
delete this.devices[pgn.src][pgn.pgn]
|
|
190
194
|
} else if ( sourceString ) {
|
|
191
195
|
pgn.input = [ sourceString ]
|
|
192
196
|
}
|
|
@@ -208,16 +212,29 @@ class Parser extends EventEmitter {
|
|
|
208
212
|
if ( pgnList.length == 0 ) {
|
|
209
213
|
//this.emit('warning', pgn, `no conversion found for pgn`)
|
|
210
214
|
trace('warning no conversion found for pgn %j', pgn)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
|
|
216
|
+
let nonMatch = this.findNonMatchPgn(origPGNList)
|
|
217
|
+
if ( nonMatch ) {
|
|
218
|
+
pgnList = [ nonMatch ]
|
|
219
|
+
pgnData = pgnList[0]
|
|
220
|
+
fields = pgnData.Fields
|
|
221
|
+
var postProcessor = fieldTypePostProcessors[field.Type]
|
|
222
|
+
if ( postProcessor ) {
|
|
223
|
+
value = postProcessor(pgnData.Fields[i], value)
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
const ts = _.get(pgn, 'timestamp', new Date())
|
|
227
|
+
pgn.timestamp = _.isDate(ts) ? ts.toISOString() : ts
|
|
228
|
+
this.emit('pgn', pgn)
|
|
229
|
+
cb && cb(undefined, pgn)
|
|
230
|
+
return pgn
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
pgnData = pgnList[0]
|
|
234
|
+
fields = pgnData.Fields
|
|
235
|
+
//console.log(`using ${JSON.stringify(pgnData, null, 2)}`)
|
|
236
|
+
value = pgnData.Fields[i].Description
|
|
216
237
|
}
|
|
217
|
-
pgnData = pgnList[0]
|
|
218
|
-
fields = pgnData.Fields
|
|
219
|
-
//console.log(`using ${JSON.stringify(pgnData, null, 2)}`)
|
|
220
|
-
value = pgnData.Fields[i].Description
|
|
221
238
|
}
|
|
222
239
|
|
|
223
240
|
if ( !_.isUndefined(value) && value != null ) {
|
|
@@ -264,6 +281,18 @@ class Parser extends EventEmitter {
|
|
|
264
281
|
}
|
|
265
282
|
}
|
|
266
283
|
|
|
284
|
+
findNonMatchPgn(pgnList) {
|
|
285
|
+
return pgnList.find(f => {
|
|
286
|
+
return !f.Fields.find(f => !_.isUndefined(f.Match))
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
findMatchPgn(pgnList) {
|
|
291
|
+
return pgnList.find(f => {
|
|
292
|
+
return f.Fields.find(f => !_.isUndefined(f.Match))
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
|
|
267
296
|
parse(data, cb) {
|
|
268
297
|
if (_.isString(data) ) {
|
|
269
298
|
return this.parseString(data, cb)
|
|
@@ -520,17 +549,26 @@ function readField(options, runPostProcessor, pgn, field, bs) {
|
|
|
520
549
|
if ( field.Resolution && typeof value === 'number' ) {
|
|
521
550
|
var resolution = field.Resolution
|
|
522
551
|
|
|
552
|
+
if ( _.isString(resolution) ) {
|
|
553
|
+
resolution = Number.parseFloat(resolution)
|
|
554
|
+
}
|
|
555
|
+
|
|
523
556
|
value = (value * resolution)
|
|
557
|
+
|
|
558
|
+
let precision = 0;
|
|
559
|
+
for (let r = resolution; (r > 0.0) && (r < 1.0); r = r * 10.0)
|
|
560
|
+
{
|
|
561
|
+
precision++;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
value = Number.parseFloat(value.toFixed(precision))
|
|
524
565
|
|
|
566
|
+
/*
|
|
525
567
|
if ( resolution === 3.125e-8 ) {
|
|
526
568
|
//yes. hack.
|
|
527
569
|
resolution = "0.0000000001"
|
|
528
570
|
}
|
|
529
|
-
|
|
530
|
-
if ( _.isString(resolution) &&
|
|
531
|
-
resolution.indexOf('.') != -1 ) {
|
|
532
|
-
value = Number.parseFloat(value.toFixed(resolution.length-2))
|
|
533
|
-
}
|
|
571
|
+
*/
|
|
534
572
|
}
|
|
535
573
|
|
|
536
574
|
if (field.EnumValues &&
|
|
@@ -662,7 +700,13 @@ function readVariableLengthField(options, pgn, field, bs) {
|
|
|
662
700
|
|
|
663
701
|
if ( refField ) {
|
|
664
702
|
var bits = (refField.BitLength + 7) & ~7; // Round # of bits in field refField up to complete bytes: 1->8, 7->8, 8->8 etc.
|
|
665
|
-
|
|
703
|
+
let res = readField(options, false, pgn, refField, bs)
|
|
704
|
+
|
|
705
|
+
if ( bits > field.BitLength ) {
|
|
706
|
+
bs.readBits(bits - refField.BitLength, false)
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
return res
|
|
666
710
|
}
|
|
667
711
|
}
|
|
668
712
|
|
|
@@ -708,7 +752,9 @@ fieldTypeReaders['ASCII string starting with length byte'] = (pgn, field, bs) =>
|
|
|
708
752
|
fieldTypeReaders["String with start/stop byte"] = (pgn, field, bs) => {
|
|
709
753
|
var len
|
|
710
754
|
var first = bs.readUint8()
|
|
711
|
-
if ( first ==
|
|
755
|
+
if ( first == 0xff ) { // no name, stop reading
|
|
756
|
+
return ''
|
|
757
|
+
} else if ( first == 0x02 ) {
|
|
712
758
|
var buf = Buffer.alloc(255)
|
|
713
759
|
var c
|
|
714
760
|
var idx = 0
|
package/lib/simpleCan.js
CHANGED
|
@@ -27,9 +27,9 @@ SimpleCan.prototype.start = function () {
|
|
|
27
27
|
|
|
28
28
|
pgn.timestamp = new Date().toISOString()
|
|
29
29
|
if ( this.plainText ) {
|
|
30
|
-
messageCb(binToActisense(pgn, msg.data, msg.data.length))
|
|
30
|
+
this.messageCb(binToActisense(pgn, msg.data, msg.data.length))
|
|
31
31
|
} else {
|
|
32
|
-
messageCb({ pgn, length: msg.data.length, data: msg.data })
|
|
32
|
+
this.messageCb({ pgn, length: msg.data.length, data: msg.data })
|
|
33
33
|
}
|
|
34
34
|
})
|
|
35
35
|
}
|
package/lib/stringMsg.js
CHANGED
|
@@ -49,7 +49,7 @@ exports.parseActisense = (input) => {
|
|
|
49
49
|
buildCanId(prio, pgn, dst, src),
|
|
50
50
|
'Actisense',
|
|
51
51
|
Buffer.from(data.join(''), 'hex'),
|
|
52
|
-
{ len: Number(len), timestamp
|
|
52
|
+
{ len: Number(len), timestamp },
|
|
53
53
|
)
|
|
54
54
|
}
|
|
55
55
|
exports.encodeActisense = ({
|
|
@@ -153,12 +153,21 @@ exports.encodeMXPGN = ({ prefix = '$MXPGN', pgn, prio, src, data }) => {
|
|
|
153
153
|
exports.isPDGY = startsWith('!PDGY,')
|
|
154
154
|
exports.parsePDGY = (input) => {
|
|
155
155
|
const parts = input.split(',')
|
|
156
|
-
if ( parts.length
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
if ( parts.length === 7 ) {
|
|
157
|
+
const [ prefix, pgn, prio, src, dst, timer, data ] = parts
|
|
158
|
+
return buildMsg(
|
|
159
|
+
buildCanId(prio, pgn, dst, src), 'PDGY', Buffer.from(data, 'base64'),
|
|
160
|
+
{ timer: Number(timer), prefix, coalesced: true },
|
|
161
|
+
)
|
|
162
|
+
} else if ( parts.length === 4 ) {
|
|
163
|
+
const [ prefix, pgn, dst, data ] = parts
|
|
164
|
+
return buildMsg(
|
|
165
|
+
buildCanId(0, pgn, dst, 0), 'PDGY', Buffer.from(data, 'base64'),
|
|
166
|
+
{ coalesced: true }
|
|
167
|
+
)
|
|
168
|
+
} else {
|
|
169
|
+
return buildErr('iKonvert', 'Invalid parts.', input)
|
|
170
|
+
}
|
|
162
171
|
}
|
|
163
172
|
exports.encodePDGY = ({ prefix = '!PDGY', pgn, data, dst = 255}) => (
|
|
164
173
|
[ prefix, pgn, dst, data.toString('base64')].join(',')
|
package/lib/stringMsg.test.js
CHANGED
|
@@ -65,7 +65,6 @@ describe('parseActisense', () => {
|
|
|
65
65
|
test('basic msg', () => {
|
|
66
66
|
const msg = '2016-04-09T16:41:09.078Z,3,127257,17,255,8,00,ff,7f,52,00,21,fe,ff'
|
|
67
67
|
expect(parseActisense(msg)).toEqual({
|
|
68
|
-
coalesced: true,
|
|
69
68
|
data: Buffer.from('00ff7f520021feff', 'hex'),
|
|
70
69
|
dst: 255,
|
|
71
70
|
len: 8,
|
|
@@ -99,6 +98,7 @@ describe('parsePDGY', () => {
|
|
|
99
98
|
prio: 3,
|
|
100
99
|
src: 2,
|
|
101
100
|
timer: 0.563,
|
|
101
|
+
coalesced: true
|
|
102
102
|
})
|
|
103
103
|
})
|
|
104
104
|
test('long msg', () => {
|
|
@@ -111,6 +111,7 @@ describe('parsePDGY', () => {
|
|
|
111
111
|
prio: 3,
|
|
112
112
|
src: 2,
|
|
113
113
|
timer: 483.236,
|
|
114
|
+
coalesced: true
|
|
114
115
|
})
|
|
115
116
|
})
|
|
116
117
|
})
|
package/lib/ydgw02.js
CHANGED