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
package/install.sh ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+
3
+ cat <<EOM >/etc/udev/rules.d/99-sportident.rules
4
+ SUBSYSTEM=="usb", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="800a", MODE="0660", GROUP="plugdev", RUN="/bin/sh -c 'echo -n \$id:1.0 > /sys/bus/usb/drivers/cp210x/unbind'"
5
+ EOM
6
+ udevadm control --reload
package/jest.config.ts ADDED
@@ -0,0 +1,49 @@
1
+ import type {Config} from 'jest';
2
+
3
+ const totalCoverage = {
4
+ branches: 100,
5
+ functions: 100,
6
+ lines: 100,
7
+ statements: 100,
8
+ };
9
+
10
+ const percentCoverage = (percent: number) => ({
11
+ branches: percent,
12
+ functions: percent,
13
+ lines: percent,
14
+ statements: percent,
15
+ })
16
+
17
+ const jestConfig: Config = {
18
+ transform: {
19
+ '^.+\\.tsx?$': 'ts-jest',
20
+ },
21
+ testEnvironment: 'jsdom',
22
+ testRegex: '.*/.*\\.test\\.tsx?',
23
+ testPathIgnorePatterns: ['node_modules/', 'lib/', '__snapshots__/', 'testUtils/'],
24
+ collectCoverage: true,
25
+ collectCoverageFrom: [
26
+ './**/src/**/*',
27
+ ],
28
+ coveragePathIgnorePatterns: ['node_modules/', 'lib/', '__snapshots__/', 'testUtils/'],
29
+ maxConcurrency: 1,
30
+ coverageThreshold: {
31
+ global: percentCoverage(10),
32
+ './src/': {
33
+ branches: 92,
34
+ functions: 89,
35
+ lines: 89,
36
+ statements: 88,
37
+ },
38
+ './src/SiCard': percentCoverage(92),
39
+ './src/SiDevice': percentCoverage(44),
40
+ './src/fakes': percentCoverage(43),
41
+ './src/SiStation': percentCoverage(83),
42
+ './src/storage': totalCoverage,
43
+ './src/utils': totalCoverage,
44
+ './src/constants.ts': totalCoverage,
45
+ './src/siProtocol.ts': totalCoverage,
46
+ './src/testUtils.ts': totalCoverage,
47
+ },
48
+ };
49
+ export default jestConfig;
package/nx.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "tasksRunnerOptions": {
3
+ "default": {
4
+ "runner": "nx/tasks-runners/default",
5
+ "options": {
6
+ "cacheableOperations": [
7
+ "build",
8
+ "eslint",
9
+ "tsc",
10
+ "webpack",
11
+ "typedoc"
12
+ ]
13
+ }
14
+ }
15
+ },
16
+ "namedInputs": {
17
+ "src": ["{projectRoot}/src/**/*"],
18
+ "babelconfig": ["{workspaceRoot}/babel.config.js"],
19
+ "eslintconfig": ["{workspaceRoot}/eslint.base.js", "{workspaceRoot}/.eslintignore"],
20
+ "tsconfig": ["{workspaceRoot}/tsconfig.base.json", "{workspaceRoot}/tsconfig.json"]
21
+ },
22
+ "targetDefaults": {
23
+ "build": {
24
+ "outputs": ["{projectRoot}/lib"]
25
+ },
26
+ "eslint": {
27
+ "inputs": ["src", "eslintconfig", "tsconfig"]
28
+ },
29
+ "tsc": {
30
+ "outputs": ["{projectRoot}/lib"]
31
+ },
32
+ "webpack": {
33
+ "outputs": ["{projectRoot}/build"]
34
+ },
35
+ "typedoc": {
36
+ "outputs": ["{projectRoot}/docs"]
37
+ }
38
+ }
39
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "sportident.js",
3
+ "version": "1.0.0",
4
+ "description": "JavaScript interface to SportIdent devices",
5
+ "scripts": {
6
+ "build": "tsc -b --verbose && lerna run build",
7
+ "build-docs": "sh build-docs.sh",
8
+ "dependency-check": "dependency-cruise --config=./.dependency-cruiser.js packages/*/src",
9
+ "lint": "eslint",
10
+ "test": "jest; echo \"\n\n Coverage: file://${PWD}/coverage/lcov-report/index.html\n\n\"",
11
+ "test:ci": "jest --runInBand",
12
+ "tsc-build": "tsc -b --verbose",
13
+ "tsc-clean": "tsc -b --clean"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/fadnincx/sportident.js.git"
18
+ },
19
+ "keywords": [
20
+ "sportident",
21
+ "javascript",
22
+ "js"
23
+ ],
24
+ "author": "Marcel Würsten",
25
+ "contributors": [
26
+ {
27
+ "name": "Simon Hatt"
28
+ },
29
+ {
30
+ "name": "Marcel Würsten"
31
+ }
32
+ ],
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "immutable": "^4.0.0-rc.12",
36
+ "lodash": "^4.17.15"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/fadnincx/sportident.js/issues"
40
+ },
41
+ "homepage": "https://github.com/fadnincx/sportident.js#readme",
42
+ "devDependencies": {
43
+ "@types/lodash": "^4.17.13",
44
+ "@types/w3c-web-serial": "^1.0.7",
45
+ "@types/w3c-web-usb": "^1.0.10",
46
+ "jest": "^29.7.0",
47
+ "jest-environment-jsdom": "^29.7.0",
48
+ "ts-jest": "^29.2.5",
49
+ "ts-node": "^10.9.2"
50
+ }
51
+ }
@@ -0,0 +1,187 @@
1
+ import { beforeEach, describe, expect, test } from '@jest/globals';
2
+ import { proto } from '../constants';
3
+ import type * as siProtocol from '../siProtocol';
4
+ import { BaseSiCard } from './BaseSiCard';
5
+
6
+ beforeEach(() => {
7
+ BaseSiCard.resetNumberRangeRegistry();
8
+ });
9
+
10
+ describe('BaseSiCard', () => {
11
+ class FakeSiCard1 extends BaseSiCard {
12
+ static typeSpecificInstanceFromMessage(_message: siProtocol.SiMessage): FakeSiCard1 | undefined {
13
+ return undefined;
14
+ }
15
+
16
+ typeSpecificRead() {
17
+ return Promise.resolve();
18
+ }
19
+ }
20
+ class FakeSiCard2 extends BaseSiCard {
21
+ static typeSpecificInstanceFromMessage(_message: siProtocol.SiMessage): FakeSiCard2 | undefined {
22
+ return undefined;
23
+ }
24
+
25
+ typeSpecificRead() {
26
+ return Promise.resolve();
27
+ }
28
+ }
29
+
30
+ test('registerNumberRange', () => {
31
+ BaseSiCard.registerNumberRange(100, 1000, FakeSiCard1);
32
+ BaseSiCard.registerNumberRange(0, 100, FakeSiCard2);
33
+ BaseSiCard.registerNumberRange(1000, 2000, FakeSiCard2);
34
+ expect(BaseSiCard.getTypeByCardNumber(-1)).toEqual(undefined);
35
+ expect(BaseSiCard.getTypeByCardNumber(0)).toEqual(FakeSiCard2);
36
+ expect(BaseSiCard.getTypeByCardNumber(99)).toEqual(FakeSiCard2);
37
+ expect(BaseSiCard.getTypeByCardNumber(100)).toEqual(FakeSiCard1);
38
+ expect(BaseSiCard.getTypeByCardNumber(999)).toEqual(FakeSiCard1);
39
+ expect(BaseSiCard.getTypeByCardNumber(1000)).toEqual(FakeSiCard2);
40
+ expect(BaseSiCard.getTypeByCardNumber(1999)).toEqual(FakeSiCard2);
41
+ expect(BaseSiCard.getTypeByCardNumber(2000)).toEqual(undefined);
42
+ });
43
+ test('fromCardNumber', () => {
44
+ BaseSiCard.registerNumberRange(100, 1000, FakeSiCard1);
45
+ const siCard500 = BaseSiCard.fromCardNumber(500);
46
+ expect(siCard500 instanceof BaseSiCard).toBe(true);
47
+ expect(siCard500 instanceof FakeSiCard1).toBe(true);
48
+ const siCard5000 = BaseSiCard.fromCardNumber(5000);
49
+ expect(siCard5000).toBe(undefined);
50
+ });
51
+ describe('detectFromMessage', () => {
52
+ let triedToGetInstance = false;
53
+ class SiCard1 extends BaseSiCard {
54
+ static typeSpecificInstanceFromMessage(message: siProtocol.SiMessage) {
55
+ triedToGetInstance = true;
56
+ if (message.mode !== undefined) {
57
+ return undefined;
58
+ }
59
+ return new SiCard1(1);
60
+ }
61
+
62
+ typeSpecificRead() {
63
+ return Promise.resolve();
64
+ }
65
+ }
66
+
67
+ beforeEach(() => {
68
+ BaseSiCard.registerNumberRange(1000, 10000, SiCard1);
69
+ BaseSiCard.registerNumberRange(10000, 20000, FakeSiCard2);
70
+ });
71
+
72
+ test('detects card from valid message', () => {
73
+ expect(triedToGetInstance).toBe(false);
74
+ const siCard500 = BaseSiCard.detectFromMessage({
75
+ command: proto.cmd.SI5_DET,
76
+ parameters: []
77
+ });
78
+ expect(triedToGetInstance).toBe(true);
79
+ expect(siCard500 instanceof SiCard1).toBe(true);
80
+ expect(siCard500?.cardNumber).toBe(1);
81
+ });
82
+
83
+ test('does not detect from NAK message', () => {
84
+ const nakMessage = BaseSiCard.detectFromMessage({
85
+ mode: proto.NAK
86
+ });
87
+ expect(nakMessage).toBe(undefined);
88
+ });
89
+ });
90
+ test('read', async () => {
91
+ class SiCard1 extends BaseSiCard {
92
+ typeSpecificRead() {
93
+ this.raceResult.startTime = {time:1};
94
+ return Promise.resolve();
95
+ }
96
+ }
97
+ const siCard500 = new SiCard1(500);
98
+ try {
99
+ await siCard500.confirm();
100
+ expect({ canConfirm: true }).toEqual({ canConfirm: false });
101
+ } catch (err) {
102
+ // ignore
103
+ }
104
+ siCard500.mainStation = {
105
+ sendMessage: (_message: siProtocol.SiMessage, _numResponses?: number) => Promise.resolve([])
106
+ };
107
+ const result = await siCard500.read();
108
+ expect(result).toBe(siCard500);
109
+ expect(JSON.stringify(siCard500.raceResult.startTime)).toBe(JSON.stringify({time:1}));
110
+ await siCard500.confirm();
111
+ });
112
+ const emptySiCard = new FakeSiCard1(501);
113
+ const nonemptySiCard = new FakeSiCard1(502);
114
+ nonemptySiCard.raceResult = {
115
+ cardNumber: 502,
116
+ clearTime: {time:1},
117
+ checkTime: {time:2},
118
+ startTime: {time:1},
119
+ punches: [{ code: 31, time: {time:2} }],
120
+ finishTime: {time:1},
121
+ cardHolder: { firstName: 'John' }
122
+ };
123
+ test('Empty SiCard toDict', async () => {
124
+ expect(emptySiCard.toDict()).toEqual({
125
+ cardNumber: 501,
126
+ clearTime: undefined,
127
+ checkTime: undefined,
128
+ startTime: undefined,
129
+ finishTime: undefined,
130
+ punches: undefined,
131
+ cardHolder: undefined
132
+ });
133
+ });
134
+ test('Non-empty SiCard toDict', async () => {
135
+ expect(nonemptySiCard.toDict()).toEqual({
136
+ cardNumber: 502,
137
+ clearTime: {time:1},
138
+ checkTime: {time:2},
139
+ startTime: {time:1},
140
+ finishTime: {time:1},
141
+ punches: [{ code: 31, time: {time:2} }],
142
+ cardHolder: { firstName: 'John' }
143
+ });
144
+ });
145
+ test('Empty SiCard toString', async () => {
146
+ expect(emptySiCard.toString()).toEqual('FakeSiCard1\nCard Number: 501\nClear: ?\nCheck: ?\nStart: ?\nFinish: ?\n? Punches\nCard Holder:\n?\n');
147
+ });
148
+ test('Non-empty SiCard toString', async () => {
149
+ expect(nonemptySiCard.toString()).toEqual('FakeSiCard1\nCard Number: 502\nClear: 1\nCheck: 2\nStart: 1\nFinish: 1\n31: 2\nCard Holder:\nfirstName: John\n');
150
+ });
151
+ test('Empty SiCard getMonotonizedRaceResult', async () => {
152
+ expect(emptySiCard.getMonotonizedRaceResult()).toEqual({
153
+ cardNumber: 501,
154
+ clearTime: undefined,
155
+ checkTime: undefined,
156
+ startTime: undefined,
157
+ finishTime: undefined,
158
+ punches: undefined,
159
+ cardHolder: undefined
160
+ });
161
+ });
162
+ test('Non-empty SiCard getMonotonizedRaceResult', async () => {
163
+ expect(nonemptySiCard.getMonotonizedRaceResult()).toEqual({
164
+ cardNumber: 502,
165
+ clearTime: {time:1},
166
+ checkTime: {time:2},
167
+ startTime: {time:43201},
168
+ finishTime: {time:86401},
169
+ punches: [{ code: 31, time: {time:43202} }],
170
+ cardHolder: { firstName: 'John' }
171
+ });
172
+ });
173
+ test('Empty SiCard getNormalizedRaceResult', async () => {
174
+ expect(() => emptySiCard.getNormalizedRaceResult()).toThrow();
175
+ });
176
+ test('Non-empty SiCard getNormalizedRaceResult', async () => {
177
+ expect(nonemptySiCard.getNormalizedRaceResult()).toEqual({
178
+ cardNumber: 502,
179
+ clearTime: {time:-43200},
180
+ checkTime: {time:-43199},
181
+ startTime: {time:0},
182
+ finishTime: {time:43200},
183
+ punches: [{ code: 31, time: {time:1} }],
184
+ cardHolder: { firstName: 'John' }
185
+ });
186
+ });
187
+ });
@@ -0,0 +1,101 @@
1
+ import _ from 'lodash';
2
+ import { proto } from '../constants';
3
+ import * as utils from '../utils';
4
+ import type * as siProtocol from '../siProtocol';
5
+ import type * as storage from '../storage';
6
+ import type { IRaceResultData } from './IRaceResultData';
7
+ import { makeStartZeroTime, monotonizeRaceResult, prettyRaceResult } from './raceResultTools';
8
+
9
+ export type SiCardType<T extends BaseSiCard> = {
10
+ new (cardNumber: number): T;
11
+ typeSpecificInstanceFromMessage: (message: siProtocol.SiMessage) => T | undefined;
12
+ };
13
+ const initialRegistry: utils.NumberRangeRegistry<SiCardType<BaseSiCard>> = new utils.NumberRangeRegistry();
14
+
15
+ export interface ISiMainStation {
16
+ sendMessage: (message: siProtocol.SiMessage, numResponses?: number, timeoutInMiliseconds?: number) => Promise<number[][]>;
17
+ }
18
+
19
+ export abstract class BaseSiCard {
20
+ // abstract static maxNumPunches: number;
21
+ static NumberRange: typeof utils.NumberRange = utils.NumberRange;
22
+ static cardNumberRangeRegistry: utils.NumberRangeRegistry<SiCardType<BaseSiCard>> = initialRegistry;
23
+
24
+ static resetNumberRangeRegistry(): void {
25
+ this.cardNumberRangeRegistry = new utils.NumberRangeRegistry();
26
+ }
27
+
28
+ static registerNumberRange<T extends BaseSiCard>(firstCardNumberInRange: number, firstCardNumberAfterRange: number, siCardType: SiCardType<T>): void {
29
+ const cardNumberRange = new utils.NumberRange(firstCardNumberInRange, firstCardNumberAfterRange);
30
+ this.cardNumberRangeRegistry.register(cardNumberRange, siCardType);
31
+ }
32
+
33
+ static getTypeByCardNumber<T extends BaseSiCard>(cardNumber: number): SiCardType<T> | undefined {
34
+ return this.cardNumberRangeRegistry.getValueForNumber(cardNumber) as SiCardType<T> | undefined;
35
+ }
36
+
37
+ // abstract static getPunchOffset(index: number): number;
38
+
39
+ static fromCardNumber(cardNumber: number): BaseSiCard | undefined {
40
+ const cardType = this.getTypeByCardNumber(cardNumber);
41
+ if (!cardType) {
42
+ return undefined;
43
+ }
44
+ return new cardType(cardNumber);
45
+ }
46
+
47
+ static detectFromMessage(message: siProtocol.SiMessage): BaseSiCard | undefined {
48
+ const possibleCards = this.cardNumberRangeRegistry.values.map((cardType) => cardType.typeSpecificInstanceFromMessage(message)).filter((cardInstance) => cardInstance !== undefined);
49
+ return possibleCards.get(0);
50
+ }
51
+
52
+ // abstract static typeSpecificInstanceFromMessage<Fields extends IBaseSiCardStorageFields>(
53
+ // _message: siProtocol.SiMessage,
54
+ // ): BaseSiCard<Fields>|undefined;
55
+
56
+ public mainStation?: ISiMainStation | undefined;
57
+ public raceResult: IRaceResultData & { cardNumber: number };
58
+ public storage: storage.ISiStorage<unknown> = {} as storage.ISiStorage<unknown>;
59
+
60
+ constructor(cardNumber: number) {
61
+ this.raceResult = { cardNumber: cardNumber };
62
+ }
63
+
64
+ get cardNumber(): number {
65
+ return this.raceResult.cardNumber;
66
+ }
67
+
68
+ read(): Promise<BaseSiCard> {
69
+ return this.typeSpecificRead().then(() => this);
70
+ }
71
+
72
+ getNormalizedRaceResult(): IRaceResultData {
73
+ return makeStartZeroTime(this.getMonotonizedRaceResult());
74
+ }
75
+
76
+ getMonotonizedRaceResult(): IRaceResultData {
77
+ return monotonizeRaceResult(this.raceResult);
78
+ }
79
+
80
+ abstract typeSpecificRead(): Promise<void>;
81
+
82
+ confirm(): Promise<number[][]> {
83
+ if (!this.mainStation) {
84
+ return Promise.reject(new Error('No main station'));
85
+ }
86
+ return this.mainStation.sendMessage(
87
+ {
88
+ mode: proto.ACK
89
+ },
90
+ 0
91
+ );
92
+ }
93
+
94
+ toDict(): IRaceResultData {
95
+ return this.raceResult;
96
+ }
97
+
98
+ toString(): string {
99
+ return `${this.constructor.name}\n${prettyRaceResult(this.raceResult)}`;
100
+ }
101
+ }
@@ -0,0 +1,16 @@
1
+ import type * as siProtocol from '../siProtocol';
2
+
3
+ export interface IRaceResultData {
4
+ cardNumber?: number;
5
+ cardHolder?: { [property: string]: unknown };
6
+ clearTime?: siProtocol.SiTimestamp;
7
+ checkTime?: siProtocol.SiTimestamp;
8
+ startTime?: siProtocol.SiTimestamp;
9
+ finishTime?: siProtocol.SiTimestamp;
10
+ punches?: IPunch[];
11
+ }
12
+
13
+ export interface IPunch {
14
+ code: number;
15
+ time: siProtocol.SiTimestamp;
16
+ }
@@ -0,0 +1,23 @@
1
+ import type * as storage from '../storage';
2
+ import type * as siProtocol from '../siProtocol';
3
+ import type { IPunch, IRaceResultData } from './IRaceResultData';
4
+
5
+ export interface ISiCard {
6
+ cardNumber: number;
7
+ storage: storage.ISiStorage<IBaseSiCardStorageFields>;
8
+ read: () => Promise<ISiCard>;
9
+ confirm: () => Promise<unknown>;
10
+ toDict: () => IRaceResultData;
11
+ toString: () => string;
12
+ }
13
+
14
+ export interface IBaseSiCardStorageFields {
15
+ cardNumber: number;
16
+ clearTime?: siProtocol.SiTimestamp;
17
+ checkTime: siProtocol.SiTimestamp;
18
+ startTime: siProtocol.SiTimestamp;
19
+ finishTime: siProtocol.SiTimestamp;
20
+ punchCount: number;
21
+ punches: IPunch[];
22
+ cardHolder: { [key: string]: unknown };
23
+ }
@@ -0,0 +1,4 @@
1
+ export interface SiCardSample {
2
+ cardData: { [attr: string]: unknown } & { cardNumber: number };
3
+ storageData: (number | undefined)[];
4
+ }
@@ -0,0 +1,2 @@
1
+ export { BaseSiCard } from './BaseSiCard';
2
+ export * from './types';