avr-oxide 0.0.5

An extremely simple Rusty operating system for AVR microcontrollers
/* event.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Oxide system events

// Imports ===================================================================
use crate::private::ringq::{RingQ, Coalesce, QueueError};
use crate::devices::button::ButtonState;
use crate::devices::serialport::SerialState;

// Declarations ==============================================================
pub type OxideEventQueue = RingQ<OxideEvent,16>;

/**
 * Events that can cause your application to wake up and need to do something.
 */
#[derive(Clone)]
pub enum OxideEvent {
  /**
   * Sent when the master clock timer tick interrupt occurs.  The number
   * of elapsed clock ticks is included.
   */
  MasterClockEvent(u16),

  /**
   * Sent when a button event occurs.  The state of the button when the
   * event was generated is included.
   */
  ButtonEvent(ButtonState),

  /**
   * Sent when a serial port event occurs.
   */
  SerialEvent(SerialState)
}

/**
 * An event sink is something that devices can call to dump their events into.
 */
pub trait EventSink {
  fn event(event: OxideEvent) -> ();
}

/**
 * A 'null' event sunk that allows you to instantiate Oxide devices that
 * discard any generated events.
 */
pub struct EventDevNull {
}

// Code ======================================================================
impl Coalesce for OxideEvent {
  fn coalesce(&mut self, with: &Self) -> Result<(), QueueError> {
    match self {
      // MasterClockEvents we coalesce by adding the ticks together
      OxideEvent::MasterClockEvent(myticks) => {
        match with {
          OxideEvent::MasterClockEvent(ticks) => {
            if (u16::MAX - *myticks) > *ticks {
              *myticks += ticks;
              Ok(())
            } else {
              Err(QueueError::CannotCoalesce)
            }
          },
          _ => Err(QueueError::CannotCoalesce)
        }
      },
      // Serial events we coalesce by just discarding duplicates
      OxideEvent::SerialEvent(state) => {
        match with {
          OxideEvent::SerialEvent(with_state) => {
            if state == with_state {
              Ok(())
            } else {
              Err(QueueError::CannotCoalesce)
            }
          },
          _ => Err(QueueError::CannotCoalesce)
        }
      },
      _ => Err(QueueError::CannotCoalesce)
    }
  }
}

/**
 * Do-nothing implementation of EventSink.
 */
impl EventSink for EventDevNull {
  fn event(_event: OxideEvent) -> () {
  }
}

// Tests =====================================================================
#[cfg(test)]
mod tests {
  use crate::event::OxideEvent;
  use crate::private::ringq::Coalesce;
  use crate::event::OxideEvent::MasterClockEvent;
  use crate::devices::serialport::SerialState;
  use crate::devices::button::ButtonState;

  #[test]
  fn test_event_coalesce_masterclockevent() {
    let mut event1 = OxideEvent::MasterClockEvent(12);
    let event2 = OxideEvent::MasterClockEvent(14);

    event1.coalesce(&event2).unwrap();

    if let MasterClockEvent(ticks) = event1 {
      println!("Coalesced MasterClockEvent (ticks == {})", ticks);
      assert_eq!(ticks, 26);
    }
  }

  #[test]
  fn test_event_coalesce_serialevent_ok() {
    let mut event1 = OxideEvent::SerialEvent(SerialState::ReadAvailable);
    let event2 = OxideEvent::SerialEvent(SerialState::ReadAvailable);

    event1.coalesce(&event2).unwrap();

    let mut event1 = OxideEvent::SerialEvent(SerialState::BreakDetected);
    let event2 = OxideEvent::SerialEvent(SerialState::BreakDetected);

    event1.coalesce(&event2).unwrap();
  }

  #[test]
  #[should_panic]
  fn test_event_coalesce_serialevent_fail() {
    let mut event1 = OxideEvent::SerialEvent(SerialState::ReadAvailable);
    let event2 = OxideEvent::SerialEvent(SerialState::BreakDetected);

    event1.coalesce(&event2).unwrap();
  }


  #[test]
  #[should_panic]
  fn test_event_coalesce_fail() {
    let mut event1 = OxideEvent::MasterClockEvent(42);
    let event2 = OxideEvent::ButtonEvent(ButtonState::Pressed);

    event1.coalesce(&event2).unwrap();
  }
}