Add rpi_pin_manager and make the app working on RPi.
This commit is contained in:
parent
38024dfc5d
commit
ddd231c0eb
10 changed files with 507 additions and 150 deletions
|
|
@ -6,6 +6,12 @@ edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[target.armv7-unknown-linux-musleabihf.dependencies]
|
||||||
|
rppal = { version = "0.11.3", features = ["hal"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
rpi_gpio = ["rppal"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-std = "1.9"
|
async-std = "1.9"
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "./cfg_reader_test.rs"]
|
||||||
|
mod cfg_reader_test;
|
||||||
|
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::{error::Error, io::Read};
|
use std::{error::Error, io::Read};
|
||||||
|
|
@ -5,17 +9,20 @@ use std::{error::Error, io::Read};
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Config {
|
pub struct S0Channel {
|
||||||
channels: Vec<S0Channel>,
|
pub id: u8,
|
||||||
// debounce_millis : Option<u8>,
|
pub gpio: u8,
|
||||||
// invert : Option<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct S0Channel {
|
pub struct Config {
|
||||||
id: u8,
|
channel: Vec<S0Channel>,
|
||||||
gpio: u8,
|
}
|
||||||
// debounce_millis: Option<u8>,
|
|
||||||
|
impl Config {
|
||||||
|
pub fn channels(&self) -> &Vec<S0Channel> {
|
||||||
|
&self.channel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use std::any::type_name;
|
// use std::any::type_name;
|
||||||
|
|
@ -46,7 +53,3 @@ fn parse_string(cfg_str: &String) -> Result<Config, Box<dyn Error>> {
|
||||||
Err(err) => Err(From::from(err)),
|
Err(err) => Err(From::from(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[path = "./cfg_reader_test.rs"]
|
|
||||||
mod cfg_reader_test;
|
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,54 @@ fn parse_file_fails_if_file_is_greater_than_1mb() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[named]
|
#[named]
|
||||||
fn parse_file_succeeds() {
|
fn parse_file_succeeds_on_entry() {
|
||||||
let cfg_file_name = create_cfg_file(
|
let cfg_file_name = create_cfg_file(
|
||||||
function_name!(),
|
function_name!(),
|
||||||
r#"
|
r#"
|
||||||
[[channels]]
|
[[channel]]
|
||||||
id = 1
|
id = 1
|
||||||
gpio = 4
|
gpio = 4
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
assert!(parse_file(&cfg_file_name).is_ok());
|
let config = parse_file(&cfg_file_name);
|
||||||
|
assert!(config.is_ok());
|
||||||
|
let config = config.unwrap();
|
||||||
|
let channels = config.channels();
|
||||||
|
assert_eq!(channels.len(), 1);
|
||||||
|
assert_eq!(channels[0].id, 1);
|
||||||
|
assert_eq!(channels[0].gpio, 4);
|
||||||
|
remove_cfg_file(&cfg_file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[named]
|
||||||
|
fn parse_file_succeeds_multiple_entries() {
|
||||||
|
let cfg_file_name = create_cfg_file(
|
||||||
|
function_name!(),
|
||||||
|
r#"
|
||||||
|
[[channel]]
|
||||||
|
id = 1
|
||||||
|
gpio = 4
|
||||||
|
[[channel]]
|
||||||
|
id = 3
|
||||||
|
gpio = 1
|
||||||
|
|
||||||
|
[[channel]]
|
||||||
|
id = 2
|
||||||
|
gpio = 9
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
let config = parse_file(&cfg_file_name);
|
||||||
|
assert!(config.is_ok());
|
||||||
|
let config = config.unwrap();
|
||||||
|
let channels = config.channels();
|
||||||
|
assert_eq!(channels.len(), 3);
|
||||||
|
assert_eq!(channels[0].id, 1);
|
||||||
|
assert_eq!(channels[0].gpio, 4);
|
||||||
|
assert_eq!(channels[1].id, 3);
|
||||||
|
assert_eq!(channels[1].gpio, 1);
|
||||||
|
assert_eq!(channels[2].id, 2);
|
||||||
|
assert_eq!(channels[2].gpio, 9);
|
||||||
remove_cfg_file(&cfg_file_name);
|
remove_cfg_file(&cfg_file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,7 +115,7 @@ fn parse_string_fails_on_unknown_section() {
|
||||||
fn parse_string_fails_on_section_format_error() {
|
fn parse_string_fails_on_section_format_error() {
|
||||||
let cfg_str = String::from(
|
let cfg_str = String::from(
|
||||||
r#"
|
r#"
|
||||||
[[channels]
|
[[channel]
|
||||||
id = 1
|
id = 1
|
||||||
gpio = 4
|
gpio = 4
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -86,7 +124,7 @@ fn parse_string_fails_on_section_format_error() {
|
||||||
|
|
||||||
let cfg_str = String::from(
|
let cfg_str = String::from(
|
||||||
r#"
|
r#"
|
||||||
[channels]
|
[channel]
|
||||||
id = 1
|
id = 1
|
||||||
gpio = 4
|
gpio = 4
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -95,7 +133,7 @@ fn parse_string_fails_on_section_format_error() {
|
||||||
|
|
||||||
let cfg_str = String::from(
|
let cfg_str = String::from(
|
||||||
r#"
|
r#"
|
||||||
[[channels]]
|
[[channel]]
|
||||||
id =
|
id =
|
||||||
gpio = 4
|
gpio = 4
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -104,7 +142,7 @@ fn parse_string_fails_on_section_format_error() {
|
||||||
|
|
||||||
let cfg_str = String::from(
|
let cfg_str = String::from(
|
||||||
r#"
|
r#"
|
||||||
[[channels]]
|
[[channel]]
|
||||||
id = 1
|
id = 1
|
||||||
gpio = 4
|
gpio = 4
|
||||||
2
|
2
|
||||||
|
|
@ -117,7 +155,7 @@ fn parse_string_fails_on_section_format_error() {
|
||||||
fn parse_string_fails_on_missing_field_id() {
|
fn parse_string_fails_on_missing_field_id() {
|
||||||
let cfg_str = String::from(
|
let cfg_str = String::from(
|
||||||
r#"
|
r#"
|
||||||
[[channels]]
|
[[channel]]
|
||||||
gpio = 4
|
gpio = 4
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
@ -128,7 +166,7 @@ fn parse_string_fails_on_missing_field_id() {
|
||||||
fn parse_string_fails_on_missing_field_gpio() {
|
fn parse_string_fails_on_missing_field_gpio() {
|
||||||
let cfg_str = String::from(
|
let cfg_str = String::from(
|
||||||
r#"
|
r#"
|
||||||
[[channels]]
|
[[channel]]
|
||||||
id = 1
|
id = 1
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
@ -139,7 +177,7 @@ fn parse_string_fails_on_missing_field_gpio() {
|
||||||
fn parse_string_ignores_unknown_attributes() {
|
fn parse_string_ignores_unknown_attributes() {
|
||||||
let cfg_str = String::from(
|
let cfg_str = String::from(
|
||||||
r#"
|
r#"
|
||||||
[[channels]]
|
[[channel]]
|
||||||
unknown = 1
|
unknown = 1
|
||||||
id = 1
|
id = 1
|
||||||
gpio = 4
|
gpio = 4
|
||||||
|
|
@ -152,11 +190,11 @@ fn parse_string_ignores_unknown_attributes() {
|
||||||
fn parse_string_succeeds() {
|
fn parse_string_succeeds() {
|
||||||
let cfg = String::from(
|
let cfg = String::from(
|
||||||
r#"
|
r#"
|
||||||
[[channels]]
|
[[channel]]
|
||||||
id = 1
|
id = 1
|
||||||
gpio = 4
|
gpio = 4
|
||||||
|
|
||||||
[[channels]]
|
[[channel]]
|
||||||
id = 2
|
id = 2
|
||||||
gpio = 17
|
gpio = 17
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -164,9 +202,9 @@ fn parse_string_succeeds() {
|
||||||
let config = parse_string(&cfg);
|
let config = parse_string(&cfg);
|
||||||
assert!(&config.is_ok());
|
assert!(&config.is_ok());
|
||||||
let config = config.unwrap();
|
let config = config.unwrap();
|
||||||
assert_eq!(config.channels.len(), 2);
|
assert_eq!(config.channel.len(), 2);
|
||||||
assert_eq!(config.channels[0].id, 1);
|
assert_eq!(config.channel[0].id, 1);
|
||||||
assert_eq!(config.channels[0].gpio, 4);
|
assert_eq!(config.channel[0].gpio, 4);
|
||||||
assert_eq!(config.channels[1].id, 2);
|
assert_eq!(config.channel[1].id, 2);
|
||||||
assert_eq!(config.channels[1].gpio, 17);
|
assert_eq!(config.channel[1].gpio, 17);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use std::sync::{mpsc, Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct PinConfiguration {
|
pub struct PinConfiguration {
|
||||||
pub id: usize,
|
pub channel_id: usize,
|
||||||
pub gpio: usize,
|
pub gpio_id: usize,
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PulseInfo {
|
pub struct PulseInfo {
|
||||||
|
|
|
||||||
104
src/main.rs
104
src/main.rs
|
|
@ -5,8 +5,18 @@ mod cfg_reader;
|
||||||
mod pulse_counter;
|
mod pulse_counter;
|
||||||
mod rest_api;
|
mod rest_api;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rppal"))]
|
||||||
|
mod input_pin_manager;
|
||||||
|
#[cfg(feature = "rppal")]
|
||||||
|
mod rpi_pin_manager;
|
||||||
|
// #[cfg(not(feature = "rppal"))]
|
||||||
|
use input_pin_manager::PinConfiguration;
|
||||||
|
#[cfg(feature = "rppal")]
|
||||||
|
use rpi_pin_manager::{input_pin_manager, RPiPinManager};
|
||||||
|
|
||||||
use async_std;
|
use async_std;
|
||||||
use async_std::task;
|
use async_std::task;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
const APP_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
const APP_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
|
@ -14,6 +24,36 @@ const DEFAULT_CONFIG_FILE_NAME: &str = "/etc/s0_logger.cfg";
|
||||||
const DEFAULT_IP_ADDRESS: &str = "127.0.0.1";
|
const DEFAULT_IP_ADDRESS: &str = "127.0.0.1";
|
||||||
const DEFAULT_IP_PORT: &str = "6310";
|
const DEFAULT_IP_PORT: &str = "6310";
|
||||||
|
|
||||||
|
struct PulseDataProvider {
|
||||||
|
pulse_counter: pulse_counter::PulseCounter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl rest_api::DataProvider for PulseDataProvider {
|
||||||
|
fn get_channels(&self) -> Vec<usize> {
|
||||||
|
self.pulse_counter.get_channels()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pulses_by_channel(
|
||||||
|
&mut self,
|
||||||
|
channel_id: usize,
|
||||||
|
) -> Result<Vec<rest_api::PulseInfo>, std::io::Error> {
|
||||||
|
match self.pulse_counter.get_pulses_by_channel(channel_id) {
|
||||||
|
Ok(pulses) => {
|
||||||
|
let mut pulse_list: Vec<rest_api::PulseInfo> = vec![];
|
||||||
|
for p in pulses {
|
||||||
|
pulse_list.push(rest_api::PulseInfo {
|
||||||
|
timestamp_ns: p.timestamp_ns,
|
||||||
|
pin_id: p.pin_id,
|
||||||
|
level: p.level,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(pulse_list)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cli_args = clap_app!(s0_meter =>
|
let cli_args = clap_app!(s0_meter =>
|
||||||
(version: APP_VERSION)
|
(version: APP_VERSION)
|
||||||
|
|
@ -36,33 +76,67 @@ fn main() {
|
||||||
.unwrap_or(DEFAULT_CONFIG_FILE_NAME);
|
.unwrap_or(DEFAULT_CONFIG_FILE_NAME);
|
||||||
println!("Read the config from file '{}'", config_file_name);
|
println!("Read the config from file '{}'", config_file_name);
|
||||||
|
|
||||||
if cfg!(feature = "rppal") {
|
let mut pin_config: Vec<PinConfiguration> = vec![];
|
||||||
|
match cfg_reader::parse_file(config_file_name) {
|
||||||
|
Ok(config) => {
|
||||||
|
println!("GPIO-Config:");
|
||||||
|
for channel in config.channels() {
|
||||||
|
println!("Channel {} @ GPIO {}", &channel.id, &channel.gpio);
|
||||||
|
&mut pin_config.push(PinConfiguration {
|
||||||
|
channel_id: channel.id as usize,
|
||||||
|
gpio_id: channel.gpio as usize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
println!("---");
|
||||||
|
}
|
||||||
|
Err(err_msg) => {
|
||||||
|
// TODO Log
|
||||||
|
panic!(
|
||||||
|
"Error while parsing config file {}: {}",
|
||||||
|
config_file_name, err_msg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(unused_assignments, unused_mut)]
|
||||||
|
let mut pin_manager: Option<
|
||||||
|
Arc<Mutex<Box<dyn input_pin_manager::InputPinManager + Send>>>,
|
||||||
|
> = None;
|
||||||
|
#[cfg(feature = "rppal")]
|
||||||
|
{
|
||||||
println!("Will access GPIO pins");
|
println!("Will access GPIO pins");
|
||||||
} else if cfg!(feature = "hal") {
|
let input_pin_manager_box: Box<dyn input_pin_manager::InputPinManager + Send> =
|
||||||
println!("Will access HAL");
|
Box::new(RPiPinManager::new());
|
||||||
} else {
|
pin_manager = Some(Arc::new(Mutex::new(input_pin_manager_box)));
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "rppal"))]
|
||||||
|
{
|
||||||
println!("Will NOT access GPIO pins");
|
println!("Will NOT access GPIO pins");
|
||||||
}
|
}
|
||||||
|
|
||||||
let rest_ip_addr = DEFAULT_IP_ADDRESS;
|
let rest_ip_addr = DEFAULT_IP_ADDRESS;
|
||||||
let rest_ip_port = DEFAULT_IP_PORT;
|
let rest_ip_port = DEFAULT_IP_PORT;
|
||||||
|
|
||||||
|
let mut p_counter = pulse_counter::PulseCounter::new(pin_manager.clone().unwrap(), pin_config);
|
||||||
|
match p_counter.start() {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => {
|
||||||
|
// TODO Log
|
||||||
|
println!("Could not start pulse_counter thread: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let dp: Arc<Mutex<Box<dyn crate::rest_api::DataProvider + Send>>> =
|
||||||
|
Arc::new(Mutex::new(Box::new(PulseDataProvider {
|
||||||
|
pulse_counter: p_counter,
|
||||||
|
})));
|
||||||
let rest_api_config = rest_api::RestApiConfig {
|
let rest_api_config = rest_api::RestApiConfig {
|
||||||
ip_and_port: format!("{}:{}", &rest_ip_addr, &rest_ip_port),
|
ip_and_port: format!("{}:{}", &rest_ip_addr, &rest_ip_port),
|
||||||
get_channels: fake_get_channels,
|
data_provider: dp.clone(),
|
||||||
get_pulses_by_channel: fake_get_pulses_by_channel,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = task::block_on(rest_api::start(&rest_api_config));
|
let _ = task::block_on(rest_api::start(&rest_api_config));
|
||||||
}
|
|
||||||
|
|
||||||
fn fake_get_channels() -> Vec<usize> {
|
if pin_manager.is_some() {
|
||||||
vec![]
|
&pin_manager.unwrap().lock().unwrap().close_inputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fake_get_pulses_by_channel(_: usize) -> Result<Vec<rest_api::PulseInfo>, std::io::Error> {
|
|
||||||
Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::NotFound,
|
|
||||||
"Not implemented",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,57 @@
|
||||||
// #![allow(dead_code)]
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "./pulse_counter_test.rs"]
|
#[path = "./pulse_counter_test.rs"]
|
||||||
mod pulse_counter_test;
|
mod pulse_counter_test;
|
||||||
|
|
||||||
#[path = "./input_pin_manager.rs"]
|
#[path = "./input_pin_manager.rs"]
|
||||||
mod input_pin_manager;
|
mod input_pin_manager;
|
||||||
use input_pin_manager::{InputPinManager, PinConfiguration, PulseInfo};
|
|
||||||
|
|
||||||
use std::collections::{hash_map::HashMap, VecDeque};
|
use std::collections::{hash_map::HashMap, VecDeque};
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
pub struct PulseCounter {
|
||||||
struct PulseCounter {
|
pin_mgr: Arc<Mutex<Box<dyn crate::input_pin_manager::InputPinManager + Send>>>,
|
||||||
pin_mgr: Box<dyn InputPinManager>,
|
pulses: Arc<Mutex<HashMap<usize, VecDeque<crate::input_pin_manager::PulseInfo>>>>,
|
||||||
pulses: Arc<Mutex<HashMap<usize, VecDeque<PulseInfo>>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PulseCounterError {
|
pub struct PulseCounterError {
|
||||||
msg: String,
|
msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PulseCounter {
|
impl PulseCounter {
|
||||||
fn new(pin_mgr: Box<dyn InputPinManager>, input_pins: Vec<PinConfiguration>) -> Self {
|
pub fn new(
|
||||||
let channel_lists_arc: Arc<Mutex<HashMap<usize, VecDeque<PulseInfo>>>> =
|
pin_mgr: Arc<Mutex<Box<dyn crate::input_pin_manager::InputPinManager + Send>>>,
|
||||||
Arc::new(Mutex::new(HashMap::with_capacity(input_pins.len())));
|
input_pins: Vec<crate::input_pin_manager::PinConfiguration>,
|
||||||
|
) -> Self {
|
||||||
|
let channel_lists_arc: Arc<
|
||||||
|
Mutex<HashMap<usize, VecDeque<crate::input_pin_manager::PulseInfo>>>,
|
||||||
|
> = Arc::new(Mutex::new(HashMap::with_capacity(input_pins.len())));
|
||||||
let mut channel_lists = channel_lists_arc.lock().unwrap();
|
let mut channel_lists = channel_lists_arc.lock().unwrap();
|
||||||
for pin in &input_pins {
|
for pin in &input_pins {
|
||||||
channel_lists.insert(pin.id, VecDeque::<PulseInfo>::new());
|
channel_lists.insert(
|
||||||
|
pin.channel_id,
|
||||||
|
VecDeque::<crate::input_pin_manager::PulseInfo>::new(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let mut myself = Self {
|
let myself = Self {
|
||||||
pin_mgr: pin_mgr,
|
pin_mgr: pin_mgr,
|
||||||
pulses: channel_lists_arc.clone(),
|
pulses: channel_lists_arc.clone(),
|
||||||
};
|
};
|
||||||
myself.pin_mgr.set_input_config(input_pins);
|
myself.pin_mgr.lock().unwrap().set_input_config(input_pins);
|
||||||
myself
|
myself
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&mut self) -> Result<(), PulseCounterError> {
|
pub fn get_channels(&self) -> Vec<usize> {
|
||||||
let msg_channel_recv_arc = Arc::clone(&self.pin_mgr.get_channel_recv());
|
let mut channels = vec![];
|
||||||
|
let pin_mgr = self.pin_mgr.lock().unwrap();
|
||||||
|
for p in pin_mgr.get_pins() {
|
||||||
|
channels.push(p.channel_id);
|
||||||
|
}
|
||||||
|
channels
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) -> Result<(), PulseCounterError> {
|
||||||
|
let msg_channel_recv_arc = Arc::clone(&self.pin_mgr.lock().unwrap().get_channel_recv());
|
||||||
let (err_tx, err_rx) = mpsc::channel::<Result<(), PulseCounterError>>();
|
let (err_tx, err_rx) = mpsc::channel::<Result<(), PulseCounterError>>();
|
||||||
let pulse_list = self.pulses.clone();
|
let pulse_list = self.pulses.clone();
|
||||||
//TODO Give this thread a name
|
//TODO Give this thread a name
|
||||||
|
|
@ -50,13 +62,13 @@ impl PulseCounter {
|
||||||
{
|
{
|
||||||
err_tx.send(Ok(())).unwrap();
|
err_tx.send(Ok(())).unwrap();
|
||||||
}
|
}
|
||||||
// let pulses = pulse_list.deref();
|
|
||||||
loop {
|
loop {
|
||||||
match msg_channel_recv.recv() {
|
match msg_channel_recv.recv() {
|
||||||
Ok(pulse_info) => {
|
Ok(pulse_info) => {
|
||||||
Self::process_pulse_info(&pulse_list, pulse_info);
|
Self::process_pulse_info(&pulse_list, pulse_info);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
// TODO Log
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,41 +85,52 @@ impl PulseCounter {
|
||||||
err_rx.recv().unwrap()
|
err_rx.recv().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) {
|
#[allow(dead_code)]
|
||||||
self.pin_mgr.close_inputs();
|
pub fn stop(&mut self) {
|
||||||
|
self.pin_mgr.lock().unwrap().close_inputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_pulse_info(
|
fn process_pulse_info(
|
||||||
pulses: &Arc<Mutex<HashMap<usize, VecDeque<PulseInfo>>>>,
|
pulses: &Arc<Mutex<HashMap<usize, VecDeque<crate::input_pin_manager::PulseInfo>>>>,
|
||||||
pulse: PulseInfo,
|
pulse: crate::input_pin_manager::PulseInfo,
|
||||||
) {
|
) {
|
||||||
//TODO Do not exceed a maximum length of the list
|
//TODO Do not exceed a maximum length of the list
|
||||||
match pulses.lock().unwrap().get_mut(&pulse.pin_id) {
|
let mut channel_lists = pulses.lock().unwrap();
|
||||||
|
match channel_lists.get_mut(&pulse.pin_id) {
|
||||||
Some(pulse_list) => {
|
Some(pulse_list) => {
|
||||||
// println!("Pushing pulse {:?} to list", pulse);
|
|
||||||
pulse_list.push_back(pulse);
|
pulse_list.push_back(pulse);
|
||||||
}
|
}
|
||||||
None => println!("Could not push pulse {:?} to list", pulse),
|
None => {
|
||||||
|
// TODO Log
|
||||||
|
println!("Could not push pulse {:?} to list", pulse)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pulses_count_by_channel(&self, channel: usize) -> usize {
|
#[allow(dead_code)]
|
||||||
|
pub fn get_pulses_count_by_channel(&self, channel: usize) -> usize {
|
||||||
match self.pulses.lock().unwrap().get(&channel) {
|
match self.pulses.lock().unwrap().get(&channel) {
|
||||||
Some(channel_pulses) => channel_pulses.len(),
|
Some(channel_pulses) => channel_pulses.len(),
|
||||||
None => 0,
|
None => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pulses_by_channel(&mut self, channel: usize) -> Vec<PulseInfo> {
|
pub fn get_pulses_by_channel(
|
||||||
let mut result: Vec<PulseInfo> = Vec::new();
|
&mut self,
|
||||||
|
channel: usize,
|
||||||
|
) -> Result<Vec<crate::input_pin_manager::PulseInfo>, std::io::Error> {
|
||||||
match self.pulses.lock().unwrap().get_mut(&channel) {
|
match self.pulses.lock().unwrap().get_mut(&channel) {
|
||||||
Some(pulse_list) => {
|
Some(pulse_list) => {
|
||||||
|
let mut result: Vec<crate::input_pin_manager::PulseInfo> = Vec::new();
|
||||||
while !pulse_list.is_empty() {
|
while !pulse_list.is_empty() {
|
||||||
result.push(pulse_list.pop_front().unwrap());
|
result.push(pulse_list.pop_front().unwrap());
|
||||||
}
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
None => Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
format!("Cannot get pulses of channel {}", channel),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
None => println!("Cannot get pulses of channel {}", channel),
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
struct TestPinManager {
|
struct TestPinManager {
|
||||||
input_pins: Vec<PinConfiguration>,
|
input_pins: Vec<crate::input_pin_manager::PinConfiguration>,
|
||||||
pulse_recv: Arc<Mutex<mpsc::Receiver<PulseInfo>>>,
|
pulse_recv: Arc<Mutex<mpsc::Receiver<crate::input_pin_manager::PulseInfo>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputPinManager for TestPinManager {
|
impl crate::input_pin_manager::InputPinManager for TestPinManager {
|
||||||
fn set_input_config(&mut self, input_pins: Vec<PinConfiguration>) {
|
fn set_input_config(&mut self, input_pins: Vec<crate::input_pin_manager::PinConfiguration>) {
|
||||||
*&mut self.input_pins = input_pins;
|
*&mut self.input_pins = input_pins;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_channel_recv(&self) -> &Arc<Mutex<mpsc::Receiver<PulseInfo>>> {
|
fn get_channel_recv(&self) -> &Arc<Mutex<mpsc::Receiver<crate::input_pin_manager::PulseInfo>>> {
|
||||||
&self.pulse_recv
|
&self.pulse_recv
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pins(&self) -> &Vec<PinConfiguration> {
|
fn get_pins(&self) -> &Vec<crate::input_pin_manager::PinConfiguration> {
|
||||||
&self.input_pins
|
&self.input_pins
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,8 +22,8 @@ impl InputPinManager for TestPinManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestPinManager {
|
impl TestPinManager {
|
||||||
fn new(cmd_rx: mpsc::Receiver<PulseInfo>) -> TestPinManager {
|
fn new(cmd_rx: mpsc::Receiver<crate::input_pin_manager::PulseInfo>) -> TestPinManager {
|
||||||
let (pulse_tx, pulse_rx) = mpsc::channel::<PulseInfo>();
|
let (pulse_tx, pulse_rx) = mpsc::channel::<crate::input_pin_manager::PulseInfo>();
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
match cmd_rx.recv() {
|
match cmd_rx.recv() {
|
||||||
Ok(cmd) => {
|
Ok(cmd) => {
|
||||||
|
|
@ -46,17 +46,26 @@ impl TestPinManager {
|
||||||
#[test]
|
#[test]
|
||||||
fn new_creates_instance() {
|
fn new_creates_instance() {
|
||||||
let (_, cmd_rx) = mpsc::channel();
|
let (_, cmd_rx) = mpsc::channel();
|
||||||
let test_pin_manager_box: Box<TestPinManager> = Box::new(TestPinManager::new(cmd_rx));
|
let test_pin_manager_box: Arc<
|
||||||
|
Mutex<Box<dyn crate::input_pin_manager::InputPinManager + Send>>,
|
||||||
|
> = Arc::new(Mutex::new(Box::new(TestPinManager::new(cmd_rx))));
|
||||||
|
|
||||||
let mut input_pins = Vec::new();
|
let mut input_pins: Vec<crate::input_pin_manager::PinConfiguration> = Vec::new();
|
||||||
input_pins.push(PinConfiguration { id: 1, gpio: 11 });
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
input_pins.push(PinConfiguration { id: 3, gpio: 33 });
|
channel_id: 1,
|
||||||
|
gpio_id: 11,
|
||||||
|
});
|
||||||
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
|
channel_id: 3,
|
||||||
|
gpio_id: 33,
|
||||||
|
});
|
||||||
|
|
||||||
let input_pins_copy = &input_pins.clone();
|
let input_pins_copy = &input_pins.clone();
|
||||||
|
|
||||||
let testee = PulseCounter::new(test_pin_manager_box, input_pins);
|
let testee = PulseCounter::new(test_pin_manager_box.clone(), input_pins);
|
||||||
|
|
||||||
let testee_pins = &testee.pin_mgr.get_pins();
|
let testee_pin_manager = testee.pin_mgr.lock().unwrap();
|
||||||
|
let testee_pins = testee_pin_manager.get_pins();
|
||||||
assert_eq!(testee_pins.len(), input_pins_copy.len());
|
assert_eq!(testee_pins.len(), input_pins_copy.len());
|
||||||
assert_eq!(testee_pins[0], input_pins_copy[0]);
|
assert_eq!(testee_pins[0], input_pins_copy[0]);
|
||||||
assert_eq!(testee_pins[1], input_pins_copy[1]);
|
assert_eq!(testee_pins[1], input_pins_copy[1]);
|
||||||
|
|
@ -65,13 +74,21 @@ fn new_creates_instance() {
|
||||||
#[test]
|
#[test]
|
||||||
fn start_and_stop_thread() {
|
fn start_and_stop_thread() {
|
||||||
let (_, cmd_rx) = mpsc::channel();
|
let (_, cmd_rx) = mpsc::channel();
|
||||||
let test_pin_manager_box: Box<TestPinManager> = Box::new(TestPinManager::new(cmd_rx));
|
let test_pin_manager_box: Arc<
|
||||||
|
Mutex<Box<dyn crate::input_pin_manager::InputPinManager + Send>>,
|
||||||
|
> = Arc::new(Mutex::new(Box::new(TestPinManager::new(cmd_rx))));
|
||||||
|
|
||||||
let mut input_pins = Vec::new();
|
let mut input_pins: Vec<crate::input_pin_manager::PinConfiguration> = Vec::new();
|
||||||
input_pins.push(PinConfiguration { id: 1, gpio: 11 });
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
input_pins.push(PinConfiguration { id: 3, gpio: 33 });
|
channel_id: 1,
|
||||||
|
gpio_id: 11,
|
||||||
|
});
|
||||||
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
|
channel_id: 3,
|
||||||
|
gpio_id: 33,
|
||||||
|
});
|
||||||
|
|
||||||
let mut testee = PulseCounter::new(test_pin_manager_box, input_pins);
|
let mut testee = PulseCounter::new(test_pin_manager_box.clone(), input_pins);
|
||||||
assert!(testee.start().is_ok());
|
assert!(testee.start().is_ok());
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
testee.stop();
|
testee.stop();
|
||||||
|
|
@ -82,13 +99,21 @@ fn start_thread_twice() {
|
||||||
// ATTENTION: Do NOT use _ as name of the tx channel because the channel would be closed
|
// ATTENTION: Do NOT use _ as name of the tx channel because the channel would be closed
|
||||||
// immediately and the thread would exit before it will be started the second time!
|
// immediately and the thread would exit before it will be started the second time!
|
||||||
let (_usused_but_required_tx, cmd_rx) = mpsc::channel();
|
let (_usused_but_required_tx, cmd_rx) = mpsc::channel();
|
||||||
let test_pin_manager_box = Box::new(TestPinManager::new(cmd_rx));
|
let test_pin_manager_box: Arc<
|
||||||
|
Mutex<Box<dyn crate::input_pin_manager::InputPinManager + Send>>,
|
||||||
|
> = Arc::new(Mutex::new(Box::new(TestPinManager::new(cmd_rx))));
|
||||||
|
|
||||||
let mut input_pins = Vec::new();
|
let mut input_pins: Vec<crate::input_pin_manager::PinConfiguration> = Vec::new();
|
||||||
input_pins.push(PinConfiguration { id: 1, gpio: 11 });
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
input_pins.push(PinConfiguration { id: 3, gpio: 33 });
|
channel_id: 1,
|
||||||
|
gpio_id: 11,
|
||||||
|
});
|
||||||
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
|
channel_id: 3,
|
||||||
|
gpio_id: 33,
|
||||||
|
});
|
||||||
|
|
||||||
let mut testee = PulseCounter::new(test_pin_manager_box, input_pins);
|
let mut testee = PulseCounter::new(test_pin_manager_box.clone(), input_pins);
|
||||||
assert!(testee.start().is_ok());
|
assert!(testee.start().is_ok());
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
let result = testee.start();
|
let result = testee.start();
|
||||||
|
|
@ -100,20 +125,28 @@ fn start_thread_twice() {
|
||||||
fn start_thread_and_send_pulses() {
|
fn start_thread_and_send_pulses() {
|
||||||
let millis_to_wait = 1u64;
|
let millis_to_wait = 1u64;
|
||||||
let (cmd_tx, cmd_rx) = mpsc::channel();
|
let (cmd_tx, cmd_rx) = mpsc::channel();
|
||||||
let test_pin_manager_box: Box<TestPinManager> = Box::new(TestPinManager::new(cmd_rx));
|
let test_pin_manager_box: Arc<
|
||||||
|
Mutex<Box<dyn crate::input_pin_manager::InputPinManager + Send>>,
|
||||||
|
> = Arc::new(Mutex::new(Box::new(TestPinManager::new(cmd_rx))));
|
||||||
|
|
||||||
let mut input_pins = Vec::new();
|
let mut input_pins: Vec<crate::input_pin_manager::PinConfiguration> = Vec::new();
|
||||||
input_pins.push(PinConfiguration { id: 1, gpio: 11 });
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
input_pins.push(PinConfiguration { id: 3, gpio: 33 });
|
channel_id: 1,
|
||||||
|
gpio_id: 11,
|
||||||
|
});
|
||||||
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
|
channel_id: 3,
|
||||||
|
gpio_id: 33,
|
||||||
|
});
|
||||||
|
|
||||||
let mut testee = PulseCounter::new(test_pin_manager_box, input_pins);
|
let mut testee = PulseCounter::new(test_pin_manager_box.clone(), input_pins);
|
||||||
assert!(testee.start().is_ok());
|
assert!(testee.start().is_ok());
|
||||||
std::thread::sleep(std::time::Duration::from_millis(millis_to_wait));
|
std::thread::sleep(std::time::Duration::from_millis(millis_to_wait));
|
||||||
|
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(1), 0);
|
assert_eq!(testee.get_pulses_count_by_channel(1), 0);
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(3), 0);
|
assert_eq!(testee.get_pulses_count_by_channel(3), 0);
|
||||||
|
|
||||||
&cmd_tx.send(PulseInfo {
|
&cmd_tx.send(crate::input_pin_manager::PulseInfo {
|
||||||
timestamp_ns: 1234u64,
|
timestamp_ns: 1234u64,
|
||||||
pin_id: 1,
|
pin_id: 1,
|
||||||
level: true,
|
level: true,
|
||||||
|
|
@ -123,7 +156,7 @@ fn start_thread_and_send_pulses() {
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(1), 1);
|
assert_eq!(testee.get_pulses_count_by_channel(1), 1);
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(3), 0);
|
assert_eq!(testee.get_pulses_count_by_channel(3), 0);
|
||||||
|
|
||||||
&cmd_tx.send(PulseInfo {
|
&cmd_tx.send(crate::input_pin_manager::PulseInfo {
|
||||||
timestamp_ns: 1235u64,
|
timestamp_ns: 1235u64,
|
||||||
pin_id: 1,
|
pin_id: 1,
|
||||||
level: false,
|
level: false,
|
||||||
|
|
@ -133,7 +166,7 @@ fn start_thread_and_send_pulses() {
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(1), 2);
|
assert_eq!(testee.get_pulses_count_by_channel(1), 2);
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(3), 0);
|
assert_eq!(testee.get_pulses_count_by_channel(3), 0);
|
||||||
|
|
||||||
&cmd_tx.send(PulseInfo {
|
&cmd_tx.send(crate::input_pin_manager::PulseInfo {
|
||||||
timestamp_ns: 1280u64,
|
timestamp_ns: 1280u64,
|
||||||
pin_id: 3,
|
pin_id: 3,
|
||||||
level: true,
|
level: true,
|
||||||
|
|
@ -143,7 +176,7 @@ fn start_thread_and_send_pulses() {
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(1), 2);
|
assert_eq!(testee.get_pulses_count_by_channel(1), 2);
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(3), 1);
|
assert_eq!(testee.get_pulses_count_by_channel(3), 1);
|
||||||
|
|
||||||
&cmd_tx.send(PulseInfo {
|
&cmd_tx.send(crate::input_pin_manager::PulseInfo {
|
||||||
timestamp_ns: 1288u64,
|
timestamp_ns: 1288u64,
|
||||||
pin_id: 3,
|
pin_id: 3,
|
||||||
level: false,
|
level: false,
|
||||||
|
|
@ -161,32 +194,40 @@ fn start_thread_and_send_pulses() {
|
||||||
fn retrieve_pulses_by_channel() {
|
fn retrieve_pulses_by_channel() {
|
||||||
let millis_to_wait = 1u64;
|
let millis_to_wait = 1u64;
|
||||||
let (cmd_tx, cmd_rx) = mpsc::channel();
|
let (cmd_tx, cmd_rx) = mpsc::channel();
|
||||||
let test_pin_manager_box: Box<TestPinManager> = Box::new(TestPinManager::new(cmd_rx));
|
let test_pin_manager_box: Arc<
|
||||||
|
Mutex<Box<dyn crate::input_pin_manager::InputPinManager + Send>>,
|
||||||
|
> = Arc::new(Mutex::new(Box::new(TestPinManager::new(cmd_rx))));
|
||||||
|
|
||||||
let mut input_pins = Vec::new();
|
let mut input_pins: Vec<crate::input_pin_manager::PinConfiguration> = Vec::new();
|
||||||
input_pins.push(PinConfiguration { id: 1, gpio: 11 });
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
input_pins.push(PinConfiguration { id: 3, gpio: 33 });
|
channel_id: 1,
|
||||||
|
gpio_id: 11,
|
||||||
|
});
|
||||||
|
input_pins.push(crate::input_pin_manager::PinConfiguration {
|
||||||
|
channel_id: 3,
|
||||||
|
gpio_id: 33,
|
||||||
|
});
|
||||||
|
|
||||||
let mut testee = PulseCounter::new(test_pin_manager_box, input_pins);
|
let mut testee = PulseCounter::new(test_pin_manager_box.clone(), input_pins);
|
||||||
assert!(testee.start().is_ok());
|
assert!(testee.start().is_ok());
|
||||||
std::thread::sleep(std::time::Duration::from_millis(millis_to_wait));
|
std::thread::sleep(std::time::Duration::from_millis(millis_to_wait));
|
||||||
|
|
||||||
&cmd_tx.send(PulseInfo {
|
&cmd_tx.send(crate::input_pin_manager::PulseInfo {
|
||||||
timestamp_ns: 1234u64,
|
timestamp_ns: 1234u64,
|
||||||
pin_id: 1,
|
pin_id: 1,
|
||||||
level: true,
|
level: true,
|
||||||
});
|
});
|
||||||
&cmd_tx.send(PulseInfo {
|
&cmd_tx.send(crate::input_pin_manager::PulseInfo {
|
||||||
timestamp_ns: 1235u64,
|
timestamp_ns: 1235u64,
|
||||||
pin_id: 1,
|
pin_id: 1,
|
||||||
level: false,
|
level: false,
|
||||||
});
|
});
|
||||||
&cmd_tx.send(PulseInfo {
|
&cmd_tx.send(crate::input_pin_manager::PulseInfo {
|
||||||
timestamp_ns: 1280u64,
|
timestamp_ns: 1280u64,
|
||||||
pin_id: 3,
|
pin_id: 3,
|
||||||
level: true,
|
level: true,
|
||||||
});
|
});
|
||||||
&cmd_tx.send(PulseInfo {
|
&cmd_tx.send(crate::input_pin_manager::PulseInfo {
|
||||||
timestamp_ns: 1288u64,
|
timestamp_ns: 1288u64,
|
||||||
pin_id: 3,
|
pin_id: 3,
|
||||||
level: false,
|
level: false,
|
||||||
|
|
@ -196,7 +237,7 @@ fn retrieve_pulses_by_channel() {
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(1), 2);
|
assert_eq!(testee.get_pulses_count_by_channel(1), 2);
|
||||||
assert_eq!(testee.get_pulses_count_by_channel(3), 2);
|
assert_eq!(testee.get_pulses_count_by_channel(3), 2);
|
||||||
|
|
||||||
let pulses_ch1 = testee.get_pulses_by_channel(1);
|
let pulses_ch1 = testee.get_pulses_by_channel(1).unwrap();
|
||||||
assert_eq!(pulses_ch1.len(), 2);
|
assert_eq!(pulses_ch1.len(), 2);
|
||||||
assert_eq!(pulses_ch1[0].timestamp_ns, 1234u64);
|
assert_eq!(pulses_ch1[0].timestamp_ns, 1234u64);
|
||||||
assert_eq!(pulses_ch1[0].pin_id, 1);
|
assert_eq!(pulses_ch1[0].pin_id, 1);
|
||||||
|
|
@ -205,10 +246,10 @@ fn retrieve_pulses_by_channel() {
|
||||||
assert_eq!(pulses_ch1[1].pin_id, 1);
|
assert_eq!(pulses_ch1[1].pin_id, 1);
|
||||||
assert_eq!(pulses_ch1[1].level, false);
|
assert_eq!(pulses_ch1[1].level, false);
|
||||||
|
|
||||||
let pulses_ch1 = testee.get_pulses_by_channel(1);
|
let pulses_ch1 = testee.get_pulses_by_channel(1).unwrap();
|
||||||
assert_eq!(pulses_ch1.len(), 0);
|
assert_eq!(pulses_ch1.len(), 0);
|
||||||
|
|
||||||
let pulses_ch1 = testee.get_pulses_by_channel(3);
|
let pulses_ch1 = testee.get_pulses_by_channel(3).unwrap();
|
||||||
assert_eq!(pulses_ch1.len(), 2);
|
assert_eq!(pulses_ch1.len(), 2);
|
||||||
assert_eq!(pulses_ch1[0].timestamp_ns, 1280u64);
|
assert_eq!(pulses_ch1[0].timestamp_ns, 1280u64);
|
||||||
assert_eq!(pulses_ch1[0].pin_id, 3);
|
assert_eq!(pulses_ch1[0].pin_id, 3);
|
||||||
|
|
@ -217,6 +258,6 @@ fn retrieve_pulses_by_channel() {
|
||||||
assert_eq!(pulses_ch1[1].pin_id, 3);
|
assert_eq!(pulses_ch1[1].pin_id, 3);
|
||||||
assert_eq!(pulses_ch1[1].level, false);
|
assert_eq!(pulses_ch1[1].level, false);
|
||||||
|
|
||||||
let pulses_ch1 = testee.get_pulses_by_channel(3);
|
let pulses_ch1 = testee.get_pulses_by_channel(3).unwrap();
|
||||||
assert_eq!(pulses_ch1.len(), 0);
|
assert_eq!(pulses_ch1.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,28 @@
|
||||||
#[allow(dead_code)]
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "./rest_api_test.rs"]
|
#[path = "./rest_api_test.rs"]
|
||||||
mod rest_api_test;
|
mod rest_api_test;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use tide;
|
use tide;
|
||||||
use tide::prelude::json;
|
use tide::prelude::json;
|
||||||
|
|
||||||
// struct PinConfiguration {
|
pub trait DataProvider {
|
||||||
// channel: usize,
|
fn get_channels(&self) -> Vec<usize>;
|
||||||
// gpio: usize,
|
fn get_pulses_by_channel(&mut self, channel: usize) -> Result<Vec<PulseInfo>, std::io::Error>;
|
||||||
// }
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct PulseInfo {
|
pub struct PulseInfo {
|
||||||
timestamp_ns: u64,
|
pub timestamp_ns: u64,
|
||||||
pin_id: usize,
|
pub pin_id: usize,
|
||||||
level: bool,
|
pub level: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RestApiConfig {
|
pub struct RestApiConfig {
|
||||||
pub ip_and_port: String,
|
pub ip_and_port: String,
|
||||||
pub get_channels: fn() -> Vec<usize>,
|
pub data_provider: Arc<Mutex<Box<dyn DataProvider + Send>>>,
|
||||||
pub get_pulses_by_channel: fn(channel: usize) -> Result<Vec<PulseInfo>, std::io::Error>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start<'a>(config: &'a RestApiConfig) -> std::io::Result<()> {
|
pub async fn start<'a>(config: &'a RestApiConfig) -> std::io::Result<()> {
|
||||||
|
|
@ -57,7 +56,9 @@ async fn api_versions_get(_: tide::Request<RestApiConfig>) -> tide::Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn v1_channels_get(req: tide::Request<RestApiConfig>) -> tide::Result {
|
async fn v1_channels_get(req: tide::Request<RestApiConfig>) -> tide::Result {
|
||||||
let channel_list = &(req.state().get_channels)();
|
let data_provider = &(*req.state().data_provider).lock().unwrap();
|
||||||
|
// let data_provider = data_provider_mut.lock().unwrap();
|
||||||
|
let channel_list = data_provider.get_channels();
|
||||||
Ok(tide::Response::builder(200)
|
Ok(tide::Response::builder(200)
|
||||||
.content_type(tide::http::mime::JSON)
|
.content_type(tide::http::mime::JSON)
|
||||||
.body(json!({ "channels": channel_list }))
|
.body(json!({ "channels": channel_list }))
|
||||||
|
|
@ -68,16 +69,14 @@ async fn v1_channel_pulses_get(req: tide::Request<RestApiConfig>) -> tide::Resul
|
||||||
match req.param("channel_id") {
|
match req.param("channel_id") {
|
||||||
Ok(channel_id) => match usize::from_str_radix(channel_id, 10) {
|
Ok(channel_id) => match usize::from_str_radix(channel_id, 10) {
|
||||||
Ok(channel_id) => {
|
Ok(channel_id) => {
|
||||||
let info = &req.state().ip_and_port;
|
let data_provider = &mut (*req.state().data_provider).lock().unwrap();
|
||||||
let channel_pulses_res = &(req.state().get_pulses_by_channel)(channel_id);
|
let channel_pulses_res = data_provider.get_pulses_by_channel(channel_id);
|
||||||
match channel_pulses_res {
|
match channel_pulses_res {
|
||||||
Ok(channel_pulses) => Ok(tide::Response::builder(200)
|
Ok(channel_pulses) => Ok(tide::Response::builder(200)
|
||||||
.content_type(tide::http::mime::JSON)
|
.content_type(tide::http::mime::JSON)
|
||||||
.body(json!({
|
.body(json!({
|
||||||
"channel": channel_id,
|
"channel": channel_id,
|
||||||
"pulses": channel_pulses,
|
"pulses": channel_pulses,
|
||||||
"question": "channel",
|
|
||||||
"info": info,
|
|
||||||
}))
|
}))
|
||||||
.build()),
|
.build()),
|
||||||
Err(_) => Ok(tide::Response::new(404)),
|
Err(_) => Ok(tide::Response::new(404)),
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,19 @@ use std::thread;
|
||||||
|
|
||||||
static IP_AND_PORT: &str = "127.0.0.1:8123";
|
static IP_AND_PORT: &str = "127.0.0.1:8123";
|
||||||
|
|
||||||
|
struct TestDataProvider {}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
// static ref TEST_DATA_PROVIDER: Arc<Mutex<Box<dyn super::DataProvider>>> =
|
||||||
|
// Arc::new(Mutex::new(Box::new(TestDataProvider{})));
|
||||||
static ref TESTEE_THREAD_HANDLE: Arc<Mutex<Option<thread::JoinHandle<()>>>> = {
|
static ref TESTEE_THREAD_HANDLE: Arc<Mutex<Option<thread::JoinHandle<()>>>> = {
|
||||||
let config = RestApiConfig {
|
let config = RestApiConfig {
|
||||||
ip_and_port: String::from(IP_AND_PORT),
|
ip_and_port: String::from(IP_AND_PORT),
|
||||||
get_channels,
|
data_provider: Arc::new(Mutex::new(Box::new(TestDataProvider{}))),
|
||||||
get_pulses_by_channel,
|
// data_provider: TEST_DATA_PROVIDER,
|
||||||
|
// get_channels: Box::new(|| get_channels()) as Box<dyn Fn() -> Vec<usize> + Send>,
|
||||||
|
// get_pulses_by_channel,
|
||||||
|
// get_pulses_by_channel: Box::new(|channel| get_pulses_by_channel(channel)) as Box<dyn Fn(usize) -> Result<Vec<PulseInfo>, std::io::Error> + Send>,
|
||||||
};
|
};
|
||||||
let hdl = Arc::new(Mutex::new(Some(thread::spawn(move || {
|
let hdl = Arc::new(Mutex::new(Some(thread::spawn(move || {
|
||||||
task::block_on(start(&config.clone())).unwrap();
|
task::block_on(start(&config.clone())).unwrap();
|
||||||
|
|
@ -32,11 +39,15 @@ lazy_static! {
|
||||||
static ref CHANNEL_PULSES: Mutex<HashMap<usize, Vec<PulseInfo>>> = Mutex::new(HashMap::new());
|
static ref CHANNEL_PULSES: Mutex<HashMap<usize, Vec<PulseInfo>>> = Mutex::new(HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_channels() -> Vec<usize> {
|
impl DataProvider for TestDataProvider {
|
||||||
|
fn get_channels(&self) -> Vec<usize> {
|
||||||
CHANNEL_LIST.lock().unwrap().to_vec()
|
CHANNEL_LIST.lock().unwrap().to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pulses_by_channel(channel_id: usize) -> Result<Vec<PulseInfo>, std::io::Error> {
|
fn get_pulses_by_channel(
|
||||||
|
&mut self,
|
||||||
|
channel_id: usize,
|
||||||
|
) -> Result<Vec<PulseInfo>, std::io::Error> {
|
||||||
let mut pulse_channel_map = CHANNEL_PULSES.lock().unwrap();
|
let mut pulse_channel_map = CHANNEL_PULSES.lock().unwrap();
|
||||||
match pulse_channel_map.get_mut(&channel_id) {
|
match pulse_channel_map.get_mut(&channel_id) {
|
||||||
Some(pulse_list) => Ok(pulse_list.drain(0..).collect()),
|
Some(pulse_list) => Ok(pulse_list.drain(0..).collect()),
|
||||||
|
|
@ -46,6 +57,7 @@ fn get_pulses_by_channel(channel_id: usize) -> Result<Vec<PulseInfo>, std::io::E
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn launch_testee() {
|
fn launch_testee() {
|
||||||
let _hdl = &*TESTEE_THREAD_HANDLE.clone();
|
let _hdl = &*TESTEE_THREAD_HANDLE.clone();
|
||||||
|
|
|
||||||
161
src/rpi_pin_manager.rs
Normal file
161
src/rpi_pin_manager.rs
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
#[path = "./input_pin_manager.rs"]
|
||||||
|
pub mod input_pin_manager;
|
||||||
|
|
||||||
|
pub use self::input_pin_manager::InputPinManager;
|
||||||
|
pub use input_pin_manager::PinConfiguration;
|
||||||
|
pub use input_pin_manager::PulseInfo;
|
||||||
|
|
||||||
|
use rppal::gpio;
|
||||||
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
|
use std::{thread, time};
|
||||||
|
|
||||||
|
struct IsrInfo {
|
||||||
|
timestamp_ns: u64,
|
||||||
|
channel: usize,
|
||||||
|
level: gpio::Level,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RPiPinManager {
|
||||||
|
gpio: gpio::Gpio,
|
||||||
|
pin_configuration: Vec<PinConfiguration>,
|
||||||
|
gpio_handles: Vec<gpio::InputPin>,
|
||||||
|
pulse_recv: Arc<Mutex<mpsc::Receiver<PulseInfo>>>,
|
||||||
|
ch_isr_send: Arc<Mutex<mpsc::Sender<IsrInfo>>>,
|
||||||
|
start_time: time::Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputPinManager for RPiPinManager {
|
||||||
|
fn set_input_config(&mut self, input_pins: Vec<PinConfiguration>) {
|
||||||
|
if self.pin_configuration.len() > 0 {
|
||||||
|
// TODO Log
|
||||||
|
println!("RPiPinManager::set_input_config() already set");
|
||||||
|
} else {
|
||||||
|
println!("set_input_config:");
|
||||||
|
let isr_start = self.start_time;
|
||||||
|
|
||||||
|
for p in input_pins {
|
||||||
|
println!(" channel: {} > gpio: {}", &p.channel_id, &p.gpio_id);
|
||||||
|
let mut pin = self.gpio.get(p.gpio_id as u8).unwrap().into_input_pullup();
|
||||||
|
|
||||||
|
let ch_isr_send = self.ch_isr_send.clone();
|
||||||
|
let pin_id = p.channel_id.clone();
|
||||||
|
|
||||||
|
let interrupt_service_routine = move |level: gpio::Level| {
|
||||||
|
let isr_info = IsrInfo {
|
||||||
|
timestamp_ns: isr_start.elapsed().as_nanos() as u64,
|
||||||
|
channel: pin_id,
|
||||||
|
level: level,
|
||||||
|
};
|
||||||
|
match ch_isr_send.lock().unwrap().send(isr_info) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
// TODO Log
|
||||||
|
println!("Error while sending: {}", e)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
pin.set_async_interrupt(gpio::Trigger::Both, interrupt_service_routine.clone())
|
||||||
|
.unwrap();
|
||||||
|
self.pin_configuration.push(p);
|
||||||
|
self.gpio_handles.push(pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_channel_recv(&self) -> &Arc<Mutex<mpsc::Receiver<PulseInfo>>> {
|
||||||
|
&self.pulse_recv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pins(&self) -> &Vec<PinConfiguration> {
|
||||||
|
&self.pin_configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_inputs(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RPiPinManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
println!("RPiPinManager::new()");
|
||||||
|
let gpio = match gpio::Gpio::new() {
|
||||||
|
Ok(gpio) => gpio,
|
||||||
|
Err(e) => {
|
||||||
|
// TODO Log
|
||||||
|
println!("ERROR: Cannot access GPIO: {}", e);
|
||||||
|
panic!("Error");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (pulse_tx, pulse_rx) = mpsc::channel::<PulseInfo>();
|
||||||
|
|
||||||
|
let (hdl_isr_send, hdl_isr_recv) = mpsc::channel::<IsrInfo>();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut last_state = gpio::Level::High;
|
||||||
|
let mut timeout = time::Duration::from_secs(u64::MAX);
|
||||||
|
loop {
|
||||||
|
if Self::wait_for_isr_event_and_process_it(
|
||||||
|
&hdl_isr_recv,
|
||||||
|
&pulse_tx,
|
||||||
|
&mut last_state,
|
||||||
|
&mut timeout,
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let start_time = time::Instant::now();
|
||||||
|
Self {
|
||||||
|
gpio,
|
||||||
|
pin_configuration: vec![],
|
||||||
|
gpio_handles: vec![],
|
||||||
|
pulse_recv: Arc::new(Mutex::new(pulse_rx)),
|
||||||
|
ch_isr_send: Arc::new(Mutex::new(hdl_isr_send)),
|
||||||
|
start_time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_for_isr_event_and_process_it(
|
||||||
|
receiver: &mpsc::Receiver<IsrInfo>,
|
||||||
|
client_channel: &mpsc::Sender<PulseInfo>,
|
||||||
|
last_state: &mut gpio::Level,
|
||||||
|
timeout: &mut time::Duration,
|
||||||
|
) -> bool {
|
||||||
|
let mut shall_stop = false;
|
||||||
|
match receiver.recv_timeout(*timeout) {
|
||||||
|
Ok(isr_info) => {
|
||||||
|
let level = match isr_info.level {
|
||||||
|
gpio::Level::High => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
let pulse_info = PulseInfo {
|
||||||
|
timestamp_ns: isr_info.timestamp_ns,
|
||||||
|
pin_id: isr_info.channel,
|
||||||
|
level,
|
||||||
|
};
|
||||||
|
match client_channel.send(pulse_info) {
|
||||||
|
Ok(_) => {
|
||||||
|
// Sending succeeded
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Silently ignore the sending error because
|
||||||
|
// there may be no event receive at the other end of this channel
|
||||||
|
println!("ISR: Send error {}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
match e {
|
||||||
|
mpsc::RecvTimeoutError::Timeout => {
|
||||||
|
*timeout = time::Duration::from_secs(u64::MAX);
|
||||||
|
println!("------> {}", last_state);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("Last sender stopped: {}", e);
|
||||||
|
shall_stop = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
shall_stop
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue