Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSDK-8714: add Servo wrappers #60

Merged
merged 4 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is missing doCommand

}
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)
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing the test for doCommand

@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