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
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -9,3 +9,6 @@ Cargo.lock
 | 
				
			|||||||
/*.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