use core::marker::PhantomData;
use crate::protocol::marker::{
HasCompId, HasCrcExtra, HasMsgId, HasPayload, HasPayloadLen, HasSignature, HasSysId, IsCompId,
IsCrcExtra, IsMsgId, IsPayload, IsPayloadLen, IsSequenced, IsSigned, IsSysId, Sequenced, Unset,
};
use crate::protocol::{
Behold, CompatFlags, ComponentId, CrcExtra, Endpoint, HeaderBuilder, IncompatFlags,
MaybeVersioned, Message, MessageId, Payload, Sequence, Signature, SystemId, Versioned,
Versionless, V1, V2,
};
use crate::Frame;
use crate::prelude::*;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FrameBuilder<
V: MaybeVersioned,
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
M: IsMsgId,
P: IsPayload,
E: IsCrcExtra,
Sig: IsSigned,
> {
pub(super) header_builder: HeaderBuilder<V, L, Seq, S, C, M>,
pub(super) payload: P,
pub(super) crc_extra: E,
pub(super) signature: Sig,
}
impl Default for FrameBuilder<Versionless, Unset, Unset, Unset, Unset, Unset, Unset, Unset, Unset> {
fn default() -> Self {
Self::new()
}
}
impl FrameBuilder<Versionless, Unset, Unset, Unset, Unset, Unset, Unset, Unset, Unset> {
pub fn new() -> Self {
Self {
header_builder: HeaderBuilder::new(),
payload: Unset,
crc_extra: Unset,
signature: Unset,
}
}
}
impl<
V: MaybeVersioned,
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
M: IsMsgId,
P: IsPayload,
E: IsCrcExtra,
Sig: IsSigned,
> FrameBuilder<V, L, Seq, S, C, M, P, E, Sig>
{
pub fn sequence(
self,
sequence: Sequence,
) -> FrameBuilder<V, L, Sequenced, S, C, M, P, E, Unset> {
FrameBuilder {
header_builder: self.header_builder.sequence(sequence),
payload: self.payload,
crc_extra: self.crc_extra,
signature: Unset,
}
}
pub fn system_id(
self,
system_id: SystemId,
) -> FrameBuilder<V, L, Seq, HasSysId, C, M, P, E, Unset> {
FrameBuilder {
header_builder: self.header_builder.system_id(system_id),
payload: self.payload,
crc_extra: self.crc_extra,
signature: Unset,
}
}
pub fn component_id(
self,
component_id: ComponentId,
) -> FrameBuilder<V, L, Seq, S, HasCompId, M, P, E, Sig> {
FrameBuilder {
header_builder: self.header_builder.component_id(component_id),
payload: self.payload,
crc_extra: self.crc_extra,
signature: self.signature,
}
}
}
impl<
V: MaybeVersioned,
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
P: IsPayload,
E: IsCrcExtra,
Sig: IsSigned,
> FrameBuilder<V, L, Seq, S, C, Unset, P, E, Sig>
{
#[must_use]
pub fn message_id(
self,
message_id: MessageId,
) -> FrameBuilder<V, L, Seq, S, C, HasMsgId, P, Unset, Unset> {
FrameBuilder {
header_builder: self.header_builder.message_id(message_id),
payload: self.payload,
crc_extra: Unset,
signature: Unset,
}
}
}
impl<
V: Versioned,
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
P: IsPayload,
E: IsCrcExtra,
Sig: IsSigned,
> FrameBuilder<V, L, Seq, S, C, HasMsgId, P, E, Sig>
{
#[must_use]
pub fn payload(
self,
bytes: &[u8],
) -> FrameBuilder<V, HasPayloadLen, Seq, S, C, HasMsgId, HasPayload, E, Unset> {
let payload = Payload::new(self.header_builder.message_id.0, bytes, V::version());
FrameBuilder {
header_builder: self
.header_builder
.message_id(payload.id())
.payload_length(payload.length()),
payload: HasPayload(payload),
crc_extra: self.crc_extra,
signature: Unset,
}
}
}
impl<
V: MaybeVersioned,
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
M: IsMsgId,
P: IsPayload,
Sig: IsSigned,
> FrameBuilder<V, L, Seq, S, C, M, P, Unset, Sig>
{
pub fn crc_extra(
self,
crc_extra: CrcExtra,
) -> FrameBuilder<V, L, Seq, S, C, M, P, HasCrcExtra, Sig> {
FrameBuilder {
header_builder: self.header_builder,
payload: self.payload,
crc_extra: HasCrcExtra(crc_extra),
signature: self.signature,
}
}
}
impl<
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
M: IsMsgId,
P: IsPayload,
E: IsCrcExtra,
Sig: IsSigned,
> FrameBuilder<Versionless, L, Seq, S, C, M, P, E, Sig>
{
pub fn version<Version: Versioned>(
self,
version: Version,
) -> FrameBuilder<Version, L, Seq, S, C, M, P, E, Sig> {
FrameBuilder {
header_builder: self.header_builder.version(version),
payload: self.payload,
crc_extra: self.crc_extra,
signature: self.signature,
}
}
}
impl<
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
M: IsMsgId,
P: IsPayload,
E: IsCrcExtra,
Sig: IsSigned,
> FrameBuilder<V2, L, Seq, S, C, M, P, E, Sig>
{
pub fn incompat_flags(
self,
incompat_flags: IncompatFlags,
) -> FrameBuilder<V2, L, Seq, S, C, M, P, E, Sig> {
FrameBuilder {
header_builder: self
.header_builder
.incompat_flags(incompat_flags)
.signed(self.signature.is_signed()),
payload: self.payload,
crc_extra: self.crc_extra,
signature: self.signature,
}
}
pub fn compat_flags(
self,
compat_flags: CompatFlags,
) -> FrameBuilder<V2, L, Seq, S, C, M, P, E, Sig> {
FrameBuilder {
header_builder: self.header_builder.compat_flags(compat_flags),
payload: self.payload,
crc_extra: self.crc_extra,
signature: self.signature,
}
}
#[allow(clippy::type_complexity)]
pub fn signature(
self,
signature: Signature,
) -> Behold<FrameBuilder<V2, L, Seq, S, C, M, P, E, HasSignature>> {
Behold::new(FrameBuilder {
header_builder: self.header_builder.signed(true),
payload: self.payload,
crc_extra: self.crc_extra,
signature: HasSignature(signature),
})
}
}
impl<
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
M: IsMsgId,
P: IsPayload,
E: IsCrcExtra,
Sig: IsSigned,
> FrameBuilder<Versionless, L, Seq, S, C, M, P, E, Sig>
{
pub fn endpoint<V: Versioned>(
self,
endpoint: &Endpoint<V>,
) -> FrameBuilder<V, L, Sequenced, HasSysId, HasCompId, M, P, E, Sig> {
FrameBuilder {
header_builder: self
.header_builder
.version(V::v())
.sequence(endpoint.next_sequence())
.system_id(endpoint.system_id())
.component_id(endpoint.component_id()),
payload: self.payload,
crc_extra: self.crc_extra,
signature: self.signature,
}
}
}
impl<
V: Versioned,
L: IsPayloadLen,
Seq: IsSequenced,
S: IsSysId,
C: IsCompId,
M: IsMsgId,
P: IsPayload,
E: IsCrcExtra,
Sig: IsSigned,
> FrameBuilder<V, L, Seq, S, C, M, P, E, Sig>
{
#[allow(clippy::type_complexity)]
pub fn message(
self,
message: &dyn Message,
) -> Result<FrameBuilder<V, HasPayloadLen, Seq, S, C, HasMsgId, HasPayload, HasCrcExtra, Sig>>
{
let payload = message.encode(V::version())?;
let crc_extra = HasCrcExtra(message.crc_extra());
Ok(FrameBuilder {
header_builder: self
.header_builder
.message_id(payload.id())
.payload_length(payload.length()),
payload: HasPayload(payload),
crc_extra,
signature: self.signature,
})
}
}
impl
FrameBuilder<
V1,
HasPayloadLen,
Sequenced,
HasSysId,
HasCompId,
HasMsgId,
HasPayload,
HasCrcExtra,
Unset,
>
{
pub fn upgrade(
self,
) -> FrameBuilder<
V2,
HasPayloadLen,
Sequenced,
HasSysId,
HasCompId,
HasMsgId,
HasPayload,
HasCrcExtra,
Unset,
> {
let payload = self.payload.0.upgraded();
FrameBuilder {
header_builder: HeaderBuilder {
mavlink_version: PhantomData,
payload_length: HasPayloadLen(payload.length()),
incompat_flags: Some(IncompatFlags::default()),
compat_flags: Some(CompatFlags::default()),
sequence: self.header_builder.sequence,
system_id: self.header_builder.system_id,
component_id: self.header_builder.component_id,
message_id: self.header_builder.message_id,
},
payload: HasPayload(payload),
crc_extra: self.crc_extra,
signature: Unset,
}
}
}
impl<V: Versioned, Sig: IsSigned>
FrameBuilder<
V,
HasPayloadLen,
Sequenced,
HasSysId,
HasCompId,
HasMsgId,
HasPayload,
HasCrcExtra,
Sig,
>
{
pub fn build(self) -> Frame<V> {
let mut frame = Frame {
header: self.header_builder.build(),
payload: self.payload.0,
checksum: 0,
signature: None,
};
frame.checksum = frame.calculate_crc(self.crc_extra.0);
frame
}
}
#[cfg(test)]
mod frame_builder_tests {
#[test]
#[cfg(feature = "dlct-minimal")]
fn build_frame_v2() {
use crate::dialects::minimal::messages::Heartbeat;
use crate::protocol::{MavLinkVersion, V2};
use crate::Frame;
let message = Heartbeat::default();
let frame = Frame::builder()
.sequence(17)
.system_id(22)
.component_id(17)
.version(V2)
.message(&message)
.unwrap()
.build();
assert!(matches!(frame.version(), MavLinkVersion::V2));
assert_eq!(frame.sequence(), 17);
assert_eq!(frame.system_id(), 22);
assert_eq!(frame.component_id(), 17);
assert_eq!(frame.message_id(), 0);
}
}