sportident.js 1.0.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/.dependency-cruiser.js +233 -0
- package/.editorconfig +12 -0
- package/.eslintignore +6 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
- package/.github/workflows/npm.yml +17 -0
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/babel.config.js +21 -0
- package/build-docs.sh +25 -0
- package/docs/index.md +9 -0
- package/docs/typedoc/index.md +11 -0
- package/eslint.base.js +232 -0
- package/install.sh +6 -0
- package/jest.config.ts +49 -0
- package/nx.json +39 -0
- package/package.json +51 -0
- package/src/SiCard/BaseSiCard.test.ts +187 -0
- package/src/SiCard/BaseSiCard.ts +101 -0
- package/src/SiCard/IRaceResultData.ts +16 -0
- package/src/SiCard/ISiCard.ts +23 -0
- package/src/SiCard/ISiCardExamples.ts +4 -0
- package/src/SiCard/index.ts +2 -0
- package/src/SiCard/raceResultTools.test.ts +260 -0
- package/src/SiCard/raceResultTools.ts +150 -0
- package/src/SiCard/types/FCard.test.ts +19 -0
- package/src/SiCard/types/FCard.ts +14 -0
- package/src/SiCard/types/ModernSiCard.test.ts +186 -0
- package/src/SiCard/types/ModernSiCard.ts +241 -0
- package/src/SiCard/types/PCard.test.ts +19 -0
- package/src/SiCard/types/PCard.ts +14 -0
- package/src/SiCard/types/SIAC.test.ts +84 -0
- package/src/SiCard/types/SIAC.ts +19 -0
- package/src/SiCard/types/SiCard10.test.ts +85 -0
- package/src/SiCard/types/SiCard10.ts +17 -0
- package/src/SiCard/types/SiCard11.test.ts +84 -0
- package/src/SiCard/types/SiCard11.ts +19 -0
- package/src/SiCard/types/SiCard5.test.ts +149 -0
- package/src/SiCard/types/SiCard5.ts +129 -0
- package/src/SiCard/types/SiCard6.test.ts +179 -0
- package/src/SiCard/types/SiCard6.ts +222 -0
- package/src/SiCard/types/SiCard8.test.ts +137 -0
- package/src/SiCard/types/SiCard8.ts +129 -0
- package/src/SiCard/types/SiCard9.test.ts +132 -0
- package/src/SiCard/types/SiCard9.ts +128 -0
- package/src/SiCard/types/TCard.test.ts +19 -0
- package/src/SiCard/types/TCard.ts +14 -0
- package/src/SiCard/types/index.test.ts +26 -0
- package/src/SiCard/types/index.ts +15 -0
- package/src/SiCard/types/modernSiCardExamples.ts +364 -0
- package/src/SiCard/types/siCard5Examples.ts +73 -0
- package/src/SiCard/types/siCard6Examples.ts +262 -0
- package/src/SiCard/types/siCard8Examples.ts +152 -0
- package/src/SiCard/types/siCard9Examples.ts +143 -0
- package/src/SiDevice/INavigatorWebSerial.ts +78 -0
- package/src/SiDevice/INavigatorWebUsb.ts +62 -0
- package/src/SiDevice/ISiDevice.ts +48 -0
- package/src/SiDevice/ISiDeviceDriver.ts +35 -0
- package/src/SiDevice/README.md +13 -0
- package/src/SiDevice/SiDevice.test.ts +354 -0
- package/src/SiDevice/SiDevice.ts +132 -0
- package/src/SiDevice/WebSerialSiDeviceDriver.ts +146 -0
- package/src/SiDevice/WebUsbSiDeviceDriver.ts +343 -0
- package/src/SiDevice/index.ts +3 -0
- package/src/SiDevice/testUtils/index.ts +2 -0
- package/src/SiDevice/testUtils/testISiDeviceDriver.ts +63 -0
- package/src/SiDevice/testUtils/testISiDeviceDriverWithAutodetection.ts +72 -0
- package/src/SiStation/BaseSiStation.test.ts +221 -0
- package/src/SiStation/BaseSiStation.ts +253 -0
- package/src/SiStation/CoupledSiStation.test.ts +41 -0
- package/src/SiStation/CoupledSiStation.ts +130 -0
- package/src/SiStation/ISiMainStation.ts +29 -0
- package/src/SiStation/ISiSendTask.ts +9 -0
- package/src/SiStation/ISiStation.ts +88 -0
- package/src/SiStation/ISiTargetMultiplexer.ts +51 -0
- package/src/SiStation/SiMainStation.test.ts +222 -0
- package/src/SiStation/SiMainStation.ts +133 -0
- package/src/SiStation/SiSendTask.ts +50 -0
- package/src/SiStation/SiTargetMultiplexer.targeting.test.ts +112 -0
- package/src/SiStation/SiTargetMultiplexer.test.ts +605 -0
- package/src/SiStation/SiTargetMultiplexer.ts +241 -0
- package/src/SiStation/index.ts +5 -0
- package/src/SiStation/siStationExamples.ts +103 -0
- package/src/constants.test.ts +17 -0
- package/src/constants.ts +92 -0
- package/src/fakes/FakeSiCard/BaseFakeSiCard.test.ts +11 -0
- package/src/fakes/FakeSiCard/BaseFakeSiCard.ts +10 -0
- package/src/fakes/FakeSiCard/IFakeSiCard.ts +6 -0
- package/src/fakes/FakeSiCard/index.ts +2 -0
- package/src/fakes/FakeSiCard/types/FakeModernSiCard.test.ts +62 -0
- package/src/fakes/FakeSiCard/types/FakeModernSiCard.ts +43 -0
- package/src/fakes/FakeSiCard/types/FakeSIAC.ts +17 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard10.ts +17 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard11.ts +17 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard5.test.ts +42 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard5.ts +40 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard6.test.ts +62 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard6.ts +44 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard8.ts +16 -0
- package/src/fakes/FakeSiCard/types/FakeSiCard9.ts +16 -0
- package/src/fakes/FakeSiCard/types/index.ts +7 -0
- package/src/fakes/FakeSiDeviceDriver.ts +148 -0
- package/src/fakes/FakeSiMainStation.test.ts +141 -0
- package/src/fakes/FakeSiMainStation.ts +118 -0
- package/src/fakes/IFakeSiMainStation.ts +15 -0
- package/src/fakes/index.ts +2 -0
- package/src/index.ts +24 -0
- package/src/siProtocol.test.ts +509 -0
- package/src/siProtocol.ts +417 -0
- package/src/storage/SiArray.test.ts +103 -0
- package/src/storage/SiArray.ts +56 -0
- package/src/storage/SiBool.test.ts +81 -0
- package/src/storage/SiBool.ts +47 -0
- package/src/storage/SiDataType.test.ts +81 -0
- package/src/storage/SiDataType.ts +60 -0
- package/src/storage/SiDict.test.ts +115 -0
- package/src/storage/SiDict.ts +60 -0
- package/src/storage/SiEnum.test.ts +77 -0
- package/src/storage/SiEnum.ts +48 -0
- package/src/storage/SiFieldValue.test.ts +58 -0
- package/src/storage/SiFieldValue.ts +23 -0
- package/src/storage/SiInt.test.ts +80 -0
- package/src/storage/SiInt.ts +84 -0
- package/src/storage/SiModified.test.ts +135 -0
- package/src/storage/SiModified.ts +59 -0
- package/src/storage/SiStorage.test.ts +51 -0
- package/src/storage/SiStorage.ts +44 -0
- package/src/storage/index.test.ts +222 -0
- package/src/storage/index.ts +12 -0
- package/src/storage/interfaces.ts +41 -0
- package/src/storage/siStringEncoding.ts +1361 -0
- package/src/testUtils.test.ts +266 -0
- package/src/testUtils.ts +75 -0
- package/src/utils/NumberRange.test.ts +66 -0
- package/src/utils/NumberRange.ts +46 -0
- package/src/utils/NumberRangeRegistry.test.ts +49 -0
- package/src/utils/NumberRangeRegistry.ts +43 -0
- package/src/utils/bytes.test.ts +126 -0
- package/src/utils/bytes.ts +69 -0
- package/src/utils/errors.test.ts +29 -0
- package/src/utils/errors.ts +20 -0
- package/src/utils/events.test.ts +112 -0
- package/src/utils/events.ts +68 -0
- package/src/utils/general.test.ts +139 -0
- package/src/utils/general.ts +69 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/mixins.test.ts +40 -0
- package/src/utils/mixins.ts +13 -0
- package/src/utils/typed.ts +3 -0
- package/tsconfig.base.json +22 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { proto } from './constants';
|
|
3
|
+
import * as utils from './utils';
|
|
4
|
+
import * as storage from './storage';
|
|
5
|
+
import type { SiIntegerPartDefinition } from './storage/SiInt';
|
|
6
|
+
|
|
7
|
+
export const SI_TIME_CUTOFF = 43200; // Half a day in seconds
|
|
8
|
+
|
|
9
|
+
export const arr2date = (arr: number[], asOf?: Date): Date | undefined => {
|
|
10
|
+
utils.assertIsByteArr(arr);
|
|
11
|
+
utils.assertArrIsOfLengths(arr, [3, 6, 7]);
|
|
12
|
+
if (arr[0] > 99) {
|
|
13
|
+
console.warn(`arr2date: Invalid year: ${arr[0]}`);
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
if (arr[1] === 0) {
|
|
17
|
+
console.warn(`arr2date: Invalid month: ${arr[1]}`);
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const maxYear = asOf ? asOf.getUTCFullYear() : new Date().getUTCFullYear();
|
|
21
|
+
const getYear = (lastTwoDigits: number): number => {
|
|
22
|
+
const maxLastTwo = maxYear % 100;
|
|
23
|
+
const maxRest = maxYear - maxLastTwo;
|
|
24
|
+
if (lastTwoDigits <= maxLastTwo) {
|
|
25
|
+
return lastTwoDigits + maxRest;
|
|
26
|
+
}
|
|
27
|
+
return lastTwoDigits + maxRest - 100;
|
|
28
|
+
};
|
|
29
|
+
const year = getYear(arr[0]);
|
|
30
|
+
const month = arr[1] - 1;
|
|
31
|
+
const day = arr[2];
|
|
32
|
+
const secs = arr.length < 6 ? 0 : utils.arr2big(arr.slice(4, 6));
|
|
33
|
+
const hours = arr.length < 6 ? 0 : (arr[3] & 0x01) * 12 + Math.floor(secs / 3600);
|
|
34
|
+
const minutes = arr.length < 6 ? 0 : Math.floor((secs % 3600) / 60);
|
|
35
|
+
const seconds = arr.length < 6 ? 0 : secs % 60;
|
|
36
|
+
const milliseconds = arr.length < 7 ? 0 : (arr[6] * 1000) / 256;
|
|
37
|
+
const date = new Date(year, month, day, hours, minutes, seconds, milliseconds);
|
|
38
|
+
const isValidDate =
|
|
39
|
+
date.getFullYear() === year &&
|
|
40
|
+
date.getMonth() === month &&
|
|
41
|
+
date.getDate() === day &&
|
|
42
|
+
date.getHours() === hours &&
|
|
43
|
+
date.getMinutes() === minutes &&
|
|
44
|
+
date.getSeconds() === seconds &&
|
|
45
|
+
date.getMilliseconds() === Math.floor(milliseconds);
|
|
46
|
+
if (!isValidDate) {
|
|
47
|
+
const rawDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
|
48
|
+
console.warn(`arr2date: Invalid date: ${date} (raw: ${rawDate})`);
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return date;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const date2arr = (dateTime: Date): number[] => {
|
|
55
|
+
const secs = (dateTime.getHours() % 12) * 3600 + dateTime.getMinutes() * 60 + dateTime.getSeconds();
|
|
56
|
+
return [
|
|
57
|
+
dateTime.getFullYear() % 100,
|
|
58
|
+
dateTime.getMonth() + 1,
|
|
59
|
+
dateTime.getDate(),
|
|
60
|
+
(dateTime.getDay() << 1) + Math.floor(dateTime.getHours() / 12),
|
|
61
|
+
secs >> 8,
|
|
62
|
+
secs & 0xff,
|
|
63
|
+
Math.floor((dateTime.getMilliseconds() * 256) / 1000)
|
|
64
|
+
];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const arr2cardNumber = (arr: (number | undefined)[]): number | undefined => {
|
|
68
|
+
if (arr.some((byte) => byte === undefined)) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
utils.assertIsByteArr(arr);
|
|
72
|
+
utils.assertArrIsOfLengths(arr, [3, 4]);
|
|
73
|
+
let cardnum = (arr[1]! << 8) | arr[0]!;
|
|
74
|
+
const fourthSet = arr.length === 4 && arr[3]! !== 0x00;
|
|
75
|
+
if (fourthSet || 4 < arr[2]!) {
|
|
76
|
+
cardnum |= arr[2]! << 16;
|
|
77
|
+
} else {
|
|
78
|
+
cardnum += arr[2]! * 100000;
|
|
79
|
+
}
|
|
80
|
+
if (arr.length === 4) {
|
|
81
|
+
cardnum |= arr[3]! << 24;
|
|
82
|
+
}
|
|
83
|
+
return cardnum;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const cardNumber2arr = (cardNumber: number | undefined): (number | undefined)[] => {
|
|
87
|
+
if (cardNumber === undefined) {
|
|
88
|
+
return [undefined, undefined, undefined, undefined];
|
|
89
|
+
}
|
|
90
|
+
const arr2 = cardNumber < 500000 ? Math.floor(cardNumber / 100000) & 0xff : (cardNumber >> 16) & 0xff;
|
|
91
|
+
const newCardNumber = cardNumber < 500000 ? cardNumber - arr2 * 100000 : cardNumber;
|
|
92
|
+
return [newCardNumber & 0xff, (newCardNumber >> 8) & 0xff, arr2, (newCardNumber >> 24) & 0xff];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export interface SiMessageWithMode {
|
|
96
|
+
mode: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface SiMessageWithoutMode {
|
|
100
|
+
mode?: undefined;
|
|
101
|
+
command: number;
|
|
102
|
+
parameters: number[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type SiMessage = SiMessageWithMode | SiMessageWithoutMode;
|
|
106
|
+
|
|
107
|
+
export const prettyMessage = (message: SiMessage): string => {
|
|
108
|
+
if (message.mode !== undefined) {
|
|
109
|
+
const prettyMode = `Mode: ${utils.prettyHex([message.mode])} (${message.mode})\n`;
|
|
110
|
+
return `${prettyMode}`;
|
|
111
|
+
}
|
|
112
|
+
const prettyCommand = `Command: ${proto.cmdLookup[message.command]} ${utils.prettyHex([message.command])} (${message.command})\n`;
|
|
113
|
+
const prettyParameters = `Parameters: ${utils.prettyHex(message.parameters)} (${JSON.stringify(message.parameters)})`;
|
|
114
|
+
return `${prettyCommand}${prettyParameters}`;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const CRC16 = (str: number[]): [number, number] => {
|
|
118
|
+
const CRC_POLYNOM = 0x8005;
|
|
119
|
+
const CRC_BITF = 0x8000;
|
|
120
|
+
if (str.length < 3) {
|
|
121
|
+
return [1 <= str.length ? str[0] : 0x00, 2 <= str.length ? str[1] : 0x00];
|
|
122
|
+
}
|
|
123
|
+
const s = str.length % 2 === 0 ? str.concat([0x00, 0x00]) : str.concat([0x00]);
|
|
124
|
+
let crc = s[0] * 0x100 + s[1];
|
|
125
|
+
for (let i = 2; i < s.length; i += 2) {
|
|
126
|
+
const c = s.slice(i, i + 2);
|
|
127
|
+
let val = c[0] * 0x100 + c[1];
|
|
128
|
+
for (let j = 0; j < 16; j++) {
|
|
129
|
+
if ((crc & CRC_BITF) !== 0) {
|
|
130
|
+
crc = crc << 1;
|
|
131
|
+
if ((val & CRC_BITF) !== 0) {
|
|
132
|
+
crc += 1;
|
|
133
|
+
}
|
|
134
|
+
crc = crc ^ CRC_POLYNOM;
|
|
135
|
+
} else {
|
|
136
|
+
crc = crc << 1;
|
|
137
|
+
if ((val & CRC_BITF) !== 0) {
|
|
138
|
+
crc += 1;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
val = val << 1;
|
|
142
|
+
}
|
|
143
|
+
crc = crc & 0xffff;
|
|
144
|
+
}
|
|
145
|
+
return [crc >> 8, crc & 0xff];
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export interface SiMessageParseResult {
|
|
149
|
+
message: SiMessage | null;
|
|
150
|
+
remainder: number[];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const parse = (inputData: number[]): SiMessageParseResult => {
|
|
154
|
+
const failAndProceed = (numBytes: number): SiMessageParseResult => ({
|
|
155
|
+
message: null,
|
|
156
|
+
remainder: inputData.slice(numBytes)
|
|
157
|
+
});
|
|
158
|
+
const specialModeAndProceed = (mode: number, numBytes: number): SiMessageParseResult => ({
|
|
159
|
+
message: {
|
|
160
|
+
mode: mode
|
|
161
|
+
},
|
|
162
|
+
remainder: inputData.slice(numBytes)
|
|
163
|
+
});
|
|
164
|
+
if (inputData.length <= 0) {
|
|
165
|
+
return failAndProceed(0);
|
|
166
|
+
}
|
|
167
|
+
if (inputData[0] === proto.WAKEUP) {
|
|
168
|
+
return specialModeAndProceed(proto.WAKEUP, 1);
|
|
169
|
+
} else if (inputData[0] === proto.ACK) {
|
|
170
|
+
return specialModeAndProceed(proto.ACK, 1);
|
|
171
|
+
} else if (inputData[0] === proto.NAK) {
|
|
172
|
+
return specialModeAndProceed(proto.NAK, 1);
|
|
173
|
+
} else if (inputData[0] !== proto.STX) {
|
|
174
|
+
console.warn(`Invalid start byte: ${utils.prettyHex([inputData[0]])}`);
|
|
175
|
+
return failAndProceed(1);
|
|
176
|
+
}
|
|
177
|
+
if (inputData.length <= 1) {
|
|
178
|
+
return failAndProceed(0);
|
|
179
|
+
}
|
|
180
|
+
const command = inputData[1];
|
|
181
|
+
if (inputData.length <= 2) {
|
|
182
|
+
return failAndProceed(0);
|
|
183
|
+
}
|
|
184
|
+
const numParameters = inputData[2];
|
|
185
|
+
if (inputData.length <= 2 + numParameters) {
|
|
186
|
+
return failAndProceed(0);
|
|
187
|
+
}
|
|
188
|
+
const parameters = inputData.slice(3, 3 + numParameters);
|
|
189
|
+
if (inputData.length <= 4 + numParameters) {
|
|
190
|
+
return failAndProceed(0);
|
|
191
|
+
}
|
|
192
|
+
if (inputData.length <= 5 + numParameters) {
|
|
193
|
+
return failAndProceed(0);
|
|
194
|
+
}
|
|
195
|
+
if (inputData[5 + numParameters] !== proto.ETX) {
|
|
196
|
+
console.warn(`Invalid ETX byte: ${utils.prettyHex([inputData[5 + numParameters]])}`);
|
|
197
|
+
return failAndProceed(1);
|
|
198
|
+
}
|
|
199
|
+
const expectedCRC = CRC16(inputData.slice(1, 3 + numParameters));
|
|
200
|
+
const actualCRC = inputData.slice(3 + numParameters, 5 + numParameters);
|
|
201
|
+
if (!_.isEqual(actualCRC, expectedCRC)) {
|
|
202
|
+
console.warn(`Invalid CRC: ${utils.prettyHex(actualCRC)} (expected ${utils.prettyHex(expectedCRC)})`);
|
|
203
|
+
return failAndProceed(6 + numParameters);
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
message: {
|
|
207
|
+
command: command,
|
|
208
|
+
parameters: parameters
|
|
209
|
+
},
|
|
210
|
+
remainder: inputData.slice(6 + numParameters)
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export interface SiMessagesParseResult {
|
|
215
|
+
messages: SiMessage[];
|
|
216
|
+
remainder: number[];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export const parseAll = (inputData: number[]): SiMessagesParseResult => {
|
|
220
|
+
let currentRemainder = inputData;
|
|
221
|
+
const messages:SiMessage[] = [];
|
|
222
|
+
let remainderWasShrinking = true;
|
|
223
|
+
while (remainderWasShrinking) {
|
|
224
|
+
const { message, remainder: newRemainder } = parse(currentRemainder);
|
|
225
|
+
remainderWasShrinking = newRemainder.length < currentRemainder.length;
|
|
226
|
+
if (message) {
|
|
227
|
+
messages.push(message);
|
|
228
|
+
}
|
|
229
|
+
currentRemainder = newRemainder;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
messages: messages,
|
|
233
|
+
remainder: currentRemainder
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export const render = (message: SiMessage): number[] => {
|
|
238
|
+
const renderCommand = (messageWithoutMode: SiMessageWithoutMode) => {
|
|
239
|
+
const commandString = [messageWithoutMode.command, messageWithoutMode.parameters.length, ...messageWithoutMode.parameters];
|
|
240
|
+
const crc = CRC16(commandString);
|
|
241
|
+
return [proto.STX, ...commandString, ...crc, proto.ETX] as number[];
|
|
242
|
+
};
|
|
243
|
+
if (message.mode === undefined) {
|
|
244
|
+
return renderCommand(message);
|
|
245
|
+
}
|
|
246
|
+
const renderFunctionsByMode: { [key: number]: () => number[] } = {
|
|
247
|
+
[proto.WAKEUP]: () => [proto.WAKEUP],
|
|
248
|
+
[proto.NAK]: () => [proto.NAK],
|
|
249
|
+
[proto.ACK]: () => [proto.ACK]
|
|
250
|
+
};
|
|
251
|
+
const renderFunction = renderFunctionsByMode[message.mode];
|
|
252
|
+
if (renderFunction === undefined) {
|
|
253
|
+
throw new Error(`Cannot render with mode ${utils.prettyHex([message.mode])}`);
|
|
254
|
+
}
|
|
255
|
+
return renderFunction();
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export class SiDate extends storage.SiDataType<Date> implements storage.ISiDataType<Date> {
|
|
259
|
+
private readonly arrayField: storage.SiArray<number>;
|
|
260
|
+
|
|
261
|
+
constructor(public length: number, public getByteOffsetAtIndex: (index: number) => number) {
|
|
262
|
+
super();
|
|
263
|
+
this.arrayField = new storage.SiArray(length, (i) => new storage.SiInt([[getByteOffsetAtIndex(i)]]));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
typeSpecificIsValueValid(_value: Date): boolean {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
typeSpecificValueToString(value: Date): string {
|
|
271
|
+
const year = value.getFullYear();
|
|
272
|
+
const month = value.getMonth();
|
|
273
|
+
const day = value.getDate();
|
|
274
|
+
const hours = value.getHours();
|
|
275
|
+
const minutes = value.getMinutes();
|
|
276
|
+
const seconds = value.getSeconds();
|
|
277
|
+
const milliseconds = value.getMilliseconds();
|
|
278
|
+
return new Date(Date.UTC(year, month, day, hours, minutes, seconds, milliseconds)).toJSON();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
typeSpecificValueFromString(string: string): Date | storage.ValueFromStringError {
|
|
282
|
+
const res = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]{3})Z$/.exec(string);
|
|
283
|
+
if (!res) {
|
|
284
|
+
return new storage.ValueFromStringError(`Not a correctly formatted date: ${string}`);
|
|
285
|
+
}
|
|
286
|
+
return new Date(Number(res[1]), Number(res[2]) - 1, Number(res[3]), Number(res[4]), Number(res[5]), Number(res[6]), Number(res[7]));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
typeSpecificExtractFromData(data: storage.ISiStorageData): Date | undefined {
|
|
290
|
+
const dateArray = this.arrayField.typeSpecificExtractFromData(data);
|
|
291
|
+
if (dateArray === undefined || dateArray.some((byte: number | undefined) => byte === undefined)) {
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
return arr2date(dateArray as number[]);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
typeSpecificUpdateData(data: storage.ISiStorageData, newValue: Date): storage.ISiStorageData {
|
|
298
|
+
const newArrayValue = date2arr(newValue).slice(0, this.arrayField.length);
|
|
299
|
+
return this.arrayField.typeSpecificUpdateData(data, newArrayValue);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export type SiTimestamp = {
|
|
304
|
+
time:number,
|
|
305
|
+
weekday?:number,
|
|
306
|
+
weekcounter?:number,
|
|
307
|
+
} | null;
|
|
308
|
+
|
|
309
|
+
export class SiTime extends storage.SiDataType<SiTimestamp> implements storage.ISiDataType<SiTimestamp> {
|
|
310
|
+
private readonly intFieldTime12: storage.SiInt | undefined
|
|
311
|
+
private readonly intFieldAmPm: storage.SiInt | undefined
|
|
312
|
+
private readonly intFieldDayOfWeek: storage.SiInt | undefined
|
|
313
|
+
private readonly intFieldWeekCounter: storage.SiInt | undefined
|
|
314
|
+
|
|
315
|
+
constructor(public byteStorage: [[number], [number], [number]] | [[number], [number]] | undefined) {
|
|
316
|
+
super();
|
|
317
|
+
if (byteStorage == undefined){
|
|
318
|
+
this.intFieldTime12 = undefined
|
|
319
|
+
this.intFieldAmPm = undefined
|
|
320
|
+
this.intFieldDayOfWeek = undefined
|
|
321
|
+
this.intFieldWeekCounter = undefined
|
|
322
|
+
}else if (byteStorage.length == 3){
|
|
323
|
+
this.intFieldTime12 = new storage.SiInt([byteStorage[0],byteStorage[1]])
|
|
324
|
+
this.intFieldAmPm = new storage.SiInt(_.map(byteStorage[2], (b:number):SiIntegerPartDefinition=>{
|
|
325
|
+
return [b,0,1] // offset, startbit, endbit
|
|
326
|
+
}))
|
|
327
|
+
this.intFieldDayOfWeek = new storage.SiInt(_.map(byteStorage[2], (b:number):SiIntegerPartDefinition=>{
|
|
328
|
+
return [b,1,4] // offset, startbit, endbit
|
|
329
|
+
}))
|
|
330
|
+
this.intFieldWeekCounter = new storage.SiInt(_.map(byteStorage[2], (b:number):SiIntegerPartDefinition=>{
|
|
331
|
+
return [b,4,6] // offset, startbit, endbit
|
|
332
|
+
}))
|
|
333
|
+
}else if (byteStorage.length == 2){
|
|
334
|
+
this.intFieldTime12 = new storage.SiInt(byteStorage)
|
|
335
|
+
this.intFieldAmPm = undefined
|
|
336
|
+
this.intFieldDayOfWeek = undefined
|
|
337
|
+
this.intFieldWeekCounter = undefined
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
typeSpecificIsValueValid(value: SiTimestamp): boolean {
|
|
342
|
+
return value === null ||
|
|
343
|
+
(value.time <= 86400 &&
|
|
344
|
+
(value.weekday == undefined || (value.weekday >=0 && value.weekday < 7)) &&
|
|
345
|
+
(value.weekcounter == undefined || (value.weekcounter >=0 && value.weekcounter < 4))
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
typeSpecificValueToString(value: SiTimestamp): string {
|
|
350
|
+
if (value === null) {
|
|
351
|
+
return 'NO_TIME';
|
|
352
|
+
}
|
|
353
|
+
const hours = Math.floor(value.time / 3600)
|
|
354
|
+
const minutes = Math.floor(value.time/60)%60
|
|
355
|
+
const seconds = value.time % 60
|
|
356
|
+
const hoursStr = `${hours}`.padStart(2, '0');
|
|
357
|
+
const minutesStr = `${minutes}`.padStart(2, '0');
|
|
358
|
+
const secondsStr = `${seconds}`.padStart(2, '0');
|
|
359
|
+
return `${hoursStr}:${minutesStr}:${secondsStr}`;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
typeSpecificValueFromString(string: string): SiTimestamp | storage.ValueFromStringError {
|
|
363
|
+
if (string === 'NO_TIME') {
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
const res = /^([0-9]{2}):([0-9]{2}):([0-9]{2})$/.exec(string);
|
|
367
|
+
if (!res) {
|
|
368
|
+
return new storage.ValueFromStringError(`Not a correctly formatted date: ${string}`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
time: Number(res[1]) * 3600 + Number(res[2]) * 60 + Number(res[3]),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
typeSpecificExtractFromData(data: storage.ISiStorageData): SiTimestamp | undefined {
|
|
377
|
+
if (this.intFieldTime12 === undefined) {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
let timeInt = this.intFieldTime12.typeSpecificExtractFromData(data);
|
|
381
|
+
if (timeInt === proto.NO_TIME) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
if (timeInt === undefined || timeInt > 86400) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const amPm = this.intFieldAmPm === undefined?undefined:this.intFieldAmPm.typeSpecificExtractFromData(data)
|
|
389
|
+
const weekcounter = this.intFieldWeekCounter === undefined?undefined:this.intFieldWeekCounter.typeSpecificExtractFromData(data)
|
|
390
|
+
const weekday = this.intFieldDayOfWeek === undefined?undefined:this.intFieldDayOfWeek.typeSpecificExtractFromData(data)
|
|
391
|
+
|
|
392
|
+
if (amPm != undefined && amPm == 1){
|
|
393
|
+
timeInt += 43200;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return {time: timeInt, weekcounter: weekcounter, weekday: weekday}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
typeSpecificUpdateData(data: storage.ISiStorageData, newValue: SiTimestamp): storage.ISiStorageData {
|
|
400
|
+
if (this.intFieldTime12 === undefined) {
|
|
401
|
+
return data;
|
|
402
|
+
}
|
|
403
|
+
const newIntValue = newValue === null ? proto.NO_TIME : newValue.time;
|
|
404
|
+
let tempData = this.intFieldTime12.typeSpecificUpdateData(data, newIntValue);
|
|
405
|
+
if (this.intFieldAmPm !== undefined && newValue != null) {
|
|
406
|
+
tempData = this.intFieldAmPm.typeSpecificUpdateData(tempData, newValue.time >= 43200?1:0)
|
|
407
|
+
}
|
|
408
|
+
if (this.intFieldDayOfWeek !== undefined && newValue != null && newValue.weekday !== undefined) {
|
|
409
|
+
tempData = this.intFieldDayOfWeek.typeSpecificUpdateData(tempData, newValue.weekday)
|
|
410
|
+
}
|
|
411
|
+
if (this.intFieldWeekCounter !== undefined && newValue != null && newValue.weekcounter !== undefined) {
|
|
412
|
+
tempData = this.intFieldWeekCounter.typeSpecificUpdateData(tempData, newValue.weekcounter)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return tempData
|
|
416
|
+
}
|
|
417
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import Immutable from 'immutable';
|
|
4
|
+
import { type ISiStorageData, ValueFromStringError } from './interfaces';
|
|
5
|
+
import { ModifyUndefinedException, SiDataType } from './SiDataType';
|
|
6
|
+
import { SiFieldValue } from './SiFieldValue';
|
|
7
|
+
import { SiArray } from './SiArray';
|
|
8
|
+
|
|
9
|
+
describe('SiArray', () => {
|
|
10
|
+
class FakeDataType extends SiDataType<string> {
|
|
11
|
+
constructor(public index: number) {
|
|
12
|
+
super();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
typeSpecificIsValueValid(_value: string) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
typeSpecificValueFromString(str: string): string | ValueFromStringError | never {
|
|
20
|
+
return str.substr(2, str.length - 2);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
typeSpecificValueToString(value: string): string {
|
|
24
|
+
return `->${value}<-`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
typeSpecificExtractFromData(data: ISiStorageData): string | undefined {
|
|
28
|
+
const byte = data.get(this.index);
|
|
29
|
+
if (byte === undefined) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return String.fromCharCode(byte);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
typeSpecificUpdateData(data: ISiStorageData, newValue: string): ISiStorageData {
|
|
36
|
+
const byte = data.get(this.index);
|
|
37
|
+
if (byte === undefined) {
|
|
38
|
+
throw new ModifyUndefinedException();
|
|
39
|
+
}
|
|
40
|
+
return data.set(this.index, newValue.charCodeAt(0));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const mySiArray = new SiArray(3, (i) => new FakeDataType(i));
|
|
44
|
+
const fieldValueOf = (arrayValue: string[]): SiFieldValue<(string | undefined)[]> => new SiFieldValue(mySiArray, arrayValue);
|
|
45
|
+
test('typeSpecificIsValueValid', () => {
|
|
46
|
+
expect(mySiArray.isValueValid([])).toBe(true);
|
|
47
|
+
expect(mySiArray.isValueValid([''])).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
test('valueToString', () => {
|
|
50
|
+
expect(mySiArray.valueToString([])).toBe('');
|
|
51
|
+
expect(mySiArray.valueToString(['test'])).toBe('->test<-');
|
|
52
|
+
expect(mySiArray.valueToString(['test', '1234'])).toBe('->test<-, ->1234<-');
|
|
53
|
+
expect(mySiArray.valueToString(['test', undefined])).toBe('->test<-, ?');
|
|
54
|
+
});
|
|
55
|
+
test('valueFromString', () => {
|
|
56
|
+
expect(mySiArray.valueFromString('->test<-') instanceof ValueFromStringError).toBe(true);
|
|
57
|
+
expect(mySiArray.valueFromString('->test<-, ->1234<-') instanceof ValueFromStringError).toBe(true);
|
|
58
|
+
expect(mySiArray.valueFromString('test') instanceof ValueFromStringError).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
test('extractFromData gives field value', () => {
|
|
61
|
+
const data = Immutable.List([0x41, 0x42, 0x43]);
|
|
62
|
+
const fieldValue = mySiArray.extractFromData(data);
|
|
63
|
+
expect(fieldValue instanceof SiFieldValue).toBe(true);
|
|
64
|
+
expect(fieldValue!.field).toBe(mySiArray);
|
|
65
|
+
expect(fieldValue!.value).toEqual(['A', 'B', 'C']);
|
|
66
|
+
});
|
|
67
|
+
test('extractFromData', () => {
|
|
68
|
+
const getExtractedFieldValue = (bytes: (number | undefined)[]) => mySiArray.extractFromData(Immutable.List(bytes));
|
|
69
|
+
|
|
70
|
+
expect(getExtractedFieldValue([0x61, 0x62, 0x63])!.value).toEqual(['a', 'b', 'c']);
|
|
71
|
+
expect(getExtractedFieldValue([undefined, 0x62, 0x63])!.value).toEqual([undefined, 'b', 'c']);
|
|
72
|
+
expect(getExtractedFieldValue([0x61, undefined, 0x63])!.value).toEqual(['a', undefined, 'c']);
|
|
73
|
+
expect(getExtractedFieldValue([0x61, 0x62, undefined])!.value).toEqual(['a', 'b', undefined]);
|
|
74
|
+
expect(getExtractedFieldValue([0x61, 0x62])!.value).toEqual(['a', 'b', undefined]);
|
|
75
|
+
expect(getExtractedFieldValue([0x61])!.value).toEqual(['a', undefined, undefined]);
|
|
76
|
+
expect(getExtractedFieldValue([])!.value).toEqual([undefined, undefined, undefined]);
|
|
77
|
+
});
|
|
78
|
+
test('updateData', () => {
|
|
79
|
+
const initialData = Immutable.List([0x00, 0x00, 0x00]);
|
|
80
|
+
const updateInitialData = (newValue: (string | undefined)[] | SiFieldValue<(string | undefined)[]>) => mySiArray.updateData(initialData, newValue).toJS();
|
|
81
|
+
|
|
82
|
+
expect(updateInitialData(['x', 'y', 'z'])).toEqual([0x78, 0x79, 0x7a]);
|
|
83
|
+
expect(updateInitialData(['x', 'y'])).toEqual([0x78, 0x79, 0x00]);
|
|
84
|
+
expect(updateInitialData(['x'])).toEqual([0x78, 0x00, 0x00]);
|
|
85
|
+
expect(updateInitialData([])).toEqual([0x00, 0x00, 0x00]);
|
|
86
|
+
expect(updateInitialData(['x', 'y', 'z', 'a'])).toEqual([0x78, 0x79, 0x7a]);
|
|
87
|
+
expect(updateInitialData(fieldValueOf(['x', 'y', 'z']))).toEqual([0x78, 0x79, 0x7a]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const updateData = (data: (number | undefined)[], newValue: (string | undefined)[] | SiFieldValue<(string | undefined)[]>) => mySiArray.updateData(Immutable.List(data), newValue).toJS();
|
|
91
|
+
test('updateData modify undefined', () => {
|
|
92
|
+
expect(() => updateData([], ['x', 'y', 'z'])).toThrow(ModifyUndefinedException);
|
|
93
|
+
expect(() => updateData([], ['x'])).toThrow(ModifyUndefinedException);
|
|
94
|
+
expect(() => updateData([], fieldValueOf(['x']))).toThrow(ModifyUndefinedException);
|
|
95
|
+
expect(() => updateData([undefined, 0x00, 0x00], ['x', 'y', 'z'])).toThrow(ModifyUndefinedException);
|
|
96
|
+
expect(() => updateData([0x00, undefined, 0x00], ['x', 'y', 'z'])).toThrow(ModifyUndefinedException);
|
|
97
|
+
expect(() => updateData([0x00, 0x00, undefined], ['x', 'y', 'z'])).toThrow(ModifyUndefinedException);
|
|
98
|
+
expect(() => updateData([0x78, undefined, undefined], ['x', 'y'])).toThrow(ModifyUndefinedException);
|
|
99
|
+
});
|
|
100
|
+
test('updateData with undefined data', () => {
|
|
101
|
+
expect(updateData([0x00, 0x00, 0x00], ['x', undefined, 'z'])).toEqual([0x78, 0x00, 0x7a]);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { type ISiDataType, type ISiStorageData, ValueFromStringError } from './interfaces';
|
|
3
|
+
import { SiDataType } from './SiDataType';
|
|
4
|
+
|
|
5
|
+
export type SiArrayValue<T> = (T | undefined)[];
|
|
6
|
+
|
|
7
|
+
export class SiArray<T> extends SiDataType<SiArrayValue<T>> implements ISiDataType<SiArrayValue<T>> {
|
|
8
|
+
constructor(public length: number, public getDefinitionAtIndex: (index: number) => ISiDataType<T>) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
typeSpecificIsValueValid(_value: SiArrayValue<T>): boolean {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
typeSpecificValueToString(value: SiArrayValue<T>): string {
|
|
17
|
+
return value
|
|
18
|
+
.map((itemValue, index) => {
|
|
19
|
+
if (itemValue === undefined) {
|
|
20
|
+
return '?';
|
|
21
|
+
}
|
|
22
|
+
const definition = this.getDefinitionAtIndex(index);
|
|
23
|
+
return definition.valueToString(itemValue);
|
|
24
|
+
})
|
|
25
|
+
.join(', ');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
typeSpecificValueFromString(_string: string): ValueFromStringError {
|
|
29
|
+
return new ValueFromStringError(`${this.constructor.name} does not support string parsing`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
typeSpecificExtractFromData(data: ISiStorageData): SiArrayValue<T> | undefined {
|
|
33
|
+
const arrayValue = _.range(this.length).map((index) => {
|
|
34
|
+
const definition = this.getDefinitionAtIndex(index);
|
|
35
|
+
const itemFieldValue = definition.extractFromData(data);
|
|
36
|
+
if (itemFieldValue === undefined) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return itemFieldValue.value;
|
|
40
|
+
});
|
|
41
|
+
return arrayValue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
typeSpecificUpdateData(data: ISiStorageData, newValue: SiArrayValue<T>): ISiStorageData {
|
|
45
|
+
const updateLength = Math.min(newValue.length, this.length);
|
|
46
|
+
let tempData = data;
|
|
47
|
+
_.range(updateLength).forEach((index) => {
|
|
48
|
+
const definition = this.getDefinitionAtIndex(index);
|
|
49
|
+
const newItemValue = newValue[index];
|
|
50
|
+
if (newItemValue !== undefined) {
|
|
51
|
+
tempData = definition.updateData(tempData, newItemValue);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return tempData;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import Immutable from 'immutable';
|
|
4
|
+
import { ValueFromStringError } from './interfaces';
|
|
5
|
+
import { ModifyUndefinedException } from './SiDataType';
|
|
6
|
+
import { SiFieldValue } from './SiFieldValue';
|
|
7
|
+
import { SiBool } from './SiBool';
|
|
8
|
+
|
|
9
|
+
describe('SiBool', () => {
|
|
10
|
+
const mySiBool = new SiBool(0x00, 4);
|
|
11
|
+
const myOtherSiBool = new SiBool(0x00);
|
|
12
|
+
const fieldValueOf = (boolValue: boolean): SiFieldValue<boolean> => new SiFieldValue(mySiBool, boolValue);
|
|
13
|
+
test('typeCheckValue', () => {
|
|
14
|
+
expect(mySiBool.isValueValid(false)).toBe(true);
|
|
15
|
+
expect(mySiBool.isValueValid(true)).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
test('valueToString', () => {
|
|
18
|
+
expect(mySiBool.valueToString(false)).toBe('false');
|
|
19
|
+
expect(mySiBool.valueToString(true)).toBe('true');
|
|
20
|
+
});
|
|
21
|
+
test('valueFromString', () => {
|
|
22
|
+
expect(mySiBool.valueFromString('false')).toBe(false);
|
|
23
|
+
expect(mySiBool.valueFromString('true')).toBe(true);
|
|
24
|
+
expect(mySiBool.valueFromString('test') instanceof ValueFromStringError).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
test('extractFromData gives field value', () => {
|
|
27
|
+
const data = Immutable.List([0x00]);
|
|
28
|
+
const fieldValue = mySiBool.extractFromData(data);
|
|
29
|
+
expect(fieldValue instanceof SiFieldValue).toBe(true);
|
|
30
|
+
expect(fieldValue!.field).toBe(mySiBool);
|
|
31
|
+
expect(fieldValue!.value).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
test('extractFromData', () => {
|
|
34
|
+
const getExtractedFieldValue = (bytes: (number | undefined)[]) => mySiBool.extractFromData(Immutable.List(bytes));
|
|
35
|
+
|
|
36
|
+
expect(getExtractedFieldValue([0x00])!.value).toBe(false);
|
|
37
|
+
expect(getExtractedFieldValue([0x10])!.value).toBe(true);
|
|
38
|
+
expect(getExtractedFieldValue([0xff])!.value).toBe(true);
|
|
39
|
+
expect(getExtractedFieldValue([undefined])).toBe(undefined);
|
|
40
|
+
expect(getExtractedFieldValue([])).toBe(undefined);
|
|
41
|
+
});
|
|
42
|
+
test('extractFromData other', () => {
|
|
43
|
+
const getExtractedFieldValue = (bytes: (number | undefined)[]) => myOtherSiBool.extractFromData(Immutable.List(bytes));
|
|
44
|
+
|
|
45
|
+
expect(getExtractedFieldValue([0x00])!.value).toBe(false);
|
|
46
|
+
expect(getExtractedFieldValue([0x01])!.value).toBe(true);
|
|
47
|
+
expect(getExtractedFieldValue([0xff])!.value).toBe(true);
|
|
48
|
+
expect(getExtractedFieldValue([undefined])).toBe(undefined);
|
|
49
|
+
expect(getExtractedFieldValue([])).toBe(undefined);
|
|
50
|
+
});
|
|
51
|
+
test('updateData', () => {
|
|
52
|
+
const updateData = (data: (number | undefined)[], newValue: boolean | SiFieldValue<boolean>) => mySiBool.updateData(Immutable.List(data), newValue).toJS();
|
|
53
|
+
|
|
54
|
+
expect(updateData([0x10], false)).toEqual([0x00]);
|
|
55
|
+
expect(updateData([0xff], false)).toEqual([0xef]);
|
|
56
|
+
expect(updateData([0x00], true)).toEqual([0x10]);
|
|
57
|
+
expect(updateData([0xef], true)).toEqual([0xff]);
|
|
58
|
+
expect(updateData([0x10], fieldValueOf(false))).toEqual([0x00]);
|
|
59
|
+
expect(updateData([0x00], fieldValueOf(true))).toEqual([0x10]);
|
|
60
|
+
});
|
|
61
|
+
test('updateData other', () => {
|
|
62
|
+
const updateData = (data: (number | undefined)[], newValue: boolean | SiFieldValue<boolean>) => myOtherSiBool.updateData(Immutable.List(data), newValue).toJS();
|
|
63
|
+
|
|
64
|
+
expect(updateData([0x01], false)).toEqual([0x00]);
|
|
65
|
+
expect(updateData([0xff], false)).toEqual([0xfe]);
|
|
66
|
+
expect(updateData([0x00], true)).toEqual([0x01]);
|
|
67
|
+
expect(updateData([0xfe], true)).toEqual([0xff]);
|
|
68
|
+
expect(updateData([0x01], fieldValueOf(false))).toEqual([0x00]);
|
|
69
|
+
expect(updateData([0x00], fieldValueOf(true))).toEqual([0x01]);
|
|
70
|
+
});
|
|
71
|
+
test('updateData modify undefined', () => {
|
|
72
|
+
const updateData = (data: (number | undefined)[], newValue: boolean | SiFieldValue<boolean>) => mySiBool.updateData(Immutable.List(data), newValue).toJS();
|
|
73
|
+
|
|
74
|
+
expect(() => updateData([], false)).toThrow(ModifyUndefinedException);
|
|
75
|
+
expect(() => updateData([], true)).toThrow(ModifyUndefinedException);
|
|
76
|
+
expect(() => updateData([], fieldValueOf(false))).toThrow(ModifyUndefinedException);
|
|
77
|
+
expect(() => updateData([], fieldValueOf(true))).toThrow(ModifyUndefinedException);
|
|
78
|
+
expect(() => updateData([undefined], false)).toThrow(ModifyUndefinedException);
|
|
79
|
+
expect(() => updateData([undefined], true)).toThrow(ModifyUndefinedException);
|
|
80
|
+
});
|
|
81
|
+
});
|