Skip to content

Commit 7c7a451

Browse files
authored
RSDK-10312: Button wrapper (#107)
1 parent bdd7b46 commit 7c7a451

File tree

7 files changed

+288
-0
lines changed

7 files changed

+288
-0
lines changed

core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java

+8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.viam.component.arm.v1.ArmServiceGrpc;
77
import com.viam.component.audioinput.v1.AudioInputServiceGrpc;
88
import com.viam.component.board.v1.BoardServiceGrpc;
9+
import com.viam.component.button.v1.ButtonServiceGrpc;
910
import com.viam.component.camera.v1.CameraServiceGrpc;
1011
import com.viam.component.encoder.v1.EncoderServiceGrpc;
1112
import com.viam.component.gantry.v1.GantryServiceGrpc;
@@ -17,6 +18,7 @@
1718
import com.viam.component.sensor.v1.SensorServiceGrpc;
1819
import com.viam.component.v1.PoseTrackerServiceGrpc;
1920
import com.viam.sdk.core.component.base.*;
21+
import com.viam.sdk.core.component.button.*;
2022
import com.viam.sdk.core.component.arm.*;
2123
import com.viam.component.servo.v1.ServoServiceGrpc;
2224
import com.viam.sdk.core.component.audioinput.*;
@@ -99,6 +101,12 @@ public class ResourceManager implements Closeable {
99101
BoardRPCService::new,
100102
BoardRPCClient::new
101103
));
104+
Registry.registerSubtype(new ResourceRegistration<>(
105+
Button.SUBTYPE,
106+
ButtonServiceGrpc.SERVICE_NAME,
107+
ButtonRPCService::new,
108+
ButtonRPCClient::new
109+
));
102110
Registry.registerSubtype(new ResourceRegistration<>(
103111
Camera.SUBTYPE,
104112
CameraServiceGrpc.SERVICE_NAME,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.viam.sdk.core.component.button
2+
3+
import com.google.protobuf.Struct
4+
import com.google.protobuf.Value
5+
import com.viam.common.v1.Common.*
6+
import com.viam.sdk.core.component.Component
7+
import com.viam.sdk.core.resource.Resource
8+
import com.viam.sdk.core.resource.Subtype
9+
import com.viam.sdk.core.robot.RobotClient
10+
11+
/**
12+
* A Button represents a physical or virtual button that can be pushed.
13+
*/
14+
abstract class Button(name: String) : Component(SUBTYPE, named(name)) {
15+
companion object {
16+
@JvmField
17+
val SUBTYPE = Subtype(Subtype.NAMESPACE_RDK, Subtype.RESOURCE_TYPE_COMPONENT, "button")
18+
19+
/**
20+
* Get the ResourceName of the component
21+
* @param name the name of the component
22+
* @return the component's ResourceName
23+
*/
24+
@JvmStatic
25+
fun named(name: String): ResourceName {
26+
return Resource.named(SUBTYPE, name)
27+
}
28+
29+
/**
30+
* Get the component with the provided name from the provided robot.
31+
* @param robot the RobotClient
32+
* @param name the name of the component
33+
* @return the component
34+
*/
35+
@JvmStatic
36+
fun fromRobot(robot: RobotClient, name: String): Button {
37+
return robot.getResource(Button::class.java, named(name))
38+
}
39+
}
40+
41+
/**
42+
* Push the button.
43+
* @param extra Additional arguments to the method
44+
*/
45+
abstract fun push(extra: Struct)
46+
47+
/**
48+
* Push the button.
49+
*/
50+
fun push() {
51+
push(Struct.getDefaultInstance())
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.viam.sdk.core.component.button
2+
3+
import com.google.protobuf.Struct
4+
import com.google.protobuf.Value
5+
import com.viam.common.v1.Common
6+
import com.viam.common.v1.Common.GetGeometriesRequest
7+
import com.viam.component.button.v1.ButtonServiceGrpc
8+
import com.viam.component.button.v1.ButtonServiceGrpc.ButtonServiceBlockingStub
9+
import com.viam.component.button.v1.Button.*
10+
import com.viam.sdk.core.rpc.Channel
11+
import java.util.*
12+
import kotlin.jvm.optionals.getOrDefault
13+
14+
class ButtonRPCClient(name: String, channel: Channel) : Button(name) {
15+
private val client: ButtonServiceBlockingStub
16+
17+
init {
18+
val client = ButtonServiceGrpc.newBlockingStub(channel)
19+
if (channel.callCredentials.isPresent) {
20+
this.client = client.withCallCredentials(channel.callCredentials.get())
21+
} else {
22+
this.client = client
23+
}
24+
}
25+
26+
override fun push(extra: Struct) {
27+
val request = PushRequest.newBuilder().setName(this.name.name).setExtra(extra).build()
28+
this.client.push(request)
29+
}
30+
31+
override fun doCommand(command: Map<String, Value>?): Struct {
32+
val request = Common.DoCommandRequest.newBuilder().setName(this.name.name)
33+
.setCommand(Struct.newBuilder().putAllFields(command).build()).build()
34+
val response = this.client.doCommand(request)
35+
return response.result
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.viam.sdk.core.component.button
2+
3+
import com.viam.common.v1.Common.*
4+
import com.viam.component.button.v1.Button.*
5+
import com.viam.component.button.v1.ButtonServiceGrpc
6+
import com.viam.sdk.core.resource.ResourceManager
7+
import com.viam.sdk.core.resource.ResourceRPCService
8+
import io.grpc.stub.StreamObserver
9+
10+
internal class ButtonRPCService(private val manager: ResourceManager) : ButtonServiceGrpc.ButtonServiceImplBase(),
11+
ResourceRPCService<Button> {
12+
13+
override fun push(
14+
request: PushRequest,
15+
responseObserver: StreamObserver<PushResponse>
16+
) {
17+
val button = getResource(Button.named(request.name))
18+
button.push(request.extra)
19+
responseObserver.onNext(PushResponse.newBuilder().build())
20+
responseObserver.onCompleted()
21+
}
22+
23+
override fun doCommand(
24+
request: DoCommandRequest, responseObserver: StreamObserver<DoCommandResponse>
25+
) {
26+
val button = getResource(Button.named(request.name))
27+
val result = button.doCommand(request.command.fieldsMap)
28+
responseObserver.onNext(DoCommandResponse.newBuilder().setResult(result).build())
29+
responseObserver.onCompleted()
30+
}
31+
32+
override fun getResourceClass(): Class<Button> {
33+
return Button::class.java
34+
}
35+
36+
override fun getManager(): ResourceManager {
37+
return this.manager
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.viam.sdk.core.component.button
2+
3+
import com.google.protobuf.Struct
4+
import com.google.protobuf.Value
5+
import com.viam.sdk.core.resource.ResourceManager
6+
import com.viam.sdk.core.rpc.BasicManagedChannel
7+
import io.grpc.inprocess.InProcessChannelBuilder
8+
import io.grpc.inprocess.InProcessServerBuilder
9+
import io.grpc.testing.GrpcCleanupRule
10+
import org.junit.Rule
11+
import org.junit.jupiter.api.Assertions.assertEquals
12+
import org.junit.jupiter.api.BeforeEach
13+
import org.junit.jupiter.api.Test
14+
import org.mockito.Mockito.*
15+
import java.util.*
16+
17+
class ButtonRPCClientTest {
18+
private lateinit var button: Button
19+
private lateinit var client: ButtonRPCClient
20+
21+
@JvmField
22+
@Rule
23+
val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule()
24+
25+
@BeforeEach
26+
fun setup() {
27+
button = mock(
28+
Button::class.java, withSettings().useConstructor("mock-button").defaultAnswer(
29+
CALLS_REAL_METHODS
30+
)
31+
)
32+
val resourceManager = ResourceManager(listOf(button))
33+
val service = ButtonRPCService(resourceManager)
34+
val serviceName = InProcessServerBuilder.generateName()
35+
grpcCleanupRule.register(
36+
InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start()
37+
)
38+
val channel = grpcCleanupRule.register(InProcessChannelBuilder.forName(serviceName).directExecutor().build())
39+
client = ButtonRPCClient("mock-button", BasicManagedChannel(channel))
40+
}
41+
42+
@Test
43+
fun push() {
44+
client.push()
45+
verify(button).push(Struct.getDefaultInstance())
46+
}
47+
48+
@Test
49+
fun doCommand() {
50+
val command = mapOf("foo" to Value.newBuilder().setStringValue("bar").build())
51+
doReturn(Struct.newBuilder().putAllFields(command).build()).`when`(button).doCommand(anyMap())
52+
val response = client.doCommand(command)
53+
verify(button).doCommand(command)
54+
assertEquals(command, response.fieldsMap)
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.viam.sdk.core.component.button
2+
3+
import com.google.protobuf.Struct
4+
import com.google.protobuf.Value
5+
import com.viam.common.v1.Common
6+
import com.viam.component.button.v1.Button.*
7+
import com.viam.component.button.v1.ButtonServiceGrpc
8+
import com.viam.component.button.v1.ButtonServiceGrpc.ButtonServiceBlockingStub
9+
import com.viam.sdk.core.resource.ResourceManager
10+
import io.grpc.inprocess.InProcessChannelBuilder
11+
import io.grpc.inprocess.InProcessServerBuilder
12+
import io.grpc.testing.GrpcCleanupRule
13+
import org.junit.Rule
14+
import org.junit.jupiter.api.Assertions.assertEquals
15+
import org.junit.jupiter.api.BeforeEach
16+
import org.junit.jupiter.api.Test
17+
import org.mockito.Mockito.*
18+
19+
class ButtonRPCServiceTest {
20+
21+
private lateinit var button: Button
22+
private lateinit var client: ButtonServiceBlockingStub
23+
24+
@JvmField
25+
@Rule
26+
val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule()
27+
28+
@BeforeEach
29+
fun setup() {
30+
button = mock(
31+
Button::class.java, withSettings().useConstructor("mock-button").defaultAnswer(
32+
CALLS_REAL_METHODS
33+
)
34+
)
35+
36+
val resourceManager = ResourceManager(listOf(button))
37+
val service = ButtonRPCService(resourceManager)
38+
val serviceName = InProcessServerBuilder.generateName()
39+
grpcCleanupRule.register(
40+
InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start()
41+
)
42+
client = ButtonServiceGrpc.newBlockingStub(
43+
grpcCleanupRule.register(
44+
InProcessChannelBuilder.forName(serviceName).build()
45+
)
46+
)
47+
}
48+
49+
@Test
50+
fun push() {
51+
val request = PushRequest.newBuilder().setName(button.name.name).build()
52+
client.push(request)
53+
verify(button).push(Struct.getDefaultInstance())
54+
}
55+
56+
@Test
57+
fun doCommand() {
58+
val command =
59+
Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build()
60+
doReturn(command).`when`(button).doCommand(anyMap())
61+
val request = Common.DoCommandRequest.newBuilder().setName(button.name.name).setCommand(command).build()
62+
val response = client.doCommand(request)
63+
verify(button).doCommand(command.fieldsMap)
64+
assertEquals(command, response.result)
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.viam.sdk.core.component.button
2+
3+
import com.google.protobuf.Struct
4+
import org.junit.jupiter.api.BeforeEach
5+
import org.junit.jupiter.api.Test
6+
import org.mockito.Answers
7+
import org.mockito.Mockito.*
8+
9+
class ButtonTest {
10+
private lateinit var button: Button
11+
12+
@BeforeEach
13+
fun setup() {
14+
button = mock(Button::class.java, Answers.CALLS_REAL_METHODS)
15+
}
16+
17+
@Test
18+
fun push() {
19+
button.push()
20+
verify(button).push()
21+
}
22+
23+
@Test
24+
fun pushWithExtra() {
25+
val extra = Struct.getDefaultInstance()
26+
button.push(extra)
27+
verify(button).push(extra)
28+
}
29+
}

0 commit comments

Comments
 (0)