diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensor.java b/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensor.java new file mode 100644 index 000000000..30b1edeab --- /dev/null +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensor.java @@ -0,0 +1,84 @@ +package com.viam.sdk.core.component.powersensor; + +import com.google.protobuf.Struct; +import com.viam.common.v1.Common; +import com.viam.sdk.core.component.Component; +import com.viam.sdk.core.resource.Resource; +import com.viam.sdk.core.resource.Subtype; +import com.viam.sdk.core.robot.RobotClient; + +import java.util.Map.Entry; + + +import java.util.Map; +import java.util.Optional; + +/** + * PowerSensor reports information about voltage, current and power. + */ +public abstract class PowerSensor extends Component { + public static final Subtype SUBTYPE = new Subtype( + Subtype.NAMESPACE_RDK, + Subtype.RESOURCE_TYPE_COMPONENT, + "power_sensor"); + + public PowerSensor(final String name) { + super(SUBTYPE, named(name)); + } + + /** + * Get the ResourceName of the component + * + * @param name the name of the component + * @return the component's ResourceName + */ + public static Common.ResourceName named(final String name) { + return Resource.named(SUBTYPE, name); + } + + /** + * Get the component with the provided name from the provided robot. + * + * @param robot the RobotClient + * @param name the name of the component + * @return the component + */ + public static PowerSensor fromRobot(final RobotClient robot, final String name) { + return robot.getResource(PowerSensor.class, named(name)); + } + + /** + * Return the voltage reading of a specified device and whether it is AC or DC. + * + * @return the pair where the first double representing the voltage reading in V, the second bool indicating whether the voltage is AC (`true`) or DC + * (`false`). + */ + public abstract Entry getVoltage(Optional extra); + + /** + * Return the current of a specified device and whether it is AC or DC. + * + * @return the pair where the first double representing the current reading in V, the second bool indicating whether the current is AC (`true`) or DC + * * (`false`). + */ + public abstract Entry getCurrent(Optional extra); + + /** + * Return the power reading in watts. + * + * @return the power reading in watts + */ + public abstract double getPower(Optional extra); + + + /** + * Get the measurements or readings that this power sensor provides. If a sensor is not configured correctly or fails to read a + * piece of data, it will not appear in the readings dictionary. + * + * @return The readings for the PowerSensor. Object should be of type Vector3, GeoPoint, Orientation, or any Value type. Includes voltage in volts (float), current in + * amperes (float), is_ac (bool), and power in watts (float). + */ + public abstract Map getReadings(Optional extra); + + +} \ No newline at end of file diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensorRPCClient.java b/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensorRPCClient.java new file mode 100644 index 000000000..3d51fd6a7 --- /dev/null +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensorRPCClient.java @@ -0,0 +1,66 @@ +package com.viam.sdk.core.component.powersensor; + +import com.google.protobuf.Struct; +import com.google.protobuf.Value; +import com.viam.common.v1.Common; +import com.viam.component.powersensor.v1.PowerSensorServiceGrpc; +import com.viam.component.powersensor.v1.Powersensor; +import com.viam.sdk.core.rpc.Channel; +import com.viam.sdk.core.util.Utils; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + +public class PowerSensorRPCClient extends PowerSensor { + private final PowerSensorServiceGrpc.PowerSensorServiceBlockingStub client; + + public PowerSensorRPCClient(final String name, final Channel chan) { + super(name); + final PowerSensorServiceGrpc.PowerSensorServiceBlockingStub client = PowerSensorServiceGrpc.newBlockingStub(chan); + if (chan.getCallCredentials().isPresent()) { + this.client = client.withCallCredentials(chan.getCallCredentials().get()); + } else { + this.client = client; + } + } + + @Override + public Struct doCommand(final Map command) { + return client.doCommand(Common.DoCommandRequest.newBuilder(). + setName(getName().getName()). + setCommand(Struct.newBuilder().putAllFields(command).build()). + build()).getResult(); + } + + @Override + public Entry getVoltage(Optional extra) { + Powersensor.GetVoltageRequest.Builder builder = com.viam.component.powersensor.v1.Powersensor.GetVoltageRequest.newBuilder().setName(this.getName().getName()); + extra.ifPresent(builder::setExtra); + Powersensor.GetVoltageResponse response = client.getVoltage(builder.build()); + return Map.entry(response.getVolts(), response.getIsAc()); + } + + @Override + public Map.Entry getCurrent(Optional extra) { + Powersensor.GetCurrentRequest.Builder builder = com.viam.component.powersensor.v1.Powersensor.GetCurrentRequest.newBuilder().setName(this.getName().getName()); + extra.ifPresent(builder::setExtra); + Powersensor.GetCurrentResponse response = client.getCurrent(builder.build()); + return Map.entry(response.getAmperes(), response.getIsAc()); + } + + @Override + public double getPower(Optional extra) { + Powersensor.GetPowerRequest.Builder builder = com.viam.component.powersensor.v1.Powersensor.GetPowerRequest.newBuilder().setName(this.getName().getName()); + extra.ifPresent(builder::setExtra); + return client.getPower(builder.build()).getWatts(); + } + + @Override + public Map getReadings(Optional extra) { + Common.GetReadingsRequest.Builder builder = Common.GetReadingsRequest.newBuilder().setName(this.getName().getName()); + extra.ifPresent(builder::setExtra); + return Utils.sensorReadingsValueToNative(client.getReadings(builder.build()).getReadingsMap()); + + } +} diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensorRPCService.java b/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensorRPCService.java new file mode 100644 index 000000000..abb506ec8 --- /dev/null +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/powersensor/PowerSensorRPCService.java @@ -0,0 +1,78 @@ +package com.viam.sdk.core.component.powersensor; + +import com.google.protobuf.Struct; +import com.google.protobuf.Value; +import com.viam.common.v1.Common; +import com.viam.component.powersensor.v1.PowerSensorServiceGrpc; +import com.viam.component.powersensor.v1.Powersensor; +import com.viam.sdk.core.resource.ResourceManager; +import com.viam.sdk.core.resource.ResourceRPCService; +import com.viam.sdk.core.util.Utils; +import io.grpc.stub.StreamObserver; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + +public class PowerSensorRPCService extends PowerSensorServiceGrpc.PowerSensorServiceImplBase implements ResourceRPCService { + + private final ResourceManager manager; + + public PowerSensorRPCService(ResourceManager manager) { + this.manager = manager; + } + + @Override + public Class getResourceClass() { + return PowerSensor.class; + } + + @Override + public ResourceManager getManager() { + return manager; + } + + + @Override + public void getVoltage(Powersensor.GetVoltageRequest request, StreamObserver responseObserver) { + PowerSensor powerSensor = getResource(PowerSensor.named(request.getName())); + Entry voltage = powerSensor.getVoltage(Optional.of(request.getExtra())); + responseObserver.onNext(com.viam.component.powersensor.v1.Powersensor.GetVoltageResponse.newBuilder().setVolts(voltage.getKey()).setIsAc(voltage.getValue()).build()); + responseObserver.onCompleted(); + } + + @Override + public void getCurrent(Powersensor.GetCurrentRequest request, StreamObserver responseObserver) { + PowerSensor powerSensor = getResource(PowerSensor.named(request.getName())); + Entry current = powerSensor.getCurrent(Optional.of(request.getExtra())); + responseObserver.onNext(com.viam.component.powersensor.v1.Powersensor.GetCurrentResponse.newBuilder().setAmperes(current.getKey()).setIsAc(current.getValue()).build()); + responseObserver.onCompleted(); + } + + @Override + public void getPower(Powersensor.GetPowerRequest request, StreamObserver responseObserver) { + PowerSensor powerSensor = getResource(PowerSensor.named(request.getName())); + double power = powerSensor.getPower(Optional.of(request.getExtra())); + responseObserver.onNext(com.viam.component.powersensor.v1.Powersensor.GetPowerResponse.newBuilder().setWatts(power).build()); + responseObserver.onCompleted(); + } + + @Override + public void getReadings(Common.GetReadingsRequest request, StreamObserver responseObserver) { + PowerSensor powerSensor = getResource(PowerSensor.named(request.getName())); + Map readings = Utils.sensorReadingsNativeToValue(powerSensor.getReadings(Optional.of(request.getExtra()))); + responseObserver.onNext(Common.GetReadingsResponse.newBuilder().putAllReadings(readings).build()); + responseObserver.onCompleted(); + } + @Override + public void doCommand(Common.DoCommandRequest request, + StreamObserver responseObserver) { + + PowerSensor powerSensor = getResource(PowerSensor.named(request.getName())); + Struct result = powerSensor.doCommand(request.getCommand().getFieldsMap()); + responseObserver.onNext(Common.DoCommandResponse.newBuilder().setResult(result).build()); + responseObserver.onCompleted(); + } + +} + diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/Sensor.java b/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/Sensor.java index 0ff1cf4fc..383dbd587 100644 --- a/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/Sensor.java +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/Sensor.java @@ -28,10 +28,6 @@ public Sensor(final String name) { super(SUBTYPE, named(name)); } - public Sensor(Subtype subtype, ResourceName name) { - super(subtype, name); - throw new UnsupportedOperationException(); - } /** * Get the ResourceName of the component diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/servo/Servo.java b/core/sdk/src/main/java/com/viam/sdk/core/component/servo/Servo.java new file mode 100644 index 000000000..1e0c489aa --- /dev/null +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/servo/Servo.java @@ -0,0 +1,94 @@ +package com.viam.sdk.core.component.servo; + +import com.google.protobuf.Struct; +import com.viam.common.v1.Common; +import com.viam.sdk.core.component.Component; +import com.viam.sdk.core.resource.Resource; +import com.viam.sdk.core.resource.Subtype; +import com.viam.sdk.core.robot.RobotClient; + + +/** + * Servo represents a physical servo + */ +public abstract class Servo extends Component { + public static final Subtype SUBTYPE = new Subtype( + Subtype.NAMESPACE_RDK, + Subtype.RESOURCE_TYPE_COMPONENT, + "servo"); + + public Servo(final String name) { + super(SUBTYPE, named(name)); + } + + /** + * Get the ResourceName of the component + * + * @param name the name of the component + * @return the component's ResourceName + */ + public static Common.ResourceName named(final String name) { + return Resource.named(SUBTYPE, name); + } + + + /** + * Get the component with the provided name from the provided robot. + * @param robot the RobotClient + * @param name the name of the component + * @return the component + */ + public static Servo fromRobot(final RobotClient robot, final String name) { + return robot.getResource(Servo.class, named(name)); + } + + /** + * Move the servo to the provided angle. + * @param angle the desired angle of the servo in degrees + */ + public abstract void move(int angle, Struct extra); + + /** + * Move the servo to the provided angle. + * @param angle the desired angle of the servo in degrees + */ + public void move(int angle){ + move(angle, Struct.getDefaultInstance()); + } + + /** + * Get the current angle (degrees) of the servo. + * @return the current angle of the servo in degrees + */ + public abstract int getPosition(Struct extra); + + /** + * Get the current angle (degrees) of the servo. + * @return the current angle of the servo in degrees + */ + public int getPosition(){ + return getPosition(Struct.getDefaultInstance()); + } + + /** + * Stop the servo. It is assumed that the servo stops immediately. + */ + public abstract void stop(Struct extra); + + /** + * Stop the servo. It is assumed that the servo stops immediately. + */ + public void stop(){ + stop(Struct.getDefaultInstance()); + } + + + /** + * Get if the servo is currently moving. + * @return whether the servo is moving + */ + public abstract boolean isMoving(); + + +} + diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/servo/ServoRPCClient.java b/core/sdk/src/main/java/com/viam/sdk/core/component/servo/ServoRPCClient.java new file mode 100644 index 000000000..c9e2feb10 --- /dev/null +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/servo/ServoRPCClient.java @@ -0,0 +1,68 @@ +package com.viam.sdk.core.component.servo; + +import com.google.protobuf.Struct; +import com.google.protobuf.Value; +import com.viam.common.v1.Common; +import com.viam.component.servo.v1.ServoServiceGrpc; + +import com.viam.sdk.core.rpc.Channel; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class ServoRPCClient extends Servo{ + private final ServoServiceGrpc.ServoServiceBlockingStub client; + + public ServoRPCClient(final String name, final Channel chan) { + super(name); + final ServoServiceGrpc.ServoServiceBlockingStub client = ServoServiceGrpc.newBlockingStub(chan); + if (chan.getCallCredentials().isPresent()) { + this.client = client.withCallCredentials(chan.getCallCredentials().get()); + } else { + this.client = client; + } + } + + @Override + public Struct doCommand(final Map command) { + return client.doCommand(Common.DoCommandRequest.newBuilder(). + setName(getName().getName()). + setCommand(Struct.newBuilder().putAllFields(command).build()). + build()).getResult(); + } + + @Override + public void move(int angle, Struct extra) { + com.viam.component.servo.v1.Servo.MoveRequest moveRequest = com.viam.component.servo.v1.Servo.MoveRequest.newBuilder().setName(this.getName().getName()).setAngleDeg(angle).setExtra(extra).build(); + client.move(moveRequest); + } + + @Override + public int getPosition(Struct extra) { + com.viam.component.servo.v1.Servo.GetPositionRequest getPositionRequest = com.viam.component.servo.v1.Servo.GetPositionRequest.newBuilder().setName(this.getName().getName()).setExtra(extra).build(); + return client.getPosition(getPositionRequest).getPositionDeg(); + } + + + @Override + public void stop(Struct extra) { + com.viam.component.servo.v1.Servo.StopRequest stopRequest = com.viam.component.servo.v1.Servo.StopRequest.newBuilder().setName(this.getName().getName()).setExtra(extra).build(); + client.stop(stopRequest); + } + + @Override + public boolean isMoving() { + com.viam.component.servo.v1.Servo.IsMovingRequest isMovingRequest = com.viam.component.servo.v1.Servo.IsMovingRequest.newBuilder().setName(this.getName().getName()).build(); + return client.isMoving(isMovingRequest).getIsMoving(); + } + + @Override + public List getGeometries(Optional extra){ + Common.GetGeometriesRequest.Builder builder = Common.GetGeometriesRequest.newBuilder().setName(this.getName().getName()); + extra.ifPresent(builder::setExtra); + return client.getGeometries(builder.build()).getGeometriesList(); + + } +} + diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/servo/ServoRPCService.java b/core/sdk/src/main/java/com/viam/sdk/core/component/servo/ServoRPCService.java new file mode 100644 index 000000000..5edbefb6c --- /dev/null +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/servo/ServoRPCService.java @@ -0,0 +1,76 @@ +package com.viam.sdk.core.component.servo; + import com.google.protobuf.Struct; + import com.viam.common.v1.Common; + import com.viam.component.servo.v1.ServoServiceGrpc; + import com.viam.sdk.core.resource.ResourceRPCService; + import com.viam.sdk.core.resource.ResourceManager; + import io.grpc.stub.StreamObserver; + + import java.util.List; + import java.util.Optional; + + +public class ServoRPCService extends ServoServiceGrpc.ServoServiceImplBase implements ResourceRPCService { + + private final ResourceManager manager; + public ServoRPCService(ResourceManager manager) { this.manager = manager; } + @Override + public Class getResourceClass() { + return Servo.class; + } + + @Override + public ResourceManager getManager() { + return manager; + } + + @Override + public void move(com.viam.component.servo.v1.Servo.MoveRequest request, StreamObserver responseObserver) { + Servo servo = getResource(Servo.named(request.getName())); + servo.move(request.getAngleDeg(), request.getExtra()); + responseObserver.onNext(com.viam.component.servo.v1.Servo.MoveResponse.newBuilder().build()); + responseObserver.onCompleted(); + } + + @Override + public void stop(com.viam.component.servo.v1.Servo.StopRequest request, StreamObserver responseObserver) { + Servo servo = getResource(Servo.named(request.getName())); + servo.stop(); + responseObserver.onNext(com.viam.component.servo.v1.Servo.StopResponse.newBuilder().build()); + responseObserver.onCompleted(); + } + + @Override + public void getPosition(com.viam.component.servo.v1.Servo.GetPositionRequest request, StreamObserver responseObserver) { + Servo servo = getResource(Servo.named(request.getName())); + int position = servo.getPosition(request.getExtra()); + responseObserver.onNext(com.viam.component.servo.v1.Servo.GetPositionResponse.newBuilder().setPositionDeg(position).build()); + responseObserver.onCompleted(); + } + + @Override + public void isMoving(com.viam.component.servo.v1.Servo.IsMovingRequest request, StreamObserver responseObserver) { + Servo servo = getResource(Servo.named(request.getName())); + boolean isMoving = servo.isMoving(); + responseObserver.onNext(com.viam.component.servo.v1.Servo.IsMovingResponse.newBuilder().setIsMoving(isMoving).build()); + responseObserver.onCompleted(); + } + + @Override + public void getGeometries(com.viam.common.v1.Common.GetGeometriesRequest request, + StreamObserver responseObserver){ + Servo servo = getResource(Servo.named(request.getName())); + List geometries = servo.getGeometries(Optional.ofNullable(request.getExtra())); + responseObserver.onNext(Common.GetGeometriesResponse.newBuilder().addAllGeometries(geometries).build()); + responseObserver.onCompleted(); + } + @Override + public void doCommand(Common.DoCommandRequest request, + StreamObserver responseObserver) { + + Servo servo = getResource(Servo.named(request.getName())); + Struct result = servo.doCommand(request.getCommand().getFieldsMap()); + responseObserver.onNext(Common.DoCommandResponse.newBuilder().setResult(result).build()); + responseObserver.onCompleted(); + } +} diff --git a/core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java b/core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java index a2085aa87..7d4d383a9 100644 --- a/core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java +++ b/core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java @@ -2,14 +2,20 @@ import com.viam.common.v1.Common; import com.viam.common.v1.Common.ResourceName; +import com.viam.component.arm.v1.ArmServiceGrpc; import com.viam.component.audioinput.v1.AudioInputServiceGrpc; import com.viam.component.board.v1.BoardServiceGrpc; import com.viam.component.camera.v1.CameraServiceGrpc; +import com.viam.component.encoder.v1.EncoderServiceGrpc; +import com.viam.component.gantry.v1.GantryServiceGrpc; import com.viam.component.generic.v1.GenericServiceGrpc; import com.viam.component.gripper.v1.GripperServiceGrpc; import com.viam.component.motor.v1.MotorServiceGrpc; import com.viam.component.movementsensor.v1.MovementSensorServiceGrpc; +import com.viam.component.powersensor.v1.PowerSensorServiceGrpc; import com.viam.component.sensor.v1.SensorServiceGrpc; +import com.viam.sdk.core.component.arm.*; +import com.viam.component.servo.v1.ServoServiceGrpc; import com.viam.sdk.core.component.audioinput.*; import com.viam.sdk.core.component.board.Board; import com.viam.sdk.core.component.board.BoardRPCClient; @@ -17,6 +23,10 @@ import com.viam.sdk.core.component.camera.Camera; import com.viam.sdk.core.component.camera.CameraRPCClient; import com.viam.sdk.core.component.camera.CameraRPCService; +import com.viam.sdk.core.component.encoder.*; +import com.viam.sdk.core.component.gantry.Gantry; +import com.viam.sdk.core.component.gantry.GantryRPCClient; +import com.viam.sdk.core.component.gantry.GantryRPCService; import com.viam.sdk.core.component.generic.Generic; import com.viam.sdk.core.component.generic.GenericRPCClient; import com.viam.sdk.core.component.generic.GenericRPCService; @@ -29,9 +39,15 @@ import com.viam.sdk.core.component.movementsensor.MovementSensor; import com.viam.sdk.core.component.movementsensor.MovementSensorRPCClient; import com.viam.sdk.core.component.movementsensor.MovementSensorRPCService; +import com.viam.sdk.core.component.powersensor.PowerSensor; +import com.viam.sdk.core.component.powersensor.PowerSensorRPCClient; +import com.viam.sdk.core.component.powersensor.PowerSensorRPCService; import com.viam.sdk.core.component.sensor.Sensor; import com.viam.sdk.core.component.sensor.SensorRPCClient; import com.viam.sdk.core.component.sensor.SensorRPCService; +import com.viam.sdk.core.component.servo.Servo; +import com.viam.sdk.core.component.servo.ServoRPCClient; +import com.viam.sdk.core.component.servo.ServoRPCService; import com.viam.sdk.core.exception.DuplicateResourceException; import com.viam.sdk.core.exception.ResourceNotFoundException; import com.viam.sdk.core.service.datamanager.DataManager; @@ -97,6 +113,37 @@ public class ResourceManager implements Closeable { SensorRPCService::new, SensorRPCClient::new )); + Registry.registerSubtype(new ResourceRegistration<>( + Encoder.SUBTYPE, + EncoderServiceGrpc.SERVICE_NAME, + EncoderRPCService::new, + EncoderRPCClient::new + )); + Registry.registerSubtype(new ResourceRegistration<>( + PowerSensor.SUBTYPE, + PowerSensorServiceGrpc.SERVICE_NAME, + PowerSensorRPCService::new, + PowerSensorRPCClient::new + )); + Registry.registerSubtype(new ResourceRegistration<>( + Arm.SUBTYPE, + ArmServiceGrpc.SERVICE_NAME, + ArmRPCService::new, + ArmRPCClient::new + )); + Registry.registerSubtype(new ResourceRegistration<>( + Servo.SUBTYPE, + ServoServiceGrpc.SERVICE_NAME, + ServoRPCService::new, + ServoRPCClient::new + )); + + Registry.registerSubtype(new ResourceRegistration<>( + Gantry.SUBTYPE, + GantryServiceGrpc.SERVICE_NAME, + GantryRPCService::new, + GantryRPCClient::new + )); Registry.registerSubtype(new ResourceRegistration<>( AudioInput.SUBTYPE, diff --git a/core/sdk/src/main/java/com/viam/sdk/core/util/Utils.java b/core/sdk/src/main/java/com/viam/sdk/core/util/Utils.java new file mode 100644 index 000000000..766e6b8af --- /dev/null +++ b/core/sdk/src/main/java/com/viam/sdk/core/util/Utils.java @@ -0,0 +1,131 @@ +package com.viam.sdk.core.util; + +import com.google.protobuf.*; +import com.viam.common.v1.Common; + +import java.util.*; + +public class Utils { + public static Object valueToNative(Value v) { + switch (v.getKindCase()) { + case STRUCT_VALUE: { + Map structMap = v.getStructValue().getFieldsMap(); + if (structMap.get("_type") != null) { + switch (structMap.get("_type").getStringValue()) { + case ("vector3"): { + return Common.Vector3.newBuilder().setX(structMap.get("x").getNumberValue()).setY(structMap.get("y").getNumberValue()).setZ(structMap.get("z").getNumberValue()).build(); + } + case ("geopoint"): { + return Common.GeoPoint.newBuilder().setLatitude(structMap.get("lat").getNumberValue()).setLongitude(structMap.get("lng").getNumberValue()).build(); + } + case ("orientation_vector_degrees"): { + return Common.Orientation.newBuilder().setOX(structMap.get("ox").getNumberValue()).setOY(structMap.get("oy").getNumberValue()).setOZ(structMap.get("oz").getNumberValue()).setTheta(structMap.get("theta").getNumberValue()).build(); + } + } + + } else { + Map nativeMap = new HashMap<>(); + for (Map.Entry entry : structMap.entrySet()) { + nativeMap.put(entry.getKey(), valueToNative(entry.getValue())); + } + return nativeMap; + + } + + + } + case LIST_VALUE: { + List list = new ArrayList<>(); + for (Value val : v.getListValue().getValuesList()) { + list.add(valueToNative(val)); + } + return list; + + } + case BOOL_VALUE: + return v.getBoolValue(); + case NUMBER_VALUE: + return v.getNumberValue(); + case STRING_VALUE: + return v.getStringValue(); + case NULL_VALUE: + return v.getNullValue(); + default: + return v; + } + + + } + + public static Value nativeToValue(Object obj) throws IllegalArgumentException { + if (obj instanceof Common.Vector3) { + Struct struct = Struct.newBuilder() + .putFields("x", Value.newBuilder().setNumberValue(((Common.Vector3) obj).getX()).build()) + .putFields("y", Value.newBuilder().setNumberValue(((Common.Vector3) obj).getY()).build()) + .putFields("z", Value.newBuilder().setNumberValue(((Common.Vector3) obj).getZ()).build()) + .putFields("_type", Value.newBuilder().setStringValue("vector3").build()) + .build(); + return Value.newBuilder().setStructValue(struct).build(); + + } else if (obj instanceof Common.GeoPoint) { + Struct struct = Struct.newBuilder() + .putFields("lat", Value.newBuilder().setNumberValue(((Common.GeoPoint) obj).getLatitude()).build()) + .putFields("lng", Value.newBuilder().setNumberValue(((Common.GeoPoint) obj).getLongitude()).build()) + .putFields("_type", Value.newBuilder().setStringValue("geopoint").build()) + .build(); + return Value.newBuilder().setStructValue(struct).build(); + } else if (obj instanceof Common.Orientation) { + Struct struct = Struct.newBuilder() + .putFields("ox", Value.newBuilder().setNumberValue(((Common.Orientation) obj).getOX()).build()) + .putFields("oy", Value.newBuilder().setNumberValue(((Common.Orientation) obj).getOY()).build()) + .putFields("oz", Value.newBuilder().setNumberValue(((Common.Orientation) obj).getOZ()).build()) + .putFields("theta", Value.newBuilder().setNumberValue(((Common.Orientation) obj).getTheta()).build()) + .putFields("_type", Value.newBuilder().setStringValue("orientation_vector_degrees").build()) + .build(); + return Value.newBuilder().setStructValue(struct).build(); + + } else if (obj instanceof Number) { + return Value.newBuilder().setNumberValue((double) obj).build(); + } else if (obj instanceof String) { + return Value.newBuilder().setStringValue((String) obj).build(); + } else if(obj instanceof byte[]){ + return Value.newBuilder().setStringValue(new String((byte[]) obj)).build(); + }else if (obj instanceof Boolean) { + return Value.newBuilder().setBoolValue((Boolean) obj).build(); + } else if (obj instanceof List) { + ListValue.Builder listBuilder = ListValue.newBuilder(); + for (Object o : (List) obj) { + listBuilder.addValues(nativeToValue(o)); + } + return Value.newBuilder().setListValue(listBuilder.build()).build(); + } else if (obj instanceof Map) { + Struct.Builder structBuilder = Struct.newBuilder(); + for (Map.Entry entry : ((Map) obj).entrySet()) { + if(!(entry.getKey() instanceof String)) throw new IllegalArgumentException(); + else structBuilder.putFields((String) entry.getKey(), nativeToValue(entry.getValue())); + } + return Value.newBuilder().setStructValue(structBuilder.build()).build(); + + } + else throw new IllegalArgumentException(); + } + + public static Map sensorReadingsNativeToValue(Map readings) { + Map valueMap = new HashMap<>(); + for (Map.Entry entry : readings.entrySet()) { + valueMap.put(entry.getKey(), nativeToValue(entry.getValue())); + } + return valueMap; + } + + public static Map sensorReadingsValueToNative(Map readings) { + Map map = new HashMap<>(); + for (Map.Entry entry : readings.entrySet()) { + map.put(entry.getKey(), valueToNative(entry.getValue())); + + } + return map; + } + + +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/Arm.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/Arm.kt new file mode 100644 index 000000000..fe24501ff --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/Arm.kt @@ -0,0 +1,158 @@ +package com.viam.sdk.core.component.arm + +import com.google.protobuf.ByteString +import com.google.protobuf.Struct +import com.viam.common.v1.Common.* +import com.viam.component.arm.v1.Arm.JointPositions +import com.viam.sdk.core.component.Component +import com.viam.sdk.core.component.arm.Arm +import com.viam.sdk.core.resource.Resource +import com.viam.sdk.core.resource.Subtype +import com.viam.sdk.core.robot.RobotClient + +/** + * An Arm represents a physical robot arm that exists in three-dimensional space. + */ +abstract class Arm(name: String) : Component(SUBTYPE, named(name)) { + companion object { + @JvmField + val SUBTYPE = Subtype(Subtype.NAMESPACE_RDK, Subtype.RESOURCE_TYPE_COMPONENT, "arm") + + /** + * Get the ResourceName of the component + * @param name the name of the component + * @return the component's ResourceName + */ + @JvmStatic + fun named(name: String): ResourceName { + return Resource.named(SUBTYPE, name) + } + + /** + * Get the component with the provided name from the provided robot. + * @param robot the RobotClient + * @param name the name of the component + * @return the component + */ + @JvmStatic + fun fromRobot(robot: RobotClient, name: String): Arm { + return robot.getResource(Arm::class.java, named(name)) + } + } + + /** + * Get the current position of the end of the arm expressed as a ``Pose``. + * @return A representation of the arm's current position as a 6 DOF (six degrees of freedom) pose. + * The ``Pose`` is composed of values for location and orientation with respect to the origin. + * Location is expressed as distance, which is represented by x, y, and z coordinate values. + * Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. + */ + abstract fun getEndPosition(extra: Struct): Pose + + /** + * Get the current position of the end of the arm expressed as a ``Pose``. + * @return A representation of the arm's current position as a 6 DOF (six degrees of freedom) pose. + * The ``Pose`` is composed of values for location and orientation with respect to the origin. + * Location is expressed as distance, which is represented by x, y, and z coordinate values. + * Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. + */ + fun getEndPosition(): Pose { + return getEndPosition(Struct.getDefaultInstance()) + } + + /** + * Move the end of the arm to the Pose specified in ``pose``. + * @param pose The destination ``Pose`` for the arm. The ``Pose`` is composed of values for location and orientation + * with respect to the origin. + * Location is expressed as distance, which is represented by x, y, and z coordinate values. + * Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. + + */ + abstract fun moveToPosition(pose: Pose, extra: Struct) + + /** + * Move the end of the arm to the Pose specified in ``pose``. + * @param pose The destination ``Pose`` for the arm. The ``Pose`` is composed of values for location and orientation + * with respect to the origin. + * Location is expressed as distance, which is represented by x, y, and z coordinate values. + * Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. + + */ + fun moveToPosition(pose: Pose) { + moveToPosition(pose, Struct.getDefaultInstance()) + } + + /** + * Move each joint on the arm to the corresponding angle specified in ``positions``. + * @param positions the destination ``JointPositions`` for the arm + */ + abstract fun moveToJointPositions(positions: JointPositions, extra: Struct) + + /** + * Move each joint on the arm to the corresponding angle specified in ``positions``. + * @param positions the destination ``JointPositions`` for the arm + */ + fun moveToJointPositions(positions: JointPositions) { + return moveToJointPositions(positions, Struct.getDefaultInstance()) + } + + /** + * Get the JointPositions representing the current position of the arm. + * @return The current ``JointPositions`` for the arm. + * ``JointPositions`` can have one attribute, ``values``, a list of joint positions with rotational values (degrees) + * and translational values (mm). + */ + abstract fun getJointPositions(extra: Struct): JointPositions + + /** + * Get the JointPositions representing the current position of the arm. + * @return The current ``JointPositions`` for the arm. + * ``JointPositions`` can have one attribute, ``values``, a list of joint positions with rotational values (degrees) + * and translational values (mm). + */ + fun getJointPositions(): JointPositions { + return getJointPositions(Struct.getDefaultInstance()) + } + + + /** + * Stop all motion of the arm. It is assumed that the arm stops immediately. + */ + abstract fun stop(extra: Struct) + + /** + * Stop all motion of the arm. It is assumed that the arm stops immediately. + */ + fun stop() { + stop(Struct.getDefaultInstance()) + } + + /** + * Get if the arm is currently moving. + * @return whether the arm is moving + */ + abstract fun isMoving(): Boolean + + + /** + * Get the kinematics information associated with the arm. + * @return A pair containing two values; the first [0] value represents the format of the + * file, either in URDF format (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_URDF``) or + * Viam's kinematic parameter format (spatial vector algebra) (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA``), + * and the second [1] value represents the byte contents of the file. + */ + abstract fun getKinematics(extra: Struct): Pair + + /** + * Get the kinematics information associated with the arm. + * @return A pair containing two values; the first [0] value represents the format of the + * file, either in URDF format (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_URDF``) or + * Viam's kinematic parameter format (spatial vector algebra) (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA``), + * and the second [1] value represents the byte contents of the file. + */ + fun getKinematics(): Pair { + return getKinematics(Struct.getDefaultInstance()) + } + + +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/ArmRPCClient.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/ArmRPCClient.kt new file mode 100644 index 000000000..32fa797be --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/ArmRPCClient.kt @@ -0,0 +1,83 @@ +package com.viam.sdk.core.component.arm + +import com.google.protobuf.ByteString +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.GetGeometriesRequest +import com.viam.common.v1.Common.GetKinematicsRequest +import com.viam.component.arm.v1.Arm.* +import com.viam.component.arm.v1.ArmServiceGrpc +import com.viam.component.arm.v1.ArmServiceGrpc.ArmServiceBlockingStub +import com.viam.sdk.core.rpc.Channel +import java.util.* +import kotlin.jvm.optionals.getOrDefault + +class ArmRPCClient(name: String, channel: Channel) : Arm(name) { + private val client: ArmServiceBlockingStub + + init { + val client = ArmServiceGrpc.newBlockingStub(channel) + if (channel.callCredentials.isPresent) { + this.client = client.withCallCredentials(channel.callCredentials.get()) + } else { + this.client = client + } + } + + override fun getEndPosition(extra: Struct): Common.Pose { + val request = GetEndPositionRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + val response = this.client.getEndPosition(request) + return response.pose + } + + override fun moveToPosition(pose: Common.Pose, extra: Struct) { + val request = MoveToPositionRequest.newBuilder().setName(this.name.name).setTo(pose).setExtra(extra).build() + this.client.moveToPosition(request) + + } + + override fun moveToJointPositions(positions: JointPositions, extra: Struct) { + val request = + MoveToJointPositionsRequest.newBuilder().setName(this.name.name).setPositions(positions).setExtra(extra) + .build() + this.client.moveToJointPositions(request) + } + + override fun getJointPositions(extra: Struct): JointPositions { + val request = GetJointPositionsRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + val response = this.client.getJointPositions(request) + return response.positions + } + + override fun stop(extra: Struct) { + val request = StopRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + this.client.stop(request) + } + + override fun isMoving(): Boolean { + val request = IsMovingRequest.newBuilder().setName(this.name.name).build() + val response = this.client.isMoving(request) + return response.isMoving + } + + override fun getKinematics(extra: Struct): Pair { + val request = GetKinematicsRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + val response = this.client.getKinematics(request) + return (response.format to response.kinematicsData) + } + + override fun doCommand(command: Map?): Struct { + val request = Common.DoCommandRequest.newBuilder().setName(this.name.name) + .setCommand(Struct.newBuilder().putAllFields(command).build()).build() + val response = this.client.doCommand(request) + return response.result + } + + override fun getGeometries(extra: Optional): List { + val request = GetGeometriesRequest.newBuilder().setName(this.name.name) + .setExtra(extra.getOrDefault(Struct.getDefaultInstance())).build() + val response = this.client.getGeometries(request) + return response.geometriesList + } +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/ArmRPCService.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/ArmRPCService.kt new file mode 100644 index 000000000..b4d593702 --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/arm/ArmRPCService.kt @@ -0,0 +1,111 @@ +package com.viam.sdk.core.component.arm + +import com.viam.common.v1.Common.* +import com.viam.component.arm.v1.Arm.* +import com.viam.component.arm.v1.ArmServiceGrpc +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.resource.ResourceRPCService +import io.grpc.stub.StreamObserver +import java.util.* + +internal class ArmRPCService(private val manager: ResourceManager) : ArmServiceGrpc.ArmServiceImplBase(), + ResourceRPCService { + override fun getEndPosition( + request: GetEndPositionRequest, + responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + val endPosition = arm.getEndPosition(request.extra) + responseObserver.onNext(GetEndPositionResponse.newBuilder().setPose(endPosition).build()) + responseObserver.onCompleted() + } + + override fun moveToPosition( + request: MoveToPositionRequest, + responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + arm.moveToPosition(request.to, request.extra) + responseObserver.onNext(MoveToPositionResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun getJointPositions( + request: GetJointPositionsRequest, + responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + val positions = arm.getJointPositions(request.extra) + responseObserver.onNext(GetJointPositionsResponse.newBuilder().setPositions(positions).build()) + responseObserver.onCompleted() + } + + override fun moveToJointPositions( + request: MoveToJointPositionsRequest, + responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + arm.moveToJointPositions(request.positions, request.extra) + responseObserver.onNext(MoveToJointPositionsResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun getKinematics( + request: GetKinematicsRequest, + responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + val kinematics = arm.getKinematics(request.extra) + responseObserver.onNext( + GetKinematicsResponse.newBuilder().setFormat(kinematics.first).setKinematicsData(kinematics.second).build() + ) + responseObserver.onCompleted() + } + + override fun stop( + request: StopRequest, responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + arm.stop(request.extra) + responseObserver.onNext(StopResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + + override fun isMoving( + request: IsMovingRequest, responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + val isMoving = arm.isMoving() + responseObserver.onNext( + IsMovingResponse.newBuilder().setIsMoving(isMoving).build() + ) + responseObserver.onCompleted() + } + + override fun doCommand( + request: DoCommandRequest, responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + val result = arm.doCommand(request.command.fieldsMap) + responseObserver.onNext(DoCommandResponse.newBuilder().setResult(result).build()) + responseObserver.onCompleted() + } + + override fun getGeometries( + request: GetGeometriesRequest, responseObserver: StreamObserver + ) { + val arm = getResource(Arm.named(request.name)) + val result = arm.getGeometries(Optional.of(request.extra)) + responseObserver.onNext(GetGeometriesResponse.newBuilder().addAllGeometries(result).build()) + responseObserver.onCompleted() + } + + override fun getResourceClass(): Class { + return Arm::class.java + } + + override fun getManager(): ResourceManager { + return this.manager + } +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/Encoder.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/Encoder.kt new file mode 100644 index 000000000..80b2607ff --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/Encoder.kt @@ -0,0 +1,133 @@ +package com.viam.sdk.core.component.encoder + +import com.google.protobuf.Struct +import com.viam.common.v1.Common.ResourceName +import com.viam.component.encoder.v1.Encoder.GetPropertiesResponse +import com.viam.component.encoder.v1.Encoder.PositionType +import com.viam.sdk.core.component.Component +import com.viam.sdk.core.resource.Resource +import com.viam.sdk.core.resource.Subtype +import com.viam.sdk.core.robot.RobotClient + +typealias Properties = GetPropertiesResponse + + +/** + * An [Encoder] represents a physical encoder. + */ +abstract class Encoder(name: String) : Component(SUBTYPE, named(name)) { + + companion object { + @JvmField + val SUBTYPE = Subtype(Subtype.NAMESPACE_RDK, Subtype.RESOURCE_TYPE_COMPONENT, "encoder") + + /** + * Get the ResourceName of the component + * @param name the name of the component + * @return the component's ResourceName + */ + @JvmStatic + fun named(name: String): ResourceName { + return Resource.named(SUBTYPE, name) + } + + /** + * Get the component with the provided name from the provided robot. + * @param robot the RobotClient + * @param name the name of the component + * @return the component + */ + @JvmStatic + fun fromRobot(robot: RobotClient, name: String): Encoder { + return robot.getResource(Encoder::class.java, named(name)) + } + } + + /** + * Set the current position to be the new zero (home) position. + */ + abstract fun resetPosition(extra: Struct) + + /** + * Set the current position to be the new zero (home) position. + */ + fun resetPosition() { + resetPosition(Struct.getDefaultInstance()) + } + + /** + * Report the position of the encoder. + * The value returned is the current position in terms of its ``position_type``. + * If no position type is given, the supported position type will be returned. + * The position will be either in relative units (ticks away from a zero position) for + * ``PositionType.POSITION_TYPE_TICKS_COUNT`` or absolute units (degrees along a circle) + * for ``PositionType.POSITION_TYPE_ANGLE_DEGREES``. + * @param positionType The desired output type of the position. + * @return A tuple containing two values; the first [0] the position of the encoder which can either be + * ticks since last zeroing for a relative encoder or degrees for an absolute encoder, and the second [1] the type of + * position the encoder returns (ticks or degrees). + * + */ + abstract fun getPosition(positionType: PositionType?, extra: Struct): Pair + + /** + * Report the position of the encoder. + * The value returned is the current position in terms of its ``position_type``. + * If no position type is given, the supported position type will be returned. + * The position will be either in relative units (ticks away from a zero position) for + * ``PositionType.POSITION_TYPE_TICKS_COUNT`` or absolute units (degrees along a circle) + * for ``PositionType.POSITION_TYPE_ANGLE_DEGREES``. + * @return A tuple containing two values; the first [0] the position of the encoder which can either be + * ticks since last zeroing for a relative encoder or degrees for an absolute encoder, and the second [1] the type of + * position the encoder returns (ticks or degrees). + */ + fun getPosition(positionType: PositionType): Pair { + return getPosition(positionType, Struct.getDefaultInstance()) + } + + /** + * Report the position of the encoder. + * The value returned is the current position in terms of its ``position_type``. + * If no position type is given, the supported position type will be returned. + * The position will be either in relative units (ticks away from a zero position) for + * ``PositionType.POSITION_TYPE_TICKS_COUNT`` or absolute units (degrees along a circle) + * for ``PositionType.POSITION_TYPE_ANGLE_DEGREES``. + * @return A tuple containing two values; the first [0] the position of the encoder which can either be + * ticks since last zeroing for a relative encoder or degrees for an absolute encoder, and the second [1] the type of + * position the encoder returns (ticks or degrees). + * + */ + fun getPosition(extra: Struct): Pair { + return getPosition(null, extra) + } + + /** + * Report the position of the encoder. + * The value returned is the current position in terms of its ``position_type``. + * If no position type is given, the supported position type will be returned. + * The position will be either in relative units (ticks away from a zero position) for + * ``PositionType.POSITION_TYPE_TICKS_COUNT`` or absolute units (degrees along a circle) + * for ``PositionType.POSITION_TYPE_ANGLE_DEGREES``. + * @return A tuple containing two values; the first [0] the position of the encoder which can either be + * ticks since last zeroing for a relative encoder or degrees for an absolute encoder, and the second [1] the type of + * position the encoder returns (ticks or degrees). + */ + fun getPosition(): Pair { + return getPosition(null, Struct.getDefaultInstance()) + } + + + /** + * Return a dictionary of the types of position reporting this encoder supports. + * @return a map of position types to supported status. + */ + abstract fun getProperties(extra: Struct): Properties + + /** + * Return a dictionary of the types of position reporting this encoder supports. + * @return a map of position types to supported status. + */ + fun getProperties(): Properties { + return getProperties(Struct.getDefaultInstance()) + } +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCClient.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCClient.kt new file mode 100644 index 000000000..ab9272b05 --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCClient.kt @@ -0,0 +1,59 @@ +package com.viam.sdk.core.component.encoder + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.GetGeometriesRequest +import com.viam.component.encoder.v1.Encoder.* +import com.viam.component.encoder.v1.EncoderServiceGrpc +import com.viam.component.encoder.v1.EncoderServiceGrpc.EncoderServiceBlockingStub +import com.viam.sdk.core.rpc.Channel +import java.util.* +import kotlin.jvm.optionals.getOrDefault + +class EncoderRPCClient(name: String, channel: Channel) : Encoder(name) { + private val client: EncoderServiceBlockingStub + + init { + val client = EncoderServiceGrpc.newBlockingStub(channel) + if (channel.callCredentials.isPresent) { + this.client = client.withCallCredentials(channel.callCredentials.get()) + } else { + this.client = client + } + } + + override fun resetPosition(extra: Struct) { + val request = ResetPositionRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + this.client.resetPosition(request) + } + + override fun getPosition(positionType: PositionType?, extra: Struct): Pair { + val request = GetPositionRequest.newBuilder().setName(this.name.name).setExtra(extra) + if (positionType != null) { + request.setPositionType(positionType) + } + val response = this.client.getPosition(request.build()) + return (response.value to response.positionType) + + } + + override fun getProperties(extra: Struct): Properties { + val request = GetPropertiesRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + return this.client.getProperties(request) + } + + override fun doCommand(command: Map?): Struct { + val request = Common.DoCommandRequest.newBuilder().setName(this.name.name) + .setCommand(Struct.newBuilder().putAllFields(command).build()).build() + val response = this.client.doCommand(request) + return response.result + } + + override fun getGeometries(extra: Optional): List { + val request = GetGeometriesRequest.newBuilder().setName(this.name.name) + .setExtra(extra.getOrDefault(Struct.getDefaultInstance())).build() + val response = this.client.getGeometries(request) + return response.geometriesList + } +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCService.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCService.kt new file mode 100644 index 000000000..03742a02c --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCService.kt @@ -0,0 +1,75 @@ +package com.viam.sdk.core.component.encoder + +import com.viam.common.v1.Common.* +import com.viam.component.encoder.v1.Encoder.* +import com.viam.component.encoder.v1.EncoderServiceGrpc +import com.viam.sdk.core.component.encoder.Encoder +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.resource.ResourceRPCService +import io.grpc.stub.StreamObserver +import java.util.* + +internal class EncoderRPCService(private val manager: ResourceManager) : EncoderServiceGrpc.EncoderServiceImplBase(), + ResourceRPCService { + + override fun resetPosition( + request: ResetPositionRequest, + responseObserver: StreamObserver + ) { + val encoder = getResource(Encoder.named(request.name)) + encoder.resetPosition(request.extra) + responseObserver.onNext(ResetPositionResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun getPosition(request: GetPositionRequest, responseObserver: StreamObserver) { + val encoder = getResource(Encoder.named(request.name)) + val position: Pair = if (request.hasPositionType()) + encoder.getPosition(request.positionType, request.extra) + else + encoder.getPosition(request.extra) + responseObserver.onNext( + GetPositionResponse.newBuilder().setPositionType(position.second).setValue(position.first).build() + ) + responseObserver.onCompleted() + } + + override fun getProperties( + request: GetPropertiesRequest, + responseObserver: StreamObserver + ) { + val encoder = getResource(Encoder.named(request.name)) + val properties = encoder.getProperties(request.extra) + responseObserver.onNext( + GetPropertiesResponse.newBuilder().setTicksCountSupported(properties.ticksCountSupported) + .setAngleDegreesSupported(properties.angleDegreesSupported).build() + ) + responseObserver.onCompleted() + } + + override fun doCommand( + request: DoCommandRequest, responseObserver: StreamObserver + ) { + val encoder = getResource(Encoder.named(request.name)) + val result = encoder.doCommand(request.command.fieldsMap) + responseObserver.onNext(DoCommandResponse.newBuilder().setResult(result).build()) + responseObserver.onCompleted() + } + + override fun getGeometries( + request: GetGeometriesRequest, responseObserver: StreamObserver + ) { + val encoder = getResource(Encoder.named(request.name)) + val result = encoder.getGeometries(Optional.of(request.extra)) + responseObserver.onNext(GetGeometriesResponse.newBuilder().addAllGeometries(result).build()) + responseObserver.onCompleted() + } + + override fun getResourceClass(): Class { + return Encoder::class.java + } + + override fun getManager(): ResourceManager { + return this.manager + } +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/Gantry.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/Gantry.kt new file mode 100644 index 000000000..589e9d12c --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/Gantry.kt @@ -0,0 +1,112 @@ +package com.viam.sdk.core.component.gantry + +import com.google.protobuf.Struct +import com.viam.common.v1.Common.ResourceName +import com.viam.sdk.core.component.Component +import com.viam.sdk.core.resource.Resource +import com.viam.sdk.core.resource.Subtype +import com.viam.sdk.core.robot.RobotClient + +abstract class Gantry(name: String) : Component(SUBTYPE, Gantry.named(name)) { + companion object { + @JvmField + val SUBTYPE = Subtype(Subtype.NAMESPACE_RDK, Subtype.RESOURCE_TYPE_COMPONENT, "gantry") + + /** + * Get the ResourceName of the component + * @param name the name of the component + * @return the component's ResourceName + */ + @JvmStatic + fun named(name: String): ResourceName { + return Resource.named(SUBTYPE, name) + } + + /** + * Get the component with the provided name from the provided robot. + * @param robot the RobotClient + * @param name the name of the component + * @return the component + */ + @JvmStatic + fun fromRobot(robot: RobotClient, name: String): Gantry { + return robot.getResource(Gantry::class.java, named(name)) + } + } + + /** + * Get the positions of the axes of the gantry in millimeters. + * @return the list of the position of the axes of the gantry in millimeters + */ + abstract fun getPosition(extra: Struct): List + + /** + * Get the positions of the axes of the gantry in millimeters. + * @return the list of the position of the axes of the gantry in millimeters + */ + fun getPosition(): List { + return getPosition(Struct.getDefaultInstance()) + } + + /** + * Move the axes of the gantry to the desired positions (mm) at the requested speeds (mm/sec). + * @param positions the list of positions for the axes of the gantry to move to, in millimeters + * @param speeds the list of speeds in millimeters per second for the gantry to move at respective to each axis + */ + abstract fun moveToPosition(positions: List, speeds: List, extra: Struct) + + /** + * Move the axes of the gantry to the desired positions (mm) at the requested speeds (mm/sec). + * @param positions the list of positions for the axes of the gantry to move to, in millimeters + * @param speeds the list of speeds in millimeters per second for the gantry to move at respective to each axis + */ + fun moveToPosition(positions: List, speeds: List) { + return moveToPosition(positions, speeds, Struct.getDefaultInstance()) + } + + /** + * Run the homing sequence of the gantry to re-calibrate the axes with respect to the limit switches. + * @return whether the gantry has run the homing sequence successfully + */ + abstract fun home(extra: Struct): Boolean + + /** + * Run the homing sequence of the gantry to re-calibrate the axes with respect to the limit switches. + * @return whether the gantry has run the homing sequence successfully + */ + fun home(): Boolean { + return home(Struct.getDefaultInstance()) + } + + /** + * Get the lengths of the axes of the gantry in millimeters. + * @return the list of the lengths of the axes of the gantry in millimeters + */ + abstract fun getLengths(extra: Struct): List + + /** + * Get the lengths of the axes of the gantry in millimeters. + * @return the list of the lengths of the axes of the gantry in millimeters + */ + fun getLengths(): List { + return getLengths(Struct.getDefaultInstance()) + } + + /** + *Stop all motion of the gantry. It is assumed that the gantry stops immediately. + */ + abstract fun stop(extra: Struct) + + /** + * Stop all motion of the gantry. It is assumed that the gantry stops immediately. + */ + fun stop() { + stop(Struct.getDefaultInstance()) + } + + /** + * Get if the gantry is currently moving. + * @returns if the gantry is moving + */ + abstract fun isMoving(): Boolean +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/GantryRPCClient.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/GantryRPCClient.kt new file mode 100644 index 000000000..db91e2bb2 --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/GantryRPCClient.kt @@ -0,0 +1,75 @@ +package com.viam.sdk.core.component.gantry + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.GetGeometriesRequest +import com.viam.component.gantry.v1.Gantry.* +import com.viam.component.gantry.v1.GantryServiceGrpc +import com.viam.component.gantry.v1.GantryServiceGrpc.GantryServiceBlockingStub +import com.viam.sdk.core.rpc.Channel +import java.util.* +import kotlin.jvm.optionals.getOrDefault + +class GantryRPCClient(name: String, channel: Channel) : Gantry(name) { + private val client: GantryServiceBlockingStub + + init { + val client = GantryServiceGrpc.newBlockingStub(channel) + if (channel.callCredentials.isPresent) { + this.client = client.withCallCredentials(channel.callCredentials.get()) + } else { + this.client = client + } + } + + override fun getPosition(extra: Struct): List { + val request = GetPositionRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + val response = this.client.getPosition(request) + return response.positionsMmList + } + + override fun moveToPosition(positions: List, speeds: List, extra: Struct) { + val request =MoveToPositionRequest.newBuilder().setName(this.name.name).setExtra(extra).addAllPositionsMm(positions).addAllSpeedsMmPerSec(speeds).build() + this.client.moveToPosition(request) + } + + override fun home(extra: Struct): Boolean { + val request = HomeRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + val response = this.client.home(request) + return response.homed + } + + override fun getLengths(extra: Struct): List { + val request = GetLengthsRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + val response = this.client.getLengths(request) + return response.lengthsMmList + } + + override fun stop(extra: Struct) { + val request = StopRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + this.client.stop(request) + } + + override fun isMoving(): Boolean { + val request = IsMovingRequest.newBuilder().setName(this.name.name).build() + val response = this.client.isMoving(request) + return response.isMoving + } + + override fun doCommand(command: Map?): Struct { + val request = Common.DoCommandRequest.newBuilder().setName(this.name.name) + .setCommand(Struct.newBuilder().putAllFields(command).build()).build() + val response = this.client.doCommand(request) + return response.result + } + + override fun getGeometries(extra: Optional): List { + val request = GetGeometriesRequest.newBuilder().setName(this.name.name) + .setExtra(extra.getOrDefault(Struct.getDefaultInstance())).build() + val response = this.client.getGeometries(request) + return response.geometriesList + } + + +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/GantryRPCService.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/GantryRPCService.kt new file mode 100644 index 000000000..4b8261571 --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/gantry/GantryRPCService.kt @@ -0,0 +1,88 @@ +package com.viam.sdk.core.component.gantry + +import com.viam.common.v1.Common.* +import com.viam.component.gantry.v1.Gantry.* +import com.viam.component.gantry.v1.GantryServiceGrpc +import com.viam.sdk.core.component.motor.Motor +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.resource.ResourceRPCService +import io.grpc.stub.StreamObserver +import java.util.* + +internal class GantryRPCService(private val manager: ResourceManager) : GantryServiceGrpc.GantryServiceImplBase(), + ResourceRPCService { + + override fun getPosition( + request: GetPositionRequest, responseObserver: StreamObserver + ) { + val gantry = getResource(Gantry.named(request.name)) + val position = gantry.getPosition(request.extra) + responseObserver.onNext(GetPositionResponse.newBuilder().addAllPositionsMm(position).build()) + responseObserver.onCompleted() + } + + override fun moveToPosition( + request: MoveToPositionRequest, responseObserver: StreamObserver + ) { + val gantry = getResource(Gantry.named(request.name)) + gantry.moveToPosition(request.positionsMmList, request.speedsMmPerSecList, request.extra) + responseObserver.onNext(MoveToPositionResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun home(request: HomeRequest, responseObserver: StreamObserver) { + val gantry = getResource(Gantry.named(request.name)) + val homed = gantry.home(request.extra) + responseObserver.onNext(HomeResponse.newBuilder().setHomed(homed).build()) + responseObserver.onCompleted() + } + + override fun getLengths(request: GetLengthsRequest, responseObserver: StreamObserver) { + val gantry = getResource(Gantry.named(request.name)) + val lengths = gantry.getLengths(request.extra) + responseObserver.onNext(GetLengthsResponse.newBuilder().addAllLengthsMm(lengths).build()) + responseObserver.onCompleted() + } + + override fun stop(request: StopRequest, responseObserver: StreamObserver) { + val gantry = getResource(Gantry.named(request.name)) + gantry.stop(request.extra) + responseObserver.onNext(StopResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun isMoving(request: IsMovingRequest, responseObserver: StreamObserver) { + val gantry = getResource(Gantry.named(request.name)) + val bool = gantry.isMoving() + responseObserver.onNext(IsMovingResponse.newBuilder().setIsMoving(bool).build()) + responseObserver.onCompleted() + } + + override fun doCommand( + request: DoCommandRequest, responseObserver: StreamObserver + ) { + val gantry = getResource(Gantry.named(request.name)) + val result = gantry.doCommand(request.command.fieldsMap) + responseObserver.onNext(DoCommandResponse.newBuilder().setResult(result).build()) + responseObserver.onCompleted() + } + + override fun getGeometries( + request: GetGeometriesRequest, responseObserver: StreamObserver + ) { + val motor = getResource(Gantry.named(request.name)) + val result = motor.getGeometries(Optional.of(request.extra)) + responseObserver.onNext(GetGeometriesResponse.newBuilder().addAllGeometries(result).build()) + responseObserver.onCompleted() + } + + + override fun getResourceClass(): Class { + return Gantry::class.java + } + + override fun getManager(): ResourceManager { + return this.manager + } + +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmRPCClientTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmRPCClientTest.kt new file mode 100644 index 000000000..e4da4616a --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmRPCClientTest.kt @@ -0,0 +1,124 @@ +package com.viam.sdk.core.component.arm + +import com.google.protobuf.ByteString +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.Geometry +import com.viam.common.v1.Common.KinematicsFileFormat +import com.viam.component.arm.v1.Arm.JointPositions +import com.viam.sdk.core.component.arm.Arm +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.rpc.BasicManagedChannel +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class ArmRPCClientTest { + private lateinit var arm: Arm + private lateinit var client: ArmRPCClient + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + arm = mock( + Arm::class.java, withSettings().useConstructor("mock-arm").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + val resourceManager = ResourceManager(listOf(arm)) + val service = ArmRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + val channel = grpcCleanupRule.register(InProcessChannelBuilder.forName(serviceName).directExecutor().build()) + client = ArmRPCClient("mock-arm", BasicManagedChannel(channel)) + } + + + @Test + fun moveToPosition() { + val pose = Common.Pose.newBuilder().setX(5.0).setY(5.0).setZ(5.0).setTheta(5.0).setOX(5.0).setOY(5.0).setOZ(5.0) + .build() + client.moveToPosition(pose) + verify(arm).moveToPosition(pose, Struct.getDefaultInstance()) + + } + + @Test + fun getEndPosition() { + val pose = Common.Pose.newBuilder().setX(5.0).setY(5.0).setZ(5.0).setTheta(5.0).setOX(5.0).setOY(5.0).setOZ(5.0) + .build() + `when`(arm.getEndPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(pose) + val response = client.getEndPosition() + verify(arm).getEndPosition(Struct.getDefaultInstance()) + assertEquals(pose, response) + } + + @Test + fun moveToJointPositions() { + val positions = JointPositions.newBuilder().addAllValues(listOf(1.0, 8.0, 2.0)).build() + client.moveToJointPositions(positions) + verify(arm).moveToJointPositions(positions, Struct.getDefaultInstance()) + } + + @Test + fun getJointPositions() { + val positions = JointPositions.newBuilder().addAllValues(listOf(1.0, 8.0, 2.0)).build() + `when`(arm.getJointPositions(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(positions) + val response = client.getJointPositions() + verify(arm).getJointPositions(Struct.getDefaultInstance()) + assertEquals(positions, response) + } + + @Test + fun getKinematics() { + val kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA to ByteString.copyFromUtf8("abc")) + `when`(arm.getKinematics(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(kinematics) + val response = client.getKinematics() + verify(arm).getKinematics(Struct.getDefaultInstance()) + assertEquals(kinematics, response) + } + + + @Test + fun stop() { + client.stop() + verify(arm).stop(Struct.getDefaultInstance()) + } + + @Test + fun isMoving() { + `when`(arm.isMoving()).thenReturn(false) + val isMoving = client.isMoving() + verify(arm).isMoving() + assertFalse(isMoving) + } + + @Test + fun doCommand() { + val command = mapOf("foo" to Value.newBuilder().setStringValue("bar").build()) + doReturn(Struct.newBuilder().putAllFields(command).build()).`when`(arm).doCommand(anyMap()) + val response = client.doCommand(command) + verify(arm).doCommand(command) + assertEquals(command, response.fieldsMap) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(arm).getGeometries(any()) + client.getGeometries(Optional.empty()) + verify(arm).getGeometries(any()) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmRPCServiceTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmRPCServiceTest.kt new file mode 100644 index 000000000..3c8ed2f48 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmRPCServiceTest.kt @@ -0,0 +1,138 @@ +package com.viam.sdk.core.component.arm + +import com.google.protobuf.ByteString +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.Geometry +import com.viam.common.v1.Common.KinematicsFileFormat +import com.viam.component.arm.v1.Arm.* +import com.viam.component.arm.v1.ArmServiceGrpc +import com.viam.component.arm.v1.ArmServiceGrpc.ArmServiceBlockingStub +import com.viam.sdk.core.component.arm.Arm +import com.viam.sdk.core.resource.ResourceManager +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class ArmRPCServiceTest { + + private lateinit var arm: Arm + private lateinit var client: ArmServiceBlockingStub + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + arm = mock( + Arm::class.java, withSettings().useConstructor("mock-arm").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + + val resourceManager = ResourceManager(listOf(arm)) + val service = ArmRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + client = ArmServiceGrpc.newBlockingStub( + grpcCleanupRule.register( + InProcessChannelBuilder.forName(serviceName).build() + ) + ) + } + + @Test + fun moveToPosition() { + val pose = Common.Pose.newBuilder().setX(5.0).setY(5.0).setZ(5.0).setTheta(5.0).setOX(5.0).setOY(5.0).setOZ(5.0) + .build() + val request = MoveToPositionRequest.newBuilder().setName(arm.name.name).setTo(pose).build() + this.client.moveToPosition(request) + verify(arm).moveToPosition(pose, Struct.getDefaultInstance()) + } + + @Test + fun getEndPosition() { + val pose = Common.Pose.newBuilder().setX(5.0).setY(5.0).setZ(5.0).setTheta(5.0).setOX(5.0).setOY(5.0).setOZ(5.0) + .build() + `when`(arm.getEndPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(pose) + val request = GetEndPositionRequest.newBuilder().setName(arm.name.name).build() + val response = this.client.getEndPosition(request) + verify(arm).getEndPosition(Struct.getDefaultInstance()) + assertEquals(pose, response.pose) + } + + @Test + fun moveToJointPositions() { + val positions = JointPositions.newBuilder().addAllValues(listOf(1.0, 8.0, 2.0)).build() + val request = MoveToJointPositionsRequest.newBuilder().setName(arm.name.name).setPositions(positions).build() + this.client.moveToJointPositions(request) + verify(arm).moveToJointPositions(positions, Struct.getDefaultInstance()) + } + + @Test + fun getJointPositions() { + val positions = JointPositions.newBuilder().addAllValues(listOf(1.0, 8.0, 2.0)).build() + `when`(arm.getJointPositions(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(positions) + val request = GetJointPositionsRequest.newBuilder().setName(arm.name.name).build() + val response = this.client.getJointPositions(request) + verify(arm).getJointPositions(Struct.getDefaultInstance()) + assertEquals(positions, response.positions) + } + + @Test + fun getKinematics() { + val kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA to ByteString.copyFromUtf8("abc")) + `when`(arm.getKinematics(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(kinematics) + val request = Common.GetKinematicsRequest.newBuilder().setName(arm.name.name).build() + val response = this.client.getKinematics(request) + verify(arm).getKinematics(Struct.getDefaultInstance()) + assertEquals(kinematics, (response.format to response.kinematicsData)) + + } + + @Test + fun stop() { + val request = StopRequest.newBuilder().setName(arm.name.name).build() + client.stop(request) + verify(arm).stop(Struct.getDefaultInstance()) + } + + + @Test + fun isMoving() { + `when`(arm.isMoving()).thenReturn(false) + val request = IsMovingRequest.newBuilder().setName(arm.name.name).build() + val response = client.isMoving(request) + assertFalse(response.isMoving) + } + + @Test + fun doCommand() { + val command = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + doReturn(command).`when`(arm).doCommand(anyMap()) + val request = Common.DoCommandRequest.newBuilder().setName(arm.name.name).setCommand(command).build() + val response = client.doCommand(request) + verify(arm).doCommand(command.fieldsMap) + assertEquals(command, response.result) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(arm).getGeometries(any()) + val request = Common.GetGeometriesRequest.newBuilder().setName(arm.name.name).build() + client.getGeometries(request) + verify(arm).getGeometries(Optional.of(Struct.getDefaultInstance())) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmTest.kt new file mode 100644 index 000000000..dd49ab22f --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/arm/ArmTest.kt @@ -0,0 +1,81 @@ +package com.viam.sdk.core.component.arm + +import com.google.protobuf.ByteString +import com.google.protobuf.Struct +import com.viam.common.v1.Common +import com.viam.common.v1.Common.KinematicsFileFormat +import com.viam.component.arm.v1.Arm.JointPositions +import com.viam.sdk.core.component.arm.Arm +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Answers +import org.mockito.Mockito.* + +class ArmTest { + private lateinit var arm: Arm + + @BeforeEach + fun setup() { + arm = mock(Arm::class.java, Answers.CALLS_REAL_METHODS) + } + + @Test + fun getEndPosition() { + val pose = Common.Pose.newBuilder().setX(5.0).setY(5.0).setZ(5.0).setTheta(5.0).setOX(5.0).setOY(5.0).setOZ(5.0) + .build() + `when`(arm.getEndPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(pose) + val endPos = arm.getEndPosition() + verify(arm).getEndPosition() + assertEquals(pose, endPos) + } + + @Test + fun moveToPosition() { + val pose = Common.Pose.newBuilder().setX(5.0).setY(5.0).setZ(5.0).setTheta(5.0).setOX(5.0).setOY(5.0).setOZ(5.0) + .build() + arm.moveToPosition(pose) + verify(arm).moveToPosition(pose) + } + + @Test + fun moveToJointPositions() { + val positions = JointPositions.newBuilder().addAllValues(listOf(1.0, 8.0, 2.0)).build() + arm.moveToJointPositions(positions) + verify(arm).moveToJointPositions(positions) + } + + @Test + fun getJointPositions() { + val positions = JointPositions.newBuilder().addAllValues(listOf(1.0, 8.0, 2.0)).build() + `when`(arm.getJointPositions(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(positions) + val jointPos = arm.getJointPositions() + verify(arm).getJointPositions() + assertEquals(positions, jointPos) + } + + @Test + fun getKinematics() { + val kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA to ByteString.copyFromUtf8("abc")) + `when`(arm.getKinematics(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(kinematics) + val result = arm.getKinematics() + verify(arm).getKinematics() + assertEquals(kinematics, result) + } + + @Test + fun stop() { + arm.stop() + verify(arm).stop() + } + + @Test + fun isMoving() { + `when`(arm.isMoving()).thenReturn(false) + val isMoving = arm.isMoving() + verify(arm).isMoving() + assertFalse(isMoving) + } + +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCClientTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCClientTest.kt new file mode 100644 index 000000000..4081a7888 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCClientTest.kt @@ -0,0 +1,103 @@ +package com.viam.sdk.core.component.encoder + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common.Geometry +import com.viam.component.encoder.v1.Encoder.GetPropertiesResponse +import com.viam.component.encoder.v1.Encoder.PositionType +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.rpc.BasicManagedChannel +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class EncoderRPCClientTest { + private lateinit var encoder: Encoder + private lateinit var client: EncoderRPCClient + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + encoder = mock( + Encoder::class.java, withSettings().useConstructor("mock-encoder").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + val resourceManager = ResourceManager(listOf(encoder)) + val service = EncoderRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + val channel = grpcCleanupRule.register(InProcessChannelBuilder.forName(serviceName).directExecutor().build()) + client = EncoderRPCClient("mock-encoder", BasicManagedChannel(channel)) + } + + @Test + fun getPosition() { + `when`( + encoder.getPosition( + eq(null), + any(Struct::class.java) ?: Struct.getDefaultInstance() + ) + ).thenReturn(23.0f to PositionType.POSITION_TYPE_TICKS_COUNT) + var pos = client.getPosition() + verify(encoder).getPosition(Struct.getDefaultInstance()) + assertEquals(23.0f, pos.first) + assertEquals(PositionType.POSITION_TYPE_TICKS_COUNT, pos.second) + + `when`( + encoder.getPosition( + eq(PositionType.POSITION_TYPE_ANGLE_DEGREES), + any(Struct::class.java) ?: Struct.getDefaultInstance() + ) + ).thenReturn(23.0f to PositionType.POSITION_TYPE_ANGLE_DEGREES) + pos = client.getPosition(PositionType.POSITION_TYPE_ANGLE_DEGREES) + verify(encoder).getPosition(PositionType.POSITION_TYPE_ANGLE_DEGREES, Struct.getDefaultInstance()) + assertEquals(23.0f, pos.first) + assertEquals(PositionType.POSITION_TYPE_ANGLE_DEGREES, pos.second) + } + + @Test + fun resetPosition() { + client.resetPosition() + verify(encoder).resetPosition(Struct.getDefaultInstance()) + } + + @Test + fun getProperties() { + `when`(encoder.getProperties(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn( + GetPropertiesResponse.newBuilder().setAngleDegreesSupported(true).setTicksCountSupported(true).build() + ) + val properties = client.getProperties() + verify(encoder).getProperties(Struct.getDefaultInstance()) + assertTrue(properties.ticksCountSupported) + assertTrue(properties.angleDegreesSupported) + } + + @Test + fun doCommand() { + val command = mapOf("foo" to Value.newBuilder().setStringValue("bar").build()) + doReturn(Struct.newBuilder().putAllFields(command).build()).`when`(encoder).doCommand(anyMap()) + val response = client.doCommand(command) + verify(encoder).doCommand(command) + assertEquals(command, response.fieldsMap) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(encoder).getGeometries(any()) + client.getGeometries(Optional.empty()) + verify(encoder).getGeometries(any()) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCServiceTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCServiceTest.kt new file mode 100644 index 000000000..ab10f8948 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCServiceTest.kt @@ -0,0 +1,116 @@ +package com.viam.sdk.core.component.encoder + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.Geometry +import com.viam.component.encoder.v1.Encoder.* +import com.viam.component.encoder.v1.EncoderServiceGrpc +import com.viam.component.encoder.v1.EncoderServiceGrpc.EncoderServiceBlockingStub +import com.viam.sdk.core.resource.ResourceManager +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class EncoderRPCServiceTest { + private lateinit var encoder: Encoder + private lateinit var client: EncoderServiceBlockingStub + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + encoder = mock( + Encoder::class.java, withSettings().useConstructor("mock-encoder").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + + val resourceManager = ResourceManager(listOf(encoder)) + val service = EncoderRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + client = EncoderServiceGrpc.newBlockingStub( + grpcCleanupRule.register( + InProcessChannelBuilder.forName(serviceName).build() + ) + ) + } + + @Test + fun getPosition() { + `when`( + encoder.getPosition( + eq(null), + any(Struct::class.java) ?: Struct.getDefaultInstance() + ) + ).thenReturn(23.0f to PositionType.POSITION_TYPE_TICKS_COUNT) + var request = GetPositionRequest.newBuilder().setName(encoder.name.name).build() + var pos = client.getPosition(request) + verify(encoder).getPosition(Struct.getDefaultInstance()) + assertEquals(23.0f, pos.value) + assertEquals(PositionType.POSITION_TYPE_TICKS_COUNT, pos.positionType) + + `when`( + encoder.getPosition( + eq(PositionType.POSITION_TYPE_ANGLE_DEGREES), + any(Struct::class.java) ?: Struct.getDefaultInstance() + ) + ).thenReturn(23.0f to PositionType.POSITION_TYPE_ANGLE_DEGREES) + request = GetPositionRequest.newBuilder().setName(encoder.name.name) + .setPositionType(PositionType.POSITION_TYPE_ANGLE_DEGREES).build() + pos = client.getPosition(request) + verify(encoder).getPosition(PositionType.POSITION_TYPE_ANGLE_DEGREES, Struct.getDefaultInstance()) + assertEquals(23.0f, pos.value) + assertEquals(PositionType.POSITION_TYPE_ANGLE_DEGREES, pos.positionType) + } + + @Test + fun resetPosition() { + val request = ResetPositionRequest.newBuilder().setName(encoder.name.name).build() + client.resetPosition(request) + verify(encoder).resetPosition(Struct.getDefaultInstance()) + } + + @Test + fun getProperties() { + `when`(encoder.getProperties(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn( + GetPropertiesResponse.newBuilder().setAngleDegreesSupported(true).setTicksCountSupported(true).build() + ) + val request = GetPropertiesRequest.newBuilder().setName(encoder.name.name).build() + val properties = client.getProperties(request) + verify(encoder).getProperties(Struct.getDefaultInstance()) + assertTrue(properties.ticksCountSupported) + assertTrue(properties.angleDegreesSupported) + } + + @Test + fun doCommand() { + val command = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + doReturn(command).`when`(encoder).doCommand(anyMap()) + val request = Common.DoCommandRequest.newBuilder().setName(encoder.name.name).setCommand(command).build() + val response = client.doCommand(request) + verify(encoder).doCommand(command.fieldsMap) + assertEquals(command, response.result) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(encoder).getGeometries(any()) + val request = Common.GetGeometriesRequest.newBuilder().setName(encoder.name.name).build() + client.getGeometries(request) + verify(encoder).getGeometries(Optional.of(Struct.getDefaultInstance())) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderTest.kt new file mode 100644 index 000000000..4714115a8 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderTest.kt @@ -0,0 +1,64 @@ +package com.viam.sdk.core.component.encoder + +import com.google.protobuf.Struct +import com.viam.component.encoder.v1.Encoder.GetPropertiesResponse +import com.viam.component.encoder.v1.Encoder.PositionType +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Answers +import org.mockito.Mockito.* + +class EncoderTest { + private lateinit var encoder: Encoder + + @BeforeEach + fun setup() { + encoder = mock(Encoder::class.java, Answers.CALLS_REAL_METHODS) + } + + @Test + fun getPosition() { + //test no position type given + `when`( + encoder.getPosition( + eq(null), + any(Struct::class.java) ?: Struct.getDefaultInstance() + ) + ).thenReturn(23.0f to PositionType.POSITION_TYPE_TICKS_COUNT) + var pos = encoder.getPosition() + verify(encoder).getPosition() + assertEquals(23.0f, pos.first) + assertEquals(PositionType.POSITION_TYPE_TICKS_COUNT, pos.second) + + //test position type given + `when`( + encoder.getPosition( + eq(PositionType.POSITION_TYPE_ANGLE_DEGREES), + any(Struct::class.java) ?: Struct.getDefaultInstance() + ) + ).thenReturn(23.0f to PositionType.POSITION_TYPE_ANGLE_DEGREES) + pos = encoder.getPosition(PositionType.POSITION_TYPE_ANGLE_DEGREES) + verify(encoder).getPosition(PositionType.POSITION_TYPE_ANGLE_DEGREES) + assertEquals(23.0f, pos.first) + assertEquals(PositionType.POSITION_TYPE_ANGLE_DEGREES, pos.second) + } + + @Test + fun resetPosition() { + encoder.resetPosition() + verify(encoder).resetPosition() + } + + @Test + fun getProperties() { + `when`(encoder.getProperties(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn( + GetPropertiesResponse.newBuilder().setAngleDegreesSupported(true).setTicksCountSupported(true).build() + ) + val properties = encoder.getProperties() + verify(encoder).getProperties() + assertTrue(properties.ticksCountSupported) + assertTrue(properties.angleDegreesSupported) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryRPCClientTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryRPCClientTest.kt new file mode 100644 index 000000000..b31fb8bc4 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryRPCClientTest.kt @@ -0,0 +1,108 @@ +package com.viam.sdk.core.component.gantry + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common.Geometry +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.rpc.BasicManagedChannel +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class GantryRPCClientTest { + private lateinit var gantry: Gantry + private lateinit var client: GantryRPCClient + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + gantry = mock( + Gantry::class.java, withSettings().useConstructor("mock-gantry").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + val resourceManager = ResourceManager(listOf(gantry)) + val service = GantryRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + val channel = grpcCleanupRule.register(InProcessChannelBuilder.forName(serviceName).directExecutor().build()) + client = GantryRPCClient("mock-gantry", BasicManagedChannel(channel)) + } + + + @Test + fun getPosition(){ + `when`(gantry.getPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(listOf(2.3, 1.2)) + val pos = client.getPosition() + verify(gantry).getPosition(Struct.getDefaultInstance()) + assertEquals(listOf(2.3, 1.2), pos) + + } + + @Test + fun moveToPosition(){ + client.moveToPosition(listOf(1.2, 3.4), listOf(5.6, 7.8)) + verify(gantry).moveToPosition( listOf(1.2, 3.4), listOf(5.6, 7.8), Struct.getDefaultInstance()) + + } + + @Test + fun home(){ + `when`(gantry.home(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(true) + val home = client.home() + verify(gantry).home(Struct.getDefaultInstance()) + assertEquals(true, home) + + } + + @Test + fun getLengths(){ + `when`(gantry.getLengths(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(listOf(32.1)) + val response = client.getLengths() + verify(gantry).getLengths(Struct.getDefaultInstance()) + assertEquals(listOf(32.1), response) + } + + @Test + fun stop(){ + client.stop() + verify(gantry).stop(Struct.getDefaultInstance()) + + } + + @Test + fun isMoving(){ + `when`(gantry.isMoving()).thenReturn(false) + val isMoving = client.isMoving() + verify(gantry).isMoving() + assertFalse(isMoving) + + } + @Test + fun doCommand() { + val command = mapOf("foo" to Value.newBuilder().setStringValue("bar").build()) + doReturn(Struct.newBuilder().putAllFields(command).build()).`when`(gantry).doCommand(anyMap()) + val response = client.doCommand(command) + verify(gantry).doCommand(command) + assertEquals(command, response.fieldsMap) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(gantry).getGeometries(any()) + client.getGeometries(Optional.empty()) + verify(gantry).getGeometries(any()) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryRPCServiceTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryRPCServiceTest.kt new file mode 100644 index 000000000..c89aa672e --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryRPCServiceTest.kt @@ -0,0 +1,124 @@ +package com.viam.sdk.core.component.gantry + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.Geometry +import com.viam.component.gantry.v1.GantryServiceGrpc +import com.viam.component.gantry.v1.GantryServiceGrpc.GantryServiceBlockingStub +import com.viam.component.gantry.v1.Gantry.* +import com.viam.sdk.core.resource.ResourceManager +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class GantryRPCServiceTest { + private lateinit var gantry: Gantry + private lateinit var client: GantryServiceBlockingStub + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + gantry = mock( + Gantry::class.java, withSettings().useConstructor("mock-gantry").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + + val resourceManager = ResourceManager(listOf(gantry)) + val service = GantryRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + client = GantryServiceGrpc.newBlockingStub( + grpcCleanupRule.register( + InProcessChannelBuilder.forName(serviceName).build() + ) + ) + } + + @Test + fun getPosition(){ + `when`(gantry.getPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(listOf(32.1)) + val request = GetPositionRequest.newBuilder().setName(gantry.name.name).build() + val response = client.getPosition(request) + verify(gantry).getPosition(Struct.getDefaultInstance()) + assertEquals(listOf(32.1), response.positionsMmList) + + } + + @Test + fun moveToPosition(){ + val request = MoveToPositionRequest.newBuilder().setName(gantry.name.name).addAllPositionsMm(listOf(1.2, 3.4)).addAllSpeedsMmPerSec(listOf(5.6, 7.8)).build() + client.moveToPosition(request) + verify(gantry).moveToPosition( listOf(1.2, 3.4), listOf(5.6, 7.8), Struct.getDefaultInstance()) + } + + @Test + fun home(){ + `when`(gantry.home(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(true) + val request = HomeRequest.newBuilder().setName(gantry.name.name).build() + val home = client.home(request) + verify(gantry).home(Struct.getDefaultInstance()) + assertEquals(true, home.homed) + + } + + @Test + fun getLengths(){ + `when`(gantry.getLengths(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(listOf(32.1)) + val request = GetLengthsRequest.newBuilder().setName(gantry.name.name).build() + val response = client.getLengths(request) + verify(gantry).getLengths(Struct.getDefaultInstance()) + assertEquals(listOf(32.1), response.lengthsMmList) + + } + + @Test + fun stop(){ + val request = StopRequest.newBuilder().setName(gantry.name.name).build() + client.stop(request) + verify(gantry).stop(Struct.getDefaultInstance()) + + } + + @Test + fun isMoving(){ + `when`(gantry.isMoving()).thenReturn(false) + val request = IsMovingRequest.newBuilder().setName(gantry.name.name).build() + val response = client.isMoving(request) + verify(gantry).isMoving() + assertFalse(response.isMoving) + + } + + @Test + fun doCommand() { + val command = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + doReturn(command).`when`(gantry).doCommand(anyMap()) + val request = Common.DoCommandRequest.newBuilder().setName(gantry.name.name).setCommand(command).build() + val response = client.doCommand(request) + verify(gantry).doCommand(command.fieldsMap) + assertEquals(command, response.result) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(gantry).getGeometries(any()) + val request = Common.GetGeometriesRequest.newBuilder().setName(gantry.name.name).build() + client.getGeometries(request) + verify(gantry).getGeometries(Optional.of(Struct.getDefaultInstance())) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryTest.kt new file mode 100644 index 000000000..14e9a3db2 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/gantry/GantryTest.kt @@ -0,0 +1,68 @@ +package com.viam.sdk.core.component.gantry + +import com.google.protobuf.Struct +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Answers +import org.mockito.Mockito.* + +class GantryTest { + private lateinit var gantry: Gantry + + @BeforeEach + fun setup() { + gantry = mock(Gantry::class.java, Answers.CALLS_REAL_METHODS) + } + + @Test + fun getPosition(){ + `when`(gantry.getPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(listOf(12.3, 23.4)) + val pos = gantry.getPosition() + verify(gantry).getPosition() + assertEquals(listOf(12.3, 23.4), pos) + + } + + @Test + fun moveToPosition(){ + gantry.moveToPosition(listOf(2.0, 3.0), listOf(1.0, 2.0, 3.0)) + verify(gantry).moveToPosition(listOf(2.0, 3.0), listOf(1.0, 2.0, 3.0)) + + } + + @Test + fun home(){ + `when`(gantry.home(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(true) + val home = gantry.home() + verify(gantry).home() + assertEquals(true, home) + + } + + @Test + fun getLengths(){ + `when`(gantry.getLengths(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(listOf(12.3, 23.4)) + val lengths = gantry.getLengths() + verify(gantry).getLengths() + assertEquals(listOf(12.3, 23.4), lengths) + + } + + @Test + fun stop(){ + gantry.stop() + verify(gantry).stop() + + } + + @Test + fun isMoving(){ + `when`(gantry.isMoving()).thenReturn(false) + val isMoving = gantry.isMoving() + verify(gantry).isMoving() + assertFalse(isMoving) + + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorRPCClientTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorRPCClientTest.kt new file mode 100644 index 000000000..c42425789 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorRPCClientTest.kt @@ -0,0 +1,99 @@ +package com.viam.sdk.core.component.powersensor + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.rpc.BasicManagedChannel +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* +import java.util.AbstractMap + +class PowerSensorRPCClientTest { + private lateinit var powerSensor: PowerSensor + private lateinit var client: PowerSensorRPCClient + + val extra = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + powerSensor = mock( + PowerSensor::class.java, withSettings().useConstructor("mock-powersensor").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + val resourceManager = ResourceManager(listOf(powerSensor)) + val service = PowerSensorRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + val channel = grpcCleanupRule.register(InProcessChannelBuilder.forName(serviceName).directExecutor().build()) + client = PowerSensorRPCClient("mock-powersensor", BasicManagedChannel(channel)) + } + + @Test + fun getVoltage() { + `when`(powerSensor.getVoltage(any>())).thenReturn(AbstractMap.SimpleEntry(3.0, true)) + val response = client.getVoltage(Optional.of(extra)) + verify(powerSensor).getVoltage(Optional.of(extra)) + assertEquals(3.0, response.key) + assertTrue(response.value) + } + + @Test + fun getCurrent() { + `when`(powerSensor.getCurrent(any>())).thenReturn(AbstractMap.SimpleEntry(3.0, true)) + val response = client.getCurrent(Optional.of(extra)) + verify(powerSensor).getCurrent(Optional.of(extra)) + assertEquals(3.0, response.key) + assertTrue(response.value) + + } + + @Test + fun getPower() { + `when`(powerSensor.getPower(any>())).thenReturn(2.1) + val response = client.getPower(Optional.of(extra)) + verify(powerSensor).getPower(Optional.of(extra)) + assertEquals(2.1, response) + } + + @Test + fun getReadings() { + val readings = mapOf( + "a" to 1.0, + "b" to 2.0, + "c" to 3.0, + "d" to mapOf("vec3" to Common.Vector3.newBuilder().setX(1.0).setY(1.0).setZ(1.0).build()) + ) + `when`(powerSensor.getReadings(any>())).thenReturn(readings) + val response = client.getReadings(Optional.of(extra)) + verify(powerSensor).getReadings(Optional.of(extra)) + assertEquals(readings, response) + + } + + @Test + fun doCommand() { + val command = mapOf("abc" to Value.newBuilder().setStringValue("123").build()) + doReturn(Struct.newBuilder().putAllFields(command).build()).`when`(powerSensor).doCommand(anyMap()) + val response = client.doCommand(command) + verify(powerSensor).doCommand(command) + assertEquals(command, response.fieldsMap) + } + +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorRPCServiceTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorRPCServiceTest.kt new file mode 100644 index 000000000..6d1baf77b --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorRPCServiceTest.kt @@ -0,0 +1,117 @@ +package com.viam.sdk.core.component.powersensor +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.component.powersensor.v1.Powersensor.* +import com.viam.component.powersensor.v1.PowerSensorServiceGrpc +import com.viam.component.powersensor.v1.PowerSensorServiceGrpc.PowerSensorServiceBlockingStub +import com.viam.sdk.core.resource.ResourceManager +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* +class PowerSensorRPCServiceTest { + private lateinit var powerSensor: PowerSensor + private lateinit var client: PowerSensorServiceBlockingStub + + val extra = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + powerSensor = mock( + PowerSensor::class.java, withSettings().useConstructor("mock-powersensor").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + + val resourceManager = ResourceManager(listOf(powerSensor)) + val service = PowerSensorRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + client = PowerSensorServiceGrpc.newBlockingStub( + grpcCleanupRule.register( + InProcessChannelBuilder.forName(serviceName).build() + ) + ) + } + @Test + fun getVoltage(){ + `when`(powerSensor.getVoltage(any>())).thenReturn(AbstractMap.SimpleEntry(3.0, true)) + val request = GetVoltageRequest.newBuilder().setName(powerSensor.name.name).setExtra(extra).build() + val response = client.getVoltage(request) + verify(powerSensor).getVoltage(Optional.of(extra)) + assertEquals(3.0, response.volts) + assertTrue(response.isAc) + + } + @Test + fun getCurrent(){ + `when`(powerSensor.getCurrent(any>())).thenReturn(AbstractMap.SimpleEntry(3.0, true)) + val request = GetCurrentRequest.newBuilder().setName(powerSensor.name.name).setExtra(extra).build() + val response = client.getCurrent(request) + verify(powerSensor).getCurrent(Optional.of(extra)) + assertEquals(3.0, response.amperes) + assertTrue(response.isAc) + } + @Test + fun getPower(){ + `when`(powerSensor.getPower(any>())).thenReturn(2.0) + val request = GetPowerRequest.newBuilder().setName(powerSensor.name.name).setExtra(extra).build() + val response = client.getPower(request) + verify(powerSensor).getPower(Optional.of(extra)) + assertEquals(2.0, response.watts) + + } + @Test + fun getReadings(){ + val readings = mapOf( + "a" to 1.0, + "b" to 2.0, + "c" to 3.0, + "d" to mapOf("vec3" to Common.Vector3.newBuilder().setX(1.0).setY(1.0).setZ(1.0).build()) + ) + val struct = Struct.newBuilder().apply{ + putFields("x", Value.newBuilder().setNumberValue(1.0).build()) + putFields("y", Value.newBuilder().setNumberValue(1.0).build()) + putFields("z", Value.newBuilder().setNumberValue(1.0).build()) + putFields("_type", Value.newBuilder().setStringValue("vector3").build()) + }.build() + + val valueReadings = mapOf("a" to Value.newBuilder().setNumberValue(1.0).build(), + "b" to Value.newBuilder().setNumberValue(2.0).build(), + "c" to Value.newBuilder().setNumberValue(3.0).build(), + "d" to Value.newBuilder().setStructValue(Struct.newBuilder().putAllFields(mapOf("vec3" to Value.newBuilder().setStructValue(struct).build()))).build()) + `when`(powerSensor.getReadings(any>())).thenReturn(readings) + val request = Common.GetReadingsRequest.newBuilder().setName(powerSensor.name.name).setExtra(extra).build() + val response = client.getReadings(request) + verify(powerSensor).getReadings(Optional.of(extra)) + assertEquals(valueReadings, response.readingsMap) + + + } + + @Test + fun doCommand() { + val command = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + doReturn(command).`when`(powerSensor).doCommand(anyMap()) + val request = Common.DoCommandRequest.newBuilder().setName(powerSensor.name.name).setCommand(command).build() + val response = client.doCommand(request) + verify(powerSensor).doCommand(command.fieldsMap) + assertEquals(command, response.result) + } + + +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorTest.kt new file mode 100644 index 000000000..7d6a16002 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/powersensor/PowerSensorTest.kt @@ -0,0 +1,65 @@ +package com.viam.sdk.core.component.powersensor + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Answers +import org.mockito.Mockito.* +import java.util.* + +class PowerSensorTest { + private lateinit var powerSensor: PowerSensor + val extra = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + + @BeforeEach + fun setup() { + powerSensor = mock(PowerSensor::class.java, Answers.CALLS_REAL_METHODS) + } + + @Test + fun getVoltage(){ + `when`(powerSensor.getVoltage(any>())).thenReturn(AbstractMap.SimpleEntry(3.0, true)) + val voltage = powerSensor.getVoltage(Optional.of(extra)) + verify(powerSensor).getVoltage(Optional.of(extra)) + assertEquals(3.0, voltage.key) + assertTrue(voltage.value) + + } + + @Test + fun getCurrent(){ + `when`(powerSensor.getCurrent(any>())).thenReturn(AbstractMap.SimpleEntry(5.0, false)) + val curr = powerSensor.getCurrent(Optional.of(extra)) + verify(powerSensor).getCurrent(Optional.of(extra)) + assertEquals(5.0, curr.key) + assertFalse(curr.value) + + } + + @Test + fun getPower(){ + `when`(powerSensor.getPower(any>())).thenReturn(4.2) + val power = powerSensor.getPower(Optional.of(Struct.getDefaultInstance())) + verify(powerSensor).getPower(Optional.of(Struct.getDefaultInstance())) + assertEquals(4.2, power) + + } + + @Test + fun getReadings(){ + val readings = mapOf( + "a" to 1, + "b" to 2, + "c" to 3, + "d" to mapOf("d1" to "vec3" to Common.Vector3.newBuilder().setX(1.0).setY(1.0).setZ(1.0).build()) + ) + `when`(powerSensor.getReadings(any>())).thenReturn(readings) + val result = powerSensor.getReadings(Optional.of(extra)) + verify(powerSensor).getReadings(Optional.of(extra)) + assertEquals(readings, result) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoRPCClientTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoRPCClientTest.kt new file mode 100644 index 000000000..917142f43 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoRPCClientTest.kt @@ -0,0 +1,92 @@ +package com.viam.sdk.core.component.servo + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common.Geometry +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.rpc.BasicManagedChannel +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class ServoRPCClientTest { + private lateinit var servo: Servo + private lateinit var client: ServoRPCClient + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + servo = mock( + Servo::class.java, withSettings().useConstructor("mock-servo").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + val resourceManager = ResourceManager(listOf(servo)) + val service = ServoRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + val channel = grpcCleanupRule.register(InProcessChannelBuilder.forName(serviceName).directExecutor().build()) + client = ServoRPCClient("mock-servo", BasicManagedChannel(channel)) + } + + @Test + fun move(){ + val extra = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + client.move(20, extra) + verify(servo).move(20, extra) + + } + + @Test + fun stop() { + client.stop() + verify(servo).stop(Struct.getDefaultInstance()) + } + + @Test + fun isMoving() { + `when`(servo.isMoving()).thenReturn(false) + val isMoving = client.isMoving() + verify(servo).isMoving() + assertFalse(isMoving) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(servo).getGeometries(any()) + client.getGeometries(Optional.empty()) + verify(servo).getGeometries(any()) + } + + @Test + fun getPosition() { + `when`(servo.getPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(80) + val pos = client.getPosition() + verify(servo).getPosition(Struct.getDefaultInstance()) + assertEquals(80, pos) + } + + @Test + fun doCommand() { + val command = mapOf("foo" to Value.newBuilder().setStringValue("bar").build()) + doReturn(Struct.newBuilder().putAllFields(command).build()).`when`(servo).doCommand(anyMap()) + val response = client.doCommand(command) + verify(servo).doCommand(command) + assertEquals(command, response.fieldsMap) + } + + +} diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoRPCServiceTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoRPCServiceTest.kt new file mode 100644 index 000000000..cc7ce585b --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoRPCServiceTest.kt @@ -0,0 +1,106 @@ +package com.viam.sdk.core.component.servo + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.Geometry +import com.viam.component.servo.v1.Servo.StopRequest +import com.viam.component.servo.v1.Servo.GetPositionRequest +import com.viam.component.servo.v1.Servo.IsMovingRequest +import com.viam.component.servo.v1.ServoServiceGrpc +import com.viam.component.servo.v1.ServoServiceGrpc.ServoServiceBlockingStub +import com.viam.sdk.core.resource.ResourceManager +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class ServoRPCServiceTest { + private lateinit var servo: Servo + private lateinit var client: ServoServiceBlockingStub + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + servo = mock( + Servo::class.java, withSettings().useConstructor("mock-servo").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + + val resourceManager = ResourceManager(listOf(servo)) + val service = ServoRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + client = ServoServiceGrpc.newBlockingStub( + grpcCleanupRule.register( + InProcessChannelBuilder.forName(serviceName).build() + ) + ) + } + + @Test + fun move(){ + val extra = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + val request = com.viam.component.servo.v1.Servo.MoveRequest.newBuilder().setName(servo.name.name).setAngleDeg(20).setExtra(extra).build() + client.move(request) + verify(servo).move(20, extra) + } + + @Test + fun isMoving(){ + `when`(servo.isMoving()).thenReturn(false) + val request = IsMovingRequest.newBuilder().setName(servo.name.name).build() + val response = client.isMoving(request) + verify(servo).isMoving() + assertFalse(response.isMoving) + } + + @Test + fun getPosition(){ + `when`(servo.getPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(80) + val request = GetPositionRequest.newBuilder().setName(servo.name.name).build() + val response = client.getPosition(request) + verify(servo).getPosition(Struct.getDefaultInstance()) + assertEquals(80, response.positionDeg) + } + + @Test + fun stop(){ + val request = StopRequest.newBuilder().setName(servo.name.name).build() + client.stop(request) + verify(servo).stop() + } + + @Test + fun getGeometries(){ + doReturn(listOf()).`when`(servo).getGeometries(any()) + val request = Common.GetGeometriesRequest.newBuilder().setName(servo.name.name).build() + client.getGeometries(request) + verify(servo).getGeometries(Optional.of(Struct.getDefaultInstance())) + } + + @Test + fun doCommand(){ + val command = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + doReturn(command).`when`(servo).doCommand(anyMap()) + val request = Common.DoCommandRequest.newBuilder().setName(servo.name.name).setCommand(command).build() + val response = client.doCommand(request) + verify(servo).doCommand(command.fieldsMap) + assertEquals(command, response.result) + } + +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoTest.kt new file mode 100644 index 000000000..ce33ef860 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/servo/ServoTest.kt @@ -0,0 +1,51 @@ +package com.viam.sdk.core.component.servo + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Answers +import org.mockito.Mockito.* + +class ServoTest { + private lateinit var servo: Servo + + @BeforeEach + fun setup() { + servo = mock(Servo::class.java, Answers.CALLS_REAL_METHODS) + } + @Test + fun move(){ + val extra = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + servo.move(20, extra) + verify(servo).move(20, extra) + } + @Test + fun isMoving(){ + `when`(servo.isMoving()).thenReturn(false) + val isMoving = servo.isMoving() + verify(servo).isMoving() + assertFalse(isMoving) + + } + + @Test + fun getPosition(){ + val extra = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + `when`(servo.getPosition(any(Struct::class.java) ?: Struct.getDefaultInstance())).thenReturn(80) + val pos = servo.getPosition(extra) + verify(servo).getPosition(extra) + assertEquals(80, pos) + + } + + @Test + fun stop(){ + servo.stop() + verify(servo).stop() + } + +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/util/UtilsTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/util/UtilsTest.kt new file mode 100644 index 000000000..d534da824 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/util/UtilsTest.kt @@ -0,0 +1,149 @@ +package com.viam.sdk.core.util + +import com.google.protobuf.* +import com.viam.common.v1.Common +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach + +class UtilsTest { + + private lateinit var simpleMap : Map + private lateinit var vector3 : Common.Vector3 + private lateinit var geoPoint : Common.GeoPoint + private lateinit var orientation : Common.Orientation + private lateinit var nestedStruct : Map + + private lateinit var vector3val : Value + private lateinit var geoPointVal : Value + private lateinit var orientationVal : Value + + @BeforeEach + fun setup(){ + simpleMap = mapOf("foo" to "bar", "baz" to true) + vector3 = Common.Vector3.newBuilder().setX(1.0).setY(2.0).setZ(3.0).build() + geoPoint = Common.GeoPoint.newBuilder().setLatitude(22.0).setLongitude(33.0).build() + orientation = Common.Orientation.newBuilder().setOX(1.0).setOY(1.0).setOZ(1.0).setTheta(1.0).build() + nestedStruct = mapOf( + "vec3" to vector3, + "geoPoint" to geoPoint, + "nest" to mapOf("orientation" to orientation, "foo" to "bar", "baz" to true)) + + vector3val = Value.newBuilder().setStructValue( + Struct.newBuilder().putAllFields(mapOf( + "x" to Value.newBuilder().setNumberValue(1.0).build(), + "y" to Value.newBuilder().setNumberValue(2.0).build(), + "z" to Value.newBuilder().setNumberValue(3.0).build(), + "_type" to Value.newBuilder().setStringValue("vector3").build())) + .build()).build() + geoPointVal = Value.newBuilder().setStructValue( + Struct.newBuilder().putAllFields(mapOf( + "lat" to Value.newBuilder().setNumberValue(22.0).build(), + "lng" to Value.newBuilder().setNumberValue(33.0).build(), + "_type" to Value.newBuilder().setStringValue("geopoint").build())) + .build()).build() + orientationVal = Value.newBuilder().setStructValue( + Struct.newBuilder().putAllFields(mapOf( + "ox" to Value.newBuilder().setNumberValue(1.0).build(), + "oy" to Value.newBuilder().setNumberValue(1.0).build(), + "oz" to Value.newBuilder().setNumberValue(1.0).build(), + "theta" to Value.newBuilder().setNumberValue(1.0).build(), + "_type" to Value.newBuilder().setStringValue("orientation_vector_degrees").build())) + .build()).build() + } + @Test + fun valueToNative(){ + var value = Value.newBuilder().setStringValue("value").build() + assertEquals("value", Utils.valueToNative(value)) + + value = Value.newBuilder().setNumberValue(2.0).build() + assertEquals(2.0, Utils.valueToNative(value)) + + value = Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build() + assertEquals(NullValue.NULL_VALUE, Utils.valueToNative(value)) + + value = Value.newBuilder().setBoolValue(true).build() + assertEquals(true, Utils.valueToNative(value)) + + val struct = Struct.newBuilder() + .putFields("foo", Value.newBuilder().setStringValue("bar").build()) + .putFields("baz", Value.newBuilder().setBoolValue(true).build()) + + value = Value.newBuilder().setStructValue(struct.build()).build() + assertEquals(simpleMap, Utils.valueToNative(value)) + + val list = ListValue.newBuilder() + .addValues(Value.newBuilder().setBoolValue(true).build()) + .addValues(Value.newBuilder().setNumberValue(1.0).build()) + .addValues(Value.newBuilder().setStringValue("two").build()) + .build() + value = Value.newBuilder().setListValue(list).build() + assertEquals(listOf(true, 1.0, "two"), Utils.valueToNative(value)) + + + assertEquals(vector3, Utils.valueToNative(vector3val)) + assertEquals(geoPoint, Utils.valueToNative(geoPointVal)) + assertEquals(orientation, Utils.valueToNative(orientationVal)) + + + struct.putFields("orientation", orientationVal) + val nestedStructVal = Struct.newBuilder().putAllFields(mapOf( + "vec3" to vector3val, + "geoPoint" to geoPointVal, + "nest" to Value.newBuilder().setStructValue(struct.build()).build())).build() + value = Value.newBuilder().setStructValue(nestedStructVal).build() + assertEquals(nestedStruct, Utils.valueToNative(value)) + + } + + @Test + fun nativeToValue(){ + var value = Value.newBuilder().setStringValue("abc").build() + assertEquals(value, Utils.nativeToValue("abc")) + assertEquals(value, Utils.nativeToValue("abc".toByteArray())) + + value = Value.newBuilder().setNumberValue(1.0).build() + assertEquals(value, Utils.nativeToValue(1.0)) + + value = Value.newBuilder().setBoolValue(false).build() + assertEquals(value, Utils.nativeToValue(false)) + + val struct = Struct.newBuilder() + .putFields("foo", Value.newBuilder().setStringValue("bar").build()) + .putFields("baz", Value.newBuilder().setBoolValue(true).build()) + + value = Value.newBuilder().setStructValue(struct.build()).build() + assertEquals(value, Utils.nativeToValue(simpleMap)) + + val list = ListValue.newBuilder().addAllValues( + listOf( + Value.newBuilder().setBoolValue(true).build(), + Value.newBuilder().setNumberValue(1.0).build(), + Value.newBuilder().setStringValue("two").build(), + Value.newBuilder().setStructValue(struct).build() + ) + ) + value = Value.newBuilder().setListValue(list).build() + assertEquals(value, Utils.nativeToValue(listOf(true, 1.0, "two", simpleMap))) + + assertEquals(vector3val, Utils.nativeToValue(vector3)) + assertEquals(geoPointVal, Utils.nativeToValue(geoPoint)) + assertEquals(orientationVal, Utils.nativeToValue(orientation)) + + //only string are allowed as keys + assertThrows(IllegalArgumentException::class.java) {Utils.nativeToValue(mapOf(1 to 2, 3 to 4))} + class UnsupportedType(){} + assertThrows(IllegalArgumentException::class.java) {Utils.nativeToValue(UnsupportedType())} + + + + } + + @Test + fun testSensorReadings(){ + val test = Utils.sensorReadingsNativeToValue(nestedStruct) + val response = Utils.sensorReadingsValueToNative(test) + assertEquals(nestedStruct, response) + + } +} \ No newline at end of file