RFC: addressing for the MAVLink-M messages that direct a specific system - #10
RFC: addressing for the MAVLink-M messages that direct a specific system#10TSC21 wants to merge 2 commits into
Conversation
Every message in the dialect is broadcast: none carries
target_system or target_component, so a message that orders one
system to do something cannot say which system it is for. A station
tasking a sensor on one aircraft, or ordering an effector to engage,
puts that message where every system reading the dialect sees it.
With two aircraft on a shared bearer, or one aircraft carrying two
payloads that both speak the dialect, nothing in the message
separates them.
The eleven messages that command a system, or that hand something
to a named one, carry the two fields: FIRES, SPLASH_CORRECTION,
TARGET_HANDOVER, ESAD_ARMING, ESAD_CONFIG, SENSOR_TASKING,
CAS_9LINE, TERMINAL_CONTROL, ENGAGEMENT_DIRECTIVE, CALL_FOR_FIRE and
LOITER_MUNITION_CONTROL. The messages that describe the world keep
broadcasting, because a contact is a contact and anything listening
should draw it.
The names are the ones MAVLink reserves. mavgen fills the target
offsets in the message-entry table only for fields called
target_system and target_component, and that table is what a router
reads to place a message: FIRES becomes {53020, 124, 64, 64, 3, 40,
41}, where 3 is the have-target flag and 40 and 41 are the byte
offsets. A field named anything else leaves that row at zero and the
message keeps flooding every endpoint.
The fields are part of the message rather than an extension, so the
wire format and the CRC change. This dialect is under development
and its messages can still be replaced, which is the moment to put
addressing in the message body where it belongs.
51e2e3f to
00bad7c
Compare
|
Q1 - not an oversight. The design assumes addressing. The MAVLink-M swarm dataflow concept specifies "Addressed TARGET_HANDOVER per target to the chosen shooter's sysid", and lists "addressed handoff" among the things the dialect already provides. It explicitly rules out the alternative: "What is explicitly not supported is a third, unsafe pattern - broadcasting one target to everyone and letting whoever arrives strike it. That produces convergence: multiple munitions wasted on one target, and multiple armed aircraft meeting at one point in space." store_id, effector_id and esad_id all carry "0 = all" semantics. No track_uid on ESAD_ARMING, ESAD_CONFIG, SENSOR_TASKING, TERMINAL_CONTROL, CAS_9LINE. Q2 - agree with the split; ESAD_ARMING especially. Q3 - yes, use the reserved names on MAVLINK_M_ACK. |
|
Thanks @ryanjAA, that answers all three. Q1: that settles it. Addressing was always the design, it just was not in a place a router can act on. Good point on store_id, effector_id and esad_id as well. Those pick the device inside an aircraft, these new fields pick the aircraft, and 0 still means all at both levels. Q2: good, the split stays as posted and TARGET_CUE stays broadcast. Q3: I will add it to this PR. Concretely, origin_sysid becomes target_system, since it already names the system the ack has to get back to, target_component goes next to it, and ack_sysid goes away because the frame header already says who is sending. That leaves MAVLINK_M_ACK looking like COMMAND_ACK in common. Tell me if ack_sysid is there for a reason I am missing, otherwise I will push that here. On track_uid, agreed, and I think it belongs in its own PR because it answers a different question: these fields say who has to act, track_uid says what the message is about. Your ESAD_ARMING against ESAD_STATE example is the clearest one. It hits SENSOR_TASKING too, where you cannot tell a sensor which track to watch, so a ground station ends up sending coordinates and the task cannot follow a moving target. Happy to write that one up if nobody else is on it. So: the ack change lands here, this PR covers who has to act, and what the message is about comes separately. If that works for you, an approval once the ack commit is up and this can stop being an RFC. |
MAVLINK_M_ACK closes the loop on a message one system sent to
another, so it has exactly one correct destination: the originator of
the acknowledged message. A field of its own naming that originator
stays invisible to a router, which places messages from the
message-entry table mavgen generates and finds a zeroed target row
there, so an acknowledgment reaches every endpoint on the bearer.
The reserved names carry the destination. target_system names the
originator the acknowledgment belongs to, and target_component names
the consumer inside it, which an aircraft carrying two systems that
speak the dialect needs in order to place the answer. The
acknowledging system is the source of the MAVLink frame, so the frame
header identifies it and the message body leaves that to the header.
The dialect generates MAVLINK_M_ACK as {53004, 224, 69, 69, 3, 16,
17}: 3 is the have-target flag and 16 and 17 are the byte offsets of
the two fields. The payload holds at 69 bytes, the CRC changes as a
field change requires, and the other twenty-five messages generate
byte-identical entry rows.
|
MAVLINK_M_ACK carries target_system and target_component, and ack_sysid is gone because the frame header already says who is sending. It generates as {53004, 224, 69, 69, 3, 16, 17}, so a router can place it, and the payload stays at 69 bytes since two fields go in and two come out. Every other message in the dialect generates a byte-identical entry row. I left origin_sysid alone on TRACK_IDENTITY, TARGET_CUE, PARTICIPANT_POSITION and ENGAGEMENT_DIRECTIVE. Those say who a report is about, not where it goes, so they are not the same field wearing a different name. |
|
I reviewed c55620e. The generated ACK entry is: CRC_EXTRA moves from 47 to 224, the payload remains 69 bytes, and target_system/target_component land at offsets 16 and 17. No wire-format objection. Worth stating explicitly that the message ID and payload length are both unchanged. Offset 16 retains the same effective value, while offset 17 changes meaning from ack_sysid to target_component.CRC_EXTRA is therefore the only on-wire compatibility guard between the old and new definitions. That is correct and fail-closed, but it is not the usual case where a changed payload length makes the incompatibility obvious, and a reader will assume it is. There is also a stronger argument for keeping the target fields in the base definition than the one in the PR body. Extension fields are excluded from CRC_EXTRA. An old sender omits them, a new receiver decodes the omission as zero, and zero targets mean broadcast. Extension-placed addressing would therefore let mixed definitions stay CRC-compatible while silently degrading to the exact behavior this PR exists to prevent. Base fields force a CRC mismatch instead. That is the case to make. Four immediate description clarifications beloww. As currently framed, none changes the wire format. The SENSOR_TASKING question, plus the separate ENGAGEMENT_DIRECTIVE.origin_sysid question below, could reveal that a field change is warranted, which is why I would rather settle both before merge.
Please also define the direction discriminator, presumably status = UNSPECIFIED for a task request and a nonzero status for a response, and state which task fields are meaningful or ignored in a response. Otherwise a single frame can carry both a command and a status with no normative rule for interpreting it.
For target_system = 0, each receiving system evaluates that rule against its own local ESADs. That is the only enforceable reading, and it exposes an important consequence for the broadcast-policy follow-up: a broadcastARM can partially apply. One system may accept and arm while another rejects because its local selected ESAD set does not share the supplied hash. No individual target system can determine the network-wide outcome; the originator would need to aggregate per-system ACKs and/or subsequentESAD_STATE reports. The follow-up should define that response obligation explicitly. ESAD_CONFIG has the corresponding unresolved multi-device rule and should state how its one challenge hash is validated when esad_id = 0,store_id = 0, or both. On ENGAGEMENT_DIRECTIVE.origin_sysid: the explanation for retaining origin_sysid fits TRACK_IDENTITY, TARGET_CUE and PARTICIPANT_POSITION, where the field records provenance or the subject of a report.ENGAGEMENT_DIRECTIVE instead describes origin_sysid as the system issuing the directive, which may duplicate the frame source. I am not asking to remove it and I am not asking for ack_sysid back. Please just say which meaning is intended. If it is the logical or original issuing authority and may differ from the immediate MAVLink sender after gateway translation, that is useful and the field should say so. If it intentionally duplicates the immediate frame source so that detached audit records stay self-contained, document that rationale. Otherwise it is accidental redundancy and worth removing while the definition is still mutable. On broadcast validity generally: the hazard predates this PR, since these messages previously had no system-level destination at all. What this PR adds is the first explicit field against which the intended policy can be stated and enforced, so it is the right moment to open the question. Please open and link a follow-up issue defining broadcast validity per operation and direction rather than per message ID. Several of these combine fail-safe actions such as DISARM, ABORT, CHECK_FIRE and WAVE_OFF, where network-wide delivery may be intentional, with affirmative actions such as ARM, RESUME, RETARGET and CLEARED_HOT, where it is not. To make the intended policy explicit: the convergence rationale I quoted earlier applies to affirmative multi-recipient directives as well. Broadcast ARM is not intended to be valid. The follow-up should codify broadcast ARM as invalid while separately and explicitly defining which fail-safe broadcasts, such as DISARM, ABORT, CHECK_FIRE or WAVE_OFF, are permitted. On track_uid, agreed it belongs in a separate PR. ESAD_ARMING is the sharpest case because it has no track or engagement binding, whileESAD_STATE reports the track against which the store is armed. ThenSENSOR_TASKING, which cannot reference a persistent track: task_id identifies the transaction, not the subject, so following a moving target requires continually re-tasking with fresh coordinates or an out-of-band association. Then CAS_9LINE, which carries the 9-line geometry but no sequence and no persistent cross-message identity. ESAD_CONFIG andTERMINAL_CONTROL are weaker, and TERMINAL_CONTROL.sequence already gives an engagement binding. For #11: check_wire_compat.py currently builds its map as{m.id: (m.name, m.crc_extra)} and compares only those tuples. Please extend it to report full generated metadata for changed messages, that isCRC_EXTRA, min_length, max_length, target flags, target_system offset and target_component offset. The #11 description also still says comparison against #10 reports eleven changed messages; after c55620e it is twelve. No wire-format objection. Once the four descriptions and the ENGAGEMENT_DIRECTIVE.origin_sysid semantics are resolved, and the broadcast-policy issue is linked, I'd say it's ready to approve. |
Contributed on behalf of RIIS, LLC.
Opening this as an RFC. If the answer is that routing belongs outside the dialect I am happy to close it, but I think a message whose definition singles out one recipient should be able to name it, the same way core MAVLink already does.
What I ran into
While integrating the dialect into QGC, which consumes the situational-awareness messages and originates sensor tasking and nothing else, I checked the dialect for addressing and there is none. All 26 messages are broadcast, none of them carries
target_systemortarget_component.For the messages that describe the world that looks right and I am reading it as intended. A contact is a contact, and anything listening should be able to draw it.
For the messages whose definition directs a single system it is a spec gap.
SENSOR_TASKINGtasks one sensor, yet it cannot say which one, so the tasking goes out and every system reading the dialect sees it. The same shape repeats across the dialect's other directive messages. On one aircraft with one payload that works by luck. With two aircraft on a shared bearer, or one aircraft carrying two payloads that both speak the dialect, nothing in the message separates them.What we do today in QGC is pick the link: we send sensor tasking only on the selected vehicle's link. That works because we own the link. It does nothing for anyone routing this over a shared bus, a mesh or a router, and it does not separate two payloads on the same aircraft.
What this PR does
Adds
target_systemandtarget_componentto the eleven messages whose definitions direct a specific system or hand something to a named one:FIRES,SPLASH_CORRECTION,TARGET_HANDOVER,ESAD_ARMING,ESAD_CONFIG,SENSOR_TASKING,CAS_9LINE,TERMINAL_CONTROL,ENGAGEMENT_DIRECTIVE,CALL_FOR_FIRE,LOITER_MUNITION_CONTROL.The messages that describe the world are left alone.
On scope, to be explicit: this adds no capability and no semantics, it only lets a directive the dialect already defines name its recipient, using the same two reserved field names core MAVLink uses for the purpose. The integration behind this RFC exercises
SENSOR_TASKINGand nothing else; the rest of the list is swept because a definitions change should treat every directive in the dialect consistently, not because I implement them or plan to.The fields are part of the message, not an extension. That changes the wire format and the CRC. The dialect is still under development and the messages can be replaced, so this is the moment to put addressing in the message body rather than bolt it on the end later.
Why the field names matter
This is the part I would have got wrong if I had named the fields something of my own.
A router does not read the XML. It reads the message-entry table mavgen generates, and mavgen fills the target offsets in that table only for fields called exactly
target_systemandtarget_component. With the change,SENSOR_TASKINGgenerates as:where 3 is the have-target flag and 22 and 23 are the byte offsets of the two fields in the payload. That is what lets a router find the target at a fixed offset without knowing anything else about the message, look up which endpoint that system was last heard on, and send it there instead of to everyone.
A picture message stays as it is:
Flag 0, no offsets, so a router floods it to every endpoint, which is what you want for a contact.
Worth saying out loud: the router still has to have this dialect compiled in. A router that does not know 53050 cannot read its target fields and will broadcast it, so addressing in the message helps the routers that carry the dialect and changes nothing for the ones that do not.
Questions
TARGET_CUEwas the one I went back and forth on, since its description says it is relayed to one or more consumers, and I left it broadcast.MAVLINK_M_ACKalready hasack_sysid, so I left it alone. Would you rather it used the reserved names too, so a router can place an acknowledgement back to the asker?Happy to extend or narrow this whichever way you prefer.