abb-rws-client 0.1.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/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/HttpSession.d.ts +92 -0
- package/dist/HttpSession.d.ts.map +1 -0
- package/dist/HttpSession.js +321 -0
- package/dist/HttpSession.js.map +1 -0
- package/dist/ResourceMapper.d.ts +95 -0
- package/dist/ResourceMapper.d.ts.map +1 -0
- package/dist/ResourceMapper.js +146 -0
- package/dist/ResourceMapper.js.map +1 -0
- package/dist/ResponseParser.d.ts +73 -0
- package/dist/ResponseParser.d.ts.map +1 -0
- package/dist/ResponseParser.js +294 -0
- package/dist/ResponseParser.js.map +1 -0
- package/dist/RwsClient.d.ts +156 -0
- package/dist/RwsClient.d.ts.map +1 -0
- package/dist/RwsClient.js +353 -0
- package/dist/RwsClient.js.map +1 -0
- package/dist/WsSubscriber.d.ts +33 -0
- package/dist/WsSubscriber.d.ts.map +1 -0
- package/dist/WsSubscriber.js +234 -0
- package/dist/WsSubscriber.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RwsClient — the single public class for the abb-rws-client package.
|
|
3
|
+
*
|
|
4
|
+
* Assembles HttpSession, ResourceMapper, ResponseParser, and WsSubscriber into
|
|
5
|
+
* a convenient typed API for ABB IRC5 robot controllers using RWS 1.0.
|
|
6
|
+
*
|
|
7
|
+
* Compatible with RobotWare 6.x only. NOT compatible with RWS 2.0 / RobotWare 7.x / OmniCore.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const client = new RwsClient({ host: '127.0.0.1' });
|
|
12
|
+
* await client.connect();
|
|
13
|
+
* const state = await client.getControllerState();
|
|
14
|
+
* await client.disconnect();
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { HttpSession } from './HttpSession.js';
|
|
18
|
+
import { WsSubscriber } from './WsSubscriber.js';
|
|
19
|
+
import { RwsError } from './types.js';
|
|
20
|
+
import { controllerState as pathControllerState, operationMode as pathOperationMode, rapidTasks as pathRapidTasks, rapidExecutionState as pathRapidExecutionState, startRapid as mapStartRapid, stopRapid as mapStopRapid, resetRapid as mapResetRapid, jointTarget as pathJointTarget, robTarget as pathRobTarget, loadModule as mapLoadModule, listModules as pathListModules, uploadFile as pathUploadFile, signal as pathSignal, setSignal as mapSetSignal, } from './ResourceMapper.js';
|
|
21
|
+
import { parseControllerState, parseOperationMode, parseExecutionState, parseJointTarget, parseRobTarget, parseSignal, parseRapidTasks, } from './ResponseParser.js';
|
|
22
|
+
export class RwsClient {
|
|
23
|
+
session;
|
|
24
|
+
subscriber;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
const sessionOptions = {
|
|
27
|
+
baseUrl: `http://${options.host}:${options.port ?? 80}`,
|
|
28
|
+
username: options.username ?? 'Default User',
|
|
29
|
+
password: options.password ?? 'robotics',
|
|
30
|
+
requestIntervalMs: options.requestIntervalMs ?? 55,
|
|
31
|
+
timeoutMs: options.timeout ?? 5000,
|
|
32
|
+
};
|
|
33
|
+
this.session = new HttpSession(sessionOptions);
|
|
34
|
+
this.subscriber = new WsSubscriber(this.session, options.host, options.port ?? 80);
|
|
35
|
+
}
|
|
36
|
+
// ─── Connection ─────────────────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Establish a session with the controller.
|
|
39
|
+
* Triggers digest authentication and verifies connectivity by reading controller state.
|
|
40
|
+
* Must be called before any other method.
|
|
41
|
+
*
|
|
42
|
+
* @throws {RwsError} code='NETWORK_ERROR' if the controller is unreachable
|
|
43
|
+
* @throws {RwsError} code='AUTH_FAILED' if credentials are incorrect
|
|
44
|
+
*/
|
|
45
|
+
async connect() {
|
|
46
|
+
try {
|
|
47
|
+
const { body } = await this.session.get(pathControllerState());
|
|
48
|
+
parseControllerState(body); // validate response is parseable
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
if (e instanceof RwsError)
|
|
52
|
+
throw e;
|
|
53
|
+
throw new RwsError(`Failed to connect: ${String(e)}`, 'NETWORK_ERROR');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Disconnect from the controller.
|
|
58
|
+
* Closes all WebSocket subscriptions and clears the session.
|
|
59
|
+
*/
|
|
60
|
+
async disconnect() {
|
|
61
|
+
try {
|
|
62
|
+
await this.subscriber.closeAll();
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
this.session.clearSession();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ─── Controller state ───────────────────────────────────────────────────────
|
|
69
|
+
/**
|
|
70
|
+
* Read the current controller state.
|
|
71
|
+
*
|
|
72
|
+
* @returns 'motoron' | 'motoroff' | 'init' | 'guardstop' | 'emergencystop' |
|
|
73
|
+
* 'emergencystopreset' | 'sysfail'
|
|
74
|
+
* @throws {RwsError} code='PARSE_ERROR' on unexpected response format
|
|
75
|
+
*/
|
|
76
|
+
async getControllerState() {
|
|
77
|
+
try {
|
|
78
|
+
const { body } = await this.session.get(pathControllerState());
|
|
79
|
+
return parseControllerState(body);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
if (e instanceof RwsError)
|
|
83
|
+
throw e;
|
|
84
|
+
throw new RwsError(`getControllerState failed: ${String(e)}`, 'UNKNOWN');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Read the current operation mode.
|
|
89
|
+
*
|
|
90
|
+
* @returns 'AUTO' | 'MANR' | 'MANF'
|
|
91
|
+
* @throws {RwsError} code='PARSE_ERROR' on unexpected response format
|
|
92
|
+
*/
|
|
93
|
+
async getOperationMode() {
|
|
94
|
+
try {
|
|
95
|
+
const { body } = await this.session.get(pathOperationMode());
|
|
96
|
+
return parseOperationMode(body);
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
if (e instanceof RwsError)
|
|
100
|
+
throw e;
|
|
101
|
+
throw new RwsError(`getOperationMode failed: ${String(e)}`, 'UNKNOWN');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// ─── RAPID execution ────────────────────────────────────────────────────────
|
|
105
|
+
/**
|
|
106
|
+
* Read the current RAPID execution state.
|
|
107
|
+
*
|
|
108
|
+
* @returns 'running' | 'stopped'
|
|
109
|
+
*/
|
|
110
|
+
async getRapidExecutionState() {
|
|
111
|
+
try {
|
|
112
|
+
const { body } = await this.session.get(pathRapidExecutionState());
|
|
113
|
+
return parseExecutionState(body);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
if (e instanceof RwsError)
|
|
117
|
+
throw e;
|
|
118
|
+
throw new RwsError(`getRapidExecutionState failed: ${String(e)}`, 'UNKNOWN');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Retrieve all RAPID tasks and their current states.
|
|
123
|
+
*
|
|
124
|
+
* @returns Array of RapidTask objects
|
|
125
|
+
*/
|
|
126
|
+
async getRapidTasks() {
|
|
127
|
+
try {
|
|
128
|
+
const { body } = await this.session.get(pathRapidTasks());
|
|
129
|
+
return parseRapidTasks(body);
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
if (e instanceof RwsError)
|
|
133
|
+
throw e;
|
|
134
|
+
throw new RwsError(`getRapidTasks failed: ${String(e)}`, 'UNKNOWN');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Start RAPID program execution.
|
|
139
|
+
* The controller must be in AUTO mode with motors on.
|
|
140
|
+
*
|
|
141
|
+
* @throws {RwsError} code='MOTORS_OFF' if motors are not on
|
|
142
|
+
* @throws {RwsError} code='CONTROLLER_BUSY' if the controller is already busy
|
|
143
|
+
*/
|
|
144
|
+
async startRapid() {
|
|
145
|
+
try {
|
|
146
|
+
const { path, body } = mapStartRapid();
|
|
147
|
+
await this.session.post(path, body);
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
if (e instanceof RwsError) {
|
|
151
|
+
// Map 400 "motors off" to a more descriptive code
|
|
152
|
+
if (e.httpStatus === 400 && e.rwsDetail?.includes('motor')) {
|
|
153
|
+
throw new RwsError('Motors are off — enable motors before starting RAPID', 'MOTORS_OFF', e.httpStatus, e.rwsDetail);
|
|
154
|
+
}
|
|
155
|
+
throw e;
|
|
156
|
+
}
|
|
157
|
+
throw new RwsError(`startRapid failed: ${String(e)}`, 'UNKNOWN');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Stop RAPID program execution.
|
|
162
|
+
*/
|
|
163
|
+
async stopRapid() {
|
|
164
|
+
try {
|
|
165
|
+
const { path, body } = mapStopRapid();
|
|
166
|
+
await this.session.post(path, body);
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
if (e instanceof RwsError)
|
|
170
|
+
throw e;
|
|
171
|
+
throw new RwsError(`stopRapid failed: ${String(e)}`, 'UNKNOWN');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Reset the RAPID program pointer to main.
|
|
176
|
+
* RAPID must be stopped before calling this.
|
|
177
|
+
*/
|
|
178
|
+
async resetRapid() {
|
|
179
|
+
try {
|
|
180
|
+
const { path, body } = mapResetRapid();
|
|
181
|
+
await this.session.post(path, body);
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
if (e instanceof RwsError)
|
|
185
|
+
throw e;
|
|
186
|
+
throw new RwsError(`resetRapid failed: ${String(e)}`, 'UNKNOWN');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// ─── Motion ─────────────────────────────────────────────────────────────────
|
|
190
|
+
/**
|
|
191
|
+
* Read the current joint-space positions for a mechanical unit.
|
|
192
|
+
*
|
|
193
|
+
* @param mechunit - Mechanical unit name; default 'ROB_1'
|
|
194
|
+
* @returns JointTarget with rax_1 … rax_6 in degrees
|
|
195
|
+
*/
|
|
196
|
+
async getJointPositions(mechunit) {
|
|
197
|
+
try {
|
|
198
|
+
const { body } = await this.session.get(pathJointTarget(mechunit));
|
|
199
|
+
return parseJointTarget(body);
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
if (e instanceof RwsError)
|
|
203
|
+
throw e;
|
|
204
|
+
throw new RwsError(`getJointPositions failed: ${String(e)}`, 'UNKNOWN');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Read the current Cartesian robot target (TCP position and orientation).
|
|
209
|
+
*
|
|
210
|
+
* @param mechunit - Mechanical unit; default 'ROB_1'
|
|
211
|
+
* @param tool - Active tool frame; default 'tool0'
|
|
212
|
+
* @param wobj - Active work object; default 'wobj0'
|
|
213
|
+
* @returns RobTarget with x, y, z (mm) and q1–q4 quaternion components
|
|
214
|
+
*/
|
|
215
|
+
async getCartesianPosition(mechunit, tool, wobj) {
|
|
216
|
+
try {
|
|
217
|
+
const { body } = await this.session.get(pathRobTarget(mechunit, tool, wobj));
|
|
218
|
+
return parseRobTarget(body);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
if (e instanceof RwsError)
|
|
222
|
+
throw e;
|
|
223
|
+
throw new RwsError(`getCartesianPosition failed: ${String(e)}`, 'UNKNOWN');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// ─── Modules ────────────────────────────────────────────────────────────────
|
|
227
|
+
/**
|
|
228
|
+
* Upload a RAPID module file to the controller filesystem.
|
|
229
|
+
* The file content is uploaded as UTF-8 bytes via PUT /fileservice/{remotePath}.
|
|
230
|
+
*
|
|
231
|
+
* @param remotePath - Controller path, e.g. '$HOME/MyMod.mod'
|
|
232
|
+
* @param content - RAPID module source as a string
|
|
233
|
+
*/
|
|
234
|
+
async uploadModule(remotePath, content) {
|
|
235
|
+
try {
|
|
236
|
+
const path = pathUploadFile(remotePath);
|
|
237
|
+
const bytes = new TextEncoder().encode(content);
|
|
238
|
+
await this.session.put(path, bytes);
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
if (e instanceof RwsError)
|
|
242
|
+
throw e;
|
|
243
|
+
throw new RwsError(`uploadModule failed: ${String(e)}`, 'UNKNOWN');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Load a RAPID module from the controller filesystem into a task.
|
|
248
|
+
* The module must have been uploaded first (see uploadModule).
|
|
249
|
+
*
|
|
250
|
+
* @param taskName - RAPID task name, e.g. 'T_ROB1'
|
|
251
|
+
* @param modulePath - Controller path to the module file, e.g. '$HOME/MyMod.mod'
|
|
252
|
+
* @throws {RwsError} code='MODULE_NOT_FOUND' if the module file does not exist
|
|
253
|
+
*/
|
|
254
|
+
async loadModule(taskName, modulePath, replace = false) {
|
|
255
|
+
try {
|
|
256
|
+
const { path, body } = mapLoadModule(taskName, modulePath, replace);
|
|
257
|
+
await this.session.post(path, body);
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
if (e instanceof RwsError)
|
|
261
|
+
throw e;
|
|
262
|
+
throw new RwsError(`loadModule failed: ${String(e)}`, 'UNKNOWN');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* List the names of all modules currently loaded in a RAPID task.
|
|
267
|
+
*
|
|
268
|
+
* @param taskName - RAPID task name, e.g. 'T_ROB1'
|
|
269
|
+
* @returns Array of module names
|
|
270
|
+
*/
|
|
271
|
+
async listModules(taskName) {
|
|
272
|
+
try {
|
|
273
|
+
const { body } = await this.session.get(pathListModules(taskName));
|
|
274
|
+
// Extract module names from <span class="name"> inside <li class="rap-module-info-li">
|
|
275
|
+
const matches = [
|
|
276
|
+
...body.matchAll(/<li[^>]*class="[^"]*\brap-module-info-li\b[^"]*"[^>]*>.*?<span[^>]*class="[^"]*\bname\b[^"]*"[^>]*>(.*?)<\/span>/gis),
|
|
277
|
+
];
|
|
278
|
+
return matches.map(([, name]) => name.trim()).filter(Boolean);
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
if (e instanceof RwsError)
|
|
282
|
+
throw e;
|
|
283
|
+
throw new RwsError(`listModules failed: ${String(e)}`, 'UNKNOWN');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// ─── I/O signals ────────────────────────────────────────────────────────────
|
|
287
|
+
/**
|
|
288
|
+
* Read an I/O signal value.
|
|
289
|
+
*
|
|
290
|
+
* @param network - I/O network name, e.g. 'Local'
|
|
291
|
+
* @param device - I/O device name, e.g. 'DRV_1'
|
|
292
|
+
* @param name - Signal name, e.g. 'DI_1'
|
|
293
|
+
* @returns Signal object with name, value, type, and lvalue
|
|
294
|
+
*/
|
|
295
|
+
async readSignal(network, device, name) {
|
|
296
|
+
try {
|
|
297
|
+
const { body } = await this.session.get(pathSignal(network, device, name));
|
|
298
|
+
return parseSignal(body);
|
|
299
|
+
}
|
|
300
|
+
catch (e) {
|
|
301
|
+
if (e instanceof RwsError)
|
|
302
|
+
throw e;
|
|
303
|
+
throw new RwsError(`readSignal failed: ${String(e)}`, 'UNKNOWN');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Write a value to a digital or analog output signal.
|
|
308
|
+
*
|
|
309
|
+
* @param network - I/O network name
|
|
310
|
+
* @param device - I/O device name
|
|
311
|
+
* @param name - Signal name
|
|
312
|
+
* @param value - New signal value, e.g. '1' (DO high), '0' (DO low), '3.14' (AO)
|
|
313
|
+
*/
|
|
314
|
+
async writeSignal(network, device, name, value) {
|
|
315
|
+
try {
|
|
316
|
+
const { path } = mapSetSignal(network, device, name);
|
|
317
|
+
await this.session.post(path, `lvalue=${encodeURIComponent(value)}`);
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
if (e instanceof RwsError)
|
|
321
|
+
throw e;
|
|
322
|
+
throw new RwsError(`writeSignal failed: ${String(e)}`, 'UNKNOWN');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// ─── Subscriptions ──────────────────────────────────────────────────────────
|
|
326
|
+
/**
|
|
327
|
+
* Subscribe to one or more RWS resource events via WebSocket.
|
|
328
|
+
*
|
|
329
|
+
* @param resources - Resources to subscribe to (execution, controllerstate, signal, etc.)
|
|
330
|
+
* @param handler - Called with each SubscriptionEvent as it arrives
|
|
331
|
+
* @returns - Async unsubscribe function; call to cancel and clean up
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts
|
|
335
|
+
* const unsubscribe = await client.subscribe(['execution'], (event) => {
|
|
336
|
+
* console.log(event.resource, event.value);
|
|
337
|
+
* });
|
|
338
|
+
* // later...
|
|
339
|
+
* await unsubscribe();
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
async subscribe(resources, handler) {
|
|
343
|
+
try {
|
|
344
|
+
return await this.subscriber.subscribe(resources, handler);
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
if (e instanceof RwsError)
|
|
348
|
+
throw e;
|
|
349
|
+
throw new RwsError(`subscribe failed: ${String(e)}`, 'UNKNOWN');
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=RwsClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RwsClient.js","sourceRoot":"","sources":["../src/RwsClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WsSubscriber — WebSocket subscription manager for ABB IRC5 RWS events.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. POST /subscription via HttpSession to register resources → get subscription ID
|
|
6
|
+
* 2. Open WebSocket to ws://{host}/subscription/{id} with robapi2_subscription subprotocol
|
|
7
|
+
* 3. Parse incoming XML event messages → emit typed SubscriptionEvent objects
|
|
8
|
+
* 4. Auto-reconnect on unexpected close: max 3 retries, exponential backoff 1s/2s/4s
|
|
9
|
+
*
|
|
10
|
+
* Uses native globalThis.WebSocket when available (Node 22+, browsers).
|
|
11
|
+
* Falls back to the 'ws' npm package for Node 18/21 where native WebSocket is experimental.
|
|
12
|
+
*/
|
|
13
|
+
import type { HttpSession } from './HttpSession.js';
|
|
14
|
+
import type { SubscriptionResource, SubscriptionEvent } from './types.js';
|
|
15
|
+
export declare class WsSubscriber {
|
|
16
|
+
private readonly session;
|
|
17
|
+
private readonly host;
|
|
18
|
+
private readonly port;
|
|
19
|
+
private subscriptions;
|
|
20
|
+
constructor(session: HttpSession, host: string, port: number);
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to one or more RWS resources. Returns an unsubscribe function.
|
|
23
|
+
*
|
|
24
|
+
* @param resources - Array of resources to subscribe to
|
|
25
|
+
* @param handler - Called for each incoming event
|
|
26
|
+
* @returns - Async function that cancels the subscription and closes the WebSocket
|
|
27
|
+
*/
|
|
28
|
+
subscribe(resources: SubscriptionResource[], handler: (event: SubscriptionEvent) => void): Promise<() => Promise<void>>;
|
|
29
|
+
/** Close all active subscriptions */
|
|
30
|
+
closeAll(): Promise<void>;
|
|
31
|
+
private openWebSocket;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=WsSubscriber.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WsSubscriber.d.ts","sourceRoot":"","sources":["../src/WsSubscriber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAsBH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAuG1E,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,aAAa,CAA8C;gBAEvD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAM5D;;;;;;OAMG;IACG,SAAS,CACb,SAAS,EAAE,oBAAoB,EAAE,EACjC,OAAO,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAC1C,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IA6D/B,qCAAqC;IAC/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB/B,OAAO,CAAC,aAAa;CA6CtB"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WsSubscriber — WebSocket subscription manager for ABB IRC5 RWS events.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. POST /subscription via HttpSession to register resources → get subscription ID
|
|
6
|
+
* 2. Open WebSocket to ws://{host}/subscription/{id} with robapi2_subscription subprotocol
|
|
7
|
+
* 3. Parse incoming XML event messages → emit typed SubscriptionEvent objects
|
|
8
|
+
* 4. Auto-reconnect on unexpected close: max 3 retries, exponential backoff 1s/2s/4s
|
|
9
|
+
*
|
|
10
|
+
* Uses native globalThis.WebSocket when available (Node 22+, browsers).
|
|
11
|
+
* Falls back to the 'ws' npm package for Node 18/21 where native WebSocket is experimental.
|
|
12
|
+
*/
|
|
13
|
+
import { RwsError } from './types.js';
|
|
14
|
+
import { createRequire } from 'module';
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a WebSocket constructor: prefer native globalThis.WebSocket,
|
|
17
|
+
* fall back to the 'ws' package if available.
|
|
18
|
+
*/
|
|
19
|
+
function resolveWebSocket() {
|
|
20
|
+
if (globalThis.WebSocket)
|
|
21
|
+
return globalThis.WebSocket;
|
|
22
|
+
try {
|
|
23
|
+
const require = createRequire(import.meta.url);
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
return require('ws');
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new RwsError('WebSocket is not available. Install the "ws" package or use Node 22+.', 'NETWORK_ERROR');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
import { subscriptions } from './ResourceMapper.js';
|
|
32
|
+
import { parseSubscriptionId } from './ResponseParser.js';
|
|
33
|
+
const BACKOFF_MS = [1000, 2000, 4000];
|
|
34
|
+
const MAX_RETRIES = 3;
|
|
35
|
+
// ─── Path builder for subscription resources ─────────────────────────────────
|
|
36
|
+
/**
|
|
37
|
+
* Map a SubscriptionResource to its RWS 1.0 event path (with ;state suffix where needed).
|
|
38
|
+
* Paths are NOT percent-encoded — semicolons must be literal in the subscription body.
|
|
39
|
+
*/
|
|
40
|
+
function resourceToPath(resource) {
|
|
41
|
+
if (resource === 'execution')
|
|
42
|
+
return '/rw/rapid/execution;ctrlexecstate';
|
|
43
|
+
if (resource === 'controllerstate')
|
|
44
|
+
return '/rw/panel/ctrlstate;ctrlstate';
|
|
45
|
+
if (resource === 'operationmode')
|
|
46
|
+
return '/rw/panel/opmode;opmode';
|
|
47
|
+
if (resource.type === 'signal') {
|
|
48
|
+
// Signal path requires network/device/name but SubscriptionResource only gives the
|
|
49
|
+
// signal name. Use a direct path that the user is expected to pass as the full path.
|
|
50
|
+
// Convention: name can be 'network/device/signalname' or just 'signalname' for
|
|
51
|
+
// simple virtual/local signals.
|
|
52
|
+
const parts = resource.name.split('/');
|
|
53
|
+
if (parts.length === 3) {
|
|
54
|
+
return `/rw/iosystem/signals/${resource.name};state`;
|
|
55
|
+
}
|
|
56
|
+
// Fallback: treat as a virtual signal on 'Virtual1/DRV1' — not universally correct,
|
|
57
|
+
// but the best we can do without network/device context in this resource type.
|
|
58
|
+
return `/rw/iosystem/signals/${resource.name};state`;
|
|
59
|
+
}
|
|
60
|
+
if (resource.type === 'persvar') {
|
|
61
|
+
// RAPID persistent variable subscription path
|
|
62
|
+
return `/rw/rapid/symbol/data/${resource.name};value`;
|
|
63
|
+
}
|
|
64
|
+
// TypeScript exhaustiveness check
|
|
65
|
+
const _ = resource;
|
|
66
|
+
void _;
|
|
67
|
+
throw new RwsError('Unknown subscription resource type', 'UNKNOWN');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build the application/x-www-form-urlencoded body for POST /subscription.
|
|
71
|
+
* Paths are NOT percent-encoded; the semicolons are literal as expected by RWS.
|
|
72
|
+
*/
|
|
73
|
+
function buildSubscriptionBody(resources) {
|
|
74
|
+
const parts = [`resources=${resources.length}`];
|
|
75
|
+
resources.forEach((resource, index) => {
|
|
76
|
+
const i = index + 1;
|
|
77
|
+
const path = resourceToPath(resource);
|
|
78
|
+
// Do NOT encodeURIComponent the path — RWS expects literal semicolons
|
|
79
|
+
parts.push(`${i}=${path}&${i}-p=1`);
|
|
80
|
+
});
|
|
81
|
+
return parts.join('&');
|
|
82
|
+
}
|
|
83
|
+
// ─── XML event parsing ────────────────────────────────────────────────────────
|
|
84
|
+
/**
|
|
85
|
+
* Parse an incoming RWS WebSocket XML event message.
|
|
86
|
+
* Expected structure (simplified):
|
|
87
|
+
* <html><body><div class="bind-data"><ul>
|
|
88
|
+
* <li class="..."><a href="/rw/rapid/execution;state">...</a>
|
|
89
|
+
* <span class="excstate">running</span>
|
|
90
|
+
* </li>
|
|
91
|
+
* </ul></div></body></html>
|
|
92
|
+
*/
|
|
93
|
+
function parseWsMessage(data) {
|
|
94
|
+
const events = [];
|
|
95
|
+
// Extract all <li> blocks in the message
|
|
96
|
+
const liPattern = /<li[^>]*>(.*?)<\/li>/gis;
|
|
97
|
+
let liMatch;
|
|
98
|
+
while ((liMatch = liPattern.exec(data)) !== null) {
|
|
99
|
+
const block = liMatch[1];
|
|
100
|
+
// Extract resource URL from the <a href="..."> anchor
|
|
101
|
+
const hrefMatch = block.match(/<a[^>]*href="([^"]+)"/i);
|
|
102
|
+
if (!hrefMatch)
|
|
103
|
+
continue;
|
|
104
|
+
const resource = hrefMatch[1];
|
|
105
|
+
// Extract value from the first <span> in this block
|
|
106
|
+
const spanMatch = block.match(/<span[^>]*>(.*?)<\/span>/is);
|
|
107
|
+
const value = spanMatch ? spanMatch[1].trim() : '';
|
|
108
|
+
events.push({ resource, value, timestamp: new Date() });
|
|
109
|
+
}
|
|
110
|
+
return events;
|
|
111
|
+
}
|
|
112
|
+
export class WsSubscriber {
|
|
113
|
+
session;
|
|
114
|
+
host;
|
|
115
|
+
port;
|
|
116
|
+
subscriptions = new Map();
|
|
117
|
+
constructor(session, host, port) {
|
|
118
|
+
this.session = session;
|
|
119
|
+
this.host = host;
|
|
120
|
+
this.port = port;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Subscribe to one or more RWS resources. Returns an unsubscribe function.
|
|
124
|
+
*
|
|
125
|
+
* @param resources - Array of resources to subscribe to
|
|
126
|
+
* @param handler - Called for each incoming event
|
|
127
|
+
* @returns - Async function that cancels the subscription and closes the WebSocket
|
|
128
|
+
*/
|
|
129
|
+
async subscribe(resources, handler) {
|
|
130
|
+
// Step 1: POST /subscription to register resources
|
|
131
|
+
const body = buildSubscriptionBody(resources);
|
|
132
|
+
const response = await this.session.post(subscriptions(), body);
|
|
133
|
+
if (response.status !== 201) {
|
|
134
|
+
throw new RwsError(`Subscription POST returned ${response.status}, expected 201`, 'UNKNOWN', response.status);
|
|
135
|
+
}
|
|
136
|
+
const locationHeader = response.headers.get('location');
|
|
137
|
+
if (!locationHeader) {
|
|
138
|
+
throw new RwsError('Subscription POST missing Location header', 'UNKNOWN');
|
|
139
|
+
}
|
|
140
|
+
const subscriptionId = parseSubscriptionId(locationHeader);
|
|
141
|
+
// Step 2: Derive WebSocket URL and HTTP delete URL from Location header.
|
|
142
|
+
// IRC5 may return ws://host/poll/{id} or http://host/subscription/{id} or a path.
|
|
143
|
+
let wsUrl;
|
|
144
|
+
let deleteUrl;
|
|
145
|
+
if (locationHeader.startsWith('ws://') || locationHeader.startsWith('wss://')) {
|
|
146
|
+
wsUrl = locationHeader;
|
|
147
|
+
deleteUrl = locationHeader.replace(/^ws:/, 'http:').replace(/^wss:/, 'https:');
|
|
148
|
+
}
|
|
149
|
+
else if (locationHeader.startsWith('http://') || locationHeader.startsWith('https://')) {
|
|
150
|
+
wsUrl = locationHeader.replace(/^http:/, 'ws:').replace(/^https:/, 'wss:');
|
|
151
|
+
deleteUrl = locationHeader;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
wsUrl = `ws://${this.host}:${this.port}${locationHeader}`;
|
|
155
|
+
deleteUrl = `http://${this.host}:${this.port}${locationHeader}`;
|
|
156
|
+
}
|
|
157
|
+
const sub = {
|
|
158
|
+
id: subscriptionId,
|
|
159
|
+
wsUrl,
|
|
160
|
+
deleteUrl,
|
|
161
|
+
ws: null,
|
|
162
|
+
handler,
|
|
163
|
+
retryCount: 0,
|
|
164
|
+
closed: false,
|
|
165
|
+
};
|
|
166
|
+
this.subscriptions.set(subscriptionId, sub);
|
|
167
|
+
this.openWebSocket(sub);
|
|
168
|
+
// Return unsubscribe function
|
|
169
|
+
return async () => {
|
|
170
|
+
sub.closed = true;
|
|
171
|
+
if (sub.ws) {
|
|
172
|
+
sub.ws.close();
|
|
173
|
+
sub.ws = null;
|
|
174
|
+
}
|
|
175
|
+
this.subscriptions.delete(subscriptionId);
|
|
176
|
+
// Best-effort DELETE — ignore errors (controller may have already cleaned up)
|
|
177
|
+
await this.session.delete(sub.deleteUrl).catch(() => undefined);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/** Close all active subscriptions */
|
|
181
|
+
async closeAll() {
|
|
182
|
+
const promises = [];
|
|
183
|
+
for (const sub of this.subscriptions.values()) {
|
|
184
|
+
sub.closed = true;
|
|
185
|
+
if (sub.ws) {
|
|
186
|
+
sub.ws.close();
|
|
187
|
+
sub.ws = null;
|
|
188
|
+
}
|
|
189
|
+
promises.push(this.session.delete(sub.deleteUrl).then(() => undefined).catch(() => undefined));
|
|
190
|
+
}
|
|
191
|
+
this.subscriptions.clear();
|
|
192
|
+
await Promise.allSettled(promises);
|
|
193
|
+
}
|
|
194
|
+
// ─── WebSocket lifecycle ────────────────────────────────────────────────────
|
|
195
|
+
openWebSocket(sub) {
|
|
196
|
+
const cookieHeader = this.session.getCookieHeader();
|
|
197
|
+
const WS = resolveWebSocket();
|
|
198
|
+
const ws = new WS(sub.wsUrl, ['robapi2_subscription'], {
|
|
199
|
+
headers: { Cookie: cookieHeader },
|
|
200
|
+
});
|
|
201
|
+
sub.ws = ws;
|
|
202
|
+
ws.onopen = () => {
|
|
203
|
+
sub.retryCount = 0;
|
|
204
|
+
};
|
|
205
|
+
ws.onmessage = (event) => {
|
|
206
|
+
try {
|
|
207
|
+
const data = typeof event.data === 'string' ? event.data : String(event.data);
|
|
208
|
+
const events = parseWsMessage(data);
|
|
209
|
+
for (const e of events) {
|
|
210
|
+
sub.handler(e);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Silently discard unparseable messages — don't crash the subscriber
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
ws.onerror = () => {
|
|
218
|
+
// onclose fires after onerror; reconnect logic lives there
|
|
219
|
+
};
|
|
220
|
+
ws.onclose = (event) => {
|
|
221
|
+
if (sub.closed)
|
|
222
|
+
return; // intentional close — do not reconnect
|
|
223
|
+
if (!event.wasClean && sub.retryCount < MAX_RETRIES) {
|
|
224
|
+
const delay = BACKOFF_MS[sub.retryCount] ?? BACKOFF_MS[BACKOFF_MS.length - 1];
|
|
225
|
+
sub.retryCount++;
|
|
226
|
+
setTimeout(() => {
|
|
227
|
+
if (!sub.closed)
|
|
228
|
+
this.openWebSocket(sub);
|
|
229
|
+
}, delay);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=WsSubscriber.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WsSubscriber.js","sourceRoot":"","sources":["../src/WsSubscriber.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* abb-rws-client — public API
|
|
3
|
+
*
|
|
4
|
+
* Typed HTTP/WebSocket client for ABB IRC5 robot controllers (RWS 1.0, RobotWare 6.x only).
|
|
5
|
+
* Not compatible with RWS 2.0 / RobotWare 7.x / OmniCore.
|
|
6
|
+
*/
|
|
7
|
+
export { RwsClient } from './RwsClient.js';
|
|
8
|
+
export { RwsError } from './types.js';
|
|
9
|
+
export type { RwsClientOptions, RwsErrorCode, ControllerState, OperationMode, ExecutionState, JointTarget, RobTarget, Signal, RapidTask, SubscriptionResource, SubscriptionEvent, } from './types.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,cAAc,EACd,WAAW,EACX,SAAS,EACT,MAAM,EACN,SAAS,EACT,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
|