abb-rws-client 0.2.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -137
- package/dist/ResourceMapper.d.ts +219 -2
- package/dist/ResourceMapper.d.ts.map +1 -1
- package/dist/ResourceMapper.js +278 -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/RwsClient.d.ts +247 -1
- package/dist/RwsClient.d.ts.map +1 -1
- package/dist/RwsClient.js +626 -2
- package/dist/RwsClient.js.map +1 -1
- package/dist/WsSubscriber.d.ts.map +1 -1
- package/dist/WsSubscriber.js +18 -11
- package/dist/WsSubscriber.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +188 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,28 +3,28 @@
|
|
|
3
3
|
A typed TypeScript/Node.js HTTP and WebSocket client for **ABB IRC5 robot controllers** using [Robot Web Services (RWS) 1.0](https://developercenter.robotstudio.com/api/rwsApi/).
|
|
4
4
|
|
|
5
5
|
> **Compatibility:** RWS 1.0 / RobotWare 6.x only.
|
|
6
|
-
>
|
|
6
|
+
> Not compatible with OmniCore, RobotWare 7.x, or RWS 2.0.
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## VS Code Extension
|
|
11
11
|
|
|
12
|
-
Prefer a GUI? The companion VS Code extension gives you live status,
|
|
12
|
+
Prefer a GUI? The companion VS Code extension gives you live status, motion data, RAPID control, I/O signals, event log, and file management directly from the sidebar — no code required.
|
|
13
13
|
|
|
14
|
-
**[ABB Robot (RWS) — VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=merajsafari.abb-rws
|
|
14
|
+
**[ABB Robot (RWS) — VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=merajsafari.abb-rws)**
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
20
|
- HTTP Digest Authentication (RFC 2617) — no external auth libraries
|
|
21
|
-
- Session cookie management (`ABBCX`, `-http-session-`)
|
|
21
|
+
- Session cookie management (`ABBCX`, `-http-session-`) with automatic reuse
|
|
22
22
|
- Request rate limiting (< 20 req/sec, configurable)
|
|
23
|
-
- Automatic
|
|
24
|
-
- WebSocket subscriptions for real-time events
|
|
23
|
+
- Automatic re-authentication after session expiry
|
|
24
|
+
- WebSocket subscriptions for real-time events
|
|
25
25
|
- Auto-reconnect on WebSocket disconnect (3 retries, exponential backoff)
|
|
26
26
|
- Fully typed public API — every method throws `RwsError` with a typed `code`
|
|
27
|
-
- Zero runtime dependencies — Node
|
|
27
|
+
- Zero runtime dependencies — Node.js built-ins only
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
@@ -34,213 +34,265 @@ Prefer a GUI? The companion VS Code extension gives you live status, joint posit
|
|
|
34
34
|
npm install abb-rws-client
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
**Requirements:** Node.js 21+
|
|
37
|
+
**Requirements:** Node.js 18+ (WebSocket subscriptions require Node 21+ or Node 18 with `--experimental-websocket`).
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
41
|
-
## Quick Start
|
|
41
|
+
## Quick Start
|
|
42
42
|
|
|
43
43
|
```ts
|
|
44
44
|
import { RwsClient, RwsError } from 'abb-rws-client';
|
|
45
45
|
|
|
46
46
|
const client = new RwsClient({
|
|
47
|
-
host: '
|
|
48
|
-
port: 80,
|
|
47
|
+
host: '192.168.125.1',
|
|
49
48
|
username: 'Default User',
|
|
50
49
|
password: 'robotics',
|
|
51
50
|
});
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
await client.connect();
|
|
52
|
+
await client.connect();
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
// Controller state
|
|
55
|
+
const state = await client.getControllerState(); // 'motoron' | 'motoroff' | ...
|
|
56
|
+
const mode = await client.getOperationMode(); // 'AUTO' | 'MANR' | 'MANF'
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
// Motion
|
|
59
|
+
const joints = await client.getJointPositions(); // rax_1..rax_6 in degrees
|
|
60
|
+
const tcp = await client.getCartesianPosition(); // x/y/z mm + quaternion
|
|
61
|
+
const tcpConfig = await client.getCartesianFull(); // + j1/j4/j6/jx config flags
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
// RAPID
|
|
64
|
+
await client.startRapid();
|
|
65
|
+
await client.stopRapid();
|
|
66
|
+
await client.resetRapid(); // PP to Main
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
// Variables
|
|
69
|
+
const val = await client.getRapidVariable('T_ROB1', 'user', 'reg1');
|
|
70
|
+
await client.setRapidVariable('T_ROB1', 'user', 'reg1', '42');
|
|
69
71
|
|
|
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();
|
|
72
|
+
// I/O signals
|
|
73
|
+
const signals = await client.listAllSignals();
|
|
74
|
+
await client.writeSignal('Local', 'DRV_1', 'DO_1', '1');
|
|
95
75
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
// Real-time subscriptions
|
|
77
|
+
const unsubscribe = await client.subscribe(
|
|
78
|
+
['execution', 'controllerstate', { type: 'signal', name: 'Local/DRV_1/DI_1' }],
|
|
79
|
+
(event) => console.log(event.resource, '=', event.value),
|
|
80
|
+
);
|
|
81
|
+
await unsubscribe();
|
|
82
|
+
|
|
83
|
+
await client.disconnect();
|
|
103
84
|
```
|
|
104
85
|
|
|
105
86
|
---
|
|
106
87
|
|
|
107
88
|
## API Reference
|
|
108
89
|
|
|
109
|
-
###
|
|
90
|
+
### Constructor
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
new RwsClient(options: RwsClientOptions)
|
|
94
|
+
```
|
|
110
95
|
|
|
111
96
|
| Option | Type | Default | Description |
|
|
112
|
-
|
|
97
|
+
|--------|------|---------|-------------|
|
|
113
98
|
| `host` | `string` | — | Controller IP or hostname |
|
|
114
99
|
| `port` | `number` | `80` | HTTP port |
|
|
115
100
|
| `username` | `string` | `'Default User'` | RWS username |
|
|
116
101
|
| `password` | `string` | `'robotics'` | RWS password |
|
|
117
|
-
| `requestIntervalMs` | `number` | `55` |
|
|
102
|
+
| `requestIntervalMs` | `number` | `55` | Min ms between requests (enforces < 20 req/sec) |
|
|
118
103
|
| `timeout` | `number` | `5000` | Request timeout in ms |
|
|
119
|
-
| `sessionCookie` | `string` | — | Saved cookie
|
|
104
|
+
| `sessionCookie` | `string` | — | Saved cookie to reuse an existing session slot |
|
|
120
105
|
|
|
121
106
|
---
|
|
122
107
|
|
|
123
108
|
### Connection
|
|
124
109
|
|
|
125
110
|
| Method | Returns | Description |
|
|
126
|
-
|
|
127
|
-
| `connect()` | `
|
|
128
|
-
| `disconnect()` | `
|
|
129
|
-
| `getSessionCookie()` | `string \| null` | Current session cookie —
|
|
111
|
+
|--------|---------|-------------|
|
|
112
|
+
| `connect()` | `void` | Establish session and authenticate |
|
|
113
|
+
| `disconnect()` | `void` | Close WebSocket subscriptions and clear session |
|
|
114
|
+
| `getSessionCookie()` | `string \| null` | Current session cookie — persist to avoid 70-session limit |
|
|
130
115
|
|
|
131
116
|
---
|
|
132
117
|
|
|
133
|
-
### Controller State
|
|
118
|
+
### Controller State & Panel
|
|
134
119
|
|
|
135
120
|
| Method | Returns | Description |
|
|
136
|
-
|
|
137
|
-
| `getControllerState()` | `
|
|
138
|
-
| `
|
|
121
|
+
|--------|---------|-------------|
|
|
122
|
+
| `getControllerState()` | `ControllerState` | Motor state: motoron / motoroff / guardstop / emergencystop / … |
|
|
123
|
+
| `setControllerState(state)` | `void` | Set motor state — requires mastership |
|
|
124
|
+
| `getOperationMode()` | `OperationMode` | AUTO / MANR / MANF |
|
|
125
|
+
| `lockOperationMode(pin, permanent?)` | `void` | Lock FlexPendant key switch with PIN |
|
|
126
|
+
| `unlockOperationMode()` | `void` | Unlock FlexPendant key switch |
|
|
127
|
+
| `getSpeedRatio()` | `number` | Speed override 0–100 |
|
|
128
|
+
| `setSpeedRatio(ratio)` | `void` | Set speed override — AUTO mode only |
|
|
129
|
+
| `getCollisionDetectionState()` | `CollisionDetectionState` | INIT / TRIGGERED / CONFIRMED / TRIGGERED_ACK |
|
|
130
|
+
| `restartController(mode?)` | `void` | restart / istart / pstart / bstart |
|
|
131
|
+
| `getControllerIdentity()` | `ControllerIdentity` | Name, ID, type, MAC address |
|
|
132
|
+
| `getSystemInfo()` | `SystemInfo` | RobotWare version, options, system ID |
|
|
133
|
+
| `getControllerClock()` | `ControllerClock` | Current date/time (UTC) |
|
|
134
|
+
| `setControllerClock(y,mo,d,h,mi,s)` | `void` | Set controller date/time (UTC) |
|
|
139
135
|
|
|
140
136
|
---
|
|
141
137
|
|
|
142
138
|
### RAPID Execution
|
|
143
139
|
|
|
144
140
|
| Method | Returns | Description |
|
|
145
|
-
|
|
146
|
-
| `getRapidExecutionState()` | `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
141
|
+
|--------|---------|-------------|
|
|
142
|
+
| `getRapidExecutionState()` | `ExecutionState` | `'running'` \| `'stopped'` |
|
|
143
|
+
| `getRapidExecutionInfo()` | `ExecutionInfo` | State + current cycle mode |
|
|
144
|
+
| `getRapidTasks()` | `RapidTask[]` | All tasks with name, type, state, active flag |
|
|
145
|
+
| `startRapid()` | `void` | Start execution (AUTO + motors on required) |
|
|
146
|
+
| `stopRapid()` | `void` | Stop execution |
|
|
147
|
+
| `resetRapid()` | `void` | Reset program pointer to Main |
|
|
148
|
+
| `setExecutionCycle(cycle)` | `void` | `'once'` \| `'forever'` \| `'asis'` |
|
|
149
|
+
| `activateRapidTask(task)` | `void` | Activate a task (multitasking) |
|
|
150
|
+
| `deactivateRapidTask(task)` | `void` | Deactivate a task |
|
|
151
|
+
| `activateAllRapidTasks()` | `void` | Activate all tasks |
|
|
152
|
+
| `deactivateAllRapidTasks()` | `void` | Deactivate all tasks |
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
### RAPID Variables & Symbols
|
|
157
|
+
|
|
158
|
+
| Method | Returns | Description |
|
|
159
|
+
|--------|---------|-------------|
|
|
160
|
+
| `getRapidVariable(task, module, symbol)` | `string` | Read variable as RAPID-syntax string |
|
|
161
|
+
| `setRapidVariable(task, module, symbol, value)` | `void` | Write variable (RAPID syntax: `'42'`, `'"hello"'`, `'[1,0,0,0]'`) |
|
|
162
|
+
| `validateRapidValue(task, value, datatype)` | `boolean` | Validate value against datatype before writing |
|
|
163
|
+
| `getRapidSymbolProperties(task, module, symbol)` | `RapidSymbolProperties` | Type, dims, storage class, flags |
|
|
164
|
+
| `searchRapidSymbols(params)` | `RapidSymbolInfo[]` | Search by task, type, datatype, or regex |
|
|
165
|
+
| `getActiveUiInstruction()` | `UiInstruction \| null` | Detect if RAPID is waiting for operator input |
|
|
166
|
+
| `setUiInstructionParam(stackurl, param, value)` | `void` | Respond to a UI instruction (TPReadNum, TPReadFK…) |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### RAPID Modules
|
|
171
|
+
|
|
172
|
+
| Method | Returns | Description |
|
|
173
|
+
|--------|---------|-------------|
|
|
174
|
+
| `listModules(taskName)` | `string[]` | Names of all loaded modules in a task |
|
|
175
|
+
| `loadModule(task, modulePath, replace?)` | `void` | Load module from controller filesystem into task |
|
|
176
|
+
| `unloadModule(task, moduleName)` | `void` | Unload module from task (RAPID must be stopped) |
|
|
151
177
|
|
|
152
178
|
---
|
|
153
179
|
|
|
154
180
|
### Motion
|
|
155
181
|
|
|
156
182
|
| Method | Returns | Description |
|
|
157
|
-
|
|
158
|
-
| `getJointPositions(mechunit?)` | `
|
|
159
|
-
| `getCartesianPosition(mechunit?, tool?, wobj?)` | `
|
|
183
|
+
|--------|---------|-------------|
|
|
184
|
+
| `getJointPositions(mechunit?)` | `JointTarget` | rax_1–rax_6 in degrees (default mechunit: ROB_1) |
|
|
185
|
+
| `getCartesianPosition(mechunit?, tool?, wobj?)` | `RobTarget` | TCP x/y/z (mm) + q1–q4 quaternion |
|
|
186
|
+
| `getCartesianFull(mechunit?)` | `CartesianFull` | TCP pose + j1/j4/j6/jx configuration flags |
|
|
160
187
|
|
|
161
188
|
---
|
|
162
189
|
|
|
163
|
-
###
|
|
190
|
+
### File System
|
|
164
191
|
|
|
165
192
|
| Method | Returns | Description |
|
|
166
|
-
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
193
|
+
|--------|---------|-------------|
|
|
194
|
+
| `listDirectory(remotePath)` | `FileEntry[]` | Browse a directory (`$HOME`, `$TEMP`, …) |
|
|
195
|
+
| `readFile(remotePath)` | `string` | Download file as UTF-8 string |
|
|
196
|
+
| `uploadModule(remotePath, content)` | `void` | Upload file content (max 800 MB) |
|
|
197
|
+
| `deleteFile(remotePath)` | `void` | Delete file |
|
|
198
|
+
| `createDirectory(parentPath, dirName)` | `void` | Create directory |
|
|
199
|
+
| `copyFile(sourcePath, destPath)` | `void` | Copy file on controller |
|
|
172
200
|
|
|
173
201
|
---
|
|
174
202
|
|
|
175
203
|
### I/O Signals
|
|
176
204
|
|
|
177
205
|
| Method | Returns | Description |
|
|
178
|
-
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
206
|
+
|--------|---------|-------------|
|
|
207
|
+
| `listAllSignals(start?, limit?)` | `Signal[]` | Paginated flat list of all signals |
|
|
208
|
+
| `readSignal(network, device, name)` | `Signal` | Read a specific signal by address |
|
|
209
|
+
| `writeSignal(network, device, name, value)` | `void` | Write DO/AO/GO — value as string: `'1'`, `'0'`, `'3.14'` |
|
|
210
|
+
| `listNetworks()` | `IoNetwork[]` | All I/O networks |
|
|
211
|
+
| `listDevices(network)` | `IoDevice[]` | Devices on a network |
|
|
212
|
+
|
|
213
|
+
> Pass `''` for `network` and `device` to use the flat signal path (works by signal name alone).
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### Event Log
|
|
218
|
+
|
|
219
|
+
| Method | Returns | Description |
|
|
220
|
+
|--------|---------|-------------|
|
|
221
|
+
| `getEventLog(domain?, lang?)` | `ElogMessage[]` | Read log messages (domain 0 = main, newest first) |
|
|
222
|
+
| `clearEventLog(domain?)` | `void` | Clear messages in one domain |
|
|
223
|
+
| `clearAllEventLogs()` | `void` | Clear all domains |
|
|
181
224
|
|
|
182
225
|
---
|
|
183
226
|
|
|
184
|
-
###
|
|
227
|
+
### Mastership
|
|
228
|
+
|
|
229
|
+
Required before modifying motor state, speed ratio, or certain RAPID operations.
|
|
230
|
+
|
|
231
|
+
| Method | Returns | Description |
|
|
232
|
+
|--------|---------|-------------|
|
|
233
|
+
| `requestMastership(domain)` | `void` | Take control — `'cfg'` \| `'motion'` \| `'rapid'` |
|
|
234
|
+
| `releaseMastership(domain)` | `void` | Release control — always call in `finally` |
|
|
185
235
|
|
|
186
236
|
```ts
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
237
|
+
await client.requestMastership('rapid');
|
|
238
|
+
try {
|
|
239
|
+
await client.setControllerState('motoron');
|
|
240
|
+
} finally {
|
|
241
|
+
await client.releaseMastership('rapid');
|
|
242
|
+
}
|
|
191
243
|
```
|
|
192
244
|
|
|
193
|
-
|
|
245
|
+
---
|
|
194
246
|
|
|
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
|
|
247
|
+
### WebSocket Subscriptions
|
|
201
248
|
|
|
202
|
-
**`SubscriptionEvent`**:
|
|
203
249
|
```ts
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
timestamp: Date; // When the event was received
|
|
208
|
-
}
|
|
250
|
+
const unsubscribe = await client.subscribe(resources, handler);
|
|
251
|
+
// ...
|
|
252
|
+
await unsubscribe();
|
|
209
253
|
```
|
|
210
254
|
|
|
255
|
+
| Resource | Type | Description |
|
|
256
|
+
|----------|------|-------------|
|
|
257
|
+
| `'execution'` | string | RAPID execution state changes |
|
|
258
|
+
| `'controllerstate'` | string | Motor state changes |
|
|
259
|
+
| `'operationmode'` | string | Operation mode changes |
|
|
260
|
+
| `'speedratio'` | string | Speed ratio changes |
|
|
261
|
+
| `'coldetstate'` | string | Collision detection state |
|
|
262
|
+
| `'uiinstr'` | string | Active UI instruction changes |
|
|
263
|
+
| `{ type: 'execycle' }` | object | Execution cycle mode changes |
|
|
264
|
+
| `{ type: 'taskchange', task }` | object | Task state changes |
|
|
265
|
+
| `{ type: 'signal', name }` | object | I/O signal value changes |
|
|
266
|
+
| `{ type: 'persvar', name }` | object | RAPID persistent variable changes |
|
|
267
|
+
| `{ type: 'elog', domain }` | object | New event log entries |
|
|
268
|
+
|
|
269
|
+
`SubscriptionEvent`: `{ resource: string, value: string, timestamp: Date }`
|
|
270
|
+
|
|
211
271
|
---
|
|
212
272
|
|
|
213
273
|
### Error Handling
|
|
214
274
|
|
|
215
|
-
All public methods throw `RwsError`
|
|
275
|
+
All public methods throw `RwsError` — never a plain `Error`.
|
|
216
276
|
|
|
217
277
|
| Code | Meaning |
|
|
218
|
-
|
|
278
|
+
|------|---------|
|
|
219
279
|
| `'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
|
|
280
|
+
| `'SESSION_EXPIRED'` | Session timed out |
|
|
281
|
+
| `'MOTORS_OFF'` | Action requires motors on |
|
|
282
|
+
| `'MODULE_NOT_FOUND'` | Module file not found on controller |
|
|
283
|
+
| `'CONTROLLER_BUSY'` | Controller returned 503 — retry later |
|
|
224
284
|
| `'RATE_LIMITED'` | Too many requests (429) |
|
|
225
|
-
| `'NETWORK_ERROR'` | TCP/timeout/WebSocket error |
|
|
285
|
+
| `'NETWORK_ERROR'` | TCP / timeout / WebSocket error |
|
|
226
286
|
| `'PARSE_ERROR'` | Unexpected XML response format |
|
|
227
|
-
| `'UNKNOWN'` | Unmapped error
|
|
287
|
+
| `'UNKNOWN'` | Unmapped error — check `httpStatus` and `rwsDetail` |
|
|
228
288
|
|
|
229
289
|
```ts
|
|
230
290
|
try {
|
|
231
291
|
await client.startRapid();
|
|
232
292
|
} catch (e) {
|
|
233
293
|
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
|
-
}
|
|
294
|
+
if (e.code === 'MOTORS_OFF') console.error('Enable motors first');
|
|
295
|
+
else throw e;
|
|
244
296
|
}
|
|
245
297
|
}
|
|
246
298
|
```
|
|
@@ -249,43 +301,53 @@ try {
|
|
|
249
301
|
|
|
250
302
|
## Session Persistence
|
|
251
303
|
|
|
252
|
-
The IRC5 controller allows a maximum of **70 concurrent RWS sessions**.
|
|
304
|
+
The IRC5 controller allows a maximum of **70 concurrent RWS sessions**. Persist the session cookie across restarts to always reuse the same slot:
|
|
253
305
|
|
|
254
306
|
```ts
|
|
255
307
|
import fs from 'fs';
|
|
256
308
|
|
|
257
|
-
//
|
|
309
|
+
// Save after connecting
|
|
258
310
|
const cookie = client.getSessionCookie();
|
|
259
311
|
if (cookie) fs.writeFileSync('.rws-session', cookie, 'utf8');
|
|
260
312
|
|
|
261
|
-
//
|
|
313
|
+
// Restore on next start
|
|
262
314
|
let sessionCookie: string | undefined;
|
|
263
315
|
try { sessionCookie = fs.readFileSync('.rws-session', 'utf8'); } catch {}
|
|
264
316
|
|
|
265
317
|
const client = new RwsClient({ host, username, password, sessionCookie });
|
|
266
|
-
await client.connect(); // reuses the existing
|
|
318
|
+
await client.connect(); // reuses the existing session slot
|
|
267
319
|
```
|
|
268
320
|
|
|
269
321
|
---
|
|
270
322
|
|
|
271
|
-
##
|
|
323
|
+
## Rate Limits
|
|
272
324
|
|
|
273
|
-
|
|
|
274
|
-
|
|
275
|
-
|
|
|
276
|
-
|
|
|
277
|
-
|
|
|
278
|
-
|
|
|
325
|
+
| Constraint | Value |
|
|
326
|
+
|------------|-------|
|
|
327
|
+
| Max request rate | 20 req/sec |
|
|
328
|
+
| Client enforced interval | 55 ms between requests |
|
|
329
|
+
| Max concurrent sessions | 70 |
|
|
330
|
+
| Session inactivity timeout | 5 minutes |
|
|
331
|
+
| Max session lifetime | 25 minutes |
|
|
332
|
+
| Max file upload | 800 MB |
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Compatibility
|
|
279
337
|
|
|
280
|
-
|
|
338
|
+
| | RobotWare 6.x (RWS 1.0) | RobotWare 7.x (RWS 2.0) |
|
|
339
|
+
|--|--|--|
|
|
340
|
+
| This package | ✅ Supported | ❌ Not compatible |
|
|
341
|
+
| IRC5 controller | ✅ | n/a |
|
|
342
|
+
| OmniCore controller | n/a | ❌ |
|
|
343
|
+
| RobotStudio virtual | ✅ (127.0.0.1) | ❌ |
|
|
281
344
|
|
|
282
345
|
---
|
|
283
346
|
|
|
284
347
|
## Resources
|
|
285
348
|
|
|
286
|
-
- [ABB RWS
|
|
287
|
-
- [
|
|
288
|
-
- [VALIDATION.md](./VALIDATION.md) — Manual test procedures against a real IRC5 / RobotStudio controller
|
|
349
|
+
- [ABB Developer Center — RWS API](https://developercenter.robotstudio.com/api/rwsApi/)
|
|
350
|
+
- [Companion VS Code Extension](https://marketplace.visualstudio.com/items?itemName=merajsafari.abb-rws)
|
|
289
351
|
|
|
290
352
|
---
|
|
291
353
|
|