diff --git a/docs/roadmap.md b/docs/roadmap.md index 2fb15cdd..2a818534 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -28,6 +28,12 @@ https://www.baeldung.com/java-a-star-pathfinding Add tickUnit to constructor of Environment and remove setter --- +better landing page + +https://github.com/squidfunk/mkdocs-material/issues/1996 +https://sdk.up42.com/ + + ## Later diff --git a/docs/userguide/docs/advanced.md b/docs/userguide/docs/advanced.md index 1866cbfa..85bfaf51 100644 --- a/docs/userguide/docs/advanced.md +++ b/docs/userguide/docs/advanced.md @@ -54,7 +54,7 @@ It may happen that a simulation is too complex to run at a defined clock. In suc ## Operational Control -Even if `kalasim` tries to provide a simplistic, efficient, declarative approach to define a simulation, it may come along with computational demands simulation. To allow introspection into time-complexity of the underlying computations, the user may want to use the built-in `env.tickMetrics` [monitor](monitors.md) to analyze how much time is spent per time unit (aka *tick*). This monitor is not enabled by default and needs to be enabled when the environment is created by passing `enableTickMetrics=true` +Even if `kalasim` tries to provide a simplistic, efficient, declarative approach to define a simulation, it may come along with computational demands simulation. To allow introspection into time-complexity of the underlying computations, the user may want to enable the built-in `env.tickMetrics` [monitor](monitors.md) to analyze how much time is spent per time unit (aka *tick*). This monitor can be enabled by calling `enableTickMetrics()` when [configuring](basics.md#configuring-a-simulation) the simulation. ```kotlin hl_lines="5" {!api/TickMetricsExample.kts!} diff --git a/docs/userguide/docs/basics.md b/docs/userguide/docs/basics.md index 220a9016..edeceab1 100644 --- a/docs/userguide/docs/basics.md +++ b/docs/userguide/docs/basics.md @@ -14,7 +14,10 @@ All entities in a simulation are governed by an environment context. Every simul The environment context of a kalasim simulation is an instance of `org.kalasim.Environment`, which can be created using simple instantiation or via a builder called `createSimulation` ```kotlin -val env : Environment = createSimulation(enableConsoleLogger = true){ +val env : Environment = createSimulation(){ + // enable logging of built-in simulation metrics + enableComponentLogger() + // Create simulation entities in here Car() Resource("Car Wash") @@ -122,6 +125,12 @@ So the key points to recall are * Race-conditions between events can be avoided by setting a `priority` +## Configuring a Simulation + +To minimze initial complexity when creating an environment, some options can be enabled within the scope of an environment +* `enableTickMetrics()` - See [tick metrics](advanced.md#operational-control) +* `enableComponentLogger()` - Enable the [component logger](events.md#component-logger) to track component status + ## Dependency Injection Kalasim is building on top of [koin](https://insert-koin.io/) to inject dependencies between elements of a simulation. This allows creating simulation entities such as resources, components or states conveniently without passing around references. diff --git a/docs/userguide/docs/changes.md b/docs/userguide/docs/changes.md index 26cc55f7..f26be11a 100644 --- a/docs/userguide/docs/changes.md +++ b/docs/userguide/docs/changes.md @@ -1,5 +1,16 @@ # Kalasim Release History +## v0.10 + +Not yet released + +Breaking changes +* tick metrics and component-logger are now [configured](basics.md#configuring-a-simulation) and not enabled via constructor parameter any longer (to minimize constructor complexity) +* + +Improvments +* More robust [dependency injection](basics.md#dependency-injection) with + ## v0.9 Released at 2023-04-13 diff --git a/modules/animation/src/test/kotlin/org/kalasim/animation/examples/elevator/ElevatorAnimated.kt b/modules/animation/src/test/kotlin/org/kalasim/animation/examples/elevator/ElevatorAnimated.kt index 35661c71..f4e2cef5 100644 --- a/modules/animation/src/test/kotlin/org/kalasim/animation/examples/elevator/ElevatorAnimated.kt +++ b/modules/animation/src/test/kotlin/org/kalasim/animation/examples/elevator/ElevatorAnimated.kt @@ -11,12 +11,10 @@ import org.openrndr.draw.* import org.openrndr.extra.gui.GUI import org.openrndr.extra.parameters.ActionParameter import org.openrndr.extra.parameters.IntParameter -import org.openrndr.ffmpeg.ScreenRecorder import org.openrndr.math.Vector3 import org.openrndr.math.transforms.buildTransform import org.openrndr.math.transforms.project import org.openrndr.shape.* -import kotlin.time.DurationUnit import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds diff --git a/src/main/kotlin/org/kalasim/Component.kt b/src/main/kotlin/org/kalasim/Component.kt index 607df2b9..7cc3a1a6 100644 --- a/src/main/kotlin/org/kalasim/Component.kt +++ b/src/main/kotlin/org/kalasim/Component.kt @@ -47,6 +47,16 @@ data class Priority(val value: Int) : Comparable { override fun compareTo(other: Priority?): Int = compareValuesBy(this, other ?: NORMAL) { value } } +//class SimSequence(val component: Component) : Sequence { +// override fun iterator(): Iterator { +// return super.iterator() +// } +//} +// +//public inline fun Component.simSequence(crossinline iterator: () -> Iterator): Sequence = object : SimSequence { +// override fun iterator(): Iterator = iterator() +//} + // TODO reassess if we should use these type-aliases //typealias ProcessDefinition = SequenceScope @@ -1294,8 +1304,13 @@ open class Component( until: TickTime? = null, priority: Priority = NORMAL, urgent: Boolean = false - ) = yieldCurrent { - this@Component.hold(duration, description, until, priority, urgent) + ) { +// val component = getThis() +// val other = this +// println(component) + yieldCurrent { + this@Component.hold(duration, description, until, priority, urgent) + } } /** @@ -1601,6 +1616,15 @@ open class Component( private suspend fun SequenceScope.yieldCurrent(builder: () -> Unit = {}) { val initialStatus = componentState + require(initialStatus == CURRENT) { + """Component context violated. Kalasim has detected an invalid call of + an outer scope function. This can happen if a sub-process defined + within a component class is calling methods of the parent scope. + Unfortunately, this can't be resolved at compile time. + """.trimIndent() + } + + builder() if(initialStatus == CURRENT) { diff --git a/src/main/kotlin/org/kalasim/Environment.kt b/src/main/kotlin/org/kalasim/Environment.kt index 550b7722..2ddc9570 100644 --- a/src/main/kotlin/org/kalasim/Environment.kt +++ b/src/main/kotlin/org/kalasim/Environment.kt @@ -19,6 +19,7 @@ import org.koin.core.qualifier.Qualifier import org.koin.dsl.koinApplication import org.koin.dsl.module import kotlinx.datetime.Instant +import org.koin.core.qualifier.named import java.util.* import kotlin.time.* import kotlin.time.Duration.Companion.days @@ -28,37 +29,31 @@ internal const val MAIN = "main" typealias KoinModule = org.koin.core.module.Module -//internal class EnvBuildContext : KoinModule() { -// var enableConsoleLogger: Boolean = true -//} -// --> not possible because Module is not open - // https://github.com/InsertKoinIO/koin/issues/801 +@Deprecated("Use createSimulation() instead", ReplaceWith("declareDependencies(builder).createSimulation {}")) fun configureEnvironment( - enableConsoleLogger: Boolean = false, builder: KoinModule.() -> Unit -): Environment = declareDependencies(builder).createSimulation(enableConsoleLogger) {} + enableComponentLogger: Boolean = false, builder: KoinModule.() -> Unit +): Environment = declareDependencies(builder).createSimulation {} +@Suppress("DeprecatedCallableAddReplaceWith") +@Deprecated("Use createSimulation() instead") fun declareDependencies( builder: KoinModule.() -> Unit ): KoinModule = module(createdAtStart = true) { builder() } fun KoinModule.createSimulation( - enableConsoleLogger: Boolean = false, /** The duration unit of this environment. Every tick corresponds to a unit duration. See https://www.kalasim.org/basics/#running-a-simulation */ durationUnit: DurationUnit = MINUTES, /** The absolute time at tick-time 0. Defaults to `null`.*/ startDate: Instant? = null, - enableTickMetrics: Boolean = false, useCustomKoin: Boolean = false, randomSeed: Int = DEFAULT_SEED, builder: Environment.() -> Unit ): Environment = createSimulation( durationUnit = durationUnit, startDate = startDate, - enableConsoleLogger = enableConsoleLogger, - enableTickMetrics = enableTickMetrics, dependencies = this, useCustomKoin = useCustomKoin, randomSeed = randomSeed, @@ -66,12 +61,10 @@ fun KoinModule.createSimulation( ) fun createSimulation( - enableConsoleLogger: Boolean = false, /** The duration unit of this environment. Every tick corresponds to a unit duration. See https://www.kalasim.org/basics/#running-a-simulation */ durationUnit: DurationUnit = MINUTES, /** The absolute time at tick-time 0. Defaults to `null`.*/ startDate: Instant? = null, - enableTickMetrics: Boolean = false, dependencies: KoinModule? = null, useCustomKoin: Boolean = false, randomSeed: Int = DEFAULT_SEED, @@ -79,8 +72,6 @@ fun createSimulation( ): Environment = Environment( durationUnit = durationUnit, startDate = startDate, - enableConsoleLogger = enableConsoleLogger, - enableTickMetrics = enableTickMetrics, dependencies = dependencies, randomSeed = randomSeed, koin = if(useCustomKoin) koinApplication { }.koin else null @@ -113,7 +104,7 @@ open class Environment( /** The absolute time at tick-time 0. Defaults to `null`.*/ var startDate: Instant? = null, /** If enabled, it will render a tabular view of recorded interaction and resource events. */ - enableConsoleLogger: Boolean = false, + enableComponentLogger: Boolean = false, /** Measure the compute time per tick as function of time. For details see https://www.kalasim.org/advanced/#operational-control */ enableTickMetrics: Boolean = false, dependencies: KoinModule? = null, @@ -141,7 +132,7 @@ open class Environment( // get() = eventQueue.map { it.component } get() = eventQueue.sortedIterator().map { it.component }.toList() - private val eventListeners = listOf().toMutableList() + internal val eventListeners = listOf().toMutableList() val trackingPolicyFactory = TrackingPolicyFactory() @@ -193,14 +184,19 @@ open class Environment( qualifier: Qualifier? = null, noinline parameters: ParametersDefinition? = null ): T = getKoin().get(qualifier, parameters) + /** Resolves a dependency in the simulation. Dependencies can be disambiguated by using a qualifier.*/ + inline fun get( + qualifier: String , noinline parameters: ParametersDefinition? = null + ): T = getKoin().get(named(qualifier), parameters) + init { // start console logger // addTraceListener { print(it) } - if(enableConsoleLogger) { - enableConsoleLogger() + if(enableComponentLogger) { + enableComponentLogger() } _koin = koin ?: run { @@ -238,16 +234,20 @@ open class Environment( // curComponent = main } - fun enableConsoleLogger() { - addEventListener(ConsoleTraceLogger()) - } - private val _tm: TickMetrics? = if(enableTickMetrics) TickMetrics(koin = koin) else null + fun enableTickMetrics(){ + require(queue.none{ it is TickMetrics}){ + "The tick-metrics monitor is already registered" + } + + TickMetrics(koin = getKoin()) + } val tickMetrics: MetricTimeline get() { - require(_tm != null) { "Use enableTickMetrics=true to enable tick metrics" } - return _tm.timeline + val tm = queue.filterIsInstance().firstOrNull() + require(tm != null) { "Use enableTickMetrics=true to enable tick metrics" } + return tm.timeline } // private var endOnEmptyEventlist = false @@ -528,6 +528,15 @@ data class QueueElement( } +fun Environment.enableComponentLogger() { + require(eventListeners.none{ it is ConsoleTraceLogger}){ + "The component-logger is already registered" + } + + addEventListener(ConsoleTraceLogger()) +} + + internal fun Environment.calcScheduleTime(until: TickTime?, duration: Number?): TickTime { return (until?.value to duration?.toDouble()).let { (till, duration) -> if(till == null) { @@ -541,19 +550,28 @@ internal fun Environment.calcScheduleTime(until: TickTime?, duration: Number?): } -inline fun KoinModule.add( +/** Register dependency in simulation context. For details see https://www.kalasim.org/basics/#dependency-injection */ +inline fun KoinModule.dependency( qualifier: Qualifier? = null, noinline definition: Definition ) { single(qualifier = qualifier, createdAtStart = true, definition = definition) - } + +/** Register dependency in simulation context. For details see https://www.kalasim.org/basics/#dependency-injection */ +inline fun Environment.dependency(qualifier: String, builder: Environment.() -> T) = dependency(named(qualifier), builder) + +/** Register dependency in simulation context. For details see https://www.kalasim.org/basics/#dependency-injection */ inline fun Environment.dependency(qualifier: Qualifier? = null, builder: Environment.() -> T): T { val something = builder(this) + getKoin().loadModules(listOf(module(createdAtStart = true) { - add(qualifier) { something } + dependency(qualifier) { something } })) + require(something !is Unit){ + "dependency must not be last element in createSimulation{} as this is causing type inference to fail internally. Add println() or any other terminal statement to work around this problem." + } return something } diff --git a/src/main/kotlin/org/kalasim/analysis/ConsoleTraceLogger.kt b/src/main/kotlin/org/kalasim/analysis/ConsoleTraceLogger.kt index a3287ddd..eb3ce5b4 100644 --- a/src/main/kotlin/org/kalasim/analysis/ConsoleTraceLogger.kt +++ b/src/main/kotlin/org/kalasim/analysis/ConsoleTraceLogger.kt @@ -5,6 +5,8 @@ import org.kalasim.misc.TRACE_DF import org.kalasim.misc.titlecaseFirstChar import java.util.logging.Level + + class ConsoleTraceLogger(var logLevel: Level = Level.INFO) : EventListener { enum class EventsTableColumn { Time, Current, Receiver, Action, Info } diff --git a/src/main/kotlin/org/kalasim/analysis/Logging.kt b/src/main/kotlin/org/kalasim/analysis/Logging.kt index ad9e2eba..d36a5573 100644 --- a/src/main/kotlin/org/kalasim/analysis/Logging.kt +++ b/src/main/kotlin/org/kalasim/analysis/Logging.kt @@ -39,6 +39,8 @@ fun interface EventListener { } + + /** * Activates a global event-log, which stores all events on the kalasim event bus. * @@ -54,6 +56,7 @@ fun Environment.enableEventLog(): EventLog { return tc } + /** A list of all events that were created in a simulation run. See [Event Log](https://www.kalasim.org/events/) for details. */ class EventLog(val events: MutableList = mutableListOf()) : EventListener, MutableList by events { diff --git a/src/main/kotlin/org/kalasim/analysis/TickMetrics.kt b/src/main/kotlin/org/kalasim/analysis/TickMetrics.kt index 95eedb96..b8861031 100644 --- a/src/main/kotlin/org/kalasim/analysis/TickMetrics.kt +++ b/src/main/kotlin/org/kalasim/analysis/TickMetrics.kt @@ -12,7 +12,8 @@ import kotlin.math.round /** Allows introspection of time-complexity of the underlying computations. The user may want to use the built-in env.tickMetrics timeline to analyze how much time is spent per time unit (aka tick). * - * https://www.kalasim.org/advanced/#operational-control */ + * https://www.kalasim.org/advanced/#operational-control + */ class TickMetrics( val sampleTicks: Double = 1.0, /** Enable recording of tick metrics via a `timeline` attribute of this object. */ @@ -24,6 +25,12 @@ class TickMetrics( val timeline = MetricTimeline(name, 0) + init{ + require( env.queue.count{ it is TickMetrics} ==1){ + "tick metrics must be enabled just once" + } + } + override fun process() = sequence { // hold(ceil(now.value)) diff --git a/src/main/kotlin/org/kalasim/examples/elevator/Elevator.kt b/src/main/kotlin/org/kalasim/examples/elevator/Elevator.kt index 4fb89aa7..df2885da 100644 --- a/src/main/kotlin/org/kalasim/examples/elevator/Elevator.kt +++ b/src/main/kotlin/org/kalasim/examples/elevator/Elevator.kt @@ -6,9 +6,7 @@ import org.kalasim.animation.AnimationComponent import org.kalasim.examples.elevator.Car.DoorState.CLOSED import org.kalasim.examples.elevator.Car.DoorState.OPEN import org.kalasim.examples.elevator.Direction.* -import org.kalasim.misc.AmbiguousDuration import org.kalasim.misc.repeat -import org.kalasim.plot.kravis.display import java.awt.Point import kotlin.time.Duration.Companion.days import kotlin.time.DurationUnit diff --git a/src/main/kotlin/org/kalasim/examples/er/EmergencyRoom.kt b/src/main/kotlin/org/kalasim/examples/er/EmergencyRoom.kt index d044eaa0..5207f4b5 100644 --- a/src/main/kotlin/org/kalasim/examples/er/EmergencyRoom.kt +++ b/src/main/kotlin/org/kalasim/examples/er/EmergencyRoom.kt @@ -247,20 +247,19 @@ class EmergencyRoom( val numRooms: Int = 4, /** The execution planning policy of the ER. */ val nurse: HeadNurse = FifoNurse(), - enableConsoleLogger: Boolean = false, + enableComponentLogger: Boolean = false, enableTickMetrics: Boolean = false, ) : Environment( - enableConsoleLogger = enableConsoleLogger, + enableComponentLogger = enableComponentLogger, enableTickMetrics = enableTickMetrics, durationUnit = DurationUnit.HOURS ) { - val waitingAreaSize = 300 - // todo this should be opt-in anyway https://github.com/holgerbrandl/kalasim/issues/66 - init { + init{ trackingPolicyFactory.disableAll() } + val waitingAreaSize = 300 // todo also here having sorted queue is causing almost more problems than solving // val waitingLine = ComponentQueue(comparator = compareBy { it.severity.value }, name = "ER Waiting Area") diff --git a/src/test/kotlin/org/kalasim/examples/CarWash.kt b/src/test/kotlin/org/kalasim/examples/CarWash.kt index d8340df5..54343799 100644 --- a/src/test/kotlin/org/kalasim/examples/CarWash.kt +++ b/src/test/kotlin/org/kalasim/examples/CarWash.kt @@ -27,9 +27,10 @@ fun main() { } - val env = declareDependencies { - add { Resource("carwash", NUM_MACHINES) } - }.createSimulation(true, randomSeed = RANDOM_SEED) { + val env = createSimulation( randomSeed = RANDOM_SEED) { + dependency { Resource("carwash", NUM_MACHINES) } + + enableComponentLogger() //Create 4 initial cars repeat(3) { Car() } diff --git a/src/test/kotlin/org/kalasim/examples/DiningPhilosophers.kt b/src/test/kotlin/org/kalasim/examples/DiningPhilosophers.kt index ae19a737..5bbf1517 100644 --- a/src/test/kotlin/org/kalasim/examples/DiningPhilosophers.kt +++ b/src/test/kotlin/org/kalasim/examples/DiningPhilosophers.kt @@ -32,7 +32,9 @@ fun main() { } } - val sim = createSimulation(true) { + val sim = createSimulation { + enableComponentLogger() + val ec = collect() // create forks and resources diff --git a/src/test/kotlin/org/kalasim/examples/MachineShop.kt b/src/test/kotlin/org/kalasim/examples/MachineShop.kt index 4e595cab..e4f424e8 100644 --- a/src/test/kotlin/org/kalasim/examples/MachineShop.kt +++ b/src/test/kotlin/org/kalasim/examples/MachineShop.kt @@ -64,7 +64,8 @@ fun main() { } - createSimulation(true, randomSeed = RANDOM_SEED) { + createSimulation(randomSeed = RANDOM_SEED) { + enableComponentLogger() val repairMan = Resource("mechanic", preemptive = true) diff --git a/src/test/kotlin/org/kalasim/examples/MachineWithParts.kt b/src/test/kotlin/org/kalasim/examples/MachineWithParts.kt index 5f52ed0c..27d57501 100644 --- a/src/test/kotlin/org/kalasim/examples/MachineWithParts.kt +++ b/src/test/kotlin/org/kalasim/examples/MachineWithParts.kt @@ -37,7 +37,9 @@ class Machine : Component() { } fun main() { - createSimulation(true) { + createSimulation { + enableComponentLogger() + dependency { Resource() } repeat(2) { Machine() } diff --git a/src/test/kotlin/org/kalasim/examples/MovieRenege.kt b/src/test/kotlin/org/kalasim/examples/MovieRenege.kt index fe4b436b..2cc1d16e 100644 --- a/src/test/kotlin/org/kalasim/examples/MovieRenege.kt +++ b/src/test/kotlin/org/kalasim/examples/MovieRenege.kt @@ -12,7 +12,8 @@ fun main() { val MOVIES = listOf("Julia Unchained", "Kill Process", "Pulp Implementation").map { Movie(it) } - createSimulation(true, randomSeed = RANDOM_SEED) { + createSimulation( randomSeed = RANDOM_SEED) { + enableComponentLogger() // note: it's not really needed to model the theater (because it has no process), but we follow the julia model here val theater = object { diff --git a/src/test/kotlin/org/kalasim/examples/SalabimExampleTests.kt b/src/test/kotlin/org/kalasim/examples/SalabimExampleTests.kt index 790b7e4c..7bd46806 100644 --- a/src/test/kotlin/org/kalasim/examples/SalabimExampleTests.kt +++ b/src/test/kotlin/org/kalasim/examples/SalabimExampleTests.kt @@ -27,10 +27,10 @@ class SalabimExampleTests { @Test fun `Bank_1_clerk should result in correct waiting line statistics`() { - val env = configureEnvironment { - add { Clerk() } - add { ComponentQueue("waiting line") } - }.apply { + val env = createSimulation { + dependency { Clerk() } + dependency { ComponentQueue("waiting line") } + org.kalasim.examples.bank.oneclerk.CustomerGenerator() } @@ -94,8 +94,8 @@ class SalabimExampleTests { // val avgQueueMeans = listOf(200).map { 1000.0 * it }.map { runtime -> // val avgQueueMeans = listOf(100000).map { runtime -> runtime to declareDependencies { - add { Clerk() } - add { ComponentQueue("waiting line") } + dependency { Clerk() } + dependency { ComponentQueue("waiting line") } }.createSimulation { org.kalasim.examples.bank.oneclerk.CustomerGenerator() run(runtime) @@ -132,9 +132,9 @@ class SalabimExampleTests { fun `Bank3clerks_reneging should work as expected`() { val env = declareDependencies { // register components needed for dependency injection - add { ComponentQueue("waitingline") } - add { State(false, "worktodo") } - add { (0..2).map { org.kalasim.examples.bank.reneging.Clerk() } } + dependency { ComponentQueue("waitingline") } + dependency { State(false, "worktodo") } + dependency { (0..2).map { org.kalasim.examples.bank.reneging.Clerk() } } }.createSimulation { // register other components to be present when starting the simulation diff --git a/src/test/kotlin/org/kalasim/examples/SimpleHospital.kt b/src/test/kotlin/org/kalasim/examples/SimpleHospital.kt index 44f524f4..d863e8ce 100644 --- a/src/test/kotlin/org/kalasim/examples/SimpleHospital.kt +++ b/src/test/kotlin/org/kalasim/examples/SimpleHospital.kt @@ -10,8 +10,10 @@ import org.kalasim.analysis.ResourceEvent import org.kalasim.plot.kravis.display fun main() { - val sim = createSimulation(true) { - // actually it would be more efficient to simply model a single Doctor with a capacity of 2 + val sim = createSimulation { + enableComponentLogger() + + // Actually, it would be more efficient to simply model a single Doctor with a capacity of 2 val nurses = Resource("nurses", 1) val doctors = Resource("doctors", 2) val admin = Resource("admin", 1) diff --git a/src/test/kotlin/org/kalasim/examples/Traffic.kts b/src/test/kotlin/org/kalasim/examples/Traffic.kts index 719b561e..59affa62 100644 --- a/src/test/kotlin/org/kalasim/examples/Traffic.kts +++ b/src/test/kotlin/org/kalasim/examples/Traffic.kts @@ -44,7 +44,9 @@ class Car(val trafficLight: TrafficLight) : Component() { } } -createSimulation(true) { +createSimulation { + enableComponentLogger() + // Add a traffic light so that we can refer to it via koin get() dependency { TrafficLight() } diff --git a/src/test/kotlin/org/kalasim/examples/api/Car.kt b/src/test/kotlin/org/kalasim/examples/api/Car.kt index eca3ff33..716b7f0f 100644 --- a/src/test/kotlin/org/kalasim/examples/api/Car.kt +++ b/src/test/kotlin/org/kalasim/examples/api/Car.kt @@ -18,7 +18,9 @@ fun main() { } } - createSimulation(enableComponentLogger = true) { + createSimulation { + enableComponentLogger() + dependency { TrafficLight() } dependency { Driver() } diff --git a/src/test/kotlin/org/kalasim/examples/api/Car.kts b/src/test/kotlin/org/kalasim/examples/api/Car.kts index 2b011d79..17d73cd1 100644 --- a/src/test/kotlin/org/kalasim/examples/api/Car.kts +++ b/src/test/kotlin/org/kalasim/examples/api/Car.kts @@ -22,7 +22,9 @@ class Car : Component() { } } -createSimulation(enableComponentLogger = true) { +createSimulation { + enableComponentLogger() + dependency { TrafficLight() } dependency { Driver() } diff --git a/src/test/kotlin/org/kalasim/examples/api/ConsumeSubProcess.kts b/src/test/kotlin/org/kalasim/examples/api/ConsumeSubProcess.kts index ca38d03a..b0f6c1aa 100644 --- a/src/test/kotlin/org/kalasim/examples/api/ConsumeSubProcess.kts +++ b/src/test/kotlin/org/kalasim/examples/api/ConsumeSubProcess.kts @@ -1,6 +1,8 @@ import org.kalasim.* -createSimulation(true) { +createSimulation { + enableComponentLogger() + object : Component() { override fun process() =sequence { diff --git a/src/test/kotlin/org/kalasim/examples/api/EventCollector.kts b/src/test/kotlin/org/kalasim/examples/api/EventCollector.kts index 5bc4faf3..fc171491 100644 --- a/src/test/kotlin/org/kalasim/examples/api/EventCollector.kts +++ b/src/test/kotlin/org/kalasim/examples/api/EventCollector.kts @@ -1,9 +1,12 @@ //EventCollector.kts import org.kalasim.analysis.* import org.kalasim.createSimulation +import org.kalasim.enableComponentLogger import org.kalasim.enableEventLog -createSimulation(enableComponentLogger = true) { +createSimulation { + enableComponentLogger() + val tc = enableEventLog() tc.filter { it is InteractionEvent && it.component?.name == "foo" } diff --git a/src/test/kotlin/org/kalasim/examples/api/RealtimeSimulation.kts b/src/test/kotlin/org/kalasim/examples/api/RealtimeSimulation.kts index f131b227..7f19a5fe 100644 --- a/src/test/kotlin/org/kalasim/examples/api/RealtimeSimulation.kts +++ b/src/test/kotlin/org/kalasim/examples/api/RealtimeSimulation.kts @@ -1,10 +1,13 @@ import org.kalasim.ClockSync import org.kalasim.createSimulation +import org.kalasim.enableComponentLogger import kotlin.time.Duration.Companion.seconds val timeBefore = System.currentTimeMillis() -createSimulation(true) { +createSimulation { + enableComponentLogger() + // enable real-time clock synchronization ClockSync(tickDuration = 1.seconds) diff --git a/src/test/kotlin/org/kalasim/examples/api/ResourceSelection.kts b/src/test/kotlin/org/kalasim/examples/api/ResourceSelection.kts index 657a36b9..6371b59b 100644 --- a/src/test/kotlin/org/kalasim/examples/api/ResourceSelection.kts +++ b/src/test/kotlin/org/kalasim/examples/api/ResourceSelection.kts @@ -2,7 +2,9 @@ import org.kalasim.* import org.kalasim.ResourceSelectionPolicy.ShortestQueue -createSimulation(false) { +createSimulation { + enableComponentLogger() + val doctors = List(3) { Resource() } class Patient : Component() { diff --git a/src/test/kotlin/org/kalasim/examples/api/SimpleInject.kts b/src/test/kotlin/org/kalasim/examples/api/SimpleInject.kts index 3f606b95..43f8dee6 100644 --- a/src/test/kotlin/org/kalasim/examples/api/SimpleInject.kts +++ b/src/test/kotlin/org/kalasim/examples/api/SimpleInject.kts @@ -10,6 +10,6 @@ class Something(val counter: Counter) : Component() { } } configureEnvironment { - add { Counter(0) } - add { Something(get()) } + dependency { Counter(0) } + dependency { Something(get()) } }.run(10) \ No newline at end of file diff --git a/src/test/kotlin/org/kalasim/examples/api/TickMetricsExample.kts b/src/test/kotlin/org/kalasim/examples/api/TickMetricsExample.kts index 0c287c26..d45f4a36 100644 --- a/src/test/kotlin/org/kalasim/examples/api/TickMetricsExample.kts +++ b/src/test/kotlin/org/kalasim/examples/api/TickMetricsExample.kts @@ -2,7 +2,8 @@ import org.kalasim.Component import org.kalasim.createSimulation import org.kalasim.plot.letsplot.display -createSimulation(enableTickMetrics = true) { +createSimulation() { + enableTickMetrics() object : Component() { override fun process() = sequence { diff --git a/src/test/kotlin/org/kalasim/examples/api/TickTrafoExample.kts b/src/test/kotlin/org/kalasim/examples/api/TickTrafoExample.kts index c8b84038..981e5b84 100644 --- a/src/test/kotlin/org/kalasim/examples/api/TickTrafoExample.kts +++ b/src/test/kotlin/org/kalasim/examples/api/TickTrafoExample.kts @@ -8,7 +8,8 @@ import kotlin.time.DurationUnit // note MINUTES is also kalasim's default -createSimulation(true, durationUnit = DurationUnit.MINUTES) { +createSimulation(durationUnit = DurationUnit.MINUTES) { + enableComponentLogger() object :Component(){ override fun process() =sequence { diff --git a/src/test/kotlin/org/kalasim/examples/bank/data/Bank3ClerksData.kt b/src/test/kotlin/org/kalasim/examples/bank/data/Bank3ClerksData.kt index 821af48b..98a7918d 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/data/Bank3ClerksData.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/data/Bank3ClerksData.kt @@ -47,9 +47,9 @@ class Clerk : Component() { fun main() { val deps = declareDependencies { // register components needed for dependency injection - add { ComponentQueue("waitingline") } - add { CustomerGenerator(get()) } - add { (1..3).map { Clerk() } } + dependency { ComponentQueue("waitingline") } + dependency { CustomerGenerator(get()) } + dependency { (1..3).map { Clerk() } } } createSimulation(dependencies = deps) { diff --git a/src/test/kotlin/org/kalasim/examples/bank/oneclerk/Bank1clerk.kt b/src/test/kotlin/org/kalasim/examples/bank/oneclerk/Bank1clerk.kt index 6c971a39..13cc7375 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/oneclerk/Bank1clerk.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/oneclerk/Bank1clerk.kt @@ -53,11 +53,13 @@ class CustomerGenerator : Component() { fun main() { val deps = declareDependencies { - add { Clerk() } - add { ComponentQueue("waiting line") } + dependency { Clerk() } + dependency { ComponentQueue("waiting line") } } - val env = createSimulation(true, dependencies = deps) { + val env = createSimulation(dependencies = deps) { + enableComponentLogger() + CustomerGenerator() } diff --git a/src/test/kotlin/org/kalasim/examples/bank/reneging/Bank3ClerksReneging.kt b/src/test/kotlin/org/kalasim/examples/bank/reneging/Bank3ClerksReneging.kt index 66cfcde6..3726a705 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/reneging/Bank3ClerksReneging.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/reneging/Bank3ClerksReneging.kt @@ -78,13 +78,13 @@ class Clerk : Component() { fun main() { - val env = configureEnvironment(true) { + val env = createSimulation { + enableComponentLogger() + // register components needed for dependency injection - add { ComponentQueue("waitingline") } - add { (0..2).map { Clerk() } } - } + dependency { ComponentQueue("waitingline") } + dependency { (0..2).map { Clerk() } } - env.apply { // register other components to be present when starting the simulation CustomerGenerator() diff --git a/src/test/kotlin/org/kalasim/examples/bank/reneging_resources/Bank3ClerksRenegingResources.kt b/src/test/kotlin/org/kalasim/examples/bank/reneging_resources/Bank3ClerksRenegingResources.kt index b1b506f5..060a5d40 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/reneging_resources/Bank3ClerksRenegingResources.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/reneging_resources/Bank3ClerksRenegingResources.kt @@ -35,7 +35,7 @@ class Customer(val clerks: Resource) : Component() { fun main() { declareDependencies { - add { Resource("clerks", capacity = 3) } + dependency { Resource("clerks", capacity = 3) } }.createSimulation { // register other components to be present when starting the simulation ComponentGenerator(iat = uniform(5.0, 15.0)) { diff --git a/src/test/kotlin/org/kalasim/examples/bank/reneging_state/Bank3ClerksRenegingState.kt b/src/test/kotlin/org/kalasim/examples/bank/reneging_state/Bank3ClerksRenegingState.kt index 2f6a5720..ea60a8c3 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/reneging_state/Bank3ClerksRenegingState.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/reneging_state/Bank3ClerksRenegingState.kt @@ -61,14 +61,16 @@ class Clerk(val workToDo: State) : Component() { fun main() { - val env = configureEnvironment(true) { + val env = createSimulation { + enableComponentLogger() + // register components needed for dependency injection - add { ComponentQueue("waitingline") } - add { State(false, "worktodo") } - add { (0..2).map { Clerk(get()) } } + dependency { ComponentQueue("waitingline") } + dependency { State(false, "worktodo") } + dependency { (0..2).map { Clerk(get()) } } } - // register other components to be present when starting the simulation + // register other components to be present when starting the simulation ComponentGenerator(iat = UniformRealDistribution(env.rg, 5.0, 15.0)) { val customer = Customer(get(), get()) customer diff --git a/src/test/kotlin/org/kalasim/examples/bank/resources/Bank3ClerksResources.kt b/src/test/kotlin/org/kalasim/examples/bank/resources/Bank3ClerksResources.kt index ad364d97..9f495c06 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/resources/Bank3ClerksResources.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/resources/Bank3ClerksResources.kt @@ -17,9 +17,9 @@ class Customer(private val clerks: Resource) : Component() { fun main() { - val env = configureEnvironment { - add { Resource("clerks", capacity = 3) } - }.apply { + val env = createSimulation { + dependency { Resource("clerks", capacity = 3) } + ComponentGenerator(iat = uniform(5.0, 15.0)) { Customer(get()) } } diff --git a/src/test/kotlin/org/kalasim/examples/bank/standby/Bank3ClerksStandby.kt b/src/test/kotlin/org/kalasim/examples/bank/standby/Bank3ClerksStandby.kt index 5915b112..cd7e2f2e 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/standby/Bank3ClerksStandby.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/standby/Bank3ClerksStandby.kt @@ -30,13 +30,14 @@ class Clerk : Component() { fun main() { val env = declareDependencies { - add { ComponentQueue("waitingline") } + dependency { ComponentQueue("waitingline") } + + }.createSimulation { + enableComponentLogger() - }.createSimulation(true) { repeat(3) { Clerk() } ComponentGenerator(uniform(5, 15)) { Customer(get()) } - } env.run(500.0) diff --git a/src/test/kotlin/org/kalasim/examples/bank/state/Bank3ClerksState.kt b/src/test/kotlin/org/kalasim/examples/bank/state/Bank3ClerksState.kt index 5b570849..d37e1951 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/state/Bank3ClerksState.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/state/Bank3ClerksState.kt @@ -46,10 +46,12 @@ class Clerk : Component() { fun main() { val env = declareDependencies { // register components needed for dependency injection - add { ComponentQueue("waitingline") } - add { State(false, "worktodo") } + dependency { ComponentQueue("waitingline") } + dependency { State(false, "worktodo") } + + }.createSimulation { + enableComponentLogger() - }.createSimulation(true) { // register other components to be present // when starting the simulation repeat(3) { Clerk() } diff --git a/src/test/kotlin/org/kalasim/examples/bank/threeclerks/Bank3Clerks.kt b/src/test/kotlin/org/kalasim/examples/bank/threeclerks/Bank3Clerks.kt index 501ae854..ae5333be 100644 --- a/src/test/kotlin/org/kalasim/examples/bank/threeclerks/Bank3Clerks.kt +++ b/src/test/kotlin/org/kalasim/examples/bank/threeclerks/Bank3Clerks.kt @@ -52,15 +52,12 @@ class Clerk : Component() { fun main() { - val env = configureEnvironment { - // register components needed for dependency injection - add { ComponentQueue("waitingline") } - add { State(false, "worktodo") } - add { CustomerGenerator() } - add { (1..3).map { Clerk() } } - } + createSimulation { + dependency { ComponentQueue("waitingline") } + dependency { State(false, "worktodo") } + dependency { CustomerGenerator() } + dependency { (1..3).map { Clerk() } } - env.apply { run(50000.0) val waitingLine: ComponentQueue = get() diff --git a/src/test/kotlin/org/kalasim/scratch/ComponentMetrics.kt b/src/test/kotlin/org/kalasim/scratch/ComponentMetrics.kt index 3a069a25..6b8cc71e 100644 --- a/src/test/kotlin/org/kalasim/scratch/ComponentMetrics.kt +++ b/src/test/kotlin/org/kalasim/scratch/ComponentMetrics.kt @@ -7,7 +7,9 @@ import org.kalasim.monitors.printConsole import org.kalasim.plot.kravis.display fun main() { - createSimulation(true) { + createSimulation { + enableComponentLogger() + val fuelPump = Resource("tank", capacity = 3) val refilPermitted = State(false) diff --git a/src/test/kotlin/org/kalasim/scratch/DataClassComponent.kt b/src/test/kotlin/org/kalasim/scratch/DataClassComponent.kt index 2bc9fcc5..937b9671 100644 --- a/src/test/kotlin/org/kalasim/scratch/DataClassComponent.kt +++ b/src/test/kotlin/org/kalasim/scratch/DataClassComponent.kt @@ -1,6 +1,7 @@ import org.kalasim.Component import org.kalasim.analysis.ConsoleTraceLogger import org.kalasim.createSimulation +import org.kalasim.enableComponentLogger // can components be extended into data classes? Yes they can. @@ -10,7 +11,9 @@ data class Foo(val bar: String) : Component() { fun main() { ConsoleTraceLogger.setColumnWidth(ConsoleTraceLogger.EventsTableColumn.Time, 30) - createSimulation(true) { + createSimulation { + enableComponentLogger() + Foo("").apply { println(toString()) } diff --git a/src/test/kotlin/org/kalasim/scratch/DinPhilKscript.kts b/src/test/kotlin/org/kalasim/scratch/DinPhilKscript.kts index 196ec15d..66291c74 100644 --- a/src/test/kotlin/org/kalasim/scratch/DinPhilKscript.kts +++ b/src/test/kotlin/org/kalasim/scratch/DinPhilKscript.kts @@ -36,7 +36,8 @@ class Philosopher(name: String, val leftFork: Fork, val rightFork: Fork) : Compo } } -val sim = createSimulation(true) { +val sim = createSimulation { + enableComponentLogger() enableEventLog() // create forks and resources diff --git a/src/test/kotlin/org/kalasim/scratch/InlinePredicates.kt b/src/test/kotlin/org/kalasim/scratch/InlinePredicates.kt index 74ab0253..5e12660c 100644 --- a/src/test/kotlin/org/kalasim/scratch/InlinePredicates.kt +++ b/src/test/kotlin/org/kalasim/scratch/InlinePredicates.kt @@ -2,6 +2,7 @@ package org.kalasim.scratch import org.kalasim.Component import org.kalasim.createSimulation +import org.kalasim.enableComponentLogger // Objective: // dream to just say something like @@ -9,10 +10,10 @@ import org.kalasim.createSimulation fun main() { - createSimulation(true) { + createSimulation { + enableComponentLogger() val waiter = object : Component() { - var isBusy = true override fun process() = diff --git a/src/test/kotlin/org/kalasim/scratch/shipyard/ShipyardRequest.kt b/src/test/kotlin/org/kalasim/scratch/shipyard/ShipyardRequest.kt index 9cc81052..eb3a1ee8 100644 --- a/src/test/kotlin/org/kalasim/scratch/shipyard/ShipyardRequest.kt +++ b/src/test/kotlin/org/kalasim/scratch/shipyard/ShipyardRequest.kt @@ -7,7 +7,8 @@ import org.kalasim.plot.kravis.display // // In contrast to ShipyardState.kt, we here do ot rely on a state for interation, but use built-in request capabilities and a depletable resource to wait until enough cargo has accumulated. fun main() { - createSimulation(true) { + createSimulation { + enableComponentLogger() val terminal = object : Component("terminal") { val tank = DepletableResource(capacity = 100, initialLevel = 10) diff --git a/src/test/kotlin/org/kalasim/test/BranchingTests.kt b/src/test/kotlin/org/kalasim/test/BranchingTests.kt index 7f593f25..9571e5f7 100644 --- a/src/test/kotlin/org/kalasim/test/BranchingTests.kt +++ b/src/test/kotlin/org/kalasim/test/BranchingTests.kt @@ -2,20 +2,27 @@ package org.kalasim.test +import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import org.junit.Test import org.kalasim.Component +import org.kalasim.State import org.kalasim.misc.AmbiguousDuration -import org.kalasim.misc.printThis import org.kalasim.tt +import kotlin.time.Duration.Companion.minutes -class Tool : Component(){ +class Tool : Component() { + + val state = State(false) override fun process() = sequence { - object : Component("branch"){ - override fun process() = sequence { - doSomething() + object : Component("branch") { + override fun process() = sequence { + shouldThrow { + doSomething() + } + now shouldBe 50.tt } } @@ -23,18 +30,26 @@ class Tool : Component(){ // doSomething() // ^^ not needed to reproduce the effect - hold(100, "busy for another some ticks") + hold(100.minutes, "busy for another some ticks") now shouldBe 100.tt println(env.toJson()) } - suspend fun SequenceScope.doSomething(){ + + private suspend fun SequenceScope.doSomething() { + wait(state, true) hold(50, "doing something") } } +//class ProcessScope(val foo: SequenceScope) { +// @OptIn(ExperimentalTypeInference::class) +// public fun sequence(@BuilderInference block: suspend SequenceScope.() -> Unit): Sequence = foo.seq +// +//} + class BranchingTests { @Test diff --git a/src/test/kotlin/org/kalasim/test/ComponentTests.kt b/src/test/kotlin/org/kalasim/test/ComponentTests.kt index c9505d55..e17a6ec9 100644 --- a/src/test/kotlin/org/kalasim/test/ComponentTests.kt +++ b/src/test/kotlin/org/kalasim/test/ComponentTests.kt @@ -141,7 +141,8 @@ class ComponentTests { @Test - fun `it should allow for an empty generator process`() = createTestSimulation(true) { + fun `it should allow for an empty generator process`() = createTestSimulation { + val c = Component() // object : Component(){} @@ -258,7 +259,7 @@ class ComponentTests { @Test fun `it should log to the console`() = captureOutput { - createTestSimulation(true) { + createTestSimulation { object : Component("tester") { override fun process() = sequence { wait(State(true), true) @@ -273,7 +274,6 @@ class ComponentTests { } }.stdout shouldBeDiff """time current receiver action info --------- --------------------- --------------------- ------------------------------------------------------ ---------------------------------- -.00 main Created .00 tester Created .00 Activated, scheduled for .00 New state: scheduled .00 main Running; Hold +5.00, scheduled for 5.00 New state: scheduled @@ -526,7 +526,8 @@ class ComponentTests { } @Test - fun `it should hold on someones elses behalf`() = createTestSimulation(true) { + fun `it should hold on someones elses behalf`() = createTestSimulation { + val c = object : Component("other") { override fun process() = sequence { @@ -555,7 +556,8 @@ class ComponentTests { @Test - fun `it should throw user exceptions`() = createTestSimulation(true) { + fun `it should throw user exceptions`() = createTestSimulation { + class MyException(msg: String) : IllegalArgumentException(msg) object : Component("other") { diff --git a/src/test/kotlin/org/kalasim/test/DepletableResourceTests.kt b/src/test/kotlin/org/kalasim/test/DepletableResourceTests.kt index 563de55d..b129d34b 100644 --- a/src/test/kotlin/org/kalasim/test/DepletableResourceTests.kt +++ b/src/test/kotlin/org/kalasim/test/DepletableResourceTests.kt @@ -14,7 +14,7 @@ import org.kalasim.* class DepletableResourceTests { @Test - fun `it should realize depletable resource semantics`() = createTestSimulation(true) { + fun `it should realize depletable resource semantics`() = createTestSimulation { DepletableResource(capacity = 100, initialLevel = 0).apply { isFull shouldBe false isDepleted shouldBe true @@ -24,7 +24,7 @@ class DepletableResourceTests { } @Test - fun `it should process an empty take`() = createTestSimulation(true) { + fun `it should process an empty take`() = createTestSimulation { val dr = DepletableResource(capacity = 10, initialLevel = 0) object : Component() { @@ -40,8 +40,7 @@ class DepletableResourceTests { } @Test - fun `it should allow to consume and refill a depletable resource`() = createTestSimulation(true) { - + fun `it should allow to consume and refill a depletable resource`() = createTestSimulation { // add new gas continuously object : Component() { override fun process() = sequence { @@ -102,7 +101,7 @@ class DepletableResourceTests { @Test - fun `it allow filling and emptying from 0 to capacity limit`() = createTestSimulation(true) { + fun `it allow filling and emptying from 0 to capacity limit`() = createTestSimulation { val gasSupply = DepletableResource(capacity = 100, initialLevel = 0) gasSupply.isFull shouldBe false @@ -147,14 +146,14 @@ class DepletableResourceTests { @Test - fun `it should forbid capacities larger than Int_MAX_VALUE`() = createTestSimulation(true) { + fun `it should forbid capacities larger than Int_MAX_VALUE`() = createTestSimulation { shouldThrow { DepletableResource(capacity = Double.MAX_VALUE, initialLevel = 0) } } @Test - fun `it ensure that capacity=INF has meaningful semantics`() = createTestSimulation(true) { + fun `it ensure that capacity=INF has meaningful semantics`() = createTestSimulation { val dp = DepletableResource(capacity = Int.MAX_VALUE, initialLevel = 0) object : Component() { @@ -174,7 +173,7 @@ class DepletableResourceTests { @Test fun `it should ensure that a level increase is stalled until capacity becomes available`() = - createTestSimulation(true) { + createTestSimulation { val dp = DepletableResource(capacity = 100, initialLevel = 0) dp.level shouldBe 0 @@ -197,7 +196,7 @@ class DepletableResourceTests { @Test - fun `it should respect queue priorities when consuming resource`() = createTestSimulation(true) { + fun `it should respect queue priorities when consuming resource`() = createTestSimulation { val dp = DepletableResource(capacity = 100, initialLevel = 0) dp.level shouldBe 0 @@ -228,7 +227,7 @@ class DepletableResourceTests { @Test - fun `it should respect queue priorities when restoring resource`() = createTestSimulation(true) { + fun `it should respect queue priorities when restoring resource`() = createTestSimulation { val resource = DepletableResource(capacity = 10, initialLevel = 7) var normalPutHonored = false diff --git a/src/test/kotlin/org/kalasim/test/DeterministicRefuel.kt b/src/test/kotlin/org/kalasim/test/DeterministicRefuel.kt index 4146a01d..3f19436b 100644 --- a/src/test/kotlin/org/kalasim/test/DeterministicRefuel.kt +++ b/src/test/kotlin/org/kalasim/test/DeterministicRefuel.kt @@ -92,20 +92,19 @@ object DeterministicRefuel { } } - configureEnvironment(true) { + createSimulation { + dependency { GasStation() } -// single(qualifier = named("gas_station")) { Resource("gas_station", 2) } - - single { GasStation() } - - single(qualifier = named(FUEL_PUMP)) { DepletableResource(FUEL_PUMP, GAS_STATION_SIZE) } - single(qualifier = named(CAR_LEAVING)) { IntTimeline(CAR_LEAVING) } - single(qualifier = named(TRUCKS_EN_ROUTE)) { IntTimeline(TRUCKS_EN_ROUTE) } - single(qualifier = named(TRUCKS_ORDERED)) { IntTimeline(TRUCKS_ORDERED) } - }.apply { + // declare dependencies + dependency(qualifier = named(FUEL_PUMP)) { DepletableResource(FUEL_PUMP, GAS_STATION_SIZE) } + dependency(qualifier = named(CAR_LEAVING)) { IntTimeline(CAR_LEAVING) } + dependency(qualifier = named(TRUCKS_EN_ROUTE)) { IntTimeline(TRUCKS_EN_ROUTE) } + dependency(qualifier = named(TRUCKS_ORDERED)) { IntTimeline(TRUCKS_ORDERED) } + // setup stochastic car arrival process ComponentGenerator(iat = T_INTER) { Car(get()) } + run(SIM_TIME) val fuelPump = get(qualifier = named(FUEL_PUMP)) diff --git a/src/test/kotlin/org/kalasim/test/EnvTests.kt b/src/test/kotlin/org/kalasim/test/EnvTests.kt index af0ad3f0..a13157fc 100644 --- a/src/test/kotlin/org/kalasim/test/EnvTests.kt +++ b/src/test/kotlin/org/kalasim/test/EnvTests.kt @@ -15,7 +15,9 @@ import org.junit.Test import org.kalasim.* import org.kalasim.analysis.EntityCreatedEvent import org.kalasim.analysis.RescheduledEvent -import org.kalasim.examples.bank.data.* +import org.kalasim.examples.bank.data.Clerk +import org.kalasim.examples.bank.data.Customer +import org.kalasim.examples.bank.data.CustomerGenerator import org.kalasim.examples.er.EmergencyRoom import org.kalasim.misc.* import org.koin.core.Koin @@ -183,7 +185,9 @@ class EnvTests { fun `it should allow to synchronize clock time`() { val timeBefore = System.currentTimeMillis() - createSimulation(true) { + createSimulation { + enableComponentLogger() + ClockSync(500.milliseconds) run(10) @@ -194,7 +198,7 @@ class EnvTests { @Test - fun `it should allow collecting events by type`() = createTestSimulation(true) { + fun `it should allow collecting events by type`() = createTestSimulation { ClockSync(500.milliseconds) val creations = collect() @@ -205,9 +209,42 @@ class EnvTests { creations.size shouldBe (cg.total + 1) // +1 because of main } + @Suppress("DEPRECATION") + @Test + fun `it still support configuring dependencies before creating the simulation`() { + class Car : Component() { + override fun process() = sequence { + hold(10.minutes) + } + } + + val env = createSimulation { + dependency { Car() } + + run(5.minutes) + + get().isScheduled shouldBe true + } + } + + @Test + fun `it still should fail when last element in createSimulation is a dependency as this will fail internally otherwise`() { + shouldThrow { + val sim = createSimulation { + dependency("foo") { 1 } + dependency("bar") { 2 } + } + + sim.get("foo") + sim.run(1.days) + } + } + @Test fun `it should fail with exception if simulation is too slow `() { - createSimulation(true) { + createSimulation { + enableComponentLogger() + object : Component() { var waitCounter = 1 @@ -484,12 +521,12 @@ class CustomKoinModuleTests { fun `it should create components on simulation start`() { val deps = declareDependencies { // register components needed for dependency injection - add { + dependency { println("foo") ComponentQueue("waitingline") } - add { CustomerGenerator(get()) } - add { (1..3).map { Clerk() } } + dependency { CustomerGenerator(get()) } + dependency { (1..3).map { Clerk() } } } createSimulation(dependencies = deps) { diff --git a/src/test/kotlin/org/kalasim/test/QueueTests.kt b/src/test/kotlin/org/kalasim/test/QueueTests.kt index 6d56ea60..c72f05ff 100644 --- a/src/test/kotlin/org/kalasim/test/QueueTests.kt +++ b/src/test/kotlin/org/kalasim/test/QueueTests.kt @@ -21,11 +21,12 @@ class QueueTests { ComponentQueue() } - val env = configureEnvironment(true) { - add { waitingLine } - } + val env = createSimulation { + enableComponentLogger() + dependency { waitingLine } - env.addEventListener(EventLog()) + enableEventLog() + } waitingLine.add(Foo()) waitingLine.add(Foo()) @@ -73,7 +74,9 @@ class QueueTests { // also see https://simpy.readthedocs.io/en/latest/topical_guides/time_and_scheduling.html - createSimulation(true) { + createSimulation { + enableComponentLogger() + // class TestComponent(name:String, at: Number): Component(name){ // override fun process(): Sequence = super.process() // } @@ -103,7 +106,9 @@ class QueueTests { } // redo but with priority - createSimulation(true) { + createSimulation { + enableComponentLogger() + // to make sure that the component methods are auto-scheduled we need to overrride them // class TestComponent(name:String, at: Number, priority: Priority = NORMAL): Component(name, priority = priority){ // override fun process(): Sequence = super.process() diff --git a/src/test/kotlin/org/kalasim/test/ResourceTests.kt b/src/test/kotlin/org/kalasim/test/ResourceTests.kt index 504f5130..68ea4e42 100644 --- a/src/test/kotlin/org/kalasim/test/ResourceTests.kt +++ b/src/test/kotlin/org/kalasim/test/ResourceTests.kt @@ -60,7 +60,7 @@ class ResourceTests { } @Test - fun `it should release resourced when terminating`() = createTestSimulation(true) { + fun `it should release resourced when terminating`() = createTestSimulation { // see bank office docs: By design resources that were claimed are automatically // released when a process terminates @@ -82,7 +82,7 @@ class ResourceTests { } @Test - fun `preemptive resources should bump components depending on priority`() = createTestSimulation(true) { + fun `preemptive resources should bump components depending on priority`() = createTestSimulation { // see bank office docs: By design resources that were claimed are automatically // released when a process terminates @@ -156,7 +156,7 @@ class ResourceTests { } @Test - fun `null prio requests should not bump each other`() = createTestSimulation(true) { + fun `null prio requests should not bump each other`() = createTestSimulation { val r = Resource(preemptive = true) class BumpComponent : Component() { @@ -178,7 +178,7 @@ class ResourceTests { } @Test - fun `it should not bump regular non-preemptive resources`() = createTestSimulation(true) { + fun `it should not bump regular non-preemptive resources`() = createTestSimulation { val r = Resource() class BumpComponent : Component() { @@ -239,7 +239,7 @@ class ResourceTests { @Test - fun `it should respect request priorities when mixing request sizes`() = createTestSimulation(true) { + fun `it should respect request priorities when mixing request sizes`() = createTestSimulation { val resource = Resource(capacity = 2) @@ -330,7 +330,9 @@ class ResourceTests { @Test fun `it should auto-release resources in builder`() { - createSimulation(true) { + createSimulation { + enableComponentLogger() + val r = Resource() object : Component() { @@ -464,7 +466,9 @@ class ResourceTests { @Test fun `it should correctly set failed after timeout`() { - createSimulation(true) { + createSimulation { + enableComponentLogger() + val r = Resource(capacity = 1) val dr = Resource(capacity = 1) @@ -536,7 +540,7 @@ class ResourceTests { class ResourceSelectionTests { @Test - fun `it should allow to select with FIRST_AVAILBLE`() = createTestSimulation(true) { + fun `it should allow to select with FIRST_AVAILBLE`() = createTestSimulation { val resources = List(3) { Resource().apply { capacity = 0.0 } } object : Component() { @@ -552,7 +556,7 @@ class ResourceSelectionTests { } @Test - fun `it should allow to select with ShortestQueue`() = createTestSimulation(true) { + fun `it should allow to select with ShortestQueue`() = createTestSimulation { val resources = List(3) { Resource() } class ResourceConsumer(val resource: Resource) : Component() { @@ -578,7 +582,7 @@ class ResourceSelectionTests { } @Test - fun `it should allow to select with RoundRobin`() = createTestSimulation(true) { + fun `it should allow to select with RoundRobin`() = createTestSimulation { val resources = List(3) { Resource() } val c = object : Component() { diff --git a/src/test/kotlin/org/kalasim/test/StateTests.kt b/src/test/kotlin/org/kalasim/test/StateTests.kt index befd491f..1a7b734f 100644 --- a/src/test/kotlin/org/kalasim/test/StateTests.kt +++ b/src/test/kotlin/org/kalasim/test/StateTests.kt @@ -54,11 +54,10 @@ class StateTests { } } - val sim = configureEnvironment(true) { - single { State("red") } - } + val sim = createSimulation { + enableComponentLogger() + dependency { State("red") } - sim.apply { val car = Car() car.activate() @@ -86,7 +85,7 @@ class StateTests { @Test fun `it trigger once and keep the other waiters`() { captureOutput { - createTestSimulation(true) { + createTestSimulation { val state = State(TestColor.RED) var wasAStarted = false @@ -118,7 +117,6 @@ class StateTests { }.stdout shouldBeDiff """ time current receiver action info --------- --------------------- --------------------- ------------------------------------------------------ ---------------------------------- - .00 main Created .00 State.1 Created Initial value: RED .00 A Created .00 Activated, scheduled for .00 New state: scheduled @@ -153,45 +151,44 @@ class StateTests { } } - val sim = configureEnvironment(true) { - single { TrafficLight() } - single { Engine() } - } + createSimulation { + enableComponentLogger() - val car = Car() + val trafficLight = dependency { TrafficLight() } + val engine = dependency { Engine() } - val trafficLight = sim.get() - val engine = sim.get() + val car = Car() - trafficLight.printSummary() + trafficLight.printSummary() - sim.run(10.0) + run(10.0) - trafficLight.snapshot.waiters.size shouldBe 1 + trafficLight.snapshot.waiters.size shouldBe 1 - // toggle state - trafficLight.value = "green" + // toggle state + trafficLight.value = "green" - sim.run(10.0) + run(10.0) - trafficLight.printSummary() + trafficLight.printSummary() - trafficLight.snapshot.waiters.size shouldBe 1 + trafficLight.snapshot.waiters.size shouldBe 1 - car.isWaiting shouldBe true + car.isWaiting shouldBe true - // now honor the engine - engine.value = true + // now honor the engine + engine.value = true - car.printSummary() + car.printSummary() - sim.run(10.0) + run(10.0) - car.isWaiting shouldBe false - car.isData shouldBe true + car.isWaiting shouldBe false + car.isData shouldBe true - trafficLight.snapshot.waiters.shouldBeEmpty() - engine.snapshot.waiters.shouldBeEmpty() + trafficLight.snapshot.waiters.shouldBeEmpty() + engine.snapshot.waiters.shouldBeEmpty() + } } @@ -215,14 +212,13 @@ class StateTests { } } - val sim = configureEnvironment(true) { + createSimulation { + enableComponentLogger() // single(TypeQualifier(String::class)) { State("red") } // single(TypeQualifier(Boolean::class)) { State(false) } - single { State("red") } - single { State(false) } - } + dependency { State("red") } + dependency { State(false) } - sim.apply { val car = Car() val trafficLight = get>() diff --git a/src/test/kotlin/org/kalasim/test/StateTransitionTests.kt b/src/test/kotlin/org/kalasim/test/StateTransitionTests.kt index d21f3245..c5176076 100644 --- a/src/test/kotlin/org/kalasim/test/StateTransitionTests.kt +++ b/src/test/kotlin/org/kalasim/test/StateTransitionTests.kt @@ -72,7 +72,7 @@ class StateTransitionTests { @Test fun `it should passivate, cancel, hold and activate on behalf of another component`() = - createTestSimulation(true) { + createTestSimulation { val r = Resource() val s = State("foo") diff --git a/src/test/kotlin/org/kalasim/test/TestUtils.kt b/src/test/kotlin/org/kalasim/test/TestUtils.kt index 136cbcc5..af06638a 100644 --- a/src/test/kotlin/org/kalasim/test/TestUtils.kt +++ b/src/test/kotlin/org/kalasim/test/TestUtils.kt @@ -42,7 +42,10 @@ internal fun captureOutput(expr: () -> Any): CapturedOutput { internal fun createTestSimulation(enableComponentLogger: Boolean = true, builder: Environment.() -> Unit) { - createSimulation(enableComponentLogger, builder = builder) + createSimulation{ + if(enableComponentLogger) enableComponentLogger() + builder() + } } internal fun testModel(sim: S, smthg: S.() -> Unit): Unit = smthg(sim) diff --git a/src/test/kotlin/org/kalasim/test/TimeTrafoTests.kt b/src/test/kotlin/org/kalasim/test/TimeTrafoTests.kt index b3ee9fec..0835f342 100644 --- a/src/test/kotlin/org/kalasim/test/TimeTrafoTests.kt +++ b/src/test/kotlin/org/kalasim/test/TimeTrafoTests.kt @@ -26,7 +26,7 @@ class TimeTrafoTests { } @Test - fun `it should correctly project simulation times with offset-trafo`() = createTestSimulation(true) { + fun `it should correctly project simulation times with offset-trafo`() = createTestSimulation { startDate = Instant.parse("2021-01-24T12:00:00.00Z") object : Component() {