Skip to content

Commit

Permalink
Merge pull request #2 from rincewound/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
rincewound authored Dec 10, 2024
2 parents 021eccc + 3ffc78f commit fa8f7b9
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 46 deletions.
11 changes: 6 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ Normal --> Analyzer

* esc - Change to normal mode
* F2 - Change display mode
* F3 - Change input mode
* F10 - Clear history

### Normal Mode
Expand All @@ -55,17 +54,19 @@ Normal --> Analyzer

### Interactive

* Up - Scroll Receive Buffer Up
* Down - Scroll Receive Buffer Down
* PageUp - Scroll Receive Buffer Up
* PageDown - Scroll Receive Buffer Down
* Enter - Send current send buffer (according to CRLF and Input settings)
* F3 - Change input mode
* F4 - Change CRLF mode
* F5 - Retain input (don't clear TX after sending)

### Analyzer

_Note_: Analyzer Features are only available in display HEX mode.

* Up/Down - Scroll Receive buffer
* Left/Right - Move analyzer cursor
* PageUp/PageDown - Scroll Receive buffer
* Up/Down/Left/Right - Move analyzer cursor
* e - switch endianness

## Display modes
Expand Down
26 changes: 22 additions & 4 deletions src/analyzer_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub struct AnalyzerMode {
active: bool,
display_history: Vec<SerialStateMessage>,
scroll_offset: u32,
analyzer_cursor_line: usize,
analyzer_cursor_pos: usize,
analyzer_endianness: Endianness,
active_display_mode: DisplayMode,
Expand All @@ -47,20 +48,29 @@ impl AnalyzerMode {
active: false,
display_history: vec![],
scroll_offset: 0,
analyzer_cursor_line: 0,
analyzer_cursor_pos: 0,
analyzer_endianness: Endianness::Little,
active_display_mode: DisplayMode::Hex,
}
}

pub(crate) fn add_to_history(&mut self, arg: &str) {
let msg = SerialStateMessage::ErrorEvent(arg.to_string());
self.display_history.push(msg);
}
}

impl ApplicationMode for AnalyzerMode {
fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {
match key_event.code {
KeyCode::Left => self.cursor_left(),
KeyCode::Right => self.cursor_right(),
KeyCode::Up => self.scroll_up(),
KeyCode::Down => self.scroll_down(),
KeyCode::Up => self.scroll_analyzer_cursor_up(),
KeyCode::Down => self.scroll_analyzer_cursor_down(),
KeyCode::PageUp => self.scroll_up(),
KeyCode::PageDown => self.scroll_down(),

//KeyCode::F(2) => self.settingsmode.rotate_display_mode(),
KeyCode::Char('e') => self.rotate_analyzer_endianness(),
_ => {}
Expand Down Expand Up @@ -100,6 +110,14 @@ impl AnalyzerMode {
self.scroll_offset = self.scroll_offset.saturating_add(1);
}

pub fn scroll_analyzer_cursor_up(&mut self) {
self.analyzer_cursor_line += 1;
}

pub fn scroll_analyzer_cursor_down(&mut self) {
self.analyzer_cursor_line = self.analyzer_cursor_line.saturating_sub(1);
}

/// Scroll down in the display history, moving the top line up by one.
pub fn scroll_down(&mut self) {
self.scroll_offset = self.scroll_offset.saturating_sub(1);
Expand Down Expand Up @@ -224,7 +242,7 @@ impl AnalyzerMode {
let mut post_cursor_color = ratatui::style::Color::Black;

if self.active_display_mode == DisplayMode::Hex
&& line_index == 0
&& line_index == self.analyzer_cursor_line
&& self.active
{
// the cursor pos is always a multiple of 3:
Expand Down Expand Up @@ -298,7 +316,7 @@ impl AnalyzerMode {
let mut items: Vec<String> = vec![];
// Use the cursor position to obtain the analyzer data: 1 byte, 2 byte, 4 bytes
let one_byte = analyzer_data[self.analyzer_cursor_pos];
items.push(format!("binary {:08b}", one_byte));
items.push(format!("binary (MSB first): {:08b}", one_byte));
items.push(format!("u8: {}", one_byte));

if (self.analyzer_cursor_pos as i32) <= (analyzer_data.len() as i32 - 2) {
Expand Down
50 changes: 34 additions & 16 deletions src/interactive_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,15 @@ pub struct InteractiveMode {
input_mode: InputMode,
command_sender: Sender<SerialCommand>,
crlf: CRLFSetting,
retain_input: bool,
}

impl ApplicationMode for InteractiveMode {
fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {
match key_event.code {
KeyCode::F(4) => self.rotate_crlf_setting(),
KeyCode::F(3) => self.rotate_input_mode(),
KeyCode::F(5) => self.toggle_retain_input(),
KeyCode::Char(x) => {
if self.input_mode == InputMode::Hex {
if x.is_ascii_hexdigit() || x == ' ' {
Expand Down Expand Up @@ -101,7 +104,10 @@ impl ApplicationMode for InteractiveMode {
format!(": {} ", self.input_mode).fg(ratatui::style::Color::Gray),
format!("CRLF").fg(ratatui::style::Color::Gray),
"(F4)".fg(highlight_color),
format!(": {}", self.crlf).fg(ratatui::style::Color::Gray),
format!(": {} ", self.crlf).fg(ratatui::style::Color::Gray),
format!("Retain Input").fg(ratatui::style::Color::Gray),
"(F5)".fg(highlight_color),
format!(": {} ", self.retain_input).fg(ratatui::style::Color::Gray),
]);

let block = Block::bordered()
Expand All @@ -127,6 +133,7 @@ impl InteractiveMode {
input_mode: InputMode::Default,
command_sender,
crlf: CRLFSetting::None,
retain_input: false,
}
}

Expand All @@ -152,51 +159,57 @@ impl InteractiveMode {
}

/// if hex input, convert data to bytes by aggregating 2 hex chars into one byte
fn apply_input_mode(&mut self) {
fn apply_input_mode(&mut self, input: Vec<u8>) -> Vec<u8> {
// if hex input, convert data to bytes by aggregating 2 hex chars into one byte
if self.input_mode == InputMode::Hex {
let mut new_buffer: Vec<u8> = vec![];
let mut idx: usize = 0;
while idx < self.send_buffer.len() - 1 {
if self.send_buffer[idx] as char == ' ' {
while idx < input.len() - 1 {
if input[idx] as char == ' ' {
idx += 1;
continue;
}
let b0 = (self.send_buffer[idx] as char).to_digit(16).unwrap() as u8;
let b1 = (self.send_buffer[idx + 1] as char).to_digit(16).unwrap() as u8;
let b0 = (input[idx] as char).to_digit(16).unwrap() as u8;
let b1 = (input[idx + 1] as char).to_digit(16).unwrap() as u8;
new_buffer.push(self.two_hex_bytes_to_char(b0, b1) as u8);
idx += 2;
}
self.send_buffer = new_buffer;
return new_buffer;
}
input.clone()
}

/// Adds the necessary CRLF bytes to the send buffer according to the
/// current CRLF setting.
fn apply_crlf_setting(&mut self) {
fn apply_crlf_setting(&mut self, input: Vec<u8>) -> Vec<u8> {
let mut res = input.clone();
match self.crlf {
CRLFSetting::CRLF => {
self.send_buffer.push(b'\r');
self.send_buffer.push(b'\n');
res.push(b'\r');
res.push(b'\n');
}
CRLFSetting::LF => {
self.send_buffer.push(b'\n');
res.push(b'\n');
}
CRLFSetting::CR => {
self.send_buffer.push(b'\r');
res.push(b'\r');
}
_ => {}
}
return res;
}

/// Sends the contents of the `send_buffer` to the serial port.
/// The contents of `send_buffer` are processed according to the current
/// `input_mode` and `crlf` settings before being sent.
fn send_tx_buffer(&mut self) {
self.apply_input_mode();
self.apply_crlf_setting();
self.send_command(SerialCommand::Send(self.send_buffer.clone()));
self.send_buffer.clear();
let mut the_buffer = self.send_buffer.clone();
the_buffer = self.apply_input_mode(the_buffer);
the_buffer = self.apply_crlf_setting(the_buffer);
self.send_command(SerialCommand::Send(the_buffer));
if !self.retain_input {
self.send_buffer.clear();
}
}

/// Rotates the input mode through the list of available input modes. The input modes
Expand All @@ -210,6 +223,7 @@ impl InteractiveMode {
selected_idx += 1;
selected_idx %= INPUT_MODES.len();
self.input_mode = INPUT_MODES[selected_idx];
self.send_buffer.clear();
}

/// Rotates the CRLF setting.
Expand All @@ -232,4 +246,8 @@ impl InteractiveMode {
fn send_command(&self, cmd: SerialCommand) {
self.command_sender.send(cmd).unwrap();
}

fn toggle_retain_input(&mut self) {
self.retain_input = !self.retain_input;
}
}
21 changes: 12 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,15 @@ impl Default for App {
let (stx, rtx): (Sender<SerialStateMessage>, Receiver<SerialStateMessage>) =
mpsc::channel();
let (tx, rx): (Sender<SerialCommand>, Receiver<SerialCommand>) = mpsc::channel();
let settings = settings_mode::SettingsMode::new();
portthread::port_background_thread(rx, stx);

let data = App {
mode: Mode::Normal,
exit: false,
command_sender: tx.clone(),
state_receiver: rtx,
settingsmode: settings_mode::SettingsMode::new(),
settingsmode: settings,
analyzermode: analyzer_mode::AnalyzerMode::new(),
interactivemode: interactive_mode::InteractiveMode::new(tx),
};
Expand Down Expand Up @@ -147,8 +148,8 @@ impl App {
fn do_interactive_mode(&mut self, key_event: KeyEvent) {
self.interactivemode.handle_key_event(key_event);
match key_event.code {
KeyCode::Up => self.analyzermode.scroll_up(),
KeyCode::Down => self.analyzermode.scroll_down(),
KeyCode::PageUp => self.analyzermode.scroll_up(),
KeyCode::PageDown => self.analyzermode.scroll_down(),
_ => {}
}
}
Expand Down Expand Up @@ -180,9 +181,6 @@ impl App {
if key_event.code == KeyCode::F(2) {
self.settingsmode.rotate_display_mode();
}
if key_event.code == KeyCode::F(3) {
self.interactivemode.rotate_input_mode();
}

if key_event.code == KeyCode::F(10) {
self.analyzermode.clear_history();
Expand Down Expand Up @@ -244,9 +242,14 @@ impl App {
/// This function will panic if there is a failure in opening the serial port or setting
/// the read/write timeouts.
fn enter_interactive_mode(&mut self) {
let ctx = self.settingsmode.create_serial_context();
self.send_command(SerialCommand::Start(ctx));
self.enable_mode(mode::Mode::Interactive);
if let Ok(ctx) = self.settingsmode.create_serial_context() {
self.send_command(SerialCommand::Stop);
self.send_command(SerialCommand::Start(ctx));
self.enable_mode(mode::Mode::Interactive);
} else {
self.analyzermode
.add_to_history("Failed to open serial port");
}
}

/// Exits the current mode and enters the settings mode, which is a mode where the user can adjust
Expand Down
6 changes: 6 additions & 0 deletions src/portthread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ use std::{
thread, vec,
};

pub enum PortError {
BadSettings,
FailedToFlush,
FailedToOpen,
}

#[derive(Debug)]
pub struct SerialContext {
port_name: String,
Expand Down
32 changes: 20 additions & 12 deletions src/settings_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use ratatui::{

use crate::{
mode::ApplicationMode,
portthread::SerialContext,
portthread::{PortError, SerialContext},
serialtypes::{BAUD_RATES, DATABITS, PARITY, STOP_BITS},
DisplayMode, DISPLAY_MODES,
};
Expand All @@ -35,7 +35,6 @@ impl ApplicationMode for SettingsMode {
KeyCode::Char('a') => self.rotate_parity(),
KeyCode::Char('d') => self.rotate_databits(),
KeyCode::Char('m') => self.rotate_display_mode(),
//KeyCode::Enter => app.enter_interactive_mode(),
_ => {}
}
}
Expand Down Expand Up @@ -98,7 +97,7 @@ impl SettingsMode {
self.display_mode
}

pub fn create_serial_context(&self) -> SerialContext {
pub fn create_serial_context(&self) -> Result<SerialContext, PortError> {
let the_port = serial2::SerialPort::open(&self.port, |mut settings: serial2::Settings| {
let _ = settings.set_baud_rate(self.baud);
let stop_bits = match self.stopbits {
Expand Down Expand Up @@ -137,15 +136,24 @@ impl SettingsMode {
Ok(settings)
});

let mut p = the_port.expect("Failed to open port with given settings.");
p.set_read_timeout(Duration::from_millis(125))
.expect("Failed to set read timeout.");
p.set_write_timeout(Duration::from_millis(2500))
.expect("Failed to set write timeout.");
p.flush().expect("Failed to flush port.");
let _ = p.discard_buffers();
let ctx = SerialContext::new(self.port.clone(), p);
ctx
if let Ok(mut p) = the_port {
if let Err(_) = p.set_read_timeout(Duration::from_millis(125)) {
return Err(PortError::BadSettings);
}
if let Err(_) = p.set_write_timeout(Duration::from_millis(2500)) {
return Err(PortError::BadSettings);
}
if let Err(_) = p.flush() {
return Err(PortError::FailedToFlush);
}
if let Err(_) = p.discard_buffers() {
return Err(PortError::BadSettings);
}

return Ok(SerialContext::new(self.port.clone(), p));
} else {
return Err(PortError::FailedToOpen);
}
}

/// Rotates the serial port to the next available serial port.
Expand Down

0 comments on commit fa8f7b9

Please sign in to comment.