use super::timer::Timer;
use crate::elevio::{self, elev as e};
use crate::world_view::{ElevatorContainer, ElevatorBehaviour};
use crate::config;
use crate::print;
use crate::network;
use crossbeam_channel as cbc;
use tokio::time::{sleep, Duration};
use tokio::process::Command;
use tokio::sync::mpsc;
struct LocalElevTxs
{
call_button: cbc::Sender<elevio::CallButton>,
floor_sensor: cbc::Sender<u8>,
stop_button: cbc::Sender<bool>,
obstruction: cbc::Sender<bool>,
}
struct LocalElevRxs
{
call_button: cbc::Receiver<elevio::CallButton>,
floor_sensor: cbc::Receiver<u8>,
stop_button: cbc::Receiver<bool>,
obstruction: cbc::Receiver<bool>,
}
struct LocalElevChannels
{
pub rxs: LocalElevRxs,
pub txs: LocalElevTxs,
}
impl LocalElevChannels
{
pub fn new() -> Self
{
let (call_button_tx, call_button_rx) = cbc::unbounded::<elevio::CallButton>();
let (floor_sensor_tx, floor_sensor_rx) = cbc::unbounded::<u8>();
let (stop_button_tx, stop_button_rx) = cbc::unbounded::<bool>();
let (obstruction_tx, obstruction_rx) = cbc::unbounded::<bool>();
LocalElevChannels
{
rxs: LocalElevRxs { call_button: call_button_rx, floor_sensor: floor_sensor_rx, stop_button: stop_button_rx, obstruction: obstruction_rx },
txs: LocalElevTxs { call_button: call_button_tx, floor_sensor: floor_sensor_tx, stop_button: stop_button_tx, obstruction: obstruction_tx }
}
}
}
fn get_ip_address() -> String
{
let self_id = network::read_self_id();
format!("{}.{}", config::NETWORK_PREFIX, self_id)
}
async fn start_elevator_server()
{
let ip_address = get_ip_address();
let ssh_password = config::SSH_PASSWORD;
if cfg!(target_os = "windows")
{
print::info(format!("Starting elevatorserver on Windows..."));
Command::new("cmd")
.args(&["/C", "start", "elevatorserver"])
.spawn()
.expect("Failed to start elevator server");
} else
{
print::info(format!("Starting elevatorserver on Linux..."));
let elevator_server_command = format!(
"sshpass -p '{}' ssh student@{} 'nohup elevatorserver > /dev/null 2>&1 &'",
ssh_password, ip_address
);
print::info(format!("\nStarting elevatorserver in new terminal:\n\t{}", elevator_server_command));
let _ = Command::new("sh")
.arg("-c")
.arg(&elevator_server_command)
.output().await
.expect("Error while starting elevatorserver");
}
print::ok(format!("Elevator server started."));
}
pub async fn init(
local_elev_tx: mpsc::Sender<elevio::ElevMessage>
) -> e::Elevator
{
start_elevator_server().await;
let local_elev_channels: LocalElevChannels = LocalElevChannels::new();
let _ = sleep(config::SLAVE_TIMEOUT);
let elevator: e::Elevator = e::Elevator::init(config::LOCAL_ELEV_IP, config::DEFAULT_NUM_FLOORS)
.expect("Error while initiating elevator");
{
let elevator = elevator.clone();
tokio::spawn(async move {
elevio::poll::call_buttons(elevator, local_elev_channels.txs.call_button, config::ELEV_POLL)
});
}
{
let elevator = elevator.clone();
tokio::spawn(async move {
elevio::poll::floor_sensor(elevator, local_elev_channels.txs.floor_sensor, config::ELEV_POLL)
});
}
{
let elevator = elevator.clone();
tokio::spawn(async move {
elevio::poll::stop_button(elevator, local_elev_channels.txs.obstruction, config::ELEV_POLL)
});
}
{
let elevator = elevator.clone();
tokio::spawn(async move {
elevio::poll::obstruction(elevator, local_elev_channels.txs.stop_button, config::ELEV_POLL)
});
}
{
tokio::spawn(async move {
let _ = read_from_local_elevator(local_elev_channels.rxs, local_elev_tx).await;
});
}
elevator
}
async fn read_from_local_elevator(
rxs: LocalElevRxs,
local_elev_tx: mpsc::Sender<elevio::ElevMessage>
) -> std::io::Result<()>
{
loop
{
if let Ok(call_button) = rxs.call_button.try_recv()
{
let msg = elevio::ElevMessage
{
msg_type: elevio::ElevMsgType::CALLBTN,
call_button: Some(call_button),
floor_sensor: None,
stop_button: None,
obstruction: None,
};
let _ = local_elev_tx.send(msg).await;
}
if let Ok(floor) = rxs.floor_sensor.try_recv()
{
let msg = elevio::ElevMessage
{
msg_type: elevio::ElevMsgType::FLOORSENS,
call_button: None,
floor_sensor: Some(floor),
stop_button: None,
obstruction: None,
};
let _ = local_elev_tx.send(msg).await;
}
if let Ok(stop) = rxs.stop_button.try_recv()
{
let msg = elevio::ElevMessage
{
msg_type: elevio::ElevMsgType::STOPBTN,
call_button: None,
floor_sensor: None,
stop_button: Some(stop),
obstruction: None,
};
let _ = local_elev_tx.send(msg).await;
}
if let Ok(obstr) = rxs.obstruction.try_recv()
{
let msg = elevio::ElevMessage
{
msg_type: elevio::ElevMsgType::OBSTRX,
call_button: None,
floor_sensor: None,
stop_button: None,
obstruction: Some(obstr),
};
let _ = local_elev_tx.send(msg).await;
}
sleep(Duration::from_millis(10)).await;
}
}
pub async fn update_elev_container_from_msgs(
local_elev_rx: &mut mpsc::Receiver<elevio::ElevMessage>,
container: &mut ElevatorContainer,
cab_priority_timer: &mut Timer,
error_timer: &mut Timer
)
{
loop
{
match local_elev_rx.try_recv()
{
Ok(msg) =>
{
match msg.msg_type
{
elevio::ElevMsgType::CALLBTN =>
{
if let Some(call_btn) = msg.call_button
{
print::info(format!("Callbutton: {:?}", call_btn));
match call_btn.call_type
{
elevio::CallType::INSIDE =>
{
cab_priority_timer.release_timer();
container.cab_requests[call_btn.floor as usize] = true;
}
elevio::CallType::UP =>
{
container.unsent_hall_request[call_btn.floor as usize][0] = true;
}
elevio::CallType::DOWN =>
{
container.unsent_hall_request[call_btn.floor as usize][1] = true;
}
elevio::CallType::COSMIC_ERROR => {},
}
}
}
elevio::ElevMsgType::FLOORSENS =>
{
print::info(format!("Floor: {:?}", msg.floor_sensor));
if let Some(floor) = msg.floor_sensor
{
container.last_floor_sensor = floor;
}
}
elevio::ElevMsgType::STOPBTN =>
{
print::info(format!("Stop button: {:?}", msg.stop_button));
if let Some(stop) = msg.stop_button
{
container.stop = stop;
}
}
elevio::ElevMsgType::OBSTRX =>
{
print::info(format!("Obstruction: {:?}", msg.obstruction));
if let Some(obs) = msg.obstruction
{
container.obstruction = obs;
if !obs && error_timer.timer_timeouted()
{
error_timer.timer_start();
container.behaviour = ElevatorBehaviour::Idle;
}
}
}
}
},
Err(_) => {break}
}
}
}