@synapticon/motion-master-client 2.3.0-beta.0 → 6.0.0-alpha.100

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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/dist/api-error.d.ts +25 -0
  3. package/dist/api-error.js +52 -0
  4. package/dist/client.d.ts +20 -0
  5. package/dist/client.js +37 -0
  6. package/dist/constants.d.ts +2 -0
  7. package/dist/constants.js +8 -0
  8. package/dist/eni.d.ts +197 -0
  9. package/dist/eni.js +61 -0
  10. package/dist/generated/Api.d.ts +1886 -0
  11. package/dist/generated/Api.js +1293 -0
  12. package/dist/generated/data-contracts.d.ts +2282 -0
  13. package/dist/generated/data-contracts.js +12 -0
  14. package/dist/generated/http-client.d.ts +60 -0
  15. package/dist/generated/http-client.js +171 -0
  16. package/dist/hex.d.ts +2 -0
  17. package/dist/hex.js +20 -0
  18. package/dist/index.d.ts +13 -7
  19. package/dist/index.js +20 -22
  20. package/dist/lan.d.ts +4 -0
  21. package/dist/lan.js +59 -0
  22. package/dist/mmpd.d.ts +53 -0
  23. package/dist/mmpd.js +322 -0
  24. package/dist/sdo.d.ts +17 -0
  25. package/dist/sdo.js +219 -0
  26. package/dist/web-socket-connection.d.ts +70 -0
  27. package/dist/web-socket-connection.js +237 -0
  28. package/dist/wire-time.d.ts +2 -0
  29. package/dist/wire-time.js +18 -0
  30. package/package.json +29 -35
  31. package/.vscode/launch.json +0 -23
  32. package/README.md +0 -118
  33. package/box.config.json +0 -7
  34. package/dist/index.d.ts.map +0 -1
  35. package/dist/index.js.map +0 -1
  36. package/dist/motion-master-client-web-socket-connection.d.ts +0 -43
  37. package/dist/motion-master-client-web-socket-connection.d.ts.map +0 -1
  38. package/dist/motion-master-client-web-socket-connection.js +0 -91
  39. package/dist/motion-master-client-web-socket-connection.js.map +0 -1
  40. package/dist/motion-master-client.d.ts +0 -105
  41. package/dist/motion-master-client.d.ts.map +0 -1
  42. package/dist/motion-master-client.js +0 -348
  43. package/dist/motion-master-client.js.map +0 -1
  44. package/dist/motion-master-notification-subscribe-data.d.ts +0 -8
  45. package/dist/motion-master-notification-subscribe-data.d.ts.map +0 -1
  46. package/dist/motion-master-notification-subscribe-data.js +0 -3
  47. package/dist/motion-master-notification-subscribe-data.js.map +0 -1
  48. package/dist/motion-master-notification-web-socket-connection.d.ts +0 -56
  49. package/dist/motion-master-notification-web-socket-connection.d.ts.map +0 -1
  50. package/dist/motion-master-notification-web-socket-connection.js +0 -115
  51. package/dist/motion-master-notification-web-socket-connection.js.map +0 -1
  52. package/dist/motion-master-notification.d.ts +0 -31
  53. package/dist/motion-master-notification.d.ts.map +0 -1
  54. package/dist/motion-master-notification.js +0 -44
  55. package/dist/motion-master-notification.js.map +0 -1
  56. package/dist/util.d.ts +0 -16
  57. package/dist/util.d.ts.map +0 -1
  58. package/dist/util.js +0 -24
  59. package/dist/util.js.map +0 -1
  60. package/licenses.format.json +0 -4
  61. package/motion-master-client.config.json +0 -7
  62. package/smm_data_dic.json +0 -1213
  63. package/src/index.ts +0 -7
  64. package/src/motion-master-client-web-socket-connection.ts +0 -104
  65. package/src/motion-master-client.ts +0 -468
  66. package/src/motion-master-notification-subscribe-data.ts +0 -7
  67. package/src/motion-master-notification-web-socket-connection.ts +0 -143
  68. package/src/motion-master-notification.ts +0 -51
  69. package/src/util.ts +0 -22
  70. package/test.sh +0 -122
  71. package/tsconfig.json +0 -63
  72. package/tslint.json +0 -118
@@ -0,0 +1,1293 @@
1
+ /* eslint-disable */
2
+ /* tslint:disable */
3
+ // @ts-nocheck
4
+ /*
5
+ * ---------------------------------------------------------------
6
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
7
+ * ## ##
8
+ * ## AUTHOR: acacode ##
9
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
10
+ * ---------------------------------------------------------------
11
+ */
12
+ import { ContentType, HttpClient } from "./http-client.js";
13
+ export class Api extends HttpClient {
14
+ /**
15
+ * No description
16
+ *
17
+ * @name GetAdapters
18
+ * @summary List network adapters on the host
19
+ * @request GET:/api/adapters
20
+ */
21
+ getAdapters = (params = {}) => this.request({
22
+ path: `/api/adapters`,
23
+ method: "GET",
24
+ format: "json",
25
+ ...params,
26
+ });
27
+ /**
28
+ * No description
29
+ *
30
+ * @name GetVersion
31
+ * @summary Get Motion Master version
32
+ * @request GET:/api/version
33
+ */
34
+ getVersion = (params = {}) => this.request({
35
+ path: `/api/version`,
36
+ method: "GET",
37
+ format: "json",
38
+ ...params,
39
+ });
40
+ /**
41
+ * @description Returns this document — the OpenAPI specification embedded in the running binary — as YAML. Lets a client discover the exact API contract of the server it is talking to without a packaged file or a matching source checkout; the served spec always matches the running version.
42
+ *
43
+ * @name GetSwaggerYml
44
+ * @summary Get this OpenAPI specification
45
+ * @request GET:/api/swagger.yml
46
+ */
47
+ getSwaggerYml = (params = {}) => this.request({
48
+ path: `/api/swagger.yml`,
49
+ method: "GET",
50
+ ...params,
51
+ });
52
+ /**
53
+ * @description The effective configuration after merging the JSONC config file (if any) over the built-in defaults — the same `Config` the server booted with. The config file is either the one passed via `--config` or a `motion-master.jsonc` auto-discovered next to the executable (`--config` wins). Read-only; changing it requires editing the config file and restarting. The shape mirrors the config file (server, fieldbus, logging, tls, gameLoop, recorder, parameterCache, parameters).
54
+ *
55
+ * @name GetStartedConfig
56
+ * @summary Get the configuration Motion Master was started with
57
+ * @request GET:/api/config
58
+ */
59
+ getStartedConfig = (params = {}) => this.request({
60
+ path: `/api/config`,
61
+ method: "GET",
62
+ format: "json",
63
+ ...params,
64
+ });
65
+ /**
66
+ * @description A best-effort snapshot of the operating system and hardware Motion Master is running on, for the Connection page's System panel. Every field is best-effort: one that cannot be determined on the current platform is returned empty (strings) or zero (numbers) rather than failing the request. Collected on demand — not cached.
67
+ *
68
+ * @name GetSystemInfo
69
+ * @summary Get host OS and hardware information
70
+ * @request GET:/api/system-info
71
+ */
72
+ getSystemInfo = (params = {}) => this.request({
73
+ path: `/api/system-info`,
74
+ method: "GET",
75
+ format: "json",
76
+ ...params,
77
+ });
78
+ /**
79
+ * @description Reports the validity window and identity of the certificate the server is currently serving. `expiresSoon` trips within 7 days of `notAfter`, so the PWA can prompt the user to refresh. Note that an already-expired certificate blocks the browser from reaching this endpoint at all; the binary self-heals an expired/missing certificate at startup (fetching from the rolling release) and exposes `--update-cert` for manual refresh. Returns 500 if the certificate file cannot be read or parsed.
80
+ *
81
+ * @name GetCert
82
+ * @summary Get the TLS certificate validity window
83
+ * @request GET:/api/cert
84
+ */
85
+ getCert = (params = {}) => this.request({
86
+ path: `/api/cert`,
87
+ method: "GET",
88
+ format: "json",
89
+ ...params,
90
+ });
91
+ /**
92
+ * @description Downloads a fresh certificate and key from the rolling release, validates the pair (parses, CN matches, not expired, key matches cert), and atomically installs them next to the binary. The new certificate only takes effect after a restart (the TLS listener loads the certificate once at startup), indicated by `restartRequired` in the response. Use the still-valid proactive case; an already-expired certificate is healed by the binary at startup instead.
93
+ *
94
+ * @name RefreshCert
95
+ * @summary Fetch and install a fresh TLS certificate
96
+ * @request POST:/api/cert/refresh
97
+ */
98
+ refreshCert = (params = {}) => this.request({
99
+ path: `/api/cert/refresh`,
100
+ method: "POST",
101
+ format: "json",
102
+ ...params,
103
+ });
104
+ /**
105
+ * No description
106
+ *
107
+ * @name GetDevice
108
+ * @summary Get a single fieldbus device by bus position
109
+ * @request GET:/api/devices/{slavePosition}
110
+ */
111
+ getDevice = (slavePosition, params = {}) => this.request({
112
+ path: `/api/devices/${slavePosition}`,
113
+ method: "GET",
114
+ format: "json",
115
+ ...params,
116
+ });
117
+ /**
118
+ * @description Constructs the requested fieldbus driver and makes it available for subsequent calls to /api/scan. The body is the `fieldbus` block of the config file, and the same rules check it. The driver defaults to SOEM when omitted. SOEM opens the network interface. It has no adapter auto-detect: a network adapter must be supplied, otherwise init fails. SPoE connects to each drive at scan time. It needs `ipAddresses`, one address per drive, and the list order is the position order.
119
+ *
120
+ * @name Init
121
+ * @summary Initialise the fieldbus driver
122
+ * @request POST:/api/init
123
+ */
124
+ init = (data, params = {}) => this.request({
125
+ path: `/api/init`,
126
+ method: "POST",
127
+ body: data,
128
+ type: ContentType.Json,
129
+ format: "json",
130
+ ...params,
131
+ });
132
+ /**
133
+ * @description Compares the bus with the last scan. Two changes make that scan wrong. A device that resets, for example after a power cycle, loses the station address the scan gave it, and no longer answers at it. A device added to or removed from the chain changes the number of devices on the wire, even when every known device still answers. The server reads the AL state of every scanned device, as `GET /api/devices/state` does, and with SOEM it counts the devices on the wire with one broadcast read. A device that lost its station address still answers a broadcast, so it is counted. Before the first scan, `devicesScanned` is 0, so any device on the wire makes `required` true. A device swapped for another at the same position without a reset is not detected, because it keeps the old address and answers normally.
134
+ *
135
+ * @name GetScanStatus
136
+ * @summary Read whether the bus needs a new scan
137
+ * @request GET:/api/scan
138
+ */
139
+ getScanStatus = (params = {}) => this.request({
140
+ path: `/api/scan`,
141
+ method: "GET",
142
+ format: "json",
143
+ ...params,
144
+ });
145
+ /**
146
+ * @description Scans the fieldbus for slaves and configures their sync managers and FMMUs. Must be called after a successful POST /api/init. With SOEM, slaves remain in INIT state after this call, and state transitions are driven by subsequent API calls. An SPoE drive starts in OP. When a scan finds a device already in SAFE-OP or OP, it reads that device's object dictionary and publishes the process image, so the exchange starts with no state change. The dictionary comes from the parameter cache when the cache has it. Otherwise the first read of a drive model takes several seconds. If this step fails, the scan still returns 200 and the server logs a warning. A later state change to SAFE-OP or OP maps the image again. An empty bus is a successful scan returning 0 slaves, not an error: the master cannot distinguish a bus with no devices powered from a disconnected one, and the user recovers by powering devices on and scanning again. A 500 is returned only on a genuine driver failure (e.g. no driver initialised). A scan does not wait for work already in flight. A procedure that is running keeps running against the devices it started on, and fails at its next bus transfer. Retained procedure snapshots are discarded, because a position may name different hardware after a scan. A scan frees the recording, the retained process images and the previous device set, so it first waits up to 200 ms for the real-time loop to leave the cycle it is in. If the loop is stalled — a cyclic task that overruns, or severe CPU contention — the scan fails with 500 and changes nothing, rather than freeing memory the loop is still reading. `GET /api/game-loop` shows the stall. The same applies to `POST /api/process-data`, which re-allocates the recorder. `POST /api/reset` never fails this way: it holds the memory back instead and reclaims it later.
147
+ *
148
+ * @name Scan
149
+ * @summary Scan the bus for slaves
150
+ * @request POST:/api/scan
151
+ */
152
+ scan = (params = {}) => this.request({
153
+ path: `/api/scan`,
154
+ method: "POST",
155
+ format: "json",
156
+ ...params,
157
+ });
158
+ /**
159
+ * @description Tears down all master-side state — unpublishes the process image, frees the recorder, removes all Device objects, and closes the network interface (releasing the NIC). It does not command the slaves to any AL state; the master simply stops talking to the bus. After this returns, POST /api/init and POST /api/scan may be called again.
160
+ *
161
+ * @name Reset
162
+ * @summary Reset the fieldbus driver and clear the device list
163
+ * @request POST:/api/reset
164
+ */
165
+ reset = (params = {}) => this.request({
166
+ path: `/api/reset`,
167
+ method: "POST",
168
+ format: "json",
169
+ ...params,
170
+ });
171
+ /**
172
+ * @description Returns the most recent in-memory log lines as plain text, one entry per line. The ring buffer holds up to 100 000 entries (~10 MB).
173
+ *
174
+ * @name GetLog
175
+ * @summary Get recent log output
176
+ * @request GET:/api/log
177
+ */
178
+ getLog = (params = {}) => this.request({
179
+ path: `/api/log`,
180
+ method: "GET",
181
+ ...params,
182
+ });
183
+ /**
184
+ * @description Reports what Motion Master knows about the auto-tuning process. The tuning calculations run in a separate executable, which Motion Master starts at startup and reaches over loopback. That executable is downloaded by the install scripts rather than shipped in a release, so a machine may not have it — this endpoint is how a client tells the four cases apart: switched off in the configuration, not installed, installed but would not start, or running. The values are a startup snapshot. `started` says the process answered when Motion Master waited for it, not that it is alive now. Nothing polls it: a call is the honest test, and `POST /api/auto-tuning/run` makes one.
185
+ *
186
+ * @name GetAutoTuning
187
+ * @summary Auto-tuning process status
188
+ * @request GET:/api/auto-tuning
189
+ */
190
+ getAutoTuning = (params = {}) => this.request({
191
+ path: `/api/auto-tuning`,
192
+ method: "GET",
193
+ format: "json",
194
+ ...params,
195
+ });
196
+ /**
197
+ * @description Forwards the request to the auto-tuning process and returns its reply unchanged. The body is a JSON object with `run`, naming the function, and `data`, holding that function's inputs — the same shape the auto-tuning program takes on its own API. **A rejected input is reported with 200 and an `error` property**, not with a 4xx. The auto-tuning program keeps 4xx and 5xx for a malformed request, an unknown function name, and a routine that threw, so a client tests for `error` in the body rather than relying on the status alone. Motion Master passes both through untouched; only 400, 502 and 503 below are its own. Measurement data travels in the body. `identify_plant_model` takes the chirp measurements as `data.csv` text, and the bode data comes back inline as `bode`, so no file is written and nothing needs a shared filesystem view. The schemas here are a copy of the auto-tuning program's own, taken from version 3.1.1, and they exist so that the generated clients are typed. That program's current description is served verbatim at `GET /api/auto-tuning/swagger.yml`, which is the authority if the two ever disagree.
198
+ *
199
+ * @name RunAutoTuning
200
+ * @summary Run an auto-tuning function
201
+ * @request POST:/api/auto-tuning/run
202
+ */
203
+ runAutoTuning = (data, params = {}) => this.request({
204
+ path: `/api/auto-tuning/run`,
205
+ method: "POST",
206
+ body: data,
207
+ type: ContentType.Json,
208
+ format: "json",
209
+ ...params,
210
+ });
211
+ /**
212
+ * @description Serves the OpenAPI document the auto-tuning process carries, fetched from it on each request. The schemas under `/api/auto-tuning/run` above are a copy of these, so this is where to look when a version of the program disagrees with them.
213
+ *
214
+ * @name GetAutoTuningSpec
215
+ * @summary The auto-tuning program's own API description
216
+ * @request GET:/api/auto-tuning/swagger.yml
217
+ */
218
+ getAutoTuningSpec = (params = {}) => this.request({
219
+ path: `/api/auto-tuning/swagger.yml`,
220
+ method: "GET",
221
+ ...params,
222
+ });
223
+ /**
224
+ * @description Returns the static catalogue of EtherCAT AL Status Codes defined in ETG.1000.6 §6.4.1. These codes appear in the alStatusCode field of the device state response when an error is present and identify the cause of the state transition failure.
225
+ *
226
+ * @name GetAlStatusCodes
227
+ * @summary List all known AL Status Codes
228
+ * @request GET:/api/meta/al-status-codes
229
+ */
230
+ getAlStatusCodes = (params = {}) => this.request({
231
+ path: `/api/meta/al-status-codes`,
232
+ method: "GET",
233
+ format: "json",
234
+ ...params,
235
+ });
236
+ /**
237
+ * @description Returns the static catalogue of well-known EtherCAT Slave Controller registers from the Beckhoff ESC datasheet (Section II) and ETG.1000.4. Use the address and length fields as inputs to the device register read/write endpoints.
238
+ *
239
+ * @name GetRegisters
240
+ * @summary List all known ESC registers
241
+ * @request GET:/api/meta/esc-registers
242
+ */
243
+ getRegisters = (params = {}) => this.request({
244
+ path: `/api/meta/esc-registers`,
245
+ method: "GET",
246
+ format: "json",
247
+ ...params,
248
+ });
249
+ /**
250
+ * @description Returns the static catalogue of standard File over EtherCAT (FoE) error codes defined in ETG.1000.6 §5.5. These codes appear in the FoE ERROR packet sent by the slave when a file transfer fails, and are surfaced in the error message returned by the file read endpoint. Vendor-specific codes outside the 0x8000 range are not listed here.
251
+ *
252
+ * @name GetFoeErrorCodes
253
+ * @summary List all known FoE error codes
254
+ * @request GET:/api/meta/foe-error-codes
255
+ */
256
+ getFoeErrorCodes = (params = {}) => this.request({
257
+ path: `/api/meta/foe-error-codes`,
258
+ method: "GET",
259
+ format: "json",
260
+ ...params,
261
+ });
262
+ /**
263
+ * @description Returns the static catalogue of CoE SDO abort codes defined in ETG.1000.6 §5.6.2.7.2, Table 41 (which reproduces the CANopen CiA 301 abort transfer codes). A slave returns one of these when a mailbox SDO read/write fails; the code is embedded in the error text returned by the SDO endpoints.
264
+ *
265
+ * @name GetSdoAbortCodes
266
+ * @summary List all known CoE SDO abort codes
267
+ * @request GET:/api/meta/sdo-abort-codes
268
+ */
269
+ getSdoAbortCodes = (params = {}) => this.request({
270
+ path: `/api/meta/sdo-abort-codes`,
271
+ method: "GET",
272
+ format: "json",
273
+ ...params,
274
+ });
275
+ /**
276
+ * @description The register maps of the two iC-Haus chips inside a Circulo's internal encoder — the reference tables for POST /api/devices/{slavePosition}/procedures/encoder-register-communication, and documentation in their own right. Static: they describe the chips, not any connected device, so no bus and no scan are needed. Two chips, and the iC-MU is the one in charge. The iC-MU is the position encoder proper, a magnetic off-axis absolute chip speaking BiSS-C to the drive. The iC-PVL is a battery-buffered Hall multiturn counter behind it, reached over I2C through the iC-MU rather than directly. The response is a list of register *spaces*, not of registers, because an address alone names nothing: the same iC-PVL address 0x00 is a configuration register in one space and the status register in another, depending on which I2C device id addressed it. Each space says which chip it belongs to and how it is reached. A row is one address, or the range the datasheet prints as one row — `address` and `lastAddress` are equal in the ordinary case. `fields` lists the named pieces of the register, most significant bit first, each with the datasheet's own one-line description; it is empty exactly when `reserved` is true. `layout` is those same fields rendered as the datasheet prints the row, e.g. `GC_M(1:0) | GF_M(5:0)`, derived rather than stored so it cannot disagree with them. `spiOnly` marks the iC-MU's 0x80-0xAF, which is reachable over SPI only and so beyond the drive's register communication service. A field's `bits` is its own bit slice as the register map prints it, not its position within the byte: `GC_M(1:0)` occupies bits 7:6 of address 0x00 and reads "1:0" because those are the field's two bits. A value wider than one register is spread across several, which is why `RESABZ` reads "7:0" at 0x13 and "15:8" at 0x14. Granularity follows each datasheet's own register map. The iC-PVL prints its status registers bit by bit, so those arrive as eight fields; the iC-MU prints its own as `STATUS0(7:0)`, so that is one field. Transcribed from the vendors' datasheets: iC-MU Series Rev B1 (Tables 90 and 91) and iC-PVL Rev F2 (Tables 5, 6 and 7), with descriptions from their CONFIGURATION PARAMETERS indexes and, for the iC-PVL status bytes, its status tables. The prose behind those one-liners is not reproduced.
277
+ *
278
+ * @name GetIcHausRegisters
279
+ * @summary The Circulo internal encoder's register maps
280
+ * @request GET:/api/meta/ic-haus-registers
281
+ */
282
+ getIcHausRegisters = (params = {}) => this.request({
283
+ path: `/api/meta/ic-haus-registers`,
284
+ method: "GET",
285
+ format: "json",
286
+ ...params,
287
+ });
288
+ /**
289
+ * @description The register map of the Integro's internal (Kübler) encoder — the reference table for POST /api/devices/{slavePosition}/procedures/kuebler-register-communication, and documentation in its own right. Static: it describes the encoder type, not any connected device, so it needs no bus and no scan. Transcribed from the vendor's own draft table, then corrected against what the encoder really does: the draft calls four registers not implemented that the encoder calibration sequence reads or writes, and leaves out four more. Each entry carries the width, the access type, whether the encoder implements it, and the vendor's bit definitions verbatim, which for a bit-field register is the only place its meaning is recorded. A register the draft does not document says so in `definition`, because a reader has nowhere else to look. `format` says how to read the assembled value, because signedness is not uniform and one register is not a single number at all: `unsigned`, `signed`, `bitField` (the number means nothing on its own), or `signedHalves` — two signed 16-bit values packed into 32 bits, which register 0x3C is the only case of. `readableInOneCommand` says whether one command can carry the register, since the command's length byte caps at 4 bytes. Every register fits today, so it is true for all of them.
290
+ *
291
+ * @name GetKueblerRegisters
292
+ * @summary The Integro internal encoder's register map
293
+ * @request GET:/api/meta/kuebler-registers
294
+ */
295
+ getKueblerRegisters = (params = {}) => this.request({
296
+ path: `/api/meta/kuebler-registers`,
297
+ method: "GET",
298
+ format: "json",
299
+ ...params,
300
+ });
301
+ /**
302
+ * @description Returns the static catalogue of CoE mailbox error codes defined in ETG.1000.4, Table 30 ("Error Reply Service Data"). A slave returns one of these when the mailbox layer (below a specific protocol such as CoE/FoE) rejects a transfer; the code is embedded in the error text the mailbox endpoints return.
303
+ *
304
+ * @name GetMailboxErrorCodes
305
+ * @summary List all known CoE mailbox error codes
306
+ * @request GET:/api/meta/mailbox-error-codes
307
+ */
308
+ getMailboxErrorCodes = (params = {}) => this.request({
309
+ path: `/api/meta/mailbox-error-codes`,
310
+ method: "GET",
311
+ format: "json",
312
+ ...params,
313
+ });
314
+ /**
315
+ * @description Returns the static catalogue of ETG.1020 data type codes used in CoE object dictionary entries. Each entry of a `DeviceParameter` carries a `dataType` field whose value is one of the codes listed here.
316
+ *
317
+ * @name GetObjectDataTypes
318
+ * @summary List all known CoE object dictionary data types
319
+ * @request GET:/api/meta/object-data-types
320
+ */
321
+ getObjectDataTypes = (params = {}) => this.request({
322
+ path: `/api/meta/object-data-types`,
323
+ method: "GET",
324
+ format: "json",
325
+ ...params,
326
+ });
327
+ /**
328
+ * @description Enumerates the entire CoE object dictionary of the device at `slavePosition` via the SDO Info service ("Get Object List" → "Get Object Description" → "Get Entry Description") and rebuilds its parameter map. The device must be in PRE-OP, SAFE-OP, or OP (mailbox communication active). On a fully populated drive the call can take several seconds. When `readValues=true` each entry is additionally read via SDO upload and the decoded value is stored on the parameter. An entry that fails to read keeps the value of an earlier read with `syncState` `stale`, or the type-appropriate default with `syncState` `unknown`, and the call still succeeds. Check `syncState` to find the entries that were not read.
329
+ *
330
+ * @name InitializeDeviceParameters
331
+ * @summary Initialise the parameter list for a device by enumerating its object dictionary
332
+ * @request POST:/api/devices/{slavePosition}/parameters/init
333
+ */
334
+ initializeDeviceParameters = (slavePosition, query, params = {}) => this.request({
335
+ path: `/api/devices/${slavePosition}/parameters/init`,
336
+ method: "POST",
337
+ query: query,
338
+ format: "json",
339
+ ...params,
340
+ });
341
+ /**
342
+ * @description Re-reads the value of every readable parameter already loaded for the device at `slavePosition`, leaving the parameter list itself intact (it does not re-enumerate the object dictionary — use `POST …/parameters/init` for that). Each value is read PDO-aware: from the live process image when the device is exchanging and the object is PDO-mapped, otherwise via an SDO upload over the mailbox. Write-only objects are skipped. Best-effort: an entry that fails to read keeps its cached value, a `synced` entry becomes `stale`, and the call still succeeds. Returns the refreshed list, ordered by `(index, subindex)`.
343
+ *
344
+ * @name ReadAllDeviceParameters
345
+ * @summary Refresh the cached value of every parameter without re-enumerating the dictionary
346
+ * @request POST:/api/devices/{slavePosition}/parameters/read
347
+ */
348
+ readAllDeviceParameters = (slavePosition, params = {}) => this.request({
349
+ path: `/api/devices/${slavePosition}/parameters/read`,
350
+ method: "POST",
351
+ format: "json",
352
+ ...params,
353
+ });
354
+ /**
355
+ * @description Returns the parameter list populated by the most recent call to `POST /api/devices/{slavePosition}/parameters/init`. Empty before the first call. Entries are ordered ascending by `(index, subindex)`.
356
+ *
357
+ * @name GetDeviceParameters
358
+ * @summary Read the previously initialised parameter list for a device
359
+ * @request GET:/api/devices/{slavePosition}/parameters
360
+ */
361
+ getDeviceParameters = (slavePosition, params = {}) => this.request({
362
+ path: `/api/devices/${slavePosition}/parameters`,
363
+ method: "GET",
364
+ format: "json",
365
+ ...params,
366
+ });
367
+ /**
368
+ * @description Reads one parameter of the device at `slavePosition` and returns the full `DeviceParameter` (value plus metadata: data type, access, sync state). Unlike the raw `GET …/sdo/{index}/{subindex}` endpoint, this is PDO-aware. With `source=auto` (the default) the value is taken from the live process image when the device is exchanging and the object is PDO-mapped, otherwise via an SDO upload, otherwise from the cache. With `source=cache` the cached value is served with no bus access at all. Both `index` and `subindex` accept decimal or hexadecimal notation; prefix with `0x` for hex (e.g. `0x6064` or `24676` for object 0x6064).
369
+ *
370
+ * @name ReadParameter
371
+ * @summary Read a single parameter value (PDO-aware, with optional cache-only mode)
372
+ * @request GET:/api/devices/{slavePosition}/parameters/{index}/{subindex}
373
+ */
374
+ readParameter = (slavePosition, index, subindex, query, params = {}) => this.request({
375
+ path: `/api/devices/${slavePosition}/parameters/${index}/${subindex}`,
376
+ method: "GET",
377
+ query: query,
378
+ format: "json",
379
+ ...params,
380
+ });
381
+ /**
382
+ * @description Writes one parameter of the device at `slavePosition`. The value is converted to the parameter's declared data type, then routed: staged into the process image when the device is exchanging and the object is output (RxPDO) mapped — sent on the next cycle — otherwise written via an SDO download, otherwise held in the cache as pending until the device is back online. This is the endpoint to set a target (e.g. target torque 0x6071) or the modes of operation: it knows which objects are PDO-mapped and stages them into the process data, falling back to SDO transparently. Returns the updated `DeviceParameter` (with the converted value and resulting sync state). A value the data type cannot hold is refused with 400, and nothing is written. That is a number outside the limits of an integer type (300 or -1 for an UNSIGNED8), a number with a fraction for an integer type, or a number outside the range of a REAL32. Every value that fits the data type is sent unchanged, even one outside the object's documented range, because the drive decides whether a value is valid. To send arbitrary bytes, use `sdoDownload`. Both `index` and `subindex` accept decimal or hexadecimal notation; prefix with `0x` for hex.
383
+ *
384
+ * @name WriteParameter
385
+ * @summary Write a single parameter value (PDO-aware)
386
+ * @request PUT:/api/devices/{slavePosition}/parameters/{index}/{subindex}
387
+ */
388
+ writeParameter = (slavePosition, index, subindex, data, params = {}) => this.request({
389
+ path: `/api/devices/${slavePosition}/parameters/${index}/${subindex}`,
390
+ method: "PUT",
391
+ body: data,
392
+ type: ContentType.Json,
393
+ format: "json",
394
+ ...params,
395
+ });
396
+ /**
397
+ * @description Reads `length` bytes from the ESC register at `address` on the slave at `slavePosition` using a Configured-Address Read (FPRD) datagram. Pass `address` in decimal (e.g. 272 for DL Status at 0x0110).
398
+ *
399
+ * @name ReadRegister
400
+ * @summary Read bytes from an ESC register
401
+ * @request GET:/api/devices/{slavePosition}/registers/{address}
402
+ */
403
+ readRegister = (slavePosition, address, query, params = {}) => this.request({
404
+ path: `/api/devices/${slavePosition}/registers/${address}`,
405
+ method: "GET",
406
+ query: query,
407
+ format: "json",
408
+ ...params,
409
+ });
410
+ /**
411
+ * @description Writes bytes to the ESC register at `address` on the slave at `slavePosition` using a Configured-Address Write (FPWR) datagram. Pass `address` in decimal (e.g. 272 for DL Status at 0x0110).
412
+ *
413
+ * @name WriteRegister
414
+ * @summary Write bytes to an ESC register
415
+ * @request POST:/api/devices/{slavePosition}/registers/{address}
416
+ */
417
+ writeRegister = (slavePosition, address, data, params = {}) => this.request({
418
+ path: `/api/devices/${slavePosition}/registers/${address}`,
419
+ method: "POST",
420
+ body: data,
421
+ type: ContentType.Json,
422
+ format: "json",
423
+ ...params,
424
+ });
425
+ /**
426
+ * @description Reads the process-data watchdog timeout the slave at `slavePosition` is configured with, decoded from the watchdog divider (0x0400) and process-data watchdog time (0x0420) ESC registers. This is the configured timeout itself, not the expiration counter exposed by `GET /api/devices/diagnostics`. A device in OP that misses process data past this timeout faults itself to SAFE-OP+error; raising it lets a device tolerate the brief PDO pause of a whole-bus re-map.
427
+ *
428
+ * @name GetProcessDataWatchdog
429
+ * @summary Read the process-data (sync-manager) watchdog configuration
430
+ * @request GET:/api/devices/{slavePosition}/watchdog
431
+ */
432
+ getProcessDataWatchdog = (slavePosition, params = {}) => this.request({
433
+ path: `/api/devices/${slavePosition}/watchdog`,
434
+ method: "GET",
435
+ format: "json",
436
+ ...params,
437
+ });
438
+ /**
439
+ * @description Sets the process-data watchdog timeout of the slave at `slavePosition`, writing the process-data watchdog time register (0x0420). The device's watchdog divider (0x0400) is left untouched (it is shared with the PDI watchdog), so the achieved timeout is rounded to that divider's tick base — the response reports the value actually programmed. A `timeoutMs` of 0 disables the watchdog. The write persists across re-maps and re-scans until the ESC reloads EEPROM (power cycle).
440
+ *
441
+ * @name SetProcessDataWatchdog
442
+ * @summary Set the process-data (sync-manager) watchdog timeout
443
+ * @request PUT:/api/devices/{slavePosition}/watchdog
444
+ */
445
+ setProcessDataWatchdog = (slavePosition, data, params = {}) => this.request({
446
+ path: `/api/devices/${slavePosition}/watchdog`,
447
+ method: "PUT",
448
+ body: data,
449
+ type: ContentType.Json,
450
+ format: "json",
451
+ ...params,
452
+ });
453
+ /**
454
+ * @description Reads the PDO mapping of the slave at `slavePosition` over CoE, grouped by mapping object: the sync-manager assignment (0x1C12 outputs / 0x1C13 inputs) and, for each assigned mapping object (0x16xx / 0x1Axx), its `pdoIndex` and ordered entries. `outputs` are RxPDO objects (master→slave), `inputs` are TxPDO objects (slave→master). Each entry carries its derived `bitOffset` within the direction's window. This is the grouped, round-trippable counterpart of the flat, whole-bus `GET /api/process-image`. Reads fresh over SDO, so the device's mailbox must be active (PRE-OP, SAFE-OP, or OP); a device in INIT or BOOT yields 409.
455
+ *
456
+ * @name GetDevicePdoMapping
457
+ * @summary Read a device's PDO mapping grouped by object (CoE)
458
+ * @request GET:/api/devices/{slavePosition}/pdo-mapping
459
+ */
460
+ getDevicePdoMapping = (slavePosition, params = {}) => this.request({
461
+ path: `/api/devices/${slavePosition}/pdo-mapping`,
462
+ method: "GET",
463
+ format: "json",
464
+ ...params,
465
+ });
466
+ /**
467
+ * @description Reconfigures the PDO mapping of the slave at `slavePosition` over CoE: both directions' sync-manager assignment (0x1C12 outputs / 0x1C13 inputs) and the referenced mapping objects (0x16xx / 0x1Axx). `outputs` are RxPDO objects (master→slave), `inputs` are TxPDO objects (slave→master); the array order is the sync-manager assignment order, which fixes each object's position in the process image. Both directions must be present — an empty array clears that sync manager's assignment. **The device must be in PRE-OP.** The mapping and assignment objects are writable only in PRE-OP; in SAFE-OP/OP the sync managers are active and the slave rejects the write, and INIT/BOOT have no CoE mailbox. This is the "drop to PRE-OP, remap, climb back" flow: take the device to PRE-OP (`POST /api/devices/state`), call this, then transition it back to SAFE-OP/OP — at which point the whole-bus process image is re-mapped from the new mapping. The write follows the CoE ordering rule (clear assignment, rewrite each mapping object, then re-assign), is read back and verified, and is retried a few times so a dropped mailbox frame cannot leave the object dictionary half-configured. The response is the device's grouped read-back mapping (same shape as GET), whose `bitOffset` values are derived by the device (the request omits them). This endpoint does not itself change the AL state or re-map the process image.
468
+ *
469
+ * @name WriteDevicePdoMapping
470
+ * @summary Rewrite a device's PDO mapping (CoE)
471
+ * @request PUT:/api/devices/{slavePosition}/pdo-mapping
472
+ */
473
+ writeDevicePdoMapping = (slavePosition, data, params = {}) => this.request({
474
+ path: `/api/devices/${slavePosition}/pdo-mapping`,
475
+ method: "PUT",
476
+ body: data,
477
+ type: ContentType.Json,
478
+ format: "json",
479
+ ...params,
480
+ });
481
+ /**
482
+ * @description Returns the drive's current control state — the decoded state-machine state, the raw status (0x6041) and control (0x6040) words, and the active operation mode (display object 0x6061). The values are read live (from the process image when the device is exchanging, else over SDO), so poll this to drive a motion UI. Only valid for a CiA402 device (see the isCia402 flag on GET /api/devices).
483
+ *
484
+ * @name GetCia402Status
485
+ * @summary Read a device's CiA402 control snapshot
486
+ * @request GET:/api/devices/{slavePosition}/cia402
487
+ */
488
+ getCia402Status = (slavePosition, params = {}) => this.request({
489
+ path: `/api/devices/${slavePosition}/cia402`,
490
+ method: "GET",
491
+ format: "json",
492
+ ...params,
493
+ });
494
+ /**
495
+ * @description Every operation mode the device has, standard and manufacturer-specific, with what the drive says about each. The standard modes are CiA402's (ETG.6010 §6.8.1) and carry the bit of "Supported drive modes" (0x6502) that advertises them, decoded into `supported`. The manufacturer modes are the vendor's own — the negative half of 0x6060 — and are listed only for a device whose vendor this server knows; their `supported` is null, because 0x6502 reserves bits 16-31 for the vendor without defining them, so a drive has no way to advertise one that a master could read. That null means "the drive does not say", not "no". Read this once per device and cache it: it is a static property of the firmware, and the one thing that makes a negative value of 0x6061 nameable rather than "Unknown".
496
+ *
497
+ * @name GetOperationModes
498
+ * @summary List a device's operation modes
499
+ * @request GET:/api/devices/{slavePosition}/operation-modes
500
+ */
501
+ getOperationModes = (slavePosition, params = {}) => this.request({
502
+ path: `/api/devices/${slavePosition}/operation-modes`,
503
+ method: "GET",
504
+ format: "json",
505
+ ...params,
506
+ });
507
+ /**
508
+ * @description Writes the requested operation mode to 0x6060 and returns the resulting control snapshot. The drive reflects the accepted mode in 0x6061 (the modeOfOperation of the response) once it takes effect. Any value object 0x6060 can hold is accepted, standard or manufacturer-specific — the negative half of that range belongs to the vendor, so a SOMANET's diagnostics (-2) or system identification (-4) go through here like any other. Whether a given drive has the mode is the drive's answer, seen in the modeOfOperation of the response; GET /api/devices/{slavePosition}/operation-modes is what lists them beforehand.
509
+ *
510
+ * @name SetCia402OperationMode
511
+ * @summary Set a device's CiA402 operation mode
512
+ * @request POST:/api/devices/{slavePosition}/cia402/mode
513
+ */
514
+ setCia402OperationMode = (slavePosition, data, params = {}) => this.request({
515
+ path: `/api/devices/${slavePosition}/cia402/mode`,
516
+ method: "POST",
517
+ body: data,
518
+ type: ContentType.Json,
519
+ format: "json",
520
+ ...params,
521
+ });
522
+ /**
523
+ * @description Walks the CiA402 device-control state machine to the state you name, issuing whatever transitions that takes and re-reading the drive between each — so multi-hop paths are ordinary: Switch On Disabled up to Operation Enabled, Quick Stop Active back down to Ready To Switch On. Starting from Fault begins with a fault reset, and a fault whose cause is still present is reported as such (with the drive's 0x603F error code) rather than as a timeout. Only the five states a master can command are accepted. Not Ready To Switch On, Fault Reaction Active and Fault are entered by the drive itself and are rejected as a bad request. **Asking for Operation Enabled while the drive is in Quick Stop Active leaves that quick stop** (CiA402 transition 16), because naming the destination is an explicit instruction to go there. The "enable" command on the sibling endpoint deliberately does not, so a procedure preparing a drive cannot undo somebody's stop by accident. The device must be exchanging process data for any of this to progress — the statusword has to update between polls, and the firmware refuses Operation Enabled unless master communication is live. Returns the resulting control snapshot.
524
+ *
525
+ * @name TransitionToCia402State
526
+ * @summary Bring a drive to a CiA402 state
527
+ * @request POST:/api/devices/{slavePosition}/cia402/state
528
+ */
529
+ transitionToCia402State = (slavePosition, data, params = {}) => this.request({
530
+ path: `/api/devices/${slavePosition}/cia402/state`,
531
+ method: "POST",
532
+ body: data,
533
+ type: ContentType.Json,
534
+ format: "json",
535
+ ...params,
536
+ });
537
+ /**
538
+ * @description Drives the CiA402 device-control state machine. "enable" walks every intermediate transition to Operation Enabled (clearing a fault first if needed); "disable" goes to Switch On Disabled; "quickStop" triggers a quick stop; "faultReset" clears a latched fault. The device must be exchanging process data for "enable" to make progress. Returns the resulting control snapshot. Use POST /api/devices/{slavePosition}/cia402/state to name a destination instead of an action — it reaches states these four cannot, and it is the one that will leave a quick stop. "enable" here deliberately will not.
539
+ *
540
+ * @name RunCia402Command
541
+ * @summary Run a CiA402 state-machine command
542
+ * @request POST:/api/devices/{slavePosition}/cia402/command
543
+ */
544
+ runCia402Command = (slavePosition, data, params = {}) => this.request({
545
+ path: `/api/devices/${slavePosition}/cia402/command`,
546
+ method: "POST",
547
+ body: data,
548
+ type: ContentType.Json,
549
+ format: "json",
550
+ ...params,
551
+ });
552
+ /**
553
+ * @description Writes the one setpoint that matches the active operation mode — target position (0x607A, INTEGER32) in PP/CSP, target velocity (0x60FF, INTEGER32) in PV/CSV, or target torque (0x6071, INTEGER16, per-mille of rated) in PT/CST. All are signed: negative values command reverse motion or regenerative torque. Routes through the live process image when the object is PDO-mapped and the device is exchanging, else an SDO download.
554
+ *
555
+ * @name SetCia402Target
556
+ * @summary Set a CiA402 cyclic setpoint (target)
557
+ * @request POST:/api/devices/{slavePosition}/cia402/target
558
+ */
559
+ setCia402Target = (slavePosition, data, params = {}) => this.request({
560
+ path: `/api/devices/${slavePosition}/cia402/target`,
561
+ method: "POST",
562
+ body: data,
563
+ type: ContentType.Json,
564
+ ...params,
565
+ });
566
+ /**
567
+ * @description Copies the actual value into the target of one kind: position actual value (0x6064) into target position (0x607A), velocity actual value (0x606C) into target velocity (0x60FF), or torque actual value (0x6077) into target torque (0x6071). Without `kind` the pair is the one the active operation mode (0x6061) acts on. A drive holds its actual position only until the first setpoint reaches it. In CSP the master sends 0x607A every cycle, so a drive enabled with a stale target position steps to it at once, and that step can pull the supply down. Call this before you enable a drive in CSP. Motion Master never does it on its own: an enable writes the controlword and nothing else.
568
+ *
569
+ * @name SetCia402TargetToActual
570
+ * @summary Set a CiA402 target to its actual value
571
+ * @request POST:/api/devices/{slavePosition}/cia402/target/actual
572
+ */
573
+ setCia402TargetToActual = (slavePosition, query, params = {}) => this.request({
574
+ path: `/api/devices/${slavePosition}/cia402/target/actual`,
575
+ method: "POST",
576
+ query: query,
577
+ format: "json",
578
+ ...params,
579
+ });
580
+ /**
581
+ * @description Returns every procedure available on the device, each with its descriptor and the state of its current or last run. One request is enough to render a whole per-device procedures view: the descriptor carries the title, description and caveats to display, and the snapshot says whether anything is running or how the last run went. The list is per device, not global — a procedure is offered only where it applies, so a third-party slave reports an empty list rather than controls that could only ever fail. A procedure that has never run reports an idle snapshot (`status` `idle`, `runCount` 0, every step `idle`), so every entry has the same shape.
582
+ *
583
+ * @name ListProcedures
584
+ * @summary List the procedures a device supports
585
+ * @request GET:/api/devices/{slavePosition}/procedures
586
+ */
587
+ listProcedures = (slavePosition, params = {}) => this.request({
588
+ path: `/api/devices/${slavePosition}/procedures`,
589
+ method: "GET",
590
+ format: "json",
591
+ ...params,
592
+ });
593
+ /**
594
+ * @description Starts the named procedure and returns as soon as the run is under way — this does **not** wait for it to finish. Poll `GET` on the same path for its outcome, or `DELETE` to cancel it. The request body carries the procedure's parameters, and each descriptor from `GET /api/devices/{slavePosition}/procedures` reports its own in `parameters` — name, type, bounds, default, and whether it is required — which is enough to build a form for any procedure without hard-coding one per name. A procedure that takes no parameters may be started with no body. Only one procedure may run on a device at a time; a second start is refused with 409 while the first is in flight. Most procedures require an active mailbox, so the device must be in PRE-OP, SAFE-OP or OP. A run the drive answers with an error is not a failure of *this* request — it is reported in the snapshot as a failed run.
595
+ *
596
+ * @name StartProcedure
597
+ * @summary Start a procedure on a device
598
+ * @request POST:/api/devices/{slavePosition}/procedures/{procedureName}
599
+ */
600
+ startProcedure = (slavePosition, procedureName, data, params = {}) => this.request({
601
+ path: `/api/devices/${slavePosition}/procedures/${procedureName}`,
602
+ method: "POST",
603
+ body: data,
604
+ type: ContentType.Json,
605
+ format: "json",
606
+ ...params,
607
+ });
608
+ /**
609
+ * @description Returns the current run, or the last one to have finished. This is the whole progress surface — there is no push channel — and polling it is lossless: every finished step keeps its terminal status and value, so a step that both starts and completes between two polls is still reported as succeeded. A polling loop is `while status == "running"`. A procedure that has never run is not an absence: it reports an idle snapshot built from the procedure's step template, so a client renders one shape and needs no empty-state branch. The snapshot is retained after a run ends, so a client that reconnects (or a user returning to a page) sees how the last run went. It is dropped when the device set is rebuilt by a scan or reset, because bus positions may then name different hardware.
610
+ *
611
+ * @name GetProcedure
612
+ * @summary Read the state of a procedure on a device
613
+ * @request GET:/api/devices/{slavePosition}/procedures/{procedureName}
614
+ */
615
+ getProcedure = (slavePosition, procedureName, params = {}) => this.request({
616
+ path: `/api/devices/${slavePosition}/procedures/${procedureName}`,
617
+ method: "GET",
618
+ format: "json",
619
+ ...params,
620
+ });
621
+ /**
622
+ * @description Asks the running procedure to stop and returns immediately. Where the procedure is waiting on the drive it also tells the drive to abort (for an OS command, 0x1024 = 3), so a run that is waiting stops when asked rather than carrying on to completion. The run then finishes with status `cancelled`. This cancels the *run*, not the record: the snapshot remains and reports how far it got.
623
+ *
624
+ * @name CancelProcedure
625
+ * @summary Cancel a running procedure on a device
626
+ * @request DELETE:/api/devices/{slavePosition}/procedures/{procedureName}
627
+ */
628
+ cancelProcedure = (slavePosition, procedureName, params = {}) => this.request({
629
+ path: `/api/devices/${slavePosition}/procedures/${procedureName}`,
630
+ method: "DELETE",
631
+ ...params,
632
+ });
633
+ /**
634
+ * @description Reports both encoder configuration records, 0x2110 (encoder 1) and 0x2112 (encoder 2), and the encoder each controller uses: position control from 0x2012:09, velocity control from 0x2011:05, commutation from 0x2010:0C. A record whose type is `none` is still listed, because the controller fields refer to the ordinal. Read this to learn what one revolution is in position counts. A position in 0x6064 or 0x607A is in counts of the position encoder, unscaled by the gear ratio (0x6091), so one revolution of that encoder is its `resolution`. The feed constant (0x6092) is not applied here.
635
+ *
636
+ * @name GetEncoders
637
+ * @summary Read a SOMANET drive's encoders and which controller uses which
638
+ * @request GET:/api/devices/{slavePosition}/encoders
639
+ */
640
+ getEncoders = (slavePosition, params = {}) => this.request({
641
+ path: `/api/devices/${slavePosition}/encoders`,
642
+ method: "GET",
643
+ format: "json",
644
+ ...params,
645
+ });
646
+ /**
647
+ * @description Reports the brake objects (0x2004) that decide what a release or engage will do: its current state, the release strategy, the pull time and the pull/hold voltages. Read this before commanding the brake. `softwareControllable` is false when the release strategy is `manualOutputVoltage`, meaning the firmware does not drive the brake at all and both commands below are no-ops. `releaseMovesShaft` is true for a pin brake, whose release procedure turns the motor to lift the load off the pin.
648
+ *
649
+ * @name GetBrake
650
+ * @summary Read a drive's brake configuration and state
651
+ * @request GET:/api/devices/{slavePosition}/brake
652
+ */
653
+ getBrake = (slavePosition, params = {}) => this.request({
654
+ path: `/api/devices/${slavePosition}/brake`,
655
+ method: "GET",
656
+ format: "json",
657
+ ...params,
658
+ });
659
+ /**
660
+ * @description Reads the files the `hrd-streaming` procedure recorded, concatenates them in order and decodes them into samples. Run that procedure first; this endpoint only reads what is already on the drive. **`data` is required and must match what was recorded.** Nothing on the drive says which signal its files hold, so the wrong selection reinterprets the same bytes as a different layout and returns plausible nonsense. The procedure's `configure-stream` step reports the selection it used for exactly this reason. Samples travel positionally — `columns` names the fields of each row, once, the way a monitoring topic ships its parameter order once — because a full recording is up to ten thousand rows. `trailingBytes` is what was left over past the last whole sample: the drive allocates its files in fixed-size blocks, so a recording that does not fill the last block ends in padding rather than data. Answers **JSON or CSV**, chosen by the `Accept` header. CSV is the same numbers in the same column order, one header row then one row per sample — a file to open in a spreadsheet rather than JSON to read, which for ten thousand rows is usually what is wanted. Values are decoded, not raw: `velocityRpm` is converted out of the file's Q15 fixed point (exactly — multiply by 32768 to recover the stored integer), while `torquePermil` and the encoder's `raw` word are the file's own values. Blocks for the transfer, which is up to five 8 KB FoE reads plus the listing.
661
+ *
662
+ * @name ReadHrdRecording
663
+ * @summary Read back a drive's high resolution data recording
664
+ * @request GET:/api/devices/{slavePosition}/hrd
665
+ */
666
+ readHrdRecording = (slavePosition, query, params = {}) => this.request({
667
+ path: `/api/devices/${slavePosition}/hrd`,
668
+ method: "GET",
669
+ query: query,
670
+ format: "json",
671
+ ...params,
672
+ });
673
+ /**
674
+ * @description Writes "disengaged" to the brake state object (0x2004:07) and waits the drive's pull time (0x2004:03) plus `settle` before answering, because the firmware blocks motion — and motion-related OS commands — until the pull time expires. Answers with the brake state read back. **The release only actually happens while the drive is in OP ENABLED.** In any other state the write merely energises the brake output (phase D). Note also that entering OP ENABLED releases the brake automatically in normal operation but **not** in diagnostics mode, where the master owns the brake — which is why a diagnostics procedure has to call this. **On a pin brake this moves the shaft**: the controller raises current progressively until the load has lifted off the pin by the minimum displacement (0x2004:08), reversing direction if it reaches the current ceiling (0x2004:09, a percentage of rated current) first. Check `releaseMovesShaft` on `GET .../brake`. Brake release is open-loop — nothing confirms the brake let go — so the wait is the only margin there is. A brake whose release strategy is `manualOutputVoltage` is left alone and the returned state says so; that is not an error. **A released brake stays released**: nothing re-engages it for you, so on a vertical or loaded axis engage it again when you are done.
675
+ *
676
+ * @name ReleaseBrake
677
+ * @summary Release (disengage) a drive's brake
678
+ * @request POST:/api/devices/{slavePosition}/brake/release
679
+ */
680
+ releaseBrake = (slavePosition, query, params = {}) => this.request({
681
+ path: `/api/devices/${slavePosition}/brake/release`,
682
+ method: "POST",
683
+ query: query,
684
+ format: "json",
685
+ ...params,
686
+ });
687
+ /**
688
+ * @description Writes "engaged" to the brake state object (0x2004:07) and waits `settle` before answering with the brake state read back. There is no pull time on the way in — a brake is spring-engaged, so engaging it is the removal of voltage — so the wait is only `settle`. As with release, a brake whose release strategy is `manualOutputVoltage` is left alone and the returned state says so.
689
+ *
690
+ * @name EngageBrake
691
+ * @summary Engage a drive's brake
692
+ * @request POST:/api/devices/{slavePosition}/brake/engage
693
+ */
694
+ engageBrake = (slavePosition, query, params = {}) => this.request({
695
+ path: `/api/devices/${slavePosition}/brake/engage`,
696
+ method: "POST",
697
+ query: query,
698
+ format: "json",
699
+ ...params,
700
+ });
701
+ /**
702
+ * @description Performs a CoE SDO upload — reads the value of object `index:subindex` from the device at `slavePosition` and returns the raw bytes as a JSON array. Both `index` and `subindex` accept decimal or hexadecimal notation; prefix with `0x` for hex (e.g. `0x6064` or `24676` for object 0x6064).
703
+ *
704
+ * @name SdoUpload
705
+ * @summary Upload an object dictionary entry from a device (CoE SDO upload)
706
+ * @request GET:/api/devices/{slavePosition}/sdo/{index}/{subindex}
707
+ */
708
+ sdoUpload = (slavePosition, index, subindex, params = {}) => this.request({
709
+ path: `/api/devices/${slavePosition}/sdo/${index}/${subindex}`,
710
+ method: "GET",
711
+ format: "json",
712
+ ...params,
713
+ });
714
+ /**
715
+ * @description Performs a CoE SDO download — writes the raw bytes in the request body to object `index:subindex` on the device at `slavePosition`. The byte count must match the object's length. Requires the device to be in PRE-OP, SAFE-OP, or OP (mailbox communication active). Both `index` and `subindex` accept decimal or hexadecimal notation; prefix with `0x` for hex (e.g. `0x6064` or `24676`).
716
+ *
717
+ * @name SdoDownload
718
+ * @summary Download an object dictionary entry to a device (CoE SDO download)
719
+ * @request PUT:/api/devices/{slavePosition}/sdo/{index}/{subindex}
720
+ */
721
+ sdoDownload = (slavePosition, index, subindex, data, params = {}) => this.request({
722
+ path: `/api/devices/${slavePosition}/sdo/${index}/${subindex}`,
723
+ method: "PUT",
724
+ body: data,
725
+ type: ContentType.Json,
726
+ format: "json",
727
+ ...params,
728
+ });
729
+ /**
730
+ * @description Lists what the device holds — firmware images, the ESI, logs, configuration and any high resolution data recording — with the size it reports for each. **Vendor-specific despite reading like a filesystem listing.** EtherCAT defines no directory service, so SOMANET firmware serves its own as a pseudo-file (`fs-getlist`) read over FoE. A device that is not a SOMANET drive is refused rather than probed. `byteCount` is absent when the device reported a name without a size, which is a formatting quirk of one entry rather than a reason to hide the file.
731
+ *
732
+ * @name ListDeviceFiles
733
+ * @summary List the files stored on a device
734
+ * @request GET:/api/devices/{slavePosition}/files
735
+ */
736
+ listDeviceFiles = (slavePosition, params = {}) => this.request({
737
+ path: `/api/devices/${slavePosition}/files`,
738
+ method: "GET",
739
+ format: "json",
740
+ ...params,
741
+ });
742
+ /**
743
+ * @description Reads and decodes the device's `.hardware_description` file over FoE — its product id and revision, serial number, the components it is built from, and the assembly it was packaged into. It is also the only source of the descriptor that decides which firmware belongs on the device, which is what most callers want it for (see `/api/devices/{slavePosition}/firmware-compatibility`). **Readable in BOOT as well as PRE-OP and above.** The bootloader deliberately allows this one file, which is what lets a compatibility check work on a drive a failed install left stranded there — precisely when knowing whether the package was the right one matters most.
744
+ *
745
+ * @name GetDeviceHardwareDescription
746
+ * @summary Read what a device says it is
747
+ * @request GET:/api/devices/{slavePosition}/hardware-description
748
+ */
749
+ getDeviceHardwareDescription = (slavePosition, params = {}) => this.request({
750
+ path: `/api/devices/${slavePosition}/hardware-description`,
751
+ method: "GET",
752
+ format: "json",
753
+ ...params,
754
+ });
755
+ /**
756
+ * @description Reads and decodes the device's `.variant` file over FoE — which features the Integro was licensed with. **A 404 is the ordinary answer for most devices.** Only Integro drives carry this file, so the resource being absent is normal rather than a problem. Its one property that matters beyond inspection is `fieldbusProtocol`, which the hardware description deliberately does not carry and a full firmware descriptor ends with.
757
+ *
758
+ * @name GetDeviceIntegroVariant
759
+ * @summary Read a device's Integro variant file
760
+ * @request GET:/api/devices/{slavePosition}/variant
761
+ */
762
+ getDeviceIntegroVariant = (slavePosition, params = {}) => this.request({
763
+ path: `/api/devices/${slavePosition}/variant`,
764
+ method: "GET",
765
+ format: "json",
766
+ ...params,
767
+ });
768
+ /**
769
+ * @description Reads the device's `.hardware_description` and `.variant`, assembles the full firmware descriptors it accepts, and compares them against the descriptor in `filename`. **A mismatch is a `200` carrying `compatible: false`**, not an error status: the client asked a question and got an answer, and the body names both descriptors so it can say which hardware the package was for. A `4xx` means the question could not be asked — the filename is not a package name, or the hardware description could not be read. **Nothing acts on the answer.** The firmware installation procedure writes whatever it is given, on purpose, so this exists to tell a user before they start.
770
+ *
771
+ * @name CheckDeviceFirmwareCompatibility
772
+ * @summary Check whether a firmware package belongs on a device
773
+ * @request GET:/api/devices/{slavePosition}/firmware-compatibility
774
+ */
775
+ checkDeviceFirmwareCompatibility = (slavePosition, query, params = {}) => this.request({
776
+ path: `/api/devices/${slavePosition}/firmware-compatibility`,
777
+ method: "GET",
778
+ query: query,
779
+ format: "json",
780
+ ...params,
781
+ });
782
+ /**
783
+ * @description Sends an FoE read request for `filename` to the device at `slavePosition` and returns the raw file bytes. FoE is available in Boot, Pre-Op, Safe-Op, and Op states (device-dependent); the caller is responsible for ensuring the device is in a suitable state before calling. The call blocks until the full file has been received. Over EtherCAT it waits up to 6 s for the device's answer and tries a read that gets no answer up to 3 times, so a device that never answers takes about 18 s to fail. A refusal from the device is not tried again. After a failure without an answer, Motion Master aborts the transfer on the device, so that the next request works. Over SPoE it waits the configured file timeout for each packet. When the device refuses the read, the error names the FoE error code it sent and its meaning, and the device's error text when that adds anything, e.g. `FOEread slave 1 'x.bin' failed (FoE error 0x8001: Not found)`. A device that never answered gives `(no response)`.
784
+ *
785
+ * @name FoeReadFile
786
+ * @summary Read a file from a device via FoE (File over EtherCAT)
787
+ * @request GET:/api/devices/{slavePosition}/files/{filename}
788
+ */
789
+ foeReadFile = (slavePosition, filename, params = {}) => this.request({
790
+ path: `/api/devices/${slavePosition}/files/${filename}`,
791
+ method: "GET",
792
+ ...params,
793
+ });
794
+ /**
795
+ * @description Sends an FoE write request for `filename` to the device at `slavePosition`, streaming the raw request body as the file contents. FoE is available in Boot, Pre-Op, Safe-Op, and Op states (device-dependent); the caller is responsible for ensuring the device is in a suitable state before calling. Over EtherCAT it waits up to 10 s for the device to acknowledge each packet. A write that fails without an answer is aborted on the device and is not tried again.
796
+ *
797
+ * @name FoeWriteFile
798
+ * @summary Write a file to a device via FoE (File over EtherCAT)
799
+ * @request PUT:/api/devices/{slavePosition}/files/{filename}
800
+ */
801
+ foeWriteFile = (slavePosition, filename, data, params = {}) => this.request({
802
+ path: `/api/devices/${slavePosition}/files/${filename}`,
803
+ method: "PUT",
804
+ body: data,
805
+ format: "json",
806
+ ...params,
807
+ });
808
+ /**
809
+ * @description Deletes `filename` from the flash of the device at `slavePosition`. Motion Master waits up to 10 s for the device to answer, and 30 s for the ESI (`SOMANET_CiA_402.xml.zip`), because the device answers only after it has erased the file. **Vendor-specific despite reading like a filesystem operation.** FoE has no delete, so SOMANET firmware acts on a pseudo-file instead: reading the name `fs-remove=<filename>` removes that file. This endpoint exists so no client has to know that, and a device that is not a SOMANET drive is refused rather than probed. **A file that was not there answers 200.** The caller asked for the file to be gone, and it is. Every other FoE failure is a 500 naming the reason. The file is gone from flash immediately. Deleting `config.csv` discards the stored parameter values, so the drive falls back to its defaults on the next power cycle.
810
+ *
811
+ * @name DeleteDeviceFile
812
+ * @summary Delete a file from a device
813
+ * @request DELETE:/api/devices/{slavePosition}/files/{filename}
814
+ */
815
+ deleteDeviceFile = (slavePosition, filename, params = {}) => this.request({
816
+ path: `/api/devices/${slavePosition}/files/${filename}`,
817
+ method: "DELETE",
818
+ format: "json",
819
+ ...params,
820
+ });
821
+ /**
822
+ * @description Reads the raw SII EEPROM image of the device at `slavePosition` through the ESC's EEPROM-control registers and returns either the parsed structure or the raw bytes, selected by the `Accept` request header. With `Accept: application/json` (or `*\/*`, or no `Accept` header) the parsed `SlaveInformationInterface` is returned — the 128-byte fixed header plus the decoded category section (strings, general info, FMMU/Sync-Manager and PDO defaults, distributed-clock settings). With `Accept: application/octet-stream` the raw EEPROM image is returned unparsed, suitable for a hex dump or archival. EEPROM access is a control-plane operation and is most reliable while the device is in INIT or PRE-OP. String-index fields in the parsed structure (e.g. `general.nameIdx`) are 1-based references into `category.strings`.
823
+ *
824
+ * @name ReadSii
825
+ * @summary Read a device's SII (Slave Information Interface / EEPROM)
826
+ * @request GET:/api/devices/{slavePosition}/sii
827
+ */
828
+ readSii = (slavePosition, params = {}) => this.request({
829
+ path: `/api/devices/${slavePosition}/sii`,
830
+ method: "GET",
831
+ format: "json",
832
+ ...params,
833
+ });
834
+ /**
835
+ * @description Writes the raw SII image in the request body to the device's EEPROM, one 16-bit word at a time. The image is parse-validated first and rejected if it is not a well-formed SII (a guard against bricking the device). **Destructive:** a wrong image can leave the device unidentifiable until re-flashed. The device adopts the new contents only after a **power cycle**, and the write is most reliable while the device is in INIT or PRE-OP. Intended for restoring a previously downloaded image.
836
+ *
837
+ * @name WriteSii
838
+ * @summary Write a raw SII (EEPROM) image to a device
839
+ * @request PUT:/api/devices/{slavePosition}/sii
840
+ */
841
+ writeSii = (slavePosition, data, params = {}) => this.request({
842
+ path: `/api/devices/${slavePosition}/sii`,
843
+ method: "PUT",
844
+ body: data,
845
+ format: "json",
846
+ ...params,
847
+ });
848
+ /**
849
+ * @description Decodes a raw SII EEPROM image supplied in the request body and returns the parsed structure — the same decoder the device read path uses, exposed as a bus-independent utility. No device is involved; the Tools SII page uses this to view previously downloaded `.bin` files offline.
850
+ *
851
+ * @name ParseSii
852
+ * @summary Parse a raw SII (EEPROM) image
853
+ * @request POST:/api/sii/parse
854
+ */
855
+ parseSii = (data, params = {}) => this.request({
856
+ path: `/api/sii/parse`,
857
+ method: "POST",
858
+ body: data,
859
+ format: "json",
860
+ ...params,
861
+ });
862
+ /**
863
+ * @description Breaks a firmware package filename into the five fields the SOMANET naming convention defines (Hardware description specification §3.4.2) — `package_<hardware>_<fullFirmwareDescriptor>_<software>_v<version>.zip` — and, where the descriptor is the numeric `<id>-<version>[-<key>[-<fieldbus>]]` kind, decodes it further. The specification allows a descriptor that is not (its own example `Branded-Drive-Elite` carries `MyProduct-v25-key3-ecat`), so those four fields are omitted rather than guessed. Bus-independent, like `/api/sii/parse` and `/api/esi/parse`, but a `GET` with a query because there is no file to upload — only a short string. This is the same decoder firmware installation uses to decide whether a package can be cached, so what it reports is what that will do.
864
+ *
865
+ * @name GetFirmwarePackageName
866
+ * @summary Decode a SOMANET firmware package filename
867
+ * @request GET:/api/firmware-package-name
868
+ */
869
+ getFirmwarePackageName = (query, params = {}) => this.request({
870
+ path: `/api/firmware-package-name`,
871
+ method: "GET",
872
+ query: query,
873
+ format: "json",
874
+ ...params,
875
+ });
876
+ /**
877
+ * @description Decodes a `.hardware_description` supplied in the request body. No device is involved — the same parse a device read uses, so a file saved from one device can be inspected with no hardware present. Three things fail a parse: content that is not JSON, content with no `device` object, and a device carrying no `id` or no `version`. Everything else is accepted as written. Those three required fields are exactly what a descriptor is built from, which is also what makes this a usable type check — arbitrary JSON is rejected rather than yielding an empty descriptor. The `fileVersion` major is reported but not enforced: a bump means "a new parser is needed", but refusing an unknown one would make a drive flashed with a future file unreadable today.
878
+ *
879
+ * @name ParseHardwareDescription
880
+ * @summary Parse a .hardware_description file
881
+ * @request POST:/api/hardware-description/parse
882
+ */
883
+ parseHardwareDescription = (data, params = {}) => this.request({
884
+ path: `/api/hardware-description/parse`,
885
+ method: "POST",
886
+ body: data,
887
+ type: ContentType.Text,
888
+ format: "json",
889
+ ...params,
890
+ });
891
+ /**
892
+ * @description Decodes a `.variant` file supplied in the request body. Binary rather than text, hence a `POST` with bytes like `/api/sii/parse` rather than a query like `/api/firmware-package-name`. Trailing bytes past the last option are accepted: the firmware reads only as many options as the count claims and ignores the rest, so refusing padding would reject a file the drive is happy with.
893
+ *
894
+ * @name ParseIntegroVariant
895
+ * @summary Parse a .variant file
896
+ * @request POST:/api/integro-variant/parse
897
+ */
898
+ parseIntegroVariant = (data, params = {}) => this.request({
899
+ path: `/api/integro-variant/parse`,
900
+ method: "POST",
901
+ body: data,
902
+ format: "json",
903
+ ...params,
904
+ });
905
+ /**
906
+ * @description Every option an Integro can be licensed with, in code order. Static data: no device, no bus, no request body. Served so a client can explain a decoded file — or simply show what is available — without carrying a second copy of a table that belongs to the firmware. The codes ascend with real gaps, and the catalogue keeps codes current firmware no longer implements, because files written years ago still carry them.
907
+ *
908
+ * @name ListIntegroVariantOptions
909
+ * @summary The whole Integro variant option catalogue
910
+ * @request GET:/api/integro-variant/options
911
+ */
912
+ listIntegroVariantOptions = (params = {}) => this.request({
913
+ path: `/api/integro-variant/options`,
914
+ method: "GET",
915
+ format: "json",
916
+ ...params,
917
+ });
918
+ /**
919
+ * @description Decodes an ENI document supplied in the request body. No device is involved, and that is the point: the useful case is reading a configuration somebody else's tool wrote — acontis EC-Engineer, TwinCAT — rather than the one this master exports from `GET /api/eni`. **An ENI is a script, not a description.** Every configuration step in it is an EtherCAT datagram or a CoE download tagged with the AL-state transition it belongs to, so a listing of the raw elements would show `FPWR to 2064, data 0018060064000100` and leave the reading undone. Each datagram therefore carries a `register` object naming what its `ado` selects, with the instance for a repeating block, and `decoded` fields where the payload is a sync-manager or FMMU register block. A write to AL Control reports `requestsState`, and the read that follows it reports `waitsForState`. Every enumeration is spelled out beside its value — `cmd` 5 with `cmdName` `FPWR`, transitions as `IP`/`PS`/`SO` — and every payload carries its hex beside its byte count, because the reader of this JSON is a person rather than a master. Tolerant by design. Only three things fail: XML that will not parse, a root that is not `EtherCATConfig`, and a missing `Config`. Anything else costs that value and a line in `warnings`, including an element this model has no room for. The sample documents ETG itself ships need that tolerance: two of them omit elements their own schema marks mandatory, and a previous port of `A` is spec-legal while ENI Schema 1.7 enumerates only `B`, `C` and `D`.
920
+ *
921
+ * @name ParseEni
922
+ * @summary Parse an EtherCAT Network Information (ENI) file
923
+ * @request POST:/api/eni/parse
924
+ */
925
+ parseEni = (data, params = {}) => this.request({
926
+ path: `/api/eni/parse`,
927
+ method: "POST",
928
+ body: data,
929
+ format: "json",
930
+ ...params,
931
+ });
932
+ /**
933
+ * @description Decodes a vendor's ESI XML supplied in the request body. No device is involved — the Tools page uses this to inspect an ESI with no hardware present, which is the only way to see object descriptions, enum option labels, engineering units and min/max bounds: the CoE SDO-Information service reports none of them. The response carries the vendor, every module, and **every device with its own assembled `entries` table** — one row per addressable `(index, subindex)`, merged from the device's own dictionary plus the dictionaries of the modules its slots reference, with each row recording in `source` where it came from. Object-level annotation (an object's description and raw properties) is attached to **subindex 0 only** — that row *is* the object — rather than repeated onto every subindex. A RECORD member still carries its own description on its own row; an ARRAY element carries none, because the ESI describes an array once rather than per element. To read an object's text for any subindex, look at subindex 0 of the same index. Where a slot offers mutually exclusive module variants the merge necessarily collides; it is resolved last-wins and reported in that device's `warnings`. Pass `modules` to model one concrete configuration instead.
934
+ *
935
+ * @name ParseEsi
936
+ * @summary Parse an EtherCAT Slave Information (ESI) file
937
+ * @request POST:/api/esi/parse
938
+ */
939
+ parseEsi = (data, query, params = {}) => this.request({
940
+ path: `/api/esi/parse`,
941
+ method: "POST",
942
+ query: query,
943
+ body: data,
944
+ format: "json",
945
+ ...params,
946
+ });
947
+ /**
948
+ * @description Reads live link-quality and watchdog diagnostics from each slave's EtherCAT Slave Controller (DL Status 0x0110, error-counter block 0x0300–0x0313, watchdog block 0x0440–0x0443) via FPRD. Counters are 8-bit, saturate at 255, and are cleared only by a power cycle or explicit write — poll this endpoint and watch for a rising delta rather than an absolute value. The per-port counters localise a degrading link to a specific cable/connector; the watchdog counters distinguish a slave that stopped receiving process data from a master-side problem. Omit `positions` to query all discovered devices. Returns 500 for transports without an ESC (e.g. SPoE).
949
+ *
950
+ * @name GetDeviceDiagnostics
951
+ * @summary Read live ESC health diagnostics for devices
952
+ * @request GET:/api/devices/diagnostics
953
+ */
954
+ getDeviceDiagnostics = (query, params = {}) => this.request({
955
+ path: `/api/devices/diagnostics`,
956
+ method: "GET",
957
+ query: query,
958
+ format: "json",
959
+ ...params,
960
+ });
961
+ /**
962
+ * @description Reads each slave's distributed-clock unit live from its EtherCAT Slave Controller (system-time delay 0x0928 and system-time difference 0x092C) via FPRD. The reference clock is the first DC-capable slave; every other DC slave continuously corrects its local clock toward it, and `systemTimeDifference` is the live deviation in nanoseconds. The figure is meaningful only while the bus is exchanging process data in SAFE-OP/OP (the reference time is distributed in the cyclic frame) and converges toward zero as the slaves' drift loops settle — poll this endpoint and watch for a slave whose deviation stays large or grows rather than an absolute value. Non-DC slaves report `dcCapable` false with zeroed values. Omit `positions` to query all discovered devices. Returns 500 for transports without an ESC (e.g. SPoE).
963
+ *
964
+ * @name GetDcSync
965
+ * @summary Read live distributed-clock synchronisation status for devices
966
+ * @request GET:/api/dc-sync
967
+ */
968
+ getDcSync = (query, params = {}) => this.request({
969
+ path: `/api/dc-sync`,
970
+ method: "GET",
971
+ query: query,
972
+ format: "json",
973
+ ...params,
974
+ });
975
+ /**
976
+ * @description Runs the capture program `motion-master-capture`, which ships beside the server binary, on the adapter the bus is on, waits for it to exit, and answers the summary. The program records every EtherCAT frame that leaves or enters the adapter with a kernel timestamp and writes them as a pcap file under `captures/` in the user cache, so the file is listed, downloaded and deleted through the `/api/user-cache` routes and opens in Wireshark. The summary matches each frame the master sent to the frame that came back for it, by the index and the header of its first datagram, and reports per kind of frame how late the returns were. The cyclic process-data frame is one kind; mailbox and register datagrams, the control plane, are the other. The bands are the three waits the driver uses, 2 ms, 20 ms and 100 ms, and they are cumulative: a frame later than 100 ms counts in all three. A frame sent again with the same index and header while its first copy had no answer counts once, as `resent`, and its return is timed from the first copy. The time is the kernel's, taken at the boundary to the NIC driver on both sides: when the frame is handed to the driver, and when the driver hands its return back. So the number covers the NIC, the wire and the device, and not the host's own queues and scheduling between that boundary and Motion Master. A capture that shows late frames puts the delay on the wire or in the device. A clean capture beside the server's own warnings, "mailbox write(s) got no frame back", puts it inside the host. Read the two together. One capture at a time. The capture adds no frame to the bus and runs on a thread of its own, so the bus is not touched. The program needs `CAP_NET_RAW`, which the packages grant it at install.
977
+ *
978
+ * @name RunCapture
979
+ * @summary Record the EtherCAT frames of the bus adapter for a number of seconds
980
+ * @request POST:/api/captures
981
+ */
982
+ runCapture = (data, params = {}) => this.request({
983
+ path: `/api/captures`,
984
+ method: "POST",
985
+ body: data,
986
+ type: ContentType.Json,
987
+ format: "json",
988
+ ...params,
989
+ });
990
+ /**
991
+ * @description Reads `captures/{name}` from the user cache and answers the summary that `POST /api/captures` answers, over any Ethernet pcap: a file the server recorded, or one from `tcpdump` on another host that a user put into `captures/` with `PUT /api/user-cache/captures/{name}`. Microsecond and nanosecond timestamps and both byte orders are read.
992
+ *
993
+ * @name GetCaptureSummary
994
+ * @summary Summarise a pcap file in the user cache
995
+ * @request GET:/api/captures/{name}
996
+ */
997
+ getCaptureSummary = (name, params = {}) => this.request({
998
+ path: `/api/captures/${name}`,
999
+ method: "GET",
1000
+ format: "json",
1001
+ ...params,
1002
+ });
1003
+ /**
1004
+ * @description Reads the current EtherCAT Application Layer state for one or more devices. Omit the `positions` query parameter to query all discovered devices.
1005
+ *
1006
+ * @name GetDeviceStates
1007
+ * @summary Read current AL state for devices
1008
+ * @request GET:/api/devices/state
1009
+ */
1010
+ getDeviceStates = (query, params = {}) => this.request({
1011
+ path: `/api/devices/state`,
1012
+ method: "GET",
1013
+ query: query,
1014
+ format: "json",
1015
+ ...params,
1016
+ });
1017
+ /**
1018
+ * @description Commands one or more devices to the requested EtherCAT Application Layer state and blocks until all targeted devices arrive or the timeout elapses. The settled state of every targeted device is then read back and returned in `devices`, each with a `reached` flag; `ok` is true only when every device reached the target. A device that did not reach it carries an `alStatusCode` explaining why. A device that does not answer at its address is not commanded, so the request does not wait out the timeout for it. Its `reachability` says why, and the other devices are still commanded. Omit `positions` or pass an empty array to target all discovered devices. State values are the standard AL state register encodings from ETG.1000.6: 1 (Init), 2 (PreOp), 3 (Boot), 4 (SafeOp), 8 (Op).
1019
+ *
1020
+ * @name TransitionToState
1021
+ * @summary Transition devices to an EtherCAT AL state
1022
+ * @request POST:/api/devices/state
1023
+ */
1024
+ transitionToState = (data, params = {}) => this.request({
1025
+ path: `/api/devices/state`,
1026
+ method: "POST",
1027
+ body: data,
1028
+ type: ContentType.Json,
1029
+ format: "json",
1030
+ ...params,
1031
+ });
1032
+ /**
1033
+ * No description
1034
+ *
1035
+ * @name GetDevices
1036
+ * @summary List all fieldbus devices
1037
+ * @request GET:/api/devices
1038
+ */
1039
+ getDevices = (params = {}) => this.request({
1040
+ path: `/api/devices`,
1041
+ method: "GET",
1042
+ format: "json",
1043
+ ...params,
1044
+ });
1045
+ /**
1046
+ * @description Returns the whole-bus process image: the byte sizes of each direction, the expected and last working counters and overall health, the number of retained image generations (one per re-map since the last reset), and every mapped object resolved to its absolute bit offset. While an image is live `configured` is true and the layout describes it. When no image is published (no device is in SAFE-OP/OP) but at least one generation has been mapped, `configured` is false and the layout describes the most recent retained generation — the last-known mapping — so a bus that has dropped out of SAFE-OP/OP stays inspectable; `lastWkc` then holds the final exchange value and working-counter health does not apply. The object lists are empty only before any image has ever been mapped. Object `name` is populated only for devices whose object dictionary has been enumerated.
1047
+ *
1048
+ * @name GetProcessImage
1049
+ * @summary Inspect the published EtherCAT process image
1050
+ * @request GET:/api/process-image
1051
+ */
1052
+ getProcessImage = (params = {}) => this.request({
1053
+ path: `/api/process-image`,
1054
+ method: "GET",
1055
+ format: "json",
1056
+ ...params,
1057
+ });
1058
+ /**
1059
+ * @description Returns a point-in-time snapshot of the RT cyclic loop: the configured period and target rate, the cumulative achieved rate since start, the executed- and skipped-cycle counters, per-cycle task-execution timing (last / worst / mean), and whether real-time scheduling was acquired. Skipped cycles accumulate whenever the loop cannot meet its period (common on non-RT hosts such as Windows userspace) — a steadily rising `skippedCycles`, or an `achievedHz` well below `targetHz`, means the configured period is too aggressive for the hardware. Poll this endpoint and diff `executedCycles`/`skippedCycles` against `timestampUs` to chart the instantaneous rate over time.
1060
+ *
1061
+ * @name GetGameLoop
1062
+ * @summary Inspect real-time game-loop health
1063
+ * @request GET:/api/game-loop
1064
+ */
1065
+ getGameLoop = (params = {}) => this.request({
1066
+ path: `/api/game-loop`,
1067
+ method: "GET",
1068
+ format: "json",
1069
+ ...params,
1070
+ });
1071
+ /**
1072
+ * @description Retimes the running RT loop to a new cycle period, in microseconds. The change takes effect within one cycle and is transient — it is not written back to the config file, so a restart reverts to the configured `gameLoop.periodUs`. Intended for tuning on hosts that cannot sustain the configured rate: if `skippedCycles` climbs steadily (common on Windows userspace), raise the period (e.g. from 1000 to 2000 µs) until the loop meets its grid. Only the master cadence is retimed — the process-data recorder ring is not resized, and no drive-side watchdog is touched, so raising the period toward a drive's PDO/SM watchdog window can fault that drive. Applying a period starts a fresh health epoch: the cumulative counters (`executedCycles`, `skippedCycles`, the `achievedHz` average, and `maxExecNs`/`avgExecNs`) reset so the snapshot reflects only the new period — letting you see straight away whether the change improved the loop's health. Returns the updated health snapshot.
1073
+ *
1074
+ * @name SetGameLoopPeriod
1075
+ * @summary Change the real-time cycle period
1076
+ * @request PUT:/api/game-loop
1077
+ */
1078
+ setGameLoopPeriod = (data, params = {}) => this.request({
1079
+ path: `/api/game-loop`,
1080
+ method: "PUT",
1081
+ body: data,
1082
+ type: ContentType.Json,
1083
+ format: "json",
1084
+ ...params,
1085
+ });
1086
+ /**
1087
+ * @description Streams the same `.mmpd` serialisation as the POST variant (the recorder's current span, with the process image embedded as a header) directly in the response body instead of writing a file — for the client SDK / browser, which parses the bytes offline. Request with `Accept: application/octet-stream`. Same span semantics as the POST; works in any state.
1088
+ *
1089
+ * @name StreamProcessDataDump
1090
+ * @summary Stream the recorder ring as a binary .mmpd
1091
+ * @request GET:/api/process-data/dump
1092
+ */
1093
+ streamProcessDataDump = (params = {}) => this.request({
1094
+ path: `/api/process-data/dump`,
1095
+ method: "GET",
1096
+ ...params,
1097
+ });
1098
+ /**
1099
+ * @description Serialises the process-data recorder's current span — every cycle from the oldest still in the ring up to the newest at the instant of the call — to a `.mmpd` file and returns its path. Each cycle's full raw input and output images, sequence, and epoch-nanosecond timestamp are written, with the process image (per-device identity and PDO object layout, including object names and data types where the object dictionary has been enumerated) embedded as a header, so the file decodes fully offline with no running Motion Master and no live bus. Works in any state: while devices are exchanging (SAFE-OP/OP) it captures tail→head at that moment and ignores cycles recorded afterwards; after the bus has left the exchange states it uses the most recent retained image layout. The file is written under the configured `recorder.dumpDir`, created if absent; by default that is a `dumps` subdirectory of the user-cache root, so the written file is also reachable through the `/api/user-cache` endpoints — list it, download it, or delete it there. Only the path is returned here. (Setting an explicit `recorder.dumpDir` outside the cache root opts out of that; the file is then only reachable from a shell on the server.)
1100
+ *
1101
+ * @name DumpProcessData
1102
+ * @summary Dump the recorder ring to a binary .mmpd file
1103
+ * @request POST:/api/process-data/dump
1104
+ */
1105
+ dumpProcessData = (params = {}) => this.request({
1106
+ path: `/api/process-data/dump`,
1107
+ method: "POST",
1108
+ format: "json",
1109
+ ...params,
1110
+ });
1111
+ /**
1112
+ * @description Stages many output (RxPDO) objects at once — the "send all" action of the Process Data page. Each value is converted to the object's declared data type and, when the object is output-mapped and its device is exchanging (SAFE-OP/OP), written into the output image so it is sent on the next real-time cycle and re-sent every cycle thereafter (set-once, sent-continuously). The batch is best-effort atomic: values are staged sequentially, so a batch can straddle two consecutive ~1 ms cycles. The request never fails as a whole — each object gets its own result, so the client can flag any object that was not cyclically staged (unknown device, a value the data type cannot hold, object not output-mapped, or bus not exchanging; in the last two cases the value is still written via SDO/cache, just not cyclically driven). A value the data type cannot hold is refused in its own result and nothing is written for it, by the same rule as `writeParameter`.
1113
+ *
1114
+ * @name StageProcessDataOutputs
1115
+ * @summary Stage a batch of output values into the process image
1116
+ * @request POST:/api/process-data/outputs
1117
+ */
1118
+ stageProcessDataOutputs = (data, params = {}) => this.request({
1119
+ path: `/api/process-data/outputs`,
1120
+ method: "POST",
1121
+ body: data,
1122
+ type: ContentType.Json,
1123
+ format: "json",
1124
+ ...params,
1125
+ });
1126
+ /**
1127
+ * @description Returns the static ESC configuration the master programmed into every slave during the last scan: station/alias addresses, mapped process-data sizes, the mailbox transport windows, the distributed-clock setup, and the configured Sync Managers and FMMUs. Read from cached state with no bus I/O and valid once the bus has been scanned. The list is empty before any scan, or when the active transport has no ESC (e.g. SPoE). Numeric fields (SM/FMMU type, mailbox protocol bits, SM flags) are raw register values; the client decodes them. `deviceName` is empty when no known device occupies the slave.
1128
+ *
1129
+ * @name GetBusConfig
1130
+ * @summary Inspect each slave's static EtherCAT Slave Controller configuration
1131
+ * @request GET:/api/bus-config
1132
+ */
1133
+ getBusConfig = (params = {}) => this.request({
1134
+ path: `/api/bus-config`,
1135
+ method: "GET",
1136
+ format: "json",
1137
+ ...params,
1138
+ });
1139
+ /**
1140
+ * @description Returns the bus as an ENI document, the vendor-neutral configuration a third-party master (acontis EC-Master, TwinCAT, CODESYS) replays to bring the same bus up. Where an ESI file describes one device family, an ENI describes one assembled network, and it is imperative rather than declarative: every configuration step is an EtherCAT datagram or a CoE download tagged with the AL-state transition it belongs to. The document carries each device's identity and addresses, its Sync Manager and FMMU register writes, its mailbox windows, its PDO assignment as CoE downloads, the AL-state walk with a read that holds the master until the device arrives, the cyclic frame, and the named process image. Conforms to ENI Schema 1.7 (ETG.2100). The bus must be configured. FMMUs and logical addresses come into being at the SAFE-OP transition, so a bus in PRE-OP has no mapping to describe and the call returns 409. **This drives the bus.** Each device's SII is read for its port layout and bootstrap mailbox, and its PDO assignment is read over CoE, so the call costs one EEPROM read and a short burst of SDO uploads per device. It is an export action, not an accessor. Distributed clocks are left out, so the generated configuration runs in free-run — which is how Motion Master runs the bus itself. The `DC` element needs an `AssignActivate` word, and a SOMANET drive does not carry the SII category that holds one. `X-Eni-Warnings` counts the parts that were asked for and not answered; each is also logged. A warning costs one optional element, never the document: a device whose SII will not read keeps every init command.
1141
+ *
1142
+ * @name ExportEni
1143
+ * @summary Export the bus as an EtherCAT Network Information (ENI) file
1144
+ * @request GET:/api/eni
1145
+ */
1146
+ exportEni = (params = {}) => this.request({
1147
+ path: `/api/eni`,
1148
+ method: "GET",
1149
+ ...params,
1150
+ });
1151
+ /**
1152
+ * @description Returns every registered monitoring as a resource — its configuration plus, per parameter, how its value is sourced, and the current buffer fill.
1153
+ *
1154
+ * @name ListMonitorings
1155
+ * @summary List all monitorings
1156
+ * @request GET:/api/monitorings
1157
+ */
1158
+ listMonitorings = (params = {}) => this.request({
1159
+ path: `/api/monitorings`,
1160
+ method: "GET",
1161
+ format: "json",
1162
+ ...params,
1163
+ });
1164
+ /**
1165
+ * @description Registers a monitoring and starts sampling it off the real-time thread. Each parameter is classified once against the published process image: PDO-mapped objects are decoded from the live image; the rest are polled over SDO in the background. Create monitorings after the bus is operational so PDO objects are recognised as PDO. Monitoring is live-only — a parameter samples `null` while its owning device is not exchanging (SAFE-OP/OP).
1166
+ *
1167
+ * @name CreateMonitoring
1168
+ * @summary Create a monitoring
1169
+ * @request POST:/api/monitorings
1170
+ */
1171
+ createMonitoring = (data, params = {}) => this.request({
1172
+ path: `/api/monitorings`,
1173
+ method: "POST",
1174
+ body: data,
1175
+ type: ContentType.Json,
1176
+ format: "json",
1177
+ ...params,
1178
+ });
1179
+ /**
1180
+ * No description
1181
+ *
1182
+ * @name GetMonitoring
1183
+ * @summary Get a monitoring
1184
+ * @request GET:/api/monitorings/{topic}
1185
+ */
1186
+ getMonitoring = (topic, params = {}) => this.request({
1187
+ path: `/api/monitorings/${topic}`,
1188
+ method: "GET",
1189
+ format: "json",
1190
+ ...params,
1191
+ });
1192
+ /**
1193
+ * @description Stops sampling and releases the monitoring's SDO parameters from the shared refresher — an object no remaining monitoring needs stops being polled.
1194
+ *
1195
+ * @name DeleteMonitoring
1196
+ * @summary Delete a monitoring
1197
+ * @request DELETE:/api/monitorings/{topic}
1198
+ */
1199
+ deleteMonitoring = (topic, params = {}) => this.request({
1200
+ path: `/api/monitorings/${topic}`,
1201
+ method: "DELETE",
1202
+ ...params,
1203
+ });
1204
+ /**
1205
+ * @description Returns one entry per cached parameter-definition file on disk, keyed by device identity (vendor, product, revision). The cache stores only the object-dictionary *definitions* (not live values), so a scan of previously-seen hardware can skip the slow SDO enumeration. Independent of the bus — works whether or not a driver is initialised.
1206
+ *
1207
+ * @name ListParameterCacheEntries
1208
+ * @summary List on-disk parameter caches
1209
+ * @request GET:/api/parameter-cache
1210
+ */
1211
+ listParameterCacheEntries = (params = {}) => this.request({
1212
+ path: `/api/parameter-cache`,
1213
+ method: "GET",
1214
+ format: "json",
1215
+ ...params,
1216
+ });
1217
+ /**
1218
+ * @description Returns the raw JSON cache file for the given id, verbatim, so it can be saved or inspected offline.
1219
+ *
1220
+ * @name GetParameterCache
1221
+ * @summary Download a parameter-cache file
1222
+ * @request GET:/api/parameter-cache/{id}
1223
+ */
1224
+ getParameterCache = (id, params = {}) => this.request({
1225
+ path: `/api/parameter-cache/${id}`,
1226
+ method: "GET",
1227
+ format: "json",
1228
+ ...params,
1229
+ });
1230
+ /**
1231
+ * @description Removes one cached file from disk. The next scan of that hardware re-enumerates it. Useful when a vendor reused a revision across an object-dictionary change.
1232
+ *
1233
+ * @name DeleteParameterCache
1234
+ * @summary Delete a parameter-cache file
1235
+ * @request DELETE:/api/parameter-cache/{id}
1236
+ */
1237
+ deleteParameterCache = (id, params = {}) => this.request({
1238
+ path: `/api/parameter-cache/${id}`,
1239
+ method: "DELETE",
1240
+ ...params,
1241
+ });
1242
+ /**
1243
+ * @description Returns every file in Motion Master's per-user cache directory, flattened: one entry per file with its full path relative to the cache root, whatever the nesting. Directories have no entries of their own. Independent of the bus — works whether or not a driver is initialised. The cache is a plain file store for data that should outlive a restart. The store itself neither validates nor interprets an upload — `PUT` accepts any bytes at any valid path, and putting a file here does not, on its own, make Motion Master do anything with it; the client chooses the paths. That is separate from whether a *feature* reads a given file, and several do: `parameters/` (the on-disk object-dictionary cache) and `dumps/` (`.mmpd` recorder dumps) are written and consumed by those features, with more to follow. Their files appear in this listing alongside everything else, so a client must not assume every entry was uploaded through this API, nor that deleting one is inconsequential.
1244
+ *
1245
+ * @name ListUserCacheFiles
1246
+ * @summary List the files in the user cache
1247
+ * @request GET:/api/user-cache
1248
+ */
1249
+ listUserCacheFiles = (params = {}) => this.request({
1250
+ path: `/api/user-cache`,
1251
+ method: "GET",
1252
+ format: "json",
1253
+ ...params,
1254
+ });
1255
+ /**
1256
+ * @description Returns the file verbatim, always as `application/octet-stream` and always with `Content-Disposition: attachment`, `X-Content-Type-Options: nosniff` and a `default-src 'none'; sandbox` CSP. The bytes are whatever a user uploaded, served from the API's own origin, so a browser must never *render* them — a rendered upload would be stored XSS against the origin that controls the drives, which CORS cannot help with. No extension is trusted to name a content type, and the attachment carries no `filename` (the path is user-controlled and must not reach a response header). Clients read the bytes and name the saved file themselves.
1257
+ *
1258
+ * @name GetUserCacheFile
1259
+ * @summary Download a user-cache file
1260
+ * @request GET:/api/user-cache/{path}
1261
+ */
1262
+ getUserCacheFile = (path, params = {}) => this.request({
1263
+ path: `/api/user-cache/${path}`,
1264
+ method: "GET",
1265
+ ...params,
1266
+ });
1267
+ /**
1268
+ * @description Writes the request body to the given path, creating parent directories as needed and replacing any existing file. The write is atomic — a concurrent reader sees either the old contents or the new one, never a partial file. The body is taken as raw bytes whatever its content type.
1269
+ *
1270
+ * @name PutUserCacheFile
1271
+ * @summary Upload a user-cache file
1272
+ * @request PUT:/api/user-cache/{path}
1273
+ */
1274
+ putUserCacheFile = (path, data, params = {}) => this.request({
1275
+ path: `/api/user-cache/${path}`,
1276
+ method: "PUT",
1277
+ body: data,
1278
+ format: "json",
1279
+ ...params,
1280
+ });
1281
+ /**
1282
+ * @description Removes a file, or a directory and everything under it. Directories left empty between the removed path and the cache root are pruned, so no husks remain. A file the running server holds open is refused — see `deletable` on `UserCacheFile`.
1283
+ *
1284
+ * @name DeleteUserCacheFile
1285
+ * @summary Delete a user-cache file or directory
1286
+ * @request DELETE:/api/user-cache/{path}
1287
+ */
1288
+ deleteUserCacheFile = (path, params = {}) => this.request({
1289
+ path: `/api/user-cache/${path}`,
1290
+ method: "DELETE",
1291
+ ...params,
1292
+ });
1293
+ }