-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
875f9b6
commit cd11f8e
Showing
7 changed files
with
515 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/Encoder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package com.viam.sdk.core.component.encoder | ||
|
||
import com.google.protobuf.Struct | ||
import com.viam.component.encoder.v1.Encoder.* | ||
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 | ||
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 it's ``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<Float, PositionType> | ||
|
||
/** | ||
* Report the position of the encoder. | ||
* The value returned is the current position in terms of it's ``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<Float, PositionType>{ | ||
return getPosition(positionType, Struct.getDefaultInstance()) | ||
} | ||
|
||
/** | ||
* Report the position of the encoder. | ||
* The value returned is the current position in terms of it's ``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<Float, PositionType>{ | ||
return getPosition( null, extra) | ||
} | ||
|
||
/** | ||
* Report the position of the encoder. | ||
* The value returned is the current position in terms of it's ``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<Float, PositionType>{ | ||
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()) | ||
} | ||
|
||
|
||
} |
57 changes: 57 additions & 0 deletions
57
core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCClient.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
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.EncoderServiceGrpc | ||
import com.viam.component.encoder.v1.EncoderServiceGrpc.EncoderServiceBlockingStub | ||
import com.viam.component.encoder.v1.Encoder.* | ||
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<Float, PositionType> { | ||
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<String, Value>?): 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<Struct>): List<Common.Geometry> { | ||
val request = GetGeometriesRequest.newBuilder().setName(this.name.name) | ||
.setExtra(extra.getOrDefault(Struct.getDefaultInstance())).build() | ||
val response = this.client.getGeometries(request) | ||
return response.geometriesList | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
core/sdk/src/main/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
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<Encoder> { | ||
|
||
override fun resetPosition( | ||
request: ResetPositionRequest, | ||
responseObserver: StreamObserver<ResetPositionResponse> | ||
) { | ||
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<GetPositionResponse>) { | ||
val encoder = getResource(Encoder.named(request.name)) | ||
val position: Pair<Float, PositionType> = 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<GetPropertiesResponse> | ||
) { | ||
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<DoCommandResponse> | ||
) { | ||
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<GetGeometriesResponse> | ||
) { | ||
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<Encoder> { | ||
return Encoder::class.java | ||
} | ||
|
||
override fun getManager(): ResourceManager { | ||
return this.manager | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
core/sdk/src/test/kotlin/com/viam/sdk/core/component/encoder/EncoderRPCClientTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
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.* | ||
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<Geometry>()).`when`(encoder).getGeometries(any()) | ||
client.getGeometries(Optional.empty()) | ||
verify(encoder).getGeometries(any()) | ||
} | ||
} |
Oops, something went wrong.