Skip to content

Commit

Permalink
Merge pull request #58 from PoorRican/device_functionality
Browse files Browse the repository at this point in the history
Implemented peripheral functionality to manipulate GPIO registers or execute arbitrary commands using GPIOCommand within the main event loop, and commands can be scheduled outside of the main event loop using Routine.
  • Loading branch information
PoorRican authored Mar 2, 2023
2 parents 3298bd9 + f46c09b commit c01f07c
Show file tree
Hide file tree
Showing 28 changed files with 888 additions and 250 deletions.
2 changes: 2 additions & 0 deletions src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ mod publisher;
mod types;
mod subscribers;
mod commands;
mod routine;

pub use command::*;
pub use commands::*;
pub use subscriber::*;
pub use subscribers::*;
pub use publisher::*;
pub use types::*;
pub use routine::*;
9 changes: 5 additions & 4 deletions src/action/command.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::io::IOEvent;
use crate::io::IOType;
use crate::errors::ErrorType;


pub type CommandType = Box<dyn Command>;
pub type CommandType<T> = Box<dyn Command<T>>;

/// Abstraction for single atomic output operation
pub trait Command {
fn execute(&self) -> Option<IOEvent>;
pub trait Command<T> {
fn execute(&self, value: Option<IOType>) -> Result<Option<T>, ErrorType>;
}
2 changes: 2 additions & 0 deletions src/action/commands.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod notifier;
mod gpio;

pub use notifier::*;
pub use gpio::*;
233 changes: 233 additions & 0 deletions src/action/commands/gpio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
use crate::action::{Command, IOCommand};
use crate::io::{DeferredDevice, IOType, DeviceTraits, IODirection};
use crate::errors::{Error, ErrorKind, ErrorType};

pub struct GPIOCommand {
func: IOCommand,
}

impl GPIOCommand {
pub fn new(func: IOCommand, device: Option<DeferredDevice>) -> Self {
if let Some(device) = device {
check_alignment(&func, device.clone()).unwrap();
}

Self { func }
}

pub fn direction(&self) -> IODirection {
match self.func {
IOCommand::Input(_) => IODirection::Input,
IOCommand::Output(_) => IODirection::Output,
}
}
}

impl Command<IOType> for GPIOCommand {
/// Execute internally stored function.
///
/// # Returns
/// If internal function is `IOCommand::Input`, then the value that is read from device is returned.
/// Otherwise, if `IOCommand::Output`, then `None` is returned.
fn execute(&self, value: Option<IOType>) -> Result<Option<IOType>, ErrorType> {
match self.func {
IOCommand::Input(inner) => {
// throw warning for unused value
if let Some(_) = value { unused_value() }

let read_value = inner();

Ok(Some(read_value))

},
IOCommand::Output(inner) => {
let unwrapped_value = value.expect("No value was passed to write...");
let _ = inner(unwrapped_value); // TODO: handle bad result

Ok(None)
},
}
}
}

/// Panic if command and device are not aligned
pub fn check_alignment(command: &IOCommand, device: DeferredDevice) -> Result<(), ErrorType> {
let aligned = command.direction() == device.direction();
match aligned {
true => Ok(()),
false => Err(misconfigured_error())
}
}

/// Generate an error for when command type does not match device type
pub fn misconfigured_error() -> ErrorType {
Error::new(ErrorKind::CommandError, "Misconfigured device! Device and command type do not match.")
}

/// Print a warning on console stderr
fn unused_value() {
const MSG: &str = "Unused value passed when reading input...";
eprintln!("{}", MSG);
}

#[cfg(test)]
mod tests {
use crate::action::{IOCommand, check_alignment, GPIOCommand, Command};
use crate::helpers::Deferrable;
use crate::io::{DeferredDevice, Device, DeviceType, GenericOutput, IdType, IODirection, GenericInput, IOType};
use crate::storage::OwnedLog;

const REGISTER_DEFAULT: IOType = IOType::PosInt8(255);
static mut REGISTER: IOType = REGISTER_DEFAULT;

unsafe fn reset_register() {
REGISTER = REGISTER_DEFAULT;
}

unsafe fn set_register(val: IOType) {
REGISTER = val;
}

fn make_device(direction: &IODirection) -> DeferredDevice {
let name = "";
let id = IdType::default();
let log = OwnedLog::new(id, None).deferred();

let device = match direction {
IODirection::Input => {
DeviceType::Input(GenericInput::new(String::from(name), id, None, Some(log)))
}
IODirection::Output => {
DeviceType::Output(GenericOutput::new(String::from(name), id, None, Some(log)))
}
};
device.deferred()
}

#[test]
fn test_check_alignment() {
{
let direction = IODirection::Input;
let device = make_device(&direction);

let command = IOCommand::Input(move || IOType::Float(0.0));

let result = check_alignment(&command, device);
match result {
Ok(_) => assert!(true),
Err(_) => assert!(false)
}
}
{
let direction = IODirection::Output;
let device = make_device(&direction);

let command = IOCommand::Output(move |_| Ok(()));

let result = check_alignment(&command, device);
match result {
Ok(_) => assert!(true),
Err(_) => assert!(false)
}
}


{
let direction = IODirection::Output;
let device = make_device(&direction);

let command = IOCommand::Input(move || IOType::Float(0.0));

let result = check_alignment(&command, device);
match result {
Ok(_) => assert!(false),
Err(_) => assert!(true)
}
}
{
let direction = IODirection::Input;
let device = make_device(&direction);

let command = IOCommand::Output(move |_| Ok(()));

let result = check_alignment(&command, device);
match result {
Ok(_) => assert!(false),
Err(_) => assert!(true)
}
}
}

#[test]
#[should_panic]
/// Assert that program panics when device and IOCommand are misaligned
/// This test case specifically uses an input device and an output command.
fn test_alignment_i_o() {
let direction = IODirection::Input;
let device = make_device(&direction);

let command = IOCommand::Output(move |_| Ok(()));

GPIOCommand::new(command, Some(device));
}

#[test]
#[should_panic]
/// Assert that program panics when device and IOCommand are misaligned
/// This test case specifically uses an output device and an input command.
fn test_alignment_o_i() {
let direction = IODirection::Output;
let device = make_device(&direction);

let command = IOCommand::Input(move || IOType::default());

GPIOCommand::new(command, Some(device));
}

#[test]
fn test_execute() {
{
unsafe { reset_register(); }

let func = IOCommand::Input(move || unsafe {
REGISTER
});
let command = GPIOCommand::new(func, None);

match command.execute(None) {
Ok(tentative) => unsafe {
match tentative {
Some(inner) => assert_eq!(REGISTER, inner),
None => assert!(false)
}
},
Err(_) => assert!(false)
}

}
{
unsafe { reset_register(); }

let func = IOCommand::Output(move |val| unsafe {
set_register(val);
Ok(())
});
let command = GPIOCommand::new(func, None);
let value = IOType::Binary(true);

unsafe { assert_ne!(REGISTER, value); }

match command.execute(Some(value)) {
Ok(tentative) => {
match tentative {
Some(_) => assert!(false),
None => ()
}
},
Err(_) => assert!(false)
}

unsafe { assert_eq!(REGISTER, value); }
}
}
}
13 changes: 7 additions & 6 deletions src/action/commands/notifier.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::sync::{Arc, Mutex};
use crate::action::{Command, CommandType};
use crate::errors::ErrorType;
use crate::helpers::{Deferrable, Deferred};
use crate::io::IOEvent;
use crate::io::{IOEvent, IOType};

/// Simple command for printing a message to stdout
pub struct SimpleNotifier {
Expand All @@ -12,20 +13,20 @@ impl SimpleNotifier {
pub fn new(msg: String) -> Self {
Self { msg }
}
pub fn command(msg: String) -> CommandType {
pub fn command(msg: String) -> CommandType<IOEvent> {
Box::new(Self::new(msg))
}
}

impl Command for SimpleNotifier {
fn execute(&self) -> Option<IOEvent> {
impl Command<IOEvent> for SimpleNotifier {
fn execute(&self, _value: Option<IOType>) -> Result<Option<IOEvent>, ErrorType> {
println!("{}", self.msg);
None
Ok(None)
}
}

impl Deferrable for SimpleNotifier {
type Inner = CommandType;
type Inner = CommandType<IOEvent>;
fn deferred(self) -> Deferred<Self::Inner> {
Arc::new(Mutex::new(Box::new(self)))
}
Expand Down
Loading

0 comments on commit c01f07c

Please sign in to comment.