Compare commits

...

7 commits

27 changed files with 197 additions and 102 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

@ -0,0 +1 @@
DROP VIEW v_relays;

View file

@ -0,0 +1,8 @@
CREATE VIEW v_relays
AS
SELECT
relays.*,
controllers.uid AS controller_uid
FROM
relays
INNER JOIN controllers ON controllers.id = relays.controller_id;

View file

@ -158,7 +158,7 @@ impl DbController {
) -> Result<Vec<DbRelay>, DatabaseError> { ) -> Result<Vec<DbRelay>, DatabaseError> {
sqlx::query_as!( sqlx::query_as!(
DbRelay, DbRelay,
"SELECT * FROM relays WHERE controller_id = ?", "SELECT * FROM v_relays WHERE v_relays.controller_id = ?",
self.id self.id
) )
.fetch_all(conn.deref_mut()) .fetch_all(conn.deref_mut())

View file

@ -5,11 +5,10 @@ use sqlx::Sqlite;
use crate::db::{DbRelay, DbSchedule}; use crate::db::{DbRelay, DbSchedule};
use crate::errors::DatabaseError; use crate::errors::DatabaseError;
use crate::types::Weekday;
pub struct DbJunctionRelaySchedule { pub struct DbJunctionRelaySchedule {
pub id: i64, pub id: i64,
pub weekday: Weekday, pub weekday: i64,
pub relay_id: i64, pub relay_id: i64,
pub schedule_id: i64, pub schedule_id: i64,
} }
@ -32,7 +31,7 @@ impl DbJunctionRelaySchedule {
pub async fn get_junction_by_relay_and_weekday( pub async fn get_junction_by_relay_and_weekday(
conn: &mut PoolConnection<Sqlite>, conn: &mut PoolConnection<Sqlite>,
relay: &DbRelay, relay: &DbRelay,
weekday: Weekday, weekday: i64,
) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> { ) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
sqlx::query_as!( sqlx::query_as!(
DbJunctionRelaySchedule, DbJunctionRelaySchedule,
@ -51,8 +50,8 @@ impl DbJunctionRelaySchedule {
) -> Result<Vec<DbRelay>, DatabaseError> { ) -> Result<Vec<DbRelay>, DatabaseError> {
sqlx::query_as!( sqlx::query_as!(
DbRelay, DbRelay,
r#"SELECT relays.* FROM relays INNER JOIN junction_relay_schedule r#"SELECT v_relays.* FROM v_relays INNER JOIN junction_relay_schedule
ON junction_relay_schedule.relay_id = relays.id ON junction_relay_schedule.relay_id = v_relays.id
WHERE junction_relay_schedule.schedule_id = ? WHERE junction_relay_schedule.schedule_id = ?
ORDER BY junction_relay_schedule.weekday"#, ORDER BY junction_relay_schedule.weekday"#,
schedule.id schedule.id
@ -65,7 +64,7 @@ impl DbJunctionRelaySchedule {
pub async fn get_schedule( pub async fn get_schedule(
conn: &mut PoolConnection<Sqlite>, conn: &mut PoolConnection<Sqlite>,
relay: &DbRelay, relay: &DbRelay,
weekday: Weekday, weekday: i64,
) -> Result<Option<DbSchedule>, DatabaseError> { ) -> Result<Option<DbSchedule>, DatabaseError> {
sqlx::query_as!( sqlx::query_as!(
DbSchedule, DbSchedule,
@ -101,7 +100,7 @@ impl DbJunctionRelaySchedule {
conn: &mut PoolConnection<Sqlite>, conn: &mut PoolConnection<Sqlite>,
relay: &DbRelay, relay: &DbRelay,
schedule: &DbSchedule, schedule: &DbSchedule,
weekday: Weekday, weekday: i64,
) -> Result<DbJunctionRelaySchedule, DatabaseError> { ) -> Result<DbJunctionRelaySchedule, DatabaseError> {
match Self::get_junction_by_relay_and_weekday(conn, relay, weekday).await? { match Self::get_junction_by_relay_and_weekday(conn, relay, weekday).await? {
None => sqlx::query_as!( None => sqlx::query_as!(
@ -139,7 +138,7 @@ impl DbJunctionRelaySchedule {
schedules: Vec<&DbSchedule>, schedules: Vec<&DbSchedule>,
) -> Result<(), DatabaseError> { ) -> Result<(), DatabaseError> {
for (weekday, schedule) in schedules.iter().enumerate() { for (weekday, schedule) in schedules.iter().enumerate() {
Self::set_schedule(conn, relay, schedule, weekday as Weekday).await?; Self::set_schedule(conn, relay, schedule, weekday as i64).await?;
} }
Ok(()) Ok(())
} }

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

@ -4,10 +4,9 @@ use serde_derive::{Deserialize, Serialize};
use sqlx::pool::PoolConnection; use sqlx::pool::PoolConnection;
use sqlx::Sqlite; use sqlx::Sqlite;
use crate::db::{DbController, DbJunctionRelaySchedule, DbJunctionTag, DbSchedule, DbTag}; use crate::db::{DbController, DbJunctionTag, DbTag};
use crate::errors::DatabaseError; use crate::errors::DatabaseError;
use crate::types::Weekday; use crate::types::EmgauwaUid;
use crate::utils;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbRelay { pub struct DbRelay {
@ -15,13 +14,15 @@ pub struct DbRelay {
pub id: i64, pub id: i64,
pub name: String, pub name: String,
pub number: i64, pub number: i64,
#[serde(rename = "controller_id")]
pub controller_uid: EmgauwaUid,
#[serde(skip)] #[serde(skip)]
pub controller_id: i64, pub controller_id: i64,
} }
impl DbRelay { impl DbRelay {
pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbRelay>, DatabaseError> { pub async fn get_all(conn: &mut PoolConnection<Sqlite>) -> Result<Vec<DbRelay>, DatabaseError> {
sqlx::query_as!(DbRelay, "SELECT * FROM relays") sqlx::query_as!(DbRelay, "SELECT * FROM v_relays")
.fetch_all(conn.deref_mut()) .fetch_all(conn.deref_mut())
.await .await
.map_err(DatabaseError::from) .map_err(DatabaseError::from)
@ -31,7 +32,7 @@ impl DbRelay {
conn: &mut PoolConnection<Sqlite>, conn: &mut PoolConnection<Sqlite>,
id: i64, id: i64,
) -> Result<Option<DbRelay>, DatabaseError> { ) -> Result<Option<DbRelay>, DatabaseError> {
sqlx::query_as!(DbRelay, "SELECT * FROM relays WHERE id = ?", id) sqlx::query_as!(DbRelay, "SELECT * FROM v_relays WHERE v_relays.id = ?", id)
.fetch_optional(conn.deref_mut()) .fetch_optional(conn.deref_mut())
.await .await
.map_err(DatabaseError::from) .map_err(DatabaseError::from)
@ -44,7 +45,7 @@ impl DbRelay {
) -> Result<Option<DbRelay>, DatabaseError> { ) -> Result<Option<DbRelay>, DatabaseError> {
sqlx::query_as!( sqlx::query_as!(
DbRelay, DbRelay,
"SELECT * FROM relays WHERE controller_id = ? AND number = ?", "SELECT * FROM v_relays WHERE v_relays.controller_id = ? AND v_relays.number = ?",
controller.id, controller.id,
number number
) )
@ -72,7 +73,7 @@ impl DbRelay {
conn: &mut PoolConnection<Sqlite>, conn: &mut PoolConnection<Sqlite>,
tag: &DbTag, tag: &DbTag,
) -> Result<Vec<DbRelay>, DatabaseError> { ) -> Result<Vec<DbRelay>, DatabaseError> {
sqlx::query_as!(DbRelay, "SELECT relay.* FROM relays AS relay INNER JOIN junction_tag ON junction_tag.relay_id = relay.id WHERE junction_tag.tag_id = ?", tag.id) sqlx::query_as!(DbRelay, "SELECT v_relays.* FROM v_relays INNER JOIN junction_tag ON junction_tag.relay_id = v_relays.id WHERE junction_tag.tag_id = ?", tag.id)
.fetch_all(conn.deref_mut()) .fetch_all(conn.deref_mut())
.await .await
.map_err(DatabaseError::from) .map_err(DatabaseError::from)
@ -84,16 +85,25 @@ impl DbRelay {
new_number: i64, new_number: i64,
new_controller: &DbController, new_controller: &DbController,
) -> Result<DbRelay, DatabaseError> { ) -> Result<DbRelay, DatabaseError> {
sqlx::query_as!( let result = sqlx::query!(
DbRelay, "INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?)",
"INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?) RETURNING *",
new_name, new_name,
new_number, new_number,
new_controller.id, new_controller.id,
) )
.fetch_optional(conn.deref_mut()) .execute(conn.deref_mut())
.await? .await?;
.ok_or(DatabaseError::InsertGetError)
let last_insert_id = result.last_insert_rowid();
sqlx::query_as!(
DbRelay,
"SELECT * FROM v_relays WHERE id = ?",
last_insert_id
)
.fetch_one(conn.deref_mut())
.await
.map_err(DatabaseError::from)
} }
pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> { pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
@ -163,14 +173,4 @@ impl DbRelay {
.await? .await?
.ok_or(DatabaseError::NotFound) .ok_or(DatabaseError::NotFound)
} }
pub async fn get_active_schedule(
&self,
conn: &mut PoolConnection<Sqlite>,
) -> Result<DbSchedule, DatabaseError> {
let weekday = utils::get_weekday();
DbJunctionRelaySchedule::get_schedule(conn, self, weekday as Weekday)
.await?
.ok_or(DatabaseError::NotFound)
}
} }

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

@ -9,8 +9,8 @@ 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, 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 {
@ -28,7 +28,7 @@ impl FromDbModel for Controller {
db_model: Self::DbModel, db_model: Self::DbModel,
) -> Result<Self, DatabaseError> { ) -> Result<Self, DatabaseError> {
let relays_db = block_on(db_model.get_relays(conn))?; let relays_db = block_on(db_model.get_relays(conn))?;
let cache = convert_db_list_cache(conn, relays_db, db_model.clone())?; let cache = convert_db_list(conn, relays_db)?;
Self::from_db_model_cache(conn, db_model, cache) Self::from_db_model_cache(conn, db_model, cache)
} }
@ -57,19 +57,20 @@ 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.apply_state(state));
relay.is_on = *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() self.relays.iter().map(RelayState::from).collect()
} }
pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> { pub fn check_next_time(&mut self, now: &EmgauwaNow) -> Option<NaiveTime> {
self.relays self.relays
.iter() .iter_mut()
.filter_map(|r| r.active_schedule.get_next_time(now)) .filter_map(|r| {
r.reload_active_schedule(now.weekday);
r.get_next_time(&now.time)
})
.min() .min()
} }
@ -80,6 +81,8 @@ impl Controller {
.find(|r| r.r.number == relay_num) .find(|r| r.r.number == relay_num)
.ok_or(EmgauwaError::Other(String::from("Relay not found")))?; .ok_or(EmgauwaError::Other(String::from("Relay not found")))?;
log::debug!("Pulsing relay {} until {:?}", relay_num, until);
relay.pulsing = Some(until); relay.pulsing = Some(until);
Ok(()) Ok(())
} }

View file

@ -3,13 +3,13 @@ use serde_derive::{Deserialize, Serialize};
use sqlx::pool::PoolConnection; use sqlx::pool::PoolConnection;
use sqlx::Sqlite; use sqlx::Sqlite;
use crate::db::{DbJunctionRelaySchedule, DbMacroAction}; use crate::db::{DbJunctionRelaySchedule, DbMacroAction, DbSchedule};
use crate::errors::{DatabaseError, EmgauwaError}; use crate::errors::{DatabaseError, EmgauwaError};
use crate::models::{FromDbModel, Relay, Schedule}; use crate::models::{FromDbModel, Relay};
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MacroAction { pub struct MacroAction {
pub schedule: Schedule, pub schedule: DbSchedule,
pub relay: Relay, pub relay: Relay,
pub weekday: i64, pub weekday: i64,
} }
@ -30,8 +30,7 @@ impl FromDbModel for MacroAction {
db_model: Self::DbModel, db_model: Self::DbModel,
_cache: Self::DbModelCache, _cache: Self::DbModelCache,
) -> Result<Self, DatabaseError> { ) -> Result<Self, DatabaseError> {
let schedule_db = block_on(db_model.get_schedule(conn))?; let schedule = block_on(db_model.get_schedule(conn))?;
let schedule = Schedule::from_db_model(conn, schedule_db)?;
let relay_db = block_on(db_model.get_relay(conn))?; let relay_db = block_on(db_model.get_relay(conn))?;
let relay = Relay::from_db_model(conn, relay_db)?; let relay = Relay::from_db_model(conn, relay_db)?;
@ -48,7 +47,7 @@ impl FromDbModel for MacroAction {
impl MacroAction { impl MacroAction {
pub async fn execute(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), EmgauwaError> { pub async fn execute(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), EmgauwaError> {
DbJunctionRelaySchedule::set_schedule(conn, &self.relay.r, &self.schedule.s, self.weekday) DbJunctionRelaySchedule::set_schedule(conn, &self.relay.r, &self.schedule, self.weekday)
.await?; .await?;
Ok(()) Ok(())
} }

View file

@ -1,66 +1,65 @@
use std::time::Instant; use std::time::Instant;
use chrono::NaiveTime; use chrono::{NaiveTime, Weekday};
use futures::executor::block_on; use futures::executor::block_on;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use sqlx::pool::PoolConnection; use sqlx::pool::PoolConnection;
use sqlx::Sqlite; use sqlx::Sqlite;
use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule}; use crate::db::{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::RelayState;
use crate::utils;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Relay { pub struct Relay {
#[serde(flatten)] #[serde(flatten)]
pub r: DbRelay, pub r: DbRelay,
pub controller: DbController,
pub controller_id: EmgauwaUid,
pub schedules: Vec<DbSchedule>, pub schedules: Vec<DbSchedule>,
pub active_schedule: DbSchedule, pub active_schedule: Option<DbSchedule>,
pub override_schedule: 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::default_weekday")]
pub override_schedule_weekday: Weekday,
} }
impl FromDbModel for Relay { impl FromDbModel for Relay {
type DbModel = DbRelay; type DbModel = DbRelay;
type DbModelCache = DbController; type DbModelCache = ();
fn from_db_model( fn from_db_model(
conn: &mut PoolConnection<Sqlite>, conn: &mut PoolConnection<Sqlite>,
db_model: Self::DbModel, db_model: Self::DbModel,
) -> Result<Self, DatabaseError> { ) -> Result<Self, DatabaseError> {
let cache = block_on(db_model.get_controller(conn))?; Self::from_db_model_cache(conn, db_model, ())
Self::from_db_model_cache(conn, db_model, cache)
} }
fn from_db_model_cache( fn from_db_model_cache(
conn: &mut PoolConnection<Sqlite>, conn: &mut PoolConnection<Sqlite>,
db_model: Self::DbModel, db_model: Self::DbModel,
cache: Self::DbModelCache, _cache: Self::DbModelCache,
) -> Result<Self, DatabaseError> { ) -> Result<Self, DatabaseError> {
let tags = block_on(db_model.get_tags(conn))?; let tags = block_on(db_model.get_tags(conn))?;
let controller_id = cache.uid.clone();
let schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &db_model))?; let schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &db_model))?;
let active_schedule = block_on(db_model.get_active_schedule(conn))?;
let is_on = None; let is_on = None;
Ok(Relay { Ok(Relay {
r: db_model, r: db_model,
controller: cache,
controller_id,
schedules, schedules,
active_schedule, active_schedule: None,
override_schedule: None,
is_on, is_on,
tags, tags,
pulsing: None, pulsing: None,
override_schedule_weekday: utils::default_weekday(),
}) })
} }
} }
@ -69,25 +68,24 @@ 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(()) Ok(())
} }
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(())
}
pub fn is_on(&self, now: &NaiveTime) -> bool { pub fn is_on(&self, now: &NaiveTime) -> bool {
self.active_schedule.is_on(now) if let Some(active_schedule) = &self.active_schedule {
active_schedule.is_on(now)
} else {
false
}
} }
pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> { pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
self.active_schedule.get_next_time(now) if let Some(active_schedule) = &self.active_schedule {
active_schedule.get_next_time(now)
} else {
None
}
} }
pub fn check_pulsing(&mut self, now: &Instant) -> Option<Instant> { pub fn check_pulsing(&mut self, now: &Instant) -> Option<Instant> {
@ -103,4 +101,30 @@ impl Relay {
None => None, None => None,
} }
} }
pub fn reload_active_schedule(&mut self, weekday: Weekday) {
if let Some(schedule) = &self.override_schedule {
if self.override_schedule_weekday == weekday {
self.active_schedule = Some(schedule.clone());
return;
}
if self.override_schedule_weekday != weekday {
self.override_schedule = None;
}
}
self.active_schedule = Some(self.schedules.get(weekday as usize).unwrap().clone())
}
pub fn apply_state(&mut self, state: &RelayState) {
self.active_schedule.clone_from(&state.active_schedule);
self.override_schedule.clone_from(&state.override_schedule);
self.is_on = state.is_on;
}
pub fn find_and_apply_state(&mut self, stated_relays: &[Relay]) {
if let Some(stated_relay) = stated_relays.iter().find(|r| r.r.id == self.r.id) {
self.apply_state(&stated_relay.into());
}
}
} }

View file

@ -16,7 +16,6 @@ pub struct Server {
#[allow(unused)] #[allow(unused)]
pub struct Logging { pub struct Logging {
pub level: String, pub level: String,
pub file: String,
} }
#[derive(Clone, Debug, Deserialize, Default)] #[derive(Clone, Debug, Deserialize, Default)]
@ -40,7 +39,6 @@ impl Default for Logging {
fn default() -> Self { fn default() -> Self {
Logging { Logging {
level: String::from("info"), level: String::from("info"),
file: String::from("stdout"),
} }
} }
} }

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

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

View file

@ -1,9 +1,13 @@
mod emgauwa_now;
mod emgauwa_uid; mod emgauwa_uid;
mod request; mod request;
mod schedule_uid; mod schedule_uid;
mod relay_state;
use actix::Message; use actix::Message;
pub use emgauwa_now::EmgauwaNow;
pub use emgauwa_uid::EmgauwaUid; pub use emgauwa_uid::EmgauwaUid;
pub use relay_state::{RelayState, RelayStates};
pub use request::*; pub use request::*;
pub use schedule_uid::ScheduleUid; pub use schedule_uid::ScheduleUid;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -12,10 +16,6 @@ use crate::db::DbSchedule;
use crate::errors::EmgauwaError; use crate::errors::EmgauwaError;
use crate::models::{Controller, Relay}; use crate::models::{Controller, Relay};
pub type Weekday = i64;
pub type RelayStates = Vec<Option<bool>>;
#[derive(Debug, Serialize, Deserialize, Message)] #[derive(Debug, Serialize, Deserialize, Message)]
#[rtype(result = "Result<(), EmgauwaError>")] #[rtype(result = "Result<(), EmgauwaError>")]
pub enum ControllerWsAction { pub enum ControllerWsAction {

22
src/types/relay_state.rs Normal file
View file

@ -0,0 +1,22 @@
use serde_derive::{Deserialize, Serialize};
use crate::db::DbSchedule;
use crate::models::Relay;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelayState {
pub active_schedule: Option<DbSchedule>,
pub override_schedule: Option<DbSchedule>,
pub is_on: Option<bool>
}
pub type RelayStates = Vec<RelayState>;
impl From<&Relay> for RelayState {
fn from(relay: &Relay) -> Self {
RelayState {
active_schedule: relay.active_schedule.clone(),
override_schedule: relay.override_schedule.clone(),
is_on: relay.is_on
}
}
}

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

@ -2,17 +2,17 @@ use std::ffi::CString;
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
use std::str::FromStr; use std::str::FromStr;
use chrono::Datelike; use chrono::{Datelike, NaiveTime, Weekday};
use log::LevelFilter; use log::LevelFilter;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use crate::errors::EmgauwaError; use crate::errors::EmgauwaError;
use crate::settings::Permissions; use crate::settings::{Logging, Permissions};
use crate::types::{RelayStates, Weekday}; use crate::types::RelayStates;
pub fn init_logging(level: &str) -> Result<(), EmgauwaError> { pub fn init_logging(logging: &Logging) -> Result<(), EmgauwaError> {
let log_level: LevelFilter = LevelFilter::from_str(level) let log_level: LevelFilter = LevelFilter::from_str(&logging.level)
.map_err(|_| EmgauwaError::Other(format!("Invalid log level: {}", level)))?; .map_err(|_| EmgauwaError::Other(format!("Invalid log level: {}", logging.level)))?;
log::trace!("Log level set to {:?}", log_level); log::trace!("Log level set to {:?}", log_level);
SimpleLogger::new() SimpleLogger::new()
@ -92,18 +92,24 @@ fn drop_privileges_user(user: &str) -> Result<(), Error> {
Ok(()) Ok(())
} }
pub fn get_weekday() -> Weekday { pub fn get_weekday(midnight: &NaiveTime) -> Weekday {
(chrono::offset::Local::now() let dt = chrono::offset::Local::now().naive_local();
.date_naive() let weekday = dt.weekday();
.weekday() if dt.time().lt(midnight) {
.number_from_monday() weekday.pred()
- 1) as Weekday } else {
weekday
}
}
pub fn default_weekday() -> Weekday {
Weekday::Mon
} }
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 => "?",