From 6bac906f1be4dd519a61755a21862bcc85d8b66a Mon Sep 17 00:00:00 2001 From: martha-johnston <106617924+martha-johnston@users.noreply.github.com> Date: Mon, 27 Jan 2025 19:44:45 +0100 Subject: [PATCH] RSDK-9621: Add Discover Service and GetModelsFromModules to Typescript (#436) Co-authored-by: John --- src/robot/client.ts | 31 +++++++++++++++- src/robot/robot.ts | 19 +++++----- src/services/discovery.ts | 2 ++ src/services/discovery/client.spec.ts | 51 +++++++++++++++++++++++++++ src/services/discovery/client.ts | 51 +++++++++++++++++++++++++++ src/services/discovery/discovery.ts | 14 ++++++++ 6 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 src/services/discovery.ts create mode 100644 src/services/discovery/client.spec.ts create mode 100644 src/services/discovery/client.ts create mode 100644 src/services/discovery/discovery.ts diff --git a/src/robot/client.ts b/src/robot/client.ts index 258fbd5c5..4aed90eb9 100644 --- a/src/robot/client.ts +++ b/src/robot/client.ts @@ -24,11 +24,14 @@ import { PowerSensorService } from '../gen/component/powersensor/v1/powersensor_ import { ServoService } from '../gen/component/servo/v1/servo_connect'; import { RobotService } from '../gen/robot/v1/robot_connect'; import { + // DiscoveryQuery deprecated, remove on march 10th DiscoveryQuery, + GetModelsFromModulesRequest, RestartModuleRequest, TransformPCDRequest, TransformPoseRequest, } from '../gen/robot/v1/robot_pb'; +import { DiscoveryService } from '../gen/service/discovery/v1/discovery_connect'; import { MotionService } from '../gen/service/motion/v1/motion_connect'; import { NavigationService } from '../gen/service/navigation/v1/navigation_connect'; import { SLAMService } from '../gen/service/slam/v1/slam_connect'; @@ -133,6 +136,10 @@ export class RobotClient extends EventDispatcher implements Robot { | PromiseClient | undefined; + private discoveryServiceClient: + | PromiseClient + | undefined; + private motionServiceClient: PromiseClient | undefined; private visionServiceClient: PromiseClient | undefined; @@ -345,6 +352,13 @@ export class RobotClient extends EventDispatcher implements Robot { return this.navigationServiceClient; } + get discoveryService() { + if (!this.discoveryServiceClient) { + throw new Error(RobotClient.notConnectedYetStr); + } + return this.discoveryServiceClient; + } + get motionService() { if (!this.motionServiceClient) { throw new Error(RobotClient.notConnectedYetStr); @@ -603,6 +617,10 @@ export class RobotClient extends EventDispatcher implements Robot { SLAMService, clientTransport ); + this.discoveryServiceClient = createPromiseClient( + DiscoveryService, + clientTransport + ); this.emit(MachineConnectionEvent.CONNECTED, {}); } catch (error) { @@ -692,15 +710,26 @@ export class RobotClient extends EventDispatcher implements Robot { return resp.pointCloudPcd; } - // DISCOVERY + // DISCOVERY - deprecated, remove on march 10th async discoverComponents(queries: DiscoveryQuery[]) { + console.warn( + 'RobotClient.discoverComponents is deprecated. It will be removed on March 10 2025. Use the DiscoveryService APIs instead.' + ); const resp = await this.robotService.discoverComponents({ queries, }); return resp.discovery; } + // GET MODELS FROM MODULES + + async getModelsFromModules() { + const request = new GetModelsFromModulesRequest({}); + const resp = await this.robotService.getModelsFromModules(request); + return resp.models; + } + // GET CLOUD METADATA async getCloudMetadata() { diff --git a/src/robot/robot.ts b/src/robot/robot.ts index 975bdfd8a..8109ea6f2 100644 --- a/src/robot/robot.ts +++ b/src/robot/robot.ts @@ -3,6 +3,7 @@ import { MachineConnectionEvent } from '../events'; import type { PoseInFrame, Transform } from '../gen/common/v1/common_pb'; import type proto from '../gen/robot/v1/robot_pb'; import type { ResourceName } from '../types'; +import type { ModuleModel } from '../gen/robot/v1/robot_pb'; export type CloudMetadata = proto.GetCloudMetadataResponse; @@ -103,6 +104,8 @@ export interface Robot { ): Promise; /** + * Deprecated: v0.36.0, use the Discovery Service APIs instead. + * * Get the list of discovered component configurations. * * @param queries - The list of component models to discovery. @@ -113,6 +116,14 @@ export interface Robot { queries: proto.DiscoveryQuery[] ): Promise; + /** + * Get the list of discovered component configurations. + * + * @group ComponentConfig + * @alpha + */ + getModelsFromModules(): Promise; + /** * Get a list of all resources on the robot. * @@ -173,12 +184,4 @@ export interface Robot { * @alpha */ restartModule(moduleId?: string, moduleName?: string): Promise; - - /** - * Get version information about the robot, such as platform, version, and api - * version. - * - * @alpha - */ - getVersion(): Promise; } diff --git a/src/services/discovery.ts b/src/services/discovery.ts new file mode 100644 index 000000000..529d9db3e --- /dev/null +++ b/src/services/discovery.ts @@ -0,0 +1,2 @@ +export { DiscoveryClient } from './discovery/client'; +export type { Discovery } from './discovery/discovery'; diff --git a/src/services/discovery/client.spec.ts b/src/services/discovery/client.spec.ts new file mode 100644 index 000000000..2f606bcd6 --- /dev/null +++ b/src/services/discovery/client.spec.ts @@ -0,0 +1,51 @@ +// @vitest-environment happy-dom + +import { + createPromiseClient, + createRouterTransport, +} from '@connectrpc/connect'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { DiscoveryService } from '../../gen/service/discovery/v1/discovery_connect'; +import { DiscoverResourcesResponse } from '../../gen/service/discovery/v1/discovery_pb'; +import { RobotClient } from '../../robot'; +import { DiscoveryClient } from './client'; +import { ComponentConfig } from '../../gen/app/v1/robot_pb'; +vi.mock('../../robot'); +vi.mock('../../gen/service/discovery/v1/discovery_pb_service'); + +const discoveryClientName = 'test-discovery'; + +let discovery: DiscoveryClient; + +const discoveries: ComponentConfig = new ComponentConfig(); + +describe('DiscoveryClient Tests', () => { + beforeEach(() => { + const mockTransport = createRouterTransport(({ service }) => { + service(DiscoveryService, { + discoverResources: () => + new DiscoverResourcesResponse({ discoveries: [discoveries] }), + }); + }); + + RobotClient.prototype.createServiceClient = vi + .fn() + .mockImplementation(() => + createPromiseClient(DiscoveryService, mockTransport) + ); + discovery = new DiscoveryClient( + new RobotClient('host'), + discoveryClientName + ); + }); + + describe('Discovery Resources Tests', () => { + it('returns resources from a machine', async () => { + const expected = [discoveries]; + + await expect(discovery.discoverResources()).resolves.toStrictEqual( + expected + ); + }); + }); +}); diff --git a/src/services/discovery/client.ts b/src/services/discovery/client.ts new file mode 100644 index 000000000..ed8cac454 --- /dev/null +++ b/src/services/discovery/client.ts @@ -0,0 +1,51 @@ +import { Struct, type JsonValue } from '@bufbuild/protobuf'; +import type { CallOptions, PromiseClient } from '@connectrpc/connect'; +import { DiscoveryService } from '../../gen/service/discovery/v1/discovery_connect'; +import { DiscoverResourcesRequest } from '../../gen/service/discovery/v1/discovery_pb'; +import type { RobotClient } from '../../robot'; +import { doCommandFromClient } from '../../utils'; +import type { Options } from '../../types'; +import type { Discovery } from './discovery'; + +/** + * A gRPC-web client for a Vision service. + * + * @group Clients + */ +export class DiscoveryClient implements Discovery { + private client: PromiseClient; + private readonly name: string; + private readonly options: Options; + public callOptions: CallOptions = { headers: {} as Record }; + + constructor(client: RobotClient, name: string, options: Options = {}) { + this.client = client.createServiceClient(DiscoveryService); + this.name = name; + this.options = options; + } + + async discoverResources(extra = {}, callOptions = this.callOptions) { + const request = new DiscoverResourcesRequest({ + name: this.name, + extra: Struct.fromJson(extra), + }); + + this.options.requestLogger?.(request); + + const resp = await this.client.discoverResources(request, callOptions); + return resp.discoveries; + } + + async doCommand( + command: Struct, + callOptions = this.callOptions + ): Promise { + return doCommandFromClient( + this.client.doCommand, + this.name, + command, + this.options, + callOptions + ); + } +} diff --git a/src/services/discovery/discovery.ts b/src/services/discovery/discovery.ts new file mode 100644 index 000000000..61f74c579 --- /dev/null +++ b/src/services/discovery/discovery.ts @@ -0,0 +1,14 @@ +import type { Struct } from '@bufbuild/protobuf'; +import type { Resource } from '../../types'; +import type { ComponentConfig } from '../../gen/app/v1/robot_pb'; + +/** A service that enables various computer vision algorithms */ +export interface Discovery extends Resource { + /** + * Get a list of component configs of all discovered components. + * + * @param discoveryName - The name of the discovery service. + * @returns - The list of ComponentConfigs. + */ + discoverResources: (extra?: Struct) => Promise; +}