Update core modules and add GUI panels
This commit is contained in:
@@ -1,83 +1,149 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use xserial_core::frame::{
|
||||
cobs::CobsFramer, fixed::FixedLengthFramer,
|
||||
Endian, Framer,
|
||||
cobs::CobsFramer,
|
||||
fixed::FixedLengthFramer,
|
||||
length::{LengthConfig, LengthPrefixedFramer},
|
||||
line::{LineConfig, LineFramer}, Endian, Framer,
|
||||
line::{LineConfig, LineFramer},
|
||||
};
|
||||
use xserial_core::protocol::{
|
||||
ProtocolDecoder,
|
||||
hex::{HexConfig, HexDecoder},
|
||||
plot::{PlotConfig, PlotDecoder, PlotFormat, SampleType},
|
||||
text::{TextDecoder, TextEncoding}, ProtocolDecoder,
|
||||
text::{TextDecoder, TextEncoding},
|
||||
};
|
||||
use xserial_core::transport::TransportConfig;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum FramerConfig {
|
||||
Line {
|
||||
#[serde(default = "default_true")] strip_cr: bool,
|
||||
#[serde(default = "default_max_line")] max_line_len: usize,
|
||||
#[serde(default = "default_true")]
|
||||
strip_cr: bool,
|
||||
#[serde(default = "default_max_line")]
|
||||
max_line_len: usize,
|
||||
},
|
||||
Fixed {
|
||||
frame_len: usize,
|
||||
},
|
||||
Fixed { frame_len: usize },
|
||||
Length {
|
||||
#[serde(default = "default_len_bytes")] len_bytes: usize,
|
||||
#[serde(default)] endian: Endian,
|
||||
#[serde(default)] length_includes_self: bool,
|
||||
#[serde(default = "default_max_payload")] max_payload: usize,
|
||||
#[serde(default = "default_len_bytes")]
|
||||
len_bytes: usize,
|
||||
#[serde(default)]
|
||||
endian: Endian,
|
||||
#[serde(default)]
|
||||
length_includes_self: bool,
|
||||
#[serde(default = "default_max_payload")]
|
||||
max_payload: usize,
|
||||
},
|
||||
Cobs {
|
||||
#[serde(default = "default_max_frame")] max_frame: usize,
|
||||
#[serde(default = "default_max_frame")]
|
||||
max_frame: usize,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_true() -> bool { true }
|
||||
fn default_max_line() -> usize { 1024 * 1024 }
|
||||
fn default_len_bytes() -> usize { 2 }
|
||||
fn default_max_payload() -> usize { 1024 * 1024 }
|
||||
fn default_max_frame() -> usize { 1024 * 1024 }
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
fn default_max_line() -> usize {
|
||||
1024 * 1024
|
||||
}
|
||||
fn default_len_bytes() -> usize {
|
||||
2
|
||||
}
|
||||
fn default_max_payload() -> usize {
|
||||
1024 * 1024
|
||||
}
|
||||
fn default_max_frame() -> usize {
|
||||
1024 * 1024
|
||||
}
|
||||
|
||||
impl FramerConfig {
|
||||
pub fn build(self) -> Box<dyn Framer> {
|
||||
match self {
|
||||
FramerConfig::Line { strip_cr, max_line_len } =>
|
||||
Box::new(LineFramer::new(LineConfig { strip_cr, max_line_len })),
|
||||
FramerConfig::Fixed { frame_len } =>
|
||||
Box::new(FixedLengthFramer::new(frame_len)),
|
||||
FramerConfig::Length { len_bytes, endian, length_includes_self, max_payload } =>
|
||||
Box::new(LengthPrefixedFramer::new(LengthConfig { len_bytes, endian, length_includes_self, max_payload })),
|
||||
FramerConfig::Cobs { max_frame } =>
|
||||
Box::new(CobsFramer::new(max_frame)),
|
||||
FramerConfig::Line {
|
||||
strip_cr,
|
||||
max_line_len,
|
||||
} => Box::new(LineFramer::new(LineConfig {
|
||||
strip_cr,
|
||||
max_line_len,
|
||||
})),
|
||||
FramerConfig::Fixed { frame_len } => Box::new(FixedLengthFramer::new(frame_len)),
|
||||
FramerConfig::Length {
|
||||
len_bytes,
|
||||
endian,
|
||||
length_includes_self,
|
||||
max_payload,
|
||||
} => Box::new(LengthPrefixedFramer::new(LengthConfig {
|
||||
len_bytes,
|
||||
endian,
|
||||
length_includes_self,
|
||||
max_payload,
|
||||
})),
|
||||
FramerConfig::Cobs { max_frame } => Box::new(CobsFramer::new(max_frame)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum DecoderConfig {
|
||||
Text { #[serde(default)] encoding: TextEncoding },
|
||||
Text {
|
||||
#[serde(default)]
|
||||
encoding: TextEncoding,
|
||||
},
|
||||
Hex {
|
||||
#[serde(default)] uppercase: bool,
|
||||
#[serde(default = "default_sep")] separator: String,
|
||||
#[serde(default = "default_one")] bytes_per_group: usize,
|
||||
#[serde(default)] endian: Endian,
|
||||
#[serde(default)]
|
||||
uppercase: bool,
|
||||
#[serde(default = "default_sep")]
|
||||
separator: String,
|
||||
#[serde(default = "default_one")]
|
||||
bytes_per_group: usize,
|
||||
#[serde(default)]
|
||||
endian: Endian,
|
||||
},
|
||||
Plot {
|
||||
sample_type: SampleType,
|
||||
#[serde(default)] endian: Endian,
|
||||
#[serde(default = "default_one")] channels: usize,
|
||||
#[serde(default)] format: PlotFormat,
|
||||
#[serde(default)]
|
||||
endian: Endian,
|
||||
#[serde(default = "default_one")]
|
||||
channels: usize,
|
||||
#[serde(default)]
|
||||
format: PlotFormat,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_sep() -> String { String::from(" ") }
|
||||
fn default_one() -> usize { 1 }
|
||||
fn default_sep() -> String {
|
||||
String::from(" ")
|
||||
}
|
||||
fn default_one() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
impl DecoderConfig {
|
||||
pub fn build(self) -> Box<dyn ProtocolDecoder> {
|
||||
match self {
|
||||
DecoderConfig::Text { encoding } => Box::new(TextDecoder::new(encoding)),
|
||||
DecoderConfig::Hex { uppercase, separator, bytes_per_group, endian } =>
|
||||
Box::new(HexDecoder::new(HexConfig { uppercase, separator, bytes_per_group, endian })),
|
||||
DecoderConfig::Plot { sample_type, endian, channels, format } =>
|
||||
Box::new(PlotDecoder::new(PlotConfig { sample_type, endian, channels, format })),
|
||||
DecoderConfig::Hex {
|
||||
uppercase,
|
||||
separator,
|
||||
bytes_per_group,
|
||||
endian,
|
||||
} => Box::new(HexDecoder::new(HexConfig {
|
||||
uppercase,
|
||||
separator,
|
||||
bytes_per_group,
|
||||
endian,
|
||||
})),
|
||||
DecoderConfig::Plot {
|
||||
sample_type,
|
||||
endian,
|
||||
channels,
|
||||
format,
|
||||
} => Box::new(PlotDecoder::new(PlotConfig {
|
||||
sample_type,
|
||||
endian,
|
||||
channels,
|
||||
format,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,17 +158,24 @@ pub struct PipelineConfig {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SessionConfig {
|
||||
pub transport: TransportConfig,
|
||||
#[serde(default)] pub pipelines: Vec<PipelineConfig>,
|
||||
#[serde(default = "default_history")] pub history_limit: usize,
|
||||
#[serde(default)] pub auto_reconnect: bool,
|
||||
#[serde(default)]
|
||||
pub pipelines: Vec<PipelineConfig>,
|
||||
#[serde(default = "default_history")]
|
||||
pub history_limit: usize,
|
||||
#[serde(default)]
|
||||
pub auto_reconnect: bool,
|
||||
}
|
||||
|
||||
fn default_history() -> usize { 10_000 }
|
||||
fn default_history() -> usize {
|
||||
10_000
|
||||
}
|
||||
|
||||
impl Default for SessionConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
transport: TransportConfig::Tcp { addr: "127.0.0.1:8080".into() },
|
||||
transport: TransportConfig::Tcp {
|
||||
addr: "127.0.0.1:8080".into(),
|
||||
},
|
||||
pipelines: Vec::new(),
|
||||
history_limit: default_history(),
|
||||
auto_reconnect: false,
|
||||
@@ -113,9 +186,9 @@ impl Default for SessionConfig {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use xserial_core::protocol::text::TextEncoding;
|
||||
use xserial_core::protocol::plot::{PlotFormat, SampleType};
|
||||
use xserial_core::protocol::Endian;
|
||||
use xserial_core::protocol::plot::{PlotFormat, SampleType};
|
||||
use xserial_core::protocol::text::TextEncoding;
|
||||
use xserial_core::transport::TransportConfig;
|
||||
|
||||
#[test]
|
||||
@@ -123,7 +196,10 @@ mod tests {
|
||||
let json = r#"{"Line":{"strip_cr":true,"max_line_len":4096}}"#;
|
||||
let cfg: FramerConfig = serde_json::from_str(json).unwrap();
|
||||
match &cfg {
|
||||
FramerConfig::Line { strip_cr, max_line_len } => {
|
||||
FramerConfig::Line {
|
||||
strip_cr,
|
||||
max_line_len,
|
||||
} => {
|
||||
assert!(*strip_cr);
|
||||
assert_eq!(*max_line_len, 4096);
|
||||
}
|
||||
@@ -144,7 +220,12 @@ mod tests {
|
||||
let json = r#"{"Length":{"len_bytes":4,"endian":"Little","length_includes_self":true}}"#;
|
||||
let cfg: FramerConfig = serde_json::from_str(json).unwrap();
|
||||
match cfg {
|
||||
FramerConfig::Length { len_bytes, endian, length_includes_self, .. } => {
|
||||
FramerConfig::Length {
|
||||
len_bytes,
|
||||
endian,
|
||||
length_includes_self,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(len_bytes, 4);
|
||||
assert_eq!(endian, Endian::Little);
|
||||
assert!(length_includes_self);
|
||||
@@ -170,10 +251,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn decoder_hex_serde() {
|
||||
let json = r#"{"Hex":{"uppercase":true,"separator":":","bytes_per_group":2,"endian":"Little"}}"#;
|
||||
let json =
|
||||
r#"{"Hex":{"uppercase":true,"separator":":","bytes_per_group":2,"endian":"Little"}}"#;
|
||||
let cfg: DecoderConfig = serde_json::from_str(json).unwrap();
|
||||
match cfg {
|
||||
DecoderConfig::Hex { uppercase, separator, bytes_per_group, endian } => {
|
||||
DecoderConfig::Hex {
|
||||
uppercase,
|
||||
separator,
|
||||
bytes_per_group,
|
||||
endian,
|
||||
} => {
|
||||
assert!(uppercase);
|
||||
assert_eq!(separator, ":");
|
||||
assert_eq!(bytes_per_group, 2);
|
||||
@@ -188,7 +275,12 @@ mod tests {
|
||||
let json = r#"{"Plot":{"sample_type":"F32","endian":"Little","channels":2,"format":"Interleaved"}}"#;
|
||||
let cfg: DecoderConfig = serde_json::from_str(json).unwrap();
|
||||
match cfg {
|
||||
DecoderConfig::Plot { sample_type, endian, channels, format } => {
|
||||
DecoderConfig::Plot {
|
||||
sample_type,
|
||||
endian,
|
||||
channels,
|
||||
format,
|
||||
} => {
|
||||
assert_eq!(sample_type, SampleType::F32);
|
||||
assert_eq!(channels, 2);
|
||||
assert_eq!(format, PlotFormat::Interleaved);
|
||||
@@ -213,7 +305,9 @@ mod tests {
|
||||
assert!(cfg.auto_reconnect);
|
||||
assert_eq!(cfg.history_limit, 5000);
|
||||
assert_eq!(cfg.pipelines.len(), 2);
|
||||
assert!(matches!(&cfg.transport, TransportConfig::Tcp { addr } if addr == "192.168.1.1:9999"));
|
||||
assert!(
|
||||
matches!(&cfg.transport, TransportConfig::Tcp { addr } if addr == "192.168.1.1:9999")
|
||||
);
|
||||
let back = serde_json::to_string_pretty(&cfg).unwrap();
|
||||
let _: SessionConfig = serde_json::from_str(&back).unwrap();
|
||||
}
|
||||
@@ -221,9 +315,17 @@ mod tests {
|
||||
#[test]
|
||||
fn framer_build_does_not_panic() {
|
||||
for cfg in [
|
||||
FramerConfig::Line { strip_cr: true, max_line_len: 256 },
|
||||
FramerConfig::Line {
|
||||
strip_cr: true,
|
||||
max_line_len: 256,
|
||||
},
|
||||
FramerConfig::Fixed { frame_len: 8 },
|
||||
FramerConfig::Length { len_bytes: 2, endian: Endian::Big, length_includes_self: false, max_payload: 65536 },
|
||||
FramerConfig::Length {
|
||||
len_bytes: 2,
|
||||
endian: Endian::Big,
|
||||
length_includes_self: false,
|
||||
max_payload: 65536,
|
||||
},
|
||||
FramerConfig::Cobs { max_frame: 1024 },
|
||||
] {
|
||||
let f = cfg.build();
|
||||
@@ -233,8 +335,35 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn decoder_build_returns_correct_name() {
|
||||
assert_eq!(DecoderConfig::Text { encoding: TextEncoding::Utf8 }.build().name(), "Text");
|
||||
assert_eq!(DecoderConfig::Hex { uppercase: false, separator: " ".into(), bytes_per_group: 1, endian: Endian::Big }.build().name(), "Hex");
|
||||
assert_eq!(DecoderConfig::Plot { sample_type: SampleType::I16, endian: Endian::Big, channels: 2, format: PlotFormat::XY }.build().name(), "Plot");
|
||||
assert_eq!(
|
||||
DecoderConfig::Text {
|
||||
encoding: TextEncoding::Utf8
|
||||
}
|
||||
.build()
|
||||
.name(),
|
||||
"Text"
|
||||
);
|
||||
assert_eq!(
|
||||
DecoderConfig::Hex {
|
||||
uppercase: false,
|
||||
separator: " ".into(),
|
||||
bytes_per_group: 1,
|
||||
endian: Endian::Big
|
||||
}
|
||||
.build()
|
||||
.name(),
|
||||
"Hex"
|
||||
);
|
||||
assert_eq!(
|
||||
DecoderConfig::Plot {
|
||||
sample_type: SampleType::I16,
|
||||
endian: Endian::Big,
|
||||
channels: 2,
|
||||
format: PlotFormat::XY
|
||||
}
|
||||
.build()
|
||||
.name(),
|
||||
"Plot"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::collections::VecDeque;
|
||||
use crate::event::DecodedEntry;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct RingBuffer<T> {
|
||||
buf: VecDeque<T>,
|
||||
@@ -8,17 +8,33 @@ pub struct RingBuffer<T> {
|
||||
|
||||
impl<T> RingBuffer<T> {
|
||||
pub fn new(limit: usize) -> Self {
|
||||
Self { buf: VecDeque::with_capacity(limit.min(1024)), limit }
|
||||
Self {
|
||||
buf: VecDeque::with_capacity(limit.min(1024)),
|
||||
limit,
|
||||
}
|
||||
}
|
||||
pub fn push(&mut self, item: T) {
|
||||
if self.buf.len() >= self.limit { self.buf.pop_front(); }
|
||||
if self.buf.len() >= self.limit {
|
||||
self.buf.pop_front();
|
||||
}
|
||||
self.buf.push_back(item);
|
||||
}
|
||||
pub fn len(&self) -> usize { self.buf.len() }
|
||||
pub fn is_empty(&self) -> bool { self.buf.is_empty() }
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> { self.buf.iter() }
|
||||
pub fn clear(&mut self) { self.buf.clear(); }
|
||||
pub fn drain_recent(&self, count: usize) -> Vec<T> where T: Clone {
|
||||
pub fn len(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.buf.is_empty()
|
||||
}
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
self.buf.iter()
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.buf.clear();
|
||||
}
|
||||
pub fn drain_recent(&self, count: usize) -> Vec<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
let start = self.buf.len().saturating_sub(count);
|
||||
self.buf.iter().skip(start).cloned().collect()
|
||||
}
|
||||
@@ -26,6 +42,9 @@ impl<T> RingBuffer<T> {
|
||||
|
||||
impl RingBuffer<DecodedEntry> {
|
||||
pub fn entries_for_pipeline(&self, name: &str) -> Vec<&DecodedEntry> {
|
||||
self.buf.iter().filter(|e| e.pipeline_name == name).collect()
|
||||
self.buf
|
||||
.iter()
|
||||
.filter(|e| e.pipeline_name == name)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
pub mod config;
|
||||
pub mod event;
|
||||
pub mod cmd;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod event;
|
||||
pub mod history;
|
||||
pub mod session;
|
||||
pub mod manager;
|
||||
pub mod lua;
|
||||
pub mod manager;
|
||||
pub mod session;
|
||||
|
||||
pub use config::{DecoderConfig, FramerConfig, PipelineConfig, SessionConfig};
|
||||
pub use error::{Error, Result};
|
||||
|
||||
Reference in New Issue
Block a user