Add better configuration support
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
And allow switching between different section interface implementations
This commit is contained in:
parent
11eba1a9e5
commit
8408f21a72
5
.gitignore
vendored
5
.gitignore
vendored
@ -8,4 +8,7 @@ Cargo.lock
|
|||||||
# Sqlite databases
|
# Sqlite databases
|
||||||
/*.db
|
/*.db
|
||||||
*.db-shm
|
*.db-shm
|
||||||
*.db-wal
|
*.db-wal
|
||||||
|
|
||||||
|
# Config file
|
||||||
|
/sprinklers_rs.json
|
@ -5,5 +5,6 @@ members = [
|
|||||||
"sprinklers_database",
|
"sprinklers_database",
|
||||||
"sprinklers_actors",
|
"sprinklers_actors",
|
||||||
"sprinklers_mqtt",
|
"sprinklers_mqtt",
|
||||||
|
"sprinklers_linux",
|
||||||
"sprinklers_rs"
|
"sprinklers_rs"
|
||||||
]
|
]
|
@ -110,7 +110,7 @@ impl Default for SecRunnerState {
|
|||||||
pub type SecRunnerStateRecv = watch::Receiver<SecRunnerState>;
|
pub type SecRunnerStateRecv = watch::Receiver<SecRunnerState>;
|
||||||
|
|
||||||
struct SectionRunnerInner {
|
struct SectionRunnerInner {
|
||||||
interface: Arc<dyn SectionInterface + Sync>,
|
interface: Arc<dyn SectionInterface>,
|
||||||
event_send: Option<SectionEventSend>,
|
event_send: Option<SectionEventSend>,
|
||||||
state_send: watch::Sender<SecRunnerState>,
|
state_send: watch::Sender<SecRunnerState>,
|
||||||
delay_future: Option<SpawnHandle>,
|
delay_future: Option<SpawnHandle>,
|
||||||
@ -289,10 +289,16 @@ impl Actor for SectionRunnerActor {
|
|||||||
|
|
||||||
fn started(&mut self, _ctx: &mut Self::Context) {
|
fn started(&mut self, _ctx: &mut Self::Context) {
|
||||||
trace!("section_runner starting");
|
trace!("section_runner starting");
|
||||||
|
for i in 0..self.inner.interface.num_sections() {
|
||||||
|
self.inner.interface.set_section_state(i, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stopped(&mut self, _ctx: &mut Self::Context) {
|
fn stopped(&mut self, _ctx: &mut Self::Context) {
|
||||||
trace!("section_runner stopped");
|
trace!("section_runner stopped");
|
||||||
|
for i in 0..self.inner.interface.num_sections() {
|
||||||
|
self.inner.interface.set_section_state(i, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,7 +458,7 @@ impl Handler<Process> for SectionRunnerActor {
|
|||||||
|
|
||||||
impl SectionRunnerActor {
|
impl SectionRunnerActor {
|
||||||
fn new(
|
fn new(
|
||||||
interface: Arc<dyn SectionInterface + Sync>,
|
interface: Arc<dyn SectionInterface>,
|
||||||
state_send: watch::Sender<SecRunnerState>,
|
state_send: watch::Sender<SecRunnerState>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -536,7 +542,7 @@ pub struct SectionRunner {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl SectionRunner {
|
impl SectionRunner {
|
||||||
pub fn new(interface: Arc<dyn SectionInterface + Sync>) -> Self {
|
pub fn new(interface: Arc<dyn SectionInterface>) -> Self {
|
||||||
let (state_send, state_recv) = watch::channel(SecRunnerState::default());
|
let (state_send, state_recv) = watch::channel(SecRunnerState::default());
|
||||||
let addr = SectionRunnerActor::new(interface, state_send).start();
|
let addr = SectionRunnerActor::new(interface, state_send).start();
|
||||||
Self {
|
Self {
|
||||||
|
@ -4,7 +4,7 @@ use tracing::debug;
|
|||||||
|
|
||||||
pub type SecId = u32;
|
pub type SecId = u32;
|
||||||
|
|
||||||
pub trait SectionInterface: Send {
|
pub trait SectionInterface: Send + Sync {
|
||||||
fn num_sections(&self) -> SecId;
|
fn num_sections(&self) -> SecId;
|
||||||
fn set_section_state(&self, id: SecId, running: bool);
|
fn set_section_state(&self, id: SecId, running: bool);
|
||||||
fn get_section_state(&self, id: SecId) -> bool;
|
fn get_section_state(&self, id: SecId) -> bool;
|
||||||
|
@ -7,6 +7,7 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["sprinklers_linux"]
|
||||||
bundled_sqlite = ["sprinklers_database/bundled"]
|
bundled_sqlite = ["sprinklers_database/bundled"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -14,14 +15,17 @@ sprinklers_core = { path = "../sprinklers_core" }
|
|||||||
sprinklers_database = { path = "../sprinklers_database" }
|
sprinklers_database = { path = "../sprinklers_database" }
|
||||||
sprinklers_actors = { path = "../sprinklers_actors" }
|
sprinklers_actors = { path = "../sprinklers_actors" }
|
||||||
sprinklers_mqtt = { path = "../sprinklers_mqtt" }
|
sprinklers_mqtt = { path = "../sprinklers_mqtt" }
|
||||||
|
sprinklers_linux = { path = "../sprinklers_linux", optional = true }
|
||||||
|
|
||||||
color-eyre = "0.5.1"
|
color-eyre = "0.5.1"
|
||||||
eyre = "0.6.0"
|
eyre = "0.6.0"
|
||||||
tokio = "0.2.22"
|
tokio = "0.2.22"
|
||||||
tracing = { version = "0.1.19", features = ["log"] }
|
tracing = { version = "0.1.19" }
|
||||||
actix = { version = "0.10.0", default-features = false }
|
actix = { version = "0.10.0", default-features = false }
|
||||||
actix-rt = "1.1.1"
|
actix-rt = "1.1.1"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
|
serde = { version = "1.0.116", features = ["derive"] }
|
||||||
|
config = { version = "0.10.1", default-features = false, features = ["json"] }
|
||||||
|
|
||||||
[dependencies.tracing-subscriber]
|
[dependencies.tracing-subscriber]
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
12
sprinklers_rs/sprinklers_rs.default.json
Normal file
12
sprinklers_rs/sprinklers_rs.default.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"mqtt": {
|
||||||
|
"broker_host": "localhost",
|
||||||
|
"broker_port": 1883,
|
||||||
|
"client_id": "sprinklers_rs-0001",
|
||||||
|
"device_id": "sprinklers_rs-0001"
|
||||||
|
},
|
||||||
|
"section_interface": {
|
||||||
|
"provider": "Mock",
|
||||||
|
"num_sections": 6
|
||||||
|
}
|
||||||
|
}
|
@ -2,28 +2,32 @@
|
|||||||
#![warn(clippy::print_stdout)]
|
#![warn(clippy::print_stdout)]
|
||||||
|
|
||||||
// mod option_future;
|
// mod option_future;
|
||||||
|
mod section_interface;
|
||||||
|
mod settings;
|
||||||
mod state_manager;
|
mod state_manager;
|
||||||
|
|
||||||
use sprinklers_actors as actors;
|
use sprinklers_actors as actors;
|
||||||
use sprinklers_core::section_interface::MockSectionInterface;
|
|
||||||
use sprinklers_database as database;
|
use sprinklers_database as database;
|
||||||
use sprinklers_mqtt as mqtt;
|
use sprinklers_mqtt as mqtt;
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::{Result, WrapErr};
|
||||||
use std::sync::Arc;
|
use settings::Settings;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
color_eyre::install()?;
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_ansi(true)
|
.with_ansi(true)
|
||||||
.with_env_filter(
|
.with_env_filter(
|
||||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
|
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
|
||||||
)
|
)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
let settings: Settings = Settings::new().wrap_err("could not load settings")?;
|
||||||
|
|
||||||
info!("Starting sprinklers_rs...");
|
info!("Starting sprinklers_rs...");
|
||||||
color_eyre::install()?;
|
|
||||||
|
|
||||||
let db_conn = database::setup_db()?;
|
let db_conn = database::setup_db()?;
|
||||||
|
|
||||||
@ -32,19 +36,13 @@ async fn main() -> Result<()> {
|
|||||||
debug!(section = debug(&sec), "read section");
|
debug!(section = debug(&sec), "read section");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Section interface which actual does something. Preferrably selectable somehow
|
let section_interface = settings.section_interface.build()?;
|
||||||
let section_interface: Arc<_> = MockSectionInterface::new(6).into();
|
|
||||||
let mut section_runner = actors::SectionRunner::new(section_interface);
|
let mut section_runner = actors::SectionRunner::new(section_interface);
|
||||||
let mut program_runner = actors::ProgramRunner::new(section_runner.clone());
|
let mut program_runner = actors::ProgramRunner::new(section_runner.clone());
|
||||||
|
|
||||||
let state_manager = crate::state_manager::StateManagerThread::start(db_conn);
|
let state_manager = crate::state_manager::StateManagerThread::start(db_conn);
|
||||||
|
|
||||||
let mqtt_options = mqtt::Options {
|
let mqtt_options = settings.mqtt;
|
||||||
broker_host: "localhost".into(),
|
|
||||||
broker_port: 1883,
|
|
||||||
device_id: "sprinklers_rs-0001".into(),
|
|
||||||
client_id: "sprinklers_rs-0001".into(),
|
|
||||||
};
|
|
||||||
// TODO: have ability to update sections / other data
|
// TODO: have ability to update sections / other data
|
||||||
let request_context = mqtt::RequestContext {
|
let request_context = mqtt::RequestContext {
|
||||||
sections: sections.clone(),
|
sections: sections.clone(),
|
||||||
|
37
sprinklers_rs/src/section_interface.rs
Normal file
37
sprinklers_rs/src/section_interface.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use sprinklers_core::section_interface::{MockSectionInterface, SecId, SectionInterface};
|
||||||
|
|
||||||
|
#[cfg(feature = "sprinklers_linux")]
|
||||||
|
use sprinklers_linux::LinuxGpioConfig;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "provider")]
|
||||||
|
pub enum SectionInterfaceConfig {
|
||||||
|
Mock {
|
||||||
|
num_sections: SecId,
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sprinklers_linux")]
|
||||||
|
LinuxGpio(LinuxGpioConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SectionInterfaceConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
SectionInterfaceConfig::Mock { num_sections: 6 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SectionInterfaceConfig {
|
||||||
|
pub fn build(self) -> eyre::Result<Arc<dyn SectionInterface>> {
|
||||||
|
Ok(match self {
|
||||||
|
SectionInterfaceConfig::Mock { num_sections } => {
|
||||||
|
Arc::new(MockSectionInterface::new(num_sections))
|
||||||
|
}
|
||||||
|
#[cfg(feature = "sprinklers_linux")]
|
||||||
|
SectionInterfaceConfig::LinuxGpio(config) => {
|
||||||
|
Arc::new(config.build()?)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
44
sprinklers_rs/src/settings.rs
Normal file
44
sprinklers_rs/src/settings.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use crate::section_interface::SectionInterfaceConfig;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(remote = "sprinklers_mqtt::Options")]
|
||||||
|
struct MqttOptions {
|
||||||
|
pub broker_host: String,
|
||||||
|
pub broker_port: u16,
|
||||||
|
pub device_id: String,
|
||||||
|
pub client_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Settings {
|
||||||
|
#[serde(with = "MqttOptions")]
|
||||||
|
pub mqtt: sprinklers_mqtt::Options,
|
||||||
|
#[serde(default)]
|
||||||
|
pub section_interface: SectionInterfaceConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn new() -> eyre::Result<Self> {
|
||||||
|
let mut s = config::Config::new();
|
||||||
|
|
||||||
|
let default_config = config::File::from_str(
|
||||||
|
include_str!("../sprinklers_rs.default.json"),
|
||||||
|
config::FileFormat::Json,
|
||||||
|
);
|
||||||
|
s.merge(default_config)?;
|
||||||
|
|
||||||
|
// TODO: specify configuration path from arguments or env
|
||||||
|
s.merge(config::File::with_name("sprinklers_rs").required(false))?;
|
||||||
|
|
||||||
|
s.merge(config::Environment::with_prefix("SPRINKLERS").separator("__"))?;
|
||||||
|
|
||||||
|
let settings: Settings = s.try_into()?;
|
||||||
|
|
||||||
|
trace!("settings: {:#?}", settings);
|
||||||
|
|
||||||
|
Ok(settings)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user