master

3
.gitignore vendored

@ -0,0 +1,3 @@
/target/
hello.zip

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/<executable file>",
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

2384
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,18 @@
[package]
name = "custom_window_frame"
version = "0.1.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.61"
publish = false
[dependencies]
eframe = "0.19.0"
serialport= "4.1.0"
async-std = "*"
local-encoding="*"
[profile.release]
opt-level = "z"

@ -0,0 +1,3 @@
```sh
cargo run -p custom_window_frame
```

@ -0,0 +1,546 @@
//! Show a custom window frame instead of the default OS window chrome decorations.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use local_encoding::{Encoder, Encoding};
use std::rc::Rc;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::{mpsc, Arc, Mutex};
use std::thread::ThreadId;
use std::{
collections::btree_set::Union,
io::{self, ErrorKind},
thread,
time::Duration,
};
use eframe::{
egui::{self, Button, RichText},
epaint::Vec2,
};
use serialport::{ClearBuffer, Error, Result, SerialPort};
//custom types
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
enum ScreenCurrent {
Config,
CheckStatus,
Diagnosis,
WorkCycle,
PortSelection,
}
impl Default for ScreenCurrent {
fn default() -> Self {
ScreenCurrent::PortSelection
}
}
#[derive(Debug)]
struct Screen {
id: ScreenCurrent,
name: String,
}
#[derive(Debug)]
struct ScreenSelector {
screen: [Screen; 5],
}
impl Default for ScreenSelector {
fn default() -> Self {
ScreenSelector {
screen: [
Screen {
id: (ScreenCurrent::Config),
name: ("Конфигурация работы".to_string()),
},
Screen {
id: (ScreenCurrent::CheckStatus),
name: ("Просмотр состояния".to_string()),
},
Screen {
id: (ScreenCurrent::Diagnosis),
name: ("Диагностика".to_string()),
},
Screen {
id: (ScreenCurrent::WorkCycle),
name: ("Работа по циклу".to_string()),
},
Screen {
id: (ScreenCurrent::PortSelection),
name: ("Выбор порта".to_string()),
},
],
}
}
}
#[derive(Debug, Default, Clone, Copy)]
#[repr(C, packed)]
struct MK_AnalogData {
term: [f32; 4],
rrg: [f32; 6],
valve: [f32; 6],
}
#[derive(Debug, Default, Clone, Copy)]
#[repr(C, packed)]
struct MK_Limits {
tmax: f32,
tmin: f32,
fmax: [f32; 6],
fmin: [f32; 6],
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
enum MKGazType {
Air,
Helium,
Nitrogen,
}
impl Default for MKGazType {
fn default() -> Self {
MKGazType::Air
}
}
#[derive(Debug, Default, Clone, Copy)]
#[repr(C, packed)]
struct MK_ModeData {
term: [f32; 4],
rrg: [f32; 6],
gaz_type: MKGazType,
}
#[derive(PartialEq)]
#[repr(u8)]
enum PortMode {
Start,
Cycle,
}
fn main() {
let port = serialport::available_ports().unwrap();
let options = eframe::NativeOptions {
// Hide the OS-specific "chrome" around the window:
decorated: false,
// To have rounded corners we need transparency:
transparent: true,
default_theme: eframe::Theme::Dark,
min_window_size: Some(egui::vec2(320.0, 100.0)),
..Default::default()
};
eframe::run_native(
"Микрогаз-ФМ", // unused title
options,
Box::new(|_cc| {
Box::new(MyApp {
ports: port,
..Default::default()
})
}),
);
}
#[derive(Default)]
struct MyApp {
enabled: bool,
is_connected: bool,
screen_current: ScreenCurrent,
screens: ScreenSelector,
ports: Vec<serialport::SerialPortInfo>,
model: u8,
// sender:Sender<u8>,
port_current_idx: usize,
}
impl eframe::App for MyApp {
fn clear_color(&self, _visuals: &egui::Visuals) -> egui::Rgba {
egui::Rgba::TRANSPARENT // Make sure we don't paint anything behind the rounded corners
}
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
let (tx, rx): (Sender<u8>, Receiver<u8>) = mpsc::channel();
let (c_tx, c_rx): (Sender<MK_AnalogData>, Receiver<MK_AnalogData>) = mpsc::channel();
let (en_tx, en_rx): (Sender<bool>, Receiver<bool>) = mpsc::channel();
let en = self.enabled.clone();
// println!("global={:?}",en);
let en_mtx = Arc::new(Mutex::new(en));
let mut mdl = "".to_string();
if self.model == 0 {
mdl = "".to_string()
} else {
mdl = self.model.to_string()
}
if self.is_connected == true && self.enabled == false {
self.is_connected = false;
println!("Disable!");
let a_mtx = Arc::clone(&en_mtx);
let en1_tx = en_tx.clone();
thread::spawn(move||loop{
let en = a_mtx.lock().unwrap();
// println!("{:?}",en);
en1_tx.send(true).ok();
thread::sleep(Duration::from_millis(100));
});
}
if self.is_connected == true && self.enabled {
self.is_connected = false;
let portname = self.ports[self.port_current_idx].port_name.as_str();
let id = port_working_thread_init(c_tx, portname.to_string(), en_rx);
println!("{:?}", id);
// let a_mtx = Arc::clone(&en_mtx);
let en1_tx = en_tx.clone();
// thread::spawn(move||loop{
// // let en = a_mtx.lock().unwrap();
// en1_tx.send(true).ok();
// thread::sleep(Duration::from_millis(100));
// });
}
let title = format!("Микрогаз-ФМ{}", mdl);
custom_window_frame(ctx, frame, title.as_str(), |ui| {
self.side_bar(ui);
match self.screen_current {
ScreenCurrent::PortSelection => {
self.ports = serialport::available_ports().unwrap();
// println!("Update");
self.connect_screen(ui, tx, rx)
}
_ => (),
}
});
}
}
impl MyApp {
fn side_bar(&mut self, ui: &mut egui::Ui) {
egui::SidePanel::left("backend_panel")
.default_width(200.0)
.resizable(false)
.show_inside(ui, |ui| {
ui.vertical_centered(|ui| {
ui.heading("Микрогаз");
});
ui.vertical(|ui| {
for screen in self.screens.screen.iter() {
if ui
.add_sized(Vec2::new(200.0, 24.0), Button::new(screen.name.as_str()))
.clicked()
{
self.screen_current = screen.id;
println!("{:#?}", self.screen_current);
};
}
});
});
}
fn connect_screen(&mut self, ui: &mut egui::Ui, snd: Sender<u8>, rx: Receiver<u8>) {
let tx = snd;
egui::CentralPanel::default().show_inside(ui, |ui| {
// CollapsingHeader::new("Соединение:").default_open(false).show(ui,|ui|{
ui.group(|ui| {
ui.vertical(|ui| {
ui.label("Выберите порт:");
if self.ports.len() > 0 {
ui.horizontal(|ui| {
egui::ComboBox::from_label("")
.selected_text(format!(
"{}",
self.ports[self.port_current_idx].port_name
))
.show_ui(ui, |ui| {
for idx in 0..self.ports.len() {
ui.selectable_value(
&mut self.port_current_idx,
idx,
format!("{}", self.ports[idx].port_name),
);
}
});
let mut msg = "";
if self.enabled == true {
msg = "Отсоединить"
} else {
msg = "Соединить"
}
if ui.button(msg).clicked() {
println!("Button {:?}", self.enabled);
if self.enabled == true {
self.enabled = false;
self.is_connected = true;
} else {
let portname =
self.ports[self.port_current_idx].port_name.as_str();
port_connection_thread_init(tx, portname.to_string());
let res = match rx.recv_timeout(Duration::from_millis(10)).ok()
{
Some(u) => u,
None => 0u8,
};
self.model = res;
// println!("{res}");
if res == 0 {
self.enabled = false;
} else {
self.enabled = true;
self.is_connected = true;
}
}
}
ui.end_row();
});
} else {
ui.label(
RichText::new("Портов не найдено!").color(eframe::epaint::Color32::RED),
);
}
});
});
});
}
}
fn custom_window_frame(
ctx: &egui::Context,
frame: &mut eframe::Frame,
title: &str,
add_contents: impl FnOnce(&mut egui::Ui),
) {
use egui::*;
let text_color = ctx.style().visuals.text_color();
// Height of the title bar
let height = 28.0;
CentralPanel::default()
.frame(Frame::none())
.show(ctx, |ui| {
let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(640.0, 300.0));
// ui.clip_rect();
let painter = ui.painter();
// Paint the frame:
painter.rect(
rect.shrink(0.1),
4.0,
ctx.style().visuals.window_fill(),
// Color32::LIGHT_RED,
Stroke::new(1.0, text_color),
);
// Paint the title:
painter.text(
rect.left_top() + vec2(4.0, height / 2.0),
Align2::LEFT_CENTER,
title,
FontId::proportional(height - 2.0),
text_color,
);
// Paint the line under the title:
painter.line_segment(
[
rect.left_top() + vec2(2.0, height),
rect.right_top() + vec2(-2.0, height),
],
Stroke::new(1.0, text_color),
);
// Add the close button:
let close_response = ui.put(
Rect::from_min_size(rect.right_top() - vec2(height, 0.0), Vec2::splat(height)),
Button::new(RichText::new("❌").size(height - 4.0)).frame(false),
);
if close_response.clicked() {
frame.close();
}
// Interact with the title bar (drag to move window):
let title_bar_rect = {
let mut rect = rect;
rect.max.y = rect.min.y + height;
rect
};
let title_bar_response =
ui.interact(title_bar_rect, Id::new("title_bar"), Sense::click());
if title_bar_response.is_pointer_button_down_on() {
frame.drag_window();
}
// Add the contents:
let content_rect = {
let mut rect = rect;
rect.min.y = title_bar_rect.max.y;
rect
}
.shrink(4.0);
let mut content_ui = ui.child_ui(content_rect, *ui.layout());
add_contents(&mut content_ui);
});
}
fn port_working_thread_init(
sender: Sender<MK_AnalogData>,
portname: String,
enabled: Receiver<bool>,
) -> ThreadId {
println!("{}", portname);
let res = thread::Builder::new()
.name("Connect to COM".to_string())
.spawn(move || {
//тут ждать пока придёт сигнал от кнопки "подключиться"
println!("Попытка соединиться ");
let port_name = portname;
let port = serialport::new(&port_name, 9600)
.timeout(Duration::from_millis(100))
.open();
let mut port = match port {
Ok(port) => port,
Err(_) => {
eprintln!("Порт \"{}\" занят другой программой", port_name);
panic!()
}
};
println!("Соединились");
let limits = get_mk_limits(&mut port);
println!("Limits: {limits:?}");
loop {
let en = enabled.try_recv().ok();
match en {
Some(en1) => {
println!("some {:?}", en1);
if en1 == false {
break;
}
}
None => (),
};
// if en == false {
// break;
// }
// let data = get_mk_analog_data(&mut port);
// println!("Analog = {data:?}");
// sender.send(data).ok();
// let mode = get_mk_current_mode(&mut port);
// println!("Mode = {mode:?}");
// for i in 0..=9 {
// let read_mode_data = get_mk_mode_data(&mut port, i);
// println!("Mode{i} = {read_mode_data:?}");
// }
thread::sleep(Duration::from_millis(100));
}
})
.unwrap();
let id = res.thread().id();
id
}
fn port_connection_thread_init(sender: Sender<u8>, portname: String) {
println!("{}", portname);
let res = thread::Builder::new()
.name("Connect to COM".to_string())
.spawn(move || {
//тут ждать пока придёт сигнал от кнопки "подключиться"
println!("Попытка соединиться ");
let port_name = portname;
let port = serialport::new(&port_name, 9600)
.timeout(Duration::from_millis(10))
.open();
let mut port = match port {
Ok(port) => port,
Err(_) => {
eprintln!("Порт \"{}\" занят другой программой", port_name);
panic!()
}
};
println!("Соединились");
let model = get_mk_model(&mut port);
println!("model = {:?}", model);
sender.send(model).ok();
})
.unwrap();
res.join().ok();
println!("Thread has stopped");
}
fn get_mk_data(port: &mut Box<dyn SerialPort + 'static>, cmd: &[u8]) -> [u8; 65] {
port.write_all(cmd).unwrap();
thread::sleep(Duration::from_millis(100));
let mut serial_buf = [0u8; 65];
port.read(&mut serial_buf).ok();
port.clear(ClearBuffer::Input).ok();
serial_buf
}
fn get_mk_model(port: &mut Box<dyn SerialPort + 'static>) -> u8 {
let data = get_mk_data(port, b"M");
let res = 10 * data[0] as u8 + data[1] as u8;
res
}
fn get_mk_analog_data(port: &mut Box<dyn SerialPort + 'static>) -> MK_AnalogData {
let data = get_mk_data(port, b"A");
#[repr(C)]
union Union {
data: MK_AnalogData,
raw: [u8; 65],
}
let mut mk_data = unsafe { std::mem::zeroed::<Union>() };
mk_data.raw = data;
let res = unsafe { mk_data.data };
res
}
fn get_mk_limits(port: &mut Box<dyn SerialPort + 'static>) -> MK_Limits {
let data = get_mk_data(port, b"B");
#[repr(C)]
union Union {
data: MK_Limits,
raw: [u8; 65],
}
let mut mk_data = unsafe { std::mem::zeroed::<Union>() };
mk_data.raw = data;
let res = unsafe { mk_data.data };
res
}
fn get_mk_current_mode(port: &mut Box<dyn SerialPort + 'static>) -> u8 {
let data = get_mk_data(port, b"R");
let res = data[0];
res
}
fn get_mk_mode_data(port: &mut Box<dyn SerialPort + 'static>, mode: u8) -> MK_ModeData {
let mut cmd: [u8; 1] = [0u8; 1];
if (mode >= 0) && (mode <= 9) {
cmd[0] = mode + 0x030;
} else {
cmd[0] = 0;
}
let data = get_mk_data(port, &cmd);
#[repr(C)]
union Union {
data: MK_ModeData,
raw: [u8; 65],
}
let mut mk_data = unsafe { std::mem::zeroed::<Union>() };
mk_data.raw = data;
let res = unsafe { mk_data.data };
res
}

@ -0,0 +1,30 @@
// CollapsingHeader::new("Соединение:").default_open(false).show(ui,|ui|{
ui.horizontal(|ui| {
ui.group(|ui| {
ui.vertical(|ui| {
ui.label("Соединение:");
if self.ports.len() > 0 {
egui::ComboBox::from_label("Выбрать COM порт")
.selected_text(format!(
"{}",
self.ports[self.port_current_idx].port_name
))
.show_ui(ui, |ui| {
for idx in 0..self.ports.len() {
ui.selectable_value(
&mut self.port_current_idx,
idx,
format!("{}", self.ports[idx].port_name),
);
}
});
ui.end_row();
} else {
ui.label(
RichText::new("Портов не найдено!")
.color(eframe::epaint::Color32::RED),
);
}
});
});
});
Loading…
Cancel
Save