Skip to content

Commit

Permalink
Removed DSL calls that would allow undesirable behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
camearle20 committed Mar 2, 2020
1 parent 7adca77 commit e031f63
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 38 deletions.
66 changes: 39 additions & 27 deletions SnakeSkin-Core/src/main/kotlin/org/snakeskin/dsl/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import org.snakeskin.measure.time.TimeMeasureSeconds
import org.snakeskin.state.*
import org.snakeskin.state.StateMachine
import org.snakeskin.subsystem.States
import kotlin.concurrent.timerTask

/**
* @author Cameron Earle
Expand All @@ -16,7 +15,8 @@ import kotlin.concurrent.timerTask
@DslMarker
annotation class DslMarkerState

open class StateMachineBuilderContext<T> {
@DslMarkerState
class StateMachineBuilder<T> {
internal val machine = StateMachine<T>()

/**
Expand All @@ -32,10 +32,7 @@ open class StateMachineBuilderContext<T> {
* @return true if the machine was in the state, false otherwise
*/
fun wasInState(state: T) = machine.wasInState(state)
}

@DslMarkerState
class StateMachineBuilder<T>: StateMachineBuilderContext<T>() {
/**
* Rejects all of the given states if the condition is met
* @param states The states to reject
Expand Down Expand Up @@ -71,24 +68,27 @@ class StateMachineBuilder<T>: StateMachineBuilderContext<T>() {
* Builds a State object
*/
@DslMarkerState
open class StateBuilder<T>(name: T, protected val machine: StateMachine<*>) {
open class StateBuilder<T>(name: T, private val machine: StateMachine<*>) {
@DslMarkerState
object Context

@DslMarkerState
inner class Context(val async: Boolean) {
inner class ActionContext(val async: Boolean) {
/**
* Sets the state of this machine
* @param state The state to set
*/
fun setState(state: T) = this@StateBuilder.machine.setStateInternal(state as Any, async)
fun setState(state: T) = this@StateBuilder.machine.setStateFromAction(state as Any, async)

/**
* Sets the machine to the disabled state
*/
fun disable() = this@StateBuilder.machine.disableInternal(async)
fun disable() = this@StateBuilder.machine.disableFromAction(async)

/**
* Sets the machine to the state it was last in
*/
fun back() = this@StateBuilder.machine.backInternal(async)
fun back() = this@StateBuilder.machine.backFromAction(async)
}

internal val state = State(name)
Expand All @@ -98,7 +98,7 @@ open class StateBuilder<T>(name: T, protected val machine: StateMachine<*>) {
* @param entryBlock The function to run on the state's entry
*/
fun entry(entryBlock: Context.() -> Unit) {
val context = Context(false)
val context = Context
state.entry = ExceptionHandlingRunnable { context.entryBlock() }
}

Expand All @@ -107,8 +107,8 @@ open class StateBuilder<T>(name: T, protected val machine: StateMachine<*>) {
* @param rate The rate, in ms, to run the action loop at
* @param actionBlock The function to run on the state's loop
*/
fun action(rate: TimeMeasureSeconds = TimeMeasureSeconds(0.02), actionBlock: Context.() -> Unit) {
val context = Context(false)
fun action(rate: TimeMeasureSeconds = TimeMeasureSeconds(0.02), actionBlock: ActionContext.() -> Unit) {
val context = ActionContext(false)
state.actionManager = DefaultStateActionManager({ context.actionBlock() }, rate)
}

Expand All @@ -117,8 +117,8 @@ open class StateBuilder<T>(name: T, protected val machine: StateMachine<*>) {
* @param executorName The name of the real-time executor to use. If not provided, the default RT executor is used
* @param rtActionBlock The function to run on the state's loop. The first parameter is the timestamp, the second is the dt
*/
fun rtAction(executorName: String? = null, rtActionBlock: Context.(timestamp: TimeMeasureSeconds, dt: TimeMeasureSeconds) -> Unit) {
val context = Context(true)
fun rtAction(executorName: String? = null, rtActionBlock: ActionContext.(timestamp: TimeMeasureSeconds, dt: TimeMeasureSeconds) -> Unit) {
val context = ActionContext(true)
state.actionManager = RealTimeStateActionManager({ t, d -> context.rtActionBlock(t, d) }, executorName, state.name.toString())
}

Expand All @@ -130,8 +130,8 @@ open class StateBuilder<T>(name: T, protected val machine: StateMachine<*>) {
* @param actionBlock The function to run once the condition has evaluated to true. This function will run continuously after this.
* @param rate The rate at which to check the condition and execute the loop at. Defaults to 20 milliseconds.
*/
fun tickedAction(timeThreshold: TimeMeasureSeconds, condition: () -> Boolean, actionBlock: Context.() -> Unit, rate: TimeMeasureSeconds = TimeMeasureSeconds(0.02)) {
val context = Context(false)
fun tickedAction(timeThreshold: TimeMeasureSeconds, condition: () -> Boolean, actionBlock: ActionContext.() -> Unit, rate: TimeMeasureSeconds = TimeMeasureSeconds(0.02)) {
val context = ActionContext(false)
state.actionManager = TickedActionManager(condition, { context.actionBlock() }, timeThreshold, rate)
}

Expand All @@ -140,28 +140,40 @@ open class StateBuilder<T>(name: T, protected val machine: StateMachine<*>) {
* @param exitBlock The function to run on the state's exit
*/
fun exit(exitBlock: Context.() -> Unit) {
val context = Context(false)
val context = Context
state.exit = ExceptionHandlingRunnable { context.exitBlock() }
}

/**
* Accessor for the rate at which the action loop of this state runs
*/
val actionRate: TimeMeasureSeconds
get() = state.actionManager.rate
}

/**
* Adds functionality to the StateBuilder, with certain special functions that can't be used in default or disabled states
*/
@DslMarkerState
class MutableStateBuilder<T>(name: T, machine: StateMachine<*>): StateBuilder<T>(name, machine) {
class MutableStateBuilder<T>(name: T, val machine: StateMachine<T>): StateBuilder<T>(name, machine) {
@DslMarkerState
inner class RejectContext {
/**
* Checks if the machine is in the state given
* @param state The state to check
* @return true if the machine is in the state, false otherwise
*/
fun isInState(state: T) = this@MutableStateBuilder.machine.isInState(state)

/**
* Checks if the machine was in the state given
* @param state The state to check
* @return true if the machine was in the state, false otherwise
*/
fun wasInState(state: T) = this@MutableStateBuilder.machine.wasInState(state)
}

/**
* Adds rejection conditions for this state.
* @param condition The function to run to check the conditions. Should return true if the state change should be rejected
*/
fun rejectIf(condition: () -> Boolean) {
state.rejectionConditions = condition
fun rejectIf(condition: RejectContext.() -> Boolean) {
val context = RejectContext()
state.rejectionConditions = { context.condition() }
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,47 @@
package org.snakeskin.runtime.impl

import org.snakeskin.executor.IExecutor
import org.snakeskin.executor.impl.ScheduledExecutorServiceExecutor
import org.snakeskin.hid.IHIDValueProviderFactory
import org.snakeskin.hid.impl.NullHIDValueProviderFactory
import org.snakeskin.rt.IRealTimeExecutor
import org.snakeskin.rt.impl.NullRealTimeExecutor
import org.snakeskin.runtime.IRuntimePlatformBinding
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit

class RuntimePlatformBindingSoftware: IRuntimePlatformBinding {
override fun blockMilliseconds(ms: Long) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
Thread.sleep(ms)
}

override fun getSystemVbusVolts(): Double {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
return 0.0
}

override fun allocatePrimaryExecutor(): IExecutor {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
val pool = ScheduledThreadPoolExecutor(8)
pool.setKeepAliveTime(10, TimeUnit.SECONDS)
pool.allowCoreThreadTimeOut(true)

return ScheduledExecutorServiceExecutor(pool)
}

override fun allocateSingleUseExecutor(): IExecutor {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
val executor = ScheduledThreadPoolExecutor(1)

return ScheduledExecutorServiceExecutor(executor)
}

override fun allocateRealTimeExecutor(rateSeconds: Double): IRealTimeExecutor {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
return NullRealTimeExecutor
}

override fun getTimestampSeconds(): Double {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
return System.nanoTime() * 1e-9
}

override fun allocateHIDValueProviderFactory(id: Int): IHIDValueProviderFactory {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
return NullHIDValueProviderFactory
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class StateMachine<T> {

if (activeState?.timeout?.value != -1.0) {
activeTimeoutHandle = executor.scheduleSingleTask(ExceptionHandlingRunnable {
activeState?.timeoutTo?.let { setStateInternal(it, false) }
activeState?.timeoutTo?.let { setStateFromAction(it, false) }
}, activeState?.timeout ?: 0.0.Seconds)
}

Expand All @@ -129,7 +129,7 @@ class StateMachine<T> {
return NullWaitable
}

internal fun setStateInternal(state: Any, async: Boolean) {
internal fun setStateFromAction(state: Any, async: Boolean) {
if (startTransition(state)) {
if (async) {
executor.scheduleSingleTaskNow(ExceptionHandlingRunnable { finishTransition(state, null) })
Expand All @@ -139,7 +139,7 @@ class StateMachine<T> {
}
}

internal fun disableInternal(async: Boolean) {
internal fun disableFromAction(async: Boolean) {
if (startTransition(States.DISABLED)) {
if (async) {
executor.scheduleSingleTaskNow(ExceptionHandlingRunnable { finishTransition(States.DISABLED, null) })
Expand All @@ -149,7 +149,7 @@ class StateMachine<T> {
}
}

internal fun backInternal(async: Boolean) {
internal fun backFromAction(async: Boolean) {
val lastState = getLastState()
if (startTransition(lastState)) {
if (async) {
Expand Down

0 comments on commit e031f63

Please sign in to comment.