Compare commits

..

5 Commits

11 changed files with 920 additions and 833 deletions

View File

@ -1,37 +1,101 @@
---
kind: pipeline
type: docker
name: default
name: cleanup-before
steps:
- name: test
image: rust:1
- name: clean
image: alpine
commands:
- apt-get update -y
- apt-get install libasound2-dev -y
- cargo build --verbose --all
- cargo test --verbose --all
- 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
- rm -rf /build/*
volumes:
- name: build
path: /build
when:
event: tag
- name: publish
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
image: plugins/gitea-release
settings:
base_url: https://git.paulbsd.com
api_key:
from_secret: gitea_token
files: "target/release/*.tar.gz"
files: "*.tar.gz"
checksum:
- sha256
- sha512
environment:
PLUGIN_TITLE: ""
title: VERSION
volumes:
- name: build
path: /drone/src/build
when:
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

1130
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,17 @@
[package]
name = "zabbixlaunch"
version = "1.0.1"
edition = "2021"
authors = ["PaulBSD <paul@paulbsd.com>"]
description = "Lights up Launchpad mini using Zabbix data"
repository = "https://git.paulbsd.com/paulbsd/zabbixlaunch"
version = "1.0.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = "4.3"
embedded-graphics = { version = "0.8", optional = true }
launchy = { git = "https://github.com/paulbsd/launchy", branch = "develop-launchpad-mini-mk3" }
nix = "0.26"
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] }
clap = { version = "2", features = ["yaml"] }
embedded-graphics = { version = "0.7", optional = true }
futures = "*"
launchy = { git = "https://github.com/kangalioo/launchy", branch = "master" }
nix = "0.23"
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }

View File

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

View File

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

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

View File

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

View File

@ -1,137 +1,126 @@
use crate::config::{Context, Mode};
use crate::zabbix::api::get_zabbix_problems;
use crate::config::{hotreload, Context};
use crate::zabbix::api::*;
use launchy::Color;
use launchy::{self, Canvas, CanvasLayout, CanvasLayoutPoller, MsgPollingWrapper, Pad};
use std::thread::sleep;
use std::time::Duration;
use tokio::sync::mpsc;
use tokio::time::sleep;
const MATRIX_WIDTH: i32 = 8;
const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH;
const REQUEST_TIMEOUT: i32 = 5;
const REQUEST_WAITFOR: i32 = 5;
const ZERO_COLOR: f32 = 0.;
const ONE_COLOR: f32 = 1.;
const SLOWEFFECT_FACTOR: f32 = 800.0;
pub fn run(ctx: &mut Context) {
match ctx.mode {
Some(Mode::Draw) => {
let (mut canvas, mut _poller) = initpad();
draw(&mut canvas, ctx);
}
Some(Mode::Input) => {
let (mut canvas, mut poller) = initpad();
input(&mut canvas, &mut poller);
}
Some(Mode::Effect) => {
let (mut canvas, mut poller) = initpad();
effect(&mut canvas, &mut poller)
}
Some(Mode::Test) => test(ctx),
None => {
println!("No valid option choosen for mode");
}
const POLLER_ITER: u64 = 17;
fn log(num: i64) {
return println!("test{} 🦀", num);
}
pub async fn run<'a>() {
log(1);
let mut ctx = Box::new(Context::new().await);
log(2);
let (tx1, mut rx1) = mpsc::channel(1);
log(3);
let t1 = tokio::task::spawn(async move { test(&mut rx1).await });
let t2 = tokio::task::spawn(async move { hotreload(&mut ctx, &tx1).await });
tokio::join!(t1, t2);
log(4);
}
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) {
let (mut canvas, poller) = launchy::CanvasLayout::new_polling();
match canvas.add_by_guess_rotated::<launchy::mini::Canvas>(0, 0, launchy::Rotation::None) {
Ok(_) => {
println!("Connected to device using USB");
clear(&mut canvas);
}
Ok(o) => o,
Err(err) => {
eprintln!("Failed to connect to device, check USB connection {err}",);
eprintln!(
"Failed to connect to device, check USB connection {err}",
err = err
);
}
};
println!("Connected to device using USB");
clear(&mut canvas);
(canvas, poller)
}
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 msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
for msg in poller
.iter_for_millis(POLLER_ITER)
.filter(|msg| msg.is_press())
{
canvas[msg.pad()] = color;
println!("{msg:?}", msg = msg.pad())
}
canvas.flush().unwrap();
let _res = canvas.flush();
}
}
fn test(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) {
/*
async fn draw(canvas: &mut CanvasLayout<'_>, ctx: &mut Context) {
println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap());
loop {
let mut max = 0;
let zabbix_data = match get_zabbix_problems(&ctx.cfg) {
let zabbix_data_result = get_zabbix_problems(&ctx.cfg);
let zabbix_data = match zabbix_data_result {
Ok(zabbix_data) => zabbix_data,
Err(_) => {
println!(
"Error requesting zabbix service, retrying in {}",
REQUEST_TIMEOUT
REQUEST_WAITFOR
);
sleep(Duration::from_secs(REQUEST_TIMEOUT as u64));
sleep(Duration::from_secs(REQUEST_WAITFOR as u64));
continue;
}
};
ctx.datamatrix.compute(&zabbix_data);
clear(canvas);
let mut max = 0;
for (i, j) in ctx.datamatrix.layout.iter().enumerate() {
let p = Pad {
x: ((i as i32) % MATRIX_WIDTH),
y: ((i as i32) / MATRIX_WIDTH) + 1,
};
canvas[p] = match j.status {
let c = match j.status {
5 => Color::RED,
4 => Color::from_hue(0.05),
3 => Color::from_hue(0.1),
2 => Color::from_hue(1.22),
_ => Color::GREEN,
};
if ctx.cfg.sloweffect.unwrap() {
sleep(Duration::from_millis(
(SLOWEFFECT_FACTOR * (1.0 / ctx.datamatrix.layout.len() as f32)) as u64,
));
}
canvas.flush().unwrap();
canvas[p] = c;
max += 1;
if max >= MATRIX_SIZE {
break;
}
if ctx.cfg.sloweffect.unwrap() {
sleep(Duration::from_millis(15));
}
canvas.flush().unwrap();
}
ctx.hotreload();
}
}
}*/
fn effect(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
loop {
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()) {
canvas[msg.pad()] = color;
println!("{msg:?}", msg = msg.pad())
}
canvas.flush().unwrap();
let _res = canvas.flush();
canvas.iter().for_each(|pad| {
let surrounding_color = pad
@ -145,19 +134,44 @@ fn effect(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
canvas[pad] = canvas[pad].mix(surrounding_color, 0.4);
});
}
clear(canvas);
}
}
fn clear(canvas: &mut CanvasLayout) {
for a in 0..MATRIX_WIDTH {
for b in 0..MATRIX_WIDTH + 1 {
for a in 0..8 {
for b in 0..9 {
canvas[Pad { x: a, y: b }] = Color {
r: ZERO_COLOR,
g: ZERO_COLOR,
b: ONE_COLOR,
r: 0f32,
g: 0f32,
b: 1f32,
};
}
}
canvas.flush().unwrap();
let _res = canvas.flush();
}
/*
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,64 +1,54 @@
use crate::config::Config;
use serde_json::json;
use serde_json::Value as JsonValue;
//use std::thread::sleep;
//use std::time::Duration;
use std::thread::sleep;
use std::time::Duration;
pub const ZABBIX_API_VERSION: &'static str = "2.0";
pub const ZABBIX_API_ID: i32 = 1;
/// Refresh the user token
pub fn get_zabbix_authtoken(cfg: &mut Config) {
pub async fn get_zabbix_authtoken(cfg: &mut Config) -> Result<(), reqwest::Error> {
let body = build_query_auth_token(&cfg.username, &cfg.password);
let resp = reqwest::blocking::Client::new()
let resp = reqwest::Client::new()
.post(&cfg.server)
.json(&body)
.send();
match resp {
Ok(v) => {
let values: JsonValue = v.json().unwrap();
.send()
.await?;
let values: JsonValue = resp.json().await?;
cfg.authtoken = Some(values["result"].as_str().unwrap().to_string());
}
Err(e) => {
println!("{}", e);
cfg.authtoken = Some("".to_string());
}
};
Ok(())
}
/// Fetch Zabbix problems
pub fn get_zabbix_problems(cfg: &Config) -> Result<JsonValue, reqwest::Error> {
pub async fn get_zabbix_problems(cfg: &Config) -> Result<JsonValue, reqwest::Error> {
let body = build_query_triggers(&cfg.authtoken.as_ref().unwrap_or(&String::from("")));
let resp = reqwest::blocking::Client::new()
let resp = reqwest::Client::new()
.post(&cfg.server)
.json(&body)
.send();
match resp {
Ok(v) => v.json(),
Err(e) => Err(e),
}
.send()
.await?;
Ok(resp.json().await?)
}
/// Check if Zabbix is operational
/// Undefinitely check that Zabbix works
/*fn check_zabbix_connection(cfg: &Config) -> bool {
async fn _async_check_zabbix_connection(cfg: &Config) -> Result<bool, reqwest::Error> {
let mut res: bool = false;
let delay = 5;
let timeout = 10;
while !res {
let req = reqwest::blocking::Client::builder().timeout(Duration::from_secs(timeout)).build().unwrap();
let resp = req.get(&cfg.server)
.send();
match resp {
Ok(_) => res = true,
Err(_) => res = false
}
println!("Waiting for {delay}", delay=delay);
let req = reqwest::Client::builder()
.timeout(Duration::from_secs(timeout))
.build()
.unwrap();
req.get(&cfg.server).send().await?;
res = true;
println!("Waiting for {delay}", delay = delay);
sleep(Duration::from_secs(delay));
}
res
}*/
Ok(res)
}
/// Build the query that fetchs the token
fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) -> JsonValue {
@ -67,7 +57,7 @@ fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) ->
"jsonrpc": ZABBIX_API_VERSION,
"method": zabbix_api_function,
"params": {
"username": zabbix_username,
"user": zabbix_username,
"password": zabbix_password
},
"id": ZABBIX_API_ID
@ -75,7 +65,7 @@ fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) ->
}
/// 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";
json!({
"jsonrpc": ZABBIX_API_VERSION,
@ -95,7 +85,7 @@ fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) ->
"auth": zabbix_token,
"id": ZABBIX_API_ID
})
}*/
}
/// Build the query that fetchs triggers
fn build_query_triggers(zabbix_token: &String) -> JsonValue {

View File

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