Skip to content

Commit

Permalink
updated user manual
Browse files Browse the repository at this point in the history
  • Loading branch information
holgerbrandl committed Nov 17, 2023
1 parent 9355e17 commit 4932953
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 34 deletions.
51 changes: 36 additions & 15 deletions docs/userguide/docs/component.md
Expand Up @@ -111,13 +111,25 @@ Effectively, creation and start of `crane1` and `crane2` is the same.

To run/consume/inline another [process definition](#process-definition), we can use[`yieldAll(subProcess())`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence-scope/yield-all.html) to inline `subProcess()` defined for the same component. This allows to inline the entire process definition in a blocking manner. Here's an example how to do so:

```kotlin hl_lines="1000"
{!api/ConsumeSubProcess.kts!}
```kotlin
//{!api/ConsumeSubProcess.kts!}
```

### Toggling processes

It's a very effective tool in discrete simulation, to toggle the process definition of a component at runtime. We can do so very effectively using [`activate()`](#activate)
It's a very effective tool in discrete simulation, to toggle the process definition of a component at runtime. Using [`activate()`](#activate) we can toggle processes very effectively in a simulation model. There are 3 ways to do so

1. From with a component's [process defintion](#process-definition)
2. Within another component process defintion
3. Outside of any process definition.

The following example illustrates these examples as well as [process inlining](#inlining-subprocesses):

```kotlin
//{!api/Restaurant.kts!}
```
Notably, we can provide process arguments here in a typesafe manner.


## Lifecycle

Expand Down Expand Up @@ -225,6 +237,9 @@ Supported parameters in `hold()` are

Either `duration` or `until` must be specified when calling `hold()` to indicate the intended delay.


#### State Contract

The [state](#lifecycle) contract when calling `hold()` is as follows

* If the component is `CURRENT`, it will suspend execution internally, and the component becomes scheduled for the specified time
Expand Down Expand Up @@ -257,6 +272,22 @@ car1.activate(process=Car::refilling)
car0.activate()
```

#### Parameters

Supported parameters in `activate()`

* `process` - The name of the process to be started. If set to `None`, the process will not be changed. If the component is a data component, the generator function `process` will be used as the default process. Optionally type safe arguments can be provided to the generator function via `processArgument` and `otherProcessArgument`
* `processArgument` - The argument to be passed to the process.
* `at` - The schedule time. If omitted, no `delay` is used.
* `delay` - The delay before starting the process. It uses a `Duration` object to specify the delay amount. The default value is `Duration.ZERO`.
* `priority` - The priority level of the activation. It uses the `Priority` enumeration with options HIGH, NORMAL, and LOW. The default value is NORMAL.
* `urgent` - Indicates whether the activation is urgent or not. If set to true, the activation will be treated as urgent. The default value is false.
* `keepRequest` - Indicates whether to keep the activation request even after the process is started. If set to true, the activation request will be kept. The default value is false.
* `keepWait` - Indicates whether to keep waiting for the process to complete before returning. If set to true, the activation will not return until the process is complete. The default value is false.
#### State Contract

The [state](#lifecycle) contract when calling `hold()` is as follows

<!--* If the component to be activated is `CURRENT`, always use `yield(activate())`. The effect is that the-->
<!-- component becomes scheduled, thus this is essentially equivalent to the preferred hold method.-->
* If the component to be activated is `DATA`, unless provided with `process` the default `Component::process` will be scheduled at the specified time.
Expand All @@ -272,6 +303,7 @@ car0.activate()
scheduled.
* If the component is `INTERRUPTED`, the component will be activated at the specified time.

#### Misc

!!!important
It is not possible to `activate()` the `CURRENT` component without providing a `process` argument. `kalasim` will throw an error in this situation. The effect of a "self"-activate would be that the component becomes scheduled, thus this is essentially equivalent to the preferred hold method, so please use `hold` instead. The error is a safe-guard mechanism to prevent the user from unintentionally rescheduling the current component again.
Expand All @@ -297,18 +329,7 @@ ship2.activate(delay=50.minutes)
```
However, in most situations this is better modelled within a [process definition](#process-definition).

Using `activate()` we can toggle processes very effectively in a simulation model. There are 3 ways to do so

1. From with a component's [process defintion](#process-definition)
2. Within another component [process defintion](#process-definition)
3. Outside of any process definition.

The following example illustrates these examples as well as [process inlining](#inlining-subprocesses):

```kotlin
//{!api/Restaurant.kts!}
```
Notably, we can provide process arguments here in a typesafe manner.
We can use `activate` to [toggle](#toggling-processes) the active process of a component

### passivate

Expand Down
2 changes: 1 addition & 1 deletion modules/benchmarks/README.md
Expand Up @@ -11,7 +11,7 @@ For gradle plugin options see https://github.com/melix/jmh-gradle-plugin
* This will execute all (methods annotated with `@Benchmark`) benchmarks with their predefined parameters:

```bash
# cd cd /d/projects/scheduling/kalasim/modules/benchmarks
# cd /d/projects/scheduling/kalasim/modules/benchmarks
./gradlew --console=plain clean jmh`
```

Expand Down
Expand Up @@ -25,9 +25,17 @@ fun simpleCrossing(): GeoMap {
val segment01_10 = PathSegment("segment01_10", node01, node10)
val segment01_11 = PathSegment("segment01_11", node01, node11)

// parking
val port00_01 = Port("port00_01", 0.2, segment00_01)
val port00_10 = Port("port00_10", 0.2, segment00_10)
val port01_10 = Port("port01_10", 0.2, segment01_10)
val port01_11 = Port("port01_11", 0.2, segment01_11)


return GeoMap(
listOf(segment00_01, segment00_10, segment01_10, segment01_11),
listOf(node00, node01, node11, node10),
listOf(port00_01, port00_10, port01_10, port01_11)
)
}

Expand All @@ -36,11 +44,13 @@ class VehicleTests {
@Test
fun `it should respect the right of way`() {
createSimulation {
enableComponentLogger()

val map = simpleCrossing()

class Car(startingPosition: Port) : Vehicle(startingPosition)

dependency { PathFinder(map) }
dependency { PathFinder(map) }

// val startTime = uniform(0, 10).minutes

Expand All @@ -49,10 +59,13 @@ class VehicleTests {
}

repeat(10) {
cars.forEach { it.move(map.ports.random(random)) }
run(1.hour)
cars.forEach {
it.activate(Vehicle::moveTo, map.ports.random(random))
}
}


run(1.hour)
}
}
}
112 changes: 98 additions & 14 deletions src/main/kotlin/org/kalasim/Component.kt
Expand Up @@ -1107,11 +1107,6 @@ open class Component(
}


// @Deprecated("no longer needed, handled by queue directly")
// private fun enterSorted(requesters: Queue<Component>, priority: Int) {
// }


// kept in Component API for api compatibility with salabim
private fun remove() {
env.remove(this)
Expand Down Expand Up @@ -1197,6 +1192,26 @@ open class Component(
}


/**
* Activate a component.
*
* This method is used to activate a component. The activation process starts a specified process with the given arguments.
*
* Please refer to the [user manual](https://www.kalasim.org/component/#activate) for more information on the `activate` contract.
*
* @param process The name of the process to be started. If set to `None`, the process will not be changed. If the
* component is a data component, the generator function `process` will be used as the default process. Optionally type safe
* arguments can be provided to the generator function via `processArgument` and `otherProcessArgument`
* @param processArgument The argument to be passed to the process.
* @param at The schedule time. If omitted, no `delay` is used.
* @param delay The delay before starting the process. It uses a `Duration` object to specify the delay amount. The default value is `Duration.ZERO`.
* @param priority The priority level of the activation. It uses the `Priority` enumeration with options HIGH, NORMAL, and LOW. The default value is NORMAL.
* @param urgent Indicates whether the activation is urgent or not. If set to true, the activation will be treated as urgent. The default value is false.
* @param keepRequest Indicates whether to keep the activation request even after the process is started. If set to true, the activation request will be kept. The default value is false.
* @param keepWait Indicates whether to keep waiting for the process to complete before returning. If set to true, the activation will not return until the process is complete. The default value is false.
*
* @return The activated component.
*/
suspend fun SequenceScope<Component>.activate(
at: SimTime? = null,
delay: Duration = Duration.ZERO,
Expand All @@ -1218,6 +1233,26 @@ open class Component(
}


/**
* Activate a component.
*
* This method is used to activate a component. The activation process starts a specified process with the given arguments.
*
* Please refer to the [user manual](https://www.kalasim.org/component/#activate) for more information on the `activate` contract.
*
* @param process The name of the process to be started. If set to `None`, the process will not be changed. If the
* component is a data component, the generator function `process` will be used as the default process. Optionally type safe
* arguments can be provided to the generator function via `processArgument` and `otherProcessArgument`
* @param processArgument The argument to be passed to the process.
* @param at The schedule time. If omitted, no `delay` is used.
* @param delay The delay before starting the process. It uses a `Duration` object to specify the delay amount. The default value is `Duration.ZERO`.
* @param priority The priority level of the activation. It uses the `Priority` enumeration with options HIGH, NORMAL, and LOW. The default value is NORMAL.
* @param urgent Indicates whether the activation is urgent or not. If set to true, the activation will be treated as urgent. The default value is false.
* @param keepRequest Indicates whether to keep the activation request even after the process is started. If set to true, the activation request will be kept. The default value is false.
* @param keepWait Indicates whether to keep waiting for the process to complete before returning. If set to true, the activation will not return until the process is complete. The default value is false.
*
* @return The activated component.
*/
suspend fun <T : Any> SequenceScope<Component>.activate(
process: ProcessPointerWithArg<T>,
processArgument: T,
Expand All @@ -1240,10 +1275,30 @@ open class Component(
)
}

/**
* Activate a component.
*
* This method is used to activate a component. The activation process starts a specified process with the given arguments.
*
* Please refer to the [user manual](https://www.kalasim.org/component/#activate) for more information on the `activate` contract.
*
* @param process The name of the process to be started. If set to `None`, the process will not be changed. If the
* component is a data component, the generator function `process` will be used as the default process. Optionally type safe
* arguments can be provided to the generator function via `processArgument` and `otherProcessArgument`
* @param processArgument The argument to be passed to the process.
* @param at The schedule time. If omitted, no `delay` is used.
* @param delay The delay before starting the process. It uses a `Duration` object to specify the delay amount. The default value is `Duration.ZERO`.
* @param priority The priority level of the activation. It uses the `Priority` enumeration with options HIGH, NORMAL, and LOW. The default value is NORMAL.
* @param urgent Indicates whether the activation is urgent or not. If set to true, the activation will be treated as urgent. The default value is false.
* @param keepRequest Indicates whether to keep the activation request even after the process is started. If set to true, the activation request will be kept. The default value is false.
* @param keepWait Indicates whether to keep waiting for the process to complete before returning. If set to true, the activation will not return until the process is complete. The default value is false.
*
* @return The activated component.
*/
suspend fun <T : Any, S : Any> SequenceScope<Component>.activate(
process: ProcessPointerWithArgs<T, S>,
processArgument: T,
otherArgument: S,
otherProcessArgument: S,
at: SimTime? = null,
delay: Duration = Duration.ZERO,
priority: Priority = NORMAL,
Expand All @@ -1253,7 +1308,7 @@ open class Component(

) = yieldCurrent {
this@Component.activateInternal(
ProcessReference(process as GeneratorFunRef, processArgument, otherArgument),
ProcessReference(process as GeneratorFunRef, processArgument, otherProcessArgument),
at,
delay,
priority,
Expand All @@ -1263,6 +1318,26 @@ open class Component(
)
}

/**
* Activate a component.
*
* This method is used to activate a component. The activation process starts a specified process with the given arguments.
*
* Please refer to the [user manual](https://www.kalasim.org/component/#activate) for more information on the `activate` contract.
*
* @param process The name of the process to be started. If set to `None`, the process will not be changed. If the
* component is a data component, the generator function `process` will be used as the default process. Optionally type safe
* arguments can be provided to the generator function via `processArgument` and `otherProcessArgument`
* @param processArgument The argument to be passed to the process.
* @param at The schedule time. If omitted, no `delay` is used.
* @param delay The delay before starting the process. It uses a `Duration` object to specify the delay amount. The default value is `Duration.ZERO`.
* @param priority The priority level of the activation. It uses the `Priority` enumeration with options HIGH, NORMAL, and LOW. The default value is NORMAL.
* @param urgent Indicates whether the activation is urgent or not. If set to true, the activation will be treated as urgent. The default value is false.
* @param keepRequest Indicates whether to keep the activation request even after the process is started. If set to true, the activation request will be kept. The default value is false.
* @param keepWait Indicates whether to keep waiting for the process to complete before returning. If set to true, the activation will not return until the process is complete. The default value is false.
*
* @return The activated component.
*/
fun activate(
at: SimTime? = null,
delay: Duration = Duration.ZERO,
Expand All @@ -1277,15 +1352,24 @@ open class Component(
}

/**
* Activate component
* Activate a component.
*
* For `activate` contract see [user manual](https://www.kalasim.org/component/#activate)
* This method is used to activate a component. The activation process starts a specified process with the given arguments.
*
* @param at Schedule time
* @param delay Schedule with a delay if omitted, no delay is used
* @param process Name of process to be started.
* * if None (default), process will not be changed
* * if the component is a data component, the generator function `process` will be used as the default process.
* Please refer to the [user manual](https://www.kalasim.org/component/#activate) for more information on the `activate` contract.
*
* @param process The name of the process to be started. If set to `None`, the process will not be changed. If the
* component is a data component, the generator function `process` will be used as the default process. Optionally type safe
* arguments can be provided to the generator function via `processArgument` and `otherProcessArgument`
* @param processArgument The argument to be passed to the process.
* @param at The schedule time. If omitted, no `delay` is used.
* @param delay The delay before starting the process. It uses a `Duration` object to specify the delay amount. The default value is `Duration.ZERO`.
* @param priority The priority level of the activation. It uses the `Priority` enumeration with options HIGH, NORMAL, and LOW. The default value is NORMAL.
* @param urgent Indicates whether the activation is urgent or not. If set to true, the activation will be treated as urgent. The default value is false.
* @param keepRequest Indicates whether to keep the activation request even after the process is started. If set to true, the activation request will be kept. The default value is false.
* @param keepWait Indicates whether to keep waiting for the process to complete before returning. If set to true, the activation will not return until the process is complete. The default value is false.
*
* @return The activated component.
*/
fun <T : Any> activate(
process: ProcessPointerWithArgs<*, T>,
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/org/kalasim/Environment.kt
Expand Up @@ -440,7 +440,7 @@ open class Environment(


/** Remove a component from the event-queue. Also, remove it from standing-by list, if currently on stand-by.*/
fun remove(c: Component) {
internal fun remove(c: Component) {
unschedule(c)

// TODO what is happening here, can we simplify that?
Expand Down

0 comments on commit 4932953

Please sign in to comment.