From 6e7a904ae98085342501005c43c871fa21a6e74b Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 20:24:41 -0400 Subject: [PATCH 1/9] add areas to LED display logic --- services/led_status_helper/Cargo.lock | 4 ++- services/led_status_helper/Cargo.toml | 2 +- services/led_status_helper/src/main.rs | 44 +++++++++++++++++++++----- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/services/led_status_helper/Cargo.lock b/services/led_status_helper/Cargo.lock index 4e0ec6bf..52c57310 100644 --- a/services/led_status_helper/Cargo.lock +++ b/services/led_status_helper/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "aho-corasick" version = "0.5.3" @@ -222,7 +224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "led_status_helper" -version = "0.2.0" +version = "0.3.0" dependencies = [ "dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "envy 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/services/led_status_helper/Cargo.toml b/services/led_status_helper/Cargo.toml index e59ed54f..89f4c4f5 100644 --- a/services/led_status_helper/Cargo.toml +++ b/services/led_status_helper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "led_status_helper" -version = "0.2.0" +version = "0.3.0" authors = ["Terkwood "] edition = "2018" diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index 26353186..ac05e71a 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -32,8 +32,26 @@ fn generate_mq_client_id() -> String { format!("led_status/{}", Uuid::new_v4()) } -fn get_num_tanks(conn: &redis::Connection, namespace: &str) -> Result { - conn.get(format!("{}/tanks", namespace)) +fn get_num_containers( + conn: &redis::Connection, + namespace: &str, + container: Container, +) -> Result { + conn.get(format!("{}/{}", namespace, container.to_string())) +} + +enum Container { + Tanks, + Areas, +} + +impl Container { + pub fn to_string(self) -> String { + match self { + Container::Tanks => "tanks".to_string(), + Container::Areas => "areas".to_string(), + } + } } struct Temp { @@ -79,7 +97,7 @@ fn c_to_f(temp_c: f64) -> f64 { temp_c * 1.8 + 32.0 } -fn get_temp_ph( +fn get_tank_data( conn: &redis::Connection, tank: i64, namespace: &str, @@ -140,11 +158,11 @@ fn generate_status( namespace: &str, staleness: &Staleness, ) -> Result { - let num_tanks = get_num_tanks(&conn, namespace)?; + let num_tanks = get_num_containers(&conn, namespace, Container::Tanks)?; - let status_results: Result, redis::RedisError> = (1..num_tanks + 1) + let tank_statuses: Result, redis::RedisError> = (1..num_tanks + 1) .map(move |tank| { - get_temp_ph(&conn, tank, namespace).map(move |(maybe_temp, maybe_ph)| { + get_tank_data(&conn, tank, namespace).map(move |(maybe_temp, maybe_ph)| { if let (&None, &None) = (&maybe_temp, &maybe_ph) { return "".to_string(); // nothing to format } @@ -173,7 +191,7 @@ fn generate_status( .map(move |ph| format!(" pH {}{}", ph.val, staleness.text(ph.update_time))) .unwrap_or("".to_string()); - let message = tank_string + &temp_string + &ph_string; + let message = tank_string + &ph_string + &temp_string; // finally, right-align the message so it lays out nicely on the LEDs let l = message.to_string().len(); @@ -190,7 +208,17 @@ fn generate_status( }) .collect(); - status_results.map(|ss| ss.join(" ")) + let tank_portion = tank_statuses.map(|ss| ss.join(" ")); + + let num_areas = get_num_containers(&conn, namespace, Container::Areas)?; + + let area_statuses: Result, redis::RedisError> = (1..num_areas + 1) + .map(move |area| unimplemented!()) + .collect(); + + let area_portion = area_statuses.map(|ss| ss.join(" ")); + + tank_portion.and_then(|tp| area_portion.map(|ap| ap + " " + &tp)) } fn main() { From 4b63561ac0c5a68d92e9d254d2af348ab9a026a9 Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 20:49:13 -0400 Subject: [PATCH 2/9] deal with areas, refactor led status --- services/led_status_helper/src/main.rs | 111 ++++++++++++++++++++----- 1 file changed, 90 insertions(+), 21 deletions(-) diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index ac05e71a..793c97a3 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -60,6 +60,16 @@ struct Temp { update_time: Option, } +/// Digital humidity and temp, e.g. DHT11 sensor +struct DHT { + humidity: f64, + temp_f: f64, + temp_c: f64, + heat_index_f: f64, + heat_index_c: f64, + update_time: Option, +} + struct PH { val: f64, update_time: Option, @@ -97,6 +107,51 @@ fn c_to_f(temp_c: f64) -> f64 { temp_c * 1.8 + 32.0 } +const NAN: f64 = -255.0; +fn get_area_data( + conn: &redis::Connection, + area: i64, + namespace: &str, +) -> Result, redis::RedisError> { + let numbers: Vec> = conn.hget( + format!("{}/areas/{}", namespace, area), + vec![ + "humidity", + "temp_f", + "temp_c", + "heat_index_f", + "heat_index_c", + ], + )?; + + let update_time_vec: Vec> = conn.hget( + format!("{}/areas/{}", namespace, area), + vec!["dht_update_time"], + )?; + + let (humidity, init_temp_f, init_temp_c, heat_index_f, heat_index_c) = ( + numbers.get(0), + numbers.get(1), + numbers.get(2), + numbers.get(3), + numbers.get(4), + ); + let update_time = unnest_ref(update_time_vec.get(0)); + + let temp = safe_temp(init_temp_f, init_temp_c, update_time); + + let (temp_f, temp_c) = temp.map(|t| (t.f, t.c)).unwrap_or((NAN, NAN)); + + Ok(Some(DHT { + humidity: unnest_ref(humidity).unwrap_or(NAN), + temp_f, + temp_c, + heat_index_f: unnest_ref(heat_index_f).unwrap_or(NAN), + heat_index_c: unnest_ref(heat_index_c).unwrap_or(NAN), + update_time, + })) +} + fn get_tank_data( conn: &redis::Connection, tank: i64, @@ -115,7 +170,21 @@ fn get_tank_data( unnest_ref(update_times.get(0)), unnest_ref(update_times.get(1)), ); - let temp = match (temp_f, temp_c) { + let temp = safe_temp(temp_f, temp_c, temp_update_time); + let ph = unnest_ref(numbers.get(2)).map(|val| PH { + val, + update_time: ph_update_time, + }); + + Ok((temp, ph)) +} + +fn safe_temp( + temp_f: Option<&Option>, + temp_c: Option<&Option>, + temp_update_time: Option, +) -> Option { + match (temp_f, temp_c) { (Some(&Some(f)), Some(&Some(c))) => Some(Temp { f, c, @@ -132,13 +201,7 @@ fn get_tank_data( update_time: temp_update_time, }), _ => None, - }; - let ph = unnest_ref(numbers.get(2)).map(|val| PH { - val, - update_time: ph_update_time, - }); - - Ok((temp, ph)) + } } fn unnest_ref(a: Option<&Option>) -> Option @@ -167,7 +230,7 @@ fn generate_status( return "".to_string(); // nothing to format } - let tank_string = format!("#{}:", tank); + let tank_string = format!("T{}:", tank); let temp_string = maybe_temp .map(move |t| { ( @@ -193,17 +256,8 @@ fn generate_status( let message = tank_string + &ph_string + &temp_string; - // finally, right-align the message so it lays out nicely on the LEDs - let l = message.to_string().len(); - if l <= 16 { - format!("{: >16}", message) - } else if l <= 32 { - format!("{: >32}", message) - } else if l <= 64 { - format!("{: >64}", message) - } else { - format!("{: >128}", message) - } + // lay out the message nicely + right_align(&message) }) }) .collect(); @@ -213,7 +267,9 @@ fn generate_status( let num_areas = get_num_containers(&conn, namespace, Container::Areas)?; let area_statuses: Result, redis::RedisError> = (1..num_areas + 1) - .map(move |area| unimplemented!()) + .map(move |area| { + get_area_data(&conn, area, namespace).map(move |maybe_dht| unimplemented!()) + }) .collect(); let area_portion = area_statuses.map(|ss| ss.join(" ")); @@ -221,6 +277,19 @@ fn generate_status( tank_portion.and_then(|tp| area_portion.map(|ap| ap + " " + &tp)) } +fn right_align(message: &str) -> String { + let l = message.to_string().len(); + if l <= 16 { + format!("{: >16}", message) + } else if l <= 32 { + format!("{: >32}", message) + } else if l <= 64 { + format!("{: >64}", message) + } else { + format!("{: >128}", message) + } +} + fn main() { dotenv::dotenv().expect("Unable to load .env file"); From 2266dea978cfe876be610088eb4ff31d65e77205 Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 21:00:24 -0400 Subject: [PATCH 3/9] get area data --- services/led_status_helper/src/main.rs | 43 +++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index 793c97a3..88edaa50 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -268,7 +268,48 @@ fn generate_status( let area_statuses: Result, redis::RedisError> = (1..num_areas + 1) .map(move |area| { - get_area_data(&conn, area, namespace).map(move |maybe_dht| unimplemented!()) + get_area_data(&conn, area, namespace).map(move |maybe_dht| { + if let &None = &maybe_dht { + return "".to_string(); // nothing to format + } + + let area_string = format!("A{}:", area); + + let data_string = maybe_dht + .map(move |dht| { + ( + dht.humidity, + match temp_unit { + 'c' | 'C' => dht.temp_c, + _ => dht.temp_f, + }, + match temp_unit { + 'c' | 'C' => dht.heat_index_c, + _ => dht.heat_index_f, + }, + dht.update_time, + ) + }) + .map(|(humidity, temp, heat_index, update_time)| { + let stale = staleness.text(update_time); + let temp_letter = temp_unit.to_ascii_uppercase(); + format!( + "{}H%{} {}°{}{} {}h{}{}", + humidity, + stale, + temp, + temp_letter, + stale, + heat_index, + temp_letter, + stale, + ) + }) + .unwrap_or("".to_string()); + + let message = area_string + &data_string; + right_align(&message) + }) }) .collect(); From 53d6384b0fcb8e645a846d757230ddb5aedbc392 Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 21:05:25 -0400 Subject: [PATCH 4/9] right align once --- services/led_status_helper/src/main.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index 88edaa50..a0686bc3 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -254,10 +254,7 @@ fn generate_status( .map(move |ph| format!(" pH {}{}", ph.val, staleness.text(ph.update_time))) .unwrap_or("".to_string()); - let message = tank_string + &ph_string + &temp_string; - - // lay out the message nicely - right_align(&message) + tank_string + &ph_string + &temp_string }) }) .collect(); @@ -307,15 +304,16 @@ fn generate_status( }) .unwrap_or("".to_string()); - let message = area_string + &data_string; - right_align(&message) + area_string + &data_string }) }) .collect(); let area_portion = area_statuses.map(|ss| ss.join(" ")); - tank_portion.and_then(|tp| area_portion.map(|ap| ap + " " + &tp)) + tank_portion + .and_then(|tp| area_portion.map(|ap| ap + " " + &tp)) // join areas and tanks + .map(|msg| right_align(&msg)) // lay out the message nicely } fn right_align(message: &str) -> String { From 34050195615e88afa8cfc9d1a24ac37152f1d701 Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 21:18:49 -0400 Subject: [PATCH 5/9] deal with strings and clone things --- services/led_status_helper/src/main.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index a0686bc3..741bb3ca 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -124,7 +124,7 @@ fn get_area_data( ], )?; - let update_time_vec: Vec> = conn.hget( + let update_time_vec: Vec> = conn.hget( format!("{}/areas/{}", namespace, area), vec!["dht_update_time"], )?; @@ -136,7 +136,8 @@ fn get_area_data( numbers.get(3), numbers.get(4), ); - let update_time = unnest_ref(update_time_vec.get(0)); + let update_time = + cloning_unnest_ref(update_time_vec.get(0)).map(|s| s.parse::().unwrap_or(0)); let temp = safe_temp(init_temp_f, init_temp_c, update_time); @@ -204,6 +205,17 @@ fn safe_temp( } } +fn cloning_unnest_ref(a: Option<&Option>) -> Option +where + A: Clone, +{ + match a.map(|aa| aa.clone()) { + Some(Some(thing)) => Some(thing), + Some(None) => None, + None => None, + } +} + fn unnest_ref(a: Option<&Option>) -> Option where A: Copy, From 95e5323506e2e383392eb31b9c1a9255a3d240ae Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 21:23:38 -0400 Subject: [PATCH 6/9] redis string --- services/led_status_helper/src/main.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index 741bb3ca..58c0f349 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -124,7 +124,7 @@ fn get_area_data( ], )?; - let update_time_vec: Vec> = conn.hget( + let update_time_vec: Option = conn.hget( format!("{}/areas/{}", namespace, area), vec!["dht_update_time"], )?; @@ -136,8 +136,8 @@ fn get_area_data( numbers.get(3), numbers.get(4), ); - let update_time = - cloning_unnest_ref(update_time_vec.get(0)).map(|s| s.parse::().unwrap_or(0)); + + let update_time = update_time_vec.map(|s| s.parse::().unwrap_or(0)); let temp = safe_temp(init_temp_f, init_temp_c, update_time); @@ -205,17 +205,6 @@ fn safe_temp( } } -fn cloning_unnest_ref(a: Option<&Option>) -> Option -where - A: Clone, -{ - match a.map(|aa| aa.clone()) { - Some(Some(thing)) => Some(thing), - Some(None) => None, - None => None, - } -} - fn unnest_ref(a: Option<&Option>) -> Option where A: Copy, From b737ed2bf220521c2057464e71ee5a544f229b59 Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 21:27:15 -0400 Subject: [PATCH 7/9] add space --- services/led_status_helper/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index 58c0f349..e548594f 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -124,6 +124,7 @@ fn get_area_data( ], )?; + // A redis string let update_time_vec: Option = conn.hget( format!("{}/areas/{}", namespace, area), vec!["dht_update_time"], @@ -271,7 +272,7 @@ fn generate_status( return "".to_string(); // nothing to format } - let area_string = format!("A{}:", area); + let area_string = format!("A{}: ", area); let data_string = maybe_dht .map(move |dht| { From d4ea1c1c37bb9d9c14b9e22d78b35d66b3f4cbbe Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 21:27:54 -0400 Subject: [PATCH 8/9] reverse chars --- services/led_status_helper/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index e548594f..bfcb1cd4 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -293,7 +293,7 @@ fn generate_status( let stale = staleness.text(update_time); let temp_letter = temp_unit.to_ascii_uppercase(); format!( - "{}H%{} {}°{}{} {}h{}{}", + "{}%H{} {}°{}{} {}h{}{}", humidity, stale, temp, From b48dad78543030a62b54d5585b84d558df9e19d8 Mon Sep 17 00:00:00 2001 From: Terkwood Date: Wed, 3 Jul 2019 21:31:28 -0400 Subject: [PATCH 9/9] more formatting --- services/led_status_helper/src/main.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/led_status_helper/src/main.rs b/services/led_status_helper/src/main.rs index bfcb1cd4..70fdc306 100644 --- a/services/led_status_helper/src/main.rs +++ b/services/led_status_helper/src/main.rs @@ -324,10 +324,16 @@ fn right_align(message: &str) -> String { format!("{: >16}", message) } else if l <= 32 { format!("{: >32}", message) + } else if l <= 48 { + format!("{: >48}", message) } else if l <= 64 { format!("{: >64}", message) - } else { + } else if l <= 96 { + format!("{: >96}", message) + } else if l <= 128 { format!("{: >128}", message) + } else { + format!("{: >256}", message) } }