Compare commits

..

29 Commits

Author SHA1 Message Date
f2bb375972 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-12 22:45:54 +02:00
872e17f6f2 Merge few changes from branch 'launchpad_mini_mk3'
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-10 19:37:03 +01:00
e30efa1d9a fix: api json data & launchy remote repository
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-10 19:25:19 +01:00
9f07930013 updated dependencies 2023-02-10 18:28:13 +01:00
b73fc7a7c2 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-16 11:42:30 +02:00
ef60d4bda9 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-04 15:33:56 +02:00
c128a7dce5 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-27 14:39:44 +02:00
50a2da488d updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-22 19:20:01 +02:00
Paul Lecuq
f6e801dc58 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-16 14:29:27 +02:00
Paul Lecuq
f3a1d5c900 updated zabbixlaunch 2022-05-16 14:29:19 +02:00
Paul Lecuq
ca349dc187 updated README.md
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-10 12:59:24 +02:00
Paul Lecuq
ccf9fd587d updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-10 12:53:44 +02:00
Paul Lecuq
ec23da0565 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-10 12:50:58 +02:00
bf03adb549 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-08 19:49:27 +02:00
Paul Lecuq
f18f25727d updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-04 19:04:29 +02:00
Paul Lecuq
e364dc7540 update to version 1.0.1
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-29 17:35:33 +02:00
Paul Lecuq
463795385d updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-21 17:31:01 +02:00
Paul Lecuq
95322749fe updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-11 10:19:14 +02:00
e9973e109f Merge branch 'master' of ssh://git.paulbsd.com:2222/paulbsd/zabbixlaunch
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-26 12:07:47 +01:00
e7850c8d72 updated .drone.yml 2022-03-26 12:07:33 +01:00
Paul Lecuq
59e1057ca7 updates on dependencies, changed some pub functions to private
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
continuous-integration/drone Build is failing
2022-03-15 13:26:24 +01:00
811f1155a9 updated .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-11 23:43:21 +01:00
Paul Lecuq
2f1be523f8 cleaned-up code and added compiler optimizations
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-04 10:14:53 +01:00
Paul Lecuq
9fefb6e8a0 some code cleaning, updated dependencies
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-01 18:09:20 +01:00
Paul Lecuq
55dc677b46 * Code cleanup, feature updated
- cleanup unsed variables
- added dynamic slow effect based on number of issues
2022-02-16 17:48:24 +01:00
Paul Lecuq
72d12319c1 misc updates and fixes 2022-01-17 17:57:57 +01:00
Paul Lecuq
0dc914beb5 updated Cargo.toml 2021-12-09 09:40:43 +01:00
Paul Lecuq
05b38b3540 updated zabbixlaunch 2021-12-07 18:49:04 +01:00
Paul Lecuq
b7a9def20d updated config management 2021-12-06 17:17:24 +01:00
11 changed files with 838 additions and 925 deletions

View File

@ -1,101 +1,37 @@
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: cleanup-before name: default
steps: steps:
- name: clean - name: test
image: alpine image: rust:1
commands: commands:
- rm -rf /build/* - apt-get update -y
volumes: - apt-get install libasound2-dev -y
- name: build - cargo build --verbose --all
path: /build - cargo test --verbose --all
when:
event: tag
volumes:
- name: build
host:
path: /tmp/ipbl/build
---
kind: pipeline
type: docker
name: default-linux-amd64
steps:
- name: Build
image: rust:latest
commands:
- cargo build --verbose --all
---
kind: pipeline
type: docker
name: gitea-release
steps:
- name: move
image: alpine
commands:
- mv target/release/* ./
volumes:
- name: build
path: /drone/src/build
when:
event: tag
- name: release - name: release
image: rust:1
commands:
- apt-get update -y
- apt-get install libasound2-dev -y
- cargo build --release --verbose --all
- cd target/release
- tar -czvf zabbixlaunch-${DRONE_TAG}.tar.gz zabbixlaunch
when:
event: tag
- name: publish
image: plugins/gitea-release image: plugins/gitea-release
settings: settings:
base_url: https://git.paulbsd.com base_url: https://git.paulbsd.com
api_key: api_key:
from_secret: gitea_token from_secret: gitea_token
files: "*.tar.gz" files: "target/release/*.tar.gz"
checksum: checksum:
- sha256 - sha256
- sha512 - sha512
title: VERSION environment:
volumes: PLUGIN_TITLE: ""
- name: build
path: /drone/src/build
when: when:
event: tag event: tag
- name: ls
image: alpine
commands:
- find .
volumes:
- name: build
path: /drone/src/build
when:
event: tag
volumes:
- name: build
host:
path: /tmp/ipbl/build
depends_on:
- default-linux-amd64
---
kind: pipeline
type: docker
name: cleanup-after
steps:
- name: clean
image: alpine
commands:
- rm -rf /build/*
volumes:
- name: build
path: /build
when:
event: tag
volumes:
- name: build
host:
path: /tmp/ipbl/build

1138
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,18 @@
[package] [package]
name = "zabbixlaunch" name = "zabbixlaunch"
version = "1.0.0" version = "1.0.1"
edition = "2018" edition = "2021"
authors = ["PaulBSD <paul@paulbsd.com>"]
description = "Lights up Launchpad mini using Zabbix data"
repository = "https://git.paulbsd.com/paulbsd/zabbixlaunch"
# 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", features = ["yaml"] } clap = "4.3"
embedded-graphics = { version = "0.7", optional = true } embedded-graphics = { version = "0.8", optional = true }
futures = "*" launchy = { git = "https://github.com/paulbsd/launchy", branch = "develop-launchpad-mini-mk3" }
launchy = { git = "https://github.com/kangalioo/launchy", branch = "master" } nix = "0.26"
nix = "0.23" reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] }
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
tokio = { version = "1", features = ["full"] }

View File

@ -1,5 +1,7 @@
# zabbixlaunch # zabbixlaunch
[![Build Status](https://drone.paulbsd.com/api/badges/paulbsd/zabbixlaunch/status.svg)](https://drone.paulbsd.com/paulbsd/zabbixlaunch)
## Summary ## Summary
zabbixlaunch is a application that take control over a launchpad mini, and draw problems handled by Zabbix zabbixlaunch is a application that take control over a launchpad mini, and draw problems handled by Zabbix
@ -14,8 +16,9 @@ zabbixlaunch is a application that take control over a launchpad mini, and draw
"server": "https://zabbix.acme.com/api_jsonrpc.php", "server": "https://zabbix.acme.com/api_jsonrpc.php",
"username": "bob", "username": "bob",
"password": "password", "password": "password",
"authtoken": "token", "sloweffect": true,
"limit": 20 "refresh": 2,
"limit": 100
} }
``` ```
@ -48,7 +51,7 @@ cargo build --release
## License ## License
```text ```text
Copyright (c) 2021 PaulBSD Copyright (c) 2021, 2022 PaulBSD
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

8
config.json.sample Normal file
View File

@ -0,0 +1,8 @@
{
"server": "https://zabbix.acme.com/api_jsonrpc.php",
"username": "bob",
"password": "password",
"sloweffect": true,
"refresh": 2,
"limit": 100
}

View File

@ -1,17 +0,0 @@
---
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,6 +1,6 @@
use crate::zabbix::api::get_zabbix_authtoken; use crate::zabbix::api::get_zabbix_authtoken;
use crate::zabbix::problems::DataMatrix; use crate::zabbix::problems::DataMatrix;
use clap::App; use clap::{Arg, ArgMatches, Command};
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;
@ -8,42 +8,60 @@ use serde_json::Value as JsonValue;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::string::String; use std::string::String;
use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use tokio::time::sleep;
#[derive(Debug)]
pub enum ReloadFrequency { pub enum ReloadFrequency {
High = 50, High = 50,
Medium = 20, Medium = 20,
Low = 10, Low = 10,
} }
#[derive(Debug, Clone)]
pub enum Mode {
Draw,
Test,
Input,
Effect,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
pub cfg: Config, pub cfg: Config,
pub configfile: String, configfile: String,
pub inotify: Inotify, inotify: Inotify,
pub datamatrix: DataMatrix, pub datamatrix: DataMatrix,
pub mode: String, pub mode: Option<Mode>,
} }
impl Context { impl Context {
pub async fn new() -> Self { pub fn new() -> Self {
let yaml = load_yaml!("cli.yml"); let argp: ArgMatches = Context::argparse();
let cli = App::from_yaml(yaml).get_matches();
let configfile = cli.value_of("config").unwrap_or("config.json").to_string(); let configfile = argp
.get_one::<String>("config")
.unwrap_or(&"config.json".to_string())
.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(), datamatrix: DataMatrix::new(),
mode: cli.value_of("mode").unwrap_or("draw").to_string(), mode: match argp.get_one::<String>("mode").unwrap().as_str() {
"draw" => Some(Mode::Draw),
"test" => Some(Mode::Test),
"input" => Some(Mode::Input),
"effect" => Some(Mode::Effect),
_ => {
println!("No valid mode choosen");
None
}
},
}; };
println!("Loading {configfile} file ...", configfile = ctx.configfile); println!("Loading {configfile} file ...", configfile = ctx.configfile);
ctx.cfg.load(&ctx.configfile.to_string()).await; ctx.cfg.load(&ctx.configfile);
println!( println!(
"Adding inotify watch on {configfile} file ...", "Adding inotify watch on {configfile} file ...",
@ -52,38 +70,57 @@ impl Context {
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).await; get_zabbix_authtoken(&mut ctx.cfg);
ctx ctx
} }
/*pub async fn hotreload(&mut self) { fn argparse() -> ArgMatches {
let events = match self.inotify.read_events() { Command::new(env!("CARGO_PKG_NAME"))
Ok(ev) => ev, .version(env!("CARGO_PKG_VERSION"))
Err(_) => vec![], .author(env!("CARGO_PKG_AUTHORS"))
}; .about(env!("CARGO_PKG_DESCRIPTION"))
if events.len() > 0 { .arg(
println!("Reloading {cfg}", cfg = self.configfile); Arg::new("config")
get_zabbix_authtoken(&mut self.cfg).await; .short('c')
self.cfg.load(self.configfile.as_str()); .long("config")
} .value_name("FILE")
std::thread::sleep(std::time::Duration::from_secs(5)); .default_value("/etc/zabbixlaunch/config.json")
}*/ .help("Sets a custom config file"),
} )
.arg(
Arg::new("mode")
.short('m')
.long("mode")
.value_name("mode")
.default_value("draw")
.value_parser(["draw", "test", "input", "effect"])
.help("Sets a when running"),
)
.arg(Arg::new("debug").short('d').help("Enable debugging"))
.get_matches()
}
pub async fn hotreload(ctx: &mut Context, ctxsender: &tokio::sync::mpsc::Sender<Context>) { pub fn hotreload(&mut self) {
println!("function hotreload, {:?}", ctxsender); let mut i = 0;
loop { let waitmilli = self.cfg.refresh.unwrap_or(5) * 1000;
let events = match ctx.to_owned().inotify.read_events() {
Ok(ev) => ev, while i < waitmilli {
Err(_) => vec![], let waitinc = waitmilli / ReloadFrequency::Medium as u64;
}; let events = match self.inotify.read_events() {
if events.len() > 0 { Ok(ev) => ev,
ctx.cfg.load(&ctx.configfile).await; Err(_) => vec![],
println!("{cfg:?}", cfg = ctx.cfg); };
if events.len() > 0 {
println!("Reloading {cfg}", cfg = self.configfile);
self.cfg.load(self.configfile.as_str());
get_zabbix_authtoken(&mut self.cfg);
break;
} else {
sleep(Duration::from_millis(waitinc));
}
i += waitinc;
} }
println!("Sending {:?}", ctx);
ctxsender.send(ctx.to_owned()).await;
} }
} }
@ -112,8 +149,21 @@ impl<'a> Config {
} }
} }
async fn load(&mut self, configfile: &String) { fn load(&'a mut self, configfile: &str) {
let mut file = match File::open(configfile) { let fileopen: Result<File, std::io::Error>;
let filemeta = std::fs::metadata(configfile);
let mut error_reading = false;
let fileexists = match filemeta {
Ok(_) => true,
Err(_) => false,
};
if !fileexists {
File::create(configfile).unwrap();
}
fileopen = File::open(configfile);
let mut file = match fileopen {
Ok(f) => f, Ok(f) => f,
Err(err) => { Err(err) => {
panic!("{err}", err = err); panic!("{err}", err = err);
@ -122,56 +172,51 @@ impl<'a> Config {
let mut contents = String::new(); let mut contents = String::new();
file.read_to_string(&mut contents).unwrap(); file.read_to_string(&mut contents).unwrap();
let parse: Result<JsonValue, JsonError> = serde_json::from_str(contents.as_str()); let parse: Result<JsonValue, JsonError> = serde_json::from_str(contents.as_str());
*self = match parse { let ncfg = match parse {
Ok(cfg) => { Ok(ncfg) => {
let tmpcfg = Config::new(); let tmpcfg = Config::new();
Config { Config {
server: cfg["server"] server: ncfg["server"]
.as_str() .as_str()
.unwrap_or(tmpcfg.server.as_str()) .unwrap_or(tmpcfg.server.as_str())
.to_string(), .to_string(),
username: cfg["username"] username: ncfg["username"]
.as_str() .as_str()
.unwrap_or(tmpcfg.username.as_str()) .unwrap_or(tmpcfg.username.as_str())
.to_string(), .to_string(),
password: cfg["password"] password: ncfg["password"]
.as_str() .as_str()
.unwrap_or(tmpcfg.password.as_str()) .unwrap_or(tmpcfg.password.as_str())
.to_string(), .to_string(),
authtoken: Some( authtoken: Some(
self.authtoken ncfg["authtoken"]
.clone() .as_str()
.unwrap_or(tmpcfg.authtoken.unwrap().to_string()) .unwrap_or(self.authtoken.clone().unwrap().as_str())
.to_string(), .to_string(),
), ),
sloweffect: Some( sloweffect: Some(
cfg["sloweffect"] ncfg["sloweffect"]
.as_bool() .as_bool()
.unwrap_or(tmpcfg.sloweffect.unwrap()), .unwrap_or(tmpcfg.sloweffect.unwrap()),
), ),
refresh: Some(cfg["refresh"].as_u64().unwrap_or(tmpcfg.refresh.unwrap())), refresh: Some(ncfg["refresh"].as_u64().unwrap_or(tmpcfg.refresh.unwrap())),
limit: Some(cfg["limit"].as_u64().unwrap_or(tmpcfg.limit.unwrap())), limit: Some(ncfg["limit"].as_u64().unwrap_or(tmpcfg.limit.unwrap())),
} }
} }
Err(err) => { Err(err) => {
error_reading = true;
println!("error occured: '{}'", err); println!("error occured: '{}'", err);
Config::new() Config::new()
} }
}; };
self.save(&configfile).await; *self = ncfg;
} if !fileexists || error_reading {
self.save(&configfile);
async fn save(&self, configfile: &String) {
let file: File;
let filemeta = std::fs::metadata(configfile);
let fileexists = match filemeta {
Ok(_) => true,
Err(_) => false,
};
if !fileexists {
file = File::create(configfile).unwrap();
serde_json::to_writer_pretty(file, &self).unwrap();
sleep(Duration::from_millis(1)).await;
} }
} }
fn save(&self, configfile: &str) {
let file = File::create(configfile).unwrap();
serde_json::to_writer_pretty(file, &self).unwrap();
}
} }

View File

@ -1,11 +1,10 @@
pub mod config; mod config;
mod padcontrol; mod padcontrol;
mod zabbix; mod zabbix;
#[macro_use] use config::Context;
extern crate clap;
#[tokio::main] fn main() {
async fn main() { let mut context = Context::new();
padcontrol::run().await; padcontrol::run(&mut context);
} }

View File

@ -1,177 +1,163 @@
use crate::config::{hotreload, Context}; use crate::config::{Context, Mode};
use crate::zabbix::api::*; use crate::zabbix::api::get_zabbix_problems;
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::time::Duration; use std::time::Duration;
use tokio::sync::mpsc;
use tokio::time::sleep;
const MATRIX_WIDTH: i32 = 8; const MATRIX_WIDTH: i32 = 8;
const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH; const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH;
const REQUEST_WAITFOR: i32 = 5; const REQUEST_TIMEOUT: i32 = 5;
const ZERO_COLOR: f32 = 0.; const ZERO_COLOR: f32 = 0.;
const ONE_COLOR: f32 = 1.; const ONE_COLOR: f32 = 1.;
const SLOWEFFECT_FACTOR: f32 = 800.0;
const POLLER_ITER: u64 = 17; pub fn run(ctx: &mut Context) {
match ctx.mode {
fn log(num: i64) { Some(Mode::Draw) => {
return println!("test{} 🦀", num); let (mut canvas, mut _poller) = initpad();
} draw(&mut canvas, ctx);
}
pub async fn run<'a>() { Some(Mode::Input) => {
log(1); let (mut canvas, mut poller) = initpad();
let mut ctx = Box::new(Context::new().await); input(&mut canvas, &mut poller);
log(2); }
let (tx1, mut rx1) = mpsc::channel(1); Some(Mode::Effect) => {
log(3); let (mut canvas, mut poller) = initpad();
let t1 = tokio::task::spawn(async move { test(&mut rx1).await }); effect(&mut canvas, &mut poller)
let t2 = tokio::task::spawn(async move { hotreload(&mut ctx, &tx1).await }); }
tokio::join!(t1, t2); Some(Mode::Test) => test(ctx),
log(4); None => {
} println!("No valid option choosen for mode");
}
async fn test<'a>(ctxreceiver: &mut tokio::sync::mpsc::Receiver<Context>) {
while let Some(val) = ctxreceiver.recv().await {
println!("Fetching zabbix problems, {:?}", val.cfg);
let zabbix_data = get_zabbix_problems(&val.cfg).await.unwrap();
println!("{}", zabbix_data["result"][0]);
println!("sleeping during {}", val.cfg.refresh.unwrap());
sleep(Duration::from_secs(val.cfg.refresh.unwrap())).await;
} }
} }
fn initpad() -> (CanvasLayout<'static>, CanvasLayoutPoller) { 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(_) => {
println!("Connected to device using USB");
clear(&mut canvas);
}
Err(err) => { Err(err) => {
eprintln!( eprintln!("Failed to connect to device, check USB connection {err}",);
"Failed to connect to device, check USB connection {err}",
err = err
);
} }
}; };
println!("Connected to device using USB");
clear(&mut canvas);
(canvas, poller) (canvas, poller)
} }
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 for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
.iter_for_millis(POLLER_ITER)
.filter(|msg| msg.is_press())
{
canvas[msg.pad()] = color; canvas[msg.pad()] = color;
println!("{msg:?}", msg = msg.pad()) println!("{msg:?}", msg = msg.pad())
} }
let _res = canvas.flush(); canvas.flush().unwrap();
} }
} }
/* fn test(ctx: &mut Context) {
async fn draw(canvas: &mut CanvasLayout<'_>, ctx: &mut Context) { println!(
"Refresh rate is {sec} seconds",
sec = ctx.cfg.refresh.unwrap()
);
loop {
let zabbix_data = match get_zabbix_problems(&ctx.cfg) {
Ok(z) => z,
Err(_) => {
println!(
"Error requesting zabbix service, retrying in {} seconds",
REQUEST_TIMEOUT
);
sleep(Duration::from_secs(REQUEST_TIMEOUT as u64));
continue;
}
};
println!("{}", zabbix_data);
ctx.hotreload();
}
}
fn draw(canvas: &mut CanvasLayout, ctx: &mut Context) {
println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap()); println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap());
loop { loop {
let zabbix_data_result = get_zabbix_problems(&ctx.cfg); let mut max = 0;
let zabbix_data = match zabbix_data_result { let zabbix_data = match get_zabbix_problems(&ctx.cfg) {
Ok(zabbix_data) => zabbix_data, Ok(zabbix_data) => zabbix_data,
Err(_) => { Err(_) => {
println!( println!(
"Error requesting zabbix service, retrying in {}", "Error requesting zabbix service, retrying in {}",
REQUEST_WAITFOR REQUEST_TIMEOUT
); );
sleep(Duration::from_secs(REQUEST_WAITFOR as u64)); sleep(Duration::from_secs(REQUEST_TIMEOUT as u64));
continue; continue;
} }
}; };
ctx.datamatrix.compute(&zabbix_data); ctx.datamatrix.compute(&zabbix_data);
clear(canvas); clear(canvas);
let mut max = 0;
for (i, j) in ctx.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,
}; };
let c = match j.status { canvas[p] = match j.status {
5 => Color::RED, 5 => Color::RED,
4 => Color::from_hue(0.05), 4 => Color::from_hue(0.05),
3 => Color::from_hue(0.1), 3 => Color::from_hue(0.1),
2 => Color::from_hue(1.22), 2 => Color::from_hue(1.22),
_ => Color::GREEN, _ => Color::GREEN,
}; };
canvas[p] = c; if ctx.cfg.sloweffect.unwrap() {
sleep(Duration::from_millis(
(SLOWEFFECT_FACTOR * (1.0 / ctx.datamatrix.layout.len() as f32)) as u64,
));
}
canvas.flush().unwrap();
max += 1; max += 1;
if max >= MATRIX_SIZE { if max >= MATRIX_SIZE {
break; break;
} }
if ctx.cfg.sloweffect.unwrap() {
sleep(Duration::from_millis(15));
}
canvas.flush().unwrap();
} }
ctx.hotreload(); ctx.hotreload();
} }
}*/ }
fn effect(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) { 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)) { loop {
for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) { for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
canvas[msg.pad()] = color; for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
println!("{msg:?}", msg = msg.pad()) canvas[msg.pad()] = color;
println!("{msg:?}", msg = msg.pad())
}
canvas.flush().unwrap();
canvas.iter().for_each(|pad| {
let surrounding_color = pad
.neighbors_5()
.iter()
.map(|&p| canvas.get(p).unwrap_or(Color::GREEN))
.sum::<Color>()
/ 5.0
/ 1.05;
canvas[pad] = canvas[pad].mix(surrounding_color, 0.4);
});
} }
let _res = canvas.flush(); clear(canvas);
canvas.iter().for_each(|pad| {
let surrounding_color = pad
.neighbors_5()
.iter()
.map(|&p| canvas.get(p).unwrap_or(Color::GREEN))
.sum::<Color>()
/ 5.0
/ 1.05;
canvas[pad] = canvas[pad].mix(surrounding_color, 0.4);
});
} }
} }
fn clear(canvas: &mut CanvasLayout) { fn clear(canvas: &mut CanvasLayout) {
for a in 0..8 { for a in 0..MATRIX_WIDTH {
for b in 0..9 { for b in 0..MATRIX_WIDTH + 1 {
canvas[Pad { x: a, y: b }] = Color { canvas[Pad { x: a, y: b }] = Color {
r: 0f32, r: ZERO_COLOR,
g: 0f32, g: ZERO_COLOR,
b: 1f32, b: ONE_COLOR,
}; };
} }
} }
let _res = canvas.flush(); canvas.flush().unwrap();
} }
/*
let (send, recv): (Sender<Context>, Receiver<Context>) = channel();
match ctx.mode.as_str() {
/*"draw" => {
let (mut canvas, mut _poller) = initpad();
draw(&mut canvas, c);
}
"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" => loop {
let mut data = cm.lock().unwrap();
let future1 = test(&mut data);
let mut data = cm.lock().unwrap();
let future2 = data.hotreload();
//futures::join!(future1, future2);
},
_ => {
println!("No valid option choosen for mode");
std::process::exit(1)
}
}*/

View File

@ -1,54 +1,64 @@
use crate::config::Config; use crate::config::Config;
use serde_json::json; use serde_json::json;
use serde_json::Value as JsonValue; use serde_json::Value as JsonValue;
use std::thread::sleep; //use std::thread::sleep;
use std::time::Duration; //use std::time::Duration;
pub const ZABBIX_API_VERSION: &'static str = "2.0"; pub const ZABBIX_API_VERSION: &'static str = "2.0";
pub const ZABBIX_API_ID: i32 = 1; pub const ZABBIX_API_ID: i32 = 1;
/// Refresh the user token /// Refresh the user token
pub async fn get_zabbix_authtoken(cfg: &mut Config) -> Result<(), reqwest::Error> { pub fn get_zabbix_authtoken(cfg: &mut Config) {
let body = build_query_auth_token(&cfg.username, &cfg.password); let body = build_query_auth_token(&cfg.username, &cfg.password);
let resp = reqwest::Client::new() let resp = reqwest::blocking::Client::new()
.post(&cfg.server) .post(&cfg.server)
.json(&body) .json(&body)
.send() .send();
.await?; match resp {
let values: JsonValue = resp.json().await?; Ok(v) => {
cfg.authtoken = Some(values["result"].as_str().unwrap().to_string()); let values: JsonValue = v.json().unwrap();
Ok(()) cfg.authtoken = Some(values["result"].as_str().unwrap().to_string());
}
Err(e) => {
println!("{}", e);
cfg.authtoken = Some("".to_string());
}
};
} }
/// Fetch Zabbix problems /// Fetch Zabbix problems
pub async fn get_zabbix_problems(cfg: &Config) -> Result<JsonValue, reqwest::Error> { pub fn get_zabbix_problems(cfg: &Config) -> Result<JsonValue, reqwest::Error> {
let body = build_query_triggers(&cfg.authtoken.as_ref().unwrap_or(&String::from(""))); let body = build_query_triggers(&cfg.authtoken.as_ref().unwrap_or(&String::from("")));
let resp = reqwest::Client::new() let resp = reqwest::blocking::Client::new()
.post(&cfg.server) .post(&cfg.server)
.json(&body) .json(&body)
.send() .send();
.await?;
Ok(resp.json().await?) match resp {
Ok(v) => v.json(),
Err(e) => Err(e),
}
} }
/// Check if Zabbix is operational /// Check if Zabbix is operational
/// Undefinitely check that Zabbix works /// Undefinitely check that Zabbix works
async fn _async_check_zabbix_connection(cfg: &Config) -> Result<bool, reqwest::Error> { /*fn check_zabbix_connection(cfg: &Config) -> bool {
let mut res: bool = false; let mut res: bool = false;
let delay = 5; let delay = 5;
let timeout = 10; let timeout = 10;
while !res { while !res {
let req = reqwest::Client::builder() let req = reqwest::blocking::Client::builder().timeout(Duration::from_secs(timeout)).build().unwrap();
.timeout(Duration::from_secs(timeout)) let resp = req.get(&cfg.server)
.build() .send();
.unwrap(); match resp {
req.get(&cfg.server).send().await?; Ok(_) => res = true,
res = true; Err(_) => res = false
println!("Waiting for {delay}", delay = delay); }
println!("Waiting for {delay}", delay=delay);
sleep(Duration::from_secs(delay)); sleep(Duration::from_secs(delay));
} }
Ok(res) res
} }*/
/// Build the query that fetchs the token /// Build the query that fetchs the token
fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) -> JsonValue { fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) -> JsonValue {
@ -57,7 +67,7 @@ fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) ->
"jsonrpc": ZABBIX_API_VERSION, "jsonrpc": ZABBIX_API_VERSION,
"method": zabbix_api_function, "method": zabbix_api_function,
"params": { "params": {
"user": zabbix_username, "username": zabbix_username,
"password": zabbix_password "password": zabbix_password
}, },
"id": ZABBIX_API_ID "id": ZABBIX_API_ID
@ -65,7 +75,7 @@ fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) ->
} }
/// Build the query that fetchs problems /// Build the query that fetchs problems
fn _build_query_problems(zabbix_token: &String, zabbix_limit: i64) -> JsonValue { /*fn build_query_problems(zabbix_token: &String, zabbix_limit: i64) -> JsonValue {
let zabbix_api_function = "problem.get"; let zabbix_api_function = "problem.get";
json!({ json!({
"jsonrpc": ZABBIX_API_VERSION, "jsonrpc": ZABBIX_API_VERSION,
@ -85,7 +95,7 @@ fn _build_query_problems(zabbix_token: &String, zabbix_limit: i64) -> JsonValue
"auth": zabbix_token, "auth": zabbix_token,
"id": ZABBIX_API_ID "id": ZABBIX_API_ID
}) })
} }*/
/// Build the query that fetchs triggers /// Build the query that fetchs triggers
fn build_query_triggers(zabbix_token: &String) -> JsonValue { fn build_query_triggers(zabbix_token: &String) -> JsonValue {

View File

@ -1,14 +1,14 @@
use serde_json::Value; use serde_json::Value;
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
#[derive(Debug, Clone)] /*#[derive(Debug, Clone)]
pub enum _ZabbixStatus { pub enum ZabbixStatus {
_Disaster = 5, Disaster = 5,
_High = 4, High = 4,
_Average = 3, Average = 3,
_Warning = 2, Warning = 2,
_Info = 1, Info = 1,
} }*/
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DataMatrix { pub struct DataMatrix {
@ -26,6 +26,10 @@ impl DataMatrix {
let severity = res["priority"].as_str().unwrap().parse::<i64>().unwrap(); let severity = res["priority"].as_str().unwrap().parse::<i64>().unwrap();
self.layout.push(ZabbixProblems { status: severity }); self.layout.push(ZabbixProblems { status: severity });
} }
// test
/*for i in 0..1000 {
self.layout.push(ZabbixProblems { status: 5 });
}*/
} }
} }