1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*!
# 📖 2.1. Dialect Constraints
<em>[← Asynchronous API](crate::docs::a4__async_api) | [Message Signing →](crate::docs::b2__signing)</em>
This chapter requires basic understanding of Maviola abstraction. Check
[Quickstart](crate::docs::a1__quickstart) or [Overview](crate::docs::a2__overview) sections before
reading this.
## Contents
1. [Intro](#intro)
1. [Canonical Dialects](#canonical-dialects)
1. [Rules of Dialect Management](#rules-of-dialect-management)
1. [Setting the Main Dialect](#setting-the-main-dialect)
1. [Adding Additional Dialects](#adding-additional-dialects)
1. [Validation](#validation)
1. [Custom Dialects](#custom-dialects)
## Intro
Dialect is a part of MAVLink specification responsible for decoding frames (or packets) to actual
messages. But there is more to it. According to MAVLink packet format, you can't verify the
consistency of a frame without knowing a dialect. This is because [`Frame::checksum`] is calculated
using a `CRC_EXTRA` fingerprint that depends on the message structure.
This decision of protocol designers has both advantages and downsides. The advantage is that once
you've fixed a dialect, you don't need to perform two calculations. One for packet consistency and
another for message integrity. You gain simplicity and performance boost at the same time. The
downside is that if you are building a network infrastructure for all possible dialects, there is
no way to confirm, that packets you are forwarding are correct. Unless you know their dialect
upfront. Which you don't.
There is no workaround for this problem as far as we keep MAVLink packet format untouched. Which
means, that refraining from choosing a dialect (or dialects) has unavoidable risk of polluting
your network infrastructure with junk.
In Maviola we provide instruments for frame validation using a special frame processor called
[`KnownDialects`] that you can attach to a [`Node`].
## Canonical Dialects
In most situations you don't need to worry about all the issues around dialects, extra CRC bytes,
and checksums. You just already know which dialect you are going to speak. If this is one of the
canonical dialect (the dialects you can pick by enabling Cargo feature), you shouldn't do anything
at all.
There is a "main sequence" (yes, you have a right [guess](https://en.wikipedia.org/wiki/Main_sequence))
of canonical dialects ordered by inclusion:
[`minimal`](https://mavlink.io/en/messages/minimal.html) <
[`standard`](https://mavlink.io/en/messages/standard.html) <
[`common`](https://mavlink.io/en/messages/common.html) <
[`ardupilotmega`](https://mavlink.io/en/messages/common.html) <
[`all`](https://mavlink.io/en/messages/all.html)
Maviola will assume the most general available canonical dialect as a [`DefaultDialect`] in all
places which require dialect specification. And that's it. Now, you may simply move to the
[next](crate::docs::b2__signing) chapter.
But what if you want something else? Something special.
## Rules of Dialect Management
ⓘ First of all, we need to mention that the [`Minimal`] dialect is irreplaceable. You can think
about it as a "rich" empty set or, if you prefer the language of
[abstract nonsense](https://en.wikipedia.org/wiki/Abstract_nonsense), an initial object. It is
always there, it can't be replaced no matter what you are doing. Maviola will hold to it as mother
carries her yet to be born child. Keeping that in mind, let's proceed to dialect management.
Each node has the main [`dialect`] and the list of [`known_dialects`]. The main dialect is used
to communicate dialect capability over MAVLink network using
[heartbeat](https://mavlink.io/en/services/heartbeat.html) protocol. The protocol requires a special
`mavlink_version` field of a
[heartbeat message](https://mavlink.io/en/messages/common.html#HEARTBEAT) to be set from the
[`Dialect::version`].
The known dialects are used to validate incoming and outgoing messages. But the main dialect is
always have precedence over other dialects. That means, that if you have a collision in message
`ID` namespace and your [`Frame::message_id`] belongs to the main dialect, then it will be checked
against this dialect. And if the message fails such validation, it will be rejected. Even if you
have a known dialect for which this frame is valid.
For now, this is the only difference between main and known dialects, but in later versions other
capabilities related to the main dialect may be added to the library.
As mentioned above, the [`Minimal`] dialect is always among the known dialects. But beyond that
you can use whatever you want as a main dialect and any combination of known dialects with one
small restriction, that [`Dialect::name`] can't collide.
## Setting the Main Dialect
When you want your [`Node`] to speak a dialect which is not a [`DefaultDialect`], then you need to
specify this dialect during node construction using `dialect` method and
[turbofish](https://turbo.fish/about) syntax:
```rust,no_run
# use maviola::prelude::*;
# use maviola::sync::prelude::*;
use maviola::protocol::dialects::Minimal;
let node = Node::builder()
.dialect::<Minimal>()
/* other node settings */
# ;
```
Here we replace default dialect with the [`Minimal`] dialect. However, the default dialect will
still be present in the list of the [`known_dialects`]. If you want to completely remove it, use
`no_default_dialect` method of a node builder:
```rust,no_run
# use maviola::prelude::*;
# use maviola::sync::prelude::*;
#
let node = Node::builder()
.no_default_dialect()
/* other node settings */
# ;
```
## Adding Additional Dialects
To add an extra dialect, simply use `add_dialect` of the node builder and our beloved (or bewitched)
[turbofish](https://turbo.fish/about) syntax:
```rust,no_run
# use maviola::prelude::*;
# use maviola::sync::prelude::*;
#
use maviola::protocol::dialects::Minimal;
let node = Node::builder()
.add_dialect::<Minimal>()
/* other node settings */
# ;
```
If your dialect has the same [`Dialect::name`] as one of the already known dialects, then this will
replace a dialect. If for some reason your dialect has a [`Dialect::name`] equal to the main dialect
or [`Minimal`] dialect, then nothing will happen. We do not return error in this case since such
situations are rare, and it takes a significant effort to shoot your leg from such uncomfortable
position (you have to define a custom dialect with a colliding name).
## Validation
Once you've specified (or kept default settings) for your main [`dialect`] and [`known_dialects`],
you can rely on the internal frame validation for nodes. Which means, that all incoming frames
will be checked against your known dialects. The main dialect have a precedence over other dialects
and the oder in which you've added your known dialects matters as well. However, this makes sense
only when you have dialects with colliding message `ID` namespaces. Which doesn't make sense.
Unless you are writing another fundamental MAVLink processing library as we do.
There is one important exception from the validation rules. If [`Frame::message_id`] does not belong
to any of the known dialect, then it will be rejected by default. However, you may change this
behavior by setting `allow_unknown_dialects` in the node builder to `true`:
```rust,no_run
# use maviola::prelude::*;
# use maviola::sync::prelude::*;
#
let node = Node::builder()
.allow_unknown_dialects(true)
/* other node settings */
# ;
```
This may be useful if you perform frame validation after receiving it from the node. Why someone
wants to do that? We don't know. But feel like you should be the one making this decision.
## Custom Dialects
Speaking about peculiar life choices. It is possible to create a custom dialect both from XML
message definition (like canonical MAVLink dialects) and purely from Rust code. The former, most
general, approach is discussed in [Custom Dialects](crate::docs::c1__custom_dialects). The latter,
easier and specific, is explained in [Ad-hoc Dialects](crate::docs::c4__ad_hoc_dialects) chapter.
<em>[← Asynchronous API](crate::docs::a4__async_api) | [Message Signing →](crate::docs::b2__signing)</em>
[`Minimal`]: crate::protocol::dialects::Minimal
[`dialect`]: crate::core::node::Node::dialect
[`known_dialects`]: crate::core::node::Node::known_dialects
*/
use crate*;
use crate*;