@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 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 pgnToYdwgRawFormat = require("./index").pgnToYdwgRawFormat;
208
+ const pgnToYdgwRawFormat = require("./index").pgnToYdgwRawFormat;
209
209
 
210
- const array = pgnToYdwgRawFormat({
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": 1,
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": 1,
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
  })
@@ -136,6 +136,7 @@
136
136
  "Teleflex Marine (SeaStar Solutions)": 1850,
137
137
  "Thrane and Thrane": 351,
138
138
  "Tides Marine": 797,
139
+ "TJC Micro": 963,
139
140
  "Tohatsu Co, JP": 431,
140
141
  "Transas USA": 518,
141
142
  "Trimble": 1856,
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 FORMAT_FAST = 1
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 = pgnList[0]
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 == FORMAT_FAST && !this.mixedFormat) ) {
115
- //if ( (len > 0x8 || pgnData.Type === 'Fast') && coalesced ) {
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
- //} else if ( pgnData.Type === 'Fast' ) {
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
- maybeBad = true
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
- if ( !maybeBad ) {
177
- packet.lastPacket = packetIndex;
178
- if (FASTPACKET_BUCKET_0_SIZE + FASTPACKET_BUCKET_N_SIZE * bucket < packet.size)
179
- {
180
- // Packet is not complete yet
181
- trace(`${pgn.pgn} not complete`)
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
- const ts = _.get(pgn, 'timestamp', new Date())
212
- pgn.timestamp = _.isDate(ts) ? ts.toISOString() : ts
213
- this.emit('pgn', pgn)
214
- cb && cb(undefined, pgn)
215
- return pgn
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
- return readField(options, false, pgn, refField, bs, bits)
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 == 0x02 ) {
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, coalesced: true },
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 != 7 ) return buildErr('iKonvert', 'Invalid parts.', input)
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 },
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(',')
@@ -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
@@ -89,7 +89,7 @@ Ydgw02Stream.prototype.sendPGN = function (pgn) {
89
89
  Ydgw02Stream.prototype.sendYdgwPGN = function (msg) {
90
90
 
91
91
  actisenseToYdgwRawFormat(msg).forEach(raw => {
92
- this.sendString(raw)
92
+ this.sendString(raw + '\r\n')
93
93
  })
94
94
 
95
95
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canboat/canboatjs",
3
- "version": "1.21.1",
3
+ "version": "1.22.2",
4
4
  "description": "Native javascript version of canboat",
5
5
  "main": "index.js",
6
6
  "scripts": {