Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions relay/src/channel.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#[derive(Debug, Clone, Copy, Default)]
pub enum ChannelMessage {
Connect,
#[default]
Disconnected,
}
168 changes: 49 additions & 119 deletions relay/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
mod ipc;
mod message;
mod state;
mod update;
mod view;

use std::io::Read;
use std::net::TcpStream;
use std::str::from_utf8;
mod channel;
use channel::ChannelMessage;

use std::{
io::{BufRead, BufReader, Write},
io::{BufRead, BufReader, Write, Read},
net::TcpStream,
str::from_utf8,
sync::mpsc::{Receiver, Sender},
thread,
};

use self::{message::Message, state::State, update::update, view::view};
use self::{
ipc::ipc_connection_loop,
message::{IpcThreadMessage, Message},
state::State,
update::update,
view::view,
};

use iced::{
time::{every, Duration},
Task,
};
use interprocess::local_socket::{
traits::{Listener, ListenerExt},
GenericNamespaced, ListenerOptions, ToNsName,
Task, Theme,
};


use interprocess::local_socket::{traits::Listener, GenericNamespaced, ListenerOptions, ToNsName};

fn main() -> iced::Result {
//tcp connection

// Connect to the server

let (send, recv) = std::sync::mpsc::channel::<ChannelMessage>();
let tcp_connection = thread::spawn(move || match TcpStream::connect("127.0.0.1:7878") {
Ok(mut stream) => {
Expand All @@ -43,148 +46,75 @@ fn main() -> iced::Result {
}
});

// Communication channels between the main_gui_thread and the ipc_connection_thread
// tx_kill = transmit FROM main_gui_thread TO ipc_thread
// named txx_kill because the only thing it does rn is send a kill message to the thread. Can be renamed
let (tx_kill, rx_kill) = std::sync::mpsc::channel();
// txx = transmit FROM ipc_thread TO main_gui_thread
let (txx, rxx): (Sender<IpcThreadMessage>, Receiver<IpcThreadMessage>) =
std::sync::mpsc::channel();

let (txx, rxx) = std::sync::mpsc::channel();
// let _ = tx.send(()); // temp
let handle = thread::spawn(move || {
// sample pulled directly from `interprocess` documentation
// IPC connection Thread
let ipc_connection_handle = thread::spawn(move || {
println!("Initial IPC Connection!");

// Create new IPC Socket Listener builder
let printname = "baton.sock";
let name = printname.to_ns_name::<GenericNamespaced>().unwrap();

let opts = ListenerOptions::new().name(name);

// Create the actual IPC Socket Listener
let listener = match opts.create_sync() {
Ok(x) => x,
Err(e) if e.kind() == std::io::ErrorKind::AddrInUse => {
eprintln!(
"Error: could not start server because the socket file is occupied. Please check if
{printname} is in use by another process and try again."
"Error: could not start server because the socket file is occupied. Please check if {printname} is in use by another process and try again."
);
return;
}
x => x.unwrap(),
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
eprintln!("Error: could not start server because the OS denied permission: \n{e}");
return;
}
Err(e) => {
eprintln!("Other Error: {e}");
return;
}
};

listener
.set_nonblocking(interprocess::local_socket::ListenerNonblockingMode::Both)
.expect("Error setting non-blocking mode on listener");

eprintln!("Server running at {printname}");

let mut buffer = String::with_capacity(128);

for conn in listener.incoming() {
let conn = match (rx_kill.try_recv(), conn) {
(Ok(()), _) => return,
(_, Ok(c)) => {
println!("success");
c
}
(_, Err(e)) if e.kind() == std::io::ErrorKind::WouldBlock => {
continue;
}
(_, Err(e)) => {
eprintln!("Incoming connection failed: {e}");
continue;
}
};

let mut conn = BufReader::new(conn);
println!("Incoming connection!");

match conn.read_line(&mut buffer) {
Ok(_) => (),
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => continue,
_ => panic!(),
}

let write_res = conn
.get_mut()
.write_all(b"Hello, from the relay prototype (Rust)!\n");

match write_res {
Ok(_) => (),
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => continue,
_ => panic!(),
}
eprintln!("Server running at {printname}\n");

print!("Client answered: {buffer}");

buffer.clear();

// send frequency test -- three seconds of receiving 100,000 dummy inputs per second to check stability
println!("beginning frequency test...");
let start = std::time::Instant::now();
let mut recvs = vec![0, 0, 0];
loop {
let elapsed = std::time::Instant::now() - start;
let idx = match elapsed {
dur if dur < Duration::from_secs(1) => 0,
dur if dur < Duration::from_secs(2) => 1,
dur if dur < Duration::from_secs(3) => 2,
_ => break,
};
match conn.read_line(&mut buffer) {
/* Ok(0) => {
println!("Termination signal received from baton");
continue;
} */
Ok(_) => recvs[idx] += 1,
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => continue,
_ => panic!(),
}
}

println!("recvs: {recvs:?}");
buffer.clear();

// Continuously receive data from plugin
loop {
// TODO: Create display in GUI for this instead of printing to stdout. Just doing this for ease for the
// demo for the time being.
match conn.read_line(&mut buffer) {
Ok(s) if s == 0 || buffer.len() == 0 => {
buffer.clear();
continue;
}
Ok(s) => {
// remove trailing newline
let _ = buffer.pop();

// display
println!("Got: {buffer} ({s} bytes read)");

if let Ok(num) = buffer.parse::<f32>() {
let _ = txx.send(num);
}
buffer.clear();
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => continue,
Err(e) => panic!("Got err {e}"),
}
}
}
// Run the connection loop with the created socket listener
ipc_connection_loop(&listener, rx_kill, txx)
});

iced::application("RELAY", update, view)
.window_size((250.0, 100.0))
.window_size((400.0, 200.0))
.exit_on_close_request(false)
.subscription(subscribe)
.theme(theme)
.run_with(|| {
// for pre-run state initialization
let state = State {
elapsed_time: Duration::ZERO,
thread_handle: Some(handle),
ipc_conn_thread_handle: Some(ipc_connection_handle),
tx_kill: Some(tx_kill),
rx_baton: Some(rxx),
latest_baton_send: None,
recv: Some(recv),
connection_status: ChannelMessage::Disconnected,
active_baton_connection: false,
};
(state, Task::none())
})
}

fn theme(_state: &State) -> Theme {
Theme::TokyoNight
}

fn subscribe(_state: &State) -> iced::Subscription<Message> {
use Message as M;

Expand Down
5 changes: 1 addition & 4 deletions relay/src/message.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum Message {
// generic time update signal
Update,
Expand All @@ -8,9 +8,6 @@ pub(crate) enum Message {

// signal to check the baton thread
BatonMessage,

ConnectionMessage

}

// Enum for messages from within the IPC connection thread
Expand Down
13 changes: 6 additions & 7 deletions relay/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ use std::thread::JoinHandle;

use iced::time::Duration;

use crate::ChannelMessage;
use crate::IpcThreadMessage;

#[derive(Default)]
#[allow(unused)]
pub(crate) struct State {
pub elapsed_time: Duration,
pub thread_handle: Option<JoinHandle<()>>,
pub ipc_conn_thread_handle: Option<JoinHandle<()>>,
pub tx_kill: Option<std::sync::mpsc::Sender<()>>,
pub rx_baton: Option<std::sync::mpsc::Receiver<f32>>,
pub connection_status: ChannelMessage,
pub latest_baton_send: Option<f32>,
pub recv: Option<std::sync::mpsc::Receiver<ChannelMessage>>,
}
pub rx_baton: Option<std::sync::mpsc::Receiver<IpcThreadMessage>>,
pub latest_baton_send: Option<String>,
pub active_baton_connection: bool,
}
7 changes: 0 additions & 7 deletions relay/src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ pub(crate) fn update(state: &mut State, message: Message) -> Task<Message> {
};
Task::none()
}
M::ConnectionMessage => {
println!("Check Connection Status");
if let Some(status) = state.recv.as_ref().and_then(|recv| recv.try_recv().ok()) {
state.connection_status = status
}
Task::none()
}
_ => Task::none(),
}
}
21 changes: 15 additions & 6 deletions relay/src/view.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
use iced::{
widget::{button, column, text},
widget::{column, container, text},
Element,
};

use crate::{Message, State};

pub(crate) fn view(state: &State) -> Element<Message> {
let baton_data = match state.latest_baton_send {
// need to update view function with float parsing? perhaps? idk
let baton_data = match &state.latest_baton_send {
Some(num) => format!("[BATON] Pilot Elevation: {num:.3} ft"),
None => "No data from baton.".into(),
};

let connection_status = &state.connection_status;
// make this better. Perhaps add a funny emoji or sm
let baton_connect_status = if state.active_baton_connection {
format!(":) Baton Connected!")
} else {
format!(":( No Baton Connection")
};

column![
text(format!("Elapsed time: {:?}", state.elapsed_time)),
text(baton_data),
text(format!("Connection Staus: {:?}", connection_status)),
button("Check Connection Status").on_press(Message::ConnectionMessage)
// if we use containers, it boxes up the text elements and makes them easier to read
container(text(baton_connect_status))
.padding(10)
.center(400)
.style(container::rounded_box)
]
.into()
}
}
7 changes: 0 additions & 7 deletions src/clientserver/Cargo.lock

This file was deleted.

14 changes: 0 additions & 14 deletions src/clientserver/Cargo.toml

This file was deleted.

Loading