docs: update configuration and diagnostics

This commit is contained in:
Onur Özkan committed 2026-08-23 00:40:50 +03:00
1 parent fdf82a5114
commit 21e9497915
8 files changed
+157 -123

No files matched your search

+20 -16
View File
@@ -39,7 +39,7 @@ kaptan-config-mavlink ──► state-only ConfigProtocol
├── one ConfigManager ──► kaptan-config ──► flight schema/store
└── one bounded MAVLink log-sink queue
│
kaptan-log-mavlink payload adapter
kaptan-diagnostics-mavlink payload adapter
│
platform-owned sole encoder / DMA TX link
```
@@ -48,17 +48,17 @@ The generated dialect is intentionally custom rather than a `PARAM_*` or QGC
adapter. The core uses numeric field IDs and typed scalar values; descriptor
text is metadata, not a wire identity. Discovery/value reads, exact-index
descriptor and enum-option requests, and structured results are custom-dialect
records. Runtime logs use one bounded text message with MAVLink severity and
flags. Remote writes use only schema-described typed scalar setters. The core
records. Runtime logs use one bounded text message with a conventional level
and flags. Remote writes use only schema-described typed scalar setters. The core
owns one RAM transaction and the distinction
between boot-active, committed, and editable candidate snapshots. In the
current P1 policy a commit is restart-only. It cannot mutate controller or
estimator state in flight.
Target-owned stores may serialize one bounded complete CBOR snapshot. SITL
supplies the host file adapter and persists a private nonzero storage sequence
alongside that snapshot. The serialization and sequence are not configuration
state and are never exposed by the MAVLink protocol.
supplies the host file adapter and atomically replaces that snapshot through a
same-directory temporary file. The serialization is not configuration state
and is never exposed by the MAVLink protocol.
Motor allocation/mixer topology is immutable compiled vehicle capability data,
never a scalar field or CBOR candidate member. F722 now recovers operational
settings from a generic two-slot journal before constructing consumers. Its
@@ -74,18 +74,22 @@ option request. The first accepted transaction binds one source principal
through a bounded post-terminal replay lease; foreign traffic cannot evict a
lost result before that lease expires.
The MAVLink dialect root includes `kaptan/config.xml`, `kaptan/log.xml`,
`kaptan/error.xml`, and `kaptan/shutdown.xml`.
The MAVLink dialect root includes `kaptan/config.xml`, `kaptan/log.xml`, and
`kaptan/error.xml`.
The build watches every fragment and generates one `message::kaptan` module;
fragments are never separately generated Rust modules. `kaptan-log` itself has
no MAVLink, platform, time, allocation, filesystem, or configuration
dependency. Event creation is independent from sink filtering, allowing a
future blackbox and MAVLink to select different policies. Each sink owns
bounded Critical and BestEffort queues. The platform drains a MAVLink record
only after TX admission. `KAPTAN_LOG` carries
severity plus one bounded text field; `KAPTAN_ERROR` carries exact error
severity and flags plus bounded path and additional-information fields. Local saturation accounting remains
inside the sink queue, facade, and producer mailboxes; heartbeat and safety
no MAVLink, platform, time, allocation, or filesystem dependency; its sole
configuration coupling is compile-time enum metadata for `LevelFilter`.
`kaptan-err-propagate` independently owns explicitly assigned typed-error identity,
function-boundary propagation, and a bounded nonblocking ingress; it has
no logging, MAVLink, platform, filesystem, or clock dependency. Event creation
is independent from sink filtering, allowing a future blackbox and MAVLink to
select different policies. The platform drains typed errors before its bounded
critical and best-effort log queues and removes a pending record only after TX
admission. `KAPTAN_LOG` carries one conventional level plus one bounded text
field; `KAPTAN_ERROR` carries an explicitly assigned 16-bit ID and the bounded
`file:line` of the reporting boundary. Its
message kind already identifies a typed firmware error. Heartbeat and safety
telemetry remain separate from diagnostics.
See [Configuration](configuration.md) for the deployment matrix and GCS transaction
+1 -1
View File
@@ -7,7 +7,7 @@ labels the external UART2 pads `T2` and `R2`.
Its board-local `UserConfig` factory selects endpoint `1/1`, raw-IMU and
heartbeat publication rates, and an enabled MAVLink diagnostic sink with an
`INFO` maximum severity. The web configurator may replace those
`Info` level filter. The web configurator may replace those
target-advertised values in the journal. Range
and optical-flow fields are absent because this image has no vertical-sensor
route. Its `ImmutableConfig` keeps the ICM-42688-P route, yaw-180° mount,
+1 -1
View File
@@ -7,7 +7,7 @@ The checked-in manufacturer datasheet is
Its board-local `UserConfig` factory selects endpoint `1/1`, raw-IMU, range,
optical-flow and heartbeat publication rates, and an enabled MAVLink diagnostic
sink with an `INFO` maximum severity. The web configurator may replace those
sink with an `Info` level filter. The web configurator may replace those
target-advertised values in the journal. Its `ImmutableConfig` keeps the
MPU-6000 route, body-aligned
mount and concrete driver setup, USART1 pins and baud, MTF01 route and baud,
+9 -6
View File
@@ -47,15 +47,18 @@ The Incoming MAVLink table at the bottom groups live, CRC-valid packets by
source and message ID and shows the measured receive frequency and total count.
Names come from the generated MAVLink dialect decoders; the browser contains no
message catalog. Selecting a message name shows the latest decoded field values;
the diagnostic projection renders `KAPTAN_ERROR` path and additional information
as text. The configurator keeps only one latest value per bounded aggregate,
the diagnostic projection renders the `KAPTAN_ERROR` ID and reporting
`file:line`. The
configurator keeps only one latest value per bounded aggregate,
rather than packet history, and clears it for every serial-link session.
The separate Runtime logs section retains the most recent 512 `KAPTAN_LOG` and
`KAPTAN_ERROR` events in receive order. Each row shows local receive time,
decoded severity, and readable diagnostic text. Repeated events remain separate;
decoded level, and readable diagnostic text. Repeated events remain separate;
older host-side entries are discarded at the fixed display bound and the UI
reports that count. This event history is independent of the packet table's
reports that count. A visible `TRUNCATED` badge marks bounded `KAPTAN_LOG`
text. This
event history is independent of the packet table's
single latest-value projection and resets when the serial link changes.
Each F722 board owns the exact settings consumed by its compiled image. Both
@@ -63,7 +66,7 @@ current boards advertise:
- MAVLink system and component identity;
- raw-IMU and heartbeat publication rates; and
- whether the MAVLink diagnostic sink is enabled and its maximum severity.
- the MAVLink diagnostic sink's Off/Error/Warn/Info/Debug/Trace level filter.
The Kakute image also advertises range and optical-flow publication rates
because its immutable route includes an MTF01. The GEPRC image declares no
@@ -105,7 +108,7 @@ controller, safety supervisor, allocator envelope, or MAVLink diagnostic sink:
- mass, collective-thrust, and tilt limits;
- rate PID gains and limits;
- actuator minimum, maximum, and slew limits; and
- the restart-only MAVLink diagnostic-sink enable and maximum severity.
- the restart-only MAVLink diagnostic-sink level filter.
The fixed Quad-X mixer is intentionally not configurable. It is reviewed
capability data, not a user-editable array.
+65 -28
View File
@@ -3,49 +3,86 @@
Public Kaptan error enums derive the pure `KaptanError` implementation:
```rust
#[derive(Debug, kaptan_log::KaptanError)]
#[derive(Debug, kaptan_err_propagate::KaptanError)]
enum SensorError {
#[kaptan_error(id = 12345)]
TransferFault,
#[kaptan_error(id = 12346)]
UnexpectedIdentity(u8),
}
#[kaptan_err_propagate::propagate]
fn service_sensor() -> Result<(), SensorError> {
read_sensor()?;
Ok(())
}
```
The derive implements `core::error::Error`, `Display`, and `ErrorReport`. It
creates a static `module::Type::Variant` error path without allocating,
publishing telemetry, exposing generic source values, or changing safety
state. Renaming a module, type, or variant changes that path; it is not a
stable machine protocol identity.
requires a nonzero explicit ID on every variant and retains no type, variant,
or module string. `Display` renders the decimal ID. Renaming, moving, or
reordering a Rust variant therefore does not change its receiver contract.
Missing and same-enum duplicate IDs are rejected by the derive. Reusing an ID
across error enums generates the same reserved linker symbol and fails the
final firmware build.
Normal-task code may use `log::error!("{error}")`; it is routed as a bounded
`KAPTAN_ERROR`. ISR-adjacent flight paths may enqueue the derived static
message through their existing bounded mailbox. Neither route is a replacement
for safety state, actuator inhibit, or typed configuration results.
`#[kaptan_err_propagate::propagate]` observes the ordinary `Result` produced
by one terminal diagnostic boundary. If an `Err` leaves that function, the attribute
makes one nonblocking admission attempt and returns the exact same `Err` to the
caller. The record contains the final converted error ID and the compile-time
`file:line` of that annotated boundary. This is where the `Err` enters
diagnostics, not necessarily the line where a deeper callee originally
constructed it. `?`, explicit returns, result aliases, and `From` conversion
retain their normal Rust behavior. An error handled inside the function is
silent.
## Stable IDs
Production error IDs are plain decimal numbers from `1` through `59999`.
Values from `60000` through `65535` are reserved for tests, and `0` is
invalid. Once issued, an ID is never renumbered or reused, even if its error
variant is removed. Adding a variant therefore requires allocating a new ID;
renaming, moving, or reordering an existing variant keeps its assigned ID.
The example numbers above are illustrative and must be replaced with
unallocated catalog values when used in production code.
Only the terminal boundary is annotated. If nested functions are both
annotated, each boundary makes its own report attempt; the ingress does not
mark or mutate error values to deduplicate them. Expected validation,
backpressure, retry, and untrusted-input results remain unannotated. In
particular, transient SPI/I²C `Busy` and unavailable range measurements stay
inside their service retry/idle paths. In contrast, every logging
macro—including `kaptan_log::error!`—produces only
`KAPTAN_LOG`. Error propagation is not a replacement for safety state,
actuator inhibit, or typed configuration results.
`kaptan-err-propagate` owns error identity and its independent one-slot
ingress. A pending record contains only the compact ID and a static reference
to the propagation boundary's `file:line`. The MAVLink adapter copies that
location into its fixed field when constructing `KAPTAN_ERROR`. Producers never
encode or transmit MAVLink themselves. Admission is bounded and lossy if a
different typed error is already pending.
## What the receiver sees
- Configuration writes receive authoritative structured failures through
`KAPTAN_CONFIG_RESULT`: operation, lifecycle state, field IDs, validation
rule, and protocol error code.
- Runtime driver and link faults reach the F722 MAVLink worker as a
`KAPTAN_ERROR` path, subject to bounded transport admission. The error
payload is `{ level, flags, path_length, additional_information_length,
path[96], additional_information[64] }`; `level` preserves the originating
MAVLink-compatible severity from Emergency through Error, each length
identifies the valid UTF-8 prefix, and the optional information field may be
empty. A `KaptanError` derive supplies the type/variant path and normally
leaves additional information empty. `log::error!` uses the Rust log target
as the path and the formatted message as additional information.
- A rejected configuration operation may enqueue a companion `KAPTAN_ERROR`
whose path is the high-level `ProtocolError` identity. It is intentionally
not correlated and carries no field/rule detail; the matching
`KAPTAN_CONFIG_RESULT` remains the sole precise authority.
rule, and protocol error code. A terminal persistence failure also reports
the storage-only `CommitTransactionError::Store` boundary once as
`KAPTAN_ERROR`; replaying the cached command result does not repeat the error
or the flash operation.
- Terminal typed `ErrorReport` faults reach the F722 MAVLink worker as a
`KAPTAN_ERROR`, subject to bounded transport admission. The payload is
`{ error_id, raised_at[96] }`. `raised_at` is a zero-padded UTF-8
`file:line` prefix for the annotated propagation boundary.
The message kind itself identifies an error; no level is duplicated in the
payload. Dynamic human-readable context belongs in `KAPTAN_LOG`, while
structured protocol failures remain in their owning response message.
- Control, navigation, safety, and simulator error types are derive-ready but
their live owners are not yet attached to a MAVLink worker.
- `KAPTAN_SHUTDOWN`, defined in `definitions/kaptan/shutdown.xml`, represents
only a future completed orderly,
output-inhibited shutdown. It is not a shutdown request, panic, hard-fault,
reset, or retention-record protocol. F722 still uses `panic-halt`; it does
not claim a live panic frame or retained-record replay.
F722 still uses `panic-halt`; it does not claim a live panic frame or
retained-record replay.
No diagnostic message may synchronously transmit from a panic handler, hard
fault, ISR, DMA callback, or motor-inhibit path. A later target integration
+54 -63
View File
@@ -1,94 +1,85 @@
# Logs
Normal host/SITL task code can use the standard facade:
Kaptan code uses the familiar logging macros on both host and flight targets:
```rust
log::debug!("estimator waiting for IMU");
log::info!("configuration loaded");
log::warn!("range aid stale");
log::error!("sensor transfer failed"); // emitted as KAPTAN_ERROR
kaptan_log::debug!("estimator waiting for IMU");
kaptan_log::info!("configuration loaded");
kaptan_log::warn!("range aid stale");
kaptan_log::error!("sensor transfer failed");
kaptan_log::trace!("prediction step complete");
```
All five macros emit `KAPTAN_LOG`; their level never selects a different wire
message. When a terminal typed firmware error escapes a reviewed
`#[kaptan_err_propagate::propagate]` function, it makes one bounded admission attempt
to the separate `KAPTAN_ERROR` ingress.
`kaptan-log` formats each event once into a fixed bounded buffer and never
sends MAVLink, allocates, blocks, changes flight state, or touches DMA. The
composition fans the event into each enabled sink; the sole MAVLink worker
drains its bounded sink queue. Ordinary text, error paths, and error details
use distinct 160-, 96-, and 64-byte storage types, so a queued record retains
exactly the common diagnostic-envelope capacity and needs no adapter-side
clipping buffer. F722 deliberately does not
link this facade in ISR, DMA, control, actuator, panic, hard-fault, or shutdown
contexts; those paths use static fault mailboxes that the worker turns into
messages later.
drains its bounded sink queue. Ordinary log text uses one 160-byte bounded
record. Typed-error records contain the explicit ID and a static reference to
the annotated propagation boundary until the MAVLink adapter copies its
`file:line` into the 96-byte `raised_at` field.
Logs and typed errors use independent fixed atomic
ingresses; interrupt and worker producers make one nonblocking admission
attempt, and only the MAVLink worker encodes or transmits a retained event.
Panic and hard-fault paths still do not attempt live telemetry.
## Receiver contract
`definitions/kaptan/log.xml`, `definitions/kaptan/error.xml`, and
`definitions/kaptan/shutdown.xml` separate ordinary logs, errors, and
orderly-shutdown observations:
`definitions/kaptan/log.xml` and `definitions/kaptan/error.xml` separate
ordinary logs and typed errors:
```text
KAPTAN_LOG { level, flags, message_length, message[160] }
KAPTAN_ERROR {
level, flags, path_length, additional_information_length,
path[96], additional_information[64]
error_id, raised_at[96]
}
KAPTAN_SHUTDOWN { reason }
```
A GCS decoder generated from the same `kaptan.xml` revision must validate
`message_length <= 160` for `KAPTAN_LOG`, and `path_length <= 96` plus
`additional_information_length <= 64` for `KAPTAN_ERROR`, before slicing the
fixed arrays into UTF-8 prefixes. Generated MAVLink framing alone does not
enforce these semantic length fields. `KAPTAN_LOG` displays its text at the
MAVLink severity in `level`; `TRACE` is encoded as `DEBUG` plus the `TRACE`
flag.
`KAPTAN_ERROR.level` preserves the originating MAVLink-compatible severity from
Emergency through Error. The error channel accepts only `TRUNCATED` and
`COALESCED`; a `TRACE` bit is never emitted there. Unknown future flag bits
must be ignored. `COALESCED` means that one or more later observations from
the same source mailbox were folded while the retained first error was pending;
it does not claim that their paths or details were identical.
`message_length <= 160` for `KAPTAN_LOG`. `KAPTAN_ERROR.raised_at` is a
zero-padded UTF-8 `file:line` prefix; receivers use bytes before the first zero,
or the complete field when no zero is present.
`KAPTAN_LOG.level` accepts only Error (3), Warning (4), Info (6), and Debug
(7); Trace is encoded as Debug (7) plus the `TRACE` flag. Other MAVLink
severity numbers are not Kaptan log levels.
The `KAPTAN_ERROR` message kind already means a typed firmware error, so it has
no redundant level or flags field.
The derived error path is intentionally not a numeric error catalog; renaming
its Rust module/type/variant changes that identity. Error details requiring
`error_id` is the stable nonzero catalog number explicitly assigned to the
Rust error variant. Renaming or reordering a variant does not change it.
`raised_at` identifies the annotated boundary where the `Err` entered
diagnostics. Error details requiring
machine action stay in their owning protocol, notably `KAPTAN_CONFIG_RESULT`.
`KAPTAN_SHUTDOWN` has only its typed reason and is valid only after an orderly
output-inhibited completion. Delivery is bounded and lossy under link pressure;
heartbeat and safety telemetry use separate paths. Logs have no publication
rate setting and are not periodic streams. A retained event leaves the
MAVLink sink queue once the lower transport admits it; temporary queue pressure
only defers that same event.
Delivery is bounded and lossy under link pressure; heartbeat and safety
telemetry use separate paths. Logs have no publication rate setting and are
not periodic streams. A retained event leaves its MAVLink sink only once the
lower transport admits it; temporary queue pressure only defers that same
event.
## Filtering
The restart-only `mavlink.logging.enabled` field controls the MAVLink
diagnostic sink independently from future sinks such as a blackbox.
`mavlink.logging.maximum_severity` accepts the complete MAVLink severity range,
Emergency (0) through Debug (7). For example, selecting Warning permits warning
and more severe events while suppressing notice, info, and debug. Disabling the
sink suppresses both `KAPTAN_LOG` and `KAPTAN_ERROR` on MAVLink without
suppressing events from another sink. F722 uses the validated policy recovered
at boot, falling back to its board factory when the journal is empty or
invalid.
The single restart-only `mavlink.logging.level_filter` field uses the familiar
Off, Error, Warn, Info, Debug, and Trace choices. `Info`, for example, accepts
Error, Warn, and Info log events. `Off` suppresses `KAPTAN_LOG` on MAVLink
without suppressing typed `KAPTAN_ERROR` reports or events sent to another
sink. There is no separate enable flag and no MAVLink-specific set of extra
runtime log levels. F722 uses the validated filter recovered at boot, falling
back to its board factory when the journal is empty or invalid.
Severity remains a typed `Severity` value inside the board and runtime. Generic
schema discovery exposes its named choices one at a time to the configurator,
so the UI does not carry a field-key-specific severity list; numeric conversion
exists only at the generic configuration transport and snapshot boundary. The MAVLink `level`
field intentionally remains `uint8_t`: this generator represents XML enums as
four-byte Rust values, so attaching an XML enum would widen the generated
payload type. The adapter performs the checked one-byte conversion at that
unavoidable wire boundary instead.
Events retain the compact typed `Level` and sink configuration retains the
compact typed `LevelFilter`. Generic schema discovery exposes the filter's
named choices to the configurator, so the UI carries no hardcoded field list.
Only the MAVLink adapter converts the five event levels to MAVLink's numeric
severity representation; Trace uses MAVLink Debug plus the Trace flag.
Cargo `log/max_level_*` features compile calls away before the facade sees
them; they are not a supported filtering mechanism. The build policy keeps
that decision out of workspace dependencies.
The SITL facade ingress is transport-neutral and unfiltered. The tested
The log ingress is transport-neutral and unfiltered. The tested
`SitlConfigRuntime` composition applies the configured MAVLink policy when
fanning an event into that sink and proves `log!` reaches a generated
`KAPTAN_LOG` payload, and rejected configuration replies enqueue a generated
`KAPTAN_ERROR` companion without replacing their authoritative result.
fanning an event into that sink and proves `kaptan_log::info!` reaches a
generated `KAPTAN_LOG` payload.
`kaptan-sitl serve` does not yet attach a config/diagnostic MAVLink link. F722
images accept only the unsigned configuration service on their directly wired
local UART; that direct-wire assumption does not extend to a shared or remote
+6 -7
View File
@@ -43,17 +43,16 @@ grants no additional command authority.
Diagnostic events also feed a separate bounded Runtime logs view at
`http://127.0.0.1:4861`. That view retains individual `KAPTAN_LOG` and
`KAPTAN_ERROR` events in receive order with local receive time, severity, and
`KAPTAN_ERROR` events in receive order with local receive time, level, and
readable text. Packet inspection remains a latest-value view; it is not used as
log history.
The custom Kaptan definitions are aggregated by `definitions/kaptan.xml` from
`kaptan/config.xml`, `kaptan/log.xml`, `kaptan/error.xml`, and
`kaptan/shutdown.xml`.
`kaptan/config.xml`, `kaptan/log.xml`, and `kaptan/error.xml`.
`KAPTAN_LOG` is an outbound human-readable four-field record. `KAPTAN_ERROR`
is its separate bounded error channel and preserves the originating error
severity, while `KAPTAN_SHUTDOWN` represents only
a completed orderly output-inhibited shutdown. Configuration messages back the
current target-described web configurator and the future signed GCS. See
is the separate bounded channel for typed firmware errors, carrying a stable
explicit ID plus the `file:line` of its propagation boundary, and needs no
redundant level field. Configuration messages back the current target-described
web configurator and the future signed GCS. See
[Configuration](configuration.md), [Logs](logs.md), and [Errors](errors.md) for
the exact contracts.
+1 -1
View File
@@ -8,7 +8,7 @@ This page collects stable limits and points to the detailed contracts.
| F722 persistence | One bounded fixed scalar snapshot inside each slot of a power-loss-safe two-slot flash journal. |
| Config transaction | One owner-bound RAM transaction with typed scalar writes; commit is restart-required. |
| Config discovery | Correlated `KAPTAN_CONFIG_INFO_REQUEST` / `KAPTAN_CONFIG_INFO`, then exact-index descriptor/enum-option and field-value requests. |
| Diagnostics | `KAPTAN_LOG` (51020) for ordinary severity/text; `KAPTAN_ERROR` (51021) for a bounded `{ level, path[96], additional_information[64] }` error; and `KAPTAN_SHUTDOWN` (51022) for completed orderly output-inhibited shutdowns. |
| Diagnostics | `KAPTAN_LOG` (51020) for macro-produced level/text; `KAPTAN_ERROR` (51021) for a bounded typed `{ error_id, raised_at[96] }` firmware error. |
| F722 configuration | Target-described fields and board factory defaults; the current local transport is a directly wired unsigned UART with motor outputs physically inhibited. |
Read [Configuration](configuration.md) for the field/consumer matrix and GCS