Skip to content

Commit

Permalink
Add board component (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
njooma authored May 6, 2024
1 parent 39d8678 commit e3e3a7a
Show file tree
Hide file tree
Showing 11 changed files with 1,438 additions and 185 deletions.
15 changes: 5 additions & 10 deletions core/sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,21 @@ plugins {
id "com.github.hierynomus.license-report" version "0.16.1"
}

apply plugin: 'java-library'
apply plugin: 'kotlin'

ext.pomDisplayName = "Viam Core SDK"

dependencies {
api 'io.grpc:grpc-protobuf-lite:1.62.2'
api 'io.grpc:grpc-stub:1.62.2'
api 'io.grpc:grpc-protobuf-lite:1.63.0'
api 'io.grpc:grpc-stub:1.63.0'
implementation 'org.json:json:20240205'
// TODO: this has DescriptorProtos work for extensions and
// should shortly be in the next lite release.
// see https://github.com/protocolbuffers/protobuf/issues/7331
// In the meantime, there's a small risk of a duplicate class exception if
// someone else has DescriptorProtos from the real protobuf-java/lite.
// A possible workaround when that happens is to exclude in the including app/library.
implementation 'build.buf:protobuf-javalite:4.26.1'
implementation 'com.google.protobuf:protobuf-javalite:4.27.0-RC1'
api 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+
implementation 'commons-io:commons-io:2.15.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation "io.grpc:grpc-testing:1.63.0"
testImplementation "io.grpc:grpc-inprocess:1.63.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

Expand Down
141 changes: 141 additions & 0 deletions core/sdk/src/main/java/com/viam/sdk/core/component/board/Board.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package com.viam.sdk.core.component.board

import com.google.protobuf.Struct
import com.viam.common.v1.Common.ResourceName
import com.viam.component.board.v1.Board.PowerMode
import com.viam.component.board.v1.Board.StreamTicksResponse
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.*
import java.util.stream.Stream
import kotlin.time.Duration

typealias Tick = StreamTicksResponse

/**
* A Board represents a physical general purpose board that contains various
* components such as analog readers and digital interrupts.
*/
abstract class Board(name: String) : Component(SUBTYPE, named(name)) {
companion object {
val SUBTYPE =
Subtype(Subtype.NAMESPACE_RDK, Subtype.RESOURCE_TYPE_COMPONENT, "board")

/**
* Get the ResourceName of the component
* @param name the name of the component
* @return the component's ResourceName
*/
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
*/
fun fromRobot(robot: RobotClient, name: String): Board {
return robot.getResource(Board::class.java, named(name))
}
}

/**
* Set the high/low state of the given pin of a board.
* @param pin the name of the GPIO pin
* @param high when true, sets the pin to high. When false, sets the pin to low.
*/
abstract fun setGpioState(pin: String, high: Boolean, extra: Optional<Struct>)

/**
* Get the high/low state of the given pin of a board.
* @param pin the name of the GPIO pin
* @return the state of the pin: true if high, false otherwise.
*/
abstract fun getGpioState(pin: String, extra: Optional<Struct>): Boolean

/**
* Set the duty cycle of the given pin of a board.
* @param pin the name of the GPIO pin
* @param dutyCyclePct the duty cycle percent
*/
abstract fun setPwm(pin: String, dutyCyclePct: Double, extra: Optional<Struct>)

/**
* Get the duty cycle of the given pin of a board.
* @param pin the name of the pin
* @returns the duty cycle percent
*/
abstract fun getPwm(pin: String, extra: Optional<Struct>): Double

/**
* Set the PWM frequency of the given pin of a board.
* @param pin the name of the pin
* @param frequencyHz the frequency to set
*/
abstract fun setPwmFrequency(pin: String, frequencyHz: Int, extra: Optional<Struct>)

/**
* Get the PWM frequency of the given pin of a board.
* @param pin the name of the pin
* @returns the frequency of the pin in Hz
*/
abstract fun getPwmFrequency(pin: String, extra: Optional<Struct>): Int

/**
* Write analog value to pin.
* @param pin the name of the pin
* @param value the value to set
*/
abstract fun writeAnalog(pin: String, value: Int, extra: Optional<Struct>)

/**
* Read the current value of an analog reader of a board.
* @param analogReader the name of the analog reader
* @returns the current value of the analog reader
*/
abstract fun getAnalogReaderValue(analogReader: String, extra: Optional<Struct>): Int

/**
* Return the current value of the interrupt which is based on the type of Interrupt.
* @param digitalInterrupt the name of the digital interrupt
* @returns the current value of the digital reader
*/
abstract fun getDigitalInterruptValue(
digitalInterrupt: String,
extra: Optional<Struct>
): Int

/**
* Stream digital interrupts ticks.
* @param interrupts the list of digital interrupts names from which to receive ticks
* @returns a [Stream] of [Tick] objects
*/
abstract fun streamTicks(interrupts: List<String>, extra: Optional<Struct>): Iterator<Tick>

/**
* Add a listener for the digital interrupts.
* @param interrupts the list of digital interrupts names from which to receive ticks
* @param tickQueue an object to receive values from the callback
*/
abstract fun addCallbacks(
interrupts: List<String>,
tickQueue: Queue<Tick>,
extra: Optional<Struct>
)

/**
* Set the board to the indicated power mode.
* @param powerMode the power mode to set
* @param duration if provided, the board will exit the given power mode after this duration
*/
abstract fun setPowerMode(
powerMode: PowerMode,
duration: Duration,
extra: Optional<Struct>
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package com.viam.sdk.core.component.board

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.board.v1.Board.*
import com.viam.component.board.v1.BoardServiceGrpc
import com.viam.component.board.v1.BoardServiceGrpc.BoardServiceBlockingStub
import com.viam.sdk.core.exception.MethodNotImplementedException
import com.viam.sdk.core.rpc.Channel
import com.viam.sdk.core.util.Durations
import java.util.*
import kotlin.jvm.optionals.getOrDefault
import kotlin.time.Duration

/**
* gRPC Client for a Board component
*/
class BoardRPCClient(name: String, channel: Channel) : Board(name) {
private val client: BoardServiceBlockingStub

init {
val client = BoardServiceGrpc.newBlockingStub(channel)
if (channel.callCredentials.isPresent) {
this.client = client.withCallCredentials(channel.callCredentials.get())
} else {
this.client = client
}
}

override fun setGpioState(pin: String, high: Boolean, extra: Optional<Struct>) {
val request = SetGPIORequest.newBuilder()
.setName(this.name.name)
.setPin(pin).setHigh(high)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
this.client.setGPIO(request)
}

override fun getGpioState(pin: String, extra: Optional<Struct>): Boolean {
val request = GetGPIORequest.newBuilder()
.setName(this.name.name)
.setPin(pin)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
return this.client.getGPIO(request).high
}

override fun setPwm(pin: String, dutyCyclePct: Double, extra: Optional<Struct>) {
val request = SetPWMRequest.newBuilder()
.setName(this.name.name)
.setPin(pin)
.setDutyCyclePct(dutyCyclePct)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
this.client.setPWM(request)
}

override fun getPwm(pin: String, extra: Optional<Struct>): Double {
val request = PWMRequest.newBuilder()
.setName(this.name.name)
.setPin(pin)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
return this.client.pWM(request).dutyCyclePct
}

override fun setPwmFrequency(pin: String, frequencyHz: Int, extra: Optional<Struct>) {
val request = SetPWMFrequencyRequest.newBuilder()
.setName(this.name.name)
.setPin(pin)
.setFrequencyHz(frequencyHz.toLong())
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
this.client.setPWMFrequency(request)
}

override fun getPwmFrequency(pin: String, extra: Optional<Struct>): Int {
val request = PWMFrequencyRequest.newBuilder()
.setName(this.name.name)
.setPin(pin)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
return this.client.pWMFrequency(request).frequencyHz.toInt()
}

override fun writeAnalog(pin: String, value: Int, extra: Optional<Struct>) {
val request = WriteAnalogRequest.newBuilder()
.setName(this.name.name)
.setPin(pin)
.setValue(value)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
this.client.writeAnalog(request)
}

override fun getAnalogReaderValue(analogReader: String, extra: Optional<Struct>): Int {
val request = ReadAnalogReaderRequest.newBuilder()
.setBoardName(this.name.name)
.setAnalogReaderName(analogReader)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
return this.client.readAnalogReader(request).value
}

override fun getDigitalInterruptValue(digitalInterrupt: String, extra: Optional<Struct>): Int {
val request = GetDigitalInterruptValueRequest.newBuilder()
.setBoardName(this.name.name)
.setDigitalInterruptName(digitalInterrupt)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
return this.client.getDigitalInterruptValue(request).value.toInt()
}

override fun streamTicks(interrupts: List<String>, extra: Optional<Struct>): Iterator<Tick> {
val request = StreamTicksRequest.newBuilder()
.setName(this.name.name)
.addAllPinNames(interrupts)
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
return this.client.streamTicks(request)
}

override fun addCallbacks(
interrupts: List<String>,
tickQueue: Queue<Tick>,
extra: Optional<Struct>
) {
throw MethodNotImplementedException("BoardRPCClient.addCallbacks")
}

override fun setPowerMode(
powerMode: PowerMode,
duration: Duration,
extra: Optional<Struct>
) {
val request = SetPowerModeRequest.newBuilder()
.setName(this.name.name)
.setPowerMode(powerMode)
.setDuration(Durations.fromNanos(duration.inWholeNanoseconds))
.setExtra(extra.getOrDefault(Struct.getDefaultInstance()))
.build()
this.client.setPowerMode(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
}
}
Loading

0 comments on commit e3e3a7a

Please sign in to comment.