view rust/src/main.rs @ 631:c57821a60e51 rust

rust work in progress ?
author Matt Johnston <matt@ucc.asn.au>
date Sat, 06 Jul 2019 18:28:34 +0800
parents d5075136442f
children bde302def78e
line wrap: on
line source

#![feature(proc_macro, conservative_impl_trait, generators)]
#![feature(await_macro, async_await, futures_api)]


#[macro_use]
extern crate tokio;
#[macro_use]
extern crate futures;

#[macro_use]
extern crate log;
extern crate env_logger;
extern crate rustc_serialize;
extern crate time;
extern crate serde_json;
extern crate libc;
extern crate atomicwrites;
extern crate hyper;

#[macro_use] 
extern crate lazy_static;

#[macro_use]
extern crate serde_derive;
extern crate serde;

extern crate toml;

extern crate docopt;

use std::io;

use tokio_core::reactor::   Core;
use futures::{Stream,Sink,Future};
use futures::sync::{mpsc};
use sensor::Sensor;

mod config;
mod sensor;
mod fridge;
mod types;
mod params;

use types::*;
use config::Config;

fn run(config: &Config, nowait: bool, testmode: bool) {

    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let params = params::Params::load(&config);
    let mut fridge = fridge::Fridge::new(&config, nowait, params, &handle);

    let sensor_stream = if testmode {
        sensor::TestSensor::new(config).stream(&handle)
    } else {
        sensor::OneWireSensor::new(config).stream(&handle)
    };

    // Send the sensors of interest to the fridge (fridge_reading_s),
    // while streaming them all to the web sender.
    let (fridge_reading_s, fridge_reading_r) = mpsc::channel(1);
    let fridge_reading_r = fridge_reading_r.map_err(|e| TemplogError::new("Problem with fridge_reading_r channel"));
    let sensor_stream = sensor_stream.map(|r| {
        debug!("sensors {:?}", r);
        let msg = fridge::Message::Sensor {
            wort: r.get_temp(&config.WORT_NAME), 
            fridge: r.get_temp(&config.FRIDGE_NAME)
        };
        let t = fridge_reading_s.clone().send(msg)
                    .map(|_| ())
                    .map_err(|e| {
                        warn!("Send error in fridge_reading_s: {}", e.to_string());
                        ()
                    });
        handle.spawn(t);
        r
    });

    let param_stream = params::ParamWaiter::stream(config.clone(), handle.clone());
    let param_stream = param_stream.map(|p| {
            fridge::Message::Params(p)
        });

    let timeouts = fridge.wakeups();

    // forward all the different types of messages to the fridge
    let all_fridge = param_stream.select(timeouts).select(fridge_reading_r).forward(fridge) .map(|_| () );

    let all_readings = sensor_stream.for_each(|_| Ok(()));

    // run forever
    let all = all_fridge.select(all_readings);
    core.run(all).ok();
}

const USAGE: &'static str = "\
Wort Temperature
Matt Johnston 2017 [email protected]
Usage: wort-templog [--help] [--new] [--daemon] [--debug] [--test] [--defconf] [--thisconf] [--nowait]

Options:
  -h, --help
  --new         Replace existing running instance
  -D, --daemon  Run in background
  -d, --debug
  -t, --test    Use fake sensors etc
  --nowait      Skip initial fridge wait
  --defconf     Print default config (customise in local.conf)
  --thisconf    Print used config
";

#[derive(RustcDecodable)]
struct Args {
    flag_new: bool,
    flag_daemon: bool,
    flag_debug: bool,
    flag_test: bool,
    flag_defconf: bool,
    flag_thisconf: bool,
    flag_nowait: bool,
}

fn setup_log(debug: bool) {
    let loglevel = if debug {
       log::LogLevelFilter::Debug
    } else {
       log::LogLevelFilter::Info
    };

    let format = |record: &log::LogRecord| {
        let datefmt = "%Y-%m-%d %I:%M:%S %p";
        let ts = time::strftime(datefmt, &time::now()).unwrap();
        format!("{}: {} - {}", ts, record.level(), record.args())
    };


    let mut builder = env_logger::LogBuilder::new();
    builder.format(format).filter(Some("wort_templog"), loglevel);
    builder.init().unwrap();
}

fn handle_args() -> Args {
    let args: Args = docopt::Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    if args.flag_defconf {
        println!("Default configuration:\n{}\n\n{}",
            "(custom options go in local.conf)",
            config::Config::default().to_toml_string());
        std::process::exit(0);
    }
    args
}

fn load_config() -> Config {
    let nconfig = config::Config::default();

    let conf_filename = "local.conf";
    nconfig.merge_file(conf_filename)
        .unwrap_or_else(|e| {
            if let TemplogErrorKind::Io(ref ioe) = *e.kind() {
                if let Some(errno) = ioe.raw_os_error() {
                    if errno == libc::ENOENT {
                        return nconfig;
                    }
                }
            }

            println!("Couldn't parse {}: {}", conf_filename, e);
            std::process::exit(1);
    })
}

fn main() {

    let args = handle_args();
    setup_log(args.flag_debug);
    //env_logger::init().unwrap();

    info!("wort-templog");
    debug!("debug mode");

    let config = load_config();

    if args.flag_thisconf {
        println!("Current configuration:\n\n{}",
            config.to_toml_string());
        std::process::exit(0);
    }

    run(&config, args.flag_nowait, args.flag_test);
}