Skip to content

Commit

Permalink
RSDK-2866: add disconnection and reconnection events (#119)
Browse files Browse the repository at this point in the history
Co-authored-by: Maxim Pertsov <[email protected]>
  • Loading branch information
purplenicole730 and maximpertsov authored Jun 15, 2023
1 parent 6ec1b2e commit 45aa325
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 29 deletions.
18 changes: 16 additions & 2 deletions examples/vanilla/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ function button() {
return <HTMLButtonElement>document.getElementById('main-button');
}

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

// This function is called when the robot is disconnected.
// Feel free to replace it with whatever logic you want to test out!
async function disconnected(event) {
console.log('The robot has been disconnected. Trying reconnect...');
}

// This function is called when the robot is reconnected.
// Feel free to replace it with whatever logic you want to test out!
async function reconnected(event) {
console.log('The robot has been reconnected. Work can be continued.');
}

async function main() {
// Connect to client
let client: VIAM.RobotClient;
try {
client = await connect();
console.log('connected!');
client.on('disconnected', disconnected);
client.on('reconnected', reconnected);
} catch (error) {
console.log(error);
return;
Expand Down
3 changes: 3 additions & 0 deletions src/events.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
type Callback = (args: unknown) => void;

export const RECONNECTED = 'reconnected';
export const DISCONNECTED = 'disconnected';

export class EventDispatcher {
listeners: Record<string, Set<Callback>> = {};

Expand Down
68 changes: 41 additions & 27 deletions src/robot/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Credentials, DialOptions } from '@viamrobotics/rpc/src/dial';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { dialDirect, dialWebRTC } from '@viamrobotics/rpc';
import type { grpc } from '@improbable-eng/grpc-web';
import { DISCONNECTED, EventDispatcher, events, RECONNECTED } from '../events';
import proto from '../gen/robot/v1/robot_pb';
import type {
PoseInFrame,
Expand All @@ -29,7 +30,6 @@ import { SLAMServiceClient } from '../gen/service/slam/v1/slam_pb_service';
import { SensorsServiceClient } from '../gen/service/sensors/v1/sensors_pb_service';
import { ServoServiceClient } from '../gen/component/servo/v1/servo_pb_service';
import { VisionServiceClient } from '../gen/service/vision/v1/vision_pb_service';
import { events } from '../events';
import { ViamResponseStream } from '../responses';
import SessionManager from './session-manager';
import type { Robot, RobotStatusStream } from './robot';
Expand All @@ -55,7 +55,7 @@ abstract class ServiceClient {
*
* @group Clients
*/
export class RobotClient implements Robot {
export class RobotClient extends EventDispatcher implements Robot {
private readonly serviceHost: string;
private readonly webrtcOptions: WebRTCOptions | undefined;
private readonly sessionOptions: SessionOptions | undefined;
Expand Down Expand Up @@ -114,6 +114,7 @@ export class RobotClient implements Robot {
webrtcOptions?: WebRTCOptions,
sessionOptions?: SessionOptions
) {
super();
this.serviceHost = serviceHost;
this.webrtcOptions = webrtcOptions;
this.sessionOptions = sessionOptions;
Expand All @@ -126,6 +127,35 @@ export class RobotClient implements Robot {
return this.transportFactory(opts);
}
);

events.on(RECONNECTED, () => {
this.emit(RECONNECTED, {});
});
events.on(DISCONNECTED, () => {
this.emit(DISCONNECTED, {});
if (this.webrtcOptions?.noReconnect) {
return;
}

let retries = 0;
// eslint-disable-next-line no-console
console.debug('connection closed, will try to reconnect');
void backOff(() =>
this.connect().then(
() => {
// eslint-disable-next-line no-console
console.debug('reconnected successfully!');
events.emit(RECONNECTED, {});
},
(error) => {
// eslint-disable-next-line no-console
console.debug(`failed to reconnect - retries count: ${retries}`);
retries += 1;
throw error;
}
)
);
});
}

get sessionId() {
Expand Down Expand Up @@ -284,6 +314,10 @@ export class RobotClient implements Robot {
this.sessionManager.reset();
}

public isConnected(): boolean {
return this.peerConn?.iceConnectionState === 'connected';
}

public async connect(
authEntity = this.savedAuthEntity,
creds = this.savedCreds
Expand Down Expand Up @@ -353,31 +387,11 @@ export class RobotClient implements Robot {
* connection getting closed, so restarting ice is not a valid way to
* recover.
*/
if (this.peerConn?.iceConnectionState === 'closed') {
if (this.webrtcOptions?.noReconnect) {
return;
}

let retries = 0;
// eslint-disable-next-line no-console
console.debug('connection closed, will try to reconnect');
void backOff(() =>
this.connect().then(
() => {
// eslint-disable-next-line no-console
console.debug('reconnected successfully!');
events.emit('reconnected', {});
},
(error) => {
// eslint-disable-next-line no-console
console.debug(
`failed to reconnect - retries count: ${retries}`
);
retries += 1;
throw error;
}
)
);
if (this.peerConn?.iceConnectionState === 'connected') {
events.emit(RECONNECTED, {});
} else if (this.peerConn?.iceConnectionState === 'closed') {
console.log('emit disconnected');
events.emit(DISCONNECTED, {});
}
});

Expand Down
18 changes: 18 additions & 0 deletions src/robot/robot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import type {
Transform,
} from '../gen/common/v1/common_pb';
import type { StructType } from '../types';
import { DISCONNECTED, RECONNECTED } from '../events';
import type proto from '../gen/robot/v1/robot_pb';
import type { ResponseStream } from '../gen/robot/v1/robot_pb_service';

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

type Callback = (args: unknown) => void;

export interface Robot {
/**
* Get the list of operations currently running on the robot.
Expand Down Expand Up @@ -146,4 +149,19 @@ export interface Robot {
resourceNames?: ResourceName.AsObject[],
durationMs?: number
): RobotStatusStream;

/**
* Call a function when an event of either 'reconnected' or 'disconnected' is
* triggered. Note that these events will only be triggered on WebRTC
* connections.
*
* @param type - The event ('reconnected' or 'disconnected') that was
* triggered.
* @param listener - The function to call
* @alpha
*/
on: (
type: typeof RECONNECTED | typeof DISCONNECTED,
listener: Callback
) => void;
}

0 comments on commit 45aa325

Please sign in to comment.