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.
Files changed (151) hide show
  1. package/.dependency-cruiser.js +233 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintignore +6 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  6. package/.github/workflows/npm.yml +17 -0
  7. package/LICENSE +21 -0
  8. package/README.md +31 -0
  9. package/babel.config.js +21 -0
  10. package/build-docs.sh +25 -0
  11. package/docs/index.md +9 -0
  12. package/docs/typedoc/index.md +11 -0
  13. package/eslint.base.js +232 -0
  14. package/install.sh +6 -0
  15. package/jest.config.ts +49 -0
  16. package/nx.json +39 -0
  17. package/package.json +51 -0
  18. package/src/SiCard/BaseSiCard.test.ts +187 -0
  19. package/src/SiCard/BaseSiCard.ts +101 -0
  20. package/src/SiCard/IRaceResultData.ts +16 -0
  21. package/src/SiCard/ISiCard.ts +23 -0
  22. package/src/SiCard/ISiCardExamples.ts +4 -0
  23. package/src/SiCard/index.ts +2 -0
  24. package/src/SiCard/raceResultTools.test.ts +260 -0
  25. package/src/SiCard/raceResultTools.ts +150 -0
  26. package/src/SiCard/types/FCard.test.ts +19 -0
  27. package/src/SiCard/types/FCard.ts +14 -0
  28. package/src/SiCard/types/ModernSiCard.test.ts +186 -0
  29. package/src/SiCard/types/ModernSiCard.ts +241 -0
  30. package/src/SiCard/types/PCard.test.ts +19 -0
  31. package/src/SiCard/types/PCard.ts +14 -0
  32. package/src/SiCard/types/SIAC.test.ts +84 -0
  33. package/src/SiCard/types/SIAC.ts +19 -0
  34. package/src/SiCard/types/SiCard10.test.ts +85 -0
  35. package/src/SiCard/types/SiCard10.ts +17 -0
  36. package/src/SiCard/types/SiCard11.test.ts +84 -0
  37. package/src/SiCard/types/SiCard11.ts +19 -0
  38. package/src/SiCard/types/SiCard5.test.ts +149 -0
  39. package/src/SiCard/types/SiCard5.ts +129 -0
  40. package/src/SiCard/types/SiCard6.test.ts +179 -0
  41. package/src/SiCard/types/SiCard6.ts +222 -0
  42. package/src/SiCard/types/SiCard8.test.ts +137 -0
  43. package/src/SiCard/types/SiCard8.ts +129 -0
  44. package/src/SiCard/types/SiCard9.test.ts +132 -0
  45. package/src/SiCard/types/SiCard9.ts +128 -0
  46. package/src/SiCard/types/TCard.test.ts +19 -0
  47. package/src/SiCard/types/TCard.ts +14 -0
  48. package/src/SiCard/types/index.test.ts +26 -0
  49. package/src/SiCard/types/index.ts +15 -0
  50. package/src/SiCard/types/modernSiCardExamples.ts +364 -0
  51. package/src/SiCard/types/siCard5Examples.ts +73 -0
  52. package/src/SiCard/types/siCard6Examples.ts +262 -0
  53. package/src/SiCard/types/siCard8Examples.ts +152 -0
  54. package/src/SiCard/types/siCard9Examples.ts +143 -0
  55. package/src/SiDevice/INavigatorWebSerial.ts +78 -0
  56. package/src/SiDevice/INavigatorWebUsb.ts +62 -0
  57. package/src/SiDevice/ISiDevice.ts +48 -0
  58. package/src/SiDevice/ISiDeviceDriver.ts +35 -0
  59. package/src/SiDevice/README.md +13 -0
  60. package/src/SiDevice/SiDevice.test.ts +354 -0
  61. package/src/SiDevice/SiDevice.ts +132 -0
  62. package/src/SiDevice/WebSerialSiDeviceDriver.ts +146 -0
  63. package/src/SiDevice/WebUsbSiDeviceDriver.ts +343 -0
  64. package/src/SiDevice/index.ts +3 -0
  65. package/src/SiDevice/testUtils/index.ts +2 -0
  66. package/src/SiDevice/testUtils/testISiDeviceDriver.ts +63 -0
  67. package/src/SiDevice/testUtils/testISiDeviceDriverWithAutodetection.ts +72 -0
  68. package/src/SiStation/BaseSiStation.test.ts +221 -0
  69. package/src/SiStation/BaseSiStation.ts +253 -0
  70. package/src/SiStation/CoupledSiStation.test.ts +41 -0
  71. package/src/SiStation/CoupledSiStation.ts +130 -0
  72. package/src/SiStation/ISiMainStation.ts +29 -0
  73. package/src/SiStation/ISiSendTask.ts +9 -0
  74. package/src/SiStation/ISiStation.ts +88 -0
  75. package/src/SiStation/ISiTargetMultiplexer.ts +51 -0
  76. package/src/SiStation/SiMainStation.test.ts +222 -0
  77. package/src/SiStation/SiMainStation.ts +133 -0
  78. package/src/SiStation/SiSendTask.ts +50 -0
  79. package/src/SiStation/SiTargetMultiplexer.targeting.test.ts +112 -0
  80. package/src/SiStation/SiTargetMultiplexer.test.ts +605 -0
  81. package/src/SiStation/SiTargetMultiplexer.ts +241 -0
  82. package/src/SiStation/index.ts +5 -0
  83. package/src/SiStation/siStationExamples.ts +103 -0
  84. package/src/constants.test.ts +17 -0
  85. package/src/constants.ts +92 -0
  86. package/src/fakes/FakeSiCard/BaseFakeSiCard.test.ts +11 -0
  87. package/src/fakes/FakeSiCard/BaseFakeSiCard.ts +10 -0
  88. package/src/fakes/FakeSiCard/IFakeSiCard.ts +6 -0
  89. package/src/fakes/FakeSiCard/index.ts +2 -0
  90. package/src/fakes/FakeSiCard/types/FakeModernSiCard.test.ts +62 -0
  91. package/src/fakes/FakeSiCard/types/FakeModernSiCard.ts +43 -0
  92. package/src/fakes/FakeSiCard/types/FakeSIAC.ts +17 -0
  93. package/src/fakes/FakeSiCard/types/FakeSiCard10.ts +17 -0
  94. package/src/fakes/FakeSiCard/types/FakeSiCard11.ts +17 -0
  95. package/src/fakes/FakeSiCard/types/FakeSiCard5.test.ts +42 -0
  96. package/src/fakes/FakeSiCard/types/FakeSiCard5.ts +40 -0
  97. package/src/fakes/FakeSiCard/types/FakeSiCard6.test.ts +62 -0
  98. package/src/fakes/FakeSiCard/types/FakeSiCard6.ts +44 -0
  99. package/src/fakes/FakeSiCard/types/FakeSiCard8.ts +16 -0
  100. package/src/fakes/FakeSiCard/types/FakeSiCard9.ts +16 -0
  101. package/src/fakes/FakeSiCard/types/index.ts +7 -0
  102. package/src/fakes/FakeSiDeviceDriver.ts +148 -0
  103. package/src/fakes/FakeSiMainStation.test.ts +141 -0
  104. package/src/fakes/FakeSiMainStation.ts +118 -0
  105. package/src/fakes/IFakeSiMainStation.ts +15 -0
  106. package/src/fakes/index.ts +2 -0
  107. package/src/index.ts +24 -0
  108. package/src/siProtocol.test.ts +509 -0
  109. package/src/siProtocol.ts +417 -0
  110. package/src/storage/SiArray.test.ts +103 -0
  111. package/src/storage/SiArray.ts +56 -0
  112. package/src/storage/SiBool.test.ts +81 -0
  113. package/src/storage/SiBool.ts +47 -0
  114. package/src/storage/SiDataType.test.ts +81 -0
  115. package/src/storage/SiDataType.ts +60 -0
  116. package/src/storage/SiDict.test.ts +115 -0
  117. package/src/storage/SiDict.ts +60 -0
  118. package/src/storage/SiEnum.test.ts +77 -0
  119. package/src/storage/SiEnum.ts +48 -0
  120. package/src/storage/SiFieldValue.test.ts +58 -0
  121. package/src/storage/SiFieldValue.ts +23 -0
  122. package/src/storage/SiInt.test.ts +80 -0
  123. package/src/storage/SiInt.ts +84 -0
  124. package/src/storage/SiModified.test.ts +135 -0
  125. package/src/storage/SiModified.ts +59 -0
  126. package/src/storage/SiStorage.test.ts +51 -0
  127. package/src/storage/SiStorage.ts +44 -0
  128. package/src/storage/index.test.ts +222 -0
  129. package/src/storage/index.ts +12 -0
  130. package/src/storage/interfaces.ts +41 -0
  131. package/src/storage/siStringEncoding.ts +1361 -0
  132. package/src/testUtils.test.ts +266 -0
  133. package/src/testUtils.ts +75 -0
  134. package/src/utils/NumberRange.test.ts +66 -0
  135. package/src/utils/NumberRange.ts +46 -0
  136. package/src/utils/NumberRangeRegistry.test.ts +49 -0
  137. package/src/utils/NumberRangeRegistry.ts +43 -0
  138. package/src/utils/bytes.test.ts +126 -0
  139. package/src/utils/bytes.ts +69 -0
  140. package/src/utils/errors.test.ts +29 -0
  141. package/src/utils/errors.ts +20 -0
  142. package/src/utils/events.test.ts +112 -0
  143. package/src/utils/events.ts +68 -0
  144. package/src/utils/general.test.ts +139 -0
  145. package/src/utils/general.ts +69 -0
  146. package/src/utils/index.ts +8 -0
  147. package/src/utils/mixins.test.ts +40 -0
  148. package/src/utils/mixins.ts +13 -0
  149. package/src/utils/typed.ts +3 -0
  150. package/tsconfig.base.json +22 -0
  151. package/tsconfig.json +15 -0
@@ -0,0 +1,241 @@
1
+ import { proto } from '../constants';
2
+ import * as utils from '../utils';
3
+ import * as siProtocol from '../siProtocol';
4
+ import type { ISiStation } from './ISiStation';
5
+ import {
6
+ type _ISiTargetMultiplexerTestData,
7
+ type ISiTargetMultiplexer,
8
+ SiTargetMultiplexerDirectMessageEvent,
9
+ type SiTargetMultiplexerEvents,
10
+ SiTargetMultiplexerMessageEvent,
11
+ SiTargetMultiplexerRemoteMessageEvent,
12
+ SiTargetMultiplexerTarget
13
+ } from './ISiTargetMultiplexer';
14
+ import { type ISiDevice, type ISiDeviceDriverData, SiDeviceState } from '../SiDevice/ISiDevice';
15
+ import { SiSendTaskState } from './ISiSendTask';
16
+ import { SiSendTask } from './SiSendTask';
17
+
18
+ /** Commands that can only be sent from a direct station. */
19
+ export const DIRECT_DEVICE_INITIATED_COMMANDS: { [command: number]: boolean } = {
20
+ [proto.cmd.TRANS_REC]: true,
21
+ [proto.cmd.SI5_DET]: true,
22
+ [proto.cmd.SI6_DET]: true,
23
+ [proto.cmd.SI8_DET]: true,
24
+ [proto.cmd.SI_REM]: true
25
+ };
26
+
27
+ /** Commands that are sent from a station without prior request. */
28
+ const DEVICE_INITIATED_COMMANDS: { [command: number]: boolean } = {
29
+ ...DIRECT_DEVICE_INITIATED_COMMANDS,
30
+ [proto.cmd.SRR_PING]: true
31
+ };
32
+
33
+ export class SiTargetMultiplexer implements ISiTargetMultiplexer {
34
+ static fromSiDevice(siDevice: ISiDevice<ISiDeviceDriverData<unknown>>): ISiTargetMultiplexer {
35
+ if (siDevice.siTargetMultiplexer) {
36
+ return siDevice.siTargetMultiplexer;
37
+ }
38
+ const instance = new this(siDevice);
39
+ siDevice.siTargetMultiplexer = instance;
40
+ siDevice.addEventListener('stateChange', (e) => {
41
+ instance.handleDeviceStateChange(e.state);
42
+ });
43
+ siDevice.addEventListener('receive', (e) => {
44
+ instance.handleReceive(e.uint8Data);
45
+ });
46
+ // TODO: deregister/close
47
+ return instance;
48
+ }
49
+
50
+ public stations: { [Target in SiTargetMultiplexerTarget]?: ISiStation<Target> } = {};
51
+ public target: SiTargetMultiplexerTarget = SiTargetMultiplexerTarget.Unknown;
52
+ // the target of the latest command scheduled
53
+ public latestTarget: SiTargetMultiplexerTarget = SiTargetMultiplexerTarget.Unknown;
54
+ private receiveBuffer: number[] = [];
55
+ private sendQueue: SiSendTask[] = [];
56
+
57
+ // eslint-disable-next-line no-useless-constructor
58
+ constructor(
59
+ public siDevice: ISiDevice<ISiDeviceDriverData<unknown>> // eslint-disable-next-line no-empty-function
60
+ ) {}
61
+
62
+ get _test(): _ISiTargetMultiplexerTestData {
63
+ return {
64
+ latestTarget: this.latestTarget,
65
+ sendQueue: this.sendQueue
66
+ };
67
+ }
68
+
69
+ handleDeviceStateChange(newState: SiDeviceState): void {
70
+ const actionByNewState: { [state in SiDeviceState]: () => void } = {
71
+ [SiDeviceState.Opening]: () => undefined,
72
+ [SiDeviceState.Opened]: () => this.startProcessingSendQueue(),
73
+ [SiDeviceState.Closing]: () => this.abortProcessingSendQueue(),
74
+ [SiDeviceState.Closed]: () => this.abortProcessingSendQueue()
75
+ };
76
+ const actionToPerform = actionByNewState[newState];
77
+ if (actionToPerform) {
78
+ actionToPerform();
79
+ }
80
+ }
81
+
82
+ startProcessingSendQueue(): void {
83
+ setTimeout(() => this._processSendQueue(), 1);
84
+ }
85
+
86
+ abortProcessingSendQueue(): void {
87
+ this.sendQueue.forEach((sendTask) => {
88
+ sendTask.fail();
89
+ });
90
+ }
91
+
92
+ handleReceive(uint8Data: number[]): void {
93
+ this.receiveBuffer.push(...uint8Data);
94
+ const { messages, remainder } = siProtocol.parseAll(this.receiveBuffer);
95
+ this.receiveBuffer = remainder;
96
+ messages.forEach((message) => {
97
+ this.updateSendQueueWithReceivedMessage(message);
98
+ this.dispatchEvent('message', new SiTargetMultiplexerMessageEvent(this, message));
99
+ if (this.target === SiTargetMultiplexerTarget.Direct) {
100
+ this.dispatchEvent('directMessage', new SiTargetMultiplexerDirectMessageEvent(this, message));
101
+ }
102
+ if (this.target === SiTargetMultiplexerTarget.Remote) {
103
+ this.dispatchEvent('remoteMessage', new SiTargetMultiplexerRemoteMessageEvent(this, message));
104
+ }
105
+ });
106
+ }
107
+
108
+ updateSendQueueWithReceivedMessage(message: siProtocol.SiMessage): void {
109
+ if (message.mode === undefined && DIRECT_DEVICE_INITIATED_COMMANDS[message.command]) {
110
+ console.debug('Received direct device-initiated command. Assuming target Direct...');
111
+ this.target = SiTargetMultiplexerTarget.Direct;
112
+ this.latestTarget = SiTargetMultiplexerTarget.Direct;
113
+ }
114
+ if (this.sendQueue.length === 0) {
115
+ return;
116
+ }
117
+ const shouldAcceptResponseInState: { [state in SiSendTaskState]: boolean } = {
118
+ [SiSendTaskState.Queued]: false,
119
+ [SiSendTaskState.Sending]: true, // This is mostly for testing purposes
120
+ [SiSendTaskState.Sent]: true,
121
+ [SiSendTaskState.Succeeded]: false,
122
+ [SiSendTaskState.Failed]: false
123
+ };
124
+ if (!shouldAcceptResponseInState[this.sendQueue[0].state]) {
125
+ return;
126
+ }
127
+ if (message.mode === proto.NAK) {
128
+ this.sendQueue[0].fail();
129
+ return;
130
+ }
131
+ if (message.mode !== undefined) {
132
+ return;
133
+ }
134
+ const expectedMessage = this.sendQueue[0].message;
135
+ if (expectedMessage.mode !== undefined) {
136
+ return;
137
+ }
138
+ if (message.command !== expectedMessage.command) {
139
+ if (DEVICE_INITIATED_COMMANDS[message.command] !== true) {
140
+ console.warn(`Strange Response: expected ${utils.prettyHex([expectedMessage.command])}, but got ${utils.prettyHex([message.command])}...`);
141
+ }
142
+ return;
143
+ }
144
+ this.sendQueue[0].addResponse(message.parameters);
145
+ }
146
+
147
+ async sendMessage(target: SiTargetMultiplexerTarget, message: siProtocol.SiMessage, numResponses?: number, timeoutInMiliseconds?: number): Promise<number[][]> {
148
+ console.warn("sendMessage to ",target)
149
+ return this.setTarget(target).then(() => this.sendMessageToLatestTarget(message, numResponses, timeoutInMiliseconds)).catch(e=>{console.warn(e); return Promise.reject(e)})
150
+ }
151
+
152
+ async setTarget(target: SiTargetMultiplexerTarget): Promise<void> {
153
+ console.warn("set Target to ",target, "last is ",this.latestTarget)
154
+ if (target === this.latestTarget) {
155
+ return Promise.resolve();
156
+ }
157
+ const setMsParameterByTarget: { [Target in SiTargetMultiplexerTarget]: number | undefined } = {
158
+ [SiTargetMultiplexerTarget.Direct]: proto.P_MS_DIRECT,
159
+ [SiTargetMultiplexerTarget.Remote]: proto.P_MS_REMOTE,
160
+ [SiTargetMultiplexerTarget.Unknown]: undefined,
161
+ [SiTargetMultiplexerTarget.Switching]: undefined
162
+ };
163
+ const setMsParameter = setMsParameterByTarget[target];
164
+ if (setMsParameter == undefined) {
165
+ console.warn("target is undefined, raise error")
166
+ return Promise.reject(new Error(`No such target: ${target}`));
167
+ }
168
+ this.latestTarget = target;
169
+ return this.sendMessageToLatestTarget(
170
+ {
171
+ command: proto.cmd.SET_MS,
172
+ parameters: [setMsParameter]
173
+ },
174
+ 1
175
+ )
176
+ .then((responses: number[][]) => {
177
+ if (responses[0][2] !== setMsParameter) {
178
+ throw new Error('contradicting SET_MS response');
179
+ }
180
+ this.target = target;
181
+ })
182
+ .catch((err) => {
183
+ this.abortProcessingSendQueue();
184
+ this.target = SiTargetMultiplexerTarget.Unknown;
185
+ throw err;
186
+ });
187
+ }
188
+
189
+ sendMessageToLatestTarget(message: siProtocol.SiMessage, numResponses = 0, timeoutInMiliseconds = 10000): Promise<number[][]> {
190
+ return new Promise((resolve, reject) => {
191
+ const sendTask = new SiSendTask(
192
+ message,
193
+ numResponses,
194
+ timeoutInMiliseconds,
195
+ (resolvedTask) => {
196
+ const shifted = this.sendQueue.shift();
197
+ if (resolvedTask !== shifted) {
198
+ throw new Error('Resolved unexecuting SendTask');
199
+ }
200
+ setTimeout(() => this._processSendQueue(), 1);
201
+ resolve(resolvedTask.responses);
202
+ },
203
+ (rejectedTask) => {
204
+ const shifted = this.sendQueue.shift();
205
+ if (rejectedTask !== shifted) {
206
+ throw new Error('Rejected unexecuting SendTask');
207
+ }
208
+ setTimeout(() => this._processSendQueue(), 1);
209
+ reject(new Error('Rejection'));
210
+ }
211
+ );
212
+ this.sendQueue.push(sendTask);
213
+ this._processSendQueue();
214
+ });
215
+ }
216
+
217
+ _processSendQueue(): void {
218
+ if (this.sendQueue.length === 0 || this.sendQueue[0].state === SiSendTaskState.Sending || this.sendQueue[0].state === SiSendTaskState.Sent || this.siDevice.state !== SiDeviceState.Opened) {
219
+ return;
220
+ }
221
+
222
+ const sendTask = this.sendQueue[0];
223
+ sendTask.state = SiSendTaskState.Sending;
224
+ const uint8Data = [proto.WAKEUP, ...siProtocol.render(sendTask.message)];
225
+ this.siDevice
226
+ .send(uint8Data)
227
+ .then(() => {
228
+ sendTask.state = SiSendTaskState.Sent;
229
+ if (sendTask.numResponses <= 0) {
230
+ sendTask.succeed();
231
+ }
232
+ })
233
+ .catch((err: utils.SiError) => {
234
+ console.warn(`Error sending: ${err}`);
235
+ sendTask.fail();
236
+ });
237
+ }
238
+ }
239
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
240
+ export interface SiTargetMultiplexer extends utils.EventTarget<SiTargetMultiplexerEvents> {}
241
+ utils.applyMixins(SiTargetMultiplexer, [utils.EventTarget]);
@@ -0,0 +1,5 @@
1
+ export { SiTargetMultiplexer } from './SiTargetMultiplexer';
2
+ export { BaseSiStation } from './BaseSiStation';
3
+ export { SiMainStation } from './SiMainStation';
4
+ export { CoupledSiStation } from './CoupledSiStation';
5
+ export { getSiStationExamples } from './siStationExamples';
@@ -0,0 +1,103 @@
1
+ import * as utils from '../utils';
2
+
3
+ const cache = {};
4
+
5
+ export interface StationSample {
6
+ stationData: { [attr: string]: unknown };
7
+ storageData: (number | undefined)[];
8
+ }
9
+
10
+ export const getBSM8Station = utils.cached(cache, () => ({
11
+ stationData: {
12
+ autoOffTimeout: 60,
13
+ autoReadout: false,
14
+ autoSend: false,
15
+ backupPointer: 256,
16
+ batteryCapacity: 14062,
17
+ batteryDate: new Date('2014-06-11 00:00:00'),
18
+ beeps: false,
19
+ buildDate: new Date('2014-06-11 00:00:00'),
20
+ code: 31,
21
+ deviceModel: 'BSM8',
22
+ extendedProtocol: true,
23
+ firmwareVersion: 3552567,
24
+ flashes: true,
25
+ handshake: true,
26
+ interval: 2621,
27
+ lastWriteDate: new Date('2019-06-20 23:17:13'),
28
+ memoryOverflow: 0,
29
+ memorySize: 128,
30
+ mode: 'Readout',
31
+ passwordOnly: false,
32
+ powerMode: 8,
33
+ program: 48,
34
+ refreshRate: 75,
35
+ serialNumber: 180641,
36
+ siCard6Mode: 193,
37
+ sprint4ms: false,
38
+ stopOnFullBackup: false,
39
+ wtf: 32760
40
+ },
41
+ storageData: [
42
+ ...utils.unPrettyHex(`
43
+ 00 02 C1 A1 F7 36 35 37 0E 06 0B 91 98 80 20 C0
44
+ 4B 08 4E FA 28 0E 06 0B 00 36 EE 80 00 00 18 04
45
+ FF 01 00 00 00 00 00 00 00 00 00 00 4D 70 FF FF
46
+ FF 00 00 C1 00 00 00 0B 00 00 00 00 FF 00 FB E5
47
+ 00 24 FC 18 FF FF 19 99 0A 3D 7F F8 85 0C 05 01
48
+ 00 00 00 00 FF FF FF FF 00 00 01 0C FF FF FF FF
49
+ 30 30 30 35 7D 20 38 00 00 00 00 00 FF FF FF FF
50
+ 30 05 1F 33 05 13 06 14 01 9E B9 00 0E 12 00 3C
51
+ `)
52
+ ]
53
+ }));
54
+
55
+ export const getBSM7Station = utils.cached(cache, () => ({
56
+ stationData: {
57
+ autoOffTimeout: 120,
58
+ autoReadout: false,
59
+ autoSend: false,
60
+ backupPointer: 1032,
61
+ batteryCapacity: 28125,
62
+ batteryDate: new Date('2016-08-04T00:00:00'),
63
+ beeps: true,
64
+ buildDate: new Date('2009-11-31 00:00:00'),
65
+ code: 10,
66
+ deviceModel: 'BSM7',
67
+ extendedProtocol: true,
68
+ firmwareVersion: 3551795,
69
+ flashes: true,
70
+ handshake: true,
71
+ interval: 1310,
72
+ lastWriteDate: new Date('2009-01-01T00:51:55'),
73
+ memoryOverflow: 0,
74
+ memorySize: 128,
75
+ mode: 'Readout',
76
+ passwordOnly: false,
77
+ powerMode: 8,
78
+ program: 36,
79
+ refreshRate: 75,
80
+ serialNumber: 130134,
81
+ siCard6Mode: 0,
82
+ sprint4ms: false,
83
+ stopOnFullBackup: false,
84
+ wtf: 32760
85
+ },
86
+ storageData: [
87
+ ...utils.unPrettyHex(`
88
+ 00 01 FC 56 F7 36 32 33 09 0C 01 91 97 80 20 DC
89
+ 4B 08 4E FA 28 10 08 04 00 6D DD 00 00 00 18 04
90
+ 00 04 08 00 00 00 00 00 00 00 00 00 4D 70 FF FF
91
+ 01 0D 90 00 00 0C 17 28 00 00 00 00 00 00 F8 FF
92
+ 00 24 02 E0 FF 38 19 99 05 1E 7F F8 85 0C 01 01
93
+ 85 0C 6B 98 FF FF FF FF FF FF FF FF FF FF FF FF
94
+ 30 30 30 35 7D 20 38 00 00 00 00 00 FF FF FF FF
95
+ 24 05 0A 35 05 09 01 01 00 0C 2B 00 1C 22 00 78
96
+ `)
97
+ ]
98
+ }));
99
+
100
+ export const getSiStationExamples = (): { [name: string]: StationSample } => ({
101
+ BSM8Station: getBSM8Station(),
102
+ BSM7Station: getBSM7Station()
103
+ });
@@ -0,0 +1,17 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import * as constants from './constants';
3
+
4
+ describe('constants', () => {
5
+ describe('proto', () => {
6
+ test('basicCmd', () => {
7
+ expect('basicCmd' in constants.proto).toBe(true);
8
+ expect('basicCmdLookup' in constants.proto).toBe(true);
9
+ expect(constants.proto.basicCmdLookup).not.toBe(undefined);
10
+ });
11
+ test('cmd', () => {
12
+ expect('cmd' in constants.proto).toBe(true);
13
+ expect('cmdLookup' in constants.proto).toBe(true);
14
+ expect(constants.proto.cmdLookup).not.toBe(undefined);
15
+ });
16
+ });
17
+ });
@@ -0,0 +1,92 @@
1
+ import { getLookup } from './utils';
2
+
3
+ export const proto = {
4
+ // Protocol characters
5
+ STX: 0x02,
6
+ ETX: 0x03,
7
+ ACK: 0x06, // when sent to BSx3..6, causes beep until SI-card taken out
8
+ NAK: 0x15,
9
+ DLE: 0x10,
10
+ WAKEUP: 0xff,
11
+
12
+ // Basic protocol commands, currently unused
13
+ basicCmd: {
14
+ SET_CARDNO: 0x30,
15
+ GET_SI5: 0x31, // read out SI-card 5 data
16
+ SI5_WRITE: 0x43, // 02 43 (block: 0x30 to 0x37) (16 bytes) 03
17
+ SI5_DET: 0x46, // SI-card 5 inserted (46 49) or removed (46 4F)
18
+ TRANS_REC: 0x53, // autosend timestamp (online control)
19
+ TRANS_TIME: 0x54, // autosend timestamp (lightbeam trigger)
20
+ GET_SI6: 0x61, // read out SI-card 6 data
21
+ SI6_DET: 0x66, // SI-card 6 inserted
22
+ SET_MS: 0x70, // \x4D="M"aster, \x53="S"lave
23
+ GET_MS: 0x71,
24
+ SET_SYS_VAL: 0x72,
25
+ GET_SYS_VAL: 0x73,
26
+ GET_BDATA: 0x74, // Note: response carries '\xC4'!
27
+ ERASE_BDATA: 0x75,
28
+ SET_TIME: 0x76,
29
+ GET_TIME: 0x77,
30
+ OFF: 0x78,
31
+ GET_BDATA2: 0x7a, // Note: response carries '\xCA'!
32
+ SET_BAUD: 0x7e // 0=4800 baud, 1=38400 baud
33
+ },
34
+ get basicCmdLookup(): { [value: number]: string } {
35
+ return getLookup(proto.basicCmd);
36
+ },
37
+
38
+ // Extended protocol commands
39
+ cmd: {
40
+ GET_BACKUP: 0x81,
41
+ SET_SYS_VAL: 0x82,
42
+ GET_SYS_VAL: 0x83,
43
+ SRR_WRITE: 0xa2, // ShortRangeRadio - SysData write
44
+ SRR_READ: 0xa3, // ShortRangeRadio - SysData read
45
+ SRR_QUERY: 0xa6, // ShortRangeRadio - network device query
46
+ SRR_PING: 0xa7, // ShortRangeRadio - heartbeat from linked devices, every 50 seconds
47
+ SRR_ADHOC: 0xa8, // ShortRangeRadio - ad-hoc message, f.ex. from SI-ActiveCard
48
+ GET_SI5: 0xb1, // read out SI-card 5 data
49
+ TRANS_REC: 0xd3, // autosend timestamp (online control)
50
+ CLEAR_CARD: 0xe0, // found on SI-dev-forum: 02 E0 00 E0 00 03 (http://www.sportident.com/en/forum/8/56#59)
51
+ GET_SI6: 0xe1, // read out SI-card 6 data block
52
+ SET_SI6: 0xe2, // write SI-card 6 line (16 bytes)
53
+ SET_SI6_SPECIAL: 0xe4, // write SI-card 6 special fields (e.g. start number)
54
+ SI5_DET: 0xe5, // SI-card 5 inserted
55
+ SI6_DET: 0xe6, // SI-card 6 inserted
56
+ SI_REM: 0xe7, // SI-card removed
57
+ SI8_DET: 0xe8, // SI-card 8/9/10/11/p/t inserted
58
+ SET_SI8: 0xea, // write SI-card 8/9/10/11/p/t data word
59
+ GET_SI8: 0xef, // read out SI-card 8/9/10/11/p/t data block
60
+ SET_MS: 0xf0, // \x4D="M"aster, \x53="S"lave
61
+ GET_MS: 0xf1,
62
+ ERASE_BDATA: 0xf5,
63
+ SET_TIME: 0xf6,
64
+ GET_TIME: 0xf7,
65
+ OFF: 0xf8,
66
+ SIGNAL: 0xf9, // 02 F9 (number of signals) (CRC16) 03
67
+ SET_BAUD: 0xfe // \x00=4800 baud, \x01=38400 baud
68
+ },
69
+ get cmdLookup(): { [value: number]: string } {
70
+ return getLookup(proto.cmd);
71
+ },
72
+
73
+ // If a punch has explicitly no time (e.g. if the start hasn't been punched)
74
+ NO_TIME: 0xeeee,
75
+
76
+ // Protocol Parameters
77
+ P_MS_DIRECT: 0x4d, // "M"aster
78
+ P_MS_REMOTE: 0x53, // "S"lave
79
+ P_SI6_CB: 0x08,
80
+
81
+ // Backup memory record length
82
+ REC_LEN: 8, // Only in extended protocol, otherwise 6!
83
+
84
+ // punch trigger in control mode data structure
85
+ T_OFFSET: 8,
86
+ T_CN: 0,
87
+ T_TIME: 5,
88
+
89
+ // backup memory in control mode
90
+ BC_CN: 3,
91
+ BC_TIME: 8
92
+ };
@@ -0,0 +1,11 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import * as testUtils from '../../testUtils';
3
+ import { BaseFakeSiCard } from './BaseFakeSiCard';
4
+
5
+ testUtils.useFakeTimers();
6
+
7
+ describe('BaseFakeSiCard', () => {
8
+ test('exists', () => {
9
+ expect(BaseFakeSiCard).not.toBe(undefined);
10
+ });
11
+ });
@@ -0,0 +1,10 @@
1
+ import type * as storage from '../../storage';
2
+ import type * as siProtocol from '../../siProtocol';
3
+
4
+ export abstract class BaseFakeSiCard {
5
+ public storage: storage.ISiStorage<unknown> = {} as storage.ISiStorage<unknown>;
6
+
7
+ abstract handleDetect(): siProtocol.SiMessage;
8
+
9
+ abstract handleRequest(message: siProtocol.SiMessage): siProtocol.SiMessage[];
10
+ }
@@ -0,0 +1,6 @@
1
+ import type * as siProtocol from '../../siProtocol';
2
+
3
+ export interface IFakeSiCard {
4
+ handleDetect: () => siProtocol.SiMessage;
5
+ handleRequest: (message: siProtocol.SiMessage) => siProtocol.SiMessage[];
6
+ }
@@ -0,0 +1,2 @@
1
+ export { BaseFakeSiCard } from './BaseFakeSiCard';
2
+ export * from './types';
@@ -0,0 +1,62 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import { proto } from '../../../constants';
3
+ import * as utils from '../../../utils';
4
+ import * as testUtils from '../../../testUtils';
5
+ import { FakeModernSiCard } from './FakeModernSiCard';
6
+ import { getEmptyCard } from '../../../SiCard/types/modernSiCardExamples';
7
+
8
+ testUtils.useFakeTimers();
9
+
10
+ describe('FakeModernSiCard', () => {
11
+ test('exists', () => {
12
+ expect(FakeModernSiCard).not.toBe(undefined);
13
+ });
14
+ const testData = getEmptyCard();
15
+ const myFakeModernSiCard = new FakeModernSiCard(testData.storageData);
16
+ test('handleDetect works', () => {
17
+ expect(myFakeModernSiCard.handleDetect()).toEqual({
18
+ command: proto.cmd.SI8_DET,
19
+ parameters: utils.unPrettyHex('00 6B 96 8C')
20
+ });
21
+ });
22
+ test('handleRequest works', () => {
23
+ expect(() =>
24
+ myFakeModernSiCard.handleRequest({
25
+ command: proto.cmd.GET_SI5,
26
+ parameters: [0x06]
27
+ })
28
+ ).toThrow();
29
+
30
+ expect(
31
+ myFakeModernSiCard.handleRequest({
32
+ command: proto.cmd.GET_SI8,
33
+ parameters: [0x06]
34
+ })
35
+ ).toEqual([
36
+ {
37
+ command: proto.cmd.GET_SI8,
38
+ parameters: [6, ...testData.storageData.slice(6 * 128, 7 * 128)]
39
+ }
40
+ ]);
41
+
42
+ expect(
43
+ myFakeModernSiCard.handleRequest({
44
+ command: proto.cmd.GET_SI8,
45
+ parameters: [0x08]
46
+ })
47
+ ).toEqual([
48
+ {
49
+ command: proto.cmd.GET_SI8,
50
+ parameters: [0, ...testData.storageData.slice(0, 128)]
51
+ },
52
+ {
53
+ command: proto.cmd.GET_SI8,
54
+ parameters: [6, ...testData.storageData.slice(6 * 128, 7 * 128)]
55
+ },
56
+ {
57
+ command: proto.cmd.GET_SI8,
58
+ parameters: [7, ...testData.storageData.slice(7 * 128, 8 * 128)]
59
+ }
60
+ ]);
61
+ });
62
+ });
@@ -0,0 +1,43 @@
1
+ import _ from 'lodash';
2
+ import { proto } from '../../../constants';
3
+ import * as siProtocol from '../../../siProtocol';
4
+ import { BaseFakeSiCard } from '../BaseFakeSiCard';
5
+ import { type IModernSiCardStorageFields, ModernSiCard, modernSiCardStorageDefinition } from '../../../SiCard/types/ModernSiCard';
6
+ import { getModernSiCardExamples } from '../../../SiCard/types/modernSiCardExamples';
7
+ import type { ISiStorage } from '../../../storage';
8
+
9
+ export class FakeModernSiCard extends BaseFakeSiCard {
10
+ static siCardClass = ModernSiCard;
11
+ static getAllExamples = getModernSiCardExamples;
12
+
13
+ storage: ISiStorage<IModernSiCardStorageFields>;
14
+
15
+ constructor(storage?: (number | undefined)[]) {
16
+ super();
17
+ this.storage = modernSiCardStorageDefinition(storage);
18
+ }
19
+
20
+ handleDetect(): siProtocol.SiMessage {
21
+ const cardNumberArr = siProtocol.cardNumber2arr(this.storage.get('cardNumber')!.value);
22
+ cardNumberArr.reverse();
23
+ return {
24
+ command: proto.cmd.SI8_DET,
25
+ parameters: [...cardNumberArr] as number[]
26
+ };
27
+ }
28
+
29
+ handleRequest(message: siProtocol.SiMessage): siProtocol.SiMessage[] {
30
+ if (message.mode !== undefined || message.command !== proto.cmd.GET_SI8 || message.parameters.length !== 1) {
31
+ throw new Error(`ModernSiCard can not handle ${siProtocol.prettyMessage(message)}`);
32
+ }
33
+ const pageIndex = message.parameters[0];
34
+ const getPageAtIndex = (index: number) => ({
35
+ command: proto.cmd.GET_SI8,
36
+ parameters: [index, ...this.storage.data.slice(index * 128, (index + 1) * 128).toJS()]
37
+ });
38
+ if (pageIndex === 0x08) {
39
+ return [0, 6, 7].map(getPageAtIndex) as siProtocol.SiMessage[];
40
+ }
41
+ return [getPageAtIndex(pageIndex)] as siProtocol.SiMessage[];
42
+ }
43
+ }
@@ -0,0 +1,17 @@
1
+ import type * as storage from '../../../storage';
2
+ import { type IModernSiCardStorageFields, modernSiCardStorageDefinition } from '../../../SiCard/types/ModernSiCard';
3
+ import { FakeModernSiCard } from './FakeModernSiCard';
4
+ import { SIAC } from '../../../SiCard/types/SIAC';
5
+ import { getModernSiCardExamples } from '../../../SiCard/types/modernSiCardExamples';
6
+
7
+ export class FakeSIAC extends FakeModernSiCard {
8
+ static siCardClass = SIAC;
9
+ static getAllExamples = getModernSiCardExamples;
10
+
11
+ public storage: storage.ISiStorage<IModernSiCardStorageFields>;
12
+
13
+ constructor(storageData: (number | undefined)[] | undefined) {
14
+ super();
15
+ this.storage = modernSiCardStorageDefinition(storageData);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import type * as storage from '../../../storage';
2
+ import { type IModernSiCardStorageFields, modernSiCardStorageDefinition } from '../../../SiCard/types/ModernSiCard';
3
+ import { FakeModernSiCard } from './FakeModernSiCard';
4
+ import { SiCard10 } from '../../../SiCard/types/SiCard10';
5
+ import { getModernSiCardExamples } from '../../../SiCard/types/modernSiCardExamples';
6
+
7
+ export class FakeSiCard10 extends FakeModernSiCard {
8
+ static siCardClass = SiCard10;
9
+ static getAllExamples = getModernSiCardExamples;
10
+
11
+ public storage: storage.ISiStorage<IModernSiCardStorageFields>;
12
+
13
+ constructor(storageData: (number | undefined)[] | undefined) {
14
+ super();
15
+ this.storage = modernSiCardStorageDefinition(storageData);
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import type * as storage from '../../../storage';
2
+ import { type IModernSiCardStorageFields, modernSiCardStorageDefinition } from '../../../SiCard/types/ModernSiCard';
3
+ import { FakeModernSiCard } from './FakeModernSiCard';
4
+ import { SiCard11 } from '../../../SiCard/types/SiCard11';
5
+ import { getModernSiCardExamples } from '../../../SiCard/types/modernSiCardExamples';
6
+
7
+ export class FakeSiCard11 extends FakeModernSiCard {
8
+ static siCardClass = SiCard11;
9
+ static getAllExamples = getModernSiCardExamples;
10
+
11
+ public storage: storage.ISiStorage<IModernSiCardStorageFields>;
12
+
13
+ constructor(storageData: (number | undefined)[] | undefined) {
14
+ super();
15
+ this.storage = modernSiCardStorageDefinition(storageData);
16
+ }
17
+ }