Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/kotlin/at/ac/uibk/dps/cirrina/Runtime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ constructor(
eventHandler.bind(
graph = graph,
instanceNames = run,
subscribedTo = instances.values.flatMap { it.subscriptions },
subscribedTo = instances.values.flatMap { it.subscriptions } /*.toSet()*/,
handlers = instances.values.map { it::pushEvent },
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ sealed interface Action {
description.type,
description.mode,
buildVariables(description.input),
buildEvents(description.emits),
description.output,
buildConditionalEvents(description.emits),
)

is MatchDescription ->
Expand Down Expand Up @@ -57,7 +58,8 @@ sealed interface Action {
ContextVariable.lazy(k, expression)
}

private fun buildEvents(events: List<EventDescription>) = events.map { Event.from(it) }
private fun buildConditionalEvents(events: List<ConditionalEventDescription>) =
events.map { ConditionalEvent.from(it) }
}
}

Expand All @@ -74,12 +76,13 @@ internal constructor(
val type: String,
val mode: InvocationMode,
val input: List<ContextVariable>,
val emits: List<Event>,
val output: List<String>,
val emits: List<ConditionalEvent>,
) : EventRaisingAction {
override fun raises(): List<Event> = emits
override fun raises(): List<Event> = emits.map { it.event }

override fun toString() =
"InvokeAction(type='$type', mode='$mode', input='$input', emits='$emits')"
"InvokeAction(type='$type', mode='$mode', input='$input', output='$output', emits='$emits')"
}

class MatchAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,30 @@ class ActionExecutor(
val delta = measureTime {
runCatching { service.invoke(input) }
.onSuccess { output ->
action.emits.forEach { eventTemplate ->
val emittedEvent = eventTemplate.copy(data = output)
if (emittedEvent.channel == EventChannel.INTERNAL) {
eventHandler.propagateToParent(emittedEvent)
} else {
eventHandler.emit(emittedEvent)
action.output.forEach { reference ->
output
.firstOrNull { it.name == reference }
?.let {
runCatching { scope.extent.set(reference, it.value) }
.onFailure { e ->
logger.warn(e) {
"failed to assign service output to variable '${reference}'"
}
}
}
?: logger.warn {
"service output does not contain expected variable '${reference}'"
}
}

action.emits.forEach { conditionalEvent ->
if (conditionalEvent.provided?.evaluate(scope.extent) != false) {
val emittedEvent = conditionalEvent.event.copy(data = output)
if (emittedEvent.channel == EventChannel.INTERNAL) {
eventHandler.propagateToParent(emittedEvent)
} else {
eventHandler.emit(emittedEvent)
}
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/Event.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package at.ac.uibk.dps.cirrina.execution.`object`

import at.ac.uibk.dps.cirrina.csm.Csml.ConditionalEventDescription
import at.ac.uibk.dps.cirrina.csm.Csml.EventChannel
import at.ac.uibk.dps.cirrina.csm.Csml.EventDescription
import at.ac.uibk.dps.cirrina.util.getInsecureUuid
Expand Down Expand Up @@ -29,3 +30,17 @@ data class Event(
}
}
}

data class ConditionalEvent(val provided: Guard?, val event: Event) {
override fun toString(): String =
"${this::class.simpleName}(provided='$provided', event='$event')"

companion object {
fun from(description: ConditionalEventDescription): ConditionalEvent {
return ConditionalEvent(
description.provided?.let { Guard.from(it) },
Event.from(description.event),
)
}
}
}
9 changes: 8 additions & 1 deletion src/main/resources/pkl/csm/csml.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,13 @@ class InvokeDescription extends ActionDescription {
type: InvocationType
mode: InvocationMode = "remote"
input: Context
emits: Listing<EventDescription>
output: Listing<VariableName>
emits: Listing<ConditionalEventDescription>
}

class ConditionalEventDescription {
provided: Expression?
event: EventDescription
}

class EvalDescription extends ActionDescription {
Expand Down Expand Up @@ -187,6 +193,7 @@ typealias Transition = TransitionDescription
typealias Action = ActionDescription
typealias Eval = EvalDescription
typealias Invoke = InvokeDescription
typealias ConditionalEvent = ConditionalEventDescription
typealias Case = CaseDescription
typealias Match = MatchDescription
typealias Emit = EmitDescription
Expand Down
2 changes: 1 addition & 1 deletion src/test/kotlin/at/ac/uibk/dps/cirrina/InvokeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class InvokeTest {
val duration = measureTime { runtime.run() }
println("invoke execution: $duration")

assertEquals(10, context.get("v"))
assertEquals(100, context.get("v"))
} finally {
server.stop(1)
}
Expand Down
34 changes: 34 additions & 0 deletions src/test/kotlin/at/ac/uibk/dps/cirrina/LoopTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package at.ac.uibk.dps.cirrina

import at.ac.uibk.dps.cirrina.data.DefaultDescriptions
import at.ac.uibk.dps.cirrina.di.DaggerTestComponent
import at.ac.uibk.dps.cirrina.di.TestModule
import at.ac.uibk.dps.cirrina.execution.provider.ContextInMemory
import java.time.Duration
import kotlin.time.measureTime
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertTimeout

class LoopTest {
@Test
fun testLoopExecute() {
assertTimeout(Duration.ofSeconds(10)) {
assertDoesNotThrow {
val context = ContextInMemory()

val runtime =
DaggerTestComponent.builder()
.testModule(TestModule(context, DefaultDescriptions.loop, listOf("one", "two")))
.build()
.runtime()

val duration = measureTime { runtime.run() }
println("loop execution: $duration")

assertEquals(1, context.get("v"))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ object DefaultDescriptions {
val noop: URI by lazy { resourceUri("pkl/noop/main.pkl") }
val empty: URI by lazy { resourceUri("pkl/empty/main.pkl") }
val events: URI by lazy { resourceUri("pkl/events/main.pkl") }
val loop: URI by lazy { resourceUri("pkl/loop/main.pkl") }
}
2 changes: 1 addition & 1 deletion src/test/resources/pkl/complete/main.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ collaborativeStateMachine {
type = "increment"
mode = "local"
input { ["v"] = "x" }
emits { new Internal { topic = "e2" } }
emits { new ConditionalEvent { event = new Internal { topic = "e2" } } }
}
}
exit { new Eval { expression = "e = true" } }
Expand Down
17 changes: 4 additions & 13 deletions src/test/resources/pkl/invoke/main.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ collaborativeStateMachine {
["a"] = new Initial {
after {
["timeout"] = new Timeout {
delay = "100"
delay = "1"
triggers = new Emit { event = new Internal { topic = "update" } }
}
}
Expand All @@ -19,24 +19,15 @@ collaborativeStateMachine {
type = "increment"
mode = "local"
input { ["v"] = "v" }
emits { new Internal { topic = "tob" } }
output { "v" }
emits { new ConditionalEvent { provided = "v >= 100"; event = new Internal { topic = "tob" } } }
}
new Eval { expression = "e = e + 1" }
}
}
["tob"] = new Transition { to = "b" }
}
}
["b"] = new State {
entry { new Eval { expression = "v = $v" } }
always {
new Transition {
to = "a"
provided = "v < 10"; or = "c"
}
}
}
["c"] = new Terminal {}
["b"] = new Terminal {}
}
}
}
Expand Down
49 changes: 49 additions & 0 deletions src/test/resources/pkl/loop/main.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
amends "modulepath:/pkl/csm/csml.pkl"

collaborativeStateMachine {
stateMachines {
["one"] {
states {
["a"] = new Initial {
entry {
new Emit { event { topic = "e1" }; target = "'two'" }
}
always {
new Transition { to = "b" }
}
}
["b"] = new Terminal {}
}
}
["two"] {
states {
["a"] = new Initial {
after {
["t"] = new Timeout {
delay = "1000"
triggers = new Emit { event = new Internal { topic = "toc" } }
}
}
on {
["e1"] { to = "b" }
["toc"] { to = "c" }
}
}
["b"] {
entry {
new Eval { expression = "v =+ 1" }
}
always {
new Transition { to = "a"; provided = "v < 1000" }
}
}
["c"] = new Terminal {}
}
}
}
persistent { ["v"] = "0" }
}
instances {
["one"] { stateMachineName = "one" }
["two"] { stateMachineName = "two" }
}
Loading