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