use thiserror::Error;
use winnow::binary::{le_u24, le_u32, u8};
use winnow::combinator::{alt, empty, repeat, separated_foldl1, seq};
use winnow::token::take;
use winnow::{PResult, Parser};
#[derive(Debug, Error)]
#[error("unknown conversion from unsigned `{input}` to ChannelId")]
pub struct TryChannelIdFromUnsignedError {
input: u8,
}
const NUM_INPUT_CHANNELS: usize = 59;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ChannelId(u8);
impl TryFrom<u8> for ChannelId {
type Error = TryChannelIdFromUnsignedError;
fn try_from(num: u8) -> Result<Self, Self::Error> {
if num < u8::try_from(NUM_INPUT_CHANNELS).unwrap() {
Ok(ChannelId(num))
} else {
Err(TryChannelIdFromUnsignedError { input: num })
}
}
}
impl From<ChannelId> for u8 {
fn from(channel: ChannelId) -> Self {
channel.0
}
}
#[derive(Clone, Copy, Debug)]
pub enum EdgeType {
Leading,
Trailing,
}
pub const TIMESTAMP_BITS: u32 = 24;
pub const TIMESTAMP_CLOCK_FREQ: f64 = 10e6;
#[derive(Clone, Copy, Debug)]
pub struct TimestampCounter {
pub channel: ChannelId,
timestamp: u32,
pub edge: EdgeType,
}
fn timestamp_counter(input: &mut &[u8]) -> PResult<TimestampCounter> {
let temp = le_u24.parse_next(input)?;
seq! {TimestampCounter{
timestamp: empty.value(temp & 0x00FFFFFE),
edge: empty.value(if temp & 1 == 1 {EdgeType::Trailing} else {EdgeType::Leading}),
channel: u8
.verify(|&n| n & 0x80 == 0x80)
.try_map(|n| ChannelId::try_from(n & 0x7F))
}}
.parse_next(input)
}
impl TimestampCounter {
pub fn timestamp(&self) -> u32 {
self.timestamp
}
}
#[derive(Clone, Copy, Debug)]
pub struct WrapAroundMarker {
pub timestamp_top_bit: bool,
counter: u32,
}
fn wrap_around_marker(input: &mut &[u8]) -> PResult<WrapAroundMarker> {
let temp = le_u24.parse_next(input)?;
seq! {WrapAroundMarker{
timestamp_top_bit: empty.value(temp & 0x00800000 == 0x00800000),
counter: empty.value(temp & 0x007FFFFF),
_: 0xFF,
}}
.parse_next(input)
}
impl WrapAroundMarker {
pub fn wrap_around_counter(&self) -> u32 {
self.counter
}
}
#[derive(Clone, Copy, Debug)]
pub enum FifoEntry {
TimestampCounter(TimestampCounter),
WrapAroundMarker(WrapAroundMarker),
}
fn fifo_entry(input: &mut &[u8]) -> PResult<FifoEntry> {
alt((
timestamp_counter.map(FifoEntry::TimestampCounter),
wrap_around_marker.map(FifoEntry::WrapAroundMarker),
))
.parse_next(input)
}
fn scalers_block(input: &mut &[u8]) -> PResult<()> {
(
b"\x3C\x00\x00\xFE",
take(NUM_INPUT_CHANNELS * std::mem::size_of::<u32>()),
le_u32,
)
.void()
.parse_next(input)
}
pub fn chronobox_fifo(input: &mut &[u8]) -> Vec<FifoEntry> {
separated_foldl1(
repeat(0.., fifo_entry),
scalers_block,
|mut l: Vec<_>, _, mut r| {
l.append(&mut r);
l
},
)
.parse_next(input)
.unwrap()
}
const CHRONOBOX_NAMES: [&str; 4] = ["cb01", "cb02", "cb03", "cb04"];
#[derive(Debug, Error)]
#[error("unknown parsing from board name `{input}` to BoardId")]
pub struct ParseBoardIdError {
input: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BoardId(&'static str);
impl TryFrom<&str> for BoardId {
type Error = ParseBoardIdError;
fn try_from(name: &str) -> Result<Self, Self::Error> {
match CHRONOBOX_NAMES.iter().find(|&&n| n == name) {
Some(&n) => Ok(BoardId(n)),
None => Err(ParseBoardIdError {
input: name.to_string(),
}),
}
}
}
impl BoardId {
pub fn name(&self) -> &str {
self.0
}
}
#[cfg(test)]
mod tests;