@synapticon/motion-master-client 6.0.0-alpha.84 → 6.0.0-alpha.85

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/eni.d.ts ADDED
@@ -0,0 +1,197 @@
1
+ export interface EniExport {
2
+ xml: string;
3
+ warnings: number;
4
+ }
5
+ export declare function fetchEni(baseUrl: string, options?: {
6
+ fetch?: typeof fetch;
7
+ signal?: AbortSignal;
8
+ }): Promise<EniExport>;
9
+ export interface EniBytes {
10
+ hex: string;
11
+ bytes: number;
12
+ }
13
+ export interface EniRegisterAnnotation {
14
+ name: string;
15
+ description: string;
16
+ instance?: number;
17
+ decoded?: Record<string, number | boolean | string>;
18
+ requestsState?: string;
19
+ waitsForState?: string;
20
+ }
21
+ export interface EniInitCmd {
22
+ transitions?: string[];
23
+ states?: string[];
24
+ inputOffs?: number;
25
+ outputOffs?: number;
26
+ cmd: number;
27
+ cmdName: string;
28
+ beforeSlave?: boolean;
29
+ comment?: string;
30
+ requires?: string;
31
+ adp?: number;
32
+ ado?: number;
33
+ addr?: number;
34
+ data?: EniBytes;
35
+ dataLength?: number;
36
+ cnt?: number;
37
+ retries?: number;
38
+ timeoutMs?: number;
39
+ validate?: {
40
+ data: EniBytes;
41
+ dataMask?: EniBytes;
42
+ timeoutMs: number;
43
+ };
44
+ register?: EniRegisterAnnotation;
45
+ }
46
+ export interface EniCoeCmd {
47
+ transitions: string[];
48
+ timeoutMs: number;
49
+ ccs: number;
50
+ ccsName: 'download' | 'upload';
51
+ index: number;
52
+ subindex: number;
53
+ comment?: string;
54
+ data?: EniBytes;
55
+ disabled?: boolean;
56
+ }
57
+ export interface EniPdoEntry {
58
+ index: number;
59
+ subindex: number;
60
+ bitLen: number;
61
+ name?: string;
62
+ dataType?: string;
63
+ comment?: string;
64
+ }
65
+ export interface EniPdo {
66
+ index: number;
67
+ name: string;
68
+ syncManager?: number;
69
+ fixed?: boolean;
70
+ mandatory?: boolean;
71
+ entries: EniPdoEntry[];
72
+ }
73
+ export interface EniSyncManager {
74
+ index: number;
75
+ type: string;
76
+ startAddress: number;
77
+ controlByte: number;
78
+ enable: boolean;
79
+ minSize?: number;
80
+ maxSize?: number;
81
+ defaultSize?: number;
82
+ watchdog?: number;
83
+ }
84
+ export interface EniMailboxWindow {
85
+ start: number;
86
+ length: number;
87
+ pollTime?: number;
88
+ statusBitAddr?: number;
89
+ }
90
+ export interface EniSlave {
91
+ info: {
92
+ name: string;
93
+ physAddr: number;
94
+ autoIncAddr: number;
95
+ physics: string;
96
+ vendorId: number;
97
+ productCode: number;
98
+ revisionNo: number;
99
+ serialNo: number;
100
+ };
101
+ processData?: {
102
+ send?: {
103
+ bitStart: number;
104
+ bitLength: number;
105
+ };
106
+ recv?: {
107
+ bitStart: number;
108
+ bitLength: number;
109
+ };
110
+ syncManagers: EniSyncManager[];
111
+ rxPdos?: EniPdo[];
112
+ txPdos?: EniPdo[];
113
+ };
114
+ mailbox?: {
115
+ send: EniMailboxWindow;
116
+ recv: EniMailboxWindow;
117
+ bootstrap?: {
118
+ send: EniMailboxWindow;
119
+ recv: EniMailboxWindow;
120
+ };
121
+ protocols: string[];
122
+ coeInitCmds: EniCoeCmd[];
123
+ };
124
+ initCmds: EniInitCmd[];
125
+ previousPorts?: {
126
+ port: string;
127
+ selected: boolean;
128
+ physAddr?: number;
129
+ deviceId?: number;
130
+ }[];
131
+ dc?: {
132
+ potentialReferenceClock?: boolean;
133
+ referenceClock?: boolean;
134
+ cycleTime0Ns?: number;
135
+ cycleTime1Ns?: number;
136
+ shiftTimeNs?: number;
137
+ };
138
+ }
139
+ export interface EniVariable {
140
+ name: string;
141
+ bitSize: number;
142
+ bitOffs: number;
143
+ comment?: string;
144
+ dataType?: string;
145
+ }
146
+ export interface EniNetwork {
147
+ master: {
148
+ name: string;
149
+ destination: string;
150
+ source: string;
151
+ etherType?: number;
152
+ mailboxStates?: {
153
+ startAddr: number;
154
+ count: number;
155
+ };
156
+ eoe?: {
157
+ maxPorts: number;
158
+ maxFrames: number;
159
+ maxMacs: number;
160
+ };
161
+ initCmds: EniInitCmd[];
162
+ };
163
+ slaves: EniSlave[];
164
+ cyclic?: {
165
+ comment?: string;
166
+ taskId?: string;
167
+ cycleTimeUs?: number;
168
+ priority?: number;
169
+ frames: {
170
+ comment?: string;
171
+ cmds: EniInitCmd[];
172
+ }[];
173
+ };
174
+ processImage?: {
175
+ inputs?: {
176
+ byteSize: number;
177
+ variables: EniVariable[];
178
+ };
179
+ outputs?: {
180
+ byteSize: number;
181
+ variables: EniVariable[];
182
+ };
183
+ };
184
+ }
185
+ export interface EniParseResult {
186
+ network: EniNetwork;
187
+ warnings: string[];
188
+ summary: {
189
+ devices: number;
190
+ datagrams: number;
191
+ coeTransfers: number;
192
+ };
193
+ }
194
+ export declare function parseEni(baseUrl: string, xml: string, options?: {
195
+ fetch?: typeof fetch;
196
+ signal?: AbortSignal;
197
+ }): Promise<EniParseResult>;
package/dist/eni.js ADDED
@@ -0,0 +1,61 @@
1
+ // EtherCAT Network Information (ENI) export.
2
+ //
3
+ // The generated client types `GET /api/eni` as a `File`, which is what swagger-typescript-api makes
4
+ // of a binary response. An ENI is text, and what a caller wants is the document plus the warning
5
+ // count that came with it, so this wraps the call.
6
+ /// Exports the bus as an ENI document (`GET /api/eni`). `baseUrl` is the HTTP API origin (e.g.
7
+ /// `https://host:61447`). Throws on a non-OK response, with the response body's `error` message
8
+ /// when there is one — a bus that has not reached SAFE-OP or OP answers 409, because before that
9
+ /// there is no mapping to describe. Pass `fetch` to inject an implementation (Node, request
10
+ /// logging).
11
+ export async function fetchEni(baseUrl, options = {}) {
12
+ const doFetch = options.fetch ?? fetch;
13
+ const res = await doFetch(`${baseUrl.replace(/\/+$/, '')}/api/eni`, {
14
+ headers: { Accept: 'application/xml' },
15
+ signal: options.signal,
16
+ });
17
+ if (!res.ok) {
18
+ let message = `HTTP ${res.status}`;
19
+ try {
20
+ const body = (await res.json());
21
+ if (body?.error) {
22
+ message = body.error;
23
+ }
24
+ }
25
+ catch {
26
+ // Non-JSON error body — keep the status message.
27
+ }
28
+ throw new Error(message);
29
+ }
30
+ const warnings = Number.parseInt(res.headers.get('X-Eni-Warnings') ?? '0', 10);
31
+ return {
32
+ xml: await res.text(),
33
+ warnings: Number.isFinite(warnings) ? warnings : 0,
34
+ };
35
+ }
36
+ /// Parses an ENI document (`POST /api/eni/parse`). `baseUrl` is the HTTP API origin. The document
37
+ /// need not have come from this server — reading one another tool wrote is the point. Throws on a
38
+ /// non-OK response, with the server's `error` message when there is one.
39
+ export async function parseEni(baseUrl, xml, options = {}) {
40
+ const doFetch = options.fetch ?? fetch;
41
+ const res = await doFetch(`${baseUrl.replace(/\/+$/, '')}/api/eni/parse`, {
42
+ method: 'POST',
43
+ headers: { 'Content-Type': 'application/xml' },
44
+ body: xml,
45
+ signal: options.signal,
46
+ });
47
+ if (!res.ok) {
48
+ let message = `HTTP ${res.status}`;
49
+ try {
50
+ const body = (await res.json());
51
+ if (body?.error) {
52
+ message = body.error;
53
+ }
54
+ }
55
+ catch {
56
+ // Non-JSON error body — keep the status message.
57
+ }
58
+ throw new Error(message);
59
+ }
60
+ return (await res.json());
61
+ }
@@ -1122,6 +1122,27 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
1122
1122
  * @request GET:/api/integro-variant/options
1123
1123
  */
1124
1124
  listIntegroVariantOptions: (params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<IntegroVariantOption[], any>>;
1125
+ /**
1126
+ * @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`.
1127
+ *
1128
+ * @name ParseEni
1129
+ * @summary Parse an EtherCAT Network Information (ENI) file
1130
+ * @request POST:/api/eni/parse
1131
+ */
1132
+ parseEni: (data: string, params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<{
1133
+ /** The master, every device, the cyclic frame and the process image, keyed by the model's own field names. */
1134
+ network: object;
1135
+ /** One line per element seen and not modelled, or per value that would not decode. Each names a place in the document. */
1136
+ warnings: string[];
1137
+ summary: {
1138
+ /** Devices in the document */
1139
+ devices: number;
1140
+ /** EtherCAT init commands, master and devices together */
1141
+ datagrams: number;
1142
+ /** CoE init commands, which live under a device's mailbox */
1143
+ coeTransfers: number;
1144
+ };
1145
+ }, void>>;
1125
1146
  /**
1126
1147
  * @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.
1127
1148
  *
@@ -1490,6 +1511,22 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
1490
1511
  * @request GET:/api/bus-config
1491
1512
  */
1492
1513
  getBusConfig: (params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<SlaveConfig[], any>>;
1514
+ /**
1515
+ * @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.
1516
+ *
1517
+ * @name ExportEni
1518
+ * @summary Export the bus as an EtherCAT Network Information (ENI) file
1519
+ * @request GET:/api/eni
1520
+ */
1521
+ exportEni: (params?: RequestParams) => Promise<import("./http-client.js").HttpResponse<File, {
1522
+ /** Human-readable reason the export could not be produced */
1523
+ error: string;
1524
+ } | {
1525
+ /** The field that could not be written */
1526
+ error: string;
1527
+ } | {
1528
+ error: string;
1529
+ }>>;
1493
1530
  /**
1494
1531
  * @description Returns every registered monitoring as a resource — its configuration plus, per parameter, how its value is sourced, and the current buffer fill.
1495
1532
  *
@@ -875,6 +875,20 @@ export class Api extends HttpClient {
875
875
  format: "json",
876
876
  ...params,
877
877
  });
878
+ /**
879
+ * @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`.
880
+ *
881
+ * @name ParseEni
882
+ * @summary Parse an EtherCAT Network Information (ENI) file
883
+ * @request POST:/api/eni/parse
884
+ */
885
+ parseEni = (data, params = {}) => this.request({
886
+ path: `/api/eni/parse`,
887
+ method: "POST",
888
+ body: data,
889
+ format: "json",
890
+ ...params,
891
+ });
878
892
  /**
879
893
  * @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.
880
894
  *
@@ -1054,6 +1068,18 @@ export class Api extends HttpClient {
1054
1068
  format: "json",
1055
1069
  ...params,
1056
1070
  });
1071
+ /**
1072
+ * @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.
1073
+ *
1074
+ * @name ExportEni
1075
+ * @summary Export the bus as an EtherCAT Network Information (ENI) file
1076
+ * @request GET:/api/eni
1077
+ */
1078
+ exportEni = (params = {}) => this.request({
1079
+ path: `/api/eni`,
1080
+ method: "GET",
1081
+ ...params,
1082
+ });
1057
1083
  /**
1058
1084
  * @description Returns every registered monitoring as a resource — its configuration plus, per parameter, how its value is sourced, and the current buffer fill.
1059
1085
  *
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from './constants.js';
6
6
  export * from './web-socket-connection.js';
7
7
  export * from './client.js';
8
8
  export * from './lan.js';
9
+ export * from './eni.js';
9
10
  export * from './hex.js';
10
11
  export * from './sdo.js';
11
12
  export * from './mmpd.js';
package/dist/index.js CHANGED
@@ -10,9 +10,10 @@ export * from './constants.js';
10
10
  export * from './web-socket-connection.js';
11
11
  export * from './client.js';
12
12
  // Pure, framework-agnostic helpers (no DOM/React): hex formatting, CoE SDO value
13
- // encode/decode, SOMANET file-list parsing, and the LAN hostname mapping that lets a browser
14
- // reach a server on another machine under a name the bundled certificate covers.
13
+ // encode/decode, SOMANET file-list parsing, the ENI export, and the LAN hostname mapping that lets
14
+ // a browser reach a server on another machine under a name the bundled certificate covers.
15
15
  export * from './lan.js';
16
+ export * from './eni.js';
16
17
  export * from './hex.js';
17
18
  export * from './sdo.js';
18
19
  export * from './mmpd.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synapticon/motion-master-client",
3
- "version": "6.0.0-alpha.84",
3
+ "version": "6.0.0-alpha.85",
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": {