Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
066e9f7bf8 | |||
e923ecb9d8 | |||
2f5bb538b2 | |||
41cc9e0622 | |||
d4ff664f74 | |||
277b159200 | |||
ce7a79d1de | |||
929985c64a | |||
473832f58a | |||
9326b66007 | |||
f26e66d687 | |||
b14049b3f6 | |||
228b366320 | |||
e9b09cd709 | |||
cacd740bd9 | |||
98db89ce03 | |||
fc4c1df09a | |||
19e2ea003b |
36 changed files with 1434 additions and 1196 deletions
.sqlx
query-2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a.jsonquery-2b5ac2227f48be1483f4097da6f890be8091daa97b0af548b6ebf60cdc03dfba.jsonquery-493ad91be9ce523e9d0f03f5caa9b3255a5426d54901f4f3aa96ad152b05ffd0.jsonquery-4a99db9678cf8d1bdb082c4a13a1f5cdd699bfe7600389e37ca980b6fad12bb5.jsonquery-5056b625241d9cbe63d98e00ac39085677c09be8be903804120c2d52579afdbb.jsonquery-5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465.jsonquery-9224ad423f2c86f3d95f2b0b7d99a27f690020f89958dfc8dd6044a31afdb31d.jsonquery-adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9.jsonquery-d57c388bf6c26fe6cadad35d0f254ca2ef93958f9975c585c6de3c437782995d.jsonquery-f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506.json
Cargo.lockCargo.tomlMakefilemigrations
src
12
.sqlx/query-2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a.json
generated
Normal file
12
.sqlx/query-2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a.json
generated
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE macros SET name = ? WHERE id = ?",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2b34934e10005378c331f489751dcc4dc5cc79a52299cb74018e36212809288a"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT * FROM relays WHERE controller_id = ?",
|
||||
"query": "SELECT * FROM v_relays WHERE id = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -22,6 +22,11 @@
|
|||
"name": "controller_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_uid",
|
||||
"ordinal": 4,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -31,8 +36,9 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "c9437ff0c3014b269dcb21304fbad12237b9cb69ea6aa4686df6d5262065faa2"
|
||||
"hash": "2b5ac2227f48be1483f4097da6f890be8091daa97b0af548b6ebf60cdc03dfba"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT relays.* FROM relays INNER JOIN junction_relay_schedule\n\t\t\tON junction_relay_schedule.relay_id = relays.id\n\t\t\tWHERE junction_relay_schedule.schedule_id = ?\n\t\t\tORDER BY junction_relay_schedule.weekday",
|
||||
"query": "SELECT v_relays.* FROM v_relays INNER JOIN junction_tag ON junction_tag.relay_id = v_relays.id WHERE junction_tag.tag_id = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -22,6 +22,11 @@
|
|||
"name": "controller_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_uid",
|
||||
"ordinal": 4,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -31,8 +36,9 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2551c285e3e223311cff8e32022d8b11e95d56b2f166326301a0b6722fc1fd44"
|
||||
"hash": "493ad91be9ce523e9d0f03f5caa9b3255a5426d54901f4f3aa96ad152b05ffd0"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT relay.* FROM relays AS relay INNER JOIN junction_tag ON junction_tag.relay_id = relay.id WHERE junction_tag.tag_id = ?",
|
||||
"query": "SELECT * FROM v_relays WHERE v_relays.controller_id = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -22,6 +22,11 @@
|
|||
"name": "controller_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_uid",
|
||||
"ordinal": 4,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -31,8 +36,9 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e94ef5bc8b267d493375bb371dcfb7b09f6355ecbc8b6e1085d5f2f9a08cac3f"
|
||||
"hash": "4a99db9678cf8d1bdb082c4a13a1f5cdd699bfe7600389e37ca980b6fad12bb5"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT * FROM relays",
|
||||
"query": "SELECT * FROM v_relays",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -22,6 +22,11 @@
|
|||
"name": "controller_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_uid",
|
||||
"ordinal": 4,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -31,8 +36,9 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "ee7da56331bece2efe21b55dbd5f420d3abb08358a1abe301dc7e08693fbef4d"
|
||||
"hash": "5056b625241d9cbe63d98e00ac39085677c09be8be903804120c2d52579afdbb"
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?) RETURNING *",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "number",
|
||||
"ordinal": 2,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "5865f27b97487b6dfd956a3d260b9bbb0e6c203b721d29cf9149f60bfdd93465"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT * FROM relays WHERE controller_id = ? AND number = ?",
|
||||
"query": "SELECT * FROM v_relays WHERE v_relays.controller_id = ? AND v_relays.number = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -22,6 +22,11 @@
|
|||
"name": "controller_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_uid",
|
||||
"ordinal": 4,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -31,8 +36,9 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b41855e635ac409559fa63cba4c1285034c573b86e3193da3995606dee412153"
|
||||
"hash": "9224ad423f2c86f3d95f2b0b7d99a27f690020f89958dfc8dd6044a31afdb31d"
|
||||
}
|
44
.sqlx/query-adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9.json
generated
Normal file
44
.sqlx/query-adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9.json
generated
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT v_relays.* FROM v_relays INNER JOIN junction_relay_schedule\n\t\t\tON junction_relay_schedule.relay_id = v_relays.id\n\t\t\tWHERE junction_relay_schedule.schedule_id = ?\n\t\t\tORDER BY junction_relay_schedule.weekday",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "number",
|
||||
"ordinal": 2,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_uid",
|
||||
"ordinal": 4,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "adbce2c94ac0b54d0826b28f99fe63322d3bb1579e52d0f053307e24bd039ef9"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT * FROM relays WHERE id = ?",
|
||||
"query": "SELECT * FROM v_relays WHERE v_relays.id = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -22,6 +22,11 @@
|
|||
"name": "controller_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "controller_uid",
|
||||
"ordinal": 4,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
|
@ -31,8 +36,9 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4f5408e64f5e6a8dd923c3b147f993ce9e4cafc90204b06977481130ec06d111"
|
||||
"hash": "d57c388bf6c26fe6cadad35d0f254ca2ef93958f9975c585c6de3c437782995d"
|
||||
}
|
12
.sqlx/query-f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506.json
generated
Normal file
12
.sqlx/query-f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506.json
generated
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f85f0a96bb98d20e47677b0679d552812362c3141738b60bc63d673a7f552506"
|
||||
}
|
1897
Cargo.lock
generated
1897
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
16
Cargo.toml
16
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "emgauwa-common"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
edition = "2021"
|
||||
authors = ["Tobias Reisinger <tobias@msrg.cc>"]
|
||||
|
||||
|
@ -8,25 +8,21 @@ authors = ["Tobias Reisinger <tobias@msrg.cc>"]
|
|||
[dependencies]
|
||||
actix = "0.13"
|
||||
actix-web = "4.4"
|
||||
actix-web-actors = "4.2"
|
||||
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_with = "3.8"
|
||||
|
||||
simple_logger = "4.2"
|
||||
simple_logger = "5.0"
|
||||
log = "0.4"
|
||||
|
||||
config = "0.13"
|
||||
config = "0.14"
|
||||
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] }
|
||||
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros"] }
|
||||
libsqlite3-sys = { version = "*", features = ["bundled"] }
|
||||
uuid = "1.6"
|
||||
uuid = { version = "1.8", features = ["v4"] }
|
||||
futures = "0.3"
|
||||
libc = "0.2"
|
||||
|
||||
rppal = "0.17"
|
||||
rppal-pfd = "0.0.5"
|
||||
rppal-mcp23s17 = "0.0.3"
|
||||
|
|
2
Makefile
2
Makefile
|
@ -1,3 +1,5 @@
|
|||
export DATABASE_URL=sqlite://${PWD}/emgauwa-dev.sqlite
|
||||
|
||||
sqlx:
|
||||
cargo sqlx database drop -y
|
||||
cargo sqlx database create
|
||||
|
|
1
migrations/20240611000000_add_relays_view.down.sql
Normal file
1
migrations/20240611000000_add_relays_view.down.sql
Normal file
|
@ -0,0 +1 @@
|
|||
DROP VIEW v_relays;
|
8
migrations/20240611000000_add_relays_view.up.sql
Normal file
8
migrations/20240611000000_add_relays_view.up.sql
Normal 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;
|
|
@ -158,7 +158,7 @@ impl DbController {
|
|||
) -> Result<Vec<DbRelay>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbRelay,
|
||||
"SELECT * FROM relays WHERE controller_id = ?",
|
||||
"SELECT * FROM v_relays WHERE v_relays.controller_id = ?",
|
||||
self.id
|
||||
)
|
||||
.fetch_all(conn.deref_mut())
|
||||
|
|
|
@ -5,11 +5,10 @@ use sqlx::Sqlite;
|
|||
|
||||
use crate::db::{DbRelay, DbSchedule};
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::types::Weekday;
|
||||
|
||||
pub struct DbJunctionRelaySchedule {
|
||||
pub id: i64,
|
||||
pub weekday: Weekday,
|
||||
pub weekday: i64,
|
||||
pub relay_id: i64,
|
||||
pub schedule_id: i64,
|
||||
}
|
||||
|
@ -32,7 +31,7 @@ impl DbJunctionRelaySchedule {
|
|||
pub async fn get_junction_by_relay_and_weekday(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
relay: &DbRelay,
|
||||
weekday: Weekday,
|
||||
weekday: i64,
|
||||
) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbJunctionRelaySchedule,
|
||||
|
@ -51,8 +50,8 @@ impl DbJunctionRelaySchedule {
|
|||
) -> Result<Vec<DbRelay>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbRelay,
|
||||
r#"SELECT relays.* FROM relays INNER JOIN junction_relay_schedule
|
||||
ON junction_relay_schedule.relay_id = relays.id
|
||||
r#"SELECT v_relays.* FROM v_relays INNER JOIN junction_relay_schedule
|
||||
ON junction_relay_schedule.relay_id = v_relays.id
|
||||
WHERE junction_relay_schedule.schedule_id = ?
|
||||
ORDER BY junction_relay_schedule.weekday"#,
|
||||
schedule.id
|
||||
|
@ -65,7 +64,7 @@ impl DbJunctionRelaySchedule {
|
|||
pub async fn get_schedule(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
relay: &DbRelay,
|
||||
weekday: Weekday,
|
||||
weekday: i64,
|
||||
) -> Result<Option<DbSchedule>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbSchedule,
|
||||
|
@ -101,7 +100,7 @@ impl DbJunctionRelaySchedule {
|
|||
conn: &mut PoolConnection<Sqlite>,
|
||||
relay: &DbRelay,
|
||||
schedule: &DbSchedule,
|
||||
weekday: Weekday,
|
||||
weekday: i64,
|
||||
) -> Result<DbJunctionRelaySchedule, DatabaseError> {
|
||||
match Self::get_junction_by_relay_and_weekday(conn, relay, weekday).await? {
|
||||
None => sqlx::query_as!(
|
||||
|
@ -139,7 +138,7 @@ impl DbJunctionRelaySchedule {
|
|||
schedules: Vec<&DbSchedule>,
|
||||
) -> Result<(), DatabaseError> {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ impl DbMacro {
|
|||
conn: &mut PoolConnection<Sqlite>,
|
||||
new_name: &str,
|
||||
) -> Result<DbMacro, DatabaseError> {
|
||||
sqlx::query!("UPDATE relays SET name = ? WHERE id = ?", new_name, self.id,)
|
||||
sqlx::query!("UPDATE macros SET name = ? WHERE id = ?", new_name, self.id,)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ pub use relays::DbRelay;
|
|||
pub use schedules::{DbPeriods, DbSchedule};
|
||||
pub use tag::DbTag;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) use model_utils::Period;
|
||||
|
||||
use crate::errors::{DatabaseError, EmgauwaError};
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!(); // defaults to "./migrations"
|
||||
|
@ -33,14 +36,14 @@ pub async fn run_migrations(pool: &Pool<Sqlite>) -> Result<(), EmgauwaError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn init(db: &str) -> Result<Pool<Sqlite>, EmgauwaError> {
|
||||
pub async fn init(db: &str, pool_size: u32) -> Result<Pool<Sqlite>, EmgauwaError> {
|
||||
let options = SqliteConnectOptions::from_str(db)?
|
||||
.create_if_missing(true)
|
||||
.log_statements(log::LevelFilter::Trace);
|
||||
|
||||
let pool: Pool<Sqlite> = SqlitePoolOptions::new()
|
||||
.acquire_timeout(std::time::Duration::from_secs(1))
|
||||
.max_connections(5)
|
||||
.max_connections(pool_size)
|
||||
.connect_with(options)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -51,13 +51,34 @@ impl Period {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_always_on(&self) -> bool {
|
||||
self.start.eq(&self.end)
|
||||
}
|
||||
|
||||
pub fn is_on(&self, now: &NaiveTime) -> bool {
|
||||
self.start.eq(&self.end) || (self.start.le(now) && self.end.gt(now))
|
||||
if self.is_always_on() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let start_after_now = self.start.gt(now);
|
||||
// add check for end time being 00:00 because end being 00:00 would cause end_after_now to always be false
|
||||
// this will handle end like 24:00 and end_after_now will be true
|
||||
// same for start_before_end
|
||||
let end_after_now = self.end.gt(now) || self.end.eq(&NaiveTime::MIN);
|
||||
let start_before_end = self.start.lt(&self.end) || self.end.eq(&NaiveTime::MIN);
|
||||
|
||||
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, false) => true, // both before now; end before start means "inverse" period around now
|
||||
(true, false, _) => false, // only start after now
|
||||
(false, true, _) => true, // only end after now
|
||||
(true, true, true) => false, // both after now but start first
|
||||
(true, true, false) => true, // both after now but end first
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
|
||||
if self.start.eq(&self.end) {
|
||||
// this period is always on
|
||||
if self.is_always_on() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,9 @@ use serde_derive::{Deserialize, Serialize};
|
|||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::{DbController, DbJunctionRelaySchedule, DbJunctionTag, DbSchedule, DbTag};
|
||||
use crate::db::{DbController, DbJunctionTag, DbTag};
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::types::Weekday;
|
||||
use crate::utils;
|
||||
use crate::types::EmgauwaUid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DbRelay {
|
||||
|
@ -15,13 +14,15 @@ pub struct DbRelay {
|
|||
pub id: i64,
|
||||
pub name: String,
|
||||
pub number: i64,
|
||||
#[serde(rename = "controller_id")]
|
||||
pub controller_uid: EmgauwaUid,
|
||||
#[serde(skip)]
|
||||
pub controller_id: i64,
|
||||
}
|
||||
|
||||
impl DbRelay {
|
||||
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())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
|
@ -31,7 +32,7 @@ impl DbRelay {
|
|||
conn: &mut PoolConnection<Sqlite>,
|
||||
id: i64,
|
||||
) -> 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())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
|
@ -44,7 +45,7 @@ impl DbRelay {
|
|||
) -> Result<Option<DbRelay>, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbRelay,
|
||||
"SELECT * FROM relays WHERE controller_id = ? AND number = ?",
|
||||
"SELECT * FROM v_relays WHERE v_relays.controller_id = ? AND v_relays.number = ?",
|
||||
controller.id,
|
||||
number
|
||||
)
|
||||
|
@ -72,7 +73,7 @@ impl DbRelay {
|
|||
conn: &mut PoolConnection<Sqlite>,
|
||||
tag: &DbTag,
|
||||
) -> 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())
|
||||
.await
|
||||
.map_err(DatabaseError::from)
|
||||
|
@ -84,16 +85,25 @@ impl DbRelay {
|
|||
new_number: i64,
|
||||
new_controller: &DbController,
|
||||
) -> Result<DbRelay, DatabaseError> {
|
||||
sqlx::query_as!(
|
||||
DbRelay,
|
||||
"INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?) RETURNING *",
|
||||
let result = sqlx::query!(
|
||||
"INSERT INTO relays (name, number, controller_id) VALUES (?, ?, ?)",
|
||||
new_name,
|
||||
new_number,
|
||||
new_controller.id,
|
||||
)
|
||||
.fetch_optional(conn.deref_mut())
|
||||
.await?
|
||||
.ok_or(DatabaseError::InsertGetError)
|
||||
.execute(conn.deref_mut())
|
||||
.await?;
|
||||
|
||||
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> {
|
||||
|
@ -163,14 +173,4 @@ impl DbRelay {
|
|||
.await?
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
use rppal::gpio::{Gpio, OutputPin};
|
||||
|
||||
use crate::drivers::RelayDriver;
|
||||
use crate::errors::EmgauwaError;
|
||||
|
||||
pub struct GpioDriver {
|
||||
pub gpio: OutputPin,
|
||||
pub inverted: bool,
|
||||
}
|
||||
|
||||
impl GpioDriver {
|
||||
pub fn new(pin: u8, inverted: bool) -> Result<Self, EmgauwaError> {
|
||||
let gpio = Gpio::new()?.get(pin)?.into_output();
|
||||
Ok(Self { gpio, inverted })
|
||||
}
|
||||
}
|
||||
|
||||
impl RelayDriver for GpioDriver {
|
||||
fn set(&mut self, value: bool) -> Result<(), EmgauwaError> {
|
||||
if self.get_high(value) {
|
||||
self.gpio.set_high();
|
||||
} else {
|
||||
self.gpio.set_low();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_pin(&self) -> u8 {
|
||||
self.gpio.pin()
|
||||
}
|
||||
|
||||
fn get_inverted(&self) -> bool {
|
||||
self.inverted
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
mod gpio;
|
||||
mod null;
|
||||
mod piface;
|
||||
|
||||
pub use gpio::GpioDriver;
|
||||
pub use null::NullDriver;
|
||||
pub use piface::PiFaceDriver;
|
||||
|
||||
use crate::errors::EmgauwaError;
|
||||
|
||||
pub trait RelayDriver {
|
||||
fn get_high(&self, value: bool) -> bool {
|
||||
value ^ self.get_inverted()
|
||||
}
|
||||
|
||||
fn set(&mut self, value: bool) -> Result<(), EmgauwaError>;
|
||||
fn get_pin(&self) -> u8;
|
||||
fn get_inverted(&self) -> bool;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
use crate::drivers::RelayDriver;
|
||||
use crate::errors::EmgauwaError;
|
||||
|
||||
pub struct NullDriver {
|
||||
pub pin: u8,
|
||||
}
|
||||
|
||||
impl NullDriver {
|
||||
pub fn new(pin: u8) -> Self {
|
||||
Self { pin }
|
||||
}
|
||||
}
|
||||
|
||||
impl RelayDriver for NullDriver {
|
||||
fn set(&mut self, _value: bool) -> Result<(), EmgauwaError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_pin(&self) -> u8 {
|
||||
self.pin
|
||||
}
|
||||
|
||||
fn get_inverted(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
use rppal_pfd::{
|
||||
ChipSelect, HardwareAddress, OutputPin, PiFaceDigital, PiFaceDigitalError, SpiBus, SpiMode,
|
||||
};
|
||||
|
||||
use crate::drivers::RelayDriver;
|
||||
use crate::errors::EmgauwaError;
|
||||
|
||||
pub struct PiFaceDriver {
|
||||
pub pfd_pin: OutputPin,
|
||||
}
|
||||
|
||||
impl PiFaceDriver {
|
||||
pub fn new(pin: u8, pfd: &Option<PiFaceDigital>) -> Result<Self, EmgauwaError> {
|
||||
let pfd = pfd.as_ref().ok_or(EmgauwaError::Hardware(String::from(
|
||||
"PiFaceDigital not initialized",
|
||||
)))?;
|
||||
let pfd_pin = pfd.get_output_pin(pin)?;
|
||||
Ok(Self { pfd_pin })
|
||||
}
|
||||
|
||||
pub fn init_piface() -> Result<PiFaceDigital, EmgauwaError> {
|
||||
let mut pfd = PiFaceDigital::new(
|
||||
HardwareAddress::new(0)?,
|
||||
SpiBus::Spi0,
|
||||
ChipSelect::Cs0,
|
||||
100_000,
|
||||
SpiMode::Mode0,
|
||||
)?;
|
||||
pfd.init()?;
|
||||
|
||||
Ok(pfd)
|
||||
}
|
||||
}
|
||||
|
||||
impl RelayDriver for PiFaceDriver {
|
||||
fn set(&mut self, value: bool) -> Result<(), EmgauwaError> {
|
||||
if self.get_high(value) {
|
||||
self.pfd_pin.set_high().map_err(PiFaceDigitalError::from)?;
|
||||
} else {
|
||||
self.pfd_pin.set_low().map_err(PiFaceDigitalError::from)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_pin(&self) -> u8 {
|
||||
self.pfd_pin.get_pin_number()
|
||||
}
|
||||
|
||||
fn get_inverted(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
|
@ -6,9 +6,6 @@ use actix::MailboxError;
|
|||
use actix_web::http::StatusCode;
|
||||
use actix_web::HttpResponse;
|
||||
use config::ConfigError;
|
||||
use rppal::gpio;
|
||||
use rppal_mcp23s17::Mcp23s17Error;
|
||||
use rppal_pfd::PiFaceDigitalError;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
|
@ -50,7 +47,9 @@ impl From<&EmgauwaError> for String {
|
|||
EmgauwaError::Database(err) => String::from(err),
|
||||
EmgauwaError::Uid(_) => String::from("the uid is in a bad format"),
|
||||
EmgauwaError::Internal(_) => String::from("internal error"),
|
||||
EmgauwaError::Connection(_) => String::from("the target controller is not connected"),
|
||||
EmgauwaError::Connection(uid) => {
|
||||
format!("unable to connect to controller with uid: {}", uid)
|
||||
}
|
||||
EmgauwaError::Other(err) => format!("other error: {}", err),
|
||||
EmgauwaError::Hardware(err) => format!("hardware error: {}", err),
|
||||
}
|
||||
|
@ -99,25 +98,6 @@ impl From<ConfigError> for EmgauwaError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<gpio::Error> for EmgauwaError {
|
||||
fn from(value: gpio::Error) -> Self {
|
||||
Self::Hardware(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PiFaceDigitalError> for EmgauwaError {
|
||||
fn from(value: PiFaceDigitalError) -> Self {
|
||||
match value {
|
||||
PiFaceDigitalError::Mcp23s17Error { source } => match source {
|
||||
Mcp23s17Error::SpiError { source } => Self::Hardware(source.to_string()),
|
||||
_ => Self::Hardware(source.to_string()),
|
||||
},
|
||||
PiFaceDigitalError::GpioError { source } => Self::Hardware(source.to_string()),
|
||||
_ => Self::Hardware(value.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&EmgauwaError> for HttpResponse {
|
||||
fn from(err: &EmgauwaError) -> Self {
|
||||
HttpResponse::build(err.get_code()).json(err)
|
||||
|
@ -139,7 +119,7 @@ impl Serialize for EmgauwaError {
|
|||
{
|
||||
let mut s = serializer.serialize_struct("error", 2)?;
|
||||
s.serialize_field("code", &self.get_code().as_u16())?;
|
||||
s.serialize_field("description", &String::from(self))?;
|
||||
s.serialize_field("message", &String::from(self))?;
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
|
66
src/lib.rs
66
src/lib.rs
|
@ -1,8 +1,72 @@
|
|||
pub mod constants;
|
||||
pub mod db;
|
||||
pub mod drivers;
|
||||
pub mod errors;
|
||||
pub mod models;
|
||||
pub mod settings;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(test)]
|
||||
mod periods {
|
||||
use chrono::NaiveTime;
|
||||
use crate::db::Period;
|
||||
use crate::types::EmgauwaNow;
|
||||
|
||||
const MIDNIGHT: NaiveTime = NaiveTime::MIN;
|
||||
|
||||
fn new_time(hour: u32, minute: u32) -> NaiveTime {
|
||||
NaiveTime::from_hms_opt(hour, minute, 0).expect("Failed to create NaiveTime")
|
||||
}
|
||||
fn new_period(start_hour: u32, start_minute: u32, end_hour: u32, end_minute: u32) -> Period {
|
||||
Period {
|
||||
start: new_time(start_hour, start_minute),
|
||||
end: new_time(end_hour, end_minute),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn always_on() {
|
||||
let period = Period::new_on();
|
||||
let now: EmgauwaNow = EmgauwaNow::now(&MIDNIGHT);
|
||||
assert_eq!(period.is_always_on(), true);
|
||||
assert_eq!(period.is_on(&MIDNIGHT), true);
|
||||
assert_eq!(period.is_on(&new_time(12, 00)), true);
|
||||
assert_eq!(period.is_on(&now.time), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_period() {
|
||||
let period = new_period(11, 00, 13, 00);
|
||||
assert_eq!(period.is_always_on(), false);
|
||||
assert_eq!(period.is_on(&MIDNIGHT), false);
|
||||
assert_eq!(period.is_on(&new_time(10, 00)), false);
|
||||
assert_eq!(period.is_on(&new_time(11, 00)), true);
|
||||
assert_eq!(period.is_on(&new_time(12, 00)), true);
|
||||
assert_eq!(period.is_on(&new_time(13, 00)), false);
|
||||
assert_eq!(period.is_on(&new_time(14, 00)), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_midnight_period() {
|
||||
let period = new_period(22, 00, 00, 00);
|
||||
assert_eq!(period.is_always_on(), false);
|
||||
assert_eq!(period.is_on(&MIDNIGHT), false);
|
||||
assert_eq!(period.is_on(&new_time(21, 00)), false);
|
||||
assert_eq!(period.is_on(&new_time(22, 00)), true);
|
||||
assert_eq!(period.is_on(&new_time(23, 00)), true);
|
||||
assert_eq!(period.is_on(&new_time(00, 00)), false);
|
||||
assert_eq!(period.is_on(&new_time(01, 00)), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_midnight_period() {
|
||||
let period = new_period(00, 00, 02, 00);
|
||||
assert_eq!(period.is_always_on(), false);
|
||||
assert_eq!(period.is_on(&MIDNIGHT), true);
|
||||
assert_eq!(period.is_on(&new_time(23, 00)), false);
|
||||
assert_eq!(period.is_on(&new_time(00, 00)), true);
|
||||
assert_eq!(period.is_on(&new_time(01, 00)), true);
|
||||
assert_eq!(period.is_on(&new_time(02, 00)), false);
|
||||
assert_eq!(period.is_on(&new_time(03, 00)), false);
|
||||
}
|
||||
}
|
|
@ -9,8 +9,8 @@ use sqlx::Sqlite;
|
|||
|
||||
use crate::db::DbController;
|
||||
use crate::errors::{DatabaseError, EmgauwaError};
|
||||
use crate::models::{convert_db_list_cache, FromDbModel, Relay};
|
||||
use crate::types::RelayStates;
|
||||
use crate::models::{convert_db_list, FromDbModel, Relay};
|
||||
use crate::types::{EmgauwaNow, RelayState, RelayStates};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, MessageResponse)]
|
||||
pub struct Controller {
|
||||
|
@ -28,7 +28,7 @@ impl FromDbModel for Controller {
|
|||
db_model: Self::DbModel,
|
||||
) -> Result<Self, DatabaseError> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -57,19 +57,20 @@ impl Controller {
|
|||
self.relays
|
||||
.iter_mut()
|
||||
.zip(relay_states.iter())
|
||||
.for_each(|(relay, is_on)| {
|
||||
relay.is_on = *is_on;
|
||||
});
|
||||
.for_each(|(relay, state)| relay.apply_state(state));
|
||||
}
|
||||
|
||||
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
|
||||
.iter()
|
||||
.filter_map(|r| r.active_schedule.get_next_time(now))
|
||||
.iter_mut()
|
||||
.filter_map(|r| {
|
||||
r.reload_active_schedule(now.weekday);
|
||||
r.get_next_time(&now.time)
|
||||
})
|
||||
.min()
|
||||
}
|
||||
|
||||
|
@ -80,6 +81,8 @@ impl Controller {
|
|||
.find(|r| r.r.number == relay_num)
|
||||
.ok_or(EmgauwaError::Other(String::from("Relay not found")))?;
|
||||
|
||||
log::debug!("Pulsing relay {} until {:?}", relay_num, until);
|
||||
|
||||
relay.pulsing = Some(until);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@ use serde_derive::{Deserialize, Serialize};
|
|||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::{DbJunctionRelaySchedule, DbMacroAction};
|
||||
use crate::db::{DbJunctionRelaySchedule, DbMacroAction, DbSchedule};
|
||||
use crate::errors::{DatabaseError, EmgauwaError};
|
||||
use crate::models::{FromDbModel, Relay, Schedule};
|
||||
use crate::models::{FromDbModel, Relay};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct MacroAction {
|
||||
pub schedule: Schedule,
|
||||
pub schedule: DbSchedule,
|
||||
pub relay: Relay,
|
||||
pub weekday: i64,
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ impl FromDbModel for MacroAction {
|
|||
db_model: Self::DbModel,
|
||||
_cache: Self::DbModelCache,
|
||||
) -> Result<Self, DatabaseError> {
|
||||
let schedule_db = block_on(db_model.get_schedule(conn))?;
|
||||
let schedule = Schedule::from_db_model(conn, schedule_db)?;
|
||||
let schedule = block_on(db_model.get_schedule(conn))?;
|
||||
|
||||
let relay_db = block_on(db_model.get_relay(conn))?;
|
||||
let relay = Relay::from_db_model(conn, relay_db)?;
|
||||
|
@ -48,7 +47,7 @@ impl FromDbModel for MacroAction {
|
|||
|
||||
impl MacroAction {
|
||||
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?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,66 +1,65 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use chrono::NaiveTime;
|
||||
use chrono::{NaiveTime, Weekday};
|
||||
use futures::executor::block_on;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::Sqlite;
|
||||
|
||||
use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
|
||||
use crate::db::{DbJunctionRelaySchedule, DbRelay, DbSchedule};
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::models::FromDbModel;
|
||||
use crate::types::EmgauwaUid;
|
||||
use crate::types::RelayState;
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Relay {
|
||||
#[serde(flatten)]
|
||||
pub r: DbRelay,
|
||||
pub controller: DbController,
|
||||
pub controller_id: EmgauwaUid,
|
||||
pub schedules: Vec<DbSchedule>,
|
||||
pub active_schedule: DbSchedule,
|
||||
pub active_schedule: Option<DbSchedule>,
|
||||
pub override_schedule: Option<DbSchedule>,
|
||||
pub is_on: Option<bool>,
|
||||
pub tags: Vec<String>,
|
||||
|
||||
// for internal use only.
|
||||
#[serde(skip)]
|
||||
pub pulsing: Option<Instant>,
|
||||
#[serde(skip, default = "utils::default_weekday")]
|
||||
pub override_schedule_weekday: Weekday,
|
||||
}
|
||||
|
||||
impl FromDbModel for Relay {
|
||||
type DbModel = DbRelay;
|
||||
type DbModelCache = DbController;
|
||||
type DbModelCache = ();
|
||||
|
||||
fn from_db_model(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
db_model: Self::DbModel,
|
||||
) -> Result<Self, DatabaseError> {
|
||||
let cache = block_on(db_model.get_controller(conn))?;
|
||||
Self::from_db_model_cache(conn, db_model, cache)
|
||||
Self::from_db_model_cache(conn, db_model, ())
|
||||
}
|
||||
|
||||
fn from_db_model_cache(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
db_model: Self::DbModel,
|
||||
cache: Self::DbModelCache,
|
||||
_cache: Self::DbModelCache,
|
||||
) -> Result<Self, DatabaseError> {
|
||||
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 active_schedule = block_on(db_model.get_active_schedule(conn))?;
|
||||
|
||||
let is_on = None;
|
||||
|
||||
Ok(Relay {
|
||||
r: db_model,
|
||||
controller: cache,
|
||||
controller_id,
|
||||
schedules,
|
||||
active_schedule,
|
||||
active_schedule: None,
|
||||
override_schedule: None,
|
||||
is_on,
|
||||
tags,
|
||||
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> {
|
||||
self.r = block_on(self.r.reload(conn))?;
|
||||
self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?;
|
||||
self.reload_active_schedule(conn)?;
|
||||
|
||||
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 {
|
||||
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> {
|
||||
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> {
|
||||
|
@ -103,4 +101,32 @@ impl Relay {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(schedule) = self.schedules.get(weekday as usize) {
|
||||
self.active_schedule = Some(schedule.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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ pub struct Server {
|
|||
#[allow(unused)]
|
||||
pub struct Logging {
|
||||
pub level: String,
|
||||
pub file: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
|
@ -40,7 +39,6 @@ impl Default for Logging {
|
|||
fn default() -> Self {
|
||||
Logging {
|
||||
level: String::from("info"),
|
||||
file: String::from("stdout"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
27
src/types/emgauwa_now.rs
Normal file
27
src/types/emgauwa_now.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
mod emgauwa_now;
|
||||
mod emgauwa_uid;
|
||||
mod request;
|
||||
mod schedule_uid;
|
||||
mod relay_state;
|
||||
|
||||
use actix::Message;
|
||||
pub use emgauwa_now::EmgauwaNow;
|
||||
pub use emgauwa_uid::EmgauwaUid;
|
||||
pub use relay_state::{RelayState, RelayStates};
|
||||
pub use request::*;
|
||||
pub use schedule_uid::ScheduleUid;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
@ -12,10 +16,6 @@ use crate::db::DbSchedule;
|
|||
use crate::errors::EmgauwaError;
|
||||
use crate::models::{Controller, Relay};
|
||||
|
||||
pub type Weekday = i64;
|
||||
|
||||
pub type RelayStates = Vec<Option<bool>>;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Message)]
|
||||
#[rtype(result = "Result<(), EmgauwaError>")]
|
||||
pub enum ControllerWsAction {
|
||||
|
|
22
src/types/relay_state.rs
Normal file
22
src/types/relay_state.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,11 +23,21 @@ pub struct RequestScheduleUpdate {
|
|||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct RequestRelayUpdate {
|
||||
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 tags: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct RequestScheduleGetTagged {
|
||||
pub strict: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct RequestRelayPulse {
|
||||
pub duration: Option<u32>,
|
||||
|
|
32
src/utils.rs
32
src/utils.rs
|
@ -2,17 +2,17 @@ use std::ffi::CString;
|
|||
use std::io::{Error, ErrorKind};
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::Datelike;
|
||||
use chrono::{Datelike, NaiveTime, Weekday};
|
||||
use log::LevelFilter;
|
||||
use simple_logger::SimpleLogger;
|
||||
|
||||
use crate::errors::EmgauwaError;
|
||||
use crate::settings::Permissions;
|
||||
use crate::types::{RelayStates, Weekday};
|
||||
use crate::settings::{Logging, Permissions};
|
||||
use crate::types::RelayStates;
|
||||
|
||||
pub fn init_logging(level: &str) -> Result<(), EmgauwaError> {
|
||||
let log_level: LevelFilter = LevelFilter::from_str(level)
|
||||
.map_err(|_| EmgauwaError::Other(format!("Invalid log level: {}", level)))?;
|
||||
pub fn init_logging(logging: &Logging) -> Result<(), EmgauwaError> {
|
||||
let log_level: LevelFilter = LevelFilter::from_str(&logging.level)
|
||||
.map_err(|_| EmgauwaError::Other(format!("Invalid log level: {}", logging.level)))?;
|
||||
log::trace!("Log level set to {:?}", log_level);
|
||||
|
||||
SimpleLogger::new()
|
||||
|
@ -92,18 +92,24 @@ fn drop_privileges_user(user: &str) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_weekday() -> Weekday {
|
||||
(chrono::offset::Local::now()
|
||||
.date_naive()
|
||||
.weekday()
|
||||
.number_from_monday()
|
||||
- 1) as Weekday
|
||||
pub fn get_weekday(midnight: &NaiveTime) -> Weekday {
|
||||
let dt = chrono::offset::Local::now().naive_local();
|
||||
let weekday = dt.weekday();
|
||||
if dt.time().lt(midnight) {
|
||||
weekday.pred()
|
||||
} else {
|
||||
weekday
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_weekday() -> Weekday {
|
||||
Weekday::Mon
|
||||
}
|
||||
|
||||
pub fn printable_relay_states(relay_states: &RelayStates) -> String {
|
||||
let mut relay_debug = String::new();
|
||||
relay_states.iter().for_each(|state| {
|
||||
relay_debug.push_str(match state {
|
||||
relay_debug.push_str(match state.is_on {
|
||||
Some(true) => "+",
|
||||
Some(false) => "-",
|
||||
None => "?",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue