Rename active_schedule to override_schedule and add EmgauwaNow

This commit is contained in:
Tobias Reisinger 2024-05-26 22:48:22 +02:00
parent 9326b66007
commit 473832f58a
Signed by: serguzim
GPG key ID: 13AD60C237A28DFE
10 changed files with 108 additions and 28 deletions

BIN
Cargo.lock generated

Binary file not shown.

View file

@ -13,6 +13,7 @@ actix-web-actors = "4.2"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
serde_with = "3.8"
simple_logger = "5.0" simple_logger = "5.0"
log = "0.4" log = "0.4"

View file

@ -1,10 +1,10 @@
use chrono::{NaiveTime, Timelike}; use chrono::{NaiveTime, Timelike};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{Decode, Encode, Sqlite, Type};
use sqlx::database::HasArguments; use sqlx::database::HasArguments;
use sqlx::encode::IsNull; use sqlx::encode::IsNull;
use sqlx::error::BoxDynError; use sqlx::error::BoxDynError;
use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef}; use sqlx::sqlite::{SqliteTypeInfo, SqliteValueRef};
use sqlx::{Decode, Encode, Sqlite, Type};
use crate::db::DbPeriods; use crate::db::DbPeriods;
@ -67,12 +67,12 @@ impl Period {
let start_before_end = self.start.lt(&self.end); let start_before_end = self.start.lt(&self.end);
match (start_after_now, end_after_now, start_before_end) { match (start_after_now, end_after_now, start_before_end) {
(false, false, true) => false, // both before now; start before end means "normal" period before now (false, false, true) => false, // both before now; start before end means "normal" period before now
(false, false, false) => true, // both before now; end before start means "inversed" period around now (false, false, false) => true, // both before now; end before start means "inversed" period around now
(true, false, _) => false, // only start after now (true, false, _) => false, // only start after now
(false, true, _) => true, // only end after now (false, true, _) => true, // only end after now
(true, true, true) => false, // both after now but start first (true, true, true) => false, // both after now but start first
(true, true, false) => true, // both after now but end first (true, true, false) => true, // both after now but end first
} }
} }

View file

@ -47,7 +47,9 @@ impl From<&EmgauwaError> for String {
EmgauwaError::Database(err) => String::from(err), EmgauwaError::Database(err) => String::from(err),
EmgauwaError::Uid(_) => String::from("the uid is in a bad format"), EmgauwaError::Uid(_) => String::from("the uid is in a bad format"),
EmgauwaError::Internal(_) => String::from("internal error"), EmgauwaError::Internal(_) => String::from("internal error"),
EmgauwaError::Connection(uid) => format!("unable to connect to controller with uid: {}", uid), EmgauwaError::Connection(uid) => {
format!("unable to connect to controller with uid: {}", uid)
}
EmgauwaError::Other(err) => format!("other error: {}", err), EmgauwaError::Other(err) => format!("other error: {}", err),
EmgauwaError::Hardware(err) => format!("hardware error: {}", err), EmgauwaError::Hardware(err) => format!("hardware error: {}", err),
} }

View file

@ -10,7 +10,7 @@ use sqlx::Sqlite;
use crate::db::DbController; use crate::db::DbController;
use crate::errors::{DatabaseError, EmgauwaError}; use crate::errors::{DatabaseError, EmgauwaError};
use crate::models::{convert_db_list_cache, FromDbModel, Relay}; use crate::models::{convert_db_list_cache, FromDbModel, Relay};
use crate::types::RelayStates; use crate::types::{EmgauwaNow, RelayState, RelayStates};
#[derive(Serialize, Deserialize, Debug, Clone, MessageResponse)] #[derive(Serialize, Deserialize, Debug, Clone, MessageResponse)]
pub struct Controller { pub struct Controller {
@ -57,19 +57,29 @@ impl Controller {
self.relays self.relays
.iter_mut() .iter_mut()
.zip(relay_states.iter()) .zip(relay_states.iter())
.for_each(|(relay, is_on)| { .for_each(|(relay, state)| {
relay.is_on = *is_on; relay.active_schedule = state.active_schedule.clone();
relay.is_on = state.is_on;
}); });
} }
pub fn get_relay_states(&self) -> RelayStates { pub fn get_relay_states(&self) -> RelayStates {
self.relays.iter().map(|r| r.is_on).collect()
}
pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
self.relays self.relays
.iter() .iter()
.filter_map(|r| r.active_schedule.get_next_time(now)) .map(|r| RelayState {
active_schedule: r.active_schedule.clone(),
is_on: r.is_on,
})
.collect()
}
pub fn check_next_time(&mut self, now: &EmgauwaNow) -> Option<NaiveTime> {
self.relays
.iter_mut()
.filter_map(|r| {
r.reload_active_schedule(now.weekday);
r.active_schedule.get_next_time(&now.time)
})
.min() .min()
} }

View file

@ -9,7 +9,8 @@ use sqlx::Sqlite;
use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule}; use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
use crate::errors::DatabaseError; use crate::errors::DatabaseError;
use crate::models::FromDbModel; use crate::models::FromDbModel;
use crate::types::EmgauwaUid; use crate::types::{EmgauwaUid, Weekday};
use crate::utils;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Relay { pub struct Relay {
@ -19,12 +20,23 @@ pub struct Relay {
pub controller_id: EmgauwaUid, pub controller_id: EmgauwaUid,
pub schedules: Vec<DbSchedule>, pub schedules: Vec<DbSchedule>,
pub active_schedule: DbSchedule, pub active_schedule: DbSchedule,
#[serde(
default, // <- important for deserialization
skip_serializing_if = "Option::is_none", // <- important for serialization
with = "::serde_with::rust::double_option",
)]
pub override_schedule: Option<Option<DbSchedule>>,
pub is_on: Option<bool>, pub is_on: Option<bool>,
pub tags: Vec<String>, pub tags: Vec<String>,
// for internal use only. // for internal use only.
#[serde(skip)] #[serde(skip)]
pub pulsing: Option<Instant>, pub pulsing: Option<Instant>,
#[serde(
skip,
default = "utils::get_weekday",
)]
pub override_schedule_weekday: Weekday,
} }
impl FromDbModel for Relay { impl FromDbModel for Relay {
@ -58,9 +70,11 @@ impl FromDbModel for Relay {
controller_id, controller_id,
schedules, schedules,
active_schedule, active_schedule,
override_schedule: None,
is_on, is_on,
tags, tags,
pulsing: None, pulsing: None,
override_schedule_weekday: Weekday::default(),
}) })
} }
} }
@ -69,16 +83,9 @@ impl Relay {
pub fn reload(&mut self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> { pub fn reload(&mut self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
self.r = block_on(self.r.reload(conn))?; self.r = block_on(self.r.reload(conn))?;
self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?; self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?;
self.reload_active_schedule(conn)?;
Ok(()) self.reload_active_schedule(utils::get_weekday());
}
pub fn reload_active_schedule(
&mut self,
conn: &mut PoolConnection<Sqlite>,
) -> Result<(), DatabaseError> {
self.active_schedule = block_on(self.r.get_active_schedule(conn))?;
Ok(()) Ok(())
} }
@ -103,4 +110,25 @@ impl Relay {
None => None, None => None,
} }
} }
pub fn reload_active_schedule(&mut self, weekday: Weekday) {
if let Some((Some(schedule), schedule_weekday)) = self.unwrap_override_schedule() {
if schedule_weekday == weekday {
self.active_schedule = schedule.clone();
return;
}
if schedule_weekday != weekday {
self.override_schedule = None;
}
}
self.active_schedule = self.schedules.get(weekday as usize).unwrap().clone()
}
pub fn unwrap_override_schedule(&self) -> Option<(&Option<DbSchedule>, Weekday)> {
if let Some(schedule) = &self.override_schedule {
return Some((schedule, self.override_schedule_weekday));
}
None
}
} }

26
src/types/emgauwa_now.rs Normal file
View file

@ -0,0 +1,26 @@
use std::time::Instant;
use chrono::{Local, NaiveTime, Timelike};
use crate::types::Weekday;
use crate::utils;
pub struct EmgauwaNow {
pub time: NaiveTime,
pub instant: Instant,
pub weekday: Weekday,
}
impl EmgauwaNow {
pub fn now() -> EmgauwaNow {
EmgauwaNow {
time: Local::now().time(),
instant: Instant::now(),
weekday: utils::get_weekday(),
}
}
pub fn time_in_s(&self) -> u32 {
self.time.num_seconds_from_midnight()
}
}

View file

@ -1,8 +1,10 @@
mod emgauwa_now;
mod emgauwa_uid; mod emgauwa_uid;
mod request; mod request;
mod schedule_uid; mod schedule_uid;
use actix::Message; use actix::Message;
pub use emgauwa_now::EmgauwaNow;
pub use emgauwa_uid::EmgauwaUid; pub use emgauwa_uid::EmgauwaUid;
pub use request::*; pub use request::*;
pub use schedule_uid::ScheduleUid; pub use schedule_uid::ScheduleUid;
@ -14,7 +16,13 @@ use crate::models::{Controller, Relay};
pub type Weekday = i64; pub type Weekday = i64;
pub type RelayStates = Vec<Option<bool>>; #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelayState {
pub active_schedule: DbSchedule,
pub is_on: Option<bool>
}
pub type RelayStates = Vec<RelayState>;
#[derive(Debug, Serialize, Deserialize, Message)] #[derive(Debug, Serialize, Deserialize, Message)]
#[rtype(result = "Result<(), EmgauwaError>")] #[rtype(result = "Result<(), EmgauwaError>")]

View file

@ -23,7 +23,12 @@ pub struct RequestScheduleUpdate {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct RequestRelayUpdate { pub struct RequestRelayUpdate {
pub name: Option<String>, pub name: Option<String>,
pub active_schedule: Option<RequestScheduleId>, #[serde(
default, // <- important for deserialization
skip_serializing_if = "Option::is_none", // <- important for serialization
with = "::serde_with::rust::double_option",
)]
pub override_schedule: Option<Option<RequestScheduleId>>,
pub schedules: Option<Vec<RequestScheduleId>>, pub schedules: Option<Vec<RequestScheduleId>>,
pub tags: Option<Vec<String>>, pub tags: Option<Vec<String>>,
} }

View file

@ -103,7 +103,7 @@ pub fn get_weekday() -> Weekday {
pub fn printable_relay_states(relay_states: &RelayStates) -> String { pub fn printable_relay_states(relay_states: &RelayStates) -> String {
let mut relay_debug = String::new(); let mut relay_debug = String::new();
relay_states.iter().for_each(|state| { relay_states.iter().for_each(|state| {
relay_debug.push_str(match state { relay_debug.push_str(match state.is_on {
Some(true) => "+", Some(true) => "+",
Some(false) => "-", Some(false) => "-",
None => "?", None => "?",