This commit is contained in:
Crom (Thibaut CHARLES) 2019-02-17 23:50:18 +01:00
parent 4f1f1ba88e
commit d8ebb16486
Signed by: tcharles
GPG Key ID: 45A3D5F880B9E6D0
3 changed files with 89 additions and 53 deletions

View File

@ -22,6 +22,7 @@ use std::env;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::{Arc, Barrier};
use std::thread; use std::thread;
use crate::filter::*; use crate::filter::*;
@ -73,7 +74,7 @@ fn main() {
simplelog::TermLogger::init( simplelog::TermLogger::init(
level, level,
simplelog::Config { simplelog::Config {
time: Some(simplelog::Level::Debug), time: None,
level: Some(simplelog::Level::Error), level: Some(simplelog::Level::Error),
target: Some(simplelog::Level::Info), target: Some(simplelog::Level::Info),
location: Some(simplelog::Level::Debug), location: Some(simplelog::Level::Debug),
@ -93,10 +94,14 @@ fn main() {
let config: AppConfig = let config: AppConfig =
serde_yaml::from_str(&config_content).expect("Invalid config file content"); serde_yaml::from_str(&config_content).expect("Invalid config file content");
let thread_count = config.monitors.len() + config.outputs.len();
let barrier = Arc::new(Barrier::new(thread_count + 1));
// Monitors -> dispatcher channel // Monitors -> dispatcher channel
let (mon_sender, mon_receiver) = mpsc::channel(); let (mon_sender, mon_receiver) = mpsc::channel();
// Instantiate monitor threads and structs // Instantiate monitor threads and structs
let mut mon_threads = vec![];
for mon_config in config.monitors { for mon_config in config.monitors {
let mon_type = mon_config let mon_type = mon_config
.get("type") .get("type")
@ -106,21 +111,23 @@ fn main() {
.to_owned(); .to_owned();
let snd = mon_sender.clone(); let snd = mon_sender.clone();
let bar = barrier.clone();
thread::spawn(move || { mon_threads.push(thread::spawn(move || {
log::info!("+> monitor: {}", mon_type); log::info!("+> monitor: {}", mon_type);
match monitors::factory(&mon_type, &mon_config) { match monitors::factory(&mon_type, &mon_config) {
Ok(mut mon) => loop { Ok(mut mon) => loop {
bar.wait();
mon.run(&snd); mon.run(&snd);
}, },
Err(e) => log::error!("Cannot instantiate monitor {}: {}", mon_type, e), Err(e) => log::error!("Cannot instantiate monitor {}: {}", mon_type, e),
} }
}); }));
} }
// Instantiate output threads and structs // Instantiate output threads and structs
// let mut outputs: Vec<(Box<Output>, Filter)> = vec![];
let mut output_senders: Vec<mpsc::Sender<Message>> = vec![]; let mut output_senders: Vec<mpsc::Sender<Message>> = vec![];
let mut output_threads = vec![];
for out_config in config.outputs { for out_config in config.outputs {
let out_type = out_config let out_type = out_config
.get("type") .get("type")
@ -133,11 +140,13 @@ fn main() {
output_senders.push(out_sender); output_senders.push(out_sender);
let filter = Filter::new(&out_config); let filter = Filter::new(&out_config);
let bar = barrier.clone();
thread::spawn(move || { output_threads.push(thread::spawn(move || {
log::info!("+> output: {}", out_type); log::info!("+> output: {}", out_type);
match outputs::factory(&out_type, &out_config) { match outputs::factory(&out_type, &out_config) {
Ok(mut output) => loop { Ok(mut output) => loop {
bar.wait();
let message = out_receiver.recv().unwrap(); let message = out_receiver.recv().unwrap();
if filter.is_message_allowed(&message) { if filter.is_message_allowed(&message) {
output.process_message(message); output.process_message(message);
@ -145,10 +154,20 @@ fn main() {
}, },
Err(e) => log::error!("Cannot instantiate output {}: {}", out_type, e), Err(e) => log::error!("Cannot instantiate output {}: {}", out_type, e),
} }
}); }));
} }
let output_senders = output_senders; let output_senders = output_senders;
// Start all the threads
barrier.wait();
log::info!("Started rnetmon");
for t in &mon_threads {
t.thread().unpark();
}
for t in &output_threads {
t.thread().unpark();
}
// Dispatch messages // Dispatch messages
loop { loop {
let message = mon_receiver.recv().unwrap(); let message = mon_receiver.recv().unwrap();

View File

@ -31,14 +31,15 @@ impl Monitor for DHCPLeases {
.get("config") .get("config")
.unwrap_or(&serde_yaml::Mapping::new().into()) .unwrap_or(&serde_yaml::Mapping::new().into())
.clone(); .clone();
let config = serde_yaml::from_value(config_node).expect("Invalid config for dhcp_leases"); let config = serde_yaml::from_value(config_node)?;
// Regex compilation // Regex compilation
let rgx_lease = Regex::new(r"(?s)lease\s+(\d+(?:\.\d+){3})\s*\{\n?(.*?)\}").unwrap(); let rgx_lease = Regex::new(r"(?s)lease\s+(\d+(?:\.\d+){3})\s*\{\n?(.*?)\}").unwrap();
let rgx_mac = let rgx_mac =
Regex::new(r"^\s*hardware\s+ethernet\s([a-f0-9]{2}(?::[a-f0-9]{2}){5})\s*;").unwrap(); Regex::new(r"(?m)^\s*hardware\s+ethernet\s([a-f0-9]{2}(?::[a-f0-9]{2}){5})\s*;")
let rgx_date_start = Regex::new(r"^\s*starts\s+(.*?)\s*;").unwrap(); .unwrap();
let rgx_date_ends = Regex::new(r"^\s*ends\s+(.*?)\s*;").unwrap(); let rgx_date_start = Regex::new(r"(?m)^\s*starts\s+\d+\s+(.*?)\s*;").unwrap();
let rgx_date_ends = Regex::new(r"(?m)^\s*ends\s+\d+\s+(.*?)\s*;").unwrap();
Ok(DHCPLeases { Ok(DHCPLeases {
config: config, config: config,
@ -66,36 +67,38 @@ impl Monitor for DHCPLeases {
let mut unauthorized_macs: Vec<String> = vec![]; let mut unauthorized_macs: Vec<String> = vec![];
for cap in self.rgx_lease.captures_iter(&config_content) { for cap in self.rgx_lease.captures_iter(&config_content) {
let ip = cap.get(1).unwrap().as_str(); let _ip = cap.get(1).unwrap().as_str();
let content = cap.get(2).unwrap().as_str(); let content = cap.get(2).unwrap().as_str();
let mac = self let mac = self
.rgx_mac .rgx_mac
.captures(content) .captures(content)
.expect("No 'hardware ethernet' field found for MAC address") .expect(&format!(
.get(0) "No 'hardware ethernet' field found for MAC address in {}",
content
))
.get(1)
.unwrap() .unwrap()
.as_str(); .as_str();
let starts_str = self let starts_str = self
.rgx_date_start .rgx_date_start
.captures(content) .captures(content)
.expect("No 'starts' field found in lease") .expect("No 'starts' field found in lease")
.get(0) .get(1)
.unwrap() .unwrap()
.as_str(); .as_str();
let starts = let starts =
chrono::naive::NaiveDateTime::parse_from_str(starts_str, "%U %Y/%m/%d %H:%M:%S") chrono::naive::NaiveDateTime::parse_from_str(starts_str, "%Y/%m/%d %H:%M:%S")
.expect("Bad date format"); .expect(&format!("Bad date format: '{}'", starts_str));
let ends_str = self let ends_str = self
.rgx_date_ends .rgx_date_ends
.captures(content) .captures(content)
.expect("No 'ends' field found in lease") .expect("No 'ends' field found in lease")
.get(0) .get(1)
.unwrap() .unwrap()
.as_str(); .as_str();
let ends = let ends = chrono::naive::NaiveDateTime::parse_from_str(ends_str, "%Y/%m/%d %H:%M:%S")
chrono::naive::NaiveDateTime::parse_from_str(ends_str, "%U %Y/%m/%d %H:%M:%S") .expect(&format!("Bad date format: '{}'", ends_str));
.expect("Bad date format");
let now = Local::now().naive_local(); let now = Local::now().naive_local();
if starts <= now && now < ends { if starts <= now && now < ends {

View File

@ -2,6 +2,7 @@ extern crate blurz;
use blurz::bluetooth_adapter::BluetoothAdapter; use blurz::bluetooth_adapter::BluetoothAdapter;
use blurz::bluetooth_device::BluetoothDevice; use blurz::bluetooth_device::BluetoothDevice;
use blurz::bluetooth_discovery_session::BluetoothDiscoverySession;
use blurz::bluetooth_gatt_characteristic::BluetoothGATTCharacteristic; use blurz::bluetooth_gatt_characteristic::BluetoothGATTCharacteristic;
use blurz::bluetooth_gatt_service::BluetoothGATTService; use blurz::bluetooth_gatt_service::BluetoothGATTService;
use blurz::bluetooth_session::BluetoothSession; use blurz::bluetooth_session::BluetoothSession;
@ -63,13 +64,19 @@ impl LightConfig {
} }
} }
impl BluetoothLightbulb { impl BluetoothLightbulb {
fn set_color(&self, characs: &Vec<BluetoothGATTCharacteristic>, r: f64, g: f64, b: f64) { fn set_color(
&self,
targets: &Vec<(BluetoothDevice, BluetoothGATTCharacteristic)>,
r: f64,
g: f64,
b: f64,
) {
let r = (r * 255.0) as u8; let r = (r * 255.0) as u8;
let g = (g * 255.0) as u8; let g = (g * 255.0) as u8;
let b = (b * 255.0) as u8; let b = (b * 255.0) as u8;
// println!("r={} g={} b={}", r, g, b); // println!("r={} g={} b={}", r, g, b);
for charac in characs { for (_, charac) in targets {
charac charac
.write_value(vec![0x55, 0x13, r, g, b, '\r' as u8, '\n' as u8], None) .write_value(vec![0x55, 0x13, r, g, b, '\r' as u8, '\n' as u8], None)
.unwrap(); .unwrap();
@ -78,10 +85,15 @@ impl BluetoothLightbulb {
fn initiate_scan(&self, adapter: &BluetoothAdapter) { fn initiate_scan(&self, adapter: &BluetoothAdapter) {
if !adapter.is_discovering().unwrap() { if !adapter.is_discovering().unwrap() {
log::info!("bluetooth adapter {}: Set scan ON", adapter.get_id()); log::info!("{}: Searching for new devices...", adapter.get_id());
adapter
let disc_session =
BluetoothDiscoverySession::create_session(&self.session, adapter.get_id())
.expect("Could not create discovery session");
disc_session
.start_discovery() .start_discovery()
.expect("Cannot activate scanning on bluetooth device"); .expect("Could not start discovery session");
} }
} }
} }
@ -94,8 +106,7 @@ impl Output for BluetoothLightbulb {
.get("config") .get("config")
.unwrap_or(&serde_yaml::Mapping::new().into()) .unwrap_or(&serde_yaml::Mapping::new().into())
.clone(); .clone();
let config: BluetoothLightbulbConfig = let config: BluetoothLightbulbConfig = serde_yaml::from_value(config_node)?;
serde_yaml::from_value(config_node).expect("Invalid config for light_beewi_bbl227");
let msg_types = config let msg_types = config
.msg_types .msg_types
@ -115,8 +126,7 @@ impl Output for BluetoothLightbulb {
.map(|(k, v)| (k.clone(), v.clone())) .map(|(k, v)| (k.clone(), v.clone()))
.collect(); .collect();
let session = let session = BluetoothSession::create_session(None)?;
BluetoothSession::create_session(None).expect("Could not initialize bluetooth session");
let ret = BluetoothLightbulb { let ret = BluetoothLightbulb {
config: config, config: config,
@ -125,9 +135,8 @@ impl Output for BluetoothLightbulb {
session: session, session: session,
}; };
// Initialize bluetooth device // Initialize bluetooth adapter
let adapter = let adapter = BluetoothAdapter::init(&ret.session)?;
BluetoothAdapter::init(&ret.session).expect("Could not initialize bluetooth adapter");
// Power on // Power on
if !adapter.is_powered().unwrap() { if !adapter.is_powered().unwrap() {
log::info!("bluetooth adapter {}: Set power ON", adapter.get_id()); log::info!("bluetooth adapter {}: Set power ON", adapter.get_id());
@ -135,8 +144,6 @@ impl Output for BluetoothLightbulb {
.set_powered(true) .set_powered(true)
.expect("Cannot power on bluetooth device"); .expect("Cannot power on bluetooth device");
} }
// Start scan
ret.initiate_scan(&adapter);
Ok(ret) Ok(ret)
} }
@ -144,7 +151,7 @@ impl Output for BluetoothLightbulb {
let adapter = let adapter =
BluetoothAdapter::init(&self.session).expect("Could not initialize bluetooth adapter"); BluetoothAdapter::init(&self.session).expect("Could not initialize bluetooth adapter");
let mut characs = vec![]; let mut targets = vec![];
let mut found_macs: HashMap<String, bool> = HashMap::new(); let mut found_macs: HashMap<String, bool> = HashMap::new();
for mac in self.config.mac.keys() { for mac in self.config.mac.keys() {
found_macs.insert(mac.to_owned(), false); found_macs.insert(mac.to_owned(), false);
@ -157,13 +164,19 @@ impl Output for BluetoothLightbulb {
if self.config.mac.contains_key(&addr) { if self.config.mac.contains_key(&addr) {
if !device.is_connected().unwrap() { if !device.is_connected().unwrap() {
log::info!("Connecting to {}", addr); log::info!("Connecting to {}", addr);
match device.connect(5000) { match device.connect(30000) {
Ok(_) => { Ok(_) => {
let power = device.get_tx_power().expect("Could not request tx power"); let power = match device.get_tx_power() {
Ok(p) => p.to_string(),
Err(_) => "?".to_owned(),
};
log::info!("Connected to {}, tx power={}", addr, power); log::info!("Connected to {}, tx power={}", addr, power);
// GATT characteristics can take a second to correctly load once connected
std::thread::sleep(std::time::Duration::from_secs(1));
} }
Err(e) => { Err(e) => {
log::error!("Could not connect to {}: {}", addr, e); log::error!("Could not connect to {}: {:?}", addr, e);
continue; continue;
} }
} }
@ -182,7 +195,7 @@ impl Output for BluetoothLightbulb {
let charac_uuid = charac.get_uuid().unwrap(); let charac_uuid = charac.get_uuid().unwrap();
if charac_uuid == "a8b3fff1-4834-4051-89d0-3de95cddd318" { if charac_uuid == "a8b3fff1-4834-4051-89d0-3de95cddd318" {
characs.push(charac); targets.push((device.clone(), charac));
found_macs.insert(addr.clone(), true); found_macs.insert(addr.clone(), true);
} }
} }
@ -191,11 +204,11 @@ impl Output for BluetoothLightbulb {
} }
} }
// List devices not found
let macs_not_found: HashMap<String, bool> = found_macs let macs_not_found: HashMap<String, bool> = found_macs
.into_iter() .into_iter()
.filter(|(_, v)| *v == false) .filter(|(_, v)| *v == false)
.collect(); .collect();
if macs_not_found.len() > 0 { if macs_not_found.len() > 0 {
log::warn!( log::warn!(
"Could not find configured lightbulbs: {:?}", "Could not find configured lightbulbs: {:?}",
@ -205,7 +218,7 @@ impl Output for BluetoothLightbulb {
return; return;
} }
// find out color & pattern to play // Merge LightConfig properties
let mut cfg = LightConfig { let mut cfg = LightConfig {
color: Some(0xffffff), color: Some(0xffffff),
animation: Some(Animation::Smooth), animation: Some(Animation::Smooth),
@ -222,6 +235,7 @@ impl Output for BluetoothLightbulb {
cfg.merge(level_cfg); cfg.merge(level_cfg);
} }
// Aimation properties
let color = cfg.color.unwrap(); let color = cfg.color.unwrap();
let r = ((color >> 16) & 0xff) as f64 / 255.0; let r = ((color >> 16) & 0xff) as f64 / 255.0;
let g = ((color >> 8) & 0xff) as f64 / 255.0; let g = ((color >> 8) & 0xff) as f64 / 255.0;
@ -235,16 +249,16 @@ impl Output for BluetoothLightbulb {
for _ in 0..cfg.repeat.unwrap() { for _ in 0..cfg.repeat.unwrap() {
match anim { match anim {
Animation::None => { Animation::None => {
self.set_color(&characs, r, g, b); self.set_color(&targets, r, g, b);
std::thread::sleep(std::time::Duration::from_millis((2000.0 / speed) as u64)); std::thread::sleep(std::time::Duration::from_millis((2000.0 / speed) as u64));
} }
Animation::Bounce => { Animation::Bounce => {
for _ in 0..4 { for _ in 0..4 {
self.set_color(&characs, r, g, b); self.set_color(&targets, r, g, b);
std::thread::sleep(std::time::Duration::from_millis( std::thread::sleep(std::time::Duration::from_millis(
(250.0 / speed) as u64, (250.0 / speed) as u64,
)); ));
self.set_color(&characs, r * 0.5, g * 0.5, b * 0.5); self.set_color(&targets, r * 0.5, g * 0.5, b * 0.5);
std::thread::sleep(std::time::Duration::from_millis( std::thread::sleep(std::time::Duration::from_millis(
(250.0 / speed) as u64, (250.0 / speed) as u64,
)); ));
@ -253,11 +267,11 @@ impl Output for BluetoothLightbulb {
Animation::Smooth => { Animation::Smooth => {
for i in 0..10 { for i in 0..10 {
let mult = i as f64 / 10.0; let mult = i as f64 / 10.0;
self.set_color(&characs, r * mult, g * mult, b * mult); self.set_color(&targets, r * mult, g * mult, b * mult);
} }
for i in 0..10 { for i in 0..10 {
let mult = 1.0 - (i as f64 / 10.0); let mult = 1.0 - (i as f64 / 10.0);
self.set_color(&characs, r * mult, g * mult, b * mult); self.set_color(&targets, r * mult, g * mult, b * mult);
} }
} }
Animation::Blink => { Animation::Blink => {
@ -266,7 +280,7 @@ impl Output for BluetoothLightbulb {
0 => (r, g, b), 0 => (r, g, b),
_ => (0.0, 0.0, 0.0), _ => (0.0, 0.0, 0.0),
}; };
self.set_color(&characs, r, g, b); self.set_color(&targets, r, g, b);
std::thread::sleep(std::time::Duration::from_millis( std::thread::sleep(std::time::Duration::from_millis(
(400.0 / speed) as u64, (400.0 / speed) as u64,
)); ));
@ -275,24 +289,24 @@ impl Output for BluetoothLightbulb {
Animation::RampUp => { Animation::RampUp => {
for i in 0..20 { for i in 0..20 {
let mult = i as f64 / 20.0; let mult = i as f64 / 20.0;
self.set_color(&characs, r * mult, g * mult, b * mult); self.set_color(&targets, r * mult, g * mult, b * mult);
} }
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(100));
self.set_color(&characs, 0.0, 0.0, 0.0); self.set_color(&targets, 0.0, 0.0, 0.0);
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(100));
} }
Animation::RampDown => { Animation::RampDown => {
self.set_color(&characs, r, g, b); self.set_color(&targets, r, g, b);
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(100));
for i in 1..20 { for i in 1..20 {
let mult = 1.0 - (i as f64 / 20.0); let mult = 1.0 - (i as f64 / 20.0);
self.set_color(&characs, r * mult, g * mult, b * mult); self.set_color(&targets, r * mult, g * mult, b * mult);
} }
self.set_color(&characs, 0.0, 0.0, 0.0); self.set_color(&targets, 0.0, 0.0, 0.0);
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(100));
} }
} }
} }
self.set_color(&characs, 0.0, 0.0, 0.0); self.set_color(&targets, 0.0, 0.0, 0.0);
} }
} }