Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 52 additions & 3 deletions src/services/data-manager/client.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Struct, type JsonValue } from '@bufbuild/protobuf';
import type { CallOptions, PromiseClient } from '@connectrpc/connect';
import type { CallOptions, Client } from '@connectrpc/connect';
import { MimeType } from '../../gen/app/datasync/v1/data_sync_pb.js';
import { DataManagerService } from '../../gen/service/datamanager/v1/data_manager_connect.js';
import { SyncRequest } from '../../gen/service/datamanager/v1/data_manager_pb.js';
import {
SyncRequest,
UploadBinaryDataToDatasetsRequest,
} from '../../gen/service/datamanager/v1/data_manager_pb.js';
import type { RobotClient } from '../../robot';
import type { Options } from '../../types';
import { doCommandFromClient } from '../../utils';
import type { DataManager } from './data-manager';

export class DataManagerClient implements DataManager {
private client: PromiseClient<typeof DataManagerService>;
private client: Client<typeof DataManagerService>;
public readonly name: string;
private readonly options: Options;
public callOptions: CallOptions = { headers: {} as Record<string, string> };
Expand Down Expand Up @@ -80,4 +84,49 @@ export class DataManagerClient implements DataManager {
callOptions
);
}

/**
* Uploads binary data to specified datasets.
*
* @example
*
* ```ts
* const dataManager = new VIAM.DataManagerClient(
* machine,
* 'my_data_manager'
* );
* await dataManager.uploadBinaryDataToDatasets(
* new Uint8Array([1, 2, 3]),
* ['tag1', 'tag2'],
* ['datasetId1', 'datasetId2'],
* MimeType.MIME_TYPE_JPEG
* );
* ```
*
* @param binaryData - The binary data to upload.
* @param tags - Tags to associate with the binary data.
* @param datasetIds - IDs of the datasets to associate the binary data with.
* @param mimeType - The MIME type of the binary data.
* @param extra - Extra arguments to pass to the upload request.
* @param callOptions - Call options for the upload request.
*/
async uploadBinaryDataToDatasets(
binaryData: Uint8Array,
tags: string[],
datasetIds: string[],
mimeType: MimeType,
extra = {},
callOptions = this.callOptions
) {
const request = new UploadBinaryDataToDatasetsRequest({
name: this.name,
binaryData,
tags,
datasetIds,
mimeType,
extra: Struct.fromJson(extra),
});
this.options.requestLogger?.(request);
await this.client.uploadBinaryDataToDatasets(request, callOptions);
}
}
8 changes: 8 additions & 0 deletions src/services/data-manager/data-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { Struct } from '@bufbuild/protobuf';
import { MimeType } from '../../gen/app/datasync/v1/data_sync_pb.js';
import type { Resource } from '../../types';

export interface DataManager extends Resource {
sync: (extra?: Struct) => Promise<void>;
uploadBinaryDataToDatasets: (
binaryData: Uint8Array,
tags: string[],
datasetIds: string[],
mimeType: MimeType,
extra?: Struct
) => Promise<void>;
}
106 changes: 77 additions & 29 deletions src/services/motion/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,32 @@ vi.mock('../../gen/service/motion/v1/motion_pb_service');
vi.mock('../../robot');

import { Struct, Timestamp } from '@bufbuild/protobuf';
import {
createPromiseClient,
createRouterTransport,
} from '@connectrpc/connect';
import { createClient, createRouterTransport } from '@connectrpc/connect';
import { MotionService } from '../../gen/service/motion/v1/motion_connect';
import {
GetPlanRequest,
ListPlanStatusesRequest,
MoveOnGlobeRequest,
MoveOnGlobeResponse,
MoveRequest,
StopPlanRequest,
} from '../../gen/service/motion/v1/motion_pb';
import { GeoGeometry, GeoPoint, ResourceName } from '../../types';
import {
GeoGeometry,
GeoPoint,
Pose,
PoseInFrame,
ResourceName,
} from '../../types';
import { MotionClient } from './client';
import {
Constraints,
GetPlanResponse,
ListPlanStatusesResponse,
MotionConfiguration,
ObstacleDetector,
PlanState,
PseudolinearConstraint,
} from './types';

const motionClientName = 'test-motion';
Expand Down Expand Up @@ -76,9 +82,7 @@ describe('moveOnGlobe', () => {

RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() =>
createPromiseClient(MotionService, mockTransport)
);
.mockImplementation(() => createClient(MotionService, mockTransport));

motion = new MotionClient(new RobotClient('host'), motionClientName);

Expand Down Expand Up @@ -226,9 +230,7 @@ describe('moveOnGlobe', () => {

RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() =>
createPromiseClient(MotionService, mockTransport)
);
.mockImplementation(() => createClient(MotionService, mockTransport));

motion = new MotionClient(new RobotClient('host'), motionClientName);

Expand Down Expand Up @@ -263,6 +265,64 @@ describe('moveOnGlobe', () => {
});
});

describe('move', () => {
it('sends a move request with pseudolinear constraints', async () => {
const expectedComponentName = new ResourceName({
namespace: 'viam',
type: 'component',
subtype: 'base',
name: 'myBase',
});
const expectedDestination = new PoseInFrame({
referenceFrame: 'world',
pose: new Pose({
x: 1,
y: 2,
z: 3,
oX: 0,
oY: 0,
oZ: 1,
theta: 90,
}),
});
const expectedPseudolinearConstraint = new PseudolinearConstraint({
lineToleranceFactor: 5,
orientationToleranceFactor: 10,
});
const expectedConstraints = new Constraints({
pseudolinearConstraint: [expectedPseudolinearConstraint],
});
const expectedExtra = { some: 'extra' };
let capturedReq: MoveRequest | undefined;
const mockTransport = createRouterTransport(({ service }) => {
service(MotionService, {
move: (req) => {
capturedReq = req;
return { success: true };
},
});
});
RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() => createClient(MotionService, mockTransport));
motion = new MotionClient(new RobotClient('host'), motionClientName);
await expect(
motion.move(
expectedDestination,
expectedComponentName,
undefined,
expectedConstraints,
expectedExtra
)
).resolves.toStrictEqual(true);
expect(capturedReq?.name).toStrictEqual(motionClientName);
expect(capturedReq?.destination).toStrictEqual(expectedDestination);
expect(capturedReq?.componentName).toStrictEqual(expectedComponentName);
expect(capturedReq?.constraints).toStrictEqual(expectedConstraints);
expect(capturedReq?.extra).toStrictEqual(Struct.fromJson(expectedExtra));
});
});

describe('stopPlan', () => {
it('return null', async () => {
const expectedComponentName = new ResourceName({
Expand All @@ -285,9 +345,7 @@ describe('stopPlan', () => {

RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() =>
createPromiseClient(MotionService, mockTransport)
);
.mockImplementation(() => createClient(MotionService, mockTransport));

motion = new MotionClient(new RobotClient('host'), motionClientName);

Expand Down Expand Up @@ -318,9 +376,7 @@ describe('stopPlan', () => {

RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() =>
createPromiseClient(MotionService, mockTransport)
);
.mockImplementation(() => createClient(MotionService, mockTransport));

motion = new MotionClient(new RobotClient('host'), motionClientName);

Expand Down Expand Up @@ -393,9 +449,7 @@ describe('getPlan', () => {

RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() =>
createPromiseClient(MotionService, mockTransport)
);
.mockImplementation(() => createClient(MotionService, mockTransport));

motion = new MotionClient(new RobotClient('host'), motionClientName);

Expand Down Expand Up @@ -430,9 +484,7 @@ describe('getPlan', () => {

RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() =>
createPromiseClient(MotionService, mockTransport)
);
.mockImplementation(() => createClient(MotionService, mockTransport));

motion = new MotionClient(new RobotClient('host'), motionClientName);

Expand Down Expand Up @@ -485,9 +537,7 @@ describe('listPlanStatuses', () => {

RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() =>
createPromiseClient(MotionService, mockTransport)
);
.mockImplementation(() => createClient(MotionService, mockTransport));

motion = new MotionClient(new RobotClient('host'), motionClientName);

Expand All @@ -513,9 +563,7 @@ describe('listPlanStatuses', () => {

RobotClient.prototype.createServiceClient = vi
.fn()
.mockImplementation(() =>
createPromiseClient(MotionService, mockTransport)
);
.mockImplementation(() => createClient(MotionService, mockTransport));

motion = new MotionClient(new RobotClient('host'), motionClientName);

Expand Down
3 changes: 3 additions & 0 deletions src/services/motion/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export type CollisionSpecification =
export type Constraints = PlainMessage<motionApi.Constraints>;
export type GetPlanResponse = motionApi.GetPlanResponse;
export type LinearConstraint = PlainMessage<motionApi.LinearConstraint>;
export type PseudolinearConstraint =
PlainMessage<motionApi.PseudolinearConstraint>;
export type ListPlanStatusesResponse = motionApi.ListPlanStatusesResponse;
export type MotionConfiguration = PlainMessage<motionApi.MotionConfiguration>;
export type ObstacleDetector = PlainMessage<motionApi.ObstacleDetector>;
Expand All @@ -18,6 +20,7 @@ export const {
Constraints,
GetPlanResponse,
LinearConstraint,
PseudolinearConstraint,
ListPlanStatusesResponse,
MotionConfiguration,
ObstacleDetector,
Expand Down