Skip to content

Commit 45aa325

Browse files
RSDK-2866: add disconnection and reconnection events (#119)
Co-authored-by: Maxim Pertsov <[email protected]>
1 parent 6ec1b2e commit 45aa325

File tree

4 files changed

+78
-29
lines changed

4 files changed

+78
-29
lines changed

examples/vanilla/src/main.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ function button() {
3434
return <HTMLButtonElement>document.getElementById('main-button');
3535
}
3636

37-
// This function runs a motor component with a given named on your robot.
38-
// Feel free to replace it whatever logic you want to test out!
37+
// This function runs a motor component with a given name on your robot.
38+
// Feel free to replace it with whatever logic you want to test out!
3939
async function run(client: VIAM.RobotClient) {
4040
// Replace with the name of a motor on your robot.
4141
const name = '<MOTOR NAME>';
@@ -52,12 +52,26 @@ async function run(client: VIAM.RobotClient) {
5252
}
5353
}
5454

55+
// This function is called when the robot is disconnected.
56+
// Feel free to replace it with whatever logic you want to test out!
57+
async function disconnected(event) {
58+
console.log('The robot has been disconnected. Trying reconnect...');
59+
}
60+
61+
// This function is called when the robot is reconnected.
62+
// Feel free to replace it with whatever logic you want to test out!
63+
async function reconnected(event) {
64+
console.log('The robot has been reconnected. Work can be continued.');
65+
}
66+
5567
async function main() {
5668
// Connect to client
5769
let client: VIAM.RobotClient;
5870
try {
5971
client = await connect();
6072
console.log('connected!');
73+
client.on('disconnected', disconnected);
74+
client.on('reconnected', reconnected);
6175
} catch (error) {
6276
console.log(error);
6377
return;

src/events.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
type Callback = (args: unknown) => void;
22

3+
export const RECONNECTED = 'reconnected';
4+
export const DISCONNECTED = 'disconnected';
5+
36
export class EventDispatcher {
47
listeners: Record<string, Set<Callback>> = {};
58

src/robot/client.ts

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Credentials, DialOptions } from '@viamrobotics/rpc/src/dial';
44
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
55
import { dialDirect, dialWebRTC } from '@viamrobotics/rpc';
66
import type { grpc } from '@improbable-eng/grpc-web';
7+
import { DISCONNECTED, EventDispatcher, events, RECONNECTED } from '../events';
78
import proto from '../gen/robot/v1/robot_pb';
89
import type {
910
PoseInFrame,
@@ -29,7 +30,6 @@ import { SLAMServiceClient } from '../gen/service/slam/v1/slam_pb_service';
2930
import { SensorsServiceClient } from '../gen/service/sensors/v1/sensors_pb_service';
3031
import { ServoServiceClient } from '../gen/component/servo/v1/servo_pb_service';
3132
import { VisionServiceClient } from '../gen/service/vision/v1/vision_pb_service';
32-
import { events } from '../events';
3333
import { ViamResponseStream } from '../responses';
3434
import SessionManager from './session-manager';
3535
import type { Robot, RobotStatusStream } from './robot';
@@ -55,7 +55,7 @@ abstract class ServiceClient {
5555
*
5656
* @group Clients
5757
*/
58-
export class RobotClient implements Robot {
58+
export class RobotClient extends EventDispatcher implements Robot {
5959
private readonly serviceHost: string;
6060
private readonly webrtcOptions: WebRTCOptions | undefined;
6161
private readonly sessionOptions: SessionOptions | undefined;
@@ -114,6 +114,7 @@ export class RobotClient implements Robot {
114114
webrtcOptions?: WebRTCOptions,
115115
sessionOptions?: SessionOptions
116116
) {
117+
super();
117118
this.serviceHost = serviceHost;
118119
this.webrtcOptions = webrtcOptions;
119120
this.sessionOptions = sessionOptions;
@@ -126,6 +127,35 @@ export class RobotClient implements Robot {
126127
return this.transportFactory(opts);
127128
}
128129
);
130+
131+
events.on(RECONNECTED, () => {
132+
this.emit(RECONNECTED, {});
133+
});
134+
events.on(DISCONNECTED, () => {
135+
this.emit(DISCONNECTED, {});
136+
if (this.webrtcOptions?.noReconnect) {
137+
return;
138+
}
139+
140+
let retries = 0;
141+
// eslint-disable-next-line no-console
142+
console.debug('connection closed, will try to reconnect');
143+
void backOff(() =>
144+
this.connect().then(
145+
() => {
146+
// eslint-disable-next-line no-console
147+
console.debug('reconnected successfully!');
148+
events.emit(RECONNECTED, {});
149+
},
150+
(error) => {
151+
// eslint-disable-next-line no-console
152+
console.debug(`failed to reconnect - retries count: ${retries}`);
153+
retries += 1;
154+
throw error;
155+
}
156+
)
157+
);
158+
});
129159
}
130160

131161
get sessionId() {
@@ -284,6 +314,10 @@ export class RobotClient implements Robot {
284314
this.sessionManager.reset();
285315
}
286316

317+
public isConnected(): boolean {
318+
return this.peerConn?.iceConnectionState === 'connected';
319+
}
320+
287321
public async connect(
288322
authEntity = this.savedAuthEntity,
289323
creds = this.savedCreds
@@ -353,31 +387,11 @@ export class RobotClient implements Robot {
353387
* connection getting closed, so restarting ice is not a valid way to
354388
* recover.
355389
*/
356-
if (this.peerConn?.iceConnectionState === 'closed') {
357-
if (this.webrtcOptions?.noReconnect) {
358-
return;
359-
}
360-
361-
let retries = 0;
362-
// eslint-disable-next-line no-console
363-
console.debug('connection closed, will try to reconnect');
364-
void backOff(() =>
365-
this.connect().then(
366-
() => {
367-
// eslint-disable-next-line no-console
368-
console.debug('reconnected successfully!');
369-
events.emit('reconnected', {});
370-
},
371-
(error) => {
372-
// eslint-disable-next-line no-console
373-
console.debug(
374-
`failed to reconnect - retries count: ${retries}`
375-
);
376-
retries += 1;
377-
throw error;
378-
}
379-
)
380-
);
390+
if (this.peerConn?.iceConnectionState === 'connected') {
391+
events.emit(RECONNECTED, {});
392+
} else if (this.peerConn?.iceConnectionState === 'closed') {
393+
console.log('emit disconnected');
394+
events.emit(DISCONNECTED, {});
381395
}
382396
});
383397

src/robot/robot.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import type {
44
Transform,
55
} from '../gen/common/v1/common_pb';
66
import type { StructType } from '../types';
7+
import { DISCONNECTED, RECONNECTED } from '../events';
78
import type proto from '../gen/robot/v1/robot_pb';
89
import type { ResponseStream } from '../gen/robot/v1/robot_pb_service';
910

1011
export type RobotStatusStream = ResponseStream<proto.Status[]>;
1112

13+
type Callback = (args: unknown) => void;
14+
1215
export interface Robot {
1316
/**
1417
* Get the list of operations currently running on the robot.
@@ -146,4 +149,19 @@ export interface Robot {
146149
resourceNames?: ResourceName.AsObject[],
147150
durationMs?: number
148151
): RobotStatusStream;
152+
153+
/**
154+
* Call a function when an event of either 'reconnected' or 'disconnected' is
155+
* triggered. Note that these events will only be triggered on WebRTC
156+
* connections.
157+
*
158+
* @param type - The event ('reconnected' or 'disconnected') that was
159+
* triggered.
160+
* @param listener - The function to call
161+
* @alpha
162+
*/
163+
on: (
164+
type: typeof RECONNECTED | typeof DISCONNECTED,
165+
listener: Callback
166+
) => void;
149167
}

0 commit comments

Comments
 (0)