@blueid/access-cli 0.21.0 → 0.22.0
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/lib/createProvisioningData/action.js +7 -3
- package/lib/createProvisioningData/index.js +11 -11
- package/lib/proto/BlueCore_pb.js +10 -9
- package/lib/proto/BlueSystem_pb.js +22 -34
- package/lib/shared/binaryToIntelHex.js +246 -0
- package/lib/shared/crc16.js +26 -0
- package/lib/shared/outputBuffer.js +58 -2
- package/package.json +2 -2
|
@@ -9,7 +9,7 @@ import { BlueSystemProvisioning } from '../proto/BlueSystem_pb.js'
|
|
|
9
9
|
|
|
10
10
|
export default async (
|
|
11
11
|
provisioningFile,
|
|
12
|
-
{ vendorIdentity, hardwareType, hardwareSerialNumber, hardwareVersion, hardwareName, meta, format }
|
|
12
|
+
{ vendorIdentity, hardwareType, hardwareSerialNumber, hardwareVersion, hardwareName, meta, format },
|
|
13
13
|
) => {
|
|
14
14
|
const { deviceId, terminalPrivateKey, signaturePublicKey } = await registerDevice({
|
|
15
15
|
vendorIdentity,
|
|
@@ -28,7 +28,11 @@ export default async (
|
|
|
28
28
|
signaturePublicKey,
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
const buffer = protoDelimited.enc(provisionData)
|
|
31
|
+
const buffer = Buffer.from(protoDelimited.enc(provisionData))
|
|
32
32
|
|
|
33
|
-
outputBuffer(buffer, format, provisioningFile
|
|
33
|
+
outputBuffer(buffer, format, provisioningFile, {
|
|
34
|
+
fileHexSize: 512,
|
|
35
|
+
fileHexCrc16: true,
|
|
36
|
+
fileHexCrc16Magic: true,
|
|
37
|
+
})
|
|
34
38
|
}
|
|
@@ -13,36 +13,36 @@ program
|
|
|
13
13
|
.addOption(
|
|
14
14
|
new Option(
|
|
15
15
|
'--vendorIdentity [vendorIdentity]',
|
|
16
|
-
'The vendor identity to be used for authentication or "test" for test-data'
|
|
17
|
-
).makeOptionMandatory(true)
|
|
16
|
+
'The vendor identity to be used for authentication or "test" for test-data',
|
|
17
|
+
).makeOptionMandatory(true),
|
|
18
18
|
)
|
|
19
19
|
.addOption(
|
|
20
20
|
new Option('--hardwareType [hardwareType]', 'The type of the device')
|
|
21
21
|
.choices(Object.values(BlueHardwareType).filter((x) => typeof x === 'string'))
|
|
22
|
-
.makeOptionMandatory(true)
|
|
22
|
+
.makeOptionMandatory(true),
|
|
23
23
|
)
|
|
24
24
|
.addOption(
|
|
25
25
|
new Option(
|
|
26
26
|
'--hardwareVersion [hardwareVersion]',
|
|
27
|
-
'The actual hardware version of the device which is vendor specific'
|
|
28
|
-
)
|
|
27
|
+
'The actual hardware version of the device which is vendor specific',
|
|
28
|
+
),
|
|
29
29
|
)
|
|
30
30
|
.addOption(
|
|
31
31
|
new Option(
|
|
32
32
|
'--hardwareSerialNumber [hardwareSerialNumber]',
|
|
33
|
-
'The vendor serial number for the device which is vendor specific'
|
|
34
|
-
).makeOptionMandatory(true)
|
|
33
|
+
'The vendor serial number for the device which is vendor specific',
|
|
34
|
+
).makeOptionMandatory(true),
|
|
35
35
|
)
|
|
36
36
|
.addOption(
|
|
37
37
|
new Option(
|
|
38
38
|
'--hardwareName [hardwareName]',
|
|
39
|
-
'The human readable identifier of the device which is vendor specific'
|
|
40
|
-
).makeOptionMandatory(true)
|
|
39
|
+
'The human readable identifier of the device which is vendor specific',
|
|
40
|
+
).makeOptionMandatory(true),
|
|
41
41
|
)
|
|
42
42
|
.addOption(new Option('--meta [meta]', 'Optional meta data in JSON format for the device'))
|
|
43
43
|
.addOption(
|
|
44
44
|
new Option('--format [format]', 'The output format to be used')
|
|
45
|
-
.choices(['file'].concat(outputFormats))
|
|
46
|
-
.makeOptionMandatory()
|
|
45
|
+
.choices(['file', 'file_hex'].concat(outputFormats))
|
|
46
|
+
.makeOptionMandatory(),
|
|
47
47
|
)
|
|
48
48
|
.action(action)
|
package/lib/proto/BlueCore_pb.js
CHANGED
|
@@ -350,7 +350,7 @@ export const BlueLocalTimeSchedule = proto2.makeMessageType(
|
|
|
350
350
|
() => [
|
|
351
351
|
{ no: 1, name: "dayOfYearStart", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
352
352
|
{ no: 2, name: "dayOfYearEnd", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
353
|
-
{ no: 3, name: "weekdays", kind: "scalar", T:
|
|
353
|
+
{ no: 3, name: "weekdays", kind: "scalar", T: 8 /* ScalarType.BOOL */, repeated: true },
|
|
354
354
|
{ no: 4, name: "timePeriod", kind: "message", T: BlueLocalTimeperiod },
|
|
355
355
|
],
|
|
356
356
|
);
|
|
@@ -386,6 +386,7 @@ export const BlueBleManufacturerInfo = proto2.makeMessageType(
|
|
|
386
386
|
{ no: 2, name: "batteryLevel", kind: "enum", T: proto2.getEnumType(BlueBatteryLevel) },
|
|
387
387
|
{ no: 3, name: "applicationVersion", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
388
388
|
{ no: 4, name: "localMidnightTimeEpoch", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
389
|
+
{ no: 5, name: "isFactory", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
389
390
|
],
|
|
390
391
|
);
|
|
391
392
|
|
|
@@ -439,10 +440,10 @@ export const BlueSPHandshakeReply = proto2.makeMessageType(
|
|
|
439
440
|
);
|
|
440
441
|
|
|
441
442
|
/**
|
|
442
|
-
* @generated from message
|
|
443
|
+
* @generated from message BlueSPTokenCommand
|
|
443
444
|
*/
|
|
444
|
-
export const
|
|
445
|
-
"
|
|
445
|
+
export const BlueSPTokenCommand = proto2.makeMessageType(
|
|
446
|
+
"BlueSPTokenCommand",
|
|
446
447
|
() => [
|
|
447
448
|
{ no: 1, name: "credentialId", kind: "message", T: BlueCredentialId },
|
|
448
449
|
{ no: 2, name: "validityStart", kind: "message", T: BlueLocalTimestamp },
|
|
@@ -453,13 +454,13 @@ export const BlueSPDataCommand = proto2.makeMessageType(
|
|
|
453
454
|
);
|
|
454
455
|
|
|
455
456
|
/**
|
|
456
|
-
* @generated from message
|
|
457
|
+
* @generated from message BlueSPToken
|
|
457
458
|
*/
|
|
458
|
-
export const
|
|
459
|
-
"
|
|
459
|
+
export const BlueSPToken = proto2.makeMessageType(
|
|
460
|
+
"BlueSPToken",
|
|
460
461
|
() => [
|
|
461
462
|
{ no: 1, name: "signature", kind: "scalar", T: 12 /* ScalarType.BYTES */ },
|
|
462
|
-
{ no: 2, name: "command", kind: "message", T:
|
|
463
|
+
{ no: 2, name: "command", kind: "message", T: BlueSPTokenCommand, oneof: "payload" },
|
|
463
464
|
{ no: 3, name: "ossSo", kind: "message", T: BlueOssSoMobile, oneof: "payload" },
|
|
464
465
|
{ no: 4, name: "ossSid", kind: "message", T: BlueOssSidMobile, oneof: "payload" },
|
|
465
466
|
],
|
|
@@ -695,7 +696,7 @@ export const BlueOssSoCredentialType = proto2.makeMessageType(
|
|
|
695
696
|
export const BlueOssSoDTScheduleDay = proto2.makeMessageType(
|
|
696
697
|
"BlueOssSoDTScheduleDay",
|
|
697
698
|
() => [
|
|
698
|
-
{ no:
|
|
699
|
+
{ no: 3, name: "weekdays", kind: "scalar", T: 8 /* ScalarType.BOOL */, repeated: true },
|
|
699
700
|
{ no: 8, name: "timePeriods", kind: "message", T: BlueLocalTimeperiod, repeated: true },
|
|
700
701
|
],
|
|
701
702
|
);
|
|
@@ -7,38 +7,16 @@ import { proto2 } from "@bufbuild/protobuf";
|
|
|
7
7
|
import { BlueBatteryLevel, BlueBlacklistEntry, BlueEvent, BlueHardwareType, BlueLocalTimeSchedule, BlueLocalTimestamp } from "./BlueCore_pb.js";
|
|
8
8
|
import { BlueLockConfig, BlueLockStatus } from "./BlueLock_pb.js";
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* @generated from enum BlueOnlineMode
|
|
12
|
-
*/
|
|
13
|
-
export const BlueOnlineMode = proto2.makeEnum(
|
|
14
|
-
"BlueOnlineMode",
|
|
15
|
-
[
|
|
16
|
-
{no: 0, name: "OfflineMode"},
|
|
17
|
-
{no: 1, name: "ManualWifi"},
|
|
18
|
-
{no: 2, name: "AlwaysOnWifi"},
|
|
19
|
-
{no: 3, name: "UseLan"},
|
|
20
|
-
],
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @generated from message BlueTimeOffsetPeriod
|
|
25
|
-
*/
|
|
26
|
-
export const BlueTimeOffsetPeriod = proto2.makeMessageType(
|
|
27
|
-
"BlueTimeOffsetPeriod",
|
|
28
|
-
() => [
|
|
29
|
-
{ no: 1, name: "epoch", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
30
|
-
{ no: 2, name: "offset", kind: "scalar", T: 5 /* ScalarType.INT32 */ },
|
|
31
|
-
],
|
|
32
|
-
);
|
|
33
|
-
|
|
34
10
|
/**
|
|
35
11
|
* @generated from message BlueBaseConfig
|
|
36
12
|
*/
|
|
37
13
|
export const BlueBaseConfig = proto2.makeMessageType(
|
|
38
14
|
"BlueBaseConfig",
|
|
39
15
|
() => [
|
|
40
|
-
{ no: 1, name: "
|
|
41
|
-
{ no: 2, name: "
|
|
16
|
+
{ no: 1, name: "isoCountry", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
17
|
+
{ no: 2, name: "isoState", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
18
|
+
{ no: 3, name: "utcOffsetMinutes", kind: "scalar", T: 5 /* ScalarType.INT32 */, default: 0 },
|
|
19
|
+
{ no: 4, name: "autoCheckSystemStatus", kind: "scalar", T: 8 /* ScalarType.BOOL */, default: false },
|
|
42
20
|
],
|
|
43
21
|
);
|
|
44
22
|
|
|
@@ -65,11 +43,21 @@ export const BlueBleConfig = proto2.makeMessageType(
|
|
|
65
43
|
export const BlueOnlineConfig = proto2.makeMessageType(
|
|
66
44
|
"BlueOnlineConfig",
|
|
67
45
|
() => [
|
|
68
|
-
{ no: 1, name: "
|
|
69
|
-
{ no: 2, name: "
|
|
70
|
-
{ no: 3, name: "
|
|
71
|
-
{ no: 4, name: "
|
|
72
|
-
|
|
46
|
+
{ no: 1, name: "connectSchedules", kind: "message", T: BlueLocalTimeSchedule, repeated: true },
|
|
47
|
+
{ no: 2, name: "wifiSSID", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
48
|
+
{ no: 3, name: "wifiPassword", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
49
|
+
{ no: 4, name: "timeoutSec", kind: "scalar", T: 13 /* ScalarType.UINT32 */, default: 30 },
|
|
50
|
+
],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @generated from message BlueAccessConfigGroup
|
|
55
|
+
*/
|
|
56
|
+
export const BlueAccessConfigGroup = proto2.makeMessageType(
|
|
57
|
+
"BlueAccessConfigGroup",
|
|
58
|
+
() => [
|
|
59
|
+
{ no: 1, name: "groupId", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
60
|
+
{ no: 2, name: "schedules", kind: "message", T: BlueLocalTimeSchedule, repeated: true },
|
|
73
61
|
],
|
|
74
62
|
);
|
|
75
63
|
|
|
@@ -81,7 +69,7 @@ export const BlueAccessConfig = proto2.makeMessageType(
|
|
|
81
69
|
() => [
|
|
82
70
|
{ no: 1, name: "id", kind: "scalar", T: 13 /* ScalarType.UINT32 */, default: 1 },
|
|
83
71
|
{ no: 2, name: "siteId", kind: "scalar", T: 13 /* ScalarType.UINT32 */, default: 1 },
|
|
84
|
-
{ no: 3, name: "
|
|
72
|
+
{ no: 3, name: "groups", kind: "message", T: BlueAccessConfigGroup, repeated: true },
|
|
85
73
|
],
|
|
86
74
|
);
|
|
87
75
|
|
|
@@ -112,7 +100,7 @@ export const BlueOssConfig = proto2.makeMessageType(
|
|
|
112
100
|
export const BlueSystemConfig = proto2.makeMessageType(
|
|
113
101
|
"BlueSystemConfig",
|
|
114
102
|
() => [
|
|
115
|
-
{ no: 1, name: "
|
|
103
|
+
{ no: 1, name: "configId", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
116
104
|
{ no: 2, name: "base", kind: "message", T: BlueBaseConfig },
|
|
117
105
|
{ no: 3, name: "ble", kind: "message", T: BlueBleConfig },
|
|
118
106
|
{ no: 4, name: "online", kind: "message", T: BlueOnlineConfig },
|
|
@@ -184,7 +172,7 @@ export const BlueSystemProvisioning = proto2.makeMessageType(
|
|
|
184
172
|
export const BlueSystemStatus = proto2.makeMessageType(
|
|
185
173
|
"BlueSystemStatus",
|
|
186
174
|
() => [
|
|
187
|
-
{ no: 1, name: "
|
|
175
|
+
{ no: 1, name: "configId", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
188
176
|
{ no: 2, name: "deviceId", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
189
177
|
{ no: 3, name: "serialNumber", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
190
178
|
{ no: 4, name: "hardwareType", kind: "enum", T: proto2.getEnumType(BlueHardwareType) },
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// From https://en.wikipedia.org/wiki/Intel_HEX
|
|
2
|
+
//
|
|
3
|
+
// Intel Hex Line Format (I32HEX, 32 bit addresses) is
|
|
4
|
+
// :llaaaatt[dd...]cc
|
|
5
|
+
// Where
|
|
6
|
+
// ll=data length
|
|
7
|
+
// aaaa=lower 16 bit address
|
|
8
|
+
// tt=record type
|
|
9
|
+
// 00 - data record
|
|
10
|
+
// 01 - end-of-file record
|
|
11
|
+
// 02 - extended segment address record (Not used in I32Hex)
|
|
12
|
+
// 04 - extended linear address record
|
|
13
|
+
// 05 - start linear address record (MDK-ARM only)
|
|
14
|
+
// cc=checksum = 2's comp of negative of sum of all record bytes
|
|
15
|
+
|
|
16
|
+
var DEFAULT_LINE_TERMINATOR = '\n'
|
|
17
|
+
var SEGMENT_LENGTH_BYTES = 0x10000
|
|
18
|
+
var MAX_BASE_ADDRESS = 0xffff
|
|
19
|
+
|
|
20
|
+
const utils = {
|
|
21
|
+
numberToHexString: function (d, padding) {
|
|
22
|
+
var hex = Number(d).toString(16).toUpperCase()
|
|
23
|
+
padding = typeof padding === 'undefined' || padding === null ? (padding = 8) : padding
|
|
24
|
+
|
|
25
|
+
while (hex.length < padding) {
|
|
26
|
+
hex = '0' + hex
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return hex
|
|
30
|
+
},
|
|
31
|
+
numberToHexStringLittleEndian: function (d, padding) {
|
|
32
|
+
if (padding % 2 != 0)
|
|
33
|
+
throw new Error('numberToHexStringLittleEndian requires an even padding [' + padding + ']')
|
|
34
|
+
|
|
35
|
+
var bigEndianHex = utils.numberToHexString(d, padding)
|
|
36
|
+
|
|
37
|
+
var littleEndianHex = ''
|
|
38
|
+
while (bigEndianHex.length) {
|
|
39
|
+
var nibble = bigEndianHex[0] + bigEndianHex[1]
|
|
40
|
+
bigEndianHex = bigEndianHex.slice(2)
|
|
41
|
+
littleEndianHex = nibble + littleEndianHex
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return littleEndianHex
|
|
45
|
+
},
|
|
46
|
+
hexStringByWords: function (hex, littleEndian) {
|
|
47
|
+
if (!hex || hex.length % 8 !== 0) throw new Error('hexStringByWords requires a word-aligned hex string')
|
|
48
|
+
littleEndian = !!littleEndian
|
|
49
|
+
|
|
50
|
+
hex = hex.toUpperCase()
|
|
51
|
+
const wordStrings = []
|
|
52
|
+
|
|
53
|
+
for (var i = 0; i < hex.length; i += 8) {
|
|
54
|
+
var ws = hex.substring(i, i + 8)
|
|
55
|
+
if (littleEndian) {
|
|
56
|
+
ws = ws.substring(6, 8) + ws.substring(4, 6) + ws.substring(2, 4) + ws.substring(0, 2)
|
|
57
|
+
}
|
|
58
|
+
wordStrings.push(ws)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return wordStrings.join(' ')
|
|
62
|
+
},
|
|
63
|
+
hexToDecimal: function (hexString) {
|
|
64
|
+
try {
|
|
65
|
+
return parseInt(hexString, 16)
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.error('Error parsing hexString: ', e)
|
|
68
|
+
return 0
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
toUInt32: function (val) {
|
|
72
|
+
return val >>> 0
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function IntelHexFileOut(startAddress) {
|
|
77
|
+
if (startAddress === void 0) startAddress = 0
|
|
78
|
+
|
|
79
|
+
this.contents = ''
|
|
80
|
+
this.extendedAddress = 0
|
|
81
|
+
this.relativeAddress = 0
|
|
82
|
+
this.lastAppendEndedAtBoundary = false
|
|
83
|
+
this.lineTerminator = DEFAULT_LINE_TERMINATOR
|
|
84
|
+
|
|
85
|
+
this.setAddress(startAddress)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
IntelHexFileOut.withIntelHex = function (initialHexString, startAddress) {
|
|
89
|
+
const intelhexProcessor = new IntelHexFileOut(startAddress)
|
|
90
|
+
intelhexProcessor.contents = initialHexString.toUpperCase()
|
|
91
|
+
intelhexProcessor.reopen()
|
|
92
|
+
return intelhexProcessor
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
IntelHexFileOut.prototype.reopen = function () {
|
|
96
|
+
const indexOfTerminator = this.contents.indexOf(this.eofMarker())
|
|
97
|
+
if (indexOfTerminator !== -1) {
|
|
98
|
+
this.contents = this.contents.substring(0, indexOfTerminator)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
IntelHexFileOut.prototype.eofMarker = function () {
|
|
103
|
+
return ':00000001FF' + this.lineTerminator
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
IntelHexFileOut.prototype.setAddress = function (address) {
|
|
107
|
+
var extendedAddress = this._extendedAddressFromAddress(address)
|
|
108
|
+
this.relativeAddress = this._addressRelativeToExtendedAddress(address, extendedAddress)
|
|
109
|
+
|
|
110
|
+
if (this.extendedAddress === extendedAddress) return
|
|
111
|
+
this.extendedAddress = extendedAddress
|
|
112
|
+
|
|
113
|
+
this._writeExtendedAddress()
|
|
114
|
+
|
|
115
|
+
this.lastAppendEndedAtBoundary = false
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
IntelHexFileOut.prototype.writeHexStringAtAddress = function (address, hexString) {
|
|
119
|
+
if (!hexString || hexString.length === 0 || hexString.length > SEGMENT_LENGTH_BYTES || hexString.length % 2 !== 0) {
|
|
120
|
+
throw new Error('invalid length of hexString [' + hexString.length + ']')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.setAddress(address)
|
|
124
|
+
this.appendHexString(hexString)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
IntelHexFileOut.prototype.appendBuffer = function (buffer, littleEndian) {
|
|
128
|
+
if (!buffer) throw new Error('Buffer required')
|
|
129
|
+
if (buffer.length % 4 !== 0) throw new Error('Buffer length not 32 bit word aligned')
|
|
130
|
+
|
|
131
|
+
const words = []
|
|
132
|
+
for (var offset = 0; offset < buffer.length; offset += 4) {
|
|
133
|
+
const uint32 = littleEndian ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset)
|
|
134
|
+
words.push(uint32)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.appendWords(words, littleEndian)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
IntelHexFileOut.prototype.appendWords = function (words, littleEndian) {
|
|
141
|
+
if (!words || words.length === 0) {
|
|
142
|
+
// || words.length * 4 > SEGMENT_LENGTH_BYTES ){
|
|
143
|
+
throw new Error('invalid number of words [' + words.length + ']')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
var encodeAsString = littleEndian ? utils.numberToHexStringLittleEndian : utils.numberToHexString
|
|
147
|
+
|
|
148
|
+
var lineHexString = ''
|
|
149
|
+
while (words.length) {
|
|
150
|
+
lineHexString += encodeAsString(words.shift(), 8)
|
|
151
|
+
if (lineHexString.length === 32) {
|
|
152
|
+
this.appendHexString(lineHexString)
|
|
153
|
+
lineHexString = ''
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (lineHexString.length) {
|
|
158
|
+
this.appendHexString(lineHexString)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
IntelHexFileOut.prototype.appendHexString = function (hexString) {
|
|
163
|
+
if (!hexString || hexString.length === 0 || hexString.length > SEGMENT_LENGTH_BYTES || hexString.length % 2 !== 0) {
|
|
164
|
+
throw new Error('invalid length of hexString [' + hexString.length + ']')
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
while (hexString.length) {
|
|
168
|
+
var lineHexCharLength = hexString.length < 32 ? hexString.length : 32
|
|
169
|
+
var lineHexString = hexString.slice(0, lineHexCharLength)
|
|
170
|
+
|
|
171
|
+
hexString = hexString.slice(lineHexCharLength)
|
|
172
|
+
|
|
173
|
+
this._handleBlockContinuation()
|
|
174
|
+
|
|
175
|
+
var byteCount = lineHexString.length / 2
|
|
176
|
+
|
|
177
|
+
if (this.relativeAddress + byteCount > 0x10000) {
|
|
178
|
+
throw new Error('appendHexString cannot write across 63336 byte boundary')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this._writeHexString(lineHexString)
|
|
182
|
+
|
|
183
|
+
this.relativeAddress += byteCount
|
|
184
|
+
if (this.relativeAddress == SEGMENT_LENGTH_BYTES) {
|
|
185
|
+
this.extendedAddress += 0x0001
|
|
186
|
+
this.relativeAddress = 0x0000
|
|
187
|
+
this.lastAppendEndedAtBoundary = true
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
IntelHexFileOut.prototype.close = function () {
|
|
193
|
+
this.contents += this.eofMarker()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
IntelHexFileOut.prototype._writeExtendedAddress = function () {
|
|
197
|
+
var data = this.extendedAddress
|
|
198
|
+
var intelHex = '02' + '0000' + '04' + utils.numberToHexString(data, 4)
|
|
199
|
+
intelHex += this._crc(intelHex)
|
|
200
|
+
this.contents += ':' + intelHex + this.lineTerminator
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
IntelHexFileOut.prototype._writeHexString = function (hexString) {
|
|
204
|
+
var byteCount = hexString.length / 2
|
|
205
|
+
var intelHex =
|
|
206
|
+
utils.numberToHexString(byteCount, 2) + utils.numberToHexString(this.relativeAddress, 4) + '00' + hexString
|
|
207
|
+
intelHex += this._crc(intelHex)
|
|
208
|
+
this.contents += ':' + intelHex + this.lineTerminator
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
IntelHexFileOut.prototype._handleBlockContinuation = function () {
|
|
212
|
+
if (this.lastAppendEndedAtBoundary) {
|
|
213
|
+
this._writeExtendedAddress()
|
|
214
|
+
this.lastAppendEndedAtBoundary = false
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
IntelHexFileOut.prototype._crc = function (intelHexString) {
|
|
219
|
+
var sum = 0
|
|
220
|
+
for (var i = 0; i < intelHexString.length; i += 2) {
|
|
221
|
+
var byteHexString = intelHexString.charAt(i) + intelHexString.charAt(i + 1)
|
|
222
|
+
sum += utils.hexToDecimal(byteHexString) & 0xff
|
|
223
|
+
}
|
|
224
|
+
var crc = ((sum ^ 0xff) + 1) & 0xff
|
|
225
|
+
return utils.numberToHexString(crc, 2)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
IntelHexFileOut.prototype._extendedAddressFromAddress = function (address) {
|
|
229
|
+
return address >>> 16
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
IntelHexFileOut.prototype._addressRelativeToExtendedAddress = function (address, extendedAddress) {
|
|
233
|
+
var baseAddress = utils.toUInt32(extendedAddress << 16)
|
|
234
|
+
if (address < baseAddress || address - baseAddress > MAX_BASE_ADDRESS) {
|
|
235
|
+
throw new Error('address must be 0 to 65535 bytes above baseAddress [' + baseAddress + ']')
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return address - baseAddress
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export default (startAddress, littleEndian, buffer) => {
|
|
242
|
+
var processor = new IntelHexFileOut(startAddress)
|
|
243
|
+
processor.appendBuffer(buffer, littleEndian)
|
|
244
|
+
processor.close()
|
|
245
|
+
return processor.contents
|
|
246
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const STORAGE_CRC_16_MAGIC_NUMBER = 0x92db
|
|
2
|
+
|
|
3
|
+
export default (buffer, start, magicMarker = false) => {
|
|
4
|
+
// CRC-16-CCITT (XModem)
|
|
5
|
+
const crcTable = [
|
|
6
|
+
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad,
|
|
7
|
+
0xe1ce, 0xf1ef,
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
let crc = 0xffff
|
|
11
|
+
|
|
12
|
+
const data = new Uint8Array(buffer)
|
|
13
|
+
|
|
14
|
+
for (let i = start; i < data.length; i++) {
|
|
15
|
+
crc = (crc << 4) ^ crcTable[((crc >>> 12) ^ data[i]) & 0x0f]
|
|
16
|
+
crc = (crc << 4) ^ crcTable[((crc >>> 12) ^ (data[i] >>> 4)) & 0x0f]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (magicMarker) {
|
|
20
|
+
crc ^= STORAGE_CRC_16_MAGIC_NUMBER
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
crc &= 0xffff
|
|
24
|
+
|
|
25
|
+
return crc
|
|
26
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import fs from 'fs'
|
|
3
3
|
|
|
4
|
+
import binaryToIntelHex from './binaryToIntelHex.js'
|
|
5
|
+
import crc16 from './crc16.js'
|
|
6
|
+
|
|
4
7
|
const bufferToCArray = (buffer) => {
|
|
5
8
|
const hexString = buffer.toString('hex')
|
|
6
9
|
const hexArray = hexString.match(/.{1,2}/g)
|
|
@@ -13,9 +16,41 @@ const bufferToProtoArray = (buffer) => {
|
|
|
13
16
|
return hexArray.map((hex) => `\\x${hex}`).join('')
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
const bufferToIntelHex = (buffer, hexStartAddress, hexSize, hexCrc16, hexCrc16Magic) => {
|
|
20
|
+
if (typeof hexStartAddress !== 'number') {
|
|
21
|
+
throw new Error('Invalid hexStartAddress')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (typeof hexSize !== 'number') {
|
|
25
|
+
throw new Error('Invalid hexSize')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof hexCrc16 !== 'boolean') {
|
|
29
|
+
throw new Error('Invalid hexCrc16')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (typeof hexCrc16Magic !== 'boolean') {
|
|
33
|
+
throw new Error('Invalid hexCrc16Magic')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const binaryBuffer = Buffer.alloc(hexSize)
|
|
37
|
+
|
|
38
|
+
if (hexCrc16) {
|
|
39
|
+
buffer.copy(binaryBuffer, 2, 0)
|
|
40
|
+
const crc16_ = crc16(binaryBuffer, 2, hexCrc16Magic)
|
|
41
|
+
binaryBuffer.writeUInt16LE(crc16_, 0)
|
|
42
|
+
console.log(`Intel Hex, startAddress=${hexStartAddress}, size=${hexSize}, crc16=${crc16_}`)
|
|
43
|
+
} else {
|
|
44
|
+
buffer.copy(binaryBuffer, 0, 0)
|
|
45
|
+
console.log(`Intel Hex, startAddress=${hexStartAddress}, size=${hexSize}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return binaryToIntelHex(hexStartAddress, true, binaryBuffer)
|
|
49
|
+
}
|
|
50
|
+
|
|
16
51
|
export const outputFormats = ['hex', 'cpphex', 'protohex', 'base64']
|
|
17
52
|
|
|
18
|
-
export default (buffer, format, filename) => {
|
|
53
|
+
export default (buffer, format, filename, settings = {}) => {
|
|
19
54
|
let result = null
|
|
20
55
|
|
|
21
56
|
switch (format) {
|
|
@@ -31,13 +66,34 @@ export default (buffer, format, filename) => {
|
|
|
31
66
|
case 'base64':
|
|
32
67
|
result = buffer.toString('base64')
|
|
33
68
|
break
|
|
34
|
-
|
|
69
|
+
case 'file':
|
|
35
70
|
{
|
|
36
71
|
const filepath = path.resolve(filename)
|
|
37
72
|
fs.writeFileSync(filepath, buffer)
|
|
38
73
|
result = `Written to file <${filepath}>`
|
|
39
74
|
}
|
|
40
75
|
break
|
|
76
|
+
case 'file_hex':
|
|
77
|
+
{
|
|
78
|
+
const [startAddress, filename_] = filename.split(':')
|
|
79
|
+
|
|
80
|
+
const startAddressNo = Number.parseInt(startAddress)
|
|
81
|
+
if (Number.isNaN(startAddressNo)) {
|
|
82
|
+
throw new Error(`Invalid startAddress ${startAddress}`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const { fileHexSize, fileHexCrc16, fileHexCrc16Magic } = settings
|
|
86
|
+
|
|
87
|
+
const hexData = bufferToIntelHex(buffer, startAddressNo, fileHexSize, fileHexCrc16, fileHexCrc16Magic)
|
|
88
|
+
|
|
89
|
+
const filepath = path.resolve(filename_)
|
|
90
|
+
fs.writeFileSync(filepath, hexData)
|
|
91
|
+
|
|
92
|
+
result = `Written to file <${filepath}>`
|
|
93
|
+
}
|
|
94
|
+
break
|
|
95
|
+
default:
|
|
96
|
+
throw new Error(`Unknown format ${format}`)
|
|
41
97
|
}
|
|
42
98
|
|
|
43
99
|
console.log(`(${format} with ${buffer.length.toString()} bytes)\n`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blueid/access-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "BlueID Access CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"package.json"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"sync-proto": "sh syncproto.sh",
|
|
17
|
+
"sync-proto": "sh syncproto.sh || exit 0",
|
|
18
18
|
"preinstall": "npm run sync-proto",
|
|
19
19
|
"prepack": "npm run sync-proto"
|
|
20
20
|
},
|