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,42 @@
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 { FakeSiCard5 } from './FakeSiCard5';
6
+ import { getCardWith16Punches } from '../../../SiCard/types/siCard5Examples';
7
+
8
+ testUtils.useFakeTimers();
9
+
10
+ describe('FakeSiCard5', () => {
11
+ test('exists', () => {
12
+ expect(FakeSiCard5).not.toBe(undefined);
13
+ });
14
+ const testData = getCardWith16Punches();
15
+ const myFakeSiCard5 = new FakeSiCard5(testData.storageData);
16
+ test('handleDetect works', () => {
17
+ expect(myFakeSiCard5.handleDetect()).toEqual({
18
+ command: proto.cmd.SI5_DET,
19
+ parameters: utils.unPrettyHex('00 04 19 02')
20
+ });
21
+ });
22
+ test('handleRequest works', () => {
23
+ expect(() =>
24
+ myFakeSiCard5.handleRequest({
25
+ command: proto.cmd.GET_SI8,
26
+ parameters: [0x06]
27
+ })
28
+ ).toThrow();
29
+
30
+ expect(
31
+ myFakeSiCard5.handleRequest({
32
+ command: proto.cmd.GET_SI5,
33
+ parameters: []
34
+ })
35
+ ).toEqual([
36
+ {
37
+ command: proto.cmd.GET_SI5,
38
+ parameters: testData.storageData
39
+ }
40
+ ]);
41
+ });
42
+ });
@@ -0,0 +1,40 @@
1
+ import _ from 'lodash';
2
+ import { proto } from '../../../constants';
3
+ import * as siProtocol from '../../../siProtocol';
4
+ import type * as storage from '../../../storage';
5
+ import { BaseFakeSiCard } from '../BaseFakeSiCard';
6
+ import { type ISiCard5StorageFields, SiCard5, siCard5StorageDefinition } from '../../../SiCard/types/SiCard5';
7
+ import { getSiCard5Examples } from '../../../SiCard/types/siCard5Examples';
8
+
9
+ export class FakeSiCard5 extends BaseFakeSiCard {
10
+ static siCardClass = SiCard5;
11
+ static getAllExamples = getSiCard5Examples;
12
+
13
+ public storage: storage.ISiStorage<ISiCard5StorageFields>;
14
+
15
+ constructor(storageData: (number | undefined)[] | undefined) {
16
+ super();
17
+ this.storage = siCard5StorageDefinition(storageData);
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.SI5_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_SI5 || message.parameters.length !== 0) {
31
+ throw new Error(`SiCard5 can not handle ${siProtocol.prettyMessage(message)}`);
32
+ }
33
+ return [
34
+ {
35
+ command: proto.cmd.GET_SI5,
36
+ parameters: this.storage.data.toJS()
37
+ }
38
+ ] as siProtocol.SiMessage[];
39
+ }
40
+ }
@@ -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 { FakeSiCard6 } from './FakeSiCard6';
6
+ import { getCardWith16Punches } from '../../../SiCard/types/siCard6Examples';
7
+
8
+ testUtils.useFakeTimers();
9
+
10
+ describe('FakeSiCard6', () => {
11
+ test('exists', () => {
12
+ expect(FakeSiCard6).not.toBe(undefined);
13
+ });
14
+ const testData = getCardWith16Punches();
15
+ const myFakeSiCard6 = new FakeSiCard6(testData.storageData);
16
+ test('handleDetect works', () => {
17
+ expect(myFakeSiCard6.handleDetect()).toEqual({
18
+ command: proto.cmd.SI6_DET,
19
+ parameters: utils.unPrettyHex('00 07 A1 3D')
20
+ });
21
+ });
22
+ test('handleRequest works', () => {
23
+ expect(() =>
24
+ myFakeSiCard6.handleRequest({
25
+ command: proto.cmd.GET_SI5,
26
+ parameters: [0x06]
27
+ })
28
+ ).toThrow();
29
+
30
+ expect(
31
+ myFakeSiCard6.handleRequest({
32
+ command: proto.cmd.GET_SI6,
33
+ parameters: [0x06]
34
+ })
35
+ ).toEqual([
36
+ {
37
+ command: proto.cmd.GET_SI6,
38
+ parameters: [6, ...testData.storageData.slice(6 * 128, 7 * 128)]
39
+ }
40
+ ]);
41
+
42
+ expect(
43
+ myFakeSiCard6.handleRequest({
44
+ command: proto.cmd.GET_SI6,
45
+ parameters: [0x08]
46
+ })
47
+ ).toEqual([
48
+ {
49
+ command: proto.cmd.GET_SI6,
50
+ parameters: [0, ...testData.storageData.slice(0, 128)]
51
+ },
52
+ {
53
+ command: proto.cmd.GET_SI6,
54
+ parameters: [6, ...testData.storageData.slice(6 * 128, 7 * 128)]
55
+ },
56
+ {
57
+ command: proto.cmd.GET_SI6,
58
+ parameters: [7, ...testData.storageData.slice(7 * 128, 8 * 128)]
59
+ }
60
+ ]);
61
+ });
62
+ });
@@ -0,0 +1,44 @@
1
+ import _ from 'lodash';
2
+ import { proto } from '../../../constants';
3
+ import * as siProtocol from '../../../siProtocol';
4
+ import type * as storage from '../../../storage';
5
+ import { BaseFakeSiCard } from '../BaseFakeSiCard';
6
+ import { type ISiCard6StorageFields, SiCard6, siCard6StorageDefinition } from '../../../SiCard/types/SiCard6';
7
+ import { getSiCard6Examples } from '../../../SiCard/types/siCard6Examples';
8
+
9
+ export class FakeSiCard6 extends BaseFakeSiCard {
10
+ static siCardClass = SiCard6;
11
+ static getAllExamples = getSiCard6Examples;
12
+
13
+ public storage: storage.ISiStorage<ISiCard6StorageFields>;
14
+
15
+ constructor(storageData: (number | undefined)[] | undefined) {
16
+ super();
17
+ this.storage = siCard6StorageDefinition(storageData);
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.SI6_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_SI6 || message.parameters.length !== 1) {
31
+ throw new Error(`SiCard6 can not handle ${siProtocol.prettyMessage(message)}`);
32
+ }
33
+ const pageIndex = message.parameters[0];
34
+ const getPageAtIndex = (index: number): siProtocol.SiMessage =>
35
+ ({
36
+ command: proto.cmd.GET_SI6,
37
+ parameters: [index, ...this.storage.data.slice(index * 128, (index + 1) * 128).toJS()]
38
+ } as siProtocol.SiMessage);
39
+ if (pageIndex === 0x08) {
40
+ return [0, 6, 7].map(getPageAtIndex);
41
+ }
42
+ return [getPageAtIndex(pageIndex)];
43
+ }
44
+ }
@@ -0,0 +1,16 @@
1
+ import type * as storage from '../../../storage';
2
+ import { FakeModernSiCard } from './FakeModernSiCard';
3
+ import { type ISiCard8StorageFields, SiCard8, siCard8StorageDefinition } from '../../../SiCard/types/SiCard8';
4
+ import { getSiCard8Examples } from '../../../SiCard/types/siCard8Examples';
5
+
6
+ export class FakeSiCard8 extends FakeModernSiCard {
7
+ static siCardClass = SiCard8;
8
+ static getAllExamples = getSiCard8Examples;
9
+
10
+ public storage: storage.ISiStorage<ISiCard8StorageFields>;
11
+
12
+ constructor(storageData: (number | undefined)[] | undefined) {
13
+ super();
14
+ this.storage = siCard8StorageDefinition(storageData);
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ import type * as storage from '../../../storage';
2
+ import { FakeModernSiCard } from './FakeModernSiCard';
3
+ import { type ISiCard9StorageFields, SiCard9, siCard9StorageDefinition } from '../../../SiCard/types/SiCard9';
4
+ import { getSiCard9Examples } from '../../../SiCard/types/siCard9Examples';
5
+
6
+ export class FakeSiCard9 extends FakeModernSiCard {
7
+ static siCardClass = SiCard9;
8
+ static getAllExamples = getSiCard9Examples;
9
+
10
+ public storage: storage.ISiStorage<ISiCard9StorageFields>;
11
+
12
+ constructor(storageData: (number | undefined)[] | undefined) {
13
+ super();
14
+ this.storage = siCard9StorageDefinition(storageData);
15
+ }
16
+ }
@@ -0,0 +1,7 @@
1
+ export { FakeSiCard5 } from './FakeSiCard5';
2
+ export { FakeSiCard6 } from './FakeSiCard6';
3
+ export { FakeSiCard8 } from './FakeSiCard8';
4
+ export { FakeSiCard9 } from './FakeSiCard9';
5
+ export { FakeSiCard10 } from './FakeSiCard10';
6
+ export { FakeSiCard11 } from './FakeSiCard11';
7
+ export { FakeSIAC } from './FakeSIAC';
@@ -0,0 +1,148 @@
1
+ import * as utils from '../utils';
2
+ import { DeviceClosedError, SiDeviceState } from '../SiDevice/ISiDevice';
3
+ import type { ISiDevice, ISiDeviceDriverData } from '../SiDevice/ISiDevice';
4
+
5
+ import { SiDeviceAddEvent, SiDeviceRemoveEvent } from '../SiDevice/ISiDeviceDriver';
6
+ import type { ISiDeviceDriver, ISiDeviceDriverWithAutodetection, ISiDeviceDriverWithDetection, SiDeviceDriverWithAutodetectionEvents } from '../SiDevice/ISiDeviceDriver';
7
+ import { SiDevice } from '../SiDevice/SiDevice';
8
+
9
+ interface FakeDevice {
10
+ ident: string;
11
+ isOpened: boolean;
12
+ }
13
+
14
+ const getIdent = (fakeDevice: FakeDevice): string => fakeDevice.ident;
15
+
16
+ export interface FakeSiDeviceDriverData extends ISiDeviceDriverData<FakeSiDeviceDriver> {
17
+ driver: FakeSiDeviceDriver;
18
+ device: FakeDevice;
19
+ }
20
+
21
+ export type IFakeSiDevice = ISiDevice<FakeSiDeviceDriverData>;
22
+ export type FakeSiDevice = SiDevice<FakeSiDeviceDriverData>;
23
+
24
+ class FakeSiDeviceDriver implements ISiDeviceDriver<FakeSiDeviceDriverData>, ISiDeviceDriverWithDetection<FakeSiDeviceDriverData, []>, ISiDeviceDriverWithAutodetection<FakeSiDeviceDriverData> {
25
+ public name = 'Fake';
26
+
27
+ private siDeviceByIdent: { [ident: string]: FakeSiDevice } = {};
28
+
29
+ private autodetectedSiDevices: { [ident: string]: FakeSiDevice } = {};
30
+
31
+ private autoDetectionRunning = false;
32
+
33
+ public identToBeDetected: string | null = null;
34
+ public identsToBeAutodetected: string[] = [];
35
+
36
+ detect(): Promise<FakeSiDevice> {
37
+ if (!this.identToBeDetected) {
38
+ return Promise.reject(new Error('No fake device ident to be detected'));
39
+ }
40
+ return this.autodetectSiDevice({
41
+ ident: this.identToBeDetected,
42
+ isOpened: false
43
+ });
44
+ }
45
+
46
+ getSiDevice(fakeDevice: FakeDevice): FakeSiDevice {
47
+ const ident = getIdent(fakeDevice);
48
+ if (this.siDeviceByIdent[ident] !== undefined) {
49
+ return this.siDeviceByIdent[ident];
50
+ }
51
+ const newSiDeviceData: FakeSiDeviceDriverData = {
52
+ driver: this,
53
+ device: fakeDevice
54
+ };
55
+ const newSiDevice = new SiDevice(ident, newSiDeviceData);
56
+ this.siDeviceByIdent[ident] = newSiDevice;
57
+ return newSiDevice;
58
+ }
59
+
60
+ async forgetSiDevice(siDevice: FakeSiDevice): Promise<void> {
61
+ const fakeDevice = siDevice.data.device;
62
+ const ident = getIdent(fakeDevice);
63
+ delete this.siDeviceByIdent[ident];
64
+ if (this.autodetectedSiDevices[ident] !== undefined) {
65
+ await this.dispatchEvent('remove', new SiDeviceRemoveEvent(siDevice));
66
+ }
67
+ delete this.autodetectedSiDevices[ident];
68
+ }
69
+
70
+ public isAutoDetectionRunning(): boolean {
71
+ return this.autoDetectionRunning;
72
+ }
73
+
74
+ startAutoDetection(): Promise<IFakeSiDevice[]> {
75
+ this.autoDetectionRunning = true;
76
+ return this.getAutodetectedDevices();
77
+ }
78
+
79
+ getAutodetectedDevices(): Promise<FakeSiDevice[]> {
80
+ return this.autodetectSiDevices(this.identsToBeAutodetected);
81
+ }
82
+
83
+ autodetectSiDevices(idents: string[]): Promise<FakeSiDevice[]> {
84
+ return Promise.all(idents.map((ident) => this.autodetectSiDevice({ ident, isOpened: false })));
85
+ }
86
+
87
+ autodetectSiDevice(fakeDevice: FakeDevice): Promise<FakeSiDevice> {
88
+ const ident = getIdent(fakeDevice);
89
+ if (this.autodetectedSiDevices[ident] !== undefined) {
90
+ return Promise.reject(new Error('Duplicate SI device'));
91
+ }
92
+ const siDevice = this.getSiDevice(fakeDevice);
93
+ this.autodetectedSiDevices[ident] = siDevice;
94
+ return Promise.resolve(siDevice);
95
+ }
96
+
97
+ public async handleDeviceConnected(ident: string): Promise<void> {
98
+ if (!this.autoDetectionRunning) {
99
+ throw new Error('autodetection not running');
100
+ }
101
+ const openedDevice = await this.autodetectSiDevice({ ident, isOpened: false });
102
+ await this.dispatchEvent('add', new SiDeviceAddEvent(openedDevice));
103
+ }
104
+
105
+ public async handleDeviceDisconnected(ident: string): Promise<void> {
106
+ if (!this.autoDetectionRunning) {
107
+ throw new Error('autodetection not running');
108
+ }
109
+ const siDevice = this.siDeviceByIdent[ident];
110
+ if (siDevice === undefined) {
111
+ throw new Error('No such device');
112
+ }
113
+ await this.forgetSiDevice(siDevice);
114
+ }
115
+
116
+ stopAutoDetection(): Promise<unknown> {
117
+ this.autoDetectionRunning = false;
118
+ return Promise.resolve();
119
+ }
120
+
121
+ open(device: IFakeSiDevice): Promise<void> {
122
+ device.data.device.isOpened = true;
123
+ return Promise.resolve();
124
+ }
125
+
126
+ close(device: IFakeSiDevice): Promise<unknown> {
127
+ device.data.device.isOpened = false;
128
+ return Promise.resolve();
129
+ }
130
+
131
+ receive(device: IFakeSiDevice): Promise<number[]> {
132
+ const fakeDevice = device.data.device;
133
+ if (fakeDevice.isOpened !== true) {
134
+ device.setState(SiDeviceState.Closed);
135
+ throw new DeviceClosedError();
136
+ }
137
+ return Promise.resolve([]);
138
+ }
139
+
140
+ send(_device: IFakeSiDevice, _uint8Data: number[]): Promise<unknown> {
141
+ return Promise.resolve(true);
142
+ }
143
+ }
144
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
145
+ interface FakeSiDeviceDriver extends utils.EventTarget<SiDeviceDriverWithAutodetectionEvents<FakeSiDeviceDriverData>> {}
146
+ utils.applyMixins(FakeSiDeviceDriver, [utils.EventTarget]);
147
+
148
+ export const getFakeSiDeviceDriver = (): FakeSiDeviceDriver => new FakeSiDeviceDriver();
@@ -0,0 +1,141 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import { proto } from '../constants';
3
+ import type * as siProtocol from '../siProtocol';
4
+ import * as testUtils from '../testUtils';
5
+ import { getBSM8Station } from '../SiStation/siStationExamples';
6
+ import type { IFakeSiCard } from './FakeSiCard/IFakeSiCard';
7
+ import type { FakeSiMainStationMessageEvent } from './IFakeSiMainStation';
8
+ import { FakeSiMainStation } from './FakeSiMainStation';
9
+
10
+ testUtils.useFakeTimers();
11
+
12
+ describe('FakeSiMainStation', () => {
13
+ test('exists', () => {
14
+ expect(FakeSiMainStation).not.toBe(undefined);
15
+ });
16
+ const myFakeSiMainStation = new FakeSiMainStation(getBSM8Station().storageData);
17
+ test('getCode', () => {
18
+ expect(myFakeSiMainStation.getCode()).toEqual([0, 31]);
19
+ });
20
+ test('getDateTime', () => {
21
+ const dateTime1 = myFakeSiMainStation.getDateTime();
22
+ expect(dateTime1 instanceof Date).toBe(true);
23
+ myFakeSiMainStation.dateOffset = 3600;
24
+ const dateTime2 = myFakeSiMainStation.getDateTime();
25
+ expect(dateTime1 instanceof Date).toBe(true);
26
+ const timeDiff = dateTime2.getTime() - dateTime1.getTime();
27
+ expect(timeDiff).toBeLessThan(3600 + 10);
28
+ expect(timeDiff).toBeGreaterThan(3600 - 10);
29
+ });
30
+ test('dispatching messages', () => {
31
+ const dispatchedMessages: siProtocol.SiMessage[] = [];
32
+ const handleMessage = (e: FakeSiMainStationMessageEvent) => {
33
+ dispatchedMessages.push(e.message);
34
+ };
35
+ myFakeSiMainStation.addEventListener('message', handleMessage);
36
+
37
+ const randomMessage1 = testUtils.getRandomMessage({ numParameters: 1 });
38
+ myFakeSiMainStation.dispatchMessage(randomMessage1);
39
+ expect(dispatchedMessages).toEqual([randomMessage1]);
40
+
41
+ const randomMessage2 = testUtils.getRandomMessage({ numParameters: 1 });
42
+ myFakeSiMainStation.dispatchCardMessage(randomMessage2);
43
+ expect(dispatchedMessages.length).toBe(2);
44
+ if (dispatchedMessages[1].mode !== undefined) {
45
+ throw new Error('message mode must be undefined');
46
+ }
47
+ expect(dispatchedMessages[1].command).toBe(randomMessage2.command);
48
+ expect(dispatchedMessages[1].parameters).toEqual([...myFakeSiMainStation.getCode(), ...randomMessage2.parameters]);
49
+
50
+ myFakeSiMainStation.removeEventListener('message', handleMessage);
51
+ });
52
+ test('insertCard', () => {
53
+ let dispatchedMessage: siProtocol.SiMessage | undefined;
54
+ const handleMessage = (e: FakeSiMainStationMessageEvent) => {
55
+ dispatchedMessage = e.message;
56
+ };
57
+ myFakeSiMainStation.addEventListener('message', handleMessage);
58
+
59
+ const cardInsertionMessage = testUtils.getRandomMessage({ numParameters: 1 });
60
+ const fakeSiCard = {
61
+ handleDetect: () => cardInsertionMessage
62
+ } as IFakeSiCard;
63
+ myFakeSiMainStation.insertCard(fakeSiCard);
64
+ if (dispatchedMessage === undefined || dispatchedMessage.mode !== undefined) {
65
+ throw new Error('message mode must be undefined');
66
+ }
67
+ expect(dispatchedMessage.command).toBe(cardInsertionMessage.command);
68
+ expect(dispatchedMessage.parameters).toEqual([...myFakeSiMainStation.getCode(), ...cardInsertionMessage.parameters]);
69
+
70
+ myFakeSiMainStation.removeEventListener('message', handleMessage);
71
+ });
72
+ test('sendMessage', () => {
73
+ const code = myFakeSiMainStation.getCode();
74
+
75
+ const getResponsesFor = (message: siProtocol.SiMessage) => {
76
+ const dispatchedMessages: siProtocol.SiMessage[] = [];
77
+ const handleMessage = (e: FakeSiMainStationMessageEvent) => {
78
+ dispatchedMessages.push(e.message);
79
+ };
80
+ myFakeSiMainStation.addEventListener('message', handleMessage);
81
+
82
+ myFakeSiMainStation.sendMessage(message);
83
+
84
+ myFakeSiMainStation.removeEventListener('message', handleMessage);
85
+ return dispatchedMessages;
86
+ };
87
+
88
+ expect(getResponsesFor({ command: proto.cmd.SIGNAL, parameters: [0x01] })).toEqual([{ command: proto.cmd.SIGNAL, parameters: [...code, 0x01] }]);
89
+
90
+ expect(getResponsesFor({ command: proto.cmd.GET_MS, parameters: [] })).toEqual([{ command: proto.cmd.GET_MS, parameters: [...code, proto.P_MS_DIRECT] }]);
91
+
92
+ expect(getResponsesFor({ command: proto.cmd.SET_MS, parameters: [proto.P_MS_REMOTE] })).toEqual([{ command: proto.cmd.SET_MS, parameters: [...code, proto.P_MS_REMOTE] }]);
93
+
94
+ expect(getResponsesFor({ command: proto.cmd.GET_MS, parameters: [] })).toEqual([{ command: proto.cmd.GET_MS, parameters: [...code, proto.P_MS_REMOTE] }]);
95
+
96
+ expect(getResponsesFor({ command: proto.cmd.SET_MS, parameters: [proto.P_MS_DIRECT] })).toEqual([{ command: proto.cmd.SET_MS, parameters: [...code, proto.P_MS_DIRECT] }]);
97
+
98
+ const getTimeResponses = getResponsesFor({ command: proto.cmd.GET_TIME, parameters: [] });
99
+ expect(getTimeResponses.length).toBe(1);
100
+ if (getTimeResponses[0].mode !== undefined) {
101
+ throw new Error('message mode must be undefined');
102
+ }
103
+ expect(getTimeResponses[0].command).toBe(proto.cmd.GET_TIME);
104
+ expect(getTimeResponses[0].parameters.length).toBe(2 + 7);
105
+
106
+ const setTimeResponses = getResponsesFor({
107
+ command: proto.cmd.SET_TIME,
108
+ parameters: [0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]
109
+ });
110
+ expect(setTimeResponses.length).toBe(1);
111
+ if (setTimeResponses[0].mode !== undefined) {
112
+ throw new Error('message mode must be undefined');
113
+ }
114
+ expect(setTimeResponses[0].command).toBe(proto.cmd.SET_TIME);
115
+ expect(setTimeResponses[0].parameters.length).toBe(2 + 7);
116
+
117
+ expect(getResponsesFor({ command: proto.cmd.GET_SYS_VAL, parameters: [0, 4] })).toEqual([{ command: proto.cmd.GET_SYS_VAL, parameters: [...code, 0, 0x00, 0x02, 0xc1, 0xa1] }]);
118
+
119
+ expect(getResponsesFor({ command: proto.cmd.SET_SYS_VAL, parameters: [0, 0x01, 0x23, 0x45, 0x67] })).toEqual([{ command: proto.cmd.SET_SYS_VAL, parameters: [...code, 0] }]);
120
+
121
+ // expect(getResponsesFor(
122
+ // {command: proto.cmd.GET_SYS_VAL, parameters: [0, 4]},
123
+ // )).toEqual([
124
+ // {command: proto.cmd.GET_SYS_VAL, parameters: [...code, 0, 0x01, 0x23, 0x45, 0x67]},
125
+ // ]);
126
+
127
+ const cardInsertionMessage = testUtils.getRandomMessage({ numParameters: 1 });
128
+ const cardRetrievalParameters = [testUtils.getRandomByte()];
129
+ const fakeSiCard: IFakeSiCard = {
130
+ handleDetect: () => cardInsertionMessage,
131
+ handleRequest: (message: siProtocol.SiMessage) => (message.mode === undefined ? [{ command: message.command, parameters: cardRetrievalParameters }] : [])
132
+ };
133
+ myFakeSiMainStation.insertCard(fakeSiCard);
134
+
135
+ expect(getResponsesFor({ command: proto.cmd.GET_SI5, parameters: [] })).toEqual([{ command: proto.cmd.GET_SI5, parameters: [...code, ...cardRetrievalParameters] }]);
136
+
137
+ expect(getResponsesFor({ command: proto.cmd.ERASE_BDATA, parameters: [] })).toEqual([{ command: proto.cmd.ERASE_BDATA, parameters: [...code] }]);
138
+
139
+ expect(getResponsesFor({ command: proto.cmd.OFF, parameters: [] })).toEqual([]);
140
+ });
141
+ });
@@ -0,0 +1,118 @@
1
+ import { proto } from '../constants';
2
+ import * as utils from '../utils';
3
+ import type * as storage from '../storage';
4
+ import * as siProtocol from '../siProtocol';
5
+ import { type ISiStationStorageFields, siStationStorageDefinition } from '../SiStation/BaseSiStation';
6
+ import { type FakeSiMainStationEvents, FakeSiMainStationMessageEvent } from './IFakeSiMainStation';
7
+ import type { IFakeSiCard } from './FakeSiCard/IFakeSiCard';
8
+
9
+ export class FakeSiMainStation {
10
+ public storage: storage.ISiStorage<ISiStationStorageFields>;
11
+ public isMaster = true;
12
+ public dateOffset = 0;
13
+ public fakeSiCard?: IFakeSiCard;
14
+
15
+ constructor(storageArg: (number | undefined)[] | undefined) {
16
+ this.storage = siStationStorageDefinition(storageArg);
17
+ }
18
+
19
+ dispatchCardMessage(cardMessage: siProtocol.SiMessage): void {
20
+ if (cardMessage.mode !== undefined) {
21
+ return;
22
+ }
23
+ const message = {
24
+ command: cardMessage.command,
25
+ parameters: [...this.getCode(), ...cardMessage.parameters]
26
+ };
27
+ this.dispatchMessage(message);
28
+ }
29
+
30
+ dispatchMessage(message: siProtocol.SiMessage): void {
31
+ this.dispatchEvent('message', new FakeSiMainStationMessageEvent(this, message));
32
+ }
33
+
34
+ getCode(): number[] {
35
+ return [(this.storage.data.get(0x73)! & 0xc0) << 2, this.storage.data.get(0x72)!];
36
+ }
37
+
38
+ getDateTime(): Date {
39
+ return new Date(Date.now() + this.dateOffset);
40
+ }
41
+
42
+ insertCard(fakeSiCard: IFakeSiCard): void {
43
+ this.fakeSiCard = fakeSiCard;
44
+ const cardMessage = this.fakeSiCard.handleDetect();
45
+ this.dispatchCardMessage(cardMessage);
46
+ }
47
+
48
+ sendMessage(message: siProtocol.SiMessage): void {
49
+ if (message.mode !== undefined) {
50
+ return;
51
+ }
52
+ if (message.command === proto.cmd.SIGNAL) {
53
+ const numSignal = message.parameters[0];
54
+ this.dispatchMessage({
55
+ command: proto.cmd.SIGNAL,
56
+ parameters: [...this.getCode(), numSignal]
57
+ });
58
+ } else if (message.command === proto.cmd.GET_MS) {
59
+ this.dispatchMessage({
60
+ command: proto.cmd.GET_MS,
61
+ parameters: [...this.getCode(), this.isMaster ? proto.P_MS_DIRECT : proto.P_MS_REMOTE]
62
+ });
63
+ } else if (message.command === proto.cmd.SET_MS) {
64
+ this.isMaster = message.parameters[0] === proto.P_MS_DIRECT;
65
+ this.dispatchMessage({
66
+ command: proto.cmd.SET_MS,
67
+ parameters: [...this.getCode(), this.isMaster ? proto.P_MS_DIRECT : proto.P_MS_REMOTE]
68
+ });
69
+ } else if (message.command === proto.cmd.GET_TIME) {
70
+ this.dispatchMessage({
71
+ command: proto.cmd.GET_TIME,
72
+ parameters: [...this.getCode(), ...siProtocol.date2arr(this.getDateTime())]
73
+ });
74
+ } else if (message.command === proto.cmd.SET_TIME) {
75
+ const newTime = siProtocol.arr2date(message.parameters.slice(0, 7));
76
+ console.log(newTime); // TODO: Use new time to set dateOffset
77
+ this.dispatchMessage({
78
+ command: proto.cmd.SET_TIME,
79
+ parameters: [...this.getCode(), ...siProtocol.date2arr(this.getDateTime())]
80
+ });
81
+ } else if (message.command === proto.cmd.GET_SYS_VAL) {
82
+ const offset = message.parameters[0];
83
+ const length = message.parameters[1];
84
+ this.dispatchMessage({
85
+ command: proto.cmd.GET_SYS_VAL,
86
+ parameters: [...this.getCode(), offset, ...this.storage.data.slice(offset, offset + length)] as number[]
87
+ });
88
+ } else if (message.command === proto.cmd.SET_SYS_VAL) {
89
+ const offset = message.parameters[0];
90
+ const newContent = message.parameters.slice(1);
91
+ let data = this.storage.data;
92
+ newContent.forEach((newByte: number, index: number) => {
93
+ data = data.set(offset + index, newByte);
94
+ });
95
+ this.storage = siStationStorageDefinition(data);
96
+ this.dispatchMessage({
97
+ command: proto.cmd.SET_SYS_VAL,
98
+ parameters: [...this.getCode(), offset]
99
+ });
100
+ } else if (message.command === proto.cmd.GET_SI5 || message.command === proto.cmd.GET_SI6 || message.command === proto.cmd.GET_SI8) {
101
+ if (this.fakeSiCard === undefined) {
102
+ return;
103
+ }
104
+ const cardMessages = this.fakeSiCard.handleRequest(message);
105
+ cardMessages.forEach((cardMessage: siProtocol.SiMessage) => {
106
+ this.dispatchCardMessage(cardMessage);
107
+ });
108
+ } else if (message.command === proto.cmd.ERASE_BDATA) {
109
+ this.dispatchMessage({
110
+ command: proto.cmd.ERASE_BDATA,
111
+ parameters: [...this.getCode()]
112
+ });
113
+ }
114
+ }
115
+ }
116
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
117
+ export interface FakeSiMainStation extends utils.EventTarget<FakeSiMainStationEvents> {}
118
+ utils.applyMixins(FakeSiMainStation, [utils.EventTarget]);
@@ -0,0 +1,15 @@
1
+ import * as utils from '../utils';
2
+ import type * as siProtocol from '../siProtocol';
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
5
+ interface IFakeSiMainStation {}
6
+
7
+ export class FakeSiMainStationMessageEvent extends utils.Event<'message'> {
8
+ constructor(public fakeSiMainStation: IFakeSiMainStation, public message: siProtocol.SiMessage) {
9
+ super();
10
+ }
11
+ }
12
+
13
+ export type FakeSiMainStationEvents = {
14
+ message: FakeSiMainStationMessageEvent;
15
+ };
@@ -0,0 +1,2 @@
1
+ export { FakeSiMainStation } from './FakeSiMainStation';
2
+ export * from './FakeSiCard';
package/src/index.ts ADDED
@@ -0,0 +1,24 @@
1
+ /* global si */
2
+ /* exported si */
3
+
4
+ import * as siCardExports from './SiCard';
5
+ import * as siDeviceExports from './SiDevice';
6
+ import * as fakesExports from './fakes';
7
+ import * as siStationExports from './SiStation';
8
+ import * as storageExports from './storage';
9
+ import * as constants from './constants';
10
+ import * as siProtocol from './siProtocol';
11
+ import * as utils from './utils';
12
+
13
+ export const si = {
14
+ ...siCardExports,
15
+ ...siDeviceExports,
16
+ ...fakesExports,
17
+ ...siStationExports,
18
+ ...storageExports,
19
+ constants: constants,
20
+ protocol: siProtocol,
21
+ utils: utils
22
+ };
23
+
24
+ export default si;