Compare commits
12 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
bc46e11e5a | |
|
|
68f239ad32 | |
|
|
d678bbe25a | |
|
|
ee3801fbc2 | |
|
|
abae4ca0c2 | |
|
|
47f3d82265 | |
|
|
9fd576e48c | |
|
|
3a7bcb9f66 | |
|
|
bfffeceafa | |
|
|
3c42ea5320 | |
|
|
ae428b40f4 | |
|
|
1d92a34093 |
32
Cargo.toml
|
|
@ -1,23 +1,31 @@
|
|||
[package]
|
||||
name = "tarangam"
|
||||
version = "0.1.2"
|
||||
version = "0.3.1"
|
||||
authors = ["PiyushXCoder <piyush.raj.kit@gmail.com>"]
|
||||
license = "GPL 3.0"
|
||||
license = "GPL-3.0-only"
|
||||
edition = "2018"
|
||||
documentation = "A simple serial plotter. एक सरल सीरीय्ल पलौटर।"
|
||||
description = "A simple serial plotter. एक सरल सीरीय्ल पलौटर।"
|
||||
readme = "README.md"
|
||||
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
|
||||
|
||||
[dependencies]
|
||||
gtk = "0.9.2"
|
||||
gdk = "0.13.2"
|
||||
gio = "0.9.1"
|
||||
glib = "0.10.3"
|
||||
png = "0.16.8"
|
||||
cairo-rs = { version = "0.9.1", features = ["png"] }
|
||||
tokio = { version = "1.5.0", features = [
|
||||
"rt",
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
"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"
|
||||
libmath = "0.2.1"
|
||||
serialport = "4.0.0"
|
||||
clap = "2.33.3"
|
||||
serialport = "4.0.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
# Tarangam
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Interface
|
||||

|
||||
|
|
|
|||
|
After Width: | Height: | Size: 2.2 KiB |
BIN
chitra.png
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
44
chitra.svg
|
|
@ -1,20 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
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"
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 1000 1000"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
|
||||
sodipodi:docname="icon.svg">
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
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
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
|
|
@ -47,19 +50,20 @@
|
|||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="605.76766"
|
||||
inkscape:cy="248.85918"
|
||||
inkscape:cx="607.14286"
|
||||
inkscape:cy="250"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1214"
|
||||
inkscape:window-height="757"
|
||||
inkscape:window-x="295"
|
||||
inkscape:window-y="198"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1002"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
units="px"
|
||||
showguides="false">
|
||||
showguides="false"
|
||||
inkscape:pagecheckerboard="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid869" />
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 117 KiB |
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
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. :)
|
||||
|
||||
pub struct Config {
|
||||
pub ui_file: String
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn generate() -> Self {
|
||||
let ui_file = std::env::var("TARANGAM_UI_FILE");
|
||||
|
||||
Config {
|
||||
ui_file: match ui_file {
|
||||
Ok(val) => val,
|
||||
Err(_) => std::env::current_exe().unwrap().parent().unwrap()
|
||||
.join("ui.glade").to_str().unwrap().to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
154
src/graph.rs
|
|
@ -20,41 +20,42 @@
|
|||
use gtk::prelude::*;
|
||||
use gtk::DrawingArea;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
/// A single line
|
||||
#[derive(Debug)]
|
||||
pub struct Line {
|
||||
pub points: Vec<(f64,f64)>,
|
||||
pub color: (f64, f64, f64)
|
||||
pub(crate) struct Line {
|
||||
pub(crate) points: Vec<(f64, f64)>,
|
||||
pub(crate) color: (f64, f64, f64),
|
||||
}
|
||||
|
||||
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 {
|
||||
points,
|
||||
color: (r,g,b)
|
||||
color: (r, g, b),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Tools to draw Graph
|
||||
pub struct Graph {
|
||||
pub area: DrawingArea,
|
||||
pub scale_x_start: f64, // start of x on pankti
|
||||
pub scale_x_size: f64, // size of pankti to show
|
||||
pub scale_y_start: f64, // start of y on stambh
|
||||
pub scale_y_size: f64, // size of stambh to show
|
||||
pub draw_patch: bool, // enable to draw circle spot on line
|
||||
pub 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 auto_adjust_y: bool, // enable to automatically adjust y axis
|
||||
pub 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) struct Graph {
|
||||
pub(crate) area: DrawingArea,
|
||||
pub(crate) scale_x_start: f64, // start of x on pankti
|
||||
pub(crate) scale_x_size: f64, // size of pankti to show
|
||||
pub(crate) scale_y_start: f64, // start of y on stambh
|
||||
pub(crate) scale_y_size: f64, // size of stambh to show
|
||||
pub(crate) draw_patch: bool, // enable to draw circle spot on line
|
||||
pub(crate) draw_box: bool, // enable to show boxes linke graph paper
|
||||
pub(crate) draw_baarik_box: bool, // enable to show baarik(similar meaning to smaller) linke graph paper
|
||||
pub(crate) auto_adjust_y: bool, // enable to automatically adjust y axis
|
||||
pub(crate) lines: HashMap<String, Line>,
|
||||
pub(crate) pankti_sankya: f64, // use used while adding to point in lines to see last count of graphable input
|
||||
}
|
||||
|
||||
impl Graph {
|
||||
pub fn new(area: DrawingArea,
|
||||
pub(crate) fn new(
|
||||
area: DrawingArea,
|
||||
scale_x_start: f64,
|
||||
scale_x_size: f64,
|
||||
scale_y_start: f64,
|
||||
|
|
@ -64,8 +65,8 @@ impl Graph {
|
|||
draw_baarik_box: bool,
|
||||
auto_adjust_y: bool,
|
||||
lines: HashMap<String, Line>,
|
||||
pankti_sankya: f64) -> Rc<RefCell<Self>> {
|
||||
|
||||
pankti_sankya: f64,
|
||||
) -> Rc<RefCell<Self>> {
|
||||
let graph = Rc::new(RefCell::new(Graph {
|
||||
area,
|
||||
scale_x_start,
|
||||
|
|
@ -77,11 +78,11 @@ impl Graph {
|
|||
draw_baarik_box,
|
||||
auto_adjust_y,
|
||||
lines,
|
||||
pankti_sankya
|
||||
pankti_sankya,
|
||||
}));
|
||||
|
||||
let graph_tmp = Rc::clone(&graph);
|
||||
graph.borrow().area.connect_draw(move |area,ctx| {
|
||||
graph.borrow().area.connect_draw(move |area, ctx| {
|
||||
Graph::draw(area, ctx, &graph_tmp);
|
||||
Inhibit(false)
|
||||
});
|
||||
|
|
@ -90,22 +91,29 @@ impl Graph {
|
|||
}
|
||||
|
||||
/// 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_line_width(1.0);
|
||||
// lines parallel to stambh
|
||||
for i in 1..math::round::ceil(area_width/cell_size, 0) as i32 {
|
||||
for i in 1..math::round::ceil(area_width / cell_size, 0) as i32 {
|
||||
let xi = i as f64 * cell_size + src_x;
|
||||
ctx.move_to(xi, 0.0);
|
||||
ctx.line_to(xi, area_height);
|
||||
}
|
||||
// lines parallel to pankti
|
||||
for i in 1..math::round::ceil(area_height/cell_size, 0) as i32 {
|
||||
for i in 1..math::round::ceil(area_height / cell_size, 0) as i32 {
|
||||
let yi = area_height - i as f64 * cell_size;
|
||||
ctx.move_to(src_x, yi);
|
||||
ctx.line_to(area_width + src_x, yi);
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.stroke().unwrap();
|
||||
}
|
||||
|
||||
/// transform point to show on graph
|
||||
|
|
@ -119,27 +127,25 @@ impl Graph {
|
|||
scale_x_size: f64,
|
||||
scale_y_size: 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,
|
||||
height - ((s - s_start) * aa_dumm_stambh) / scale_y_size
|
||||
((p - p_start) * aa_dumm_pankti) / scale_x_size + stambh_scale_width,
|
||||
height - ((s - s_start) * aa_dumm_stambh) / scale_y_size,
|
||||
)
|
||||
}
|
||||
|
||||
/// callback of drawing area to draw graph
|
||||
fn draw(area: >k::DrawingArea,
|
||||
ctx: &cairo::Context,
|
||||
graph: &Rc<RefCell<Graph>>) {
|
||||
|
||||
fn draw(area: >k::DrawingArea, ctx: &cairo::Context, graph: &Rc<RefCell<Graph>>) {
|
||||
let graph = graph.borrow();
|
||||
|
||||
ctx.set_source_rgb(0.1, 0.5, 0.5);
|
||||
ctx.paint();
|
||||
ctx.paint().unwrap();
|
||||
|
||||
let pankti_scale_height = 50.0;
|
||||
let stambh_scale_width = 60.0;
|
||||
let width = area.get_allocated_width() as f64 - stambh_scale_width;
|
||||
let height = area.get_allocated_height() as f64 - pankti_scale_height;
|
||||
let width = area.allocated_width() as f64 - stambh_scale_width;
|
||||
let height = area.allocated_height() as f64 - pankti_scale_height;
|
||||
|
||||
let manjusa_maap = 50.0;
|
||||
|
||||
|
|
@ -164,8 +170,8 @@ impl Graph {
|
|||
ctx.set_line_width(2.0);
|
||||
ctx.set_line_cap(cairo::LineCap::Round);
|
||||
let draw_patch = graph.draw_patch;
|
||||
for (_,line) in graph.lines.iter() {
|
||||
for (i, (p,s)) in line.points.iter().enumerate() {
|
||||
for (_, line) in graph.lines.iter() {
|
||||
for (i, (p, s)) in line.points.iter().enumerate() {
|
||||
// check if point is last poin on line
|
||||
let (p_dumm, s_dumm) = if i < line.points.len() - 1 {
|
||||
line.points[i + 1]
|
||||
|
|
@ -202,14 +208,19 @@ impl Graph {
|
|||
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.line_to(bindu_t.0, bindu_t.1);
|
||||
ctx.stroke();
|
||||
ctx.stroke().unwrap();
|
||||
|
||||
// draw circle over point
|
||||
if draw_patch {
|
||||
ctx.set_source_rgb(0.0, 0.0, 1.0);
|
||||
ctx.arc(bindu_dumm_t.0, bindu_dumm_t.1,
|
||||
5.0, 0.0, std::f64::consts::PI * 2.0);
|
||||
ctx.stroke();
|
||||
ctx.arc(
|
||||
bindu_dumm_t.0,
|
||||
bindu_dumm_t.1,
|
||||
5.0,
|
||||
0.0,
|
||||
std::f64::consts::PI * 2.0,
|
||||
);
|
||||
ctx.stroke().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,33 +229,41 @@ impl Graph {
|
|||
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(stambh_scale_width, height, width, pankti_scale_height);
|
||||
ctx.fill();
|
||||
ctx.fill().unwrap();
|
||||
|
||||
ctx.set_source_rgb(1.0, 1.0, 1.0);
|
||||
// write numbers on pankti scale
|
||||
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 f = ctx.text_extents(&text);
|
||||
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();
|
||||
let text =
|
||||
math::round::floor(i as f64 * anupat_pankti + graph.scale_x_start, 4).to_string();
|
||||
let f = ctx.text_extents(&text).expect("Text dimension");
|
||||
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.show_text(&text);
|
||||
ctx.restore();
|
||||
ctx.show_text(&text).unwrap();
|
||||
ctx.restore().unwrap();
|
||||
}
|
||||
// write numbers on stambh scale
|
||||
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 f = ctx.text_extents(&text);
|
||||
ctx.move_to(stambh_scale_width - f.width, height - i as f64 * manjusa_maap + f.width * 0.5);
|
||||
ctx.save();
|
||||
let text =
|
||||
math::round::floor(i as f64 * anupat_stambh + graph.scale_y_start, 4).to_string();
|
||||
let f = ctx.text_extents(&text).expect("Text dimension");
|
||||
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.show_text(&text);
|
||||
ctx.restore();
|
||||
ctx.show_text(&text).unwrap();
|
||||
ctx.restore().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
// stambh
|
||||
|
|
@ -267,16 +286,16 @@ impl Graph {
|
|||
}
|
||||
|
||||
/// 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
|
||||
if self.lines.len() == 0 {
|
||||
return (0.0,0.0,0.0,0.0);
|
||||
return (0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
let mut mx_x:Option<f64> = None;
|
||||
let mut mi_x:Option<f64> = None;
|
||||
let mut mx_y:Option<f64> = None;
|
||||
let mut mi_y:Option<f64> = None;
|
||||
let mut mx_x: Option<f64> = None;
|
||||
let mut mi_x: Option<f64> = None;
|
||||
let mut mx_y: Option<f64> = None;
|
||||
let mut mi_y: Option<f64> = None;
|
||||
|
||||
for (_, line) in self.lines.iter() {
|
||||
if line.points.len() == 0 {
|
||||
|
|
@ -307,7 +326,7 @@ impl Graph {
|
|||
mi_y = Some(line.points[0].1);
|
||||
}
|
||||
|
||||
for (x,y) in line.points.iter().skip(1) {
|
||||
for (x, y) in line.points.iter().skip(1) {
|
||||
mx_x = Some(f64::max(mx_x.unwrap(), *x));
|
||||
mi_x = Some(f64::min(mi_x.unwrap(), *x));
|
||||
mx_y = Some(f64::max(mx_y.unwrap(), *y));
|
||||
|
|
@ -320,17 +339,16 @@ impl Graph {
|
|||
|
||||
// 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
|
||||
pub fn trim_lines(&mut self) {
|
||||
pub(crate) fn trim_lines(&mut self) {
|
||||
for (_, line) in self.lines.iter_mut() {
|
||||
let mut i = 0;
|
||||
while i < line.points.len() {
|
||||
|
||||
match line.points.get(i + 2) {
|
||||
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);
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
if line.points[line.points.len() - 1].0 < self.scale_x_start {
|
||||
line.points.clear();
|
||||
|
|
|
|||
749
src/lib.rs
|
|
@ -17,358 +17,301 @@
|
|||
|
||||
//! 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 rand::Rng;
|
||||
|
||||
use std::{collections::HashMap, sync::{Arc, Mutex}};
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::io::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::io::BufReader;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use graph::Graph;
|
||||
use port_util as putil;
|
||||
use util::Properties;
|
||||
|
||||
/// 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
|
||||
}
|
||||
// Building and propsuring GUI
|
||||
pub fn build_ui(app: >k::Application) {
|
||||
let props = Arc::new(Properties::default());
|
||||
let ui_file = include_str!("ui.glade");
|
||||
let builder = gtk::Builder::from_string(ui_file);
|
||||
|
||||
/// 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
|
||||
#[derive(Debug)]
|
||||
enum MessageSerialThread {
|
||||
Msg(String, MessageSerialThreadMsgType),
|
||||
Points(Vec<(String, f64)>),
|
||||
Status(String)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MessageSerialThreadMsgType {
|
||||
Point,
|
||||
Log
|
||||
}
|
||||
|
||||
// Building and configuring GUI
|
||||
pub fn build_ui(app: >k::Application, ui_file: &str) {
|
||||
let config = Arc::new(Mutex::new(Config::new()));
|
||||
let builder = gtk::Builder::from_file(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));
|
||||
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(
|
||||
builder.get_object::<gtk::DrawingArea>("draw_area").expect("Resource file missing!"),
|
||||
0.0, 100.0,
|
||||
0.0, 100.0,
|
||||
builder
|
||||
.object::<gtk::DrawingArea>("draw_area")
|
||||
.expect("Resource file missing!"),
|
||||
0.0,
|
||||
100.0,
|
||||
0.0,
|
||||
100.0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
HashMap::new(),
|
||||
0.0
|
||||
0.0,
|
||||
);
|
||||
|
||||
win.show_all();
|
||||
|
||||
// exit_menu
|
||||
let exit_menu = builder.get_object::<gtk::MenuItem>("exit_menu").expect("Resource file missing!");
|
||||
|
||||
let tmp_win = win.clone();
|
||||
exit_menu.connect_activate(move |_|{
|
||||
unsafe {
|
||||
tmp_win.destroy();
|
||||
// required by signals
|
||||
let stambh_1 = builder
|
||||
.object::<gtk::Entry>("stambh_1")
|
||||
.expect("Resource file missing!");
|
||||
let stambh_2 = builder
|
||||
.object::<gtk::Entry>("stambh_2")
|
||||
.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.set_version(Some(env!("CARGO_PKG_VERSION")));
|
||||
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_bar = bar.clone();
|
||||
save_window.connect_response(move |win, res| {
|
||||
match res {
|
||||
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();
|
||||
if let Some(path) = win.filename() {
|
||||
if let Some(buf) = tmp_log_area.buffer() {
|
||||
let text = buf
|
||||
.text(&buf.start_iter(), &buf.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::<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);
|
||||
_ => (),
|
||||
});
|
||||
|
||||
/*
|
||||
|
|
@ -380,28 +323,32 @@ pub fn build_ui(app: >k::Application, ui_file: &str) {
|
|||
*/
|
||||
let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
|
||||
let tmp_config = Arc::clone(&config);
|
||||
std::thread::spawn(move || {
|
||||
let tmp_props = Arc::clone(&props);
|
||||
tokio::task::spawn(async move {
|
||||
let mut bufread: Option<BufReader<Box<dyn serialport::SerialPort>>> = None;
|
||||
let mut buf = String::new();
|
||||
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
|
||||
let full_log = builder.get_object::<gtk::CheckButton>("full_log").expect("Resource file missing!");
|
||||
let graph_data = builder.get_object::<gtk::TextView>("graph_data").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);
|
||||
receiver.attach(None, move |msg| {
|
||||
match msg {
|
||||
MessageSerialThread::Msg(text, msg_type) => {
|
||||
util::MessageSerialThread::Msg(text, msg_type) => {
|
||||
receiver_for_msg(text, &msg_type, &full_log, &log_area);
|
||||
},
|
||||
MessageSerialThread::Points(points) => {
|
||||
}
|
||||
util::MessageSerialThread::Points(points) => {
|
||||
receiver_for_points(points, &tmp_graph, &graph_data);
|
||||
}
|
||||
MessageSerialThread::Status(text) => {
|
||||
util::MessageSerialThread::Status(text) => {
|
||||
bar.push(1, &text);
|
||||
}
|
||||
}
|
||||
|
|
@ -409,100 +356,33 @@ pub fn build_ui(app: >k::Application, ui_file: &str) {
|
|||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
fn receiver_for_msg(text: String, msg_type: &MessageSerialThreadMsgType, full_log: >k::CheckButton, log_area: >k::TextView) {
|
||||
if !full_log.get_active(){
|
||||
if let MessageSerialThreadMsgType::Point = msg_type {
|
||||
fn receiver_for_msg(
|
||||
text: String,
|
||||
msg_type: &util::MessageSerialThreadMsgType,
|
||||
full_log: >k::CheckButton,
|
||||
log_area: >k::TextView,
|
||||
) {
|
||||
if !full_log.is_active() {
|
||||
if let util::MessageSerialThreadMsgType::Point = msg_type {
|
||||
return;
|
||||
}
|
||||
}
|
||||
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);
|
||||
if text.len() <= 0 {
|
||||
return;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
// 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>>, graph_data: >k::TextView) {
|
||||
fn receiver_for_points(
|
||||
points: Vec<(String, f64)>,
|
||||
graph: &Rc<RefCell<Graph>>,
|
||||
graph_data: >k::TextView,
|
||||
) {
|
||||
for (line, point) in points {
|
||||
let mut gp = graph.borrow_mut();
|
||||
|
||||
|
|
@ -510,27 +390,30 @@ fn receiver_for_points(points: Vec<(String, f64)>, graph: &Rc<RefCell<Graph>>, g
|
|||
match gp.lines.get_mut(&line) {
|
||||
Some(val) => {
|
||||
val.points.push((sankhya, point));
|
||||
} None => {
|
||||
}
|
||||
None => {
|
||||
let v = vec![(sankhya, point)];
|
||||
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));
|
||||
let buf = graph_data.get_buffer().expect("Couldn't get graph_data");
|
||||
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.get_end_iter(), "##");
|
||||
buf.insert(&mut buf.end_iter(), "##");
|
||||
|
||||
let tag = gtk::TextTag::new(None);
|
||||
let rgba = gdk::RGBA {
|
||||
red: line.color.0,
|
||||
green: line.color.1,
|
||||
blue: line.color.2,
|
||||
alpha: 1.0
|
||||
};
|
||||
tag.set_property_background_rgba(Some(&rgba));
|
||||
tag.set_property_foreground_rgba(Some(&rgba));
|
||||
buf.get_tag_table().unwrap().add(&tag);
|
||||
buf.apply_tag(&tag, &buf.get_iter_at_offset(buf.get_end_iter().get_offset() - 2), &buf.get_end_iter());
|
||||
buf.insert(&mut buf.get_end_iter(), &format!(" {}, ", key));
|
||||
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();
|
||||
}
|
||||
|
|
@ -539,27 +422,3 @@ fn receiver_for_points(points: Vec<(String, f64)>, graph: &Rc<RefCell<Graph>>, g
|
|||
}
|
||||
graph.borrow_mut().pankti_sankya += 1.0;
|
||||
}
|
||||
|
||||
// Sends text through Serial Post to device
|
||||
fn send_text(config: &Arc<Mutex<Config>>, 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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
20
src/main.rs
|
|
@ -12,22 +12,16 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#![windows_subsystem = "windows"]
|
||||
|
||||
mod config;
|
||||
use gio::{prelude::*, ApplicationFlags};
|
||||
|
||||
use gio::prelude::*;
|
||||
use std::env::args;
|
||||
|
||||
fn main() {
|
||||
let conf = config::Config::generate();
|
||||
|
||||
let app = gtk::Application::new(Some("sng.tarangm"), Default::default())
|
||||
.expect("Failed to initiate gtk");
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let app = gtk::Application::new(Some("sng.tarangm"), ApplicationFlags::default());
|
||||
|
||||
app.connect_activate(move |app| {
|
||||
tarangam::build_ui(app, &conf.ui_file);
|
||||
tarangam::build_ui(app);
|
||||
});
|
||||
|
||||
app.run(&args().collect::<Vec<_>>());
|
||||
app.run();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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: >k::Entry, bar: >k::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("");
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<object class="GtkAboutDialog" id="about_window">
|
||||
<property name="can-focus">False</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="program-name">Tarangam (तरंगम्)</property>
|
||||
<property name="comments" translatable="yes">A simple serial plotter.
|
||||
|
|
@ -15,6 +15,8 @@
|
|||
<property name="authors">Piyush Mishra(पीयूष मिश्र:)</property>
|
||||
<property name="logo">chitra.png</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">
|
||||
<object class="GtkBox">
|
||||
<property name="can-focus">False</property>
|
||||
|
|
@ -43,11 +45,6 @@
|
|||
</object>
|
||||
</child>
|
||||
</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">
|
||||
<property name="lower">5</property>
|
||||
<property name="upper">500</property>
|
||||
|
|
@ -57,9 +54,12 @@
|
|||
</object>
|
||||
<object class="GtkFileChooserDialog" id="save_window">
|
||||
<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="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">
|
||||
<object class="GtkBox">
|
||||
<property name="can-focus">False</property>
|
||||
|
|
@ -98,7 +98,7 @@
|
|||
<property name="title" translatable="yes">Tarangam</property>
|
||||
<property name="default-width">850</property>
|
||||
<property name="default-height">600</property>
|
||||
<property name="icon">chitra.svg</property>
|
||||
<property name="icon">chitra-small.png</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
|
|
@ -119,12 +119,11 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="save_menu">
|
||||
<property name="label">Save Log</property>
|
||||
<object class="GtkMenuItem" id="save_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="image">document_save</property>
|
||||
<property name="use-stock">False</property>
|
||||
<property name="label" translatable="yes">Save Log</property>
|
||||
<signal name="activate" handler="save_menu_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
|
@ -134,12 +133,12 @@
|
|||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="exit_menu">
|
||||
<property name="label">gtk-quit</property>
|
||||
<object class="GtkMenuItem" id="exit_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Exit</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="activate" handler="exit_menu_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -157,12 +156,12 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="about_menu">
|
||||
<property name="label">gtk-about</property>
|
||||
<object class="GtkMenuItem" id="about_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">About</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="activate" handler="about_menu_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -188,6 +187,7 @@
|
|||
<property name="label" translatable="yes">refresh</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="icon-name">view-refresh</property>
|
||||
<signal name="clicked" handler="refresh_port_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
@ -202,6 +202,7 @@
|
|||
<object class="GtkComboBoxText" id="port">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<signal name="changed" handler="port_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -241,6 +242,7 @@
|
|||
<item translatable="yes">460800</item>
|
||||
<item translatable="yes">921600</item>
|
||||
</items>
|
||||
<signal name="changed" handler="bondrate_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -256,6 +258,7 @@
|
|||
<property name="label" translatable="yes">Connect</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="icon-name">media-playback-start</property>
|
||||
<signal name="clicked" handler="jagrit_btn_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
@ -269,6 +272,7 @@
|
|||
<property name="label" translatable="yes">Stop</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="icon-name">media-playback-stop</property>
|
||||
<signal name="clicked" handler="avrodith_btn_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
@ -302,6 +306,7 @@
|
|||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Clear</property>
|
||||
<property name="use-underline">True</property>
|
||||
<signal name="clicked" handler="clear_graph_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
@ -319,6 +324,7 @@
|
|||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
<signal name="toggled" handler="draw_patches_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -339,6 +345,7 @@
|
|||
<property name="receives-default">False</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
<signal name="toggled" handler="draw_box_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -358,6 +365,7 @@
|
|||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
<signal name="toggled" handler="draw_baarik_box_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -406,6 +414,7 @@
|
|||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="wrap">True</property>
|
||||
<signal name="value-changed" handler="pankti_value_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -475,6 +484,7 @@
|
|||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
<signal name="toggled" handler="nimna_stambh_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -513,6 +523,7 @@
|
|||
<property name="can-focus">True</property>
|
||||
<property name="width-chars">8</property>
|
||||
<property name="text" translatable="yes">0</property>
|
||||
<signal name="changed" handler="stambh_1_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -551,6 +562,7 @@
|
|||
<property name="can-focus">True</property>
|
||||
<property name="width-chars">8</property>
|
||||
<property name="text" translatable="yes">100</property>
|
||||
<signal name="changed" handler="stambh_2_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -587,6 +599,7 @@
|
|||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Clear</property>
|
||||
<property name="use-underline">True</property>
|
||||
<signal name="clicked" handler="clear_log_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
@ -604,6 +617,7 @@
|
|||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
<signal name="toggled" handler="full_log_toggled" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
@ -649,6 +663,7 @@
|
|||
<object class="GtkEntry" id="send_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<signal name="key-press-event" handler="send_entry_key_press_event" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
|
@ -663,6 +678,7 @@
|
|||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="image">send_image</property>
|
||||
<signal name="clicked" handler="send_btn_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||