use crate::config;
use crate::network;
use crate::world_view::{Dirn, ElevatorBehaviour, WorldView};
use ansi_term::Colour::{self, Green, Red, Yellow, Purple, White};
use unicode_width::UnicodeWidthStr;
pub fn color(
msg: String,
color: Colour)
{
let print_stat = config::PRINT_ELSE_ON.lock().unwrap().clone();
if print_stat
{
println!("{}{}\n", color.paint("[CUSTOM]: "), color.paint(msg));
}
}
pub fn err(msg: String)
{
let print_stat = config::PRINT_ERR_ON.lock().unwrap().clone();
if print_stat {
println!("{}{}\n", Red.paint("[ERROR]: "), Red.paint(msg));
}
}
pub fn warn(msg: String)
{
let print_stat = config::PRINT_WARN_ON.lock().unwrap().clone();
if print_stat {
println!("{}{}\n", Yellow.paint("[WARNING]: "), Yellow.paint(msg));
}
}
pub fn ok(msg: String)
{
let print_stat = config::PRINT_OK_ON.lock().unwrap().clone();
if print_stat {
println!("{}{}\n", Green.paint("[OK]: "), Green.paint(msg));
}
}
pub fn info(msg: String)
{
let print_stat = config::PRINT_INFO_ON.lock().unwrap().clone();
let light_blue = Colour::RGB(102, 178, 255);
if print_stat {
println!("{}{}\n", light_blue.paint("[INFO]: "), light_blue.paint(msg));
}
}
pub fn master(msg: String)
{
let print_stat = config::PRINT_ELSE_ON.lock().unwrap().clone();
let pink = Colour::RGB(255, 51, 255);
if print_stat {
println!("{}[MASTER]: {}\n", pink.paint(""), pink.paint(msg));
}
}
pub fn slave(msg: String)
{
let print_stat = config::PRINT_ELSE_ON.lock().unwrap().clone();
let random = Colour::RGB(153, 76, 0);
if print_stat {
println!("{}{}\n", random.paint("[MASTER]: "), random.paint(msg));
}
}
pub fn cosmic_err(fun: String)
{
print!("{}", Colour::Red.paint("[ERROR]: "));
let colors = [
Colour::Red,
Colour::Yellow,
Colour::Green,
Colour::Cyan,
Colour::Blue,
Colour::Purple,
];
let message = format!("Cosmic rays flipped a bit! 👽 ⚛️ 🔄 1️⃣ 0️⃣ IN: {}", fun);
for (i, c) in message.chars().enumerate()
{
let color = colors[i % colors.len()];
print!("{}", color.paint(c.to_string()));
}
println!();
}
fn pad_text(text: &str, width: usize) -> String
{
let visible_width = UnicodeWidthStr::width(text);
let padding = width.saturating_sub(visible_width);
format!("{}{}", text, " ".repeat(padding))
}
fn colored_bool_label(value: bool, width: usize) -> String
{
let raw_text = if value { "true" } else { "false" };
let padded = pad_text(raw_text, width); if value {
Green.paint(padded).to_string()
} else {
Red.paint(padded).to_string()
}
}
fn rgb_color_for_loss(loss: u8) -> String
{
let (r, g) = if loss <= 50
{
let ratio = loss as f32 / 50.0;
let r = (ratio * 255.0) as u8;
(r, 255)
} else
{
let ratio = (loss as f32 - 50.0) / 50.0;
let g = ((1.0 - ratio) * 255.0) as u8;
(255, g)
};
format!("\x1b[38;2;{};{};0m", r, g)
}
fn colored_loss_bar(loss: u8, width: usize) -> String {
let mut filled = (loss as usize * width) / 100;
if loss == 0
{
filled = 1;
}
let mut bar = String::new();
let k = 20.0;
for i in 0..width
{
let symbol = if i < filled { "█" } else { " " };
let x = i as f32 / width as f32; let intensity = ((1.0 + k * x).ln()) / ((1.0 + k).ln());
let r = (intensity * 255.0) as u8;
let g = ((1.0 - intensity) * 255.0) as u8;
let color = format!("\x1b[38;2;{};{};0m", r, g);
bar.push_str(&format!("{}{}{}", color, symbol, "\x1b[0m"));
}
bar
}
pub fn worldview(worldview: &WorldView, connection: Option<network::ConnectionStatus> )
{
let print_stat = config::PRINT_WV_ON.lock().unwrap().clone();
if !print_stat {return}
println!("{}", ansi_term::Colour::Cyan.bold().paint("┌────────────────────────────────┐"));
println!("{}", ansi_term::Colour::Cyan.bold().paint("│ ELEVATOR NETWORK CONNECTION │"));
println!("{}", ansi_term::Colour::Cyan.bold().paint("└────────────────────────────────┘"));
match connection
{
Some(status) =>
{
let on_net_color = colored_bool_label(status.on_internett, 5);
let elev_net_color = colored_bool_label(status.connected_on_elevator_network, 5);
let color_prefix = rgb_color_for_loss(status.packet_loss);
let reset = "\x1b[0m";
let bar = colored_loss_bar(status.packet_loss, 27);
println!("┌───────────────────────────────┐");
println!("│ On internett: {} │", on_net_color);
println!("│ Elevator network: {} │", elev_net_color);
println!("│ Packet loss: {}{:>8}%{:>2} │", color_prefix, status.packet_loss, reset);
println!("│ [{}] │", bar);
println!("└───────────────────────────────┘");
}
None =>
{
println!("┌───────────────────────────────┐");
println!("│ Connection status: Not set │");
println!("└───────────────────────────────┘");
}
}
println!("{}", Purple.bold().paint("┌────────────────────────────────┐"));
println!("{}", Purple.bold().paint("│ WORLD VIEW STATUS │"));
println!("{}", Purple.bold().paint("└────────────────────────────────┘"));
println!("┌─────────────┬──────────┬────────────────────┐");
println!("{}", White.bold().paint("│ Num heiser │ MasterID │ Pending tasks │"));
println!("├─────────────┼──────────┼────────────────────┤");
println!(
"│ {:<11} │ {:<8} │ │",
worldview.get_num_elev(),
worldview.master_id
);
for (floor, calls) in worldview.hall_request.iter().enumerate().rev()
{
let up = if floor != worldview.hall_request.len() - 1
{
if calls[0] { "🟢" } else { "🔴" }
} else
{
" " };
let down = if floor != 0
{
if calls[1] { "🟢" } else { "🔴" }
} else
{
" " };
println!(
"│ floor:{:<5} │ │ {} {} │",
floor,
down,
up
);
}
println!("└─────────────┴──────────┴────────────────────┘");
println!("┌──────┬──────────┬──────────────┬──────────────┬─────────────┬──────────────────────┬───────────────┐");
println!("{}", ansi_term::Colour::White.bold().paint("│ ID │ Dør │ Obstruksjon │ Tasks │ Siste etasje│ Calls (Etg:Call) │ Elev status │"));
println!("├──────┼──────────┼──────────────┼──────────────┼─────────────┼──────────────────────┼───────────────┤");
for elev in &worldview.elevator_containers
{
let id_text = pad_text(&format!("{}", elev.elevator_id), 4);
let door_text = if elev.behaviour == ElevatorBehaviour::DoorOpen || elev.behaviour == ElevatorBehaviour::ObstructionError
{
pad_text(&Yellow.paint("Open").to_string(), 17)
} else
{
pad_text(&Green.paint("Lukka").to_string(), 17)
};
let obstruction_text = if elev.obstruction
{
pad_text(&Red.paint("Ja").to_string(), 21)
} else
{
pad_text(&Green.paint("Nei").to_string(), 21)
};
let tasks_emoji: Vec<String> = elev.cab_requests.iter().enumerate().rev()
.map(|(floor, task)| format!("{:<2} {}", floor, if *task { "🟢" } else { "🔴" }))
.collect();
let num_floors = elev.tasks.len();
let call_list_emoji: Vec<String> = elev.tasks.iter().enumerate().rev()
.map(|(floor, calls)| {
let up = if floor != num_floors - 1 {
if calls[0] { "🟢" } else { "🔴" }
} else {
"⚫" };
let down = if floor != 0 {
if calls[1] { "🟢" } else { "🔴" }
} else {
"⚫" };
format!("{:<2} {} {}", floor, down, up)
})
.collect();
let task_status = match (elev.dirn, elev.behaviour)
{
(_, ElevatorBehaviour::Idle) => pad_text(&Green.paint("Idle").to_string(), 22),
(Dirn::Up, ElevatorBehaviour::Moving) => pad_text(&Yellow.paint("⬆️ Moving").to_string(), 23),
(Dirn::Down, ElevatorBehaviour::Moving) => pad_text(&Yellow.paint("⬇️ Moving").to_string(), 23),
(Dirn::Stop, ElevatorBehaviour::Moving) => pad_text(&Yellow.paint("Not Moving").to_string(), 22),
(_, ElevatorBehaviour::DoorOpen) => pad_text(&Purple.paint("Door Open").to_string(), 22),
(_, ElevatorBehaviour::ObstructionError) => pad_text(&Red.paint("Obstruction Error").to_string(), 22),
(_, ElevatorBehaviour::TravelError) => pad_text(&Red.paint("Travel Error").to_string(), 22),
(_, ElevatorBehaviour::CosmicError) => pad_text(&Red.paint("Cosmic Error?").to_string(), 22),
};
let max_rows = std::cmp::max(tasks_emoji.len(), call_list_emoji.len());
for i in 0..max_rows
{
let task_entry = tasks_emoji.get(i).cloned().unwrap_or_else(|| " ".to_string());
let call_entry = call_list_emoji.get(i).cloned().unwrap_or_else(|| " ".to_string());
if i == 0
{
println!(
"│ {} │ {} │ {} │ {:<11} │ {:<11} │ {:<18} │ {} │",
id_text, door_text, obstruction_text, task_entry, elev.last_floor_sensor, call_entry, task_status
);
} else
{
println!(
"│ │ │ │ {:<11} │ │ {:<18} │ │",
task_entry, call_entry
);
}
}
println!("├──────┼──────────┼──────────────┼──────────────┼─────────────┼──────────────────────┼───────────────┤");
}
println!("└──────┴──────────┴──────────────┴──────────────┴─────────────┴──────────────────────┴───────────────┘");
}