Compare commits

...

13 Commits
0.1.1 ... main

16 changed files with 1012 additions and 875 deletions

517
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,31 @@
[package] [package]
name = "tarangam" name = "tarangam"
version = "0.1.1" version = "0.3.1"
authors = ["PiyushXCoder <piyush.raj.kit@gmail.com>"] authors = ["PiyushXCoder <piyush.raj.kit@gmail.com>"]
license = "GPL 3.0" license = "GPL-3.0-only"
edition = "2018" edition = "2018"
documentation = "A simple serial plotter. एक सरल सीरीय्ल पलौटर।" description = "A simple serial plotter. एक सरल सीरीय्ल पलौटर।"
readme = "README.md" readme = "README.md"
repository = "https://github.com/PiyushXCoder/Tarangam" repository = "https://github.com/PiyushXCoder/Tarangam"
keywords = ["plotter", "serial", "serialplotter", "arduino", "gtk"] keywords = ["plotter", "serial", "serialplotter", "arduino", "gtk"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
gtk = "0.9.2" tokio = { version = "1.5.0", features = [
gio = "0.9.1" "rt",
glib = "0.10.3" "rt-multi-thread",
png = "0.16.8" "macros",
cairo-rs = { version = "0.9.1", features = ["png"] } "time",
"sync",
] }
gtk = "0.15"
gdk = "0.15"
gio = "0.15"
glib = "0.15"
png = "0.17"
cairo-rs = { version = "0.15", features = ["png"] }
rand = "0.8.1" rand = "0.8.1"
libmath = "0.2.1" libmath = "0.2.1"
serialport = "4.0.0" serialport = "4.0.1"

View File

@ -1,6 +1,5 @@
# Tarangam # Tarangam
It is a straightforward application to see the logs from [serial ports](https://wiki.osdev.org/Serial_Ports) and plot information on graph. You can use it with IOT boards ([Arduino](https://www.arduino.cc/), [ESP boards](https://www.espressif.com/),...) in your DIY projects. It gives many basic controls to modify the graph.
It is a simple application to see log from [serialports](https://wiki.osdev.org/Serial_Ports) and plot information on graph. You can use it with IOT boards([ardino](https://www.arduino.cc/), [esp boards](https://www.espressif.com/),...) in you DIY projects. It gives to many basic controls to control graph.
## Interface ## Interface
![1](screenshots/1.png) ![1](screenshots/1.png)

BIN
chitra-small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -1,20 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" width="256"
xmlns:cc="http://creativecommons.org/ns#" height="256"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000"
height="1000"
viewBox="0 0 1000 1000" viewBox="0 0 1000 1000"
version="1.1" version="1.1"
id="svg8" id="svg8"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)" inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="icon.svg"> sodipodi:docname="chitra.svg"
inkscape:export-filename="/home/piyush/Projects/tarangam/chitra-small.png"
inkscape:export-xdpi="24"
inkscape:export-ydpi="24"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs <defs
id="defs2"> id="defs2">
<linearGradient <linearGradient
@ -47,19 +50,20 @@
inkscape:pageopacity="0.0" inkscape:pageopacity="0.0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="0.35" inkscape:zoom="0.35"
inkscape:cx="605.76766" inkscape:cx="607.14286"
inkscape:cy="248.85918" inkscape:cy="250"
inkscape:document-units="px" inkscape:document-units="px"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
inkscape:document-rotation="0" inkscape:document-rotation="0"
showgrid="false" showgrid="false"
inkscape:window-width="1214" inkscape:window-width="1920"
inkscape:window-height="757" inkscape:window-height="1002"
inkscape:window-x="295" inkscape:window-x="0"
inkscape:window-y="198" inkscape:window-y="27"
inkscape:window-maximized="0" inkscape:window-maximized="1"
units="px" units="px"
showguides="false"> showguides="false"
inkscape:pagecheckerboard="1">
<inkscape:grid <inkscape:grid
type="xygrid" type="xygrid"
id="grid869" /> id="grid869" />

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -20,40 +20,42 @@
use gtk::prelude::*; use gtk::prelude::*;
use gtk::DrawingArea; use gtk::DrawingArea;
use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
/// A single line /// A single line
pub struct Line { #[derive(Debug)]
pub points: Vec<(f64,f64)>, pub(crate) struct Line {
pub color: (f64, f64, f64) pub(crate) points: Vec<(f64, f64)>,
pub(crate) color: (f64, f64, f64),
} }
impl Line { impl Line {
pub fn new(r: f64, g: f64, b: f64, points: Vec<(f64,f64)>) -> Self { pub(crate) fn new(r: f64, g: f64, b: f64, points: Vec<(f64, f64)>) -> Self {
Line { Line {
points, points,
color: (r,g,b) color: (r, g, b),
} }
} }
} }
/// Tools to draw Graph /// Tools to draw Graph
pub struct Graph { pub(crate) struct Graph {
pub area: DrawingArea, pub(crate) area: DrawingArea,
pub scale_x_start: f64, // start of x on pankti pub(crate) scale_x_start: f64, // start of x on pankti
pub scale_x_size: f64, // size of pankti to show pub(crate) scale_x_size: f64, // size of pankti to show
pub scale_y_start: f64, // start of y on stambh pub(crate) scale_y_start: f64, // start of y on stambh
pub scale_y_size: f64, // size of stambh to show pub(crate) scale_y_size: f64, // size of stambh to show
pub draw_patch: bool, // enable to draw circle spot on line pub(crate) draw_patch: bool, // enable to draw circle spot on line
pub draw_box: bool, // enable to show boxes linke graph paper pub(crate) draw_box: bool, // enable to show boxes linke graph paper
pub draw_baarik_box: bool, // enable to show baarik(similar meaning to smaller) linke graph paper pub(crate) draw_baarik_box: bool, // enable to show baarik(similar meaning to smaller) linke graph paper
pub auto_adjust_y: bool, // enable to automatically adjust y axis pub(crate) auto_adjust_y: bool, // enable to automatically adjust y axis
pub lines: HashMap<String, Line>, pub(crate) lines: HashMap<String, Line>,
pub pankti_sankya: f64 // use used while adding to point in lines to see last count of graphable input pub(crate) pankti_sankya: f64, // use used while adding to point in lines to see last count of graphable input
} }
impl Graph { impl Graph {
pub fn new(area: DrawingArea, pub(crate) fn new(
area: DrawingArea,
scale_x_start: f64, scale_x_start: f64,
scale_x_size: f64, scale_x_size: f64,
scale_y_start: f64, scale_y_start: f64,
@ -63,8 +65,8 @@ impl Graph {
draw_baarik_box: bool, draw_baarik_box: bool,
auto_adjust_y: bool, auto_adjust_y: bool,
lines: HashMap<String, Line>, lines: HashMap<String, Line>,
pankti_sankya: f64) -> Rc<RefCell<Self>> { pankti_sankya: f64,
) -> Rc<RefCell<Self>> {
let graph = Rc::new(RefCell::new(Graph { let graph = Rc::new(RefCell::new(Graph {
area, area,
scale_x_start, scale_x_start,
@ -76,7 +78,7 @@ impl Graph {
draw_baarik_box, draw_baarik_box,
auto_adjust_y, auto_adjust_y,
lines, lines,
pankti_sankya pankti_sankya,
})); }));
let graph_tmp = Rc::clone(&graph); let graph_tmp = Rc::clone(&graph);
@ -89,7 +91,14 @@ impl Graph {
} }
/// used to draw box and baarik box /// used to draw box and baarik box
fn draw_boxes(ctx: &cairo::Context, area_width: f64, area_height: f64, src_x: f64, cell_size: f64, color: f64) { fn draw_boxes(
ctx: &cairo::Context,
area_width: f64,
area_height: f64,
src_x: f64,
cell_size: f64,
color: f64,
) {
ctx.set_source_rgb(color, color, color); ctx.set_source_rgb(color, color, color);
ctx.set_line_width(1.0); ctx.set_line_width(1.0);
// lines parallel to stambh // lines parallel to stambh
@ -104,7 +113,7 @@ impl Graph {
ctx.move_to(src_x, yi); ctx.move_to(src_x, yi);
ctx.line_to(area_width + src_x, yi); ctx.line_to(area_width + src_x, yi);
} }
ctx.stroke(); ctx.stroke().unwrap();
} }
/// transform point to show on graph /// transform point to show on graph
@ -118,27 +127,25 @@ impl Graph {
scale_x_size: f64, scale_x_size: f64,
scale_y_size: f64, scale_y_size: f64,
height: f64, height: f64,
stambh_scale_width: f64) -> (f64, f64){ stambh_scale_width: f64,
) -> (f64, f64) {
( (
((p - p_start) * aa_dumm_pankti) / scale_x_size + stambh_scale_width, ((p - p_start) * aa_dumm_pankti) / scale_x_size + stambh_scale_width,
height - ((s - s_start) * aa_dumm_stambh) / scale_y_size height - ((s - s_start) * aa_dumm_stambh) / scale_y_size,
) )
} }
/// callback of drawing area to draw graph /// callback of drawing area to draw graph
fn draw(area: &gtk::DrawingArea, fn draw(area: &gtk::DrawingArea, ctx: &cairo::Context, graph: &Rc<RefCell<Graph>>) {
ctx: &cairo::Context,
graph: &Rc<RefCell<Graph>>) {
let graph = graph.borrow(); let graph = graph.borrow();
ctx.set_source_rgb(0.1, 0.5, 0.5); ctx.set_source_rgb(0.1, 0.5, 0.5);
ctx.paint(); ctx.paint().unwrap();
let pankti_scale_height = 50.0; let pankti_scale_height = 50.0;
let stambh_scale_width = 60.0; let stambh_scale_width = 60.0;
let width = area.get_allocated_width() as f64 - stambh_scale_width; let width = area.allocated_width() as f64 - stambh_scale_width;
let height = area.get_allocated_height() as f64 - pankti_scale_height; let height = area.allocated_height() as f64 - pankti_scale_height;
let manjusa_maap = 50.0; let manjusa_maap = 50.0;
@ -201,14 +208,19 @@ impl Graph {
ctx.set_source_rgb(line.color.0, line.color.1, line.color.2); ctx.set_source_rgb(line.color.0, line.color.1, line.color.2);
ctx.move_to(bindu_dumm_t.0, bindu_dumm_t.1); ctx.move_to(bindu_dumm_t.0, bindu_dumm_t.1);
ctx.line_to(bindu_t.0, bindu_t.1); ctx.line_to(bindu_t.0, bindu_t.1);
ctx.stroke(); ctx.stroke().unwrap();
// draw circle over point // draw circle over point
if draw_patch { if draw_patch {
ctx.set_source_rgb(0.0, 0.0, 1.0); ctx.set_source_rgb(0.0, 0.0, 1.0);
ctx.arc(bindu_dumm_t.0, bindu_dumm_t.1, ctx.arc(
5.0, 0.0, std::f64::consts::PI * 2.0); bindu_dumm_t.0,
ctx.stroke(); bindu_dumm_t.1,
5.0,
0.0,
std::f64::consts::PI * 2.0,
);
ctx.stroke().unwrap();
} }
} }
} }
@ -217,33 +229,41 @@ impl Graph {
ctx.set_source_rgb(0.1, 0.4, 0.4); ctx.set_source_rgb(0.1, 0.4, 0.4);
ctx.rectangle(0.0, 0.0, stambh_scale_width, height + pankti_scale_height); ctx.rectangle(0.0, 0.0, stambh_scale_width, height + pankti_scale_height);
ctx.rectangle(stambh_scale_width, height, width, pankti_scale_height); ctx.rectangle(stambh_scale_width, height, width, pankti_scale_height);
ctx.fill(); ctx.fill().unwrap();
ctx.set_source_rgb(1.0, 1.0, 1.0); ctx.set_source_rgb(1.0, 1.0, 1.0);
// write numbers on pankti scale // write numbers on pankti scale
for i in 0..(rekha_sankhya_pankti as i32 + 1) { for i in 0..(rekha_sankhya_pankti as i32 + 1) {
let text = math::round::floor(i as f64 * anupat_pankti + graph.scale_x_start, 4).to_string(); let text =
let f = ctx.text_extents(&text); math::round::floor(i as f64 * anupat_pankti + graph.scale_x_start, 4).to_string();
ctx.move_to(i as f64 * manjusa_maap - f.width + stambh_scale_width + f.height / 0.866, height + f.width * 0.5 + f.height); let f = ctx.text_extents(&text).expect("Text dimension");
ctx.save(); ctx.move_to(
i as f64 * manjusa_maap - f.width + stambh_scale_width + f.height / 0.866,
height + f.width * 0.5 + f.height,
);
ctx.save().unwrap();
ctx.rotate(std::f64::consts::PI / -6.0); ctx.rotate(std::f64::consts::PI / -6.0);
ctx.show_text(&text); ctx.show_text(&text).unwrap();
ctx.restore(); ctx.restore().unwrap();
} }
// write numbers on stambh scale // write numbers on stambh scale
for i in (0..aa_dumm_stambh as i32 + 1).rev() { for i in (0..aa_dumm_stambh as i32 + 1).rev() {
let text = math::round::floor(i as f64 * anupat_stambh + graph.scale_y_start, 4).to_string(); let text =
let f = ctx.text_extents(&text); math::round::floor(i as f64 * anupat_stambh + graph.scale_y_start, 4).to_string();
ctx.move_to(stambh_scale_width - f.width, height - i as f64 * manjusa_maap + f.width * 0.5); let f = ctx.text_extents(&text).expect("Text dimension");
ctx.save(); ctx.move_to(
stambh_scale_width - f.width,
height - i as f64 * manjusa_maap + f.width * 0.5,
);
ctx.save().unwrap();
ctx.rotate(std::f64::consts::PI / -6.0); ctx.rotate(std::f64::consts::PI / -6.0);
ctx.show_text(&text); ctx.show_text(&text).unwrap();
ctx.restore(); ctx.restore().unwrap();
} }
} }
/// Adjust stambh and pankti as needed , trim lines and redraws /// Adjust stambh and pankti as needed , trim lines and redraws
pub fn redraw(&mut self) { pub(crate) fn redraw(&mut self) {
let (mx_x, mi_x, mx_y, mi_y) = self.get_extremes(); let (mx_x, mi_x, mx_y, mi_y) = self.get_extremes();
// stambh // stambh
@ -266,7 +286,7 @@ impl Graph {
} }
/// find minimun and maximum value from all lines /// find minimun and maximum value from all lines
pub fn get_extremes(&self) -> (f64, f64, f64, f64){ pub(crate) fn get_extremes(&self) -> (f64, f64, f64, f64) {
// trick to avoid no lines // trick to avoid no lines
if self.lines.len() == 0 { if self.lines.len() == 0 {
return (0.0, 0.0, 0.0, 0.0); return (0.0, 0.0, 0.0, 0.0);
@ -319,17 +339,16 @@ impl Graph {
// tims line form left side of line. Why left?? to avoid wasting time in ponits in range // tims line form left side of line. Why left?? to avoid wasting time in ponits in range
// it skips i point out of screen to keep a non terminating line experience // it skips i point out of screen to keep a non terminating line experience
pub fn trim_lines(&mut self) { pub(crate) fn trim_lines(&mut self) {
for (_, line) in self.lines.iter_mut() { for (_, line) in self.lines.iter_mut() {
let mut i = 0; let mut i = 0;
while i < line.points.len() { while i < line.points.len() {
match line.points.get(i + 2) { match line.points.get(i + 2) {
Some(_) => { Some(_) => {
if line.points[i + 1].0 < self.scale_x_start { if line.points[i + 1].0 < self.scale_x_start {
line.points.remove(i); line.points.remove(i);
} }
}, }
None => { None => {
if line.points[line.points.len() - 1].0 < self.scale_x_start { if line.points[line.points.len() - 1].0 < self.scale_x_start {
line.points.clear(); line.points.clear();

View File

@ -17,356 +17,301 @@
//! Feel free to see through codes. Application is not written to be used as a library for other app. :) //! Feel free to see through codes. Application is not written to be used as a library for other app. :)
mod graph; pub(crate) mod graph;
pub(crate) mod port_util;
pub(crate) mod util;
use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use rand::Rng; use rand::Rng;
use std::{collections::HashMap, sync::{Arc, Mutex}};
use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::io::prelude::*; use std::collections::HashMap;
use std::io::BufReader; use std::io::BufReader;
use std::rc::Rc;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use graph::Graph; use graph::Graph;
use port_util as putil;
use util::Properties;
/// Status of Serial reading // Building and propsuring GUI
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, MessageSerialThreadMsgType),
Points(Vec<(String, f64)>),
Status(String)
}
enum MessageSerialThreadMsgType {
Point,
Log
}
// Building and configuring GUI
pub fn build_ui(app: &gtk::Application) { pub fn build_ui(app: &gtk::Application) {
let config = Arc::new(Mutex::new(Config::new())); let props = Arc::new(Properties::default());
let builder = gtk::Builder::from_file(std::env::current_exe().unwrap().parent().unwrap().join("ui.glade")); let ui_file = include_str!("ui.glade");
let builder = gtk::Builder::from_string(ui_file);
let win = builder.get_object::<gtk::ApplicationWindow>("win").expect("Resource file missing!"); let win = builder
.object::<gtk::ApplicationWindow>("win")
.expect("Resource file missing!");
win.set_application(Some(app)); win.set_application(Some(app));
let bar = builder.get_object::<gtk::Statusbar>("status_bar").expect("Resource file missing!");
let log_area = builder.get_object::<gtk::TextView>("log_area").expect("Resource file missing!"); // Status Bar
let bar = builder
.object::<gtk::Statusbar>("status_bar")
.expect("Resource file missing!");
// Logging Area
let log_area = builder
.object::<gtk::TextView>("log_area")
.expect("Resource file missing!");
// About Window
let about_window = builder
.object::<gtk::AboutDialog>("about_window")
.expect("Resource file missing!");
// Save Window
let save_window = builder
.object::<gtk::FileChooserDialog>("save_window")
.expect("Resource file missing!");
save_window.set_transient_for(Some(&win));
save_window.set_action(gtk::FileChooserAction::Save);
save_window.add_button("_Save", gtk::ResponseType::Apply);
save_window.add_button("_Cancel", gtk::ResponseType::Cancel);
let graph = Graph::new( let graph = Graph::new(
builder.get_object::<gtk::DrawingArea>("draw_area").expect("Resource file missing!"), builder
0.0, 100.0, .object::<gtk::DrawingArea>("draw_area")
0.0, 100.0, .expect("Resource file missing!"),
0.0,
100.0,
0.0,
100.0,
false, false,
true, true,
false, false,
true, true,
HashMap::new(), HashMap::new(),
0.0 0.0,
); );
win.show_all(); win.show_all();
// exit_menu // required by signals
let exit_menu = builder.get_object::<gtk::MenuItem>("exit_menu").expect("Resource file missing!"); let stambh_1 = builder
.object::<gtk::Entry>("stambh_1")
let tmp_win = win.clone(); .expect("Resource file missing!");
exit_menu.connect_activate(move |_|{ let stambh_2 = builder
unsafe { .object::<gtk::Entry>("stambh_2")
tmp_win.destroy(); .expect("Resource file missing!");
let draw_baarik_box = builder
.object::<gtk::CheckButton>("draw_baarik_box")
.expect("Resource file missing!");
let port = builder
.object::<gtk::ComboBoxText>("port")
.expect("Resource file missing!");
let send_entry = builder
.object::<gtk::Entry>("send_entry")
.expect("Resource file missing!");
// Signals
builder.connect_signals(|_, handler_name| {
match handler_name {
"exit_menu_activate" => Box::new(clone!(@weak win => @default-return None, move |_| {
unsafe { win.destroy(); }
None
})),
"about_menu_activate" => Box::new(clone!(@weak about_window => @default-return None, move |_| {
about_window.show();
about_window.present();
None
})),
"save_menu_activate" => Box::new(clone!(@weak save_window => @default-return None, move |_| {
save_window.show();
save_window.present();
None
})),
"gtk_main_quit" => Box::new(clone!(@weak save_window => @default-return None, move |_| {
save_window.show();
save_window.present();
None
})),
"pankti_value_changed" => Box::new(clone!(@weak graph => @default-return None, move |a| {
let btn = a[0].get::<gtk::SpinButton>().unwrap();
let mut tmp_graph = graph.borrow_mut();
tmp_graph.scale_x_size = btn.value();
tmp_graph.redraw();
None
})),
"stambh_1_changed" => Box::new(clone!(@weak graph => @default-return None, move |a| {
let entry = a[0].get::<gtk::Entry>().unwrap();
let mut tmp_graph = graph.borrow_mut();
let val = entry.text().parse::<f64>().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();
None
})),
"stambh_2_changed" => Box::new(clone!(@weak graph => @default-return None, move |a| {
let entry = a[0].get::<gtk::Entry>().unwrap();
let mut tmp_graph = graph.borrow_mut();
let val = entry.text().parse::<f64>().unwrap_or(0.0);
let y_start = tmp_graph.scale_y_start;
tmp_graph.scale_y_size = (val - y_start).abs();
tmp_graph.redraw();
None
})),
"nimna_stambh_toggled" => Box::new(clone!(@weak graph, @weak stambh_1, @weak stambh_2 => @default-return None, move |a| {
let btn = a[0].get::<gtk::CheckButton>().unwrap();
graph.borrow_mut().auto_adjust_y = !btn.is_active();
stambh_1.set_sensitive(btn.is_active());
stambh_2.set_sensitive(btn.is_active());
if btn.is_active() {
stambh_1.emit_activate();
stambh_2.emit_activate();
}
None
})),
"draw_patches_toggled" => Box::new(clone!(@weak graph => @default-return None, move |a| {
let btn = a[0].get::<gtk::CheckButton>().unwrap();
let mut tmp_graph = graph.borrow_mut();
tmp_graph.draw_patch = btn.is_active();
tmp_graph.redraw();
None
})),
"draw_box_toggled" => Box::new(clone!(@weak graph, @weak draw_baarik_box => @default-return None, move |a| {
let btn = a[0].get::<gtk::CheckButton>().unwrap();
let mut tmp_graph = graph.borrow_mut();
draw_baarik_box.set_sensitive(btn.is_active());
tmp_graph.draw_box = btn.is_active();
tmp_graph.redraw();
None
})),
"draw_baarik_box_toggled" => Box::new(clone!(@weak graph => @default-return None, move |a| {
let btn = a[0].get::<gtk::CheckButton>().unwrap();
let mut tmp_graph = graph.borrow_mut();
tmp_graph.draw_baarik_box = btn.is_active();
tmp_graph.redraw();
None
})),
"clear_graph_clicked"=> Box::new(clone!(@weak graph => @default-return None, move |_| {
let mut tmp_graph = graph.borrow_mut();
tmp_graph.pankti_sankya = 0.0;
tmp_graph.lines.clear();
tmp_graph.redraw();
None
})),
"bondrate_changed" => Box::new(clone!(@weak props => @default-return None, move |a| {
let btn = a[0].get::<gtk::ComboBoxText>().unwrap();
props.bondrate.store(btn.active_text().unwrap().parse::<u32>().unwrap_or(9600u32), Ordering::SeqCst);
None
})),
"port_changed" => Box::new(clone!(@weak props, @weak bar => @default-return None, move |a| {
let btn = a[0].get::<gtk::ComboBoxText>().unwrap();
if let Some(val) = btn.active_text() {
match props.port.lock() {
Ok(mut a) => { *a = val.to_string() },
Err(_) => { bar.push(1, "Can't set Port"); }
}
}
None
})),
"refresh_port_clicked" => Box::new(clone!(@weak port, @weak bar, @weak props => @default-return None, move |_| {
port.remove_all();
match props.status.lock() {
Ok(mut a) => { *a = util::Status::AVRODTIH },
Err(_) => { bar.push(1, "Can't Avrodhit"); return None; }
}
bar.push(1, "Avrodhit");
match serialport::available_ports() {
Ok(ports) => {
if ports.len() == 0 { bar.push(1, "No port found!"); }
for p in ports {
port.append_text(p.port_name.as_str());
}
}, Err(_) => {
bar.push(1, "No port found!");
}
}
None
})),
"jagrit_btn_clicked" => Box::new(clone!(@weak props, @weak graph, @weak bar => @default-return None, move |_| {
let mut tmp_graph = graph.borrow_mut();
tmp_graph.pankti_sankya = 0.0;
tmp_graph.lines.clear();
tmp_graph.redraw();
bar.push(1, "Jagrit");
match props.status.lock() {
Ok(mut a) => { *a = util::Status::PARIVARTIT },
Err(_) => { bar.push(1, "Can't Jagrit"); }
}
None
})),
"avrodith_btn_clicked" => Box::new(clone!(@weak props, @weak bar => @default-return None, move |_| {
bar.push(1, "Avrodhit");
match props.status.lock() {
Ok(mut a) => { *a = util::Status::AVRODTIH },
Err(_) => { bar.push(1, "Can't Avrodhit"); }
}
None
})),
"clear_log_clicked" => Box::new(clone!(@weak log_area => @default-return None, move |_| {
log_area.buffer().expect("Couldn't get window").set_text("");
None
})),
"send_entry_key_press_event" => Box::new(clone!(@weak props, @weak bar => @default-return None, move |a| {
let ev = a[1].get::<gdk::Event>().unwrap();
let ev: Result<gdk::EventKey,_> = gdk::FromEvent::from(ev);
if ev.unwrap().keyval() == gdk::keys::constants::Return {
let ent = a[0].get::<gtk::Entry>().unwrap();
putil::send_text(&props, &ent, &bar);
}
Some(false.to_value())
})),
"send_btn_clicked" => Box::new(clone!(@weak props, @weak bar, @weak send_entry => @default-return None, move |_| {
putil::send_text(&props, &send_entry, &bar);
None
})),
"about_window_delete" => Box::new(|a| {
let win = a[0].get::<gtk::AboutDialog>().unwrap();
win.hide();
Some(true.to_value())
}),
"save_window_delete" => Box::new(|a| {
let win = a[0].get::<gtk::FileChooserDialog>().unwrap();
win.hide();
Some(true.to_value())
}),
"about_window_close" => Box::new(clone!(@weak about_window => @default-return None, move |_| {
about_window.hide();
None
})),
"save_window_close" => Box::new(clone!(@weak save_window => @default-return None, move |_| {
save_window.hide();
None
})),
_ => Box::new(|_| {None})
} }
}); });
// about_menu
let about_menu = builder.get_object::<gtk::MenuItem>("about_menu").expect("Resource file missing!");
let about_window = builder.get_object::<gtk::AboutDialog>("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::<gtk::MenuItem>("save_menu").expect("Resource file missing!");
let save_window = builder.get_object::<gtk::FileChooserDialog>("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_log_area = log_area.clone();
let tmp_bar = bar.clone(); let tmp_bar = bar.clone();
save_window.connect_response(move |win, res| { save_window.connect_response(move |win, res| match res {
match res {
gtk::ResponseType::Cancel => win.hide(), gtk::ResponseType::Cancel => win.hide(),
gtk::ResponseType::Apply => { gtk::ResponseType::Apply => {
if let Some(path) = win.get_filename() { if let Some(path) = win.filename() {
if let Some(buf) = tmp_log_area.get_buffer() { if let Some(buf) = tmp_log_area.buffer() {
let text = buf.get_text(&buf.get_start_iter(), &buf.get_end_iter(), false).unwrap().to_string(); let text = buf
.text(&buf.start_iter(), &buf.end_iter(), false)
.unwrap()
.to_string();
match std::fs::write(path, text) { match std::fs::write(path, text) {
Ok(_) => { Ok(_) => {
win.hide(); win.hide();
}, }
Err(_) => { Err(_) => {
tmp_bar.push(1, "Failed to save!"); tmp_bar.push(1, "Failed to save!");
} }
} }
} }
} }
},
_ => ()
} }
}); _ => (),
save_menu.connect_activate(move |_|{
save_window.show();
});
// pankti
let pankti = builder.get_object::<gtk::SpinButton>("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::<gtk::Entry>("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::<f64>().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::<gtk::Entry>("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::<f64>().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::<gtk::CheckButton>("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::<gtk::CheckButton>("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::<gtk::CheckButton>("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::<gtk::CheckButton>("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::<gtk::ComboBoxText>("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::<u32>().unwrap_or(9600u32),
None => 9600
};
}, Err(_) => {
tmp_bar.push(1, "Failed to change bondrate!");
}
}
});
// port
let refresh_port = builder.get_object::<gtk::ToolButton>("refresh_port").expect("Resource file missing!");
let port = builder.get_object::<gtk::ComboBoxText>("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::<gtk::ToolButton>("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::<gtk::ToolButton>("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::<gtk::ToolButton>("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::<gtk::ToolButton>("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::<gtk::Entry>("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::<gtk::Button>("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);
}); });
/* /*
@ -378,27 +323,32 @@ pub fn build_ui(app: &gtk::Application) {
*/ */
let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let tmp_config = Arc::clone(&config); let tmp_props = Arc::clone(&props);
std::thread::spawn(move || { tokio::task::spawn(async move {
let mut bufread: Option<BufReader<Box<dyn serialport::SerialPort>>> = None; let mut bufread: Option<BufReader<Box<dyn serialport::SerialPort>>> = None;
let mut buf = String::new(); let mut buf = String::new();
loop { loop {
serial_thread_work(&tmp_config, &mut bufread, &sender, &mut buf); putil::serial_thread_work(&tmp_props, &mut bufread, &sender, &mut buf).await;
} }
}); });
// Reciver for MessageSerialThread from the "Thread to manage Serial Port" and works accordingly // Reciver for MessageSerialThread from the "Thread to manage Serial Port" and works accordingly
let full_log = builder.get_object::<gtk::CheckButton>("full_log").expect("Resource file missing!"); let full_log = builder
.object::<gtk::CheckButton>("full_log")
.expect("Resource file missing!");
let graph_data = builder
.object::<gtk::TextView>("graph_data")
.expect("Resource file missing!");
let tmp_graph = Rc::clone(&graph); let tmp_graph = Rc::clone(&graph);
receiver.attach(None, move |msg| { receiver.attach(None, move |msg| {
match msg { match msg {
MessageSerialThread::Msg(text, msg_type) => { util::MessageSerialThread::Msg(text, msg_type) => {
receiver_for_msg(text, &msg_type, &full_log, &log_area); receiver_for_msg(text, &msg_type, &full_log, &log_area);
},
MessageSerialThread::Points(points) => {
receiver_for_points(points, &tmp_graph);
} }
MessageSerialThread::Status(text) => { util::MessageSerialThread::Points(points) => {
receiver_for_points(points, &tmp_graph, &graph_data);
}
util::MessageSerialThread::Status(text) => {
bar.push(1, &text); bar.push(1, &text);
} }
} }
@ -406,100 +356,33 @@ pub fn build_ui(app: &gtk::Application) {
}); });
} }
// Controls the thread and read from serial port
fn serial_thread_work(
config: &Arc<Mutex<Config>>,
bufread: &mut Option<BufReader<Box<dyn serialport::SerialPort>>>,
sender: &glib::Sender<MessageSerialThread>,
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) {
for line in buf.lines() {
if line.len() == 0 {
continue;
} else if line.starts_with("#") {
let mut points: Vec<(String, f64)> = Vec::new();
for (index, line) in line[1..].split(" ").enumerate() {
let part = line.split("=");
let part = part.into_iter().collect::<Vec<&str>>();
if part.len() == 1 {
let num = match part[0].trim().parse::<f64>() {
Ok(val) => val,
Err(_) => {
continue;
}
};
points.push((index.to_string(), num));
} else if part.len() == 2 {
points.push((part[0].trim().to_owned(), part[1].parse::<f64>().unwrap()));
}
}
sender.send(MessageSerialThread::Points(points)).unwrap();
sender.send(MessageSerialThread::Msg(line.to_owned(), MessageSerialThreadMsgType::Point)).unwrap();
} else {
sender.send(MessageSerialThread::Msg(line.to_owned(), MessageSerialThreadMsgType::Log)).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 adds message to text area // Receives MessageSerialThread from Serial Port managing thread adds message to text area
fn receiver_for_msg(text: String, msg_type: &MessageSerialThreadMsgType, full_log: &gtk::CheckButton, log_area: &gtk::TextView) { fn receiver_for_msg(
if !full_log.get_active(){ text: String,
if let MessageSerialThreadMsgType::Point = msg_type { msg_type: &util::MessageSerialThreadMsgType,
full_log: &gtk::CheckButton,
log_area: &gtk::TextView,
) {
if !full_log.is_active() {
if let util::MessageSerialThreadMsgType::Point = msg_type {
return; return;
} }
} }
let buf = log_area.get_buffer() if text.len() <= 0 {
.expect("Couldn't get log_area"); return;
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); let buf = log_area.buffer().expect("Couldn't get log_area");
buf.insert(&mut buf.end_iter(), &format!("{}\n", text));
log_area.scroll_to_iter(&mut buf.end_iter(), 0.4, true, 0.0, 0.0);
log_area.queue_draw(); log_area.queue_draw();
} }
// Receives MessageSerialThread from Serial Port managing thread and add points to draw on graph // Receives MessageSerialThread from Serial Port managing thread and add points to draw on graph
fn receiver_for_points(points: Vec<(String, f64)>, graph: &Rc<RefCell<Graph>>) { fn receiver_for_points(
points: Vec<(String, f64)>,
graph: &Rc<RefCell<Graph>>,
graph_data: &gtk::TextView,
) {
for (line, point) in points { for (line, point) in points {
let mut gp = graph.borrow_mut(); let mut gp = graph.borrow_mut();
@ -507,37 +390,35 @@ fn receiver_for_points(points: Vec<(String, f64)>, graph: &Rc<RefCell<Graph>>) {
match gp.lines.get_mut(&line) { match gp.lines.get_mut(&line) {
Some(val) => { Some(val) => {
val.points.push((sankhya, point)); val.points.push((sankhya, point));
} None => { }
None => {
let v = vec![(sankhya, point)]; let v = vec![(sankhya, point)];
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
gp.lines.insert(line, graph::Line::new(rng.gen_range(0.0..1.0), 0.0, rng.gen_range(0.0..1.0), v)); gp.lines.insert(
line,
graph::Line::new(rng.gen_range(0.0..1.0), 0.0, rng.gen_range(0.0..1.0), v),
);
let buf = graph_data.buffer().expect("Couldn't get graph_data");
buf.set_text("");
gp.lines.iter().for_each(|(key, line)| {
buf.insert(&mut buf.end_iter(), "##");
let tag = gtk::TextTag::new(None);
let rgba = gdk::RGBA::new(line.color.0, line.color.1, line.color.2, 1.0);
tag.set_background_rgba(Some(&rgba));
tag.set_foreground_rgba(Some(&rgba));
buf.tag_table().unwrap().add(&tag);
buf.apply_tag(
&tag,
&buf.iter_at_offset(buf.end_iter().offset() - 2),
&buf.end_iter(),
);
buf.insert(&mut buf.end_iter(), &format!(" {}, ", key));
});
graph_data.queue_draw();
} }
} }
gp.redraw(); gp.redraw();
} }
graph.borrow_mut().pankti_sankya += 1.0; graph.borrow_mut().pankti_sankya += 1.0;
} }
// Sends text through Serial Post to device
fn send_text(config: &Arc<Mutex<Config>>, entry: &gtk::Entry, bar: &gtk::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!");
}
}
}

View File

@ -12,18 +12,16 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#![windows_subsystem = "windows"]
use gio::prelude::*; use gio::{prelude::*, ApplicationFlags};
use std::env::args;
fn main() { #[tokio::main]
let app = gtk::Application::new(Some("sng.tarangm"), Default::default()) async fn main() {
.expect("Failed to initiate gtk"); let app = gtk::Application::new(Some("sng.tarangm"), ApplicationFlags::default());
app.connect_activate(move |app| { app.connect_activate(move |app| {
tarangam::build_ui(app); tarangam::build_ui(app);
}); });
app.run();
app.run(&args().collect::<Vec<_>>());
} }

163
src/port_util.rs Normal file
View File

@ -0,0 +1,163 @@
/*
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 <https://www.gnu.org/licenses/>
*/
//! Feel free to see through codes. Application is not written to be used as a library for other app. :)
use gtk::prelude::*;
use std::io::prelude::*;
use std::io::BufReader;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use crate::{util, util::Properties};
// Controls the thread and read from serial port
pub(crate) async fn serial_thread_work(
config: &Arc<Properties>,
bufread: &mut Option<BufReader<Box<dyn serialport::SerialPort>>>,
sender: &glib::Sender<util::MessageSerialThread>,
buf: &mut String,
) {
let status = match config.status.try_lock() {
Ok(a) => a.to_owned(),
Err(_) => {
return;
}
};
match status {
util::Status::AVRODTIH => {
*bufread = None;
match config.status.lock() {
Ok(mut a) => *a = util::Status::SAYAN,
Err(_) => {
return;
}
};
}
util::Status::JAGRIT => {
if let Some(read) = bufread {
if let Ok(_) = read.read_line(buf) {
for line in buf.lines() {
if line.len() == 0 {
continue;
} else if line.starts_with("#") {
let mut points: Vec<(String, f64)> = Vec::new();
for (index, line) in line[1..].split(" ").enumerate() {
let part = line.split("=");
let part = part.into_iter().collect::<Vec<&str>>();
if part.len() == 1 {
let num = match part[0].trim().parse::<f64>() {
Ok(val) => val,
Err(_) => {
continue;
}
};
points.push((index.to_string(), num));
} else if part.len() == 2 {
points.push((
part[0].trim().to_owned(),
part[1].parse::<f64>().unwrap(),
));
}
}
sender
.send(util::MessageSerialThread::Points(points))
.unwrap();
sender
.send(util::MessageSerialThread::Msg(
line.to_owned(),
util::MessageSerialThreadMsgType::Point,
))
.unwrap();
} else {
sender
.send(util::MessageSerialThread::Msg(
line.to_owned(),
util::MessageSerialThreadMsgType::Log,
))
.unwrap();
}
}
buf.clear();
}
}
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
util::Status::PARIVARTIT => {
let port = match config.port.lock() {
Ok(a) => a.to_owned(),
Err(_) => {
return;
}
};
let p = match serialport::new(&port, config.bondrate.load(Ordering::SeqCst)).open() {
Ok(p) => p,
Err(_) => {
return;
}
};
*bufread = Some(BufReader::new(p));
match config.status.try_lock() {
Ok(mut a) => *a = util::Status::JAGRIT,
Err(_) => {
return;
}
};
}
_ => {
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
}
}
}
// // Sends text through Serial Post to device
pub(crate) fn send_text(config: &Arc<Properties>, entry: &gtk::Entry, bar: &gtk::Statusbar) {
let status = match config.status.try_lock() {
Ok(a) => a.to_owned(),
Err(_) => {
return;
}
};
if let util::Status::JAGRIT = status {
let port = match config.port.lock() {
Ok(a) => a.to_owned(),
Err(_) => {
bar.push(1, "Failed to set port!");
return;
}
};
let mut p = match serialport::new(port, config.bondrate.load(Ordering::SeqCst)).open() {
Ok(p) => p,
Err(_) => {
bar.push(1, "Failed to change port!");
return;
}
};
unsafe {
p.write_all(entry.text().to_string().as_bytes_mut())
.unwrap();
}
entry.set_text("");
}
}

View File

@ -5,10 +5,9 @@
<object class="GtkAboutDialog" id="about_window"> <object class="GtkAboutDialog" id="about_window">
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="title" translatable="yes">Tarangam</property> <property name="title" translatable="yes">Tarangam</property>
<property name="icon">chitra.svg</property> <property name="icon">chitra-small.png</property>
<property name="type-hint">dialog</property> <property name="type-hint">dialog</property>
<property name="program-name">Tarangam (तरंगम्)</property> <property name="program-name">Tarangam (तरंगम्)</property>
<property name="version">0.1.1</property>
<property name="comments" translatable="yes">A simple serial plotter. <property name="comments" translatable="yes">A simple serial plotter.
एक सरल सीरीय्ल पलौटर।</property> एक सरल सीरीय्ल पलौटर।</property>
<property name="website">https://github.com/PiyushXCoder/Tarangam</property> <property name="website">https://github.com/PiyushXCoder/Tarangam</property>
@ -16,6 +15,8 @@
<property name="authors">Piyush Mishra(पीयूष मिश्र:)</property> <property name="authors">Piyush Mishra(पीयूष मिश्र:)</property>
<property name="logo">chitra.png</property> <property name="logo">chitra.png</property>
<property name="license-type">gpl-3-0</property> <property name="license-type">gpl-3-0</property>
<signal name="close" handler="about_window_close" swapped="no"/>
<signal name="delete-event" handler="about_window_delete" swapped="no"/>
<child internal-child="vbox"> <child internal-child="vbox">
<object class="GtkBox"> <object class="GtkBox">
<property name="can-focus">False</property> <property name="can-focus">False</property>
@ -44,11 +45,6 @@
</object> </object>
</child> </child>
</object> </object>
<object class="GtkImage" id="document_save">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">document-save</property>
</object>
<object class="GtkAdjustment" id="pankti_adjustment"> <object class="GtkAdjustment" id="pankti_adjustment">
<property name="lower">5</property> <property name="lower">5</property>
<property name="upper">500</property> <property name="upper">500</property>
@ -58,9 +54,12 @@
</object> </object>
<object class="GtkFileChooserDialog" id="save_window"> <object class="GtkFileChooserDialog" id="save_window">
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="icon">chitra.svg</property> <property name="icon">chitra-small.png</property>
<property name="type-hint">dialog</property> <property name="type-hint">dialog</property>
<property name="action">select-folder</property> <property name="action">select-folder</property>
<signal name="close" handler="save_window_close" swapped="no"/>
<signal name="delete-event" handler="save_window_delete" swapped="no"/>
<signal name="response" handler="save_window_response" swapped="no"/>
<child internal-child="vbox"> <child internal-child="vbox">
<object class="GtkBox"> <object class="GtkBox">
<property name="can-focus">False</property> <property name="can-focus">False</property>
@ -99,7 +98,7 @@
<property name="title" translatable="yes">Tarangam</property> <property name="title" translatable="yes">Tarangam</property>
<property name="default-width">850</property> <property name="default-width">850</property>
<property name="default-height">600</property> <property name="default-height">600</property>
<property name="icon">chitra.svg</property> <property name="icon">chitra-small.png</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
@ -120,12 +119,11 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkImageMenuItem" id="save_menu"> <object class="GtkMenuItem" id="save_menu">
<property name="label">Save Log</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="image">document_save</property> <property name="label" translatable="yes">Save Log</property>
<property name="use-stock">False</property> <signal name="activate" handler="save_menu_activate" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
@ -135,12 +133,12 @@
</object> </object>
</child> </child>
<child> <child>
<object class="GtkImageMenuItem" id="exit_menu"> <object class="GtkMenuItem" id="exit_menu">
<property name="label">gtk-quit</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Exit</property>
<property name="use-underline">True</property> <property name="use-underline">True</property>
<property name="use-stock">True</property> <signal name="activate" handler="exit_menu_activate" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -158,12 +156,12 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkImageMenuItem" id="about_menu"> <object class="GtkMenuItem" id="about_menu">
<property name="label">gtk-about</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">About</property>
<property name="use-underline">True</property> <property name="use-underline">True</property>
<property name="use-stock">True</property> <signal name="activate" handler="about_menu_activate" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -189,6 +187,7 @@
<property name="label" translatable="yes">refresh</property> <property name="label" translatable="yes">refresh</property>
<property name="use-underline">True</property> <property name="use-underline">True</property>
<property name="icon-name">view-refresh</property> <property name="icon-name">view-refresh</property>
<signal name="clicked" handler="refresh_port_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -203,6 +202,7 @@
<object class="GtkComboBoxText" id="port"> <object class="GtkComboBoxText" id="port">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<signal name="changed" handler="port_changed" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -242,6 +242,7 @@
<item translatable="yes">460800</item> <item translatable="yes">460800</item>
<item translatable="yes">921600</item> <item translatable="yes">921600</item>
</items> </items>
<signal name="changed" handler="bondrate_changed" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -257,6 +258,7 @@
<property name="label" translatable="yes">Connect</property> <property name="label" translatable="yes">Connect</property>
<property name="use-underline">True</property> <property name="use-underline">True</property>
<property name="icon-name">media-playback-start</property> <property name="icon-name">media-playback-start</property>
<signal name="clicked" handler="jagrit_btn_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -270,6 +272,7 @@
<property name="label" translatable="yes">Stop</property> <property name="label" translatable="yes">Stop</property>
<property name="use-underline">True</property> <property name="use-underline">True</property>
<property name="icon-name">media-playback-stop</property> <property name="icon-name">media-playback-stop</property>
<signal name="clicked" handler="avrodith_btn_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -303,6 +306,7 @@
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Clear</property> <property name="label" translatable="yes">Clear</property>
<property name="use-underline">True</property> <property name="use-underline">True</property>
<signal name="clicked" handler="clear_graph_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -320,6 +324,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">False</property> <property name="receives-default">False</property>
<property name="draw-indicator">True</property> <property name="draw-indicator">True</property>
<signal name="toggled" handler="draw_patches_toggled" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -340,6 +345,7 @@
<property name="receives-default">False</property> <property name="receives-default">False</property>
<property name="active">True</property> <property name="active">True</property>
<property name="draw-indicator">True</property> <property name="draw-indicator">True</property>
<signal name="toggled" handler="draw_box_toggled" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -359,6 +365,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">False</property> <property name="receives-default">False</property>
<property name="draw-indicator">True</property> <property name="draw-indicator">True</property>
<signal name="toggled" handler="draw_baarik_box_toggled" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -407,6 +414,7 @@
<property name="digits">1</property> <property name="digits">1</property>
<property name="numeric">True</property> <property name="numeric">True</property>
<property name="wrap">True</property> <property name="wrap">True</property>
<signal name="value-changed" handler="pankti_value_changed" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -422,6 +430,30 @@
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkScrolledWindow">
<property name="height-request">35</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="vscrollbar-policy">never</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTextView" id="graph_data">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
<property name="left-margin">5</property>
<property name="top-margin">7</property>
<property name="cursor-visible">False</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child> <child>
<object class="GtkDrawingArea" id="draw_area"> <object class="GtkDrawingArea" id="draw_area">
<property name="width-request">500</property> <property name="width-request">500</property>
@ -434,7 +466,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -452,6 +484,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">False</property> <property name="receives-default">False</property>
<property name="draw-indicator">True</property> <property name="draw-indicator">True</property>
<signal name="toggled" handler="nimna_stambh_toggled" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -490,6 +523,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="width-chars">8</property> <property name="width-chars">8</property>
<property name="text" translatable="yes">0</property> <property name="text" translatable="yes">0</property>
<signal name="changed" handler="stambh_1_changed" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -528,6 +562,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="width-chars">8</property> <property name="width-chars">8</property>
<property name="text" translatable="yes">100</property> <property name="text" translatable="yes">100</property>
<signal name="changed" handler="stambh_2_changed" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -540,7 +575,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">2</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
</object> </object>
@ -564,6 +599,7 @@
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Clear</property> <property name="label" translatable="yes">Clear</property>
<property name="use-underline">True</property> <property name="use-underline">True</property>
<signal name="clicked" handler="clear_log_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -581,6 +617,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">False</property> <property name="receives-default">False</property>
<property name="draw-indicator">True</property> <property name="draw-indicator">True</property>
<signal name="toggled" handler="full_log_toggled" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@ -626,6 +663,7 @@
<object class="GtkEntry" id="send_entry"> <object class="GtkEntry" id="send_entry">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<signal name="key-press-event" handler="send_entry_key_press_event" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@ -640,6 +678,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">True</property> <property name="receives-default">True</property>
<property name="image">send_image</property> <property name="image">send_image</property>
<signal name="clicked" handler="send_btn_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

61
src/util.rs Normal file
View File

@ -0,0 +1,61 @@
/*
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 <https://www.gnu.org/licenses/>
*/
//! Feel free to see through codes. Application is not written to be used as a library for other app. :)
use std::sync::{atomic::*, Mutex};
/// Status of Serial reading
#[derive(Debug, Clone, Copy)]
pub(crate) enum Status {
AVRODTIH, // Mode of being stopped
SAYAN, // Mode of Sleeping
JAGRIT, // Mode of Active
PARIVARTIT, // Mode of being values modified
}
#[derive(Debug)]
pub(crate) struct Properties {
pub(crate) bondrate: AtomicU32,
pub(crate) port: Mutex<String>,
pub(crate) status: Mutex<Status>,
}
/// For communication between mpsc of graph and serial port
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) enum MessageSerialThread {
Msg(String, MessageSerialThreadMsgType),
Points(Vec<(String, f64)>),
Status(String),
}
#[derive(Debug)]
pub(crate) enum MessageSerialThreadMsgType {
Point,
Log,
}
impl Properties {
pub(crate) fn default() -> Self {
Properties {
bondrate: AtomicU32::new(9600),
port: Mutex::new(String::new()),
status: Mutex::new(Status::AVRODTIH),
}
}
}

View File

@ -1,28 +0,0 @@
use std::io::prelude::*;
use std::io::BufReader;
use std::time::Duration;
#[test]
fn start() {
let ports = serialport::available_ports();
println!("{:?}",ports);
let mut p = serialport::new("/dev/ttyUSB1", 9600).timeout(Duration::from_millis(10))
.open().expect("Failed to open port");
unsafe {
p.write_all("buf".to_owned().as_bytes_mut()).unwrap();
}
let mut read = BufReader::new(p);
let mut buf = String::new();
loop {
match read.read_line(&mut buf) {
Ok(_) =>
{
print!("{}", buf);
buf.clear();
},
Err(_) => {}
}
}
}