motion-master-client 0.0.21 → 0.0.22

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 (126) hide show
  1. package/.babelrc +3 -0
  2. package/.eslintrc.json +18 -0
  3. package/jest.config.ts +16 -0
  4. package/motion-master.proto +1822 -0
  5. package/package.json +2 -17
  6. package/project.json +45 -0
  7. package/src/lib/cia402.spec.ts +77 -0
  8. package/src/lib/cia402.ts +414 -0
  9. package/src/lib/config-file.spec.ts +114 -0
  10. package/src/lib/config-file.ts +63 -0
  11. package/src/lib/device-log-line.ts +5 -0
  12. package/src/lib/device-parameter.spec.ts +85 -0
  13. package/src/lib/device-parameter.ts +79 -0
  14. package/src/lib/device.ts +10 -0
  15. package/src/lib/hardware-description.spec.ts +253 -0
  16. package/src/lib/hardware-description.ts +129 -0
  17. package/src/lib/logger.ts +5 -0
  18. package/src/lib/monitoring-config.ts +6 -0
  19. package/src/lib/{monitoring-entry.d.ts → monitoring-entry.ts} +5 -4
  20. package/src/lib/motion-master-client.ts +221 -0
  21. package/src/lib/motion-master-pub-sub-client.ts +95 -0
  22. package/src/lib/motion-master-pub-sub-socket.ts +40 -0
  23. package/src/lib/motion-master-pub-sub-web-socket.ts +78 -0
  24. package/src/lib/motion-master-pub-sub-worker-socket.ts +51 -0
  25. package/src/lib/motion-master-req-res-client.spec.ts +740 -0
  26. package/src/lib/motion-master-req-res-client.ts +2120 -0
  27. package/src/lib/motion-master-req-res-socket.ts +62 -0
  28. package/src/lib/motion-master-req-res-web-socket.ts +124 -0
  29. package/src/lib/motion-master-req-res-worker-socket.ts +87 -0
  30. package/src/lib/motion-master.proto.js +2 -2
  31. package/src/lib/operators.ts +90 -0
  32. package/src/lib/options.ts +12 -0
  33. package/src/lib/parameter.spec.ts +160 -0
  34. package/src/lib/parameter.ts +170 -0
  35. package/src/lib/product-id-range.ts +8 -0
  36. package/src/lib/request-status-resolver.ts +403 -0
  37. package/src/lib/system-log-line.ts +9 -0
  38. package/src/lib/{types.d.ts → types.ts} +74 -143
  39. package/src/lib/urls.ts +6 -0
  40. package/src/lib/util.ts +305 -0
  41. package/tsconfig.json +23 -0
  42. package/tsconfig.lib.json +10 -0
  43. package/tsconfig.spec.json +20 -0
  44. package/typedoc.json +10 -0
  45. package/src/index.js +0 -29
  46. package/src/index.js.map +0 -1
  47. package/src/lib/cia402.d.ts +0 -182
  48. package/src/lib/cia402.js +0 -392
  49. package/src/lib/cia402.js.map +0 -1
  50. package/src/lib/config-file.d.ts +0 -13
  51. package/src/lib/config-file.js +0 -50
  52. package/src/lib/config-file.js.map +0 -1
  53. package/src/lib/device-log-line.d.ts +0 -5
  54. package/src/lib/device-log-line.js +0 -3
  55. package/src/lib/device-log-line.js.map +0 -1
  56. package/src/lib/device-parameter.d.ts +0 -56
  57. package/src/lib/device-parameter.js +0 -39
  58. package/src/lib/device-parameter.js.map +0 -1
  59. package/src/lib/device.d.ts +0 -9
  60. package/src/lib/device.js +0 -3
  61. package/src/lib/device.js.map +0 -1
  62. package/src/lib/hardware-description.d.ts +0 -41
  63. package/src/lib/hardware-description.js +0 -94
  64. package/src/lib/hardware-description.js.map +0 -1
  65. package/src/lib/logger.d.ts +0 -1
  66. package/src/lib/logger.js +0 -8
  67. package/src/lib/logger.js.map +0 -1
  68. package/src/lib/monitoring-config.d.ts +0 -6
  69. package/src/lib/monitoring-config.js +0 -3
  70. package/src/lib/monitoring-config.js.map +0 -1
  71. package/src/lib/monitoring-entry.js +0 -3
  72. package/src/lib/monitoring-entry.js.map +0 -1
  73. package/src/lib/motion-master-client.d.ts +0 -52
  74. package/src/lib/motion-master-client.js +0 -151
  75. package/src/lib/motion-master-client.js.map +0 -1
  76. package/src/lib/motion-master-pub-sub-client.d.ts +0 -16
  77. package/src/lib/motion-master-pub-sub-client.js +0 -68
  78. package/src/lib/motion-master-pub-sub-client.js.map +0 -1
  79. package/src/lib/motion-master-pub-sub-socket.d.ts +0 -34
  80. package/src/lib/motion-master-pub-sub-socket.js +0 -3
  81. package/src/lib/motion-master-pub-sub-socket.js.map +0 -1
  82. package/src/lib/motion-master-pub-sub-web-socket.d.ts +0 -14
  83. package/src/lib/motion-master-pub-sub-web-socket.js +0 -67
  84. package/src/lib/motion-master-pub-sub-web-socket.js.map +0 -1
  85. package/src/lib/motion-master-pub-sub-worker-socket.d.ts +0 -14
  86. package/src/lib/motion-master-pub-sub-worker-socket.js +0 -42
  87. package/src/lib/motion-master-pub-sub-worker-socket.js.map +0 -1
  88. package/src/lib/motion-master-req-res-client.d.ts +0 -920
  89. package/src/lib/motion-master-req-res-client.js +0 -1680
  90. package/src/lib/motion-master-req-res-client.js.map +0 -1
  91. package/src/lib/motion-master-req-res-socket.d.ts +0 -52
  92. package/src/lib/motion-master-req-res-socket.js +0 -3
  93. package/src/lib/motion-master-req-res-socket.js.map +0 -1
  94. package/src/lib/motion-master-req-res-web-socket.d.ts +0 -24
  95. package/src/lib/motion-master-req-res-web-socket.js +0 -99
  96. package/src/lib/motion-master-req-res-web-socket.js.map +0 -1
  97. package/src/lib/motion-master-req-res-worker-socket.d.ts +0 -20
  98. package/src/lib/motion-master-req-res-worker-socket.js +0 -69
  99. package/src/lib/motion-master-req-res-worker-socket.js.map +0 -1
  100. package/src/lib/operators.d.ts +0 -18
  101. package/src/lib/operators.js +0 -76
  102. package/src/lib/operators.js.map +0 -1
  103. package/src/lib/options.d.ts +0 -10
  104. package/src/lib/options.js +0 -14
  105. package/src/lib/options.js.map +0 -1
  106. package/src/lib/parameter.d.ts +0 -72
  107. package/src/lib/parameter.js +0 -119
  108. package/src/lib/parameter.js.map +0 -1
  109. package/src/lib/product-id-range.d.ts +0 -7
  110. package/src/lib/product-id-range.js +0 -12
  111. package/src/lib/product-id-range.js.map +0 -1
  112. package/src/lib/request-status-resolver.d.ts +0 -4
  113. package/src/lib/request-status-resolver.js +0 -345
  114. package/src/lib/request-status-resolver.js.map +0 -1
  115. package/src/lib/system-log-line.d.ts +0 -9
  116. package/src/lib/system-log-line.js +0 -3
  117. package/src/lib/system-log-line.js.map +0 -1
  118. package/src/lib/types.js +0 -28
  119. package/src/lib/types.js.map +0 -1
  120. package/src/lib/urls.d.ts +0 -3
  121. package/src/lib/urls.js +0 -10
  122. package/src/lib/urls.js.map +0 -1
  123. package/src/lib/util.d.ts +0 -40
  124. package/src/lib/util.js +0 -316
  125. package/src/lib/util.js.map +0 -1
  126. /package/src/{index.d.ts → index.ts} +0 -0
@@ -0,0 +1,62 @@
1
+ import { BehaviorSubject, Observable } from "rxjs";
2
+ import { IMotionMasterMessage } from "./types";
3
+
4
+ export interface MotionMasterReqResSocket {
5
+ /**
6
+ * Emits a boolean value when socket gets opened or closed.
7
+ */
8
+ readonly opened$: BehaviorSubject<boolean>;
9
+
10
+ /**
11
+ * Motion Master is considered alive when it sends messages in regular time interval.
12
+ */
13
+ readonly alive$: BehaviorSubject<boolean>;
14
+
15
+ /**
16
+ * Decoded message instances coming from Motion Master.
17
+ */
18
+ readonly message$: Observable<IMotionMasterMessage>;
19
+
20
+ /**
21
+ * URL set in call to open.
22
+ */
23
+ get url(): string | undefined;
24
+
25
+ /**
26
+ * Send ping system messages to Motion Master with a fixed time delay between each call.
27
+ */
28
+ get pingSystemInterval(): number | undefined;
29
+
30
+ /**
31
+ * How long in milliseconds to consider Motion Master alive.
32
+ */
33
+ get systemAliveTimeout(): number | undefined;
34
+
35
+ /**
36
+ * Open socket.
37
+ *
38
+ * The connected$ and eventually alive$ observables will emit true values.
39
+ */
40
+ open(url: string, pingSystemInterval?: number, systemAliveTimeout?: number): void;
41
+
42
+ /**
43
+ * Close socket.
44
+ *
45
+ * The opened$ and alive$ observables will emit false values.
46
+ */
47
+ close(): void;
48
+
49
+ /**
50
+ * Reopen socket.
51
+ *
52
+ * Reopen if closed using the same url, pingSystemInterval, and systemAliveTimeout previously set in open.
53
+ */
54
+ reopen(): void;
55
+
56
+ /**
57
+ * Send an instance of a message.
58
+ *
59
+ * Subscribe to the message$ observable in order to receive the response messages.
60
+ */
61
+ send(message: IMotionMasterMessage): void;
62
+ }
@@ -0,0 +1,124 @@
1
+ import { BehaviorSubject, filter, interval, map, Subject, Subscription, tap } from "rxjs";
2
+ import { webSocket, WebSocketSubject, WebSocketSubjectConfig } from "rxjs/webSocket";
3
+ import { logger } from "./logger";
4
+ import { MotionMasterReqResSocket } from "./motion-master-req-res-socket";
5
+ import { IMotionMasterMessage, MotionMasterMessage } from "./types";
6
+ import { convertMotionMasterMessageToLoggerContext, createPlainObjectFromMotionMasterMessage } from "./util";
7
+
8
+ export class MotionMasterReqResWebSocket implements MotionMasterReqResSocket {
9
+
10
+ readonly opened$ = new BehaviorSubject<boolean>(false);
11
+
12
+ readonly alive$ = new BehaviorSubject<boolean>(false);
13
+
14
+ readonly message$ = new Subject<IMotionMasterMessage>();
15
+
16
+ private aliveTimeoutId = 0;
17
+
18
+ private pingSystemObserver = { next: () => this.send({ request: { pingSystem: {} } }) };
19
+
20
+ private pingSystemSubscription = new Subscription();
21
+
22
+ private webSocket$?: WebSocketSubject<Uint8Array>;
23
+
24
+ private _url?: string;
25
+ private _pingSystemInterval?: number;
26
+ private _systemAliveTimeout?: number;
27
+
28
+ get url(): string | undefined {
29
+ return this._url;
30
+ }
31
+
32
+ get pingSystemInterval(): number | undefined {
33
+ return this._pingSystemInterval;
34
+ }
35
+
36
+ get systemAliveTimeout(): number | undefined {
37
+ return this._systemAliveTimeout;
38
+ }
39
+
40
+ open(url: string, pingSystemInterval = 250, systemAliveTimeout = 1000): void {
41
+ if (this.opened$.getValue() === true) {
42
+ throw new Error('MotionMasterReqResWebSocket is already opened!');
43
+ }
44
+
45
+ this._url = url;
46
+ this._pingSystemInterval = pingSystemInterval;
47
+ this._systemAliveTimeout = systemAliveTimeout;
48
+
49
+ this.webSocket$ = this.createWebSocket(url, pingSystemInterval);
50
+
51
+ this.webSocket$?.pipe(
52
+ map((data: Uint8Array) => MotionMasterMessage.decode(data)),
53
+ tap(() => this.keepalive(systemAliveTimeout)),
54
+ filter((message) => !(message.status?.systemPong)),
55
+ map(message => createPlainObjectFromMotionMasterMessage(message)),
56
+ tap((message) => {
57
+ const context = convertMotionMasterMessageToLoggerContext(message);
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ logger.info(context as any, `Received ${context.name}`);
60
+ }),
61
+ ).subscribe({
62
+ next: (message) => this.message$.next(message),
63
+ error: (err) => {
64
+ console.error(err);
65
+ },
66
+ });
67
+ }
68
+
69
+ close(): void {
70
+ clearTimeout(this.aliveTimeoutId);
71
+ this.pingSystemSubscription.unsubscribe();
72
+ this.webSocket$?.unsubscribe();
73
+ }
74
+
75
+ reopen() {
76
+ if (this.url && this.opened$.value === false) {
77
+ this.open(this.url, this.pingSystemInterval, this.systemAliveTimeout);
78
+ }
79
+ }
80
+
81
+ send(message: IMotionMasterMessage): void {
82
+ if (!message.request?.pingSystem) {
83
+ const context = convertMotionMasterMessageToLoggerContext(message);
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
+ logger.info(context as any, `Send ${context.name}`);
86
+ }
87
+
88
+ this.webSocket$?.next(MotionMasterMessage.encode(message).finish());
89
+ }
90
+
91
+ private createWebSocket(url: string, pingSystemInterval = 250) {
92
+ const webSocketConfig: WebSocketSubjectConfig<Uint8Array> = {
93
+ binaryType: 'arraybuffer',
94
+ closeObserver: {
95
+ next: () => {
96
+ this.opened$.next(false);
97
+ logger.info(`Connection closed ${url}`);
98
+ },
99
+ },
100
+ deserializer: (e: MessageEvent) => new Uint8Array(e.data as unknown as Iterable<number>),
101
+ openObserver: {
102
+ next: () => {
103
+ this.pingSystemSubscription = interval(pingSystemInterval)
104
+ .subscribe(this.pingSystemObserver);
105
+ this.opened$.next(true);
106
+ logger.info(`Connection opened ${url}`);
107
+ },
108
+ },
109
+ serializer: (value: Uint8Array) => value,
110
+ url,
111
+ };
112
+
113
+ return webSocket(webSocketConfig);
114
+ }
115
+
116
+ private keepalive(systemAliveTimeout = 1000) {
117
+ if (this.alive$.getValue() === false) {
118
+ this.alive$.next(true);
119
+ }
120
+ clearTimeout(this.aliveTimeoutId);
121
+ this.aliveTimeoutId = setTimeout(() => this.alive$.next(false), systemAliveTimeout) as unknown as number;
122
+ }
123
+
124
+ }
@@ -0,0 +1,87 @@
1
+ import { BehaviorSubject, Subject } from "rxjs";
2
+ import { logger } from "./logger";
3
+ import { MotionMasterReqResSocket } from "./motion-master-req-res-socket";
4
+ import { IMotionMasterMessage } from "./types";
5
+ import { convertMotionMasterMessageToLoggerContext } from "./util";
6
+
7
+ export class MotionMasterReqResWorkerSocket implements MotionMasterReqResSocket {
8
+
9
+ readonly opened$ = new BehaviorSubject<boolean>(false);
10
+
11
+ readonly alive$ = new BehaviorSubject<boolean>(false);
12
+
13
+ readonly message$ = new Subject<IMotionMasterMessage>();
14
+
15
+ private _url?: string;
16
+ private _pingSystemInterval?: number;
17
+ private _systemAliveTimeout?: number;
18
+
19
+ constructor(
20
+ public readonly worker: Worker,
21
+ ) { }
22
+
23
+ get url(): string | undefined {
24
+ return this._url;
25
+ }
26
+
27
+ get pingSystemInterval(): number | undefined {
28
+ return this._pingSystemInterval;
29
+ }
30
+
31
+ get systemAliveTimeout(): number | undefined {
32
+ return this._systemAliveTimeout;
33
+ }
34
+
35
+ open(url: string, pingSystemInterval = 250, systemAliveTimeout = 1000): void {
36
+ if (this.opened$.getValue() === true) {
37
+ throw new Error('MotionMasterReqResWorkerSocket is already opened!');
38
+ }
39
+
40
+ this._url = url;
41
+ this._pingSystemInterval = pingSystemInterval;
42
+ this._systemAliveTimeout = systemAliveTimeout;
43
+
44
+ this.worker.onmessage = ({ data }) => {
45
+ if ('opened' in data) {
46
+ const { opened } = data;
47
+ this.opened$.next(opened);
48
+ logger.info(opened ? `Opened req/res 🔌 ${url}` : `Closed req/res 🔌 ${url}`);
49
+ } else if ('alive' in data) {
50
+ const { alive } = data;
51
+ this.alive$.next(alive);
52
+ logger.info(alive ? `System is alive 💓` : 'System is as dead as 🦤');
53
+ } else if ('message' in data) {
54
+ const { message } = data;
55
+
56
+ const context = convertMotionMasterMessageToLoggerContext(message);
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ logger.info(context as any, `Received ${context.name}`);
59
+
60
+ this.message$.next(message);
61
+ }
62
+ }
63
+
64
+ this.worker.postMessage({
65
+ open: { url, pingSystemInterval, systemAliveTimeout },
66
+ });
67
+ }
68
+
69
+ reopen() {
70
+ if (this.url && this.opened$.value === false) {
71
+ this.open(this.url, this.pingSystemInterval, this.systemAliveTimeout);
72
+ }
73
+ }
74
+
75
+ close(): void {
76
+ this.worker.postMessage({ close: true });
77
+ }
78
+
79
+ send(message: IMotionMasterMessage): void {
80
+ const context = convertMotionMasterMessageToLoggerContext(message);
81
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
+ logger.info(context as any, `Send ${context.name}`);
83
+
84
+ this.worker.postMessage({ message });
85
+ }
86
+
87
+ }
@@ -4,8 +4,8 @@
4
4
  var $protobuf = require("protobufjs/minimal");
5
5
 
6
6
  // @ts-expect-error Explicitly disable long.js support
7
- protobufjs.util.Long = undefined;
8
- protobufjs.configure();
7
+ $protobuf.util.Long = undefined;
8
+ $protobuf.configure();
9
9
 
10
10
  // Common aliases
11
11
  const $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util;
@@ -0,0 +1,90 @@
1
+ import { pipe, filter, map, timeout, takeWhile } from 'rxjs';
2
+ import { getParameterValue } from './util';
3
+ import { requestStatusResolver } from './request-status-resolver';
4
+ import { IMotionMasterMessage, MotionMasterMessage, ParameterValueType, StatusKey } from './types';
5
+ import { Parameter } from './parameter';
6
+
7
+ export function selectMotionMasterMessageStatusByMessageId<T>(key: StatusKey, id?: string) {
8
+ return pipe(
9
+ filter((message: IMotionMasterMessage) => message.id === id),
10
+ map((message) => {
11
+ // Handling the special case when Motion Master sends SystemEvent instead of e.g. DeviceParameterInfo status for
12
+ // GetDeviceParameterInfo request message. This typically happens when request contains an invalid device address.
13
+ if (message.status?.systemEvent && key !== 'systemEvent') {
14
+ if (message.status.systemEvent.error) {
15
+ throw new Error(`Error selecting message: ${message.status.systemEvent.error.message ?? ''}`);
16
+ } else {
17
+ throw new Error(`Error selecting message. Received System Event instead of "${key.toString()}": ${JSON.stringify(message)}`);
18
+ }
19
+ }
20
+ return message.status?.[key] as T;
21
+ }),
22
+ );
23
+ }
24
+
25
+ export function extendStatus<T>(data: { statusKey: StatusKey, messageId: string }) {
26
+ return pipe(
27
+ map((status: T) => {
28
+ const { statusKey, messageId } = data;
29
+ const request = requestStatusResolver[statusKey]?.<T>(status);
30
+ return { ...status, messageId, request };
31
+ }),
32
+ );
33
+ }
34
+
35
+ export function transformMotionMasterMessageToStatus<T>(statusKey: StatusKey, requestTimeout: number, messageId: string) {
36
+ if (typeof requestTimeout !== 'number') {
37
+ throw new Error(`Invalid requestTimeout=${requestTimeout} provided for ${statusKey}`);
38
+ }
39
+
40
+ return pipe(
41
+ selectMotionMasterMessageStatusByMessageId<T>(statusKey, messageId),
42
+ timeout(requestTimeout),
43
+ extendStatus({ statusKey, messageId }),
44
+ takeWhile((status) => status.request !== 'succeeded' && status.request !== 'failed', true),
45
+ );
46
+ }
47
+
48
+ export function selectMotionMasterMessageByTopic<T>(topic: string) {
49
+ return pipe(
50
+ filter(([t]: [string, T]) => t === topic),
51
+ map(([, data]) => data),
52
+ );
53
+ }
54
+
55
+ export function selectMotionMasterMessageStatusByKey<T>(key: keyof MotionMasterMessage.IStatus) {
56
+ return pipe(
57
+ filter((message: IMotionMasterMessage) => !!(message.status && message.status[key])),
58
+ map((message) => message.status?.[key] as T),
59
+ );
60
+ }
61
+
62
+ export function mapMonitoringParameterValuesStatusMessageToParameterValues() {
63
+ return pipe(
64
+ map((message: IMotionMasterMessage) => {
65
+ if (message.status?.monitoringParameterValues) {
66
+ const { timestamp, deviceParameterValues } = message.status.monitoringParameterValues;
67
+ return [
68
+ timestamp,
69
+ ...(deviceParameterValues?.parameterValues?.map((parameterValue) => getParameterValue(parameterValue)) ?? []),
70
+ ] as ParameterValueType[];
71
+ }
72
+ return [];
73
+ }),
74
+ );
75
+ }
76
+
77
+ export function mapMonitoringParameterValuesStatusMessageToDeviceParameters() {
78
+ return pipe(
79
+ map((message: IMotionMasterMessage) => {
80
+ if (message.status?.monitoringParameterValues) {
81
+ const { timestamp, deviceParameterValues } = message.status.monitoringParameterValues;
82
+ return [
83
+ timestamp,
84
+ ...deviceParameterValues?.parameterValues ?? [],
85
+ ] as Parameter[];
86
+ }
87
+ return [];
88
+ }),
89
+ );
90
+ }
@@ -0,0 +1,12 @@
1
+ import { MotionMasterMessage } from './types';
2
+
3
+ export const parameterValueTypeOptions = {
4
+ 'intValue': 'intValue',
5
+ 'uintValue': 'uintValue',
6
+ 'floatValue': 'floatValue',
7
+ 'stringValue': 'stringValue',
8
+ 'rawValue': 'rawValue',
9
+ };
10
+
11
+ export const ethercatNetworkStateOptions = MotionMasterMessage.Status.EthercatNetworkState.State;
12
+ export const positionControllerTypeOptions = MotionMasterMessage.Request.ComputeAutoTuningGains.PositionParameters.ControllerType;
@@ -0,0 +1,160 @@
1
+ import { differenceParameters, intersectionParameters, makeParameterId, splitParameterId } from "./parameter";
2
+
3
+ describe('parameter', () => {
4
+
5
+ describe('makeParameterId', () => {
6
+
7
+ test.each<[[(number | null | undefined), (number | null | undefined)], string]>([
8
+ [[0x1234, 123], '0x1234:7B'],
9
+ [[23, 23], '0x0017:17'],
10
+ [[0, 0], '0x0000:00'],
11
+ [[0x2000, undefined], '0x2000:00'],
12
+ [[0x2003, null], '0x2003:00'],
13
+ [[0x10000, 3], '0x00010000:03'],
14
+ [[0x100F0, undefined], '0x000100F0:00'],
15
+ ])('should for tuple %j return "%s"', (tuple, expected) => {
16
+ const result = makeParameterId(tuple);
17
+
18
+ expect(result).toBe(expected);
19
+ });
20
+
21
+ test.each([
22
+ [{}, '0x0000:00'],
23
+ [{ index: 0x2004, subindex: 4 }, '0x2004:04'],
24
+ [{ index: 0x1024 }, '0x1024:00'],
25
+ ])('should for parameter %j return "%s"', (parameter, expected) => {
26
+ const result = makeParameterId(parameter);
27
+
28
+ expect(result).toBe(expected);
29
+ });
30
+
31
+ test.each([
32
+ [0x1234, 123, '0x1234:7B'],
33
+ [23, 23, '0x0017:17'],
34
+ [0, 0, '0x0000:00'],
35
+ [0x2000, undefined, '0x2000:00'],
36
+ [0x2003, null, '0x2003:00'],
37
+ [0x10000, 3, '0x00010000:03'],
38
+ [0x100F0, undefined, '0x000100F0:00'],
39
+ ])('should for index %d and subindex %d return "%s"', (index, subindex, expected) => {
40
+ const result = makeParameterId(index, subindex);
41
+
42
+ expect(result).toBe(expected);
43
+ });
44
+
45
+ it('should throw if index is less than 0', () => {
46
+ expect(() => {
47
+ makeParameterId(-123, 0);
48
+ }).toThrow();
49
+ });
50
+
51
+ it('should throw if subindex is less than 0', () => {
52
+ expect(() => {
53
+ makeParameterId(0x1024, -3);
54
+ }).toThrow();
55
+ });
56
+
57
+ });
58
+
59
+ describe('splitParameterId', () => {
60
+
61
+ test.each([
62
+ ['0x1234:7B', [4660, 123]],
63
+ ['0x000100F0:00', [65776, 0]],
64
+ ])('should for id "%s" return tuple %j', (id, expected) => {
65
+ const result = splitParameterId(id);
66
+
67
+ expect(result).toEqual(expected);
68
+ });
69
+
70
+ test.each([
71
+ ['0x12:7B'],
72
+ ['0x1234:F'],
73
+ ['0x1234:FG'],
74
+ ['0x1H34:FF'],
75
+ ])('should throw error for id "%s"', (id) => {
76
+ expect(() => splitParameterId(id)).toThrow();
77
+ });
78
+
79
+ });
80
+
81
+ describe('differenceParameters', () => {
82
+ it('should return an empty array if there are no differences', () => {
83
+ const parameters = differenceParameters(
84
+ [
85
+ { index: 0x2030, subindex: 1 },
86
+ { index: 0x2031, subindex: 1 },
87
+ ],
88
+ [
89
+ { index: 0x2030, subindex: 1 },
90
+ { index: 0x2031, subindex: 1 },
91
+ ],
92
+ );
93
+
94
+ expect(parameters).toEqual([]);
95
+ });
96
+
97
+ it('should return an array of differences', () => {
98
+ const parameters = differenceParameters(
99
+ [
100
+ { index: 0x2030, subindex: 1 },
101
+ { index: 0x2031, subindex: 1 },
102
+ { index: 0x6040, subindex: 0 },
103
+ { index: 0x6091, subindex: 0 },
104
+ ],
105
+ [
106
+ { index: 0x2031, subindex: 1 },
107
+ { index: 0x6040, subindex: 0 },
108
+ { index: 0x6041, subindex: 0 },
109
+ ],
110
+ );
111
+
112
+ expect(parameters).toEqual([
113
+ { index: 0x2030, subindex: 1 },
114
+ { index: 0x6091, subindex: 0 },
115
+ ]);
116
+ });
117
+ });
118
+
119
+ fdescribe('intersectionParameters', () => {
120
+ it('should return full array if there are no differences', () => {
121
+ const parameters = intersectionParameters(
122
+ [
123
+ { index: 0x2030, subindex: 1 },
124
+ { index: 0x2031, subindex: 1 },
125
+ ],
126
+ [
127
+ { index: 0x2030, subindex: 1 },
128
+ { index: 0x2031, subindex: 1 },
129
+ ],
130
+ );
131
+
132
+ expect(parameters).toEqual([
133
+ { index: 0x2030, subindex: 1 },
134
+ { index: 0x2031, subindex: 1 },
135
+ ]);
136
+ });
137
+
138
+ it('should return an array excluding differences', () => {
139
+ const parameters = intersectionParameters(
140
+ [
141
+ { index: 0x2031, subindex: 1 },
142
+ { index: 0x6040, subindex: 0 },
143
+ { index: 0x6041, subindex: 0 },
144
+ ],
145
+ [
146
+ { index: 0x2030, subindex: 1 },
147
+ { index: 0x2031, subindex: 1 },
148
+ { index: 0x6040, subindex: 0 },
149
+ { index: 0x6091, subindex: 0 },
150
+ ],
151
+ );
152
+
153
+ expect(parameters).toEqual([
154
+ { index: 0x2031, subindex: 1 },
155
+ { index: 0x6040, subindex: 0 },
156
+ ]);
157
+ });
158
+ });
159
+
160
+ });