tudelft-xray-sim 1.0.0

simulation library for the modeling assignment in the course 'Software Systems' at the TU Delft.
Documentation
use crate::*;

/// A mapping from pedal actions to requests.
///
/// Each pedal is either pressed or not.
/// When a pedal is pressed, [`PedalMapper::on_press`] is called with the just-pressed pedal.
/// When the pedal is released, [`PedalMapper::on_release`] is called with the just-released pedal.
///
/// Pedals are variants of either [`ThreePedals`] or [`SixPedals`] depending on
/// whether the system has one or two planes.
pub trait PedalMapper {
    /// The type of pedals passed to `on_press` and `on_release`.
    /// For a single-plane system this should be [`ThreePedals`]
    /// and for a two-place system this should be [`SixPedals`].
    type Pedals;

    /// Called when a pedal is **pressed**. Returns an optional request.
    /// This is because sometimes no request needs to be sent,
    /// in which case the [`None`] variant should be returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # use tudelft_xray_sim::{PedalMapper, Request, Projection, Dose, Mode, ThreePedals};
    /// struct MyMapping;
    ///
    /// impl PedalMapper for MyMapping {
    ///     type Pedals = ThreePedals;
    ///
    ///     fn on_press(&self, pedal: Self::Pedals) -> Option<Request> {
    ///         use ThreePedals::*;
    ///         match pedal {
    ///             // Start low dose video.
    ///             Pedal1 => Some(Request::start(Projection::Frontal, Dose::Low, Mode::Video)),
    ///             // Stop low dose video.
    ///             Pedal2 => Some(Request::stop(Projection::Frontal, Dose::Low, Mode::Video)),
    ///             // Nothing needs to be dose when the pedal with index 2 is pressed, so we return None.
    ///             Pedal3 => None,
    ///             // This is a single-plane system, so other pedal indices are not possible.
    ///             _ => unreachable!()
    ///         }        
    ///     }
    ///
    ///     fn on_release(&self, pedal: Self::Pedals) -> Option<Request> { None }
    /// }
    /// ```
    fn on_press(&self, pedal: Self::Pedals) -> Option<Request>;

    /// Called when a pedal is **released**. Returns an optional request.
    /// This is because sometimes no request needs to be sent,
    /// in which case the [`None`] variant should be returned.
    ///
    /// Pedals always begin unpressed, so an `on_release` for a specific pedal
    /// can only be called if `on_press` for that pedal was called before.
    ///
    /// # Examples
    ///
    /// ```
    /// # use tudelft_xray_sim::{PedalMapper, Request, Projection, Dose, Mode, SixPedals};
    /// struct MyMapping;
    ///
    /// impl PedalMapper for MyMapping {
    ///     type Pedals = SixPedals;
    ///
    ///     // Start low dose video on press of Pedal1.
    ///     fn on_press(&self, pedal: Self::Pedals) -> Option<Request> {
    ///         if pedal == SixPedals::Pedal1 {
    ///             Some(Request::start(Projection::Frontal, Dose::Low, Mode::Video))
    ///         } else {
    ///             None
    ///         }
    ///     }
    ///
    ///     // Stop low dose video on release of Pedal1.
    ///     fn on_release(&self, pedal: Self::Pedals) -> Option<Request> {
    ///         if pedal == SixPedals::Pedal1 {
    ///             Some(Request::stop(Projection::Frontal, Dose::Low, Mode::Video))
    ///         } else {
    ///             None
    ///         }
    ///     }
    /// }
    /// ```
    fn on_release(&self, pedal: Self::Pedals) -> Option<Request>;
}

/// Resolve requests into X-ray controller actions.
///
/// For example you might get a request to start a low dose video with the frontal projection,
/// but because there are other pedals pressed you might choose to ignore it or do something else.
/// This is where that decision logic goes.
///
/// The [`ActionLogic::handle_request`] function also gets a mutable reference to `self` so it is possible
/// to modify and check the state of your system. You could, for example, keep track of the
/// currently selected projection, and then modify it when you get a [`Request::ToggleSelectedProjection`].
///
/// Requests are synchronous and handled in the same order that they are sent.
pub trait ActionLogic<const IS_TWO_PLANE: bool> {
    /// Handle a request.
    ///
    /// Use `controller`, which is a mutable reference to [`Controller`] to
    /// activate or deactivate X-rays.
    ///
    /// You can read and modify information in `self` to store the state of your system.
    ///
    /// # Examples
    ///
    /// ```
    /// # use tudelft_xray_sim::{ActionLogic, Request, Controller, Projection, Dose, Mode};
    /// struct Foo {
    ///     requests_handled: u64,
    ///     selected: Projection,
    /// }
    ///
    /// impl ActionLogic<true> for Foo {
    ///     fn handle_request(&mut self, request: Request, controller: &mut Controller<true>) {
    ///         match request {
    ///             Request::ToggleSelectedProjection => {
    ///                 self.selected = match self.selected {
    ///                     Projection::Frontal => Projection::Lateral,
    ///                     Projection::Lateral => Projection::Biplane,
    ///                     Projection::Biplane => Projection::Frontal,
    ///                 }
    ///             },
    ///             Request::StartSelectedProjection { dose, mode } => controller.activate_xray(self.selected, dose, mode),
    ///             Request::StopSelectedProjection { .. } => controller.deactivate_xray(),
    ///             _ => {} // Ignore other types of requests.
    ///         }
    ///
    ///         self.requests_handled += 1;
    ///     }
    /// }
    /// ```
    fn handle_request(&mut self, request: Request, controller: &mut Controller<IS_TWO_PLANE>);
}