-
Notifications
You must be signed in to change notification settings - Fork 1
Fault Injector
FaultInjection is used to interrupt or change the normal behaviour of a simulation. At the moment, the fault incetion allows the following cases:
- Block a platform in order to prevent trains from using it
- Block schedules to cancel train departures
- Block tracks so they can not be used
- Change the speed limit of a track
- Change the priority of a train
- Change the maximum speed of a train in order to slow it down
The cases written in italic are currently not fully functionally, mainly because they affect parts of the simulation, which are currently not used by the RouteController
.
Inject faults into the Spawner or Interlocking to test the robustness of the simulation.
classDiagram
Fault <-- FaultInjector
Fault <|-- PlatformBlockedFault
Fault <|-- TrackBlockedFault
Fault <|-- TrackSpeedLimitFault
Fault <|-- ScheduleBlockedFault
Fault <|-- TrainPrioFault
Fault <|-- TrainSpeedFault
Fault <-- FaultConfiguration
PlatformBlockedFault <-- PlatformBlockedFaultConfiguration
TrackBlockedFault <-- TrackBlockedFaultConfiguration
TrackSpeedLimitFault <-- TrackSpeedLimitFaultConfiguration
ScheduleBlockedFault <-- ScheduleBlockedFaultConfiguration
TrainPrioFault <-- TrainPrioFaultConfiguration
TrainSpeedFault <-- TrainSpeedFaultConfiguration
FaultConfiguration <|-- PlatformBlockedFaultConfiguration
FaultConfiguration <|-- TrackBlockedFaultConfiguration
FaultConfiguration <|-- TrackSpeedLimitFaultConfiguration
FaultConfiguration <|-- ScheduleBlockedFaultConfiguration
FaultConfiguration <|-- TrainPrioFaultConfiguration
FaultConfiguration <|-- TrainSpeedFaultConfiguration
FaultStrategy <|-- RegularFaultStrategy
FaultStrategy <|-- RandomFaultStrategy
FaultStrategy <-- Fault
class FaultInjector {
+faults: list[Fault]
+add_fault(Fault)
+next_tick(tick)
}
class FaultStrategy {
+should_inject(tick, configuration, injected)
+should_resolve(tick, configuration, injected)
}
class RegularFaultStrategy {
}
class RandomFaultStrategy {
}
class Fault {
+injected: bool
+configuration: FaultConfiguration
+logger: Logger
+simulation_object_updater: SimulationObjectUpdatingComponent
+interlocking: IInterlockingDisruptor
+strategy: FaultStrategy
+inject_fault(component)
+resolve_fault(component)
+next_tick(tick)
}
class PlatformBlockedFault {
+platform: Platform
+configuration: PlatformBlockedFaultConfiguration
}
class TrackBlockedFault {
+configuration: TrackBlockedFaultConfiguration
+track: Track
}
class TrackSpeedLimitFault {
+configuration: TrackSpeedLimitFaultConfiguration
+old_speed_limit: float
track: Track
}
class ScheduleBlockedFault {
+configuration: ScheduleBlockedFaultConfiguration
+spawner: Spawner
}
class TrainPrioFault {
+configuration: TrainPrioFaultConfiguration
+old_prio: int
+train: Train
}
class TrainSpeedFault {
+configuration: TrainSpeedFaultConfiguration
+old_speed: float
+train: Train
}
class FaultConfiguration {
+start_tick: IntegerField
+end_tick: IntegerField
+inject_probability = FloatField
+resolve_probability = FloatField
+strategy = TextField
+description: TextField
}
class PlatformBlockedFaultConfiguration {
+affected_elemnt_id: TextField
}
class TrackBlockedFaultConfiguration {
+affected_elemnt_id: TextField
}
class TrackSpeedLimitFaultConfiguration {
+affected_elemnt_id: TextField
+new_speed_limit: IntegerField
}
class ScheduleBlockedFaultConfiguration {
+affected_elemnt_id: TextField
}
class TrainPrioFaultConfiguration {
+affected_elemnt_id: TextField
+new_prio: IntegerField
}
class TrainSpeedFaultConfiguration {
+affected_elemnt_id: TextField
+new_speed = FloatField
}
The class FaultInjector
holds all Fault
objects in the simulation. Its function next_tick
gets called for every simulation tick and passes the tick to all Fault
objects. It also offers the method add_fault
to register new Fault
objects. This happens in the initialization for configured faults.
The Fault
classes provide the functionality to manipulate elements of the simulation, which should be affected by the fault injection. This is enabled by the following classes:
PlatformBlockedFault
ScheduleBlockedFault
TrackBlockedFault
TrackSpeedLimitFault
TrainPrioFault
TrainSpeedFault
All of these classes inherit from an abstract class Fault
, which provides attributes and methods, all speicific Fault
classes should have. This includes the methods inject_fault
, resolve_fault
and next_tick
. While the first two of them are abstract, the latter provides functionality to inject or resolve the Fault
. The decision for that depends on the used strategy, which will be explained later on this page.
Additinoally, the class provides the following attributes:
-
injected: bool
. Indicates, if aFault
is currently injected. -
configuration: FaultConfiguration
. The configuration, which provides configuration data for theFault
like the affected simulation object. -
event_bus: EventBus
. TheEventBus
, which enables to log and distribute events in the software system. For more info see the according wiki page. -
simulation_object_updater: SimulationObjectUpdatingComponent
. The component which holds the simulaton elements, see thewrapper
wiki page. -
interlocking_disruptor: IInterlockingDisruptor
. Interface used by theRouteController
component. In the case of injecting or resolving a fault this component may need a notification. -
strategy: FaultStrategy
. The strategy, which helps with the decision wether aFault
should be injected in the current simulation tick.
The ScheduleBlockedFault
has the additional attribute spawner
, which is the currently used Spawner
object, in order to block Schedules
from this class.
The Fault
classes use strategies to determine, if the Fault
should be injected or resolved in the current simulation tick. This enables planned and unplanned faults. The specific strategies RegularFaultStrategy
and RandomFaultStrategy
implement the methods should_inject
and should_resolve
of the abstract class FaultStrategy
both inherit from. The methods return a boolean which indicates the decision made on basis of probability or comparison of configured and current ticks.
Each Fault
class has a corresponding FaultConfiguration
class. The FaultConfiguration
classes are stored in a database with ORM and provide information needed by the Fault
objects. This includes the following attributes:
-
start_time
. The simulated second in which theFault
should be injected. Only used when using aRegularFaultStrategy
. -
end_time
. The simulated second in which theFault
should be resolved. Only used when using aRegularFaultStrategy
. -
inject_probability
. Probability with which aFault
gets injected in a simulation tick. Only used when using aRandomFaultStrategy
. -
resolve_probability
. Probability with which aFault
gets resolved in a simulation tick. Only used when using aRandomFaultStrategy
. -
description
. Optional description to provide information about the specifics of aFault
. -
strategy
. String"regular"
or"random"
that indicates the type of strategy that should be used. -
affected_element_id
. The id of the simulation object, that should be affected by theFault
.
Some FaultConfigurations
may include a field to set a value to a new one. This is for example the case for the TrainSpeedFaultConfiguration
which includes the attribute new_speed
.
Examples for the configuration can be found in scripts/insert_fault_injection_examples.py.
The functionality for fault chains is not fully developed yet. The EventBus
can distribute events within the software system, but the FaultInjection
component is lacking the implementation for reacting on these events.
⚠ The following is just a proof of concept ⚠
A very hardcoded example for a fault chain can be seen below. A serious implementation could use an additional FaultStrategy
, which could allow dependencies between Faults
class FaultInjector(Component):
"""
Class for fault injection. Faults can be injected into the
InterlockingController, the Spawner and the Wrapper
"""
_faults: list[Fault] = []
_callback_called: bool = True
def __init__(self, event_bus: EventBus, priority: str):
super().__init__(event_bus, "HIGH")
def add_fault(self, fault: Fault):
"""Adds faults that should be injected to the fault injector
:param json_faults: The list of faults as JSON objects
:type json_faults: list[str]
"""
self._faults.append(fault)
def inject_dependent_fault(self, event):
for fault in self._faults:
if fault.configuration.description == "test ScheduleBlockedFault" and not self._callback_called:
self._callback_called = True
fault.inject_fault(520)
def next_tick(self, tick: int):
"""Called by the wrapper to announce the next tick of the simulation
:param tick: The current simulation tick
:type tick: int
"""
for fault in self._faults:
if fault.configuration.description == "test ScheduleBlockedFault":
self.event_bus.register_callback(self.inject_dependent_fault, EventType.INJECT_FAULT)
for fault in self._faults:
fault.next_tick(tick)