Buildable again
This commit is contained in:
parent
efbad0d676
commit
5b8ffc8dd9
7 changed files with 73 additions and 93 deletions
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"workbench.colorCustomizations": {
|
||||||
|
"activityBar.background": "#0E3502",
|
||||||
|
"titleBar.activeBackground": "#144B03",
|
||||||
|
"titleBar.activeForeground": "#F1FEED"
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,4 +17,6 @@ path = "src/gui.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serialport = "*"
|
serialport = "*"
|
||||||
gtk = { version = "*", features = ["v3_24"] }
|
gtk = { version = "*", features = ["v3_24"] }
|
||||||
utilities = { git = "http://dimov.cloud:80/hodasemi/Context.git" }
|
anyhow = "1.0"
|
||||||
|
serde = { version = "*", features = ["derive"] }
|
||||||
|
serde_json = "*"
|
38
src/gui.rs
38
src/gui.rs
|
@ -2,7 +2,7 @@ use gtk;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Builder, Window};
|
use gtk::{Builder, Window};
|
||||||
|
|
||||||
use utilities::prelude::*;
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -11,10 +11,10 @@ mod shared;
|
||||||
|
|
||||||
use shared::config::{Config, COMMAND_COUNT};
|
use shared::config::{Config, COMMAND_COUNT};
|
||||||
|
|
||||||
fn main() -> VerboseResult<()> {
|
fn main() -> Result<()> {
|
||||||
gtk::init().map_err(|_| "failed to initialize GTK")?;
|
gtk::init()?;
|
||||||
|
|
||||||
let builder = Builder::new_from_string(include_str!("../macro_ui.glade"));
|
let builder = Builder::from_string(include_str!("../macro_ui.glade"));
|
||||||
|
|
||||||
setup_gui(builder)?;
|
setup_gui(builder)?;
|
||||||
|
|
||||||
|
@ -23,15 +23,15 @@ fn main() -> VerboseResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_gui(builder: Builder) -> VerboseResult<()> {
|
fn setup_gui(builder: Builder) -> Result<()> {
|
||||||
let config = load_config()?;
|
let config = load_config()?;
|
||||||
|
|
||||||
apply_config(&builder, &config)?;
|
apply_config(&builder, &config)?;
|
||||||
setup_save(&builder)?;
|
setup_save(&builder)?;
|
||||||
|
|
||||||
let window: Window = builder
|
let window: Window = builder
|
||||||
.get_object("MainWindow")
|
.object("MainWindow")
|
||||||
.ok_or("failed getting main window")?;
|
.ok_or(anyhow!("failed getting main window"))?;
|
||||||
|
|
||||||
window.show_all();
|
window.show_all();
|
||||||
|
|
||||||
|
@ -44,8 +44,8 @@ fn setup_gui(builder: Builder) -> VerboseResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_config() -> VerboseResult<Config> {
|
fn load_config() -> Result<Config> {
|
||||||
let home_dir = std::env::var("HOME").map_err(|_| "failed getting home directory")?;
|
let home_dir = std::env::var("HOME")?;
|
||||||
|
|
||||||
if !Path::new(&format!("{}/.config", home_dir)).exists() {
|
if !Path::new(&format!("{}/.config", home_dir)).exists() {
|
||||||
fs::create_dir(format!("{}/.config", home_dir))?;
|
fs::create_dir(format!("{}/.config", home_dir))?;
|
||||||
|
@ -58,26 +58,26 @@ fn load_config() -> VerboseResult<Config> {
|
||||||
Config::open(&format!("{}/.config/MacroBoard/commands.cfg", home_dir))
|
Config::open(&format!("{}/.config/MacroBoard/commands.cfg", home_dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_save(builder: &Builder) -> VerboseResult<()> {
|
fn setup_save(builder: &Builder) -> Result<()> {
|
||||||
let mut text_fields = Vec::with_capacity(COMMAND_COUNT);
|
let mut text_fields = Vec::with_capacity(COMMAND_COUNT);
|
||||||
|
|
||||||
for i in 0..COMMAND_COUNT {
|
for i in 0..COMMAND_COUNT {
|
||||||
let command_text_field: gtk::Entry = builder
|
let command_text_field: gtk::Entry = builder
|
||||||
.get_object(&format!("ButtonCommand{}", i + 1))
|
.object(&format!("ButtonCommand{}", i + 1))
|
||||||
.ok_or(format!("Failed getting Entry for command {}", i + 1))?;
|
.ok_or(anyhow!("Failed getting Entry for command {}", i + 1))?;
|
||||||
|
|
||||||
text_fields.push(command_text_field);
|
text_fields.push(command_text_field);
|
||||||
}
|
}
|
||||||
|
|
||||||
let save_button: gtk::Button = builder
|
let save_button: gtk::Button = builder
|
||||||
.get_object("SaveButton")
|
.object("SaveButton")
|
||||||
.ok_or("Failed getting save button")?;
|
.ok_or(anyhow!("Failed getting save button"))?;
|
||||||
|
|
||||||
save_button.connect_clicked(move |_| {
|
save_button.connect_clicked(move |_| {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
|
|
||||||
for i in 0..COMMAND_COUNT {
|
for i in 0..COMMAND_COUNT {
|
||||||
let command = text_fields[i].get_buffer().get_text();
|
let command = text_fields[i].buffer().text();
|
||||||
|
|
||||||
config.commands[i] = Some(command);
|
config.commands[i] = Some(command);
|
||||||
}
|
}
|
||||||
|
@ -98,14 +98,14 @@ fn setup_save(builder: &Builder) -> VerboseResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_config(builder: &Builder, config: &Config) -> VerboseResult<()> {
|
fn apply_config(builder: &Builder, config: &Config) -> Result<()> {
|
||||||
for i in 0..COMMAND_COUNT {
|
for i in 0..COMMAND_COUNT {
|
||||||
if let Some(command) = &config.commands[i] {
|
if let Some(command) = &config.commands[i] {
|
||||||
let command_text_field: gtk::Entry = builder
|
let command_text_field: gtk::Entry = builder
|
||||||
.get_object(&format!("ButtonCommand{}", i + 1))
|
.object(&format!("ButtonCommand{}", i + 1))
|
||||||
.ok_or(format!("Failed getting Entry for command {}", i + 1))?;
|
.ok_or(anyhow!("Failed getting Entry for command {}", i + 1))?;
|
||||||
|
|
||||||
command_text_field.get_buffer().set_text(command);
|
command_text_field.buffer().set_text(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
mod service_specific;
|
mod service_specific;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use service_specific::*;
|
use service_specific::*;
|
||||||
use utilities::prelude::*;
|
|
||||||
|
|
||||||
fn main() -> VerboseResult<()> {
|
fn main() -> Result<()> {
|
||||||
let mut port = Port::open(SerialPortSettings {
|
let mut port = Port::open(SerialPortSettings {
|
||||||
baud_rate: 9600,
|
baud_rate: 9600,
|
||||||
data_bits: DataBits::Eight,
|
data_bits: DataBits::Eight,
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl MessageBuilder {
|
||||||
if !self.complete {
|
if !self.complete {
|
||||||
match msg.chars().position(|c| c == MESSAGE_END) {
|
match msg.chars().position(|c| c == MESSAGE_END) {
|
||||||
Some(position) => {
|
Some(position) => {
|
||||||
let mut remaining_msg = msg.split_off(position);
|
let remaining_msg = msg.split_off(position);
|
||||||
|
|
||||||
self.message.push_str(&msg);
|
self.message.push_str(&msg);
|
||||||
self.complete = true;
|
self.complete = true;
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use serialport;
|
use anyhow::{anyhow, Result};
|
||||||
use serialport::prelude::*;
|
|
||||||
|
|
||||||
use utilities::prelude::*;
|
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
pub use serialport::{DataBits, FlowControl, Parity, SerialPortSettings, StopBits};
|
pub use serialport::{DataBits, FlowControl, Parity, SerialPort, StopBits};
|
||||||
pub use std::time::Duration;
|
pub use std::time::Duration;
|
||||||
|
|
||||||
pub enum SerialReadResult {
|
pub enum SerialReadResult {
|
||||||
|
@ -13,60 +10,68 @@ pub enum SerialReadResult {
|
||||||
Timeout,
|
Timeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SerialPortSettings {
|
||||||
|
pub baud_rate: u32,
|
||||||
|
pub data_bits: DataBits,
|
||||||
|
pub parity: Parity,
|
||||||
|
pub stop_bits: StopBits,
|
||||||
|
pub flow_control: FlowControl,
|
||||||
|
pub timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Port {
|
pub struct Port {
|
||||||
serial_port: Box<dyn SerialPort>,
|
serial_port: Box<dyn SerialPort>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Port {
|
impl Port {
|
||||||
pub fn open(settings: SerialPortSettings) -> VerboseResult<Self> {
|
pub fn open(settings: SerialPortSettings) -> Result<Self> {
|
||||||
let port_path = Self::find_macroboard()?;
|
let port_path = Self::find_macroboard()?;
|
||||||
|
|
||||||
let port = serialport::open_with_settings(&port_path, &settings).map_err(|err| {
|
let port = serialport::new(port_path, settings.baud_rate)
|
||||||
format!(
|
.data_bits(settings.data_bits)
|
||||||
"could not open serial port ({:?}, {}, {})",
|
.parity(settings.parity)
|
||||||
err.kind(),
|
.stop_bits(settings.stop_bits)
|
||||||
err,
|
.flow_control(settings.flow_control)
|
||||||
port_path
|
.timeout(settings.timeout)
|
||||||
)
|
.open()?;
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(Port { serial_port: port })
|
Ok(Port { serial_port: port })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn read(&mut self) -> VerboseResult<SerialReadResult> {
|
pub fn read(&mut self) -> Result<SerialReadResult> {
|
||||||
let mut buf: Vec<u8> = (0..255).collect();
|
let mut buf: Vec<u8> = (0..255).collect();
|
||||||
|
|
||||||
match self.serial_port.read(&mut buf[..]) {
|
match self.serial_port.read(&mut buf[..]) {
|
||||||
Ok(t) => Ok(SerialReadResult::Message(
|
Ok(t) => Ok(SerialReadResult::Message(
|
||||||
String::from_utf8(buf[0..t].to_vec())
|
String::from_utf8(buf[0..t].to_vec())
|
||||||
.map_err(|err| format!("failed converting utf8 buffer ({})", err))?,
|
.map_err(|err| anyhow!("failed converting utf8 buffer ({})", err))?,
|
||||||
)),
|
)),
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => Ok(SerialReadResult::Timeout),
|
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => Ok(SerialReadResult::Timeout),
|
||||||
Err(err) => create_error!(format!("failed reading serial port ({})", err)),
|
Err(err) => Err(anyhow!("failed reading serial port ({})", err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn write(&mut self, msg: &str) -> VerboseResult<()> {
|
pub fn write(&mut self, msg: &str) -> Result<()> {
|
||||||
self.serial_port
|
self.serial_port
|
||||||
.write(msg.as_bytes())
|
.write(msg.as_bytes())
|
||||||
.map_err(|err| format!("failed writing to serial port ({})", err))?;
|
.map_err(|err| anyhow!("failed writing to serial port ({})", err))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_macroboard() -> VerboseResult<String> {
|
fn find_macroboard() -> Result<String> {
|
||||||
let available_ports = serialport::available_ports()
|
let available_ports = serialport::available_ports()
|
||||||
.map_err(|err| format!("error querying serial ports ( {})", err))?;
|
.map_err(|err| anyhow!("error querying serial ports ( {})", err))?;
|
||||||
|
|
||||||
for available_port in available_ports.iter() {
|
for available_port in available_ports.iter() {
|
||||||
if let serialport::SerialPortType::UsbPort(usb_info) = &available_port.port_type {
|
if let serialport::SerialPortType::UsbPort(usb_info) = &available_port.port_type {
|
||||||
// check for the correct device
|
// check for the correct device
|
||||||
if let Some(product_name) = &usb_info.product {
|
if let Some(product_name) = &usb_info.product {
|
||||||
if product_name == "FT232R_USB_UART"
|
if product_name == "FT232R_USB_UART"
|
||||||
&& usb_info.vid == 1027
|
&& usb_info.vid == 0x0403
|
||||||
&& usb_info.pid == 24577
|
&& usb_info.pid == 0x6001
|
||||||
{
|
{
|
||||||
return Ok(available_port.port_name.clone());
|
return Ok(available_port.port_name.clone());
|
||||||
}
|
}
|
||||||
|
@ -74,6 +79,6 @@ impl Port {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_error!("macro board not found on usb bus")
|
return Err(anyhow!("macro board not found on usb bus".to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +1,27 @@
|
||||||
use utilities::prelude::*;
|
use anyhow::Result;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use std::{fs::File, io::Write};
|
||||||
|
|
||||||
pub const COMMAND_COUNT: usize = 8;
|
pub const COMMAND_COUNT: usize = 8;
|
||||||
|
|
||||||
const COMMANDS_TAG: &str = "Commands";
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
const COMMANDS: [&str; COMMAND_COUNT] = [
|
|
||||||
"command_1",
|
|
||||||
"command_2",
|
|
||||||
"command_3",
|
|
||||||
"command_4",
|
|
||||||
"command_5",
|
|
||||||
"command_6",
|
|
||||||
"command_7",
|
|
||||||
"command_8",
|
|
||||||
];
|
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub commands: [Option<String>; COMMAND_COUNT],
|
pub commands: [Option<String>; COMMAND_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn open(path: &str) -> VerboseResult<Config> {
|
pub fn open(path: &str) -> Result<Config> {
|
||||||
let mut config = Config::default();
|
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
|
||||||
|
|
||||||
let config_loader = match ConfigHandler::read_config(path) {
|
|
||||||
Ok(config_loader) => config_loader,
|
|
||||||
Err(_) => return Ok(config),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(commands) = config_loader.get(COMMANDS_TAG) {
|
|
||||||
for i in 0..COMMAND_COUNT {
|
|
||||||
if let Some(command) = commands.get(COMMANDS[i]) {
|
|
||||||
config.commands[i] = Some(command.to_value()?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(config)
|
pub fn save(&self, path: &str) -> Result<()> {
|
||||||
}
|
let mut file = File::create(path)?;
|
||||||
|
let s = serde_json::to_string(self)?;
|
||||||
|
|
||||||
pub fn save(&self, path: &str) -> VerboseResult<()> {
|
write!(file, "{}", s)?;
|
||||||
let fields = (0..COMMAND_COUNT)
|
|
||||||
.collect::<Vec<usize>>()
|
|
||||||
.iter()
|
|
||||||
.map(|i| {
|
|
||||||
(
|
|
||||||
COMMANDS[*i],
|
|
||||||
match &self.commands[*i] {
|
|
||||||
Some(command) => Value::from_value(command),
|
|
||||||
None => Value::empty(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let data = &[(COMMANDS_TAG, fields)];
|
Ok(())
|
||||||
|
|
||||||
ConfigHandler::write_config(path, data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue