Skip to content

Commit 453ef89

Browse files
authored
feat: adding invoke action output and conditional emits (#128)
1 parent 7c67d4a commit 453ef89

11 files changed

Lines changed: 146 additions & 28 deletions

File tree

src/main/kotlin/at/ac/uibk/dps/cirrina/Runtime.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ constructor(
8484
eventHandler.bind(
8585
graph = graph,
8686
instanceNames = run,
87-
subscribedTo = instances.values.flatMap { it.subscriptions },
87+
subscribedTo = instances.values.flatMap { it.subscriptions } /*.toSet()*/,
8888
handlers = instances.values.map { it::pushEvent },
8989
)
9090
}

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/Action.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ sealed interface Action {
1313
description.type,
1414
description.mode,
1515
buildVariables(description.input),
16-
buildEvents(description.emits),
16+
description.output,
17+
buildConditionalEvents(description.emits),
1718
)
1819

1920
is MatchDescription ->
@@ -57,7 +58,8 @@ sealed interface Action {
5758
ContextVariable.lazy(k, expression)
5859
}
5960

60-
private fun buildEvents(events: List<EventDescription>) = events.map { Event.from(it) }
61+
private fun buildConditionalEvents(events: List<ConditionalEventDescription>) =
62+
events.map { ConditionalEvent.from(it) }
6163
}
6264
}
6365

@@ -74,12 +76,13 @@ internal constructor(
7476
val type: String,
7577
val mode: InvocationMode,
7678
val input: List<ContextVariable>,
77-
val emits: List<Event>,
79+
val output: List<String>,
80+
val emits: List<ConditionalEvent>,
7881
) : EventRaisingAction {
79-
override fun raises(): List<Event> = emits
82+
override fun raises(): List<Event> = emits.map { it.event }
8083

8184
override fun toString() =
82-
"InvokeAction(type='$type', mode='$mode', input='$input', emits='$emits')"
85+
"InvokeAction(type='$type', mode='$mode', input='$input', output='$output', emits='$emits')"
8386
}
8487

8588
class MatchAction

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/ActionCommand.kt

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,30 @@ class ActionExecutor(
5050
val delta = measureTime {
5151
runCatching { service.invoke(input) }
5252
.onSuccess { output ->
53-
action.emits.forEach { eventTemplate ->
54-
val emittedEvent = eventTemplate.copy(data = output)
55-
if (emittedEvent.channel == EventChannel.INTERNAL) {
56-
eventHandler.propagateToParent(emittedEvent)
57-
} else {
58-
eventHandler.emit(emittedEvent)
53+
action.output.forEach { reference ->
54+
output
55+
.firstOrNull { it.name == reference }
56+
?.let {
57+
runCatching { scope.extent.set(reference, it.value) }
58+
.onFailure { e ->
59+
logger.warn(e) {
60+
"failed to assign service output to variable '${reference}'"
61+
}
62+
}
63+
}
64+
?: logger.warn {
65+
"service output does not contain expected variable '${reference}'"
66+
}
67+
}
68+
69+
action.emits.forEach { conditionalEvent ->
70+
if (conditionalEvent.provided?.evaluate(scope.extent) != false) {
71+
val emittedEvent = conditionalEvent.event.copy(data = output)
72+
if (emittedEvent.channel == EventChannel.INTERNAL) {
73+
eventHandler.propagateToParent(emittedEvent)
74+
} else {
75+
eventHandler.emit(emittedEvent)
76+
}
5977
}
6078
}
6179
}

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/Event.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package at.ac.uibk.dps.cirrina.execution.`object`
22

3+
import at.ac.uibk.dps.cirrina.csm.Csml.ConditionalEventDescription
34
import at.ac.uibk.dps.cirrina.csm.Csml.EventChannel
45
import at.ac.uibk.dps.cirrina.csm.Csml.EventDescription
56
import at.ac.uibk.dps.cirrina.util.getInsecureUuid
@@ -29,3 +30,17 @@ data class Event(
2930
}
3031
}
3132
}
33+
34+
data class ConditionalEvent(val provided: Guard?, val event: Event) {
35+
override fun toString(): String =
36+
"${this::class.simpleName}(provided='$provided', event='$event')"
37+
38+
companion object {
39+
fun from(description: ConditionalEventDescription): ConditionalEvent {
40+
return ConditionalEvent(
41+
description.provided?.let { Guard.from(it) },
42+
Event.from(description.event),
43+
)
44+
}
45+
}
46+
}

src/main/resources/pkl/csm/csml.pkl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,13 @@ class InvokeDescription extends ActionDescription {
9696
type: InvocationType
9797
mode: InvocationMode = "remote"
9898
input: Context
99-
emits: Listing<EventDescription>
99+
output: Listing<VariableName>
100+
emits: Listing<ConditionalEventDescription>
101+
}
102+
103+
class ConditionalEventDescription {
104+
provided: Expression?
105+
event: EventDescription
100106
}
101107

102108
class EvalDescription extends ActionDescription {
@@ -187,6 +193,7 @@ typealias Transition = TransitionDescription
187193
typealias Action = ActionDescription
188194
typealias Eval = EvalDescription
189195
typealias Invoke = InvokeDescription
196+
typealias ConditionalEvent = ConditionalEventDescription
190197
typealias Case = CaseDescription
191198
typealias Match = MatchDescription
192199
typealias Emit = EmitDescription

src/test/kotlin/at/ac/uibk/dps/cirrina/InvokeTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class InvokeTest {
3636
val duration = measureTime { runtime.run() }
3737
println("invoke execution: $duration")
3838

39-
assertEquals(10, context.get("v"))
39+
assertEquals(100, context.get("v"))
4040
} finally {
4141
server.stop(1)
4242
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package at.ac.uibk.dps.cirrina
2+
3+
import at.ac.uibk.dps.cirrina.data.DefaultDescriptions
4+
import at.ac.uibk.dps.cirrina.di.DaggerTestComponent
5+
import at.ac.uibk.dps.cirrina.di.TestModule
6+
import at.ac.uibk.dps.cirrina.execution.provider.ContextInMemory
7+
import java.time.Duration
8+
import kotlin.time.measureTime
9+
import org.junit.jupiter.api.Assertions.assertEquals
10+
import org.junit.jupiter.api.Test
11+
import org.junit.jupiter.api.assertDoesNotThrow
12+
import org.junit.jupiter.api.assertTimeout
13+
14+
class LoopTest {
15+
@Test
16+
fun testLoopExecute() {
17+
assertTimeout(Duration.ofSeconds(10)) {
18+
assertDoesNotThrow {
19+
val context = ContextInMemory()
20+
21+
val runtime =
22+
DaggerTestComponent.builder()
23+
.testModule(TestModule(context, DefaultDescriptions.loop, listOf("one", "two")))
24+
.build()
25+
.runtime()
26+
27+
val duration = measureTime { runtime.run() }
28+
println("loop execution: $duration")
29+
30+
assertEquals(1, context.get("v"))
31+
}
32+
}
33+
}
34+
}

src/test/kotlin/at/ac/uibk/dps/cirrina/data/DefaultDescriptions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ object DefaultDescriptions {
1111
val noop: URI by lazy { resourceUri("pkl/noop/main.pkl") }
1212
val empty: URI by lazy { resourceUri("pkl/empty/main.pkl") }
1313
val events: URI by lazy { resourceUri("pkl/events/main.pkl") }
14+
val loop: URI by lazy { resourceUri("pkl/loop/main.pkl") }
1415
}

src/test/resources/pkl/complete/main.pkl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ collaborativeStateMachine {
2828
type = "increment"
2929
mode = "local"
3030
input { ["v"] = "x" }
31-
emits { new Internal { topic = "e2" } }
31+
emits { new ConditionalEvent { event = new Internal { topic = "e2" } } }
3232
}
3333
}
3434
exit { new Eval { expression = "e = true" } }

src/test/resources/pkl/invoke/main.pkl

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ collaborativeStateMachine {
77
["a"] = new Initial {
88
after {
99
["timeout"] = new Timeout {
10-
delay = "100"
10+
delay = "1"
1111
triggers = new Emit { event = new Internal { topic = "update" } }
1212
}
1313
}
@@ -19,24 +19,15 @@ collaborativeStateMachine {
1919
type = "increment"
2020
mode = "local"
2121
input { ["v"] = "v" }
22-
emits { new Internal { topic = "tob" } }
22+
output { "v" }
23+
emits { new ConditionalEvent { provided = "v >= 100"; event = new Internal { topic = "tob" } } }
2324
}
24-
new Eval { expression = "e = e + 1" }
2525
}
2626
}
2727
["tob"] = new Transition { to = "b" }
2828
}
2929
}
30-
["b"] = new State {
31-
entry { new Eval { expression = "v = $v" } }
32-
always {
33-
new Transition {
34-
to = "a"
35-
provided = "v < 10"; or = "c"
36-
}
37-
}
38-
}
39-
["c"] = new Terminal {}
30+
["b"] = new Terminal {}
4031
}
4132
}
4233
}

0 commit comments

Comments
 (0)