use crate::config;
use crate::ip_help_functions::ip2id;
use crate::network;
use crate::print;
use crate::world_view::{self, ElevatorContainer, WorldView};
use std::env;
use std::net::SocketAddr;
use std::time::Duration;
use tokio::net::UdpSocket;
use tokio::time::{sleep, timeout, Instant};
use tokio::process::Command;
use socket2::{Domain, Socket, Type};
use local_ip_address::local_ip;
pub async fn initialize_worldview(
self_container : Option<&world_view::ElevatorContainer>
) -> WorldView
{
let mut worldview = WorldView::default();
let elev_container: &mut ElevatorContainer = if let Some(container) = self_container
{
&mut container.to_owned()
} else
{
let container = ElevatorContainer::default();
&mut container.clone()
};
let ip = match local_ip()
{
Ok(ip) => ip,
Err(e) =>
{
print::err(format!("Failed to get local IP at startup: {}", e));
panic!();
}
};
network::set_self_id(ip2id(ip));
elev_container.elevator_id = network::read_self_id();
worldview.master_id = network::read_self_id();
worldview.add_elev(elev_container.clone());
let mut wv_from_udp = match check_for_udp().await
{
Some(wv) => wv,
None =>
{
print::info("No other elevators detected on the network.".to_string());
return worldview
},
};
let saved_cab_requests: std::collections::HashMap<u8, Vec<bool>> = wv_from_udp.cab_requests_backup.clone();
if let Some(saved_requests) = saved_cab_requests.get(&elev_container.elevator_id)
{
elev_container.cab_requests = saved_requests.clone();
}
wv_from_udp.add_elev(elev_container.clone());
if wv_from_udp.master_id > network::read_self_id()
{
wv_from_udp.master_id = network::read_self_id();
}
wv_from_udp
}
async fn check_for_udp() -> Option<WorldView>
{
let broadcast_listen_addr = format!("{}:{}", config::BC_LISTEN_ADDR, config::BROADCAST_PORT);
let socket_addr: SocketAddr = broadcast_listen_addr.parse().expect("Invalid address");
let socket_temp = Socket::new(Domain::IPV4, Type::DGRAM, None)
.expect("Failed to create new socket");
socket_temp.set_nonblocking(true).expect("Failed to set non-blocking");
socket_temp.set_reuse_address(true).expect("Failed to set reuse address");
socket_temp.set_broadcast(true).expect("Failed to enable broadcast mode");
socket_temp.bind(&socket_addr.into()).expect("Failed to bind socket");
let socket = UdpSocket::from_std(socket_temp.into()).expect("Failed to create UDP socket");
let mut buf = [0; config::UDP_BUFFER];
let mut read_wv: Option<WorldView>;
let time_start = Instant::now();
let duration = Duration::from_secs(1);
while Instant::now().duration_since(time_start) < duration
{
let recv_result = timeout(duration, socket.recv_from(&mut buf)).await;
match recv_result
{
Ok(Ok((len, _))) =>
{
read_wv = network::udp_broadcast::parse_message(&buf[..len]);
}
Ok(Err(e)) =>
{
print::err(format!("init.rs, udp_listener(): {}", e));
continue;
}
Err(_) =>
{
print::warn("Timeout - no data received within 1 second.".to_string());
break;
}
}
match read_wv
{
Some(wv) =>
{
return Some(wv);
},
None =>
{
continue;
}
}
}
drop(socket);
None
}
pub fn parse_args() -> bool
{
let args: Vec<String> = env::args().collect();
if args.len() <= 0 {return false}
for arg in &args[1..]
{
let parts: Vec<&str> = arg.split("::").collect();
if parts.len() == 2
{
let key = parts[0].to_lowercase();
let value = parts[1].to_lowercase();
let is_true = value == "true";
match key.as_str()
{
"print_wv" => *config::PRINT_WV_ON.lock().unwrap() = is_true,
"print_err" => *config::PRINT_ERR_ON.lock().unwrap() = is_true,
"print_warn" => *config::PRINT_WARN_ON.lock().unwrap() = is_true,
"print_ok" => *config::PRINT_OK_ON.lock().unwrap() = is_true,
"print_info" => *config::PRINT_INFO_ON.lock().unwrap() = is_true,
"print_else" => *config::PRINT_ELSE_ON.lock().unwrap() = is_true,
"debug" => { *config::PRINT_WV_ON.lock().unwrap() = false;
*config::PRINT_WARN_ON.lock().unwrap() = false;
*config::PRINT_OK_ON.lock().unwrap() = false;
*config::PRINT_INFO_ON.lock().unwrap() = false;
*config::PRINT_ELSE_ON.lock().unwrap() = false;
}
_ => {}
}
} else if arg.to_lowercase() == "help"
{
println!("Tilgjengelige argument:");
println!(" print_wv::true/false");
println!(" print_err::true/false");
println!(" print_warn::true/false");
println!(" print_ok::true/false");
println!(" print_info::true/false");
println!(" print_else::true/false");
println!(" debug (kun error-meldingar vises)");
println!(" backup (starter backup-prosess)");
std::process::exit(0);
} else if arg.to_lowercase() == "backup"
{
return true;
}
}
false
}
pub fn get_terminal_command() -> (String, Vec<String>)
{
if cfg!(target_os = "windows")
{
("cmd".to_string(), vec!["/C".to_string(), "start".to_string()])
} else
{
("gnome-terminal".to_string(), vec!["--".to_string()])
}
}
pub async fn build_cost_fn()
{
let output = Command::new("bash")
.arg("build.sh")
.current_dir("libs/Project_resources/cost_fns/hall_request_assigner")
.output()
.await
.expect("Failed to run build.sh");
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("stderr: {}", String::from_utf8_lossy(&output.stderr));
if output.status.success()
{
println!("build.sh completed successfully.");
} else
{
eprintln!("build.sh failed. Please try building manually in a new terminal:");
eprintln!("1. cd libs/Project_resources/cost_fns/hall_request_assigner");
eprintln!("2. bash build.sh");
panic!("Failed to build hall_request_assigner.");
}
sleep(Duration::from_millis(2000)).await;
}