Skip to content

Commit

Permalink
RSDK-8714: add Servo wrappers (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
gloriacai01 committed Sep 11, 2024
1 parent 875f9b6 commit ecac847
Show file tree
Hide file tree
Showing 8 changed files with 497 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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();


}

Original file line number Diff line number Diff line change
@@ -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<String, Value> 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<Common.Geometry> getGeometries(Optional<Struct> extra){
Common.GetGeometriesRequest.Builder builder = Common.GetGeometriesRequest.newBuilder().setName(this.getName().getName());
extra.ifPresent(builder::setExtra);
return client.getGeometries(builder.build()).getGeometriesList();

}
}

Original file line number Diff line number Diff line change
@@ -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<Servo> {

private final ResourceManager manager;
public ServoRPCService(ResourceManager manager) { this.manager = manager; }
@Override
public Class<Servo> getResourceClass() {
return Servo.class;
}

@Override
public ResourceManager getManager() {
return manager;
}

@Override
public void move(com.viam.component.servo.v1.Servo.MoveRequest request, StreamObserver<com.viam.component.servo.v1.Servo.MoveResponse> 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<com.viam.component.servo.v1.Servo.StopResponse> 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<com.viam.component.servo.v1.Servo.GetPositionResponse> 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<com.viam.component.servo.v1.Servo.IsMovingResponse> 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<com.viam.common.v1.Common.GetGeometriesResponse> responseObserver){
Servo servo = getResource(Servo.named(request.getName()));
List<Common.Geometry> 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<Common.DoCommandResponse> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.viam.component.motor.v1.MotorServiceGrpc;
import com.viam.component.movementsensor.v1.MovementSensorServiceGrpc;
import com.viam.component.sensor.v1.SensorServiceGrpc;
import com.viam.component.servo.v1.ServoServiceGrpc;
import com.viam.sdk.core.component.board.Board;
import com.viam.sdk.core.component.board.BoardRPCClient;
import com.viam.sdk.core.component.board.BoardRPCService;
Expand All @@ -30,6 +31,9 @@
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;
Expand Down Expand Up @@ -95,6 +99,12 @@ public class ResourceManager implements Closeable {
SensorRPCService::new,
SensorRPCClient::new
));
Registry.registerSubtype(new ResourceRegistration<>(
Servo.SUBTYPE,
ServoServiceGrpc.SERVICE_NAME,
ServoRPCService::new,
ServoRPCClient::new
));

// SERVICES
Registry.registerSubtype(new ResourceRegistration<>(
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Geometry>()).`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)
}


}
Loading

0 comments on commit ecac847

Please sign in to comment.