avr-oxide 0.0.6

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;
use crate::hal::generic::timer::TimerIdentity;
use crate::hal::generic::port::PinIdentity;
use crate::hal::generic::serial::SerialPortIdentity;

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

/**
 * Events that can cause your application to wake up and need to do something.
 */
#[derive(Clone,Copy)]
pub enum OxideEvent {
  /**
   * Sent when the system starts up to allow the application to process any
   * initialisation tasks it needs.  This is guaranteed to be the first
   * event received by the application program.
   */
  Initialise,

  /**
   * Sent when the master clock timer tick interrupt occurs.  The number
   * of elapsed clock ticks is included.
   */
  MasterClockEvent(TimerIdentity, u16),

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

  /**
   * Sent when a serial port event occurs.
   */
  SerialEvent(SerialPortIdentity, 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(source1, myticks) => {
        match with {
          OxideEvent::MasterClockEvent(source2, ticks) => {
            if (source1 == source2) && (u16::MAX - *myticks) > *ticks {
              *myticks += ticks;
              Ok(())
            } else {
              Err(QueueError::CannotCoalesce)
            }
          },
          _ => Err(QueueError::CannotCoalesce)
        }
      },
      // Serial events we coalesce by just discarding duplicates
      OxideEvent::SerialEvent(source1, state) => {
        match with {
          OxideEvent::SerialEvent(source2, with_state) => {
            if (source1 == source2) && (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;
  use crate::hal::generic::timer::TimerIdentity;
  use crate::hal::generic::serial::SerialPortIdentity;
  use crate::hal::generic::port::PinIdentity;

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

    event1.coalesce(&event2);

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

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

    event1.coalesce(&event2);

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

    event1.coalesce(&event2);
  }

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

    if let Ok(_) = event1.coalesce(&event2) {
    } else {
      panic!();
    }
  }


  #[test]
  #[should_panic]
  fn test_event_coalesce_fail() {
    let mut event1 = OxideEvent::MasterClockEvent(TimerIdentity::Tcb0, 42);
    let event2 = OxideEvent::ButtonEvent(PinIdentity::PortA(0),ButtonState::Pressed);

    if let Ok(_) = event1.coalesce(&event2) {
    } else {
      panic!();
    }
  }
}