motion-master-client 0.0.2 → 0.0.3

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.
package/README.md CHANGED
@@ -20,7 +20,7 @@ const clientId = v4();
20
20
  reqResSocket.open(`ws://oblac-drives-7dc95497.local:63524?clientId=${clientId}`);
21
21
  pubSubSocket.open(`ws://oblac-drives-7dc95497.local:63525?clientId=${clientId}`);
22
22
 
23
- this.client.request.getDeviceInfo().subscribe(console.log);
23
+ client.request.getDeviceInfo().subscribe(console.log);
24
24
  ```
25
25
 
26
26
  ## Communication
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "motion-master-client",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "A library and CLI program used for communicating with Motion Master."
6
6
  }
@@ -13,19 +13,22 @@ import { decodeTextContent, valueTypeToParameterTypeValueKeyMap, getParameterVal
13
13
  /**
14
14
  * This class contains methods for making requests to Motion Master using the injected request/response socket.
15
15
  *
16
- * Each request message defined in the proto file has two corresponding request methods in this class. One method supports Reactive and the other Promise-based API.
17
- * Reactive API is the default one. Unlike promises which resolve to a single value, observables can emit multiple values over time. For some requests like firmware installation,
18
- * Motion Master will send progress messages until the firmware installation is done.
16
+ * Each request message defined in the proto file has two corresponding request methods in this class.
17
+ * One method supports Reactive and the other Promise-based API. Reactive API is the default one.
18
+ * Unlike promises which resolve to a single value, observables can emit multiple values over time.
19
+ * For some requests like firmware installation, Motion Master will send progress messages until the firmware installation is done.
19
20
  * Reactive request methods are named like `getDeviceInfo` and Promise-based are named like `getDeviceInfoAsync` (notice the `Async` suffix).
20
21
  *
21
22
  * Reactive and Promise-based request methods have the same set of input parameters, but they differ in the return values.
22
23
  * Call to Reactive request methods will return an Observable of (1) corresponding status message, for example call to `getDeviceInfo`
23
24
  * will return an instance of `MotionMasterMessage.Status.DeviceInfo`, (2) request status, and (3) request message id.
24
- * Promise-based request methods can only return (resolve to) a single value. These methods call the corresponding Reactive request method and return data
25
- * from the last emmited status message. For example `getDeviceInfoAsync` calls `getDeviceInfo` and returns a list of devices from
26
- * the last emmited status message. If the request fails on Motion Master the function will throw an error. It can also throw an error if request times out.
25
+ * Promise-based request methods can only return (resolve to) a single value.
26
+ * These methods call the corresponding Reactive request method and return data from the last emmited status message.
27
+ * For example `getDeviceInfoAsync` calls `getDeviceInfo` and returns a list of devices from the last emmited status message.
28
+ * If the request fails on Motion Master the function will throw an error. It can also throw an error if the request times out.
27
29
  *
28
- * Request methods have optional `messageId` input parameter. If `messageId` is not provided, one will be generated before sending the request message to Motion Master.
30
+ * Request methods have optional `messageId` input parameter.
31
+ * If `messageId` is not provided, one will be generated before sending the request message to Motion Master.
29
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.
30
33
  * This is how we match request with response messages in the full-duplex asynchronous communication (WebSockets, ZeroMQ).
31
34
  * Matching request/response by message id is inspired by [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification).
@@ -70,7 +73,7 @@ import { decodeTextContent, valueTypeToParameterTypeValueKeyMap, getParameterVal
70
73
  * Beside device address and device serial number, devices can be referenced by position (sequence number in the network).
71
74
  *
72
75
  * It is ensured that Observable returned from a call to Reactive request method will eventually complete, so a call to unsubscribe is not required.
73
- * Observable will complete when request status is either "succeeded" or "failed", and it can complete (error) due to timeout.
76
+ * Observable will complete when request status is either "succeeded" or "failed", or it will complete (error) due to timeout.
74
77
  * Finding out if a request has completed, failed or is it still on-going is different for each request - it dependends on the status messages, see {@link requestStatusResolver}.
75
78
  * Reactive request methods handle this and for each request they will emit status messages with one of "succeeded", "failed", or "running" request status.
76
79
  *
@@ -98,7 +101,7 @@ export class MotionMasterReqResClient {
98
101
  * When device parameter info is fetched this map gets updated by mapping device parameters type to oneof proto type, e.g. `INTEGER32 -> intValue`.
99
102
  *
100
103
  * This map helps in simplifying the API by allowing users to set parameter values without specifying the type value field.
101
- * One can simply call `await setParameterValue(2045285448, 0x2004, 0x03, 2500)` and the correct type value field on the proto message will be set, `uintValue` in this example.
104
+ * One can simply call `setParameterValue(2045285448, 0x2004, 0x03, 2500)` and the correct type value field on the proto message will be set, `uintValue` in this example.
102
105
  */
103
106
  readonly parameterTypeValueKeyMap = new Map<number, Map<string, ParameterTypeValueKey>>();
104
107
 
@@ -117,8 +120,8 @@ export class MotionMasterReqResClient {
117
120
  * For scripts and applications that use this library, having a permanent device identifier is a better option.
118
121
  * Device serial number read from the hardware description file (.hardware_description) stored on a device is a permanent identifier.
119
122
  *
120
- * This map helps in making all request functions related to a device accept device reference instead of just device address as an identifier.
121
- * Device reference can be one of: device address, device serial number, or device position.
123
+ * This map helps in making all request methods related to a device accept device reference instead of just device address as an identifier.
124
+ * Device reference {@link DeviceRefObj} can be one of: device address, device serial number, or device position.
122
125
  * Prior to sending a request message to Motion Master, device address is resolved from device reference and this map is used for that.
123
126
  */
124
127
  readonly deviceAddressMap = new Map<string, number>();
@@ -180,8 +183,12 @@ export class MotionMasterReqResClient {
180
183
  /**
181
184
  * Get device info.
182
185
  *
183
- * Device info includes a list of devices on network.
184
- * Call this method to get device addresses for requests specific to devices.
186
+ * Device info includes a list of devices on the network. Each device has a type, position in the network, and device address.
187
+ *
188
+ * Motion Master will assign a unique device address to each device in the list.
189
+ * Device address is what the client libraries use to make requests like `getDeviceParameter` from a particular device.
190
+ * Device address is only valid for the duration of the session, that is until the list of devices is read again, for example
191
+ * when devices are power cycled or Motion Master process is restarted.
185
192
  */
186
193
  getDeviceInfo(requestTimeout: number, messageId?: string) {
187
194
  const getDeviceInfo = MotionMasterMessage.Request.GetDeviceInfo.create();
@@ -204,6 +211,22 @@ export class MotionMasterReqResClient {
204
211
  throw new Error('Get device info request failed.');
205
212
  }
206
213
 
214
+ /**
215
+ * Get device parameter info.
216
+ *
217
+ * Device parameter info includes a list of all parameters for the requested device, but without the parameter values.
218
+ * Getting device parameter values can take more than 20ms per parameter using the IgH EtherCAT Master library.
219
+ * Not all clients require all the parameter values at once like OBLAC Drives, so this method is good for getting
220
+ * the list of available parameters and then getting the values of only some parameters.
221
+ *
222
+ * The returned list of device parameters will include some basic data for each parameter: index, subindex,
223
+ * name, unit, group (ARRAY or RECORD name for subitems), read and write access, min and max, and value type.
224
+ * The same info and more can be read from the "SOMANET_CiA_402.xml" file, but getting info from ESI file is more complicated.
225
+ * It requires an XML parsing library and knowing how to read objects info from a dictionary and assigned modules.
226
+ *
227
+ * In addition to fetching device parameter info, this method will map parameter base data type (ETG.1020) to proto3 types.
228
+ * The map removes the need to specify proto3 type field when setting a parameter value {@link parameterTypeValueKeyMap}.
229
+ */
207
230
  getDeviceParameterInfo(props: MotionMasterMessage.Request.IGetDeviceParameterInfo & DeviceRefObj, requestTimeout: number, messageId?: string) {
208
231
  return from(this.resolveDeviceAddress(props)).pipe(
209
232
  mergeMap((deviceAddress) => {
@@ -231,6 +254,9 @@ export class MotionMasterReqResClient {
231
254
  );
232
255
  }
233
256
 
257
+ /**
258
+ * {@inheritDoc getDeviceParameterInfo}
259
+ */
234
260
  async getDeviceParameterInfoAsync(props: MotionMasterMessage.Request.IGetDeviceParameterInfo & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.DeviceParameterInfo.IParameter[]> {
235
261
  const status = await lastValueFrom(this.getDeviceParameterInfo(props, requestTimeout, messageId));
236
262
 
@@ -241,6 +267,9 @@ export class MotionMasterReqResClient {
241
267
  throw new Error('Get device parameter info request failed.');
242
268
  }
243
269
 
270
+ /**
271
+ * Get device parameter values.
272
+ */
244
273
  getDeviceParameterValues(props: MotionMasterMessage.Request.IGetDeviceParameterValues & DeviceRefObj, requestTimeout: number, messageId?: string) {
245
274
  return from(this.resolveDeviceAddress(props)).pipe(
246
275
  mergeMap((deviceAddress) => {
@@ -250,15 +279,21 @@ export class MotionMasterReqResClient {
250
279
  transformMotionMasterMessageToStatus<MotionMasterMessage.Status.DeviceParameterValues>('deviceParameterValues', requestTimeout, id),
251
280
  );
252
281
  }),
253
- )
282
+ );
254
283
  }
255
284
 
285
+ /**
286
+ * {@inheritDoc getDeviceParameterValues}
287
+ */
256
288
  async getDeviceParameterValuesAsync(props: MotionMasterMessage.Request.IGetDeviceParameterValues & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.DeviceParameterValues.IParameterValue[]> {
257
289
  const status = await lastValueFrom(this.getDeviceParameterValues(props, requestTimeout, messageId));
258
290
 
259
291
  return status.parameterValues;
260
292
  }
261
293
 
294
+ /**
295
+ * Get multi device parameter values.
296
+ */
262
297
  getMultiDeviceParameterValues(props: MotionMasterMessage.Request.IGetMultiDeviceParameterValues, requestTimeout: number, messageId?: string) {
263
298
  const getMultiDeviceParameterValues = MotionMasterMessage.Request.GetMultiDeviceParameterValues.create(props);
264
299
  const id = this.sendRequest({ getMultiDeviceParameterValues }, messageId);
@@ -267,12 +302,18 @@ export class MotionMasterReqResClient {
267
302
  );
268
303
  }
269
304
 
305
+ /**
306
+ * {@inheritDoc getMultiDeviceParameterValues}
307
+ */
270
308
  async getMultiDeviceParameterValuesAsync(props: MotionMasterMessage.Request.IGetMultiDeviceParameterValues, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.IDeviceParameterValues[]> {
271
309
  const status = await lastValueFrom(this.getMultiDeviceParameterValues(props, requestTimeout, messageId));
272
310
 
273
311
  return status.collection;
274
312
  }
275
313
 
314
+ /**
315
+ * Set device parameter values.
316
+ */
276
317
  setDeviceParameterValues(props: MotionMasterMessage.Request.ISetDeviceParameterValues & DeviceRefObj, requestTimeout: number, messageId?: string) {
277
318
  return from(this.resolveDeviceAddress(props)).pipe(
278
319
  mergeMap((deviceAddress) => {
@@ -285,12 +326,18 @@ export class MotionMasterReqResClient {
285
326
  );
286
327
  }
287
328
 
329
+ /**
330
+ * {@inheritDoc setDeviceParameterValues}
331
+ */
288
332
  async setDeviceParameterValuesAsync(props: MotionMasterMessage.Request.ISetDeviceParameterValues & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.DeviceParameterValues.IParameterValue[]> {
289
333
  const status = await lastValueFrom(this.setDeviceParameterValues(props, requestTimeout, messageId));
290
334
 
291
335
  return status.parameterValues;
292
336
  }
293
337
 
338
+ /**
339
+ * Set multi device parameter values.
340
+ */
294
341
  setMultiDeviceParameterValues(props: MotionMasterMessage.Request.ISetMultiDeviceParameterValues, requestTimeout: number, messageId?: string) {
295
342
  const setMultiDeviceParameterValues = MotionMasterMessage.Request.SetMultiDeviceParameterValues.create(props);
296
343
  const id = this.sendRequest({ setMultiDeviceParameterValues }, messageId);
@@ -299,6 +346,9 @@ export class MotionMasterReqResClient {
299
346
  );
300
347
  }
301
348
 
349
+ /**
350
+ * {@inheritDoc setMultiDeviceParameterValues}
351
+ */
302
352
  async setMultiDeviceParameterValuesAsync(props: MotionMasterMessage.Request.ISetMultiDeviceParameterValues, requestTimeout: number, messageId?: string): Promise<MotionMasterMessage.Status.IDeviceParameterValues[]> {
303
353
  const status = await lastValueFrom(this.setMultiDeviceParameterValues(props, requestTimeout, messageId));
304
354
 
@@ -350,7 +400,7 @@ export class MotionMasterReqResClient {
350
400
  /**
351
401
  * Get device file.
352
402
  *
353
- * Motion Master uses the Filetransfer over EtherCAT (FoE) to read and send back the content of a file from device flash memory.
403
+ * Motion Master uses the Filetransfer over EtherCAT (FoE) to read and send back content of a file from device flash memory.
354
404
  *
355
405
  * The IgH EtherCAT Master library used by Motion Master limits the file read buffer to 9KB, so any file written to flash that is larger than 9KB
356
406
  * needs to be split into parts of max 9KB. Motion Master does this automatically on request to {@link setDeviceFile}.
@@ -428,6 +478,15 @@ export class MotionMasterReqResClient {
428
478
  throw new Error(`Failed to set device file. ${status.error?.message} ${JSON.stringify(props)}`);
429
479
  }
430
480
 
481
+ /**
482
+ * Delete device file.
483
+ *
484
+ * Motion Master uses the Filetransfer over EtherCAT (FoE) to delete files from device flash memory.
485
+ *
486
+ * If the file to delete is written in parts, Motion Master will ensure that all parts are deleted.
487
+ * It does that by reading the list of files on a device, see {@link getDeviceFileList}. It will remove all parts from the list and then
488
+ * it will re-read the list again to ensure that there are no left overs if there were more than 32 files in the list.
489
+ */
431
490
  deleteDeviceFile(props: MotionMasterMessage.Request.IDeleteDeviceFile & DeviceRefObj, requestTimeout: number, messageId?: string) {
432
491
  return from(this.resolveDeviceAddress(props)).pipe(
433
492
  mergeMap((deviceAddress) => {
@@ -440,6 +499,9 @@ export class MotionMasterReqResClient {
440
499
  );
441
500
  }
442
501
 
502
+ /**
503
+ * {@inheritDoc deleteDeviceFile}
504
+ */
443
505
  async deleteDeviceFileAsync(props: MotionMasterMessage.Request.IDeleteDeviceFile & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
444
506
  const status = await lastValueFrom(this.deleteDeviceFile(props, requestTimeout, messageId));
445
507
 
@@ -525,6 +587,19 @@ export class MotionMasterReqResClient {
525
587
  throw new Error(`Failed to stop device. ${status.error?.message} ${JSON.stringify(props)}`);
526
588
  }
527
589
 
590
+ /**
591
+ * Start device firmware installation.
592
+ *
593
+ * When Motion Master receives this request it will do the following:
594
+ * - switch device to BOOT EtherCAT state, device will then run the bootloader instead of the firmware
595
+ * - delete files from device that it finds in the provided package (SOMANET_CiA_402.xml and stack_image.svg)
596
+ * - write files other than firmware binaries and SII from package to device (SOMANET_CiA_402.xml and stack_image.svg)
597
+ * - optionally install the SII file using the IgH EtherCAT Master library write SII function
598
+ * - write firmware binaries to device using the predefined names like app_firmware.bin
599
+ *
600
+ * During the firmware installation Motion Master will send progress messages back to clients.
601
+ * If Motion Master fails in any step other than SII write it will send error. If SII write fails only warning will be sent.
602
+ */
528
603
  startDeviceFirmwareInstallation(props: MotionMasterMessage.Request.IStartDeviceFirmwareInstallation & DeviceRefObj, requestTimeout: number, messageId?: string) {
529
604
  return from(this.resolveDeviceAddress(props)).pipe(
530
605
  mergeMap((deviceAddress) => {
@@ -537,6 +612,9 @@ export class MotionMasterReqResClient {
537
612
  );
538
613
  }
539
614
 
615
+ /**
616
+ * {@inheritDoc startDeviceFirmwareInstallation}
617
+ */
540
618
  async startDeviceFirmwareInstallationAsync(props: MotionMasterMessage.Request.IStartDeviceFirmwareInstallation & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
541
619
  const status = await lastValueFrom(this.startDeviceFirmwareInstallation(props, requestTimeout, messageId));
542
620
 
@@ -547,6 +625,13 @@ export class MotionMasterReqResClient {
547
625
  throw new Error(`Failed to start device firmware installation. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
548
626
  }
549
627
 
628
+ /**
629
+ * Get device log.
630
+ *
631
+ * Device log is stored in two files due to IgH EtherCAT Master library 9KB file size limitation, see {@link getDeviceFile}.
632
+ * Device automatically writes log to logging_curr.log and copies that file to logging_prev.log once it reaches 9KB.
633
+ * When Motion Master receives this request it will read the both files and return their combined contents.
634
+ */
550
635
  getDeviceLog(props: MotionMasterMessage.Request.IGetDeviceLog & DeviceRefObj, requestTimeout: number, messageId?: string) {
551
636
  return from(this.resolveDeviceAddress(props)).pipe(
552
637
  mergeMap((deviceAddress) => {
@@ -559,6 +644,9 @@ export class MotionMasterReqResClient {
559
644
  );
560
645
  }
561
646
 
647
+ /**
648
+ * {@inheritDoc getDeviceLog}
649
+ */
562
650
  async getDeviceLogAsync(props: MotionMasterMessage.Request.IGetDeviceLog & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<string | null | undefined> {
563
651
  const status = await lastValueFrom(this.getDeviceLog(props, requestTimeout, messageId));
564
652
 
@@ -569,6 +657,33 @@ export class MotionMasterReqResClient {
569
657
  throw new Error(`Failed to get device log. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
570
658
  }
571
659
 
660
+ /**
661
+ * Start cogging torque recording.
662
+ *
663
+ * Cogging torque recording (CTC) can be started with or without auto-config.
664
+ * In the proto file skipping the auto-config is currently named skip_auto_tuning. The field name is deprecated and will be renamed.
665
+ *
666
+ * If CTC is started without auto-config then users must ensure that encoder on the motor shaft is used for all functions: commutation, position, and velocity.
667
+ * If this is not the case, then Motion Master will return invalid encoder configuration error.
668
+ * Recording can also fail if the position controller is not tuned.
669
+ *
670
+ * If CTC is started with auto-config then Motion Master will:
671
+ * - change the encoder configuration to have all functions on the motor shaft
672
+ * - disabled CTC
673
+ * - change the velocity feed forward value
674
+ * - change the position and velocity filter types and cutoff frequencies
675
+ * - run iterative sharpening position auto-tuning in order to compute the optimal PID values (gains) using the auto tuning
676
+ *
677
+ * All parameter values that Motion Master changes as part of the auto-config are first stored in memory and once the CTC recording is done are reverted back.
678
+ *
679
+ * CTC recording is started by changing the Modes of operation (0x6060) parameter value to -1 (Cogging compensation recording mode).
680
+ * During the operation Motion Master will monitor the Cogging torque compensation state parameter (0x2008:01) to know when the recording is in progress and when it's done.
681
+ *
682
+ * The result of recording CTC is the cogging_torque.bin file written to device flash memory. Size of this file is 2048 bytes.
683
+ * Each pair of bytes represents a 16-bit signed integer value in mNm (milli newton meter).
684
+ *
685
+ * This request will turn the motor.
686
+ */
572
687
  startCoggingTorqueRecording(props: MotionMasterMessage.Request.IStartCoggingTorqueRecording & DeviceRefObj, requestTimeout: number, messageId?: string) {
573
688
  return from(this.resolveDeviceAddress(props)).pipe(
574
689
  mergeMap((deviceAddress) => {
@@ -581,6 +696,9 @@ export class MotionMasterReqResClient {
581
696
  );
582
697
  }
583
698
 
699
+ /**
700
+ * {@inheritDoc startCoggingTorqueRecording}
701
+ */
584
702
  async startCoggingTorqueRecordingAsync(props: MotionMasterMessage.Request.IStartCoggingTorqueRecording & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
585
703
  const status = await lastValueFrom(this.startCoggingTorqueRecording(props, requestTimeout, messageId));
586
704
 
@@ -591,6 +709,14 @@ export class MotionMasterReqResClient {
591
709
  throw new Error(`Failed to start cogging torque recording. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
592
710
  }
593
711
 
712
+ /**
713
+ * Get cogging torque data.
714
+ *
715
+ * The cogging_torque.bin file is the result of recording cogging torque compensation, see {@link startCoggingTorqueRecording}.
716
+ * The size of this file is 2048 bytes where each pair of bytes represents a 16-bit signed integer value in mNm (milli newton meter).
717
+ *
718
+ * Motion Master will read the cogging_torque.bin file, convert bytes to 16-bit signed integer values and return that as an array.
719
+ */
594
720
  getCoggingTorqueData(props: MotionMasterMessage.Request.IGetCoggingTorqueData & DeviceRefObj, requestTimeout: number, messageId?: string) {
595
721
  return from(this.resolveDeviceAddress(props)).pipe(
596
722
  mergeMap((deviceAddress) => {
@@ -603,6 +729,9 @@ export class MotionMasterReqResClient {
603
729
  );
604
730
  }
605
731
 
732
+ /**
733
+ * {@inheritDoc getCoggingTorqueData}
734
+ */
606
735
  async getCoggingTorqueDataAsync(props: MotionMasterMessage.Request.IGetCoggingTorqueData & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<number[]> {
607
736
  const status = await lastValueFrom(this.getCoggingTorqueData(props, requestTimeout, messageId));
608
737
 
@@ -613,6 +742,34 @@ export class MotionMasterReqResClient {
613
742
  throw new Error(`Failed to get cogging torque data. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
614
743
  }
615
744
 
745
+ /**
746
+ * Start offset detection.
747
+ *
748
+ * Running offset detection is different in firmwares <v5 and >=v5.
749
+ *
750
+ * For devices that run firmware >=v5 Motion Master will run multiple OS commands.
751
+ * Before starting any command device is switched to Modes of operation -2 (Diagnostics mode) and
752
+ * it's put into operation enabled CiA402 state. The OS commands are run in the following order:
753
+ * - open phase detection
754
+ * - phase resistance measurement
755
+ * - phase inductance measurement
756
+ * - pole pair detection
757
+ * - motor phase order detection
758
+ * - commutation offset measurement
759
+ *
760
+ * Motion Master will return error if any of the following OS commands fail: open phase detection,
761
+ * phase order detection, commutation offset measurement.
762
+ * If other OS commands fail Motion Master will continue with the procedure and only return warning.
763
+ *
764
+ * Once the commutation offset measurement command completes it will update:
765
+ * - 0x2001:00 Commutation angle offset to the computed offset value
766
+ * - 0x2009:01 Commutation offset State to 2 (OFFSET_VALID)
767
+ *
768
+ * For devices that run firmware <5 the same Modes of operation -2 is used, but it's named Commutation offset detection mode.
769
+ * The end result is the same, the procedure will update 0x2001:00 and 0x2009:01 parameter values.
770
+ *
771
+ * This request will turn the motor.
772
+ */
616
773
  startOffsetDetection(props: MotionMasterMessage.Request.IStartOffsetDetection & DeviceRefObj, requestTimeout: number, messageId?: string) {
617
774
  return from(this.resolveDeviceAddress(props)).pipe(
618
775
  mergeMap((deviceAddress) => {
@@ -625,6 +782,9 @@ export class MotionMasterReqResClient {
625
782
  );
626
783
  }
627
784
 
785
+ /**
786
+ * {@inheritDoc startOffsetDetection}
787
+ */
628
788
  async startOffsetDetectionAsync(props: MotionMasterMessage.Request.IStartOffsetDetection & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
629
789
  const status = await lastValueFrom(this.startOffsetDetection(props, requestTimeout, messageId));
630
790
 
@@ -663,6 +823,21 @@ export class MotionMasterReqResClient {
663
823
  throw new Error(`Failed start plant identification. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
664
824
  }
665
825
 
826
+ /**
827
+ * Compute auto tuning gains.
828
+ *
829
+ * Auto tuning gains can be computed for position or velocity controller.
830
+ *
831
+ * Prerequisites for running this procedure are the configured motor and the existence of
832
+ * plant_model.csv file created by a previous run of the system identification {@link startSystemIdentification}.
833
+ * Motion Master will return error if the plant_model.csv file is not found.
834
+ *
835
+ * In OBLAC Drives this request is called when the tuning sliders (damping ratio, bandwidth) are moved and
836
+ * during the CTC recording {@link startCoggingTorqueRecording} when auto config is not skipped.
837
+ *
838
+ * Motion Master will compute the PID gains using the Python scripts and it will
839
+ * update the Kp, Ki, Kd values of 0x2011 and 0x2012 parameters.
840
+ */
666
841
  computeAutoTuningGains(props: MotionMasterMessage.Request.IComputeAutoTuningGains & DeviceRefObj, requestTimeout: number, messageId?: string) {
667
842
  return from(this.resolveDeviceAddress(props)).pipe(
668
843
  mergeMap((deviceAddress) => {
@@ -675,6 +850,9 @@ export class MotionMasterReqResClient {
675
850
  );
676
851
  }
677
852
 
853
+ /**
854
+ * {@inheritDoc computeAutoTuningGains}
855
+ */
678
856
  async computeAutoTuningGainsAsync(props: MotionMasterMessage.Request.IComputeAutoTuningGains & DeviceRefObj, requestTimeout: number, messageId?: string) {
679
857
  const status = await lastValueFrom(this.computeAutoTuningGains(props, requestTimeout, messageId));
680
858
 
@@ -685,6 +863,13 @@ export class MotionMasterReqResClient {
685
863
  throw new Error(`Failed to compute auto tuning gains. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
686
864
  }
687
865
 
866
+ /**
867
+ * Set motion controller parameters.
868
+ *
869
+ * This request sets the target value for a device on Motion Master.
870
+ *
871
+ * A previous call to {@link enableMotionController} will set the mode of operation to CSP, CSV, or CST and optionally enable filtering.
872
+ */
688
873
  setMotionControllerParameters(props: MotionMasterMessage.Request.ISetMotionControllerParameters & DeviceRefObj, messageId?: string) {
689
874
  return from(this.resolveDeviceAddress(props)).pipe(
690
875
  mergeMap((deviceAddress) => {
@@ -695,10 +880,23 @@ export class MotionMasterReqResClient {
695
880
  );
696
881
  }
697
882
 
883
+ /**
884
+ * {@inheritDoc setMotionControllerParameters}
885
+ */
698
886
  async setMotionControllerParametersAsync(props: MotionMasterMessage.Request.ISetMotionControllerParameters & DeviceRefObj, messageId?: string): Promise<void> {
699
887
  return await lastValueFrom(this.setMotionControllerParameters(props, messageId), { defaultValue: undefined });
700
888
  }
701
889
 
890
+ /**
891
+ * Enable motion controller.
892
+ *
893
+ * This request will set the mode of operation to CSP, CSV, or CST and optionally enable filtering.
894
+ *
895
+ * If filtering is enabled Motion Master will compute an intermediate target value in each cycle, and depending on the active mode of operation
896
+ * set the value of one of 0x607A Target position, 0x60FF Target velocity, 0x6071 Target torque to that value.
897
+ *
898
+ * Target value can be updated with {@link setMotionControllerParameters}.
899
+ */
702
900
  enableMotionController(props: MotionMasterMessage.Request.IEnableMotionController & DeviceRefObj, requestTimeout: number, messageId?: string) {
703
901
  return from(this.resolveDeviceAddress(props)).pipe(
704
902
  mergeMap((deviceAddress) => {
@@ -711,6 +909,9 @@ export class MotionMasterReqResClient {
711
909
  );
712
910
  }
713
911
 
912
+ /**
913
+ * {@inheritDoc enableMotionController}
914
+ */
714
915
  async enableMotionControllerAsync(props: MotionMasterMessage.Request.IEnableMotionController & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<{ controllerType: MotionMasterMessage.Request.EnableMotionController.ControllerType, enabled: boolean, filter: boolean }> {
715
916
  const status = await lastValueFrom(this.enableMotionController(props, requestTimeout, messageId));
716
917
 
@@ -721,6 +922,13 @@ export class MotionMasterReqResClient {
721
922
  throw new Error(`Failed to enable motion controller. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
722
923
  }
723
924
 
925
+ /**
926
+ * Disable motion controller.
927
+ *
928
+ * Depending on the current CiA402 state this request will do the following:
929
+ * - if device is in Quick stop active it will transition to Switch on disabled state
930
+ * - if device is in Operation enabled it will transition to Switched on state
931
+ */
724
932
  disableMotionController(props: MotionMasterMessage.Request.IDisableMotionController & DeviceRefObj, requestTimeout: number, messageId?: string) {
725
933
  return from(this.resolveDeviceAddress(props)).pipe(
726
934
  mergeMap((deviceAddress) => {
@@ -733,6 +941,9 @@ export class MotionMasterReqResClient {
733
941
  );
734
942
  }
735
943
 
944
+ /**
945
+ * {@inheritDoc disableMotionController}
946
+ */
736
947
  async disableMotionControllerAsync(props: MotionMasterMessage.Request.IDisableMotionController & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<{ controllerType: MotionMasterMessage.Request.EnableMotionController.ControllerType, enabled: boolean, filter: boolean }> {
737
948
  const status = await lastValueFrom(this.disableMotionController(props, requestTimeout, messageId));
738
949
 
@@ -743,6 +954,13 @@ export class MotionMasterReqResClient {
743
954
  throw new Error(`Failed to disable motion controller. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
744
955
  }
745
956
 
957
+ /**
958
+ * Set signal generator parameters.
959
+ *
960
+ * This request will update the signal generator parameters for a device on Motion Master.
961
+ * The selected signal generator is then started by {@link startSignalGenerator}.
962
+ * Signal generator must be started in the next 60 seconds before Motion Master invalidates the previously set parameters.
963
+ */
746
964
  setSignalGeneratorParameters(props: MotionMasterMessage.Request.ISetSignalGeneratorParameters & DeviceRefObj, requestTimeout: number, messageId?: string) {
747
965
  return from(this.resolveDeviceAddress(props)).pipe(
748
966
  mergeMap((deviceAddress) => {
@@ -755,6 +973,9 @@ export class MotionMasterReqResClient {
755
973
  );
756
974
  }
757
975
 
976
+ /**
977
+ * {@inheritDoc setSignalGeneratorParameters}
978
+ */
758
979
  async setSignalGeneratorParametersAsync(props: MotionMasterMessage.Request.ISetSignalGeneratorParameters & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
759
980
  const status = await lastValueFrom(this.setSignalGeneratorParameters(props, requestTimeout, messageId));
760
981
 
@@ -765,6 +986,47 @@ export class MotionMasterReqResClient {
765
986
  throw new Error(`Failed to set signal generator parameters. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
766
987
  }
767
988
 
989
+ /**
990
+ * Start signal generator.
991
+ *
992
+ * This request will start one of the signal generators based on the
993
+ * previously set parameters with {@link setSignalGeneratorParameters}.
994
+ *
995
+ * Signal generators are started by changing the mode of operation, putting device into Operation enabled CiA402 state,
996
+ * and setting the target value. What mode of operation is set depends on the type of signal generator and controller:
997
+ * - For the simple & advanced step response and sine wave one of CSP, CSV, or CST is set.
998
+ * - For ramp, trapezoidal, and bidirectional signal generators, also called profiles, one of PP, PV, TQ is set.
999
+ *
1000
+ * Currently the only profile type that firmware supports is ramp.
1001
+ * Motion Master uses this profile type to run the following signal generators: ramp, trapezoidal, and bidirectional.
1002
+ * Sine wave is also a profile but it is currently generated on Motion Master.
1003
+ * Firmwares >=5.2 will support generating and running sine wave profiles on devices. This will enable lossless profile
1004
+ * execution on possibly higher frequencies. Future Motion Master versions will switch to this method.
1005
+ *
1006
+ * For the simple step response Motion Master will only set the target value,
1007
+ * wait for the specified holding duration, and then send Quick Stop.
1008
+ *
1009
+ * The advanced step response is similar to simple, but after the holding duration it will set the new target
1010
+ * multiple times depending on the shape, and then send Quick Stop if repeat is set to NO. If repeat is set to YES,
1011
+ * then the signal generator will run indefinitely and users must stop it.
1012
+ *
1013
+ * Before running profiles Motion Master will check if device supports the profile modes by looking at 0x6502 Supported drives modes.
1014
+ * If profile modes are not supported then the profiles will be generated on Motion Master. This means the targets will be computed up-front.
1015
+ * If profiles are supported then ramp, trapezoidal, and bidirectional signal generators are run in profile mode of operation.
1016
+ * Motion Master will set the following parameters:
1017
+ * - 0x6081:00 Profile velocity (only for position controller)
1018
+ * - 0x6083:00 Profile acceleration
1019
+ * - 0x6084:00 Profile deceleration
1020
+ * Motion Master knows when the profile has completed by comparing the demand with the actual value, taking into account the
1021
+ * holding duration and the shape of profile. Once the profile is considered complete Motion Master will send Quick Stop, but
1022
+ * only if repeat is set to NO.
1023
+ *
1024
+ * Sine wave profile is generated up-front on Motion Master. Targets are computed for each master cycle (millisecond).
1025
+ * Once the last computed target has been set, Motion Master will send Quick Stop if repeat is set to NO.
1026
+ *
1027
+ * For all signal generators Motion Master will modify the requested target based on
1028
+ * the software position limits and it will return warning.
1029
+ */
768
1030
  startSignalGenerator(props: MotionMasterMessage.Request.IStartSignalGenerator & DeviceRefObj, requestTimeout: number, messageId?: string) {
769
1031
  return from(this.resolveDeviceAddress(props)).pipe(
770
1032
  mergeMap((deviceAddress) => {
@@ -777,6 +1039,9 @@ export class MotionMasterReqResClient {
777
1039
  );
778
1040
  }
779
1041
 
1042
+ /**
1043
+ * {@inheritDoc startSignalGenerator}
1044
+ */
780
1045
  async startSignalGeneratorAsync(props: MotionMasterMessage.Request.IStartSignalGenerator & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
781
1046
  const status = await lastValueFrom(this.startSignalGenerator(props, requestTimeout, messageId));
782
1047
 
@@ -787,6 +1052,11 @@ export class MotionMasterReqResClient {
787
1052
  throw new Error(`Failed to start signal generator. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
788
1053
  }
789
1054
 
1055
+ /**
1056
+ * Stop signal generator.
1057
+ *
1058
+ * This request will send Quick Stop to a device.
1059
+ */
790
1060
  stopSignalGenerator(props: MotionMasterMessage.Request.IStopSignalGenerator & DeviceRefObj, requestTimeout: number, messageId?: string) {
791
1061
  return from(this.resolveDeviceAddress(props)).pipe(
792
1062
  mergeMap((deviceAddress) => {
@@ -799,6 +1069,9 @@ export class MotionMasterReqResClient {
799
1069
  );
800
1070
  }
801
1071
 
1072
+ /**
1073
+ * {@inheritDoc stopSignalGenerator}
1074
+ */
802
1075
  async stopSignalGeneratorAsync(props: MotionMasterMessage.Request.IStopSignalGenerator & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
803
1076
  const status = await lastValueFrom(this.stopSignalGenerator(props, requestTimeout, messageId));
804
1077
 
@@ -861,6 +1134,13 @@ export class MotionMasterReqResClient {
861
1134
  throw new Error(`Failed get ethercat network state. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
862
1135
  }
863
1136
 
1137
+ /**
1138
+ * Set EtherCAT network state.
1139
+ *
1140
+ * Set EtherCAT network state to one of: BOOT, INIT, PRE-OP, SAFE-OP, OP.
1141
+ *
1142
+ * From PRE-OP to OP MM will reinitialize slaves in order to get new PDO mapping.
1143
+ */
864
1144
  setEthercatNetworkState(props: MotionMasterMessage.Request.ISetEthercatNetworkState & DeviceRefObj, requestTimeout: number, messageId?: string) {
865
1145
  return from(this.resolveDeviceAddress(props)).pipe(
866
1146
  mergeMap((deviceAddress) => {
@@ -873,6 +1153,9 @@ export class MotionMasterReqResClient {
873
1153
  );
874
1154
  }
875
1155
 
1156
+ /**
1157
+ * {@inheritDoc setEthercatNetworkState}
1158
+ */
876
1159
  async setEthercatNetworkStateAsync(props: MotionMasterMessage.Request.ISetEthercatNetworkState & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
877
1160
  const status = await lastValueFrom(this.setEthercatNetworkState(props, requestTimeout, messageId));
878
1161
 
@@ -911,16 +1194,47 @@ export class MotionMasterReqResClient {
911
1194
  throw new Error(`Failed start narrow angle calibration. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
912
1195
  }
913
1196
 
1197
+ /**
1198
+ * Set system client timeout.
1199
+ *
1200
+ * This request will update the client timeout.
1201
+ *
1202
+ * Client timeout specifies how long will Motion Master wait for the client to send ping or any other message before
1203
+ * considering it gone and clearing the resources (stopping procedures and monitorings) related to that client.
1204
+ *
1205
+ * Default client timeout is 1000ms.
1206
+ *
1207
+ * Client will typically change this once the connection is established.
1208
+ */
914
1209
  setSystemClientTimeout(props: MotionMasterMessage.Request.ISetSystemClientTimeout, messageId?: string) {
915
1210
  const setSystemClientTimeout = MotionMasterMessage.Request.SetSystemClientTimeout.create(props);
916
1211
  this.sendRequest({ setSystemClientTimeout }, messageId);
917
1212
  return EMPTY;
918
1213
  }
919
1214
 
1215
+ /**
1216
+ * {@inheritDoc setSystemClientTimeout}
1217
+ */
920
1218
  async setSystemClientTimeoutAsync(props: MotionMasterMessage.Request.ISetSystemClientTimeout, messageId?: string) {
921
1219
  return await lastValueFrom(this.setSystemClientTimeout(props, messageId), { defaultValue: undefined });
922
1220
  }
923
1221
 
1222
+ /**
1223
+ * Start system identification.
1224
+ *
1225
+ * pre-condition motor and encoder configured (setup wizard), without position and velocity tuning
1226
+ * torque only, used for computing the PID values during the auto tuning and full auto tuning and ctc which uses auto-tuning
1227
+ * error when, fault state, cannot set op mode, state, system failed, chirp profile is generate on master, each point in a cycle (millisecond)
1228
+ * CST sets targets, after the last target value MM sends Quick Stop, failed if fault, everything happens on master, but idea to run chirp on slave
1229
+ * and record data on slave, so that communication doesn't impact the result, MM records velocity and torque and computes, compute how many data is missing warning
1230
+ * error if there are not enough data, missing more than 80% of data points, if between 5%-80% warning, <5% ok, linear interpolation of missing data points
1231
+ * conversion every all velocity to SI, check amplitude of signal no torque, no velocity, or small amplitudes warning, errors 0 amplitudes
1232
+ * identification plant model, check stability of computed model (math only) can result in error, compute and write to plant_model.csv
1233
+ * set parameters integral limits 0x2012:4 0x2012:8, 0x2011:4 Position|Velocity loop integral limit
1234
+ * result plant_model.csv - check what values are and in what unit
1235
+ *
1236
+ * This request will turn motor
1237
+ */
924
1238
  startSystemIdentification(props: MotionMasterMessage.Request.IStartSystemIdentification & DeviceRefObj, requestTimeout: number, messageId?: string) {
925
1239
  return from(this.resolveDeviceAddress(props)).pipe(
926
1240
  mergeMap((deviceAddress) => {
@@ -933,6 +1247,9 @@ export class MotionMasterReqResClient {
933
1247
  );
934
1248
  }
935
1249
 
1250
+ /**
1251
+ * {@inheritDoc startSystemIdentification}
1252
+ */
936
1253
  async startSystemIdentificationAsync(props: MotionMasterMessage.Request.IStartSystemIdentification & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<void> {
937
1254
  const status = await lastValueFrom(this.startSystemIdentification(props, requestTimeout, messageId));
938
1255
 
@@ -943,6 +1260,15 @@ export class MotionMasterReqResClient {
943
1260
  throw new Error(`Failed start system identification. ${status.error?.code}: ${status.error?.message} ${JSON.stringify(props)}`);
944
1261
  }
945
1262
 
1263
+ /**
1264
+ * Get circulo encoder magnet distance.
1265
+ *
1266
+ * check encoder ordinal (1 or 2)
1267
+ * ring revision
1268
+ * is it based on fw 5 in fw <5 this doesn't exist error in that case
1269
+ * read encoder registers with OS command, registers read 2B and 2F, some values go through math depending on the version of Circulo, port, distance value is computed
1270
+ * @todo check what OS command
1271
+ */
946
1272
  getCirculoEncoderMagnetDistance(props: MotionMasterMessage.Request.IGetCirculoEncoderMagnetDistance & DeviceRefObj, requestTimeout: number, messageId?: string) {
947
1273
  return from(this.resolveDeviceAddress(props)).pipe(
948
1274
  mergeMap((deviceAddress) => {
@@ -955,6 +1281,9 @@ export class MotionMasterReqResClient {
955
1281
  );
956
1282
  }
957
1283
 
1284
+ /**
1285
+ * {@inheritDoc getCirculoEncoderMagnetDistance}
1286
+ */
958
1287
  async getCirculoEncoderMagnetDistanceAsync(props: MotionMasterMessage.Request.IGetCirculoEncoderMagnetDistance & DeviceRefObj, requestTimeout: number, messageId?: string): Promise<{ distance: number | null | undefined, encoderOrdinal: number | null | undefined, position: number | null | undefined }> {
959
1288
  const status = await lastValueFrom(this.getCirculoEncoderMagnetDistance(props, requestTimeout, messageId));
960
1289
 
@@ -965,6 +1294,39 @@ export class MotionMasterReqResClient {
965
1294
  throw new Error(`Failed to get circulo encoder magnet distance. ${JSON.stringify(props)}`);
966
1295
  }
967
1296
 
1297
+ /**
1298
+ * Start circulo encoder narrow angle calibration procedure.
1299
+ *
1300
+ * check encoder ordinal
1301
+ * check fw version 5
1302
+ * check if SMM, has to be disable safety encoder source type
1303
+ * ignore biss status bit, OS command for encoder to ignore BiSS status bits
1304
+ * initialize velocity profile PV
1305
+ * check software position limits
1306
+ * initialize intern values, feed constant, single turn resolution, si unit, get parameter values, gear ratio, what encoder, port, if gear ratio etc etc
1307
+ * if gear box and si unit velocity not mRPM, and position only encoder, and si unit * feed constant is <10% of gear ratio, error not enough velocity resolution
1308
+ * hardware description file, different values for RPM for different version of Circulo, inner|outer encoder ring, multiturn, not, change profile acc|dec max motor speed etc.
1309
+ * after everything rollback
1310
+ * check if enough turns is possible, if not enough space, error
1311
+ * set default calibration register values
1312
+ * set biss to raw mode, no longer position - raw position
1313
+ * ic house lib is initialized
1314
+ * remove previous files for high resolution data 4kHz, for each iteration, hight res data contains raw data from encoder
1315
+ * OS command configure HRD streaming, automatically sets data from encoder, can only configure how long to record
1316
+ * slave saves data to HRD files
1317
+ * after each iteration which lasts 6 seconds, every iteration is one direction, MM takes data recorded in HRD files and puts that passes that to
1318
+ * master/noniuns track data it takes 32-bit values are both splitted into 2 parts (master nonius), two arrays one master one nonious
1319
+ * that data are passed to iChouse library which give depending on encoder type track master nonius, return some sine offset, cos offset, gain, etc.
1320
+ * that procuderu can have errors: data out of range, motor to slow, bad data, internal error - if error in any iteration the procedure stops
1321
+ * if no error then check if calibration succeeded depending on the values (iterativelly improve values by writing them back to some registers), if not run another iteration
1322
+ * max iteration to run is 13 (up to 2 mins)
1323
+ * treshold is determined by Petr
1324
+ * if iteration is successfull, HRD files are moved after deinitializing (succesfful run),
1325
+ * the result of calibration is setting multiple register values
1326
+ * all parameters are reverted back, motor is rotated back to the starging position, commutation offset measurement (just one OS command) is run again
1327
+ * how many iterations are run are based on results
1328
+ * final itertion is for outputting does nothhing just measures the values
1329
+ */
968
1330
  startCirculoEncoderNarrowAngleCalibrationProcedure(props: MotionMasterMessage.Request.IStartCirculoEncoderNarrowAngleCalibrationProcedure & DeviceRefObj, requestTimeout: number, messageId?: string) {
969
1331
  return from(this.resolveDeviceAddress(props)).pipe(
970
1332
  mergeMap((deviceAddress) => {
@@ -977,6 +1339,9 @@ export class MotionMasterReqResClient {
977
1339
  );
978
1340
  }
979
1341
 
1342
+ /**
1343
+ * {@inheritDoc startCirculoEncoderNarrowAngleCalibrationProcedure}
1344
+ */
980
1345
  async startCirculoEncoderNarrowAngleCalibrationProcedureAsync(props: MotionMasterMessage.Request.IStartCirculoEncoderNarrowAngleCalibrationProcedure & DeviceRefObj, requestTimeout: number, messageId?: string) {
981
1346
  const status = await lastValueFrom(this.startCirculoEncoderNarrowAngleCalibrationProcedure(props, requestTimeout, messageId));
982
1347