Skip to content

Commit

Permalink
core: introduce a switch initialization policy
Browse files Browse the repository at this point in the history
This initialization policy replaces the default switch configuration:
- switches do not all have a default configuration
- we may need to make pessimistic or optimistic assumptions, depending on the context
  • Loading branch information
multun committed Nov 29, 2022
1 parent a9f8a00 commit 65200f6
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ interface MovableElementsInfra {
val movableElements: StaticIdxSpace<MovableElement>
fun getMovableElementConfigs(movableElement: MovableElementId): StaticIdxSpace<MovableElementConfig>
fun getMovableElementDelay(movableElement: MovableElementId): Duration
fun getMovableElementDefaultConfig(movableElement: MovableElementId): MovableElementConfigId
fun getMovableElementConfigName(movableElement: MovableElementId, config: MovableElementConfigId): String
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ import kotlin.time.Duration
class MovableElementDescriptorBuilder @PublishedApi internal constructor (
private val delay: Duration,
private val configs: StaticPool<MovableElementConfig, MovableElementConfigDescriptor>,
var defaultConfig: MovableElementConfigId?
) {
fun config(name: String): MovableElementConfigId {
return configs.add(MovableElementConfigDescriptor(name))
}

@PublishedApi internal fun build(): MovableElementDescriptor {
if (defaultConfig == null)
throw RuntimeException("invalid MovableElement: there must be a default config")
return MovableElementDescriptor(delay, configs, defaultConfig!!)
return MovableElementDescriptor(delay, configs)
}
}

Expand All @@ -43,7 +40,7 @@ class SimInfraBuilder @PublishedApi internal constructor(
)

inline fun movableElement(delay: Duration, init: MovableElementDescriptorBuilder.() -> Unit): MovableElementId {
val movableElementBuilder = MovableElementDescriptorBuilder(delay, StaticPool(), null)
val movableElementBuilder = MovableElementDescriptorBuilder(delay, StaticPool())
movableElementBuilder.init()
val movableElement = movableElementBuilder.build()
return movableElementPool.add(movableElement)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ value class MovableElementConfigDescriptor(val name: String)
class MovableElementDescriptor(
val delay: Duration,
val configs: StaticPool<MovableElementConfig, MovableElementConfigDescriptor>,
val defaultConfig: StaticIdx<MovableElementConfig>
)

@JvmInline
Expand Down Expand Up @@ -66,10 +65,6 @@ class SimInfraImpl(
return movableElementPool[movableElement].delay
}

override fun getMovableElementDefaultConfig(movableElement: MovableElementId): MovableElementConfigId {
return movableElementPool[movableElement].defaultConfig
}

override fun getMovableElementConfigName(
movableElement: MovableElementId,
config: MovableElementConfigId
Expand Down
8 changes: 7 additions & 1 deletion core/kt-osrd-sim/src/main/kotlin/fr/sncf/osrd/sim/api/Sim.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ import kotlin.contracts.contract

// region MOVABLE ELEMENTS

/** Defines how movable elements are initialized */
enum class MovableElementInitPolicy {
OPTIMISTIC,
PESSIMISTIC,
}

interface MovableElementSim {
fun watchMovableElement(movable: MovableElementId): StateFlow<MovableElementConfigId>
fun watchMovableElement(movable: MovableElementId): StateFlow<MovableElementConfigId?>
suspend fun move(movable: MovableElementId, config: MovableElementConfigId)
suspend fun lockMovableElement(movable: MovableElementId)
suspend fun unlockMovableElement(movable: MovableElementId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.sncf.osrd.sim.impl

import fr.sncf.osrd.sim.api.MovableElementInitPolicy
import fr.sncf.osrd.sim_infra.api.*
import fr.sncf.osrd.sim.api.MovableElementSim
import fr.sncf.osrd.utils.indexing.get
Expand All @@ -10,18 +11,21 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.sync.Mutex


fun movableElementSim(infra: MovableElementsInfra): MovableElementSim {
return MovableElementSimImpl(infra)
fun movableElementSim(infra: MovableElementsInfra, initPolicy: MovableElementInitPolicy): MovableElementSim {
return MovableElementSimImpl(infra, initPolicy)
}

internal class MovableElementSimImpl(private val infra: MovableElementsInfra) : MovableElementSim {
private val states = infra.movableElements.map { id ->
MutableStateFlow(infra.getMovableElementDefaultConfig(id))
internal class MovableElementSimImpl(
private val infra: MovableElementsInfra,
private val initPolicy: MovableElementInitPolicy,
) : MovableElementSim {
private val states: List<MutableStateFlow<MovableElementConfigId?>> = infra.movableElements.map { id ->
MutableStateFlow(null)
}

private val locks = infra.movableElements.map { Mutex() }

override fun watchMovableElement(movable: MovableElementId): StateFlow<MovableElementConfigId> {
override fun watchMovableElement(movable: MovableElementId): StateFlow<MovableElementConfigId?> {
return states[movable.index]
}

Expand All @@ -32,7 +36,10 @@ internal class MovableElementSimImpl(private val infra: MovableElementsInfra) :
override suspend fun move(movable: MovableElementId, config: MovableElementConfigId) {
assert(locks[movable.index].isLocked) { "cannot move a non-locked movable element" }
states[movable.index].update { prevConfig ->
if (prevConfig != config)
if (prevConfig == null) {
if (initPolicy == MovableElementInitPolicy.PESSIMISTIC)
delay(infra.getMovableElementDelay(movable))
} else if (prevConfig != config)
delay(infra.getMovableElementDelay(movable))
config
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ class TestLocation {
// setup test data
val infra = simInfra {
// create a test switch
val switchA = movableElement(delay = 42L.milliseconds) {
defaultConfig = config("a")
val switchA = movableElement(delay = 42L.milliseconds, MovableElementInitPolicy.OPTIMISTIC) {
config("a")
config("b")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fr.sncf.osrd.sim

import fr.sncf.osrd.sim.impl.MovableElementSimImpl
import fr.sncf.osrd.sim.api.withLock
import fr.sncf.osrd.sim_infra.api.MovableElementInitPolicy
import fr.sncf.osrd.sim_infra.impl.simInfra
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
Expand All @@ -16,8 +17,8 @@ class TestMovableElements {
fun lockMoveTest() = runTest {
// setup test data
val infra = simInfra {
movableElement(delay = 42L.milliseconds) {
defaultConfig = config("a")
movableElement(delay = 42L.milliseconds, MovableElementInitPolicy.OPTIMISTIC) {
config("a")
config("b")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fr.sncf.osrd.sim.api.ZoneReservation
import fr.sncf.osrd.sim.api.ZoneReservationStatus.*
import fr.sncf.osrd.sim.api.ZoneState
import fr.sncf.osrd.sim.impl.*
import fr.sncf.osrd.sim_infra.api.MovableElementInitPolicy
import fr.sncf.osrd.sim_infra.api.ZonePath
import fr.sncf.osrd.sim_infra.api.normal
import fr.sncf.osrd.sim_infra.api.reverse
Expand Down Expand Up @@ -38,8 +39,8 @@ class TestReservation {

// region build the test infrastructure
val builder = SimInfraBuilder()
val switch = builder.movableElement(delay = 42L.milliseconds) {
defaultConfig = config("a")
val switch = builder.movableElement(delay = 42L.milliseconds, MovableElementInitPolicy.OPTIMISTIC) {
config("a")
config("b")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fr.sncf.osrd.sim

import fr.sncf.osrd.sim.api.Train
import fr.sncf.osrd.sim.impl.*
import fr.sncf.osrd.sim_infra.api.MovableElementInitPolicy
import fr.sncf.osrd.sim_infra.api.ZonePath
import fr.sncf.osrd.sim_infra.api.normal
import fr.sncf.osrd.sim_infra.api.reverse
Expand Down Expand Up @@ -38,8 +39,8 @@ class TestRouting {
// region build the test infrastructure
val builder = SimInfraBuilder()
// region switches
val switch = builder.movableElement(delay = 10L.milliseconds) {
defaultConfig = config("xy")
val switch = builder.movableElement(delay = 10L.milliseconds, MovableElementInitPolicy.OPTIMISTIC) {
config("xy")
config("vy")
}

Expand Down

0 comments on commit 65200f6

Please sign in to comment.