diff --git a/lib/src/components/arm/arm.dart b/lib/src/components/arm/arm.dart index 6a34bb2e792..02e4abe991f 100644 --- a/lib/src/components/arm/arm.dart +++ b/lib/src/components/arm/arm.dart @@ -1,5 +1,6 @@ import '../../../viam_sdk.dart'; import '../../gen/common/v1/common.pb.dart'; +import '../../utils.dart'; /// {@category Components} /// Arm represents a physical robot arm that exists in three-dimensional space. @@ -88,6 +89,15 @@ abstract class Arm extends Resource { /// For more information, see [Arm component](https://docs.viam.com/dev/reference/apis/components/arm/#getkinematics). Future getKinematics({Map? extra}); + /// Get all geometries associated with the [Arm] + /// + /// ``` + /// final geometries = await myArm.getGeometries(); + /// ``` + /// + /// For more information, see [Arm component](https://docs.viam.com/dev/reference/apis/components/arm/#getgeometries). + Future> getGeometries({Map? extra}); + /// Get the [ResourceName] for this [Arm] with the given [name]. /// /// ``` diff --git a/lib/src/components/arm/client.dart b/lib/src/components/arm/client.dart index 3e948769a42..07e475c5138 100644 --- a/lib/src/components/arm/client.dart +++ b/lib/src/components/arm/client.dart @@ -5,7 +5,6 @@ import '../../gen/component/arm/v1/arm.pbgrpc.dart'; import '../../gen/google/protobuf/struct.pb.dart'; import '../../resource/base.dart'; import '../../utils.dart'; -import '../gripper/gripper.dart'; import 'arm.dart'; /// {@category Components} @@ -101,4 +100,13 @@ class ArmClient extends Arm with RPCDebugLoggerMixin implements ResourceRPCClien final response = await client.getKinematics(request, options: callOptions); return Kinematics.fromProto(response); } + + @override + Future> getGeometries({Map? extra}) async { + final request = GetGeometriesRequest() + ..name = name + ..extra = extra?.toStruct() ?? Struct(); + final response = await client.getGeometries(request, options: callOptions); + return response.geometries; + } } diff --git a/lib/src/components/arm/service.dart b/lib/src/components/arm/service.dart index 2565f9cbb14..8c20a835d12 100644 --- a/lib/src/components/arm/service.dart +++ b/lib/src/components/arm/service.dart @@ -78,9 +78,10 @@ class ArmService extends ArmServiceBase { } @override - Future getGeometries(ServiceCall call, GetGeometriesRequest request) { - // TODO: implement getGeometries - throw UnimplementedError(); + Future getGeometries(ServiceCall call, GetGeometriesRequest request) async { + final arm = _armFromManager(request.name); + final geometries = await arm.getGeometries(extra: request.extra.toMap()); + return GetGeometriesResponse()..geometries.addAll(geometries); } @override diff --git a/lib/src/components/gantry/client.dart b/lib/src/components/gantry/client.dart index f541fe6cbca..2eef1fd94f3 100644 --- a/lib/src/components/gantry/client.dart +++ b/lib/src/components/gantry/client.dart @@ -81,4 +81,22 @@ class GantryClient extends Gantry with RPCDebugLoggerMixin implements ResourceRP final response = await client.doCommand(request, options: callOptions); return response.result.toMap(); } + + @override + Future getKinematics({Map? extra}) async { + final request = GetKinematicsRequest() + ..name = name + ..extra = extra?.toStruct() ?? Struct(); + final response = await client.getKinematics(request, options: callOptions); + return Kinematics.fromProto(response); + } + + @override + Future> getGeometries({Map? extra}) async { + final request = GetGeometriesRequest() + ..name = name + ..extra = extra?.toStruct() ?? Struct(); + final response = await client.getGeometries(request, options: callOptions); + return response.geometries; + } } diff --git a/lib/src/components/gantry/gantry.dart b/lib/src/components/gantry/gantry.dart index 5817833a558..c91e93b5d9f 100644 --- a/lib/src/components/gantry/gantry.dart +++ b/lib/src/components/gantry/gantry.dart @@ -1,6 +1,7 @@ import '../../gen/common/v1/common.pb.dart'; import '../../resource/base.dart'; import '../../robot/client.dart'; +import '../../utils.dart'; /// {@category Components} /// Gantry represents a physical Gantry and can be used for controlling gantries of N axes. @@ -63,6 +64,24 @@ abstract class Gantry extends Resource { /// For more information, see [Gantry component](https://docs.viam.com/dev/reference/apis/components/gantry/#ismoving). Future isMoving(); + /// Get the kinematics data associated with the [Gantry] + /// + /// ``` + /// var kinematics = await myGantry.getKinematics(); + /// ``` + /// + /// For more information, see [Gantry component](https://docs.viam.com/dev/reference/apis/components/gantry/#getkinematics). + Future getKinematics({Map? extra}); + + /// Get all geometries associated with the [Gantry] + /// + /// ``` + /// final geometries = await myGantry.getGeometries(); + /// ``` + /// + /// For more information, see [Gantry component](https://docs.viam.com/dev/reference/apis/components/gantry/#getgeometries). + Future> getGeometries({Map? extra}); + /// Get the [ResourceName] for this [Gantry] with the given [name] /// /// ``` diff --git a/lib/src/components/gantry/service.dart b/lib/src/components/gantry/service.dart index 186b7fa717f..b3f68aca54c 100644 --- a/lib/src/components/gantry/service.dart +++ b/lib/src/components/gantry/service.dart @@ -71,14 +71,18 @@ class GantryService extends GantryServiceBase { } @override - Future getGeometries(ServiceCall call, GetGeometriesRequest request) { - // TODO: implement getGeometries - throw UnimplementedError(); + Future getGeometries(ServiceCall call, GetGeometriesRequest request) async { + final gantry = _fromManager(request.name); + final geometries = await gantry.getGeometries(extra: request.extra.toMap()); + return GetGeometriesResponse()..geometries.addAll(geometries); } @override - Future getKinematics(ServiceCall call, GetKinematicsRequest request) { - // TODO: implement getKinematics - throw UnimplementedError(); + Future getKinematics(ServiceCall call, GetKinematicsRequest request) async { + final gantry = _fromManager(request.name); + final response = await gantry.getKinematics(extra: request.extra.toMap()); + return GetKinematicsResponse() + ..format = response.format + ..kinematicsData = response.raw; } } diff --git a/lib/src/components/gripper/gripper.dart b/lib/src/components/gripper/gripper.dart index 1ab7a0da83e..dad59fa116e 100644 --- a/lib/src/components/gripper/gripper.dart +++ b/lib/src/components/gripper/gripper.dart @@ -4,18 +4,6 @@ import '../../resource/base.dart'; import '../../robot/client.dart'; import '../../utils.dart'; -/// {@category Viam SDK} -class Kinematics { - KinematicsFileFormat format; - List raw; - - Kinematics(this.format, this.raw); - - factory Kinematics.fromProto(GetKinematicsResponse gkResponse) { - return Kinematics(gkResponse.format, gkResponse.kinematicsData); - } -} - /// {@category Viam SDK} /// HoldingStatus represents whether the gripper is currently holding onto /// an object as well as any additional contextual information (stored in `Meta`). diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 3885c234b1b..77e3fe9872e 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -11,6 +11,17 @@ import 'gen/google/protobuf/struct.pb.dart'; final _logger = Logger(); +class Kinematics { + KinematicsFileFormat format; + List raw; + + Kinematics(this.format, this.raw); + + factory Kinematics.fromProto(GetKinematicsResponse gkResponse) { + return Kinematics(gkResponse.format, gkResponse.kinematicsData); + } +} + extension NullableStringUtils on String? { bool get isNullOrEmpty { return this?.isEmpty ?? true; diff --git a/test/unit_test/components/arm_test.dart b/test/unit_test/components/arm_test.dart index b3c992a7d31..a9ae33feb33 100644 --- a/test/unit_test/components/arm_test.dart +++ b/test/unit_test/components/arm_test.dart @@ -19,7 +19,14 @@ class FakeArm extends Arm { Map? extra; Map arm3DModels = {}; Kinematics armKinematics = Kinematics(KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, [1, 2, 3]); - + List armGeometries = [ + Geometry() + ..box = (RectangularPrism() + ..dimsMm = (Vector3() + ..x = 1 + ..y = 2 + ..z = 3)), + ]; @override String name; @@ -74,6 +81,12 @@ class FakeArm extends Arm { return arm3DModels; } + @override + Future> getGeometries({Map? extra}) async { + this.extra = extra; + return armGeometries; + } + @override Future getKinematics({Map? extra}) async { this.extra = extra; @@ -154,6 +167,14 @@ void main() { expect(arm.extra, {'foo': 'bar'}); }); + test('getGeometries', () async { + final geometries = await arm.getGeometries(); + expect(geometries[0].whichGeometryType(), Geometry_GeometryType.box); + expect(geometries[0].box.dimsMm.x, 1); + expect(geometries[0].box.dimsMm.y, 2); + expect(geometries[0].box.dimsMm.z, 3); + }); + test('getKinematics', () async { final kinematics = await arm.getKinematics(); expect(kinematics.format, KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA); @@ -175,7 +196,11 @@ void main() { service = ArmService(manager); server = Server.create(services: [service]); await serveServerAtUnusedPort(server); - channel = ClientChannel('localhost', port: server.port!, options: const ChannelOptions(credentials: ChannelCredentials.insecure())); + channel = ClientChannel( + 'localhost', + port: server.port!, + options: const ChannelOptions(credentials: ChannelCredentials.insecure()), + ); }); tearDown(() async { @@ -262,9 +287,11 @@ void main() { final cmd = {'foo': 'bar'}; final client = ArmServiceClient(channel); - final resp = await client.doCommand(DoCommandRequest() - ..name = name - ..command = cmd.toStruct()); + final resp = await client.doCommand( + DoCommandRequest() + ..name = name + ..command = cmd.toStruct(), + ); expect(resp.result.toMap()['command'], cmd); }); @@ -272,9 +299,11 @@ void main() { expect(arm.extra, null); final client = ArmServiceClient(channel); - await client.stop(StopRequest() - ..name = name - ..extra = {'foo': 'bar'}.toStruct()); + await client.stop( + StopRequest() + ..name = name + ..extra = {'foo': 'bar'}.toStruct(), + ); expect(arm.extra, {'foo': 'bar'}); }); @@ -285,6 +314,16 @@ void main() { expect(response.format, KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA); expect(response.kinematicsData, [1, 2, 3]); }); + + test('getGeometries', () async { + final client = ArmServiceClient(channel); + final request = GetGeometriesRequest()..name = name; + final response = await client.getGeometries(request); + expect(response.geometries[0].whichGeometryType(), Geometry_GeometryType.box); + expect(response.geometries[0].box.dimsMm.x, 1); + expect(response.geometries[0].box.dimsMm.y, 2); + expect(response.geometries[0].box.dimsMm.z, 3); + }); }); group('Arm Client Tests', () { @@ -365,5 +404,14 @@ void main() { expect(kinematics.format, KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA); expect(kinematics.raw, [1, 2, 3]); }); + + test('getGeometries', () async { + final client = ArmClient(name, channel); + final geometries = await client.getGeometries(); + expect(geometries[0].whichGeometryType(), Geometry_GeometryType.box); + expect(geometries[0].box.dimsMm.x, 1); + expect(geometries[0].box.dimsMm.y, 2); + expect(geometries[0].box.dimsMm.z, 3); + }); }); } diff --git a/test/unit_test/components/gantry_test.dart b/test/unit_test/components/gantry_test.dart index 79803185924..cb21c611ad7 100644 --- a/test/unit_test/components/gantry_test.dart +++ b/test/unit_test/components/gantry_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:grpc/grpc.dart'; +import 'package:viam_sdk/protos/common/common.dart'; import 'package:viam_sdk/src/components/gantry/service.dart'; import 'package:viam_sdk/src/gen/component/gantry/v1/gantry.pbgrpc.dart'; import 'package:viam_sdk/src/resource/manager.dart'; @@ -14,6 +15,15 @@ class FakeGantry extends Gantry { List mLengths; bool isStopped = true; Map? extra; + Kinematics gantryKinematics = Kinematics(KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, [1, 2, 3]); + List gantryGeometries = [ + Geometry() + ..box = (RectangularPrism() + ..dimsMm = (Vector3() + ..x = 1 + ..y = 2 + ..z = 3)), + ]; @override String name; @@ -57,6 +67,18 @@ class FakeGantry extends Gantry { Future> position({Map? extra}) async { return positions; } + + @override + Future getKinematics({Map? extra}) async { + this.extra = extra; + return gantryKinematics; + } + + @override + Future> getGeometries({Map? extra}) async { + this.extra = extra; + return gantryGeometries; + } } void main() { @@ -126,6 +148,20 @@ void main() { await gantry.stop(extra: {'foo': 'bar'}); expect(gantry.extra, {'foo': 'bar'}); }); + + test('getKinematics', () async { + final kinematics = await gantry.getKinematics(); + expect(kinematics.format, KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA); + expect(kinematics.raw, [1, 2, 3]); + }); + + test('getGeometries', () async { + final geometries = await gantry.getGeometries(); + expect(geometries[0].whichGeometryType(), Geometry_GeometryType.box); + expect(geometries[0].box.dimsMm.x, 1); + expect(geometries[0].box.dimsMm.y, 2); + expect(geometries[0].box.dimsMm.z, 3); + }); }); group('Gantry RPC Tests', () { @@ -143,7 +179,11 @@ void main() { service = GantryService(manager); server = Server.create(services: [service]); await serveServerAtUnusedPort(server); - channel = ClientChannel('localhost', port: server.port!, options: const ChannelOptions(credentials: ChannelCredentials.insecure())); + channel = ClientChannel( + 'localhost', + port: server.port!, + options: const ChannelOptions(credentials: ChannelCredentials.insecure()), + ); }); tearDown(() async { @@ -218,9 +258,11 @@ void main() { final cmd = {'foo': 'bar'}; final client = GantryServiceClient(channel); - final resp = await client.doCommand(DoCommandRequest() - ..name = name - ..command = cmd.toStruct()); + final resp = await client.doCommand( + DoCommandRequest() + ..name = name + ..command = cmd.toStruct(), + ); expect(resp.result.toMap()['command'], cmd); }); @@ -228,11 +270,31 @@ void main() { expect(gantry.extra, null); final client = GantryServiceClient(channel); - await client.stop(StopRequest() - ..name = name - ..extra = {'foo': 'bar'}.toStruct()); + await client.stop( + StopRequest() + ..name = name + ..extra = {'foo': 'bar'}.toStruct(), + ); expect(gantry.extra, {'foo': 'bar'}); }); + + test('getKinematics', () async { + final client = GantryServiceClient(channel); + final request = GetKinematicsRequest()..name = name; + final response = await client.getKinematics(request); + expect(response.format, KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA); + expect(response.kinematicsData, [1, 2, 3]); + }); + + test('getGeometries', () async { + final client = GantryServiceClient(channel); + final request = GetGeometriesRequest()..name = name; + final response = await client.getGeometries(request); + expect(response.geometries[0].whichGeometryType(), Geometry_GeometryType.box); + expect(response.geometries[0].box.dimsMm.x, 1); + expect(response.geometries[0].box.dimsMm.y, 2); + expect(response.geometries[0].box.dimsMm.z, 3); + }); }); group('Gantry Client Tests', () { test('position', () async { @@ -292,6 +354,22 @@ void main() { await client.stop(extra: {'foo': 'bar'}); expect(gantry.extra, {'foo': 'bar'}); }); + + test('getKinematics', () async { + final client = GantryClient(name, channel); + final kinematics = await client.getKinematics(); + expect(kinematics.format, KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA); + expect(kinematics.raw, [1, 2, 3]); + }); + + test('getGeometries', () async { + final client = GantryClient(name, channel); + final geometries = await client.getGeometries(); + expect(geometries[0].whichGeometryType(), Geometry_GeometryType.box); + expect(geometries[0].box.dimsMm.x, 1); + expect(geometries[0].box.dimsMm.y, 2); + expect(geometries[0].box.dimsMm.z, 3); + }); }); }); }