Skip to content

Commit

Permalink
add powersensor/powersensor tests and also utils for converting betwe…
Browse files Browse the repository at this point in the history
…en protobuf value / native types
  • Loading branch information
gloriacai01 committed Sep 3, 2024
1 parent 4ea7699 commit 8dd6094
Show file tree
Hide file tree
Showing 9 changed files with 799 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<Double, Boolean> getVoltage(Optional<Struct> 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<Double, Boolean> getCurrent(Optional<Struct> extra);

/**
* Return the power reading in watts.
*
* @return the power reading in watts
*/
public abstract double getPower(Optional<Struct> 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<String, Object> getReadings(Optional<Struct> extra);


}
Original file line number Diff line number Diff line change
@@ -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<String, Value> command) {
return client.doCommand(Common.DoCommandRequest.newBuilder().
setName(getName().getName()).
setCommand(Struct.newBuilder().putAllFields(command).build()).
build()).getResult();
}

@Override
public Entry<Double, Boolean> getVoltage(Optional<Struct> 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<Double, Boolean> getCurrent(Optional<Struct> 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<Struct> 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<String, Object> getReadings(Optional<Struct> extra) {
Common.GetReadingsRequest.Builder builder = Common.GetReadingsRequest.newBuilder().setName(this.getName().getName());
extra.ifPresent(builder::setExtra);
return Utils.sensorReadingsValueToNative(client.getReadings(builder.build()).getReadingsMap());

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

private final ResourceManager manager;

public PowerSensorRPCService(ResourceManager manager) {
this.manager = manager;
}

@Override
public Class<PowerSensor> getResourceClass() {
return PowerSensor.class;
}

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


@Override
public void getVoltage(Powersensor.GetVoltageRequest request, StreamObserver<Powersensor.GetVoltageResponse> responseObserver) {
PowerSensor powerSensor = getResource(PowerSensor.named(request.getName()));
Entry<Double, Boolean> 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<Powersensor.GetCurrentResponse> responseObserver) {
PowerSensor powerSensor = getResource(PowerSensor.named(request.getName()));
Entry<Double, Boolean> 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<Powersensor.GetPowerResponse> 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<Common.GetReadingsResponse> responseObserver) {
PowerSensor powerSensor = getResource(PowerSensor.named(request.getName()));
Map<String, Value> 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<Common.DoCommandResponse> 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();
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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.component.servo.v1.ServoServiceGrpc;
import com.viam.sdk.core.component.board.Board;
Expand All @@ -28,6 +29,9 @@
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;
Expand Down Expand Up @@ -105,6 +109,12 @@ public class ResourceManager implements Closeable {
ServoRPCService::new,
ServoRPCClient::new
));
Registry.registerSubtype(new ResourceRegistration<>(
PowerSensor.SUBTYPE,
PowerSensorServiceGrpc.SERVICE_NAME,
PowerSensorRPCService::new,
PowerSensorRPCClient::new
));

// SERVICES
Registry.registerSubtype(new ResourceRegistration<>(
Expand Down
131 changes: 131 additions & 0 deletions core/sdk/src/main/java/com/viam/sdk/core/util/Utils.java
Original file line number Diff line number Diff line change
@@ -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<String, Value> 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<String, Object> nativeMap = new HashMap<>();
for (Map.Entry<String, Value> entry : structMap.entrySet()) {
nativeMap.put(entry.getKey(), valueToNative(entry.getValue()));
}
return nativeMap;

}


}
case LIST_VALUE: {
List<Object> 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<Object>) 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<Object, Object> entry : ((Map<Object, Object>) 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<String, Value> sensorReadingsNativeToValue(Map<String, Object> readings) {
Map<String, Value> valueMap = new HashMap<>();
for (Map.Entry<String, Object> entry : readings.entrySet()) {
valueMap.put(entry.getKey(), nativeToValue(entry.getValue()));
}
return valueMap;
}

public static Map<String, Object> sensorReadingsValueToNative(Map<String, Value> readings) {
Map<String, Object> map = new HashMap<>();
for (Map.Entry<String, Value> entry : readings.entrySet()) {
map.put(entry.getKey(), valueToNative(entry.getValue()));

}
return map;
}


}
Loading

0 comments on commit 8dd6094

Please sign in to comment.