motion-master-client 0.0.3 → 0.0.6

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 (118) hide show
  1. package/README.md +123 -49
  2. package/package.json +21 -6
  3. package/src/{index.ts → index.d.ts} +23 -22
  4. package/src/index.js +27 -0
  5. package/src/index.js.map +1 -0
  6. package/src/lib/cia402.d.ts +30 -0
  7. package/src/lib/cia402.js +112 -0
  8. package/src/lib/cia402.js.map +1 -0
  9. package/src/lib/device-log-line.d.ts +5 -0
  10. package/src/lib/device-log-line.js +3 -0
  11. package/src/lib/device-log-line.js.map +1 -0
  12. package/src/lib/{device-parameter.ts → device-parameter.d.ts} +56 -69
  13. package/src/lib/device-parameter.js +39 -0
  14. package/src/lib/device-parameter.js.map +1 -0
  15. package/src/lib/device.d.ts +8 -0
  16. package/src/lib/device.js +3 -0
  17. package/src/lib/device.js.map +1 -0
  18. package/src/lib/examples/get-firmware-version-for-all-devices.d.ts +1 -0
  19. package/src/lib/examples/get-firmware-version-for-all-devices.js +15 -0
  20. package/src/lib/examples/get-firmware-version-for-all-devices.js.map +1 -0
  21. package/src/lib/hardware-description.d.ts +37 -0
  22. package/src/lib/hardware-description.js +48 -0
  23. package/src/lib/hardware-description.js.map +1 -0
  24. package/src/lib/logger.d.ts +1 -0
  25. package/src/lib/logger.js +8 -0
  26. package/src/lib/logger.js.map +1 -0
  27. package/src/lib/monitoring-config.d.ts +6 -0
  28. package/src/lib/monitoring-config.js +3 -0
  29. package/src/lib/monitoring-config.js.map +1 -0
  30. package/src/lib/monitoring-entry.d.ts +9 -0
  31. package/src/lib/monitoring-entry.js +3 -0
  32. package/src/lib/monitoring-entry.js.map +1 -0
  33. package/src/lib/motion-master-client.d.ts +18 -0
  34. package/src/lib/motion-master-client.js +73 -0
  35. package/src/lib/motion-master-client.js.map +1 -0
  36. package/src/lib/motion-master-pub-sub-client.d.ts +16 -0
  37. package/src/lib/motion-master-pub-sub-client.js +68 -0
  38. package/src/lib/motion-master-pub-sub-client.js.map +1 -0
  39. package/src/lib/motion-master-pub-sub-socket.d.ts +28 -0
  40. package/src/lib/motion-master-pub-sub-socket.js +3 -0
  41. package/src/lib/motion-master-pub-sub-socket.js.map +1 -0
  42. package/src/lib/motion-master-pub-sub-web-socket.d.ts +13 -0
  43. package/src/lib/motion-master-pub-sub-web-socket.js +61 -0
  44. package/src/lib/motion-master-pub-sub-web-socket.js.map +1 -0
  45. package/src/lib/motion-master-pub-sub-worker-socket.d.ts +13 -0
  46. package/src/lib/motion-master-pub-sub-worker-socket.js +37 -0
  47. package/src/lib/motion-master-pub-sub-worker-socket.js.map +1 -0
  48. package/src/lib/motion-master-req-res-client.d.ts +956 -0
  49. package/src/lib/motion-master-req-res-client.js +1521 -0
  50. package/src/lib/motion-master-req-res-client.js.map +1 -0
  51. package/src/lib/motion-master-req-res-socket.d.ts +38 -0
  52. package/src/lib/motion-master-req-res-socket.js +3 -0
  53. package/src/lib/motion-master-req-res-socket.js.map +1 -0
  54. package/src/lib/motion-master-req-res-web-socket.d.ts +20 -0
  55. package/src/lib/motion-master-req-res-web-socket.js +100 -0
  56. package/src/lib/motion-master-req-res-web-socket.js.map +1 -0
  57. package/src/lib/motion-master-req-res-worker-socket.d.ts +15 -0
  58. package/src/lib/motion-master-req-res-worker-socket.js +53 -0
  59. package/src/lib/motion-master-req-res-worker-socket.js.map +1 -0
  60. package/src/lib/motion-master.proto.js +5 -3
  61. package/src/lib/operators.d.ts +21 -0
  62. package/src/lib/operators.js +71 -0
  63. package/src/lib/operators.js.map +1 -0
  64. package/src/lib/options.d.ts +34 -0
  65. package/src/lib/options.js +38 -0
  66. package/src/lib/options.js.map +1 -0
  67. package/src/lib/parameter.d.ts +65 -0
  68. package/src/lib/parameter.js +70 -0
  69. package/src/lib/parameter.js.map +1 -0
  70. package/src/lib/request-status-resolver.d.ts +4 -0
  71. package/src/lib/request-status-resolver.js +345 -0
  72. package/src/lib/request-status-resolver.js.map +1 -0
  73. package/src/lib/system-log-line.d.ts +8 -0
  74. package/src/lib/system-log-line.js +3 -0
  75. package/src/lib/system-log-line.js.map +1 -0
  76. package/src/lib/types.d.ts +50 -0
  77. package/src/lib/types.js +23 -0
  78. package/src/lib/types.js.map +1 -0
  79. package/src/lib/urls.d.ts +3 -0
  80. package/src/lib/urls.js +10 -0
  81. package/src/lib/urls.js.map +1 -0
  82. package/src/lib/util.d.ts +30 -0
  83. package/src/lib/util.js +279 -0
  84. package/src/lib/util.js.map +1 -0
  85. package/.babelrc +0 -3
  86. package/.eslintrc.json +0 -18
  87. package/jest.config.ts +0 -15
  88. package/motion-master.proto +0 -1822
  89. package/src/lib/device-log-line.ts +0 -5
  90. package/src/lib/device-parameter.spec.ts +0 -85
  91. package/src/lib/hardware-description.spec.ts +0 -223
  92. package/src/lib/hardware-description.ts +0 -79
  93. package/src/lib/logger.ts +0 -5
  94. package/src/lib/monitoring-config.ts +0 -6
  95. package/src/lib/monitoring-entry.ts +0 -11
  96. package/src/lib/motion-master-client.ts +0 -94
  97. package/src/lib/motion-master-pub-sub-client.ts +0 -94
  98. package/src/lib/motion-master-pub-sub-socket.ts +0 -33
  99. package/src/lib/motion-master-pub-sub-web-socket.ts +0 -71
  100. package/src/lib/motion-master-pub-sub-worker-socket.ts +0 -58
  101. package/src/lib/motion-master-req-res-client.spec.ts +0 -126
  102. package/src/lib/motion-master-req-res-client.ts +0 -1990
  103. package/src/lib/motion-master-req-res-socket.ts +0 -45
  104. package/src/lib/motion-master-req-res-web-socket.ts +0 -119
  105. package/src/lib/motion-master-req-res-worker-socket.ts +0 -63
  106. package/src/lib/operators.ts +0 -93
  107. package/src/lib/options.ts +0 -12
  108. package/src/lib/parameter.spec.ts +0 -81
  109. package/src/lib/parameter.ts +0 -119
  110. package/src/lib/request-status-resolver.ts +0 -380
  111. package/src/lib/system-log-line.ts +0 -8
  112. package/src/lib/types.ts +0 -58
  113. package/src/lib/urls.ts +0 -6
  114. package/src/lib/util.ts +0 -261
  115. package/tsconfig.json +0 -22
  116. package/tsconfig.lib.json +0 -10
  117. package/tsconfig.spec.json +0 -20
  118. package/typedoc.json +0 -10
@@ -1,1990 +0,0 @@
1
- import { defer, EMPTY, from, lastValueFrom, Observable, tap } from "rxjs";
2
- import { delay, map, mergeMap } from "rxjs/operators";
3
- import { v4 } from "uuid";
4
- import { splitDeviceParameterId } from "./device-parameter";
5
- import { getApiIdentifierFromHardwareDescription, getSerialNumberFromHardwareDescription, HardwareDescription } from "./hardware-description";
6
- import { MotionMasterReqResSocket } from "./motion-master-req-res-socket";
7
- import { retryWithDelay, transformMotionMasterMessageToStatus } from "./operators";
8
- import { makeParameterId, Parameter, splitParameterId } from "./parameter";
9
- import { SystemLogLine } from "./system-log-line";
10
- import { MotionMasterMessage, ParameterValueType, ParameterTypeValue, ParameterInfoAndValue, DeviceParameterValuesStatus, ParameterAddressValue, ParameterTypeValueKey, DeviceRefObj, RequestError } from "./types";
11
- import { decodeTextContent, valueTypeToParameterTypeValueKeyMap, getParameterValue, parseSystemLogContent, makeDeviceRefObj } from "./util";
12
-
13
- /**
14
- * This class contains methods for making requests to Motion Master using the injected request/response socket.
15
- *
16
- * Each request message defined in the proto file has two corresponding request methods in this class.
17
- * One method supports Reactive and the other Promise-based API. Reactive API is the default one.
18
- * Unlike promises which resolve to a single value, observables can emit multiple values over time.
19
- * For some requests like firmware installation, Motion Master will send progress messages until the firmware installation is done.
20
- * Reactive request methods are named like `getDeviceInfo` and Promise-based are named like `getDeviceInfoAsync` (notice the `Async` suffix).
21
- *
22
- * Reactive and Promise-based request methods have the same set of input parameters, but they differ in the return values.
23
- * Call to Reactive request methods will return an Observable of (1) corresponding status message, for example call to `getDeviceInfo`
24
- * will return an instance of `MotionMasterMessage.Status.DeviceInfo`, (2) request status, and (3) request message id.
25
- * Promise-based request methods can only return (resolve to) a single value.
26
- * These methods call the corresponding Reactive request method and return data from the last emmited status message.
27
- * For example `getDeviceInfoAsync` calls `getDeviceInfo` and returns a list of devices from the last emmited status message.
28
- * If the request fails on Motion Master the function will throw an error. It can also throw an error if the request times out.
29
- *
30
- * Request methods have optional `messageId` input parameter.
31
- * If `messageId` is not provided, one will be generated before sending the request message to Motion Master.
32
- * Status messages (responses) received from Motion Master that correspond to the previous request will have the same `messageId` as the one in the request.
33
- * This is how we match request with response messages in the full-duplex asynchronous communication (WebSockets, ZeroMQ).
34
- * Matching request/response by message id is inspired by [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification).
35
- *
36
- * Almost all request methods have mandatory `requestTimeout` input parameter.
37
- * This parameter specifies how long (in milliseconds) will the client wait for the status (response) messages corresponding to the previous request.
38
- * If status message is not received in due time the request method will throw a timeout error.
39
- * The error doesn't mean the request has failed on Motion Master, but rather that client has given up waiting for the response(s).
40
- *
41
- * @example
42
- * How to handle Reactive request method timeout error:
43
- * ```ts
44
- * req.getDeviceInfo(10).subscribe({
45
- * next: (status) => console.log(status),
46
- * error: (err: unknown) => {
47
- * if (err instanceof TimeoutError) {
48
- * alert('Request has timed out');
49
- * }
50
- * }
51
- * });
52
- * ```
53
- * How to handle Promise-based request method timeout error:
54
- * ```ts
55
- * async () => {
56
- * try {
57
- * const devices = await req.getDeviceInfoAsync(10);
58
- * console.log(devices);
59
- * } catch (err: unknown) {
60
- * if (err instanceof TimeoutError) {
61
- * alert('Request has timed out');
62
- * }
63
- * }
64
- * }
65
- * ```
66
- *
67
- * Request methods for a particular device, like `getDeviceParameterInfo`, as the first input parameter accept the type of request message as defined in the proto file.
68
- * For `getDeviceParameterInfo` that's `MotionMasterMessage.Request.IGetDeviceParameterInfo`. This type is extended (via type intersection) by `DeviceRefObj`.
69
- * This is done in order to support multiple ways to reference a device. Motion Master only supports device address which it re-generates for each device on power-cycle.
70
- * Because device address can change, for scripts and applications it's preferred to use a device reference that won't change.
71
- * Device serial number in the hardware description file uniquely identifies device and doesn't change. This is what each request message is extended with.
72
- * Before sending the request message to Motion Master, device address gets resolved and is set on the request message.
73
- * Beside device address and device serial number, devices can be referenced by position (sequence number in the network).
74
- *
75
- * It is ensured that Observable returned from a call to Reactive request method will eventually complete, so a call to unsubscribe is not required.
76
- * Observable will complete when request status is either "succeeded" or "failed", or it will complete (error) due to timeout.
77
- * Finding out if a request has completed, failed or is it still on-going is different for each request - it dependends on the status messages, see {@link requestStatusResolver}.
78
- * Reactive request methods handle this and for each request they will emit status messages with one of "succeeded", "failed", or "running" request status.
79
- *
80
- * Beside the request methods this class contains many helper methods such as the ones for getting and setting device parameter values and
81
- * saving all parameters which involves complex logic like multiple repeating requests in order to determine if saving all parameters has completed successfully.
82
- */
83
- export class MotionMasterReqResClient {
84
-
85
- /**
86
- * Map device address to a map of parameter id to type value key.
87
- *
88
- * @example
89
- * ```ts
90
- * new Map([
91
- * [2045285448, new Map([
92
- * ["0x1018:02", "uintValue"]
93
- * ])]
94
- * ]);
95
- * ```
96
- *
97
- * When setting a parameter value only one of int, uint, float, string, or raw type value fields must be set.
98
- * This is defined in the proto file SetDeviceParameterValues request message.
99
- *
100
- * Device parameters have richer set of base types defined in ESI and they're provided by DeviceParameterInfo status message.
101
- * When device parameter info is fetched this map gets updated by mapping device parameters type to oneof proto type, e.g. `INTEGER32 -> intValue`.
102
- *
103
- * This map helps in simplifying the API by allowing users to set parameter values without specifying the type value field.
104
- * One can simply call `setParameterValue(2045285448, 0x2004, 0x03, 2500)` and the correct type value field on the proto message will be set, `uintValue` in this example.
105
- */
106
- readonly parameterTypeValueKeyMap = new Map<number, Map<string, ParameterTypeValueKey>>();
107
-
108
- /**
109
- * Map device serial number to device address.
110
- *
111
- * @example
112
- * ```ts
113
- * new Map([
114
- * ["8502-03-0001353-2115", 2045285448]
115
- * ]);
116
- * ```
117
- *
118
- * When making requests related to a device, device address is used as an identifier.
119
- * Device address is not permanent. The number is re-generated by Motion Master when devices are power-cycled.
120
- * For scripts and applications that use this library, having a permanent device identifier is a better option.
121
- * Device serial number read from the hardware description file (.hardware_description) stored on a device is a permanent identifier.
122
- *
123
- * This map helps in making all request methods related to a device accept device reference instead of just device address as an identifier.
124
- * Device reference {@link DeviceRefObj} can be one of: device address, device serial number, or device position.
125
- * Prior to sending a request message to Motion Master, device address is resolved from device reference and this map is used for that.
126
- */
127
- readonly deviceAddressMap = new Map<string, number>();
128
-
129
- constructor(
130
- public readonly socket: MotionMasterReqResSocket,
131
- ) { }
132
-
133
- //
134
- // Requests
135
- //
136
-
137
- /**
138
- * Ping system.
139
- *
140
- * Client must send messages to Motion Master in regular intervals in order for Motion Master to consider it alive.
141
- * When client is considered "not alive", Motion Master will stop the ongoing procedures and monitorings started by that client.
142
- * Each client on the Motion Master side has its own default timeout which can be changed by {@link setSystemClientTimeout}.
143
- */
144
- pingSystem(messageId?: string) {
145
- const pingSystem = MotionMasterMessage.Request.PingSystem.create();
146
- this.sendRequest({ pingSystem }, messageId);
147
- return EMPTY;
148
- }
149
-
150
- /**
151
- * {@inheritDoc pingSystem}
152
- */
153
- async pingSystemAsync(messageId?: string) {
154
- return await lastValueFrom(this.pingSystem(messageId), { defaultValue: undefined });
155
- }
156
-
157
- /**
158
- * Get system version.
159
- *
160
- * Motion Master version follows [Semantic Versioning 2.0.0](https://semver.org/).
161
- */
162
- getSystemVersion(requestTimeout: number, messageId?: string) {
163
- const getSystemVersion = MotionMasterMessage.Request.GetSystemVersion.create();
164
- const id = this.sendRequest({ getSystemVersion }, messageId);
165
- return this.socket.message$.pipe(
166
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.SystemVersion>('systemVersion', requestTimeout, id),
167
- );
168
- }
169
-
170
- /**
171
- * {@inheritDoc getSystemVersion}
172
- */
173
- async getSystemVersionAsync(requestTimeout: number, messageId?: string) {
174
- const status = await lastValueFrom(this.getSystemVersion(requestTimeout, messageId));
175
-
176
- if (status.request === 'succeeded') {
177
- return status.version;
178
- }
179
-
180
- throw new RequestError('Get system version request failed.');
181
- }
182
-
183
- /**
184
- * Get device info.
185
- *
186
- * Device info includes a list of devices on the network. Each device has a type, position in the network, and device address.
187
- *
188
- * Motion Master will assign a unique device address to each device in the list.
189
- * Device address is what the client libraries use to make requests like `getDeviceParameter` from a particular device.
190
- * Device address is only valid for the duration of the session, that is until the list of devices is read again, for example
191
- * when devices are power cycled or Motion Master process is restarted.
192
- */
193
- getDeviceInfo(requestTimeout: number, messageId?: string) {
194
- const getDeviceInfo = MotionMasterMessage.Request.GetDeviceInfo.create();
195
- const id = this.sendRequest({ getDeviceInfo }, messageId);
196
- return this.socket.message$.pipe(
197
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceInfo>('deviceInfo', requestTimeout, id),
198
- );
199
- }
200
-
201
- /**
202
- * {@inheritDoc getDeviceInfo}
203
- */
204
- async getDeviceInfoAsync(requestTimeout: number, messageId?: string) {
205
- const status = await lastValueFrom(this.getDeviceInfo(requestTimeout, messageId));
206
-
207
- if (status.request === 'succeeded') {
208
- return status.devices;
209
- }
210
-
211
- throw new Error('Get device info request failed.');
212
- }
213
-
214
- /**
215
- * Get device parameter info.
216
- *
217
- * Device parameter info includes a list of all parameters for the requested device, but without the parameter values.
218
- * Getting device parameter values can take more than 20ms per parameter using the IgH EtherCAT Master library.
219
- * Not all clients require all the parameter values at once like OBLAC Drives, so this method is good for getting
220
- * the list of available parameters and then getting the values of only some parameters.
221
- *
222
- * The returned list of device parameters will include some basic data for each parameter: index, subindex,
223
- * name, unit, group (ARRAY or RECORD name for subitems), read and write access, min and max, and value type.
224
- * The same info and more can be read from the "SOMANET_CiA_402.xml" file, but getting info from ESI file is more complicated.
225
- * It requires an XML parsing library and knowing how to read objects info from a dictionary and assigned modules.
226
- *
227
- * In addition to fetching device parameter info, this method will map parameter base data type (ETG.1020) to proto3 types.
228
- * The map removes the need to specify proto3 type field when setting a parameter value {@link parameterTypeValueKeyMap}.
229
- */
230
- getDeviceParameterInfo(props: MotionMasterMessage.Request.IGetDeviceParameterInfo & DeviceRefObj, requestTimeout: number, messageId?: string) {
231
- return from(this.resolveDeviceAddress(props)).pipe(
232
- mergeMap((deviceAddress) => {
233
- const getDeviceParameterInfo = MotionMasterMessage.Request.GetDeviceParameterInfo.create({ ...props, deviceAddress });
234
- const id = this.sendRequest({ getDeviceParameterInfo }, messageId);
235
- return this.socket.message$.pipe(
236
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceParameterInfo>('deviceParameterInfo', requestTimeout, id),
237
- tap((deviceParameterInfo) => {
238
- const parameters = deviceParameterInfo?.parameters;
239
- if (parameters) {
240
- const map: Map<string, ParameterTypeValueKey> = new Map();
241
- this.parameterTypeValueKeyMap.set(+deviceAddress, map);
242
- for (const parameter of parameters) {
243
- if (parameter.valueType) {
244
- const typeValueKey = valueTypeToParameterTypeValueKeyMap.get(parameter.valueType);
245
- if (typeValueKey) {
246
- map.set(makeParameterId(parameter), typeValueKey);
247
- }
248
- }
249
- }
250
- }
251
- }),
252
- );
253
- }),
254
- );
255
- }
256
-
257
- /**
258
- * {@inheritDoc getDeviceParameterInfo}
259
- */
260
- async getDeviceParameterInfoAsync(props: MotionMasterMessage.Request.IGetDeviceParameterInfo & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.DeviceParameterInfo.IParameter[]> {
261
- const status = await lastValueFrom(this.getDeviceParameterInfo(props, requestTimeout, messageId));
262
-
263
- if (status.request === 'succeeded') {
264
- return status.parameters;
265
- }
266
-
267
- throw new Error('Get device parameter info request failed.');
268
- }
269
-
270
- /**
271
- * Get device parameter values.
272
- */
273
- getDeviceParameterValues(props: MotionMasterMessage.Request.IGetDeviceParameterValues & DeviceRefObj, requestTimeout: number, messageId?: string) {
274
- return from(this.resolveDeviceAddress(props)).pipe(
275
- mergeMap((deviceAddress) => {
276
- const getDeviceParameterValues = MotionMasterMessage.Request.GetDeviceParameterValues.create({ ...props, deviceAddress });
277
- const id = this.sendRequest({ getDeviceParameterValues }, messageId);
278
- return this.socket.message$.pipe(
279
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceParameterValues>('deviceParameterValues', requestTimeout, id),
280
- );
281
- }),
282
- );
283
- }
284
-
285
- /**
286
- * {@inheritDoc getDeviceParameterValues}
287
- */
288
- async getDeviceParameterValuesAsync(props: MotionMasterMessage.Request.IGetDeviceParameterValues & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.DeviceParameterValues.IParameterValue[]> {
289
- const status = await lastValueFrom(this.getDeviceParameterValues(props, requestTimeout, messageId));
290
-
291
- return status.parameterValues;
292
- }
293
-
294
- /**
295
- * Get multi device parameter values.
296
- */
297
- getMultiDeviceParameterValues(props: MotionMasterMessage.Request.IGetMultiDeviceParameterValues, requestTimeout: number, messageId?: string) {
298
- const getMultiDeviceParameterValues = MotionMasterMessage.Request.GetMultiDeviceParameterValues.create(props);
299
- const id = this.sendRequest({ getMultiDeviceParameterValues }, messageId);
300
- return this.socket.message$.pipe(
301
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.MultiDeviceParameterValues>('multiDeviceParameterValues', requestTimeout, id),
302
- );
303
- }
304
-
305
- /**
306
- * {@inheritDoc getMultiDeviceParameterValues}
307
- */
308
- async getMultiDeviceParameterValuesAsync(props: MotionMasterMessage.Request.IGetMultiDeviceParameterValues, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.IDeviceParameterValues[]> {
309
- const status = await lastValueFrom(this.getMultiDeviceParameterValues(props, requestTimeout, messageId));
310
-
311
- return status.collection;
312
- }
313
-
314
- /**
315
- * Set device parameter values.
316
- */
317
- setDeviceParameterValues(props: MotionMasterMessage.Request.ISetDeviceParameterValues & DeviceRefObj, requestTimeout: number, messageId?: string) {
318
- return from(this.resolveDeviceAddress(props)).pipe(
319
- mergeMap((deviceAddress) => {
320
- const setDeviceParameterValues = MotionMasterMessage.Request.SetDeviceParameterValues.create({ ...props, deviceAddress });
321
- const id = this.sendRequest({ setDeviceParameterValues }, messageId);
322
- return this.socket.message$.pipe(
323
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceParameterValues>('deviceParameterValues', requestTimeout, id),
324
- );
325
- }),
326
- );
327
- }
328
-
329
- /**
330
- * {@inheritDoc setDeviceParameterValues}
331
- */
332
- async setDeviceParameterValuesAsync(props: MotionMasterMessage.Request.ISetDeviceParameterValues & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.DeviceParameterValues.IParameterValue[]> {
333
- const status = await lastValueFrom(this.setDeviceParameterValues(props, requestTimeout, messageId));
334
-
335
- return status.parameterValues;
336
- }
337
-
338
- /**
339
- * Set multi device parameter values.
340
- */
341
- setMultiDeviceParameterValues(props: MotionMasterMessage.Request.ISetMultiDeviceParameterValues, requestTimeout: number, messageId?: string) {
342
- const setMultiDeviceParameterValues = MotionMasterMessage.Request.SetMultiDeviceParameterValues.create(props);
343
- const id = this.sendRequest({ setMultiDeviceParameterValues }, messageId);
344
- return this.socket.message$.pipe(
345
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.MultiDeviceParameterValues>('multiDeviceParameterValues', requestTimeout, id),
346
- );
347
- }
348
-
349
- /**
350
- * {@inheritDoc setMultiDeviceParameterValues}
351
- */
352
- async setMultiDeviceParameterValuesAsync(props: MotionMasterMessage.Request.ISetMultiDeviceParameterValues, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.IDeviceParameterValues[]> {
353
- const status = await lastValueFrom(this.setMultiDeviceParameterValues(props, requestTimeout, messageId));
354
-
355
- return status.collection;
356
- }
357
-
358
- /**
359
- * Get device file list.
360
- *
361
- * Motion Master will use Filetransfer over EtherCAT (FoE) to get the list of files from a device.
362
- * There isn't really a command for getting the list, but rather the content of a specially named file "fs-getlist" is the file list.
363
- * The list can be read like any other file from a device using the IgH EtherCAT Master for Linux CLI program:
364
- *
365
- * ```
366
- * $ ethercat foe_read fs-getlist
367
- * ```
368
- *
369
- * At the time of writing this document "fs-getlist" returns only 32 files in the list. This is the firmware's limitation.
370
- * More than 32 files can be stored on device flash memory, but they won't appear in the list.
371
- * Also note that FoE can behave differently in the bootstrap firmware which runs in the BOOT EtherCAT state and
372
- * SOMANET firmware which runs in other EtherCAT states like OP. Bootstrap tends to be buggier since it cannot be updated easily, so
373
- * Motion Master has certain workarounds to overcome the FoE differences.
374
- */
375
- getDeviceFileList(props: MotionMasterMessage.Request.IGetDeviceFileList & DeviceRefObj, requestTimeout: number, messageId?: string) {
376
- return from(this.resolveDeviceAddress(props)).pipe(
377
- mergeMap((deviceAddress) => {
378
- const getDeviceFileList = MotionMasterMessage.Request.GetDeviceFileList.create({ ...props, deviceAddress });
379
- const id = this.sendRequest({ getDeviceFileList }, messageId);
380
- return this.socket.message$.pipe(
381
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceFileList>('deviceFileList', requestTimeout, id),
382
- );
383
- }),
384
- );
385
- }
386
-
387
- /**
388
- * {@inheritDoc getDeviceFileList}
389
- */
390
- async getDeviceFileListAsync(props: MotionMasterMessage.Request.IGetDeviceFileList & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<string[]> {
391
- const status = await lastValueFrom(this.getDeviceFileList(props, requestTimeout, messageId));
392
-
393
- if (status.request === 'succeeded') {
394
- return status.fileList?.files ?? [];
395
- }
396
-
397
- throw new Error(`Failed to get device file list. ${status.error?.message} ${JSON.stringify(props)}`);
398
- }
399
-
400
- /**
401
- * Get device file.
402
- *
403
- * Motion Master uses the Filetransfer over EtherCAT (FoE) to read and send back content of a file from device flash memory.
404
- *
405
- * The IgH EtherCAT Master library used by Motion Master limits the file read buffer to 9KB, so any file written to flash that is larger than 9KB
406
- * needs to be split into parts of max 9KB. Motion Master does this automatically on request to {@link setDeviceFile}.
407
- *
408
- * Upon receiving this request Motion Master will first read the list of files on a device, see {@link getDeviceFileList}.
409
- * It does that in order to determine if the requested file, for example "SOMANET_CiA402.xml", has been stored in multiple parts.
410
- * The parts are named like "SOMANET_CiA_402.xml.zip.part000", "SOMANET_CiA_402.xml.zip.part001" and so on.
411
- * Motion Master will read all parts of the requested file, unzip it and send the content back to a client.
412
- * Since the number of items in the list of files is limited to 32 (based on the firmware version) Motion Master might not see the file,
413
- * but in any case it will try to read the content of it. If the file exists it will return its content, otherwise it will send the file not found error.
414
- *
415
- * In BOOT EtherCAT state it's only allowed to read the .hardware_description file. This was done because
416
- * the bootloader firmware would hang when trying to read a corrupted or missing file.
417
- *
418
- * If file content is empty Motion Master will return not found error. This is a subject to change now that Motion Master reads the file error codes.
419
- */
420
- getDeviceFile(props: MotionMasterMessage.Request.IGetDeviceFile & DeviceRefObj, requestTimeout: number, messageId?: string) {
421
- return from(this.resolveDeviceAddress(props)).pipe(
422
- mergeMap((deviceAddress) => {
423
- const getDeviceFile = MotionMasterMessage.Request.GetDeviceFile.create({ ...props, deviceAddress });
424
- const id = this.sendRequest({ getDeviceFile }, messageId);
425
- return this.socket.message$.pipe(
426
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceFile>('deviceFile', requestTimeout, id),
427
- );
428
- }),
429
- );
430
- }
431
-
432
- /**
433
- * {@inheritDoc getDeviceFile}
434
- */
435
- async getDeviceFileAsync(props: MotionMasterMessage.Request.IGetDeviceFile & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<Uint8Array> {
436
- const status = await lastValueFrom(this.getDeviceFile(props, requestTimeout, messageId));
437
-
438
- if (status.request === 'succeeded') {
439
- if (status.content) {
440
- return status.content;
441
- }
442
- }
443
-
444
- throw new Error(`Failed to get device file. ${status.error?.message} ${JSON.stringify(props)}`);
445
- }
446
-
447
- /**
448
- * Set device file.
449
- *
450
- * Motion Master uses the Filetransfer over EtherCAT (FoE) to write files to device flash memory.
451
- *
452
- * The IgH EtherCAT Master library used by Motion Master limits the file read buffer to 9KB, so any file written to flash that is larger than 9KB
453
- * needs to be split into parts of max 9KB. Motion Master does this automatically by first zipping the content and then writing the parts.
454
- * The parts are named like "SOMANET_CiA_402.xml.zip.part000", "SOMANET_CiA_402.xml.zip.part001" and so on.
455
- */
456
- setDeviceFile(props: MotionMasterMessage.Request.ISetDeviceFile & DeviceRefObj, requestTimeout: number, messageId?: string) {
457
- return from(this.resolveDeviceAddress(props)).pipe(
458
- mergeMap((deviceAddress) => {
459
- const setDeviceFile = MotionMasterMessage.Request.SetDeviceFile.create({ ...props, deviceAddress });
460
- const id = this.sendRequest({ setDeviceFile }, messageId);
461
- return this.socket.message$.pipe(
462
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceFile>('deviceFile', requestTimeout, id),
463
- );
464
- }),
465
- );
466
- }
467
-
468
- /**
469
- * {@inheritDoc setDeviceFile}
470
- */
471
- async setDeviceFileAsync(props: MotionMasterMessage.Request.ISetDeviceFile & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
472
- const status = await lastValueFrom(this.setDeviceFile(props, requestTimeout, messageId));
473
-
474
- if (status.request === 'succeeded') {
475
- return;
476
- }
477
-
478
- throw new Error(`Failed to set device file. ${status.error?.message} ${JSON.stringify(props)}`);
479
- }
480
-
481
- /**
482
- * Delete device file.
483
- *
484
- * Motion Master uses the Filetransfer over EtherCAT (FoE) to delete files from device flash memory.
485
- *
486
- * If the file to delete is written in parts, Motion Master will ensure that all parts are deleted.
487
- * It does that by reading the list of files on a device, see {@link getDeviceFileList}. It will remove all parts from the list and then
488
- * it will re-read the list again to ensure that there are no left overs if there were more than 32 files in the list.
489
- */
490
- deleteDeviceFile(props: MotionMasterMessage.Request.IDeleteDeviceFile & DeviceRefObj, requestTimeout: number, messageId?: string) {
491
- return from(this.resolveDeviceAddress(props)).pipe(
492
- mergeMap((deviceAddress) => {
493
- const deleteDeviceFile = MotionMasterMessage.Request.DeleteDeviceFile.create({ ...props, deviceAddress });
494
- const id = this.sendRequest({ deleteDeviceFile }, messageId);
495
- return this.socket.message$.pipe(
496
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceFile>('deviceFile', requestTimeout, id),
497
- );
498
- }),
499
- );
500
- }
501
-
502
- /**
503
- * {@inheritDoc deleteDeviceFile}
504
- */
505
- async deleteDeviceFileAsync(props: MotionMasterMessage.Request.IDeleteDeviceFile & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
506
- const status = await lastValueFrom(this.deleteDeviceFile(props, requestTimeout, messageId));
507
-
508
- if (status.request === 'succeeded') {
509
- return;
510
- }
511
-
512
- throw new Error(`Failed to delete device file. ${status.error?.message} ${JSON.stringify(props)}`);
513
- }
514
-
515
- /**
516
- * Reset device fault.
517
- *
518
- * When error occurs on a device it will go to Fault reaction active state (transition 13) and then automatically to Fault state (transition 14).
519
- * The CiA402 state of a device can be derived from the value of Statusword object (0x6041:00).
520
- * In order to go out of Fault and into Switch on disabled state (transition 15) master must send Fault reset command via Controlword object (0x6040:00).
521
- *
522
- * When this request is sent to Motion Master it will try to reset fault. Resetting fault can succeed or fail.
523
- * While resetting the fault Motion Master will use the current Controlword value and set the Reset fault bit to 1,
524
- * but when it completes (either success or error) it will set it back to 0.
525
- *
526
- * Motion Master will return "No Fault" warning when this request is sent and device is not in Fault or Fault reaction active state.
527
- * If this request is sent when device is in Fault reaction active state, then Motion Master will
528
- * wait for the automatic transition (14) to Fault state before trying to reset fault.
529
- * When Motion Master sets the Controlword Fault bit to 1, it will occasionally check if device is no longer in Fault state.
530
- * After a couple of seconds, if device is still in Fault state, it will give up and return the time out error.
531
- */
532
- resetDeviceFault(props: MotionMasterMessage.Request.IResetDeviceFault & DeviceRefObj, requestTimeout: number, messageId?: string) {
533
- return from(this.resolveDeviceAddress(props)).pipe(
534
- mergeMap((deviceAddress) => {
535
- const resetDeviceFault = MotionMasterMessage.Request.ResetDeviceFault.create({ ...props, deviceAddress });
536
- const id = this.sendRequest({ resetDeviceFault }, messageId);
537
- return this.socket.message$.pipe(
538
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.IDeviceFaultReset>('deviceFaultReset', requestTimeout, id),
539
- );
540
- }),
541
- );
542
- }
543
-
544
- /**
545
- * {@inheritDoc resetDeviceFault}
546
- */
547
- async resetDeviceFaultAsync(props: MotionMasterMessage.Request.IResetDeviceFault & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
548
- const status = await lastValueFrom(this.resetDeviceFault(props, requestTimeout, messageId));
549
-
550
- if (status.request === 'succeeded') {
551
- return;
552
- }
553
-
554
- throw new Error(`Failed to reset device fault. ${status.error?.message} ${JSON.stringify(props)}`);
555
- }
556
-
557
- /**
558
- * Stop device.
559
- *
560
- * Motion Master will use the current value of the Controlword object and set the command on it to Quick stop.
561
- * It will then wait for up to 2 seconds for a device to get into one of the following CiA402 states: Switch on disabled, Operation enabled, Fault.
562
- * It does that by periodically checking the value of the Statusword object. If the device doesn't go into one of those states
563
- * Motion Master will return the timeout or quick stop failed error.
564
- */
565
- stopDevice(props: MotionMasterMessage.Request.IStopDevice & DeviceRefObj, requestTimeout: number, messageId?: string) {
566
- return from(this.resolveDeviceAddress(props)).pipe(
567
- mergeMap((deviceAddress) => {
568
- const stopDevice = MotionMasterMessage.Request.StopDevice.create({ ...props, deviceAddress });
569
- const id = this.sendRequest({ stopDevice }, messageId);
570
- return this.socket.message$.pipe(
571
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceStop>('deviceStop', requestTimeout, id),
572
- );
573
- }),
574
- );
575
- }
576
-
577
- /**
578
- * {@inheritDoc stopDevice}
579
- */
580
- async stopDeviceAsync(props: MotionMasterMessage.Request.IStopDevice & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
581
- const status = await lastValueFrom(this.stopDevice(props, requestTimeout, messageId));
582
-
583
- if (status.request === 'succeeded') {
584
- return;
585
- }
586
-
587
- throw new Error(`Failed to stop device. ${status.error?.message} ${JSON.stringify(props)}`);
588
- }
589
-
590
- /**
591
- * Start device firmware installation.
592
- *
593
- * When Motion Master receives this request it will do the following:
594
- * - switch device to BOOT EtherCAT state, device will then run the bootloader instead of the firmware
595
- * - delete files from device that it finds in the provided package (SOMANET_CiA_402.xml and stack_image.svg)
596
- * - write files other than firmware binaries and SII from package to device (SOMANET_CiA_402.xml and stack_image.svg)
597
- * - optionally install the SII file using the IgH EtherCAT Master library write SII function
598
- * - write firmware binaries to device using the predefined names like app_firmware.bin
599
- *
600
- * During the firmware installation Motion Master will send progress messages back to clients.
601
- * If Motion Master fails in any step other than SII write it will send error. If SII write fails only warning will be sent.
602
- */
603
- startDeviceFirmwareInstallation(props: MotionMasterMessage.Request.IStartDeviceFirmwareInstallation & DeviceRefObj, requestTimeout: number, messageId?: string) {
604
- return from(this.resolveDeviceAddress(props)).pipe(
605
- mergeMap((deviceAddress) => {
606
- const startDeviceFirmwareInstallation = MotionMasterMessage.Request.StartDeviceFirmwareInstallation.create({ ...props, deviceAddress });
607
- const id = this.sendRequest({ startDeviceFirmwareInstallation }, messageId);
608
- return this.socket.message$.pipe(
609
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceFirmwareInstallation>('deviceFirmwareInstallation', requestTimeout, id),
610
- );
611
- }),
612
- );
613
- }
614
-
615
- /**
616
- * {@inheritDoc startDeviceFirmwareInstallation}
617
- */
618
- async startDeviceFirmwareInstallationAsync(props: MotionMasterMessage.Request.IStartDeviceFirmwareInstallation & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
619
- const status = await lastValueFrom(this.startDeviceFirmwareInstallation(props, requestTimeout, messageId));
620
-
621
- if (status.request === 'succeeded') {
622
- return;
623
- }
624
-
625
- throw new Error(`Failed to start device firmware installation. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
626
- }
627
-
628
- /**
629
- * Get device log.
630
- *
631
- * Device log is stored in two files due to IgH EtherCAT Master library 9KB file size limitation, see {@link getDeviceFile}.
632
- * Device automatically writes log to logging_curr.log and copies that file to logging_prev.log once it reaches 9KB.
633
- * When Motion Master receives this request it will read the both files and return their combined contents.
634
- */
635
- getDeviceLog(props: MotionMasterMessage.Request.IGetDeviceLog & DeviceRefObj, requestTimeout: number, messageId?: string) {
636
- return from(this.resolveDeviceAddress(props)).pipe(
637
- mergeMap((deviceAddress) => {
638
- const getDeviceLog = MotionMasterMessage.Request.GetDeviceLog.create({ ...props, deviceAddress });
639
- const id = this.sendRequest({ getDeviceLog }, messageId);
640
- return this.socket.message$.pipe(
641
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.IDeviceLog>('deviceLog', requestTimeout, id),
642
- );
643
- }),
644
- );
645
- }
646
-
647
- /**
648
- * {@inheritDoc getDeviceLog}
649
- */
650
- async getDeviceLogAsync(props: MotionMasterMessage.Request.IGetDeviceLog & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<string | null | undefined> {
651
- const status = await lastValueFrom(this.getDeviceLog(props, requestTimeout, messageId));
652
-
653
- if (status.request === 'succeeded') {
654
- return status.content;
655
- }
656
-
657
- throw new Error(`Failed to get device log. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
658
- }
659
-
660
- /**
661
- * Start cogging torque recording.
662
- *
663
- * Cogging torque recording (CTC) can be started with or without auto-config.
664
- * In the proto file skipping the auto-config is currently named skip_auto_tuning. The field name is deprecated and will be renamed.
665
- *
666
- * If CTC is started without auto-config then users must ensure that encoder on the motor shaft is used for all functions: commutation, position, and velocity.
667
- * If this is not the case, then Motion Master will return invalid encoder configuration error.
668
- * Recording can also fail if the position controller is not tuned.
669
- *
670
- * If CTC is started with auto-config then Motion Master will:
671
- * - change the encoder configuration to have all functions on the motor shaft
672
- * - disabled CTC
673
- * - change the velocity feed forward value
674
- * - change the position and velocity filter types and cutoff frequencies
675
- * - run iterative sharpening position auto-tuning in order to compute the optimal PID values (gains) using the auto tuning
676
- *
677
- * All parameter values that Motion Master changes as part of the auto-config are first stored in memory and once the CTC recording is done are reverted back.
678
- *
679
- * CTC recording is started by changing the Modes of operation (0x6060) parameter value to -1 (Cogging compensation recording mode).
680
- * During the operation Motion Master will monitor the Cogging torque compensation state parameter (0x2008:01) to know when the recording is in progress and when it's done.
681
- *
682
- * The result of recording CTC is the cogging_torque.bin file written to device flash memory. Size of this file is 2048 bytes.
683
- * Each pair of bytes represents a 16-bit signed integer value in mNm (milli newton meter).
684
- *
685
- * This request will turn the motor.
686
- */
687
- startCoggingTorqueRecording(props: MotionMasterMessage.Request.IStartCoggingTorqueRecording & DeviceRefObj, requestTimeout: number, messageId?: string) {
688
- return from(this.resolveDeviceAddress(props)).pipe(
689
- mergeMap((deviceAddress) => {
690
- const startCoggingTorqueRecording = MotionMasterMessage.Request.StartCoggingTorqueRecording.create({ ...props, deviceAddress });
691
- const id = this.sendRequest({ startCoggingTorqueRecording }, messageId);
692
- return this.socket.message$.pipe(
693
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.ICoggingTorqueRecording>('coggingTorqueRecording', requestTimeout, id),
694
- );
695
- }),
696
- );
697
- }
698
-
699
- /**
700
- * {@inheritDoc startCoggingTorqueRecording}
701
- */
702
- async startCoggingTorqueRecordingAsync(props: MotionMasterMessage.Request.IStartCoggingTorqueRecording & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
703
- const status = await lastValueFrom(this.startCoggingTorqueRecording(props, requestTimeout, messageId));
704
-
705
- if (status.request === 'succeeded') {
706
- return;
707
- }
708
-
709
- throw new Error(`Failed to start cogging torque recording. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
710
- }
711
-
712
- /**
713
- * Get cogging torque data.
714
- *
715
- * The cogging_torque.bin file is the result of recording cogging torque compensation, see {@link startCoggingTorqueRecording}.
716
- * The size of this file is 2048 bytes where each pair of bytes represents a 16-bit signed integer value in mNm (milli newton meter).
717
- *
718
- * Motion Master will read the cogging_torque.bin file, convert bytes to 16-bit signed integer values and return that as an array.
719
- */
720
- getCoggingTorqueData(props: MotionMasterMessage.Request.IGetCoggingTorqueData & DeviceRefObj, requestTimeout: number, messageId?: string) {
721
- return from(this.resolveDeviceAddress(props)).pipe(
722
- mergeMap((deviceAddress) => {
723
- const getCoggingTorqueData = MotionMasterMessage.Request.GetCoggingTorqueData.create({ ...props, deviceAddress });
724
- const id = this.sendRequest({ getCoggingTorqueData }, messageId);
725
- return this.socket.message$.pipe(
726
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.ICoggingTorqueData>('coggingTorqueData', requestTimeout, id),
727
- );
728
- }),
729
- );
730
- }
731
-
732
- /**
733
- * {@inheritDoc getCoggingTorqueData}
734
- */
735
- async getCoggingTorqueDataAsync(props: MotionMasterMessage.Request.IGetCoggingTorqueData & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<number[]> {
736
- const status = await lastValueFrom(this.getCoggingTorqueData(props, requestTimeout, messageId));
737
-
738
- if (status.request === 'succeeded') {
739
- return status.table?.data ?? [];
740
- }
741
-
742
- throw new Error(`Failed to get cogging torque data. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
743
- }
744
-
745
- /**
746
- * Start offset detection.
747
- *
748
- * Running offset detection is different in firmwares <v5 and >=v5.
749
- *
750
- * For devices that run firmware >=v5 Motion Master will run multiple OS commands.
751
- * Before starting any command device is switched to Modes of operation -2 (Diagnostics mode) and
752
- * it's put into operation enabled CiA402 state. The OS commands are run in the following order:
753
- * - open phase detection
754
- * - phase resistance measurement
755
- * - phase inductance measurement
756
- * - pole pair detection
757
- * - motor phase order detection
758
- * - commutation offset measurement
759
- *
760
- * Motion Master will return error if any of the following OS commands fail: open phase detection,
761
- * phase order detection, commutation offset measurement.
762
- * If other OS commands fail Motion Master will continue with the procedure and only return warning.
763
- *
764
- * Once the commutation offset measurement command completes it will update:
765
- * - 0x2001:00 Commutation angle offset to the computed offset value
766
- * - 0x2009:01 Commutation offset State to 2 (OFFSET_VALID)
767
- *
768
- * For devices that run firmware <5 the same Modes of operation -2 is used, but it's named Commutation offset detection mode.
769
- * The end result is the same, the procedure will update 0x2001:00 and 0x2009:01 parameter values.
770
- *
771
- * This request will turn the motor.
772
- */
773
- startOffsetDetection(props: MotionMasterMessage.Request.IStartOffsetDetection & DeviceRefObj, requestTimeout: number, messageId?: string) {
774
- return from(this.resolveDeviceAddress(props)).pipe(
775
- mergeMap((deviceAddress) => {
776
- const startOffsetDetection = MotionMasterMessage.Request.StartOffsetDetection.create({ ...props, deviceAddress });
777
- const id = this.sendRequest({ startOffsetDetection }, messageId);
778
- return this.socket.message$.pipe(
779
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.IOffsetDetection>('offsetDetection', requestTimeout, id),
780
- );
781
- }),
782
- );
783
- }
784
-
785
- /**
786
- * {@inheritDoc startOffsetDetection}
787
- */
788
- async startOffsetDetectionAsync(props: MotionMasterMessage.Request.IStartOffsetDetection & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
789
- const status = await lastValueFrom(this.startOffsetDetection(props, requestTimeout, messageId));
790
-
791
- if (status.request === 'succeeded') {
792
- return;
793
- }
794
-
795
- throw new Error(`Failed start offset detection. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
796
- }
797
-
798
- /**
799
- * @deprecated
800
- */
801
- startPlantIdentification(props: MotionMasterMessage.Request.IStartPlantIdentification & DeviceRefObj, requestTimeout: number, messageId?: string) {
802
- return from(this.resolveDeviceAddress(props)).pipe(
803
- mergeMap((deviceAddress) => {
804
- const startPlantIdentification = MotionMasterMessage.Request.StartPlantIdentification.create({ ...props, deviceAddress });
805
- const id = this.sendRequest({ startPlantIdentification }, messageId);
806
- return this.socket.message$.pipe(
807
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.IPlantIdentification>('plantIdentification', requestTimeout, id),
808
- );
809
- }),
810
- );
811
- }
812
-
813
- /**
814
- * @deprecated
815
- */
816
- async startPlantIdentificationAsync(props: MotionMasterMessage.Request.IStartPlantIdentification & DeviceRefObj, requestTimeout: number, messageId?: string) {
817
- const status = await lastValueFrom(this.startPlantIdentification(props, requestTimeout, messageId));
818
-
819
- if (status.request === 'succeeded') {
820
- return;
821
- }
822
-
823
- throw new Error(`Failed start plant identification. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
824
- }
825
-
826
- /**
827
- * Compute auto tuning gains.
828
- *
829
- * Auto tuning gains can be computed for position or velocity controller.
830
- *
831
- * Prerequisites for running this procedure are the configured motor and the existence of
832
- * plant_model.csv file created by a previous run of the system identification {@link startSystemIdentification}.
833
- * Motion Master will return error if the plant_model.csv file is not found.
834
- *
835
- * In OBLAC Drives this request is called when the tuning sliders (damping ratio, bandwidth) are moved and
836
- * during the CTC recording {@link startCoggingTorqueRecording} when auto config is not skipped.
837
- *
838
- * Motion Master will compute the PID gains using the Python scripts and it will
839
- * update the Kp, Ki, Kd values of 0x2011 and 0x2012 parameters.
840
- */
841
- computeAutoTuningGains(props: MotionMasterMessage.Request.IComputeAutoTuningGains & DeviceRefObj, requestTimeout: number, messageId?: string) {
842
- return from(this.resolveDeviceAddress(props)).pipe(
843
- mergeMap((deviceAddress) => {
844
- const computeAutoTuningGains = MotionMasterMessage.Request.ComputeAutoTuningGains.create({ ...props, deviceAddress });
845
- const id = this.sendRequest({ computeAutoTuningGains }, messageId);
846
- return this.socket.message$.pipe(
847
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.IAutoTuning>('autoTuning', requestTimeout, id),
848
- );
849
- }),
850
- );
851
- }
852
-
853
- /**
854
- * {@inheritDoc computeAutoTuningGains}
855
- */
856
- async computeAutoTuningGainsAsync(props: MotionMasterMessage.Request.IComputeAutoTuningGains & DeviceRefObj, requestTimeout: number, messageId?: string) {
857
- const status = await lastValueFrom(this.computeAutoTuningGains(props, requestTimeout, messageId));
858
-
859
- if (status.request === 'succeeded') {
860
- return;
861
- }
862
-
863
- throw new Error(`Failed to compute auto tuning gains. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
864
- }
865
-
866
- /**
867
- * Set motion controller parameters.
868
- *
869
- * This request sets the target value for a device on Motion Master.
870
- *
871
- * A previous call to {@link enableMotionController} will set the mode of operation to CSP, CSV, or CST and optionally enable filtering.
872
- */
873
- setMotionControllerParameters(props: MotionMasterMessage.Request.ISetMotionControllerParameters & DeviceRefObj, messageId?: string) {
874
- return from(this.resolveDeviceAddress(props)).pipe(
875
- mergeMap((deviceAddress) => {
876
- const setMotionControllerParameters = MotionMasterMessage.Request.SetMotionControllerParameters.create({ ...props, deviceAddress });
877
- this.sendRequest({ setMotionControllerParameters }, messageId);
878
- return EMPTY;
879
- }),
880
- );
881
- }
882
-
883
- /**
884
- * {@inheritDoc setMotionControllerParameters}
885
- */
886
- async setMotionControllerParametersAsync(props: MotionMasterMessage.Request.ISetMotionControllerParameters & DeviceRefObj, messageId?: string): Promise<void> {
887
- return await lastValueFrom(this.setMotionControllerParameters(props, messageId), { defaultValue: undefined });
888
- }
889
-
890
- /**
891
- * Enable motion controller.
892
- *
893
- * This request will set the mode of operation to CSP, CSV, or CST and optionally enable filtering.
894
- *
895
- * If filtering is enabled Motion Master will compute an intermediate target value in each cycle, and depending on the active mode of operation
896
- * set the value of one of 0x607A Target position, 0x60FF Target velocity, 0x6071 Target torque to that value.
897
- *
898
- * Target value can be updated with {@link setMotionControllerParameters}.
899
- */
900
- enableMotionController(props: MotionMasterMessage.Request.IEnableMotionController & DeviceRefObj, requestTimeout: number, messageId?: string) {
901
- return from(this.resolveDeviceAddress(props)).pipe(
902
- mergeMap((deviceAddress) => {
903
- const enableMotionController = MotionMasterMessage.Request.EnableMotionController.create({ ...props, deviceAddress });
904
- const id = this.sendRequest({ enableMotionController }, messageId);
905
- return this.socket.message$.pipe(
906
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.MotionController>('motionController', requestTimeout, id),
907
- );
908
- }),
909
- );
910
- }
911
-
912
- /**
913
- * {@inheritDoc enableMotionController}
914
- */
915
- async enableMotionControllerAsync(props: MotionMasterMessage.Request.IEnableMotionController & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<{ controllerType: MotionMasterMessage.Request.EnableMotionController.ControllerType, enabled: boolean, filter: boolean }> {
916
- const status = await lastValueFrom(this.enableMotionController(props, requestTimeout, messageId));
917
-
918
- if (status.request === 'succeeded') {
919
- return (({ controllerType, enabled, filter }) => ({ controllerType, enabled, filter }))(status);
920
- }
921
-
922
- throw new Error(`Failed to enable motion controller. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
923
- }
924
-
925
- /**
926
- * Disable motion controller.
927
- *
928
- * Depending on the current CiA402 state this request will do the following:
929
- * - if device is in Quick stop active it will transition to Switch on disabled state
930
- * - if device is in Operation enabled it will transition to Switched on state
931
- */
932
- disableMotionController(props: MotionMasterMessage.Request.IDisableMotionController & DeviceRefObj, requestTimeout: number, messageId?: string) {
933
- return from(this.resolveDeviceAddress(props)).pipe(
934
- mergeMap((deviceAddress) => {
935
- const disableMotionController = MotionMasterMessage.Request.DisableMotionController.create({ ...props, deviceAddress });
936
- const id = this.sendRequest({ disableMotionController }, messageId);
937
- return this.socket.message$.pipe(
938
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.MotionController>('motionController', requestTimeout, id),
939
- );
940
- }),
941
- );
942
- }
943
-
944
- /**
945
- * {@inheritDoc disableMotionController}
946
- */
947
- async disableMotionControllerAsync(props: MotionMasterMessage.Request.IDisableMotionController & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<{ controllerType: MotionMasterMessage.Request.EnableMotionController.ControllerType, enabled: boolean, filter: boolean }> {
948
- const status = await lastValueFrom(this.disableMotionController(props, requestTimeout, messageId));
949
-
950
- if (status.request === 'succeeded') {
951
- return (({ controllerType, enabled, filter }) => ({ controllerType, enabled, filter }))(status);
952
- }
953
-
954
- throw new Error(`Failed to disable motion controller. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
955
- }
956
-
957
- /**
958
- * Set signal generator parameters.
959
- *
960
- * This request will update the signal generator parameters for a device on Motion Master.
961
- * The selected signal generator is then started by {@link startSignalGenerator}.
962
- * Signal generator must be started in the next 60 seconds before Motion Master invalidates the previously set parameters.
963
- */
964
- setSignalGeneratorParameters(props: MotionMasterMessage.Request.ISetSignalGeneratorParameters & DeviceRefObj, requestTimeout: number, messageId?: string) {
965
- return from(this.resolveDeviceAddress(props)).pipe(
966
- mergeMap((deviceAddress) => {
967
- const setSignalGeneratorParameters = MotionMasterMessage.Request.SetSignalGeneratorParameters.create({ ...props, deviceAddress });
968
- const id = this.sendRequest({ setSignalGeneratorParameters }, messageId);
969
- return this.socket.message$.pipe(
970
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.SignalGenerator>('signalGenerator', requestTimeout, id),
971
- );
972
- }),
973
- );
974
- }
975
-
976
- /**
977
- * {@inheritDoc setSignalGeneratorParameters}
978
- */
979
- async setSignalGeneratorParametersAsync(props: MotionMasterMessage.Request.ISetSignalGeneratorParameters & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
980
- const status = await lastValueFrom(this.setSignalGeneratorParameters(props, requestTimeout, messageId));
981
-
982
- if (status.request === 'succeeded') {
983
- return;
984
- }
985
-
986
- throw new Error(`Failed to set signal generator parameters. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
987
- }
988
-
989
- /**
990
- * Start signal generator.
991
- *
992
- * This request will start one of the signal generators based on the
993
- * previously set parameters with {@link setSignalGeneratorParameters}.
994
- *
995
- * Signal generators are started by changing the mode of operation, putting device into Operation enabled CiA402 state,
996
- * and setting the target value. What mode of operation is set depends on the type of signal generator and controller:
997
- * - For the simple & advanced step response and sine wave one of CSP, CSV, or CST is set.
998
- * - For ramp, trapezoidal, and bidirectional signal generators, also called profiles, one of PP, PV, TQ is set.
999
- *
1000
- * Currently the only profile type that firmware supports is ramp.
1001
- * Motion Master uses this profile type to run the following signal generators: ramp, trapezoidal, and bidirectional.
1002
- * Sine wave is also a profile but it is currently generated on Motion Master.
1003
- * Firmwares >=5.2 will support generating and running sine wave profiles on devices. This will enable lossless profile
1004
- * execution on possibly higher frequencies. Future Motion Master versions will switch to this method.
1005
- *
1006
- * For the simple step response Motion Master will only set the target value,
1007
- * wait for the specified holding duration, and then send Quick Stop.
1008
- *
1009
- * The advanced step response is similar to simple, but after the holding duration it will set the new target
1010
- * multiple times depending on the shape, and then send Quick Stop if repeat is set to NO. If repeat is set to YES,
1011
- * then the signal generator will run indefinitely and users must stop it.
1012
- *
1013
- * Before running profiles Motion Master will check if device supports the profile modes by looking at 0x6502 Supported drives modes.
1014
- * If profile modes are not supported then the profiles will be generated on Motion Master. This means the targets will be computed up-front.
1015
- * If profiles are supported then ramp, trapezoidal, and bidirectional signal generators are run in profile mode of operation.
1016
- * Motion Master will set the following parameters:
1017
- * - 0x6081:00 Profile velocity (only for position controller)
1018
- * - 0x6083:00 Profile acceleration
1019
- * - 0x6084:00 Profile deceleration
1020
- * Motion Master knows when the profile has completed by comparing the demand with the actual value, taking into account the
1021
- * holding duration and the shape of profile. Once the profile is considered complete Motion Master will send Quick Stop, but
1022
- * only if repeat is set to NO.
1023
- *
1024
- * Sine wave profile is generated up-front on Motion Master. Targets are computed for each master cycle (millisecond).
1025
- * Once the last computed target has been set, Motion Master will send Quick Stop if repeat is set to NO.
1026
- *
1027
- * For all signal generators Motion Master will modify the requested target based on
1028
- * the software position limits and it will return warning.
1029
- */
1030
- startSignalGenerator(props: MotionMasterMessage.Request.IStartSignalGenerator & DeviceRefObj, requestTimeout: number, messageId?: string) {
1031
- return from(this.resolveDeviceAddress(props)).pipe(
1032
- mergeMap((deviceAddress) => {
1033
- const startSignalGenerator = MotionMasterMessage.Request.StartSignalGenerator.create({ ...props, deviceAddress });
1034
- const id = this.sendRequest({ startSignalGenerator }, messageId);
1035
- return this.socket.message$.pipe(
1036
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.SignalGenerator>('signalGenerator', requestTimeout, id),
1037
- );
1038
- }),
1039
- );
1040
- }
1041
-
1042
- /**
1043
- * {@inheritDoc startSignalGenerator}
1044
- */
1045
- async startSignalGeneratorAsync(props: MotionMasterMessage.Request.IStartSignalGenerator & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1046
- const status = await lastValueFrom(this.startSignalGenerator(props, requestTimeout, messageId));
1047
-
1048
- if (status.request === 'succeeded') {
1049
- return;
1050
- }
1051
-
1052
- throw new Error(`Failed to start signal generator. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1053
- }
1054
-
1055
- /**
1056
- * Stop signal generator.
1057
- *
1058
- * This request will send Quick Stop to a device.
1059
- */
1060
- stopSignalGenerator(props: MotionMasterMessage.Request.IStopSignalGenerator & DeviceRefObj, requestTimeout: number, messageId?: string) {
1061
- return from(this.resolveDeviceAddress(props)).pipe(
1062
- mergeMap((deviceAddress) => {
1063
- const stopSignalGenerator = MotionMasterMessage.Request.StopSignalGenerator.create({ ...props, deviceAddress });
1064
- const id = this.sendRequest({ stopSignalGenerator }, messageId);
1065
- return this.socket.message$.pipe(
1066
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.SignalGenerator>('signalGenerator', requestTimeout, id),
1067
- );
1068
- }),
1069
- );
1070
- }
1071
-
1072
- /**
1073
- * {@inheritDoc stopSignalGenerator}
1074
- */
1075
- async stopSignalGeneratorAsync(props: MotionMasterMessage.Request.IStopSignalGenerator & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1076
- const status = await lastValueFrom(this.stopSignalGenerator(props, requestTimeout, messageId));
1077
-
1078
- if (status.request === 'succeeded') {
1079
- return;
1080
- }
1081
-
1082
- throw new Error(`Failed to stop signal generator. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1083
- }
1084
-
1085
- startMonitoringDeviceParameterValues(props: MotionMasterMessage.Request.IStartMonitoringDeviceParameterValues, requestTimeout: number, messageId?: string) {
1086
- const startMonitoringDeviceParameterValues = MotionMasterMessage.Request.StartMonitoringDeviceParameterValues.create(props);
1087
- const id = this.sendRequest({ startMonitoringDeviceParameterValues }, messageId);
1088
- return this.socket.message$.pipe(
1089
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.MonitoringParameterValues>('monitoringParameterValues', requestTimeout, id),
1090
- );
1091
- }
1092
-
1093
- async startMonitoringDeviceParameterValuesAsync(props: MotionMasterMessage.Request.IStartMonitoringDeviceParameterValues, requestTimeout: number, messageId?: string): Promise<string | undefined> {
1094
- const status = await lastValueFrom(this.startMonitoringDeviceParameterValues(props, requestTimeout, messageId));
1095
-
1096
- if (status.request === 'succeeded') {
1097
- return status.messageId;
1098
- }
1099
-
1100
- throw new Error(`Failed to start monitoring device parameter values. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1101
- }
1102
-
1103
- stopMonitoringDeviceParameterValues(props: MotionMasterMessage.Request.IStopMonitoringDeviceParameterValues, messageId?: string) {
1104
- const stopMonitoringDeviceParameterValues = MotionMasterMessage.Request.StopMonitoringDeviceParameterValues.create(props);
1105
- this.sendRequest({ stopMonitoringDeviceParameterValues }, messageId);
1106
- return EMPTY;
1107
- }
1108
-
1109
- async stopMonitoringDeviceParameterValuesAsync(props: MotionMasterMessage.Request.IStopMonitoringDeviceParameterValues, messageId?: string) {
1110
- return await lastValueFrom(this.stopMonitoringDeviceParameterValues(props, messageId), { defaultValue: undefined });
1111
- }
1112
-
1113
- getEthercatNetworkState(props: MotionMasterMessage.Request.IGetEthercatNetworkState & DeviceRefObj, requestTimeout: number, messageId?: string) {
1114
- return from(this.resolveDeviceAddress(props)).pipe(
1115
- mergeMap((deviceAddress) => {
1116
- const getEthercatNetworkState = MotionMasterMessage.Request.GetEthercatNetworkState.create({ ...props, deviceAddress });
1117
- const id = this.sendRequest({ getEthercatNetworkState }, messageId);
1118
- return this.socket.message$.pipe(
1119
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.IEthercatNetworkState>('ethercatNetworkState', requestTimeout, id),
1120
- );
1121
- }),
1122
- );
1123
- }
1124
-
1125
- async getEthercatNetworkStateAsync(props: MotionMasterMessage.Request.IGetEthercatNetworkState & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.EthercatNetworkState.State> {
1126
- const status = await lastValueFrom(this.getEthercatNetworkState(props, requestTimeout, messageId));
1127
-
1128
- if (status.request === 'succeeded') {
1129
- if (typeof status.state === 'number') {
1130
- return status.state;
1131
- }
1132
- }
1133
-
1134
- throw new Error(`Failed get ethercat network state. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1135
- }
1136
-
1137
- /**
1138
- * Set EtherCAT network state.
1139
- *
1140
- * Set EtherCAT network state to one of: BOOT, INIT, PRE-OP, SAFE-OP, OP.
1141
- *
1142
- * From PRE-OP to OP MM will reinitialize slaves in order to get new PDO mapping.
1143
- */
1144
- setEthercatNetworkState(props: MotionMasterMessage.Request.ISetEthercatNetworkState & DeviceRefObj, requestTimeout: number, messageId?: string) {
1145
- return from(this.resolveDeviceAddress(props)).pipe(
1146
- mergeMap((deviceAddress) => {
1147
- const setEthercatNetworkState = MotionMasterMessage.Request.SetEthercatNetworkState.create({ ...props, deviceAddress });
1148
- const id = this.sendRequest({ setEthercatNetworkState }, messageId);
1149
- return this.socket.message$.pipe(
1150
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.IEthercatNetworkState>('ethercatNetworkState', requestTimeout, id),
1151
- );
1152
- }),
1153
- );
1154
- }
1155
-
1156
- /**
1157
- * {@inheritDoc setEthercatNetworkState}
1158
- */
1159
- async setEthercatNetworkStateAsync(props: MotionMasterMessage.Request.ISetEthercatNetworkState & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1160
- const status = await lastValueFrom(this.setEthercatNetworkState(props, requestTimeout, messageId));
1161
-
1162
- if (status.request === 'succeeded') {
1163
- return;
1164
- }
1165
-
1166
- throw new Error(`Failed set ethercat network state. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1167
- }
1168
-
1169
- /**
1170
- * @deprecated
1171
- */
1172
- startNarrowAngleCalibration(props: MotionMasterMessage.Request.IStartNarrowAngleCalibration & DeviceRefObj, requestTimeout: number, messageId?: string) {
1173
- return from(this.resolveDeviceAddress(props)).pipe(
1174
- mergeMap((deviceAddress) => {
1175
- const startNarrowAngleCalibration = MotionMasterMessage.Request.StartNarrowAngleCalibration.create({ ...props, deviceAddress });
1176
- const id = this.sendRequest({ startNarrowAngleCalibration }, messageId);
1177
- return this.socket.message$.pipe(
1178
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.INarrowAngleCalibration>('narrowAngleCalibration', requestTimeout, id),
1179
- );
1180
- }),
1181
- );
1182
- }
1183
-
1184
- /**
1185
- * @deprecated
1186
- */
1187
- async startNarrowAngleCalibrationAsync(props: MotionMasterMessage.Request.IStartNarrowAngleCalibration & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1188
- const status = await lastValueFrom(this.startNarrowAngleCalibration(props, requestTimeout, messageId));
1189
-
1190
- if (status.request === 'succeeded') {
1191
- return;
1192
- }
1193
-
1194
- throw new Error(`Failed start narrow angle calibration. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1195
- }
1196
-
1197
- /**
1198
- * Set system client timeout.
1199
- *
1200
- * This request will update the client timeout.
1201
- *
1202
- * Client timeout specifies how long will Motion Master wait for the client to send ping or any other message before
1203
- * considering it gone and clearing the resources (stopping procedures and monitorings) related to that client.
1204
- *
1205
- * Default client timeout is 1000ms.
1206
- *
1207
- * Client will typically change this once the connection is established.
1208
- */
1209
- setSystemClientTimeout(props: MotionMasterMessage.Request.ISetSystemClientTimeout, messageId?: string) {
1210
- const setSystemClientTimeout = MotionMasterMessage.Request.SetSystemClientTimeout.create(props);
1211
- this.sendRequest({ setSystemClientTimeout }, messageId);
1212
- return EMPTY;
1213
- }
1214
-
1215
- /**
1216
- * {@inheritDoc setSystemClientTimeout}
1217
- */
1218
- async setSystemClientTimeoutAsync(props: MotionMasterMessage.Request.ISetSystemClientTimeout, messageId?: string) {
1219
- return await lastValueFrom(this.setSystemClientTimeout(props, messageId), { defaultValue: undefined });
1220
- }
1221
-
1222
- /**
1223
- * Start system identification.
1224
- *
1225
- * pre-condition motor and encoder configured (setup wizard), without position and velocity tuning
1226
- * torque only, used for computing the PID values during the auto tuning and full auto tuning and ctc which uses auto-tuning
1227
- * error when, fault state, cannot set op mode, state, system failed, chirp profile is generate on master, each point in a cycle (millisecond)
1228
- * CST sets targets, after the last target value MM sends Quick Stop, failed if fault, everything happens on master, but idea to run chirp on slave
1229
- * and record data on slave, so that communication doesn't impact the result, MM records velocity and torque and computes, compute how many data is missing warning
1230
- * error if there are not enough data, missing more than 80% of data points, if between 5%-80% warning, <5% ok, linear interpolation of missing data points
1231
- * conversion every all velocity to SI, check amplitude of signal no torque, no velocity, or small amplitudes warning, errors 0 amplitudes
1232
- * identification plant model, check stability of computed model (math only) can result in error, compute and write to plant_model.csv
1233
- * set parameters integral limits 0x2012:4 0x2012:8, 0x2011:4 Position|Velocity loop integral limit
1234
- * result plant_model.csv - check what values are and in what unit
1235
- *
1236
- * This request will turn motor
1237
- */
1238
- startSystemIdentification(props: MotionMasterMessage.Request.IStartSystemIdentification & DeviceRefObj, requestTimeout: number, messageId?: string) {
1239
- return from(this.resolveDeviceAddress(props)).pipe(
1240
- mergeMap((deviceAddress) => {
1241
- const startSystemIdentification = MotionMasterMessage.Request.StartSystemIdentification.create({ ...props, deviceAddress });
1242
- const id = this.sendRequest({ startSystemIdentification }, messageId);
1243
- return this.socket.message$.pipe(
1244
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.ISystemIdentification>('systemIdentification', requestTimeout, id),
1245
- );
1246
- }),
1247
- );
1248
- }
1249
-
1250
- /**
1251
- * {@inheritDoc startSystemIdentification}
1252
- */
1253
- async startSystemIdentificationAsync(props: MotionMasterMessage.Request.IStartSystemIdentification & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1254
- const status = await lastValueFrom(this.startSystemIdentification(props, requestTimeout, messageId));
1255
-
1256
- if (status.request === 'succeeded') {
1257
- return;
1258
- }
1259
-
1260
- throw new Error(`Failed start system identification. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1261
- }
1262
-
1263
- /**
1264
- * Get circulo encoder magnet distance.
1265
- *
1266
- * check encoder ordinal (1 or 2)
1267
- * ring revision
1268
- * is it based on fw 5 in fw <5 this doesn't exist error in that case
1269
- * read encoder registers with OS command, registers read 2B and 2F, some values go through math depending on the version of Circulo, port, distance value is computed
1270
- * @todo check what OS command
1271
- */
1272
- getCirculoEncoderMagnetDistance(props: MotionMasterMessage.Request.IGetCirculoEncoderMagnetDistance & DeviceRefObj, requestTimeout: number, messageId?: string) {
1273
- return from(this.resolveDeviceAddress(props)).pipe(
1274
- mergeMap((deviceAddress) => {
1275
- const getCirculoEncoderMagnetDistance = MotionMasterMessage.Request.GetCirculoEncoderMagnetDistance.create({ ...props, deviceAddress });
1276
- const id = this.sendRequest({ getCirculoEncoderMagnetDistance }, messageId);
1277
- return this.socket.message$.pipe(
1278
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.ICirculoEncoderMagnetDistance>('circuloEncoderMagnetDistance', requestTimeout, id),
1279
- );
1280
- }),
1281
- );
1282
- }
1283
-
1284
- /**
1285
- * {@inheritDoc getCirculoEncoderMagnetDistance}
1286
- */
1287
- async getCirculoEncoderMagnetDistanceAsync(props: MotionMasterMessage.Request.IGetCirculoEncoderMagnetDistance & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<{ distance: number | null | undefined, encoderOrdinal: number | null | undefined, position: number | null | undefined }> {
1288
- const status = await lastValueFrom(this.getCirculoEncoderMagnetDistance(props, requestTimeout, messageId));
1289
-
1290
- if (status.request === 'succeeded') {
1291
- return (({ distance, encoderOrdinal, position }) => ({ distance, encoderOrdinal, position }))(status);
1292
- }
1293
-
1294
- throw new Error(`Failed to get circulo encoder magnet distance. ${JSON.stringify(props)}`);
1295
- }
1296
-
1297
- /**
1298
- * Start circulo encoder narrow angle calibration procedure.
1299
- *
1300
- * check encoder ordinal
1301
- * check fw version 5
1302
- * check if SMM, has to be disable safety encoder source type
1303
- * ignore biss status bit, OS command for encoder to ignore BiSS status bits
1304
- * initialize velocity profile PV
1305
- * check software position limits
1306
- * initialize intern values, feed constant, single turn resolution, si unit, get parameter values, gear ratio, what encoder, port, if gear ratio etc etc
1307
- * if gear box and si unit velocity not mRPM, and position only encoder, and si unit * feed constant is <10% of gear ratio, error not enough velocity resolution
1308
- * hardware description file, different values for RPM for different version of Circulo, inner|outer encoder ring, multiturn, not, change profile acc|dec max motor speed etc.
1309
- * after everything rollback
1310
- * check if enough turns is possible, if not enough space, error
1311
- * set default calibration register values
1312
- * set biss to raw mode, no longer position - raw position
1313
- * ic house lib is initialized
1314
- * remove previous files for high resolution data 4kHz, for each iteration, hight res data contains raw data from encoder
1315
- * OS command configure HRD streaming, automatically sets data from encoder, can only configure how long to record
1316
- * slave saves data to HRD files
1317
- * after each iteration which lasts 6 seconds, every iteration is one direction, MM takes data recorded in HRD files and puts that passes that to
1318
- * master/noniuns track data it takes 32-bit values are both splitted into 2 parts (master nonius), two arrays one master one nonious
1319
- * that data are passed to iChouse library which give depending on encoder type track master nonius, return some sine offset, cos offset, gain, etc.
1320
- * that procuderu can have errors: data out of range, motor to slow, bad data, internal error - if error in any iteration the procedure stops
1321
- * if no error then check if calibration succeeded depending on the values (iterativelly improve values by writing them back to some registers), if not run another iteration
1322
- * max iteration to run is 13 (up to 2 mins)
1323
- * treshold is determined by Petr
1324
- * if iteration is successfull, HRD files are moved after deinitializing (succesfful run),
1325
- * the result of calibration is setting multiple register values
1326
- * all parameters are reverted back, motor is rotated back to the starging position, commutation offset measurement (just one OS command) is run again
1327
- * how many iterations are run are based on results
1328
- * final itertion is for outputting does nothhing just measures the values
1329
- */
1330
- startCirculoEncoderNarrowAngleCalibrationProcedure(props: MotionMasterMessage.Request.IStartCirculoEncoderNarrowAngleCalibrationProcedure & DeviceRefObj, requestTimeout: number, messageId?: string) {
1331
- return from(this.resolveDeviceAddress(props)).pipe(
1332
- mergeMap((deviceAddress) => {
1333
- const startCirculoEncoderNarrowAngleCalibrationProcedure = MotionMasterMessage.Request.StartCirculoEncoderNarrowAngleCalibrationProcedure.create({ ...props, deviceAddress });
1334
- const id = this.sendRequest({ startCirculoEncoderNarrowAngleCalibrationProcedure }, messageId);
1335
- return this.socket.message$.pipe(
1336
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.CirculoEncoderNarrowAngleCalibrationProcedure>('circuloEncoderNarrowAngleCalibrationProcedure', requestTimeout, id),
1337
- );
1338
- }),
1339
- );
1340
- }
1341
-
1342
- /**
1343
- * {@inheritDoc startCirculoEncoderNarrowAngleCalibrationProcedure}
1344
- */
1345
- async startCirculoEncoderNarrowAngleCalibrationProcedureAsync(props: MotionMasterMessage.Request.IStartCirculoEncoderNarrowAngleCalibrationProcedure & DeviceRefObj, requestTimeout: number, messageId?: string) {
1346
- const status = await lastValueFrom(this.startCirculoEncoderNarrowAngleCalibrationProcedure(props, requestTimeout, messageId));
1347
-
1348
- if (status.request === 'succeeded') {
1349
- return;
1350
- }
1351
-
1352
- throw new Error(`Failed start circulo encoder narrow angle calibration procedure. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1353
- }
1354
-
1355
- getDeviceCia402State(props: MotionMasterMessage.Request.IGetDeviceCiA402State & DeviceRefObj, requestTimeout: number, messageId?: string) {
1356
- return from(this.resolveDeviceAddress(props)).pipe(
1357
- mergeMap((deviceAddress) => {
1358
- const getDeviceCia402State = MotionMasterMessage.Request.GetDeviceCiA402State.create({ ...props, deviceAddress });
1359
- const id = this.sendRequest({ getDeviceCia402State }, messageId);
1360
- return this.socket.message$.pipe(
1361
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceCiA402State>('deviceCia402State', requestTimeout, id),
1362
- );
1363
- }),
1364
- );
1365
- }
1366
-
1367
- async getDeviceCia402StateAsync(props: MotionMasterMessage.Request.IGetDeviceCiA402State & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.DeviceCiA402State.State> {
1368
- const status = await lastValueFrom(this.getDeviceCia402State(props, requestTimeout, messageId));
1369
-
1370
- if (status.request === 'succeeded') {
1371
- if (status.state) {
1372
- return status.state;
1373
- }
1374
- }
1375
-
1376
- throw new Error(`Failed to get device cia 402 state. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1377
- }
1378
-
1379
- setDeviceCia402State(props: MotionMasterMessage.Request.ISetDeviceCiA402State & DeviceRefObj, requestTimeout: number, messageId?: string) {
1380
- return from(this.resolveDeviceAddress(props)).pipe(
1381
- mergeMap((deviceAddress) => {
1382
- const setDeviceCia402State = MotionMasterMessage.Request.SetDeviceCiA402State.create({ ...props, deviceAddress });
1383
- const id = this.sendRequest({ setDeviceCia402State }, messageId);
1384
- return this.socket.message$.pipe(
1385
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceCiA402State>('deviceCia402State', requestTimeout, id),
1386
- );
1387
- }),
1388
- );
1389
- }
1390
-
1391
- async setDeviceCia402StateAsync(props: MotionMasterMessage.Request.ISetDeviceCiA402State & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1392
- const status = await lastValueFrom(this.setDeviceCia402State(props, requestTimeout, messageId));
1393
-
1394
- if (status.request === 'succeeded') {
1395
- return;
1396
- }
1397
-
1398
- throw new Error(`Failed to set device cia 402 state. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1399
- }
1400
-
1401
- getSystemLog(requestTimeout: number, messageId?: string) {
1402
- const getSystemLog = MotionMasterMessage.Request.GetSystemVersion.create();
1403
- const id = this.sendRequest({ getSystemLog }, messageId);
1404
- return this.socket.message$.pipe(
1405
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.SystemLog>('systemLog', requestTimeout, id),
1406
- );
1407
- }
1408
-
1409
- async getSystemLogAsync(requestTimeout: number): Promise<{ content: string, runEnv: string }> {
1410
- const status = await lastValueFrom(this.getSystemLog(requestTimeout));
1411
-
1412
- if (status.request === 'succeeded') {
1413
- return (({ content, runEnv }) => ({ content, runEnv }))(status);
1414
- }
1415
-
1416
- throw new Error(`Failed to get system log.`);
1417
- }
1418
-
1419
- startDeviceSiiRestore(props: MotionMasterMessage.Request.IStartDeviceSiiRestore, requestTimeout: number, messageId?: string) {
1420
- const startDeviceSiiRestore = MotionMasterMessage.Request.StartDeviceSiiRestore.create(props);
1421
- const id = this.sendRequest({ startDeviceSiiRestore }, messageId);
1422
- return this.socket.message$.pipe(
1423
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceSiiRestore>('deviceSiiRestore', requestTimeout, id),
1424
- );
1425
- }
1426
-
1427
- async startDeviceSiiRestoreAsync(props: MotionMasterMessage.Request.IStartDeviceSiiRestore, requestTimeout: number, messageId?: string): Promise<void> {
1428
- const status = await lastValueFrom(this.startDeviceSiiRestore(props, requestTimeout, messageId));
1429
-
1430
- if (status.request === 'succeeded') {
1431
- return;
1432
- }
1433
-
1434
- throw new Error(`Failed to start device sii restore. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1435
- }
1436
-
1437
- startOpenLoopFieldControl(props: MotionMasterMessage.Request.IStartOpenLoopFieldControl & DeviceRefObj, requestTimeout: number, messageId?: string) {
1438
- return from(this.resolveDeviceAddress(props)).pipe(
1439
- mergeMap((deviceAddress) => {
1440
- const startOpenLoopFieldControl = MotionMasterMessage.Request.StartOpenLoopFieldControl.create({ ...props, deviceAddress });
1441
- const id = this.sendRequest({ startOpenLoopFieldControl }, messageId);
1442
- return this.socket.message$.pipe(
1443
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.OpenLoopFieldControl>('openLoopFieldControl', requestTimeout, id),
1444
- );
1445
- }),
1446
- );
1447
- }
1448
-
1449
- async startOpenLoopFieldControlAsync(props: MotionMasterMessage.Request.IStartOpenLoopFieldControl & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1450
- const status = await lastValueFrom(this.startOpenLoopFieldControl(props, requestTimeout, messageId));
1451
-
1452
- if (status.request === 'succeeded') {
1453
- return;
1454
- }
1455
-
1456
- throw new Error(`Failed to start open loop field control. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1457
- }
1458
-
1459
- /**
1460
- * @deprecated
1461
- */
1462
- computeFullAutoTuningGains(props: MotionMasterMessage.Request.IComputeFullAutoTuningGains & DeviceRefObj, requestTimeout: number, messageId?: string) {
1463
- return from(this.resolveDeviceAddress(props)).pipe(
1464
- mergeMap((deviceAddress) => {
1465
- const computeFullAutoTuningGains = MotionMasterMessage.Request.ComputeFullAutoTuningGains.create({ ...props, deviceAddress });
1466
- const id = this.sendRequest({ computeFullAutoTuningGains }, messageId);
1467
- return this.socket.message$.pipe(
1468
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.FullAutoTuning>('fullAutoTuning', requestTimeout, id),
1469
- );
1470
- }),
1471
- );
1472
- }
1473
-
1474
- /**
1475
- * @deprecated
1476
- */
1477
- async computeFullAutoTuningGainsAsync(props: MotionMasterMessage.Request.IComputeFullAutoTuningGains & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1478
- const status = await lastValueFrom(this.computeFullAutoTuningGains(props, requestTimeout, messageId));
1479
-
1480
- if (status.request === 'succeeded') {
1481
- return;
1482
- }
1483
-
1484
- throw new Error(`Failed to compute full auto tuning gains. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1485
- }
1486
-
1487
- startFullAutoTuning(props: MotionMasterMessage.Request.IStartFullAutoTuning & DeviceRefObj, requestTimeout: number, messageId?: string) {
1488
- return from(this.resolveDeviceAddress(props)).pipe(
1489
- mergeMap((deviceAddress) => {
1490
- const startFullAutoTuning = MotionMasterMessage.Request.StartFullAutoTuning.create({ ...props, deviceAddress });
1491
- const id = this.sendRequest({ startFullAutoTuning }, messageId);
1492
- return this.socket.message$.pipe(
1493
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.FullAutoTuning>('fullAutoTuning', requestTimeout, id),
1494
- );
1495
- }),
1496
- );
1497
- }
1498
-
1499
- async startFullAutoTuningAsync(props: MotionMasterMessage.Request.IStartFullAutoTuning & DeviceRefObj, requestTimeout: number, messageId?: string) {
1500
- const status = await lastValueFrom(this.startFullAutoTuning(props, requestTimeout, messageId));
1501
-
1502
- if (status.request === 'succeeded') {
1503
- if (status.settlingTimeOrBandwidth === 'bandwidth') {
1504
- return (({ bandwidth, dampingRatio }) => ({ bandwidth, dampingRatio }))(status);
1505
- } else if (status.settlingTimeOrBandwidth === 'settlingTime') {
1506
- return (({ settlingTime, dampingRatio }) => ({ dampingRatio, settlingTime }))(status);
1507
- }
1508
- }
1509
-
1510
- throw new Error(`Failed to start full auto tuning. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1511
- }
1512
-
1513
- stopFullAutoTuning(props: MotionMasterMessage.Request.IStopFullAutoTuning & DeviceRefObj, requestTimeout: number, messageId?: string) {
1514
- return from(this.resolveDeviceAddress(props)).pipe(
1515
- mergeMap((deviceAddress) => {
1516
- const stopFullAutoTuning = MotionMasterMessage.Request.StopFullAutoTuning.create({ ...props, deviceAddress });
1517
- const id = this.sendRequest({ stopFullAutoTuning }, messageId);
1518
- return this.socket.message$.pipe(
1519
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.FullAutoTuning>('fullAutoTuning', requestTimeout, id),
1520
- map((status) => {
1521
- if (status.success?.code === MotionMasterMessage.Status.FullAutoTuning.Success.Code.POSITION_STOPPED ||
1522
- status.success?.code === MotionMasterMessage.Status.FullAutoTuning.Success.Code.VELOCITY_STOPPED) {
1523
- // Full auto tuning status is shared between start and stop.
1524
- // When full auto tuning has stopped this request is considered successful.
1525
- status.request = 'succeeded';
1526
- }
1527
- return status;
1528
- }),
1529
- );
1530
- }),
1531
- );
1532
- }
1533
-
1534
- async stopFullAutoTuningAsync(props: MotionMasterMessage.Request.IStopFullAutoTuning & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1535
- const status = await lastValueFrom(this.stopFullAutoTuning(props, requestTimeout, messageId));
1536
-
1537
- if (status.request === 'succeeded') {
1538
- return;
1539
- }
1540
-
1541
- throw new Error(`Failed to stop full auto tuning. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1542
- }
1543
-
1544
- startCirculoEncoderConfiguration(props: MotionMasterMessage.Request.IStartCirculoEncoderConfiguration & DeviceRefObj, requestTimeout: number, messageId?: string) {
1545
- return from(this.resolveDeviceAddress(props)).pipe(
1546
- mergeMap((deviceAddress) => {
1547
- const startCirculoEncoderConfiguration = MotionMasterMessage.Request.StartCirculoEncoderConfiguration.create({ ...props, deviceAddress });
1548
- const id = this.sendRequest({ startCirculoEncoderConfiguration }, messageId);
1549
- return this.socket.message$.pipe(
1550
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.CirculoEncoderConfiguration>('circuloEncoderConfiguration', requestTimeout, id),
1551
- );
1552
- }),
1553
- );
1554
- }
1555
-
1556
- async startCirculoEncoderConfigurationAsync(props: MotionMasterMessage.Request.IStartCirculoEncoderConfiguration & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
1557
- const status = await lastValueFrom(this.startCirculoEncoderConfiguration(props, requestTimeout, messageId));
1558
-
1559
- if (status.request === 'succeeded') {
1560
- return;
1561
- }
1562
-
1563
- throw new Error(`Failed to start circulo encoder configuration. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1564
- }
1565
-
1566
- stopCirculoEncoderNarrowAngleCalibrationProcedure(props: MotionMasterMessage.Request.IStopCirculoEncoderNarrowAngleCalibrationProcedure & DeviceRefObj, requestTimeout: number, messageId?: string) {
1567
- return from(this.resolveDeviceAddress(props)).pipe(
1568
- mergeMap((deviceAddress) => {
1569
- const stopCirculoEncoderNarrowAngleCalibrationProcedure = MotionMasterMessage.Request.StopCirculoEncoderNarrowAngleCalibrationProcedure.create({ ...props, deviceAddress });
1570
- const id = this.sendRequest({ stopCirculoEncoderNarrowAngleCalibrationProcedure }, messageId);
1571
- return this.socket.message$.pipe(
1572
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.CirculoEncoderNarrowAngleCalibrationProcedure>('circuloEncoderNarrowAngleCalibrationProcedure', requestTimeout, id),
1573
- );
1574
- }),
1575
- );
1576
- }
1577
-
1578
- async stopCirculoEncoderNarrowAngleCalibrationProcedureAsync(props: MotionMasterMessage.Request.IStopCirculoEncoderNarrowAngleCalibrationProcedure & DeviceRefObj, requestTimeout: number, messageId?: string) {
1579
- const status = await lastValueFrom(this.stopCirculoEncoderNarrowAngleCalibrationProcedure(props, requestTimeout, messageId));
1580
-
1581
- if (status.request === 'succeeded') {
1582
- return;
1583
- }
1584
-
1585
- throw new Error(`Failed to stop circulo encoder narrow angle calibration procedure. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1586
- }
1587
-
1588
- startOsCommand(props: MotionMasterMessage.Request.IStartOsCommand & DeviceRefObj, requestTimeout: number, messageId?: string) {
1589
- return from(this.resolveDeviceAddress(props)).pipe(
1590
- mergeMap((deviceAddress) => {
1591
- const startOsCommand = MotionMasterMessage.Request.StartOsCommand.create({ ...props, deviceAddress });
1592
- const id = this.sendRequest({ startOsCommand }, messageId);
1593
- return this.socket.message$.pipe(
1594
- transformMotionMasterMessageToStatus<MotionMasterMessage.Status.OsCommand>('osCommand', requestTimeout, id),
1595
- );
1596
- }),
1597
- );
1598
- }
1599
-
1600
- async startOsCommandAsync(props: MotionMasterMessage.Request.IStartOsCommand & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<Uint8Array> {
1601
- const status = await lastValueFrom(this.startOsCommand(props, requestTimeout, messageId));
1602
-
1603
- if (status.request === 'succeeded') {
1604
- return status.response;
1605
- }
1606
-
1607
- throw new Error(`Failed to start os command. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
1608
- }
1609
-
1610
- sendRequest(request: MotionMasterMessage.IRequest, messageId?: string) {
1611
- const id = messageId ?? v4();
1612
- const message = MotionMasterMessage.create({ request, id });
1613
- this.socket.send(message);
1614
- return id;
1615
- }
1616
-
1617
- //
1618
- // Helpers
1619
- //
1620
-
1621
- async resolveDeviceAddress(deviceRefObj: DeviceRefObj): Promise<number> {
1622
- if (deviceRefObj.deviceAddress) {
1623
- return deviceRefObj.deviceAddress;
1624
- }
1625
-
1626
- if (this.deviceAddressMap.size === 0) {
1627
- const devices = await this.getDeviceInfoAsync(2000);
1628
- for (const device of devices) {
1629
- if (device.deviceAddress) {
1630
- const hardwareDescripition = await this.getDeviceHardwareDescription(device.deviceAddress);
1631
- const serialNumber = getSerialNumberFromHardwareDescription(hardwareDescripition);
1632
- this.deviceAddressMap.set(serialNumber, device.deviceAddress);
1633
- }
1634
- }
1635
- }
1636
-
1637
- if (deviceRefObj.deviceSerialNumber) {
1638
- const deviceAddress = this.deviceAddressMap.get(deviceRefObj.deviceSerialNumber);
1639
- if (deviceAddress) {
1640
- return deviceAddress;
1641
- }
1642
- throw new Error(`No device found by device serial number "${deviceRefObj.deviceSerialNumber}" when resolving device address`);
1643
- }
1644
-
1645
- if (typeof deviceRefObj.devicePosition === 'number') {
1646
- const values = Array.from(this.deviceAddressMap.values());
1647
- const deviceAddress = values[deviceRefObj.devicePosition];
1648
- if (typeof deviceAddress === 'number') {
1649
- return deviceAddress;
1650
- }
1651
- throw new Error(`No device found by device position when resolving device address: ${deviceRefObj.devicePosition}`);
1652
- }
1653
-
1654
- throw new Error(`Failed to resolve device address. One of device address, serial number, or position must be specified.`);
1655
- }
1656
-
1657
- async getDeviceAddresses(requestTimeout = 2000): Promise<number[]> {
1658
- const deviceInfo = await this.getDeviceInfoAsync(requestTimeout);
1659
- if (deviceInfo) {
1660
- return deviceInfo.reduce((acc, val) => {
1661
- if (typeof val.deviceAddress === 'number') {
1662
- acc.push(val.deviceAddress);
1663
- }
1664
- return acc;
1665
- }, [] as number[]);
1666
- }
1667
- return [];
1668
- }
1669
-
1670
- async getDeviceHardwareDescription(deviceRef: number | string, requestTimeout = 2000): Promise<HardwareDescription> {
1671
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1672
- const content = await this.getDecodedDeviceFile({ ...deviceRefObj, name: '.hardware_description' }, requestTimeout);
1673
- return JSON.parse(content) as HardwareDescription;
1674
- }
1675
-
1676
- async getSystemLogLines(requestTimeout = 2000): Promise<SystemLogLine[]> {
1677
- const systemLog = await this.getSystemLogAsync(requestTimeout);
1678
- return parseSystemLogContent(systemLog.content);
1679
- }
1680
-
1681
- getAllParameters(props: { loadFromCache?: boolean, sendProgress?: boolean } & DeviceRefObj, requestTimeout: number): Observable<Parameter[]> {
1682
- return from(this.resolveDeviceAddress(props)).pipe(
1683
- mergeMap((deviceAddress) => {
1684
- return this.getDeviceParameterInfo({ deviceAddress }, requestTimeout).pipe(
1685
- mergeMap(deviceParameterInfoStatus => {
1686
- const { loadFromCache, sendProgress } = props;
1687
- const parameters = deviceParameterInfoStatus.parameters.map(p => ({ index: p.index ?? 0, subindex: p.subindex ?? 0, loadFromCache }));
1688
- return this.getDeviceParameterValues({ deviceAddress, sendProgress, parameters }, requestTimeout).pipe(
1689
- map(deviceParameterValuesStatus => {
1690
- const parameters: Parameter[] = deviceParameterValuesStatus.parameterValues.map((value, i) => {
1691
- const index = value.index ?? 0;
1692
- const subindex = value.subindex ?? 0;
1693
- return ({ ...value, ...deviceParameterInfoStatus.parameters[i], index, subindex, value: getParameterValue(value) }) as Parameter;
1694
- });
1695
- return parameters;
1696
- }),
1697
- );
1698
- }),
1699
- );
1700
- }),
1701
- );
1702
- }
1703
-
1704
- async getAllParametersAsync(props: { loadFromCache?: boolean, sendProgress?: boolean } & DeviceRefObj, requestTimeout: number) {
1705
- return await lastValueFrom(
1706
- this.getAllParameters(props, requestTimeout),
1707
- );
1708
- }
1709
-
1710
- async getParameterValue<T extends ParameterValueType = number>(deviceParameterId: string, loadFromCache?: boolean, requestTimeout?: number): Promise<T>;
1711
- async getParameterValue<T extends ParameterValueType = number>(deviceRef: number | string, parameter: [number, number] | { index: number, subindex: number } | string, loadFromCache?: boolean, requestTimeout?: number): Promise<T>;
1712
- async getParameterValue<T extends ParameterValueType = number>(deviceRef: number | string, index: number, subindex: number, loadFromCache?: boolean, requestTimeout?: number): Promise<T>;
1713
- async getParameterValue<T extends ParameterValueType = number>(a: string | number, b?: boolean | { index: number, subindex: number } | [number, number] | number | string, c?: number | boolean, d?: boolean | number, e?: number): Promise<T> {
1714
- let deviceSerialNumber: string;
1715
- let index: number | undefined;
1716
- let subindex = 0;
1717
- let loadFromCache = false;
1718
- let requestTimeout = 1000;
1719
- let deviceRefObj: DeviceRefObj | undefined;
1720
-
1721
- if (Array.isArray(b)) { // tuple of index and subindex, e.g. [0x2110, 0]
1722
- deviceRefObj = makeDeviceRefObj(a);
1723
- [index, subindex] = b;
1724
- if (typeof c === 'boolean') {
1725
- loadFromCache = c;
1726
- }
1727
- if (typeof d === 'number') {
1728
- requestTimeout = d;
1729
- }
1730
- } else if (typeof b === 'object') { // object with index and subindex properties, e.g. { index: 0x2110, subindex: 0x1A }
1731
- deviceRefObj = makeDeviceRefObj(a);
1732
- index = b.index;
1733
- subindex = b.subindex;
1734
- if (typeof c === 'boolean') {
1735
- loadFromCache = c;
1736
- }
1737
- if (typeof d === 'number') {
1738
- requestTimeout = d;
1739
- }
1740
- } else if (typeof b === 'string') { // parameter id, e.g. "0x2110:1A"
1741
- [index, subindex] = splitParameterId(b);
1742
- deviceRefObj = makeDeviceRefObj(a);
1743
- if (typeof c === 'boolean') {
1744
- loadFromCache = c;
1745
- }
1746
- if (typeof d === 'number') {
1747
- requestTimeout = d;
1748
- }
1749
- } else if (typeof b === 'number') { // object index, e.g. 0x2110
1750
- deviceRefObj = makeDeviceRefObj(a);
1751
- index = b;
1752
- if (typeof c === 'number') {
1753
- subindex = c;
1754
- }
1755
- if (typeof d === 'boolean') {
1756
- loadFromCache = d;
1757
- }
1758
- if (typeof e === 'number') {
1759
- requestTimeout = e;
1760
- }
1761
- } else if (typeof a === 'string') { // device parameter id, e.g. "0x2110:1A.8502-03-0001353-2115"
1762
- [deviceSerialNumber, index, subindex] = splitDeviceParameterId(a);
1763
- deviceRefObj = makeDeviceRefObj(deviceSerialNumber);
1764
- if (typeof b === 'boolean') {
1765
- loadFromCache = b;
1766
- }
1767
- if (typeof c === 'number') {
1768
- requestTimeout = c;
1769
- }
1770
- }
1771
-
1772
- const status = await lastValueFrom(
1773
- this.getDeviceParameterValues({ ...deviceRefObj, parameters: [{ index, subindex, loadFromCache }] }, requestTimeout),
1774
- );
1775
-
1776
- const parameterValue = status.parameterValues[0] as MotionMasterMessage.Status.DeviceParameterValues.ParameterValue;
1777
-
1778
- if (parameterValue.success) {
1779
- const typeValue = parameterValue.typeValue;
1780
- if (typeValue) {
1781
- return status.parameterValues[0][typeValue] as T;
1782
- }
1783
- }
1784
-
1785
- const props = { ...deviceRefObj, index, subindex, loadFromCache };
1786
-
1787
- const errorCodeName = MotionMasterMessage.Status.DeviceParameterValues.ParameterValue.Error.Code[status.parameterValues[0]?.error?.code ?? 0];
1788
- throw new Error(`Failed to get parameter value. ${errorCodeName} ${JSON.stringify(props)}`);
1789
- }
1790
-
1791
- async setParameterValue(deviceRef: number | string, index: number, subindex: number, value: ParameterValueType, valueTypeKey?: keyof Required<ParameterTypeValue>, requestTimeout = 1000): Promise<void> {
1792
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1793
- const deviceAddress = await this.resolveDeviceAddress(deviceRefObj);
1794
-
1795
- const props = { ...deviceRefObj, index, subindex, value, valueTypeKey };
1796
-
1797
- if (!valueTypeKey) {
1798
- const parameterId = makeParameterId(index, subindex);
1799
- valueTypeKey = this.parameterTypeValueKeyMap.get(deviceAddress)?.get(parameterId);
1800
- if (!valueTypeKey) {
1801
- throw new Error(`Failed to set device parameter value. Value type key is not set. ${JSON.stringify(props)}`);
1802
- }
1803
- }
1804
-
1805
- const parameterValue: MotionMasterMessage.Request.SetDeviceParameterValues.IParameterValue = { index, subindex, [valueTypeKey]: value };
1806
-
1807
- const status = await lastValueFrom(
1808
- this.setDeviceParameterValues({ deviceAddress, parameterValues: [parameterValue] }, requestTimeout),
1809
- );
1810
-
1811
- if (status.request === 'succeeded') {
1812
- return;
1813
- }
1814
-
1815
- throw new Error(`Failed to set device parameter value. ${status.parameterValues[0]?.error?.message} ${JSON.stringify(props)}`);
1816
- }
1817
-
1818
- async setParameterValues(deviceRef: number | string, values: ParameterAddressValue[], requestTimeout = 1000): Promise<void> {
1819
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1820
- const deviceAddress = await this.resolveDeviceAddress(deviceRefObj);
1821
-
1822
- const props = { deviceAddress, parameters: values };
1823
-
1824
- const parameterValues = values.map(([index, subindex, value, valueTypeKey]) => {
1825
- if (!valueTypeKey) {
1826
- const parameterId = makeParameterId(index, subindex);
1827
- valueTypeKey = this.parameterTypeValueKeyMap.get(deviceAddress)?.get(parameterId);
1828
- if (!valueTypeKey) {
1829
- throw new Error(`Failed to set device parameter values. Value type key is not set. ${JSON.stringify(props)}`);
1830
- }
1831
- }
1832
- return { index, subindex, [valueTypeKey]: value };
1833
- }) as MotionMasterMessage.Request.SetDeviceParameterValues.IParameterValue[];
1834
-
1835
- const status = await lastValueFrom(
1836
- this.setDeviceParameterValues({ deviceAddress, parameterValues }, requestTimeout),
1837
- );
1838
-
1839
- if (status.request === 'succeeded') {
1840
- return;
1841
- }
1842
-
1843
- throw new Error(`Failed to set device parameter values. ${status.parameterValues[0]?.error?.message} ${JSON.stringify(props)}`);
1844
- }
1845
-
1846
- async getDecodedDeviceFile(props: MotionMasterMessage.Request.IGetDeviceFile, requestTimeout: number, messageId?: string): Promise<string> {
1847
- const content = await this.getDeviceFileAsync(props, requestTimeout, messageId);
1848
- return decodeTextContent(content);
1849
- }
1850
-
1851
- async getDeviceFiles(deviceRef: number | string, requestTimeout = 30000) {
1852
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1853
- return this.getDeviceFileListAsync(deviceRefObj, requestTimeout);
1854
- }
1855
-
1856
- getDeviceParameterInfoAndValues(props: { deviceAddress?: number | null, loadFromCache?: boolean | null, sendProgress?: boolean | null }, requestTimeout: number): Observable<DeviceParameterValuesStatus> {
1857
- const { deviceAddress, loadFromCache, sendProgress } = props;
1858
- return this.getDeviceParameterInfo({ deviceAddress }, requestTimeout).pipe(
1859
- mergeMap(deviceParameterInfo => {
1860
- const parameters = deviceParameterInfo.parameters.map(p => ({ index: p.index ?? 0, subindex: p.subindex ?? 0, loadFromCache }));
1861
- return this.getDeviceParameterValues({ deviceAddress, parameters, sendProgress }, requestTimeout).pipe(
1862
- map(deviceParameterValues => {
1863
- const parameters: ParameterInfoAndValue[] = deviceParameterValues.parameterValues
1864
- .map((value, index) => ({ ...value, ...deviceParameterInfo.parameters[index] }));
1865
- deviceParameterValues.parameterValues = parameters;
1866
- return deviceParameterValues;
1867
- }),
1868
- );
1869
- }),
1870
- );
1871
- }
1872
-
1873
- async getDeviceParameterInfoAndValuesAsync(props: { deviceAddress?: number | null, loadFromCache?: boolean | null, sendProgress?: boolean | null }, requestTimeout: number) {
1874
- const status = await lastValueFrom(
1875
- this.getDeviceParameterInfoAndValues(props, requestTimeout),
1876
- );
1877
-
1878
- if (status.request === 'succeeded') {
1879
- return status.parameterValues;
1880
- }
1881
-
1882
- throw new Error(`Failed to get device parameter info and values. ${JSON.stringify(props)}`);
1883
- }
1884
-
1885
- saveAllParameters(deviceRef: number | string, requestTimeout: number) {
1886
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1887
-
1888
- return from(this.resolveDeviceAddress(deviceRefObj)).pipe(
1889
- mergeMap((deviceAddress) => {
1890
- const parameter: MotionMasterMessage.Request.SetDeviceParameterValues.IParameterValue = { index: 0x1010, subindex: 1, uintValue: 0x65766173 }; // "evas"
1891
- const parameterId = makeParameterId(parameter);
1892
-
1893
- const result$ = defer(() => this.checkDeviceParameterValue({ deviceAddress, parameter, expectedValue: 1 }, requestTimeout)).pipe(
1894
- retryWithDelay<void>(500, 10),
1895
- );
1896
-
1897
- return this.setDeviceParameterValues({ deviceAddress, parameterValues: [parameter] }, requestTimeout).pipe(
1898
- delay(2000), // give some initial time for operation to complete
1899
- mergeMap(({ parameterValues }) => {
1900
- const parameter = parameterValues[0];
1901
- if (parameter) {
1902
- if (parameter.error) {
1903
- throw new Error(`Failed to set ${parameterId} value to ${parameter.uintValue} on device ${deviceAddress}`);
1904
- }
1905
- } else {
1906
- throw new Error(`No parameters in status for ${parameterId} on device ${deviceAddress}`);
1907
- }
1908
- return result$;
1909
- }),
1910
- );
1911
- }),
1912
- );
1913
- }
1914
-
1915
- restoreAllDefaultParameters(deviceRef: number | string, requestTimeout: number) {
1916
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1917
-
1918
- return from(this.resolveDeviceAddress(deviceRefObj)).pipe(
1919
- mergeMap((deviceAddress) => {
1920
- const parameter: MotionMasterMessage.Request.SetDeviceParameterValues.IParameterValue = { index: 0x1011, subindex: 1, uintValue: 0x64616f6c }; // "daol"
1921
- const parameterId = makeParameterId(parameter);
1922
-
1923
- const result$ = defer(() => this.checkDeviceParameterValue({ deviceAddress, parameter, expectedValue: 1 }, requestTimeout)).pipe(
1924
- retryWithDelay<void>(500, 10),
1925
- );
1926
-
1927
- return this.setDeviceParameterValues({ deviceAddress, parameterValues: [parameter] }, requestTimeout).pipe(
1928
- delay(500), // give some initial time for operation to complete
1929
- mergeMap(({ parameterValues }) => {
1930
- const parameter = parameterValues[0];
1931
- if (parameter) {
1932
- if (parameter.error) {
1933
- throw new Error(`Failed to set ${parameterId} value to ${parameter.uintValue} on device ${deviceAddress}`);
1934
- }
1935
- } else {
1936
- throw new Error(`No parameters in status for ${parameterId} on device ${deviceAddress}`);
1937
- }
1938
- return result$;
1939
- }),
1940
- );
1941
- }),
1942
- );
1943
- }
1944
-
1945
- checkDeviceParameterValue(props: { parameter: MotionMasterMessage.Request.GetDeviceParameterValues.IParameter, expectedValue: number | string } & DeviceRefObj, requestTimeout: number, messageId?: string) {
1946
- return from(this.resolveDeviceAddress(props)).pipe(
1947
- mergeMap((deviceAddress) => {
1948
- const { expectedValue } = props;
1949
- const parameterId = makeParameterId(props.parameter);
1950
-
1951
- return this.getDeviceParameterValues({ deviceAddress, parameters: [props.parameter] }, requestTimeout, messageId).pipe(
1952
- map(({ parameterValues }) => {
1953
- const parameter = parameterValues[0];
1954
- if (parameter) {
1955
- const parameterValue = getParameterValue(parameter);
1956
- if (parameter.error) {
1957
- throw new Error(`Failed to get ${parameterId} on device ${deviceAddress}`);
1958
- } else if (parameterValue !== expectedValue) {
1959
- throw new Error(`Value of ${parameterId} didn't change to ${expectedValue} on device ${deviceAddress}`);
1960
- }
1961
- } else {
1962
- throw new Error(`No parameters in status for ${parameterId} on ${deviceAddress}`);
1963
- }
1964
- }),
1965
- );
1966
- }),
1967
- );
1968
- }
1969
-
1970
- async getDeviceApiIdentifier(deviceRef: number | string, requestTimeout = 2000) {
1971
- const hardwareDescription = await this.getDeviceHardwareDescription(deviceRef, requestTimeout);
1972
- return getApiIdentifierFromHardwareDescription(hardwareDescription);
1973
- }
1974
-
1975
- async enableMotion(deviceRef: number | string, controllerType: MotionMasterMessage.Request.EnableMotionController.ControllerType, filter = false, requestTimeout = 2000) {
1976
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1977
- return await this.enableMotionControllerAsync({ ...deviceRefObj, controllerType, filter }, requestTimeout);
1978
- }
1979
-
1980
- async setMotionTarget(deviceRef: number | string, target: number) {
1981
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1982
- return await this.setMotionControllerParametersAsync({ ...deviceRefObj, target });
1983
- }
1984
-
1985
- async disableMotion(deviceRef: number | string, requestTimeout = 3000) {
1986
- const deviceRefObj = makeDeviceRefObj(deviceRef);
1987
- return await this.disableMotionControllerAsync(deviceRefObj, requestTimeout);
1988
- }
1989
-
1990
- }