@canboat/canboatjs 3.0.2 → 3.2.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 +18 -9
- package/.github/workflows/test.yml +2 -2
- package/.github/workflows/test_canboat_changes.yml +91 -0
- package/.husky/pre-commit +4 -0
- package/.mocharc.js +7 -0
- package/.prettierrc.json +5 -0
- package/README.md +5 -14
- package/dist/actisense-serial.d.ts +17 -0
- package/dist/actisense-serial.d.ts.map +1 -0
- package/dist/actisense-serial.js +569 -0
- package/dist/actisense-serial.js.map +1 -0
- package/dist/bin/actisense-file.d.ts +3 -0
- package/dist/bin/actisense-file.d.ts.map +1 -0
- package/dist/bin/actisense-file.js +49 -0
- package/dist/bin/actisense-file.js.map +1 -0
- package/dist/bin/actisense-n2k-tcp.d.ts +3 -0
- package/dist/bin/actisense-n2k-tcp.d.ts.map +1 -0
- package/dist/bin/actisense-n2k-tcp.js +46 -0
- package/dist/bin/actisense-n2k-tcp.js.map +1 -0
- package/dist/bin/actisense-serialjs.d.ts +3 -0
- package/dist/bin/actisense-serialjs.d.ts.map +1 -0
- package/dist/bin/actisense-serialjs.js +51 -0
- package/dist/bin/actisense-serialjs.js.map +1 -0
- package/dist/bin/analyzerjs.d.ts +3 -0
- package/dist/bin/analyzerjs.d.ts.map +1 -0
- package/dist/bin/analyzerjs.js +61 -0
- package/dist/bin/analyzerjs.js.map +1 -0
- package/dist/bin/candumpjs.d.ts +3 -0
- package/dist/bin/candumpjs.d.ts.map +1 -0
- package/dist/bin/candumpjs.js +73 -0
- package/dist/bin/candumpjs.js.map +1 -0
- package/dist/bin/cansend.d.ts +3 -0
- package/dist/bin/cansend.d.ts.map +1 -0
- package/dist/bin/cansend.js +123 -0
- package/dist/bin/cansend.js.map +1 -0
- package/dist/bin/ikonvert-serial.d.ts +3 -0
- package/dist/bin/ikonvert-serial.d.ts.map +1 -0
- package/dist/bin/ikonvert-serial.js +36 -0
- package/dist/bin/ikonvert-serial.js.map +1 -0
- package/dist/bin/to-pgn.d.ts +3 -0
- package/dist/bin/to-pgn.d.ts.map +1 -0
- package/dist/bin/to-pgn.js +57 -0
- package/dist/bin/to-pgn.js.map +1 -0
- package/dist/canId.d.ts +21 -0
- package/dist/canId.d.ts.map +1 -0
- package/dist/canId.js +66 -0
- package/dist/canId.js.map +1 -0
- package/dist/canId.test.d.ts +2 -0
- package/dist/canId.test.d.ts.map +1 -0
- package/dist/canId.test.js +58 -0
- package/dist/canId.test.js.map +1 -0
- package/dist/canbus.d.ts +17 -0
- package/dist/canbus.d.ts.map +1 -0
- package/dist/canbus.js +261 -0
- package/dist/canbus.js.map +1 -0
- package/dist/candevice.d.ts +23 -0
- package/dist/candevice.d.ts.map +1 -0
- package/dist/candevice.js +36 -0
- package/dist/candevice.js.map +1 -0
- package/dist/codes.d.ts +2 -0
- package/dist/codes.d.ts.map +1 -0
- package/dist/codes.js +24 -0
- package/dist/codes.js.map +1 -0
- package/dist/discovery.d.ts +17 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +119 -0
- package/dist/discovery.js.map +1 -0
- package/dist/fromPgn.d.ts +51 -0
- package/dist/fromPgn.d.ts.map +1 -0
- package/dist/fromPgn.js +989 -0
- package/dist/fromPgn.js.map +1 -0
- package/dist/fromPgnStream.d.ts +17 -0
- package/dist/fromPgnStream.d.ts.map +1 -0
- package/dist/fromPgnStream.js +47 -0
- package/dist/fromPgnStream.js.map +1 -0
- package/dist/ikonvert.d.ts +17 -0
- package/dist/ikonvert.d.ts.map +1 -0
- package/dist/ikonvert.js +224 -0
- package/dist/ikonvert.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/n2k-actisense.d.ts +24 -0
- package/dist/n2k-actisense.d.ts.map +1 -0
- package/dist/n2k-actisense.js +126 -0
- package/dist/n2k-actisense.js.map +1 -0
- package/dist/n2k-actisense.test.d.ts +2 -0
- package/dist/n2k-actisense.test.d.ts.map +1 -0
- package/dist/n2k-actisense.test.js +49 -0
- package/dist/n2k-actisense.test.js.map +1 -0
- package/dist/n2kDevice.d.ts +44 -0
- package/dist/n2kDevice.d.ts.map +1 -0
- package/dist/n2kDevice.js +436 -0
- package/dist/n2kDevice.js.map +1 -0
- package/dist/pgns.d.ts +22 -0
- package/dist/pgns.d.ts.map +1 -0
- package/dist/pgns.js +157 -0
- package/dist/pgns.js.map +1 -0
- package/dist/pgns.test.d.ts +2 -0
- package/dist/pgns.test.d.ts.map +1 -0
- package/dist/pgns.test.js +14 -0
- package/dist/pgns.test.js.map +1 -0
- package/dist/simpleCan.d.ts +17 -0
- package/dist/simpleCan.d.ts.map +1 -0
- package/dist/simpleCan.js +125 -0
- package/dist/simpleCan.js.map +1 -0
- package/dist/stringMsg.d.ts +33 -0
- package/dist/stringMsg.d.ts.map +1 -0
- package/dist/stringMsg.js +359 -0
- package/dist/stringMsg.js.map +1 -0
- package/dist/stringMsg.test.d.ts +2 -0
- package/dist/stringMsg.test.d.ts.map +1 -0
- package/dist/stringMsg.test.js +244 -0
- package/dist/stringMsg.test.js.map +1 -0
- package/dist/toPgn.d.ts +34 -0
- package/dist/toPgn.d.ts.map +1 -0
- package/dist/toPgn.js +500 -0
- package/dist/toPgn.js.map +1 -0
- package/dist/utilities.d.ts +28 -0
- package/dist/utilities.d.ts.map +1 -0
- package/dist/utilities.js +159 -0
- package/dist/utilities.js.map +1 -0
- package/dist/utilities.test.d.ts +2 -0
- package/dist/utilities.test.d.ts.map +1 -0
- package/dist/utilities.test.js +9 -0
- package/dist/utilities.test.js.map +1 -0
- package/dist/venus-mqtt.d.ts +11 -0
- package/dist/venus-mqtt.d.ts.map +1 -0
- package/dist/venus-mqtt.js +100 -0
- package/dist/venus-mqtt.js.map +1 -0
- package/dist/venus.d.ts +11 -0
- package/dist/venus.d.ts.map +1 -0
- package/dist/venus.js +71 -0
- package/dist/venus.js.map +1 -0
- package/dist/w2k01.d.ts +17 -0
- package/dist/w2k01.d.ts.map +1 -0
- package/dist/w2k01.js +119 -0
- package/dist/w2k01.js.map +1 -0
- package/dist/yddevice.d.ts +25 -0
- package/dist/yddevice.d.ts.map +1 -0
- package/dist/yddevice.js +43 -0
- package/dist/yddevice.js.map +1 -0
- package/dist/ydgw02.d.ts +17 -0
- package/dist/ydgw02.d.ts.map +1 -0
- package/dist/ydgw02.js +163 -0
- package/dist/ydgw02.js.map +1 -0
- package/dist/ydvr.d.ts +17 -0
- package/dist/ydvr.d.ts.map +1 -0
- package/dist/ydvr.js +120 -0
- package/dist/ydvr.js.map +1 -0
- package/eslint.config.js +58 -0
- package/jest.config.js +5 -0
- package/lib/actisense-serial.ts +644 -0
- package/lib/bin/actisense-file.ts +53 -0
- package/lib/bin/actisense-n2k-tcp.ts +50 -0
- package/lib/bin/actisense-serialjs.ts +55 -0
- package/{bin/analyzerjs → lib/bin/analyzerjs.ts} +16 -15
- package/{bin/candumpjs → lib/bin/candumpjs.ts} +19 -20
- package/lib/bin/cansend.ts +131 -0
- package/lib/bin/ikonvert-serial.ts +44 -0
- package/{bin/to-pgn → lib/bin/to-pgn.ts} +23 -17
- package/lib/bin/ydvr-file +33 -0
- package/lib/canId.test.js +35 -15
- package/lib/canId.ts +84 -0
- package/lib/canbus.ts +293 -0
- package/lib/candevice.ts +41 -0
- package/lib/codes.ts +21 -0
- package/lib/{discovery.js → discovery.ts} +32 -30
- package/lib/fromPgn.ts +1207 -0
- package/lib/{fromPgnStream.js → fromPgnStream.ts} +17 -12
- package/lib/{ikonvert.js → ikonvert.ts} +99 -94
- package/lib/index.ts +48 -0
- package/lib/n2k-actisense.test.js +31 -32
- package/lib/n2k-actisense.ts +152 -0
- package/lib/n2kDevice.ts +516 -0
- package/lib/pgns.test.ts +12 -0
- package/lib/pgns.ts +191 -0
- package/lib/simpleCan.ts +140 -0
- package/lib/stringMsg.test.js +58 -41
- package/lib/stringMsg.ts +464 -0
- package/lib/toPgn.ts +597 -0
- package/lib/utilities.ts +169 -0
- package/lib/venus-mqtt.js +69 -73
- package/lib/venus.js +8 -8
- package/lib/w2k01.ts +142 -0
- package/lib/{yddevice.js → yddevice.ts} +18 -16
- package/lib/{ydgw02.js → ydgw02.ts} +58 -51
- package/lib/ydvr.js +65 -147
- package/package.json +54 -18
- package/tsconfig.json +19 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/CHANGELOG.md +0 -101
- package/bin/actisense-file +0 -47
- package/bin/actisense-n2k-tcp +0 -45
- package/bin/actisense-serialjs +0 -52
- package/bin/candumpanalyzerjs +0 -29
- package/bin/cansend +0 -130
- package/bin/ikonvert-serial +0 -50
- package/bin/ydgw-analyzerjs +0 -36
- package/bin/ydvr-file +0 -33
- package/index.js +0 -53
- package/ios_canboat.js/main.js +0 -2
- package/ios_canboat.js/main.js.LICENSE.txt +0 -17
- package/ios_canboat.js/main.js.map +0 -1
- package/lib/canId.js +0 -64
- package/lib/canbus.js +0 -278
- package/lib/candevice.js +0 -51
- package/lib/codes.js +0 -69
- package/lib/codes.test.js +0 -17
- package/lib/codesMfgs.json +0 -166
- package/lib/fromPgn.js +0 -1027
- package/lib/n2k-actisense.js +0 -108
- package/lib/n2kDevice.js +0 -390
- package/lib/pgns.js +0 -196
- package/lib/pgns.test.js +0 -13
- package/lib/serial.js +0 -647
- package/lib/simpleCan.js +0 -105
- package/lib/stringMsg.js +0 -339
- package/lib/toPgn.js +0 -518
- package/lib/utilities.js +0 -111
- package/lib/w2k01.js +0 -129
package/lib/fromPgn.js
DELETED
|
@@ -1,1027 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright 2018 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
|
-
const debug = require('debug')('canboatjs:fromPgn')
|
|
18
|
-
const trace = require('debug')('canboatjs:fromPgn:trace')
|
|
19
|
-
const EventEmitter = require('events')
|
|
20
|
-
const pkg = require('../package.json')
|
|
21
|
-
const _ = require('lodash')
|
|
22
|
-
const { pgns, getCustomPgn, addCustomPgns, lookupEnumerationName, lookupFieldTypeEnumerationName, lookupBitEnumerationName, lookupFieldTypeEnumerationBits, lookupFieldTypeEnumerationValue } = require('./pgns')
|
|
23
|
-
const BitStream = require('bit-buffer').BitStream
|
|
24
|
-
const BitView = require('bit-buffer').BitView
|
|
25
|
-
const Int64LE = require('int64-buffer').Int64LE
|
|
26
|
-
const Uint64LE = require('int64-buffer').Uint64LE
|
|
27
|
-
|
|
28
|
-
const { getPGNFromCanId } = require('./utilities')
|
|
29
|
-
const { getIndustryName, getManufacturerName } = require('./codes')
|
|
30
|
-
const { parseCanId } = require('./canId')
|
|
31
|
-
const { parseN2kString, parseYDRAW, isN2KOver0183, parseN2KOver0183, parsePDGY, parseActisenseN2KASCII } = require('./stringMsg')
|
|
32
|
-
|
|
33
|
-
var fieldTypeReaders = {}
|
|
34
|
-
var fieldTypePostProcessors = {}
|
|
35
|
-
|
|
36
|
-
const timeUnitsPerSecond = 10000
|
|
37
|
-
const maxUint64 = new Int64LE(0xffffffff, 0xffffffff)
|
|
38
|
-
const maxInt64 = new Int64LE(0x7fffffff, 0xffffffff)
|
|
39
|
-
|
|
40
|
-
const FORMAT_PLAIN = 0
|
|
41
|
-
const FORMAT_COALESCED = 1
|
|
42
|
-
const RES_BINARY = 'Binary data'
|
|
43
|
-
|
|
44
|
-
const FASTPACKET_INDEX = 0
|
|
45
|
-
const FASTPACKET_SIZE = 1
|
|
46
|
-
const FASTPACKET_BUCKET_0_SIZE = 6
|
|
47
|
-
const FASTPACKET_BUCKET_N_SIZE = 7
|
|
48
|
-
const FASTPACKET_BUCKET_0_OFFSET = 2
|
|
49
|
-
const FASTPACKET_BUCKET_N_OFFSET = 1
|
|
50
|
-
const FASTPACKET_MAX_INDEX = 0x1f
|
|
51
|
-
const FASTPACKET_MAX_SIZE = (FASTPACKET_BUCKET_0_SIZE + FASTPACKET_BUCKET_N_SIZE * (FASTPACKET_MAX_INDEX - 1))
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//we can't handle these, so ignore them
|
|
55
|
-
const ignoredPgns = []//130820, 126720 ]
|
|
56
|
-
|
|
57
|
-
class Parser extends EventEmitter {
|
|
58
|
-
constructor (opts) {
|
|
59
|
-
super()
|
|
60
|
-
this.options = _.isUndefined(opts) ? {} : opts
|
|
61
|
-
|
|
62
|
-
if ( this.options.returnNulls === undefined ) {
|
|
63
|
-
this.options.returnNulls = false
|
|
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
|
-
}
|
|
73
|
-
|
|
74
|
-
this.name = pkg.name
|
|
75
|
-
this.version = pkg.version
|
|
76
|
-
this.author = pkg.author
|
|
77
|
-
this.license = pkg.license
|
|
78
|
-
this.format = _.isUndefined(this.options.format) ? -1 : this.options.format
|
|
79
|
-
this.devices = {}
|
|
80
|
-
this.mixedFormat = this.options.mixedFormat || false
|
|
81
|
-
|
|
82
|
-
if ( this.options.onPropertyValues ) {
|
|
83
|
-
this.options.onPropertyValues('canboat-custom-pgns', values => {
|
|
84
|
-
values.filter(v => v != null).forEach(pv => {
|
|
85
|
-
addCustomPgns(pv.value, pv.setter)
|
|
86
|
-
})
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
_parse(pgn, bs, len, coalesced, cb, sourceString) {
|
|
92
|
-
if ( ignoredPgns.indexOf(pgn.pgn) != -1 ) {
|
|
93
|
-
this.emit('warning', pgn, 'ignoring pgn')
|
|
94
|
-
return undefined
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const customPgns = getCustomPgn(pgn.pgn)
|
|
98
|
-
let pgnList = pgns[pgn.pgn]
|
|
99
|
-
|
|
100
|
-
if (!pgnList && !customPgns ) {
|
|
101
|
-
this.emit('warning', pgn, `no conversion found for pgn ${JSON.stringify(pgn)}`)
|
|
102
|
-
return undefined
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if ( customPgns ) {
|
|
106
|
-
pgnList = [ ...customPgns.definitions, ...(pgnList||[]) ]
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
let pgnData
|
|
110
|
-
let origPGNList = pgnList
|
|
111
|
-
|
|
112
|
-
if ( pgnList.length > 1 ) {
|
|
113
|
-
pgnData = this.findMatchPgn(pgnList)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if ( !pgnData ) {
|
|
117
|
-
pgnData = pgnList[0]
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
let couldBeMulti = false;
|
|
121
|
-
|
|
122
|
-
if ( pgnList.length > 0 && len == 8 ) {
|
|
123
|
-
pgnList.forEach(pgnD => {
|
|
124
|
-
if ( pgnD.Length > 8 ) {
|
|
125
|
-
couldBeMulti = true
|
|
126
|
-
}
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
trace(`${pgn.pgn} ${len} ${pgnData.Length} ${couldBeMulti}`)
|
|
131
|
-
if ( coalesced || len > 0x8 || (this.format == FORMAT_COALESCED && !this.mixedFormat) ) {
|
|
132
|
-
this.format = FORMAT_COALESCED
|
|
133
|
-
if ( sourceString ) {
|
|
134
|
-
pgn.input = [ sourceString ]
|
|
135
|
-
}
|
|
136
|
-
//} else if ( pgnData.Length > 0x8 || (len == 0x8 && (pgnData.RepeatingFields || couldBeMulti))) {
|
|
137
|
-
} else if ( pgnData.Type === 'Fast' ) {
|
|
138
|
-
//partial packet
|
|
139
|
-
this.format = FORMAT_PLAIN
|
|
140
|
-
|
|
141
|
-
if ( _.isUndefined(this.devices[pgn.src]) ) {
|
|
142
|
-
this.devices[pgn.src] = {}
|
|
143
|
-
}
|
|
144
|
-
var packet = this.devices[pgn.src][pgn.pgn]
|
|
145
|
-
|
|
146
|
-
if ( !packet ) {
|
|
147
|
-
packet = { bufferSize:0, lastPacket: 0, src: [] }
|
|
148
|
-
this.devices[pgn.src][pgn.pgn] = packet
|
|
149
|
-
}
|
|
150
|
-
if ( sourceString ) {
|
|
151
|
-
packet.src.push(sourceString)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
var start = bs.byteIndex
|
|
155
|
-
var packetIndex = bs.view.buffer.readUInt8(FASTPACKET_INDEX)
|
|
156
|
-
var bucket = packetIndex & FASTPACKET_MAX_INDEX;
|
|
157
|
-
|
|
158
|
-
trace(`${pgn.pgn} partial ${packetIndex} ${bucket} ${packet.size}`)
|
|
159
|
-
|
|
160
|
-
if ( bucket == 0 ) {
|
|
161
|
-
packet.size = bs.view.buffer.readUInt8(FASTPACKET_SIZE)
|
|
162
|
-
var newSize = packet.size + FASTPACKET_BUCKET_N_SIZE;
|
|
163
|
-
if ( newSize > packet.bufferSize ) {
|
|
164
|
-
var newBuf = Buffer.alloc(newSize)
|
|
165
|
-
packet.bufferSize = newSize
|
|
166
|
-
if ( packet.buffer ) {
|
|
167
|
-
packet.buffer.copy(newBuf)
|
|
168
|
-
}
|
|
169
|
-
packet.buffer = newBuf
|
|
170
|
-
}
|
|
171
|
-
bs.view.buffer.copy(packet.buffer, 0, FASTPACKET_BUCKET_0_OFFSET, 8)
|
|
172
|
-
trace(`${pgn.pgn} targetStart: 0 sourceStart: ${FASTPACKET_BUCKET_0_OFFSET}`)
|
|
173
|
-
} else if ( !packet.buffer ) {
|
|
174
|
-
//we got a non-zero bucket, but we never got the zero bucket
|
|
175
|
-
debug(`PGN ${pgn.pgn} malformed packet for ${pgn.src} received; got a non-zero bucket first`)
|
|
176
|
-
cb && cb(`Could not parse ${JSON.stringify(pgn)}`)
|
|
177
|
-
bs.byteIndex = start
|
|
178
|
-
delete this.devices[pgn.src][pgn.pgn]
|
|
179
|
-
return
|
|
180
|
-
} else {
|
|
181
|
-
if (packet.lastPacket + 1 != packetIndex) {
|
|
182
|
-
debug(`PGN ${pgn.pgn} malformed packet for ${pgn.src} received; expected ${packet.lastPacket+1} but got ${packetIndex}`)
|
|
183
|
-
cb && cb(`Could not parse ${JSON.stringify(pgn)}`)
|
|
184
|
-
bs.byteIndex = start
|
|
185
|
-
delete this.devices[pgn.src][pgn.pgn]
|
|
186
|
-
return
|
|
187
|
-
} else {
|
|
188
|
-
trace(`${pgn.pgn} targetStart: ${FASTPACKET_BUCKET_0_SIZE + FASTPACKET_BUCKET_N_SIZE * (bucket-1)} sourceStart: ${FASTPACKET_BUCKET_N_OFFSET} sourceEned: ${FASTPACKET_BUCKET_N_SIZE}`)
|
|
189
|
-
bs.view.buffer.copy(
|
|
190
|
-
packet.buffer,
|
|
191
|
-
FASTPACKET_BUCKET_0_SIZE + FASTPACKET_BUCKET_N_SIZE * (bucket-1)
|
|
192
|
-
, FASTPACKET_BUCKET_N_OFFSET, 8
|
|
193
|
-
)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
packet.lastPacket = packetIndex;
|
|
197
|
-
if (FASTPACKET_BUCKET_0_SIZE + FASTPACKET_BUCKET_N_SIZE * bucket < packet.size)
|
|
198
|
-
{
|
|
199
|
-
// Packet is not complete yet
|
|
200
|
-
trace(`${pgn.pgn} not complete`)
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
var view = new BitView(packet.buffer)
|
|
204
|
-
bs = new BitStream(view)
|
|
205
|
-
trace(`${pgn.pgn} done`)
|
|
206
|
-
pgn.input = packet.src
|
|
207
|
-
delete this.devices[pgn.src][pgn.pgn]
|
|
208
|
-
} else if ( sourceString ) {
|
|
209
|
-
pgn.input = [ sourceString ]
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
let RepeatingFields = pgnData.RepeatingFieldSet1Size ? pgnData.RepeatingFieldSet1Size : 0
|
|
213
|
-
|
|
214
|
-
pgn.fields = {}
|
|
215
|
-
|
|
216
|
-
try {
|
|
217
|
-
var fields = pgnData.Fields
|
|
218
|
-
|
|
219
|
-
for ( var i = 0; i < fields.length-RepeatingFields; i++ ) {
|
|
220
|
-
var field = fields[i]
|
|
221
|
-
var hasMatch = !_.isUndefined(field.Match)
|
|
222
|
-
|
|
223
|
-
var value = readField(this.options, !hasMatch, pgn, field, bs, fields)
|
|
224
|
-
|
|
225
|
-
if ( hasMatch ) {
|
|
226
|
-
//console.log(`looking for ${field.Name} == ${value}`)
|
|
227
|
-
//console.log(JSON.stringify(pgnList, null, 2))
|
|
228
|
-
pgnList = pgnList.filter(f => f.Fields[i].Match == value)
|
|
229
|
-
if ( pgnList.length == 0 ) {
|
|
230
|
-
//this.emit('warning', pgn, `no conversion found for pgn`)
|
|
231
|
-
trace('warning no conversion found for pgn %j', pgn)
|
|
232
|
-
|
|
233
|
-
let nonMatch = this.findNonMatchPgn(origPGNList)
|
|
234
|
-
if ( nonMatch ) {
|
|
235
|
-
pgnList = [ nonMatch ]
|
|
236
|
-
pgnData = pgnList[0]
|
|
237
|
-
fields = pgnData.Fields
|
|
238
|
-
var postProcessor = fieldTypePostProcessors[field.FieldType]
|
|
239
|
-
if ( postProcessor ) {
|
|
240
|
-
value = postProcessor(pgnData.Fields[i], value)
|
|
241
|
-
}
|
|
242
|
-
} else {
|
|
243
|
-
const ts = _.get(pgn, 'timestamp', new Date())
|
|
244
|
-
pgn.timestamp = _.isDate(ts) ? ts.toISOString() : ts
|
|
245
|
-
if ( !_.isUndefined(value) && (value != null ||
|
|
246
|
-
this.options.returnNulls) ) {
|
|
247
|
-
this.setField(pgn.fields, field, value)
|
|
248
|
-
}
|
|
249
|
-
this.emit('pgn', pgn)
|
|
250
|
-
cb && cb(undefined, pgn)
|
|
251
|
-
return pgn
|
|
252
|
-
}
|
|
253
|
-
} else {
|
|
254
|
-
pgnData = pgnList[0]
|
|
255
|
-
fields = pgnData.Fields
|
|
256
|
-
//console.log(`using ${JSON.stringify(pgnData, null, 2)}`)
|
|
257
|
-
value = pgnData.Fields[i].Description
|
|
258
|
-
if ( value == null ) {
|
|
259
|
-
value = pgnData.Fields[i].Match
|
|
260
|
-
}
|
|
261
|
-
RepeatingFields = pgnData.RepeatingFieldSet1Size ? pgnData.RepeatingFieldSet1Size : 0
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if ( !_.isUndefined(value) && (value != null ||
|
|
266
|
-
this.options.returnNulls) ) {
|
|
267
|
-
this.setField(pgn.fields, field, value)
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
if ( RepeatingFields > 0 ) {
|
|
271
|
-
var repeating = fields.slice(fields.length-RepeatingFields)
|
|
272
|
-
pgn.fields.list = []
|
|
273
|
-
|
|
274
|
-
let count
|
|
275
|
-
|
|
276
|
-
if ( pgnData.RepeatingFieldSet1CountField !== undefined ) {
|
|
277
|
-
count = pgn.fields[pgnData.Fields[pgnData.RepeatingFieldSet1CountField-1][this.options.useCamel ? 'Id' : 'Name']]
|
|
278
|
-
} else {
|
|
279
|
-
count = 2048
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
while ( bs.bitsLeft > 0 && --count >= 0 ) {
|
|
283
|
-
var group = {}
|
|
284
|
-
repeating.forEach(field => {
|
|
285
|
-
if ( bs.bitsLeft > 0 ) {
|
|
286
|
-
var value = readField(this.options, true, pgn, field, bs, fields)
|
|
287
|
-
if ( !_.isUndefined(value) && value != null ) {
|
|
288
|
-
this.setField(group, field, value)
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
})
|
|
292
|
-
if ( _.keys(group).length > 0 ) {
|
|
293
|
-
pgn.fields.list.push(group)
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
pgn.description = pgnData.Description
|
|
299
|
-
|
|
300
|
-
//console.log(`pgn: ${JSON.stringify(pgn)}`)
|
|
301
|
-
|
|
302
|
-
// Stringify timestamp because SK Server needs it that way.
|
|
303
|
-
const ts = _.get(pgn, 'timestamp', new Date())
|
|
304
|
-
pgn.timestamp = _.isDate(ts) ? ts.toISOString() : ts
|
|
305
|
-
this.emit('pgn', pgn)
|
|
306
|
-
cb && cb(undefined, pgn)
|
|
307
|
-
if ( pgnData.callback ) {
|
|
308
|
-
pgnData.callback(pgn)
|
|
309
|
-
}
|
|
310
|
-
return pgn
|
|
311
|
-
} catch ( error ) {
|
|
312
|
-
this.emit('error', pgn, error)
|
|
313
|
-
cb && cb(error)
|
|
314
|
-
return
|
|
315
|
-
}
|
|
316
|
-
}
|
|
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
|
-
|
|
329
|
-
findNonMatchPgn(pgnList) {
|
|
330
|
-
return pgnList.find(f => {
|
|
331
|
-
return !f.Fields.find(f => !_.isUndefined(f.Match))
|
|
332
|
-
})
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
findMatchPgn(pgnList) {
|
|
336
|
-
return pgnList.find(f => {
|
|
337
|
-
return f.Fields.find(f => !_.isUndefined(f.Match))
|
|
338
|
-
})
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
parse(data, cb) {
|
|
342
|
-
if (_.isString(data) ) {
|
|
343
|
-
return this.parseString(data, cb)
|
|
344
|
-
} else if ( _.isBuffer(data) ) {
|
|
345
|
-
return this.parseBuffer(data, cb)
|
|
346
|
-
} else {
|
|
347
|
-
return this.parsePgnData(data.pgn, data.length, data.data, data.coalesced === true, cb, data.sourceString)
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
parsePgnData(pgn, length, data, coalesced, cb, sourceString) {
|
|
352
|
-
try
|
|
353
|
-
{
|
|
354
|
-
var buffer = data
|
|
355
|
-
if ( !_.isBuffer(data) ) {
|
|
356
|
-
var array = new Int16Array(length)
|
|
357
|
-
data.forEach((num, index) => {
|
|
358
|
-
array[index] = parseInt(num, 16)
|
|
359
|
-
})
|
|
360
|
-
buffer = Buffer.from(array)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
var bv = new BitView(buffer);
|
|
364
|
-
var bs = new BitStream(bv)
|
|
365
|
-
const res = this._parse(pgn, bs, length, coalesced, cb, sourceString);
|
|
366
|
-
if ( res ) {
|
|
367
|
-
debug('parsed pgn %j', pgn)
|
|
368
|
-
}
|
|
369
|
-
return res
|
|
370
|
-
} catch ( error ) {
|
|
371
|
-
cb && cb(error)
|
|
372
|
-
this.emit('error', pgn, error)
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
isN2KOver0183(sentence) {
|
|
377
|
-
return isN2KOver0183(sentence)
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
parseN2KOver0183(sentence, cb) {
|
|
381
|
-
return this.parseString(sentence, cb)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Venus MQTT-N2K
|
|
385
|
-
parseVenusMQTT(pgn_data, cb) {
|
|
386
|
-
try
|
|
387
|
-
{
|
|
388
|
-
const pgn = {
|
|
389
|
-
pgn: pgn_data.pgn,
|
|
390
|
-
timestamp: new Date().toISOString(),
|
|
391
|
-
src: pgn_data.src,
|
|
392
|
-
dst: pgn_data.dst
|
|
393
|
-
}
|
|
394
|
-
const bs = new BitStream(Buffer.from(pgn_data.data, 'base64'))
|
|
395
|
-
const res = this._parse(pgn, bs, 8, false, cb)
|
|
396
|
-
if ( res ) {
|
|
397
|
-
debug('parsed pgn %j', pgn)
|
|
398
|
-
}
|
|
399
|
-
return res
|
|
400
|
-
} catch ( error ) {
|
|
401
|
-
cb && cb(error)
|
|
402
|
-
this.emit('error', pgn, error)
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
//Yacht Devices NMEA2000 Wifi gateway
|
|
407
|
-
parseYDGW02(pgn_data, cb) {
|
|
408
|
-
try {
|
|
409
|
-
const { data, direction, error, ...pgn } = parseYDRAW(pgn_data)
|
|
410
|
-
if (!error && direction === 'R') {
|
|
411
|
-
const bs = new BitStream(data)
|
|
412
|
-
delete pgn.format
|
|
413
|
-
const res = this._parse(pgn, bs, data.length, false, cb, pgn_data)
|
|
414
|
-
if ( res ) {
|
|
415
|
-
debug('parsed ydgw02 pgn %j', pgn_data)
|
|
416
|
-
return res
|
|
417
|
-
}
|
|
418
|
-
} else if ( error ) {
|
|
419
|
-
cb && cb(error)
|
|
420
|
-
this.emit('error', pgn_data, error)
|
|
421
|
-
}
|
|
422
|
-
} catch ( error ) {
|
|
423
|
-
cb && cb(error)
|
|
424
|
-
this.emit('error', pgn_data, error)
|
|
425
|
-
}
|
|
426
|
-
return undefined
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
//Actisense W2k-1
|
|
430
|
-
parseActisenceN2KAscii(pgn_data, cb) {
|
|
431
|
-
try {
|
|
432
|
-
const { data, error, ...pgn } = parseActisenseN2KASCII(pgn_data)
|
|
433
|
-
if (!error ) {
|
|
434
|
-
const bs = new BitStream(data)
|
|
435
|
-
delete pgn.format
|
|
436
|
-
const res = this._parse(pgn, bs, data.length, false, cb, pgn_data)
|
|
437
|
-
if ( res ) {
|
|
438
|
-
debug('parsed n2k ascii pgn %j', pgn_data)
|
|
439
|
-
return res
|
|
440
|
-
}
|
|
441
|
-
} else if ( error ) {
|
|
442
|
-
cb && cb(error)
|
|
443
|
-
this.emit('error', pgn_data, error)
|
|
444
|
-
}
|
|
445
|
-
} catch ( error ) {
|
|
446
|
-
cb && cb(error)
|
|
447
|
-
this.emit('error', pgn_data, error)
|
|
448
|
-
}
|
|
449
|
-
return undefined
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
parsePDGY(pgn_data, cb) {
|
|
453
|
-
if ( pgn_data[0] != '!') {
|
|
454
|
-
return
|
|
455
|
-
}
|
|
456
|
-
try {
|
|
457
|
-
const { coalesced, data, error, len, ...pgn } = parsePDGY(pgn_data)
|
|
458
|
-
if (error) {
|
|
459
|
-
cb && cb(error)
|
|
460
|
-
this.emit('error', pgn, error)
|
|
461
|
-
return
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
const bs = new BitStream(data)
|
|
465
|
-
delete pgn.format
|
|
466
|
-
delete pgn.type
|
|
467
|
-
delete pgn.prefix
|
|
468
|
-
const res = this._parse(pgn, bs, len || data.length, coalesced, cb, pgn_data)
|
|
469
|
-
if (res) {
|
|
470
|
-
debug('parsed pgn %j', pgn)
|
|
471
|
-
}
|
|
472
|
-
return res
|
|
473
|
-
} catch ( error ) {
|
|
474
|
-
cb && cb(error)
|
|
475
|
-
this.emit('error', pgn_data, error)
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
parseString (pgn_data, cb) {
|
|
480
|
-
try {
|
|
481
|
-
const { coalesced, data, error, len, ...pgn } = parseN2kString(pgn_data, this.options)
|
|
482
|
-
if (error) {
|
|
483
|
-
cb && cb(error)
|
|
484
|
-
this.emit('error', pgn, error)
|
|
485
|
-
return
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const bs = new BitStream(data)
|
|
489
|
-
delete pgn.format
|
|
490
|
-
delete pgn.type
|
|
491
|
-
delete pgn.prefix
|
|
492
|
-
const res = this._parse(pgn, bs, len || data.length, coalesced, cb, pgn_data)
|
|
493
|
-
if (res) {
|
|
494
|
-
debug('parsed pgn %j', pgn)
|
|
495
|
-
}
|
|
496
|
-
return res
|
|
497
|
-
} catch ( error ) {
|
|
498
|
-
cb && cb(error)
|
|
499
|
-
this.emit('error', pgn_data, error)
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// What uses this? What created the buffer?
|
|
504
|
-
parseBuffer (pgn_data, cb) {
|
|
505
|
-
try {
|
|
506
|
-
var bv = new BitView(pgn_data);
|
|
507
|
-
var bs = new BitStream(bv)
|
|
508
|
-
|
|
509
|
-
var pgn = {}
|
|
510
|
-
|
|
511
|
-
// This might be good to move to canId.js ?
|
|
512
|
-
pgn.prio = bs.readUint8()
|
|
513
|
-
pgn.pgn = bs.readUint8() + 256 * (bs.readUint8() + 256 * bs.readUint8());
|
|
514
|
-
pgn.dst = bs.readUint8()
|
|
515
|
-
pgn.src = bs.readUint8()
|
|
516
|
-
pgn.timestamp = new Date().toISOString()
|
|
517
|
-
|
|
518
|
-
var timestamp = bs.readUint32()
|
|
519
|
-
var len = bs.readUint8()
|
|
520
|
-
const res = this._parse(pgn, bs, len, true, cb)
|
|
521
|
-
if ( res ) {
|
|
522
|
-
debug('parsed pgn %j', pgn)
|
|
523
|
-
}
|
|
524
|
-
return res
|
|
525
|
-
} catch ( error ) {
|
|
526
|
-
const err = new Error(`error reading pgn ${JSON.stringify(pgn_data)} ${error}`)
|
|
527
|
-
cb && cb(err)
|
|
528
|
-
this.emit('error', pgn_data, error)
|
|
529
|
-
console.error(err)
|
|
530
|
-
return
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
function getField(pgn, index, data) {
|
|
537
|
-
var pgnList = pgns[pgn]
|
|
538
|
-
if ( pgnList ) {
|
|
539
|
-
var pgn = pgnList[0]
|
|
540
|
-
let dataList = data.list ? data.list : data.fields.list
|
|
541
|
-
|
|
542
|
-
if ( pgnList.length > 1 ) {
|
|
543
|
-
let idx = 0
|
|
544
|
-
while ( idx < pgn.Fields.length )
|
|
545
|
-
{
|
|
546
|
-
field = pgn.Fields[idx]
|
|
547
|
-
var hasMatch = !_.isUndefined(field.Match)
|
|
548
|
-
if ( hasMatch && dataList.length > 0 ) {
|
|
549
|
-
let param = dataList.find(f => f.Parameter === idx+1)
|
|
550
|
-
|
|
551
|
-
if ( param ) {
|
|
552
|
-
pgnList = pgnList.filter(f => f.Fields[idx].Match == param.Value)
|
|
553
|
-
if ( pgnList.length == 0 ) {
|
|
554
|
-
throw new Error('unable to read: ' + JSON.stringify(data))
|
|
555
|
-
return
|
|
556
|
-
} else {
|
|
557
|
-
pgn = pgnList[0]
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
idx++
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
if ( index >= 0 && index < pgn.Fields.length ) {
|
|
566
|
-
return pgn.Fields[index]
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const RepeatingFields = pgn.RepeatingFieldSet1Size ? pgn.RepeatingFieldSet1Size : 0
|
|
570
|
-
if ( RepeatingFields ) {
|
|
571
|
-
var startOfRepeatingFields = pgn.Fields.length - RepeatingFields
|
|
572
|
-
index = startOfRepeatingFields + ((index - startOfRepeatingFields) % RepeatingFields);
|
|
573
|
-
return pgn.Fields[index]
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return null;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
function pad2(x) {
|
|
580
|
-
x = x.toString()
|
|
581
|
-
return x.length === 1 ? "0" + x : x
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
function pad(n, p, c)
|
|
585
|
-
{
|
|
586
|
-
n = n.toString()
|
|
587
|
-
var pad_char = typeof c !== 'undefined' ? c : '0';
|
|
588
|
-
var pad = new Array(1 + p).join(pad_char);
|
|
589
|
-
return (pad + n).slice(-pad.length);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function lookup(field, value) {
|
|
593
|
-
var name
|
|
594
|
-
if ( field.LookupEnumeration ) {
|
|
595
|
-
name = lookupEnumerationName(field.LookupEnumeration, value)
|
|
596
|
-
} else {
|
|
597
|
-
name = lookupFieldTypeEnumerationName(field.LookupFieldTypeEnumeration, value)
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
return name ? name : value
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
function readField(options, runPostProcessor, pgn, field, bs, fields) {
|
|
604
|
-
var value
|
|
605
|
-
|
|
606
|
-
var reader = fieldTypeReaders[field.FieldType]
|
|
607
|
-
if ( reader ) {
|
|
608
|
-
value = reader(pgn, field, bs)
|
|
609
|
-
} else {
|
|
610
|
-
if ( field.FieldType !== RES_BINARY && bs.bitsLeft < field.BitLength ) {
|
|
611
|
-
//no more data
|
|
612
|
-
bs.readBits(bs.bitsLeft, false)
|
|
613
|
-
return
|
|
614
|
-
}
|
|
615
|
-
value = readValue(options, pgn, field, bs, fields)
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
//console.log(`${field.Name} ${value} ${field.Resolution}`)
|
|
619
|
-
|
|
620
|
-
if ( value != null && !_.isUndefined(value) ) {
|
|
621
|
-
let type = field.FieldType //hack, missing type
|
|
622
|
-
var postProcessor = fieldTypePostProcessors[type]
|
|
623
|
-
if ( postProcessor ) {
|
|
624
|
-
if ( runPostProcessor ) {
|
|
625
|
-
value = postProcessor(field, value)
|
|
626
|
-
}
|
|
627
|
-
} else {
|
|
628
|
-
if ( field.Offset ) {
|
|
629
|
-
value += field.Offset
|
|
630
|
-
}
|
|
631
|
-
let max
|
|
632
|
-
if ( typeof field.RangeMax !== 'undefined'
|
|
633
|
-
&& field.Resolution ) {
|
|
634
|
-
max = field.RangeMax / field.Resolution
|
|
635
|
-
}
|
|
636
|
-
if ( options.checkForInvalidFields !== false && max !== 'undefined' &&
|
|
637
|
-
field.FieldType !== 'LOOKUP' &&
|
|
638
|
-
field.FieldType !== 'DYNAMIC_FIELD_KEY' &&
|
|
639
|
-
field.FieldType !== 'PGN' &&
|
|
640
|
-
field.BitLength > 1 &&
|
|
641
|
-
max - value < 0 ) {
|
|
642
|
-
//console.log(`Bad field ${field.Name} ${max - value}`)
|
|
643
|
-
value = null
|
|
644
|
-
} if ( field.Resolution && typeof value === 'number' ) {
|
|
645
|
-
var resolution = field.Resolution
|
|
646
|
-
|
|
647
|
-
if ( _.isString(resolution) ) {
|
|
648
|
-
resolution = Number.parseFloat(resolution)
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
value = (value * resolution)
|
|
652
|
-
|
|
653
|
-
let precision = 0;
|
|
654
|
-
for (let r = resolution; (r > 0.0) && (r < 1.0); r = r * 10.0)
|
|
655
|
-
{
|
|
656
|
-
precision++;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
value = Number.parseFloat(value.toFixed(precision))
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
if ((field.FieldType === 'LOOKUP' ||
|
|
663
|
-
field.FieldType === 'DYNAMIC_FIELD_KEY') &&
|
|
664
|
-
runPostProcessor &&
|
|
665
|
-
(_.isUndefined(options.resolveEnums) ||
|
|
666
|
-
options.resolveEnums)) {
|
|
667
|
-
if (field.Id === "timeStamp" && value < 60) {
|
|
668
|
-
value = value.toString()
|
|
669
|
-
} else {
|
|
670
|
-
value = lookup(field, value)
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if ( field.Name === 'Industry Code' && _.isNumber(value) && runPostProcessor ) {
|
|
675
|
-
var name = getIndustryName(value)
|
|
676
|
-
if ( name ) {
|
|
677
|
-
value = name
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
if ( field.Unit === "kWh" ) {
|
|
682
|
-
value *= 3.6e6; // 1 kWh = 3.6 MJ.
|
|
683
|
-
} else if (field.Unit === "Ah") {
|
|
684
|
-
value *= 3600.0; // 1 Ah = 3600 C.
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
return value
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
function readValue(options, pgn, field, bs, fields, bitLength) {
|
|
692
|
-
var value
|
|
693
|
-
if ( _.isUndefined(bitLength) ) {
|
|
694
|
-
if ( field.BitLengthVariable && field.FieldType === "DYNAMIC_FIELD_VALUE" ) {
|
|
695
|
-
bitLength = lookupKeyBitLength(pgn.fields, fields)
|
|
696
|
-
} else {
|
|
697
|
-
bitLength = field.BitLength
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
if ( field.FieldType == 'VARIABLE' ) {
|
|
701
|
-
return readVariableLengthField(options, pgn, field, bs)
|
|
702
|
-
} else if (bitLength === 8) {
|
|
703
|
-
if ( field.Signed ) {
|
|
704
|
-
value = bs.readInt8()
|
|
705
|
-
value = value === 0x7f ? null : value
|
|
706
|
-
} else {
|
|
707
|
-
value = bs.readUint8()
|
|
708
|
-
value = value === 0xff ? null : value
|
|
709
|
-
}
|
|
710
|
-
} else if ( bitLength == 16 ) {
|
|
711
|
-
if ( field.Signed ) {
|
|
712
|
-
value = bs.readInt16()
|
|
713
|
-
value = value === 0x7fff ? null : value
|
|
714
|
-
} else {
|
|
715
|
-
value = bs.readUint16()
|
|
716
|
-
value = value === 0xffff ? null : value
|
|
717
|
-
}
|
|
718
|
-
} else if ( bitLength == 24 ) {
|
|
719
|
-
var b1 = bs.readUint8()
|
|
720
|
-
var b2 = bs.readUint8()
|
|
721
|
-
var b3 = bs.readUint8()
|
|
722
|
-
|
|
723
|
-
//debug(`24 bit ${b1.toString(16)} ${b2.toString(16)} ${b3.toString(16)}`)
|
|
724
|
-
value = (b3 << 16) + (b2 << 8) + (b1)
|
|
725
|
-
//debug(`value ${value.toString(16)}`)
|
|
726
|
-
} else if ( bitLength == 32 ) {
|
|
727
|
-
if ( field.Signed ) {
|
|
728
|
-
value = bs.readInt32()
|
|
729
|
-
value = value === 0x7fffffff ? null : value
|
|
730
|
-
} else {
|
|
731
|
-
value = bs.readUint32()
|
|
732
|
-
value = value === 0xffffffff ? null : value
|
|
733
|
-
}
|
|
734
|
-
} else if ( bitLength == 48 ) {
|
|
735
|
-
var a = bs.readUint32()
|
|
736
|
-
var b = bs.readUint16()
|
|
737
|
-
|
|
738
|
-
if ( field.Signed ) {
|
|
739
|
-
value = a == 0xffffffff && b == 0x7fff ? null : new Int64LE(b, a)
|
|
740
|
-
} else {
|
|
741
|
-
value = a == 0xffffffff && b == 0xffff ? null : new Int64LE(b, a)
|
|
742
|
-
}
|
|
743
|
-
} else if ( bitLength == 64 ) {
|
|
744
|
-
var x = bs.readUint32()
|
|
745
|
-
var y = bs.readUint32()
|
|
746
|
-
|
|
747
|
-
if ( field.Signed ) {
|
|
748
|
-
value = (x === 0xffffffff || x === 0xfffffffe) && y == 0x7fffffff ? null : new Int64LE(y,x)
|
|
749
|
-
} else {
|
|
750
|
-
value = (x === 0xffffffff || x === 0xfffffffe) && y == 0xffffffff ? null : new Uint64LE(y,x)
|
|
751
|
-
}
|
|
752
|
-
} else if ( bitLength <= 64 ) {
|
|
753
|
-
value = bs.readBits(bitLength, field.Signed)
|
|
754
|
-
if ( bitLength > 1 && isMax(bitLength, value, field.Signed) ) {
|
|
755
|
-
value = null
|
|
756
|
-
}
|
|
757
|
-
} else {
|
|
758
|
-
if ( bs.bitsLeft < field.BitLength ) {
|
|
759
|
-
bitLength = bs.bitsLeft
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
value = bs.readArrayBuffer(bitLength/8, field.Signed)
|
|
763
|
-
value = new Uint32Array(value)
|
|
764
|
-
.reduce(function(acc, i) {
|
|
765
|
-
acc.push(i.toString(16));
|
|
766
|
-
return acc;
|
|
767
|
-
}, [])
|
|
768
|
-
.map(x => (x.length === 1 ? "0" + x : x))
|
|
769
|
-
.join(" ")
|
|
770
|
-
|
|
771
|
-
return value
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
if ( value != null && typeof value !== 'undefined' && typeof value !== 'number' ) {
|
|
775
|
-
value = Number(value)
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
return value
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
function isMax(numBits, value, signed)
|
|
782
|
-
{
|
|
783
|
-
if ( signed ) {
|
|
784
|
-
numBits--
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
while ( numBits-- ) {
|
|
788
|
-
if ( (value & 1) == 0 ){
|
|
789
|
-
return false
|
|
790
|
-
}
|
|
791
|
-
value = value >> 1
|
|
792
|
-
}
|
|
793
|
-
return signed ? (value & 1) == 0 : true
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
function readVariableLengthField(options, pgn, field, bs) {
|
|
797
|
-
/* PGN 126208 contains variable field length.
|
|
798
|
-
* The field length can be derived from the PGN mentioned earlier in the message,
|
|
799
|
-
* plus the field number.
|
|
800
|
-
*/
|
|
801
|
-
|
|
802
|
-
/*
|
|
803
|
-
* This is rather hacky. We know that the 'data' pointer points to the n-th variable field
|
|
804
|
-
* length and thus that the field number is exactly one byte earlier.
|
|
805
|
-
*/
|
|
806
|
-
|
|
807
|
-
try {
|
|
808
|
-
var refField = getField(pgn.fields.PGN, bs.view.buffer[bs.byteIndex-1]-1, pgn)
|
|
809
|
-
|
|
810
|
-
if ( refField ) {
|
|
811
|
-
var bits = (refField.BitLength + 7) & ~7; // Round # of bits in field refField up to complete bytes: 1->8, 7->8, 8->8 etc.
|
|
812
|
-
let res = readField(options, false, pgn, refField, bs)
|
|
813
|
-
|
|
814
|
-
if ( bits > refField.BitLength ) {
|
|
815
|
-
bs.readBits(bits - refField.BitLength, false)
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
return res
|
|
819
|
-
}
|
|
820
|
-
} catch ( error ) {
|
|
821
|
-
debug(error)
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
fieldTypeReaders[
|
|
826
|
-
'STRING_LAU'
|
|
827
|
-
//'ASCII or UNICODE string starting with length and control byte'
|
|
828
|
-
] = (pgn, field, bs) => {
|
|
829
|
-
|
|
830
|
-
if ( bs.bitsLeft >= 16 ) {
|
|
831
|
-
const len = bs.readUint8()-2
|
|
832
|
-
const control = bs.readUint8()
|
|
833
|
-
let nameLen = len
|
|
834
|
-
|
|
835
|
-
if ( field.Name === 'AtoN Name' && len > 20 ) {
|
|
836
|
-
nameLen = 20
|
|
837
|
-
} else if ( len <= 0 ) {
|
|
838
|
-
return null
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
var buf = Buffer.alloc(len)
|
|
842
|
-
var idx = 0
|
|
843
|
-
for ( ; idx < len && bs.bitsLeft >= 8; idx++ ) {
|
|
844
|
-
var c = bs.readUint8()
|
|
845
|
-
buf.writeUInt8(c, idx)
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
if ( buf[buf.length-1] === 0 ) {
|
|
849
|
-
nameLen = nameLen - 1
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
return buf.toString(control == 0 ? 'utf8' : 'ascii', 0, idx < nameLen ? idx : nameLen).trim()
|
|
853
|
-
} else {
|
|
854
|
-
return null
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
fieldTypeReaders[
|
|
859
|
-
'STRING_LZ'
|
|
860
|
-
//'ASCII string starting with length byte'
|
|
861
|
-
] = (pgn, field, bs) => {
|
|
862
|
-
var len = bs.readUint8()
|
|
863
|
-
|
|
864
|
-
var buf = Buffer.alloc(len)
|
|
865
|
-
var idx = 0
|
|
866
|
-
for ( ; idx < len && bs.bitsLeft >= 8; idx++ ) {
|
|
867
|
-
var c = bs.readUint8()
|
|
868
|
-
buf.writeUInt8(c, idx)
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
return buf.toString('utf-8', 0, idx)
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
fieldTypeReaders["String with start/stop byte"] = (pgn, field, bs) => {
|
|
875
|
-
var len
|
|
876
|
-
var first = bs.readUint8()
|
|
877
|
-
if ( first == 0xff ) { // no name, stop reading
|
|
878
|
-
return ''
|
|
879
|
-
} else if ( first == 0x02 ) {
|
|
880
|
-
var buf = Buffer.alloc(255)
|
|
881
|
-
var c
|
|
882
|
-
var idx = 0
|
|
883
|
-
while ( (c = bs.readUint8()) != 0x01 ) {
|
|
884
|
-
buf.writeUInt8(c, idx++)
|
|
885
|
-
}
|
|
886
|
-
return buf.toString('ascii', 0, idx)
|
|
887
|
-
} else if ( first > 0x02 ) {
|
|
888
|
-
var len = first
|
|
889
|
-
var second = bs.readUint8()
|
|
890
|
-
var buf = Buffer.alloc(len)
|
|
891
|
-
var idx = 0
|
|
892
|
-
if ( second == 0x01 ) {
|
|
893
|
-
len -= 2
|
|
894
|
-
} else {
|
|
895
|
-
buf.writeUInt8(second)
|
|
896
|
-
idx = 1
|
|
897
|
-
}
|
|
898
|
-
for ( ; idx < len; idx++ ) {
|
|
899
|
-
var c = bs.readUint8()
|
|
900
|
-
buf.writeUInt8(c, idx)
|
|
901
|
-
}
|
|
902
|
-
return buf.toString('ascii', 0, idx)
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
fieldTypeReaders['STRING_FIX'] = (pgn, field, bs) => {
|
|
907
|
-
var len = field.BitLength / 8
|
|
908
|
-
var buf = Buffer.alloc(len)
|
|
909
|
-
|
|
910
|
-
for ( var i = 0; i < len && bs.bitsLeft >= 8; i++ ) {
|
|
911
|
-
buf.writeUInt8(bs.readUint8(), i)
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
var lastbyte = buf[len-1]
|
|
915
|
-
while (len > 0 && (lastbyte == 0xff ||
|
|
916
|
-
lastbyte == 32 ||
|
|
917
|
-
lastbyte == 0 ||
|
|
918
|
-
lastbyte == 64
|
|
919
|
-
)) {
|
|
920
|
-
len--
|
|
921
|
-
lastbyte = buf[len-1]
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
//look for a zero byte, some proprietary Raymarine pgns do this
|
|
925
|
-
let zero = 0
|
|
926
|
-
while ( zero < len ) {
|
|
927
|
-
if ( buf[zero] == 0 ) {
|
|
928
|
-
len = zero
|
|
929
|
-
break
|
|
930
|
-
}
|
|
931
|
-
zero++
|
|
932
|
-
}
|
|
933
|
-
len = zero
|
|
934
|
-
let res = len > 0 ? buf.toString('ascii', 0, len) : undefined
|
|
935
|
-
return res
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
fieldTypeReaders['BITLOOKUP'] = (pgn, field, bs) => {
|
|
939
|
-
var value = []
|
|
940
|
-
for ( var i = 0; i < field.BitLength; i++ ) {
|
|
941
|
-
if ( bs.readBits(1, 0) ) {
|
|
942
|
-
value.push(
|
|
943
|
-
lookupBitEnumerationName(field.LookupBitEnumeration, i)
|
|
944
|
-
)
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
return value
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
function lookupKeyBitLength(data, fields)
|
|
951
|
-
{
|
|
952
|
-
let field = fields.find(field => (field.Name === 'Key'))
|
|
953
|
-
|
|
954
|
-
let val = data['Key'] || data['key']
|
|
955
|
-
if ( typeof val === 'string' ) {
|
|
956
|
-
val = lookupFieldTypeEnumerationValue(field.LookupFieldTypeEnumeration, val)
|
|
957
|
-
}
|
|
958
|
-
return lookupFieldTypeEnumerationBits(field.LookupFieldTypeEnumeration, val)
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
fieldTypePostProcessors['DATE'] = (field, value) => {
|
|
962
|
-
if ( value >= 0xfffd ) {
|
|
963
|
-
value = undefined
|
|
964
|
-
} else {
|
|
965
|
-
var date = new Date((value) * 86400 * 1000)
|
|
966
|
-
//var date = moment.unix(0).add(value+1, 'days').utc().toDate()
|
|
967
|
-
value = `${date.getUTCFullYear()}.${pad2(date.getUTCMonth()+1)}.${pad2(date.getUTCDate())}`
|
|
968
|
-
}
|
|
969
|
-
return value
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
fieldTypePostProcessors['TIME'] = (field, value) => {
|
|
973
|
-
if (value >= 0xfffffffd) {
|
|
974
|
-
value = undefined
|
|
975
|
-
} else {
|
|
976
|
-
var seconds = (value * field.Resolution)
|
|
977
|
-
var minutes = (seconds / 60);
|
|
978
|
-
var seconds = seconds % 60;
|
|
979
|
-
var hours = Math.floor(minutes / 60);
|
|
980
|
-
var minutes = Math.floor(minutes % 60);
|
|
981
|
-
|
|
982
|
-
value = `${pad2(hours)}:${pad2(minutes)}:${pad2(Math.floor(seconds))}`
|
|
983
|
-
|
|
984
|
-
if ( seconds % 1 > 0 ) {
|
|
985
|
-
value = value + (seconds%1).toFixed(5).substring(1)
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
return value
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
fieldTypePostProcessors['DURATION'] = fieldTypePostProcessors['TIME']
|
|
992
|
-
|
|
993
|
-
fieldTypePostProcessors['Pressure'] = (field, value) => {
|
|
994
|
-
if (field.Unit)
|
|
995
|
-
{
|
|
996
|
-
switch (field.Unit[0]) {
|
|
997
|
-
case 'h':
|
|
998
|
-
case 'H':
|
|
999
|
-
value *= 100;
|
|
1000
|
-
break;
|
|
1001
|
-
case 'k':
|
|
1002
|
-
case 'K':
|
|
1003
|
-
value *= 1000;
|
|
1004
|
-
break;
|
|
1005
|
-
case 'd':
|
|
1006
|
-
value /= 10;
|
|
1007
|
-
break;
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
return value
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
fieldTypePostProcessors[RES_BINARY] = (field, value) => {
|
|
1014
|
-
return value.toString()
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
/*
|
|
1018
|
-
fieldTypePostProcessors['Manufacturer code'] = (field, value) => {
|
|
1019
|
-
var manufacturer = getManufacturerName(value)
|
|
1020
|
-
return manufacturer ? manufacturer : value
|
|
1021
|
-
}
|
|
1022
|
-
*/
|
|
1023
|
-
|
|
1024
|
-
module.exports = {
|
|
1025
|
-
Parser: Parser,
|
|
1026
|
-
getField: getField,
|
|
1027
|
-
}
|