Add ControllerWsAction
This commit is contained in:
parent
cb47dcda5c
commit
3b596de06f
10 changed files with 134 additions and 34 deletions
|
@ -2,13 +2,12 @@ openapi: 3.0.0
|
||||||
info:
|
info:
|
||||||
contact:
|
contact:
|
||||||
name: Tobias Reisinger
|
name: Tobias Reisinger
|
||||||
url: 'https://serguzim.me'
|
url: 'https://git.serguzim.me/emgauwa/'
|
||||||
title: Emgauwa API v1
|
title: Emgauwa API v1
|
||||||
version: 0.0.1
|
version: 0.5.0
|
||||||
description: Server API to manage an Emgauwa system.
|
description: Server API to manage an Emgauwa system.
|
||||||
servers:
|
servers:
|
||||||
- url: 'http://emgauwa-test-raspi.fritz.box'
|
- url: 'http://localhost:4419'
|
||||||
- url: 'http://localhost:5000'
|
|
||||||
tags:
|
tags:
|
||||||
- name: schedules
|
- name: schedules
|
||||||
- name: relays
|
- name: relays
|
||||||
|
@ -319,7 +318,7 @@ paths:
|
||||||
'404':
|
'404':
|
||||||
description: Not Found
|
description: Not Found
|
||||||
operationId: delete-controllers-controller_id
|
operationId: delete-controllers-controller_id
|
||||||
description: Delete a single controller. To recover the controller you need to use the conbtrollers/discover feature.
|
description: Delete a single controller. To recover the controller you need to use the controllers/discover feature.
|
||||||
'/api/v1/controllers/{controller_id}/relays':
|
'/api/v1/controllers/{controller_id}/relays':
|
||||||
parameters:
|
parameters:
|
||||||
- schema:
|
- schema:
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::str;
|
||||||
use crate::relay_loop::run_relay_loop;
|
use crate::relay_loop::run_relay_loop;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use emgauwa_lib::db::{DbController, DbRelay};
|
use emgauwa_lib::db::{DbController, DbRelay};
|
||||||
|
use emgauwa_lib::handlers::v1::ws::controllers::ControllerWsAction;
|
||||||
use emgauwa_lib::models::convert_db_list;
|
use emgauwa_lib::models::convert_db_list;
|
||||||
use emgauwa_lib::types::ControllerUid;
|
use emgauwa_lib::types::ControllerUid;
|
||||||
use emgauwa_lib::{db, models};
|
use emgauwa_lib::{db, models};
|
||||||
|
@ -96,10 +97,6 @@ async fn main() {
|
||||||
relays,
|
relays,
|
||||||
};
|
};
|
||||||
|
|
||||||
let this_json = serde_json::to_string(&this).unwrap();
|
|
||||||
|
|
||||||
println!("{}", this_json);
|
|
||||||
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"ws://{}:{}/api/v1/ws/controllers",
|
"ws://{}:{}/api/v1/ws/controllers",
|
||||||
settings.core.host, settings.core.port
|
settings.core.host, settings.core.port
|
||||||
|
@ -112,7 +109,11 @@ async fn main() {
|
||||||
|
|
||||||
let (mut write, read) = ws_stream.split();
|
let (mut write, read) = ws_stream.split();
|
||||||
|
|
||||||
write.send(Message::text(this_json)).await.unwrap();
|
let ws_action = ControllerWsAction::Register(this);
|
||||||
|
println!("Sending action: {:?}", ws_action);
|
||||||
|
let ws_action_json = serde_json::to_string(&ws_action).unwrap();
|
||||||
|
println!("Sending json: {}", ws_action_json);
|
||||||
|
write.send(Message::text(ws_action_json)).await.unwrap();
|
||||||
let ws_to_stdout = read.for_each(handle_message);
|
let ws_to_stdout = read.for_each(handle_message);
|
||||||
let stdin_to_ws = stdin_rx.map(Ok).forward(write);
|
let stdin_to_ws = stdin_rx.map(Ok).forward(write);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ use tokio::time;
|
||||||
pub async fn run_relay_loop(settings: Settings) {
|
pub async fn run_relay_loop(settings: Settings) {
|
||||||
let default_duration = Duration::from_millis(1000);
|
let default_duration = Duration::from_millis(1000);
|
||||||
loop {
|
loop {
|
||||||
// naivetime timestamp for now
|
|
||||||
let next_timestamp = Local::now().naive_local().time() + default_duration;
|
let next_timestamp = Local::now().naive_local().time() + default_duration;
|
||||||
time::sleep(default_duration).await;
|
time::sleep(default_duration).await;
|
||||||
println!("Relay loop: {}", next_timestamp)
|
println!("Relay loop: {}", next_timestamp)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use serde_derive::Serialize;
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
use sqlx::pool::PoolConnection;
|
use sqlx::pool::PoolConnection;
|
||||||
|
@ -8,9 +8,11 @@ use crate::db::errors::DatabaseError;
|
||||||
use crate::db::DbTag;
|
use crate::db::DbTag;
|
||||||
use crate::types::ControllerUid;
|
use crate::types::ControllerUid;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct DbController {
|
pub struct DbController {
|
||||||
|
#[serde(skip)]
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
#[serde(rename = "id")]
|
||||||
pub uid: ControllerUid,
|
pub uid: ControllerUid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub relay_count: i64,
|
pub relay_count: i64,
|
||||||
|
@ -51,6 +53,19 @@ impl DbController {
|
||||||
.map_err(DatabaseError::from)
|
.map_err(DatabaseError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_by_uid_or_create(
|
||||||
|
conn: &mut PoolConnection<Sqlite>,
|
||||||
|
uid: &ControllerUid,
|
||||||
|
new_name: &str,
|
||||||
|
new_relay_count: i64,
|
||||||
|
new_active: bool,
|
||||||
|
) -> Result<DbController, DatabaseError> {
|
||||||
|
match DbController::get_by_uid(conn, uid).await? {
|
||||||
|
Some(tag) => Ok(tag),
|
||||||
|
None => DbController::create(conn, uid, new_name, new_relay_count, new_active).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_by_tag(
|
pub async fn get_by_tag(
|
||||||
conn: &mut PoolConnection<Sqlite>,
|
conn: &mut PoolConnection<Sqlite>,
|
||||||
tag: &DbTag,
|
tag: &DbTag,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use serde_derive::Serialize;
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
use crate::db::DbController;
|
use crate::db::DbController;
|
||||||
|
@ -8,7 +8,7 @@ use sqlx::Sqlite;
|
||||||
use crate::db::errors::DatabaseError;
|
use crate::db::errors::DatabaseError;
|
||||||
use crate::db::DbTag;
|
use crate::db::DbTag;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Clone, sqlx::FromRow)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct DbRelay {
|
pub struct DbRelay {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
@ -51,6 +51,18 @@ impl DbRelay {
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_by_controller_and_num_or_create(
|
||||||
|
conn: &mut PoolConnection<Sqlite>,
|
||||||
|
controller: &DbController,
|
||||||
|
number: i64,
|
||||||
|
new_name: &str,
|
||||||
|
) -> Result<DbRelay, DatabaseError> {
|
||||||
|
match DbRelay::get_by_controller_and_num(conn, controller, number).await? {
|
||||||
|
Some(relay) => Ok(relay),
|
||||||
|
None => DbRelay::create(conn, new_name, number, controller).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_by_tag(
|
pub async fn get_by_tag(
|
||||||
conn: &mut PoolConnection<Sqlite>,
|
conn: &mut PoolConnection<Sqlite>,
|
||||||
tag: &DbTag,
|
tag: &DbTag,
|
||||||
|
|
|
@ -10,11 +10,11 @@ use crate::db::model_utils::Period;
|
||||||
use crate::db::DbTag;
|
use crate::db::DbTag;
|
||||||
use crate::types::ScheduleUid;
|
use crate::types::ScheduleUid;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct DbSchedule {
|
pub struct DbSchedule {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
#[serde(rename(serialize = "id"))]
|
#[serde(rename = "id")]
|
||||||
pub uid: ScheduleUid,
|
pub uid: ScheduleUid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub periods: DbPeriods,
|
pub periods: DbPeriods,
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
use crate::db::DbSchedule;
|
use crate::db::errors::DatabaseError;
|
||||||
|
use crate::db::{DbController, DbRelay};
|
||||||
use crate::handlers::errors::ApiError;
|
use crate::handlers::errors::ApiError;
|
||||||
|
use crate::models::Controller;
|
||||||
use actix::{Actor, StreamHandler};
|
use actix::{Actor, StreamHandler};
|
||||||
use actix_web::{get, web, HttpRequest, HttpResponse};
|
use actix_web::{get, web, HttpRequest, HttpResponse};
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
use actix_web_actors::ws::ProtocolError;
|
use actix_web_actors::ws::ProtocolError;
|
||||||
|
use futures::FutureExt;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use sqlx::pool::PoolConnection;
|
||||||
use sqlx::{Pool, Sqlite};
|
use sqlx::{Pool, Sqlite};
|
||||||
use ws::Message;
|
use ws::Message;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum ControllerWsAction {
|
||||||
|
Register(Controller),
|
||||||
|
}
|
||||||
|
|
||||||
struct ControllerWs {
|
struct ControllerWs {
|
||||||
pub pool: Pool<Sqlite>,
|
pub pool: Pool<Sqlite>,
|
||||||
}
|
}
|
||||||
|
@ -15,24 +25,57 @@ impl Actor for ControllerWs {
|
||||||
type Context = ws::WebsocketContext<Self>;
|
type Context = ws::WebsocketContext<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_schedules(pool: &mut Pool<Sqlite>) -> Result<Vec<DbSchedule>, ApiError> {
|
|
||||||
let mut pool_conn = pool.acquire().await?;
|
|
||||||
|
|
||||||
Ok(DbSchedule::get_all(&mut pool_conn).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StreamHandler<Result<Message, ProtocolError>> for ControllerWs {
|
impl StreamHandler<Result<Message, ProtocolError>> for ControllerWs {
|
||||||
fn handle(&mut self, msg: Result<Message, ProtocolError>, ctx: &mut Self::Context) {
|
fn handle(&mut self, msg: Result<Message, ProtocolError>, ctx: &mut Self::Context) {
|
||||||
let schedules = futures::executor::block_on(get_schedules(&mut self.pool)).unwrap();
|
let mut pool_conn = futures::executor::block_on(self.pool.acquire()).unwrap();
|
||||||
let schedules_json = serde_json::to_string(&schedules).unwrap();
|
|
||||||
match msg {
|
match msg {
|
||||||
Ok(Message::Ping(msg)) => ctx.pong(&msg),
|
Ok(Message::Ping(msg)) => ctx.pong(&msg),
|
||||||
Ok(Message::Text(text)) => {
|
Ok(Message::Text(text)) => {
|
||||||
println!("Got text: {}", text);
|
let action: ControllerWsAction = serde_json::from_str(&text).unwrap();
|
||||||
ctx.text(schedules_json)
|
let action_res = futures::executor::block_on(handle_action(&mut pool_conn, action));
|
||||||
|
if let Err(e) = action_res {
|
||||||
|
log::error!("Error handling action: {:?}", e);
|
||||||
|
ctx.text(serde_json::to_string(&e).unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//let schedules = futures::executor::block_on(DbSchedule::get_all(&mut pool_conn)).unwrap();
|
||||||
|
//let schedules_json = serde_json::to_string(&schedules).unwrap();
|
||||||
|
//ctx.text(schedules_json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_action(
|
||||||
|
conn: &mut PoolConnection<Sqlite>,
|
||||||
|
action: ControllerWsAction,
|
||||||
|
) -> Result<(), DatabaseError> {
|
||||||
|
match action {
|
||||||
|
ControllerWsAction::Register(controller) => {
|
||||||
|
log::info!("Registering controller: {:?}", controller);
|
||||||
|
let c = &controller.controller;
|
||||||
|
let controller_db =
|
||||||
|
DbController::get_by_uid_or_create(conn, &c.uid, &c.name, c.relay_count, c.active)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
println!("Controller: {:?}", controller_db);
|
||||||
|
|
||||||
|
for relay in &controller.relays {
|
||||||
|
let r = &relay.relay;
|
||||||
|
let relay_db = DbRelay::get_by_controller_and_num_or_create(
|
||||||
|
conn,
|
||||||
|
&controller_db,
|
||||||
|
r.number,
|
||||||
|
&r.name,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
println!("Controller relay: {:?}", relay_db);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::db::errors::DatabaseError;
|
use crate::db::errors::DatabaseError;
|
||||||
use crate::db::{DbRelay, DbSchedule};
|
use crate::db::{DbRelay, DbSchedule};
|
||||||
|
use crate::types::ControllerUid;
|
||||||
use futures::executor;
|
use futures::executor;
|
||||||
use serde_derive::Serialize;
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use sqlx::pool::PoolConnection;
|
use sqlx::pool::PoolConnection;
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
|
|
||||||
|
@ -17,22 +18,23 @@ pub trait FromDbModel {
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Schedule {
|
pub struct Schedule {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub schedule: DbSchedule,
|
pub schedule: DbSchedule,
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Relay {
|
pub struct Relay {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub relay: DbRelay,
|
pub relay: DbRelay,
|
||||||
pub controller: db::DbController,
|
pub controller: db::DbController,
|
||||||
|
pub controller_id: ControllerUid,
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub controller: db::DbController,
|
pub controller: db::DbController,
|
||||||
|
@ -62,11 +64,13 @@ impl FromDbModel for Relay {
|
||||||
) -> Result<Self, DatabaseError> {
|
) -> Result<Self, DatabaseError> {
|
||||||
let relay = db_model.clone();
|
let relay = db_model.clone();
|
||||||
let controller = executor::block_on(relay.get_controller(conn))?;
|
let controller = executor::block_on(relay.get_controller(conn))?;
|
||||||
|
let controller_id = controller.uid.clone();
|
||||||
let tags = executor::block_on(relay.get_tags(conn))?;
|
let tags = executor::block_on(relay.get_tags(conn))?;
|
||||||
|
|
||||||
Ok(Relay {
|
Ok(Relay {
|
||||||
relay,
|
relay,
|
||||||
controller,
|
controller,
|
||||||
|
controller_id,
|
||||||
tags,
|
tags,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
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 sqlx::{Decode, Encode, Sqlite, Type};
|
||||||
|
use std::str::FromStr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -24,6 +25,16 @@ impl Serialize for ControllerUid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for ControllerUid {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Self::try_from(String::deserialize(deserializer)?.as_str())
|
||||||
|
.map_err(|_| serde::de::Error::custom("invalid controller uid"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&ControllerUid> for String {
|
impl From<&ControllerUid> for String {
|
||||||
fn from(uid: &ControllerUid) -> String {
|
fn from(uid: &ControllerUid) -> String {
|
||||||
uid.0.as_hyphenated().to_string()
|
uid.0.as_hyphenated().to_string()
|
||||||
|
@ -55,6 +66,12 @@ impl<'r> Decode<'r, Sqlite> for ControllerUid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&str> for ControllerUid {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Self(Uuid::from_str(value).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&[u8]> for ControllerUid {
|
impl From<&[u8]> for ControllerUid {
|
||||||
fn from(value: &[u8]) -> Self {
|
fn from(value: &[u8]) -> Self {
|
||||||
Self(Uuid::from_slice(value).unwrap())
|
Self(Uuid::from_slice(value).unwrap())
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::convert::TryFrom;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use sqlx::database::HasArguments;
|
use sqlx::database::HasArguments;
|
||||||
use sqlx::encode::IsNull;
|
use sqlx::encode::IsNull;
|
||||||
use sqlx::error::BoxDynError;
|
use sqlx::error::BoxDynError;
|
||||||
|
@ -75,6 +75,16 @@ impl Serialize for ScheduleUid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for ScheduleUid {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Self::try_from(String::deserialize(deserializer)?.as_str())
|
||||||
|
.map_err(|_| serde::de::Error::custom("invalid schedule uid"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Uuid> for ScheduleUid {
|
impl From<Uuid> for ScheduleUid {
|
||||||
fn from(uid: Uuid) -> Self {
|
fn from(uid: Uuid) -> Self {
|
||||||
match uid.as_u128() {
|
match uid.as_u128() {
|
||||||
|
|
Loading…
Reference in a new issue