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,221 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import type * as siProtocol from '../siProtocol';
|
|
4
|
+
import * as testUtils from '../testUtils';
|
|
5
|
+
import { proto } from '../constants';
|
|
6
|
+
import { SiDataType } from '../storage';
|
|
7
|
+
import { type ISiTargetMultiplexer, SiTargetMultiplexerTarget } from './ISiTargetMultiplexer';
|
|
8
|
+
import { BaseSiStation } from './BaseSiStation';
|
|
9
|
+
import { getSiStationExamples } from './siStationExamples';
|
|
10
|
+
|
|
11
|
+
testUtils.useFakeTimers();
|
|
12
|
+
|
|
13
|
+
describe('SiStation', () => {
|
|
14
|
+
class MySiStation extends BaseSiStation<SiTargetMultiplexerTarget.Direct> {}
|
|
15
|
+
|
|
16
|
+
test('SiStation info', async () => {
|
|
17
|
+
const mySiStation = new MySiStation({} as unknown as ISiTargetMultiplexer, SiTargetMultiplexerTarget.Direct);
|
|
18
|
+
interface MessageRecord {
|
|
19
|
+
target: SiTargetMultiplexerTarget;
|
|
20
|
+
command: number | undefined;
|
|
21
|
+
parameters: number[] | undefined;
|
|
22
|
+
numResponses: number | undefined;
|
|
23
|
+
}
|
|
24
|
+
const messagesSent: MessageRecord[] = [];
|
|
25
|
+
mySiStation.siTargetMultiplexer = {
|
|
26
|
+
sendMessage: (target, message, numResponses) => {
|
|
27
|
+
const command = message.mode === undefined ? message.command : undefined;
|
|
28
|
+
const parameters = message.mode === undefined ? message.parameters : undefined;
|
|
29
|
+
messagesSent.push({
|
|
30
|
+
target: target,
|
|
31
|
+
command: command,
|
|
32
|
+
parameters: parameters,
|
|
33
|
+
numResponses: numResponses
|
|
34
|
+
});
|
|
35
|
+
if (command === proto.cmd.GET_SYS_VAL) {
|
|
36
|
+
return Promise.resolve([[0x00, 0x00, 0x00, ..._.range(128)]]);
|
|
37
|
+
}
|
|
38
|
+
if (command === proto.cmd.SET_SYS_VAL) {
|
|
39
|
+
return Promise.resolve([[0x00, 0x00, 0x72]]);
|
|
40
|
+
}
|
|
41
|
+
throw new Error();
|
|
42
|
+
}
|
|
43
|
+
} as ISiTargetMultiplexer;
|
|
44
|
+
const timeState = { infoHasBeenRead: false, changesHaveBeenWritten: false };
|
|
45
|
+
mySiStation.readInfo().then(() => {
|
|
46
|
+
timeState.infoHasBeenRead = true;
|
|
47
|
+
});
|
|
48
|
+
expect(mySiStation.storage.data.toJS()).toEqual(_.range(128).map(() => undefined));
|
|
49
|
+
expect(mySiStation.getField('code') instanceof SiDataType).toBe(true);
|
|
50
|
+
expect(mySiStation.getInfo('code')).toBe(undefined);
|
|
51
|
+
|
|
52
|
+
await testUtils.advanceTimersByTime(0);
|
|
53
|
+
expect(messagesSent).toEqual([
|
|
54
|
+
{
|
|
55
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
56
|
+
command: proto.cmd.GET_SYS_VAL,
|
|
57
|
+
numResponses: 1,
|
|
58
|
+
parameters: [0, 128]
|
|
59
|
+
}
|
|
60
|
+
]);
|
|
61
|
+
expect(timeState).toEqual({ infoHasBeenRead: true, changesHaveBeenWritten: false });
|
|
62
|
+
expect(mySiStation.storage.data.toJS()).toEqual(_.range(128));
|
|
63
|
+
expect(mySiStation.getInfo('code')).not.toBe(undefined);
|
|
64
|
+
expect(mySiStation.setInfo('code', 0)).toBe(undefined);
|
|
65
|
+
expect(mySiStation.getInfo('code')!.value).toBe(0);
|
|
66
|
+
mySiStation.writeChanges().then(() => {
|
|
67
|
+
timeState.changesHaveBeenWritten = true;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await testUtils.nTimesAsync(2, () => testUtils.advanceTimersByTime(0));
|
|
71
|
+
expect(messagesSent).toEqual([
|
|
72
|
+
{
|
|
73
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
74
|
+
command: proto.cmd.GET_SYS_VAL,
|
|
75
|
+
numResponses: 1,
|
|
76
|
+
parameters: [0, 128]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
80
|
+
command: proto.cmd.GET_SYS_VAL,
|
|
81
|
+
numResponses: 1,
|
|
82
|
+
parameters: [0, 128]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
86
|
+
command: proto.cmd.SET_SYS_VAL,
|
|
87
|
+
numResponses: 1,
|
|
88
|
+
parameters: [0x72, 0x00, 0x33]
|
|
89
|
+
}
|
|
90
|
+
]);
|
|
91
|
+
expect(timeState).toEqual({ infoHasBeenRead: true, changesHaveBeenWritten: true });
|
|
92
|
+
mySiStation.atomically(() => {
|
|
93
|
+
mySiStation.setInfo('code', 10);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await testUtils.nTimesAsync(2, () => testUtils.advanceTimersByTime(0));
|
|
97
|
+
expect(messagesSent).toEqual([
|
|
98
|
+
{
|
|
99
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
100
|
+
command: proto.cmd.GET_SYS_VAL,
|
|
101
|
+
numResponses: 1,
|
|
102
|
+
parameters: [0, 128]
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
106
|
+
command: proto.cmd.GET_SYS_VAL,
|
|
107
|
+
numResponses: 1,
|
|
108
|
+
parameters: [0, 128]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
112
|
+
command: proto.cmd.SET_SYS_VAL,
|
|
113
|
+
numResponses: 1,
|
|
114
|
+
parameters: [0x72, 0x00, 0x33]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
118
|
+
command: proto.cmd.GET_SYS_VAL,
|
|
119
|
+
numResponses: 1,
|
|
120
|
+
parameters: [0, 128]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
target: SiTargetMultiplexerTarget.Direct,
|
|
124
|
+
command: proto.cmd.SET_SYS_VAL,
|
|
125
|
+
numResponses: 1,
|
|
126
|
+
parameters: [0x72, 0x0a, 0x33]
|
|
127
|
+
}
|
|
128
|
+
]);
|
|
129
|
+
expect(mySiStation.getInfo('code')!.value).toBe(10);
|
|
130
|
+
});
|
|
131
|
+
const examples = getSiStationExamples();
|
|
132
|
+
Object.keys(examples).forEach((exampleName) => {
|
|
133
|
+
const { storageData, stationData } = examples[exampleName];
|
|
134
|
+
test(`works with ${exampleName} example`, (done) => {
|
|
135
|
+
const mySiStation = new MySiStation(
|
|
136
|
+
{
|
|
137
|
+
sendMessage: (_target: SiTargetMultiplexerTarget, message: siProtocol.SiMessage, numResponses: number) => {
|
|
138
|
+
if (message.mode !== undefined) {
|
|
139
|
+
return Promise.resolve([]);
|
|
140
|
+
}
|
|
141
|
+
const { command, parameters } = message;
|
|
142
|
+
if (command === proto.cmd.GET_SYS_VAL) {
|
|
143
|
+
const getRange = (offset: number, length: number) => [...[0x00, 0x00, offset], ...storageData.slice(offset, offset + length)];
|
|
144
|
+
if (numResponses !== 1) {
|
|
145
|
+
throw new Error(`Invalid numResponses ${numResponses} (expected 1)`);
|
|
146
|
+
}
|
|
147
|
+
return Promise.resolve([getRange(parameters![0]!, parameters![1]!)]);
|
|
148
|
+
}
|
|
149
|
+
return Promise.resolve([]);
|
|
150
|
+
}
|
|
151
|
+
} as unknown as ISiTargetMultiplexer,
|
|
152
|
+
SiTargetMultiplexerTarget.Direct
|
|
153
|
+
);
|
|
154
|
+
mySiStation.readInfo().then(() => {
|
|
155
|
+
Object.keys(stationData).forEach((stationDataKey) => {
|
|
156
|
+
// @ts-ignore
|
|
157
|
+
expect(mySiStation.getInfo(stationDataKey)!.value).toEqual(stationData[stationDataKey]);
|
|
158
|
+
});
|
|
159
|
+
done();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('get/set time', async () => {
|
|
165
|
+
const mySiStation = new MySiStation(
|
|
166
|
+
{
|
|
167
|
+
sendMessage: () => Promise.resolve([[0x00, 0x00, 0x01, 0x0c, 0x1f, 0x00, 0xa8, 0xbf, 0x40]])
|
|
168
|
+
} as unknown as ISiTargetMultiplexer,
|
|
169
|
+
SiTargetMultiplexerTarget.Direct
|
|
170
|
+
);
|
|
171
|
+
const timeState: { [key: string]: Date | undefined } = {
|
|
172
|
+
retrievedTime: undefined,
|
|
173
|
+
setTime: undefined
|
|
174
|
+
};
|
|
175
|
+
mySiStation.getTime().then((time) => {
|
|
176
|
+
timeState.retrievedTime = time;
|
|
177
|
+
});
|
|
178
|
+
await testUtils.nTimesAsync(1, () => testUtils.advanceTimersByTime(0));
|
|
179
|
+
expect(timeState.retrievedTime instanceof Date).toBe(true);
|
|
180
|
+
|
|
181
|
+
mySiStation.setTime(new Date()).then((time) => {
|
|
182
|
+
timeState.setTime = time;
|
|
183
|
+
});
|
|
184
|
+
await testUtils.nTimesAsync(1, () => testUtils.advanceTimersByTime(0));
|
|
185
|
+
expect(timeState.setTime instanceof Date).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
test('signal', async () => {
|
|
188
|
+
const mySiStation = new MySiStation(
|
|
189
|
+
{
|
|
190
|
+
sendMessage: () => Promise.resolve([[0x00, 0x00, 0x02]])
|
|
191
|
+
} as unknown as ISiTargetMultiplexer,
|
|
192
|
+
SiTargetMultiplexerTarget.Direct
|
|
193
|
+
);
|
|
194
|
+
const timeState = { signalTwiceSucceeded: false, signalOnceFailed: false };
|
|
195
|
+
mySiStation.signal(2).then(() => {
|
|
196
|
+
timeState.signalTwiceSucceeded = true;
|
|
197
|
+
});
|
|
198
|
+
await testUtils.nTimesAsync(1, () => testUtils.advanceTimersByTime(0));
|
|
199
|
+
expect(timeState).toEqual({ signalTwiceSucceeded: true, signalOnceFailed: false });
|
|
200
|
+
|
|
201
|
+
mySiStation.signal(1).catch(() => {
|
|
202
|
+
timeState.signalOnceFailed = true;
|
|
203
|
+
});
|
|
204
|
+
await testUtils.nTimesAsync(1, () => testUtils.advanceTimersByTime(0));
|
|
205
|
+
expect(timeState).toEqual({ signalTwiceSucceeded: true, signalOnceFailed: true });
|
|
206
|
+
});
|
|
207
|
+
test('powerOff', async () => {
|
|
208
|
+
const mySiStation = new MySiStation(
|
|
209
|
+
{
|
|
210
|
+
sendMessage: () => Promise.resolve()
|
|
211
|
+
} as unknown as ISiTargetMultiplexer,
|
|
212
|
+
SiTargetMultiplexerTarget.Direct
|
|
213
|
+
);
|
|
214
|
+
const timeState = { powerOffSucceeded: false };
|
|
215
|
+
mySiStation.powerOff().then(() => {
|
|
216
|
+
timeState.powerOffSucceeded = true;
|
|
217
|
+
});
|
|
218
|
+
await testUtils.nTimesAsync(1, () => testUtils.advanceTimersByTime(0));
|
|
219
|
+
expect(timeState).toEqual({ powerOffSucceeded: true });
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { proto } from '../constants';
|
|
2
|
+
import * as storage from '../storage';
|
|
3
|
+
import * as siProtocol from '../siProtocol';
|
|
4
|
+
import { type ISiStation, SiStationMode, SiStationModel } from './ISiStation';
|
|
5
|
+
import { type ISiTargetMultiplexer, SiTargetMultiplexerTarget } from './ISiTargetMultiplexer';
|
|
6
|
+
|
|
7
|
+
export interface ISiStationStorageFields {
|
|
8
|
+
code: number;
|
|
9
|
+
mode: keyof typeof SiStationMode;
|
|
10
|
+
beeps: boolean;
|
|
11
|
+
flashes: boolean;
|
|
12
|
+
autoSend: boolean;
|
|
13
|
+
extendedProtocol: boolean;
|
|
14
|
+
serialNumber: number;
|
|
15
|
+
firmwareVersion: number;
|
|
16
|
+
buildDate: Date;
|
|
17
|
+
deviceModel: keyof typeof SiStationModel;
|
|
18
|
+
memorySize: number;
|
|
19
|
+
batteryDate: Date;
|
|
20
|
+
batteryCapacity: number;
|
|
21
|
+
batteryState: number;
|
|
22
|
+
backupPointer: number;
|
|
23
|
+
siCard6Mode: number;
|
|
24
|
+
memoryOverflow: number;
|
|
25
|
+
lastWriteDate: Date;
|
|
26
|
+
autoOffTimeout: number;
|
|
27
|
+
refreshRate: number;
|
|
28
|
+
powerMode: number;
|
|
29
|
+
interval: number;
|
|
30
|
+
wtf: number;
|
|
31
|
+
program: number;
|
|
32
|
+
handshake: boolean;
|
|
33
|
+
sprint4ms: boolean;
|
|
34
|
+
passwordOnly: boolean;
|
|
35
|
+
stopOnFullBackup: boolean;
|
|
36
|
+
autoReadout: boolean;
|
|
37
|
+
sleepDay: number;
|
|
38
|
+
sleepSeconds: number;
|
|
39
|
+
workingMinutes: number;
|
|
40
|
+
}
|
|
41
|
+
export const siStationStorageLocations: storage.ISiStorageLocations<ISiStationStorageFields> = {
|
|
42
|
+
code: new storage.SiInt([[0x72], [0x73, 6, 8]]),
|
|
43
|
+
mode: new storage.SiEnum([[0x71]], SiStationMode),
|
|
44
|
+
beeps: new storage.SiBool(0x73, 2),
|
|
45
|
+
flashes: new storage.SiBool(0x73, 0),
|
|
46
|
+
autoSend: new storage.SiBool(0x74, 1),
|
|
47
|
+
extendedProtocol: new storage.SiBool(0x74, 0),
|
|
48
|
+
serialNumber: new storage.SiInt([[0x03], [0x02], [0x01], [0x00]]),
|
|
49
|
+
firmwareVersion: new storage.SiInt([[0x07], [0x06], [0x05]]),
|
|
50
|
+
buildDate: new siProtocol.SiDate(3, (i) => 0x08 + i),
|
|
51
|
+
deviceModel: new storage.SiEnum([[0x0c], [0x0b]], SiStationModel),
|
|
52
|
+
memorySize: new storage.SiInt([[0x0d]]),
|
|
53
|
+
batteryDate: new siProtocol.SiDate(3, (i) => 0x15 + i),
|
|
54
|
+
batteryCapacity: new storage.SiInt([[0x1a], [0x19]]),
|
|
55
|
+
batteryState: new storage.SiInt([[0x37], [0x36], [0x35], [0x34]]),
|
|
56
|
+
// 2000mAh: 000000=0%, 6E0000=100%, 1000mAh:000000=0%, 370000=100%
|
|
57
|
+
backupPointer: new storage.SiInt([[0x22], [0x21], [0x1d], [0x1c]]),
|
|
58
|
+
siCard6Mode: new storage.SiInt([[0x33]]),
|
|
59
|
+
// 08 or FF = 192 punches, 00 or C1 normal
|
|
60
|
+
memoryOverflow: new storage.SiInt([[0x3d]]),
|
|
61
|
+
// overflow if != 00
|
|
62
|
+
lastWriteDate: new siProtocol.SiDate(6, (i) => 0x75 + i),
|
|
63
|
+
autoOffTimeout: new storage.SiInt([[0x7f], [0x7e]]),
|
|
64
|
+
refreshRate: new storage.SiInt([[0x10]]),
|
|
65
|
+
// in 3/sec ???
|
|
66
|
+
powerMode: new storage.SiInt([[0x11]]),
|
|
67
|
+
// 06 low power, 08 standard/sprint
|
|
68
|
+
interval: new storage.SiInt([[0x49], [0x48]]),
|
|
69
|
+
// in 32*ms
|
|
70
|
+
wtf: new storage.SiInt([[0x4b], [0x4a]]),
|
|
71
|
+
// in 32*ms
|
|
72
|
+
program: new storage.SiInt([[0x70]]),
|
|
73
|
+
// xx0xxxxxb competition, xx1xxxxxb training
|
|
74
|
+
handshake: new storage.SiBool(0x74, 2),
|
|
75
|
+
sprint4ms: new storage.SiBool(0x74, 3),
|
|
76
|
+
passwordOnly: new storage.SiBool(0x74, 4),
|
|
77
|
+
stopOnFullBackup: new storage.SiBool(0x74, 5),
|
|
78
|
+
autoReadout: new storage.SiBool(0x74, 7),
|
|
79
|
+
// depends on autoSend
|
|
80
|
+
sleepDay: new storage.SiInt([[0x7b]]),
|
|
81
|
+
// xxxxxxx0b - seconds relative to midnight/midday: 0 = am, 1 = pm
|
|
82
|
+
// xxxx000xb - day of week: 000 = Sunday, 110 = Saturday
|
|
83
|
+
// xx00xxxxb - week counter 0..3, relative to programming date
|
|
84
|
+
sleepSeconds: new storage.SiInt([[0x7d], [0x7c]]),
|
|
85
|
+
workingMinutes: new storage.SiInt([[0x7f], [0x7e]])
|
|
86
|
+
};
|
|
87
|
+
export const siStationStorageDefinition = storage.defineStorage(0x80, siStationStorageLocations);
|
|
88
|
+
// export type ISiStationStorageFields = storage.FieldsFromStorageDefinition<typeof siStationStorageDefinition>;
|
|
89
|
+
// export type ISiStationStorageFields = storage.FieldsFromStorageLocations<typeof siStationStorageLocations>;
|
|
90
|
+
|
|
91
|
+
export abstract class BaseSiStation<T extends SiTargetMultiplexerTarget> {
|
|
92
|
+
public storage: storage.ISiStorage<ISiStationStorageFields>;
|
|
93
|
+
|
|
94
|
+
protected static fromSiTargetMultiplexerWithGivenTarget<U extends SiTargetMultiplexerTarget>(
|
|
95
|
+
multiplexer: ISiTargetMultiplexer,
|
|
96
|
+
multiplexerTarget: U,
|
|
97
|
+
createNewInstance: () => ISiStation<U>
|
|
98
|
+
): ISiStation<U> {
|
|
99
|
+
const existingStationObject = multiplexer.stations[multiplexerTarget] as ISiStation<U> | undefined;
|
|
100
|
+
if (existingStationObject) {
|
|
101
|
+
return existingStationObject;
|
|
102
|
+
}
|
|
103
|
+
const instance = createNewInstance();
|
|
104
|
+
// @ts-ignore
|
|
105
|
+
multiplexer.stations[multiplexerTarget] = instance;
|
|
106
|
+
// TODO: deregister/close
|
|
107
|
+
return instance;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
constructor(public siTargetMultiplexer: ISiTargetMultiplexer, public readonly multiplexerTarget: T) {
|
|
111
|
+
this.storage = siStationStorageDefinition();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get ident(): string {
|
|
115
|
+
const multiplexerTargetString = SiTargetMultiplexerTarget[this.multiplexerTarget];
|
|
116
|
+
const deviceIdentString = this.siTargetMultiplexer.siDevice.ident;
|
|
117
|
+
return `${multiplexerTargetString}-${deviceIdentString}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
sendMessage(message: siProtocol.SiMessage, numResponses = 0, timeoutInMiliseconds = 10000): Promise<number[][]> {
|
|
121
|
+
return this.siTargetMultiplexer.sendMessage(this.multiplexerTarget, message, numResponses, timeoutInMiliseconds);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
readInfo(): Promise<void> {
|
|
125
|
+
return this.sendMessage(
|
|
126
|
+
{
|
|
127
|
+
command: proto.cmd.GET_SYS_VAL,
|
|
128
|
+
parameters: [0x00, 0x80]
|
|
129
|
+
},
|
|
130
|
+
1
|
|
131
|
+
).then((data) => {
|
|
132
|
+
this.storage.splice(0x00, 0x80, ...data[0].slice(3));
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getField<Field extends keyof ISiStationStorageFields>(infoName: Field): storage.ISiStorageLocations<ISiStationStorageFields>[Field] {
|
|
137
|
+
return this.storage.locations[infoName];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getInfo<Field extends keyof ISiStationStorageFields>(infoName: Field): storage.ISiFieldValue<ISiStationStorageFields[Field]> | undefined {
|
|
141
|
+
return this.storage.get(infoName);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
setInfo<Field extends keyof ISiStationStorageFields>(infoName: Field, newValue: storage.ISiFieldValue<ISiStationStorageFields[Field]> | ISiStationStorageFields[Field]): void {
|
|
145
|
+
this.storage.set(infoName, newValue);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
writeChanges(): Promise<void> {
|
|
149
|
+
const newStorage = this.storage.data;
|
|
150
|
+
return this.readInfo().then(() => {
|
|
151
|
+
const oldStorage = this.storage.data;
|
|
152
|
+
return this.writeDiff(oldStorage, newStorage);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
atomically(doThings: () => void): Promise<void> {
|
|
157
|
+
return this.readInfo().then(() => {
|
|
158
|
+
const oldStorage = this.storage.data;
|
|
159
|
+
doThings();
|
|
160
|
+
const newStorage = this.storage.data;
|
|
161
|
+
return this.writeDiff(oldStorage, newStorage);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
writeDiff(oldStorage: storage.ISiStorageData, newStorage: storage.ISiStorageData): Promise<void> {
|
|
166
|
+
const zippedStorageBytes = oldStorage.zip(newStorage);
|
|
167
|
+
const isByteDirty = zippedStorageBytes.map((oldAndNew: [unknown, unknown]) => oldAndNew[0] !== oldAndNew[1]);
|
|
168
|
+
const dirtyRanges = isByteDirty.reduce((ranges: [number, number][], isDirty: boolean, byteIndex: number): [number, number][] => {
|
|
169
|
+
if (!isDirty) {
|
|
170
|
+
return ranges;
|
|
171
|
+
}
|
|
172
|
+
const numRanges = ranges.length;
|
|
173
|
+
const lastRange = ranges[numRanges - 1];
|
|
174
|
+
if (lastRange && lastRange[1] === byteIndex) {
|
|
175
|
+
return [...ranges.slice(0, numRanges - 1), [lastRange[0], byteIndex + 1]];
|
|
176
|
+
}
|
|
177
|
+
return [...ranges, [byteIndex, byteIndex + 1]];
|
|
178
|
+
}, [] as [number, number][]);
|
|
179
|
+
let dirtyRangeIndex = 0;
|
|
180
|
+
const processDirtyRanges = (): Promise<void> => {
|
|
181
|
+
if (dirtyRangeIndex >= dirtyRanges.length) {
|
|
182
|
+
return Promise.resolve();
|
|
183
|
+
}
|
|
184
|
+
const dirtyRange = dirtyRanges[dirtyRangeIndex];
|
|
185
|
+
const parameters = [dirtyRange[0], ...newStorage.slice(dirtyRange[0], dirtyRange[1])] as number[];
|
|
186
|
+
return this.sendMessage(
|
|
187
|
+
{
|
|
188
|
+
command: proto.cmd.SET_SYS_VAL,
|
|
189
|
+
parameters: parameters
|
|
190
|
+
},
|
|
191
|
+
1
|
|
192
|
+
).then((d) => {
|
|
193
|
+
const data = d[0];
|
|
194
|
+
data.splice(0, 2);
|
|
195
|
+
if (data[0] !== parameters[0]) {
|
|
196
|
+
console.warn(`SET_SYS_VAL error: ${data[0]} (expected ${parameters[0]})`);
|
|
197
|
+
}
|
|
198
|
+
dirtyRangeIndex += 1;
|
|
199
|
+
return processDirtyRanges();
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
return processDirtyRanges();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
getTime(): Promise<Date | undefined> {
|
|
208
|
+
return this.sendMessage(
|
|
209
|
+
{
|
|
210
|
+
command: proto.cmd.GET_TIME,
|
|
211
|
+
parameters: []
|
|
212
|
+
},
|
|
213
|
+
1
|
|
214
|
+
).then((d) => siProtocol.arr2date(d[0].slice(2)));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
setTime(newTime: Date): Promise<Date | undefined> {
|
|
218
|
+
// TODO: compensate for waiting time
|
|
219
|
+
return this.sendMessage(
|
|
220
|
+
{
|
|
221
|
+
command: proto.cmd.SET_TIME,
|
|
222
|
+
parameters: [...siProtocol.date2arr(newTime)]
|
|
223
|
+
},
|
|
224
|
+
1
|
|
225
|
+
).then((d) => siProtocol.arr2date(d[0].slice(2)));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
signal(countArg: number): Promise<void> {
|
|
229
|
+
const count = countArg < 1 ? 1 : countArg;
|
|
230
|
+
return this.sendMessage(
|
|
231
|
+
{
|
|
232
|
+
command: proto.cmd.SIGNAL,
|
|
233
|
+
parameters: [count]
|
|
234
|
+
},
|
|
235
|
+
1
|
|
236
|
+
).then((data) => {
|
|
237
|
+
if (data[0][2] !== count) {
|
|
238
|
+
throw new Error('NUM BEEPS');
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
powerOff(): Promise<number[][]> {
|
|
244
|
+
// Does not power off BSM8 (USB powered), though
|
|
245
|
+
return this.sendMessage(
|
|
246
|
+
{
|
|
247
|
+
command: proto.cmd.OFF,
|
|
248
|
+
parameters: []
|
|
249
|
+
},
|
|
250
|
+
0
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import * as testUtils from '../testUtils';
|
|
3
|
+
import type { ISiDevice, ISiDeviceDriverData } from '../SiDevice/ISiDevice';
|
|
4
|
+
import type { ISiDeviceDriver } from '../SiDevice/ISiDeviceDriver';
|
|
5
|
+
import { SiDevice } from '../SiDevice/SiDevice';
|
|
6
|
+
import { SiTargetMultiplexerTarget } from './ISiTargetMultiplexer';
|
|
7
|
+
import { CoupledSiStation } from './CoupledSiStation';
|
|
8
|
+
import { SiTargetMultiplexer } from './SiTargetMultiplexer';
|
|
9
|
+
|
|
10
|
+
testUtils.useFakeTimers();
|
|
11
|
+
|
|
12
|
+
function mockDriver(driver: Partial<ISiDeviceDriver<ISiDeviceDriverData<unknown>>>) {
|
|
13
|
+
return driver as unknown as ISiDeviceDriver<ISiDeviceDriverData<unknown>>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('CoupledSiStation', () => {
|
|
17
|
+
test('fromSiDevice', () => {
|
|
18
|
+
const fakeSiDevice = new SiDevice('fromSiDevice', {
|
|
19
|
+
driver: mockDriver({ name: 'FakeSiDevice' })
|
|
20
|
+
});
|
|
21
|
+
const myCoupledStation1 = CoupledSiStation.fromSiDevice(fakeSiDevice);
|
|
22
|
+
expect(myCoupledStation1 instanceof CoupledSiStation).toBe(true);
|
|
23
|
+
expect(myCoupledStation1.ident).toBe('Remote-FakeSiDevice-fromSiDevice');
|
|
24
|
+
expect(myCoupledStation1.multiplexerTarget).toBe(SiTargetMultiplexerTarget.Remote);
|
|
25
|
+
const myCoupledStation2 = CoupledSiStation.fromSiDevice(fakeSiDevice);
|
|
26
|
+
expect(myCoupledStation2).toBe(myCoupledStation1);
|
|
27
|
+
expect(myCoupledStation2.ident).toBe('Remote-FakeSiDevice-fromSiDevice');
|
|
28
|
+
expect(myCoupledStation2.multiplexerTarget).toBe(SiTargetMultiplexerTarget.Remote);
|
|
29
|
+
});
|
|
30
|
+
test('fromSiTargetMultiplexer', () => {
|
|
31
|
+
const myTargetMultiplexer = new SiTargetMultiplexer({ ident: 'fake-ident' } as ISiDevice<ISiDeviceDriverData<unknown>>);
|
|
32
|
+
const myCoupledStation1 = CoupledSiStation.fromSiTargetMultiplexer(myTargetMultiplexer);
|
|
33
|
+
expect(myCoupledStation1 instanceof CoupledSiStation).toBe(true);
|
|
34
|
+
expect(myCoupledStation1.ident).toBe('Remote-fake-ident');
|
|
35
|
+
expect(myCoupledStation1.multiplexerTarget).toBe(SiTargetMultiplexerTarget.Remote);
|
|
36
|
+
const myCoupledStation2 = CoupledSiStation.fromSiTargetMultiplexer(myTargetMultiplexer);
|
|
37
|
+
expect(myCoupledStation2).toBe(myCoupledStation1);
|
|
38
|
+
expect(myCoupledStation2.ident).toBe('Remote-fake-ident');
|
|
39
|
+
expect(myCoupledStation2.multiplexerTarget).toBe(SiTargetMultiplexerTarget.Remote);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { ISiDevice, ISiDeviceDriverData } from '../SiDevice/ISiDevice';
|
|
2
|
+
import type { ISiStation } from './ISiStation';
|
|
3
|
+
import { type ISiTargetMultiplexer, SiTargetMultiplexerTarget } from './ISiTargetMultiplexer';
|
|
4
|
+
import { BaseSiStation } from './BaseSiStation';
|
|
5
|
+
import { SiTargetMultiplexer } from './SiTargetMultiplexer';
|
|
6
|
+
import { proto } from '../constants';
|
|
7
|
+
import * as siProtocol from '../siProtocol';
|
|
8
|
+
|
|
9
|
+
export class CoupledSiStation extends BaseSiStation<SiTargetMultiplexerTarget.Remote> implements ISiStation<SiTargetMultiplexerTarget.Remote> {
|
|
10
|
+
static fromSiDevice(siDevice: ISiDevice<ISiDeviceDriverData<unknown>>): CoupledSiStation {
|
|
11
|
+
const multiplexer = SiTargetMultiplexer.fromSiDevice(siDevice);
|
|
12
|
+
return this.fromSiTargetMultiplexer(multiplexer);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static fromSiTargetMultiplexer(multiplexer: ISiTargetMultiplexer): CoupledSiStation {
|
|
16
|
+
return this.fromSiTargetMultiplexerWithGivenTarget(multiplexer, SiTargetMultiplexerTarget.Remote, () => new this(multiplexer, SiTargetMultiplexerTarget.Remote)) as CoupledSiStation;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getBackupData(turnOff:boolean=true): Promise<{ code: number, cardNumber: number|undefined ; date: Date | undefined}[]> {
|
|
20
|
+
let backupData: { code: number, cardNumber: number|undefined ; date: Date | undefined}[] = []
|
|
21
|
+
let backupNextWritePointer:number = 0
|
|
22
|
+
|
|
23
|
+
// Get backup pointer
|
|
24
|
+
for(let tries = 0; tries<10 && (backupNextWritePointer == 0 || backupNextWritePointer == undefined); tries++){
|
|
25
|
+
try{
|
|
26
|
+
await this.siTargetMultiplexer.sendMessage(
|
|
27
|
+
SiTargetMultiplexerTarget.Remote,
|
|
28
|
+
{
|
|
29
|
+
command: proto.cmd.GET_SYS_VAL,
|
|
30
|
+
parameters: [0x1c,0x07]
|
|
31
|
+
},
|
|
32
|
+
1, 10000).then((d) => {
|
|
33
|
+
backupNextWritePointer = (d[0][3]<<24) | (d[0][4]<<16) | (d[0][8]<<8) | d[0][9]
|
|
34
|
+
}).catch(async (e) => {
|
|
35
|
+
await this.siTargetMultiplexer.sendMessage(
|
|
36
|
+
SiTargetMultiplexerTarget.Remote,
|
|
37
|
+
{
|
|
38
|
+
command: proto.WAKEUP,
|
|
39
|
+
parameters: []
|
|
40
|
+
},
|
|
41
|
+
0, 10000)
|
|
42
|
+
await new Promise( resolve => setTimeout(resolve, 500) );
|
|
43
|
+
})
|
|
44
|
+
}catch (e){ // ignore, try again
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if(backupNextWritePointer == 0 || backupNextWritePointer == undefined){
|
|
48
|
+
return Promise.reject(new Error("Unable to access coupled si station!"))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
let backupReadLocation = 0x0100
|
|
53
|
+
let backupMaxLocation = 0x200000
|
|
54
|
+
let backupStorageSize = 128
|
|
55
|
+
while(backupReadLocation < backupNextWritePointer && backupReadLocation < backupMaxLocation){
|
|
56
|
+
try{
|
|
57
|
+
const actualReadLength = Math.min(backupStorageSize,backupNextWritePointer-backupReadLocation)
|
|
58
|
+
await this.siTargetMultiplexer.sendMessage(
|
|
59
|
+
SiTargetMultiplexerTarget.Remote,
|
|
60
|
+
{
|
|
61
|
+
command: proto.cmd.GET_BACKUP,
|
|
62
|
+
parameters: [(backupReadLocation>>16)&0xff,(backupReadLocation>>8)&0xff,backupReadLocation&0xff,actualReadLength]
|
|
63
|
+
},
|
|
64
|
+
1, 10000)
|
|
65
|
+
.then((d) => {
|
|
66
|
+
let cn = (d[0][0]<<8)|d[0][1]
|
|
67
|
+
let addr = (d[0][2]<<16)|(d[0][3]<<8)|d[0][4]
|
|
68
|
+
let p = 5
|
|
69
|
+
while (p<d[0].length){
|
|
70
|
+
let sicard = siProtocol.arr2cardNumber([d[0][p+2],d[0][p+1],d[0][p+0]])
|
|
71
|
+
let datedata = [
|
|
72
|
+
d[0][p+3]>>2, // Year = bit 7-2
|
|
73
|
+
(((d[0][p+3]&0x3)<<2)|((d[0][p+4]>>6)&0x3)), // Month = bit 1-0 and 7-6
|
|
74
|
+
((d[0][p+4]>>1)&0x1F), // day = bit 5-1
|
|
75
|
+
(d[0][p+4]&0x1), // am/pm halfday = 0 bit
|
|
76
|
+
d[0][p+5],
|
|
77
|
+
d[0][p+6],
|
|
78
|
+
d[0][p+7]
|
|
79
|
+
]
|
|
80
|
+
let date = siProtocol.arr2date(datedata)
|
|
81
|
+
if(addr+p<=backupNextWritePointer){
|
|
82
|
+
backupData.push({code:cn,cardNumber:sicard,date:date})
|
|
83
|
+
}else{
|
|
84
|
+
break
|
|
85
|
+
}
|
|
86
|
+
p+=proto.REC_LEN
|
|
87
|
+
}
|
|
88
|
+
backupReadLocation += actualReadLength
|
|
89
|
+
})
|
|
90
|
+
}catch(e){
|
|
91
|
+
if(backupStorageSize >= 2*proto.REC_LEN){
|
|
92
|
+
backupStorageSize/=2
|
|
93
|
+
console.log("reduce requested backup block size to", backupStorageSize)
|
|
94
|
+
}else{
|
|
95
|
+
return Promise.reject("Unable to read backup data")
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Confirm read end with signal
|
|
102
|
+
await this.siTargetMultiplexer.sendMessage(
|
|
103
|
+
SiTargetMultiplexerTarget.Remote,
|
|
104
|
+
{
|
|
105
|
+
command: proto.cmd.SIGNAL,
|
|
106
|
+
parameters: [0x2]
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
// Turn of unit if requested
|
|
110
|
+
if(turnOff){
|
|
111
|
+
await new Promise( resolve => setTimeout(resolve, 250) );
|
|
112
|
+
for(let tries = 0, hasTurnedOff=false; tries<5&&!hasTurnedOff; tries++){
|
|
113
|
+
await this.siTargetMultiplexer.sendMessage(
|
|
114
|
+
SiTargetMultiplexerTarget.Remote,
|
|
115
|
+
{
|
|
116
|
+
command: proto.cmd.OFF,
|
|
117
|
+
parameters: []
|
|
118
|
+
}
|
|
119
|
+
,1,10000
|
|
120
|
+
).then(()=>{
|
|
121
|
+
hasTurnedOff = true
|
|
122
|
+
}).catch(e=>{
|
|
123
|
+
hasTurnedOff = false
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return backupData
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ISiCard } from '../SiCard/ISiCard';
|
|
2
|
+
import * as utils from '../utils';
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
5
|
+
export interface ISiMainStation extends utils.IEventTarget<SiMainStationEvents> {}
|
|
6
|
+
|
|
7
|
+
export class SiMainStationSiCardInsertedEvent extends utils.Event<'siCardInserted'> {
|
|
8
|
+
constructor(public siMainStation: ISiMainStation, public siCard: ISiCard) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class SiMainStationSiCardRemovedEvent extends utils.Event<'siCardRemoved'> {
|
|
14
|
+
constructor(public siMainStation: ISiMainStation, public siCard: ISiCard) {
|
|
15
|
+
super();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class SiMainStationSiCardObservedEvent extends utils.Event<'siCardObserved'> {
|
|
20
|
+
constructor(public siMainStation: ISiMainStation, public siCard: ISiCard) {
|
|
21
|
+
super();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type SiMainStationEvents = {
|
|
26
|
+
siCardInserted: SiMainStationSiCardInsertedEvent;
|
|
27
|
+
siCardRemoved: SiMainStationSiCardRemovedEvent;
|
|
28
|
+
siCardObserved: SiMainStationSiCardObservedEvent;
|
|
29
|
+
};
|