use crate::error::IncompatFlagsError;
use crate::protocol::{CompatFlags, CrcExtra, DialectSpec, IncompatFlags};
use crate::prelude::*;
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CompatProcessor {
incompat_flags: Option<IncompatFlags>,
compat_flags: Option<CompatFlags>,
incoming: CompatStrategy,
outgoing: CompatStrategy,
ignore_signature: bool,
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CompatStrategy {
Reject,
RejectSet,
Enforce,
EnforceProxy,
Proxy,
}
pub trait IntoCompatProcessor {
fn into_compat_processor(self) -> CompatProcessor;
}
#[derive(Clone, Debug)]
pub struct CompatProcessorBuilder {
inner: CompatProcessor,
}
impl CompatProcessor {
#[inline(always)]
pub fn builder() -> CompatProcessorBuilder {
CompatProcessorBuilder::default()
}
#[inline(always)]
pub fn update(self) -> CompatProcessorBuilder {
CompatProcessorBuilder { inner: self }
}
#[inline(always)]
pub fn incompat_flags(&self) -> Option<IncompatFlags> {
self.incompat_flags
}
#[inline(always)]
pub fn compat_flags(&self) -> Option<CompatFlags> {
self.compat_flags
}
#[inline(always)]
pub fn incoming(&self) -> CompatStrategy {
self.incoming
}
#[inline(always)]
pub fn outgoing(&self) -> CompatStrategy {
self.outgoing
}
#[inline(always)]
pub fn ignore_signature(&self) -> bool {
self.ignore_signature
}
#[inline(always)]
pub fn process_incoming<V: MaybeVersioned>(
&self,
frame: &mut Frame<V>,
dialects: &[&DialectSpec],
) -> core::result::Result<(), FrameError> {
self.process_for_strategy(frame, self.incoming, dialects)
}
#[inline(always)]
pub fn process_incoming_with_crc_extra<V: MaybeVersioned>(
&self,
frame: &mut Frame<V>,
crc_extra: CrcExtra,
) -> core::result::Result<(), IncompatFlagsError> {
self.process_for_strategy_with_crc_extra(frame, self.incoming, crc_extra)
}
#[inline(always)]
pub fn process_outgoing<V: MaybeVersioned>(
&self,
frame: &mut Frame<V>,
dialects: &[&DialectSpec],
) -> core::result::Result<(), FrameError> {
self.process_for_strategy(frame, self.outgoing, dialects)
}
#[inline(always)]
pub fn process_outgoing_with_crc_extra<V: MaybeVersioned>(
&self,
frame: &mut Frame<V>,
crc_extra: CrcExtra,
) -> core::result::Result<(), IncompatFlagsError> {
self.process_for_strategy_with_crc_extra(frame, self.outgoing, crc_extra)
}
pub fn process_for_strategy<V: MaybeVersioned>(
&self,
frame: &mut Frame<V>,
strategy: CompatStrategy,
dialects: &[&DialectSpec],
) -> core::result::Result<(), FrameError> {
for dialect in dialects {
if let Ok(info) = dialect.message_info(frame.message_id()) {
self.process_for_strategy_with_crc_extra(frame, strategy, info.crc_extra())?;
return Ok(());
}
}
Err(FrameError::NotInDialect(frame.message_id()))
}
pub fn process_for_strategy_with_crc_extra<V: MaybeVersioned>(
&self,
frame: &mut Frame<V>,
strategy: CompatStrategy,
crc_extra: CrcExtra,
) -> core::result::Result<(), IncompatFlagsError> {
if frame.matches_version(V2) {
if let Some(compat_flags) = self.compat_flags {
match strategy {
CompatStrategy::Enforce | CompatStrategy::RejectSet => {
frame.header.compat_flags = compat_flags;
frame.checksum = frame.calculate_crc(crc_extra);
}
_ => {}
}
}
if let Some(mut incompat_flags) = self.incompat_flags {
if self.ignore_signature {
incompat_flags.set(IncompatFlags::MAVLINK_IFLAG_SIGNED, frame.is_signed());
}
match strategy {
CompatStrategy::Reject | CompatStrategy::RejectSet => {
if incompat_flags != frame.header.incompat_flags {
return Err(IncompatFlagsError {
expected: incompat_flags,
actual: frame.header.incompat_flags,
});
}
}
CompatStrategy::Enforce | CompatStrategy::EnforceProxy => {
frame.header.incompat_flags = incompat_flags;
frame.checksum = frame.calculate_crc(crc_extra);
}
_ => {}
}
}
}
Ok(())
}
}
impl IntoCompatProcessor for CompatProcessor {
fn into_compat_processor(self) -> CompatProcessor {
self
}
}
impl CompatProcessorBuilder {
pub fn new() -> CompatProcessorBuilder {
CompatProcessorBuilder {
inner: CompatProcessor {
incompat_flags: None,
compat_flags: None,
incoming: CompatStrategy::Reject,
outgoing: CompatStrategy::Enforce,
ignore_signature: true,
},
}
}
pub fn incompat_flags(mut self, incompat_flags: IncompatFlags) -> Self {
self.inner.incompat_flags = Some(incompat_flags);
self
}
pub fn compat_flags(mut self, compat_flags: CompatFlags) -> Self {
self.inner.compat_flags = Some(compat_flags);
self
}
pub fn incoming(mut self, strategy: CompatStrategy) -> Self {
self.inner.incoming = strategy;
self
}
pub fn outgoing(mut self, strategy: CompatStrategy) -> Self {
self.inner.outgoing = strategy;
self
}
pub fn ignore_signature(mut self, value: bool) -> Self {
self.inner.ignore_signature = value;
self
}
pub fn build(self) -> CompatProcessor {
self.inner
}
}
impl Default for CompatProcessorBuilder {
fn default() -> Self {
CompatProcessorBuilder::new()
}
}
impl IntoCompatProcessor for CompatProcessorBuilder {
fn into_compat_processor(self) -> CompatProcessor {
self.build()
}
}
#[cfg(test)]
#[cfg(feature = "dlct-minimal")]
mod tests {
use super::*;
use crate::dialects::Minimal;
#[test]
fn compat_workflow() {
let processor = CompatProcessor::builder()
.compat_flags(CompatFlags::BIT_1 | CompatFlags::BIT_2)
.incompat_flags(IncompatFlags::BIT_4 | IncompatFlags::BIT_5)
.outgoing(CompatStrategy::Enforce)
.incoming(CompatStrategy::Reject)
.ignore_signature(true)
.build();
let mut frame = Frame::builder()
.version(V2)
.sequence(0)
.system_id(0)
.component_id(0)
.message_id(0)
.payload(&[0; 0])
.crc_extra(0)
.build();
processor
.process_outgoing(&mut frame, &[Minimal::spec()])
.unwrap();
assert!(frame
.incompat_flags()
.contains(IncompatFlags::BIT_4 | IncompatFlags::BIT_5));
}
}