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,84 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import type { ISiDataType, ISiStorageData } from './interfaces';
|
|
3
|
+
import { ValueFromStringError } from './interfaces';
|
|
4
|
+
|
|
5
|
+
import { ModifyUndefinedException, SiDataType } from './SiDataType';
|
|
6
|
+
|
|
7
|
+
export type SiIntegerPartDefinition = [number, number, number] | [number];
|
|
8
|
+
|
|
9
|
+
export interface SiIntegerPart {
|
|
10
|
+
byteOffset: number;
|
|
11
|
+
startBit: number;
|
|
12
|
+
endBit: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class SiInt extends SiDataType<number> implements ISiDataType<number> {
|
|
16
|
+
public parts: SiIntegerPart[];
|
|
17
|
+
|
|
18
|
+
constructor(parts: SiIntegerPartDefinition[]) {
|
|
19
|
+
super();
|
|
20
|
+
this.parts = parts.map((rawPart) => ({
|
|
21
|
+
byteOffset: rawPart[0],
|
|
22
|
+
startBit: rawPart.length === 3 ? rawPart[1] : 0,
|
|
23
|
+
endBit: rawPart.length === 3 ? rawPart[2] : 8
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
typeSpecificIsValueValid(value: number): boolean {
|
|
28
|
+
return _.isInteger(value) && value >= 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
typeSpecificValueToString(value: number): string {
|
|
32
|
+
return value.toString();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
typeSpecificValueFromString(string: string): number | ValueFromStringError {
|
|
36
|
+
const intValue = parseInt(string, 10);
|
|
37
|
+
if (!_.isInteger(intValue)) {
|
|
38
|
+
return new ValueFromStringError(`Value for SiInt must be integer, not "${string}"`);
|
|
39
|
+
}
|
|
40
|
+
return intValue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
isUndefined(data: ISiStorageData): boolean {
|
|
44
|
+
return this.parts.some((part) => data.get(part.byteOffset) === undefined);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
typeSpecificExtractFromData(data: ISiStorageData): number | undefined {
|
|
48
|
+
if (this.isUndefined(data)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
let bitOffset = 0;
|
|
52
|
+
let intValue = 0;
|
|
53
|
+
this.parts.forEach((part) => {
|
|
54
|
+
const { byteOffset, startBit, endBit } = part;
|
|
55
|
+
const bitLength = endBit - startBit;
|
|
56
|
+
const lengthMask = (0x01 << bitLength) - 1;
|
|
57
|
+
const existingByte = data.get(byteOffset) as number;
|
|
58
|
+
const partValue = (existingByte >> startBit) & lengthMask;
|
|
59
|
+
intValue |= partValue << bitOffset;
|
|
60
|
+
bitOffset += bitLength;
|
|
61
|
+
});
|
|
62
|
+
return intValue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
typeSpecificUpdateData(data: ISiStorageData, newValue: number): ISiStorageData {
|
|
66
|
+
if (this.isUndefined(data)) {
|
|
67
|
+
throw new ModifyUndefinedException();
|
|
68
|
+
}
|
|
69
|
+
let bitOffset = 0;
|
|
70
|
+
let tempData = data;
|
|
71
|
+
this.parts.forEach((part) => {
|
|
72
|
+
const { byteOffset, startBit, endBit } = part;
|
|
73
|
+
const bitLength = endBit - startBit;
|
|
74
|
+
const lengthMask = (0x01 << bitLength) - 1;
|
|
75
|
+
const newPartValue = (newValue >> bitOffset) & lengthMask;
|
|
76
|
+
const existingByte = tempData.get(byteOffset) as number;
|
|
77
|
+
const preservationMask = (lengthMask << startBit) ^ 0xff;
|
|
78
|
+
const newByte = (existingByte & preservationMask) | (newPartValue << startBit);
|
|
79
|
+
tempData = tempData.set(byteOffset, newByte);
|
|
80
|
+
bitOffset += bitLength;
|
|
81
|
+
});
|
|
82
|
+
return tempData;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import Immutable from 'immutable';
|
|
4
|
+
import { type ISiStorageData, ValueToStringError, ValueFromStringError } from './interfaces';
|
|
5
|
+
import { ModifyUndefinedException, SiDataType } from './SiDataType';
|
|
6
|
+
import { SiFieldValue } from './SiFieldValue';
|
|
7
|
+
import { SiModified } from './SiModified';
|
|
8
|
+
|
|
9
|
+
type FakeSiStorageData = (number | undefined)[];
|
|
10
|
+
|
|
11
|
+
describe('SiModified', () => {
|
|
12
|
+
class FakeDataType extends SiDataType<string> {
|
|
13
|
+
constructor(public index: number) {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
typeSpecificIsValueValid(_value: string) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
typeSpecificValueFromString(str: string): string | ValueFromStringError | never {
|
|
22
|
+
return str.substr(2, str.length - 2);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
typeSpecificValueToString(value: string): string {
|
|
26
|
+
return `->${value}<-`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
typeSpecificExtractFromData(data: ISiStorageData): string | undefined {
|
|
30
|
+
const byte = data.get(this.index);
|
|
31
|
+
if (byte === undefined) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return String.fromCharCode(byte);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
typeSpecificUpdateData(data: ISiStorageData, newValue: string): ISiStorageData {
|
|
38
|
+
const byte = data.get(this.index);
|
|
39
|
+
if (byte === undefined) {
|
|
40
|
+
throw new ModifyUndefinedException();
|
|
41
|
+
}
|
|
42
|
+
return data.set(this.index, newValue.charCodeAt(0) & 0xff);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const mySiModified = new SiModified(
|
|
47
|
+
new FakeDataType(1),
|
|
48
|
+
(char: string) => char.charCodeAt(0),
|
|
49
|
+
(charCode: number) => (charCode < 32 ? undefined : String.fromCharCode(charCode)),
|
|
50
|
+
(charCode: number) => charCode.toString(16),
|
|
51
|
+
(hexString: string) => {
|
|
52
|
+
const num = parseInt(hexString, 16);
|
|
53
|
+
return Number.isNaN(num) ? new ValueFromStringError('NaN') : num;
|
|
54
|
+
},
|
|
55
|
+
(charCode: number) => _.isInteger(charCode) && charCode >= 0
|
|
56
|
+
);
|
|
57
|
+
const fieldValueOf = (modifiedValue: number) => new SiFieldValue(mySiModified, modifiedValue);
|
|
58
|
+
test('typeSpecificIsValueValid', () => {
|
|
59
|
+
expect(mySiModified.typeSpecificIsValueValid(0)).toBe(true);
|
|
60
|
+
expect(mySiModified.typeSpecificIsValueValid(1)).toBe(true);
|
|
61
|
+
expect(mySiModified.typeSpecificIsValueValid(0xff)).toBe(true);
|
|
62
|
+
expect(mySiModified.typeSpecificIsValueValid(-1)).toBe(false);
|
|
63
|
+
expect(mySiModified.typeSpecificIsValueValid(1.5)).toBe(false);
|
|
64
|
+
expect(mySiModified.typeSpecificIsValueValid(-7.5)).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
test('valueToString', () => {
|
|
67
|
+
expect(mySiModified.valueToString(0)).toBe('0');
|
|
68
|
+
expect(mySiModified.valueToString(1)).toBe('1');
|
|
69
|
+
expect(mySiModified.valueToString(0xff)).toBe('ff');
|
|
70
|
+
expect(mySiModified.valueToString(-1) instanceof ValueToStringError).toBe(true);
|
|
71
|
+
expect(mySiModified.valueToString(-15) instanceof ValueToStringError).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
test('valueFromString', () => {
|
|
74
|
+
expect(mySiModified.valueFromString('0')).toBe(0);
|
|
75
|
+
expect(mySiModified.valueFromString('1')).toBe(1);
|
|
76
|
+
expect(mySiModified.valueFromString('ff')).toBe(0xff);
|
|
77
|
+
expect(mySiModified.valueFromString('0xFF')).toBe(0xff);
|
|
78
|
+
expect(mySiModified.valueFromString('g') instanceof ValueFromStringError).toBe(true);
|
|
79
|
+
expect(mySiModified.valueFromString('test') instanceof ValueFromStringError).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
test('extractFromData gives field value', () => {
|
|
82
|
+
const data = Immutable.List([0x00, 0x00]);
|
|
83
|
+
const fieldValue = mySiModified.extractFromData(data);
|
|
84
|
+
expect(fieldValue instanceof SiFieldValue).toBe(true);
|
|
85
|
+
expect(fieldValue!.field).toBe(mySiModified);
|
|
86
|
+
expect(fieldValue!.value).toBe(0);
|
|
87
|
+
});
|
|
88
|
+
test('extractFromData', () => {
|
|
89
|
+
const getExtractedFieldValue = (bytes: FakeSiStorageData) => mySiModified.extractFromData(Immutable.List(bytes));
|
|
90
|
+
|
|
91
|
+
expect(getExtractedFieldValue([0x00, 0x00])!.value).toBe(0x00);
|
|
92
|
+
expect(getExtractedFieldValue([0x0f, 0x00])!.value).toBe(0x00);
|
|
93
|
+
expect(getExtractedFieldValue([0x00, 0x0f])!.value).toBe(0x0f);
|
|
94
|
+
expect(getExtractedFieldValue([0xff, 0x0f])!.value).toBe(0x0f);
|
|
95
|
+
expect(getExtractedFieldValue([0x00, 0xf0])!.value).toBe(0xf0);
|
|
96
|
+
expect(getExtractedFieldValue([0xab, 0xcd])!.value).toBe(0xcd);
|
|
97
|
+
expect(getExtractedFieldValue([0x00, undefined])).toBe(undefined);
|
|
98
|
+
expect(getExtractedFieldValue([undefined, 0x00])!.value).toBe(0x00);
|
|
99
|
+
expect(getExtractedFieldValue([0x00])).toBe(undefined);
|
|
100
|
+
expect(getExtractedFieldValue([])).toBe(undefined);
|
|
101
|
+
});
|
|
102
|
+
test('updateData', () => {
|
|
103
|
+
const initialData = Immutable.List([0x00, 0x00]);
|
|
104
|
+
const updateInitialData = (newValue: number | SiFieldValue<number>): FakeSiStorageData => mySiModified.updateData(initialData, newValue).toJS();
|
|
105
|
+
|
|
106
|
+
expect(updateInitialData(0x000)).toEqual([0x00, 0x00]);
|
|
107
|
+
expect(updateInitialData(0x00f)).toEqual([0x00, 0x00]);
|
|
108
|
+
expect(updateInitialData(0x020)).toEqual([0x00, 0x20]);
|
|
109
|
+
expect(updateInitialData(0x0ff)).toEqual([0x00, 0xff]);
|
|
110
|
+
expect(updateInitialData(0xfff)).toEqual([0x00, 0xff]);
|
|
111
|
+
expect(updateInitialData(0xf00)).toEqual([0x00, 0x00]);
|
|
112
|
+
expect(updateInitialData(0xcab)).toEqual([0x00, 0xab]);
|
|
113
|
+
expect(updateInitialData(fieldValueOf(0x7357))).toEqual([0x00, 0x57]);
|
|
114
|
+
});
|
|
115
|
+
test('updateData modify undefined', () => {
|
|
116
|
+
const updateData = (data: FakeSiStorageData, newValue: number | SiFieldValue<number>): FakeSiStorageData => mySiModified.updateData(Immutable.List(data), newValue).toJS();
|
|
117
|
+
|
|
118
|
+
expect(() => updateData([], 0x000)).not.toThrow(ModifyUndefinedException);
|
|
119
|
+
expect(() => updateData([], 0x01f)).not.toThrow(ModifyUndefinedException);
|
|
120
|
+
expect(() => updateData([], 0x020)).toThrow(ModifyUndefinedException);
|
|
121
|
+
expect(() => updateData([], 0xcab)).toThrow(ModifyUndefinedException);
|
|
122
|
+
expect(() => updateData([], fieldValueOf(0x7357))).toThrow(ModifyUndefinedException);
|
|
123
|
+
expect(() => updateData([0x00, undefined], 0xcab)).toThrow(ModifyUndefinedException);
|
|
124
|
+
expect(() => updateData([0xab, undefined], fieldValueOf(0x0ab))).toThrow(ModifyUndefinedException);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const nullSiModified = new SiModified(new FakeDataType(1));
|
|
128
|
+
test('defaults if modification functions are undefined', () => {
|
|
129
|
+
expect(nullSiModified.isValueValid(0)).toBe(true);
|
|
130
|
+
expect(nullSiModified.valueToString(5) instanceof ValueToStringError).toBe(true);
|
|
131
|
+
expect(nullSiModified.valueFromString('5') instanceof ValueFromStringError).toBe(true);
|
|
132
|
+
expect(nullSiModified.extractFromData(Immutable.List([]))).toBe(undefined);
|
|
133
|
+
expect(nullSiModified.updateData(Immutable.List([]), 0)).toEqual(Immutable.List([]));
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ISiDataType, ISiStorageData } from './interfaces';
|
|
2
|
+
import { ValueToStringError, ValueFromStringError } from './interfaces';
|
|
3
|
+
import { SiDataType } from './SiDataType';
|
|
4
|
+
|
|
5
|
+
export class SiModified<T, U> extends SiDataType<U> implements ISiDataType<U> {
|
|
6
|
+
constructor(
|
|
7
|
+
public readonly dataType: ISiDataType<T>,
|
|
8
|
+
public readonly modifyExtracted?: (value: T) => U | undefined,
|
|
9
|
+
public readonly modifyForUpdate?: (value: U) => T | undefined,
|
|
10
|
+
public readonly modifiedToString?: (value: U) => string | ValueToStringError,
|
|
11
|
+
public readonly modifiedFromString?: (value: string) => U | ValueFromStringError,
|
|
12
|
+
public readonly modifiedIsValid?: (value: U) => boolean
|
|
13
|
+
) {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
typeSpecificIsValueValid(value: U): boolean {
|
|
18
|
+
if (!this.modifiedIsValid) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return this.modifiedIsValid(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
typeSpecificValueToString(value: U): string | ValueToStringError {
|
|
25
|
+
if (!this.modifiedToString) {
|
|
26
|
+
return new ValueToStringError('modifiedToString was not provided');
|
|
27
|
+
}
|
|
28
|
+
return this.modifiedToString(value);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
typeSpecificValueFromString(string: string): U | ValueFromStringError {
|
|
32
|
+
if (!this.modifiedFromString) {
|
|
33
|
+
return new ValueFromStringError('modifiedFromString was not provided');
|
|
34
|
+
}
|
|
35
|
+
return this.modifiedFromString(string);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
typeSpecificExtractFromData(data: ISiStorageData): U | undefined {
|
|
39
|
+
if (!this.modifyExtracted) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const internalData = this.dataType.typeSpecificExtractFromData(data);
|
|
43
|
+
if (internalData === undefined) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return this.modifyExtracted(internalData);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
typeSpecificUpdateData(data: ISiStorageData, newValue: U): ISiStorageData {
|
|
50
|
+
if (!this.modifyForUpdate) {
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
const internalData = this.modifyForUpdate(newValue);
|
|
54
|
+
if (internalData === undefined) {
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
57
|
+
return this.dataType.typeSpecificUpdateData(data, internalData);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import Immutable from 'immutable';
|
|
4
|
+
import * as utils from '../utils';
|
|
5
|
+
import { defineStorage } from './SiStorage';
|
|
6
|
+
import { SiFieldValue } from './SiFieldValue';
|
|
7
|
+
import { SiBool } from './SiBool';
|
|
8
|
+
import { SiInt } from './SiInt';
|
|
9
|
+
|
|
10
|
+
describe('storage utils', () => {
|
|
11
|
+
const isWeirdField = new SiBool(0x00, 7);
|
|
12
|
+
const weirdnessField = new SiInt([[0x01], [0x00, 0, 7]]);
|
|
13
|
+
const locations = {
|
|
14
|
+
isWeird: isWeirdField,
|
|
15
|
+
weirdness: weirdnessField
|
|
16
|
+
};
|
|
17
|
+
test('SiStorage init', () => {
|
|
18
|
+
const weirdStorage = defineStorage(0x02, locations);
|
|
19
|
+
const weirdStorageFromArray = weirdStorage(utils.unPrettyHex('01 23'));
|
|
20
|
+
expect(weirdStorageFromArray.locations).toEqual(locations);
|
|
21
|
+
expect(weirdStorageFromArray.data.toJS()).toEqual(utils.unPrettyHex('01 23'));
|
|
22
|
+
const weirdStorageFromList = weirdStorage(Immutable.List(utils.unPrettyHex('45 67')));
|
|
23
|
+
expect(weirdStorageFromList.locations).toEqual(locations);
|
|
24
|
+
expect(weirdStorageFromList.data.toJS()).toEqual(utils.unPrettyHex('45 67'));
|
|
25
|
+
const weirdStorageFromUndefined = weirdStorage();
|
|
26
|
+
expect(weirdStorageFromUndefined.locations).toEqual(locations);
|
|
27
|
+
expect(weirdStorageFromUndefined.data.toJS()).toEqual(utils.unPrettyHex('?? ??'));
|
|
28
|
+
});
|
|
29
|
+
test('SiStorage init wrong length', () => {
|
|
30
|
+
const weirdStorage = defineStorage(0x02, {});
|
|
31
|
+
expect(() => weirdStorage([0x00])).toThrow();
|
|
32
|
+
});
|
|
33
|
+
test('SiStorage splice', () => {
|
|
34
|
+
const weirdStorage = defineStorage(0x04, {});
|
|
35
|
+
const myWeirdStorage = weirdStorage(utils.unPrettyHex('00 00 00 00'));
|
|
36
|
+
|
|
37
|
+
myWeirdStorage.splice(1, 2, ...(utils.unPrettyHex('12 34') as number[]));
|
|
38
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('00 12 34 00'));
|
|
39
|
+
|
|
40
|
+
expect(() => myWeirdStorage.splice(1, 2)).toThrow();
|
|
41
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('00 12 34 00'));
|
|
42
|
+
});
|
|
43
|
+
test('SiStorage get', () => {
|
|
44
|
+
const weirdStorage = defineStorage(0x02, locations);
|
|
45
|
+
const myWeirdStorage = weirdStorage(utils.unPrettyHex('01 23'));
|
|
46
|
+
const isWeirdFieldValue = myWeirdStorage.get('isWeird')!;
|
|
47
|
+
expect(isWeirdFieldValue instanceof SiFieldValue).toBe(true);
|
|
48
|
+
expect(isWeirdFieldValue.field).toBe(isWeirdField);
|
|
49
|
+
expect(isWeirdFieldValue.value).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import Immutable from 'immutable';
|
|
3
|
+
import type { ISiFieldValue, ISiStorage, ISiStorageData, ISiStorageDefinition, ISiStorageLocations } from './interfaces';
|
|
4
|
+
|
|
5
|
+
export class SiStorage<T> implements ISiStorage<T> {
|
|
6
|
+
private internalData: ISiStorageData;
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line no-useless-constructor
|
|
9
|
+
constructor(public readonly size: number, public readonly locations: ISiStorageLocations<T>, initArg?: Immutable.List<number | undefined> | Array<number | undefined>) {
|
|
10
|
+
const initArrayOrList = (initArg === undefined ? _.range(size).map(() => undefined) : initArg) as Immutable.List<number | undefined> | Array<number | undefined>;
|
|
11
|
+
const initList = (initArrayOrList instanceof Immutable.List ? initArrayOrList : Immutable.List(initArrayOrList)) as Immutable.List<number | undefined>;
|
|
12
|
+
if (initList.size !== size) {
|
|
13
|
+
throw new Error(`SiStorage constructor list "${initArg}" => "${initList}" ` + `must have size ${size} (but is ${initList.size})`);
|
|
14
|
+
}
|
|
15
|
+
this.internalData = initList;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get data(): ISiStorageData {
|
|
19
|
+
return this.internalData;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get<U extends keyof T>(fieldName: U): ISiFieldValue<T[U]> | undefined {
|
|
23
|
+
const fieldDefinition = this.locations[fieldName];
|
|
24
|
+
return fieldDefinition.extractFromData(this.internalData);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
set<U extends keyof T>(fieldName: U, newValue: ISiFieldValue<T[U]> | T[U]): void {
|
|
28
|
+
const fieldDefinition = this.locations[fieldName];
|
|
29
|
+
this.internalData = fieldDefinition.updateData(this.internalData, newValue);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
splice(index: number, removeNum: number, ...values: number[]): void {
|
|
33
|
+
const newData = this.internalData.splice(index, removeNum, ...values);
|
|
34
|
+
if (newData.size !== this.internalData.size) {
|
|
35
|
+
throw new Error('SiStorage.splice must preserve the size of the storage data ' + `(${this.internalData.size} -> ${newData.size})`);
|
|
36
|
+
}
|
|
37
|
+
this.internalData = newData;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const defineStorage =
|
|
42
|
+
<T>(size: number, locations: ISiStorageLocations<T>): ISiStorageDefinition<T> =>
|
|
43
|
+
(initArg?: Immutable.List<number | undefined> | Array<number | undefined>): ISiStorage<T> =>
|
|
44
|
+
new SiStorage<T>(size, locations, initArg);
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import * as utils from '../utils';
|
|
4
|
+
import * as storage from './index';
|
|
5
|
+
|
|
6
|
+
describe('storage', () => {
|
|
7
|
+
test('SiBool SiStorage integration', () => {
|
|
8
|
+
const weirdStorage = storage.defineStorage(0x02, {
|
|
9
|
+
isWeird: new storage.SiBool(0x00, 7),
|
|
10
|
+
isCrazy: new storage.SiBool(0x01),
|
|
11
|
+
isLoco: new storage.SiBool(0x01, 1)
|
|
12
|
+
});
|
|
13
|
+
const myWeirdStorage = weirdStorage(utils.unPrettyHex('00 00') as number[]);
|
|
14
|
+
expect(myWeirdStorage instanceof storage.SiStorage).toBe(true);
|
|
15
|
+
|
|
16
|
+
expect(myWeirdStorage.get('isWeird')!.value).toBe(false);
|
|
17
|
+
myWeirdStorage.set('isWeird', true);
|
|
18
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('80 00') as number[]);
|
|
19
|
+
expect(myWeirdStorage.get('isWeird')!.value).toBe(true);
|
|
20
|
+
|
|
21
|
+
expect(myWeirdStorage.get('isLoco')!.value).toBe(false);
|
|
22
|
+
myWeirdStorage.set('isLoco', true);
|
|
23
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('80 02') as number[]);
|
|
24
|
+
expect(myWeirdStorage.get('isLoco')!.value).toBe(true);
|
|
25
|
+
|
|
26
|
+
expect(myWeirdStorage.get('isCrazy')!.value).toBe(false);
|
|
27
|
+
myWeirdStorage.set('isCrazy', true);
|
|
28
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('80 03') as number[]);
|
|
29
|
+
expect(myWeirdStorage.get('isCrazy')!.value).toBe(true);
|
|
30
|
+
|
|
31
|
+
const unknownWeirdStorage = weirdStorage();
|
|
32
|
+
const { ModifyUndefinedException } = storage;
|
|
33
|
+
|
|
34
|
+
expect(unknownWeirdStorage.get('isWeird')).toBe(undefined);
|
|
35
|
+
expect(() => unknownWeirdStorage.set('isWeird', true)).toThrow(ModifyUndefinedException);
|
|
36
|
+
|
|
37
|
+
expect(unknownWeirdStorage.get('isLoco')).toBe(undefined);
|
|
38
|
+
expect(() => unknownWeirdStorage.set('isLoco', true)).toThrow(ModifyUndefinedException);
|
|
39
|
+
|
|
40
|
+
expect(unknownWeirdStorage.get('isCrazy')).toBe(undefined);
|
|
41
|
+
expect(() => unknownWeirdStorage.set('isCrazy', true)).toThrow(ModifyUndefinedException);
|
|
42
|
+
});
|
|
43
|
+
test('SiInt SiStorage integration', () => {
|
|
44
|
+
const weirdStorage = storage.defineStorage(0x03, {
|
|
45
|
+
weirdness: new storage.SiInt([[0x00]]),
|
|
46
|
+
crazyness: new storage.SiInt([[0x01, 0, 4]]),
|
|
47
|
+
loconess: new storage.SiInt([[0x02], [0x01, 4, 8]])
|
|
48
|
+
});
|
|
49
|
+
const myWeirdStorage = weirdStorage(utils.unPrettyHex('00 00 00') as number[]);
|
|
50
|
+
expect(myWeirdStorage instanceof storage.SiStorage).toBe(true);
|
|
51
|
+
|
|
52
|
+
expect(myWeirdStorage.get('weirdness')!.value).toBe(0x00);
|
|
53
|
+
myWeirdStorage.set('weirdness', 0x555);
|
|
54
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('55 00 00') as number[]);
|
|
55
|
+
expect(myWeirdStorage.get('weirdness')!.value).toBe(0x55);
|
|
56
|
+
|
|
57
|
+
expect(myWeirdStorage.get('crazyness')!.value).toBe(0x00);
|
|
58
|
+
myWeirdStorage.set('crazyness', 0xaa);
|
|
59
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('55 0A 00') as number[]);
|
|
60
|
+
expect(myWeirdStorage.get('crazyness')!.value).toBe(0x0a);
|
|
61
|
+
|
|
62
|
+
expect(myWeirdStorage.get('loconess')!.value).toBe(0x00);
|
|
63
|
+
myWeirdStorage.set('loconess', 0xabcd);
|
|
64
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('55 BA CD') as number[]);
|
|
65
|
+
expect(myWeirdStorage.get('loconess')!.value).toBe(0xbcd);
|
|
66
|
+
|
|
67
|
+
const unknownWeirdStorage = weirdStorage();
|
|
68
|
+
const { ModifyUndefinedException } = storage;
|
|
69
|
+
|
|
70
|
+
expect(unknownWeirdStorage.get('weirdness')).toBe(undefined);
|
|
71
|
+
expect(() => unknownWeirdStorage.set('weirdness', 0x555)).toThrow(ModifyUndefinedException);
|
|
72
|
+
|
|
73
|
+
expect(unknownWeirdStorage.get('crazyness')).toBe(undefined);
|
|
74
|
+
expect(() => unknownWeirdStorage.set('crazyness', 0xaa)).toThrow(ModifyUndefinedException);
|
|
75
|
+
|
|
76
|
+
expect(unknownWeirdStorage.get('loconess')).toBe(undefined);
|
|
77
|
+
expect(() => unknownWeirdStorage.set('loconess', 0xabcd)).toThrow(ModifyUndefinedException);
|
|
78
|
+
});
|
|
79
|
+
test('SiEnum SiStorage integration', () => {
|
|
80
|
+
const weirdStorage = storage.defineStorage(0x03, {
|
|
81
|
+
weirdness: new storage.SiEnum([[0x00]], { NotWeird: 0x00, Weird: 0x55 }),
|
|
82
|
+
crazyness: new storage.SiEnum([[0x01, 0, 4]], { NotCrazy: 0x00, Crazy: 0x0a }),
|
|
83
|
+
loconess: new storage.SiEnum([[0x02], [0x01, 4, 8]], { NotLoco: 0x000, Loco: 0xbcd })
|
|
84
|
+
});
|
|
85
|
+
const myWeirdStorage = weirdStorage(utils.unPrettyHex('00 00 00') as number[]);
|
|
86
|
+
expect(myWeirdStorage instanceof storage.SiStorage).toBe(true);
|
|
87
|
+
|
|
88
|
+
expect(myWeirdStorage.get('weirdness')!.value).toBe('NotWeird');
|
|
89
|
+
myWeirdStorage.set('weirdness', 'Weird');
|
|
90
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('55 00 00') as number[]);
|
|
91
|
+
expect(myWeirdStorage.get('weirdness')!.value).toBe('Weird');
|
|
92
|
+
|
|
93
|
+
expect(myWeirdStorage.get('crazyness')!.value).toBe('NotCrazy');
|
|
94
|
+
myWeirdStorage.set('crazyness', 'Crazy');
|
|
95
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('55 0A 00') as number[]);
|
|
96
|
+
expect(myWeirdStorage.get('crazyness')!.value).toBe('Crazy');
|
|
97
|
+
|
|
98
|
+
expect(myWeirdStorage.get('loconess')!.value).toBe('NotLoco');
|
|
99
|
+
myWeirdStorage.set('loconess', 'Loco');
|
|
100
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('55 BA CD') as number[]);
|
|
101
|
+
expect(myWeirdStorage.get('loconess')!.value).toBe('Loco');
|
|
102
|
+
|
|
103
|
+
const unknownWeirdStorage = weirdStorage();
|
|
104
|
+
const { ModifyUndefinedException } = storage;
|
|
105
|
+
|
|
106
|
+
expect(unknownWeirdStorage.get('weirdness')).toBe(undefined);
|
|
107
|
+
expect(() => unknownWeirdStorage.set('weirdness', 'Weird')).toThrow(ModifyUndefinedException);
|
|
108
|
+
|
|
109
|
+
expect(unknownWeirdStorage.get('crazyness')).toBe(undefined);
|
|
110
|
+
expect(() => unknownWeirdStorage.set('crazyness', 'Crazy')).toThrow(ModifyUndefinedException);
|
|
111
|
+
|
|
112
|
+
expect(unknownWeirdStorage.get('loconess')).toBe(undefined);
|
|
113
|
+
expect(() => unknownWeirdStorage.set('loconess', 'Loco')).toThrow(ModifyUndefinedException);
|
|
114
|
+
});
|
|
115
|
+
test('SiArray SiStorage integration', () => {
|
|
116
|
+
const weirdStorage = storage.defineStorage(0x03, {
|
|
117
|
+
areWeird: new storage.SiArray(3, (i) => new storage.SiBool(0x00, i)),
|
|
118
|
+
crazynesses: new storage.SiArray(2, (i) => new storage.SiInt([[0x01 + i]]))
|
|
119
|
+
});
|
|
120
|
+
const myWeirdStorage = weirdStorage(utils.unPrettyHex('00 00 00') as number[]);
|
|
121
|
+
expect(myWeirdStorage instanceof storage.SiStorage).toBe(true);
|
|
122
|
+
|
|
123
|
+
expect(myWeirdStorage.get('areWeird')!.value).toEqual([false, false, false]);
|
|
124
|
+
myWeirdStorage.set('areWeird', [true, true, false]);
|
|
125
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('03 00 00') as number[]);
|
|
126
|
+
expect(myWeirdStorage.get('areWeird')!.value).toEqual([true, true, false]);
|
|
127
|
+
|
|
128
|
+
expect(myWeirdStorage.get('crazynesses')!.value).toEqual([0x00, 0x00]);
|
|
129
|
+
myWeirdStorage.set('crazynesses', [0x01, 0x23]);
|
|
130
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('03 01 23') as number[]);
|
|
131
|
+
expect(myWeirdStorage.get('crazynesses')!.value).toEqual([0x01, 0x23]);
|
|
132
|
+
|
|
133
|
+
const unknownWeirdStorage = weirdStorage();
|
|
134
|
+
const { ModifyUndefinedException } = storage;
|
|
135
|
+
|
|
136
|
+
expect(unknownWeirdStorage.get('areWeird')!.value).toEqual([undefined, undefined, undefined]);
|
|
137
|
+
expect(() => unknownWeirdStorage.set('areWeird', [true, true, false])).toThrow(ModifyUndefinedException);
|
|
138
|
+
|
|
139
|
+
expect(unknownWeirdStorage.get('crazynesses')!.value).toEqual([undefined, undefined]);
|
|
140
|
+
expect(() => unknownWeirdStorage.set('crazynesses', [0x01, 0x23])).toThrow(ModifyUndefinedException);
|
|
141
|
+
});
|
|
142
|
+
test('SiDict SiStorage integration', () => {
|
|
143
|
+
const weirdStorage = storage.defineStorage(0x03, {
|
|
144
|
+
bustWaistHip: new storage.SiDict({
|
|
145
|
+
bust: new storage.SiInt([[0x00]]),
|
|
146
|
+
waist: new storage.SiInt([[0x01]]),
|
|
147
|
+
hip: new storage.SiInt([[0x02]])
|
|
148
|
+
})
|
|
149
|
+
});
|
|
150
|
+
const myWeirdStorage = weirdStorage(utils.unPrettyHex('00 00 00') as number[]);
|
|
151
|
+
expect(myWeirdStorage instanceof storage.SiStorage).toBe(true);
|
|
152
|
+
|
|
153
|
+
expect(myWeirdStorage.get('bustWaistHip')!.value).toEqual({
|
|
154
|
+
bust: 0x00,
|
|
155
|
+
waist: 0x00,
|
|
156
|
+
hip: 0x00
|
|
157
|
+
});
|
|
158
|
+
myWeirdStorage.set('bustWaistHip', { bust: 0x90, waist: 0x60, hip: 0x90 });
|
|
159
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('90 60 90') as number[]);
|
|
160
|
+
expect(myWeirdStorage.get('bustWaistHip')!.value).toEqual({
|
|
161
|
+
bust: 0x90,
|
|
162
|
+
waist: 0x60,
|
|
163
|
+
hip: 0x90
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const unknownWeirdStorage = weirdStorage();
|
|
167
|
+
const { ModifyUndefinedException } = storage;
|
|
168
|
+
|
|
169
|
+
expect(unknownWeirdStorage.get('bustWaistHip')!.value).toEqual({
|
|
170
|
+
bust: undefined,
|
|
171
|
+
waist: undefined,
|
|
172
|
+
hip: undefined
|
|
173
|
+
});
|
|
174
|
+
expect(() => unknownWeirdStorage.set('bustWaistHip', { bust: 0x90, waist: 0x60, hip: 0x90 })).toThrow(ModifyUndefinedException);
|
|
175
|
+
});
|
|
176
|
+
test('SiArray SiDict combinations', () => {
|
|
177
|
+
const weirdStorage = storage.defineStorage(0x06, {
|
|
178
|
+
measurements: new storage.SiArray(
|
|
179
|
+
3,
|
|
180
|
+
(i) =>
|
|
181
|
+
new storage.SiDict({
|
|
182
|
+
time: new storage.SiInt([[0x00 + i * 2]]),
|
|
183
|
+
value: new storage.SiInt([[0x01 + i * 2]])
|
|
184
|
+
})
|
|
185
|
+
)
|
|
186
|
+
});
|
|
187
|
+
const myWeirdStorage = weirdStorage(utils.unPrettyHex('00 00 00 00 00 00') as number[]);
|
|
188
|
+
expect(myWeirdStorage instanceof storage.SiStorage).toBe(true);
|
|
189
|
+
|
|
190
|
+
expect(myWeirdStorage.get('measurements')!.value).toEqual([
|
|
191
|
+
{ time: 0x00, value: 0x00 },
|
|
192
|
+
{ time: 0x00, value: 0x00 },
|
|
193
|
+
{ time: 0x00, value: 0x00 }
|
|
194
|
+
]);
|
|
195
|
+
myWeirdStorage.set('measurements', [
|
|
196
|
+
{ time: 0x01, value: 0x01 },
|
|
197
|
+
{ time: 0x03, value: 0x09 },
|
|
198
|
+
{ time: 0x04, value: 0x10 }
|
|
199
|
+
]);
|
|
200
|
+
expect(myWeirdStorage.data.toJS()).toEqual(utils.unPrettyHex('01 01 03 09 04 10') as number[]);
|
|
201
|
+
expect(myWeirdStorage.get('measurements')!.value).toEqual([
|
|
202
|
+
{ time: 0x01, value: 0x01 },
|
|
203
|
+
{ time: 0x03, value: 0x09 },
|
|
204
|
+
{ time: 0x04, value: 0x10 }
|
|
205
|
+
]);
|
|
206
|
+
});
|
|
207
|
+
test('everything exported', () => {
|
|
208
|
+
expect(storage.ValueToStringError).not.toBe(undefined);
|
|
209
|
+
expect(storage.ValueFromStringError).not.toBe(undefined);
|
|
210
|
+
expect(storage.defineStorage).not.toBe(undefined);
|
|
211
|
+
expect(storage.SiStorage).not.toBe(undefined);
|
|
212
|
+
expect(storage.SiFieldValue).not.toBe(undefined);
|
|
213
|
+
expect(storage.ModifyUndefinedException).not.toBe(undefined);
|
|
214
|
+
expect(storage.SiDataType).not.toBe(undefined);
|
|
215
|
+
expect(storage.SiArray).not.toBe(undefined);
|
|
216
|
+
expect(storage.SiBool).not.toBe(undefined);
|
|
217
|
+
expect(storage.SiDict).not.toBe(undefined);
|
|
218
|
+
expect(storage.SiEnum).not.toBe(undefined);
|
|
219
|
+
expect(storage.SiInt).not.toBe(undefined);
|
|
220
|
+
expect(storage.SiModified).not.toBe(undefined);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { ValueToStringError, ValueFromStringError } from './interfaces';
|
|
2
|
+
export type { ISiDataType, ISiFieldValue, ISiStorage, ISiStorageData, ISiStorageLocations } from './interfaces';
|
|
3
|
+
export { defineStorage, SiStorage } from './SiStorage';
|
|
4
|
+
export { SiFieldValue } from './SiFieldValue';
|
|
5
|
+
export { ModifyUndefinedException, SiDataType } from './SiDataType';
|
|
6
|
+
export { SiArray } from './SiArray';
|
|
7
|
+
export { SiBool } from './SiBool';
|
|
8
|
+
export { SiDict } from './SiDict';
|
|
9
|
+
export { SiEnum } from './SiEnum';
|
|
10
|
+
export { SiInt } from './SiInt';
|
|
11
|
+
export { SiModified } from './SiModified';
|
|
12
|
+
export { siStringToUtf8 } from './siStringEncoding'
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type Immutable from 'immutable';
|
|
2
|
+
import * as utils from '../utils';
|
|
3
|
+
|
|
4
|
+
export class ValueToStringError extends utils.SiError {}
|
|
5
|
+
export class ValueFromStringError extends utils.SiError {}
|
|
6
|
+
|
|
7
|
+
export interface ISiFieldValue<T> {
|
|
8
|
+
field: ISiDataType<T>;
|
|
9
|
+
value: T;
|
|
10
|
+
toString: () => string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ISiStorageData = Immutable.List<number | undefined>;
|
|
14
|
+
|
|
15
|
+
export interface ISiDataType<T> {
|
|
16
|
+
isValueValid: (value: T) => boolean;
|
|
17
|
+
typeSpecificIsValueValid: (value: T) => boolean;
|
|
18
|
+
valueToString: (value: T) => string | ValueToStringError;
|
|
19
|
+
typeSpecificValueToString: (value: T) => string | ValueToStringError;
|
|
20
|
+
valueFromString: (string: string) => T | ValueFromStringError;
|
|
21
|
+
typeSpecificValueFromString: (string: string) => T | ValueFromStringError;
|
|
22
|
+
extractFromData: (data: ISiStorageData) => ISiFieldValue<T> | undefined;
|
|
23
|
+
typeSpecificExtractFromData: (data: ISiStorageData) => T | undefined;
|
|
24
|
+
updateData: (data: ISiStorageData, newValue: T | ISiFieldValue<T>) => ISiStorageData;
|
|
25
|
+
typeSpecificUpdateData: (data: ISiStorageData, newValue: T) => ISiStorageData;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type ISiStorageLocations<Fields> = {
|
|
29
|
+
[id in keyof Fields]: ISiDataType<Fields[id]>;
|
|
30
|
+
};
|
|
31
|
+
/** Consists of locations and size */
|
|
32
|
+
export type ISiStorageDefinition<Fields> = (initArg?: Immutable.List<number | undefined> | Array<number | undefined>) => ISiStorage<Fields>;
|
|
33
|
+
|
|
34
|
+
export interface ISiStorage<T> {
|
|
35
|
+
size: number;
|
|
36
|
+
locations: ISiStorageLocations<T>;
|
|
37
|
+
data: ISiStorageData;
|
|
38
|
+
get: <U extends keyof T>(fieldName: U) => ISiFieldValue<T[U]> | undefined;
|
|
39
|
+
set: <U extends keyof T>(fieldName: U, newValue: ISiFieldValue<T[U]> | T[U]) => void;
|
|
40
|
+
splice: (index: number, removeNum: number, ...values: number[]) => void;
|
|
41
|
+
}
|