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::io::prelude::*;
use std::sync::mpsc;
use std::sync::{Arc, Barrier};
use std::thread;
use crate::filter::*;
@ -73,7 +74,7 @@ fn main() {
simplelog::TermLogger::init(
level,
simplelog::Config {
time: Some(simplelog::Level::Debug),
time: None,
level: Some(simplelog::Level::Error),
target: Some(simplelog::Level::Info),
location: Some(simplelog::Level::Debug),
@ -93,10 +94,14 @@ fn main() {
let config: AppConfig =
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
let (mon_sender, mon_receiver) = mpsc::channel();
// Instantiate monitor threads and structs
let mut mon_threads = vec![];
for mon_config in config.monitors {
let mon_type = mon_config
.get("type")
@ -106,21 +111,23 @@ fn main() {
.to_owned();
let snd = mon_sender.clone();
let bar = barrier.clone();
thread::spawn(move || {
mon_threads.push(thread::spawn(move || {
log::info!("+> monitor: {}", mon_type);
match monitors::factory(&mon_type, &mon_config) {
Ok(mut mon) => loop {
bar.wait();
mon.run(&snd);
},
Err(e) => log::error!("Cannot instantiate monitor {}: {}", mon_type, e),
}
});
}));
}
// 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_threads = vec![];
for out_config in config.outputs {
let out_type = out_config
.get("type")
@ -133,11 +140,13 @@ fn main() {
output_senders.push(out_sender);
let filter = Filter::new(&out_config);
let bar = barrier.clone();
thread::spawn(move || {
output_threads.push(thread::spawn(move || {
log::info!("+> output: {}", out_type);
match outputs::factory(&out_type, &out_config) {
Ok(mut output) => loop {
bar.wait();
let message = out_receiver.recv().unwrap();
if filter.is_message_allowed(&message) {
output.process_message(message);
@ -145,10 +154,20 @@ fn main() {
},
Err(e) => log::error!("Cannot instantiate output {}: {}", out_type, e),
}
});
}));
}
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
loop {
let message = mon_receiver.recv().unwrap();

View File

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

View File

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