org.kalasim.State.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kalasim Show documentation
Show all versions of kalasim Show documentation
kalasim is a process-oriented discrete event simulation engine
package org.kalasim
import org.kalasim.analysis.EntityCreatedEvent
import org.kalasim.analysis.StateChangedEvent
import org.kalasim.analysis.snapshot.StateSnapshot
import org.kalasim.misc.DependencyContext
import org.kalasim.misc.StateTrackingConfig
import org.kalasim.monitors.*
import org.koin.core.Koin
/**
* States together with the Component.wait() method provide a powerful way of process interaction.
*
* A state will have a certain value at a given time. In its simplest form a component can then wait for a specific value of a state. Once that value is reached, the component will be resumed.
*
* @sample org.kalasim.dokka.statesHowTo
*/
open class State(
initialValue: T,
name: String? = null,
koin: Koin = DependencyContext.get(),
val trackingConfig: StateTrackingConfig = koin.getEnvDefaults().DefaultStateConfig,
) : SimulationEntity(name, koin) {
private var maxTriggerCxt: Int? = null
private val isTriggerCxt
get() = maxTriggerCxt != null
var value: T = initialValue
set(value) {
if(field == value) return
field = value
// salabim also logs the set even if the state.value may not change
log(trackingConfig.logTriggers) {
StateChangedEvent(
env.now,
this,
value,
current = env.currentComponent,
if(isTriggerCxt) maxTriggerCxt else null,
)
}
changeListeners.forEach { it.stateChanged(this) }
timeline.addValue(value)
// if (Thread.currentThread().getStackTrace()[2].methodName != "trigger") {
if(!isTriggerCxt) {
tryWait()
}
}
val timeline = CategoryTimeline(initialValue = value, koin = koin, name = "State of '$name'")
internal val waiters = ComponentQueue("waiters of ${this.name}", koin = koin)
// val waiters = PriorityQueue()
/** Tracks the queue length level along time. */
val queueLength: MetricTimeline
get() = waiters.sizeTimeline
/** Tracks the length of stay in the queue over time*/
val lengthOfStay: NumericStatisticMonitor
get() = waiters.lengthOfStayStatistics
init {
with(trackingConfig) {
lengthOfStay.enabled = trackQueueStatistics
queueLength.enabled = trackQueueStatistics
timeline.enabled = trackValue
}
log(trackingConfig.logCreation) {
EntityCreatedEvent(now, env.currentComponent, this, "Initial value: $value")
}
}
override fun toString(): String = super.toString() + "[${value}]"
private var isTrigger: Boolean = false
/**
* Sets the value `value` and triggers any components waiting,
* then at most max waiting components for this state will be honored and next
* the value will be set to value_after and again checked for possible honors.
*
* @param value the new value
* @param valueAfter After the trigger this will be the new value. If omitted, return to the value before the trigger
* @param max Maximum number of components to be honored for the trigger value
*/
fun trigger(value: T, valueAfter: T = this.value, max: Int = Int.MAX_VALUE) {
triggerContext(max) {
this.value = value
}
tryWait(max)
// restore to valueAfter
this.value = valueAfter
}
private fun triggerContext(max: Int, smthg: () -> Unit) {
maxTriggerCxt = max
smthg()
maxTriggerCxt = null
}
// private fun tryWait(maxHonor: Int = Int.MAX_VALUE) {
// var mx = maxHonor
// waiters.q.map { it.component }.takeWhile {
// // wait max times but consider honor return state of tryWait
// if(it.tryWait()) mx--
// mx > 0
// }
// }
// throws concurrent modification exception
// private fun tryWait(maxHonor: Int = Int.MAX_VALUE) {
// var remainingHonor = maxHonor
// val iterator = waiters.q.iterator()
//
// while (remainingHonor > 0 && iterator.hasNext()) {
// val waiter = iterator.next().component
// if (waiter.tryWait()) {
// remainingHonor--
// }
// }
// }
private fun tryWait(maxHonor: Int = Int.MAX_VALUE) {
val copyOfQ = ArrayList(waiters.q) // Make a copy of the collection
var remainingHonor = maxHonor
for(waiter in copyOfQ) {
val component = waiter.component
if(remainingHonor <= 0) {
break
}
if(component.tryWait()) {
remainingHonor--
}
}
}
fun printHistograms() {
waiters.printHistogram()
timeline.printHistogram()
}
internal val changeListeners = mutableListOf>()
fun interface StateChangeListener {
fun stateChanged(state: State)
}
/** Register a change listener. Will be invoked on every value change of the state. */
fun onChange(function: StateChangeListener) = changeListeners.add(function)
override val snapshot
get() = StateSnapshot(env.now, name, value.toString(), waiters.q.map { it.component.name })
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy