Skip to content

Fault Injector

Anton edited this page Jul 20, 2023 · 47 revisions

FaultInjection

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.

Architecture of the Component for fault injection

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
  }
Loading

FaultInjector

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.

Fault

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 a Fault is currently injected.
  • configuration: FaultConfiguration. The configuration, which provides configuration data for the Fault like the affected simulation object.
  • event_bus: EventBus. The EventBus, 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 the wrapper wiki page.
  • interlocking_disruptor: IInterlockingDisruptor. Interface used by the RouteController 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 a Fault 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.

FaultStrategy

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.

FaultConfiguration

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 the Fault should be injected. Only used when using a RegularFaultStrategy.
  • end_time. The simulated second in which the Fault should be resolved. Only used when using a RegularFaultStrategy.
  • inject_probability. Probability with which a Fault gets injected in a simulation tick. Only used when using a RandomFaultStrategy.
  • resolve_probability. Probability with which a Fault gets resolved in a simulation tick. Only used when using a RandomFaultStrategy.
  • description. Optional description to provide information about the specifics of a Fault.
  • 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 the Fault.

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.

Fault Chains

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)