Let's say I have an uart receiving fixed size data frames, and I want to reply other data frames according to pattern matching on the received frame. This pattern matching should be able to reply N times a frame, then M times another frame, then indefinitely a thrid frame for the same pattern received.
here is the types I think could fit the need, together with the wanted unit test behaviour:
use heapless::Vec;
pub struct Reply {
pub until: usize,
pub reply: Vec<u8, 5>,
}
pub struct Matching {
pub pattern: Vec<u8, 5>,
pub mask: Vec<u8, 5>,
pub replies: Vec<Reply, 16>,
pub matched_cnt: usize,
}
pub struct Config {
pub matches: Vec<Matching, 16>,
}
impl Config {
pub fn get_reply(&mut self, frame: [u8; 5]) -> Option<[u8; 5]> {
todo!("find efficient implementation");
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_reply() {
let mut conf: Config = Config {
matches: Vec::from_slice(&[
Matching {
pattern: Vec::from_slice(&[0x55, 0xaa]).unwrap(),
mask: Vec::from_slice(&[0xff, 0xff]).unwrap(),
replies: Vec::from_slice(&[
Reply {
until: 2,
reply: Vec::from_slice(&[1u8; 5]).unwrap(),
},
Reply {
until: 3,
reply: Vec::from_slice(&[2u8; 5]).unwrap(),
},
Reply {
until: 0,
reply: Vec::from_slice(&[3u8; 5]).unwrap(),
},
])
.unwrap(),
matched_cnt: 0,
},
Matching {
pattern: Vec::from_slice(&[0x55, 0xbb]).unwrap(),
mask: Vec::from_slice(&[0xff, 0xff]).unwrap(),
replies: Vec::from_slice(&[Reply {
until: 0,
reply: Vec::from_slice(&[4u8; 5]).unwrap(),
}])
.unwrap(),
matched_cnt: 0,
},
])
.unwrap(),
};
assert_eq!(conf.get_reply([0; 5]), None);
assert_eq!(conf.get_reply([0x55, 0xaa, 1, 2, 3]), Some(&[1u8; 5]));
assert_eq!(conf.get_reply([0x55, 0xaa, 2, 2, 3]), Some(&[1u8; 5]));
assert_eq!(conf.get_reply([0x55, 0xa0, 2, 2, 3]), None);
assert_eq!(conf.get_reply([0x55, 0xaa, 0, 2, 3]), Some(&[2u8; 5]));
assert_eq!(conf.get_reply([0x55, 0xaa, 0, 2, 3]), Some(&[3u8; 5]));
assert_eq!(conf.get_reply([0x55, 0xaa, 0, 2, 3]), Some(&[3u8; 5]));
assert_eq!(conf.get_reply([0x55, 0xbb, 0, 0, 0]), Some(&[4u8; 5]));
}
}
I am struggeling implementing the get_reply() with efficiency. Could you propose some hints ?
Edit: the best I can do is :
impl Matching {
pub fn matching(&self, frame: [u8; 5]) -> bool {
for (i, _) in self.pattern.iter().enumerate() {
if self.pattern[i] & self.mask[i] != frame[i] & self.mask[i] {
return false;
}
}
self.matched_cnt += 1;
true
}
pub fn matched(&mut self) -> &mut Self {
self.matched_cnt += 1;
self
}
}
impl Config {
pub fn get_reply(&mut self, frame: [u8; 5]) -> Option<&[u8; 5]> {
self.matches
.iter_mut()
.filter(|m| m.pattern.len() == m.mask.len())
.find(|m| m.matching(frame))
.map(|m| m.matched())
.and_then(|m| {
m.replies
.iter()
.find(|r| r.until == 0 || m.matched_cnt <= r.until)
.map(|r| array_ref![r.reply.as_slice(), 0, 5] as &[u8; 5])
})
}
}
but the common iteration by index in matching() seems creepy...