updates on hotreload branch

This commit is contained in:
Paul 2021-10-24 16:45:19 +02:00
parent 8c9575b6bd
commit b322e4bdb1
7 changed files with 169 additions and 97 deletions

7
Cargo.lock generated
View File

@ -117,6 +117,7 @@ dependencies = [
"textwrap", "textwrap",
"unicode-width", "unicode-width",
"vec_map", "vec_map",
"yaml-rust",
] ]
[[package]] [[package]]
@ -1230,6 +1231,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "yaml-rust"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992"
[[package]] [[package]]
name = "zabbixlaunch" name = "zabbixlaunch"
version = "1.0.0" version = "1.0.0"

View File

@ -6,7 +6,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
[dependencies] [dependencies]
clap = { version = "2.33.3" } clap = { version = "2.33.3", features = ["yaml"] }
embedded-graphics = { version = "0.7.1", optional = true } embedded-graphics = { version = "0.7.1", optional = true }
launchy = { git = "https://github.com/kangalioo/launchy", branch = "master" } launchy = { git = "https://github.com/kangalioo/launchy", branch = "master" }
reqwest = { version = "0.11.5", features = ["blocking", "json"] } reqwest = { version = "0.11.5", features = ["blocking", "json"] }

17
src/config/cli.yml Normal file
View File

@ -0,0 +1,17 @@
---
name: zabbixlaunch
version: "1.0.0"
author: PaulBSD <paul@paulbsd.com>
about: Lights up Launchpad mini using Zabbix data
args:
- config:
short: c
long: config
value_name: FILE
help: Sets a custom config file
takes_value: true
- mode:
short: m
value_name: MODE
help: Handles mode
takes_value: true

View File

@ -1,68 +1,83 @@
use clap::{App, Arg, ArgMatches}; use crate::zabbix::api::get_zabbix_authtoken;
use crate::zabbix::problems::DataMatrix;
use clap::{App, ArgMatches};
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify}; use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Error as JsonError; use serde_json::Error as JsonError;
use std::fs::File;
use std::io::Read;
use std::string::String; use std::string::String;
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use std::{fs::File, io::Read};
#[derive(Debug)]
pub enum ReloadFrequency {
High = 50,
Medium = 20,
Low = 10,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
pub cfg: Config, pub cfg: Config,
pub configfile: String, pub configfile: String,
pub inotify: Inotify, pub inotify: Inotify,
pub datamatrix: DataMatrix,
pub mode: String,
} }
impl Context { impl Context {
pub fn new() -> Self { pub fn new() -> Self {
let configfile = Context::argparse() let yaml = load_yaml!("cli.yml");
.value_of("config") let cli = App::from_yaml(yaml).get_matches();
.unwrap_or("config.json")
.to_string(); let configfile = cli.value_of("config").unwrap_or("config.json").to_string();
let mut ctx = Context { let mut ctx = Context {
cfg: Config::new(), cfg: Config::new(),
configfile: configfile, configfile: configfile,
inotify: Inotify::init(InitFlags::IN_NONBLOCK).unwrap(), inotify: Inotify::init(InitFlags::IN_NONBLOCK).unwrap(),
datamatrix: DataMatrix::new(),
mode: cli.value_of("mode").unwrap_or("draw").to_string(),
}; };
println!("Loading {} file ...", ctx.configfile); println!("Loading {configfile} file ...", configfile = ctx.configfile);
ctx.cfg.load(&ctx.configfile); ctx.cfg.load(&ctx.configfile);
println!("Adding inotify watch on {} file ...", ctx.configfile); println!(
"Adding inotify watch on {configfile} file ...",
configfile = ctx.configfile
);
ctx.inotify ctx.inotify
.add_watch(ctx.configfile.as_str(), AddWatchFlags::IN_MODIFY) .add_watch(ctx.configfile.as_str(), AddWatchFlags::IN_MODIFY)
.unwrap(); .unwrap();
get_zabbix_authtoken(&mut ctx.cfg);
ctx ctx
} }
pub fn hotreload(&mut self) { pub fn hotreload(&mut self) {
let mut i = 0;
let waitmilli = self.cfg.refresh.unwrap_or(5) * 1000;
while i < waitmilli {
let waitinc = waitmilli / ReloadFrequency::Medium as u64;
let events = match self.inotify.read_events() { let events = match self.inotify.read_events() {
Ok(ev) => ev, Ok(ev) => ev,
Err(_) => vec![], Err(_) => vec![],
}; };
if events.len() > 0 { if events.len() > 0 {
self.cfg.load(self.configfile.as_str()); self.cfg.load(self.configfile.as_str());
println!("{:?} {} {:?}", self.cfg, events.len(), events); println!(
"{cfg:?} {numevents} {events:?}",
cfg = self.cfg,
numevents = events.len(),
events = events
);
break;
} else { } else {
sleep(Duration::from_secs(self.cfg.refresh.unwrap_or(5))); sleep(Duration::from_millis(waitinc));
} }
i += waitinc;
} }
pub fn argparse() -> ArgMatches<'static> {
App::new("Zabbix Launch")
.version("1.0.0")
.author("PaulBSD <paul@paulbsd.com>")
.about("Lights up Launchpad mini using Zabbix data")
.arg(
Arg::with_name("config")
.short("c")
.long("config")
.value_name("FILE")
.help("Sets a custom config file")
.takes_value(true),
)
.get_matches()
} }
} }
@ -126,8 +141,8 @@ impl<'a> Config {
let mut file = match fileopen { let mut file = match fileopen {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(err) => {
panic!("{}", e); panic!("{err}", err = err);
} }
}; };
let mut contents = String::new(); let mut contents = String::new();

View File

@ -2,16 +2,14 @@ pub mod config;
mod padcontrol; mod padcontrol;
mod zabbix; mod zabbix;
#[macro_use]
extern crate clap;
use config::Context; use config::Context;
use zabbix::problems::ZabbixLayout;
fn main() { fn main() {
let mut datamatrix = ZabbixLayout::new();
// load configuration
let mut context = Context::new(); let mut context = Context::new();
zabbix::api::get_zabbix_authtoken(&mut context.cfg);
//let (mut canvas, mut _poller) = padcontrol::initpad();
//padcontrol::draw(&mut canvas, &mut datamatrix, &mut context); //padcontrol::draw(&mut canvas, &mut datamatrix, &mut context);
padcontrol::test(&mut context); //padcontrol::test(&mut context)
padcontrol::run(&mut context);
} }

View File

@ -1,21 +1,44 @@
use crate::config::Context; use crate::config::Context;
use crate::zabbix::api::get_zabbix_problems; use crate::zabbix::api::get_zabbix_problems;
use crate::zabbix::problems::ZabbixLayout;
use launchy::Color; use launchy::Color;
use launchy::{self, Canvas, CanvasLayout, CanvasLayoutPoller, MsgPollingWrapper, Pad}; use launchy::{self, Canvas, CanvasLayout, CanvasLayoutPoller, MsgPollingWrapper, Pad};
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
pub const MATRIX_WIDTH: i32 = 8; const MATRIX_WIDTH: i32 = 8;
pub const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH; const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH;
pub fn initpad() -> (CanvasLayout<'static>, CanvasLayoutPoller) { pub fn run(ctx: &mut Context) {
match ctx.mode.as_str() {
"draw" => {
let (mut canvas, mut _poller) = initpad();
draw(&mut canvas, ctx);
}
"input" => {
let (mut canvas, mut poller) = initpad();
input(&mut canvas, &mut poller);
}
"effect" => {
let (mut canvas, mut poller) = initpad();
effect(&mut canvas, &mut poller)
}
"test" => test(ctx),
_ => {
println!("No valid option choosen for mode");
std::process::exit(1)
}
}
}
fn initpad() -> (CanvasLayout<'static>, CanvasLayoutPoller) {
let (mut canvas, poller) = launchy::CanvasLayout::new_polling(); let (mut canvas, poller) = launchy::CanvasLayout::new_polling();
match canvas.add_by_guess_rotated::<launchy::mini::Canvas>(0, 0, launchy::Rotation::None) { match canvas.add_by_guess_rotated::<launchy::mini::Canvas>(0, 0, launchy::Rotation::None) {
Ok(o) => o, Ok(o) => o,
_ => { Err(err) => {
eprintln!("Failed to connect to device, check USB connection"); eprintln!(
std::process::exit(1); "Failed to connect to device, check USB connection {err}",
err = err
);
} }
}; };
println!("Connected to device using USB"); println!("Connected to device using USB");
@ -23,50 +46,40 @@ pub fn initpad() -> (CanvasLayout<'static>, CanvasLayoutPoller) {
(canvas, poller) (canvas, poller)
} }
pub fn _input(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) { fn input(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) { for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) { for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
canvas[msg.pad()] = color; canvas[msg.pad()] = color;
println!("{:?}", msg.pad()) println!("{msg:?}", msg = msg.pad())
} }
let _res = canvas.flush(); let _res = canvas.flush();
} }
} }
pub fn test(ctx: &mut Context) { fn draw(canvas: &mut CanvasLayout, ctx: &mut Context) {
println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap()); println!(
loop { "Refresh rate is {sec} seconds",
let zabbix_data_result = get_zabbix_problems(&ctx.cfg); sec = ctx.cfg.refresh.unwrap()
let zabbix_data = match zabbix_data_result { );
Ok(z) => z,
Err(_) => {
let duration = 10;
println!("Error requesting zabbix service, retrying in {}", duration);
sleep(Duration::from_secs(duration));
continue;
}
};
println!("{}", zabbix_data);
ctx.hotreload();
}
}
pub fn draw(canvas: &mut CanvasLayout, datamatrix: &mut ZabbixLayout, ctx: &mut Context) {
println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap());
loop { loop {
let zabbix_data_result = get_zabbix_problems(&ctx.cfg); let zabbix_data_result = get_zabbix_problems(&ctx.cfg);
let zabbix_data = match zabbix_data_result { let zabbix_data = match zabbix_data_result {
Ok(zabbix_data) => zabbix_data, Ok(zabbix_data) => zabbix_data,
Err(_) => { Err(err) => {
println!("Error requesting service"); let duration = 10;
sleep(Duration::from_secs(10)); println!(
"Error requesting zabbix service, err is {err}, retrying in {duration}",
err = err,
duration = duration
);
sleep(Duration::from_secs(duration));
continue; continue;
} }
}; };
datamatrix.compute(&zabbix_data); ctx.datamatrix.compute(&zabbix_data);
clear(canvas); clear(canvas);
let mut max = 0; let mut max = 0;
for (i, j) in datamatrix.layout.iter().enumerate() { for (i, j) in ctx.datamatrix.layout.iter().enumerate() {
let p = Pad { let p = Pad {
x: ((i as i32) % MATRIX_WIDTH), x: ((i as i32) % MATRIX_WIDTH),
y: ((i as i32) / MATRIX_WIDTH) + 1, y: ((i as i32) / MATRIX_WIDTH) + 1,
@ -88,15 +101,40 @@ pub fn draw(canvas: &mut CanvasLayout, datamatrix: &mut ZabbixLayout, ctx: &mut
} }
canvas.flush().unwrap(); canvas.flush().unwrap();
} }
sleep(Duration::from_secs(ctx.cfg.refresh.unwrap_or(5))); ctx.hotreload();
} }
} }
fn _effect(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) { fn test(ctx: &mut Context) {
println!(
"Refresh rate is {sec} seconds",
sec = ctx.cfg.refresh.unwrap()
);
loop {
let zabbix_data_result = get_zabbix_problems(&ctx.cfg);
let zabbix_data = match zabbix_data_result {
Ok(z) => z,
Err(err) => {
let duration = 10;
println!(
"Error requesting zabbix service, err is {err}, retrying in {duration}",
err = err,
duration = duration
);
sleep(Duration::from_secs(duration));
continue;
}
};
println!("{zabbix_data}", zabbix_data = zabbix_data);
ctx.hotreload();
}
}
fn effect(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) { for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) { for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
canvas[msg.pad()] = color; canvas[msg.pad()] = color;
println!("{:?}", msg.pad()) println!("{msg:?}", msg = msg.pad())
} }
let _res = canvas.flush(); let _res = canvas.flush();

View File

@ -1,7 +1,7 @@
use serde_json::Value; use serde_json::Value;
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum ZabbixStatus { pub enum ZabbixStatus {
High = 4, High = 4,
Average = 3, Average = 3,
@ -9,29 +9,15 @@ pub enum ZabbixStatus {
Info = 1, Info = 1,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct ZabbixLayout { pub struct DataMatrix {
pub layout: Vec<ZabbixProblems>, pub layout: Vec<ZabbixProblems>,
} }
impl ZabbixLayout { impl DataMatrix {
pub fn new() -> Self { pub fn new() -> Self {
Self { layout: Vec::new() } Self { layout: Vec::new() }
} }
}
#[derive(Debug)]
pub struct ZabbixProblems {
pub status: i64,
}
impl Display for ZabbixProblems {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "status: {:?}", self.status)
}
}
impl ZabbixLayout {
pub fn compute(&mut self, input: &Value) { pub fn compute(&mut self, input: &Value) {
self.layout = Vec::new(); self.layout = Vec::new();
for j in input.as_object().unwrap()["result"].as_array().unwrap() { for j in input.as_object().unwrap()["result"].as_array().unwrap() {
@ -41,3 +27,14 @@ impl ZabbixLayout {
} }
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct ZabbixProblems {
pub status: i64,
}
impl Display for ZabbixProblems {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "status: {status:?}", status = self.status)
}
}