/* This file is part of Tarangam. Tarangam is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Tarangam is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Tarangam. If not, see */ //! Feel free to see through codes. Application is not written to be used as a library for other app. :) mod graph; use gtk::prelude::*; use rand::Rng; use std::{collections::HashMap, sync::{Arc, Mutex}}; use std::rc::Rc; use std::cell::RefCell; use std::io::prelude::*; use std::io::BufReader; use graph::Graph; /// Status of Serial reading enum Status { JAGRIT, // Mode of Active SAYAN, // Mode of Sleeping AVRODTIH, // Mode of being stopped PARIVARTIT // Mode of being values modified } /// Configuration to read from Serial Port pub struct Config { status: Status, bondrate: u32, port: String } impl Config { pub fn new() -> Config { Config { status: Status::AVRODTIH, bondrate: 9600, port: "".to_owned() } } } /// For communication between mpsc of graph and serial port enum MessageSerialThread { Msg(String), Status(String) } // Building and configuring GUI pub fn build_ui(app: >k::Application) { let config = Arc::new(Mutex::new(Config::new())); let builder = gtk::Builder::from_file("ui.glade"); let win = builder.get_object::("win").expect("Resource file missing!"); win.set_application(Some(app)); let bar = builder.get_object::("status_bar").expect("Resource file missing!"); let log_area = builder.get_object::("log_area").expect("Resource file missing!"); let graph = Graph::new( builder.get_object::("draw_area").expect("Resource file missing!"), 0.0, 100.0, 0.0, 100.0, false, true, false, true, HashMap::new(), 0.0 ); win.show_all(); // exit_menu let exit_menu = builder.get_object::("exit_menu").expect("Resource file missing!"); let tmp_win = win.clone(); exit_menu.connect_activate(move |_|{ unsafe { tmp_win.destroy(); } }); // about_menu let about_menu = builder.get_object::("about_menu").expect("Resource file missing!"); let about_window = builder.get_object::("about_window").expect("Resource file missing!"); about_window.set_transient_for(Some(&win)); about_window.connect_delete_event(|win,_| { win.hide(); Inhibit(true) }); let a_win = about_window.clone(); about_menu.connect_activate(move |_|{ a_win.show(); a_win.present(); }); // save_log let save_menu = builder.get_object::("save_menu").expect("Resource file missing!"); let save_window = builder.get_object::("save_window").expect("Resource file missing!"); save_window.set_transient_for(Some(&win)); save_window.set_action(gtk::FileChooserAction::Save); save_window.connect_delete_event(|win,_| { win.hide(); Inhibit(true) }); save_window.add_button("_Save", gtk::ResponseType::Apply); save_window.add_button("_Cancel", gtk::ResponseType::Cancel); let tmp_log_area = log_area.clone(); let tmp_bar = bar.clone(); save_window.connect_response(move |win, res| { match res { gtk::ResponseType::Cancel => win.hide(), gtk::ResponseType::Apply => { if let Some(path) = win.get_filename() { if let Some(buf) = tmp_log_area.get_buffer() { let text = buf.get_text(&buf.get_start_iter(), &buf.get_end_iter(), false).unwrap().to_string(); match std::fs::write(path, text) { Ok(_) => { win.hide(); }, Err(_) => { tmp_bar.push(1, "Failed to save!"); } } } } }, _ => () } }); save_menu.connect_activate(move |_|{ save_window.show(); }); // pankti let pankti = builder.get_object::("pankti").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); pankti.connect_value_changed(move |btn| { let mut tmp_graph = tmp_graph.borrow_mut(); tmp_graph.scale_x_size = btn.get_value(); tmp_graph.redraw(); }); // stambh_1 let stambh_1 = builder.get_object::("stambh_1").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); stambh_1.connect_activate(move |entry| { let mut tmp_graph = tmp_graph.borrow_mut(); let val = entry.get_text().parse::().unwrap_or(0.0); let purana_y_start = tmp_graph.scale_y_start; let y_size = tmp_graph.scale_y_size; tmp_graph.scale_y_start = val; tmp_graph.scale_y_size = y_size + (purana_y_start - val); tmp_graph.redraw(); }); // stambh_2 let stambh_2 = builder.get_object::("stambh_2").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); stambh_2.connect_activate(move |entry| { let mut tmp_graph = tmp_graph.borrow_mut(); let val = entry.get_text().parse::().unwrap_or(0.0); let y_start = tmp_graph.scale_y_start; tmp_graph.scale_y_size = (val - y_start).abs(); tmp_graph.redraw(); }); // nimna_stambh let nimna_stambh = builder.get_object::("nimna_stambh").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); nimna_stambh.connect_clicked(move |btn| { tmp_graph.borrow_mut().auto_adjust_y = !btn.get_active(); stambh_1.set_sensitive(btn.get_active()); stambh_2.set_sensitive(btn.get_active()); if btn.get_active() { stambh_1.emit_activate(); stambh_2.emit_activate(); } }); // draw_patches let draw_patches = builder.get_object::("draw_patches").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); draw_patches.connect_clicked(move |btn| { let mut tmp_graph = tmp_graph.borrow_mut(); tmp_graph.draw_patch = btn.get_active(); tmp_graph.redraw(); }); // draw_baarik_box let draw_baarik_box = builder.get_object::("draw_baarik_box").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); draw_baarik_box.connect_clicked(move |btn| { let mut tmp_graph = tmp_graph.borrow_mut(); tmp_graph.draw_baarik_box = btn.get_active(); tmp_graph.redraw(); }); // draw_box let draw_box = builder.get_object::("draw_box").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); draw_box.connect_clicked(move |btn| { draw_baarik_box.set_sensitive(btn.get_active()); let mut tmp_graph = tmp_graph.borrow_mut(); tmp_graph.draw_box = btn.get_active(); tmp_graph.redraw(); }); // Bondrate let bondrate = builder.get_object::("bondrate").expect("Resource file missing!"); let tmp_bar = bar.clone(); let tmp_config = Arc::clone(&config); bondrate.connect_changed(move |cbx| { match tmp_config.lock() { Ok(mut config) => { config.bondrate = match cbx.get_active_text() { Some(txt) => txt.to_string().parse::().unwrap_or(9600u32), None => 9600 }; }, Err(_) => { tmp_bar.push(1, "Failed to change bondrate!"); } } }); // port let refresh_port = builder.get_object::("refresh_port").expect("Resource file missing!"); let port = builder.get_object::("port").expect("Resource file missing!"); let tmp_bar = bar.clone(); let tmp_port = port.clone(); refresh_port.connect_clicked(move |_| { tmp_port.remove_all(); match serialport::available_ports() { Ok(ports) => { if ports.len() == 0 { tmp_bar.push(1, "No port found!"); } for p in ports { tmp_port.append_text(p.port_name.as_str()); } }, Err(_) => { tmp_bar.push(1, "No port found!"); } } }); let tmp_bar = bar.clone(); let tmp_config = Arc::clone(&config); port.connect_changed(move |cbx| { match tmp_config.lock() { Ok(mut config) => { config.port = match cbx.get_active_text() { Some(txt) => txt.to_string(), None => "".to_owned() }; }, Err(_) => { tmp_bar.push(1, "Failed to change port!"); } } }); // clear_graph let clear_graph = builder.get_object::("clear_graph").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); clear_graph.connect_clicked(move |_ | { tmp_graph.borrow_mut().pankti_sankya = 0.0; tmp_graph.borrow_mut().lines.clear(); tmp_graph.borrow_mut().redraw(); }); // jagrit_btn let jagrit_btn = builder.get_object::("jagrit_btn").expect("Resource file missing!"); let tmp_bar = bar.clone(); let tmp_config = Arc::clone(&config); let tmp_graph = Rc::clone(&graph); jagrit_btn.connect_clicked(move |_ | { match tmp_config.lock() { Ok(mut config) => { tmp_graph.borrow_mut().pankti_sankya = 0.0; tmp_graph.borrow_mut().lines.clear(); tmp_graph.borrow_mut().redraw(); tmp_bar.push(1, "Jagrit"); config.status = Status::PARIVARTIT; }, Err(_) => { tmp_bar.push(1, "Failed to change port!"); } } }); // avrodith_btn let avrodith_btn = builder.get_object::("avrodith_btn").expect("Resource file missing!"); let tmp_bar = bar.clone(); let tmp_config = Arc::clone(&config); avrodith_btn.connect_clicked(move |_| { match tmp_config.lock() { Ok(mut config) => { tmp_bar.push(1, "Avrodhit"); config.status = Status::AVRODTIH; }, Err(_) => { tmp_bar.push(1, "Failed to change port!"); } } }); // clear_log let clear_log = builder.get_object::("clear_log").expect("Resource file missing!"); let tmp_log_area = log_area.clone(); clear_log.connect_clicked(move |_| { tmp_log_area.get_buffer().expect("Couldn't get window").set_text(""); }); // send_entry let send_entry = builder.get_object::("send_entry").expect("Resource file missing!"); let tmp_bar = bar.clone(); let tmp_config = Arc::clone(&config); send_entry.connect_activate(move |ent| { send_text(&tmp_config, ent, &tmp_bar); }); // send_btn let send_btn = builder.get_object::("send_btn").expect("Resource file missing!"); let tmp_bar = bar.clone(); let tmp_config = Arc::clone(&config); send_btn.connect_clicked(move |_| { send_text(&tmp_config, &send_entry, &tmp_bar); }); // Serial Thread let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let tmp_config = Arc::clone(&config); std::thread::spawn(move || { let mut bufread: Option>> = None; let mut buf = String::new(); loop { serial_thread_work(&tmp_config, &mut bufread, &sender, &mut buf); } }); // Serial Thread Reciver let full_log = builder.get_object::("full_log").expect("Resource file missing!"); let tmp_graph = Rc::clone(&graph); receiver.attach(None, move |msg| { match msg { MessageSerialThread::Msg(text) => { receiver_for_msg(text, &tmp_graph, &full_log, &log_area); }, MessageSerialThread::Status(text) => { bar.push(1, &text); } } glib::Continue(true) }); } // Controls the thread and read from serial port fn serial_thread_work( config: &Arc>, bufread: &mut Option>>, sender: &glib::Sender, buf: &mut String) { let mut do_sleep = false; match config.lock() { Ok(mut config) => { match config.status { Status::AVRODTIH => { *bufread = None; config.status = Status::SAYAN; }, Status::JAGRIT => { if let Some(read) = bufread { if let Ok(_) = read.read_line(buf) { sender.send(MessageSerialThread::Msg(buf.clone())).unwrap(); buf.clear(); } } }, Status::PARIVARTIT => { let p = match serialport::new(&config.port, config.bondrate).open() { Ok(p) => p, Err(_) => { return; } }; *bufread = Some(BufReader::new(p)); config.status = Status::JAGRIT; }, Status::SAYAN => { do_sleep = true; } } }, Err(_) => { sender.send(MessageSerialThread::Status("Faild prepare for communication!".to_owned())).unwrap(); return; } }; // Hack for smooth performance if do_sleep { std::thread::sleep(std::time::Duration::from_millis(100)); } else { std::thread::sleep(std::time::Duration::from_nanos(1)); } } // Receives MessageSerialThread from Serial Port managing thread and add points to draw on graph fn receiver_for_msg(text: String, graph: &Rc>, full_log: >k::CheckButton, log_area: >k::TextView) { for text in text.lines() { if text.len() == 0 { return; } else if text.starts_with("#") { graph.borrow_mut().pankti_sankya += 1.0; for (index, line) in text[1..].split(" ").enumerate() { let part = line.split("="); let part = part.into_iter().collect::>(); if part.len() == 1 { let num = match part[0].trim().parse::() { Ok(val) => val, Err(_) => { continue; } }; let mut gp = graph.borrow_mut(); let sankhya = gp.pankti_sankya; match gp.lines.get_mut(&index.to_string()) { Some(val) => { val.points.push((sankhya, num)); } None => { let v = vec![(sankhya, num)]; let mut rng = rand::thread_rng(); gp.lines.insert(index.to_string(), graph::Line::new(rng.gen_range(0.0..1.0), 0.0, rng.gen_range(0.0..1.0), v)); } } gp.redraw(); } else if part.len() == 2 { let num = match part[1].trim().parse::() { Ok(val) => val, Err(_) => { continue; } }; let mut gp = graph.borrow_mut(); let sankhya = gp.pankti_sankya; match gp.lines.get_mut(part[0]) { Some(val) => { val.points.push((sankhya, num)); } None => { let v = vec![(sankhya, num)]; let mut rng = rand::thread_rng(); gp.lines.insert(part[0].to_owned(), graph::Line::new(rng.gen_range(0.0..1.0), 0.0, rng.gen_range(0.0..1.0), v)); } } gp.redraw(); } } if full_log.get_active(){ let buf = log_area.get_buffer() .expect("Couldn't get log_area"); buf.insert(&mut buf.get_end_iter(), &format!("{}\n",text)); log_area.scroll_to_iter(&mut buf.get_end_iter(), 0.4, true, 0.0, 0.0); log_area.queue_draw(); } } else { let buf = log_area.get_buffer() .expect("Couldn't get log_area"); buf.insert(&mut buf.get_end_iter(), &format!("{}\n",text)); log_area.scroll_to_iter(&mut buf.get_end_iter(), 0.4, true, 0.0, 0.0); log_area.queue_draw(); } } } fn send_text(config: &Arc>, entry: >k::Entry, bar: >k::Statusbar) { match config.lock() { Ok(config) => { if let Status::JAGRIT = config.status { let mut p = match serialport::new(&config.port, config.bondrate).open() { Ok(p) => p, Err(_) => { bar.push(1, "Failed to change port!"); return; } }; unsafe { p.write_all(entry.get_text().to_string().as_bytes_mut()).unwrap(); } entry.set_text(""); } }, Err(_) => { bar.push(1, "Failed to change port!"); } } }