diff --git a/src/gpio/mod.rs b/src/gpio/mod.rs index a4952a0f..32ba4106 100644 --- a/src/gpio/mod.rs +++ b/src/gpio/mod.rs @@ -1,10 +1,10 @@ mod pin_descriptions; +use pin_descriptions::*; +use serde::{Deserialize, Serialize}; use std::fs::File; use std::io; use std::io::{BufReader, Write}; -use serde::{Deserialize, Serialize}; -use pin_descriptions::*; // All the possible functions a pin can be given #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] @@ -30,7 +30,7 @@ pub enum PinFunction { PCM_CLK, SPIO_CE0_N, SPIO_CE1_N, - ID_SC + ID_SC, } // [board_pin_number] refer to the pins by the number of the pin printed on the board @@ -42,14 +42,21 @@ pub struct PinDescription { pub board_pin_number: u8, bcm_pin_number: Option, pub name: &'static str, - options: &'static[PinFunction], // The set of functions the pin can have, chosen by user config + pub options: &'static [PinFunction], // The set of functions the pin can have, chosen by user config +} +impl std::fmt::Display for PinFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } } // Model the 40 pin GPIO connections - including Ground, 3.3V and 5V outputs -pub const GPIO_DESCRIPTION : [PinDescription; 40] = [PIN_1, PIN_2, PIN_3, PIN_4, PIN_5, PIN_6, PIN_7, PIN_8, PIN_9, PIN_10, - PIN_11, PIN_12, PIN_13, PIN_14, PIN_15, PIN_16, PIN_17, PIN_18, PIN_19, PIN_20, - PIN_21, PIN_22, PIN_23, PIN_24, PIN_25, PIN_26, PIN_27, PIN_28, PIN_29, PIN_30, - PIN_31, PIN_32, PIN_33, PIN_34, PIN_35, PIN_36, PIN_37, PIN_38, PIN_39, PIN_40]; +pub const GPIO_DESCRIPTION: [PinDescription; 40] = [ + PIN_1, PIN_2, PIN_3, PIN_4, PIN_5, PIN_6, PIN_7, PIN_8, PIN_9, PIN_10, PIN_11, PIN_12, PIN_13, + PIN_14, PIN_15, PIN_16, PIN_17, PIN_18, PIN_19, PIN_20, PIN_21, PIN_22, PIN_23, PIN_24, PIN_25, + PIN_26, PIN_27, PIN_28, PIN_29, PIN_30, PIN_31, PIN_32, PIN_33, PIN_34, PIN_35, PIN_36, PIN_37, + PIN_38, PIN_39, PIN_40, +]; // A vector of tuples of (board_pin_number, PinFunction) #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -60,8 +67,8 @@ pub struct GPIOConfig { impl GPIOConfig { #[cfg(feature = "gui")] #[allow(dead_code)] // "pi" build enables piglet which doesn't use this :-( TODO - // TODO take AsPath/AsRef etc - pub fn load(filename: &str) -> io::Result { + // TODO take AsPath/AsRef etc + pub fn load(filename: &str) -> io::Result { let file = File::open(filename)?; let reader = BufReader::new(file); let config = serde_json::from_reader(reader)?; @@ -85,17 +92,17 @@ pub type PinLevel = bool; #[derive(Debug)] #[allow(dead_code)] pub struct GPIOState { - pub pin_state: [Option; 40] // TODO make private later + pub pin_state: [Option; 40], // TODO make private later } #[cfg(test)] mod test { + use crate::gpio::{GPIOConfig, PinFunction}; use std::fs; use std::fs::File; use std::io::Write; use std::path::PathBuf; use tempfile::tempdir; - use crate::gpio::{GPIOConfig, PinFunction}; #[test] fn create_a_config() { @@ -109,7 +116,8 @@ mod test { let output_dir = tempdir().expect("Could not create a tempdir").into_path(); let test_file = output_dir.join("test.piggui"); let mut file = File::create(&test_file).expect("Could not create test file"); - file.write_all(pin_config.as_bytes()).expect("Could not write to test file"); + file.write_all(pin_config.as_bytes()) + .expect("Could not write to test file"); let config = GPIOConfig::load(test_file.to_str().unwrap()).unwrap(); assert_eq!(config.configured_pins.len(), 1); assert_eq!(config.configured_pins[0].0, 1); @@ -118,8 +126,7 @@ mod test { #[test] fn load_test_file() { - let root = std::env::var("CARGO_MANIFEST_DIR") - .expect("Could not get manifest dir"); + let root = std::env::var("CARGO_MANIFEST_DIR").expect("Could not get manifest dir"); let mut path = PathBuf::from(root); path = path.join("tests/one_pin_config.piggui"); let config = GPIOConfig::load(path.to_str().unwrap()).unwrap(); @@ -131,7 +138,7 @@ mod test { #[test] fn save_one_pin_config() { let config = GPIOConfig { - configured_pins: vec!((1, PinFunction::Input)) + configured_pins: vec![(1, PinFunction::Input)], }; let output_dir = tempdir().expect("Could not create a tempdir").into_path(); @@ -140,8 +147,7 @@ mod test { config.save(test_file.to_str().unwrap()).unwrap(); let pin_config = r#"{"configured_pins":[[1,"Input"]]}"#; - let contents = fs::read_to_string(test_file) - .expect("Could not read test file"); + let contents = fs::read_to_string(test_file).expect("Could not read test file"); assert_eq!(contents, pin_config); } -} \ No newline at end of file +} diff --git a/src/piggui.rs b/src/piggui.rs index b5c6ba9f..253c87f7 100644 --- a/src/piggui.rs +++ b/src/piggui.rs @@ -1,3 +1,15 @@ +use std::env; + +use iced::widget::{button, container, pick_list, Column, Row, Text}; +use iced::{alignment, window, Alignment, Color, Element, Length, Sandbox, Settings, Theme}; + +// Using Custom Widgets +use custom_widgets::{circle::circle, line::line}; + +// This binary will only be built with the "iced" feature enabled, by use of "required-features" +// in Cargo.toml so no need for the feature to be used here for conditional compiling +use crate::gpio::{GPIOConfig, PinDescription, PinFunction, GPIO_DESCRIPTION}; + mod gpio; mod hw; mod custom_widgets { @@ -5,15 +17,6 @@ mod custom_widgets { pub mod line; } -use std::env; -// This binary will only be built with the "iced" feature enabled, by use of "required-features" -// in Cargo.toml so no need for the feature to be used here for conditional compiling -use crate::gpio::{GPIOConfig, PinDescription, GPIO_DESCRIPTION}; -// Using Custom Widgets -use custom_widgets::{circle::circle, line::line}; -use iced::widget::{button, container, Column, Row, Text}; -use iced::{alignment, window, Alignment, Color, Element, Length, Sandbox, Settings, Theme}; - // Use Hardware via trait //use hw::Hardware; @@ -40,6 +43,7 @@ struct Gpio { config_file: Option, // filename where to load and save config file to/from gpio_description: [PinDescription; 40], gpio_config: GPIOConfig, + pub pin_function_selected: Vec>, clicked: bool, } @@ -47,6 +51,7 @@ struct Gpio { enum Message { Activate, + PinFunctionSelected(usize, PinFunction), } impl Sandbox for Gpio { @@ -78,10 +83,14 @@ impl Sandbox for Gpio { } }; + let num_pins = GPIO_DESCRIPTION.len(); + let pin_function_selected = vec![None; num_pins]; + Self { config_file, gpio_description: GPIO_DESCRIPTION, gpio_config, + pin_function_selected, clicked: false, } } @@ -93,11 +102,14 @@ impl Sandbox for Gpio { fn update(&mut self, message: Message) { match message { Message::Activate => self.clicked = true, + Message::PinFunctionSelected(pin_index, pin_function) => { + self.pin_function_selected[pin_index] = Some(pin_function); + } } } fn view(&self) -> iced::Element { - container(pin_view(&self.gpio_description, &self.gpio_config)) + container(pin_view(&self.gpio_description, &self.gpio_config, self)) .height(Length::Fill) .width(Length::Fill) .align_x(alignment::Horizontal::Center) @@ -117,10 +129,27 @@ impl Sandbox for Gpio { fn pin_view( pin_descriptions: &[PinDescription; 40], _pin_config: &GPIOConfig, + gpio: &Gpio, ) -> Element<'static, Message> { let mut column = Column::new().width(Length::Shrink).height(Length::Shrink); - for pair in pin_descriptions.chunks(2) { + for (idx, pair) in pin_descriptions.chunks(2).enumerate() { + let mut pin_option_left = Column::new() + .width(Length::Fixed(100f32)) + .align_items(Alignment::Center); + + if pair[0].options.len() > 1 { + let mut pin_options_row_left = Row::new().align_items(Alignment::Center); + + pin_options_row_left = pin_options_row_left.push(pick_list( + pair[0].options, + gpio.pin_function_selected[idx * 2], + move |pin_function| Message::PinFunctionSelected(idx * 2, pin_function), + )); + + pin_option_left = pin_option_left.push(pin_options_row_left); + } + let mut pin_name_left = Column::new() .width(Length::Fixed(55f32)) .align_items(Alignment::Center); @@ -373,13 +402,32 @@ fn pin_view( right_pin = right_pin.push(right_pin_row); } } + + let mut pin_option_right = Column::new() + .width(Length::Fixed(100f32)) + .align_items(Alignment::Center); + + if pair[1].options.len() > 1 { + let mut pin_options_row_right = Row::new().align_items(Alignment::Center); + + pin_options_row_right = pin_options_row_right.push(pick_list( + pair[1].options, + gpio.pin_function_selected[idx * 2 + 1], + move |pin_function| Message::PinFunctionSelected(idx * 2 + 1, pin_function), + )); + + pin_option_right = pin_option_right.push(pin_options_row_right); + } + let row = Row::new() + .push(pin_option_left) .push(pin_name_left) .push(pin_arrow_left) .push(left_pin) .push(right_pin) .push(pin_arrow_right) .push(pin_name_right) + .push(pin_option_right) .spacing(10) .align_items(Alignment::Center);