Skip to content

Commit

Permalink
Merge pull request #2 from PoorRican/polling
Browse files Browse the repository at this point in the history
Implemented polling
  • Loading branch information
PoorRican authored Jan 28, 2023
2 parents 6159381 + a21429d commit 5259745
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 73 deletions.
17 changes: 8 additions & 9 deletions src/container.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::hash_map::Iter;
/// Data structures and interfaces to store data
///
/// The main workhorses that provide functionality are `Containerized` and `Container`. The `Containerized`
Expand All @@ -18,11 +19,9 @@
/// store a collection of objects of a specific type `T`, and identified by a specific key type `K`. The relationship
/// between `Containerized` and `Container` is that `Containerized` defines how the `Container` should be created
/// and used for a specific type, while `Container` actually holds the collection of objects.
use std::collections::HashMap;
use std::hash::Hash;


/// A trait for creating a specialized `Container` instance
///
/// # Notes
Expand Down Expand Up @@ -73,15 +72,15 @@ use std::hash::Hash;
/// ```
pub trait Containerized<T, K>
where K: Eq + Hash
where
K: Eq + Hash,
{
// TODO: add type
/// Returns a new instance of the `Container` struct for storing objects of type T
/// which can be accessed by key-values of type K.
fn container() -> Container<T, K>;
}


/// Define a basic interface to interact with underlying data.
/// T is the data type being stored and K is the key type to access stored data.
pub trait Collection<T, K> {
Expand Down Expand Up @@ -109,10 +108,11 @@ pub trait Collection<T, K> {
/// The key only needs to be hashable.
#[derive(Debug)]
pub struct Container<T, K>
where K: Eq + Hash
where
K: Eq + Hash,
{
// The inner field is a HashMap with key type K and value type T
inner: HashMap<K, T>
inner: HashMap<K, T>,
}

impl<T, K: Eq + Hash> Container<T, K> {
Expand All @@ -123,14 +123,13 @@ impl<T, K: Eq + Hash> Container<T, K> {
}

/// Return a readonly reference to stored HashMap
pub fn _inner(&self) -> &HashMap<K, T> {
&self.inner
pub fn iter(&self) -> Iter<'_, K, T> {
self.inner.iter()
}
}

/// Implement the `Collection` interface for `Container`
impl<T, K: Hash + Eq> Collection<T, K> for Container<T, K> {

/// Add a key-value pair to the collection and return a boolean indicating if the value has been added to the collection.
/// Using `entry` method on the inner HashMap to check if the key already exists in the HashMap
/// - If the key already exists, the returned value is `std::collections::hash_map::Entry::Occupied`, which returns false.
Expand Down
58 changes: 38 additions & 20 deletions src/device.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
/// Provide Low-level Device Functionality
use chrono::{Duration, Utc};
use chrono::{DateTime, Duration, Utc};
use std::fmt::Formatter;

use crate::io;
use crate::container::{Container, Containerized};

use crate::io;

/// Basic interface for GPIO device metadata
pub trait Device<T> {
Expand All @@ -14,7 +12,6 @@ pub trait Device<T> {
fn id(&self) -> i32;
}


/// Interface for an input device
/// It is used as a trait object and can be stored in a container using the `Containerized` trait.
///
Expand Down Expand Up @@ -46,20 +43,23 @@ pub trait Device<T> {
pub trait Sensor<T>: Device<T> {
fn read(&self) -> T;

fn get_event(&self) -> io::IOEvent<T> where Self: Sized {
io::IOEvent::create(self,
Utc::now(),
self.read())
fn get_event(&self, dt: DateTime<Utc>) -> io::IOEvent<T> {
io::IOEvent::create(self, dt, self.read())
}
}

impl<T> std::fmt::Debug for dyn Sensor<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Sensor {{ name: {}, id: {}, info: {}", self.name(), self.id(), self.get_metadata())
write!(
f,
"Sensor {{ name: {}, id: {}, info: {}",
self.name(),
self.id(),
self.get_metadata()
)
}
}


/// Defines an interface for an input device that needs to be calibrated
pub trait Calibrated {
/// Initiate the calibration procedures for a specific device instance.
Expand Down Expand Up @@ -97,7 +97,7 @@ pub struct DeviceMetadata<T> {
max_value: T,
resolution: T,

min_delay: Duration,
pub min_delay: Duration,
}

impl<T> DeviceMetadata<T> {
Expand All @@ -117,28 +117,46 @@ impl<T> DeviceMetadata<T> {
/// # Returns
///
/// A new instance with given specified parameters
pub fn new(name: String, version_id: i32, sensor_id: i32, kind: io::IOKind,
min_value: T, max_value: T, resolution: T, min_delay: Duration) -> Self {
pub fn new(
name: String,
version_id: i32,
sensor_id: i32,
kind: io::IOKind,
min_value: T,
max_value: T,
resolution: T,
min_delay: Duration,
) -> Self {
DeviceMetadata {
name, version_id, sensor_id, kind,
min_value, max_value, resolution,
name,
version_id,
sensor_id,
kind,
min_value,
max_value,
resolution,
min_delay,
}
}
}

impl<T> std::fmt::Display for DeviceMetadata<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Device Info {{ Kind: {}, Min. Delay: {} }}", self.kind, self.min_delay.to_string())
write!(
f,
"Device Info {{ Kind: {}, Min. Delay: {} }}",
self.kind,
self.min_delay.to_string()
)
}
}


/// Returns a new instance of `Container` for storing objects which implement the `Sensor` trait which are accessed ``
/// Objects are stored as `Box<dyn Sensor<T>>`
impl<T, K> Containerized<Box<dyn Sensor<T>>, K> for dyn Sensor<T>
where T: std::fmt::Debug,
K: std::hash::Hash + Eq
where
T: std::fmt::Debug,
K: std::hash::Hash + Eq,
{
fn container() -> Container<Box<dyn Sensor<T>>, K> {
Container::<Box<dyn Sensor<T>>, K>::new()
Expand Down
22 changes: 14 additions & 8 deletions src/io.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/// Encapsulate IO for devices
use chrono::{DateTime, Utc};
use std::fmt::Formatter;

use chrono::{Utc, DateTime};
use crate::device;
use crate::container::{Container, Containerized};
use crate::device;

/// Defines sensor type. Used to classify data along with `IOData`.
#[derive(Debug, Clone, Copy, PartialEq)]
Expand Down Expand Up @@ -51,12 +51,14 @@ impl std::fmt::Display for IOKind {
// TODO: enum for `IODirection` when implementing control system

/// Encapsulates sensor data. Provides a unified data type for returning data.
#[derive(Debug)]
pub struct IOData<T> {
pub kind: IOKind,
pub data: T
pub data: T,
}

/// Encapsulates `IOData` alongside of timestamp and device data
#[derive(Debug)]
pub struct IOEvent<T> {
pub version_id: i32,
pub sensor_id: i32,
Expand All @@ -81,27 +83,31 @@ impl<T> IOEvent<T> {
/// ```
///
/// ```
pub fn create( device: &impl device::Device<T>, timestamp: DateTime<Utc>, value: T ) -> Self {
pub fn create(
device: &(impl device::Device<T> + ?Sized),
timestamp: DateTime<Utc>,
value: T,
) -> Self {
let info = device.get_metadata();
let version_id = info.version_id;
let sensor_id = info.sensor_id;
let data = IOData {
kind: info.kind.clone(),
data: value
data: value,
};
IOEvent {
version_id,
sensor_id,
timestamp,
data
data,
}
}
}


/// Return a new instance of `Container` with for storing `IOEvent<T>` which are accessed by `DateTime<Utc>` as keys
impl<T> Containerized<IOEvent<T>, DateTime<Utc>> for IOEvent<T>
where T: std::fmt::Debug
where
T: std::fmt::Debug,
{
fn container() -> Container<IOEvent<T>, DateTime<Utc>> {
Container::<IOEvent<T>, DateTime<Utc>>::new()
Expand Down
9 changes: 5 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
extern crate chrono;

pub mod sensors;
pub mod units;
pub mod io;
pub mod container;
pub mod device;
pub mod io;
pub mod polling;
pub mod sensors;
pub mod settings;
pub mod container;
pub mod units;
41 changes: 22 additions & 19 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,37 @@ extern crate chrono;
mod container;
mod device;
mod io;
mod settings;
mod polling;
mod sensors;
mod settings;
mod units;

use chrono::Duration;
use chrono::{DateTime, Duration, Utc};

use crate::container::{Collection, Container, Containerized};
use crate::device::Sensor;
use crate::polling::Poller;
use crate::sensors::ph::MockPhSensor;
use crate::settings::Settings;
use crate::units::Ph;


fn main() {
let _settings = Settings::initialize();

let s0 = MockPhSensor::new(
"test name".to_string(),
0,
Duration::seconds(5),
);
let s1 = MockPhSensor::new(
"second sensor".to_string(),
1,
Duration::seconds(10),
);
let mut container: Container<Box<dyn Sensor<Ph>>, i32> = <dyn Sensor::<Ph>>::container();
container.add(0, Box::new(s0));
container.add(1, Box::new(s1));
dbg!(container._inner());
/// # Load Settings
let settings: Settings = Settings::initialize();

/// # Setup Poller
let mut poller: Poller<Ph, i32> = Poller::new(settings.interval, Utc::now() - settings.interval);

let s0 = MockPhSensor::new("test name".to_string(), 0, Duration::seconds(5));
let s1 = MockPhSensor::new("second sensor".to_string(), 1, Duration::seconds(10));

poller.sensors.add(0, Box::new(s0));
poller.sensors.add(1, Box::new(s1));

loop {
poller.poll();
std::thread::sleep(std::time::Duration::from_secs(1));
dbg!(&poller.log)
}

}
49 changes: 49 additions & 0 deletions src/polling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use chrono::{DateTime, Duration, Utc};
use std::hash::Hash;

use crate::container::{Collection, Container, Containerized};
use crate::device::Sensor;
use crate::io::IOEvent;

/// Mediator to periodically poll sensors of various types, and store the resulting `IOEvent` objects in a `Container`.
///
/// `poll()` is the primary callable and iterates through the `Sensor` container to call `get_event()` on each sensor.
/// Resulting `IOEvent` objects are then added to the `log` container.
///
/// The `interval` field indicates the duration between each poll and the `last_execution` field indicates the last time the poll method was executed
///
/// TODO: multithreaded polling. Implement `RwLock` or `Mutex` to synchronize access to the sensors and
/// log containers in order to make the poll() function thread-safe.
pub struct Poller<T, K: Eq + Hash> {
interval: Duration,
last_execution: DateTime<Utc>,

// internal containers
pub sensors: Container<Box<dyn Sensor<T>>, K>,
pub log: Container<IOEvent<T>, DateTime<Utc>>,
}

impl<T: std::fmt::Debug, K: Eq + Hash> Poller<T, K> {
/// Iterate through container once. Call `get_event()` on each value.
/// Update according to the lowest rate.
pub fn poll(&mut self) {
let next_execution = self.last_execution + self.interval;

if next_execution <= Utc::now() {
for (_, sensor) in self.sensors.iter() {
self.last_execution = next_execution;
let event = sensor.get_event(next_execution);
self.log.add(next_execution, event);
dbg!(sensor);
}
}
}

/// Constructor for `Poller` struct.
/// Internal containers are instantiated as empty.
pub fn new( interval: Duration, last_execution: DateTime<Utc> ) -> Self {
let sensors: Container<Box<dyn Sensor<T>>, K> = <dyn Sensor<T>>::container();
let log: Container<IOEvent<T>, DateTime<Utc>> = <IOEvent<T>>::container();
Self { interval, last_execution, sensors, log }
}
}
Loading

0 comments on commit 5259745

Please sign in to comment.