abb-rws-client 0.2.0 → 0.7.1
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/CHANGELOG.md +171 -0
- package/README.md +364 -143
- package/dist/HttpSession.d.ts.map +1 -1
- package/dist/HttpSession.js +18 -1
- package/dist/HttpSession.js.map +1 -1
- package/dist/IRWSAdapter.d.ts +421 -0
- package/dist/IRWSAdapter.d.ts.map +1 -0
- package/dist/IRWSAdapter.js +2 -0
- package/dist/IRWSAdapter.js.map +1 -0
- package/dist/Logger.d.ts +35 -0
- package/dist/Logger.d.ts.map +1 -0
- package/dist/Logger.js +24 -0
- package/dist/Logger.js.map +1 -0
- package/dist/MultiRobotManager.d.ts +56 -0
- package/dist/MultiRobotManager.d.ts.map +1 -0
- package/dist/MultiRobotManager.js +129 -0
- package/dist/MultiRobotManager.js.map +1 -0
- package/dist/RWS1Adapter.d.ts +297 -0
- package/dist/RWS1Adapter.d.ts.map +1 -0
- package/dist/RWS1Adapter.js +870 -0
- package/dist/RWS1Adapter.js.map +1 -0
- package/dist/RWS2Adapter.d.ts +20 -0
- package/dist/RWS2Adapter.d.ts.map +1 -0
- package/dist/RWS2Adapter.js +19 -0
- package/dist/RWS2Adapter.js.map +1 -0
- package/dist/ResourceMapper.d.ts +226 -2
- package/dist/ResourceMapper.d.ts.map +1 -1
- package/dist/ResourceMapper.js +286 -3
- package/dist/ResourceMapper.js.map +1 -1
- package/dist/ResponseParser.d.ts +81 -2
- package/dist/ResponseParser.d.ts.map +1 -1
- package/dist/ResponseParser.js +309 -3
- package/dist/ResponseParser.js.map +1 -1
- package/dist/RobotManager.d.ts +536 -0
- package/dist/RobotManager.d.ts.map +1 -0
- package/dist/RobotManager.js +1523 -0
- package/dist/RobotManager.js.map +1 -0
- package/dist/RwsClient.d.ts +280 -4
- package/dist/RwsClient.d.ts.map +1 -1
- package/dist/RwsClient.js +698 -10
- package/dist/RwsClient.js.map +1 -1
- package/dist/RwsClient2.d.ts +438 -0
- package/dist/RwsClient2.d.ts.map +1 -0
- package/dist/RwsClient2.js +1672 -0
- package/dist/RwsClient2.js.map +1 -0
- package/dist/WsSubscriber.d.ts.map +1 -1
- package/dist/WsSubscriber.js +18 -11
- package/dist/WsSubscriber.js.map +1 -1
- package/dist/XhtmlParser.d.ts +24 -0
- package/dist/XhtmlParser.d.ts.map +1 -0
- package/dist/XhtmlParser.js +56 -0
- package/dist/XhtmlParser.js.map +1 -0
- package/dist/detect.d.ts +54 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +187 -0
- package/dist/detect.js.map +1 -0
- package/dist/index.d.ts +30 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +190 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/examples/01-quickstart-auto.mjs +20 -0
- package/examples/02-rws1-explicit.mjs +30 -0
- package/examples/03-rws2-explicit.mjs +26 -0
- package/examples/04-multi-robot.mjs +35 -0
- package/examples/05-remote-control-rmmp.mjs +52 -0
- package/examples/06-pull-module-source.mjs +38 -0
- package/package.json +31 -5
package/README.md
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
# abb-rws-client
|
|
2
2
|
|
|
3
|
-
A typed TypeScript/Node.js
|
|
3
|
+
A typed TypeScript/Node.js client for **ABB Robot Web Services** — both protocols ABB ships:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
- **RWS 1.0** — IRC5 / RobotWare 6.x → `RwsClient`
|
|
6
|
+
- **RWS 2.0** — OmniCore / RobotWare 7.x → `RwsClient2`
|
|
7
|
+
|
|
8
|
+
> **Compatibility:** dual-protocol since v0.7.0. Single-line auto-detection via `createClient()` if you don't know which one your controller speaks.
|
|
7
9
|
|
|
8
10
|
---
|
|
9
11
|
|
|
10
12
|
## VS Code Extension
|
|
11
13
|
|
|
12
|
-
Prefer a GUI? The companion VS Code extension gives you live status,
|
|
14
|
+
Prefer a GUI? The companion VS Code extension gives you live status, motion data, RAPID control, I/O signals, event log, file management, and CFG database editing directly from the sidebar — no code required. Works against both IRC5 and OmniCore.
|
|
13
15
|
|
|
14
|
-
**[ABB Robot (RWS) — VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=merajsafari.abb-rws
|
|
16
|
+
**[ABB Robot (RWS) — VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=merajsafari.abb-rws)**
|
|
15
17
|
|
|
16
18
|
---
|
|
17
19
|
|
|
18
20
|
## Features
|
|
19
21
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
22
|
+
- **Dual-protocol** — RWS 1.0 (Digest, JSON) and RWS 2.0 (Basic, XHTML;v=2.0)
|
|
23
|
+
- **Auto-detection** — `createClient(host)` probes the auth challenge and returns the right client
|
|
24
|
+
- **Multi-robot** — `MultiRobotManager` for orchestrating several controllers in one process
|
|
25
|
+
- **Connection lifecycle** — `RobotManager` handles port discovery, polling, WebSocket subscriptions with polling fallback, reconnect-on-failure
|
|
26
|
+
- **Typed adapter pattern** — `IRWSAdapter` lets you write code that works across both protocols
|
|
27
|
+
- **WebSocket subscriptions** for real-time events (panel state, RAPID exec, signals, persvar, elog, jointtarget, …)
|
|
28
|
+
- Session cookie management (avoids the controller's 70-session pool fill)
|
|
29
|
+
- Automatic `/logout` on disconnect to release server-side mastership and free the session slot
|
|
30
|
+
- Request rate limiting (< 20 req/sec)
|
|
26
31
|
- Fully typed public API — every method throws `RwsError` with a typed `code`
|
|
27
|
-
-
|
|
32
|
+
- Single dependency: `ws` (only one we don't reimplement)
|
|
28
33
|
|
|
29
34
|
---
|
|
30
35
|
|
|
@@ -34,213 +39,419 @@ Prefer a GUI? The companion VS Code extension gives you live status, joint posit
|
|
|
34
39
|
npm install abb-rws-client
|
|
35
40
|
```
|
|
36
41
|
|
|
37
|
-
**Requirements:** Node.js
|
|
42
|
+
**Requirements:** Node.js 18+.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Quick Start (auto-detect)
|
|
47
|
+
|
|
48
|
+
The simplest path: `createClient` probes the controller and returns the right protocol's client.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { createClient, RwsClient2 } from 'abb-rws-client';
|
|
52
|
+
|
|
53
|
+
const client = await createClient({
|
|
54
|
+
host: '192.168.125.1',
|
|
55
|
+
// username/password default to 'Admin' / 'robotics' (built-in admin account, full UAS grants)
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log(`Connected via ${client instanceof RwsClient2 ? 'RWS 2.0' : 'RWS 1.0'}`);
|
|
59
|
+
|
|
60
|
+
const state = await client.getControllerState(); // 'motoron' | 'motoroff' | …
|
|
61
|
+
const mode = await client.getOperationMode(); // 'AUTO' | 'MANR' | 'MANF'
|
|
62
|
+
const joints = await client.getJointPositions(); // { rax_1, …, rax_6 }
|
|
63
|
+
|
|
64
|
+
await client.disconnect();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If you only target one protocol, skip the helper and instantiate the client directly. See [`examples/`](./examples/) for runnable scripts.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Choosing a client
|
|
72
|
+
|
|
73
|
+
| Controller | Protocol | Class | Auth | Default port |
|
|
74
|
+
|---|---|---|---|---|
|
|
75
|
+
| **IRC5** (RobotWare 6.x) | RWS 1.0 | `RwsClient` | HTTP Digest | 80 (real), 80 / 11811 (VC) |
|
|
76
|
+
| **OmniCore** (RobotWare 7.x) | RWS 2.0 | `RwsClient2` | HTTP Basic | 443 (real), 5466 (VC HTTPS) |
|
|
77
|
+
|
|
78
|
+
Both classes expose **the same method names** for ~140 endpoints (controller state, RAPID execution, modules, variables, motion, I/O, file service, CFG database, mastership, event log, etc.). The protocol differences (URL shapes, response format, mastership-domain naming, `$HOME` vs `HOME`) are handled internally — your code looks the same.
|
|
79
|
+
|
|
80
|
+
If you need a single typed reference that holds either:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { createAdapter, type IRWSAdapter } from 'abb-rws-client';
|
|
84
|
+
|
|
85
|
+
const adapter: IRWSAdapter = await createAdapter({ host: '192.168.125.1' });
|
|
86
|
+
// adapter is RWS1Adapter or RWS2Adapter — both implement IRWSAdapter
|
|
87
|
+
```
|
|
38
88
|
|
|
39
89
|
---
|
|
40
90
|
|
|
41
|
-
##
|
|
91
|
+
## RWS 1.0 explicit usage
|
|
42
92
|
|
|
43
93
|
```ts
|
|
44
94
|
import { RwsClient, RwsError } from 'abb-rws-client';
|
|
45
95
|
|
|
46
96
|
const client = new RwsClient({
|
|
47
|
-
host: '
|
|
48
|
-
|
|
49
|
-
username: 'Default User',
|
|
97
|
+
host: '192.168.125.1',
|
|
98
|
+
username: 'Admin',
|
|
50
99
|
password: 'robotics',
|
|
51
100
|
});
|
|
52
101
|
|
|
53
|
-
|
|
54
|
-
await client.connect();
|
|
102
|
+
await client.connect();
|
|
55
103
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
104
|
+
// Controller state
|
|
105
|
+
const state = await client.getControllerState(); // 'motoron' | 'motoroff' | ...
|
|
106
|
+
const mode = await client.getOperationMode(); // 'AUTO' | 'MANR' | 'MANF'
|
|
59
107
|
|
|
60
|
-
|
|
61
|
-
|
|
108
|
+
// Motion
|
|
109
|
+
const joints = await client.getJointPositions(); // rax_1..rax_6 in degrees
|
|
110
|
+
const tcp = await client.getCartesianPosition(); // x/y/z mm + quaternion
|
|
111
|
+
const tcpConfig = await client.getCartesianFull(); // + j1/j4/j6/jx config flags
|
|
62
112
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
113
|
+
// RAPID
|
|
114
|
+
await client.startRapid();
|
|
115
|
+
await client.stopRapid();
|
|
116
|
+
await client.resetRapid(); // PP to Main
|
|
66
117
|
|
|
67
|
-
|
|
68
|
-
|
|
118
|
+
// Variables
|
|
119
|
+
const val = await client.getRapidVariable('T_ROB1', 'user', 'reg1');
|
|
120
|
+
await client.setRapidVariable('T_ROB1', 'user', 'reg1', '42');
|
|
69
121
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// I/O signals
|
|
75
|
-
const di = await client.readSignal('Local', 'DRV_1', 'DI_1');
|
|
76
|
-
console.log('DI_1 =', di.value);
|
|
77
|
-
await client.writeSignal('Local', 'DRV_1', 'DO_1', '1');
|
|
78
|
-
|
|
79
|
-
// Upload and load a RAPID module
|
|
80
|
-
const modSource = `MODULE MyMod\n PROC main()\n TPWrite "Hello";\n ENDPROC\nENDMODULE`;
|
|
81
|
-
await client.uploadModule('$HOME/MyMod.mod', modSource);
|
|
82
|
-
await client.loadModule('T_ROB1', '$HOME/MyMod.mod');
|
|
83
|
-
|
|
84
|
-
// Subscribe to real-time events
|
|
85
|
-
const unsubscribe = await client.subscribe(
|
|
86
|
-
['execution', 'controllerstate'],
|
|
87
|
-
(event) => {
|
|
88
|
-
console.log(`[${event.timestamp.toISOString()}] ${event.resource} = ${event.value}`);
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
// ... later, unsubscribe and disconnect
|
|
93
|
-
await unsubscribe();
|
|
94
|
-
await client.disconnect();
|
|
122
|
+
// I/O signals
|
|
123
|
+
const signals = await client.listAllSignals();
|
|
124
|
+
await client.writeSignal('Local', 'DRV_1', 'DO_1', '1');
|
|
95
125
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
126
|
+
// Real-time subscriptions
|
|
127
|
+
const unsubscribe = await client.subscribe(
|
|
128
|
+
['execution', 'controllerstate', { type: 'signal', name: 'Local/DRV_1/DI_1' }],
|
|
129
|
+
(event) => console.log(event.resource, '=', event.value),
|
|
130
|
+
);
|
|
131
|
+
await unsubscribe();
|
|
132
|
+
|
|
133
|
+
await client.disconnect();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## RWS 2.0 explicit usage
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { RwsClient2 } from 'abb-rws-client';
|
|
142
|
+
|
|
143
|
+
// RWS 2.0 takes a base URL (scheme + host + port).
|
|
144
|
+
// Real OmniCore: https://<host>:443
|
|
145
|
+
// OmniCore VC: https://127.0.0.1:5466
|
|
146
|
+
const client = new RwsClient2(
|
|
147
|
+
'https://127.0.0.1:5466',
|
|
148
|
+
'Admin',
|
|
149
|
+
'robotics',
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
await client.connect();
|
|
153
|
+
|
|
154
|
+
// Same method names as RWS 1.0 — only the underlying protocol differs.
|
|
155
|
+
console.log('state:', await client.getControllerState());
|
|
156
|
+
console.log('joints:', await client.getJointPositions());
|
|
157
|
+
|
|
158
|
+
// RAPID variable read — RWS 2.0 symbol API uses suffix-style URLs internally
|
|
159
|
+
// (`/rw/rapid/symbol/{symburl}/data`); the method shape is the same.
|
|
160
|
+
const tool0 = await client.getRapidVariable('T_ROB1', 'BASE', 'tool0');
|
|
161
|
+
|
|
162
|
+
// WebSocket subscriptions over `robapi2_subscription` subprotocol.
|
|
163
|
+
const unsubscribe = await client.subscribe(
|
|
164
|
+
['controllerstate', 'execution'],
|
|
165
|
+
(event) => console.log(event.resource, '=', event.value),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
await unsubscribe();
|
|
169
|
+
await client.disconnect();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Notable RWS 2.0 quirks (handled automatically)
|
|
173
|
+
|
|
174
|
+
These are documented because they bite anyone who tries to write an RWS 2.0 client from scratch:
|
|
175
|
+
|
|
176
|
+
- **HTTP Basic auth**, not Digest (RWS 1.0).
|
|
177
|
+
- **XHTML responses only** — `Accept: application/json` returns 406. Library uses `Accept: application/xhtml+xml;v=2.0`.
|
|
178
|
+
- **Path-based actions** — `/rw/rapid/execution/stop`, not `?action=stop`.
|
|
179
|
+
- **Mastership domains collapsed** — both `'rapid'` and `'cfg'` map to `'edit'`. The adapter maps internally so either name works.
|
|
180
|
+
- **File service home** is `'HOME'`, not `'$HOME'`.
|
|
181
|
+
- **Symbol API path is suffix-style** — `/rw/rapid/symbol/{symburl}/data` (RWS 1.0 puts `/data` at the front).
|
|
182
|
+
- **Module unload** is `POST /rw/rapid/tasks/{task}/unloadmod` with body, NOT `DELETE` on the module URL (returns 405).
|
|
183
|
+
- **Self-signed TLS** on virtual controllers — library uses `rejectUnauthorized: false` for HTTPS.
|
|
184
|
+
- **WebSocket subscription URL** comes from the `Location` header (real hardware) or the XHTML body (VC). Subprotocol: `robapi2_subscription`.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Multi-robot orchestration
|
|
189
|
+
|
|
190
|
+
For applications that talk to several controllers, use `MultiRobotManager`:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { MultiRobotManager } from 'abb-rws-client';
|
|
194
|
+
|
|
195
|
+
const multi = MultiRobotManager.fromConfigs([
|
|
196
|
+
{ id: 'cell-A', name: 'Cell A IRB120', host: '192.168.125.1', port: 80, useHttps: false, username: 'Admin', password: 'robotics' },
|
|
197
|
+
{ id: 'cell-B', name: 'Cell B IRB1200', host: '192.168.125.2', port: 443, useHttps: true, username: 'Admin', password: 'robotics' },
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
multi.onError((msg, actions) => {
|
|
201
|
+
console.error(`Robot error: ${msg}`);
|
|
202
|
+
return Promise.resolve(undefined); // headless: just log
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
multi.onDidChange(() => {
|
|
206
|
+
console.log(`active=${multi.activeId} state=${multi.state.ctrlstate}`);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
for (const { id } of multi.entries) {
|
|
210
|
+
await multi.connectRobot(id);
|
|
102
211
|
}
|
|
212
|
+
// One robot is "active" at a time (handy for UIs); state for all is polled.
|
|
213
|
+
multi.setActive('cell-B');
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`MultiRobotManager` wraps individual `RobotManager` instances. Each `RobotManager` handles its own:
|
|
217
|
+
- Auto port discovery (probes 5466/9403/443/80/11811, plus a wide-scan fallback)
|
|
218
|
+
- Protocol auto-detection (Digest vs Basic challenge)
|
|
219
|
+
- 5-second polling for non-subscription state
|
|
220
|
+
- WebSocket subscriptions with automatic polling fallback
|
|
221
|
+
- Reconnect-on-failure (3-strike)
|
|
222
|
+
- Clean `/logout` on disconnect (avoids orphan mastership locks)
|
|
223
|
+
|
|
224
|
+
You can also create a `RobotManager` directly if you only have one robot.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Logging
|
|
229
|
+
|
|
230
|
+
The lib ships a no-op logger by default. Hosts (CLIs, services, the VS Code extension) install their own:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { setLogger } from 'abb-rws-client';
|
|
234
|
+
|
|
235
|
+
setLogger({
|
|
236
|
+
info: (msg) => console.log(`[info] ${msg}`),
|
|
237
|
+
warn: (msg) => console.warn(`[warn] ${msg}`),
|
|
238
|
+
error: (msg, err) => console.error(`[error] ${msg}`, err),
|
|
239
|
+
show: () => { /* bring log surface to front; no-op for CLIs */ },
|
|
240
|
+
});
|
|
103
241
|
```
|
|
104
242
|
|
|
243
|
+
Internal lifecycle events (connect/disconnect, polling cycles, subscription state, error recovery) flow through this.
|
|
244
|
+
|
|
105
245
|
---
|
|
106
246
|
|
|
107
247
|
## API Reference
|
|
108
248
|
|
|
109
|
-
###
|
|
249
|
+
### Constructor
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
new RwsClient(options: RwsClientOptions)
|
|
253
|
+
```
|
|
110
254
|
|
|
111
255
|
| Option | Type | Default | Description |
|
|
112
|
-
|
|
256
|
+
|--------|------|---------|-------------|
|
|
113
257
|
| `host` | `string` | — | Controller IP or hostname |
|
|
114
258
|
| `port` | `number` | `80` | HTTP port |
|
|
115
|
-
| `username` | `string` | `'
|
|
259
|
+
| `username` | `string` | `'Admin'` | RWS username |
|
|
116
260
|
| `password` | `string` | `'robotics'` | RWS password |
|
|
117
|
-
| `requestIntervalMs` | `number` | `55` |
|
|
261
|
+
| `requestIntervalMs` | `number` | `55` | Min ms between requests (enforces < 20 req/sec) |
|
|
118
262
|
| `timeout` | `number` | `5000` | Request timeout in ms |
|
|
119
|
-
| `sessionCookie` | `string` | — | Saved cookie
|
|
263
|
+
| `sessionCookie` | `string` | — | Saved cookie to reuse an existing session slot |
|
|
120
264
|
|
|
121
265
|
---
|
|
122
266
|
|
|
123
267
|
### Connection
|
|
124
268
|
|
|
125
269
|
| Method | Returns | Description |
|
|
126
|
-
|
|
127
|
-
| `connect()` | `
|
|
128
|
-
| `disconnect()` | `
|
|
129
|
-
| `getSessionCookie()` | `string \| null` | Current session cookie —
|
|
270
|
+
|--------|---------|-------------|
|
|
271
|
+
| `connect()` | `void` | Establish session and authenticate |
|
|
272
|
+
| `disconnect()` | `void` | Close WebSocket subscriptions and clear session |
|
|
273
|
+
| `getSessionCookie()` | `string \| null` | Current session cookie — persist to avoid 70-session limit |
|
|
130
274
|
|
|
131
275
|
---
|
|
132
276
|
|
|
133
|
-
### Controller State
|
|
277
|
+
### Controller State & Panel
|
|
134
278
|
|
|
135
279
|
| Method | Returns | Description |
|
|
136
|
-
|
|
137
|
-
| `getControllerState()` | `
|
|
138
|
-
| `
|
|
280
|
+
|--------|---------|-------------|
|
|
281
|
+
| `getControllerState()` | `ControllerState` | Motor state: motoron / motoroff / guardstop / emergencystop / … |
|
|
282
|
+
| `setControllerState(state)` | `void` | Set motor state — requires mastership |
|
|
283
|
+
| `getOperationMode()` | `OperationMode` | AUTO / MANR / MANF |
|
|
284
|
+
| `lockOperationMode(pin, permanent?)` | `void` | Lock FlexPendant key switch with PIN |
|
|
285
|
+
| `unlockOperationMode()` | `void` | Unlock FlexPendant key switch |
|
|
286
|
+
| `getSpeedRatio()` | `number` | Speed override 0–100 |
|
|
287
|
+
| `setSpeedRatio(ratio)` | `void` | Set speed override — AUTO mode only |
|
|
288
|
+
| `getCollisionDetectionState()` | `CollisionDetectionState` | INIT / TRIGGERED / CONFIRMED / TRIGGERED_ACK |
|
|
289
|
+
| `restartController(mode?)` | `void` | restart / istart / pstart / bstart |
|
|
290
|
+
| `getControllerIdentity()` | `ControllerIdentity` | Name, ID, type, MAC address |
|
|
291
|
+
| `getSystemInfo()` | `SystemInfo` | RobotWare version, options, system ID |
|
|
292
|
+
| `getControllerClock()` | `ControllerClock` | Current date/time (UTC) |
|
|
293
|
+
| `setControllerClock(y,mo,d,h,mi,s)` | `void` | Set controller date/time (UTC) |
|
|
139
294
|
|
|
140
295
|
---
|
|
141
296
|
|
|
142
297
|
### RAPID Execution
|
|
143
298
|
|
|
144
299
|
| Method | Returns | Description |
|
|
145
|
-
|
|
146
|
-
| `getRapidExecutionState()` | `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
300
|
+
|--------|---------|-------------|
|
|
301
|
+
| `getRapidExecutionState()` | `ExecutionState` | `'running'` \| `'stopped'` |
|
|
302
|
+
| `getRapidExecutionInfo()` | `ExecutionInfo` | State + current cycle mode |
|
|
303
|
+
| `getRapidTasks()` | `RapidTask[]` | All tasks with name, type, state, active flag |
|
|
304
|
+
| `startRapid()` | `void` | Start execution (AUTO + motors on required) |
|
|
305
|
+
| `stopRapid()` | `void` | Stop execution |
|
|
306
|
+
| `resetRapid()` | `void` | Reset program pointer to Main |
|
|
307
|
+
| `setExecutionCycle(cycle)` | `void` | `'once'` \| `'forever'` \| `'asis'` |
|
|
308
|
+
| `activateRapidTask(task)` | `void` | Activate a task (multitasking) |
|
|
309
|
+
| `deactivateRapidTask(task)` | `void` | Deactivate a task |
|
|
310
|
+
| `activateAllRapidTasks()` | `void` | Activate all tasks |
|
|
311
|
+
| `deactivateAllRapidTasks()` | `void` | Deactivate all tasks |
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
### RAPID Variables & Symbols
|
|
316
|
+
|
|
317
|
+
| Method | Returns | Description |
|
|
318
|
+
|--------|---------|-------------|
|
|
319
|
+
| `getRapidVariable(task, module, symbol)` | `string` | Read variable as RAPID-syntax string |
|
|
320
|
+
| `setRapidVariable(task, module, symbol, value)` | `void` | Write variable (RAPID syntax: `'42'`, `'"hello"'`, `'[1,0,0,0]'`) |
|
|
321
|
+
| `validateRapidValue(task, value, datatype)` | `boolean` | Validate value against datatype before writing |
|
|
322
|
+
| `getRapidSymbolProperties(task, module, symbol)` | `RapidSymbolProperties` | Type, dims, storage class, flags |
|
|
323
|
+
| `searchRapidSymbols(params)` | `RapidSymbolInfo[]` | Search by task, type, datatype, or regex |
|
|
324
|
+
| `getActiveUiInstruction()` | `UiInstruction \| null` | Detect if RAPID is waiting for operator input |
|
|
325
|
+
| `setUiInstructionParam(stackurl, param, value)` | `void` | Respond to a UI instruction (TPReadNum, TPReadFK…) |
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
### RAPID Modules
|
|
330
|
+
|
|
331
|
+
| Method | Returns | Description |
|
|
332
|
+
|--------|---------|-------------|
|
|
333
|
+
| `listModules(taskName)` | `string[]` | Names of all loaded modules in a task |
|
|
334
|
+
| `loadModule(task, modulePath, replace?)` | `void` | Load module from controller filesystem into task |
|
|
335
|
+
| `unloadModule(task, moduleName)` | `void` | Unload module from task (RAPID must be stopped) |
|
|
151
336
|
|
|
152
337
|
---
|
|
153
338
|
|
|
154
339
|
### Motion
|
|
155
340
|
|
|
156
341
|
| Method | Returns | Description |
|
|
157
|
-
|
|
158
|
-
| `getJointPositions(mechunit?)` | `
|
|
159
|
-
| `getCartesianPosition(mechunit?, tool?, wobj?)` | `
|
|
342
|
+
|--------|---------|-------------|
|
|
343
|
+
| `getJointPositions(mechunit?)` | `JointTarget` | rax_1–rax_6 in degrees (default mechunit: ROB_1) |
|
|
344
|
+
| `getCartesianPosition(mechunit?, tool?, wobj?)` | `RobTarget` | TCP x/y/z (mm) + q1–q4 quaternion |
|
|
345
|
+
| `getCartesianFull(mechunit?)` | `CartesianFull` | TCP pose + j1/j4/j6/jx configuration flags |
|
|
160
346
|
|
|
161
347
|
---
|
|
162
348
|
|
|
163
|
-
###
|
|
349
|
+
### File System
|
|
164
350
|
|
|
165
351
|
| Method | Returns | Description |
|
|
166
|
-
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
352
|
+
|--------|---------|-------------|
|
|
353
|
+
| `listDirectory(remotePath)` | `FileEntry[]` | Browse a directory (`$HOME`, `$TEMP`, …) |
|
|
354
|
+
| `readFile(remotePath)` | `string` | Download file as UTF-8 string |
|
|
355
|
+
| `uploadModule(remotePath, content)` | `void` | Upload file content (max 800 MB) |
|
|
356
|
+
| `deleteFile(remotePath)` | `void` | Delete file |
|
|
357
|
+
| `createDirectory(parentPath, dirName)` | `void` | Create directory |
|
|
358
|
+
| `copyFile(sourcePath, destPath)` | `void` | Copy file on controller |
|
|
172
359
|
|
|
173
360
|
---
|
|
174
361
|
|
|
175
362
|
### I/O Signals
|
|
176
363
|
|
|
177
364
|
| Method | Returns | Description |
|
|
178
|
-
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
365
|
+
|--------|---------|-------------|
|
|
366
|
+
| `listAllSignals(start?, limit?)` | `Signal[]` | Paginated flat list of all signals |
|
|
367
|
+
| `readSignal(network, device, name)` | `Signal` | Read a specific signal by address |
|
|
368
|
+
| `writeSignal(network, device, name, value)` | `void` | Write DO/AO/GO — value as string: `'1'`, `'0'`, `'3.14'` |
|
|
369
|
+
| `listNetworks()` | `IoNetwork[]` | All I/O networks |
|
|
370
|
+
| `listDevices(network)` | `IoDevice[]` | Devices on a network |
|
|
371
|
+
|
|
372
|
+
> Pass `''` for `network` and `device` to use the flat signal path (works by signal name alone).
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
### Event Log
|
|
377
|
+
|
|
378
|
+
| Method | Returns | Description |
|
|
379
|
+
|--------|---------|-------------|
|
|
380
|
+
| `getEventLog(domain?, lang?)` | `ElogMessage[]` | Read log messages (domain 0 = main, newest first) |
|
|
381
|
+
| `clearEventLog(domain?)` | `void` | Clear messages in one domain |
|
|
382
|
+
| `clearAllEventLogs()` | `void` | Clear all domains |
|
|
181
383
|
|
|
182
384
|
---
|
|
183
385
|
|
|
184
|
-
###
|
|
386
|
+
### Mastership
|
|
387
|
+
|
|
388
|
+
Required before modifying motor state, speed ratio, or certain RAPID operations.
|
|
389
|
+
|
|
390
|
+
| Method | Returns | Description |
|
|
391
|
+
|--------|---------|-------------|
|
|
392
|
+
| `requestMastership(domain)` | `void` | Take control — `'cfg'` \| `'motion'` \| `'rapid'` |
|
|
393
|
+
| `releaseMastership(domain)` | `void` | Release control — always call in `finally` |
|
|
185
394
|
|
|
186
395
|
```ts
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
396
|
+
await client.requestMastership('rapid');
|
|
397
|
+
try {
|
|
398
|
+
await client.setControllerState('motoron');
|
|
399
|
+
} finally {
|
|
400
|
+
await client.releaseMastership('rapid');
|
|
401
|
+
}
|
|
191
402
|
```
|
|
192
403
|
|
|
193
|
-
|
|
404
|
+
---
|
|
194
405
|
|
|
195
|
-
|
|
196
|
-
- `'execution'` — RAPID execution state changes
|
|
197
|
-
- `'controllerstate'` — controller state changes
|
|
198
|
-
- `'operationmode'` — operation mode changes
|
|
199
|
-
- `{ type: 'signal'; name: 'network/device/signalname' }` — I/O signal changes
|
|
200
|
-
- `{ type: 'persvar'; name: 'task/varname' }` — RAPID persistent variable changes
|
|
406
|
+
### WebSocket Subscriptions
|
|
201
407
|
|
|
202
|
-
**`SubscriptionEvent`**:
|
|
203
408
|
```ts
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
timestamp: Date; // When the event was received
|
|
208
|
-
}
|
|
409
|
+
const unsubscribe = await client.subscribe(resources, handler);
|
|
410
|
+
// ...
|
|
411
|
+
await unsubscribe();
|
|
209
412
|
```
|
|
210
413
|
|
|
414
|
+
| Resource | Type | Description |
|
|
415
|
+
|----------|------|-------------|
|
|
416
|
+
| `'execution'` | string | RAPID execution state changes |
|
|
417
|
+
| `'controllerstate'` | string | Motor state changes |
|
|
418
|
+
| `'operationmode'` | string | Operation mode changes |
|
|
419
|
+
| `'speedratio'` | string | Speed ratio changes |
|
|
420
|
+
| `'coldetstate'` | string | Collision detection state |
|
|
421
|
+
| `'uiinstr'` | string | Active UI instruction changes |
|
|
422
|
+
| `{ type: 'execycle' }` | object | Execution cycle mode changes |
|
|
423
|
+
| `{ type: 'taskchange', task }` | object | Task state changes |
|
|
424
|
+
| `{ type: 'signal', name }` | object | I/O signal value changes |
|
|
425
|
+
| `{ type: 'persvar', name }` | object | RAPID persistent variable changes |
|
|
426
|
+
| `{ type: 'elog', domain }` | object | New event log entries |
|
|
427
|
+
|
|
428
|
+
`SubscriptionEvent`: `{ resource: string, value: string, timestamp: Date }`
|
|
429
|
+
|
|
211
430
|
---
|
|
212
431
|
|
|
213
432
|
### Error Handling
|
|
214
433
|
|
|
215
|
-
All public methods throw `RwsError`
|
|
434
|
+
All public methods throw `RwsError` — never a plain `Error`.
|
|
216
435
|
|
|
217
436
|
| Code | Meaning |
|
|
218
|
-
|
|
437
|
+
|------|---------|
|
|
219
438
|
| `'AUTH_FAILED'` | Wrong credentials or session rejected |
|
|
220
|
-
| `'SESSION_EXPIRED'` | Session timed out
|
|
221
|
-
| `'MOTORS_OFF'` | Action requires motors
|
|
222
|
-
| `'MODULE_NOT_FOUND'` | Module file not found on controller
|
|
223
|
-
| `'CONTROLLER_BUSY'` | Controller returned 503
|
|
439
|
+
| `'SESSION_EXPIRED'` | Session timed out |
|
|
440
|
+
| `'MOTORS_OFF'` | Action requires motors on |
|
|
441
|
+
| `'MODULE_NOT_FOUND'` | Module file not found on controller |
|
|
442
|
+
| `'CONTROLLER_BUSY'` | Controller returned 503 — retry later |
|
|
224
443
|
| `'RATE_LIMITED'` | Too many requests (429) |
|
|
225
|
-
| `'NETWORK_ERROR'` | TCP/timeout/WebSocket error |
|
|
444
|
+
| `'NETWORK_ERROR'` | TCP / timeout / WebSocket error |
|
|
226
445
|
| `'PARSE_ERROR'` | Unexpected XML response format |
|
|
227
|
-
| `'UNKNOWN'` | Unmapped error
|
|
446
|
+
| `'UNKNOWN'` | Unmapped error — check `httpStatus` and `rwsDetail` |
|
|
228
447
|
|
|
229
448
|
```ts
|
|
230
449
|
try {
|
|
231
450
|
await client.startRapid();
|
|
232
451
|
} catch (e) {
|
|
233
452
|
if (e instanceof RwsError) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
console.error('Enable motors on the FlexPendant first');
|
|
237
|
-
break;
|
|
238
|
-
case 'AUTH_FAILED':
|
|
239
|
-
console.error('Check username/password');
|
|
240
|
-
break;
|
|
241
|
-
default:
|
|
242
|
-
console.error(`Unexpected error [${e.code}]: ${e.message}`);
|
|
243
|
-
}
|
|
453
|
+
if (e.code === 'MOTORS_OFF') console.error('Enable motors first');
|
|
454
|
+
else throw e;
|
|
244
455
|
}
|
|
245
456
|
}
|
|
246
457
|
```
|
|
@@ -249,43 +460,53 @@ try {
|
|
|
249
460
|
|
|
250
461
|
## Session Persistence
|
|
251
462
|
|
|
252
|
-
The IRC5 controller allows a maximum of **70 concurrent RWS sessions**.
|
|
463
|
+
The IRC5 controller allows a maximum of **70 concurrent RWS sessions**. Persist the session cookie across restarts to always reuse the same slot:
|
|
253
464
|
|
|
254
465
|
```ts
|
|
255
466
|
import fs from 'fs';
|
|
256
467
|
|
|
257
|
-
//
|
|
468
|
+
// Save after connecting
|
|
258
469
|
const cookie = client.getSessionCookie();
|
|
259
470
|
if (cookie) fs.writeFileSync('.rws-session', cookie, 'utf8');
|
|
260
471
|
|
|
261
|
-
//
|
|
472
|
+
// Restore on next start
|
|
262
473
|
let sessionCookie: string | undefined;
|
|
263
474
|
try { sessionCookie = fs.readFileSync('.rws-session', 'utf8'); } catch {}
|
|
264
475
|
|
|
265
476
|
const client = new RwsClient({ host, username, password, sessionCookie });
|
|
266
|
-
await client.connect(); // reuses the existing
|
|
477
|
+
await client.connect(); // reuses the existing session slot
|
|
267
478
|
```
|
|
268
479
|
|
|
269
480
|
---
|
|
270
481
|
|
|
271
|
-
##
|
|
482
|
+
## Rate Limits
|
|
483
|
+
|
|
484
|
+
| Constraint | Value |
|
|
485
|
+
|------------|-------|
|
|
486
|
+
| Max request rate | 20 req/sec |
|
|
487
|
+
| Client enforced interval | 55 ms between requests |
|
|
488
|
+
| Max concurrent sessions | 70 |
|
|
489
|
+
| Session inactivity timeout | 5 minutes |
|
|
490
|
+
| Max session lifetime | 25 minutes |
|
|
491
|
+
| Max file upload | 800 MB |
|
|
272
492
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
| Controller: IRC5 | ✓ | n/a |
|
|
277
|
-
| Controller: OmniCore | n/a | ✗ |
|
|
278
|
-
| RobotStudio (virtual) | ✓ (127.0.0.1) | ✗ |
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Compatibility
|
|
279
496
|
|
|
280
|
-
|
|
497
|
+
| | RobotWare 6.x (RWS 1.0) | RobotWare 7.x (RWS 2.0) |
|
|
498
|
+
|--|--|--|
|
|
499
|
+
| This package | ✅ Supported | ❌ Not compatible |
|
|
500
|
+
| IRC5 controller | ✅ | n/a |
|
|
501
|
+
| OmniCore controller | n/a | ❌ |
|
|
502
|
+
| RobotStudio virtual | ✅ (127.0.0.1) | ❌ |
|
|
281
503
|
|
|
282
504
|
---
|
|
283
505
|
|
|
284
506
|
## Resources
|
|
285
507
|
|
|
286
|
-
- [ABB RWS
|
|
287
|
-
- [
|
|
288
|
-
- [VALIDATION.md](./VALIDATION.md) — Manual test procedures against a real IRC5 / RobotStudio controller
|
|
508
|
+
- [ABB Developer Center — RWS API](https://developercenter.robotstudio.com/api/rwsApi/)
|
|
509
|
+
- [Companion VS Code Extension](https://marketplace.visualstudio.com/items?itemName=merajsafari.abb-rws)
|
|
289
510
|
|
|
290
511
|
---
|
|
291
512
|
|