abb-rws-client 0.1.9 → 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 +222 -127
- 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,20 +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
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## VS Code Extension
|
|
11
|
+
|
|
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
|
+
|
|
14
|
+
**[ABB Robot (RWS) — VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=merajsafari.abb-rws)**
|
|
7
15
|
|
|
8
16
|
---
|
|
9
17
|
|
|
10
18
|
## Features
|
|
11
19
|
|
|
12
20
|
- HTTP Digest Authentication (RFC 2617) — no external auth libraries
|
|
13
|
-
- Session cookie management (`ABBCX`, `-http-session-`)
|
|
21
|
+
- Session cookie management (`ABBCX`, `-http-session-`) with automatic reuse
|
|
14
22
|
- Request rate limiting (< 20 req/sec, configurable)
|
|
15
|
-
- Automatic
|
|
16
|
-
- WebSocket subscriptions for real-time events
|
|
23
|
+
- Automatic re-authentication after session expiry
|
|
24
|
+
- WebSocket subscriptions for real-time events
|
|
17
25
|
- Auto-reconnect on WebSocket disconnect (3 retries, exponential backoff)
|
|
18
26
|
- Fully typed public API — every method throws `RwsError` with a typed `code`
|
|
19
|
-
- Zero runtime dependencies — Node
|
|
27
|
+
- Zero runtime dependencies — Node.js built-ins only
|
|
20
28
|
|
|
21
29
|
---
|
|
22
30
|
|
|
@@ -26,233 +34,320 @@ A typed TypeScript/Node.js HTTP and WebSocket client for **ABB IRC5 robot contro
|
|
|
26
34
|
npm install abb-rws-client
|
|
27
35
|
```
|
|
28
36
|
|
|
29
|
-
**Requirements:** Node.js 21+
|
|
37
|
+
**Requirements:** Node.js 18+ (WebSocket subscriptions require Node 21+ or Node 18 with `--experimental-websocket`).
|
|
30
38
|
|
|
31
39
|
---
|
|
32
40
|
|
|
33
|
-
## Quick Start
|
|
41
|
+
## Quick Start
|
|
34
42
|
|
|
35
43
|
```ts
|
|
36
44
|
import { RwsClient, RwsError } from 'abb-rws-client';
|
|
37
45
|
|
|
38
46
|
const client = new RwsClient({
|
|
39
|
-
host: '
|
|
40
|
-
port: 80,
|
|
47
|
+
host: '192.168.125.1',
|
|
41
48
|
username: 'Default User',
|
|
42
49
|
password: 'robotics',
|
|
43
50
|
});
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
await client.connect();
|
|
52
|
+
await client.connect();
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
// Controller state
|
|
55
|
+
const state = await client.getControllerState(); // 'motoron' | 'motoroff' | ...
|
|
56
|
+
const mode = await client.getOperationMode(); // 'AUTO' | 'MANR' | 'MANF'
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
// RAPID
|
|
64
|
+
await client.startRapid();
|
|
65
|
+
await client.stopRapid();
|
|
66
|
+
await client.resetRapid(); // PP to Main
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
// Variables
|
|
69
|
+
const val = await client.getRapidVariable('T_ROB1', 'user', 'reg1');
|
|
70
|
+
await client.setRapidVariable('T_ROB1', 'user', 'reg1', '42');
|
|
61
71
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// I/O signals
|
|
67
|
-
const di = await client.readSignal('Local', 'DRV_1', 'DI_1');
|
|
68
|
-
console.log('DI_1 =', di.value);
|
|
69
|
-
await client.writeSignal('Local', 'DRV_1', 'DO_1', '1');
|
|
70
|
-
|
|
71
|
-
// Upload and load a RAPID module
|
|
72
|
-
const modSource = `MODULE MyMod\n PROC main()\n TPWrite "Hello";\n ENDPROC\nENDMODULE`;
|
|
73
|
-
await client.uploadModule('$HOME/MyMod.mod', modSource);
|
|
74
|
-
await client.loadModule('T_ROB1', '$HOME/MyMod.mod');
|
|
75
|
-
|
|
76
|
-
// Subscribe to real-time events
|
|
77
|
-
const unsubscribe = await client.subscribe(
|
|
78
|
-
['execution', 'controllerstate'],
|
|
79
|
-
(event) => {
|
|
80
|
-
console.log(`[${event.timestamp.toISOString()}] ${event.resource} = ${event.value}`);
|
|
81
|
-
}
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
// ... later, unsubscribe and disconnect
|
|
85
|
-
await unsubscribe();
|
|
86
|
-
await client.disconnect();
|
|
72
|
+
// I/O signals
|
|
73
|
+
const signals = await client.listAllSignals();
|
|
74
|
+
await client.writeSignal('Local', 'DRV_1', 'DO_1', '1');
|
|
87
75
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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();
|
|
95
84
|
```
|
|
96
85
|
|
|
97
86
|
---
|
|
98
87
|
|
|
99
88
|
## API Reference
|
|
100
89
|
|
|
101
|
-
###
|
|
90
|
+
### Constructor
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
new RwsClient(options: RwsClientOptions)
|
|
94
|
+
```
|
|
102
95
|
|
|
103
96
|
| Option | Type | Default | Description |
|
|
104
|
-
|
|
97
|
+
|--------|------|---------|-------------|
|
|
105
98
|
| `host` | `string` | — | Controller IP or hostname |
|
|
106
99
|
| `port` | `number` | `80` | HTTP port |
|
|
107
100
|
| `username` | `string` | `'Default User'` | RWS username |
|
|
108
101
|
| `password` | `string` | `'robotics'` | RWS password |
|
|
109
|
-
| `requestIntervalMs` | `number` | `55` |
|
|
102
|
+
| `requestIntervalMs` | `number` | `55` | Min ms between requests (enforces < 20 req/sec) |
|
|
110
103
|
| `timeout` | `number` | `5000` | Request timeout in ms |
|
|
104
|
+
| `sessionCookie` | `string` | — | Saved cookie to reuse an existing session slot |
|
|
111
105
|
|
|
112
106
|
---
|
|
113
107
|
|
|
114
108
|
### Connection
|
|
115
109
|
|
|
116
110
|
| Method | Returns | Description |
|
|
117
|
-
|
|
118
|
-
| `connect()` | `
|
|
119
|
-
| `disconnect()` | `
|
|
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 |
|
|
120
115
|
|
|
121
116
|
---
|
|
122
117
|
|
|
123
|
-
### Controller State
|
|
118
|
+
### Controller State & Panel
|
|
124
119
|
|
|
125
120
|
| Method | Returns | Description |
|
|
126
|
-
|
|
127
|
-
| `getControllerState()` | `
|
|
128
|
-
| `
|
|
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) |
|
|
129
135
|
|
|
130
136
|
---
|
|
131
137
|
|
|
132
138
|
### RAPID Execution
|
|
133
139
|
|
|
134
140
|
| Method | Returns | Description |
|
|
135
|
-
|
|
136
|
-
| `getRapidExecutionState()` | `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
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) |
|
|
141
177
|
|
|
142
178
|
---
|
|
143
179
|
|
|
144
180
|
### Motion
|
|
145
181
|
|
|
146
182
|
| Method | Returns | Description |
|
|
147
|
-
|
|
148
|
-
| `getJointPositions(mechunit?)` | `
|
|
149
|
-
| `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 |
|
|
150
187
|
|
|
151
188
|
---
|
|
152
189
|
|
|
153
|
-
###
|
|
190
|
+
### File System
|
|
154
191
|
|
|
155
192
|
| Method | Returns | Description |
|
|
156
|
-
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
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 |
|
|
160
200
|
|
|
161
201
|
---
|
|
162
202
|
|
|
163
203
|
### I/O Signals
|
|
164
204
|
|
|
165
205
|
| Method | Returns | Description |
|
|
166
|
-
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
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 |
|
|
169
224
|
|
|
170
225
|
---
|
|
171
226
|
|
|
172
|
-
###
|
|
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` |
|
|
173
235
|
|
|
174
236
|
```ts
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
237
|
+
await client.requestMastership('rapid');
|
|
238
|
+
try {
|
|
239
|
+
await client.setControllerState('motoron');
|
|
240
|
+
} finally {
|
|
241
|
+
await client.releaseMastership('rapid');
|
|
242
|
+
}
|
|
179
243
|
```
|
|
180
244
|
|
|
181
|
-
|
|
245
|
+
---
|
|
182
246
|
|
|
183
|
-
|
|
184
|
-
- `'execution'` — RAPID execution state changes
|
|
185
|
-
- `'controllerstate'` — controller state changes
|
|
186
|
-
- `'operationmode'` — operation mode changes
|
|
187
|
-
- `{ type: 'signal'; name: 'network/device/signalname' }` — I/O signal changes
|
|
188
|
-
- `{ type: 'persvar'; name: 'task/varname' }` — RAPID persistent variable changes
|
|
247
|
+
### WebSocket Subscriptions
|
|
189
248
|
|
|
190
|
-
**`SubscriptionEvent`**:
|
|
191
249
|
```ts
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
timestamp: Date; // When the event was received
|
|
196
|
-
}
|
|
250
|
+
const unsubscribe = await client.subscribe(resources, handler);
|
|
251
|
+
// ...
|
|
252
|
+
await unsubscribe();
|
|
197
253
|
```
|
|
198
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
|
+
|
|
199
271
|
---
|
|
200
272
|
|
|
201
273
|
### Error Handling
|
|
202
274
|
|
|
203
|
-
All public methods throw `RwsError`
|
|
275
|
+
All public methods throw `RwsError` — never a plain `Error`.
|
|
204
276
|
|
|
205
277
|
| Code | Meaning |
|
|
206
|
-
|
|
278
|
+
|------|---------|
|
|
207
279
|
| `'AUTH_FAILED'` | Wrong credentials or session rejected |
|
|
208
|
-
| `'SESSION_EXPIRED'` | Session timed out
|
|
209
|
-
| `'MOTORS_OFF'` | Action requires motors
|
|
210
|
-
| `'MODULE_NOT_FOUND'` | Module file not found on controller
|
|
211
|
-
| `'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 |
|
|
212
284
|
| `'RATE_LIMITED'` | Too many requests (429) |
|
|
213
|
-
| `'NETWORK_ERROR'` | TCP/timeout/WebSocket error |
|
|
285
|
+
| `'NETWORK_ERROR'` | TCP / timeout / WebSocket error |
|
|
214
286
|
| `'PARSE_ERROR'` | Unexpected XML response format |
|
|
215
|
-
| `'UNKNOWN'` | Unmapped error
|
|
287
|
+
| `'UNKNOWN'` | Unmapped error — check `httpStatus` and `rwsDetail` |
|
|
216
288
|
|
|
217
289
|
```ts
|
|
218
290
|
try {
|
|
219
291
|
await client.startRapid();
|
|
220
292
|
} catch (e) {
|
|
221
293
|
if (e instanceof RwsError) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
console.error('Enable motors on the FlexPendant first');
|
|
225
|
-
break;
|
|
226
|
-
case 'AUTH_FAILED':
|
|
227
|
-
console.error('Check username/password');
|
|
228
|
-
break;
|
|
229
|
-
default:
|
|
230
|
-
console.error(`Unexpected error [${e.code}]: ${e.message}`);
|
|
231
|
-
}
|
|
294
|
+
if (e.code === 'MOTORS_OFF') console.error('Enable motors first');
|
|
295
|
+
else throw e;
|
|
232
296
|
}
|
|
233
297
|
}
|
|
234
298
|
```
|
|
235
299
|
|
|
236
300
|
---
|
|
237
301
|
|
|
238
|
-
##
|
|
302
|
+
## Session Persistence
|
|
303
|
+
|
|
304
|
+
The IRC5 controller allows a maximum of **70 concurrent RWS sessions**. Persist the session cookie across restarts to always reuse the same slot:
|
|
239
305
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
306
|
+
```ts
|
|
307
|
+
import fs from 'fs';
|
|
308
|
+
|
|
309
|
+
// Save after connecting
|
|
310
|
+
const cookie = client.getSessionCookie();
|
|
311
|
+
if (cookie) fs.writeFileSync('.rws-session', cookie, 'utf8');
|
|
312
|
+
|
|
313
|
+
// Restore on next start
|
|
314
|
+
let sessionCookie: string | undefined;
|
|
315
|
+
try { sessionCookie = fs.readFileSync('.rws-session', 'utf8'); } catch {}
|
|
316
|
+
|
|
317
|
+
const client = new RwsClient({ host, username, password, sessionCookie });
|
|
318
|
+
await client.connect(); // reuses the existing session slot
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Rate Limits
|
|
324
|
+
|
|
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
|
|
246
337
|
|
|
247
|
-
|
|
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) | ❌ |
|
|
248
344
|
|
|
249
345
|
---
|
|
250
346
|
|
|
251
347
|
## Resources
|
|
252
348
|
|
|
253
|
-
- [ABB RWS
|
|
254
|
-
- [
|
|
255
|
-
- [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)
|
|
256
351
|
|
|
257
352
|
---
|
|
258
353
|
|