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
|
||||
/*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
*.db-wal
|
||||
|
||||
# Config file
|
||||
/sprinklers_rs.json
|
@ -5,5 +5,6 @@ members = [
|
||||
"sprinklers_database",
|
||||
"sprinklers_actors",
|
||||
"sprinklers_mqtt",
|
||||
"sprinklers_linux",
|
||||
"sprinklers_rs"
|
||||
]
|
@ -110,7 +110,7 @@ impl Default for SecRunnerState {
|
||||
pub type SecRunnerStateRecv = watch::Receiver<SecRunnerState>;
|
||||
|
||||
struct SectionRunnerInner {
|
||||
interface: Arc<dyn SectionInterface + Sync>,
|
||||
interface: Arc<dyn SectionInterface>,
|
||||
event_send: Option<SectionEventSend>,
|
||||
state_send: watch::Sender<SecRunnerState>,
|
||||
delay_future: Option<SpawnHandle>,
|
||||
@ -289,10 +289,16 @@ impl Actor for SectionRunnerActor {
|
||||
|
||||
fn started(&mut self, _ctx: &mut Self::Context) {
|
||||
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) {
|
||||
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 {
|
||||
fn new(
|
||||
interface: Arc<dyn SectionInterface + Sync>,
|
||||
interface: Arc<dyn SectionInterface>,
|
||||
state_send: watch::Sender<SecRunnerState>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -536,7 +542,7 @@ pub struct SectionRunner {
|
||||
|
||||
#[allow(dead_code)]
|
||||
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 addr = SectionRunnerActor::new(interface, state_send).start();
|
||||
Self {
|
||||
|
@ -4,7 +4,7 @@ use tracing::debug;
|
||||
|
||||
pub type SecId = u32;
|
||||
|
||||
pub trait SectionInterface: Send {
|
||||
pub trait SectionInterface: Send + Sync {
|
||||
fn num_sections(&self) -> SecId;
|
||||
fn set_section_state(&self, id: SecId, running: 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
|
||||
|
||||
[features]
|
||||
default = ["sprinklers_linux"]
|
||||
bundled_sqlite = ["sprinklers_database/bundled"]
|
||||
|
||||
[dependencies]
|
||||
@ -14,14 +15,17 @@ sprinklers_core = { path = "../sprinklers_core" }
|
||||
sprinklers_database = { path = "../sprinklers_database" }
|
||||
sprinklers_actors = { path = "../sprinklers_actors" }
|
||||
sprinklers_mqtt = { path = "../sprinklers_mqtt" }
|
||||
sprinklers_linux = { path = "../sprinklers_linux", optional = true }
|
||||
|
||||
color-eyre = "0.5.1"
|
||||
eyre = "0.6.0"
|
||||
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-rt = "1.1.1"
|
||||
chrono = "0.4.19"
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
config = { version = "0.10.1", default-features = false, features = ["json"] }
|
||||
|
||||
[dependencies.tracing-subscriber]
|
||||
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)]
|
||||
|
||||
// mod option_future;
|
||||
mod section_interface;
|
||||
mod settings;
|
||||
mod state_manager;
|
||||
|
||||
use sprinklers_actors as actors;
|
||||
use sprinklers_core::section_interface::MockSectionInterface;
|
||||
use sprinklers_database as database;
|
||||
use sprinklers_mqtt as mqtt;
|
||||
|
||||
use eyre::Result;
|
||||
use std::sync::Arc;
|
||||
use eyre::{Result, WrapErr};
|
||||
use settings::Settings;
|
||||
use tracing::{debug, info};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
tracing_subscriber::fmt()
|
||||
.with_ansi(true)
|
||||
.with_env_filter(
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
|
||||
)
|
||||
.init();
|
||||
|
||||
let settings: Settings = Settings::new().wrap_err("could not load settings")?;
|
||||
|
||||
info!("Starting sprinklers_rs...");
|
||||
color_eyre::install()?;
|
||||
|
||||
let db_conn = database::setup_db()?;
|
||||
|
||||
@ -32,19 +36,13 @@ async fn main() -> Result<()> {
|
||||
debug!(section = debug(&sec), "read section");
|
||||
}
|
||||
|
||||
// TODO: Section interface which actual does something. Preferrably selectable somehow
|
||||
let section_interface: Arc<_> = MockSectionInterface::new(6).into();
|
||||
let section_interface = settings.section_interface.build()?;
|
||||
let mut section_runner = actors::SectionRunner::new(section_interface);
|
||||
let mut program_runner = actors::ProgramRunner::new(section_runner.clone());
|
||||
|
||||
let state_manager = crate::state_manager::StateManagerThread::start(db_conn);
|
||||
|
||||
let mqtt_options = mqtt::Options {
|
||||
broker_host: "localhost".into(),
|
||||
broker_port: 1883,
|
||||
device_id: "sprinklers_rs-0001".into(),
|
||||
client_id: "sprinklers_rs-0001".into(),
|
||||
};
|
||||
let mqtt_options = settings.mqtt;
|
||||
// TODO: have ability to update sections / other data
|
||||
let request_context = mqtt::RequestContext {
|
||||
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