@synapticon/motion-master-client 6.0.0-alpha.62 → 6.0.0-alpha.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/Api.d.ts +133 -7
- package/dist/generated/Api.js +169 -9
- package/dist/generated/data-contracts.d.ts +302 -0
- package/package.json +1 -1
package/dist/generated/Api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AlStatusCode, Cia402Status, DcSyncStatus, DeviceDiagnostics, DeviceParameter, EscRegister, FoeErrorCode, GameLoopHealth, MailboxErrorCode, Monitoring, ObjectDataTypeInfo, OutputStageResult, ParameterCacheEntry, PdoMapping, PdoMappingRequest, ProcessDataWatchdog, ProcessImageObject, SdoAbortCode, SlaveConfig, SlaveInformationInterface } from "./data-contracts.js";
|
|
1
|
+
import { AlStatusCode, BrakeState, Cia402Status, DcSyncStatus, DeviceDiagnostics, DeviceParameter, EscRegister, EsiParseResult, FoeErrorCode, GameLoopHealth, MailboxErrorCode, Monitoring, ObjectDataTypeInfo, OutputStageResult, ParameterCacheEntry, PdoMapping, PdoMappingRequest, ProcedureListing, ProcedureRequest, ProcedureSnapshot, ProcessDataWatchdog, ProcessImageObject, SdoAbortCode, SlaveConfig, SlaveInformationInterface, UserCacheListing } from "./data-contracts.js";
|
|
2
2
|
import { HttpClient, RequestParams } from "./http-client.js";
|
|
3
3
|
export declare class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
|
|
4
4
|
/**
|
|
@@ -662,6 +662,78 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
|
|
|
662
662
|
*/
|
|
663
663
|
value: number;
|
|
664
664
|
}, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<void, void>>;
|
|
665
|
+
/**
|
|
666
|
+
* @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.
|
|
667
|
+
*
|
|
668
|
+
* @name ListProcedures
|
|
669
|
+
* @summary List the procedures a device supports
|
|
670
|
+
* @request GET:/api/devices/{slavePosition}/procedures
|
|
671
|
+
*/
|
|
672
|
+
listProcedures: (slavePosition: number, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<ProcedureListing[], void>>;
|
|
673
|
+
/**
|
|
674
|
+
* @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; which fields it accepts (and whether it needs any at all) is per procedure, so consult the descriptor from `GET /api/devices/{slavePosition}/procedures`. 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.
|
|
675
|
+
*
|
|
676
|
+
* @name StartProcedure
|
|
677
|
+
* @summary Start a procedure on a device
|
|
678
|
+
* @request POST:/api/devices/{slavePosition}/procedures/{procedureName}
|
|
679
|
+
*/
|
|
680
|
+
startProcedure: (slavePosition: number, procedureName: string, data?: ProcedureRequest, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<ProcedureSnapshot, void>>;
|
|
681
|
+
/**
|
|
682
|
+
* @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.
|
|
683
|
+
*
|
|
684
|
+
* @name GetProcedure
|
|
685
|
+
* @summary Read the state of a procedure on a device
|
|
686
|
+
* @request GET:/api/devices/{slavePosition}/procedures/{procedureName}
|
|
687
|
+
*/
|
|
688
|
+
getProcedure: (slavePosition: number, procedureName: string, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<ProcedureSnapshot, void>>;
|
|
689
|
+
/**
|
|
690
|
+
* @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.
|
|
691
|
+
*
|
|
692
|
+
* @name CancelProcedure
|
|
693
|
+
* @summary Cancel a running procedure on a device
|
|
694
|
+
* @request DELETE:/api/devices/{slavePosition}/procedures/{procedureName}
|
|
695
|
+
*/
|
|
696
|
+
cancelProcedure: (slavePosition: number, procedureName: string, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<void, void>>;
|
|
697
|
+
/**
|
|
698
|
+
* @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.
|
|
699
|
+
*
|
|
700
|
+
* @name GetBrake
|
|
701
|
+
* @summary Read a drive's brake configuration and state
|
|
702
|
+
* @request GET:/api/devices/{slavePosition}/brake
|
|
703
|
+
*/
|
|
704
|
+
getBrake: (slavePosition: number, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<BrakeState, void>>;
|
|
705
|
+
/**
|
|
706
|
+
* @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.
|
|
707
|
+
*
|
|
708
|
+
* @name ReleaseBrake
|
|
709
|
+
* @summary Release (disengage) a drive's brake
|
|
710
|
+
* @request POST:/api/devices/{slavePosition}/brake/release
|
|
711
|
+
*/
|
|
712
|
+
releaseBrake: (slavePosition: number, query?: {
|
|
713
|
+
/**
|
|
714
|
+
* Extra wait in milliseconds on top of the drive's pull time. Size it for the machine — release is open-loop, so this is the only margin between "commanded" and "assumed released".
|
|
715
|
+
* @min 0
|
|
716
|
+
* @default 50
|
|
717
|
+
* @example 50
|
|
718
|
+
*/
|
|
719
|
+
settle?: number;
|
|
720
|
+
}, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<BrakeState, void>>;
|
|
721
|
+
/**
|
|
722
|
+
* @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.
|
|
723
|
+
*
|
|
724
|
+
* @name EngageBrake
|
|
725
|
+
* @summary Engage a drive's brake
|
|
726
|
+
* @request POST:/api/devices/{slavePosition}/brake/engage
|
|
727
|
+
*/
|
|
728
|
+
engageBrake: (slavePosition: number, query?: {
|
|
729
|
+
/**
|
|
730
|
+
* How long to wait in milliseconds after commanding the brake before answering.
|
|
731
|
+
* @min 0
|
|
732
|
+
* @default 50
|
|
733
|
+
* @example 50
|
|
734
|
+
*/
|
|
735
|
+
settle?: number;
|
|
736
|
+
}, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<BrakeState, void>>;
|
|
665
737
|
/**
|
|
666
738
|
* @description Commands the device to persist its current parameters (the generic CANopen "store parameters" object 0x1010:01) and waits for confirmation. The server writes the ASCII "save" signature, waits a fixed settle (~1 s) for the drive to begin the flash write, then polls 0x1010:01 until it reads back 1 ("save completed"). Because a store in progress can leave the mailbox briefly unresponsive, a poll that does not yet confirm — a value mismatch or a transient read error alike — is retried up to `retries` more times, `interval` apart. The call blocks until the store is confirmed or the retry budget is exhausted (up to a few seconds); the device's mailbox must be active (PRE-OP/SAFE-OP/OP).
|
|
667
739
|
*
|
|
@@ -827,6 +899,17 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
|
|
|
827
899
|
* @request POST:/api/sii/parse
|
|
828
900
|
*/
|
|
829
901
|
parseSii: (data: File, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<SlaveInformationInterface, void>>;
|
|
902
|
+
/**
|
|
903
|
+
* @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.
|
|
904
|
+
*
|
|
905
|
+
* @name ParseEsi
|
|
906
|
+
* @summary Parse an EtherCAT Slave Information (ESI) file
|
|
907
|
+
* @request POST:/api/esi/parse
|
|
908
|
+
*/
|
|
909
|
+
parseEsi: (data: string, query?: {
|
|
910
|
+
/** Comma-separated `ModuleIdent` values (hexadecimal or decimal) restricting the merge, applied to every device by intersection with the idents that device's slots actually reference. Omit to merge every module each device references. */
|
|
911
|
+
modules?: string;
|
|
912
|
+
}, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<EsiParseResult, void>>;
|
|
830
913
|
/**
|
|
831
914
|
* @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).
|
|
832
915
|
*
|
|
@@ -1120,7 +1203,7 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
|
|
|
1120
1203
|
error: string;
|
|
1121
1204
|
}>>;
|
|
1122
1205
|
/**
|
|
1123
|
-
* @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
|
|
1206
|
+
* @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.)
|
|
1124
1207
|
*
|
|
1125
1208
|
* @name DumpProcessData
|
|
1126
1209
|
* @summary Dump the recorder ring to a binary .mmpd file
|
|
@@ -1221,17 +1304,17 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
|
|
|
1221
1304
|
/**
|
|
1222
1305
|
* @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.
|
|
1223
1306
|
*
|
|
1224
|
-
* @name
|
|
1307
|
+
* @name ListParameterCacheEntries
|
|
1225
1308
|
* @summary List on-disk parameter caches
|
|
1226
|
-
* @request GET:/api/parameter-
|
|
1309
|
+
* @request GET:/api/parameter-cache
|
|
1227
1310
|
*/
|
|
1228
|
-
|
|
1311
|
+
listParameterCacheEntries: (params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<ParameterCacheEntry[], any>>;
|
|
1229
1312
|
/**
|
|
1230
1313
|
* @description Returns the raw JSON cache file for the given id, verbatim, so it can be saved or inspected offline.
|
|
1231
1314
|
*
|
|
1232
1315
|
* @name GetParameterCache
|
|
1233
1316
|
* @summary Download a parameter-cache file
|
|
1234
|
-
* @request GET:/api/parameter-
|
|
1317
|
+
* @request GET:/api/parameter-cache/{id}
|
|
1235
1318
|
*/
|
|
1236
1319
|
getParameterCache: (id: string, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<object, void>>;
|
|
1237
1320
|
/**
|
|
@@ -1239,7 +1322,50 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
|
|
|
1239
1322
|
*
|
|
1240
1323
|
* @name DeleteParameterCache
|
|
1241
1324
|
* @summary Delete a parameter-cache file
|
|
1242
|
-
* @request DELETE:/api/parameter-
|
|
1325
|
+
* @request DELETE:/api/parameter-cache/{id}
|
|
1243
1326
|
*/
|
|
1244
1327
|
deleteParameterCache: (id: string, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<void, void>>;
|
|
1328
|
+
/**
|
|
1329
|
+
* @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.
|
|
1330
|
+
*
|
|
1331
|
+
* @name ListUserCacheFiles
|
|
1332
|
+
* @summary List the files in the user cache
|
|
1333
|
+
* @request GET:/api/user-cache
|
|
1334
|
+
*/
|
|
1335
|
+
listUserCacheFiles: (params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<UserCacheListing, void>>;
|
|
1336
|
+
/**
|
|
1337
|
+
* @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.
|
|
1338
|
+
*
|
|
1339
|
+
* @name GetUserCacheFile
|
|
1340
|
+
* @summary Download a user-cache file
|
|
1341
|
+
* @request GET:/api/user-cache/{path}
|
|
1342
|
+
*/
|
|
1343
|
+
getUserCacheFile: (path: string, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<Blob, void>>;
|
|
1344
|
+
/**
|
|
1345
|
+
* @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.
|
|
1346
|
+
*
|
|
1347
|
+
* @name PutUserCacheFile
|
|
1348
|
+
* @summary Upload a user-cache file
|
|
1349
|
+
* @request PUT:/api/user-cache/{path}
|
|
1350
|
+
*/
|
|
1351
|
+
putUserCacheFile: (path: string, data: File, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<{
|
|
1352
|
+
/**
|
|
1353
|
+
* The path written, relative to the cache root.
|
|
1354
|
+
* @example "configs/machine-a.json"
|
|
1355
|
+
*/
|
|
1356
|
+
path: string;
|
|
1357
|
+
/**
|
|
1358
|
+
* Bytes written.
|
|
1359
|
+
* @example 20480
|
|
1360
|
+
*/
|
|
1361
|
+
size: number;
|
|
1362
|
+
}, void>>;
|
|
1363
|
+
/**
|
|
1364
|
+
* @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.
|
|
1365
|
+
*
|
|
1366
|
+
* @name DeleteUserCacheFile
|
|
1367
|
+
* @summary Delete a user-cache file or directory
|
|
1368
|
+
* @request DELETE:/api/user-cache/{path}
|
|
1369
|
+
*/
|
|
1370
|
+
deleteUserCacheFile: (path: string, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<void, void>>;
|
|
1245
1371
|
}
|
package/dist/generated/Api.js
CHANGED
|
@@ -456,6 +456,100 @@ export class Api extends HttpClient {
|
|
|
456
456
|
type: ContentType.Json,
|
|
457
457
|
...params,
|
|
458
458
|
});
|
|
459
|
+
/**
|
|
460
|
+
* @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.
|
|
461
|
+
*
|
|
462
|
+
* @name ListProcedures
|
|
463
|
+
* @summary List the procedures a device supports
|
|
464
|
+
* @request GET:/api/devices/{slavePosition}/procedures
|
|
465
|
+
*/
|
|
466
|
+
listProcedures = (slavePosition, params = {}) => this.request({
|
|
467
|
+
path: `/api/devices/${slavePosition}/procedures`,
|
|
468
|
+
method: "GET",
|
|
469
|
+
format: "json",
|
|
470
|
+
...params,
|
|
471
|
+
});
|
|
472
|
+
/**
|
|
473
|
+
* @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; which fields it accepts (and whether it needs any at all) is per procedure, so consult the descriptor from `GET /api/devices/{slavePosition}/procedures`. 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.
|
|
474
|
+
*
|
|
475
|
+
* @name StartProcedure
|
|
476
|
+
* @summary Start a procedure on a device
|
|
477
|
+
* @request POST:/api/devices/{slavePosition}/procedures/{procedureName}
|
|
478
|
+
*/
|
|
479
|
+
startProcedure = (slavePosition, procedureName, data, params = {}) => this.request({
|
|
480
|
+
path: `/api/devices/${slavePosition}/procedures/${procedureName}`,
|
|
481
|
+
method: "POST",
|
|
482
|
+
body: data,
|
|
483
|
+
type: ContentType.Json,
|
|
484
|
+
format: "json",
|
|
485
|
+
...params,
|
|
486
|
+
});
|
|
487
|
+
/**
|
|
488
|
+
* @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.
|
|
489
|
+
*
|
|
490
|
+
* @name GetProcedure
|
|
491
|
+
* @summary Read the state of a procedure on a device
|
|
492
|
+
* @request GET:/api/devices/{slavePosition}/procedures/{procedureName}
|
|
493
|
+
*/
|
|
494
|
+
getProcedure = (slavePosition, procedureName, params = {}) => this.request({
|
|
495
|
+
path: `/api/devices/${slavePosition}/procedures/${procedureName}`,
|
|
496
|
+
method: "GET",
|
|
497
|
+
format: "json",
|
|
498
|
+
...params,
|
|
499
|
+
});
|
|
500
|
+
/**
|
|
501
|
+
* @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.
|
|
502
|
+
*
|
|
503
|
+
* @name CancelProcedure
|
|
504
|
+
* @summary Cancel a running procedure on a device
|
|
505
|
+
* @request DELETE:/api/devices/{slavePosition}/procedures/{procedureName}
|
|
506
|
+
*/
|
|
507
|
+
cancelProcedure = (slavePosition, procedureName, params = {}) => this.request({
|
|
508
|
+
path: `/api/devices/${slavePosition}/procedures/${procedureName}`,
|
|
509
|
+
method: "DELETE",
|
|
510
|
+
...params,
|
|
511
|
+
});
|
|
512
|
+
/**
|
|
513
|
+
* @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.
|
|
514
|
+
*
|
|
515
|
+
* @name GetBrake
|
|
516
|
+
* @summary Read a drive's brake configuration and state
|
|
517
|
+
* @request GET:/api/devices/{slavePosition}/brake
|
|
518
|
+
*/
|
|
519
|
+
getBrake = (slavePosition, params = {}) => this.request({
|
|
520
|
+
path: `/api/devices/${slavePosition}/brake`,
|
|
521
|
+
method: "GET",
|
|
522
|
+
format: "json",
|
|
523
|
+
...params,
|
|
524
|
+
});
|
|
525
|
+
/**
|
|
526
|
+
* @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.
|
|
527
|
+
*
|
|
528
|
+
* @name ReleaseBrake
|
|
529
|
+
* @summary Release (disengage) a drive's brake
|
|
530
|
+
* @request POST:/api/devices/{slavePosition}/brake/release
|
|
531
|
+
*/
|
|
532
|
+
releaseBrake = (slavePosition, query, params = {}) => this.request({
|
|
533
|
+
path: `/api/devices/${slavePosition}/brake/release`,
|
|
534
|
+
method: "POST",
|
|
535
|
+
query: query,
|
|
536
|
+
format: "json",
|
|
537
|
+
...params,
|
|
538
|
+
});
|
|
539
|
+
/**
|
|
540
|
+
* @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.
|
|
541
|
+
*
|
|
542
|
+
* @name EngageBrake
|
|
543
|
+
* @summary Engage a drive's brake
|
|
544
|
+
* @request POST:/api/devices/{slavePosition}/brake/engage
|
|
545
|
+
*/
|
|
546
|
+
engageBrake = (slavePosition, query, params = {}) => this.request({
|
|
547
|
+
path: `/api/devices/${slavePosition}/brake/engage`,
|
|
548
|
+
method: "POST",
|
|
549
|
+
query: query,
|
|
550
|
+
format: "json",
|
|
551
|
+
...params,
|
|
552
|
+
});
|
|
459
553
|
/**
|
|
460
554
|
* @description Commands the device to persist its current parameters (the generic CANopen "store parameters" object 0x1010:01) and waits for confirmation. The server writes the ASCII "save" signature, waits a fixed settle (~1 s) for the drive to begin the flash write, then polls 0x1010:01 until it reads back 1 ("save completed"). Because a store in progress can leave the mailbox briefly unresponsive, a poll that does not yet confirm — a value mismatch or a transient read error alike — is retried up to `retries` more times, `interval` apart. The call blocks until the store is confirmed or the retry budget is exhausted (up to a few seconds); the device's mailbox must be active (PRE-OP/SAFE-OP/OP).
|
|
461
555
|
*
|
|
@@ -577,6 +671,21 @@ export class Api extends HttpClient {
|
|
|
577
671
|
format: "json",
|
|
578
672
|
...params,
|
|
579
673
|
});
|
|
674
|
+
/**
|
|
675
|
+
* @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.
|
|
676
|
+
*
|
|
677
|
+
* @name ParseEsi
|
|
678
|
+
* @summary Parse an EtherCAT Slave Information (ESI) file
|
|
679
|
+
* @request POST:/api/esi/parse
|
|
680
|
+
*/
|
|
681
|
+
parseEsi = (data, query, params = {}) => this.request({
|
|
682
|
+
path: `/api/esi/parse`,
|
|
683
|
+
method: "POST",
|
|
684
|
+
query: query,
|
|
685
|
+
body: data,
|
|
686
|
+
format: "json",
|
|
687
|
+
...params,
|
|
688
|
+
});
|
|
580
689
|
/**
|
|
581
690
|
* @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).
|
|
582
691
|
*
|
|
@@ -701,7 +810,7 @@ export class Api extends HttpClient {
|
|
|
701
810
|
...params,
|
|
702
811
|
});
|
|
703
812
|
/**
|
|
704
|
-
* @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
|
|
813
|
+
* @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.)
|
|
705
814
|
*
|
|
706
815
|
* @name DumpProcessData
|
|
707
816
|
* @summary Dump the recorder ring to a binary .mmpd file
|
|
@@ -797,12 +906,12 @@ export class Api extends HttpClient {
|
|
|
797
906
|
/**
|
|
798
907
|
* @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.
|
|
799
908
|
*
|
|
800
|
-
* @name
|
|
909
|
+
* @name ListParameterCacheEntries
|
|
801
910
|
* @summary List on-disk parameter caches
|
|
802
|
-
* @request GET:/api/parameter-
|
|
911
|
+
* @request GET:/api/parameter-cache
|
|
803
912
|
*/
|
|
804
|
-
|
|
805
|
-
path: `/api/parameter-
|
|
913
|
+
listParameterCacheEntries = (params = {}) => this.request({
|
|
914
|
+
path: `/api/parameter-cache`,
|
|
806
915
|
method: "GET",
|
|
807
916
|
format: "json",
|
|
808
917
|
...params,
|
|
@@ -812,10 +921,10 @@ export class Api extends HttpClient {
|
|
|
812
921
|
*
|
|
813
922
|
* @name GetParameterCache
|
|
814
923
|
* @summary Download a parameter-cache file
|
|
815
|
-
* @request GET:/api/parameter-
|
|
924
|
+
* @request GET:/api/parameter-cache/{id}
|
|
816
925
|
*/
|
|
817
926
|
getParameterCache = (id, params = {}) => this.request({
|
|
818
|
-
path: `/api/parameter-
|
|
927
|
+
path: `/api/parameter-cache/${id}`,
|
|
819
928
|
method: "GET",
|
|
820
929
|
format: "json",
|
|
821
930
|
...params,
|
|
@@ -825,10 +934,61 @@ export class Api extends HttpClient {
|
|
|
825
934
|
*
|
|
826
935
|
* @name DeleteParameterCache
|
|
827
936
|
* @summary Delete a parameter-cache file
|
|
828
|
-
* @request DELETE:/api/parameter-
|
|
937
|
+
* @request DELETE:/api/parameter-cache/{id}
|
|
829
938
|
*/
|
|
830
939
|
deleteParameterCache = (id, params = {}) => this.request({
|
|
831
|
-
path: `/api/parameter-
|
|
940
|
+
path: `/api/parameter-cache/${id}`,
|
|
941
|
+
method: "DELETE",
|
|
942
|
+
...params,
|
|
943
|
+
});
|
|
944
|
+
/**
|
|
945
|
+
* @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.
|
|
946
|
+
*
|
|
947
|
+
* @name ListUserCacheFiles
|
|
948
|
+
* @summary List the files in the user cache
|
|
949
|
+
* @request GET:/api/user-cache
|
|
950
|
+
*/
|
|
951
|
+
listUserCacheFiles = (params = {}) => this.request({
|
|
952
|
+
path: `/api/user-cache`,
|
|
953
|
+
method: "GET",
|
|
954
|
+
format: "json",
|
|
955
|
+
...params,
|
|
956
|
+
});
|
|
957
|
+
/**
|
|
958
|
+
* @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.
|
|
959
|
+
*
|
|
960
|
+
* @name GetUserCacheFile
|
|
961
|
+
* @summary Download a user-cache file
|
|
962
|
+
* @request GET:/api/user-cache/{path}
|
|
963
|
+
*/
|
|
964
|
+
getUserCacheFile = (path, params = {}) => this.request({
|
|
965
|
+
path: `/api/user-cache/${path}`,
|
|
966
|
+
method: "GET",
|
|
967
|
+
...params,
|
|
968
|
+
});
|
|
969
|
+
/**
|
|
970
|
+
* @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.
|
|
971
|
+
*
|
|
972
|
+
* @name PutUserCacheFile
|
|
973
|
+
* @summary Upload a user-cache file
|
|
974
|
+
* @request PUT:/api/user-cache/{path}
|
|
975
|
+
*/
|
|
976
|
+
putUserCacheFile = (path, data, params = {}) => this.request({
|
|
977
|
+
path: `/api/user-cache/${path}`,
|
|
978
|
+
method: "PUT",
|
|
979
|
+
body: data,
|
|
980
|
+
format: "json",
|
|
981
|
+
...params,
|
|
982
|
+
});
|
|
983
|
+
/**
|
|
984
|
+
* @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.
|
|
985
|
+
*
|
|
986
|
+
* @name DeleteUserCacheFile
|
|
987
|
+
* @summary Delete a user-cache file or directory
|
|
988
|
+
* @request DELETE:/api/user-cache/{path}
|
|
989
|
+
*/
|
|
990
|
+
deleteUserCacheFile = (path, params = {}) => this.request({
|
|
991
|
+
path: `/api/user-cache/${path}`,
|
|
832
992
|
method: "DELETE",
|
|
833
993
|
...params,
|
|
834
994
|
});
|
|
@@ -106,6 +106,33 @@ export interface GameLoopHealth {
|
|
|
106
106
|
*/
|
|
107
107
|
timestampUs: number;
|
|
108
108
|
}
|
|
109
|
+
/** The user cache's location on disk plus every file it holds. */
|
|
110
|
+
export interface UserCacheListing {
|
|
111
|
+
/**
|
|
112
|
+
* Absolute path of the cache directory on the server's filesystem. Platform-dependent (and overridable via the `userCache.directory` config key), so it is reported rather than assumed — show it to the user so they can find the files themselves.
|
|
113
|
+
* @example "/home/user/.cache/motion-master"
|
|
114
|
+
*/
|
|
115
|
+
root: string;
|
|
116
|
+
files: UserCacheFile[];
|
|
117
|
+
}
|
|
118
|
+
/** One file in the user cache. */
|
|
119
|
+
export interface UserCacheFile {
|
|
120
|
+
/**
|
|
121
|
+
* Path relative to the cache root, always with `/` separators. Use it verbatim in the download/upload/delete endpoints.
|
|
122
|
+
* @example "configs/machine-a.json"
|
|
123
|
+
*/
|
|
124
|
+
path: string;
|
|
125
|
+
/**
|
|
126
|
+
* File size in bytes.
|
|
127
|
+
* @example 20480
|
|
128
|
+
*/
|
|
129
|
+
size: number;
|
|
130
|
+
/**
|
|
131
|
+
* Last-write time, in milliseconds since the Unix epoch.
|
|
132
|
+
* @example 1783968687738
|
|
133
|
+
*/
|
|
134
|
+
modifiedMs: number;
|
|
135
|
+
}
|
|
109
136
|
/** Summary of one on-disk parameter-cache file (its identity, size, and count). */
|
|
110
137
|
export interface ParameterCacheEntry {
|
|
111
138
|
/**
|
|
@@ -317,6 +344,155 @@ export interface ProcessImageObject {
|
|
|
317
344
|
*/
|
|
318
345
|
bitLength: number;
|
|
319
346
|
}
|
|
347
|
+
/** One step of a procedure. `id` is stable across runs, so a client keys its label and its value formatting off it. A step never reports cancellation: cancelling stops the procedure, leaving the step it was on `running` and the rest `idle`, which records how far it got. */
|
|
348
|
+
export interface ProgressStep {
|
|
349
|
+
/**
|
|
350
|
+
* Stable identifier of the step within its procedure.
|
|
351
|
+
* @example "command"
|
|
352
|
+
*/
|
|
353
|
+
id: string;
|
|
354
|
+
/** @example "succeeded" */
|
|
355
|
+
status: "idle" | "running" | "succeeded" | "failed";
|
|
356
|
+
/**
|
|
357
|
+
* What the step produced, if anything — shape is per procedure. Absent when the step produced nothing. For `os-command` it is an object with `status`, `data`, and `errorCode` when the drive sent one.
|
|
358
|
+
* @example {"status":1,"data":[1,2,3,4,5,6]}
|
|
359
|
+
*/
|
|
360
|
+
value?: any;
|
|
361
|
+
/** Why the step failed. Absent unless it did. */
|
|
362
|
+
error?: string;
|
|
363
|
+
}
|
|
364
|
+
/** The complete state of a procedure on one device — an accumulating snapshot rather than an event, which is what makes polling lossless. */
|
|
365
|
+
export interface ProcedureSnapshot {
|
|
366
|
+
/**
|
|
367
|
+
* The one field a polling loop checks. `cancelled` is distinct from `failed` on purpose: "I stopped it" and "the drive could not do it" are different outcomes.
|
|
368
|
+
* @example "succeeded"
|
|
369
|
+
*/
|
|
370
|
+
status: "idle" | "running" | "succeeded" | "failed" | "cancelled";
|
|
371
|
+
/**
|
|
372
|
+
* How many runs have been accepted on this device since the last scan or reset; a rejected start does not count. Doubles as a generation counter — with no run id, a changed `runCount` is how a poller can tell one run from the next.
|
|
373
|
+
* @example 3
|
|
374
|
+
*/
|
|
375
|
+
runCount: number;
|
|
376
|
+
/**
|
|
377
|
+
* Epoch milliseconds the run began. Absent when never run.
|
|
378
|
+
* @format int64
|
|
379
|
+
* @example 1735821000123
|
|
380
|
+
*/
|
|
381
|
+
startedAt?: number;
|
|
382
|
+
/**
|
|
383
|
+
* Epoch milliseconds the run ended. Absent while running.
|
|
384
|
+
* @format int64
|
|
385
|
+
* @example 1735821042456
|
|
386
|
+
*/
|
|
387
|
+
finishedAt?: number;
|
|
388
|
+
/** The procedure's ordered steps, always the full template. */
|
|
389
|
+
steps: ProgressStep[];
|
|
390
|
+
/** Why the run failed when no step captured it — a failure belonging to no step, such as the device not being the kind the procedure needs. Absent otherwise. */
|
|
391
|
+
error?: string;
|
|
392
|
+
}
|
|
393
|
+
/** A drive's brake configuration and current state — the parts of object 0x2004 that decide what commanding the brake will actually do. Returned by every brake endpoint, so the outcome of a release or engage arrives in the same shape as a plain read. */
|
|
394
|
+
export interface BrakeState {
|
|
395
|
+
/**
|
|
396
|
+
* Brake state (0x2004:07). A brake is spring-engaged, so `engaged` is also its powered-off state.
|
|
397
|
+
* @example "engaged"
|
|
398
|
+
*/
|
|
399
|
+
status: "notConfigured" | "engaged" | "disengaged";
|
|
400
|
+
/**
|
|
401
|
+
* How the brake is driven (0x2004:04). `manualOutputVoltage` means the firmware does not drive it — the brake is a raw output voltage (0x2004:10) instead.
|
|
402
|
+
* @example "clutch"
|
|
403
|
+
*/
|
|
404
|
+
releaseStrategy: "manualOutputVoltage" | "clutch" | "pin";
|
|
405
|
+
/**
|
|
406
|
+
* How long the pull voltage is applied to release the brake (0x2004:03). The firmware blocks motion, and motion-related OS commands, until this expires.
|
|
407
|
+
* @example 120
|
|
408
|
+
*/
|
|
409
|
+
pullTimeMs: number;
|
|
410
|
+
/**
|
|
411
|
+
* Voltage that disengages the brake, in millivolts (0x2004:01).
|
|
412
|
+
* @example 24000
|
|
413
|
+
*/
|
|
414
|
+
pullVoltageMv: number;
|
|
415
|
+
/**
|
|
416
|
+
* Lower voltage that keeps it disengaged once released, in millivolts (0x2004:02).
|
|
417
|
+
* @example 12000
|
|
418
|
+
*/
|
|
419
|
+
holdVoltageMv: number;
|
|
420
|
+
/**
|
|
421
|
+
* Whether commanding the brake does anything at all — false for `manualOutputVoltage`. Derived from the release strategy and served so a client need not encode that rule.
|
|
422
|
+
* @example true
|
|
423
|
+
*/
|
|
424
|
+
softwareControllable: boolean;
|
|
425
|
+
/**
|
|
426
|
+
* Whether releasing this brake turns the motor. True only for a pin brake, whose release raises current progressively to lift the load off the pin.
|
|
427
|
+
* @example false
|
|
428
|
+
*/
|
|
429
|
+
releaseMovesShaft: boolean;
|
|
430
|
+
}
|
|
431
|
+
/** What a procedure *is*, independent of any run — everything needed to render a control for it. The text is served rather than held per client, because the house rule that every action carries a description and its caveats means it has to exist somewhere, and duplicating it in each client is how it goes stale. */
|
|
432
|
+
export interface ProcedureDescriptor {
|
|
433
|
+
/**
|
|
434
|
+
* Identifier: the `procedureName` path segment, and the key its snapshot is retained under.
|
|
435
|
+
* @example "os-command"
|
|
436
|
+
*/
|
|
437
|
+
name: string;
|
|
438
|
+
/**
|
|
439
|
+
* Short human-readable name.
|
|
440
|
+
* @example "OS command"
|
|
441
|
+
*/
|
|
442
|
+
title: string;
|
|
443
|
+
/** What the procedure does. */
|
|
444
|
+
description: string;
|
|
445
|
+
/** What a user must know before running it. May be empty. */
|
|
446
|
+
caveats: string[];
|
|
447
|
+
/**
|
|
448
|
+
* True if running it can move the shaft.
|
|
449
|
+
* @example true
|
|
450
|
+
*/
|
|
451
|
+
movesMotor: boolean;
|
|
452
|
+
/**
|
|
453
|
+
* True if the drive must be enabled before it will run.
|
|
454
|
+
* @example false
|
|
455
|
+
*/
|
|
456
|
+
requiresEnabled: boolean;
|
|
457
|
+
/**
|
|
458
|
+
* The ordered step ids the procedure reports against — bare ids, because a template's per-step status is always idle and says nothing. Live status for the same ids is in the snapshot's `steps`.
|
|
459
|
+
* @example ["command"]
|
|
460
|
+
*/
|
|
461
|
+
steps: string[];
|
|
462
|
+
}
|
|
463
|
+
/** One procedure paired with how its last run on this device went — an entry of `GET /api/devices/{slavePosition}/procedures`. The pairing is what lets a page render in a single request instead of one per procedure. */
|
|
464
|
+
export interface ProcedureListing {
|
|
465
|
+
/** What a procedure *is*, independent of any run — everything needed to render a control for it. The text is served rather than held per client, because the house rule that every action carries a description and its caveats means it has to exist somewhere, and duplicating it in each client is how it goes stale. */
|
|
466
|
+
descriptor: ProcedureDescriptor;
|
|
467
|
+
/** The complete state of a procedure on one device — an accumulating snapshot rather than an event, which is what makes polling lossless. */
|
|
468
|
+
snapshot: ProcedureSnapshot;
|
|
469
|
+
}
|
|
470
|
+
/** A procedure's parameters. Which fields apply is per procedure — consult its descriptor — so this schema is open, and a procedure that takes none may be started with no body at all. The properties below are those of `os-command`, where `command` is required. */
|
|
471
|
+
export interface ProcedureRequest {
|
|
472
|
+
/**
|
|
473
|
+
* `os-command` only, and required for it: the eight request bytes. Byte 0 is the OS command ID; bytes 1-7 are that command's parameters.
|
|
474
|
+
* @maxItems 8
|
|
475
|
+
* @minItems 8
|
|
476
|
+
* @example [8,0,0,0,0,0,0,0]
|
|
477
|
+
*/
|
|
478
|
+
command?: number[];
|
|
479
|
+
/**
|
|
480
|
+
* Ceiling on the whole command. Not a liveness check — the drive fails a command no service acknowledges within 5 s on its own — so size it for the command being run. Hitting it aborts the command on the drive.
|
|
481
|
+
* @min 1
|
|
482
|
+
* @max 600000
|
|
483
|
+
* @default 1000
|
|
484
|
+
* @example 30000
|
|
485
|
+
*/
|
|
486
|
+
timeoutMs?: number;
|
|
487
|
+
/**
|
|
488
|
+
* How often the server reads the drive's response object while waiting.
|
|
489
|
+
* @min 1
|
|
490
|
+
* @max 1000
|
|
491
|
+
* @default 10
|
|
492
|
+
*/
|
|
493
|
+
pollIntervalMs?: number;
|
|
494
|
+
[key: string]: any;
|
|
495
|
+
}
|
|
320
496
|
export interface OutputStageResult {
|
|
321
497
|
/**
|
|
322
498
|
* 1-based bus position of the owning device (echoes the request)
|
|
@@ -939,3 +1115,129 @@ export interface SiiPdo {
|
|
|
939
1115
|
flags?: number;
|
|
940
1116
|
}[];
|
|
941
1117
|
}
|
|
1118
|
+
/** One addressable `(index, subindex)` of a device's object dictionary, assembled from the ESI. `defaultData`/`minData`/`maxData` are **raw little-endian bytes** as uppercase hexBinary, the same spelling the ESI itself uses — the first byte is the least significant, so `92010200` is `0x00020192`. Decode them with `dataType`; a byte-wise comparison of `minData` against `maxData` is wrong when `isSigned`. */
|
|
1119
|
+
export interface EsiEntry {
|
|
1120
|
+
/** Effective CoE index */
|
|
1121
|
+
index: number;
|
|
1122
|
+
subindex: number;
|
|
1123
|
+
/** Index as written in the source dictionary; omitted when unrelocated */
|
|
1124
|
+
rawIndex?: number;
|
|
1125
|
+
/** The parent object's name — identical across all its subindices */
|
|
1126
|
+
objectName: string;
|
|
1127
|
+
/** This subindex's own name */
|
|
1128
|
+
entryName: string;
|
|
1129
|
+
/** The ESI's display-name override, else `entryName` */
|
|
1130
|
+
displayName: string;
|
|
1131
|
+
objectCode: "VAR" | "ARRAY" | "RECORD";
|
|
1132
|
+
/** The CoE object code (ETG.1000.6 §5) — 7, 8 or 9 */
|
|
1133
|
+
objectCodeValue?: number;
|
|
1134
|
+
/** Highest subindex; omitted for a VAR */
|
|
1135
|
+
numberOfEntries?: number;
|
|
1136
|
+
/**
|
|
1137
|
+
* ESI/IEC 61131-3 type name, e.g. `UDINT`, `STRING(50)`
|
|
1138
|
+
* @example "UDINT"
|
|
1139
|
+
*/
|
|
1140
|
+
dataTypeName: string;
|
|
1141
|
+
/**
|
|
1142
|
+
* ETG.1020 code, matching `DeviceParameter.dataType`; 0 if unresolved
|
|
1143
|
+
* @example 7
|
|
1144
|
+
*/
|
|
1145
|
+
dataType: number;
|
|
1146
|
+
isSigned: boolean;
|
|
1147
|
+
bitSize: number;
|
|
1148
|
+
/** Offset within the object's complete-access image */
|
|
1149
|
+
bitOffset: number;
|
|
1150
|
+
/** Uppercase hexBinary */
|
|
1151
|
+
defaultData?: string;
|
|
1152
|
+
minData?: string;
|
|
1153
|
+
maxData?: string;
|
|
1154
|
+
/** Packed ETG.1004 notation value */
|
|
1155
|
+
unit?: number;
|
|
1156
|
+
/** @example "mV" */
|
|
1157
|
+
unitSymbol?: string;
|
|
1158
|
+
access: {
|
|
1159
|
+
mode: "ro" | "rw" | "wo";
|
|
1160
|
+
readRestrictions?: "PreOP" | "PreOP_SafeOP" | "PreOP_OP" | "SafeOP" | "SafeOP_OP" | "OP";
|
|
1161
|
+
writeRestrictions?: "PreOP" | "PreOP_SafeOP" | "PreOP_OP" | "SafeOP" | "SafeOP_OP" | "OP";
|
|
1162
|
+
};
|
|
1163
|
+
/** Mandatory, optional or conditional */
|
|
1164
|
+
category: "m" | "o" | "c";
|
|
1165
|
+
/** Empty when not mappable; `t` TxPDO, `r` RxPDO */
|
|
1166
|
+
pdoMapping: "" | "t" | "r" | "tr";
|
|
1167
|
+
safetyMapping?: "si" | "so" | "sio" | "sp";
|
|
1168
|
+
sdoAccess: "SubIndexAccess" | "CompleteAccess";
|
|
1169
|
+
backup: boolean;
|
|
1170
|
+
setting: boolean;
|
|
1171
|
+
/** SoE IDN */
|
|
1172
|
+
attribute?: number;
|
|
1173
|
+
/** ETG.1000.6 ObjAccess bitfield synthesised from the flags — bits 0-2 read in PreOP/SafeOP/OP, 3-5 write, 6 RxPDO-mappable, 7 TxPDO-mappable, 8 backup, 9 setting. Matches `DeviceParameter.access`. */
|
|
1174
|
+
objAccess: number;
|
|
1175
|
+
/** This row's description (HTML), decoded from `properties`. On subindex 0 it is the object's; on a RECORD member it is that member's; an ARRAY element has none. */
|
|
1176
|
+
description?: string;
|
|
1177
|
+
/** Enum labels, from `<EnumInfo>` and/or an `options` property */
|
|
1178
|
+
options?: {
|
|
1179
|
+
label: string;
|
|
1180
|
+
value: number;
|
|
1181
|
+
}[];
|
|
1182
|
+
/** Raw `<Property>` annotations as written, for whatever this row describes. This is the ESI's generic extension mechanism; `description` and `options` above are conveniences decoded from the two conventional names, so a vendor using other names still has its data here. **Subindex 0 carries the object's own annotation** (stored once, not repeated onto every subindex); other rows carry only what that entry itself declares. */
|
|
1183
|
+
properties?: EsiProperties;
|
|
1184
|
+
/** Which dictionary this entry came from. The module's *name* is not repeated here — resolve `moduleIdent` against the top-level `modules` list. */
|
|
1185
|
+
source: {
|
|
1186
|
+
kind: "device" | "module";
|
|
1187
|
+
moduleIdent?: number;
|
|
1188
|
+
/** Zero-based slot number */
|
|
1189
|
+
slot?: number;
|
|
1190
|
+
/** Slot offset applied to `rawIndex` */
|
|
1191
|
+
indexOffset?: number;
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
/** Raw `<Property>` annotations as written, for whatever this row describes. This is the ESI's generic extension mechanism; `description` and `options` above are conveniences decoded from the two conventional names, so a vendor using other names still has its data here. **Subindex 0 carries the object's own annotation** (stored once, not repeated onto every subindex); other rows carry only what that entry itself declares. */
|
|
1195
|
+
export type EsiProperties = {
|
|
1196
|
+
name: string;
|
|
1197
|
+
value: string;
|
|
1198
|
+
}[];
|
|
1199
|
+
export interface EsiDeviceSummary {
|
|
1200
|
+
/** Zero-based position in the file */
|
|
1201
|
+
ordinal: number;
|
|
1202
|
+
/** @example "SOMANET Node" */
|
|
1203
|
+
type: string;
|
|
1204
|
+
name: string;
|
|
1205
|
+
productCode?: number;
|
|
1206
|
+
revisionNo?: number;
|
|
1207
|
+
/** @example 402 */
|
|
1208
|
+
profileNo?: number;
|
|
1209
|
+
groupType: string;
|
|
1210
|
+
/** Objects in the device's own dictionary, before any module merge */
|
|
1211
|
+
objectCount: number;
|
|
1212
|
+
rxPdoCount?: number;
|
|
1213
|
+
txPdoCount?: number;
|
|
1214
|
+
/** Every `ModuleIdent` the device's slots reference, in slot order */
|
|
1215
|
+
moduleIdents: number[];
|
|
1216
|
+
/** The parsed `<Slots>` block */
|
|
1217
|
+
slots?: object;
|
|
1218
|
+
/** This device's flat, slot-merged object dictionary */
|
|
1219
|
+
entries?: EsiEntry[];
|
|
1220
|
+
/** Problems found while assembling *this device* — a value whose byte length disagrees with its declared width, an unresolvable type, a module collision. Distinct from the top-level `warnings`, which are about the document; a different `modules` selection would produce a different list here. */
|
|
1221
|
+
warnings?: string[];
|
|
1222
|
+
}
|
|
1223
|
+
/** A parsed ESI. Every device carries its own assembled `entries` table. */
|
|
1224
|
+
export interface EsiParseResult {
|
|
1225
|
+
/** The `EtherCATInfo/@Version` attribute */
|
|
1226
|
+
version?: string;
|
|
1227
|
+
vendor: {
|
|
1228
|
+
/** @example 8914 */
|
|
1229
|
+
id: number;
|
|
1230
|
+
name: string;
|
|
1231
|
+
url?: string;
|
|
1232
|
+
};
|
|
1233
|
+
devices: EsiDeviceSummary[];
|
|
1234
|
+
modules: {
|
|
1235
|
+
moduleIdent: number;
|
|
1236
|
+
/** @example "CiA402 Dictionary" */
|
|
1237
|
+
type: string;
|
|
1238
|
+
name: string;
|
|
1239
|
+
objectCount?: number;
|
|
1240
|
+
}[];
|
|
1241
|
+
/** Recoverable problems in the document itself — a skipped malformed object, an unparsable value. A file with warnings still parses. Per-device assembly problems are reported on the device instead. */
|
|
1242
|
+
warnings?: string[];
|
|
1243
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synapticon/motion-master-client",
|
|
3
|
-
"version": "6.0.0-alpha.
|
|
3
|
+
"version": "6.0.0-alpha.64",
|
|
4
4
|
"description": "Isomorphic TypeScript client for Motion Master: the generated HTTP API client, the WebSocket connection, and helpers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|