motion-master-client 0.0.77 → 0.0.86

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 (85) hide show
  1. package/README.md +227 -138
  2. package/package.json +1 -1
  3. package/src/cli.d.ts +1 -1
  4. package/src/cli.js +160 -160
  5. package/src/index.d.ts +30 -28
  6. package/src/index.js +33 -31
  7. package/src/index.js.map +1 -1
  8. package/src/lib/cia402.d.ts +184 -182
  9. package/src/lib/cia402.js +393 -391
  10. package/src/lib/cia402.js.map +1 -1
  11. package/src/lib/config-file.d.ts +13 -13
  12. package/src/lib/config-file.js +49 -49
  13. package/src/lib/data-monitoring.d.ts +12 -12
  14. package/src/lib/data-monitoring.js +33 -33
  15. package/src/lib/device-log-line.d.ts +5 -5
  16. package/src/lib/device-log-line.js +2 -2
  17. package/src/lib/device-parameter.d.ts +56 -56
  18. package/src/lib/device-parameter.js +38 -38
  19. package/src/lib/device.d.ts +46 -9
  20. package/src/lib/device.js +166 -2
  21. package/src/lib/device.js.map +1 -1
  22. package/src/lib/encoder-register.d.ts +16 -16
  23. package/src/lib/encoder-register.js +116 -116
  24. package/src/lib/hardware-description.d.ts +46 -46
  25. package/src/lib/hardware-description.js +109 -109
  26. package/src/lib/integro-encoder-calibration.d.ts +31 -0
  27. package/src/lib/integro-encoder-calibration.js +196 -0
  28. package/src/lib/integro-encoder-calibration.js.map +1 -0
  29. package/src/lib/logger.d.ts +1 -1
  30. package/src/lib/logger.js +7 -7
  31. package/src/lib/monitoring-config.d.ts +31 -31
  32. package/src/lib/monitoring-config.js +2 -2
  33. package/src/lib/monitoring-entry.d.ts +9 -9
  34. package/src/lib/monitoring-entry.js +2 -2
  35. package/src/lib/motion-master-client.d.ts +77 -77
  36. package/src/lib/motion-master-client.js +196 -196
  37. package/src/lib/motion-master-pub-sub-client.d.ts +17 -17
  38. package/src/lib/motion-master-pub-sub-client.js +72 -72
  39. package/src/lib/motion-master-pub-sub-client.js.map +1 -1
  40. package/src/lib/motion-master-pub-sub-socket.d.ts +42 -42
  41. package/src/lib/motion-master-pub-sub-socket.js +2 -2
  42. package/src/lib/motion-master-pub-sub-web-socket.d.ts +18 -18
  43. package/src/lib/motion-master-pub-sub-web-socket.js +65 -65
  44. package/src/lib/motion-master-pub-sub-worker-socket.d.ts +18 -18
  45. package/src/lib/motion-master-pub-sub-worker-socket.js +47 -47
  46. package/src/lib/motion-master-req-res-client.d.ts +1104 -1062
  47. package/src/lib/motion-master-req-res-client.js +2137 -1997
  48. package/src/lib/motion-master-req-res-client.js.map +1 -1
  49. package/src/lib/motion-master-req-res-socket.d.ts +60 -60
  50. package/src/lib/motion-master-req-res-socket.js +2 -2
  51. package/src/lib/motion-master-req-res-web-socket.d.ts +28 -28
  52. package/src/lib/motion-master-req-res-web-socket.js +97 -97
  53. package/src/lib/motion-master-req-res-worker-socket.d.ts +24 -24
  54. package/src/lib/motion-master-req-res-worker-socket.js +71 -71
  55. package/src/lib/motion-master.proto.d.ts +5183 -5183
  56. package/src/lib/motion-master.proto.js +53218 -53218
  57. package/src/lib/operators.d.ts +20 -20
  58. package/src/lib/operators.js +82 -83
  59. package/src/lib/operators.js.map +1 -1
  60. package/src/lib/options.d.ts +10 -10
  61. package/src/lib/options.js +13 -13
  62. package/src/lib/os-command.d.ts +387 -173
  63. package/src/lib/os-command.js +555 -269
  64. package/src/lib/os-command.js.map +1 -1
  65. package/src/lib/parameter.d.ts +156 -138
  66. package/src/lib/parameter.js +377 -152
  67. package/src/lib/parameter.js.map +1 -1
  68. package/src/lib/product-id-range.d.ts +7 -7
  69. package/src/lib/product-id-range.js +11 -11
  70. package/src/lib/request-status-resolver.d.ts +4 -4
  71. package/src/lib/request-status-resolver.js +344 -344
  72. package/src/lib/somanet-product.d.ts +37 -0
  73. package/src/lib/somanet-product.js +126 -0
  74. package/src/lib/somanet-product.js.map +1 -0
  75. package/src/lib/system-log-line.d.ts +9 -9
  76. package/src/lib/system-log-line.js +2 -2
  77. package/src/lib/types.d.ts +141 -141
  78. package/src/lib/types.js +28 -28
  79. package/src/lib/urls.d.ts +3 -3
  80. package/src/lib/urls.js +9 -9
  81. package/src/lib/util.d.ts +21 -45
  82. package/src/lib/util.js +113 -379
  83. package/src/lib/util.js.map +1 -1
  84. package/src/lib/web-socket-connection-close-codes.d.ts +8 -8
  85. package/src/lib/web-socket-connection-close-codes.js +88 -88
@@ -1,1998 +1,2138 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MotionMasterReqResClient = void 0;
4
- const tslib_1 = require("tslib");
5
- const rxjs_1 = require("rxjs");
6
- const operators_1 = require("rxjs/operators");
7
- const uuid_1 = require("uuid");
8
- const device_parameter_1 = require("./device-parameter");
9
- const hardware_description_1 = require("./hardware-description");
10
- const operators_2 = require("./operators");
11
- const parameter_1 = require("./parameter");
12
- const types_1 = require("./types");
13
- const util_1 = require("./util");
14
- const cia402_1 = require("./cia402");
15
- const cia402_2 = require("./cia402");
16
- const cia402_3 = require("./cia402");
17
- const os_command_1 = require("./os-command");
18
- /**
19
- * This class contains methods for making requests to Motion Master using the injected request/response socket.
20
- *
21
- * Each request message defined in the proto file has a corresponding request method in this class.
22
- * For some requests, like firmware installation, Motion Master will send progress messages until the firmware installation is complete.
23
- *
24
- * A call to a request method will return an Observable of: (1) the corresponding status message,
25
- * for example a call to `getDeviceInfo` will return an instance of `MotionMasterMessage.Status.DeviceInfo`,
26
- * (2) request status - one of "succeeded," "failed," or "running.", and (3) request message id.
27
- *
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 an 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 requests with response messages in full-duplex asynchronous communication (WebSockets, ZeroMQ).
34
- * Matching request/response by message id is inspired by the [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification).
35
- *
36
- * Almost all request methods have a mandatory `requestTimeout` input parameter.
37
- * This parameter specifies how long (in milliseconds) the client will wait for the status (response) messages corresponding to the previous request.
38
- * If the 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 the client has given up waiting for the response(s).
40
- * It's possible to stop some ongoing procedures on Motion Master with the corresponding stop messages, for example, StopFullAutoTuning.
41
- *
42
- * @example
43
- * How to handle a request method timeout error:
44
- * ```typescript
45
- * req.getDeviceInfo(10).subscribe({
46
- * next: (status) => console.log(status),
47
- * error: (err: unknown) => {
48
- * if (err instanceof TimeoutError) {
49
- * alert('Request has timed out');
50
- * }
51
- * }
52
- * });
53
- * ```
54
- *
55
- * Request methods for a particular device, like `getDeviceParameterInfo`, accept the type of request message as the first input parameter as defined in the proto file.
56
- * For `getDeviceParameterInfo`, that's `MotionMasterMessage.Request.IGetDeviceParameterInfo`. This type is extended (via type intersection) by `DeviceRefObj`.
57
- * This is done in order to support multiple ways to reference a device. Motion Master only supports device addresses, which it re-generates for each device on power cycle.
58
- * Because device addresses can change, for scripts and applications, it's preferred to use a device reference that won't change.
59
- * The device serial number in the hardware description file uniquely identifies the device and doesn't change. This is what each request message is extended with.
60
- * Before sending the request message to Motion Master, the device address gets resolved and is set on the request message.
61
- * Besides the device address and device serial number, devices can be referenced by position (sequence number in the network chain).
62
- *
63
- * It is ensured that the Observable returned from a call to the request method will eventually complete, so a call to unsubscribe is not required.
64
- * The Observable will complete when the request status is either "succeeded" or "failed," or it will complete (error) due to a timeout.
65
- * Finding out if a request has completed, failed, or is still ongoing is different for each request - it depends on the status messages, see {@link requestStatusResolver}.
66
- * Request methods handle this and for each request, they will emit status messages with one of "succeeded," "failed," or "running" request statuses.
67
- *
68
- * Besides the request methods, this class contains many helper methods such as the ones for getting and setting device parameter values and
69
- * saving all parameters, which involves complex logic like multiple repeating requests to determine if saving all parameters has completed successfully.
70
- */
71
- class MotionMasterReqResClient {
72
- constructor(socket) {
73
- this.socket = socket;
74
- /**
75
- * Maps a device address to a map of parameter ID to a type value key.
76
- *
77
- * @example
78
- * ```typescript
79
- * new Map([
80
- * [2045285448, new Map([
81
- * ["0x1018:02", {
82
- * id: '0x1018:02',
83
- * index: 4120,
84
- * subindex: 2,
85
- * name: 'Product Code',
86
- * unit: '',
87
- * group: 'Identity object',
88
- * readAccess: true,
89
- * writeAccess: false,
90
- * min: undefined,
91
- * max: undefined,
92
- * valueType: 7, // UNSIGNED32
93
- * typeValueKey: "uintValue"
94
- * }]
95
- * ])]
96
- * ]);
97
- * ```
98
- *
99
- * When setting a parameter value, only one of int, uint, float, string, or raw type value fields must be set.
100
- * This is defined in the proto file SetDeviceParameterValues request message.
101
- *
102
- * Device parameters have a richer set of base types defined in ESI, and they are provided by the DeviceParameterInfo status message.
103
- * When device parameter info is fetched, this map gets updated by mapping device parameter types to "oneof" proto types, e.g., `INTEGER32 -> intValue`.
104
- *
105
- * This map helps in simplifying the API by allowing users to set parameter values without specifying the type value field.
106
- * 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.
107
- */
108
- this.deviceParameterInfoMap = new Map();
109
- /**
110
- * Maps a device serial number to a device.
111
- *
112
- * @example
113
- * ```typescript
114
- * new Map([
115
- * ["8502-03-0001353-2115", { deviceAddress: 2045285448, ... }]
116
- * ]);
117
- * ```
118
- *
119
- * When making requests related to a device, the device address is used as an identifier.
120
- * The device address is not permanent. The number is regenerated by Motion Master when devices are power-cycled.
121
- * For scripts and applications using this library, having a permanent device identifier is a better option.
122
- * The device serial number read from the hardware description file (.hardware_description) stored on a device is a permanent identifier.
123
- *
124
- * This map helps in making all request methods related to a device accept a device reference instead of just a device address as an identifier.
125
- * The device reference {@link DeviceRefObj} can be one of the following: device address, device serial number, or device position.
126
- * Before sending a request message to Motion Master, the device address is resolved from the device reference, and this map is used for that.
127
- */
128
- this.deviceMap = new Map();
129
- }
130
- /**
131
- * Clears the state of an instance of this class.
132
- *
133
- * This method is typically called when Motion Master is deinitializing or the socket is closing.
134
- * Both maps are implicitly populated when a device or parameter type value key gets resolved.
135
- */
136
- clearDevicesInformation() {
137
- this.deviceParameterInfoMap.clear();
138
- this.deviceMap.clear();
139
- }
140
- //
141
- // Requests
142
- //
143
- /**
144
- * Pings the system.
145
- *
146
- * The client must send messages to Motion Master at regular intervals for Motion Master to consider it alive.
147
- * When the client is considered "not alive", Motion Master will stop the ongoing procedures and monitorings started by that client.
148
- * Each client on the Motion Master side has its own default timeout, which can be changed by {@link setSystemClientTimeout}.
149
- */
150
- pingSystem(messageId) {
151
- const pingSystem = types_1.MotionMasterMessage.Request.PingSystem.create();
152
- this.sendRequest({ pingSystem }, messageId);
153
- return rxjs_1.EMPTY;
154
- }
155
- /**
156
- * Get the system version.
157
- *
158
- * Retrieve the version of Motion Master. The version follows [Semantic Versioning 2.0.0](https://semver.org/).
159
- */
160
- getSystemVersion(requestTimeout, messageId) {
161
- const getSystemVersion = types_1.MotionMasterMessage.Request.GetSystemVersion.create();
162
- const id = this.sendRequest({ getSystemVersion }, messageId);
163
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('systemVersion', requestTimeout, id));
164
- }
165
- /**
166
- * Get device info.
167
- *
168
- * Device info includes a list of devices on the network. Each device has a type, position in the network, and a device address.
169
- * At the time of writing, the device alias is not yet supported.
170
- *
171
- * Motion Master will assign a unique device address to each device in the list.
172
- * The device address is what the client libraries use to make requests like `getDeviceParameter` for a particular device.
173
- * The device address is only valid for the duration of the session, that is, until Motion Master re-initializes the devices, for example,
174
- * when devices are power cycled or the Motion Master process is restarted.
175
- */
176
- getDeviceInfo(requestTimeout, messageId) {
177
- const getDeviceInfo = types_1.MotionMasterMessage.Request.GetDeviceInfo.create();
178
- const id = this.sendRequest({ getDeviceInfo }, messageId);
179
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceInfo', requestTimeout, id));
180
- }
181
- /**
182
- * Get device parameter info.
183
- *
184
- * Device parameter info includes a list of all parameters for the requested device, but without the parameter values.
185
- * Getting device parameter values can take more than 20ms per parameter using the IgH EtherCAT Master library.
186
- * Not all clients require all the parameter values at once, like OBLAC Drives, so this method is useful for getting
187
- * the list of available parameters and then separately obtaining the values of some required parameters.
188
- *
189
- * The returned list of device parameters will include some basic data for each parameter: index, subindex,
190
- * name, unit, group (ARRAY or RECORD name for subitems), read and write access, min and max, and value type.
191
- * Min and max values are not yet fully supported - they contain the min/max values for the used base type.
192
- * The same info and more can be read from the "SOMANET_CiA_402.xml" file, but getting info from the ESI file is more complicated.
193
- * It requires an XML parsing library and knowing how to read objects info from a dictionary and assigned modules.
194
- */
195
- getDeviceParameterInfo(props, requestTimeout, messageId) {
196
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
197
- const getDeviceParameterInfo = types_1.MotionMasterMessage.Request.GetDeviceParameterInfo.create(Object.assign(Object.assign({}, props), { deviceAddress }));
198
- const id = this.sendRequest({ getDeviceParameterInfo }, messageId);
199
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceParameterInfo', requestTimeout, id));
200
- }));
201
- }
202
- /**
203
- * Get device parameter values.
204
- *
205
- * As mentioned earlier, fetching parameter values can take 20ms or more. However,
206
- * it can also fail completely or timeout on the master side, registering as a "cannot refresh value" error.
207
- */
208
- getDeviceParameterValues(props, requestTimeout, messageId) {
209
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
210
- const getDeviceParameterValues = types_1.MotionMasterMessage.Request.GetDeviceParameterValues.create(Object.assign(Object.assign({}, props), { deviceAddress }));
211
- const id = this.sendRequest({ getDeviceParameterValues }, messageId);
212
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceParameterValues', requestTimeout, id));
213
- }));
214
- }
215
- /**
216
- * Get multi-device parameter values.
217
- *
218
- * Allows requesting parameter values from multiple devices at the same time.
219
- */
220
- getMultiDeviceParameterValues(props, requestTimeout, messageId) {
221
- const getMultiDeviceParameterValues = types_1.MotionMasterMessage.Request.GetMultiDeviceParameterValues.create(props);
222
- const id = this.sendRequest({ getMultiDeviceParameterValues }, messageId);
223
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('multiDeviceParameterValues', requestTimeout, id));
224
- }
225
- /**
226
- * Set device parameter values.
227
- */
228
- setDeviceParameterValues(props, requestTimeout, messageId) {
229
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
230
- const setDeviceParameterValues = types_1.MotionMasterMessage.Request.SetDeviceParameterValues.create(Object.assign(Object.assign({}, props), { deviceAddress }));
231
- const id = this.sendRequest({ setDeviceParameterValues }, messageId);
232
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceParameterValues', requestTimeout, id));
233
- }));
234
- }
235
- /**
236
- * Set multi-device parameter values.
237
- *
238
- * Allows setting parameter values on multiple devices at the same time.
239
- */
240
- setMultiDeviceParameterValues(props, requestTimeout, messageId) {
241
- const setMultiDeviceParameterValues = types_1.MotionMasterMessage.Request.SetMultiDeviceParameterValues.create(props);
242
- const id = this.sendRequest({ setMultiDeviceParameterValues }, messageId);
243
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('multiDeviceParameterValues', requestTimeout, id));
244
- }
245
- /**
246
- * Get device file list.
247
- *
248
- * Motion Master will use Filetransfer over EtherCAT (FoE) to obtain the list of files from a device.
249
- * There isn't an actual command for getting the list, but rather the content of a specially named file "fs-getlist" is the file list.
250
- * The list can be read like any other file from a device using the IgH EtherCAT Master for Linux CLI program:
251
- *
252
- * ```
253
- * $ ethercat foe_read fs-getlist
254
- * ```
255
- *
256
- * At the time of writing this document, "fs-getlist" can return a maximum of 32 files (subject to change) in the list. This is a firmware limitation.
257
- * However, more than 32 files can be stored on the device flash memory, but they won't appear in the list.
258
- * Also, note that FoE can behave differently in the bootstrap firmware, which runs in the BOOT EtherCAT state, and
259
- * SOMANET firmware, which runs in other EtherCAT states like PREOP/OP. Bootstrap tends to be buggier since it cannot be updated easily, so
260
- * Motion Master has certain workarounds to overcome these FoE differences and bugs.
261
- */
262
- getDeviceFileList(props, requestTimeout, messageId) {
263
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
264
- const getDeviceFileList = types_1.MotionMasterMessage.Request.GetDeviceFileList.create(Object.assign(Object.assign({}, props), { deviceAddress }));
265
- const id = this.sendRequest({ getDeviceFileList }, messageId);
266
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFileList', requestTimeout, id));
267
- }));
268
- }
269
- /**
270
- * Get device file.
271
- *
272
- * Motion Master uses Filetransfer over EtherCAT (FoE) to read and send back the content of a file from the device flash memory.
273
- *
274
- * 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
275
- * needs to be split into parts of max 9KB. Motion Master does this automatically on request to {@link setDeviceFile}.
276
- *
277
- * Upon receiving this request, Motion Master will first read the list of files on a device, see {@link getDeviceFileList}.
278
- * It does that in order to determine if the requested file, for example, "SOMANET_CiA_402.xml," has been stored in multiple parts.
279
- * The parts are named like "SOMANET_CiA_402.xml.zip.part000," "SOMANET_CiA_402.xml.zip.part001," and so on.
280
- * Motion Master will read all parts of the requested file, unzip it, and send the content back to a client.
281
- * 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,
282
- * but in any case, it will try to read the content of it.
283
- * If the file exists, it will return its content; otherwise, it will return the file not found error.
284
- *
285
- * Zipped files will be automatically unzipped, and their content returned; for example, "stack_image.svg.zip" can be stored
286
- * in one or multiple files depending on the size. No matter how it's stored, it will be unzipped, and its content returned as
287
- * text (SVG) instead of zipped content (binary).
288
- *
289
- * In BOOT EtherCAT state, it's only allowed to read the .hardware_description file. This was done because
290
- * the bootloader firmware would hang when trying to read a corrupted or missing file.
291
- *
292
- * If the requested file content is empty, Motion Master will return a not found error. This is subject to change now that Motion Master reads the file error codes.
293
- */
294
- getDeviceFile(props, requestTimeout, messageId) {
295
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
296
- const getDeviceFile = types_1.MotionMasterMessage.Request.GetDeviceFile.create(Object.assign(Object.assign({}, props), { deviceAddress }));
297
- const id = this.sendRequest({ getDeviceFile }, messageId);
298
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFile', requestTimeout, id), (0, operators_1.map)((status) => this.fixProductionScrewUp(status)));
299
- }));
300
- }
301
- /**
302
- * Fixing a production screw-up that affects a very important customer.
303
- * Added on February 29th, 2024. This code can be removed once all customer drives are fixed.
304
- * We should consider supporting SOMANET drives with no .hardware_description files.
305
- */
306
- fixProductionScrewUp(status) {
307
- if (status.name === '.hardware_description') {
308
- if (status.content) {
309
- const text = (0, util_1.decodeTextContent)(status.content);
310
- if (text.includes('\\":')) {
311
- const replacedText = text.replace(/\\"/g, '"').replace(/"{/, '{').replace(/}"/, '}');
312
- const content = new TextEncoder().encode(replacedText);
313
- this.unlockProtectedFiles(status.deviceAddress, 10000).subscribe({
314
- next: () => {
315
- this.setDeviceFile({ content, deviceAddress: status.deviceAddress, name: '.hardware_description', overwrite: true }, 10000).subscribe({
316
- error(err) {
317
- if (err instanceof Error) {
318
- console.warn(`Failed to replace the invalid .hardware_description file. Error: ${err.message}`);
319
- }
320
- },
321
- });
322
- },
323
- error: (err) => {
324
- if (err instanceof Error) {
325
- console.warn(`Failed to unlock protected files. Error: ${err.message}`);
326
- }
327
- },
328
- });
329
- status.content = content;
330
- }
331
- }
332
- else {
333
- if (status.deviceAddress) {
334
- const addr = status.deviceAddress.toString();
335
- const sn = addr.slice(0, 4) + '-' + addr.slice(4, 6) + '-' + addr.slice(6, 10) + '000-0000';
336
- const hw = JSON.parse(JSON.stringify(hardware_description_1.dummyHardwareDescriptionFileContentTemplate));
337
- hw.device.serialNumber = sn;
338
- const content = new TextEncoder().encode(JSON.stringify(hw));
339
- status.content = content;
340
- // @ts-ignore
341
- status['data'] = 'success';
342
- status.error = null;
343
- status.request = 'succeeded';
344
- status.success = {};
345
- }
346
- }
347
- }
348
- return status;
349
- }
350
- /**
351
- * Set device file.
352
- *
353
- * Motion Master uses Filetransfer over EtherCAT (FoE) to write files to the device flash memory.
354
- *
355
- * 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
356
- * needs to be split into parts of max 9KB. Motion Master does this automatically by first zipping the content and then writing the parts.
357
- * The parts are named in the following manner: "SOMANET_CiA_402.xml.zip.part000," "SOMANET_CiA_402.xml.zip.part001," and so on.
358
- */
359
- setDeviceFile(props, requestTimeout, messageId) {
360
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
361
- const setDeviceFile = types_1.MotionMasterMessage.Request.SetDeviceFile.create(Object.assign(Object.assign({}, props), { deviceAddress }));
362
- const id = this.sendRequest({ setDeviceFile }, messageId);
363
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFile', requestTimeout, id));
364
- }));
365
- }
366
- /**
367
- * Delete device file.
368
- *
369
- * Motion Master uses Filetransfer over EtherCAT (FoE) to delete files from the device flash memory.
370
- *
371
- * If the file to delete is written in parts, Motion Master will ensure that all parts are deleted.
372
- * It does this by reading the list of files on a device, see {@link getDeviceFileList}, removing all parts from that list, and then
373
- * re-reading the list again to ensure that there are no leftovers if there were more than 32 files in the list. If there are still
374
- * more than 32 files in the list, Motion Master will blindly try to make sure there are no file parts left in the memory (may or may not work).
375
- */
376
- deleteDeviceFile(props, requestTimeout, messageId) {
377
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
378
- const deleteDeviceFile = types_1.MotionMasterMessage.Request.DeleteDeviceFile.create(Object.assign(Object.assign({}, props), { deviceAddress }));
379
- const id = this.sendRequest({ deleteDeviceFile }, messageId);
380
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFile', requestTimeout, id));
381
- }));
382
- }
383
- /**
384
- * Reset device fault.
385
- *
386
- * When an error occurs on a device, the device will go into the Fault reaction active state (transition 13) and then automatically to the Fault state (transition 14).
387
- * The CiA402 state of a device can be derived from the value of the Statusword object (0x6041:00).
388
- * In order to exit the Fault state and enter the Switch on disabled state (transition 15), the master must send a Fault reset command via the Controlword object (0x6040:00).
389
- *
390
- * When this request is sent to Motion Master, it will attempt to reset the fault. Resetting the fault can either succeed or fail.
391
- * While resetting the fault, Motion Master will use the current Controlword value and set the Reset fault bit to 1.
392
- * However, upon completion, whether successful or encountering an error, Motion Master will set it back to 0.
393
- *
394
- * Motion Master will return a "No Fault" warning when this request is sent, and the device is not in the Fault or Fault reaction active state.
395
- * If this request is sent when the device is in the Fault reaction active state, Motion Master will
396
- * wait for the automatic transition (14) to the Fault state before attempting to reset the fault.
397
- * When Motion Master sets the Controlword Fault bit to 1, it will also periodically check if the device is no longer in the Fault state.
398
- * After a few seconds, if the device is still in the Fault state, Motion Master will give up and return a timeout error.
399
- */
400
- resetDeviceFault(props, requestTimeout, messageId) {
401
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
402
- const resetDeviceFault = types_1.MotionMasterMessage.Request.ResetDeviceFault.create(Object.assign(Object.assign({}, props), { deviceAddress }));
403
- const id = this.sendRequest({ resetDeviceFault }, messageId);
404
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFaultReset', requestTimeout, id));
405
- }));
406
- }
407
- /**
408
- * Stop device.
409
- *
410
- * Motion Master will use the current value of the Controlword object and set the command on it to Quick stop.
411
- * It will then wait for up to 2 seconds for a device to enter one of the following CiA402 states: Switch on disabled, Operation enabled, Fault.
412
- * This is achieved by periodically checking the value of the Statusword object. If the device fails to enter one of those states within the specified time,
413
- * Motion Master will return a timeout or a quick stop failed error.
414
- */
415
- stopDevice(props, requestTimeout, messageId) {
416
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
417
- const stopDevice = types_1.MotionMasterMessage.Request.StopDevice.create(Object.assign(Object.assign({}, props), { deviceAddress }));
418
- const id = this.sendRequest({ stopDevice }, messageId);
419
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceStop', requestTimeout, id));
420
- }));
421
- }
422
- /**
423
- * Start device firmware installation.
424
- *
425
- * When Motion Master receives this request for a device, it will perform the following steps:
426
- * - Switch the device to the BOOT EtherCAT state; the device will then run the bootloader instead of the firmware.
427
- * - Validate the firmware package.
428
- * - Delete files found in the package from the device flash memory (SOMANET_CiA_402.xml and stack_image.svg).
429
- * - Write files other than the firmware binaries and SII from the package to the device (SOMANET_CiA_402.xml and stack_image.svg).
430
- * - (Optional) Install the SII file using the IgH EtherCAT Master library write SII function, or SOEM EEPROM tool.
431
- * - Write the firmware binaries to the device using predefined names like app_firmware.bin and com_firmware.bin.
432
- *
433
- * During the firmware installation, Motion Master will send progress messages back to clients.
434
- * If Motion Master encounters a failure in any step other than SII write, it will send an error. If SII write fails, only a warning will be sent.
435
- */
436
- startDeviceFirmwareInstallation(props, requestTimeout, messageId) {
437
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
438
- const startDeviceFirmwareInstallation = types_1.MotionMasterMessage.Request.StartDeviceFirmwareInstallation.create(Object.assign(Object.assign({}, props), { deviceAddress }));
439
- const id = this.sendRequest({ startDeviceFirmwareInstallation }, messageId);
440
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFirmwareInstallation', requestTimeout, id));
441
- }));
442
- }
443
- /**
444
- * Get device log.
445
- *
446
- * The device log is stored in two files due to the IgH EtherCAT Master library 9KB file size limitation, as explained in {@link getDeviceFile}.
447
- * The device automatically writes the log to logging_curr.log and copies that file to logging_prev.log once it reaches 9KB.
448
- * When Motion Master receives this request, it will read both of these files and return their combined contents.
449
- */
450
- getDeviceLog(props, requestTimeout, messageId) {
451
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
452
- const getDeviceLog = types_1.MotionMasterMessage.Request.GetDeviceLog.create(Object.assign(Object.assign({}, props), { deviceAddress }));
453
- const id = this.sendRequest({ getDeviceLog }, messageId);
454
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceLog', requestTimeout, id));
455
- }));
456
- }
457
- /**
458
- * Start cogging torque recording.
459
- *
460
- * Cogging torque compensation (CTC) recording can be initiated with or without auto-config.
461
- * In the proto file, skipping the auto-config is currently named skip_auto_tuning. The field name is deprecated and will be renamed.
462
- *
463
- * If CTC recording is started without auto-config, users must ensure that the encoder on the motor shaft is used for all functions: commutation, position, and velocity.
464
- * If this is not the case, Motion Master will return an invalid encoder configuration error.
465
- * Recording can also fail if the position controller is not tuned.
466
- *
467
- * If CTC recording is started with auto-config, Motion Master will:
468
- * - Change the encoder configuration to have all encoder functions on the motor shaft.
469
- * - Disable CTC.
470
- * - Change the velocity feed forward value.
471
- * - Change the position and velocity filter types and cutoff frequencies.
472
- * - Run iterative sharpening position controller auto-tuning to compute the optimal PID values (gains) for CTC recording.
473
- *
474
- * 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, they are reverted back.
475
- *
476
- * CTC recording is initiated by changing the Modes of operation (0x6060) parameter value to -1 (Cogging compensation recording mode).
477
- * 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.
478
- *
479
- * The result of CTC recording is the cogging_torque.bin file written to the device flash memory. The size of this file is 2048 bytes.
480
- * Each pair of bytes represents a 16-bit signed integer value in mNm (millinewton-meter).
481
- *
482
- * This request will turn the motor.
483
- */
484
- startCoggingTorqueRecording(props, requestTimeout, messageId) {
485
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
486
- const startCoggingTorqueRecording = types_1.MotionMasterMessage.Request.StartCoggingTorqueRecording.create(Object.assign(Object.assign({}, props), { deviceAddress }));
487
- const id = this.sendRequest({ startCoggingTorqueRecording }, messageId);
488
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('coggingTorqueRecording', requestTimeout, id));
489
- }));
490
- }
491
- /**
492
- * Get cogging torque data.
493
- *
494
- * The cogging_torque.bin file is the result of cogging torque compensation recording, as explained in {@link startCoggingTorqueRecording}.
495
- * The size of this file is 2048 bytes, where each pair of bytes represents a 16-bit signed integer value in mNm (millinewton-meter).
496
- *
497
- * Motion Master will read the cogging_torque.bin file, convert the bytes to 16-bit signed integer values, and return them as an array of numbers.
498
- */
499
- getCoggingTorqueData(props, requestTimeout, messageId) {
500
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
501
- const getCoggingTorqueData = types_1.MotionMasterMessage.Request.GetCoggingTorqueData.create(Object.assign(Object.assign({}, props), { deviceAddress }));
502
- const id = this.sendRequest({ getCoggingTorqueData }, messageId);
503
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('coggingTorqueData', requestTimeout, id));
504
- }));
505
- }
506
- /**
507
- * Start offset detection.
508
- *
509
- * Running offset detection differs for firmwares <v5 and >=v5.
510
- *
511
- * For devices running firmware >=v5, Motion Master will execute multiple OS commands.
512
- * Before initiating any command, the device is switched to Modes of operation -2 (Diagnostics mode), and
513
- * it's put into the operation-enabled CiA402 state. The OS commands are then executed in the following order:
514
- * - Open phase detection
515
- * - Phase resistance measurement
516
- * - Phase inductance measurement
517
- * - Pole pair detection
518
- * - Motor phase order detection
519
- * - Commutation offset measurement
520
- *
521
- * Motion Master will return an error if any of the following OS commands fail: open phase detection,
522
- * phase order detection, commutation offset measurement.
523
- * If other OS commands fail, Motion Master will continue with the procedure and only return a warning.
524
- *
525
- * Once the commutation offset measurement command completes, it will update the following parameter values:
526
- * - 0x2001:00 Commutation angle offset to the computed offset value
527
- * - 0x2009:01 Commutation offset State to 2 (OFFSET_VALID)
528
- * - 0x2003:05 Motor phases inverted will be changed by the firmware
529
- *
530
- * For devices running firmware <5, the same Modes of operation -2 is used, but it's named Commutation offset detection mode,
531
- * and it will run as a single procedure as soon as the device is switched to CiA402 operation-enabled state.
532
- * The end result is the same; the procedure will update 0x2001:00 and 0x2009:01 parameter values.
533
- *
534
- * This request will turn the motor.
535
- */
536
- startOffsetDetection(props, requestTimeout, messageId) {
537
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
538
- const startOffsetDetection = types_1.MotionMasterMessage.Request.StartOffsetDetection.create(Object.assign(Object.assign({}, props), { deviceAddress }));
539
- const id = this.sendRequest({ startOffsetDetection }, messageId);
540
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('offsetDetection', requestTimeout, id));
541
- }));
542
- }
543
- /**
544
- * @deprecated use {@link startSystemIdentification} instead
545
- */
546
- startPlantIdentification(props, requestTimeout, messageId) {
547
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
548
- const startPlantIdentification = types_1.MotionMasterMessage.Request.StartPlantIdentification.create(Object.assign(Object.assign({}, props), { deviceAddress }));
549
- const id = this.sendRequest({ startPlantIdentification }, messageId);
550
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('plantIdentification', requestTimeout, id));
551
- }));
552
- }
553
- /**
554
- * Compute auto-tuning gains.
555
- *
556
- * Auto-tuning gains can be computed for the position or the velocity controller.
557
- *
558
- * The prerequisites for running this procedure are a configured motor and the existence of
559
- * the plant_model.csv file created by previously running the system identification {@link startSystemIdentification}.
560
- * Motion Master will return an error if the plant_model.csv file is not found.
561
- *
562
- * In OBLAC Drives, this request is called when the tuning sliders (damping ratio, bandwidth) are moved and
563
- * during the CTC recording {@link startCoggingTorqueRecording} when auto-config is not skipped.
564
- *
565
- * Motion Master will compute the PID gains using a proprietary algorithm and update the Kp, Ki, Kd values of the 0x2011 and 0x2012 parameters.
566
- */
567
- computeAutoTuningGains(props, requestTimeout, messageId) {
568
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
569
- const computeAutoTuningGains = types_1.MotionMasterMessage.Request.ComputeAutoTuningGains.create(Object.assign(Object.assign({}, props), { deviceAddress }));
570
- const id = this.sendRequest({ computeAutoTuningGains }, messageId);
571
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('autoTuning', requestTimeout, id));
572
- }));
573
- }
574
- /**
575
- * Set motion controller parameters.
576
- *
577
- * This request sets the target value for a device on Motion Master.
578
- *
579
- * A previous call to {@link enableMotionController} will set the mode of operation to CSP, CSV, or CST and optionally enable filtering.
580
- */
581
- setMotionControllerParameters(props, messageId) {
582
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
583
- const setMotionControllerParameters = types_1.MotionMasterMessage.Request.SetMotionControllerParameters.create(Object.assign(Object.assign({}, props), { deviceAddress }));
584
- this.sendRequest({ setMotionControllerParameters }, messageId);
585
- return rxjs_1.EMPTY;
586
- }));
587
- }
588
- /**
589
- * Enable motion controller.
590
- *
591
- * This request will set the mode of operation to CSP, CSV, or CST and optionally enable filtering.
592
- *
593
- * If filtering is enabled, Motion Master will compute an intermediate target value in each cycle, and depending on the active mode of operation,
594
- * set the value of one of 0x607A Target position, 0x60FF Target velocity, 0x6071 Target torque to that value.
595
- *
596
- * The target value can be updated with {@link setMotionControllerParameters}.
597
- */
598
- enableMotionController(props, requestTimeout, messageId) {
599
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
600
- const enableMotionController = types_1.MotionMasterMessage.Request.EnableMotionController.create(Object.assign(Object.assign({}, props), { deviceAddress }));
601
- const id = this.sendRequest({ enableMotionController }, messageId);
602
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('motionController', requestTimeout, id));
603
- }));
604
- }
605
- /**
606
- * Disable motion controller.
607
- *
608
- * Depending on the current CiA402 state, this request will do the following:
609
- * - If the device is in Quick stop active, it will transition to Switch on disabled state.
610
- * - If the device is in Operation enabled, it will transition to Switched on state.
611
- */
612
- disableMotionController(props, requestTimeout, messageId) {
613
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
614
- const disableMotionController = types_1.MotionMasterMessage.Request.DisableMotionController.create(Object.assign(Object.assign({}, props), { deviceAddress }));
615
- const id = this.sendRequest({ disableMotionController }, messageId);
616
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('motionController', requestTimeout, id));
617
- }));
618
- }
619
- /**
620
- * Set signal generator parameters.
621
- *
622
- * This request will update the signal generator parameter values for a device on Motion Master.
623
- * The selected signal generator is then started by {@link startSignalGenerator}.
624
- * The signal generator must be started within the next 60 seconds before Motion Master invalidates the previously set parameters.
625
- */
626
- setSignalGeneratorParameters(props, requestTimeout, messageId) {
627
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
628
- const setSignalGeneratorParameters = types_1.MotionMasterMessage.Request.SetSignalGeneratorParameters.create(Object.assign(Object.assign({}, props), { deviceAddress }));
629
- const id = this.sendRequest({ setSignalGeneratorParameters }, messageId);
630
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('signalGenerator', requestTimeout, id));
631
- }));
632
- }
633
- /**
634
- * Start signal generator.
635
- *
636
- * This request will start one of the signal generators based on the
637
- * previously set parameters with {@link setSignalGeneratorParameters}.
638
- *
639
- * Signal generators are started by changing the mode of operation, putting the device into Operation enabled CiA402 state,
640
- * and setting the target value. The mode of operation set depends on the type of signal generator and the selected controller:
641
- * - For the simple & advanced step response and the sine wave, one of CSP, CSV, or CST is set.
642
- * - For the ramp, trapezoidal, and bidirectional signal generators, also called profiles, one of the PP, PV, TQ is set.
643
- *
644
- * Currently, the only profile type that the firmware supports is the ramp profile.
645
- * Motion Master uses this profile type to construct and run the following signals: ramp, trapezoidal, and bidirectional.
646
- * Sine wave is also a profile, but it is currently being generated on Motion Master alone.
647
- * Firmwares >=5.2 will support generating and running sine wave profiles on devices themselves. This will enable lossless profile
648
- * execution on possibly higher frequencies. Future Motion Master versions will switch to this method.
649
- *
650
- * For the simple step response, Motion Master will only set the target value, wait for the specified holding duration,
651
- * and then send a Quick Stop command.
652
- *
653
- * The advanced step response is similar to the simple one, but, after the holding duration, it will set the new target
654
- * multiple times depending on the shape, and then send a Quick Stop if the repeat option is set to NO. If repeat is set to YES,
655
- * then the signal generator will run indefinitely, and the users must stop it manually.
656
- *
657
- * Before running a profile, Motion Master will check if the device supports the profile modes by looking at 0x6502 Supported drive modes.
658
- * If profile modes are not supported, then the profiles will be generated on Motion Master. This means the targets will be computed upfront.
659
- * If profiles are supported, then the ramp, trapezoidal, and bidirectional signal generators are run in profile mode of operation.
660
- * To prepare the signal generator for execution, Motion Master will set the following parameters:
661
- * - 0x6081:00 Profile velocity (only for the position controller)
662
- * - 0x6083:00 Profile acceleration
663
- * - 0x6084:00 Profile deceleration
664
- * Motion Master knows when the profile has completed by comparing the demand with the actual value, taking into account the
665
- * holding duration and the shape of the profile. Once the profile is considered complete, Motion Master will send a Quick Stop, but
666
- * only if repeat is set to NO.
667
- *
668
- * The sine wave profile is generated upfront on Motion Master. Targets are computed for each master cycle (millisecond).
669
- * Once the last computed target has been set, Motion Master will send a Quick Stop if repeat is set to NO.
670
- *
671
- * For all signal generators, Motion Master will check the requested target against the currently set software position limits
672
- * and, if necessary, alter it while returning a warning.
673
- *
674
- * This request will turn the motor.
675
- */
676
- startSignalGenerator(props, requestTimeout, messageId) {
677
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
678
- const startSignalGenerator = types_1.MotionMasterMessage.Request.StartSignalGenerator.create(Object.assign(Object.assign({}, props), { deviceAddress }));
679
- const id = this.sendRequest({ startSignalGenerator }, messageId);
680
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('signalGenerator', requestTimeout, id));
681
- }));
682
- }
683
- /**
684
- * Stop signal generator.
685
- *
686
- * This request will send a Quick Stop command to the device.
687
- */
688
- stopSignalGenerator(props, requestTimeout, messageId) {
689
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
690
- const stopSignalGenerator = types_1.MotionMasterMessage.Request.StopSignalGenerator.create(Object.assign(Object.assign({}, props), { deviceAddress }));
691
- const id = this.sendRequest({ stopSignalGenerator }, messageId);
692
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('signalGenerator', requestTimeout, id));
693
- }));
694
- }
695
- /**
696
- * Start monitoring device parameter values.
697
- *
698
- * When monitoring is started, Motion Master will begin and continue reading values from the device
699
- * and send them back to a client at the specified interval on the given topic.
700
- *
701
- * The request is made through the req/res socket {@link MotionMasterReqResSocket},
702
- * but the status messages will arrive on the pub/sub socket {@link MotionMasterPubSubSocket}.
703
- *
704
- * The same monitoring can read both PDO and SDO values. PDO values will be updated on every Motion Master cycle (1ms).
705
- * How fast SDO values will update depends on the number of SDOs being monitored. Motion Master reads the SDO values in a sequence,
706
- * so, for example, if there are 10 SDOs and it takes 20ms to read a single SDO, it will take approximately 200ms for each SDO to get updated.
707
- * IgH EtherCAT Master supports non-blocking SDO reading with a request to read and check the state of the request (one of not used, busy, failed, succeeded).
708
- * A future version of Motion Master will likely leverage this and update SDO values out of order as each of the requests to update SDO succeeds.
709
- * This should decrease the time it takes to read multiple SDOs.
710
- * If a parameter is required to be updated more frequently, it should be mapped as a PDO.
711
- *
712
- * Firmware versions >=5.2 support SDO complete access, but it is currently not being supported in Motion Master.
713
- * With SDO complete access, RECORD and ARRAY subitems can be read with a single request.
714
- *
715
- * The interval is given in microseconds.
716
- *
717
- * The topic should be unique. The previous monitoring will stop if the given topic is reused.
718
- * Clients can easily stop one another's monitoring by using the same topic.
719
- * Prefixing topics with the client id or some other unique identifier is suggested.
720
- *
721
- * When the monitoring request is received, Motion Master will mark all requested parameters as stale.
722
- * The first time a parameter gets refreshed from a device, it'll be marked as not stale.
723
- * Motion Master won't start publishing parameter values until all parameters are marked as not stale.
724
- * This was done to satisfy the firmware test client applications.
725
- *
726
- * Stopping the ongoing monitoring is done through the req/res socket using
727
- * the request message ID of the initial start monitoring message {@link stopMonitoringDeviceParameterValues}.
728
- */
729
- startMonitoringDeviceParameterValues(props, requestTimeout, messageId) {
730
- const startMonitoringDeviceParameterValues = types_1.MotionMasterMessage.Request.StartMonitoringDeviceParameterValues.create(props);
731
- const id = this.sendRequest({ startMonitoringDeviceParameterValues }, messageId);
732
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('monitoringParameterValues', requestTimeout, id));
733
- }
734
- /**
735
- * Stop monitoring device parameter values.
736
- *
737
- * The Message ID used for {@link startMonitoringDeviceParameterValues} uniquely identifies a monitoring and is used for stopping it.
738
- */
739
- stopMonitoringDeviceParameterValues(props, messageId) {
740
- const stopMonitoringDeviceParameterValues = types_1.MotionMasterMessage.Request.StopMonitoringDeviceParameterValues.create(props);
741
- this.sendRequest({ stopMonitoringDeviceParameterValues }, messageId);
742
- return rxjs_1.EMPTY;
743
- }
744
- /**
745
- * Get the EtherCAT network state.
746
- *
747
- * Returns one of the EtherCAT network states: BOOT, INIT, PRE-OP, SAFE-OP, OP.
748
- * The state is returned as an enum value: INIT: 1, PREOP: 2, BOOT: 3, SAFEOP: 4, OP: 5.
749
- */
750
- getEthercatNetworkState(props, requestTimeout, messageId) {
751
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
752
- const getEthercatNetworkState = types_1.MotionMasterMessage.Request.GetEthercatNetworkState.create(Object.assign(Object.assign({}, props), { deviceAddress }));
753
- const id = this.sendRequest({ getEthercatNetworkState }, messageId);
754
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('ethercatNetworkState', requestTimeout, id));
755
- }));
756
- }
757
- /**
758
- * Set the EtherCAT network state.
759
- *
760
- * Set the EtherCAT network state to one of: BOOT, INIT, PRE-OP, SAFE-OP, OP.
761
- * The state is set by an enum value: INIT: 1, PREOP: 2, BOOT: 3, SAFEOP: 4, OP: 5.
762
- *
763
- * When switching from PRE-OP to OP, MM will reinitialize slaves to update the PDO mapping.
764
- */
765
- setEthercatNetworkState(props, requestTimeout, messageId) {
766
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
767
- const setEthercatNetworkState = types_1.MotionMasterMessage.Request.SetEthercatNetworkState.create(Object.assign(Object.assign({}, props), { deviceAddress }));
768
- const id = this.sendRequest({ setEthercatNetworkState }, messageId);
769
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('ethercatNetworkState', requestTimeout, id));
770
- }));
771
- }
772
- /**
773
- * @deprecated use {@link startCirculoEncoderNarrowAngleCalibrationProcedure} instead
774
- */
775
- startNarrowAngleCalibration(props, requestTimeout, messageId) {
776
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
777
- const startNarrowAngleCalibration = types_1.MotionMasterMessage.Request.StartNarrowAngleCalibration.create(Object.assign(Object.assign({}, props), { deviceAddress }));
778
- const id = this.sendRequest({ startNarrowAngleCalibration }, messageId);
779
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('narrowAngleCalibration', requestTimeout, id));
780
- }));
781
- }
782
- /**
783
- * Set system client timeout.
784
- *
785
- * This request will update the client timeout.
786
- *
787
- * The client timeout specifies how long Motion Master will wait for the client to send a ping or any other message before
788
- * considering it gone and clearing the resources (stopping procedures and monitorings) related to that client.
789
- *
790
- * The default client timeout is 1000ms.
791
- *
792
- * Clients will typically change this once the socket connection is established.
793
- */
794
- setSystemClientTimeout(props, messageId) {
795
- const setSystemClientTimeout = types_1.MotionMasterMessage.Request.SetSystemClientTimeout.create(props);
796
- this.sendRequest({ setSystemClientTimeout }, messageId);
797
- return rxjs_1.EMPTY;
798
- }
799
- /**
800
- * Start system identification.
801
- *
802
- * This request will run the system identification procedure, which identifies the plant model.
803
- *
804
- * Pre-conditions: a configured motor and configured encoders.
805
- *
806
- * The chirp signal that the procedure generates only uses the torque controller.
807
- * The chirp signal is generated on Motion Master, and then, on every master cycle (1ms), the target torque value is set on a device.
808
- * Motion Master will send a Quick Stop to a device after the last target value is set.
809
- *
810
- * The request can fail for multiple reasons: the device is in the fault state, cannot set the op mode state,
811
- * or the system identification fails because not enough data points have been collected.
812
- * While running the chirp signal, the data points are collected, but due to potential communication lag, it can happen
813
- * that the master doesn't collect enough data points. If more than 80% of data points are missing, the procedure will fail.
814
- * If between 20%-80% of data points are missing, only a warning will be returned. The exact thresholds are subject to change.
815
- *
816
- * Before computing the plant model data based on the collected data points, Motion Master will interpolate the missing data points.
817
- * Plant model data is then computed and written to the plant_model.csv file on the device flash memory.
818
- * The plant model file is later used to compute the PID values using {@link computeAutoTuningGains} and {@link startFullAutoTuning}.
819
- * It's also used indirectly in {@link startCoggingTorqueRecording} when auto-config is turned on.
820
- * Motion Master will also update the integral limit values in 0x2011: Velocity controller and 0x2012: Position controller during this procedure.
821
- *
822
- * In future versions of the firmware, the chirp signal would be executed on the devices themselves, so no data points will be missing,
823
- * and the system identification will be able to run at higher frequencies and with higher precision.
824
- *
825
- * This request will turn the motor.
826
- */
827
- startSystemIdentification(props, requestTimeout, messageId) {
828
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
829
- const startSystemIdentification = types_1.MotionMasterMessage.Request.StartSystemIdentification.create(Object.assign(Object.assign({}, props), { deviceAddress }));
830
- const id = this.sendRequest({ startSystemIdentification }, messageId);
831
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('systemIdentification', requestTimeout, id));
832
- }));
833
- }
834
- /**
835
- * Get Circulo encoder magnet distance.
836
- *
837
- * This feature is only available for firmwares >=v5. For older firmwares, Motion Master will return an error.
838
- *
839
- * Motion Master will read registers 0x2B and 0x2F on the specified encoder (by encoder ordinal), and then it will compute the magnet distance.
840
- * The computation depends on the Circulo type, encoder port, and ring revision.
841
- */
842
- getCirculoEncoderMagnetDistance(props, requestTimeout, messageId) {
843
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
844
- const getCirculoEncoderMagnetDistance = types_1.MotionMasterMessage.Request.GetCirculoEncoderMagnetDistance.create(Object.assign(Object.assign({}, props), { deviceAddress }));
845
- const id = this.sendRequest({ getCirculoEncoderMagnetDistance }, messageId);
846
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('circuloEncoderMagnetDistance', requestTimeout, id));
847
- }));
848
- }
849
- /**
850
- * Start the Circulo encoder narrow-angle calibration procedure.
851
- *
852
- * This feature is only available on firmwares >=v5.
853
- *
854
- * Before starting the procedure, Motion Master will check and return an error if:
855
- * - the firmware version is not >=v5
856
- * - software position limits are set so that the required motor rotation is not possible
857
- * - for SMM devices, the encoder source type is not set to None
858
- * - the velocity resolution determined by the gear ratio, SI unit velocity, and feed constant is not high enough
859
- *
860
- * Before starting the procedure, Motion Master will do the following:
861
- * - send OS command 14 (Ignore BiSS status bits) so that no firmware warnings or errors are raised during the procedure.
862
- * - set certain encoder registers to their default values.
863
- * - set the BiSS encoder to raw mode.
864
- * - change the values of some device parameters: profile acceleration, deceleration, max motor speed, etc.
865
- *
866
- * The parameter values Motion Master sets depend on the type of the device (fetched from the hardware description file).
867
- * Once the operation has completed, Motion Master will roll back all of the above parameters to their initial values.
868
- *
869
- * Procedure:
870
- * - The procedure is run in velocity profile mode of operation.
871
- * - The procedure will do multiple iterations until the encoder data is satisfactory.
872
- * - The procedure can fail for various reasons: data out of range, motor too slow, bad data, internal error, etc.
873
- * - The procedure will run a maximum of 13 calibration iterations, up to 2 mins in total.
874
- * - One iteration turns the motor in one direction, and then the next one reverses the direction.
875
- * - Each iteration will record encoder data into HRD files (4kHz) that contain the raw data from the specified encoder.
876
- * - Motion Master will remove previous high-resolution data (HRD) files after each iteration. HRD streaming is configured/started using OS commands.
877
- * - After each iteration, which lasts ~6 seconds, the recorded data in HRD files is read and passed to the iC House library.
878
- * - The recorded data is sliced into 32-bit pairs of values of master and nonius track data. Motion Master creates two arrays out of this data and
879
- * passes that to the iC House library which computes the new calibration parameters depending on encoder type sine and cosine offset, gain, etc.
880
- * - The iterations are being repeated until the encoder (raw) data satisfies a certain threshold.
881
- *
882
- * The result of a successful calibration is setting multiple parameters into registers of the specified encoder.
883
- *
884
- * At the end of the procedure Motion Master will rotate the motor back to the starting position and run commutation offset detection.
885
- * There will also be a final iteration that only measures the values of a (now) calibrated encoder for display.
886
- *
887
- * This request will turn the motor.
888
- */
889
- startCirculoEncoderNarrowAngleCalibrationProcedure(props, requestTimeout, messageId) {
890
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
891
- const startCirculoEncoderNarrowAngleCalibrationProcedure = types_1.MotionMasterMessage.Request.StartCirculoEncoderNarrowAngleCalibrationProcedure.create(Object.assign(Object.assign({}, props), { deviceAddress }));
892
- const id = this.sendRequest({ startCirculoEncoderNarrowAngleCalibrationProcedure }, messageId);
893
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('circuloEncoderNarrowAngleCalibrationProcedure', requestTimeout, id));
894
- }));
895
- }
896
- /**
897
- * Get device CiA402 state.
898
- */
899
- getDeviceCia402State(props, requestTimeout, messageId) {
900
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
901
- const getDeviceCia402State = types_1.MotionMasterMessage.Request.GetDeviceCiA402State.create(Object.assign(Object.assign({}, props), { deviceAddress }));
902
- const id = this.sendRequest({ getDeviceCia402State }, messageId);
903
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceCia402State', requestTimeout, id));
904
- }));
905
- }
906
- /**
907
- * Set device CiA402 state.
908
- */
909
- setDeviceCia402State(props, requestTimeout, messageId) {
910
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
911
- const setDeviceCia402State = types_1.MotionMasterMessage.Request.SetDeviceCiA402State.create(Object.assign(Object.assign({}, props), { deviceAddress }));
912
- const id = this.sendRequest({ setDeviceCia402State }, messageId);
913
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceCia402State', requestTimeout, id));
914
- }));
915
- }
916
- /**
917
- * Get system log.
918
- *
919
- * The system log represents the collected standard output from the Motion Master process.
920
- *
921
- * This request will return the entire system log, which can be a maximum of approximately 2MB.
922
- *
923
- * The log level can be set as an environmental variable when the Motion Master process is started.
924
- * The log level can be selected in the OBLAC Drives Update Service expert mode before installing a release.
925
- * The supported log levels are:
926
- * - 6: Request content
927
- * - 5: Request type
928
- * - 4: Parameter value
929
- * - 3: Monitoring
930
- * - 2: Verbose 2
931
- * - 1: Verbose 1
932
- * - 0: Info
933
- * - 1: Warnings
934
- * - 2: Errors
935
- * - 3: Fatal errors
936
- */
937
- getSystemLog(requestTimeout, messageId) {
938
- const getSystemLog = types_1.MotionMasterMessage.Request.GetSystemVersion.create();
939
- const id = this.sendRequest({ getSystemLog }, messageId);
940
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('systemLog', requestTimeout, id));
941
- }
942
- /**
943
- * Start device SII restore.
944
- *
945
- * This request uses the EEPROM tool from SOEM to overwrite the SII portion of the EEPROM on a device.
946
- *
947
- * Motion Master will verify the provided SII file before writing it to EEPROM. It will:
948
- * - Check the file content length,
949
- * - Compute CRC from a part of the SII file content and compare it to the CRC inside the file,
950
- * - Check the SII category types in the category header.
951
- *
952
- * Unlike IgH EtherCAT Master, which identifies interfaces by MAC address, the SOEM tool requires an adapter name.
953
- * Before calling the SOEM tool, Motion Master will find the adapter name by looking into /sys/class/net.
954
- *
955
- * The SOEM tool binary is delivered as a part of the Motion Master Docker image.
956
- */
957
- startDeviceSiiRestore(props, requestTimeout, messageId) {
958
- const startDeviceSiiRestore = types_1.MotionMasterMessage.Request.StartDeviceSiiRestore.create(props);
959
- const id = this.sendRequest({ startDeviceSiiRestore }, messageId);
960
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceSiiRestore', requestTimeout, id));
961
- }
962
- /**
963
- * Start open-loop field control.
964
- *
965
- * The precondition for running the open-loop field control is to have a configured motor.
966
- * No encoder configuration or offset detection is required.
967
- *
968
- * Open-loop field control is started by changing the Modes of operation (0x6060) parameter value to -3 (Open-loop field mode) and
969
- * setting the CiA402 state to Ready to switch on.
970
- * After that, Motion Master will run several OS commands that configure the open-loop field control parameters:
971
- * - Start angle [milliradian]
972
- * - End angle [milliradian]
973
- * - Max rotational speed [radian/s]
974
- * - Rotational acceleration [radian/s^2]
975
- * - Start length [permille of rated torque]
976
- * - End length [permille of rated torque]
977
- * - Length speed [permille of rated torque]
978
- *
979
- * After the configuration, Motion Master will switch the device to Operation enabled CiA402 state, and the profile will start.
980
- *
981
- * While the open-loop field control is running, Motion Master will monitor the target reached bit in the statusword.
982
- * When the target reached bit becomes 1, Motion Master will stop the procedure by changing the CiA402 state to Switched on.
983
- *
984
- * Motion Master will return an error if the device goes out of the Operation enabled state before the target reached bit becomes 1.
985
- * There is no timeout on the Motion Master side.
986
- *
987
- * This request will turn the motor.
988
- */
989
- startOpenLoopFieldControl(props, requestTimeout, messageId) {
990
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
991
- const startOpenLoopFieldControl = types_1.MotionMasterMessage.Request.StartOpenLoopFieldControl.create(Object.assign(Object.assign({}, props), { deviceAddress }));
992
- const id = this.sendRequest({ startOpenLoopFieldControl }, messageId);
993
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('openLoopFieldControl', requestTimeout, id));
994
- }));
995
- }
996
- /**
997
- * @deprecated use {@link startFullAutoTuning} instead
998
- */
999
- computeFullAutoTuningGains(props, requestTimeout, messageId) {
1000
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1001
- const computeFullAutoTuningGains = types_1.MotionMasterMessage.Request.ComputeFullAutoTuningGains.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1002
- const id = this.sendRequest({ computeFullAutoTuningGains }, messageId);
1003
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('fullAutoTuning', requestTimeout, id));
1004
- }));
1005
- }
1006
- /**
1007
- * Start full auto-tuning.
1008
- *
1009
- * The preconditions for running the full auto-tuning are a properly configured motor, brake, and encoders,
1010
- * and having executed the offset detection and the system identification procedures.
1011
- *
1012
- * In order to successfully run the full auto-tuning procedure, the motor must not rotate, and the device must not be in CiA402 Operation enabled.
1013
- *
1014
- * Motion Master will try to read the plant_model.csv file, and if it doesn't exist, it will return an error.
1015
- *
1016
- * Before the operation starts, Motion Master will measure the actual velocity for 500ms at a standstill to determine the encoder noise.
1017
- * The noise is then sent, along with some other parameters (velocity feedback filter, feed forward, DC link voltage, firmware version, and similar)
1018
- * and data to a proprietary computation algorithm.
1019
- * ADC current ratio, which is used in the computation script, is determined by the firmware version and the device hardware ID (e.g., 9500, 9501, 8501...).
1020
- *
1021
- * Full auto-tuning will compute the PID gains, and Motion Master will update the parameter values in the velocity or position controller, depending on the request.
1022
- *
1023
- * Motion Master will return the damping ratio, settling time, and bandwidth values when the procedure completes. UI will use these values to update the sliders in the Tuning screen.
1024
- *
1025
- * This procedure can be stopped with {@link stopFullAutoTuning}.
1026
- */
1027
- startFullAutoTuning(props, requestTimeout, messageId) {
1028
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1029
- const startFullAutoTuning = types_1.MotionMasterMessage.Request.StartFullAutoTuning.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1030
- const id = this.sendRequest({ startFullAutoTuning }, messageId);
1031
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('fullAutoTuning', requestTimeout, id));
1032
- }));
1033
- }
1034
- /**
1035
- * Stop full auto-tuning.
1036
- *
1037
- * Motion Master has only one exit point for this procedure, which is after the computation algorithm ends.
1038
- * This means that, once stopped, the procedure will compute the gains, but Motion Master will not update the controller parameters.
1039
- * A previously started full auto-tuning request will complete with an aborted error.
1040
- */
1041
- stopFullAutoTuning(props, requestTimeout, messageId) {
1042
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1043
- const stopFullAutoTuning = types_1.MotionMasterMessage.Request.StopFullAutoTuning.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1044
- const id = this.sendRequest({ stopFullAutoTuning }, messageId);
1045
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('fullAutoTuning', requestTimeout, id), (0, operators_1.map)((status) => {
1046
- var _a, _b;
1047
- if (((_a = status.success) === null || _a === void 0 ? void 0 : _a.code) === types_1.MotionMasterMessage.Status.FullAutoTuning.Success.Code.POSITION_STOPPED ||
1048
- ((_b = status.success) === null || _b === void 0 ? void 0 : _b.code) === types_1.MotionMasterMessage.Status.FullAutoTuning.Success.Code.VELOCITY_STOPPED) {
1049
- // Full auto tuning status is shared between start and stop.
1050
- // When full auto tuning has stopped this request is considered successful.
1051
- status.request = 'succeeded';
1052
- }
1053
- return status;
1054
- }));
1055
- }));
1056
- }
1057
- /**
1058
- * Start Circulo encoder configuration.
1059
- *
1060
- * The preconditions for this procedure are to have a Circulo device with internal encoders and firmware >=v5.
1061
- *
1062
- * This procedure will set various internal encoder registers based on the hardware description, Circulo device type, and the encoder ordinal.
1063
- *
1064
- * Before updating the registers, Motion Master will send an OS command to ignore the BiSS status bits.
1065
- * Once the procedure is done, the value of this register will be reverted.
1066
- *
1067
- * Depending on the setup, this procedure can take up to 30 seconds.
1068
- *
1069
- * This procedure can fail if it's already running, if it's not supported because of the firmware version,
1070
- * or if the hardware description file is missing.
1071
- *
1072
- * While this procedure runs, Motion Master will report its progress.
1073
- */
1074
- startCirculoEncoderConfiguration(props, requestTimeout, messageId) {
1075
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1076
- const startCirculoEncoderConfiguration = types_1.MotionMasterMessage.Request.StartCirculoEncoderConfiguration.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1077
- const id = this.sendRequest({ startCirculoEncoderConfiguration }, messageId);
1078
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('circuloEncoderConfiguration', requestTimeout, id));
1079
- }));
1080
- }
1081
- /**
1082
- * @deprecated use {@link stopDevice} instead
1083
- */
1084
- stopCirculoEncoderNarrowAngleCalibrationProcedure(props, requestTimeout, messageId) {
1085
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1086
- const stopCirculoEncoderNarrowAngleCalibrationProcedure = types_1.MotionMasterMessage.Request.StopCirculoEncoderNarrowAngleCalibrationProcedure.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1087
- const id = this.sendRequest({ stopCirculoEncoderNarrowAngleCalibrationProcedure }, messageId);
1088
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('circuloEncoderNarrowAngleCalibrationProcedure', requestTimeout, id));
1089
- }));
1090
- }
1091
- /**
1092
- * Start OS command.
1093
- *
1094
- * This request will execute an OS command and return the result as a byte array.
1095
- *
1096
- * Motion Master will check if an OS command is already running and use a mutex to ensure only one OS command runs at a time.
1097
- *
1098
- * This request can fail if the requested OS command is not supported.
1099
- *
1100
- * Motion Master will check if the OS command has failed and return an error.
1101
- *
1102
- * The OS command can timeout if it takes longer than the provided timeout in milliseconds, in which case Motion Master will return an error.
1103
- */
1104
- startOsCommand(props, requestTimeout, messageId) {
1105
- return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1106
- const startOsCommand = types_1.MotionMasterMessage.Request.StartOsCommand.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1107
- const id = this.sendRequest({ startOsCommand }, messageId);
1108
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('osCommand', requestTimeout, id));
1109
- }));
1110
- }
1111
- /**
1112
- * Send request.
1113
- *
1114
- * Create a Motion Master message with the provided request, optionally generate a unique message ID,
1115
- * and then send the message through the socket.
1116
- *
1117
- * @returns the message ID
1118
- */
1119
- sendRequest(request, messageId) {
1120
- const id = messageId !== null && messageId !== void 0 ? messageId : (0, uuid_1.v4)();
1121
- const message = types_1.MotionMasterMessage.create({ request, id });
1122
- this.socket.send(message);
1123
- return id;
1124
- }
1125
- //
1126
- // Helpers
1127
- //
1128
- /**
1129
- * Get the device serial number by device address.
1130
- *
1131
- * This function does not make any requests; instead, it utilizes the previously mapped device serial number to an instance of a device.
1132
- */
1133
- getSerialNumberByDeviceAddress(deviceAddress) {
1134
- for (let [serialNumber, device] of this.deviceMap.entries()) {
1135
- if (device.deviceAddress === deviceAddress) {
1136
- return serialNumber;
1137
- }
1138
- }
1139
- return;
1140
- }
1141
- /**
1142
- * Resolve a device.
1143
- *
1144
- * Resolves the device object by its reference, which can be a position, serial number, or device address.
1145
- *
1146
- * This function will make requests to retrieve the list of devices and the .hardware_description file for each device,
1147
- * but only if it has not been previously retrieved for this session.
1148
- *
1149
- * @throws errors if the device reference is invalid
1150
- */
1151
- resolveDevice(deviceRef, requestTimeout = 20000) {
1152
- const deviceRefObj = (typeof deviceRef === 'object') ? deviceRef : (0, util_1.makeDeviceRefObj)(deviceRef);
1153
- if (!(0, util_1.isValidDeviceRefObj)(deviceRefObj)) {
1154
- return (0, rxjs_1.throwError)(() => new Error(`Device ref object is not valid ${JSON.stringify(deviceRefObj)}`));
1155
- }
1156
- const device = this.getCachedDeviceByDeviceRefObj(deviceRefObj);
1157
- if (device) {
1158
- return (0, rxjs_1.of)(device);
1159
- }
1160
- else {
1161
- return this.getDevices(requestTimeout).pipe((0, rxjs_1.tap)((devices) => {
1162
- this.deviceMap.clear();
1163
- devices.forEach((device) => {
1164
- if (!device.hardwareDescription) {
1165
- throw new Error(`Device ${device.position} has no hardware description`);
1166
- }
1167
- const deviceSerialNumber = (0, hardware_description_1.getSerialNumberFromHardwareDescription)(device.hardwareDescription);
1168
- this.deviceMap.set(deviceSerialNumber, device);
1169
- });
1170
- }), (0, operators_1.map)(() => {
1171
- const device = this.getCachedDeviceByDeviceRefObj(deviceRefObj);
1172
- if (!device) {
1173
- throw new Error(`Device is not found by deviceRefObj ${JSON.stringify(deviceRefObj)}`);
1174
- }
1175
- return device;
1176
- }));
1177
- }
1178
- }
1179
- /**
1180
- * Resolve device address.
1181
- *
1182
- * This method should return the device address by a device reference object,
1183
- * or throw an error if the device address cannot be found.
1184
- *
1185
- * Refer to the description of this class for more information.
1186
- */
1187
- resolveDeviceAddress(deviceRef, requestTimeout = 60000) {
1188
- const deviceRefObj = (typeof deviceRef === 'object') ? deviceRef : (0, util_1.makeDeviceRefObj)(deviceRef);
1189
- return this.resolveDevice(deviceRefObj, requestTimeout).pipe((0, operators_1.map)((device) => device.deviceAddress));
1190
- }
1191
- /**
1192
- * Get devices.
1193
- *
1194
- * Retrieve device information from Motion Master.
1195
- *
1196
- * For each device in the list, read its .hardware_description file and assign its content to the device.
1197
- */
1198
- getDevices(requestTimeout = 10000) {
1199
- return this.getDeviceInfo(requestTimeout).pipe((0, operators_1.mergeMap)((deviceInfo) => {
1200
- var _a;
1201
- const devices = ((_a = deviceInfo.devices) !== null && _a !== void 0 ? _a : []);
1202
- return (0, rxjs_1.forkJoin)(devices.map((device) => {
1203
- const deviceAddress = device.deviceAddress;
1204
- const getDeviceFile = types_1.MotionMasterMessage.Request.GetDeviceFile
1205
- .create({ name: '.hardware_description', deviceAddress });
1206
- const id = this.sendRequest({ getDeviceFile }, (0, uuid_1.v4)());
1207
- return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFile', requestTimeout, id), (0, operators_1.map)((deviceFile) => {
1208
- const status = this.fixProductionScrewUp(deviceFile);
1209
- if (status.content) {
1210
- const data = (0, util_1.decodeTextContent)(status.content);
1211
- const hardwareDescription = JSON.parse(data);
1212
- const id = (0, hardware_description_1.getSerialNumberFromHardwareDescription)(hardwareDescription);
1213
- return Object.assign(Object.assign({}, device), { id, hardwareDescription });
1214
- }
1215
- return Object.assign({}, device);
1216
- }));
1217
- }));
1218
- }));
1219
- }
1220
- getParameterValue(a, b, c, d, e, f) {
1221
- try {
1222
- const { deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId } = this.resolveGetDeviceParameterValueArguments(a, b, c, d, e, f);
1223
- return this.createGetParameterValueObservable(deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId);
1224
- }
1225
- catch (err) {
1226
- return (0, rxjs_1.throwError)(() => err);
1227
- }
1228
- }
1229
- upload(a, b, c, d, e, f) {
1230
- const { deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId } = this.resolveGetDeviceParameterValueArguments(a, b, c, d, e, f);
1231
- return (0, rxjs_1.lastValueFrom)(this.createGetParameterValueObservable(deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId));
1232
- }
1233
- createGetParameterValueObservable(deviceRefObj, index, subindex, loadFromCache = false, requestTimeout = 1000, messageId) {
1234
- return (0, rxjs_1.defer)(() => {
1235
- return this.getDeviceParameterValues(Object.assign(Object.assign({}, deviceRefObj), { parameters: [{ index, subindex, loadFromCache }] }), requestTimeout, messageId).pipe((0, operators_1.map)(({ parameterValues }) => {
1236
- var _a, _b, _c;
1237
- const parameterValue = parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0];
1238
- if (parameterValue === null || parameterValue === void 0 ? void 0 : parameterValue.success) {
1239
- const typeValue = parameterValue.typeValue;
1240
- if (typeValue) {
1241
- return parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0][typeValue];
1242
- }
1243
- }
1244
- const props = Object.assign(Object.assign({}, deviceRefObj), { index, subindex, loadFromCache, requestTimeout, messageId });
1245
- const errorCodeName = types_1.MotionMasterMessage.Status.DeviceParameterValues.ParameterValue.Error.Code[(_c = (_b = (_a = parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0]) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.code) !== null && _c !== void 0 ? _c : 0];
1246
- throw new Error(`Failed to get parameter value (${errorCodeName}) for ${JSON.stringify(props)}`);
1247
- }));
1248
- });
1249
- }
1250
- resolveGetDeviceParameterValueArguments(a, b, c, d, e, f) {
1251
- let deviceSerialNumber;
1252
- let index;
1253
- let subindex = 0;
1254
- let loadFromCache = false;
1255
- let requestTimeout = 1000;
1256
- let deviceRefObj;
1257
- let messageId;
1258
- if (typeof b === 'number' && typeof c === 'number') { // index, subindex, e.g. 0x6060, 1
1259
- deviceRefObj = (0, util_1.makeDeviceRefObj)(a);
1260
- index = b;
1261
- subindex = c;
1262
- if (typeof d === 'boolean') {
1263
- loadFromCache = d;
1264
- }
1265
- if (typeof e === 'number') {
1266
- requestTimeout = e;
1267
- }
1268
- if (typeof f === 'string') {
1269
- messageId = f;
1270
- }
1271
- }
1272
- else if (typeof a === 'string') { // device parameter id, e.g. "0x2110:1A.8502-03-0001353-2115"
1273
- [deviceSerialNumber, index, subindex] = (0, device_parameter_1.splitDeviceParameterId)(a);
1274
- deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceSerialNumber);
1275
- if (typeof b === 'boolean') {
1276
- loadFromCache = b;
1277
- }
1278
- if (typeof c === 'number') {
1279
- requestTimeout = c;
1280
- }
1281
- if (typeof d === 'string') {
1282
- messageId = d;
1283
- }
1284
- }
1285
- if (!index) {
1286
- throw new Error('Device parameter index is not provided!');
1287
- }
1288
- if (!deviceRefObj) {
1289
- throw new Error('Device address, position or serial number is not provided!');
1290
- }
1291
- return { deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId };
1292
- }
1293
- getParameterValues(ids, loadFromCache = false, requestTimeout = 9000, messageId) {
1294
- return this.resolveGetParameterValuesIds(ids).pipe((0, operators_1.mergeMap)((tuples) => {
1295
- var _a;
1296
- const collection = [];
1297
- for (const [deviceAddress, index, subindex] of tuples) {
1298
- let item = collection.find(c => c.deviceAddress === deviceAddress);
1299
- if (!item) {
1300
- item = { deviceAddress, parameters: [], sendProgress: false };
1301
- collection.push(item);
1302
- }
1303
- (_a = item.parameters) === null || _a === void 0 ? void 0 : _a.push({ index, subindex, loadFromCache });
1304
- }
1305
- return this.getMultiDeviceParameterValues({ collection }, requestTimeout, messageId).pipe((0, operators_1.map)((multiDeviceParameterValues) => {
1306
- if (!(multiDeviceParameterValues === null || multiDeviceParameterValues === void 0 ? void 0 : multiDeviceParameterValues.collection)) {
1307
- throw new Error(`Multi device parameter values collection is empty`);
1308
- }
1309
- return multiDeviceParameterValues.collection.map(({ parameterValues, deviceAddress }) => {
1310
- if (!parameterValues) {
1311
- throw new Error(`No parameterValues for device ${deviceAddress}`);
1312
- }
1313
- return parameterValues.map((parameterValue) => {
1314
- const value = parameterValue;
1315
- const typeValue = value === null || value === void 0 ? void 0 : value.typeValue;
1316
- if (typeValue) {
1317
- return parameterValue[typeValue];
1318
- }
1319
- throw new Error(`No type value for parameter ${(0, parameter_1.makeParameterId)(value === null || value === void 0 ? void 0 : value.index, value === null || value === void 0 ? void 0 : value.subindex)}`);
1320
- });
1321
- }).flat();
1322
- }));
1323
- }));
1324
- }
1325
- uploadMany(ids, loadFromCache = false, requestTimeout = 9000, messageId) {
1326
- return (0, rxjs_1.lastValueFrom)(this.getParameterValues(ids, loadFromCache, requestTimeout, messageId));
1327
- }
1328
- resolveGetParameterValuesIds(ids) {
1329
- if (ids.length === 0) {
1330
- return (0, rxjs_1.throwError)(() => new Error('The provided array of device parameter ids or addresses is empty!'));
1331
- }
1332
- let refs;
1333
- if ((0, types_1.isArrayOfDeviceSerialNumbers)(ids)) {
1334
- refs = ids.map(id => (0, device_parameter_1.splitDeviceParameterId)(id));
1335
- }
1336
- else {
1337
- refs = ids;
1338
- }
1339
- return (0, rxjs_1.defer)(() => {
1340
- return (0, rxjs_1.forkJoin)(refs.map(([deviceRef, index, subindex]) => {
1341
- return this.resolveDeviceAddress((0, util_1.makeDeviceRefObj)(deviceRef)).pipe((0, operators_1.map)((deviceAddress) => ([deviceAddress, index, subindex])));
1342
- }));
1343
- });
1344
- }
1345
- setParameterValue(a, b, c, d, e, f, g) {
1346
- try {
1347
- const { deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId } = this.resolveSetDeviceParameterValueArguments(a, b, c, d, e, f, g);
1348
- return this.createSetParameterValueObservable(deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId);
1349
- }
1350
- catch (err) {
1351
- return (0, rxjs_1.throwError)(() => err);
1352
- }
1353
- }
1354
- download(a, b, c, d, e, f, g) {
1355
- const { deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId } = this.resolveSetDeviceParameterValueArguments(a, b, c, d, e, f, g);
1356
- return (0, rxjs_1.lastValueFrom)(this.createSetParameterValueObservable(deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId));
1357
- }
1358
- createSetParameterValueObservable(deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout = 1000, messageId) {
1359
- return (0, rxjs_1.defer)(() => {
1360
- return this.resolveDeviceParameterTypeValueKey(deviceRefObj, index, subindex, valueTypeKey).pipe((0, operators_1.mergeMap)((valueTypeKey) => {
1361
- const parameterValue = { index, subindex, [valueTypeKey]: value };
1362
- return this.setDeviceParameterValues(Object.assign(Object.assign({}, deviceRefObj), { parameterValues: [parameterValue] }), requestTimeout, messageId).pipe((0, operators_1.map)(({ parameterValues }) => {
1363
- var _a, _b, _c;
1364
- const parameterValue = parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0];
1365
- if (parameterValue === null || parameterValue === void 0 ? void 0 : parameterValue.success) {
1366
- const typeValue = parameterValue.typeValue;
1367
- if (typeValue) {
1368
- return;
1369
- }
1370
- }
1371
- const props = Object.assign(Object.assign({}, deviceRefObj), { index, subindex, value, valueTypeKey, requestTimeout, messageId });
1372
- const errorCodeName = types_1.MotionMasterMessage.Status.DeviceParameterValues.ParameterValue.Error.Code[(_c = (_b = (_a = parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0]) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.code) !== null && _c !== void 0 ? _c : 0];
1373
- throw new Error(`Failed to set parameter value (${errorCodeName}) for ${JSON.stringify(props)}`);
1374
- }));
1375
- }));
1376
- });
1377
- }
1378
- /**
1379
- * Resolve set device parameter value arguments.
1380
- */
1381
- resolveSetDeviceParameterValueArguments(a, b, c, d, e, f, g) {
1382
- let deviceSerialNumber;
1383
- let index;
1384
- let subindex = 0;
1385
- let value = 0;
1386
- let valueTypeKey;
1387
- let requestTimeout = 1000;
1388
- let deviceRefObj;
1389
- let messageId;
1390
- if (typeof b === 'number' && typeof c === 'number') { // index, subindex, e.g. 0x6060, 1
1391
- deviceRefObj = (0, util_1.makeDeviceRefObj)(a);
1392
- index = b;
1393
- subindex = c;
1394
- if (d !== undefined) {
1395
- value = d;
1396
- }
1397
- valueTypeKey = e;
1398
- if (typeof f === 'number') {
1399
- requestTimeout = f;
1400
- }
1401
- messageId = g;
1402
- }
1403
- else if (typeof a === 'string') { // device parameter id, e.g. "0x2110:1A.8502-03-0001353-2115"
1404
- [deviceSerialNumber, index, subindex] = (0, device_parameter_1.splitDeviceParameterId)(a);
1405
- deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceSerialNumber);
1406
- value = b;
1407
- if (typeof c !== 'number') {
1408
- valueTypeKey = c;
1409
- }
1410
- if (typeof d === 'number') {
1411
- requestTimeout = d;
1412
- }
1413
- messageId = e;
1414
- }
1415
- if (!index) {
1416
- throw new Error('Device parameter index is not provided!');
1417
- }
1418
- if (!deviceRefObj) {
1419
- throw new Error('Device address, position or serial number is not provided!');
1420
- }
1421
- return { deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId };
1422
- }
1423
- setParameterValues(ids, requestTimeout = 9000, messageId) {
1424
- if (ids.length === 0) {
1425
- return (0, rxjs_1.throwError)(() => new Error('The provided array of device parameter ids or addresses is empty!'));
1426
- }
1427
- let refs;
1428
- if ((0, types_1.isObjectOfDeviceSerialNumberToValues)(ids)) {
1429
- refs = Object.entries(ids).map(([id, value]) => ([...(0, device_parameter_1.splitDeviceParameterId)(id), value]));
1430
- }
1431
- else {
1432
- refs = ids;
1433
- }
1434
- return (0, rxjs_1.defer)(() => {
1435
- return (0, rxjs_1.forkJoin)(refs.map(([deviceRef, index, subindex, value, typeValueKey]) => {
1436
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1437
- return this.resolveDeviceAddress(deviceRefObj).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1438
- return this.resolveDeviceParameterTypeValueKey(deviceRefObj, index, subindex, typeValueKey).pipe((0, operators_1.map)((typeValueKey) => ([deviceAddress, index, subindex, value, typeValueKey])));
1439
- }));
1440
- })).pipe((0, operators_1.mergeMap)((tuples) => {
1441
- var _a;
1442
- const collection = [];
1443
- for (const [deviceAddress, index, subindex, value, typeValueKey] of tuples) {
1444
- let item = collection.find(c => c.deviceAddress === deviceAddress);
1445
- if (!item) {
1446
- item = { deviceAddress, parameterValues: [] };
1447
- collection.push(item);
1448
- }
1449
- (_a = item.parameterValues) === null || _a === void 0 ? void 0 : _a.push({ index, subindex, [typeValueKey]: value });
1450
- }
1451
- return this.setMultiDeviceParameterValues({ collection }, requestTimeout, messageId).pipe((0, operators_1.map)((multiDeviceParameterValues) => {
1452
- var _a;
1453
- (_a = multiDeviceParameterValues.collection) === null || _a === void 0 ? void 0 : _a.map(({ parameterValues, deviceAddress }) => {
1454
- if (!parameterValues) {
1455
- throw new Error(`No parameterValues for device ${deviceAddress}`);
1456
- }
1457
- });
1458
- }));
1459
- }));
1460
- });
1461
- }
1462
- downloadMany(ids, requestTimeout = 9000, messageId) {
1463
- return (0, rxjs_1.lastValueFrom)(this.setParameterValues(ids, requestTimeout, messageId));
1464
- }
1465
- resolveDeviceParameterTypeValueKey(deviceRefObj, index, subindex, typeValueKey) {
1466
- if (typeValueKey) {
1467
- return (0, rxjs_1.of)(typeValueKey);
1468
- }
1469
- return this.resolveDeviceParameterInfoMap(deviceRefObj).pipe((0, operators_1.map)((infoMap) => {
1470
- const parameterId = (0, parameter_1.makeParameterId)(index, subindex);
1471
- const infoParameter = infoMap.get(parameterId);
1472
- if (!(infoParameter === null || infoParameter === void 0 ? void 0 : infoParameter.typeValueKey)) {
1473
- throw new Error(`No type value key is set for device ${JSON.stringify(deviceRefObj)} ${parameterId}`);
1474
- }
1475
- return infoParameter.typeValueKey;
1476
- }));
1477
- }
1478
- resolveDeviceParameterInfoMap(deviceRef, requestTimeout = 5000) {
1479
- return this.resolveDeviceAddress(deviceRef).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1480
- let infoMap = this.deviceParameterInfoMap.get(deviceAddress);
1481
- if (infoMap) {
1482
- return (0, rxjs_1.of)(infoMap);
1483
- }
1484
- return this.getDeviceParameterInfo({ deviceAddress }, requestTimeout).pipe((0, operators_1.map)(({ parameters }) => {
1485
- if (!parameters) {
1486
- throw new Error(`Device parameter info has no parameters ${deviceAddress}`);
1487
- }
1488
- infoMap = new Map();
1489
- this.deviceParameterInfoMap.set(deviceAddress, infoMap);
1490
- for (const parameter of parameters) {
1491
- if (parameter.valueType) {
1492
- const typeValue = util_1.valueTypeToParameterTypeValueKeyMap.get(parameter.valueType);
1493
- if (!typeValue) {
1494
- throw new Error(`No type value for device ${deviceAddress} in resolveDeviceParameterInfoMap`);
1495
- }
1496
- const serialNumber = this.getSerialNumberByDeviceAddress(deviceAddress);
1497
- if (!serialNumber) {
1498
- throw new Error(`No serial number for device ${deviceAddress} in resolveDeviceParameterInfoMap`);
1499
- }
1500
- const id = (0, device_parameter_1.makeDeviceParameterId)(serialNumber, parameter.index, parameter.subindex);
1501
- const key = (0, parameter_1.makeParameterId)(parameter);
1502
- infoMap.set(key, Object.assign(Object.assign({}, parameter), { id, index: parameter.index, subindex: parameter.subindex, name: parameter.name, typeValueKey: typeValue }));
1503
- }
1504
- }
1505
- return infoMap;
1506
- }));
1507
- }));
1508
- }
1509
- /**
1510
- * Get device parameters.
1511
- */
1512
- getDeviceParameters(props, requestTimeout = 30000) {
1513
- return this.resolveDevice(props).pipe((0, operators_1.mergeMap)((device) => {
1514
- return this.getDeviceParameterInfo({ deviceAddress: device.deviceAddress }, requestTimeout).pipe((0, operators_1.mergeMap)(deviceParameterInfoStatus => {
1515
- var _a;
1516
- const { loadFromCache, sendProgress } = props;
1517
- const parameters = (_a = deviceParameterInfoStatus.parameters) === null || _a === void 0 ? void 0 : _a.map(p => { var _a, _b; return ({ index: (_a = p.index) !== null && _a !== void 0 ? _a : 0, subindex: (_b = p.subindex) !== null && _b !== void 0 ? _b : 0, loadFromCache }); });
1518
- return this.getDeviceParameterValues({ deviceAddress: device.deviceAddress, sendProgress, parameters }, requestTimeout).pipe((0, operators_1.map)(deviceParameterValuesStatus => {
1519
- var _a, _b;
1520
- if (!deviceParameterValuesStatus.parameterValues) {
1521
- throw new Error('Device parameter values is empty');
1522
- }
1523
- const deviceParameters = deviceParameterValuesStatus.parameterValues.map((value, i) => {
1524
- var _a, _b, _c;
1525
- const index = (_a = value.index) !== null && _a !== void 0 ? _a : 0;
1526
- const subindex = (_b = value.subindex) !== null && _b !== void 0 ? _b : 0;
1527
- const id = (0, device_parameter_1.makeDeviceParameterId)(device.id, index, subindex);
1528
- return (Object.assign(Object.assign(Object.assign({ id }, value), (_c = deviceParameterInfoStatus.parameters) === null || _c === void 0 ? void 0 : _c[i]), { index, subindex, value: (0, util_1.getParameterValue)(value) }));
1529
- });
1530
- const progress = sendProgress ? (_b = (_a = deviceParameterValuesStatus === null || deviceParameterValuesStatus === void 0 ? void 0 : deviceParameterValuesStatus.progress) === null || _a === void 0 ? void 0 : _a.percentage) !== null && _b !== void 0 ? _b : 0 : 100;
1531
- return { parameters: deviceParameters, progress };
1532
- }));
1533
- }));
1534
- }));
1535
- }
1536
- /**
1537
- * Get files.
1538
- */
1539
- getFiles(deviceRef, requestTimeout = 30000, messageId) {
1540
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1541
- return this.getDeviceFileList(deviceRefObj, requestTimeout, messageId).pipe((0, operators_1.map)((status) => { var _a, _b; return (_b = (_a = status.fileList) === null || _a === void 0 ? void 0 : _a.files) !== null && _b !== void 0 ? _b : []; }));
1542
- }
1543
- /**
1544
- * Get file.
1545
- */
1546
- getFile(deviceRef, name, requestTimeout = 5000, messageId) {
1547
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1548
- return this.getDeviceFile(Object.assign(Object.assign({}, deviceRefObj), { name }), requestTimeout, messageId).pipe((0, operators_1.map)(({ content }) => content));
1549
- }
1550
- /**
1551
- * Get decoded file.
1552
- *
1553
- * Content of a device file is returned as a binary data buffer.
1554
- * This functions tries to decode the content as a text in utf-8 character encoding.
1555
- */
1556
- getDecodedFile(deviceRef, name, requestTimeout = 2000, messageId) {
1557
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1558
- return this.getDeviceFile(Object.assign(Object.assign({}, deviceRefObj), { name }), requestTimeout, messageId).pipe((0, operators_1.map)(({ content }) => {
1559
- if (content) {
1560
- return (0, util_1.decodeTextContent)(content);
1561
- }
1562
- throw new Error(`Device file ${name} content for device ${deviceRef} is undefined!}`);
1563
- }));
1564
- }
1565
- /**
1566
- * Unlock protected files.
1567
- *
1568
- * This will enable the deletion of protected files.
1569
- * Protected files begin with a period, such as '.hardware_description'.
1570
- */
1571
- unlockProtectedFiles(deviceRef, requestTimeout = 5000, messageId) {
1572
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1573
- return this.getDeviceFile(Object.assign(Object.assign({}, deviceRefObj), { name: 'fs-stackunlock=DD1317' }), requestTimeout, messageId);
1574
- }
1575
- /**
1576
- * Delete file.
1577
- */
1578
- deleteFile(deviceRef, name, requestTimeout = 3000, messageId) {
1579
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1580
- return this.deleteDeviceFile(Object.assign(Object.assign({}, deviceRefObj), { name }), requestTimeout, messageId);
1581
- }
1582
- /**
1583
- * Get system log lines.
1584
- *
1585
- * Get the system log content and parse it into an array of lines, where each line contains the date, uptime, ID, file, level, and message.
1586
- */
1587
- getSystemLogLines(requestTimeout = 2000, messageId) {
1588
- return this.getSystemLog(requestTimeout, messageId).pipe((0, operators_1.map)((systemLog) => { var _a; return (0, util_1.parseSystemLogContent)((_a = systemLog.content) !== null && _a !== void 0 ? _a : ''); }));
1589
- }
1590
- /**
1591
- * Store parameters.
1592
- *
1593
- * Command the device to store the parameters into config.csv, then delay initially and subsequently check if the value of
1594
- * the Store parameters has changed to 1, indicating the successful completion.
1595
- *
1596
- * {@link https://www.synapticon.com/documentation/circulo/sw5.0/device_info/save_restore_config.html}
1597
- */
1598
- storeParameters(deviceRef) {
1599
- return this.setParameterValue(deviceRef, 0x1010, 1, 0x65766173).pipe(// "evas"
1600
- (0, operators_1.delay)(2000), (0, operators_1.mergeMap)(() => this.matchParameterValue(deviceRef, 0x1010, 1, 1).pipe((0, operators_1.retry)({ count: 10, delay: 500 }))));
1601
- }
1602
- /**
1603
- * Restore default parameters.
1604
- *
1605
- * Command the device to restore the default parameters, then delay initially and subsequently check if the value of
1606
- * the Restore default parameters has changed to 1, indicating the successful completion.
1607
- *
1608
- * {@link https://www.synapticon.com/documentation/circulo/sw5.0/device_info/save_restore_config.html}
1609
- */
1610
- restoreDefaultParameters(deviceRef, type = types_1.RestoreDefaultParametersType.RESTORE_ALL_DEFAULT_PARAMETERS) {
1611
- return this.setParameterValue(deviceRef, 0x1011, type, 0x64616f6c).pipe(// "daol"
1612
- (0, operators_1.delay)(2000), (0, operators_1.mergeMap)(() => this.matchParameterValue(deviceRef, 0x1011, type, 1).pipe((0, operators_1.retry)({ count: 10, delay: 500 }))));
1613
- }
1614
- /**
1615
- * Match parameter value.
1616
- *
1617
- * This function will retrieve a parameter value from a device and compare it with the expected value.
1618
- *
1619
- * @throws Will throw an error if the values do not match.
1620
- */
1621
- matchParameterValue(deviceRef, index, subindex, expectedValue) {
1622
- return this.getParameterValue(deviceRef, index, subindex).pipe((0, operators_1.map)((value) => {
1623
- if (value !== expectedValue) {
1624
- throw new Error(`Device parameter value and the expected value don't match: ${value} ${expectedValue}`);
1625
- }
1626
- }));
1627
- }
1628
- matchCia402States(deviceRef, expectedStates) {
1629
- return this.getParameterValue(deviceRef, 0x6041, 0).pipe((0, operators_1.map)((value) => {
1630
- const state = (0, cia402_1.getCia402State)(value);
1631
- if (!expectedStates.includes(state)) {
1632
- throw new Error(`Device CiA402 state and the expected state don't match: ${state} ${expectedStates}`);
1633
- }
1634
- }));
1635
- }
1636
- /**
1637
- * Helper function to enable motion.
1638
- */
1639
- enableMotion(deviceRef, controllerType, filter = false, requestTimeout = 2000, messageId) {
1640
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1641
- return this.enableMotionController(Object.assign(Object.assign({}, deviceRefObj), { controllerType, filter }), requestTimeout, messageId);
1642
- }
1643
- /**
1644
- * A helper function to disable motion.
1645
- */
1646
- disableMotion(deviceRef, requestTimeout = 3000, messageId) {
1647
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1648
- return this.disableMotionController(deviceRefObj, requestTimeout, messageId);
1649
- }
1650
- /**
1651
- * A helper function to set the motion target.
1652
- */
1653
- setMotionTarget(deviceRef, target, messageId) {
1654
- const deviceRefObj = (0, util_1.makeDeviceRefObj)(deviceRef);
1655
- return this.setMotionControllerParameters(Object.assign(Object.assign({}, deviceRefObj), { target }), messageId);
1656
- }
1657
- /**
1658
- * A helper function to set the value of the Modes of Operation parameter (0x6060:00).
1659
- */
1660
- setModesOfOperation(deviceRef, modesOfOperation) {
1661
- return this.setParameterValue(deviceRef, 0x6060, 0, modesOfOperation);
1662
- }
1663
- /**
1664
- * Get CiA402 state.
1665
- */
1666
- getCia402State(deviceRef, requestTimeout = 5000, messageId) {
1667
- return this.getParameterValue(deviceRef, 0x6041, 0, false, requestTimeout, messageId).pipe((0, operators_1.map)((value) => (0, cia402_1.getCia402State)(value)));
1668
- }
1669
- /**
1670
- * Transition to CiA402 state.
1671
- *
1672
- * This function will utilize the current value of the controlword and modify it to transition to the requested CiA402 state.
1673
- *
1674
- * Only one transition will occur, except when the current CiA402 state is SWITCH_ON_DISABLED. In this case,
1675
- * the first transition is from SOD to RTSO, and then from RTSO to the requested CiA402 state.
1676
- *
1677
- * The Statusword will be polled to check if the device has successfully transitioned to the requested CiA402 state.
1678
- * When transitioning to QSA, the Statusword will be checked against both QSA and SOD.
1679
- * This is necessary because the automatic transition from QSA to SOD can occur faster than the client's polling interval.
1680
- */
1681
- transitionToCia402State(deviceRef, targetState) {
1682
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
1683
- let currentState = yield (0, rxjs_1.lastValueFrom)(this.getCia402State(deviceRef));
1684
- if (targetState === currentState) {
1685
- return;
1686
- }
1687
- if (currentState === cia402_1.Cia402State.NOT_READY_TO_SWITCH_ON || currentState === cia402_1.Cia402State.FAULT_REACTION_ACTIVE) {
1688
- throw new Error(`It is not possible to transition from state ${currentState} to state ${targetState}. The transition from the current ${currentState} state is automatic.`);
1689
- }
1690
- let controlword = yield this.upload(deviceRef, 0x6040, 0);
1691
- let transition = cia402_2.cia402Transitions.find(t => t.from === currentState && t.to === targetState);
1692
- // From SOD it's only possible to transition to RTSO.
1693
- if (currentState === cia402_1.Cia402State.SWITCH_ON_DISABLED && targetState !== cia402_1.Cia402State.READY_TO_SWITCH_ON) {
1694
- transition = cia402_2.cia402Transitions.find(t => t.from === cia402_1.Cia402State.READY_TO_SWITCH_ON && t.to === targetState);
1695
- }
1696
- if (!transition) {
1697
- throw new Error(`It is not possible to transition from state ${currentState} to state ${targetState}.`);
1698
- }
1699
- if (currentState === cia402_1.Cia402State.SWITCH_ON_DISABLED) {
1700
- const sodToRtsoTransition = cia402_2.cia402Transitions[0];
1701
- const command = (0, cia402_3.createControlwordCommand)(controlword, sodToRtsoTransition.command);
1702
- yield (0, rxjs_1.lastValueFrom)(this.setParameterValue(deviceRef, 0x6040, 0, command).pipe((0, operators_1.mergeMap)(() => this.matchCia402States(deviceRef, [cia402_1.Cia402State.READY_TO_SWITCH_ON]).pipe((0, operators_1.retry)({ count: 5, delay: 1000 })))));
1703
- controlword = yield this.upload(deviceRef, 0x6040, 0);
1704
- currentState = yield (0, rxjs_1.lastValueFrom)(this.getCia402State(deviceRef));
1705
- if (targetState === currentState) {
1706
- return;
1707
- }
1708
- }
1709
- const command = (0, cia402_3.createControlwordCommand)(controlword, transition.command);
1710
- const expectedStates = [targetState];
1711
- if (targetState === cia402_1.Cia402State.QUICK_STOP_ACTIVE) {
1712
- expectedStates.push(cia402_1.Cia402State.SWITCH_ON_DISABLED);
1713
- }
1714
- yield (0, rxjs_1.lastValueFrom)(this.setParameterValue(deviceRef, 0x6040, 0, command).pipe((0, operators_1.mergeMap)(() => this.matchCia402States(deviceRef, expectedStates).pipe((0, operators_1.retry)({ count: 10, delay: 1000 }))), (0, operators_1.catchError)(() => {
1715
- throw new Error(`Timeout occurred while transitioning from ${currentState} state to ${targetState} state.`);
1716
- })));
1717
- });
1718
- }
1719
- /**
1720
- * Reset fault.
1721
- *
1722
- * Resetting a fault can only be done from the FAULT CiA402 state by transitioning to SWITCH_ON_DISABLED.
1723
- * This function will do the rising edge, that is it will first set the controlword reset fault bit to 0, wait a bit, and then set it to 1.
1724
- */
1725
- resetFault(deviceRef) {
1726
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
1727
- const currentState = yield (0, rxjs_1.lastValueFrom)(this.getCia402State(deviceRef));
1728
- if (currentState !== cia402_1.Cia402State.FAULT) {
1729
- throw new Error(`Device ${deviceRef} is not in FAULT CiA402 state.`);
1730
- }
1731
- let controlword = yield this.upload(deviceRef, 0x6040, 0);
1732
- controlword = controlword & 0xFF7F;
1733
- yield this.download(deviceRef, 0x6040, 0, controlword);
1734
- yield (0, util_1.resolveAfter)(25);
1735
- const command = (0, cia402_3.createControlwordCommand)(controlword, cia402_1.ControlwordCommand.FAULT_RESET);
1736
- yield this.download(deviceRef, 0x6040, 0, command);
1737
- });
1738
- }
1739
- /**
1740
- * Quick stop.
1741
- *
1742
- * Quickly stopping a drive can only be done from the OPERATION_ENABLED CiA402 state by transitioning to QUICK_STOP_ACTIVE.
1743
- */
1744
- quickStop(deviceRef) {
1745
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
1746
- const currentState = yield (0, rxjs_1.lastValueFrom)(this.getCia402State(deviceRef));
1747
- if (currentState !== cia402_1.Cia402State.OPERATION_ENABLED) {
1748
- throw new Error(`Device ${deviceRef} is not in OPERATION_ENABLED CiA402 state.`);
1749
- }
1750
- const controlword = yield this.upload(deviceRef, 0x6040, 0);
1751
- const command = (0, cia402_3.createControlwordCommand)(controlword, cia402_1.ControlwordCommand.QUICK_STOP);
1752
- yield this.download(deviceRef, 0x6040, 0, command);
1753
- });
1754
- }
1755
- /**
1756
- * Reset targets.
1757
- *
1758
- * Set torque and velocity targets to 0, and set the position target to the current position value.
1759
- */
1760
- resetTargets(deviceRef) {
1761
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
1762
- const positionActualValue = yield this.upload(deviceRef, 0x6064, 0);
1763
- yield this.downloadMany([
1764
- [deviceRef, 0x6071, 0, 0],
1765
- [deviceRef, 0x607A, 0, positionActualValue],
1766
- [deviceRef, 0x60FF, 0, 0], // target velocity
1767
- ]);
1768
- });
1769
- }
1770
- /**
1771
- * Apply set point.
1772
- *
1773
- * When running in position profile mode and setting a new position target value, it's important to note that the device won't immediately move to the new position.
1774
- * To execute a new position set-point or target value, you need to make certain changes to the controlword.
1775
- *
1776
- * To indicate that the new set-point is valid and should be executed, you need to trigger a rising edge of the new set-point bit in the controlword.
1777
- * This rising edge serves as a signal to the device that a new set-point is being provided.
1778
- *
1779
- * Once the device receives the rising edge of the new set-point bit, it will set the set-point acknowledge bit in the statusword to 1.
1780
- * This indicates that the device has acknowledged the new set-point and will execute the corresponding action, such as moving to the specified position.
1781
- *
1782
- * Afterwards, the device will signal its ability to accept new set-points by setting the set-point acknowledge bit back to 0 in the statusword.
1783
- * This signifies that the device is ready to receive and process additional set-points.
1784
- *
1785
- * It's important to note that the device won't wait for the previous set-point to finish before executing a new set-point.
1786
- * If you want to initiate a new set-point while the previous one is still being executed, you need to generate another rising edge of the new set-point bit in the controlword.
1787
- */
1788
- applySetPoint(deviceRef) {
1789
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
1790
- let controlword = yield this.upload(deviceRef, 0x6040, 0);
1791
- if ((controlword & 0x0010) > 0) {
1792
- controlword = controlword & 0xFFEF; // set new set-point bit to 0
1793
- yield this.download(deviceRef, 0x6040, 0, controlword);
1794
- yield (0, util_1.resolveAfter)(30);
1795
- }
1796
- controlword = controlword | 0x0010 | 0x0020; // set new set-point and change set immediately bits to 1
1797
- yield this.download(deviceRef, 0x6040, 0, controlword);
1798
- });
1799
- }
1800
- /**
1801
- * Run OS command.
1802
- *
1803
- * The OS command is a standard way of triggering actions or services that cannot be accommodated through a single SDO upload/download.
1804
- *
1805
- * The Dictionary object 0x1023 is used for executing OS commands. The following is the subindex list and their meanings:
1806
- *
1807
- * - 1: Command (OCTET_STRING), 8 bytes
1808
- * - Byte 0: OS command ID, OS commands are identified by number, and here we specify which OS command to run, for example, 0 is encoder register communication
1809
- * - Byte 1-7: Service request data, the remaining 7 bytes serve as arguments to the OS command, specifying details such as what register to read the value from
1810
- * - 2: Status (UNSIGNED8), 1 byte
1811
- * - 0: last command completed, no errors, no response data
1812
- * - 1: last command completed, no errors, response data available
1813
- * - 2: last command completed, error, no response data
1814
- * - 3: last command completed, error, response data available
1815
- * - 100-200: command executing with percentage (100 = 0%, 200 = 100%)
1816
- * - 255: command executing (if percentage display is not supported)
1817
- * - 3: Response (OCTET_STRING), 8 bytes
1818
- * - Byte 0: Same as subindex 2
1819
- * - Byte 1: unused
1820
- * - Byte 2-7: Service response data, if the last command completed with response data, it will be available in these 6 bytes, for example, the value of the BiSS register
1821
- *
1822
- * Note that:
1823
- * - As soon as the value of 1: Command Byte 0 changes, the OS command will start to execute.
1824
- * - If the last command completed with response data, it will be available in bytes 2-7 of 3: Response subindex.
1825
- * - The value of subindex 2: Status will be the same as that of 3: Response Byte 0.
1826
- *
1827
- * All possible cases of 0x1023:03 Response subindex:
1828
- *
1829
- * | Description | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
1830
- * |-------------------------------------------------------|:-------:|:------:|:-------------:|:------:|:------:|:------:|:------:|:------:|
1831
- * | Command in progress with percentage | 100-200 | - | - | - | - | - | - | - |
1832
- * | Command in progress without percentage | 255 | - | - | - | - | - | - | - |
1833
- * | Command completed without errors and without response | 0 | - | - | - | - | - | - | - |
1834
- * | Command completed without errors and with response | 1 | - | Data | Data | Data | Data | Data | Data |
1835
- * | Command completed with error and without response | 2 | - | - | - | - | - | - | - |
1836
- * | Command completed with error and with response | 3 | - | OS error code | Data | Data | Data | Data | Data |
1837
- *
1838
- * Note that in the case of "Command completed without errors and with response," 6 bytes can be used for data.
1839
- * However, only 5 bytes are available in the case of "Command completed with error and with response."
1840
- * This limitation arises because Byte 2 in the latter response is allocated for the OS error code.
1841
- * The OS error code serves to indicate the reason behind the error occurrence.
1842
- *
1843
- * 0x1023: OS command is used in combination with 0x1024: OS command mode object.
1844
- * In our firmware, only the values 0 and 3 from the 0x1024 command mode object (USINT) are handled, and their meanings are as follows:
1845
- * - 0: Execute the next command immediatelly (default)
1846
- * - 3: Abort the current command and all commands in the buffer
1847
- *
1848
- * Note that if you abort the OS command, you must switch the value of 0x1024 back to 0 before executing another OS command.
1849
- */
1850
- runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1851
- let response;
1852
- // defer must be used to make a request to Motion Master on each repeat step
1853
- const getResponse$ = (0, rxjs_1.defer)(() => this.getParameterValue(deviceRef, 0x1023, 3).pipe((0, operators_1.map)(os_command_1.parseOsCommandResponse), (0, rxjs_1.tap)((r) => response = r)));
1854
- return (0, rxjs_1.concat)(executeTheNextOsCommandImmediatelly ? this.setOsCommandMode(deviceRef, os_command_1.OsCommandMode.EXECUTE_THE_NEXT_COMMAND_IMMEDIATELY) : rxjs_1.EMPTY, this.setParameterValue(deviceRef, 0x1023, 1, command), getResponse$.pipe((0, operators_1.repeat)({
1855
- delay: () => (response && (response.request === 'succeeded' || response.request === 'failed'))
1856
- ? rxjs_1.EMPTY
1857
- : (0, rxjs_1.timer)(responsePollingInterval),
1858
- }))).pipe((0, operators_1.takeUntil)((0, rxjs_1.timer)(commandTimeout).pipe((0, operators_1.mergeMap)(() => (0, rxjs_1.throwError)(() => new Error(`A timeout has occured for the OS command ${JSON.stringify(command)} on device ${deviceRef}.`))))), (0, operators_1.filter)(Boolean));
1859
- }
1860
- setOsCommandMode(deviceRef, value) {
1861
- return this.setParameterValue(deviceRef, 0x1024, 0, value);
1862
- }
1863
- abortOsCommand(deviceRef) {
1864
- return this.setOsCommandMode(deviceRef, os_command_1.OsCommandMode.ABORT_THE_CURRENT_COMMAND_AND_ALL_COMMANDS_IN_THE_BUFFER);
1865
- }
1866
- runEncoderRegisterCommunicationOsCommand(deviceRef, encoderOrdinal = 1, slaveAddress = 0, rw = 0, registerAddress = 0, registerWriteValue = 0, commandTimeout = 10000, responsePollingInterval = 1000) {
1867
- const command = (0, os_command_1.createEncoderRegisterCommunicationOsCommand)(encoderOrdinal, slaveAddress, rw, registerAddress, registerWriteValue);
1868
- return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval).pipe((0, operators_1.map)((osCommandResponse) => {
1869
- if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1870
- return Object.assign(Object.assign({}, osCommandResponse), { registerValue: osCommandResponse.data[0] });
1871
- }
1872
- return osCommandResponse;
1873
- }));
1874
- }
1875
- runMotorPhaseOrderDetectionOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000) {
1876
- const command = (0, os_command_1.createMotorPhaseOrderDetectionOsCommand)();
1877
- return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval).pipe((0, operators_1.map)((osCommandResponse) => {
1878
- if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1879
- return Object.assign(Object.assign({}, osCommandResponse), { motorPhaseOrder: osCommandResponse.data[0] });
1880
- }
1881
- return osCommandResponse;
1882
- }));
1883
- }
1884
- runCommutationOffsetMeasurementOsCommand(deviceRef, commandTimeout = 30000, responsePollingInterval = 1000) {
1885
- const command = (0, os_command_1.createCommutationOffsetMeasurementOsCommand)();
1886
- return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval).pipe((0, operators_1.map)((osCommandResponse) => {
1887
- if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1888
- const [b0, b1] = osCommandResponse.data;
1889
- const commutationAngleOffset = (b0 << 8) | b1;
1890
- return Object.assign(Object.assign({}, osCommandResponse), { commutationAngleOffset });
1891
- }
1892
- return osCommandResponse;
1893
- }));
1894
- }
1895
- runOpenPhaseDetectionOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000) {
1896
- const command = (0, os_command_1.createMotorPhaseOrderDetectionOsCommand)();
1897
- return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval).pipe((0, operators_1.map)((osCommandResponse) => {
1898
- if (osCommandResponse.request === 'succeeded') {
1899
- return Object.assign(Object.assign({}, osCommandResponse), { phaseOpened: false });
1900
- }
1901
- if (osCommandResponse.request === 'failed') {
1902
- if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode <= 250) {
1903
- const { errorName, errorDescription } = os_command_1.openPhaseDetectionErrors[osCommandResponse.errorCode];
1904
- return Object.assign(Object.assign({}, osCommandResponse), { phaseOpened: true, errorName, errorDescription });
1905
- }
1906
- return Object.assign(Object.assign({}, osCommandResponse), { phaseOpened: true });
1907
- }
1908
- return osCommandResponse;
1909
- }));
1910
- }
1911
- runPolePairDetectionOsCommand(deviceRef, commandTimeout = 60000, responsePollingInterval = 1000) {
1912
- const command = (0, os_command_1.createPolePairDetectionOsCommand)();
1913
- return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval).pipe((0, operators_1.map)((osCommandResponse) => {
1914
- if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1915
- return Object.assign(Object.assign({}, osCommandResponse), { numberOfPolePairs: osCommandResponse.data[0] });
1916
- }
1917
- if (osCommandResponse.request === 'failed') {
1918
- if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode === 0) {
1919
- return Object.assign(Object.assign({}, osCommandResponse), { errorName: 'Current amplitude error', errorDescription: 'The drive is not able to increase the amplitude of motor phase currents to the required value. This problem can occur due to limited Vdc or high motor phase impedances.' });
1920
- }
1921
- }
1922
- return osCommandResponse;
1923
- }));
1924
- }
1925
- runPhaseResistanceMeasurementOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000) {
1926
- const command = (0, os_command_1.createPhaseResistanceMeasurementOsCommand)();
1927
- return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval).pipe((0, operators_1.map)((osCommandResponse) => {
1928
- if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1929
- const [b0, b1, b2, b3] = osCommandResponse.data;
1930
- const phaseResistance = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1931
- return Object.assign(Object.assign({}, osCommandResponse), { phaseResistance });
1932
- }
1933
- if (osCommandResponse.request === 'failed') {
1934
- if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode === 0) {
1935
- return Object.assign(Object.assign({}, osCommandResponse), { errorName: 'Current amplitude error', errorDescription: 'Unable to increase the current amplitude.' });
1936
- }
1937
- }
1938
- return osCommandResponse;
1939
- }));
1940
- }
1941
- runPhaseInductanceMeasurementOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000) {
1942
- const command = (0, os_command_1.createPhaseInductanceMeasurementOsCommand)();
1943
- return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval).pipe((0, operators_1.map)((osCommandResponse) => {
1944
- if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1945
- const [b0, b1, b2, b3] = osCommandResponse.data;
1946
- const phaseInductance = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1947
- return Object.assign(Object.assign({}, osCommandResponse), { phaseInductance });
1948
- }
1949
- if (osCommandResponse.request === 'failed') {
1950
- if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode === 0) {
1951
- return Object.assign(Object.assign({}, osCommandResponse), { errorName: 'Current amplitude error', errorDescription: 'Unable to increase the current amplitude.' });
1952
- }
1953
- }
1954
- return osCommandResponse;
1955
- }));
1956
- }
1957
- readOsCommandResponse(deviceRef) {
1958
- return this.getParameterValue(deviceRef, 0x1023, 3).pipe((0, operators_1.map)(os_command_1.parseOsCommandResponse));
1959
- }
1960
- /**
1961
- * Set halt bit.
1962
- *
1963
- * The behavior of the halt function (bit 8) in the controlword depends on the specific operation mode of the device.
1964
- * When the halt bit is set to 1, the commanded motion will be interrupted, and the behavior of the device will follow the defined rules for the halt option code.
1965
- *
1966
- * After releasing the halt function by setting the halt bit to 0, the drive device will attempt to continue the commanded motion if possible,
1967
- * based on the specific operation mode and the conditions of the system.
1968
- */
1969
- setHaltBit(deviceRef, value) {
1970
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
1971
- let controlword = yield this.upload(deviceRef, 0x6040, 0);
1972
- controlword = value ? (controlword | 0x0100) : (controlword & 0xFEFF);
1973
- yield this.download(deviceRef, 0x6040, 0, controlword);
1974
- });
1975
- }
1976
- getCachedDeviceByDeviceRefObj(deviceRefObj) {
1977
- if (deviceRefObj.deviceSerialNumber) {
1978
- return this.deviceMap.get(deviceRefObj.deviceSerialNumber);
1979
- }
1980
- else if (typeof deviceRefObj.devicePosition === 'number') {
1981
- for (const device of this.deviceMap.values()) {
1982
- if (device.position === deviceRefObj.devicePosition) {
1983
- return device;
1984
- }
1985
- }
1986
- }
1987
- else if (typeof deviceRefObj.deviceAddress === 'number') {
1988
- for (const device of this.deviceMap.values()) {
1989
- if (device.deviceAddress === deviceRefObj.deviceAddress) {
1990
- return device;
1991
- }
1992
- }
1993
- }
1994
- return;
1995
- }
1996
- }
1997
- exports.MotionMasterReqResClient = MotionMasterReqResClient;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MotionMasterReqResClient = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const rxjs_1 = require("rxjs");
6
+ const operators_1 = require("rxjs/operators");
7
+ const uuid_1 = require("uuid");
8
+ const device_1 = require("./device");
9
+ const device_parameter_1 = require("./device-parameter");
10
+ const hardware_description_1 = require("./hardware-description");
11
+ const operators_2 = require("./operators");
12
+ const parameter_1 = require("./parameter");
13
+ const types_1 = require("./types");
14
+ const cia402_1 = require("./cia402");
15
+ const cia402_2 = require("./cia402");
16
+ const cia402_3 = require("./cia402");
17
+ const os_command_1 = require("./os-command");
18
+ const util_1 = require("./util");
19
+ /**
20
+ * This class contains methods for making requests to Motion Master using the injected request/response socket.
21
+ *
22
+ * Each request message defined in the proto file has a corresponding request method in this class.
23
+ * For some requests, like firmware installation, Motion Master will send progress messages until the firmware installation is complete.
24
+ *
25
+ * A call to a request method will return an Observable of: (1) the corresponding status message,
26
+ * for example a call to `getDeviceInfo` will return an instance of `MotionMasterMessage.Status.DeviceInfo`,
27
+ * (2) request status - one of "succeeded," "failed," or "running.", and (3) request message id.
28
+ *
29
+ * If the request fails on Motion Master, the function will throw an error. It can also throw an error if the request times out.
30
+ *
31
+ * Request methods have an optional messageId input parameter.
32
+ * If `messageId` is not provided, one will be generated before sending the request message to Motion Master.
33
+ * Status messages (responses) received from Motion Master that correspond to the previous request will have the same `messageId` as the one in the request.
34
+ * This is how we match requests with response messages in full-duplex asynchronous communication (WebSockets, ZeroMQ).
35
+ * Matching request/response by message id is inspired by the [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification).
36
+ *
37
+ * Almost all request methods have a mandatory `requestTimeout` input parameter.
38
+ * This parameter specifies how long (in milliseconds) the client will wait for the status (response) messages corresponding to the previous request.
39
+ * If the status message is not received in due time, the request method will throw a timeout error.
40
+ * The error doesn't mean the request has failed on Motion Master, but rather that the client has given up waiting for the response(s).
41
+ * It's possible to stop some ongoing procedures on Motion Master with the corresponding stop messages, for example, StopFullAutoTuning.
42
+ *
43
+ * @example
44
+ * How to handle a request method timeout error:
45
+ * ```typescript
46
+ * req.getDeviceInfo(10).subscribe({
47
+ * next: (status) => console.log(status),
48
+ * error: (err: unknown) => {
49
+ * if (err instanceof TimeoutError) {
50
+ * alert('Request has timed out');
51
+ * }
52
+ * }
53
+ * });
54
+ * ```
55
+ *
56
+ * Request methods for a particular device, like `getDeviceParameterInfo`, accept the type of request message as the first input parameter as defined in the proto file.
57
+ * For `getDeviceParameterInfo`, that's `MotionMasterMessage.Request.IGetDeviceParameterInfo`. This type is extended (via type intersection) by `DeviceRefObj`.
58
+ * This is done in order to support multiple ways to reference a device. Motion Master only supports device addresses, which it re-generates for each device on power cycle.
59
+ * Because device addresses can change, for scripts and applications, it's preferred to use a device reference that won't change.
60
+ * The device serial number in the hardware description file uniquely identifies the device and doesn't change. This is what each request message is extended with.
61
+ * Before sending the request message to Motion Master, the device address gets resolved and is set on the request message.
62
+ * Besides the device address and device serial number, devices can be referenced by position (sequence number in the network chain).
63
+ *
64
+ * It is ensured that the Observable returned from a call to the request method will eventually complete, so a call to unsubscribe is not required.
65
+ * The Observable will complete when the request status is either "succeeded" or "failed," or it will complete (error) due to a timeout.
66
+ * Finding out if a request has completed, failed, or is still ongoing is different for each request - it depends on the status messages, see {@link requestStatusResolver}.
67
+ * Request methods handle this and for each request, they will emit status messages with one of "succeeded," "failed," or "running" request statuses.
68
+ *
69
+ * Besides the request methods, this class contains many helper methods such as the ones for getting and setting device parameter values and
70
+ * saving all parameters, which involves complex logic like multiple repeating requests to determine if saving all parameters has completed successfully.
71
+ */
72
+ class MotionMasterReqResClient {
73
+ constructor(socket) {
74
+ this.socket = socket;
75
+ /**
76
+ * Maps a device address to a map of parameter ID to a type value key.
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * new Map([
81
+ * [2045285448, new Map([
82
+ * ["0x1018:02", {
83
+ * id: '0x1018:02',
84
+ * index: 4120,
85
+ * subindex: 2,
86
+ * name: 'Product Code',
87
+ * unit: '',
88
+ * group: 'Identity object',
89
+ * readAccess: true,
90
+ * writeAccess: false,
91
+ * min: undefined,
92
+ * max: undefined,
93
+ * valueType: 7, // UNSIGNED32
94
+ * typeValueKey: "uintValue"
95
+ * }]
96
+ * ])]
97
+ * ]);
98
+ * ```
99
+ *
100
+ * When setting a parameter value, only one of int, uint, float, string, or raw type value fields must be set.
101
+ * This is defined in the proto file SetDeviceParameterValues request message.
102
+ *
103
+ * Device parameters have a richer set of base types defined in ESI, and they are provided by the DeviceParameterInfo status message.
104
+ * When device parameter info is fetched, this map gets updated by mapping device parameter types to "oneof" proto types, e.g., `INTEGER32 -> intValue`.
105
+ *
106
+ * This map helps in simplifying the API by allowing users to set parameter values without specifying the type value field.
107
+ * 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.
108
+ */
109
+ this.deviceParameterInfoMap = new Map();
110
+ /**
111
+ * Maps a device serial number to a device.
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * new Map([
116
+ * ["8502-03-0001353-2115", { deviceAddress: 2045285448, ... }]
117
+ * ]);
118
+ * ```
119
+ *
120
+ * When making requests related to a device, the device address is used as an identifier.
121
+ * The device address is not permanent. The number is regenerated by Motion Master when devices are power-cycled.
122
+ * For scripts and applications using this library, having a permanent device identifier is a better option.
123
+ * The device serial number read from the hardware description file (.hardware_description) stored on a device is a permanent identifier.
124
+ *
125
+ * This map helps in making all request methods related to a device accept a device reference instead of just a device address as an identifier.
126
+ * The device reference {@link DeviceRefObj} can be one of the following: device address, device serial number, or device position.
127
+ * Before sending a request message to Motion Master, the device address is resolved from the device reference, and this map is used for that.
128
+ */
129
+ this.deviceMap = new Map();
130
+ }
131
+ /**
132
+ * Clears the state of an instance of this class.
133
+ *
134
+ * This method is typically called when Motion Master is deinitializing or the socket is closing.
135
+ * Both maps are implicitly populated when a device or parameter type value key gets resolved.
136
+ */
137
+ clearDevicesInformation() {
138
+ this.deviceParameterInfoMap.clear();
139
+ this.deviceMap.clear();
140
+ }
141
+ //
142
+ // Requests
143
+ //
144
+ /**
145
+ * Pings the system.
146
+ *
147
+ * The client must send messages to Motion Master at regular intervals for Motion Master to consider it alive.
148
+ * When the client is considered "not alive", Motion Master will stop the ongoing procedures and monitorings started by that client.
149
+ * Each client on the Motion Master side has its own default timeout, which can be changed by {@link setSystemClientTimeout}.
150
+ */
151
+ pingSystem(messageId) {
152
+ const pingSystem = types_1.MotionMasterMessage.Request.PingSystem.create();
153
+ this.sendRequest({ pingSystem }, messageId);
154
+ return rxjs_1.EMPTY;
155
+ }
156
+ /**
157
+ * Get the system version.
158
+ *
159
+ * Retrieve the version of Motion Master. The version follows [Semantic Versioning 2.0.0](https://semver.org/).
160
+ */
161
+ getSystemVersion(requestTimeout, messageId) {
162
+ const getSystemVersion = types_1.MotionMasterMessage.Request.GetSystemVersion.create();
163
+ const id = this.sendRequest({ getSystemVersion }, messageId);
164
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('systemVersion', requestTimeout, id));
165
+ }
166
+ /**
167
+ * Get device info.
168
+ *
169
+ * Device info includes a list of devices on the network. Each device has a type, position in the network, and a device address.
170
+ * At the time of writing, the device alias is not yet supported.
171
+ *
172
+ * Motion Master will assign a unique device address to each device in the list.
173
+ * The device address is what the client libraries use to make requests like `getDeviceParameter` for a particular device.
174
+ * The device address is only valid for the duration of the session, that is, until Motion Master re-initializes the devices, for example,
175
+ * when devices are power cycled or the Motion Master process is restarted.
176
+ */
177
+ getDeviceInfo(requestTimeout, messageId) {
178
+ const getDeviceInfo = types_1.MotionMasterMessage.Request.GetDeviceInfo.create();
179
+ const id = this.sendRequest({ getDeviceInfo }, messageId);
180
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceInfo', requestTimeout, id));
181
+ }
182
+ /**
183
+ * Get device parameter info.
184
+ *
185
+ * Device parameter info includes a list of all parameters for the requested device, but without the parameter values.
186
+ * Getting device parameter values can take more than 20ms per parameter using the IgH EtherCAT Master library.
187
+ * Not all clients require all the parameter values at once, like OBLAC Drives, so this method is useful for getting
188
+ * the list of available parameters and then separately obtaining the values of some required parameters.
189
+ *
190
+ * The returned list of device parameters will include some basic data for each parameter: index, subindex,
191
+ * name, unit, group (ARRAY or RECORD name for subitems), read and write access, min and max, and value type.
192
+ * Min and max values are not yet fully supported - they contain the min/max values for the used base type.
193
+ * The same info and more can be read from the "SOMANET_CiA_402.xml" file, but getting info from the ESI file is more complicated.
194
+ * It requires an XML parsing library and knowing how to read objects info from a dictionary and assigned modules.
195
+ */
196
+ getDeviceParameterInfo(props, requestTimeout, messageId) {
197
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
198
+ const getDeviceParameterInfo = types_1.MotionMasterMessage.Request.GetDeviceParameterInfo.create(Object.assign(Object.assign({}, props), { deviceAddress }));
199
+ const id = this.sendRequest({ getDeviceParameterInfo }, messageId);
200
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceParameterInfo', requestTimeout, id));
201
+ }));
202
+ }
203
+ /**
204
+ * Get device parameter values.
205
+ *
206
+ * As mentioned earlier, fetching parameter values can take 20ms or more. However,
207
+ * it can also fail completely or timeout on the master side, registering as a "cannot refresh value" error.
208
+ */
209
+ getDeviceParameterValues(props, requestTimeout, messageId) {
210
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
211
+ const getDeviceParameterValues = types_1.MotionMasterMessage.Request.GetDeviceParameterValues.create(Object.assign(Object.assign({}, props), { deviceAddress }));
212
+ const id = this.sendRequest({ getDeviceParameterValues }, messageId);
213
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceParameterValues', requestTimeout, id));
214
+ }));
215
+ }
216
+ /**
217
+ * Get multi-device parameter values.
218
+ *
219
+ * Allows requesting parameter values from multiple devices at the same time.
220
+ */
221
+ getMultiDeviceParameterValues(props, requestTimeout, messageId) {
222
+ const getMultiDeviceParameterValues = types_1.MotionMasterMessage.Request.GetMultiDeviceParameterValues.create(props);
223
+ const id = this.sendRequest({ getMultiDeviceParameterValues }, messageId);
224
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('multiDeviceParameterValues', requestTimeout, id));
225
+ }
226
+ /**
227
+ * Set device parameter values.
228
+ */
229
+ setDeviceParameterValues(props, requestTimeout, messageId) {
230
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
231
+ const setDeviceParameterValues = types_1.MotionMasterMessage.Request.SetDeviceParameterValues.create(Object.assign(Object.assign({}, props), { deviceAddress }));
232
+ const id = this.sendRequest({ setDeviceParameterValues }, messageId);
233
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceParameterValues', requestTimeout, id));
234
+ }));
235
+ }
236
+ /**
237
+ * Set multi-device parameter values.
238
+ *
239
+ * Allows setting parameter values on multiple devices at the same time.
240
+ */
241
+ setMultiDeviceParameterValues(props, requestTimeout, messageId) {
242
+ const setMultiDeviceParameterValues = types_1.MotionMasterMessage.Request.SetMultiDeviceParameterValues.create(props);
243
+ const id = this.sendRequest({ setMultiDeviceParameterValues }, messageId);
244
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('multiDeviceParameterValues', requestTimeout, id));
245
+ }
246
+ /**
247
+ * Get device file list.
248
+ *
249
+ * Motion Master will use Filetransfer over EtherCAT (FoE) to obtain the list of files from a device.
250
+ * There isn't an actual command for getting the list, but rather the content of a specially named file "fs-getlist" is the file list.
251
+ * The list can be read like any other file from a device using the IgH EtherCAT Master for Linux CLI program:
252
+ *
253
+ * ```
254
+ * $ ethercat foe_read fs-getlist
255
+ * ```
256
+ *
257
+ * At the time of writing this document, "fs-getlist" can return a maximum of 32 files (subject to change) in the list. This is a firmware limitation.
258
+ * However, more than 32 files can be stored on the device flash memory, but they won't appear in the list.
259
+ * Also, note that FoE can behave differently in the bootstrap firmware, which runs in the BOOT EtherCAT state, and
260
+ * SOMANET firmware, which runs in other EtherCAT states like PREOP/OP. Bootstrap tends to be buggier since it cannot be updated easily, so
261
+ * Motion Master has certain workarounds to overcome these FoE differences and bugs.
262
+ */
263
+ getDeviceFileList(props, requestTimeout, messageId) {
264
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
265
+ const getDeviceFileList = types_1.MotionMasterMessage.Request.GetDeviceFileList.create(Object.assign(Object.assign({}, props), { deviceAddress }));
266
+ const id = this.sendRequest({ getDeviceFileList }, messageId);
267
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFileList', requestTimeout, id));
268
+ }));
269
+ }
270
+ /**
271
+ * Get device file.
272
+ *
273
+ * Motion Master uses Filetransfer over EtherCAT (FoE) to read and send back the content of a file from the device flash memory.
274
+ *
275
+ * 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
276
+ * needs to be split into parts of max 9KB. Motion Master does this automatically on request to {@link setDeviceFile}.
277
+ *
278
+ * Upon receiving this request, Motion Master will first read the list of files on a device, see {@link getDeviceFileList}.
279
+ * It does that in order to determine if the requested file, for example, "SOMANET_CiA_402.xml," has been stored in multiple parts.
280
+ * The parts are named like "SOMANET_CiA_402.xml.zip.part000," "SOMANET_CiA_402.xml.zip.part001," and so on.
281
+ * Motion Master will read all parts of the requested file, unzip it, and send the content back to a client.
282
+ * 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,
283
+ * but in any case, it will try to read the content of it.
284
+ * If the file exists, it will return its content; otherwise, it will return the file not found error.
285
+ *
286
+ * Zipped files will be automatically unzipped, and their content returned; for example, "stack_image.svg.zip" can be stored
287
+ * in one or multiple files depending on the size. No matter how it's stored, it will be unzipped, and its content returned as
288
+ * text (SVG) instead of zipped content (binary).
289
+ *
290
+ * In BOOT EtherCAT state, it's only allowed to read the .hardware_description file. This was done because
291
+ * the bootloader firmware would hang when trying to read a corrupted or missing file.
292
+ *
293
+ * If the requested file content is empty, Motion Master will return a not found error. This is subject to change now that Motion Master reads the file error codes.
294
+ */
295
+ getDeviceFile(props, requestTimeout, messageId) {
296
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
297
+ const getDeviceFile = types_1.MotionMasterMessage.Request.GetDeviceFile.create(Object.assign(Object.assign({}, props), { deviceAddress }));
298
+ const id = this.sendRequest({ getDeviceFile }, messageId);
299
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFile', requestTimeout, id), (0, operators_1.map)((status) => this.fixProductionScrewUp(status)));
300
+ }));
301
+ }
302
+ /**
303
+ * Fixing a production screw-up that affects a very important customer. Added on February 29th, 2024.
304
+ * Besides fixing the production screw-up, this function will return a dummy .hardware_description file if one is not found on the device.
305
+ */
306
+ fixProductionScrewUp(status) {
307
+ if (status.name === '.hardware_description') {
308
+ if (status.content) {
309
+ const text = (0, util_1.decodeTextContent)(status.content);
310
+ if (text.includes('\\":')) {
311
+ const replacedText = text.replace(/\\"/g, '"').replace(/"{/, '{').replace(/}"/, '}');
312
+ const content = new TextEncoder().encode(replacedText);
313
+ this.unlockProtectedFiles(status.deviceAddress, 10000).subscribe({
314
+ next: () => {
315
+ this.setDeviceFile({ content, deviceAddress: status.deviceAddress, name: '.hardware_description', overwrite: true }, 10000).subscribe({
316
+ error(err) {
317
+ if (err instanceof Error) {
318
+ console.warn(`Failed to replace the invalid .hardware_description file. Error: ${err.message}`);
319
+ }
320
+ },
321
+ });
322
+ },
323
+ error: (err) => {
324
+ if (err instanceof Error) {
325
+ console.warn(`Failed to unlock protected files. Error: ${err.message}`);
326
+ }
327
+ },
328
+ });
329
+ status.content = content;
330
+ }
331
+ }
332
+ else {
333
+ if (status.deviceAddress) {
334
+ const addr = status.deviceAddress.toString();
335
+ const sn = addr.slice(0, 4) + '-' + addr.slice(4, 6) + '-' + addr.slice(6, 10) + '000-0000';
336
+ const hw = JSON.parse(JSON.stringify(hardware_description_1.dummyHardwareDescriptionFileContentTemplate));
337
+ hw.device.serialNumber = sn;
338
+ const content = new TextEncoder().encode(JSON.stringify(hw));
339
+ status.content = content;
340
+ // @ts-ignore
341
+ status['data'] = 'success';
342
+ status.error = null;
343
+ status.request = 'succeeded';
344
+ status.success = {};
345
+ }
346
+ }
347
+ }
348
+ return status;
349
+ }
350
+ /**
351
+ * Set device file.
352
+ *
353
+ * Motion Master uses Filetransfer over EtherCAT (FoE) to write files to the device flash memory.
354
+ *
355
+ * 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
356
+ * needs to be split into parts of max 9KB. Motion Master does this automatically by first zipping the content and then writing the parts.
357
+ * The parts are named in the following manner: "SOMANET_CiA_402.xml.zip.part000," "SOMANET_CiA_402.xml.zip.part001," and so on.
358
+ */
359
+ setDeviceFile(props, requestTimeout, messageId) {
360
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
361
+ const setDeviceFile = types_1.MotionMasterMessage.Request.SetDeviceFile.create(Object.assign(Object.assign({}, props), { deviceAddress }));
362
+ const id = this.sendRequest({ setDeviceFile }, messageId);
363
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFile', requestTimeout, id));
364
+ }));
365
+ }
366
+ /**
367
+ * Delete device file.
368
+ *
369
+ * Motion Master uses Filetransfer over EtherCAT (FoE) to delete files from the device flash memory.
370
+ *
371
+ * If the file to delete is written in parts, Motion Master will ensure that all parts are deleted.
372
+ * It does this by reading the list of files on a device, see {@link getDeviceFileList}, removing all parts from that list, and then
373
+ * re-reading the list again to ensure that there are no leftovers if there were more than 32 files in the list. If there are still
374
+ * more than 32 files in the list, Motion Master will blindly try to make sure there are no file parts left in the memory (may or may not work).
375
+ */
376
+ deleteDeviceFile(props, requestTimeout, messageId) {
377
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
378
+ const deleteDeviceFile = types_1.MotionMasterMessage.Request.DeleteDeviceFile.create(Object.assign(Object.assign({}, props), { deviceAddress }));
379
+ const id = this.sendRequest({ deleteDeviceFile }, messageId);
380
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFile', requestTimeout, id));
381
+ }));
382
+ }
383
+ /**
384
+ * Reset device fault.
385
+ *
386
+ * When an error occurs on a device, the device will go into the Fault reaction active state (transition 13) and then automatically to the Fault state (transition 14).
387
+ * The CiA402 state of a device can be derived from the value of the Statusword object (0x6041:00).
388
+ * In order to exit the Fault state and enter the Switch on disabled state (transition 15), the master must send a Fault reset command via the Controlword object (0x6040:00).
389
+ *
390
+ * When this request is sent to Motion Master, it will attempt to reset the fault. Resetting the fault can either succeed or fail.
391
+ * While resetting the fault, Motion Master will use the current Controlword value and set the Reset fault bit to 1.
392
+ * However, upon completion, whether successful or encountering an error, Motion Master will set it back to 0.
393
+ *
394
+ * Motion Master will return a "No Fault" warning when this request is sent, and the device is not in the Fault or Fault reaction active state.
395
+ * If this request is sent when the device is in the Fault reaction active state, Motion Master will
396
+ * wait for the automatic transition (14) to the Fault state before attempting to reset the fault.
397
+ * When Motion Master sets the Controlword Fault bit to 1, it will also periodically check if the device is no longer in the Fault state.
398
+ * After a few seconds, if the device is still in the Fault state, Motion Master will give up and return a timeout error.
399
+ */
400
+ resetDeviceFault(props, requestTimeout, messageId) {
401
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
402
+ const resetDeviceFault = types_1.MotionMasterMessage.Request.ResetDeviceFault.create(Object.assign(Object.assign({}, props), { deviceAddress }));
403
+ const id = this.sendRequest({ resetDeviceFault }, messageId);
404
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFaultReset', requestTimeout, id));
405
+ }));
406
+ }
407
+ /**
408
+ * Stop device.
409
+ *
410
+ * Motion Master will use the current value of the Controlword object and set the command on it to Quick stop.
411
+ * It will then wait for up to 2 seconds for a device to enter one of the following CiA402 states: Switch on disabled, Operation enabled, Fault.
412
+ * This is achieved by periodically checking the value of the Statusword object. If the device fails to enter one of those states within the specified time,
413
+ * Motion Master will return a timeout or a quick stop failed error.
414
+ */
415
+ stopDevice(props, requestTimeout, messageId) {
416
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
417
+ const stopDevice = types_1.MotionMasterMessage.Request.StopDevice.create(Object.assign(Object.assign({}, props), { deviceAddress }));
418
+ const id = this.sendRequest({ stopDevice }, messageId);
419
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceStop', requestTimeout, id));
420
+ }));
421
+ }
422
+ /**
423
+ * Start device firmware installation.
424
+ *
425
+ * When Motion Master receives this request for a device, it will perform the following steps:
426
+ * - Switch the device to the BOOT EtherCAT state; the device will then run the bootloader instead of the firmware.
427
+ * - Validate the firmware package.
428
+ * - Delete files found in the package from the device flash memory (SOMANET_CiA_402.xml and stack_image.svg).
429
+ * - Write files other than the firmware binaries and SII from the package to the device (SOMANET_CiA_402.xml and stack_image.svg).
430
+ * - (Optional) Install the SII file using the IgH EtherCAT Master library write SII function, or SOEM EEPROM tool.
431
+ * - Write the firmware binaries to the device using predefined names like app_firmware.bin and com_firmware.bin.
432
+ *
433
+ * During the firmware installation, Motion Master will send progress messages back to clients.
434
+ * If Motion Master encounters a failure in any step other than SII write, it will send an error. If SII write fails, only a warning will be sent.
435
+ */
436
+ startDeviceFirmwareInstallation(props, requestTimeout, messageId) {
437
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
438
+ const startDeviceFirmwareInstallation = types_1.MotionMasterMessage.Request.StartDeviceFirmwareInstallation.create(Object.assign(Object.assign({}, props), { deviceAddress }));
439
+ const id = this.sendRequest({ startDeviceFirmwareInstallation }, messageId);
440
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFirmwareInstallation', requestTimeout, id));
441
+ }));
442
+ }
443
+ /**
444
+ * Get device log.
445
+ *
446
+ * The device log is stored in two files due to the IgH EtherCAT Master library 9KB file size limitation, as explained in {@link getDeviceFile}.
447
+ * The device automatically writes the log to logging_curr.log and copies that file to logging_prev.log once it reaches 9KB.
448
+ * When Motion Master receives this request, it will read both of these files and return their combined contents.
449
+ */
450
+ getDeviceLog(props, requestTimeout, messageId) {
451
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
452
+ const getDeviceLog = types_1.MotionMasterMessage.Request.GetDeviceLog.create(Object.assign(Object.assign({}, props), { deviceAddress }));
453
+ const id = this.sendRequest({ getDeviceLog }, messageId);
454
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceLog', requestTimeout, id));
455
+ }));
456
+ }
457
+ /**
458
+ * Start cogging torque recording.
459
+ *
460
+ * Cogging torque compensation (CTC) recording can be initiated with or without auto-config.
461
+ * In the proto file, skipping the auto-config is currently named skip_auto_tuning. The field name is deprecated and will be renamed.
462
+ *
463
+ * If CTC recording is started without auto-config, users must ensure that the encoder on the motor shaft is used for all functions: commutation, position, and velocity.
464
+ * If this is not the case, Motion Master will return an invalid encoder configuration error.
465
+ * Recording can also fail if the position controller is not tuned.
466
+ *
467
+ * If CTC recording is started with auto-config, Motion Master will:
468
+ * - Change the encoder configuration to have all encoder functions on the motor shaft.
469
+ * - Disable CTC.
470
+ * - Change the velocity feed forward value.
471
+ * - Change the position and velocity filter types and cutoff frequencies.
472
+ * - Run iterative sharpening position controller auto-tuning to compute the optimal PID values (gains) for CTC recording.
473
+ *
474
+ * 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, they are reverted back.
475
+ *
476
+ * CTC recording is initiated by changing the Modes of operation (0x6060) parameter value to -1 (Cogging compensation recording mode).
477
+ * 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.
478
+ *
479
+ * The result of CTC recording is the cogging_torque.bin file written to the device flash memory. The size of this file is 2048 bytes.
480
+ * Each pair of bytes represents a 16-bit signed integer value in mNm (millinewton-meter).
481
+ *
482
+ * This request will turn the motor.
483
+ */
484
+ startCoggingTorqueRecording(props, requestTimeout, messageId) {
485
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
486
+ const startCoggingTorqueRecording = types_1.MotionMasterMessage.Request.StartCoggingTorqueRecording.create(Object.assign(Object.assign({}, props), { deviceAddress }));
487
+ const id = this.sendRequest({ startCoggingTorqueRecording }, messageId);
488
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('coggingTorqueRecording', requestTimeout, id));
489
+ }));
490
+ }
491
+ /**
492
+ * Get cogging torque data.
493
+ *
494
+ * The cogging_torque.bin file is the result of cogging torque compensation recording, as explained in {@link startCoggingTorqueRecording}.
495
+ * The size of this file is 2048 bytes, where each pair of bytes represents a 16-bit signed integer value in mNm (millinewton-meter).
496
+ *
497
+ * Motion Master will read the cogging_torque.bin file, convert the bytes to 16-bit signed integer values, and return them as an array of numbers.
498
+ */
499
+ getCoggingTorqueData(props, requestTimeout, messageId) {
500
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
501
+ const getCoggingTorqueData = types_1.MotionMasterMessage.Request.GetCoggingTorqueData.create(Object.assign(Object.assign({}, props), { deviceAddress }));
502
+ const id = this.sendRequest({ getCoggingTorqueData }, messageId);
503
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('coggingTorqueData', requestTimeout, id));
504
+ }));
505
+ }
506
+ /**
507
+ * Start offset detection.
508
+ *
509
+ * Running offset detection differs for firmwares <v5 and >=v5.
510
+ *
511
+ * For devices running firmware >=v5, Motion Master will execute multiple OS commands.
512
+ * Before initiating any command, the device is switched to Modes of operation -2 (Diagnostics mode), and
513
+ * it's put into the operation-enabled CiA402 state. The OS commands are then executed in the following order:
514
+ * - Open phase detection
515
+ * - Phase resistance measurement
516
+ * - Phase inductance measurement
517
+ * - Pole pair detection
518
+ * - Motor phase order detection
519
+ * - Commutation offset measurement
520
+ *
521
+ * Motion Master will return an error if any of the following OS commands fail: open phase detection,
522
+ * phase order detection, commutation offset measurement.
523
+ * If other OS commands fail, Motion Master will continue with the procedure and only return a warning.
524
+ *
525
+ * Once the commutation offset measurement command completes, it will update the following parameter values:
526
+ * - 0x2001:00 Commutation angle offset to the computed offset value
527
+ * - 0x2009:01 Commutation offset State to 2 (OFFSET_VALID)
528
+ * - 0x2003:05 Motor phases inverted will be changed by the firmware
529
+ *
530
+ * For devices running firmware <5, the same Modes of operation -2 is used, but it's named Commutation offset detection mode,
531
+ * and it will run as a single procedure as soon as the device is switched to CiA402 operation-enabled state.
532
+ * The end result is the same; the procedure will update 0x2001:00 and 0x2009:01 parameter values.
533
+ *
534
+ * This request will turn the motor.
535
+ */
536
+ startOffsetDetection(props, requestTimeout, messageId) {
537
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
538
+ const startOffsetDetection = types_1.MotionMasterMessage.Request.StartOffsetDetection.create(Object.assign(Object.assign({}, props), { deviceAddress }));
539
+ const id = this.sendRequest({ startOffsetDetection }, messageId);
540
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('offsetDetection', requestTimeout, id));
541
+ }));
542
+ }
543
+ /**
544
+ * @deprecated use {@link startSystemIdentification} instead
545
+ */
546
+ startPlantIdentification(props, requestTimeout, messageId) {
547
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
548
+ const startPlantIdentification = types_1.MotionMasterMessage.Request.StartPlantIdentification.create(Object.assign(Object.assign({}, props), { deviceAddress }));
549
+ const id = this.sendRequest({ startPlantIdentification }, messageId);
550
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('plantIdentification', requestTimeout, id));
551
+ }));
552
+ }
553
+ /**
554
+ * Compute auto-tuning gains.
555
+ *
556
+ * Auto-tuning gains can be computed for the position or the velocity controller.
557
+ *
558
+ * The prerequisites for running this procedure are a configured motor and the existence of
559
+ * the plant_model.csv file created by previously running the system identification {@link startSystemIdentification}.
560
+ * Motion Master will return an error if the plant_model.csv file is not found.
561
+ *
562
+ * In OBLAC Drives, this request is called when the tuning sliders (damping ratio, bandwidth) are moved and
563
+ * during the CTC recording {@link startCoggingTorqueRecording} when auto-config is not skipped.
564
+ *
565
+ * Motion Master will compute the PID gains using a proprietary algorithm and update the Kp, Ki, Kd values of the 0x2011 and 0x2012 parameters.
566
+ */
567
+ computeAutoTuningGains(props, requestTimeout, messageId) {
568
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
569
+ const computeAutoTuningGains = types_1.MotionMasterMessage.Request.ComputeAutoTuningGains.create(Object.assign(Object.assign({}, props), { deviceAddress }));
570
+ const id = this.sendRequest({ computeAutoTuningGains }, messageId);
571
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('autoTuning', requestTimeout, id));
572
+ }));
573
+ }
574
+ /**
575
+ * Set motion controller parameters.
576
+ *
577
+ * This request sets the target value for a device on Motion Master.
578
+ *
579
+ * A previous call to {@link enableMotionController} will set the mode of operation to CSP, CSV, or CST and optionally enable filtering.
580
+ */
581
+ setMotionControllerParameters(props, messageId) {
582
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
583
+ const setMotionControllerParameters = types_1.MotionMasterMessage.Request.SetMotionControllerParameters.create(Object.assign(Object.assign({}, props), { deviceAddress }));
584
+ this.sendRequest({ setMotionControllerParameters }, messageId);
585
+ return rxjs_1.EMPTY;
586
+ }));
587
+ }
588
+ /**
589
+ * Enable motion controller.
590
+ *
591
+ * This request will set the mode of operation to CSP, CSV, or CST and optionally enable filtering.
592
+ *
593
+ * If filtering is enabled, Motion Master will compute an intermediate target value in each cycle, and depending on the active mode of operation,
594
+ * set the value of one of 0x607A Target position, 0x60FF Target velocity, 0x6071 Target torque to that value.
595
+ *
596
+ * The target value can be updated with {@link setMotionControllerParameters}.
597
+ */
598
+ enableMotionController(props, requestTimeout, messageId) {
599
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
600
+ const enableMotionController = types_1.MotionMasterMessage.Request.EnableMotionController.create(Object.assign(Object.assign({}, props), { deviceAddress }));
601
+ const id = this.sendRequest({ enableMotionController }, messageId);
602
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('motionController', requestTimeout, id));
603
+ }));
604
+ }
605
+ /**
606
+ * Disable motion controller.
607
+ *
608
+ * Depending on the current CiA402 state, this request will do the following:
609
+ * - If the device is in Quick stop active, it will transition to Switch on disabled state.
610
+ * - If the device is in Operation enabled, it will transition to Switched on state.
611
+ */
612
+ disableMotionController(props, requestTimeout, messageId) {
613
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
614
+ const disableMotionController = types_1.MotionMasterMessage.Request.DisableMotionController.create(Object.assign(Object.assign({}, props), { deviceAddress }));
615
+ const id = this.sendRequest({ disableMotionController }, messageId);
616
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('motionController', requestTimeout, id));
617
+ }));
618
+ }
619
+ /**
620
+ * Set signal generator parameters.
621
+ *
622
+ * This request will update the signal generator parameter values for a device on Motion Master.
623
+ * The selected signal generator is then started by {@link startSignalGenerator}.
624
+ * The signal generator must be started within the next 60 seconds before Motion Master invalidates the previously set parameters.
625
+ */
626
+ setSignalGeneratorParameters(props, requestTimeout, messageId) {
627
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
628
+ const setSignalGeneratorParameters = types_1.MotionMasterMessage.Request.SetSignalGeneratorParameters.create(Object.assign(Object.assign({}, props), { deviceAddress }));
629
+ const id = this.sendRequest({ setSignalGeneratorParameters }, messageId);
630
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('signalGenerator', requestTimeout, id));
631
+ }));
632
+ }
633
+ /**
634
+ * Start signal generator.
635
+ *
636
+ * This request will start one of the signal generators based on the
637
+ * previously set parameters with {@link setSignalGeneratorParameters}.
638
+ *
639
+ * Signal generators are started by changing the mode of operation, putting the device into Operation enabled CiA402 state,
640
+ * and setting the target value. The mode of operation set depends on the type of signal generator and the selected controller:
641
+ * - For the simple & advanced step response and the sine wave, one of CSP, CSV, or CST is set.
642
+ * - For the ramp, trapezoidal, and bidirectional signal generators, also called profiles, one of the PP, PV, TQ is set.
643
+ *
644
+ * Currently, the only profile type that the firmware supports is the ramp profile.
645
+ * Motion Master uses this profile type to construct and run the following signals: ramp, trapezoidal, and bidirectional.
646
+ * Sine wave is also a profile, but it is currently being generated on Motion Master alone.
647
+ * Firmwares >=5.2 will support generating and running sine wave profiles on devices themselves. This will enable lossless profile
648
+ * execution on possibly higher frequencies. Future Motion Master versions will switch to this method.
649
+ *
650
+ * For the simple step response, Motion Master will only set the target value, wait for the specified holding duration,
651
+ * and then send a Quick Stop command.
652
+ *
653
+ * The advanced step response is similar to the simple one, but, after the holding duration, it will set the new target
654
+ * multiple times depending on the shape, and then send a Quick Stop if the repeat option is set to NO. If repeat is set to YES,
655
+ * then the signal generator will run indefinitely, and the users must stop it manually.
656
+ *
657
+ * Before running a profile, Motion Master will check if the device supports the profile modes by looking at 0x6502 Supported drive modes.
658
+ * If profile modes are not supported, then the profiles will be generated on Motion Master. This means the targets will be computed upfront.
659
+ * If profiles are supported, then the ramp, trapezoidal, and bidirectional signal generators are run in profile mode of operation.
660
+ * To prepare the signal generator for execution, Motion Master will set the following parameters:
661
+ * - 0x6081:00 Profile velocity (only for the position controller)
662
+ * - 0x6083:00 Profile acceleration
663
+ * - 0x6084:00 Profile deceleration
664
+ * Motion Master knows when the profile has completed by comparing the demand with the actual value, taking into account the
665
+ * holding duration and the shape of the profile. Once the profile is considered complete, Motion Master will send a Quick Stop, but
666
+ * only if repeat is set to NO.
667
+ *
668
+ * The sine wave profile is generated upfront on Motion Master. Targets are computed for each master cycle (millisecond).
669
+ * Once the last computed target has been set, Motion Master will send a Quick Stop if repeat is set to NO.
670
+ *
671
+ * For all signal generators, Motion Master will check the requested target against the currently set software position limits
672
+ * and, if necessary, alter it while returning a warning.
673
+ *
674
+ * This request will turn the motor.
675
+ */
676
+ startSignalGenerator(props, requestTimeout, messageId) {
677
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
678
+ const startSignalGenerator = types_1.MotionMasterMessage.Request.StartSignalGenerator.create(Object.assign(Object.assign({}, props), { deviceAddress }));
679
+ const id = this.sendRequest({ startSignalGenerator }, messageId);
680
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('signalGenerator', requestTimeout, id));
681
+ }));
682
+ }
683
+ /**
684
+ * Stop signal generator.
685
+ *
686
+ * This request will send a Quick Stop command to the device.
687
+ */
688
+ stopSignalGenerator(props, requestTimeout, messageId) {
689
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
690
+ const stopSignalGenerator = types_1.MotionMasterMessage.Request.StopSignalGenerator.create(Object.assign(Object.assign({}, props), { deviceAddress }));
691
+ const id = this.sendRequest({ stopSignalGenerator }, messageId);
692
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('signalGenerator', requestTimeout, id));
693
+ }));
694
+ }
695
+ /**
696
+ * Start monitoring device parameter values.
697
+ *
698
+ * When monitoring is started, Motion Master will begin and continue reading values from the device
699
+ * and send them back to a client at the specified interval on the given topic.
700
+ *
701
+ * The request is made through the req/res socket {@link MotionMasterReqResSocket},
702
+ * but the status messages will arrive on the pub/sub socket {@link MotionMasterPubSubSocket}.
703
+ *
704
+ * The same monitoring can read both PDO and SDO values. PDO values will be updated on every Motion Master cycle (1ms).
705
+ * How fast SDO values will update depends on the number of SDOs being monitored. Motion Master reads the SDO values in a sequence,
706
+ * so, for example, if there are 10 SDOs and it takes 20ms to read a single SDO, it will take approximately 200ms for each SDO to get updated.
707
+ * IgH EtherCAT Master supports non-blocking SDO reading with a request to read and check the state of the request (one of not used, busy, failed, succeeded).
708
+ * A future version of Motion Master will likely leverage this and update SDO values out of order as each of the requests to update SDO succeeds.
709
+ * This should decrease the time it takes to read multiple SDOs.
710
+ * If a parameter is required to be updated more frequently, it should be mapped as a PDO.
711
+ *
712
+ * Firmware versions >=5.2 support SDO complete access, but it is currently not being supported in Motion Master.
713
+ * With SDO complete access, RECORD and ARRAY subitems can be read with a single request.
714
+ *
715
+ * The interval is given in microseconds.
716
+ *
717
+ * The topic should be unique. The previous monitoring will stop if the given topic is reused.
718
+ * Clients can easily stop one another's monitoring by using the same topic.
719
+ * Prefixing topics with the client id or some other unique identifier is suggested.
720
+ *
721
+ * When the monitoring request is received, Motion Master will mark all requested parameters as stale.
722
+ * The first time a parameter gets refreshed from a device, it'll be marked as not stale.
723
+ * Motion Master won't start publishing parameter values until all parameters are marked as not stale.
724
+ * This was done to satisfy the firmware test client applications.
725
+ *
726
+ * Stopping the ongoing monitoring is done through the req/res socket using
727
+ * the request message ID of the initial start monitoring message {@link stopMonitoringDeviceParameterValues}.
728
+ */
729
+ startMonitoringDeviceParameterValues(props, requestTimeout, messageId) {
730
+ const startMonitoringDeviceParameterValues = types_1.MotionMasterMessage.Request.StartMonitoringDeviceParameterValues.create(props);
731
+ const id = this.sendRequest({ startMonitoringDeviceParameterValues }, messageId);
732
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('monitoringParameterValues', requestTimeout, id));
733
+ }
734
+ /**
735
+ * Stop monitoring device parameter values.
736
+ *
737
+ * The Message ID used for {@link startMonitoringDeviceParameterValues} uniquely identifies a monitoring and is used for stopping it.
738
+ */
739
+ stopMonitoringDeviceParameterValues(props, messageId) {
740
+ const stopMonitoringDeviceParameterValues = types_1.MotionMasterMessage.Request.StopMonitoringDeviceParameterValues.create(props);
741
+ this.sendRequest({ stopMonitoringDeviceParameterValues }, messageId);
742
+ return rxjs_1.EMPTY;
743
+ }
744
+ /**
745
+ * Get the EtherCAT network state.
746
+ *
747
+ * Returns one of the EtherCAT network states: BOOT, INIT, PRE-OP, SAFE-OP, OP.
748
+ * The state is returned as an enum value: INIT: 1, PREOP: 2, BOOT: 3, SAFEOP: 4, OP: 5.
749
+ */
750
+ getEthercatNetworkState(props, requestTimeout, messageId) {
751
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
752
+ const getEthercatNetworkState = types_1.MotionMasterMessage.Request.GetEthercatNetworkState.create(Object.assign(Object.assign({}, props), { deviceAddress }));
753
+ const id = this.sendRequest({ getEthercatNetworkState }, messageId);
754
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('ethercatNetworkState', requestTimeout, id));
755
+ }));
756
+ }
757
+ /**
758
+ * Set the EtherCAT network state.
759
+ *
760
+ * Set the EtherCAT network state to one of: BOOT, INIT, PRE-OP, SAFE-OP, OP.
761
+ * The state is set by an enum value: INIT: 1, PREOP: 2, BOOT: 3, SAFEOP: 4, OP: 5.
762
+ *
763
+ * When switching from PRE-OP to OP, MM will reinitialize slaves to update the PDO mapping.
764
+ */
765
+ setEthercatNetworkState(props, requestTimeout, messageId) {
766
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
767
+ const setEthercatNetworkState = types_1.MotionMasterMessage.Request.SetEthercatNetworkState.create(Object.assign(Object.assign({}, props), { deviceAddress }));
768
+ const id = this.sendRequest({ setEthercatNetworkState }, messageId);
769
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('ethercatNetworkState', requestTimeout, id));
770
+ }));
771
+ }
772
+ /**
773
+ * @deprecated use {@link startCirculoEncoderNarrowAngleCalibrationProcedure} instead
774
+ */
775
+ startNarrowAngleCalibration(props, requestTimeout, messageId) {
776
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
777
+ const startNarrowAngleCalibration = types_1.MotionMasterMessage.Request.StartNarrowAngleCalibration.create(Object.assign(Object.assign({}, props), { deviceAddress }));
778
+ const id = this.sendRequest({ startNarrowAngleCalibration }, messageId);
779
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('narrowAngleCalibration', requestTimeout, id));
780
+ }));
781
+ }
782
+ /**
783
+ * Set system client timeout.
784
+ *
785
+ * This request will update the client timeout.
786
+ *
787
+ * The client timeout specifies how long Motion Master will wait for the client to send a ping or any other message before
788
+ * considering it gone and clearing the resources (stopping procedures and monitorings) related to that client.
789
+ *
790
+ * The default client timeout is 1000ms.
791
+ *
792
+ * Clients will typically change this once the socket connection is established.
793
+ */
794
+ setSystemClientTimeout(props, messageId) {
795
+ const setSystemClientTimeout = types_1.MotionMasterMessage.Request.SetSystemClientTimeout.create(props);
796
+ this.sendRequest({ setSystemClientTimeout }, messageId);
797
+ return rxjs_1.EMPTY;
798
+ }
799
+ /**
800
+ * Start system identification.
801
+ *
802
+ * This request will run the system identification procedure, which identifies the plant model.
803
+ *
804
+ * Pre-conditions: a configured motor and configured encoders.
805
+ *
806
+ * The chirp signal that the procedure generates only uses the torque controller.
807
+ * The chirp signal is generated on Motion Master, and then, on every master cycle (1ms), the target torque value is set on a device.
808
+ * Motion Master will send a Quick Stop to a device after the last target value is set.
809
+ *
810
+ * The request can fail for multiple reasons: the device is in the fault state, cannot set the op mode state,
811
+ * or the system identification fails because not enough data points have been collected.
812
+ * While running the chirp signal, the data points are collected, but due to potential communication lag, it can happen
813
+ * that the master doesn't collect enough data points. If more than 80% of data points are missing, the procedure will fail.
814
+ * If between 20%-80% of data points are missing, only a warning will be returned. The exact thresholds are subject to change.
815
+ *
816
+ * Before computing the plant model data based on the collected data points, Motion Master will interpolate the missing data points.
817
+ * Plant model data is then computed and written to the plant_model.csv file on the device flash memory.
818
+ * The plant model file is later used to compute the PID values using {@link computeAutoTuningGains} and {@link startFullAutoTuning}.
819
+ * It's also used indirectly in {@link startCoggingTorqueRecording} when auto-config is turned on.
820
+ * Motion Master will also update the integral limit values in 0x2011: Velocity controller and 0x2012: Position controller during this procedure.
821
+ *
822
+ * In future versions of the firmware, the chirp signal would be executed on the devices themselves, so no data points will be missing,
823
+ * and the system identification will be able to run at higher frequencies and with higher precision.
824
+ *
825
+ * This request will turn the motor.
826
+ */
827
+ startSystemIdentification(props, requestTimeout, messageId) {
828
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
829
+ const startSystemIdentification = types_1.MotionMasterMessage.Request.StartSystemIdentification.create(Object.assign(Object.assign({}, props), { deviceAddress }));
830
+ const id = this.sendRequest({ startSystemIdentification }, messageId);
831
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('systemIdentification', requestTimeout, id));
832
+ }));
833
+ }
834
+ /**
835
+ * Get Circulo encoder magnet distance.
836
+ *
837
+ * This feature is only available for firmwares >=v5. For older firmwares, Motion Master will return an error.
838
+ *
839
+ * Motion Master will read registers 0x2B and 0x2F on the specified encoder (by encoder ordinal), and then it will compute the magnet distance.
840
+ * The computation depends on the Circulo type, encoder port, and ring revision.
841
+ */
842
+ getCirculoEncoderMagnetDistance(props, requestTimeout, messageId) {
843
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
844
+ const getCirculoEncoderMagnetDistance = types_1.MotionMasterMessage.Request.GetCirculoEncoderMagnetDistance.create(Object.assign(Object.assign({}, props), { deviceAddress }));
845
+ const id = this.sendRequest({ getCirculoEncoderMagnetDistance }, messageId);
846
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('circuloEncoderMagnetDistance', requestTimeout, id));
847
+ }));
848
+ }
849
+ /**
850
+ * Start the Circulo encoder narrow-angle calibration procedure.
851
+ *
852
+ * This feature is only available on firmwares >=v5.
853
+ *
854
+ * Before starting the procedure, Motion Master will check and return an error if:
855
+ * - the firmware version is not >=v5
856
+ * - software position limits are set so that the required motor rotation is not possible
857
+ * - for SMM devices, the encoder source type is not set to None
858
+ * - the velocity resolution determined by the gear ratio, SI unit velocity, and feed constant is not high enough
859
+ *
860
+ * Before starting the procedure, Motion Master will do the following:
861
+ * - send OS command 14 (Ignore BiSS status bits) so that no firmware warnings or errors are raised during the procedure.
862
+ * - set certain encoder registers to their default values.
863
+ * - set the BiSS encoder to raw mode.
864
+ * - change the values of some device parameters: profile acceleration, deceleration, max motor speed, etc.
865
+ *
866
+ * The parameter values Motion Master sets depend on the type of the device (fetched from the hardware description file).
867
+ * Once the operation has completed, Motion Master will roll back all of the above parameters to their initial values.
868
+ *
869
+ * Procedure:
870
+ * - The procedure is run in velocity profile mode of operation.
871
+ * - The procedure will do multiple iterations until the encoder data is satisfactory.
872
+ * - The procedure can fail for various reasons: data out of range, motor too slow, bad data, internal error, etc.
873
+ * - The procedure will run a maximum of 13 calibration iterations, up to 2 mins in total.
874
+ * - One iteration turns the motor in one direction, and then the next one reverses the direction.
875
+ * - Each iteration will record encoder data into HRD files (4kHz) that contain the raw data from the specified encoder.
876
+ * - Motion Master will remove previous high-resolution data (HRD) files after each iteration. HRD streaming is configured/started using OS commands.
877
+ * - After each iteration, which lasts ~6 seconds, the recorded data in HRD files is read and passed to the iC House library.
878
+ * - The recorded data is sliced into 32-bit pairs of values of master and nonius track data. Motion Master creates two arrays out of this data and
879
+ * passes that to the iC House library which computes the new calibration parameters depending on encoder type sine and cosine offset, gain, etc.
880
+ * - The iterations are being repeated until the encoder (raw) data satisfies a certain threshold.
881
+ *
882
+ * The result of a successful calibration is setting multiple parameters into registers of the specified encoder.
883
+ *
884
+ * At the end of the procedure Motion Master will rotate the motor back to the starting position and run commutation offset detection.
885
+ * There will also be a final iteration that only measures the values of a (now) calibrated encoder for display.
886
+ *
887
+ * This request will turn the motor.
888
+ */
889
+ startCirculoEncoderNarrowAngleCalibrationProcedure(props, requestTimeout, messageId) {
890
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
891
+ const startCirculoEncoderNarrowAngleCalibrationProcedure = types_1.MotionMasterMessage.Request.StartCirculoEncoderNarrowAngleCalibrationProcedure.create(Object.assign(Object.assign({}, props), { deviceAddress }));
892
+ const id = this.sendRequest({ startCirculoEncoderNarrowAngleCalibrationProcedure }, messageId);
893
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('circuloEncoderNarrowAngleCalibrationProcedure', requestTimeout, id));
894
+ }));
895
+ }
896
+ /**
897
+ * Get device CiA402 state.
898
+ */
899
+ getDeviceCia402State(props, requestTimeout, messageId) {
900
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
901
+ const getDeviceCia402State = types_1.MotionMasterMessage.Request.GetDeviceCiA402State.create(Object.assign(Object.assign({}, props), { deviceAddress }));
902
+ const id = this.sendRequest({ getDeviceCia402State }, messageId);
903
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceCia402State', requestTimeout, id));
904
+ }));
905
+ }
906
+ /**
907
+ * Set device CiA402 state.
908
+ */
909
+ setDeviceCia402State(props, requestTimeout, messageId) {
910
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
911
+ const setDeviceCia402State = types_1.MotionMasterMessage.Request.SetDeviceCiA402State.create(Object.assign(Object.assign({}, props), { deviceAddress }));
912
+ const id = this.sendRequest({ setDeviceCia402State }, messageId);
913
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceCia402State', requestTimeout, id));
914
+ }));
915
+ }
916
+ /**
917
+ * Get system log.
918
+ *
919
+ * The system log represents the collected standard output from the Motion Master process.
920
+ *
921
+ * This request will return the entire system log, which can be a maximum of approximately 2MB.
922
+ *
923
+ * The log level can be set as an environmental variable when the Motion Master process is started.
924
+ * The log level can be selected in the OBLAC Drives Update Service expert mode before installing a release.
925
+ * The supported log levels are:
926
+ * - 6: Request content
927
+ * - 5: Request type
928
+ * - 4: Parameter value
929
+ * - 3: Monitoring
930
+ * - 2: Verbose 2
931
+ * - 1: Verbose 1
932
+ * - 0: Info
933
+ * - 1: Warnings
934
+ * - 2: Errors
935
+ * - 3: Fatal errors
936
+ */
937
+ getSystemLog(requestTimeout, messageId) {
938
+ const getSystemLog = types_1.MotionMasterMessage.Request.GetSystemVersion.create();
939
+ const id = this.sendRequest({ getSystemLog }, messageId);
940
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('systemLog', requestTimeout, id));
941
+ }
942
+ /**
943
+ * Start device SII restore.
944
+ *
945
+ * This request uses the EEPROM tool from SOEM to overwrite the SII portion of the EEPROM on a device.
946
+ *
947
+ * Motion Master will verify the provided SII file before writing it to EEPROM. It will:
948
+ * - Check the file content length,
949
+ * - Compute CRC from a part of the SII file content and compare it to the CRC inside the file,
950
+ * - Check the SII category types in the category header.
951
+ *
952
+ * Unlike IgH EtherCAT Master, which identifies interfaces by MAC address, the SOEM tool requires an adapter name.
953
+ * Before calling the SOEM tool, Motion Master will find the adapter name by looking into /sys/class/net.
954
+ *
955
+ * The SOEM tool binary is delivered as a part of the Motion Master Docker image.
956
+ */
957
+ startDeviceSiiRestore(props, requestTimeout, messageId) {
958
+ const startDeviceSiiRestore = types_1.MotionMasterMessage.Request.StartDeviceSiiRestore.create(props);
959
+ const id = this.sendRequest({ startDeviceSiiRestore }, messageId);
960
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceSiiRestore', requestTimeout, id));
961
+ }
962
+ /**
963
+ * Start open-loop field control.
964
+ *
965
+ * The precondition for running the open-loop field control is to have a configured motor.
966
+ * No encoder configuration or offset detection is required.
967
+ *
968
+ * Open-loop field control is started by changing the Modes of operation (0x6060) parameter value to -3 (Open-loop field mode) and
969
+ * setting the CiA402 state to Ready to switch on.
970
+ * After that, Motion Master will run several OS commands that configure the open-loop field control parameters:
971
+ * - Start angle [milliradian]
972
+ * - End angle [milliradian]
973
+ * - Max rotational speed [radian/s]
974
+ * - Rotational acceleration [radian/s^2]
975
+ * - Start length [permille of rated torque]
976
+ * - End length [permille of rated torque]
977
+ * - Length speed [permille of rated torque]
978
+ *
979
+ * After the configuration, Motion Master will switch the device to Operation enabled CiA402 state, and the profile will start.
980
+ *
981
+ * While the open-loop field control is running, Motion Master will monitor the target reached bit in the statusword.
982
+ * When the target reached bit becomes 1, Motion Master will stop the procedure by changing the CiA402 state to Switched on.
983
+ *
984
+ * Motion Master will return an error if the device goes out of the Operation enabled state before the target reached bit becomes 1.
985
+ * There is no timeout on the Motion Master side.
986
+ *
987
+ * This request will turn the motor.
988
+ */
989
+ startOpenLoopFieldControl(props, requestTimeout, messageId) {
990
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
991
+ const startOpenLoopFieldControl = types_1.MotionMasterMessage.Request.StartOpenLoopFieldControl.create(Object.assign(Object.assign({}, props), { deviceAddress }));
992
+ const id = this.sendRequest({ startOpenLoopFieldControl }, messageId);
993
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('openLoopFieldControl', requestTimeout, id));
994
+ }));
995
+ }
996
+ /**
997
+ * @deprecated use {@link startFullAutoTuning} instead
998
+ */
999
+ computeFullAutoTuningGains(props, requestTimeout, messageId) {
1000
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1001
+ const computeFullAutoTuningGains = types_1.MotionMasterMessage.Request.ComputeFullAutoTuningGains.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1002
+ const id = this.sendRequest({ computeFullAutoTuningGains }, messageId);
1003
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('fullAutoTuning', requestTimeout, id));
1004
+ }));
1005
+ }
1006
+ /**
1007
+ * Start full auto-tuning.
1008
+ *
1009
+ * The preconditions for running the full auto-tuning are a properly configured motor, brake, and encoders,
1010
+ * and having executed the offset detection and the system identification procedures.
1011
+ *
1012
+ * In order to successfully run the full auto-tuning procedure, the motor must not rotate, and the device must not be in CiA402 Operation enabled.
1013
+ *
1014
+ * Motion Master will try to read the plant_model.csv file, and if it doesn't exist, it will return an error.
1015
+ *
1016
+ * Before the operation starts, Motion Master will measure the actual velocity for 500ms at a standstill to determine the encoder noise.
1017
+ * The noise is then sent, along with some other parameters (velocity feedback filter, feed forward, DC link voltage, firmware version, and similar)
1018
+ * and data to a proprietary computation algorithm.
1019
+ * ADC current ratio, which is used in the computation script, is determined by the firmware version and the device hardware ID (e.g., 9500, 9501, 8501...).
1020
+ *
1021
+ * Full auto-tuning will compute the PID gains, and Motion Master will update the parameter values in the velocity or position controller, depending on the request.
1022
+ *
1023
+ * Motion Master will return the damping ratio, settling time, and bandwidth values when the procedure completes. UI will use these values to update the sliders in the Tuning screen.
1024
+ *
1025
+ * This procedure can be stopped with {@link stopFullAutoTuning}.
1026
+ */
1027
+ startFullAutoTuning(props, requestTimeout, messageId) {
1028
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1029
+ const startFullAutoTuning = types_1.MotionMasterMessage.Request.StartFullAutoTuning.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1030
+ const id = this.sendRequest({ startFullAutoTuning }, messageId);
1031
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('fullAutoTuning', requestTimeout, id));
1032
+ }));
1033
+ }
1034
+ /**
1035
+ * Stop full auto-tuning.
1036
+ *
1037
+ * Motion Master has only one exit point for this procedure, which is after the computation algorithm ends.
1038
+ * This means that, once stopped, the procedure will compute the gains, but Motion Master will not update the controller parameters.
1039
+ * A previously started full auto-tuning request will complete with an aborted error.
1040
+ */
1041
+ stopFullAutoTuning(props, requestTimeout, messageId) {
1042
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1043
+ const stopFullAutoTuning = types_1.MotionMasterMessage.Request.StopFullAutoTuning.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1044
+ const id = this.sendRequest({ stopFullAutoTuning }, messageId);
1045
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('fullAutoTuning', requestTimeout, id), (0, operators_1.map)((status) => {
1046
+ var _a, _b;
1047
+ if (((_a = status.success) === null || _a === void 0 ? void 0 : _a.code) === types_1.MotionMasterMessage.Status.FullAutoTuning.Success.Code.POSITION_STOPPED ||
1048
+ ((_b = status.success) === null || _b === void 0 ? void 0 : _b.code) === types_1.MotionMasterMessage.Status.FullAutoTuning.Success.Code.VELOCITY_STOPPED) {
1049
+ // Full auto tuning status is shared between start and stop.
1050
+ // When full auto tuning has stopped this request is considered successful.
1051
+ status.request = 'succeeded';
1052
+ }
1053
+ return status;
1054
+ }));
1055
+ }));
1056
+ }
1057
+ /**
1058
+ * Start Circulo encoder configuration.
1059
+ *
1060
+ * The preconditions for this procedure are to have a Circulo device with internal encoders and firmware >=v5.
1061
+ *
1062
+ * This procedure will set various internal encoder registers based on the hardware description, Circulo device type, and the encoder ordinal.
1063
+ *
1064
+ * Before updating the registers, Motion Master will send an OS command to ignore the BiSS status bits.
1065
+ * Once the procedure is done, the value of this register will be reverted.
1066
+ *
1067
+ * Depending on the setup, this procedure can take up to 30 seconds.
1068
+ *
1069
+ * This procedure can fail if it's already running, if it's not supported because of the firmware version,
1070
+ * or if the hardware description file is missing.
1071
+ *
1072
+ * While this procedure runs, Motion Master will report its progress.
1073
+ */
1074
+ startCirculoEncoderConfiguration(props, requestTimeout, messageId) {
1075
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1076
+ const startCirculoEncoderConfiguration = types_1.MotionMasterMessage.Request.StartCirculoEncoderConfiguration.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1077
+ const id = this.sendRequest({ startCirculoEncoderConfiguration }, messageId);
1078
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('circuloEncoderConfiguration', requestTimeout, id));
1079
+ }));
1080
+ }
1081
+ /**
1082
+ * @deprecated use {@link stopDevice} instead
1083
+ */
1084
+ stopCirculoEncoderNarrowAngleCalibrationProcedure(props, requestTimeout, messageId) {
1085
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1086
+ const stopCirculoEncoderNarrowAngleCalibrationProcedure = types_1.MotionMasterMessage.Request.StopCirculoEncoderNarrowAngleCalibrationProcedure.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1087
+ const id = this.sendRequest({ stopCirculoEncoderNarrowAngleCalibrationProcedure }, messageId);
1088
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('circuloEncoderNarrowAngleCalibrationProcedure', requestTimeout, id));
1089
+ }));
1090
+ }
1091
+ /**
1092
+ * Start OS command.
1093
+ *
1094
+ * This request will execute an OS command and return the result as a byte array.
1095
+ *
1096
+ * Motion Master will check if an OS command is already running and use a mutex to ensure only one OS command runs at a time.
1097
+ *
1098
+ * This request can fail if the requested OS command is not supported.
1099
+ *
1100
+ * Motion Master will check if the OS command has failed and return an error.
1101
+ *
1102
+ * The OS command can timeout if it takes longer than the provided timeout in milliseconds, in which case Motion Master will return an error.
1103
+ */
1104
+ startOsCommand(props, requestTimeout, messageId) {
1105
+ return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1106
+ const startOsCommand = types_1.MotionMasterMessage.Request.StartOsCommand.create(Object.assign(Object.assign({}, props), { deviceAddress }));
1107
+ const id = this.sendRequest({ startOsCommand }, messageId);
1108
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('osCommand', requestTimeout, id));
1109
+ }));
1110
+ }
1111
+ /**
1112
+ * Send request.
1113
+ *
1114
+ * Create a Motion Master message with the provided request, optionally generate a unique message ID,
1115
+ * and then send the message through the socket.
1116
+ *
1117
+ * @returns the message ID
1118
+ */
1119
+ sendRequest(request, messageId) {
1120
+ const id = messageId !== null && messageId !== void 0 ? messageId : (0, uuid_1.v4)();
1121
+ const message = types_1.MotionMasterMessage.create({ request, id });
1122
+ this.socket.send(message);
1123
+ return id;
1124
+ }
1125
+ //
1126
+ // Helpers
1127
+ //
1128
+ /**
1129
+ * Get the device serial number by device address.
1130
+ *
1131
+ * This function does not make any requests; instead, it utilizes the previously mapped device serial number to an instance of a device.
1132
+ */
1133
+ getSerialNumberByDeviceAddress(deviceAddress) {
1134
+ for (let [serialNumber, device] of this.deviceMap.entries()) {
1135
+ if (device.deviceAddress === deviceAddress) {
1136
+ return serialNumber;
1137
+ }
1138
+ }
1139
+ return;
1140
+ }
1141
+ /**
1142
+ * Resolve a device.
1143
+ *
1144
+ * Resolves the device object by its reference, which can be a position, serial number, or device address.
1145
+ *
1146
+ * This function will make requests to retrieve the list of devices and the .hardware_description file for each device,
1147
+ * but only if it has not been previously retrieved for this session.
1148
+ *
1149
+ * @throws errors if the device reference is invalid
1150
+ */
1151
+ resolveDevice(deviceRef, requestTimeout = 20000) {
1152
+ const deviceRefObj = (typeof deviceRef === 'object') ? deviceRef : (0, device_1.makeDeviceRefObj)(deviceRef);
1153
+ if (!(0, device_1.isValidDeviceRefObj)(deviceRefObj)) {
1154
+ return (0, rxjs_1.throwError)(() => new Error(`Device ref object is not valid ${JSON.stringify(deviceRefObj)}`));
1155
+ }
1156
+ const device = this.getCachedDeviceByDeviceRefObj(deviceRefObj);
1157
+ if (device) {
1158
+ return (0, rxjs_1.of)(device);
1159
+ }
1160
+ else {
1161
+ return this.getDevices(requestTimeout).pipe((0, rxjs_1.tap)((devices) => {
1162
+ this.deviceMap.clear();
1163
+ devices.forEach((device) => {
1164
+ if (!device.hardwareDescription) {
1165
+ throw new Error(`Device ${device.position} has no hardware description`);
1166
+ }
1167
+ const deviceSerialNumber = (0, hardware_description_1.getSerialNumberFromHardwareDescription)(device.hardwareDescription);
1168
+ this.deviceMap.set(deviceSerialNumber, device);
1169
+ });
1170
+ }), (0, operators_1.map)(() => {
1171
+ const device = this.getCachedDeviceByDeviceRefObj(deviceRefObj);
1172
+ if (!device) {
1173
+ throw new Error(`Device is not found by deviceRefObj ${JSON.stringify(deviceRefObj)}`);
1174
+ }
1175
+ return device;
1176
+ }));
1177
+ }
1178
+ }
1179
+ /**
1180
+ * Resolve device address.
1181
+ *
1182
+ * This method should return the device address by a device reference object,
1183
+ * or throw an error if the device address cannot be found.
1184
+ *
1185
+ * Refer to the description of this class for more information.
1186
+ */
1187
+ resolveDeviceAddress(deviceRef, requestTimeout = 60000) {
1188
+ const deviceRefObj = (typeof deviceRef === 'object') ? deviceRef : (0, device_1.makeDeviceRefObj)(deviceRef);
1189
+ return this.resolveDevice(deviceRefObj, requestTimeout).pipe((0, operators_1.map)((device) => device.deviceAddress));
1190
+ }
1191
+ /**
1192
+ * Get devices.
1193
+ *
1194
+ * Retrieve device information from Motion Master.
1195
+ *
1196
+ * For each device in the list, read its .hardware_description file and assign its content to the device.
1197
+ */
1198
+ getDevices(requestTimeout = 10000) {
1199
+ return this.getDeviceInfo(requestTimeout).pipe((0, operators_1.mergeMap)((deviceInfo) => {
1200
+ var _a;
1201
+ const devices = ((_a = deviceInfo.devices) !== null && _a !== void 0 ? _a : []);
1202
+ return (0, rxjs_1.forkJoin)(devices.map((device) => {
1203
+ const deviceAddress = device.deviceAddress;
1204
+ const getDeviceFile = types_1.MotionMasterMessage.Request.GetDeviceFile
1205
+ .create({ name: '.hardware_description', deviceAddress });
1206
+ const id = this.sendRequest({ getDeviceFile }, (0, uuid_1.v4)());
1207
+ return this.socket.message$.pipe((0, operators_2.transformMotionMasterMessageToStatus)('deviceFile', requestTimeout, id), (0, operators_1.map)((deviceFile) => {
1208
+ const status = this.fixProductionScrewUp(deviceFile);
1209
+ if (status.content) {
1210
+ const data = (0, util_1.decodeTextContent)(status.content);
1211
+ const hardwareDescription = JSON.parse(data);
1212
+ const id = (0, hardware_description_1.getSerialNumberFromHardwareDescription)(hardwareDescription);
1213
+ return Object.assign(Object.assign({}, device), { id, hardwareDescription });
1214
+ }
1215
+ return Object.assign({}, device);
1216
+ }));
1217
+ }));
1218
+ }));
1219
+ }
1220
+ getParameterValue(a, b, c, d, e, f) {
1221
+ try {
1222
+ const { deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId } = this.resolveGetDeviceParameterValueArguments(a, b, c, d, e, f);
1223
+ return this.createGetParameterValueObservable(deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId);
1224
+ }
1225
+ catch (err) {
1226
+ return (0, rxjs_1.throwError)(() => err);
1227
+ }
1228
+ }
1229
+ upload(a, b, c, d, e, f) {
1230
+ const { deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId } = this.resolveGetDeviceParameterValueArguments(a, b, c, d, e, f);
1231
+ return (0, rxjs_1.lastValueFrom)(this.createGetParameterValueObservable(deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId));
1232
+ }
1233
+ createGetParameterValueObservable(deviceRefObj, index, subindex, loadFromCache = false, requestTimeout = 1000, messageId) {
1234
+ return (0, rxjs_1.defer)(() => {
1235
+ return this.getDeviceParameterValues(Object.assign(Object.assign({}, deviceRefObj), { parameters: [{ index, subindex, loadFromCache }] }), requestTimeout, messageId).pipe((0, operators_1.map)(({ parameterValues }) => {
1236
+ var _a, _b, _c;
1237
+ const parameterValue = parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0];
1238
+ if (parameterValue === null || parameterValue === void 0 ? void 0 : parameterValue.success) {
1239
+ const typeValue = parameterValue.typeValue;
1240
+ if (typeValue) {
1241
+ return parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0][typeValue];
1242
+ }
1243
+ }
1244
+ const props = Object.assign(Object.assign({}, deviceRefObj), { index, subindex, loadFromCache, requestTimeout, messageId });
1245
+ const errorCodeName = types_1.MotionMasterMessage.Status.DeviceParameterValues.ParameterValue.Error.Code[(_c = (_b = (_a = parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0]) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.code) !== null && _c !== void 0 ? _c : 0];
1246
+ throw new Error(`Failed to get parameter value (${errorCodeName}) for ${JSON.stringify(props)}`);
1247
+ }));
1248
+ });
1249
+ }
1250
+ resolveGetDeviceParameterValueArguments(a, b, c, d, e, f) {
1251
+ let deviceSerialNumber;
1252
+ let index;
1253
+ let subindex = 0;
1254
+ let loadFromCache = false;
1255
+ let requestTimeout = 1000;
1256
+ let deviceRefObj;
1257
+ let messageId;
1258
+ if (typeof b === 'number' && typeof c === 'number') { // index, subindex, e.g. 0x6060, 1
1259
+ deviceRefObj = (0, device_1.makeDeviceRefObj)(a);
1260
+ index = b;
1261
+ subindex = c;
1262
+ if (typeof d === 'boolean') {
1263
+ loadFromCache = d;
1264
+ }
1265
+ if (typeof e === 'number') {
1266
+ requestTimeout = e;
1267
+ }
1268
+ if (typeof f === 'string') {
1269
+ messageId = f;
1270
+ }
1271
+ }
1272
+ else if (typeof a === 'string') { // device parameter id, e.g. "0x2110:1A.8502-03-0001353-2115"
1273
+ [deviceSerialNumber, index, subindex] = (0, device_parameter_1.splitDeviceParameterId)(a);
1274
+ deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceSerialNumber);
1275
+ if (typeof b === 'boolean') {
1276
+ loadFromCache = b;
1277
+ }
1278
+ if (typeof c === 'number') {
1279
+ requestTimeout = c;
1280
+ }
1281
+ if (typeof d === 'string') {
1282
+ messageId = d;
1283
+ }
1284
+ }
1285
+ if (!index) {
1286
+ throw new Error('Device parameter index is not provided!');
1287
+ }
1288
+ if (!deviceRefObj) {
1289
+ throw new Error('Device address, position or serial number is not provided!');
1290
+ }
1291
+ return { deviceRefObj, index, subindex, loadFromCache, requestTimeout, messageId };
1292
+ }
1293
+ getParameterValues(ids, loadFromCache = false, requestTimeout = 9000, messageId) {
1294
+ return this.resolveGetParameterValuesIds(ids).pipe((0, operators_1.mergeMap)((tuples) => {
1295
+ var _a;
1296
+ const collection = [];
1297
+ for (const [deviceAddress, index, subindex] of tuples) {
1298
+ let item = collection.find(c => c.deviceAddress === deviceAddress);
1299
+ if (!item) {
1300
+ item = { deviceAddress, parameters: [], sendProgress: false };
1301
+ collection.push(item);
1302
+ }
1303
+ (_a = item.parameters) === null || _a === void 0 ? void 0 : _a.push({ index, subindex, loadFromCache });
1304
+ }
1305
+ return this.getMultiDeviceParameterValues({ collection }, requestTimeout, messageId).pipe((0, operators_1.map)((multiDeviceParameterValues) => {
1306
+ if (!(multiDeviceParameterValues === null || multiDeviceParameterValues === void 0 ? void 0 : multiDeviceParameterValues.collection)) {
1307
+ throw new Error(`Multi device parameter values collection is empty`);
1308
+ }
1309
+ return multiDeviceParameterValues.collection.map(({ parameterValues, deviceAddress }) => {
1310
+ if (!parameterValues) {
1311
+ throw new Error(`No parameterValues for device ${deviceAddress}`);
1312
+ }
1313
+ return parameterValues.map((parameterValue) => {
1314
+ const value = parameterValue;
1315
+ const typeValue = value === null || value === void 0 ? void 0 : value.typeValue;
1316
+ if (typeValue) {
1317
+ return parameterValue[typeValue];
1318
+ }
1319
+ throw new Error(`No type value for parameter ${(0, parameter_1.makeParameterId)(value === null || value === void 0 ? void 0 : value.index, value === null || value === void 0 ? void 0 : value.subindex)}`);
1320
+ });
1321
+ }).flat();
1322
+ }));
1323
+ }));
1324
+ }
1325
+ uploadMany(ids, loadFromCache = false, requestTimeout = 9000, messageId) {
1326
+ return (0, rxjs_1.lastValueFrom)(this.getParameterValues(ids, loadFromCache, requestTimeout, messageId));
1327
+ }
1328
+ resolveGetParameterValuesIds(ids) {
1329
+ if (ids.length === 0) {
1330
+ return (0, rxjs_1.throwError)(() => new Error('The provided array of device parameter ids or addresses is empty!'));
1331
+ }
1332
+ let refs;
1333
+ if ((0, types_1.isArrayOfDeviceSerialNumbers)(ids)) {
1334
+ refs = ids.map(id => (0, device_parameter_1.splitDeviceParameterId)(id));
1335
+ }
1336
+ else {
1337
+ refs = ids;
1338
+ }
1339
+ return (0, rxjs_1.defer)(() => {
1340
+ return (0, rxjs_1.forkJoin)(refs.map(([deviceRef, index, subindex]) => {
1341
+ return this.resolveDeviceAddress((0, device_1.makeDeviceRefObj)(deviceRef)).pipe((0, operators_1.map)((deviceAddress) => ([deviceAddress, index, subindex])));
1342
+ }));
1343
+ });
1344
+ }
1345
+ setParameterValue(a, b, c, d, e, f, g) {
1346
+ try {
1347
+ const { deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId } = this.resolveSetDeviceParameterValueArguments(a, b, c, d, e, f, g);
1348
+ return this.createSetParameterValueObservable(deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId);
1349
+ }
1350
+ catch (err) {
1351
+ return (0, rxjs_1.throwError)(() => err);
1352
+ }
1353
+ }
1354
+ download(a, b, c, d, e, f, g) {
1355
+ const { deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId } = this.resolveSetDeviceParameterValueArguments(a, b, c, d, e, f, g);
1356
+ return (0, rxjs_1.lastValueFrom)(this.createSetParameterValueObservable(deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId));
1357
+ }
1358
+ createSetParameterValueObservable(deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout = 1000, messageId) {
1359
+ return (0, rxjs_1.defer)(() => {
1360
+ return this.resolveDeviceParameterTypeValueKey(deviceRefObj, index, subindex, valueTypeKey).pipe((0, operators_1.mergeMap)((valueTypeKey) => {
1361
+ const parameterValue = { index, subindex, [valueTypeKey]: value };
1362
+ return this.setDeviceParameterValues(Object.assign(Object.assign({}, deviceRefObj), { parameterValues: [parameterValue] }), requestTimeout, messageId).pipe((0, operators_1.map)(({ parameterValues }) => {
1363
+ var _a, _b, _c;
1364
+ const parameterValue = parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0];
1365
+ if (parameterValue === null || parameterValue === void 0 ? void 0 : parameterValue.success) {
1366
+ const typeValue = parameterValue.typeValue;
1367
+ if (typeValue) {
1368
+ return;
1369
+ }
1370
+ }
1371
+ const props = Object.assign(Object.assign({}, deviceRefObj), { index, subindex, value, valueTypeKey, requestTimeout, messageId });
1372
+ const errorCodeName = types_1.MotionMasterMessage.Status.DeviceParameterValues.ParameterValue.Error.Code[(_c = (_b = (_a = parameterValues === null || parameterValues === void 0 ? void 0 : parameterValues[0]) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.code) !== null && _c !== void 0 ? _c : 0];
1373
+ throw new Error(`Failed to set parameter value (${errorCodeName}) for ${JSON.stringify(props)}`);
1374
+ }));
1375
+ }));
1376
+ });
1377
+ }
1378
+ /**
1379
+ * Resolve set device parameter value arguments.
1380
+ */
1381
+ resolveSetDeviceParameterValueArguments(a, b, c, d, e, f, g) {
1382
+ let deviceSerialNumber;
1383
+ let index;
1384
+ let subindex = 0;
1385
+ let value = 0;
1386
+ let valueTypeKey;
1387
+ let requestTimeout = 1000;
1388
+ let deviceRefObj;
1389
+ let messageId;
1390
+ if (typeof b === 'number' && typeof c === 'number') { // index, subindex, e.g. 0x6060, 1
1391
+ deviceRefObj = (0, device_1.makeDeviceRefObj)(a);
1392
+ index = b;
1393
+ subindex = c;
1394
+ if (d !== undefined) {
1395
+ value = d;
1396
+ }
1397
+ valueTypeKey = e;
1398
+ if (typeof f === 'number') {
1399
+ requestTimeout = f;
1400
+ }
1401
+ messageId = g;
1402
+ }
1403
+ else if (typeof a === 'string') { // device parameter id, e.g. "0x2110:1A.8502-03-0001353-2115"
1404
+ [deviceSerialNumber, index, subindex] = (0, device_parameter_1.splitDeviceParameterId)(a);
1405
+ deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceSerialNumber);
1406
+ value = b;
1407
+ if (typeof c !== 'number') {
1408
+ valueTypeKey = c;
1409
+ }
1410
+ if (typeof d === 'number') {
1411
+ requestTimeout = d;
1412
+ }
1413
+ messageId = e;
1414
+ }
1415
+ if (!index) {
1416
+ throw new Error('Device parameter index is not provided!');
1417
+ }
1418
+ if (!deviceRefObj) {
1419
+ throw new Error('Device address, position or serial number is not provided!');
1420
+ }
1421
+ return { deviceRefObj, index, subindex, value, valueTypeKey, requestTimeout, messageId };
1422
+ }
1423
+ setParameterValues(ids, requestTimeout = 9000, messageId) {
1424
+ if (ids.length === 0) {
1425
+ return (0, rxjs_1.throwError)(() => new Error('The provided array of device parameter ids or addresses is empty!'));
1426
+ }
1427
+ let refs;
1428
+ if ((0, types_1.isObjectOfDeviceSerialNumberToValues)(ids)) {
1429
+ refs = Object.entries(ids).map(([id, value]) => ([...(0, device_parameter_1.splitDeviceParameterId)(id), value]));
1430
+ }
1431
+ else {
1432
+ refs = ids;
1433
+ }
1434
+ return (0, rxjs_1.defer)(() => {
1435
+ return (0, rxjs_1.forkJoin)(refs.map(([deviceRef, index, subindex, value, typeValueKey]) => {
1436
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1437
+ return this.resolveDeviceAddress(deviceRefObj).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1438
+ return this.resolveDeviceParameterTypeValueKey(deviceRefObj, index, subindex, typeValueKey).pipe((0, operators_1.map)((typeValueKey) => ([deviceAddress, index, subindex, value, typeValueKey])));
1439
+ }));
1440
+ })).pipe((0, operators_1.mergeMap)((tuples) => {
1441
+ var _a;
1442
+ const collection = [];
1443
+ for (const [deviceAddress, index, subindex, value, typeValueKey] of tuples) {
1444
+ let item = collection.find(c => c.deviceAddress === deviceAddress);
1445
+ if (!item) {
1446
+ item = { deviceAddress, parameterValues: [] };
1447
+ collection.push(item);
1448
+ }
1449
+ (_a = item.parameterValues) === null || _a === void 0 ? void 0 : _a.push({ index, subindex, [typeValueKey]: value });
1450
+ }
1451
+ return this.setMultiDeviceParameterValues({ collection }, requestTimeout, messageId).pipe((0, operators_1.map)((multiDeviceParameterValues) => {
1452
+ var _a;
1453
+ (_a = multiDeviceParameterValues.collection) === null || _a === void 0 ? void 0 : _a.map(({ parameterValues, deviceAddress }) => {
1454
+ if (!parameterValues) {
1455
+ throw new Error(`No parameterValues for device ${deviceAddress}`);
1456
+ }
1457
+ });
1458
+ }));
1459
+ }));
1460
+ });
1461
+ }
1462
+ downloadMany(ids, requestTimeout = 9000, messageId) {
1463
+ return (0, rxjs_1.lastValueFrom)(this.setParameterValues(ids, requestTimeout, messageId));
1464
+ }
1465
+ resolveDeviceParameterTypeValueKey(deviceRefObj, index, subindex, typeValueKey) {
1466
+ if (typeValueKey) {
1467
+ return (0, rxjs_1.of)(typeValueKey);
1468
+ }
1469
+ return this.resolveDeviceParameterInfoMap(deviceRefObj).pipe((0, operators_1.map)((infoMap) => {
1470
+ const parameterId = (0, parameter_1.makeParameterId)(index, subindex);
1471
+ const infoParameter = infoMap.get(parameterId);
1472
+ if (!(infoParameter === null || infoParameter === void 0 ? void 0 : infoParameter.typeValueKey)) {
1473
+ throw new Error(`No type value key is set for device ${JSON.stringify(deviceRefObj)} ${parameterId}`);
1474
+ }
1475
+ return infoParameter.typeValueKey;
1476
+ }));
1477
+ }
1478
+ resolveDeviceParameterInfoMap(deviceRef, requestTimeout = 5000) {
1479
+ return this.resolveDeviceAddress(deviceRef).pipe((0, operators_1.mergeMap)((deviceAddress) => {
1480
+ let infoMap = this.deviceParameterInfoMap.get(deviceAddress);
1481
+ if (infoMap) {
1482
+ return (0, rxjs_1.of)(infoMap);
1483
+ }
1484
+ return this.getDeviceParameterInfo({ deviceAddress }, requestTimeout).pipe((0, operators_1.map)(({ parameters }) => {
1485
+ if (!parameters) {
1486
+ throw new Error(`Device parameter info has no parameters ${deviceAddress}`);
1487
+ }
1488
+ infoMap = new Map();
1489
+ this.deviceParameterInfoMap.set(deviceAddress, infoMap);
1490
+ for (const parameter of parameters) {
1491
+ if (parameter.valueType) {
1492
+ const typeValue = parameter_1.valueTypeToParameterTypeValueKeyMap.get(parameter.valueType);
1493
+ if (!typeValue) {
1494
+ throw new Error(`No type value for device ${deviceAddress} in resolveDeviceParameterInfoMap`);
1495
+ }
1496
+ const serialNumber = this.getSerialNumberByDeviceAddress(deviceAddress);
1497
+ if (!serialNumber) {
1498
+ throw new Error(`No serial number for device ${deviceAddress} in resolveDeviceParameterInfoMap`);
1499
+ }
1500
+ const id = (0, device_parameter_1.makeDeviceParameterId)(serialNumber, parameter.index, parameter.subindex);
1501
+ const key = (0, parameter_1.makeParameterId)(parameter);
1502
+ infoMap.set(key, Object.assign(Object.assign({}, parameter), { id, index: parameter.index, subindex: parameter.subindex, name: parameter.name, typeValueKey: typeValue }));
1503
+ }
1504
+ }
1505
+ return infoMap;
1506
+ }));
1507
+ }));
1508
+ }
1509
+ /**
1510
+ * Get device parameters.
1511
+ */
1512
+ getDeviceParameters(props, requestTimeout = 30000) {
1513
+ return this.resolveDevice(props).pipe((0, operators_1.mergeMap)((device) => {
1514
+ return this.getDeviceParameterInfo({ deviceAddress: device.deviceAddress }, requestTimeout).pipe((0, operators_1.mergeMap)(deviceParameterInfoStatus => {
1515
+ var _a;
1516
+ const { loadFromCache, sendProgress } = props;
1517
+ const parameters = (_a = deviceParameterInfoStatus.parameters) === null || _a === void 0 ? void 0 : _a.map(p => { var _a, _b; return ({ index: (_a = p.index) !== null && _a !== void 0 ? _a : 0, subindex: (_b = p.subindex) !== null && _b !== void 0 ? _b : 0, loadFromCache }); });
1518
+ return this.getDeviceParameterValues({ deviceAddress: device.deviceAddress, sendProgress, parameters }, requestTimeout).pipe((0, operators_1.map)(deviceParameterValuesStatus => {
1519
+ var _a, _b;
1520
+ if (!deviceParameterValuesStatus.parameterValues) {
1521
+ throw new Error('Device parameter values is empty');
1522
+ }
1523
+ const deviceParameters = deviceParameterValuesStatus.parameterValues.map((value, i) => {
1524
+ var _a, _b, _c;
1525
+ const index = (_a = value.index) !== null && _a !== void 0 ? _a : 0;
1526
+ const subindex = (_b = value.subindex) !== null && _b !== void 0 ? _b : 0;
1527
+ const id = (0, device_parameter_1.makeDeviceParameterId)(device.id, index, subindex);
1528
+ return (Object.assign(Object.assign(Object.assign({ id }, value), (_c = deviceParameterInfoStatus.parameters) === null || _c === void 0 ? void 0 : _c[i]), { index, subindex, value: (0, parameter_1.getParameterValue)(value) }));
1529
+ });
1530
+ const progress = sendProgress ? (_b = (_a = deviceParameterValuesStatus === null || deviceParameterValuesStatus === void 0 ? void 0 : deviceParameterValuesStatus.progress) === null || _a === void 0 ? void 0 : _a.percentage) !== null && _b !== void 0 ? _b : 0 : 100;
1531
+ return { parameters: deviceParameters, progress };
1532
+ }));
1533
+ }));
1534
+ }));
1535
+ }
1536
+ /**
1537
+ * Get files.
1538
+ */
1539
+ getFiles(deviceRef, requestTimeout = 30000, messageId) {
1540
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1541
+ return this.getDeviceFileList(deviceRefObj, requestTimeout, messageId).pipe((0, operators_1.map)((status) => { var _a, _b; return (_b = (_a = status.fileList) === null || _a === void 0 ? void 0 : _a.files) !== null && _b !== void 0 ? _b : []; }));
1542
+ }
1543
+ /**
1544
+ * Get file.
1545
+ */
1546
+ getFile(deviceRef, name, requestTimeout = 5000, messageId) {
1547
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1548
+ return this.getDeviceFile(Object.assign(Object.assign({}, deviceRefObj), { name }), requestTimeout, messageId).pipe((0, operators_1.map)(({ content }) => content));
1549
+ }
1550
+ /**
1551
+ * Get decoded file.
1552
+ *
1553
+ * Content of a device file is returned as a binary data buffer.
1554
+ * This functions tries to decode the content as a text in utf-8 character encoding.
1555
+ */
1556
+ getDecodedFile(deviceRef, name, requestTimeout = 2000, messageId) {
1557
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1558
+ return this.getDeviceFile(Object.assign(Object.assign({}, deviceRefObj), { name }), requestTimeout, messageId).pipe((0, operators_1.map)(({ content }) => {
1559
+ if (content) {
1560
+ return (0, util_1.decodeTextContent)(content);
1561
+ }
1562
+ throw new Error(`Device file ${name} content for device ${deviceRef} is undefined!}`);
1563
+ }));
1564
+ }
1565
+ /**
1566
+ * Unlock protected files.
1567
+ *
1568
+ * This will enable the deletion of protected files.
1569
+ * Protected files begin with a period, such as '.hardware_description'.
1570
+ */
1571
+ unlockProtectedFiles(deviceRef, requestTimeout = 5000, messageId) {
1572
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1573
+ return this.getDeviceFile(Object.assign(Object.assign({}, deviceRefObj), { name: 'fs-stackunlock=DD1317' }), requestTimeout, messageId);
1574
+ }
1575
+ /**
1576
+ * Delete file.
1577
+ */
1578
+ deleteFile(deviceRef, name, requestTimeout = 3000, messageId) {
1579
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1580
+ return this.deleteDeviceFile(Object.assign(Object.assign({}, deviceRefObj), { name }), requestTimeout, messageId);
1581
+ }
1582
+ /**
1583
+ * Get system log lines.
1584
+ *
1585
+ * Get the system log content and parse it into an array of lines, where each line contains the date, uptime, ID, file, level, and message.
1586
+ */
1587
+ getSystemLogLines(requestTimeout = 2000, messageId) {
1588
+ return this.getSystemLog(requestTimeout, messageId).pipe((0, operators_1.map)((systemLog) => { var _a; return (0, util_1.parseSystemLogContent)((_a = systemLog.content) !== null && _a !== void 0 ? _a : ''); }));
1589
+ }
1590
+ /**
1591
+ * Store parameters.
1592
+ *
1593
+ * Command the device to store the parameters into config.csv, then delay initially and subsequently check if the value of
1594
+ * the Store parameters has changed to 1, indicating the successful completion.
1595
+ *
1596
+ * {@link https://www.synapticon.com/documentation/circulo/sw5.0/device_info/save_restore_config.html}
1597
+ */
1598
+ storeParameters(deviceRef) {
1599
+ return this.setParameterValue(deviceRef, 0x1010, 1, 0x65766173).pipe(// "evas"
1600
+ (0, operators_1.delay)(2000), (0, operators_1.mergeMap)(() => this.matchParameterValue(deviceRef, 0x1010, 1, 1).pipe((0, operators_1.retry)({ count: 10, delay: 500 }))));
1601
+ }
1602
+ /**
1603
+ * Restore default parameters.
1604
+ *
1605
+ * Command the device to restore the default parameters, then delay initially and subsequently check if the value of
1606
+ * the Restore default parameters has changed to 1, indicating the successful completion.
1607
+ *
1608
+ * {@link https://www.synapticon.com/documentation/circulo/sw5.0/device_info/save_restore_config.html}
1609
+ */
1610
+ restoreDefaultParameters(deviceRef, type = types_1.RestoreDefaultParametersType.RESTORE_ALL_DEFAULT_PARAMETERS) {
1611
+ return this.setParameterValue(deviceRef, 0x1011, type, 0x64616f6c).pipe(// "daol"
1612
+ (0, operators_1.delay)(2000), (0, operators_1.mergeMap)(() => this.matchParameterValue(deviceRef, 0x1011, type, 1).pipe((0, operators_1.retry)({ count: 10, delay: 500 }))));
1613
+ }
1614
+ /**
1615
+ * Match parameter value.
1616
+ *
1617
+ * This function will retrieve a parameter value from a device and compare it with the expected value.
1618
+ *
1619
+ * @throws Will throw an error if the values do not match.
1620
+ */
1621
+ matchParameterValue(deviceRef, index, subindex, expectedValue) {
1622
+ return this.getParameterValue(deviceRef, index, subindex).pipe((0, operators_1.map)((value) => {
1623
+ if (value !== expectedValue) {
1624
+ throw new Error(`Device parameter value and the expected value don't match: ${value} ${expectedValue}`);
1625
+ }
1626
+ }));
1627
+ }
1628
+ matchCia402States(deviceRef, expectedStates) {
1629
+ return this.getParameterValue(deviceRef, 0x6041, 0).pipe((0, operators_1.map)((value) => {
1630
+ const state = (0, cia402_1.getCia402State)(value);
1631
+ if (!expectedStates.includes(state)) {
1632
+ throw new Error(`Device CiA402 state and the expected state don't match: ${state} ${expectedStates}`);
1633
+ }
1634
+ }));
1635
+ }
1636
+ /**
1637
+ * Helper function to enable motion.
1638
+ */
1639
+ enableMotion(deviceRef, controllerType, filter = false, requestTimeout = 2000, messageId) {
1640
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1641
+ return this.enableMotionController(Object.assign(Object.assign({}, deviceRefObj), { controllerType, filter }), requestTimeout, messageId);
1642
+ }
1643
+ /**
1644
+ * A helper function to disable motion.
1645
+ */
1646
+ disableMotion(deviceRef, requestTimeout = 3000, messageId) {
1647
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1648
+ return this.disableMotionController(deviceRefObj, requestTimeout, messageId);
1649
+ }
1650
+ /**
1651
+ * A helper function to set the motion target.
1652
+ */
1653
+ setMotionTarget(deviceRef, target, messageId) {
1654
+ const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
1655
+ return this.setMotionControllerParameters(Object.assign(Object.assign({}, deviceRefObj), { target }), messageId);
1656
+ }
1657
+ /**
1658
+ * A helper function to set the value of the Modes of Operation parameter (0x6060:00).
1659
+ */
1660
+ setModesOfOperation(deviceRef, modesOfOperation) {
1661
+ return this.setParameterValue(deviceRef, 0x6060, 0, modesOfOperation);
1662
+ }
1663
+ /**
1664
+ * Get CiA402 state.
1665
+ */
1666
+ getCia402State(deviceRef, requestTimeout = 5000, messageId) {
1667
+ return this.getParameterValue(deviceRef, 0x6041, 0, false, requestTimeout, messageId).pipe((0, operators_1.map)((value) => (0, cia402_1.getCia402State)(value)));
1668
+ }
1669
+ /**
1670
+ * Transition to CiA402 state.
1671
+ *
1672
+ * This function will utilize the current value of the controlword and modify it to transition to the requested CiA402 state.
1673
+ *
1674
+ * Only one transition will occur, except when the current CiA402 state is SWITCH_ON_DISABLED. In this case,
1675
+ * the first transition is from SOD to RTSO, and then from RTSO to the requested CiA402 state.
1676
+ *
1677
+ * The Statusword will be polled to check if the device has successfully transitioned to the requested CiA402 state.
1678
+ * When transitioning to QSA, the Statusword will be checked against both QSA and SOD.
1679
+ * This is necessary because the automatic transition from QSA to SOD can occur faster than the client's polling interval.
1680
+ */
1681
+ transitionToCia402State(deviceRef, targetState) {
1682
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1683
+ let currentState = yield (0, rxjs_1.lastValueFrom)(this.getCia402State(deviceRef));
1684
+ if (targetState === currentState) {
1685
+ return;
1686
+ }
1687
+ if (currentState === cia402_1.Cia402State.NOT_READY_TO_SWITCH_ON || currentState === cia402_1.Cia402State.FAULT_REACTION_ACTIVE) {
1688
+ throw new Error(`It is not possible to transition from state ${currentState} to state ${targetState}. The transition from the current ${currentState} state is automatic.`);
1689
+ }
1690
+ let controlword = yield this.upload(deviceRef, 0x6040, 0);
1691
+ let transition = cia402_2.cia402Transitions.find(t => t.from === currentState && t.to === targetState);
1692
+ // From SOD it's only possible to transition to RTSO.
1693
+ if (currentState === cia402_1.Cia402State.SWITCH_ON_DISABLED && targetState !== cia402_1.Cia402State.READY_TO_SWITCH_ON) {
1694
+ transition = cia402_2.cia402Transitions.find(t => t.from === cia402_1.Cia402State.READY_TO_SWITCH_ON && t.to === targetState);
1695
+ }
1696
+ if (!transition) {
1697
+ throw new Error(`It is not possible to transition from state ${currentState} to state ${targetState}.`);
1698
+ }
1699
+ if (currentState === cia402_1.Cia402State.SWITCH_ON_DISABLED) {
1700
+ const sodToRtsoTransition = cia402_2.cia402Transitions[0];
1701
+ const command = (0, cia402_3.createControlwordCommand)(controlword, sodToRtsoTransition.command);
1702
+ yield (0, rxjs_1.lastValueFrom)(this.setParameterValue(deviceRef, 0x6040, 0, command).pipe((0, operators_1.mergeMap)(() => this.matchCia402States(deviceRef, [cia402_1.Cia402State.READY_TO_SWITCH_ON]).pipe((0, operators_1.retry)({ count: 5, delay: 1000 })))));
1703
+ controlword = yield this.upload(deviceRef, 0x6040, 0);
1704
+ currentState = yield (0, rxjs_1.lastValueFrom)(this.getCia402State(deviceRef));
1705
+ if (targetState === currentState) {
1706
+ return;
1707
+ }
1708
+ }
1709
+ const command = (0, cia402_3.createControlwordCommand)(controlword, transition.command);
1710
+ const expectedStates = [targetState];
1711
+ if (targetState === cia402_1.Cia402State.QUICK_STOP_ACTIVE) {
1712
+ expectedStates.push(cia402_1.Cia402State.SWITCH_ON_DISABLED);
1713
+ }
1714
+ yield (0, rxjs_1.lastValueFrom)(this.setParameterValue(deviceRef, 0x6040, 0, command).pipe((0, operators_1.mergeMap)(() => this.matchCia402States(deviceRef, expectedStates).pipe((0, operators_1.retry)({ count: 10, delay: 1000 }))), (0, operators_1.catchError)(() => {
1715
+ throw new Error(`Timeout occurred while transitioning from ${currentState} state to ${targetState} state.`);
1716
+ })));
1717
+ });
1718
+ }
1719
+ /**
1720
+ * Reset fault.
1721
+ *
1722
+ * Resetting a fault can only be done from the FAULT CiA402 state by transitioning to SWITCH_ON_DISABLED.
1723
+ * This function will do the rising edge, that is it will first set the controlword reset fault bit to 0, wait a bit, and then set it to 1.
1724
+ */
1725
+ resetFault(deviceRef) {
1726
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1727
+ const currentState = yield (0, rxjs_1.lastValueFrom)(this.getCia402State(deviceRef));
1728
+ if (currentState !== cia402_1.Cia402State.FAULT) {
1729
+ throw new Error(`Device ${deviceRef} is not in FAULT CiA402 state.`);
1730
+ }
1731
+ let controlword = yield this.upload(deviceRef, 0x6040, 0);
1732
+ controlword = controlword & 0xFF7F;
1733
+ yield this.download(deviceRef, 0x6040, 0, controlword);
1734
+ yield (0, util_1.resolveAfter)(25);
1735
+ const command = (0, cia402_3.createControlwordCommand)(controlword, cia402_1.ControlwordCommand.FAULT_RESET);
1736
+ yield this.download(deviceRef, 0x6040, 0, command);
1737
+ });
1738
+ }
1739
+ /**
1740
+ * Quick stop.
1741
+ *
1742
+ * Quickly stopping a drive can only be done from the OPERATION_ENABLED CiA402 state by transitioning to QUICK_STOP_ACTIVE.
1743
+ */
1744
+ quickStop(deviceRef) {
1745
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1746
+ const currentState = yield (0, rxjs_1.lastValueFrom)(this.getCia402State(deviceRef));
1747
+ if (currentState !== cia402_1.Cia402State.OPERATION_ENABLED) {
1748
+ throw new Error(`Device ${deviceRef} is not in OPERATION_ENABLED CiA402 state.`);
1749
+ }
1750
+ const controlword = yield this.upload(deviceRef, 0x6040, 0);
1751
+ const command = (0, cia402_3.createControlwordCommand)(controlword, cia402_1.ControlwordCommand.QUICK_STOP);
1752
+ yield this.download(deviceRef, 0x6040, 0, command);
1753
+ });
1754
+ }
1755
+ /**
1756
+ * Reset targets.
1757
+ *
1758
+ * Set torque and velocity targets to 0, and set the position target to the current position value.
1759
+ */
1760
+ resetTargets(deviceRef) {
1761
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1762
+ const positionActualValue = yield this.upload(deviceRef, 0x6064, 0);
1763
+ yield this.downloadMany([
1764
+ [deviceRef, 0x6071, 0, 0],
1765
+ [deviceRef, 0x607A, 0, positionActualValue],
1766
+ [deviceRef, 0x60FF, 0, 0], // target velocity
1767
+ ]);
1768
+ });
1769
+ }
1770
+ /**
1771
+ * Apply set point.
1772
+ *
1773
+ * When running in position profile mode and setting a new position target value, it's important to note that the device won't immediately move to the new position.
1774
+ * To execute a new position set-point or target value, you need to make certain changes to the controlword.
1775
+ *
1776
+ * To indicate that the new set-point is valid and should be executed, you need to trigger a rising edge of the new set-point bit in the controlword.
1777
+ * This rising edge serves as a signal to the device that a new set-point is being provided.
1778
+ *
1779
+ * Once the device receives the rising edge of the new set-point bit, it will set the set-point acknowledge bit in the statusword to 1.
1780
+ * This indicates that the device has acknowledged the new set-point and will execute the corresponding action, such as moving to the specified position.
1781
+ *
1782
+ * Afterwards, the device will signal its ability to accept new set-points by setting the set-point acknowledge bit back to 0 in the statusword.
1783
+ * This signifies that the device is ready to receive and process additional set-points.
1784
+ *
1785
+ * It's important to note that the device won't wait for the previous set-point to finish before executing a new set-point.
1786
+ * If you want to initiate a new set-point while the previous one is still being executed, you need to generate another rising edge of the new set-point bit in the controlword.
1787
+ */
1788
+ applySetPoint(deviceRef) {
1789
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1790
+ let controlword = yield this.upload(deviceRef, 0x6040, 0);
1791
+ if ((controlword & 0x0010) > 0) {
1792
+ controlword = controlword & 0xFFEF; // set new set-point bit to 0
1793
+ yield this.download(deviceRef, 0x6040, 0, controlword);
1794
+ yield (0, util_1.resolveAfter)(30);
1795
+ }
1796
+ controlword = controlword | 0x0010 | 0x0020; // set new set-point and change set immediately bits to 1
1797
+ yield this.download(deviceRef, 0x6040, 0, controlword);
1798
+ });
1799
+ }
1800
+ /**
1801
+ * Run OS command.
1802
+ *
1803
+ * The OS command is a standard way of triggering actions or services that cannot be accommodated through a single SDO upload/download.
1804
+ *
1805
+ * The Dictionary object 0x1023 is used for executing OS commands. The following is the subindex list and their meanings:
1806
+ *
1807
+ * - 1: Command (OCTET_STRING), 8 bytes
1808
+ * - Byte 0: OS command ID, OS commands are identified by number, and here we specify which OS command to run, for example, 0 is encoder register communication
1809
+ * - Byte 1-7: Service request data, the remaining 7 bytes serve as arguments to the OS command, specifying details such as what register to read the value from
1810
+ * - 2: Status (UNSIGNED8), 1 byte
1811
+ * - 0: last command completed, no errors, no response data
1812
+ * - 1: last command completed, no errors, response data available
1813
+ * - 2: last command completed, error, no response data
1814
+ * - 3: last command completed, error, response data available
1815
+ * - 100-200: command executing with percentage (100 = 0%, 200 = 100%)
1816
+ * - 255: command executing (if percentage display is not supported)
1817
+ * - 3: Response (OCTET_STRING), 8 bytes
1818
+ * - Byte 0: Same as subindex 2
1819
+ * - Byte 1: unused
1820
+ * - Byte 2-7: Service response data, if the last command completed with response data, it will be available in these 6 bytes, for example, the value of the BiSS register
1821
+ *
1822
+ * Note that:
1823
+ * - As soon as the value of 1: Command Byte 0 changes, the OS command will start to execute.
1824
+ * - If the last command completed with response data, it will be available in bytes 2-7 of 3: Response subindex.
1825
+ * - The value of subindex 2: Status will be the same as that of 3: Response Byte 0.
1826
+ *
1827
+ * All possible cases of 0x1023:03 Response subindex:
1828
+ *
1829
+ * | Description | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
1830
+ * |-------------------------------------------------------|:-------:|:------:|:-------------:|:------:|:------:|:------:|:------:|:------:|
1831
+ * | Command in progress with percentage | 100-200 | - | - | - | - | - | - | - |
1832
+ * | Command in progress without percentage | 255 | - | - | - | - | - | - | - |
1833
+ * | Command completed without errors and without response | 0 | - | - | - | - | - | - | - |
1834
+ * | Command completed without errors and with response | 1 | - | Data | Data | Data | Data | Data | Data |
1835
+ * | Command completed with error and without response | 2 | - | - | - | - | - | - | - |
1836
+ * | Command completed with error and with response | 3 | - | OS error code | Data | Data | Data | Data | Data |
1837
+ *
1838
+ * Note that in the case of "Command completed without errors and with response," 6 bytes can be used for data.
1839
+ * However, only 5 bytes are available in the case of "Command completed with error and with response."
1840
+ * This limitation arises because Byte 2 in the latter response is allocated for the OS error code.
1841
+ * The OS error code serves to indicate the reason behind the error occurrence.
1842
+ *
1843
+ * 0x1023: OS command is used in combination with 0x1024: OS command mode object.
1844
+ * In our firmware, only the values 0 and 3 from the 0x1024 command mode object (USINT) are handled, and their meanings are as follows:
1845
+ * - 0: Execute the next command immediatelly (default)
1846
+ * - 3: Abort the current command and all commands in the buffer
1847
+ *
1848
+ * Note that if you abort the OS command, you must switch the value of 0x1024 back to 0 before executing another OS command.
1849
+ */
1850
+ runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1851
+ let response;
1852
+ // defer must be used to make a request to Motion Master on each repeat step
1853
+ const getResponse$ = (0, rxjs_1.defer)(() => this.getParameterValue(deviceRef, 0x1023, 3).pipe((0, operators_1.map)((value) => (0, os_command_1.parseOsCommandResponse)(value, command)), (0, rxjs_1.tap)((r) => response = r)));
1854
+ return (0, rxjs_1.concat)(executeTheNextOsCommandImmediatelly ? this.setOsCommandMode(deviceRef, os_command_1.OsCommandMode.EXECUTE_THE_NEXT_COMMAND_IMMEDIATELY) : rxjs_1.EMPTY, this.setParameterValue(deviceRef, 0x1023, 1, command), getResponse$.pipe((0, operators_1.repeat)({
1855
+ delay: () => (response && (response.request === 'succeeded' || response.request === 'failed'))
1856
+ ? rxjs_1.EMPTY
1857
+ : (0, rxjs_1.timer)(responsePollingInterval),
1858
+ }))).pipe((0, operators_1.takeUntil)((0, rxjs_1.timer)(commandTimeout).pipe((0, operators_1.mergeMap)(() => (0, rxjs_1.throwError)(() => new Error(`A timeout has occured for the OS command ${JSON.stringify(command)} on device ${deviceRef}.`))))), (0, operators_1.filter)(Boolean));
1859
+ }
1860
+ setOsCommandMode(deviceRef, value) {
1861
+ return this.setParameterValue(deviceRef, 0x1024, 0, value);
1862
+ }
1863
+ abortOsCommand(deviceRef) {
1864
+ return this.setOsCommandMode(deviceRef, os_command_1.OsCommandMode.ABORT_THE_CURRENT_COMMAND_AND_ALL_COMMANDS_IN_THE_BUFFER);
1865
+ }
1866
+ runEncoderRegisterCommunicationOsCommand(deviceRef, encoderOrdinal = 1, slaveAddress = 0, rw = 0, registerAddress = 0, registerWriteValue = 0, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1867
+ const command = (0, os_command_1.createEncoderRegisterCommunicationOsCommand)(encoderOrdinal, slaveAddress, rw, registerAddress, registerWriteValue);
1868
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1869
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1870
+ return Object.assign(Object.assign({}, osCommandResponse), { registerValue: osCommandResponse.data[0] });
1871
+ }
1872
+ return osCommandResponse;
1873
+ }));
1874
+ }
1875
+ runIcMuCalibrationModeOsCommand(deviceRef, encoderOrdinal = 1, modeEnable = 0, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1876
+ const command = (0, os_command_1.createIcMuCalibrationModeOsCommand)(encoderOrdinal, modeEnable);
1877
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1878
+ if (osCommandResponse.request === 'succeeded') {
1879
+ return Object.assign(Object.assign({}, osCommandResponse), { calibrationModeSet: true });
1880
+ }
1881
+ return osCommandResponse;
1882
+ }));
1883
+ }
1884
+ runOpenLoopFieldModeOsCommand(deviceRef, parameterIndex = 0, parameterValue = 0, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1885
+ const command = (0, os_command_1.createOpenLoopFieldModeOsCommand)(parameterIndex, parameterValue);
1886
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1887
+ if (osCommandResponse.request === 'succeeded') {
1888
+ return Object.assign(Object.assign({}, osCommandResponse), { openLoopFieldMode: true });
1889
+ }
1890
+ return osCommandResponse;
1891
+ }));
1892
+ }
1893
+ runHrdStreamingOsCommand(deviceRef, action = 0, dataIndex = 0, duration = 1000, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1894
+ const command = (0, os_command_1.createHrdStreamingOsCommand)(action, dataIndex, duration);
1895
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1896
+ if (osCommandResponse.request === 'succeeded') {
1897
+ return Object.assign(Object.assign({}, osCommandResponse), { hrdStreamingComplete: true });
1898
+ }
1899
+ if (osCommandResponse.request === 'failed') {
1900
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode <= 250) {
1901
+ const { errorName, errorDescription } = os_command_1.hrdStreamingOsCommandErrors[osCommandResponse.errorCode];
1902
+ return Object.assign(Object.assign({}, osCommandResponse), { hrdStreamingComplete: false, errorName, errorDescription });
1903
+ }
1904
+ return Object.assign(Object.assign({}, osCommandResponse), { hrdStreamingComplete: false });
1905
+ }
1906
+ return osCommandResponse;
1907
+ }));
1908
+ }
1909
+ runMotorPhaseOrderDetectionOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1910
+ const command = (0, os_command_1.createMotorPhaseOrderDetectionOsCommand)();
1911
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1912
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1913
+ return Object.assign(Object.assign({}, osCommandResponse), { motorPhaseOrder: osCommandResponse.data[0] });
1914
+ }
1915
+ return osCommandResponse;
1916
+ }));
1917
+ }
1918
+ runCommutationOffsetMeasurementOsCommand(deviceRef, commandTimeout = 30000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1919
+ const command = (0, os_command_1.createCommutationOffsetMeasurementOsCommand)();
1920
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1921
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1922
+ const [b0, b1] = osCommandResponse.data;
1923
+ const commutationAngleOffset = (b0 << 8) | b1;
1924
+ return Object.assign(Object.assign({}, osCommandResponse), { commutationAngleOffset });
1925
+ }
1926
+ return osCommandResponse;
1927
+ }));
1928
+ }
1929
+ runOpenPhaseDetectionOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1930
+ const command = (0, os_command_1.createOpenPhaseDetectionOsCommand)();
1931
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly = true).pipe((0, operators_1.map)((osCommandResponse) => {
1932
+ if (osCommandResponse.request === 'succeeded') {
1933
+ return Object.assign(Object.assign({}, osCommandResponse), { phaseOpened: false });
1934
+ }
1935
+ if (osCommandResponse.request === 'failed') {
1936
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode <= 250) {
1937
+ const { errorName, errorDescription } = os_command_1.openPhaseDetectionErrors[osCommandResponse.errorCode];
1938
+ return Object.assign(Object.assign({}, osCommandResponse), { phaseOpened: true, errorName, errorDescription });
1939
+ }
1940
+ return Object.assign(Object.assign({}, osCommandResponse), { phaseOpened: true });
1941
+ }
1942
+ return osCommandResponse;
1943
+ }));
1944
+ }
1945
+ runPolePairDetectionOsCommand(deviceRef, commandTimeout = 60000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1946
+ const command = (0, os_command_1.createPolePairDetectionOsCommand)();
1947
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1948
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1949
+ return Object.assign(Object.assign({}, osCommandResponse), { numberOfPolePairs: osCommandResponse.data[0] });
1950
+ }
1951
+ if (osCommandResponse.request === 'failed') {
1952
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode === 0) {
1953
+ return Object.assign(Object.assign({}, osCommandResponse), { errorName: 'Current amplitude error', errorDescription: 'The drive is not able to increase the amplitude of motor phase currents to the required value. This problem can occur due to limited Vdc or high motor phase impedances.' });
1954
+ }
1955
+ }
1956
+ return osCommandResponse;
1957
+ }));
1958
+ }
1959
+ runPhaseResistanceMeasurementOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1960
+ const command = (0, os_command_1.createPhaseResistanceMeasurementOsCommand)();
1961
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1962
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1963
+ const [b0, b1, b2, b3] = osCommandResponse.data;
1964
+ const phaseResistance = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1965
+ return Object.assign(Object.assign({}, osCommandResponse), { phaseResistance });
1966
+ }
1967
+ if (osCommandResponse.request === 'failed') {
1968
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode === 0) {
1969
+ return Object.assign(Object.assign({}, osCommandResponse), { errorName: 'Current amplitude error', errorDescription: 'Unable to increase the current amplitude.' });
1970
+ }
1971
+ }
1972
+ return osCommandResponse;
1973
+ }));
1974
+ }
1975
+ runPhaseInductanceMeasurementOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1976
+ const command = (0, os_command_1.createPhaseInductanceMeasurementOsCommand)();
1977
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1978
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1979
+ const [b0, b1, b2, b3] = osCommandResponse.data;
1980
+ const phaseInductance = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1981
+ return Object.assign(Object.assign({}, osCommandResponse), { phaseInductance });
1982
+ }
1983
+ if (osCommandResponse.request === 'failed') {
1984
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode === 0) {
1985
+ return Object.assign(Object.assign({}, osCommandResponse), { errorName: 'Current amplitude error', errorDescription: 'Unable to increase the current amplitude.' });
1986
+ }
1987
+ }
1988
+ return osCommandResponse;
1989
+ }));
1990
+ }
1991
+ runTorqueConstantMeasurementOsCommand(deviceRef, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
1992
+ const command = (0, os_command_1.createTorqueConstantMeasurementOsCommand)();
1993
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
1994
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
1995
+ const [b0, b1, b2, b3] = osCommandResponse.data;
1996
+ const torqueConstant = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1997
+ return Object.assign(Object.assign({}, osCommandResponse), { torqueConstant });
1998
+ }
1999
+ return osCommandResponse;
2000
+ }));
2001
+ }
2002
+ runSkippedCyclesCounterOsCommand(deviceRef, firmwareService = 0, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
2003
+ const command = (0, os_command_1.createSkippedCyclesCounterOsCommand)(firmwareService);
2004
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
2005
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
2006
+ const [b0, b1, b2, b3] = osCommandResponse.data;
2007
+ const skippedCycles = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
2008
+ return Object.assign(Object.assign({}, osCommandResponse), { skippedCycles });
2009
+ }
2010
+ return osCommandResponse;
2011
+ }));
2012
+ }
2013
+ runIgnoreBissStatusOsCommand(deviceRef, trigger = 1, encoderOrdinal = 1, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
2014
+ const command = (0, os_command_1.createIgnoreBissStatusBitsOsCommand)(trigger, encoderOrdinal);
2015
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
2016
+ if (osCommandResponse.request === 'succeeded') {
2017
+ return Object.assign(Object.assign({}, osCommandResponse), { ignoringBissStatusBits: true });
2018
+ }
2019
+ return osCommandResponse;
2020
+ }));
2021
+ }
2022
+ runSystemIdentificationOsCommand(deviceRef, parameterIndex = 0, parameterValue = 0, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
2023
+ const command = (0, os_command_1.createSystemIdentificationOsCommand)(parameterIndex, parameterValue);
2024
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
2025
+ if (osCommandResponse.request === 'succeeded') {
2026
+ return Object.assign(Object.assign({}, osCommandResponse), { systemIdentificationCompleted: true });
2027
+ }
2028
+ return osCommandResponse;
2029
+ }));
2030
+ }
2031
+ runTriggerErrorOsCommand(deviceRef, firmwareService = 0, errorType = 11, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
2032
+ const command = (0, os_command_1.createTriggerErrorOsCommand)(firmwareService, errorType);
2033
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
2034
+ if (osCommandResponse.request === 'succeeded') {
2035
+ return Object.assign(Object.assign({}, osCommandResponse), { errorSet: true });
2036
+ }
2037
+ return osCommandResponse;
2038
+ }));
2039
+ }
2040
+ runKublerEncoderCommandOsCommand(deviceRef, inputCommand = 0, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
2041
+ const command = (0, os_command_1.createKublerEncoderCommand)(inputCommand);
2042
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
2043
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
2044
+ const numberOfBytesWritten = (osCommandResponse.data[1] << 8) | osCommandResponse.data[0];
2045
+ const responseInFoeBuffer = osCommandResponse.data[3];
2046
+ return Object.assign(Object.assign({}, osCommandResponse), { numberOfBytesWritten, responseInFoeBuffer });
2047
+ }
2048
+ if (osCommandResponse.request === 'failed') {
2049
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode <= 250) {
2050
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) === 3 && inputCommand === 0) {
2051
+ const { errorName, errorDescription } = os_command_1.kublerEncoderCommandWriteCorrectionTableOsCommandErrors[osCommandResponse.errorCode + 1];
2052
+ return Object.assign(Object.assign({}, osCommandResponse), { errorName, errorDescription });
2053
+ }
2054
+ const { errorName, errorDescription } = os_command_1.kublerEncoderCommandSendScpiCommandOsCommandErrors[osCommandResponse.errorCode];
2055
+ return Object.assign(Object.assign({}, osCommandResponse), { errorName, errorDescription });
2056
+ }
2057
+ }
2058
+ return osCommandResponse;
2059
+ }));
2060
+ }
2061
+ runUseInternalEncoderVelocityOsCommand(deviceRef, trigger = 0, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
2062
+ const command = (0, os_command_1.createUseInternalEncoderVelocityOsCommand)(trigger);
2063
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
2064
+ if (osCommandResponse.request === 'succeeded') {
2065
+ return Object.assign(Object.assign({}, osCommandResponse), { usingInternalEncoderVelocity: true });
2066
+ }
2067
+ return osCommandResponse;
2068
+ }));
2069
+ }
2070
+ runKublerEncoderRegisterCommunicationOsCommand(deviceRef, rw = 0, registerAddress = 0, registerLength = 0, registerWriteValue = 0, commandTimeout = 10000, responsePollingInterval = 1000, executeTheNextOsCommandImmediatelly = true) {
2071
+ const command = (0, os_command_1.createKublerEncoderRegisterCommunicationOsCommand)(rw, registerAddress, registerLength, registerWriteValue);
2072
+ return this.runOsCommand(deviceRef, command, commandTimeout, responsePollingInterval, executeTheNextOsCommandImmediatelly).pipe((0, operators_1.map)((osCommandResponse) => {
2073
+ if (osCommandResponse.request === 'succeeded' && osCommandResponse.data) {
2074
+ const [b0, b1, b2, b3] = osCommandResponse.data;
2075
+ const kublerRegisterValue = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
2076
+ return Object.assign(Object.assign({}, osCommandResponse), { kublerRegisterValue });
2077
+ }
2078
+ if (osCommandResponse.request === 'failed') {
2079
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) && osCommandResponse.errorCode <= 250) {
2080
+ if ((osCommandResponse === null || osCommandResponse === void 0 ? void 0 : osCommandResponse.errorCode) === 3 && rw === 0) {
2081
+ const { errorName, errorDescription } = os_command_1.kublerEncoderCommandWriteCorrectionTableOsCommandErrors[osCommandResponse.errorCode + 1];
2082
+ return Object.assign(Object.assign({}, osCommandResponse), { errorName, errorDescription });
2083
+ }
2084
+ const { errorName, errorDescription } = os_command_1.kublerEncoderCommandWriteCorrectionTableOsCommandErrors[osCommandResponse.errorCode];
2085
+ return Object.assign(Object.assign({}, osCommandResponse), { errorName, errorDescription });
2086
+ }
2087
+ }
2088
+ return osCommandResponse;
2089
+ }));
2090
+ }
2091
+ readOsCommandResponse(deviceRef) {
2092
+ return this.getParameterValue(deviceRef, 0x1023, 3).pipe((0, operators_1.map)((value) => (0, os_command_1.parseOsCommandResponse)(value)));
2093
+ }
2094
+ /**
2095
+ * Set halt bit.
2096
+ *
2097
+ * The behavior of the halt function (bit 8) in the controlword depends on the specific operation mode of the device.
2098
+ * When the halt bit is set to 1, the commanded motion will be interrupted, and the behavior of the device will follow the defined rules for the halt option code.
2099
+ *
2100
+ * After releasing the halt function by setting the halt bit to 0, the drive device will attempt to continue the commanded motion if possible,
2101
+ * based on the specific operation mode and the conditions of the system.
2102
+ */
2103
+ setHaltBit(deviceRef, value) {
2104
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
2105
+ let controlword = yield this.upload(deviceRef, 0x6040, 0);
2106
+ controlword = value ? (controlword | 0x0100) : (controlword & 0xFEFF);
2107
+ yield this.download(deviceRef, 0x6040, 0, controlword);
2108
+ });
2109
+ }
2110
+ getCachedDeviceByDeviceRefObj(deviceRefObj) {
2111
+ if (deviceRefObj.deviceSerialNumber) {
2112
+ return this.deviceMap.get(deviceRefObj.deviceSerialNumber);
2113
+ }
2114
+ else if (typeof deviceRefObj.devicePosition === 'number') {
2115
+ for (const device of this.deviceMap.values()) {
2116
+ if (device.position === deviceRefObj.devicePosition) {
2117
+ return device;
2118
+ }
2119
+ }
2120
+ }
2121
+ else if (typeof deviceRefObj.deviceAddress === 'number') {
2122
+ for (const device of this.deviceMap.values()) {
2123
+ if (device.deviceAddress === deviceRefObj.deviceAddress) {
2124
+ return device;
2125
+ }
2126
+ }
2127
+ }
2128
+ return;
2129
+ }
2130
+ getFeedConstant(deviceRef, requestTimeout = 5000) {
2131
+ return this.getParameterValues([
2132
+ [deviceRef, 0x6092, 1],
2133
+ [deviceRef, 0x6092, 2], // Shaft revolutions
2134
+ ], false, requestTimeout).pipe((0, operators_1.map)((values) => values[0] / values[1]));
2135
+ }
2136
+ }
2137
+ exports.MotionMasterReqResClient = MotionMasterReqResClient;
1998
2138
  //# sourceMappingURL=motion-master-req-res-client.js.map