1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#![forbid(unsafe_code)]
// to avoid the warning from diesel macros
#![allow(proc_macro_derive_resolution_fallback)]

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;

pub mod app;
pub mod cli;
pub mod errors;
pub mod handlers;
pub mod models;
pub mod rpi;
pub mod schema;
pub mod settings;
pub mod setup;
pub mod utilities;
pub mod validation;

use crate::app::AppState;
use crate::cli::get_cli_args;
use crate::handlers::DbExecutor;
use crate::setup::setup_rpi_and_db;
use crate::utilities::reset_table_gpio_state;
use crate::validation::validate_setup;
use actix::SyncArbiter;
use actix_web::server;
use diesel::{r2d2::ConnectionManager, SqliteConnection};
use dotenv::dotenv;

pub fn setup_and_run() {
    // Get CLI args
    let cli_args = get_cli_args();

    // Get settings from configuration file
    let config = settings::Settings::new(cli_args).expect("Could not read config file");
    let database_url = &config.database.database_url;
    let hostname = config.webserver.hostname;
    let port = config.webserver.port;

    // Read environment variables from .env - must come before env_logger::init()
    dotenv().ok();

    // Initialize logger
    env_logger::init();

    // Create database connection pool
    let manager = ConnectionManager::<SqliteConnection>::new(database_url.to_string());
    let pool = r2d2::Pool::builder()
        .build(manager)
        .expect("Failed to create r2d2 pool.");
    let connection = pool.get().expect("Failed to acquire connection");

    // Reset database
    reset_table_gpio_state(&connection).expect("Unable to update table 'gpio_state'");

    // Check consistency of parsed_variables
    validate_setup(&config.gpioconfig).expect("Provided setup variables are inconsistent");

    // Arc<Mutex<rppal::gpio::Gpio>> or ARM, Arc<Mutex<i32>> on other architectures
    let gpio_arc_mutex = rpi::create_gpio_arc_mutex().expect("Could not acquire GPIO");

    // If variables are consistent, setup Raspberry Pi and database
    setup_rpi_and_db(&config.gpioconfig, &connection, gpio_arc_mutex.clone())
        .expect("Error when setting up Raspberry Pi and database");

    let sys = actix::System::new("raspberry-web");
    // https://github.com/actix/actix-website/blob/master/content/docs/databases.md
    // https://docs.rs/actix-web/0.6.3/actix_web/struct.State.html
    let addr = SyncArbiter::start(3, move || DbExecutor(pool.clone()));

    let ip_port = format!("{}:{}", hostname, port);
    let _server = server::new(move || {
        app::create_app(AppState {
            db: addr.clone(),
            gpio_arc_mutex: gpio_arc_mutex.clone(),
        })
    })
    .bind(&ip_port)
    .expect(&format!("Can not bind to {}", &ip_port))
    .start();

    let _sys = sys.run();
}