use std::fmt;
use chrono::Local;
use probe_rs_rtt::{DownChannel, UpChannel};
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
pub enum DataFormat {
String,
BinaryLE,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ChannelConfig {
pub up: Option<usize>,
pub down: Option<usize>,
pub name: Option<String>,
pub format: DataFormat,
}
#[derive(Debug)]
pub struct ChannelState {
up_channel: Option<UpChannel>,
down_channel: Option<DownChannel>,
name: String,
format: DataFormat,
messages: Vec<String>,
data: Vec<u8>,
last_line_done: bool,
input: String,
scroll_offset: usize,
rtt_buffer: RttBuffer,
show_timestamps: bool,
}
impl ChannelState {
pub fn new(
up_channel: Option<UpChannel>,
down_channel: Option<DownChannel>,
name: Option<String>,
show_timestamps: bool,
format: DataFormat,
) -> Self {
let name = name
.or_else(|| up_channel.as_ref().and_then(|up| up.name().map(Into::into)))
.or_else(|| {
down_channel
.as_ref()
.and_then(|down| down.name().map(Into::into))
})
.unwrap_or_else(|| "Unnamed channel".to_owned());
Self {
up_channel,
down_channel,
name,
format,
messages: Vec::new(),
last_line_done: true,
input: String::new(),
scroll_offset: 0,
rtt_buffer: RttBuffer([0u8; 1024]),
show_timestamps,
data: Vec::new(),
}
}
pub fn has_down_channel(&self) -> bool {
self.down_channel.is_some()
}
pub fn messages(&self) -> &Vec<String> {
&self.messages
}
pub fn input(&self) -> &str {
&self.input
}
pub fn input_mut(&mut self) -> &mut String {
&mut self.input
}
pub fn scroll_offset(&self) -> usize {
self.scroll_offset
}
pub fn scroll_up(&mut self) {
self.scroll_offset += 1;
}
pub fn scroll_down(&mut self) {
if self.scroll_offset > 0 {
self.scroll_offset -= 1;
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn format(&self) -> DataFormat {
self.format
}
pub fn set_scroll_offset(&mut self, value: usize) {
self.scroll_offset = value;
}
pub fn data(&self) -> &Vec<u8> {
&self.data
}
pub fn poll_rtt(&mut self) {
let count = if let Some(channel) = self.up_channel.as_mut() {
match channel.read(self.rtt_buffer.0.as_mut()) {
Ok(count) => count,
Err(err) => {
eprintln!("\nError reading from RTT: {}", err);
return;
}
}
} else {
0
};
if count == 0 {
return;
}
match self.format {
DataFormat::String => {
let now = Local::now();
let mut incoming = String::from_utf8_lossy(&self.rtt_buffer.0[..count]).to_string();
let last_line_done = self.last_line_done;
if !last_line_done {
if let Some(last_line) = self.messages.pop() {
incoming = last_line + &incoming;
}
}
self.last_line_done = incoming.ends_with('\n');
for (i, line) in incoming.split_terminator('\n').enumerate() {
if self.show_timestamps && (last_line_done || i > 0) {
let ts = now.format("%H:%M:%S%.3f");
self.messages.push(format!("{} {}", ts, line));
} else {
self.messages.push(line.to_string());
}
if self.scroll_offset != 0 {
self.scroll_offset += 1;
}
}
}
DataFormat::BinaryLE => {
self.data.extend_from_slice(&self.rtt_buffer.0[..count]);
}
};
}
pub fn push_rtt(&mut self) {
if let Some(down_channel) = self.down_channel.as_mut() {
self.input += "\n";
down_channel.write(&self.input.as_bytes()).unwrap();
self.input.clear();
}
}
}
struct RttBuffer([u8; 1024]);
impl fmt::Debug for RttBuffer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}