Motion Master 6.0.0-alpha.86
Next-generation motion control software
Loading...
Searching...
No Matches
foe_error.h
Go to the documentation of this file.
1#pragma once
2
3// ── The error type of FieldbusDriver::readFile / writeFile. ────────────────────────────────────
4//
5// Every other FieldbusDriver operation reports failure as std::expected<T, std::string>. That is
6// the deliberate default (see CLAUDE.md, the no-exceptions mandate): std::string is right
7// everywhere a caller only logs, forwards, or shows the error. FoE is the one surface that has
8// earned more, and this header stood ready — deliberately unused — from 2026-07-17 until the
9// caller it was written for arrived.
10//
11// That caller is the firmware installation procedure (libs/node/firmware_procedures.cc), which
12// branches on the error twice, and neither branch can be written against a string without matching
13// on its wording:
14// • It retries a Transient failure (a packet desync, or a bootloader still warming up after the
15// device entered BOOT) and aborts immediately on a Permanent one (no such file, undersized
16// buffer) — the exact distinction the `retry` tag carries.
17// • It removes a file before rewriting it, and FileNotFound means the removal already had
18// nothing to do — a success, not a failure.
19// String-matching a message to decide either is the smell that says a surface earned a
20// structured error.
21//
22// Because FoeError keeps a string face (operator<<, .message, .what()), the forwarding callers that
23// only display it changed by one word (.error() → .error().message) and nothing rippled further —
24// which was the point of shaping it this way before there was a caller.
25//
26// This is the same design split the wider ecosystem settled on, and the mandate is a deliberate
27// pick of one side of it:
28// • Rust codified it as two libraries — anyhow::Error (a message you propagate and Display) for
29// leaf/application code, thiserror (typed enums you `match` on) for a library whose callers
30// branch. "std::string by default, FoeError where you branch" is that rule verbatim.
31// • Go is the same shape: fmt.Errorf("...: %w", err) (string + wrap) almost everywhere, and only
32// sentinel errors + errors.Is/errors.As at the specific call site that must distinguish a
33// cause.
34// The rejected third option is a single generic error type swept across the whole codebase: its
35// enum is either too coarse to serve the branching caller (who then string-matches anyway) or a
36// grab-bag union of every layer's failure modes (coupling with no abstraction win). So the rule is
37// "typed per surface, or a string" with nothing in between — and this type is named for its surface
38// (FoeError, not OpError) so both the type and the coupling to it stay local to FoE callers. See
39// NEXTGEN.md, Session 2026-07-17.
40//
41// The `retry` tag is the one axis worth sharing across surfaces (an SDO error would carry the same
42// Transient/Permanent distinction), so it rides *alongside* the FoE-specific kind rather than being
43// folded into it — the absl::Status insight (a universal code + operation-specific detail), sized
44// down to the one distinction callers here actually act on.
45// ──────────────────────────────────────────────────────────────────────────────────────────────
46
47#include <cstdint>
48#include <format>
49#include <ostream>
50#include <string>
51#include <string_view>
52
53namespace mm::comm {
54
59enum class Retry {
60 Transient,
61 Permanent,
62};
63
73
75inline std::string_view foeReason(FoeErrorKind kind) {
76 switch (kind) {
78 return "no response";
80 return "file not found";
82 return "buffer too small";
84 return "packet number mismatch";
86 return "FoE error";
87 }
88 return "FoE error";
89}
90
95 switch (kind) {
98 return Retry::Permanent;
102 return Retry::Transient;
103 }
104 return Retry::Transient;
105}
106
109struct FoeError {
112 std::string message;
113
115 const std::string& what() const { return message; }
116};
117
119inline std::ostream& operator<<(std::ostream& os, const FoeError& e) { return os << e.message; }
120
124inline FoeError makeFoeError(FoeErrorKind kind, std::string_view op, uint16_t slave,
125 std::string_view filename) {
126 return {kind, foeRetry(kind),
127 std::format("{} slave {} '{}' failed ({})", op, slave, filename, foeReason(kind))};
128}
129
130} // namespace mm::comm
Definition al_status_codes.cc:5
std::string_view foeReason(FoeErrorKind kind)
Short human-readable reason for a FoE kind (no surrounding punctuation).
Definition foe_error.h:75
FoeErrorKind
Specific File-over-EtherCAT failure kind. Transport-agnostic: a SOEM driver decodes it from a negated...
Definition foe_error.h:66
@ NoResponse
The slave answered nothing (work counter 0 — timeout or not ready).
@ FileNotFound
Firmware recognises no such filename.
@ PacketMismatch
Packet-number desync mid-transfer.
@ Protocol
Generic FoE-protocol error with no finer classification.
@ BufferTooSmall
The slave's buffer cannot hold the requested transfer.
Retry foeRetry(FoeErrorKind kind)
Retry disposition implied by a FoE kind. A missing file or an undersized buffer is a fixed condition ...
Definition foe_error.h:94
FoeError makeFoeError(FoeErrorKind kind, std::string_view op, uint16_t slave, std::string_view filename)
Builds a fully-formed FoeError from a decoded kind plus call context. Keeps the message wording ident...
Definition foe_error.h:124
std::ostream & operator<<(std::ostream &os, const FoeError &e)
Streams the message, so ASSERT_TRUE(r) << r.error() and spdlog {} work unchanged.
Definition foe_error.h:119
Retry
Retry disposition of a failed operation: whether re-issuing the identical call could plausibly succee...
Definition foe_error.h:59
@ Permanent
The identical call will fail the same way (no such file, undersized buffer).
@ Transient
A fresh attempt may succeed (protocol desync, slave not yet ready after BOOT).
A structured FoE failure. String-like where a caller only forwards it (operator<<,...
Definition foe_error.h:109
std::string message
Definition foe_error.h:112
Retry retry
Definition foe_error.h:111
FoeErrorKind kind
Definition foe_error.h:110
const std::string & what() const
Exception-shaped accessor for the message, for call sites that prefer .what().
Definition foe_error.h:115