diff --git a/src/error.rs b/src/error.rs index 65b760adf64a0379192f52b583112878d9923202..92038ac18d5b9c29335b5aebf2b017e4cf1dc610 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,3 +41,48 @@ pub enum CommentError { #[snafu(display("Given buffer too small for comment data"))] BufferOverflow, } + +#[derive(Debug, Snafu)] +pub enum DigipeatError { + #[snafu(display("No route"))] + NoRoute, + + #[snafu(display("Identification is us"))] + Us, + + #[snafu(display("Max hops hit"))] + MaxHops, + + #[snafu(display("Already digipeated by this node"))] + AlreadyDigipeated, + + #[snafu(display("Future hop(s) are already set"))] + SetDestiny, +} + +#[derive(Debug, Snafu, PartialEq, Eq)] +pub enum AppendNodeError { + #[snafu(display("Future hop(s) already set to a different node"))] + SetFuture, + + #[snafu(display("Max hops hit"))] + HopsOverflow, + + #[snafu(display("Node already in route"))] + DuplicateNode, + + #[snafu(display("Given data causes the route whisker to overflow"))] + RouteOverflow, +} + +#[derive(Debug, Snafu, PartialEq, Eq)] +pub enum PacketRouteAppendError { + #[snafu(display("No route whisker on packet"))] + NoRouteWhisker, + + #[snafu(display("Route error"))] + Route { error: AppendNodeError }, + + #[snafu(display("CATS packet overflow"))] + PacketOverflow, +} diff --git a/src/packet.rs b/src/packet.rs index 093362873f9f4bcf746056b3d3a527cd27bac075..bb43dce53d35bc075f710797facf2738477faaaf 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -4,10 +4,10 @@ use crc::{Crc, CRC_16_IBM_SDLC}; use crate::{ buffer::{Buffer, BufferOverflow}, - error::{CommentError, DecodeError, EncodeError}, + error::{CommentError, DecodeError, DigipeatError, EncodeError, PacketRouteAppendError}, interleaver, ldpc, utf8, whisker::{ - Arbitrary, Comment, Destination, Gps, Identification, Route, Timestamp, + Arbitrary, Comment, Destination, Gps, Identification, Route, RouteNode, Timestamp, ValidatedWhiskerIter, Whisker, WhiskerIter, COMMENT_TYPE, }, whitener, @@ -271,6 +271,75 @@ impl<'a, const N: usize> Packet<'a, N> { self.clear_by_type(COMMENT_TYPE, true); } + /// Given the callsign and ssid of a node, should it digipeat this packet? + /// Takes into account things such as if we've digipeated it already, the max hops, etc. + pub fn should_digipeat(&self, callsign: &str, ssid: u8) -> Result<(), DigipeatError> { + let route = match self.route() { + Some(x) => x, + None => { + return Err(DigipeatError::NoRoute); + } + }; + + if let Some(ident) = self.identification() { + if &ident.callsign == callsign && ident.ssid == ssid { + return Err(DigipeatError::Us); + } + } + + let max_hops: usize = route.max_hops.into(); + let cur_hops = route + .iter() + .filter(|r| match r { + RouteNode::Internet => false, + RouteNode::Identity(_, _, is_future) => !is_future, + }) + .count(); + + if max_hops <= cur_hops { + return Err(DigipeatError::MaxHops); + } + + let already_digipeated = route.iter().any(|r| match r { + RouteNode::Internet => false, + RouteNode::Identity(rc, rs, is_future) => rc == callsign && rs == ssid && !is_future, + }); + + if already_digipeated { + return Err(DigipeatError::AlreadyDigipeated); + } + + let next_node = route.iter().find_map(|r| match r { + RouteNode::Identity(c, s, is_future) if is_future => Some((c, s)), + _ => None, + }); + + match next_node { + Some((rc, rs)) if rc != callsign || rs != ssid => { + return Err(DigipeatError::SetDestiny) + } + _ => Ok(()), + } + } + + /// Note that if this fails due to a CATS overflow, it will wipe the route off of the packet + pub fn append_to_route( + &mut self, + callsign: &str, + ssid: u8, + ) -> Result<(), PacketRouteAppendError> { + let mut route = self.route().ok_or(PacketRouteAppendError::NoRouteWhisker)?; + route + .append_node(callsign, ssid) + .map_err(|error| PacketRouteAppendError::Route { error })?; + self.clear_route(); + + self.add_route(route) + .map_err(|_| PacketRouteAppendError::PacketOverflow)?; + + Ok(()) + } + fn clear_by_type(&mut self, whisker_type: u8, all: bool) { let mut i = 0; while i < self.buf.len() { diff --git a/src/whisker/route.rs b/src/whisker/route.rs index 1ce625d882b6fad94c388e103dd2fa32728bcbed..89810915547c87677e813003126d5d9c87187278 100644 --- a/src/whisker/route.rs +++ b/src/whisker/route.rs @@ -1,5 +1,7 @@ use arrayvec::ArrayVec; +use crate::error::AppendNodeError; + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Route { pub max_hops: u8, @@ -59,6 +61,93 @@ impl Route { Some(()) } + /// Append a callsign/ssid pair to the route, intelligently. + /// I.e. replace future node if possible + /// Returns an Err if the route is out of space, or appending the node doesn't make logical sense + /// (there's a future node that doesn't match) + pub fn append_node(&mut self, callsign: &str, ssid: u8) -> Result<(), AppendNodeError> { + if usize::from(self.max_hops) == self.iter().count() { + return Err(AppendNodeError::HopsOverflow); + } + + let mut new_route = Route::new(self.max_hops); + + let mut already_inserted = false; + for rn in self.iter() { + match rn { + RouteNode::Internet => new_route + .push_internet() + .ok_or(AppendNodeError::RouteOverflow)?, + RouteNode::Identity(rc, rs, is_future) => { + let us = rc == callsign && rs == ssid; + + if is_future { + if us { + if already_inserted { + return Err(AppendNodeError::DuplicateNode); + } else { + new_route.push_callsign(callsign, ssid, false); + already_inserted = true; + } + } else { + if already_inserted { + new_route.push_callsign(rc, rs, true); + } else { + return Err(AppendNodeError::SetFuture); + } + } + } else { + if us { + return Err(AppendNodeError::DuplicateNode); + } else { + new_route.push_callsign(rc, rs, false); + } + } + } + } + } + + if !already_inserted { + new_route.push_callsign(callsign, ssid, false); + } + + *self = new_route; + Ok(()) + /* + let replace_future = r.iter().any(|rn| match rn { + RouteNode::Identity(rc, rs, is_future) => rc == callsign && rs == ssid && is_future, + _ => false, + }); + + if replace_future { + let mut new_route = Route::new(r.max_hops); + let mut already_replaced = false; + + for rn in r.iter() { + match rn { + RouteNode::Identity(rc, rs, is_future) + if rc == callsign && rs == ssid && is_future && !already_replaced => + { + already_replaced = true; + new_route.push_callsign(callsign, ssid, false)?; + } + RouteNode::Identity(rc, rs, is_future) => { + new_route.push_callsign(rc, rs, is_future)?; + } + RouteNode::Internet => { + new_route.push_internet()?; + } + } + } + + *r = new_route; + } else { + r.push_callsign(callsign, ssid, false)?; + } + + Some(())*/ + } + pub fn iter(&'_ self) -> RouteIter<'_> { RouteIter::new(self) } @@ -194,3 +283,84 @@ impl<'a> Iterator for RouteIter<'a> { Some(self.iter.next()?.unwrap()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn append_fails_when_existing_future() { + let mut r = Route::new(5); + r.push_callsign("C1", 0, false); + r.push_callsign("C2", 0, false); + r.push_callsign("C3", 0, true); + + assert_eq!( + AppendNodeError::SetFuture, + r.append_node("C3", 1).unwrap_err() + ); + + assert_eq!( + AppendNodeError::SetFuture, + r.append_node("C4", 0).unwrap_err() + ); + } + + #[test] + fn append_fails_when_would_exceed_max_hops() { + let mut r = Route::new(3); + r.append_node("C1", 0).unwrap(); + r.append_node("C2", 0).unwrap(); + r.append_node("C2", 1).unwrap(); + assert_eq!( + AppendNodeError::HopsOverflow, + r.append_node("C4", 0).unwrap_err() + ); + } + + #[test] + fn append_fails_when_already_in_route() { + let mut r = Route::new(3); + r.append_node("C1", 0).unwrap(); + + assert_eq!( + AppendNodeError::DuplicateNode, + r.append_node("C1", 0).unwrap_err() + ); + } + + #[test] + fn append_overwrites_future() { + let mut r = Route::new(5); + r.push_callsign("C1", 0, false); + r.push_callsign("C2", 0, false); + r.push_callsign("C3", 0, true); + r.push_callsign("C4", 0, true); + + r.append_node("C3", 0).unwrap(); + + let mut iter = r.iter(); + assert_eq!(Some(RouteNode::Identity("C1", 0, false)), iter.next()); + assert_eq!(Some(RouteNode::Identity("C2", 0, false)), iter.next()); + assert_eq!(Some(RouteNode::Identity("C3", 0, false)), iter.next()); + assert_eq!(Some(RouteNode::Identity("C4", 0, true)), iter.next()); + assert_eq!(None, iter.next()); + } + + #[test] + fn append_appends_when_no_future() { + let mut r = Route::new(5); + r.push_callsign("C1", 0, false); + r.push_callsign("C2", 0, false); + r.push_callsign("C3", 0, false); + + r.append_node("C4", 0).unwrap(); + + let mut iter = r.iter(); + assert_eq!(Some(RouteNode::Identity("C1", 0, false)), iter.next()); + assert_eq!(Some(RouteNode::Identity("C2", 0, false)), iter.next()); + assert_eq!(Some(RouteNode::Identity("C3", 0, false)), iter.next()); + assert_eq!(Some(RouteNode::Identity("C4", 0, false)), iter.next()); + assert_eq!(None, iter.next()); + } +}