Skip to content

Commit

Permalink
Add support for async EventListener DSL PR #35 from pambrose/AsyncEve…
Browse files Browse the repository at this point in the history
…ntListenerDsl

Add support for async EventListener DSL
  • Loading branch information
holgerbrandl committed Dec 11, 2021
2 parents d4d6600 + 4055e58 commit 761dcf0
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 1 deletion.
6 changes: 6 additions & 0 deletions docs/userguide/docs/event_log.md
Expand Up @@ -94,6 +94,12 @@ Trace logs a suitable for standard kotlin collection processing. E.g. we could s
In the example, we can think of a channel as a pipe between two coroutines. For details see the great articlle [_Kotlin: Diving in to Coroutines and Channels_](
https://proandroiddev.com/kotlin-coroutines-channels-csp-android-db441400965f).

Alternatively, we can use the simpler `AsyncEventListener` DSL to process the events:

```kotlin
//{!analysis/LogChannelConsumerDsl.kts!}
```

## Logging Configuration

Typically, only some types of event logging are required in a given simulation. To optimize simulation performance, the engine allows to suppress selectivly per event type and simulation entity. This is configured via [tracking policy factory](advanced.md#continuous-simulation)
Expand Down
6 changes: 6 additions & 0 deletions docs/userguide/docs/examples/misc.md
Expand Up @@ -4,3 +4,9 @@
```kotlin
//{!analysis/LogChannelConsumer.kts!}
```

Alternatively, you can use the `AsyncEventListener` DSL to process events:

```kotlin
//{!analysis/LogChannelConsumerDsl.kts!}
```
17 changes: 16 additions & 1 deletion src/main/kotlin/org/kalasim/Environment.kt
@@ -1,6 +1,8 @@
package org.kalasim

import com.github.holgerbrandl.jsonbuilder.json
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import org.apache.commons.math3.random.JDKRandomGenerator
import org.apache.commons.math3.random.RandomGenerator
import org.json.JSONObject
Expand Down Expand Up @@ -110,7 +112,10 @@ open class Environment(
get() = eventQueue.map { it.component }


private val eventListeners = mutableListOf<EventListener>()

// This is not private because addEventListener is inlined.
val eventListeners = listOf<EventListener>().toMutableList()


val trackingPolicyFactory = TrackingPolicyFactory()
// val traceFilters = mutableListOf<EventFilter>()
Expand Down Expand Up @@ -343,6 +348,16 @@ open class Environment(
standBy.add(component)
}

inline fun <reified T : Event> addEventListener(
scope: CoroutineScope = GlobalScope,
crossinline block: (T) -> Unit
): EventListener =
AsyncEventListener()
.also { listener ->
listener.start(scope, block)
eventListeners.add(listener)
}

fun addEventListener(listener: EventListener) = eventListeners.add(listener)

@Suppress("unused")
Expand Down
30 changes: 30 additions & 0 deletions src/main/kotlin/org/kalasim/misc/AsyncEventListener.kt
@@ -0,0 +1,30 @@
package org.kalasim.misc

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import org.kalasim.Event
import org.kalasim.EventListener

class AsyncEventListener() : EventListener {
val eventChannel = Channel<Event>()

inline fun <reified T : Event> start(scope: CoroutineScope = GlobalScope, crossinline block: (event: T) -> Unit) {
scope.launch {
eventChannel.receiveAsFlow()
.collect { event: Event ->
if (event is T)
block.invoke(event)
}
}
}

override fun consume(event: Event) {
GlobalScope.launch {
eventChannel.trySend(event)
}
}
}
@@ -0,0 +1,21 @@
package org.kalasim.examples.analysis

import org.kalasim.Component
import org.kalasim.ComponentGenerator
import org.kalasim.InteractionEvent
import org.kalasim.asDist
import org.kalasim.createSimulation

// create simulation with no default logging
val sim = createSimulation {
ComponentGenerator(iat = 1.asDist()) { Component("Car.${it}") }

// add custom log consumer
addEventListener<InteractionEvent> { event ->
if (event.curComponent?.name == "ComponentGenerator.1")
println(event)
}

// run the simulation
run(100)
}

0 comments on commit 761dcf0

Please sign in to comment.