Skip to main content

printobserver_sdk/
contract.rs

1//! Generated by `contract-codegen` from the checked-in contract schemas.
2//!
3//! This file is the Rust client's request and response types.
4//!
5//! Do not edit by hand: change the contracts, regenerate with `just
6//! generate-clients`, and commit what that writes.
7
8use std::collections::BTreeMap;
9
10use serde::{Deserialize, Serialize};
11
12use crate::{Client, ClientError};
13
14/// The version of the server contract these types were generated from: the
15/// version of the workspace this crate is compiled from. A published crate
16/// reports the release version it was built at, and a crate compiled from a
17/// checkout reports that checkout's workspace version.
18pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
19
20/// Every operation this client exposes a method for, which is every operation
21/// the server declares and no other.
22pub const OPERATION_NAMES: [&str; 29] = [
23    "prints",
24    "status",
25    "context",
26    "image",
27    "history",
28    "events",
29    "manifest_get",
30    "look",
31    "manifest_set",
32    "spools",
33    "spool",
34    "printer_history",
35    "preflight",
36    "pause",
37    "resume",
38    "cancel",
39    "start_print",
40    "set_feedrate_factor",
41    "set_flowrate_factor",
42    "set_tool_target_c",
43    "set_bed_target_c",
44    "set_fan_percent",
45    "acknowledge_failure",
46    "close_print",
47    "register_spool",
48    "load_spool",
49    "unload_spool",
50    "adjust_spool",
51    "retire_spool",
52];
53
54/// A field that was sent, null included, as what was sent.
55fn present<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
56where
57    D: serde::Deserializer<'de>,
58    T: Deserialize<'de>,
59{
60    T::deserialize(deserializer).map(Some)
61}
62
63/// What an operator's acknowledgement of a failure event asks for next.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
65pub enum AcknowledgementDisposition {
66    /// Carry on printing.
67    #[serde(rename = "continue")]
68    Continue,
69    /// Carry on printing, watched more closely.
70    #[serde(rename = "watch")]
71    Watch,
72    /// The print should stop. Recorded for whoever may cancel it: the
73    /// acknowledgement itself asks nothing of the machine.
74    #[serde(rename = "stop")]
75    Stop,
76}
77
78/// What one mutating request left behind it.
79#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80pub struct ActionAnswer {
81    /// The bounded intervention it opened, when it opened one.
82    #[serde(default, skip_serializing_if = "Option::is_none")]
83    pub intervention: Option<Intervention>,
84    /// What the printer said, when the request reached it and it refused.
85    #[serde(default, skip_serializing_if = "Option::is_none")]
86    pub printer_refusal: Option<String>,
87    /// The record of the request and the decision taken on it. A rejected
88    /// request's rejection is here, carrying its reason, the value asked for
89    /// and the range allowed, so a caller can ask again inside the range.
90    pub record: ActionRecord,
91}
92
93/// An accepted action reached the printer.
94#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
95pub struct ActionExecutedPayload {
96    /// The action's identifier.
97    pub action_id: ActionId,
98    /// The bounded intervention it opened, when it opened one.
99    #[serde(default, skip_serializing_if = "Option::is_none")]
100    pub intervention_id: Option<InterventionId>,
101}
102
103/// A lowercase hyphenated version 7 UUID identifying one requested action.
104pub type ActionId = String;
105
106/// One action of the closed vocabulary, named without its payload.
107///
108/// This is what a safety envelope grants and what a policy rejection names;
109/// the
110/// payload lives on `PrintAction` itself.
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112pub enum ActionKind {
113    /// Pause the print.
114    #[serde(rename = "pause")]
115    Pause,
116    /// Resume the print.
117    #[serde(rename = "resume")]
118    Resume,
119    /// Cancel the print.
120    #[serde(rename = "cancel")]
121    Cancel,
122    /// Start a print of a named file.
123    #[serde(rename = "start_print")]
124    StartPrint,
125    /// Set the feedrate factor.
126    #[serde(rename = "set_feedrate_factor")]
127    SetFeedrateFactor,
128    /// Set the flowrate factor.
129    #[serde(rename = "set_flowrate_factor")]
130    SetFlowrateFactor,
131    /// Set a tool's target temperature.
132    #[serde(rename = "set_tool_target_c")]
133    SetToolTargetC,
134    /// Set the bed's target temperature.
135    #[serde(rename = "set_bed_target_c")]
136    SetBedTargetC,
137    /// Set the fan percentage.
138    #[serde(rename = "set_fan_percent")]
139    SetFanPercent,
140    /// Acknowledge a failure event.
141    #[serde(rename = "acknowledge_failure")]
142    AcknowledgeFailure,
143    /// Close a print record printobserver left open.
144    #[serde(rename = "close_print")]
145    ClosePrint,
146    /// Register a spool.
147    #[serde(rename = "register_spool")]
148    RegisterSpool,
149    /// Load a spool onto a tool.
150    #[serde(rename = "load_spool")]
151    LoadSpool,
152    /// Unload a spool from its tool.
153    #[serde(rename = "unload_spool")]
154    UnloadSpool,
155    /// Correct a spool's remaining figure.
156    #[serde(rename = "adjust_spool")]
157    AdjustSpool,
158    /// Retire a spool.
159    #[serde(rename = "retire_spool")]
160    RetireSpool,
161}
162
163/// One request, the decision taken on it, and what became of it.
164///
165/// `executed_at` and `outcome` are both optional and are absent together: a
166/// record is written when the decision is taken, which is before — and, for a
167/// rejected request, instead of — anything reaching the printer.
168#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
169pub struct ActionRecord {
170    /// The decision policy took on it.
171    pub decision: PolicyDecision,
172    /// When it reached the printer, if it did.
173    #[serde(default, skip_serializing_if = "Option::is_none")]
174    pub executed_at: Option<Timestamp>,
175    /// This record's identifier, minted by the store.
176    pub id: ActionId,
177    /// What the printer made of it, if it reached the printer.
178    #[serde(default, skip_serializing_if = "Option::is_none")]
179    pub outcome: Option<ExecutionOutcome>,
180    /// The print this request was made against.
181    pub print_id: PrintId,
182    /// The request itself.
183    pub request: ActionRequest,
184}
185
186/// Policy refused an action.
187#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
188pub struct ActionRejectedPayload {
189    /// The action's identifier.
190    pub action_id: ActionId,
191    /// The whole decision, carrying which rejection it was.
192    pub decision: PolicyDecision,
193}
194
195/// One request an actor made, at the instant it made it.
196#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
197pub struct ActionRequest {
198    /// What was asked for.
199    pub action: PrintAction,
200    /// Who asked.
201    pub actor: Actor,
202    /// When they asked.
203    pub requested_at: Timestamp,
204}
205
206/// An actor asked for an action.
207#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
208pub struct ActionRequestedPayload {
209    /// What was asked for.
210    pub action: PrintAction,
211    /// The action's identifier.
212    pub action_id: ActionId,
213    /// Who asked.
214    pub actor: Actor,
215}
216
217/// Who asked for something.
218#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
219pub enum Actor {
220    /// The supervising agent, naming its session.
221    #[serde(rename = "agent")]
222    Agent {
223        /// The supervision session the agent is acting in.
224        session_name: String,
225    },
226    /// A person.
227    #[serde(rename = "operator")]
228    Operator,
229    /// The supervisor itself.
230    #[serde(rename = "system")]
231    System,
232}
233
234/// An actor class, which is what a safety envelope grants actions to.
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
236pub enum ActorClass {
237    /// The supervising agent.
238    #[serde(rename = "agent")]
239    Agent,
240    /// A person.
241    #[serde(rename = "operator")]
242    Operator,
243    /// The supervisor itself.
244    #[serde(rename = "system")]
245    System,
246}
247
248/// One thing an adjustment may change.
249pub type Adjustable = String;
250
251/// The agent's written record of one supervision turn.
252///
253/// This is deliberately not how the agent acts: acting is a
254/// `PrintAction` that policy rules on.
255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
256pub struct AgentAssessment {
257    /// How sure the agent is.
258    pub confidence: Confidence,
259    /// What the agent did.
260    pub did: String,
261    /// Whether the agent is escalating to a person.
262    pub escalating: bool,
263    /// Whether the print should carry on.
264    pub should_continue: bool,
265    /// One line saying what is happening.
266    pub summary: String,
267    /// Why it did it.
268    pub why: String,
269}
270
271/// The agent wrote down what it made of a turn.
272#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
273pub struct AgentAssessmentPayload {
274    /// What the agent answered with.
275    pub assessment: AgentAssessment,
276    /// The session the turn ran in.
277    pub session_name: String,
278}
279
280/// The camera gave no usable frame for a look or a periodic observation,
281/// however many times it was asked.
282///
283/// Written in place of the look or the observation it was for, so that no
284/// frame the camera could not give is handed to the agent as one it did.
285#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
286pub struct CameraDegradedPayload {
287    /// How many frames were fetched, every one of them bad.
288    pub attempts: i64,
289    /// Why the last of them was bad.
290    pub cause: FrameCause,
291    /// What was wrong with each, in words.
292    pub detail: String,
293}
294
295/// Somebody took a fresh look at the print.
296///
297/// Written down before the camera is asked for a frame, so that a frame the
298/// camera would not give is recorded against a look that is already in the
299/// history; the frame itself, when there is one, is this event's image.
300#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
301pub struct CameraLookPayload {
302    /// The events for the print that arrived while it waited, and that it
303    /// handed to whoever looked, oldest first.
304    #[serde(default, skip_serializing_if = "Option::is_none")]
305    pub delivered: Option<Vec<EventId>>,
306    /// How long the look waited for something to happen before it was taken,
307    /// in whole seconds.
308    pub waited_s: i64,
309}
310
311/// The durable identity of one debit, so that a replay can never debit twice.
312#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
313#[serde(tag = "key")]
314pub enum ChargeKey {
315    /// One segment of a print this system watched, on one tool.
316    #[serde(rename = "print")]
317    Print {
318        /// The print.
319        print_id: PrintId,
320        /// Which segment: zero from the print's start, and one more each time
321        /// a mid-print swap or a filament-change signal closes the current
322        /// one.
323        segment: i64,
324        /// The tool, in the printer's own numbering.
325        tool: i64,
326    },
327    /// One print this system never saw, on one tool.
328    #[serde(rename = "unobserved")]
329    Unobserved {
330        /// The file that was printed.
331        file_name: String,
332        /// The file's tally generation, which advances each time the
333        /// printer's
334        /// tally for the file is reset.
335        generation: i64,
336        /// Where the file lives, in the printer's own vocabulary.
337        origin: String,
338        /// The print's ordinal in the file's tally — completed and failed
339        /// prints counted up to and including this one — within that
340        /// generation.
341        print_number: i64,
342        /// The tool, in the printer's own numbering.
343        tool: i64,
344    },
345}
346
347/// How the length a charge debits was worked out.
348#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
349pub enum ChargeMethod {
350    /// A completed print: the length the file's analysis gives.
351    #[serde(rename = "slicer_estimate")]
352    SlicerEstimate,
353    /// The extrusion the file's own moves add up to, up to a position in it.
354    #[serde(rename = "gcode_extrusion")]
355    GcodeExtrusion,
356    /// The analysed length scaled by how far into the file the print got; the
357    /// fallback used only when the file itself could not be read.
358    #[serde(rename = "file_fraction")]
359    FileFraction,
360    /// A completed print this system never saw: the file's analysed length.
361    #[serde(rename = "unobserved_estimate")]
362    UnobservedEstimate,
363}
364
365/// Filament a tool fed after an unconfirmed filament change was held for the
366/// next spool loaded onto that tool, or confirmed on it.
367#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
368pub struct ChargePendedPayload {
369    /// What was held.
370    pub pending: PendingCharge,
371}
372
373/// What a charge took from a spool, and the whole calculation behind it.
374///
375/// Present or absent as one: a debit names its spool exactly when it carries
376/// the grams, the figures they were worked out with, and the spool's figure
377/// either side of them.
378#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
379pub struct ChargedFilament {
380    /// The density the grams were worked out with, in grams per cubic
381    /// centimetre.
382    pub density_g_cm3: f64,
383    /// The diameter the grams were worked out with, in millimetres.
384    pub diameter_mm: f64,
385    /// What the length weighs, in grams; what is subtracted from the spool.
386    pub grams: f64,
387    /// The spool's figure after it, in grams.
388    pub remaining_after_g: f64,
389    /// The spool's figure before the charge, in grams.
390    pub remaining_before_g: f64,
391    /// The spool charged.
392    pub spool_id: SpoolId,
393}
394
395/// One mark a spool command cleared, and the spool it was cleared from.
396#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
397pub struct ClearedMark {
398    /// The reason that no longer stands.
399    pub reason: UnverifiedReason,
400    /// The spool the mark was cleared from.
401    pub spool_id: SpoolId,
402}
403
404/// How sure the agent is.
405///
406/// A closed vocabulary rather than a number, because a number invites a
407/// precision the agent does not have.
408#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
409pub enum Confidence {
410    /// Not sure.
411    #[serde(rename = "low")]
412    Low,
413    /// Fairly sure.
414    #[serde(rename = "medium")]
415    Medium,
416    /// Sure.
417    #[serde(rename = "high")]
418    High,
419}
420
421/// What a context read answers.
422///
423/// The latest image is **materialized** here rather than left as the handle
424/// `PrintContext` carries: the one thing a supervision turn does with an
425/// image is look at it, and a handle is not something anything can open. The
426/// path is absolute on this server's own filesystem, exactly as
427/// `ImageAnswer`'s is, and is absent when there is no image or when the
428/// record is intact and the file is not.
429#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
430pub struct ContextAnswer {
431    /// Everything a supervision turn is given about the print.
432    pub context: PrintContext,
433    /// Where the latest image's bytes are, absolute on this host.
434    #[serde(default, skip_serializing_if = "Option::is_none")]
435    pub image_path: Option<String>,
436}
437
438/// The failure detector's health, and since when it has been so: `since` is
439/// `null` exactly while the status is `unchecked`.
440#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
441#[serde(tag = "status")]
442pub enum DetectorHealth {
443    /// Nothing checks it: no detector health address is configured, or no
444    /// check has been made yet.
445    #[serde(rename = "unchecked")]
446    Unchecked {
447        /// Nothing: an unchecked detector has no instant it became so.
448        since: (),
449    },
450    /// It answered its health check, from `since` on.
451    #[serde(rename = "available")]
452    Available {
453        /// One shape of the contracts, which say nothing more about it.
454        since: Timestamp,
455    },
456    /// It did not answer its health check, from `since` on.
457    #[serde(rename = "unavailable")]
458    Unavailable {
459        /// One shape of the contracts, which say nothing more about it.
460        since: Timestamp,
461    },
462}
463
464/// The failure detector's own health changed.
465///
466/// The printer's rather than any print's, so it is written with no print.
467#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
468pub struct DetectorHealthChangedPayload {
469    /// What it was answered, in words.
470    pub detail: String,
471    /// What the check found.
472    pub status: DetectorStatus,
473}
474
475/// What a check of the failure detector's own health found.
476#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
477pub enum DetectorStatus {
478    /// It answered its health check.
479    #[serde(rename = "available")]
480    Available,
481    /// It did not: refused, reset, answered otherwise, or not in time.
482    #[serde(rename = "unavailable")]
483    Unavailable,
484}
485
486/// The envelope intersected with the print's manifest.
487///
488/// This is what context reports, so that the agent can see its own limits
489/// before it asks. Computing one is the supervision core's; this crate
490/// declares
491/// the shape and nothing that produces it.
492#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
493pub struct EffectiveBounds {
494    /// The range each adjustable may be set to, inclusive.
495    pub allowed: BTreeMap<String, Range>,
496}
497
498/// What this server answers when it will not do what it was asked.
499#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
500pub struct ErrorAnswer {
501    /// One line saying why, in the words of whatever refused it.
502    pub error: String,
503}
504
505/// Whose error a job's error is.
506#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
507pub enum ErrorSource {
508    /// The printer's own message: its firmware stopped the job.
509    #[serde(rename = "printer")]
510    Printer,
511    /// The host's: it lost the line to the printer, or failed in itself.
512    #[serde(rename = "host")]
513    Host,
514}
515
516/// A lowercase hyphenated version 7 UUID identifying one event.
517pub type EventId = String;
518
519/// The name one kind of event is written down under: lowercase `snake_case`,
520/// declared by the domain that owns the event.
521pub type EventKind = String;
522
523/// One event, as the store holds it and the server serves it.
524///
525/// `raw` holds the bytes exactly as received for an externally sourced event
526/// and is absent for an internally raised one — it is what makes the history
527/// auditable when a normalization turns out to be wrong. `print_id` is
528/// optional, because an externally sourced event may name no print this
529/// system
530/// knows.
531#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
532pub struct EventRecord {
533    /// This event's identifier, minted by the store.
534    pub id: EventId,
535    /// The image it arrived with, when it arrived with one.
536    #[serde(default, skip_serializing_if = "Option::is_none")]
537    pub image: Option<ImageRef>,
538    /// Which event this is.
539    pub kind: EventKind,
540    /// What it carries, in the form its kind declares.
541    pub payload: serde_json::Value,
542    /// The print it belongs to, when it belongs to one.
543    #[serde(default, skip_serializing_if = "Option::is_none")]
544    pub print_id: Option<PrintId>,
545    /// The bytes exactly as received, for an externally sourced event.
546    #[serde(default, skip_serializing_if = "Option::is_none")]
547    pub raw: Option<RawBytes>,
548    /// When it was received.
549    pub received_at: Timestamp,
550    /// Where it came from.
551    pub source: EventSource,
552}
553
554/// Where an event came from, as the bare string the domain that raised it
555/// declares for itself.
556pub type EventSource = String;
557
558/// What an events read answers: the events stored after its cursor that pass
559/// its filters, oldest first, and where the next read picks up.
560#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
561pub struct EventsAnswer {
562    /// The last event this read examined, whether or not it passed the
563    /// filters, given as `after` to read on from it; null when the log held
564    /// nothing to start after. Always sent, as null when it is that.
565    pub cursor: Option<EventId>,
566    /// The events, oldest first in the order they were stored, each exactly
567    /// as
568    /// a history read serves it.
569    pub events: Vec<EventRecord>,
570    /// The watched print's end as stored, whatever the filters said, and null
571    /// while it is open. Absent from a read that names no print.
572    #[serde(
573        default,
574        skip_serializing_if = "Option::is_none",
575        deserialize_with = "present"
576    )]
577    pub print_ended: Option<Option<PrintEnded>>,
578}
579
580/// What happened when an accepted action reached the printer.
581#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
582pub enum ExecutionOutcome {
583    /// The printer took it.
584    #[serde(rename = "succeeded")]
585    Succeeded,
586    /// The printer did not, for this reason.
587    #[serde(rename = "failed")]
588    Failed {
589        /// Why it failed.
590        reason: String,
591    },
592}
593
594/// A filament change was signalled during a print.
595#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
596pub struct FilamentChangeSignalledPayload {
597    /// The position the spool was charged up to, and how fresh it is, when
598    /// one
599    /// was known; absent, there is no position and so no claim about one.
600    #[serde(default, skip_serializing_if = "Option::is_none")]
601    pub position: Option<SignalPosition>,
602    /// The kind of the alert that carried the signal.
603    pub source_kind: EventKind,
604    /// The spool that was on it, when one was tracked.
605    #[serde(default, skip_serializing_if = "Option::is_none")]
606    pub spool_id: Option<SpoolId>,
607    /// The tool the change was signalled for, when it is known.
608    #[serde(default, skip_serializing_if = "Option::is_none")]
609    pub tool: Option<i64>,
610}
611
612/// Filament was charged against a spool, or recorded uncharged.
613#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
614pub struct FilamentConsumedPayload {
615    /// The file's analysed length for the tool, in millimetres, when it had
616    /// one.
617    #[serde(default, skip_serializing_if = "Option::is_none")]
618    pub analysed_length_mm: Option<f64>,
619    /// The durable identity of this debit.
620    pub charge_key: ChargeKey,
621    /// The spool charged and the whole calculation of what it was charged;
622    /// absent when nothing was tracked on the tool, and the filament was
623    /// recorded uncharged.
624    #[serde(default, skip_serializing_if = "Option::is_none")]
625    pub charged: Option<ChargedFilament>,
626    /// How much was fed, in millimetres.
627    pub length_mm: f64,
628    /// How the length was worked out.
629    pub method: ChargeMethod,
630    /// Anything a reader needs to make sense of the figure.
631    #[serde(default, skip_serializing_if = "Option::is_none")]
632    pub note: Option<String>,
633    /// How far into the file the charge runs, when that was read.
634    #[serde(default, skip_serializing_if = "Option::is_none")]
635    pub position: Option<FilePosition>,
636    /// The tool that fed it, as its key names it.
637    pub tool: i64,
638}
639
640/// A file name a printer's own file API can be asked for: no path separator,
641/// no NUL byte, no `.` or `..` segment, no drive prefix, and not empty.
642pub type FileName = String;
643
644/// How far into its file a print had got, and when that was read.
645#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
646pub struct FilePosition {
647    /// The byte the printer had read the file up to.
648    pub bytes: i64,
649    /// When the position was read.
650    pub observed_at: Timestamp,
651    /// The file's size in bytes, when it was known.
652    #[serde(default, skip_serializing_if = "Option::is_none")]
653    pub size_bytes: Option<i64>,
654}
655
656/// The printer's tally for a file went down — the file was deleted and
657/// uploaded again — so the file's tally generation advanced, and the new
658/// count
659/// is that generation's growth from zero.
660///
661/// The rebaseline itself marks no spool; only an `unverified` disposition
662/// does.
663#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
664pub struct FileTallyRebaselinedPayload {
665    /// What became of the prints the new generation counts.
666    pub disposition: RebaselineDisposition,
667    /// The file whose tally went down.
668    pub file_name: String,
669    /// The failed prints the new tally counts.
670    pub new_failures: i64,
671    /// The generation it is in now.
672    pub new_generation: i64,
673    /// The completed prints the new tally counts.
674    pub new_successes: i64,
675    /// The failed prints the old tally counted.
676    pub old_failures: i64,
677    /// The generation the tally was in.
678    pub old_generation: i64,
679    /// The completed prints the old tally counted.
680    pub old_successes: i64,
681    /// Where it lives, in the printer's own vocabulary.
682    pub origin: String,
683}
684
685/// Why a camera frame was not one to hand the agent.
686#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
687pub enum FrameCause {
688    /// It decoded, but is near-uniform grey: its grey-level spread is below
689    /// the configured blank-frame bound.
690    #[serde(rename = "blank")]
691    Blank,
692    /// It is not a whole image: a strict decode refused it.
693    #[serde(rename = "undecodable")]
694    Undecodable,
695}
696
697/// One tool heater whose target was above zero.
698#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
699pub struct HeatedTool {
700    /// Its target, in degrees Celsius.
701    pub target_c: f64,
702    /// The tool's number.
703    pub tool: i64,
704}
705
706/// One heater, as a source reported it.
707///
708/// Every field is optional, because a source that reports no heater at all
709/// reports none of these; each is a plausibility-ranged reported value in
710/// degrees Celsius.
711#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
712pub struct HeaterSnapshot {
713    /// The temperature the heater is at.
714    #[serde(default, skip_serializing_if = "Option::is_none")]
715    pub actual_c: Option<Reported>,
716    /// The offset applied to this heater's target.
717    #[serde(default, skip_serializing_if = "Option::is_none")]
718    pub offset_c: Option<Reported>,
719    /// The temperature the heater is driving towards.
720    #[serde(default, skip_serializing_if = "Option::is_none")]
721    pub target_c: Option<Reported>,
722}
723
724/// What a history read answers.
725#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
726pub struct HistoryAnswer {
727    /// The print's events, newest first.
728    pub events: Vec<EventRecord>,
729}
730
731/// What an image read answers.
732///
733/// The path is absolute on this server's own filesystem, and is the whole of
734/// the answer about the bytes: nothing here renders them, encodes them or
735/// serves them. It is absent when the record is intact and the file is not,
736/// which is a different answer from there being no such image.
737#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
738pub struct ImageAnswer {
739    /// Where its bytes are, absolute on this host.
740    #[serde(default, skip_serializing_if = "Option::is_none")]
741    pub path: Option<String>,
742    /// The record.
743    pub record: ImageRecord,
744}
745
746/// A lowercase hyphenated version 7 UUID identifying one image.
747pub type ImageId = String;
748
749/// One image, stored beside the event it arrived with.
750///
751/// `relative_path` is relative to the configured state directory, so that the
752/// store stays portable. Materializing an image resolves that to an absolute
753/// path; nothing in this system renders image bytes into JSON.
754#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
755pub struct ImageRecord {
756    /// How many bytes it is.
757    pub byte_len: i64,
758    /// The content type it was served as.
759    pub content_type: String,
760    /// The event it arrived with.
761    pub event_id: EventId,
762    /// When it was fetched.
763    pub fetched_at: Timestamp,
764    /// This image's identifier, minted by the store.
765    pub id: ImageId,
766    /// The print it belongs to.
767    pub print_id: PrintId,
768    /// Where it lives, relative to the configured state directory.
769    pub relative_path: String,
770    /// The SHA-256 of its bytes, lowercase hexadecimal.
771    pub sha256: String,
772    /// Where it was fetched from, when it was fetched from somewhere.
773    #[serde(default, skip_serializing_if = "Option::is_none")]
774    pub source_url: Option<String>,
775}
776
777/// The handle an image travels in context under.
778#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
779pub struct ImageRef {
780    /// The image's identifier.
781    pub id: ImageId,
782    /// The SHA-256 of its bytes, lowercase hexadecimal.
783    pub sha256: String,
784}
785
786/// One adjustment made for a bounded time.
787///
788/// `prior_value` is read from the printer snapshot taken before the change,
789/// and
790/// is absent when the printer reported none — in which case expiry restores
791/// nothing and the outcome says so rather than guessing a default.
792#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
793pub struct Intervention {
794    /// The action that asked for it.
795    pub action_id: ActionId,
796    /// What it changed.
797    pub adjustable: Adjustable,
798    /// When it was applied.
799    pub applied_at: Timestamp,
800    /// What it was changed to.
801    pub applied_value: f64,
802    /// When it stops standing.
803    pub expires_at: Timestamp,
804    /// This intervention's identifier, minted by the store.
805    pub id: InterventionId,
806    /// What became of it.
807    pub outcome: InterventionOutcome,
808    /// The print it was made against.
809    pub print_id: PrintId,
810    /// What the printer reported before the change, if it reported anything.
811    #[serde(default, skip_serializing_if = "Option::is_none")]
812    pub prior_value: Option<f64>,
813    /// When the prior value was put back, if it was.
814    #[serde(default, skip_serializing_if = "Option::is_none")]
815    pub restored_at: Option<Timestamp>,
816}
817
818/// A bounded intervention expired.
819#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
820pub struct InterventionExpiredPayload {
821    /// What it had changed.
822    pub adjustable: Adjustable,
823    /// The intervention's identifier.
824    pub intervention_id: InterventionId,
825    /// What became of it.
826    pub outcome: InterventionOutcome,
827}
828
829/// A lowercase hyphenated version 7 UUID identifying one bounded
830/// intervention.
831pub type InterventionId = String;
832
833/// What became of a bounded change.
834#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
835pub enum InterventionOutcome {
836    /// It is still in force.
837    #[serde(rename = "still_active")]
838    StillActive,
839    /// The prior value was put back.
840    #[serde(rename = "restored")]
841    Restored,
842    /// There was no prior value to put back.
843    #[serde(rename = "restore_unavailable")]
844    RestoreUnavailable,
845    /// Putting the prior value back failed.
846    #[serde(rename = "restore_failed")]
847    RestoreFailed {
848        /// Why it failed.
849        reason: String,
850    },
851    /// Another intervention replaced it before it expired.
852    #[serde(rename = "superseded")]
853    Superseded {
854        /// The intervention that replaced it.
855        by: InterventionId,
856    },
857}
858
859/// A job's error, and whose it is.
860#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
861pub struct JobError {
862    /// Whose it is.
863    pub source: ErrorSource,
864    /// The error, as the source reported it.
865    pub text: String,
866}
867
868/// What a sliced job declares about itself and about what may be adjusted.
869///
870/// An adjustable the manifest does not name takes the envelope's own range; a
871/// manifest range wider than the envelope's is narrowed to the envelope's and
872/// the narrowing is recorded on the print. A manifest may only narrow.
873#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
874pub struct JobManifest {
875    /// The range each named adjustable may take, inclusive.
876    pub allowed: BTreeMap<String, Range>,
877    /// The file this manifest is about, as the slicer named it.
878    pub file_name: String,
879    /// The material the job is sliced for.
880    pub material: String,
881    /// Whatever else the slicer recorded.
882    pub metadata: BTreeMap<String, String>,
883    /// The nozzle the job is sliced for, in millimetres.
884    pub nozzle_diameter_mm: f64,
885    /// The slicer profile the job was sliced with.
886    pub slicer_profile: String,
887}
888
889/// The job a printer reports it is running.
890///
891/// Every field but `state` is optional: an absent one means the source did
892/// not
893/// report it.
894#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
895pub struct JobSnapshot {
896    /// How far through the print is, as a fraction from zero to one.
897    ///
898    /// Normalized to a fraction here regardless of how the source expresses
899    /// it.
900    #[serde(default, skip_serializing_if = "Option::is_none")]
901    pub completion: Option<Reported>,
902    /// The error the source reports, when it reports one, and whose it is.
903    #[serde(default, skip_serializing_if = "Option::is_none")]
904    pub error: Option<JobError>,
905    /// The whole print's estimated duration, in whole seconds.
906    #[serde(default, skip_serializing_if = "Option::is_none")]
907    pub estimated_print_time_s: Option<i64>,
908    /// The filament the source's analysis of the file says each tool uses;
909    /// empty when the source reported none.
910    #[serde(default, skip_serializing_if = "Option::is_none")]
911    pub filament: Option<Vec<ToolFilament>>,
912    /// The name of the file being printed, as the source reported it.
913    #[serde(default, skip_serializing_if = "Option::is_none")]
914    pub file_name: Option<String>,
915    /// Where the file lives, in the source's own vocabulary.
916    #[serde(default, skip_serializing_if = "Option::is_none")]
917    pub file_origin: Option<String>,
918    /// How far into the file the printer has read, in bytes, when the source
919    /// reported it.
920    #[serde(default, skip_serializing_if = "Option::is_none")]
921    pub file_position_bytes: Option<i64>,
922    /// How long the print has left, in whole seconds.
923    #[serde(default, skip_serializing_if = "Option::is_none")]
924    pub print_time_left_s: Option<i64>,
925    /// How long the print has been running, in whole seconds.
926    #[serde(default, skip_serializing_if = "Option::is_none")]
927    pub print_time_s: Option<i64>,
928    /// The file's size in bytes.
929    #[serde(default, skip_serializing_if = "Option::is_none")]
930    pub size_bytes: Option<i64>,
931    /// The state the source reports the job to be in.
932    pub state: PrinterState,
933}
934
935/// One spool loaded on one tool, as a print started.
936#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
937pub struct LoadedSpool {
938    /// The spool loaded on it.
939    pub spool_id: SpoolId,
940    /// The tool, in the printer's own numbering.
941    pub tool: i64,
942}
943
944/// One fresh look at a print.
945///
946/// An answer rather than a record, so it admits fields beside its own the way
947/// every answer of the server does: a client that could not open the frame's
948/// path says so in a field of its own beside the rest of the answer.
949#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
950pub struct Look {
951    /// The events for this print that arrived while the look waited, oldest
952    /// first; the look returned early when there were any.
953    #[serde(default, skip_serializing_if = "Option::is_none")]
954    pub arrived: Option<Vec<EventRecord>>,
955    /// Whether the print is paused and the pause is the detector's, which is
956    /// what an adjustment asked for now would be applied under.
957    pub detector_paused: bool,
958    /// The look itself, as it was written into the print's history, carrying
959    /// the frame as its image when there is one.
960    pub event: EventRecord,
961    /// The frame the camera gave, absent when no camera is configured or it
962    /// gave none — which the history records as a port failure.
963    #[serde(default, skip_serializing_if = "Option::is_none")]
964    pub frame: Option<ImageRef>,
965    /// The absolute path the frame's bytes are at, on the supervisor's host.
966    #[serde(default, skip_serializing_if = "Option::is_none")]
967    pub image_path: Option<String>,
968    /// The job the printer reports now, absent when it could not be read.
969    #[serde(default, skip_serializing_if = "Option::is_none")]
970    pub job: Option<JobSnapshot>,
971    /// The printer's state now, absent when it could not be read.
972    #[serde(default, skip_serializing_if = "Option::is_none")]
973    pub printer: Option<PrinterSnapshot>,
974}
975
976/// An external body arrived that could not be read.
977///
978/// This kind always carries its `raw` bytes, and it exists so that an alert
979/// this system cannot read is written down rather than dropped.
980#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
981pub struct MalformedExternalEventPayload {
982    /// One line saying why the body could not be read.
983    pub detail: String,
984}
985
986/// What a manifest read or write answers.
987#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
988pub struct ManifestAnswer {
989    /// The manifest, when the print has one.
990    #[serde(default, skip_serializing_if = "Option::is_none")]
991    pub manifest: Option<JobManifest>,
992    /// Every range this manifest asked wider than the envelope allows,
993    /// narrowed
994    /// to the envelope's — recorded on the print, so nobody has to wonder
995    /// later
996    /// which bound applied.
997    pub narrowings: Vec<ManifestNarrowing>,
998}
999
1000/// One adjustable whose manifest range was wider than the envelope's.
1001#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1002pub struct ManifestNarrowing {
1003    /// The adjustable that was narrowed.
1004    pub adjustable: Adjustable,
1005    /// The range that stands.
1006    pub applied: Range,
1007    /// The range the manifest asked for.
1008    pub requested: Range,
1009}
1010
1011/// Obico reported a print failure.
1012///
1013/// The two instants are optional because Obico's own field for each is a Unix
1014/// timestamp number, an empty string, or absent, and the last two both mean
1015/// the
1016/// producer reported no instant. An absent field here is that, never an epoch
1017/// date standing in for it.
1018#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1019pub struct ObicoFailureAlertPayload {
1020    /// When the print ended, when Obico reported an instant for it.
1021    #[serde(default, skip_serializing_if = "Option::is_none")]
1022    pub ended_at: Option<Timestamp>,
1023    /// The file being printed, when Obico named one.
1024    #[serde(default, skip_serializing_if = "Option::is_none")]
1025    pub file_name: Option<String>,
1026    /// Whether Obico called it a warning rather than a failure.
1027    pub is_warning: bool,
1028    /// Obico's own identifier for the print, when it named one.
1029    #[serde(default, skip_serializing_if = "Option::is_none")]
1030    pub obico_print_id: Option<i64>,
1031    /// Whether Obico paused the print itself.
1032    pub print_paused: bool,
1033    /// When the print started, when Obico reported an instant for it.
1034    #[serde(default, skip_serializing_if = "Option::is_none")]
1035    pub started_at: Option<Timestamp>,
1036}
1037
1038/// The kind of printer notification Obico sent, normalized.
1039#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1040pub enum ObicoNotificationType {
1041    /// A print started.
1042    #[serde(rename = "started")]
1043    Started,
1044    /// A print finished.
1045    #[serde(rename = "done")]
1046    Done,
1047    /// A print was cancelled.
1048    #[serde(rename = "cancelled")]
1049    Cancelled,
1050    /// A print was paused.
1051    #[serde(rename = "paused")]
1052    Paused,
1053    /// A print was resumed.
1054    #[serde(rename = "resumed")]
1055    Resumed,
1056    /// The printer is waiting for a filament change.
1057    #[serde(rename = "filament_change")]
1058    FilamentChange,
1059    /// A heater cooled down.
1060    #[serde(rename = "heater_cooled")]
1061    HeaterCooled,
1062    /// A heater reached its target.
1063    #[serde(rename = "heater_target")]
1064    HeaterTarget,
1065}
1066
1067/// Obico sent a printer notification.
1068///
1069/// The two instants are optional for the same reason
1070/// `ObicoFailureAlertPayload`'s are, and are absent along with the rest of
1071/// the print's fields when the notification is about no print at all.
1072#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1073pub struct ObicoPrinterNotificationPayload {
1074    /// When the print ended, when Obico reported an instant for it.
1075    #[serde(default, skip_serializing_if = "Option::is_none")]
1076    pub ended_at: Option<Timestamp>,
1077    /// The file being printed, when the notification is about one.
1078    #[serde(default, skip_serializing_if = "Option::is_none")]
1079    pub file_name: Option<String>,
1080    /// Which notification it is.
1081    pub notification_type: ObicoNotificationType,
1082    /// Obico's own identifier for the print, when the notification is about
1083    /// one.
1084    #[serde(default, skip_serializing_if = "Option::is_none")]
1085    pub obico_print_id: Option<i64>,
1086    /// When the print started, when Obico reported an instant for it.
1087    #[serde(default, skip_serializing_if = "Option::is_none")]
1088    pub started_at: Option<Timestamp>,
1089}
1090
1091/// An operator acknowledged an event.
1092#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1093pub struct OperatorAcknowledgementPayload {
1094    /// The event being acknowledged.
1095    pub acknowledged_event_id: EventId,
1096    /// What the operator asked for next.
1097    pub disposition: AcknowledgementDisposition,
1098}
1099
1100/// Filament a tool fed after a filament change nobody has confirmed, held
1101/// until
1102/// somebody says which spool fed it.
1103///
1104/// A length rather than grams, because the grams follow the spool that takes
1105/// it.
1106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1107pub struct PendingCharge {
1108    /// The file's analysed length for the tool, in millimetres, when it had
1109    /// one.
1110    #[serde(default, skip_serializing_if = "Option::is_none")]
1111    pub analysed_length_mm: Option<f64>,
1112    /// The print segment the filament was fed in, which names the print and
1113    /// the tool that fed it.
1114    pub charge_key: PrintSegmentKey,
1115    /// How much was fed, in millimetres.
1116    pub length_mm: f64,
1117    /// How that length was worked out.
1118    pub method: ChargeMethod,
1119    /// Anything a reader needs to make sense of the figure.
1120    #[serde(default, skip_serializing_if = "Option::is_none")]
1121    pub note: Option<String>,
1122    /// When it was held.
1123    pub pended_at: Timestamp,
1124    /// How far into the file the segment ran, when that was read.
1125    #[serde(default, skip_serializing_if = "Option::is_none")]
1126    pub position: Option<FilePosition>,
1127}
1128
1129/// One pending charge a spool command took.
1130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1131pub struct PendingTaken {
1132    /// The key it is charged under.
1133    pub charge_key: ChargeKey,
1134    /// The print it was fed in.
1135    pub print_id: PrintId,
1136}
1137
1138/// The supervisor looked in on an active print on its own schedule, with no
1139/// detector having said anything about it.
1140///
1141/// What a failure detector never alerts on — a progress that stopped moving,
1142/// a
1143/// heater whose target fell to nothing mid-print, a fan cut at a bridge, a
1144/// wall
1145/// going thin — is still on the machine and in the camera's frame, so every
1146/// active print is given one of these each interval and a turn on it. It
1147/// carries what the printer reported as it was taken; the frame the camera
1148/// gave
1149/// at the same moment, untagged by any detector, is this event's image.
1150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1151pub struct PeriodicObservationPayload {
1152    /// How often these are taken, in whole seconds.
1153    pub interval_s: i64,
1154    /// The job the printer reported as the observation was taken, absent when
1155    /// it could not be read.
1156    #[serde(default, skip_serializing_if = "Option::is_none")]
1157    pub job: Option<JobSnapshot>,
1158    /// The printer's state as the observation was taken, absent when it could
1159    /// not be read — which the history records as a port failure.
1160    #[serde(default, skip_serializing_if = "Option::is_none")]
1161    pub printer: Option<PrinterSnapshot>,
1162}
1163
1164/// The decision policy took on one request.
1165#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1166pub enum PolicyDecision {
1167    /// The request may proceed.
1168    #[serde(rename = "accepted")]
1169    Accepted,
1170    /// The request may not, for this reason.
1171    #[serde(rename = "rejected")]
1172    Rejected(RejectionReason),
1173}
1174
1175/// A port failed while one event was being handled.
1176///
1177/// The event is named rather than implied, so that a reader holding an
1178/// event's
1179/// identifier reaches every failure recorded while that event was being
1180/// handled. A failure recorded here is one the handling survived: the event
1181/// is
1182/// already in the history by the time any of these sites is reached, and the
1183/// loop goes on to handle the next event.
1184#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1185pub struct PortFailurePayload {
1186    /// What the port said about it, in the port's own words.
1187    pub detail: String,
1188    /// The event whose handling reached the failing call.
1189    pub event_id: EventId,
1190    /// Where it failed.
1191    pub site: PortFailureSite,
1192}
1193
1194/// Where a port failed while an event was being handled.
1195///
1196/// A closed set of exactly the sites at which a failure has nowhere else to
1197/// be
1198/// recorded. The printer's action methods record theirs on the
1199/// `ActionRecord` the request minted, and a restoring
1200/// call records its own on the `Intervention` it was
1201/// expiring; those are not sites here, because a second record of them would
1202/// be
1203/// a second version of one fact.
1204#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1205pub enum PortFailureSite {
1206    /// Reading the printer's own state.
1207    #[serde(rename = "printer_snapshot")]
1208    PrinterSnapshot,
1209    /// Reading the job the printer reports it is running.
1210    #[serde(rename = "printer_job")]
1211    PrinterJob,
1212    /// Writing the image the event arrived with.
1213    #[serde(rename = "image_write")]
1214    ImageWrite,
1215    /// Running the supervision turn the event prompted.
1216    #[serde(rename = "supervision_turn")]
1217    SupervisionTurn,
1218    /// Taking a fresh frame from the camera, for a look at the print or for a
1219    /// periodic observation of it.
1220    #[serde(rename = "camera_look")]
1221    CameraLook,
1222    /// Telling the detector that paused the print its detection was handled.
1223    #[serde(rename = "detector_acknowledgement")]
1224    DetectorAcknowledgement,
1225}
1226
1227/// How fresh the position a filament change charged up to is.
1228#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1229pub enum PositionFreshness {
1230    /// Read from the job as the signal was handled: where the spool stopped.
1231    #[serde(rename = "read_at_signal")]
1232    ReadAtSignal,
1233    /// The last position recorded before the signal, the job reporting none
1234    /// as it was handled: the spool may have fed past it.
1235    #[serde(rename = "last_recorded")]
1236    LastRecorded,
1237}
1238
1239/// What a filament check found for one print.
1240#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1241pub struct Preflight {
1242    /// When the check was made.
1243    pub checked_at: Timestamp,
1244    /// The file the print is of.
1245    pub file_name: String,
1246    /// What it found for each tool.
1247    pub tools: Vec<PreflightTool>,
1248    /// The worst of the tools' verdicts, in the order insufficient,
1249    /// unverified spool, no analysis, warned, overridden, unchecked, passed.
1250    pub verdict: PreflightVerdict,
1251}
1252
1253/// What a filament check answers.
1254#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1255pub struct PreflightAnswer {
1256    /// What the check found, read now.
1257    pub preflight: Preflight,
1258}
1259
1260/// The filament check before a start was made.
1261#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1262pub struct PreflightCheckedPayload {
1263    /// The start it was made for, when it was made for one.
1264    #[serde(default, skip_serializing_if = "Option::is_none")]
1265    pub action_id: Option<ActionId>,
1266    /// What the check found.
1267    pub preflight: Preflight,
1268}
1269
1270/// What a print needs from one spool, in grams: both figures a comparison
1271/// with what the spool holds uses, or neither.
1272#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1273pub struct PreflightNeed {
1274    /// The margin asked for beyond that.
1275    pub buffer_g: f64,
1276    /// What the print needs from the spool, by the file's analysis and the
1277    /// spool's own diameter and density.
1278    pub required_g: f64,
1279}
1280
1281/// What a check that refuses a start found for one tool.
1282#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1283#[serde(tag = "verdict")]
1284pub enum PreflightObjection {
1285    /// The spool holds less than the print needs and its buffer.
1286    #[serde(rename = "insufficient")]
1287    Insufficient {
1288        /// The spool's marks at the check, oldest first, so a spool both
1289        /// short
1290        /// and unverified says both.
1291        #[serde(default, skip_serializing_if = "Option::is_none")]
1292        marks: Option<Vec<UnverifiedMark>>,
1293        /// What the print needs from it.
1294        need: PreflightNeed,
1295        /// What the spool holds, in grams.
1296        remaining_g: f64,
1297        /// The spool.
1298        spool: PreflightSpool,
1299    },
1300    /// The spool's figure is in doubt.
1301    #[serde(rename = "unverified_spool")]
1302    UnverifiedSpool {
1303        /// Every reason the spool's figure is in doubt, oldest first; never
1304        /// empty.
1305        // llmlint: ignore[invalid_states_unrepresentable] See suppressions.toml.
1306        marks: Vec<UnverifiedMark>,
1307        /// What the print needs from it, when the file's analysis says.
1308        #[serde(default, skip_serializing_if = "Option::is_none")]
1309        need: Option<PreflightNeed>,
1310        /// What the spool holds, in grams.
1311        remaining_g: f64,
1312        /// The spool.
1313        spool: PreflightSpool,
1314    },
1315    /// The file has no analysis, so what the spool must hold is unknown.
1316    #[serde(rename = "no_analysis")]
1317    NoAnalysis {
1318        /// What the spool holds, in grams.
1319        remaining_g: f64,
1320        /// The spool.
1321        spool: PreflightSpool,
1322    },
1323}
1324
1325/// The spool a check judged, named beside its identifier so a reader need
1326/// not look it up.
1327#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1328pub struct PreflightSpool {
1329    /// The spool's identifier.
1330    pub id: SpoolId,
1331    /// The spool's name.
1332    pub name: String,
1333}
1334
1335/// What a filament check found for one tool.
1336#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1337pub struct PreflightTool {
1338    /// What the check found for it, with the evidence that finding rests on.
1339    pub result: PreflightToolResult,
1340    /// The tool, in the printer's own numbering.
1341    pub tool: i64,
1342}
1343
1344/// What a filament check found for one tool, tagged by its verdict, each
1345/// verdict carrying exactly the evidence it rests on.
1346#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1347#[serde(tag = "verdict")]
1348pub enum PreflightToolResult {
1349    /// The spool holds what the print needs and its buffer, and its figure is
1350    /// not in doubt.
1351    #[serde(rename = "passed")]
1352    Passed {
1353        /// What the print needs from it.
1354        need: PreflightNeed,
1355        /// What the spool holds, in grams.
1356        remaining_g: f64,
1357        /// The spool.
1358        spool: PreflightSpool,
1359    },
1360    /// No tracked spool is loaded on the tool, so nothing was checked.
1361    #[serde(rename = "unchecked")]
1362    Unchecked,
1363    /// The spool holds less than the print needs and its buffer.
1364    #[serde(rename = "insufficient")]
1365    Insufficient {
1366        /// The spool's marks at the check, oldest first, so a spool both
1367        /// short
1368        /// and unverified says both.
1369        #[serde(default, skip_serializing_if = "Option::is_none")]
1370        marks: Option<Vec<UnverifiedMark>>,
1371        /// What the print needs from it.
1372        need: PreflightNeed,
1373        /// What the spool holds, in grams.
1374        remaining_g: f64,
1375        /// The spool.
1376        spool: PreflightSpool,
1377    },
1378    /// The spool's figure is in doubt.
1379    #[serde(rename = "unverified_spool")]
1380    UnverifiedSpool {
1381        /// Every reason the spool's figure is in doubt, oldest first; never
1382        /// empty.
1383        // llmlint: ignore[invalid_states_unrepresentable] See suppressions.toml.
1384        marks: Vec<UnverifiedMark>,
1385        /// What the print needs from it, when the file's analysis says.
1386        #[serde(default, skip_serializing_if = "Option::is_none")]
1387        need: Option<PreflightNeed>,
1388        /// What the spool holds, in grams.
1389        remaining_g: f64,
1390        /// The spool.
1391        spool: PreflightSpool,
1392    },
1393    /// `OctoPrint`'s analysis of the file had not appeared, so what the spool
1394    /// must hold is unknown.
1395    #[serde(rename = "no_analysis")]
1396    NoAnalysis {
1397        /// What the spool holds, in grams.
1398        remaining_g: f64,
1399        /// The spool.
1400        spool: PreflightSpool,
1401    },
1402    /// The start went ahead despite this objection, because an operator said
1403    /// so.
1404    #[serde(rename = "overridden")]
1405    Overridden {
1406        /// What the check objected to.
1407        objection: PreflightObjection,
1408    },
1409    /// The check is set to warn, and let the start go ahead despite this
1410    /// objection.
1411    #[serde(rename = "warned")]
1412    Warned {
1413        /// What the check objected to.
1414        objection: PreflightObjection,
1415    },
1416}
1417
1418/// What a filament check found, for one tool or for a whole print.
1419#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1420pub enum PreflightVerdict {
1421    /// The spool holds what the print needs and its buffer.
1422    #[serde(rename = "passed")]
1423    Passed,
1424    /// No tracked spool is loaded on the tool, so nothing was checked.
1425    #[serde(rename = "unchecked")]
1426    Unchecked,
1427    /// The spool holds less than the print needs and its buffer.
1428    #[serde(rename = "insufficient")]
1429    Insufficient,
1430    /// The spool's figure is in doubt.
1431    #[serde(rename = "unverified_spool")]
1432    UnverifiedSpool,
1433    /// `OctoPrint`'s analysis of the file had still not appeared when the
1434    /// start's wait for it elapsed, or has not appeared yet when the check is
1435    /// only read.
1436    #[serde(rename = "no_analysis")]
1437    NoAnalysis,
1438    /// The start went ahead despite the check, because the actor said so.
1439    #[serde(rename = "overridden")]
1440    Overridden,
1441    /// The check is set to warn, and let an insufficient, unverified or
1442    /// unanalysed start go ahead.
1443    #[serde(rename = "warned")]
1444    Warned,
1445}
1446
1447/// The whole vocabulary an actor may ask for, and there is no other.
1448#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1449#[serde(tag = "action")]
1450pub enum PrintAction {
1451    /// Pause the print.
1452    #[serde(rename = "pause")]
1453    Pause {
1454        /// Who is asking.
1455        actor: Actor,
1456        /// Why the actor is asking for this.
1457        reason: String,
1458    },
1459    /// Resume the print.
1460    #[serde(rename = "resume")]
1461    Resume {
1462        /// Who is asking.
1463        actor: Actor,
1464        /// Why the actor is asking for this.
1465        reason: String,
1466    },
1467    /// Cancel the print.
1468    #[serde(rename = "cancel")]
1469    Cancel {
1470        /// Who is asking.
1471        actor: Actor,
1472        /// Why the actor is asking for this.
1473        reason: String,
1474    },
1475    /// Start a print of a named file, with a manifest.
1476    #[serde(rename = "start_print")]
1477    StartPrint {
1478        /// Who is asking.
1479        actor: Actor,
1480        /// The file to print, validated as a name a file API can be asked
1481        /// for.
1482        file_name: FileName,
1483        /// The manifest this print is bounded by.
1484        manifest: JobManifest,
1485        /// Start despite an insufficient or unverified spool, or a file
1486        /// `OctoPrint` has not analysed; the start's own reason is the
1487        /// override's reason.
1488        #[serde(default, skip_serializing_if = "Option::is_none")]
1489        override_filament_check: Option<bool>,
1490        /// Why the actor is asking for this.
1491        reason: String,
1492    },
1493    /// Set the feedrate factor, where one means one hundred percent.
1494    #[serde(rename = "set_feedrate_factor")]
1495    SetFeedrateFactor {
1496        /// Who is asking.
1497        actor: Actor,
1498        /// How long the change stands for, in whole seconds.
1499        #[serde(default, skip_serializing_if = "Option::is_none")]
1500        duration_s: Option<i64>,
1501        /// The multiplier asked for.
1502        factor: f64,
1503        /// Why the actor is asking for this.
1504        reason: String,
1505    },
1506    /// Set the flowrate factor, where one means one hundred percent.
1507    #[serde(rename = "set_flowrate_factor")]
1508    SetFlowrateFactor {
1509        /// Who is asking.
1510        actor: Actor,
1511        /// How long the change stands for, in whole seconds.
1512        #[serde(default, skip_serializing_if = "Option::is_none")]
1513        duration_s: Option<i64>,
1514        /// The multiplier asked for.
1515        factor: f64,
1516        /// Why the actor is asking for this.
1517        reason: String,
1518    },
1519    /// Set one tool's target temperature.
1520    #[serde(rename = "set_tool_target_c")]
1521    SetToolTargetC {
1522        /// Who is asking.
1523        actor: Actor,
1524        /// How long the change stands for, in whole seconds.
1525        #[serde(default, skip_serializing_if = "Option::is_none")]
1526        duration_s: Option<i64>,
1527        /// Why the actor is asking for this.
1528        reason: String,
1529        /// The target temperature asked for, in degrees Celsius.
1530        target_c: f64,
1531        /// The tool, in the printer's own numbering.
1532        tool: i64,
1533    },
1534    /// Set the bed's target temperature.
1535    #[serde(rename = "set_bed_target_c")]
1536    SetBedTargetC {
1537        /// Who is asking.
1538        actor: Actor,
1539        /// How long the change stands for, in whole seconds.
1540        #[serde(default, skip_serializing_if = "Option::is_none")]
1541        duration_s: Option<i64>,
1542        /// Why the actor is asking for this.
1543        reason: String,
1544        /// The target temperature asked for, in degrees Celsius.
1545        target_c: f64,
1546    },
1547    /// Set the part-cooling fan percentage.
1548    #[serde(rename = "set_fan_percent")]
1549    SetFanPercent {
1550        /// Who is asking.
1551        actor: Actor,
1552        /// How long the change stands for, in whole seconds.
1553        #[serde(default, skip_serializing_if = "Option::is_none")]
1554        duration_s: Option<i64>,
1555        /// The percentage asked for.
1556        percent: f64,
1557        /// Why the actor is asking for this.
1558        reason: String,
1559    },
1560    /// Acknowledge a failure event, with a disposition.
1561    #[serde(rename = "acknowledge_failure")]
1562    AcknowledgeFailure {
1563        /// Who is asking.
1564        actor: Actor,
1565        /// What to do next.
1566        disposition: AcknowledgementDisposition,
1567        /// The event being acknowledged.
1568        event_id: EventId,
1569        /// Why the actor is asking for this.
1570        reason: String,
1571    },
1572    /// Close a print record printobserver left open, with the outcome the
1573    /// operator judges it ended with. It asks nothing of the machine and
1574    /// charges no spool: the record's consumption was never seen to end.
1575    #[serde(rename = "close_print")]
1576    ClosePrint {
1577        /// Who is asking.
1578        actor: Actor,
1579        /// How the print ended, in the operator's judgement.
1580        outcome: PrintOutcome,
1581        /// Why the actor is asking for this.
1582        reason: String,
1583    },
1584}
1585
1586/// Everything a supervision turn is given about one print.
1587#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1588pub struct PrintContext {
1589    /// The limits in force, so that the agent can see them before it asks.
1590    pub bounds: EffectiveBounds,
1591    /// The failure detector's health, and since when it has been so.
1592    #[serde(default, skip_serializing_if = "Option::is_none")]
1593    pub detector: Option<DetectorHealth>,
1594    /// The interventions still active.
1595    pub interventions: Vec<Intervention>,
1596    /// The job, when a snapshot could be taken.
1597    #[serde(default, skip_serializing_if = "Option::is_none")]
1598    pub job: Option<JobSnapshot>,
1599    /// The most recent image, when there is one.
1600    #[serde(default, skip_serializing_if = "Option::is_none")]
1601    pub latest_image: Option<ImageRef>,
1602    /// The manifest, when the print has one.
1603    #[serde(default, skip_serializing_if = "Option::is_none")]
1604    pub manifest: Option<JobManifest>,
1605    /// The print itself.
1606    pub print: PrintRecord,
1607    /// The printer, when a snapshot could be taken.
1608    #[serde(default, skip_serializing_if = "Option::is_none")]
1609    pub printer: Option<PrinterSnapshot>,
1610    /// A bounded list of this print's events, newest first.
1611    pub recent_events: Vec<EventRecord>,
1612}
1613
1614/// One print's end, as its `print_ended` event stored it.
1615#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1616pub struct PrintEnded {
1617    /// The `print_ended` event.
1618    pub event_id: EventId,
1619    /// How the print ended.
1620    pub outcome: PrintOutcome,
1621    /// When that event was received.
1622    pub received_at: Timestamp,
1623}
1624
1625/// A print ended.
1626#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1627pub struct PrintEndedPayload {
1628    /// How far into its file it last read, when that was read.
1629    #[serde(default, skip_serializing_if = "Option::is_none")]
1630    pub last_position: Option<FilePosition>,
1631    /// How it ended.
1632    pub outcome: PrintOutcome,
1633    /// Why it ended, in the words the end was recorded with.
1634    pub reason: String,
1635    /// The state the printer was in as it ended.
1636    pub state: PrinterState,
1637}
1638
1639/// A lowercase hyphenated version 7 UUID identifying one print.
1640pub type PrintId = String;
1641
1642/// How a print ended.
1643#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1644pub enum PrintOutcome {
1645    /// It ran to its end.
1646    #[serde(rename = "completed")]
1647    Completed,
1648    /// Somebody cancelled it.
1649    #[serde(rename = "cancelled")]
1650    Cancelled,
1651    /// It stopped on an error.
1652    #[serde(rename = "failed")]
1653    Failed,
1654    /// It stopped with no cancel, no error the printer reported and no
1655    /// finish. Charged as a failed print is: what the file's moves extruded
1656    /// up
1657    /// to the last file position seen.
1658    #[serde(rename = "interrupted")]
1659    Interrupted,
1660}
1661
1662/// One print, and the record supervision keys from.
1663///
1664/// The provider's own print id is carried beside this record's identifier
1665/// rather than as it, because a print may be observed before the provider
1666/// that reports it has one.
1667#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1668pub struct PrintRecord {
1669    /// Why it ended, if it has.
1670    #[serde(default, skip_serializing_if = "Option::is_none")]
1671    pub end_reason: Option<String>,
1672    /// When it ended, if it has.
1673    #[serde(default, skip_serializing_if = "Option::is_none")]
1674    pub ended_at: Option<Timestamp>,
1675    /// The file being printed, as the source reported it.
1676    #[serde(default, skip_serializing_if = "Option::is_none")]
1677    pub file_name: Option<String>,
1678    /// This print's identifier, minted by the store.
1679    pub id: PrintId,
1680    /// The longest running time, in whole seconds, the printer reported for
1681    /// this print's job at a read that found it printing rather than paused.
1682    ///
1683    /// The time a job has spent printing only grows, pause or no pause, so a
1684    /// job of this print's file reporting less — by more than
1685    /// `listing::JOB_IDENTITY_TOLERANCE_S` — is a later job. Absent until a
1686    /// read finds the job printing.
1687    #[serde(default, skip_serializing_if = "Option::is_none")]
1688    pub job_print_time_s: Option<i64>,
1689    /// When the printer's job this print records began, as the printer's own
1690    /// running time put it at the latest read that found the job running or
1691    /// paused: the instant of that read, less the running time it reported.
1692    ///
1693    /// `OctoPrint` counts a pause into the running time until the job resumes
1694    /// and then takes the pause back out, so a pause moves this later: a read
1695    /// putting it further than `listing::JOB_IDENTITY_TOLERANCE_S` from the
1696    /// one
1697    /// recorded resets it, rather than it being fixed once. Absent when no
1698    /// read has
1699    /// found this print's job reporting a running time — a print opened by an
1700    /// alert or a start before any read, and every print recorded before this
1701    /// was.
1702    #[serde(default, skip_serializing_if = "Option::is_none")]
1703    pub job_started_at: Option<Timestamp>,
1704    /// Every manifest range this print narrowed to the envelope's.
1705    pub narrowings: Vec<ManifestNarrowing>,
1706    /// When the print was opened.
1707    pub opened_at: Timestamp,
1708    /// The provider's own identifier for the print, whichever provider
1709    /// reported it, when one has.
1710    #[serde(default, skip_serializing_if = "Option::is_none")]
1711    pub provider_print_id: Option<i64>,
1712    /// The state the print is in.
1713    pub state: PrinterState,
1714}
1715
1716/// One segment of a print this system watched, on one tool: the key of a
1717/// `ChargeKey::Print` debit, and the only key filament held pending can be
1718/// under.
1719#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1720pub struct PrintSegmentKey {
1721    /// The print.
1722    pub print_id: PrintId,
1723    /// Which segment, numbered as `ChargeKey::Print` numbers it.
1724    pub segment: i64,
1725    /// The tool, in the printer's own numbering.
1726    pub tool: i64,
1727}
1728
1729/// A print started.
1730#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1731pub struct PrintStartedPayload {
1732    /// The file being printed, when it is known.
1733    #[serde(default, skip_serializing_if = "Option::is_none")]
1734    pub file_name: Option<String>,
1735    /// The spool loaded on each tool as it started.
1736    pub loaded: Vec<LoadedSpool>,
1737    /// The filament check made before it started, when one was.
1738    #[serde(default, skip_serializing_if = "Option::is_none")]
1739    pub preflight: Option<Preflight>,
1740    /// The action that started it; absent when the print was observed rather
1741    /// than started through this system.
1742    #[serde(default, skip_serializing_if = "Option::is_none")]
1743    pub started_by: Option<ActionId>,
1744}
1745
1746/// A print that was already open when spool tracking was installed: it is
1747/// never charged, and this says why it carries no charge.
1748#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1749pub struct PrintUntrackedPayload {
1750    /// Why the print carries no charge.
1751    pub reason: String,
1752}
1753
1754/// The printer's connection changed into or out of `offline` or `error`: a
1755/// serial disconnect, a reconnect, a printer error.
1756///
1757/// The printer's rather than any print's, so it is written with no print.
1758#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1759pub struct PrinterConnectionChangedPayload {
1760    /// What the watch read, in words.
1761    pub detail: String,
1762    /// The state the previous read found.
1763    pub from: PrinterState,
1764    /// The state this read found.
1765    pub to: PrinterState,
1766}
1767
1768/// The printer reset: its firmware restarted, and whatever it held is gone.
1769///
1770/// Recorded on each reset the printer pushed, one while idle included, which
1771/// changes nothing a read of its state reports. The printer's rather than any
1772/// print's, so it is written with no print; `during_print` names the print it
1773/// happened during, when one was running.
1774#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1775pub struct PrinterResetPayload {
1776    /// What the watch knew of it, in words.
1777    pub detail: String,
1778    /// The print that was running when it reset, `null` when none was.
1779    #[serde(default, skip_serializing_if = "Option::is_none")]
1780    pub during_print: Option<PrintId>,
1781}
1782
1783/// A printer, as a source reported it at one instant.
1784#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1785pub struct PrinterSnapshot {
1786    /// The bed heater, when the printer reports one.
1787    #[serde(default, skip_serializing_if = "Option::is_none")]
1788    pub bed: Option<HeaterSnapshot>,
1789    /// The chamber heater, when the printer reports one.
1790    #[serde(default, skip_serializing_if = "Option::is_none")]
1791    pub chamber: Option<HeaterSnapshot>,
1792    /// The state the printer is in.
1793    pub connection: PrinterState,
1794    /// The part-cooling fan, in percent.
1795    #[serde(default, skip_serializing_if = "Option::is_none")]
1796    pub fan_percent: Option<Reported>,
1797    /// The feedrate multiplier, where one means one hundred percent.
1798    #[serde(default, skip_serializing_if = "Option::is_none")]
1799    pub feedrate_factor: Option<Reported>,
1800    /// The flowrate multiplier, where one means one hundred percent.
1801    #[serde(default, skip_serializing_if = "Option::is_none")]
1802    pub flowrate_factor: Option<Reported>,
1803    /// The instant this observation was taken.
1804    pub observed_at: Timestamp,
1805    /// The tool heaters, indexed by tool number.
1806    pub tools: Vec<HeaterSnapshot>,
1807}
1808
1809/// The state a source reports a printer or a print to be in.
1810///
1811/// The `unknown` arm exists so that a state nobody anticipated is recorded
1812/// carrying the source's own word for it rather than lost.
1813#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1814pub enum PrinterState {
1815    /// Connected and idle.
1816    #[serde(rename = "operational")]
1817    Operational,
1818    /// Printing, but paused.
1819    #[serde(rename = "paused")]
1820    Paused,
1821    /// Printing.
1822    #[serde(rename = "printing")]
1823    Printing,
1824    /// Cancelling a print.
1825    #[serde(rename = "cancelling")]
1826    Cancelling,
1827    /// In an error state.
1828    #[serde(rename = "error")]
1829    Error,
1830    /// Not reachable.
1831    #[serde(rename = "offline")]
1832    Offline,
1833    /// A state this vocabulary does not name, in the source's own word for
1834    /// it.
1835    #[serde(rename = "unknown")]
1836    Unknown(String),
1837}
1838
1839/// What the prints read answers: every print, and the one the printer is
1840/// running.
1841///
1842/// An open print's recorded `state` is the state it was opened in, which is
1843/// `printing` whether or not the machine is paused now; what the machine is
1844/// doing is the status read's answer.
1845#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1846pub struct PrintsAnswer {
1847    /// The print the printer's current job belongs to, present exactly when
1848    /// the
1849    /// printer reports a job it is printing or has paused. Absent when the
1850    /// printer could not be read.
1851    #[serde(default, skip_serializing_if = "Option::is_none")]
1852    pub active: Option<PrintId>,
1853    /// Every print this server holds, ended or not, most recently opened
1854    /// first.
1855    pub prints: Vec<PrintRecord>,
1856}
1857
1858/// An inclusive pair of 64-bit floats.
1859#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1860pub struct Range {
1861    /// The highest value the range admits, inclusive.
1862    pub max: f64,
1863    /// The lowest value the range admits, inclusive.
1864    pub min: f64,
1865}
1866
1867/// Bytes exactly as received, base64-encoded.
1868pub type RawBytes = String;
1869
1870/// What became of the prints a file's new tally generation counts.
1871#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1872pub enum RebaselineDisposition {
1873    /// They were accounted as prints this system never saw.
1874    #[serde(rename = "accounted")]
1875    Accounted,
1876    /// It could not be told which of them this system already settled, so the
1877    /// loaded spool was marked unverified rather than charged on a guess.
1878    #[serde(rename = "unverified")]
1879    Unverified,
1880}
1881
1882/// Why a request was refused.
1883///
1884/// Each rejection is a distinct variant, so a consumer distinguishes them by
1885/// matching rather than by reading a message.
1886#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1887pub enum RejectionReason {
1888    /// The value asked for is outside the range allowed for that adjustable.
1889    #[serde(rename = "out_of_bounds")]
1890    OutOfBounds {
1891        /// The adjustable that was asked for.
1892        adjustable: Adjustable,
1893        /// The range that was allowed.
1894        allowed: Range,
1895        /// The value that was asked for.
1896        requested: f64,
1897    },
1898    /// This actor class may not request this action at all.
1899    #[serde(rename = "actor_may_not_request")]
1900    ActorMayNotRequest {
1901        /// The action they asked for.
1902        action: ActionKind,
1903        /// The class of the actor that asked.
1904        actor_class: ActorClass,
1905    },
1906    /// There is no active print to act on.
1907    #[serde(rename = "no_active_print")]
1908    NoActivePrint,
1909    /// The printer is not in a state this action is valid from.
1910    #[serde(rename = "invalid_from_state")]
1911    InvalidFromState {
1912        /// The state the printer is in.
1913        state: PrinterState,
1914    },
1915    /// The agent's minimum interval has not elapsed.
1916    #[serde(rename = "min_interval_not_elapsed")]
1917    MinIntervalNotElapsed {
1918        /// The minimum interval, in whole seconds.
1919        interval_s: i64,
1920        /// How long it has been since the last agent action, in whole
1921        /// seconds.
1922        since_last_s: i64,
1923    },
1924    /// The adjustable is not one this printer has.
1925    #[serde(rename = "unsupported_adjustable")]
1926    UnsupportedAdjustable {
1927        /// The adjustable this printer cannot express.
1928        adjustable: Adjustable,
1929    },
1930    /// A start was refused because the spool on one tool holds less than the
1931    /// print needs and its buffer.
1932    #[serde(rename = "insufficient_filament")]
1933    InsufficientFilament {
1934        /// The margin asked for beyond that, in grams.
1935        buffer_g: f64,
1936        /// What the spool holds, in grams.
1937        remaining_g: f64,
1938        /// What the print needs from it, in grams.
1939        required_g: f64,
1940        /// The spool loaded on it.
1941        spool_id: SpoolId,
1942        /// That spool's name.
1943        spool_name: String,
1944        /// The tool whose spool is short.
1945        tool: i64,
1946        /// The spool's oldest mark, when the short spool is also unverified.
1947        #[serde(default, skip_serializing_if = "Option::is_none")]
1948        unverified: Option<UnverifiedReason>,
1949    },
1950    /// A start was refused because the spool on one tool is unverified.
1951    #[serde(rename = "unverified_spool")]
1952    UnverifiedSpool {
1953        /// The spool's oldest mark.
1954        reason: UnverifiedReason,
1955        /// The spool loaded on it.
1956        spool_id: SpoolId,
1957        /// That spool's name.
1958        spool_name: String,
1959        /// The tool whose spool is unverified.
1960        tool: i64,
1961    },
1962    /// A start was refused because `OctoPrint`'s analysis of the file had not
1963    /// appeared when the start's wait for it elapsed.
1964    #[serde(rename = "analysis_unavailable")]
1965    AnalysisUnavailable {
1966        /// The file that has no analysis.
1967        file_name: String,
1968        /// How long the start waited for one, in whole seconds.
1969        waited_s: i64,
1970    },
1971    /// A close was refused because the print record has already ended.
1972    #[serde(rename = "print_already_ended")]
1973    PrintAlreadyEnded,
1974    /// A close was refused because the printer shows a job of the record's
1975    /// file printing, paused or cancelling.
1976    #[serde(rename = "print_still_active")]
1977    PrintStillActive {
1978        /// The state the printer reported that job in.
1979        state: PrinterState,
1980    },
1981    /// A close was refused because an intervention for the print has neither
1982    /// expired nor been restored.
1983    #[serde(rename = "intervention_still_active")]
1984    InterventionStillActive {
1985        /// The intervention still standing.
1986        intervention_id: InterventionId,
1987    },
1988    /// A spool command could not be carried out.
1989    #[serde(rename = "spool_refused")]
1990    SpoolRefused {
1991        /// Why, in a form a client acts on without reading `message`.
1992        cause: SpoolRefusalCause,
1993        /// Why, in a sentence naming what to do instead, for a person.
1994        message: String,
1995    },
1996}
1997
1998/// A value as a source reported it, flagged when it is outside the
1999/// plausibility range this crate declares for its field.
2000#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2001pub struct Reported {
2002    /// One shape of the contracts, which say nothing more about it.
2003    pub out_of_range: bool,
2004    /// One shape of the contracts, which say nothing more about it.
2005    pub value: f64,
2006}
2007
2008/// The position a filament change charged up to, and how fresh it is.
2009#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2010pub struct SignalPosition {
2011    /// Whether it was read when the signal was handled, or is the last one
2012    /// recorded before it.
2013    pub freshness: PositionFreshness,
2014    /// The position.
2015    pub position: FilePosition,
2016}
2017
2018/// A spool's remaining figure was corrected.
2019#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2020pub struct SpoolAdjustedPayload {
2021    /// Who corrected it.
2022    pub actor: Actor,
2023    /// Every mark the correction cleared.
2024    #[serde(default, skip_serializing_if = "Option::is_none")]
2025    pub cleared_marks: Option<Vec<ClearedMark>>,
2026    /// The scale reading the figure was worked out from, when it was given as
2027    /// one.
2028    #[serde(default, skip_serializing_if = "Option::is_none")]
2029    pub gross_g: Option<f64>,
2030    /// Why.
2031    pub reason: String,
2032    /// Its figure after, in grams.
2033    pub remaining_after_g: f64,
2034    /// Its figure before, in grams, after any pending charge it took.
2035    pub remaining_before_g: f64,
2036    /// The spool corrected.
2037    pub spool_id: SpoolId,
2038    /// The pending charges the correction took first, each also its own
2039    /// `filament_consumed` event under its key.
2040    #[serde(default, skip_serializing_if = "Option::is_none")]
2041    pub took_pending: Option<Vec<PendingTaken>>,
2042}
2043
2044/// What one spool's read answers: the spool, and the newest of the printer's
2045/// own events that name it.
2046#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2047pub struct SpoolAnswer {
2048    /// The newest timeline events naming it, newest first.
2049    pub recent: Vec<EventRecord>,
2050    /// The spool, retired or not.
2051    pub spool: SpoolRecord,
2052}
2053
2054/// The whole vocabulary of the spool ledger, and there is no other.
2055#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2056#[serde(tag = "command")]
2057pub enum SpoolCommand {
2058    /// Register a spool.
2059    #[serde(rename = "register_spool")]
2060    RegisterSpool {
2061        /// Who is asking.
2062        actor: Actor,
2063        /// The filament's colour.
2064        #[serde(default, skip_serializing_if = "Option::is_none")]
2065        color: Option<String>,
2066        /// The material's density, in grams per cubic centimetre; the known
2067        /// density of the material when absent, and a material with none
2068        /// known
2069        /// is refused.
2070        #[serde(default, skip_serializing_if = "Option::is_none")]
2071        density_g_cm3: Option<f64>,
2072        /// The filament's diameter, in millimetres; 1.75 when absent.
2073        #[serde(default, skip_serializing_if = "Option::is_none")]
2074        diameter_mm: Option<f64>,
2075        /// The net filament on the spool when full, in grams.
2076        initial_g: f64,
2077        /// The material, such as `PLA`.
2078        material: String,
2079        /// The operator's own label for it.
2080        name: String,
2081        /// Why the actor is asking for this.
2082        reason: String,
2083        /// The filament on it now, in grams; the full figure when absent.
2084        #[serde(default, skip_serializing_if = "Option::is_none")]
2085        remaining_g: Option<f64>,
2086        /// The empty spool's own weight, in grams.
2087        #[serde(default, skip_serializing_if = "Option::is_none")]
2088        tare_g: Option<f64>,
2089    },
2090    /// Load a spool onto a tool.
2091    ///
2092    /// Onto an occupied tool it is a swap, and the spool there is unloaded in
2093    /// the same write. Naming the spool already on the tool confirms it was
2094    /// never swapped, which is accepted only while that tool holds a pending
2095    /// charge or that spool holds a filament-change mark.
2096    #[serde(rename = "load_spool")]
2097    LoadSpool {
2098        /// Who is asking.
2099        actor: Actor,
2100        /// Why the actor is asking for this.
2101        reason: String,
2102        /// The spool to load.
2103        spool_id: SpoolId,
2104        /// The tool, in the printer's own numbering.
2105        tool: i64,
2106    },
2107    /// Unload a spool from its tool.
2108    #[serde(rename = "unload_spool")]
2109    UnloadSpool {
2110        /// Who is asking.
2111        actor: Actor,
2112        /// Why the actor is asking for this.
2113        reason: String,
2114        /// The spool to unload.
2115        spool_id: SpoolId,
2116    },
2117    /// Correct a spool's remaining figure, from exactly one of the two; a
2118    /// command naming both or neither is refused as `spool_refused`.
2119    #[serde(rename = "adjust_spool")]
2120    AdjustSpool {
2121        /// Who is asking.
2122        actor: Actor,
2123        /// A scale reading of the spool, in grams, which needs its tare.
2124        #[serde(default, skip_serializing_if = "Option::is_none")]
2125        gross_g: Option<f64>,
2126        /// Why the actor is asking for this.
2127        reason: String,
2128        /// The filament left, in grams.
2129        #[serde(default, skip_serializing_if = "Option::is_none")]
2130        remaining_g: Option<f64>,
2131        /// The spool to correct.
2132        spool_id: SpoolId,
2133    },
2134    /// Retire a spool, after which it refuses every command.
2135    #[serde(rename = "retire_spool")]
2136    RetireSpool {
2137        /// Who is asking.
2138        actor: Actor,
2139        /// Why the actor is asking for this.
2140        reason: String,
2141        /// The spool to retire.
2142        spool_id: SpoolId,
2143    },
2144}
2145
2146/// What a spool command answers, accepted or refused.
2147///
2148/// A refusal is the same shape and deliberately so: the decision carries the
2149/// grant or the ledger's own reason, and the events are the record the
2150/// refusal
2151/// wrote.
2152#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2153pub struct SpoolCommandAnswer {
2154    /// The decision taken on the command.
2155    pub decision: PolicyDecision,
2156    /// Every event it wrote, in the order they were written.
2157    pub events: Vec<EventRecord>,
2158    /// The spool it was about, as the command left it, when there is one.
2159    #[serde(default, skip_serializing_if = "Option::is_none")]
2160    pub spool: Option<SpoolRecord>,
2161}
2162
2163/// A spool command was refused.
2164#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2165pub struct SpoolCommandRejectedPayload {
2166    /// The command, as it was asked for.
2167    pub command: SpoolCommand,
2168    /// The whole decision, carrying which refusal it was.
2169    pub decision: PolicyDecision,
2170}
2171
2172/// One field of a spool command, as the command names it.
2173#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2174pub enum SpoolField {
2175    /// The operator's own label for the spool.
2176    #[serde(rename = "name")]
2177    Name,
2178    /// The material, such as `PLA`.
2179    #[serde(rename = "material")]
2180    Material,
2181    /// The filament's diameter, in millimetres.
2182    #[serde(rename = "diameter_mm")]
2183    DiameterMm,
2184    /// The material's density, in grams per cubic centimetre.
2185    #[serde(rename = "density_g_cm3")]
2186    DensityGCm3,
2187    /// The net filament on the spool when full, in grams.
2188    #[serde(rename = "initial_g")]
2189    InitialG,
2190    /// The filament on the spool now, in grams.
2191    #[serde(rename = "remaining_g")]
2192    RemainingG,
2193    /// The empty spool's own weight, in grams.
2194    #[serde(rename = "tare_g")]
2195    TareG,
2196    /// A scale reading of the spool, in grams.
2197    #[serde(rename = "gross_g")]
2198    GrossG,
2199}
2200
2201/// A lowercase hyphenated version 7 UUID identifying one registered spool.
2202pub type SpoolId = String;
2203
2204/// Where a spool is in its life.
2205///
2206/// One state rather than a loaded tool beside a retirement instant, because a
2207/// retired spool is on no tool: retiring a loaded spool takes it off.
2208#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2209#[serde(tag = "state")]
2210pub enum SpoolLifecycle {
2211    /// Registered, and loaded on no tool.
2212    #[serde(rename = "shelved")]
2213    Shelved,
2214    /// Loaded on one tool.
2215    #[serde(rename = "loaded")]
2216    Loaded {
2217        /// The tool, in the printer's own numbering.
2218        tool: i64,
2219    },
2220    /// Retired: it takes no further command, and is on no tool.
2221    #[serde(rename = "retired")]
2222    Retired {
2223        /// When it was retired.
2224        at: Timestamp,
2225    },
2226}
2227
2228/// A spool was loaded onto a tool, or confirmed as never having left it.
2229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2230pub struct SpoolLoadedPayload {
2231    /// Who loaded it.
2232    pub actor: Actor,
2233    /// Every mark the load cleared, on either spool.
2234    #[serde(default, skip_serializing_if = "Option::is_none")]
2235    pub cleared_marks: Option<Vec<ClearedMark>>,
2236    /// Why.
2237    pub reason: String,
2238    /// The spool the load swapped off the tool, when it was occupied.
2239    #[serde(default, skip_serializing_if = "Option::is_none")]
2240    pub replaced: Option<SpoolId>,
2241    /// The spool loaded.
2242    pub spool_id: SpoolId,
2243    /// The pending charges the load took, each also its own
2244    /// `filament_consumed` event under its key.
2245    #[serde(default, skip_serializing_if = "Option::is_none")]
2246    pub took_pending: Option<Vec<PendingTaken>>,
2247    /// The tool, in the printer's own numbering.
2248    pub tool: i64,
2249}
2250
2251/// One spool the operator registered, and what is known of it now.
2252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2253pub struct SpoolRecord {
2254    /// The filament's colour, when the operator gave one.
2255    #[serde(default, skip_serializing_if = "Option::is_none")]
2256    pub color: Option<String>,
2257    /// The material's density, in grams per cubic centimetre; above zero.
2258    pub density_g_cm3: f64,
2259    /// The filament's diameter, in millimetres; above zero.
2260    pub diameter_mm: f64,
2261    /// This spool's identifier, minted when it was registered.
2262    pub id: SpoolId,
2263    /// The net filament on the spool when it was full, in grams; above zero.
2264    pub initial_g: f64,
2265    /// Where the spool is in its life: on the shelf, loaded on a tool, or
2266    /// retired — and never both of the last two.
2267    pub lifecycle: SpoolLifecycle,
2268    /// The material, as given and trimmed, such as `PLA`; never empty.
2269    pub material: String,
2270    /// The operator's own label for the spool; never empty.
2271    pub name: String,
2272    /// When the spool was registered.
2273    pub registered_at: Timestamp,
2274    /// The filament left, in grams.
2275    ///
2276    /// Always the initial figure less every charge plus every correction. A
2277    /// charge is never refused or clamped, so this may go below zero.
2278    pub remaining_g: f64,
2279    /// The empty spool's own weight, in grams, when the operator gave it.
2280    #[serde(default, skip_serializing_if = "Option::is_none")]
2281    pub tare_g: Option<f64>,
2282    /// How far the remaining figure can be trusted.
2283    pub trust: SpoolTrust,
2284    /// When anything about the spool last changed.
2285    pub updated_at: Timestamp,
2286}
2287
2288/// Why the spool ledger refused a command, each cause carrying what a client
2289/// needs to choose the next valid request.
2290#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2291#[serde(tag = "cause")]
2292pub enum SpoolRefusalCause {
2293    /// No spool was ever registered under this identifier.
2294    #[serde(rename = "unknown_spool")]
2295    UnknownSpool {
2296        /// The identifier the command named.
2297        spool_id: SpoolId,
2298    },
2299    /// The spool is retired, and takes no further command.
2300    #[serde(rename = "retired_spool")]
2301    RetiredSpool {
2302        /// The retired spool.
2303        spool_id: SpoolId,
2304    },
2305    /// The spool named is already on the tool, and nothing is pending there
2306    /// or
2307    /// marked on it for a load to confirm.
2308    #[serde(rename = "nothing_to_confirm")]
2309    NothingToConfirm {
2310        /// The tool the spool is already on.
2311        tool: i64,
2312    },
2313    /// A figure the command gave is not one the field can hold: not a number,
2314    /// or out of the field's range.
2315    #[serde(rename = "invalid_adjustment_figure")]
2316    InvalidAdjustmentFigure {
2317        /// The field the figure was given for.
2318        field: SpoolField,
2319        /// The figure given; always a number, because a figure JSON cannot
2320        /// carry is refused as it would be written rather than written as
2321        /// `null`.
2322        value: f64,
2323    },
2324    /// A scale reading was given for a spool with no tare, so it cannot be
2325    /// turned into a figure.
2326    #[serde(rename = "tare_required")]
2327    TareRequired {
2328        /// The spool with no tare.
2329        spool_id: SpoolId,
2330    },
2331    /// The material has no known density, so the command needs one.
2332    #[serde(rename = "density_required")]
2333    DensityRequired {
2334        /// The material, as given and trimmed.
2335        material: String,
2336        /// The spool, when the command was about one that exists; absent for
2337        /// a registration.
2338        #[serde(default, skip_serializing_if = "Option::is_none")]
2339        spool_id: Option<SpoolId>,
2340    },
2341    /// A field the command needs was empty or absent.
2342    #[serde(rename = "missing_field")]
2343    MissingField {
2344        /// The field; for a correction naming neither figure, `remaining_g`,
2345        /// which `gross_g` may stand in for.
2346        field: SpoolField,
2347    },
2348    /// The command gave fields only one of which it may carry.
2349    #[serde(rename = "conflicting_fields")]
2350    ConflictingFields {
2351        /// Every field given that conflicts, in the command's own order.
2352        fields: Vec<SpoolField>,
2353    },
2354    /// The command named a tool the printer cannot have.
2355    #[serde(rename = "unknown_tool")]
2356    UnknownTool {
2357        /// The tool named.
2358        tool: i64,
2359    },
2360    /// The spool is loaded on no tool, so there is nothing to unload.
2361    #[serde(rename = "not_loaded")]
2362    NotLoaded {
2363        /// The spool.
2364        spool_id: SpoolId,
2365    },
2366    /// The ledger refused the write the command made, because the spools
2367    /// changed under it; reading them again shows how.
2368    #[serde(rename = "ledger_refused")]
2369    LedgerRefused,
2370}
2371
2372/// A spool was registered.
2373#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2374pub struct SpoolRegisteredPayload {
2375    /// Who registered it.
2376    pub actor: Actor,
2377    /// Why.
2378    pub reason: String,
2379    /// The spool, as it was registered.
2380    pub spool: SpoolRecord,
2381}
2382
2383/// A spool was retired.
2384#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2385pub struct SpoolRetiredPayload {
2386    /// Who retired it.
2387    pub actor: Actor,
2388    /// Why.
2389    pub reason: String,
2390    /// Its figure as it was retired, in grams.
2391    pub remaining_g: f64,
2392    /// The spool retired.
2393    pub spool_id: SpoolId,
2394}
2395
2396/// How far a spool's remaining figure can be trusted.
2397#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2398#[serde(tag = "state")]
2399pub enum SpoolTrust {
2400    /// Every charge against the spool was taken exactly.
2401    #[serde(rename = "verified")]
2402    Verified,
2403    /// At least one thing happened that could not be charged exactly.
2404    #[serde(rename = "unverified")]
2405    Unverified {
2406        /// Every reason the figure is in doubt, oldest first, at most one per
2407        /// distinct reason; never empty.
2408        // llmlint: ignore[invalid_states_unrepresentable] See suppressions.toml.
2409        marks: Vec<UnverifiedMark>,
2410    },
2411}
2412
2413/// A spool was unloaded from its tool.
2414#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2415pub struct SpoolUnloadedPayload {
2416    /// Who unloaded it.
2417    pub actor: Actor,
2418    /// Why.
2419    pub reason: String,
2420    /// The spool unloaded.
2421    pub spool_id: SpoolId,
2422    /// The tool it left.
2423    pub tool: i64,
2424}
2425
2426/// A spool was marked as one whose figure is in doubt.
2427#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2428pub struct SpoolUnverifiedPayload {
2429    /// Why its figure is in doubt.
2430    pub reason: UnverifiedReason,
2431    /// The spool marked.
2432    pub spool_id: SpoolId,
2433}
2434
2435/// What the spools read answers.
2436///
2437/// `at_a_glance` is one line per spool, in the order `spools` lists them,
2438/// composed here so that what is left on each spool is the first thing a
2439/// reader of either rendering meets rather than a figure inside a whole
2440/// record. Its name is what puts it first: both renderings walk an answer's
2441/// fields in name order, ahead of `pending` and `spools`.
2442#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2443pub struct SpoolsAnswer {
2444    /// One line per spool in `spools`, in the same order.
2445    pub at_a_glance: Vec<String>,
2446    /// Every charge held after a filament change nobody has confirmed, oldest
2447    /// first: what the next spool loaded on that tool is charged.
2448    pub pending: Vec<PendingCharge>,
2449    /// Every spool, retired ones only when asked for, most recently
2450    /// registered
2451    /// first.
2452    pub spools: Vec<SpoolRecord>,
2453}
2454
2455/// What one restart put back the way it found it.
2456///
2457/// A supervisor that has been restarted adopts whatever the store holds
2458/// rather
2459/// than starting empty, and each of these is one of those adoptions. They are
2460/// recorded rather than merely done, because a print that carried on across a
2461/// restart and one that was started again look identical afterwards unless
2462/// the
2463/// history says which happened.
2464#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2465pub enum StartupOutcome {
2466    /// A print left open was adopted as the print this supervisor is
2467    /// watching.
2468    #[serde(rename = "print_adopted")]
2469    PrintAdopted,
2470    /// A session left open was resumed rather than replaced.
2471    #[serde(rename = "session_resumed")]
2472    SessionResumed {
2473        /// The session's own name in the harness.
2474        session_name: String,
2475    },
2476    /// An intervention already past its expiry was expired on start.
2477    #[serde(rename = "intervention_expired")]
2478    InterventionExpired {
2479        /// What it had changed.
2480        adjustable: Adjustable,
2481        /// The intervention that had outlived its bound.
2482        intervention_id: InterventionId,
2483        /// What became of putting the prior value back.
2484        outcome: InterventionOutcome,
2485    },
2486}
2487
2488/// A supervisor reconciled one thing the store held when it started.
2489#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2490pub struct StartupReconciliationPayload {
2491    /// What was reconciled.
2492    pub outcome: StartupOutcome,
2493    /// The print it is about.
2494    pub print_id: PrintId,
2495}
2496
2497/// What a status read answers.
2498#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2499pub struct StatusAnswer {
2500    /// The failure detector's health, and since when it has been so.
2501    pub detector: DetectorHealth,
2502    /// The bounded interventions still in force.
2503    pub interventions: Vec<Intervention>,
2504    /// The job, when a snapshot could be taken.
2505    #[serde(default, skip_serializing_if = "Option::is_none")]
2506    pub job: Option<JobSnapshot>,
2507    /// The print.
2508    pub print: PrintRecord,
2509    /// The printer, when a snapshot could be taken.
2510    #[serde(default, skip_serializing_if = "Option::is_none")]
2511    pub printer: Option<PrinterSnapshot>,
2512    /// The supervision session watching it, when one is.
2513    #[serde(default, skip_serializing_if = "Option::is_none")]
2514    pub session: Option<SupervisionSession>,
2515}
2516
2517/// The supervision session keyed to one print.
2518#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2519pub struct SupervisionSession {
2520    /// Why it was closed, if it was.
2521    #[serde(default, skip_serializing_if = "Option::is_none")]
2522    pub close_reason: Option<String>,
2523    /// When the session was closed, if it was.
2524    #[serde(default, skip_serializing_if = "Option::is_none")]
2525    pub closed_at: Option<Timestamp>,
2526    /// When the session was opened.
2527    pub created_at: Timestamp,
2528    /// The identity the harness ran the session under.
2529    pub harness_identity: String,
2530    /// When the last turn ran.
2531    pub last_turn_at: Timestamp,
2532    /// The print this session watches.
2533    pub print_id: PrintId,
2534    /// The session's own name in the harness.
2535    pub session_name: String,
2536}
2537
2538/// A supervision session was closed.
2539#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2540pub struct SupervisionSessionClosedPayload {
2541    /// Why it was closed.
2542    pub close_reason: String,
2543    /// The session's own name in the harness.
2544    pub session_name: String,
2545}
2546
2547/// A supervision session was opened.
2548#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2549pub struct SupervisionSessionOpenedPayload {
2550    /// The identity the harness ran it under.
2551    pub harness_identity: String,
2552    /// The session's own name in the harness.
2553    pub session_name: String,
2554}
2555
2556/// What a printer history read answers: one page of the printer's own events.
2557#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2558pub struct TimelineAnswer {
2559    /// The events, newest first.
2560    pub events: Vec<EventRecord>,
2561    /// The cursor for the following, older page, given as `before`; absent
2562    /// when there is none.
2563    #[serde(default, skip_serializing_if = "Option::is_none")]
2564    pub next: Option<EventId>,
2565}
2566
2567/// An instant in UTC, as an RFC 3339 string with a zero offset.
2568pub type Timestamp = String;
2569
2570/// The filament one tool feeds, as a length and, when it is known, a volume.
2571#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2572pub struct ToolFilament {
2573    /// The length of filament fed, in millimetres.
2574    pub length_mm: f64,
2575    /// The tool, in the printer's own numbering.
2576    pub tool: i64,
2577    /// The volume of filament fed, in cubic centimetres, when the source
2578    /// reported one.
2579    #[serde(default, skip_serializing_if = "Option::is_none")]
2580    pub volume_cm3: Option<f64>,
2581}
2582
2583/// A print this system never saw was found in the printer's own tally.
2584#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2585pub struct UnaccountedPrintPayload {
2586    /// Whether it was charged against a spool.
2587    pub charged: bool,
2588    /// The file that was printed.
2589    pub file_name: String,
2590    /// When it was printed, when the printer says.
2591    #[serde(default, skip_serializing_if = "Option::is_none")]
2592    pub printed_at: Option<Timestamp>,
2593    /// Whether it completed.
2594    pub success: bool,
2595}
2596
2597/// The printer heated or moved with no job running, soon after a print ended
2598/// `interrupted` or `failed`.
2599///
2600/// A transition rather than a reading: written once when such activity
2601/// begins,
2602/// and again only when what it reports changes. The printer's rather than any
2603/// print's, so it is written with no print; `last_print_id` names the print
2604/// it
2605/// came after.
2606#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2607pub struct UnattendedPrinterActivityPayload {
2608    /// The bed's target, when it is above zero; `null` otherwise.
2609    #[serde(default, skip_serializing_if = "Option::is_none")]
2610    pub bed_target_c: Option<f64>,
2611    /// What the watch read, in words.
2612    pub detail: String,
2613    /// The print that ended before it.
2614    pub last_print_id: PrintId,
2615    /// Whether the print head moved since the last position read, `null` when
2616    /// no position could be read.
2617    #[serde(default, skip_serializing_if = "Option::is_none")]
2618    pub moved: Option<bool>,
2619    /// Every tool whose target is above zero.
2620    pub tools: Vec<HeatedTool>,
2621}
2622
2623/// One reason a spool's figure is in doubt, and since when.
2624#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2625pub struct UnverifiedMark {
2626    /// Why the figure is in doubt.
2627    pub reason: UnverifiedReason,
2628    /// When the spool was marked for it.
2629    pub since: Timestamp,
2630}
2631
2632/// Why a spool's figure is in doubt.
2633#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2634#[serde(tag = "reason")]
2635pub enum UnverifiedReason {
2636    /// A print ran on the spool's tool and nothing could be charged, because
2637    /// the file had no analysis — an SD-card file, for one.
2638    #[serde(rename = "uncharged_print")]
2639    UnchargedPrint {
2640        /// The print nothing could be charged for.
2641        print_id: PrintId,
2642    },
2643    /// A print this system never saw ran and did not complete.
2644    #[serde(rename = "unaccounted_print")]
2645    UnaccountedPrint {
2646        /// The file that was printed.
2647        file_name: String,
2648        /// When it was printed.
2649        printed_at: Timestamp,
2650    },
2651    /// A filament change was signalled and nobody has said which spool is now
2652    /// loaded. The spool was charged up to a position read when the signal
2653    /// was
2654    /// handled.
2655    #[serde(rename = "filament_change_unconfirmed")]
2656    FilamentChangeUnconfirmed {
2657        /// The print the change was signalled during.
2658        print_id: PrintId,
2659    },
2660    /// A filament change was signalled, nobody has said which spool is now
2661    /// loaded, and no fresh position could be read at the signal: the spool
2662    /// was
2663    /// charged only up to the last recorded position, so its figure may be
2664    /// overstated.
2665    #[serde(rename = "filament_change_unconfirmed_position")]
2666    FilamentChangeUnconfirmedPosition {
2667        /// The print the change was signalled during.
2668        print_id: PrintId,
2669    },
2670    /// A file's print tally was reset, and it cannot be told which of the
2671    /// prints since the re-upload were already settled.
2672    #[serde(rename = "tally_reset")]
2673    TallyReset {
2674        /// The file whose tally was reset.
2675        file_name: String,
2676        /// Where the file lives, in the printer's own vocabulary.
2677        origin: String,
2678    },
2679}
2680
2681/// One payload type declared under a kind name, by the domain that owns it.
2682///
2683/// The table from kind name to payload type, as the server declares it: every
2684/// kind below has exactly one implementation here, and a kind this client
2685/// does not know flows through `EventRecord` untouched.
2686pub trait EventPayloadKind: serde::de::DeserializeOwned {
2687    /// The name this payload's events are written down under.
2688    const KIND: &'static str;
2689}
2690
2691impl EventPayloadKind for ActionExecutedPayload {
2692    const KIND: &'static str = "action_executed";
2693}
2694
2695impl EventPayloadKind for ActionRejectedPayload {
2696    const KIND: &'static str = "action_rejected";
2697}
2698
2699impl EventPayloadKind for ActionRequestedPayload {
2700    const KIND: &'static str = "action_requested";
2701}
2702
2703impl EventPayloadKind for AgentAssessmentPayload {
2704    const KIND: &'static str = "agent_assessment";
2705}
2706
2707impl EventPayloadKind for CameraDegradedPayload {
2708    const KIND: &'static str = "camera_degraded";
2709}
2710
2711impl EventPayloadKind for CameraLookPayload {
2712    const KIND: &'static str = "camera_look";
2713}
2714
2715impl EventPayloadKind for ChargePendedPayload {
2716    const KIND: &'static str = "charge_pended";
2717}
2718
2719impl EventPayloadKind for DetectorHealthChangedPayload {
2720    const KIND: &'static str = "detector_health_changed";
2721}
2722
2723impl EventPayloadKind for FilamentChangeSignalledPayload {
2724    const KIND: &'static str = "filament_change_signalled";
2725}
2726
2727impl EventPayloadKind for FilamentConsumedPayload {
2728    const KIND: &'static str = "filament_consumed";
2729}
2730
2731impl EventPayloadKind for FileTallyRebaselinedPayload {
2732    const KIND: &'static str = "file_tally_rebaselined";
2733}
2734
2735impl EventPayloadKind for InterventionExpiredPayload {
2736    const KIND: &'static str = "intervention_expired";
2737}
2738
2739impl EventPayloadKind for MalformedExternalEventPayload {
2740    const KIND: &'static str = "malformed_external_event";
2741}
2742
2743impl EventPayloadKind for ObicoFailureAlertPayload {
2744    const KIND: &'static str = "obico_failure_alert";
2745}
2746
2747impl EventPayloadKind for ObicoPrinterNotificationPayload {
2748    const KIND: &'static str = "obico_printer_notification";
2749}
2750
2751impl EventPayloadKind for OperatorAcknowledgementPayload {
2752    const KIND: &'static str = "operator_acknowledgement";
2753}
2754
2755impl EventPayloadKind for PeriodicObservationPayload {
2756    const KIND: &'static str = "periodic_observation";
2757}
2758
2759impl EventPayloadKind for PortFailurePayload {
2760    const KIND: &'static str = "port_failure";
2761}
2762
2763impl EventPayloadKind for PreflightCheckedPayload {
2764    const KIND: &'static str = "preflight_checked";
2765}
2766
2767impl EventPayloadKind for PrintEndedPayload {
2768    const KIND: &'static str = "print_ended";
2769}
2770
2771impl EventPayloadKind for PrintStartedPayload {
2772    const KIND: &'static str = "print_started";
2773}
2774
2775impl EventPayloadKind for PrintUntrackedPayload {
2776    const KIND: &'static str = "print_untracked";
2777}
2778
2779impl EventPayloadKind for PrinterConnectionChangedPayload {
2780    const KIND: &'static str = "printer_connection_changed";
2781}
2782
2783impl EventPayloadKind for PrinterResetPayload {
2784    const KIND: &'static str = "printer_reset";
2785}
2786
2787impl EventPayloadKind for SpoolAdjustedPayload {
2788    const KIND: &'static str = "spool_adjusted";
2789}
2790
2791impl EventPayloadKind for SpoolCommandRejectedPayload {
2792    const KIND: &'static str = "spool_command_rejected";
2793}
2794
2795impl EventPayloadKind for SpoolLoadedPayload {
2796    const KIND: &'static str = "spool_loaded";
2797}
2798
2799impl EventPayloadKind for SpoolRegisteredPayload {
2800    const KIND: &'static str = "spool_registered";
2801}
2802
2803impl EventPayloadKind for SpoolRetiredPayload {
2804    const KIND: &'static str = "spool_retired";
2805}
2806
2807impl EventPayloadKind for SpoolUnloadedPayload {
2808    const KIND: &'static str = "spool_unloaded";
2809}
2810
2811impl EventPayloadKind for SpoolUnverifiedPayload {
2812    const KIND: &'static str = "spool_unverified";
2813}
2814
2815impl EventPayloadKind for StartupReconciliationPayload {
2816    const KIND: &'static str = "startup_reconciliation";
2817}
2818
2819impl EventPayloadKind for SupervisionSessionClosedPayload {
2820    const KIND: &'static str = "supervision_session_closed";
2821}
2822
2823impl EventPayloadKind for SupervisionSessionOpenedPayload {
2824    const KIND: &'static str = "supervision_session_opened";
2825}
2826
2827impl EventPayloadKind for UnaccountedPrintPayload {
2828    const KIND: &'static str = "unaccounted_print";
2829}
2830
2831impl EventPayloadKind for UnattendedPrinterActivityPayload {
2832    const KIND: &'static str = "unattended_printer_activity";
2833}
2834
2835/// Every kind the server declares a payload type for, in name order.
2836pub const EVENT_KINDS: [&str; 36] = [
2837    "action_executed",
2838    "action_rejected",
2839    "action_requested",
2840    "agent_assessment",
2841    "camera_degraded",
2842    "camera_look",
2843    "charge_pended",
2844    "detector_health_changed",
2845    "filament_change_signalled",
2846    "filament_consumed",
2847    "file_tally_rebaselined",
2848    "intervention_expired",
2849    "malformed_external_event",
2850    "obico_failure_alert",
2851    "obico_printer_notification",
2852    "operator_acknowledgement",
2853    "periodic_observation",
2854    "port_failure",
2855    "preflight_checked",
2856    "print_ended",
2857    "print_started",
2858    "print_untracked",
2859    "printer_connection_changed",
2860    "printer_reset",
2861    "spool_adjusted",
2862    "spool_command_rejected",
2863    "spool_loaded",
2864    "spool_registered",
2865    "spool_retired",
2866    "spool_unloaded",
2867    "spool_unverified",
2868    "startup_reconciliation",
2869    "supervision_session_closed",
2870    "supervision_session_opened",
2871    "unaccounted_print",
2872    "unattended_printer_activity",
2873];
2874
2875impl EventRecord {
2876    /// The payload as `P`, when this event is under the kind `P` declares.
2877    ///
2878    /// Answers `None` for an event of any other kind — one this client knows
2879    /// or one it does not — and the parse failure for an event under the
2880    /// right kind whose payload is not of the type.
2881    #[must_use]
2882    pub fn payload_as<P: EventPayloadKind>(&self) -> Option<Result<P, serde_json::Error>> {
2883        (self.kind == P::KIND).then(|| serde_json::from_value(self.payload.clone()))
2884    }
2885}
2886
2887impl Client {
2888    /// Call `prints` on the configured supervisor.
2889    ///
2890    /// # Errors
2891    ///
2892    /// Returns `ClientError` when the supervisor could not be reached, when
2893    /// it answered something this client cannot read.
2894    // llmlint: ignore[names_match_behavior] See suppressions.toml.
2895    pub fn prints(&self) -> Result<PrintsAnswer, ClientError> {
2896        let target = String::from("/v1/prints");
2897        let asked: Vec<(String, String)> = Vec::new();
2898        let sending: Option<serde_json::Value> = None;
2899        self.call("GET", &target, &asked, sending.as_ref())
2900    }
2901
2902    /// Call `status` on the configured supervisor.
2903    ///
2904    /// # Errors
2905    ///
2906    /// Returns `ClientError` when the supervisor could not be reached, when
2907    /// it answered something this client cannot read.
2908    pub fn status(&self, print_id: &str) -> Result<StatusAnswer, ClientError> {
2909        let target = format!("/v1/prints/{print_id}/status");
2910        let asked: Vec<(String, String)> = Vec::new();
2911        let sending: Option<serde_json::Value> = None;
2912        self.call("GET", &target, &asked, sending.as_ref())
2913    }
2914
2915    /// Call `context` on the configured supervisor.
2916    ///
2917    /// # Errors
2918    ///
2919    /// Returns `ClientError` when the supervisor could not be reached, when
2920    /// it answered something this client cannot read.
2921    pub fn context(&self, print_id: &str) -> Result<ContextAnswer, ClientError> {
2922        let target = format!("/v1/prints/{print_id}/context");
2923        let asked: Vec<(String, String)> = Vec::new();
2924        let sending: Option<serde_json::Value> = None;
2925        self.call("GET", &target, &asked, sending.as_ref())
2926    }
2927
2928    /// Call `image` on the configured supervisor.
2929    ///
2930    /// # Errors
2931    ///
2932    /// Returns `ClientError` when the supervisor could not be reached, when
2933    /// it answered something this client cannot read.
2934    pub fn image(&self, image_id: &str) -> Result<ImageAnswer, ClientError> {
2935        let target = format!("/v1/images/{image_id}");
2936        let asked: Vec<(String, String)> = Vec::new();
2937        let sending: Option<serde_json::Value> = None;
2938        self.call("GET", &target, &asked, sending.as_ref())
2939    }
2940
2941    /// Call `history` on the configured supervisor.
2942    ///
2943    /// # Errors
2944    ///
2945    /// Returns `ClientError` when the supervisor could not be reached, when
2946    /// it answered something this client cannot read.
2947    pub fn history(
2948        &self,
2949        print_id: &str,
2950        limit: Option<i64>,
2951    ) -> Result<HistoryAnswer, ClientError> {
2952        let target = format!("/v1/prints/{print_id}/history");
2953        let mut asked: Vec<(String, String)> = Vec::new();
2954        if let Some(value) = limit {
2955            asked.push(("limit".to_owned(), value.to_string()));
2956        }
2957        let sending: Option<serde_json::Value> = None;
2958        self.call("GET", &target, &asked, sending.as_ref())
2959    }
2960
2961    /// Call `events` on the configured supervisor.
2962    ///
2963    /// # Errors
2964    ///
2965    /// Returns `ClientError` when the supervisor could not be reached, when
2966    /// it answered something this client cannot read.
2967    pub fn events(
2968        &self,
2969        after: Option<String>,
2970        print_id: Option<String>,
2971        kinds: Option<Vec<String>>,
2972        exclude_kinds: Option<Vec<String>>,
2973        wait_s: Option<i64>,
2974        limit: Option<i64>,
2975    ) -> Result<EventsAnswer, ClientError> {
2976        let target = String::from("/v1/events");
2977        let mut asked: Vec<(String, String)> = Vec::new();
2978        if let Some(value) = after {
2979            asked.push(("after".to_owned(), value));
2980        }
2981        if let Some(value) = print_id {
2982            asked.push(("print_id".to_owned(), value));
2983        }
2984        for value in kinds.into_iter().flatten() {
2985            asked.push(("kinds".to_owned(), value));
2986        }
2987        for value in exclude_kinds.into_iter().flatten() {
2988            asked.push(("exclude_kinds".to_owned(), value));
2989        }
2990        if let Some(value) = wait_s {
2991            asked.push(("wait_s".to_owned(), value.to_string()));
2992        }
2993        if let Some(value) = limit {
2994            asked.push(("limit".to_owned(), value.to_string()));
2995        }
2996        let sending: Option<serde_json::Value> = None;
2997        self.call("GET", &target, &asked, sending.as_ref())
2998    }
2999
3000    /// Call `manifest_get` on the configured supervisor.
3001    ///
3002    /// # Errors
3003    ///
3004    /// Returns `ClientError` when the supervisor could not be reached, when
3005    /// it answered something this client cannot read.
3006    pub fn manifest_get(&self, print_id: &str) -> Result<ManifestAnswer, ClientError> {
3007        let target = format!("/v1/prints/{print_id}/manifest");
3008        let asked: Vec<(String, String)> = Vec::new();
3009        let sending: Option<serde_json::Value> = None;
3010        self.call("GET", &target, &asked, sending.as_ref())
3011    }
3012
3013    /// Call `look` on the configured supervisor.
3014    ///
3015    /// # Errors
3016    ///
3017    /// Returns `ClientError` when the supervisor could not be reached, when
3018    /// it answered something this client cannot read.
3019    pub fn look(&self, print_id: &str, wait_s: Option<i64>) -> Result<Look, ClientError> {
3020        let target = format!("/v1/prints/{print_id}/look");
3021        let mut asked: Vec<(String, String)> = Vec::new();
3022        if let Some(value) = wait_s {
3023            asked.push(("wait_s".to_owned(), value.to_string()));
3024        }
3025        let sending: Option<serde_json::Value> = None;
3026        self.call("GET", &target, &asked, sending.as_ref())
3027    }
3028
3029    /// Call `manifest_set` on the configured supervisor.
3030    ///
3031    /// # Errors
3032    ///
3033    /// Returns `ClientError` when the supervisor could not be reached, when
3034    /// it answered something this client cannot read, when this client
3035    /// refuses the call before making it because the reason is empty, and
3036    /// when the policy refused the action — which arrives as
3037    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3038    /// the range allowed.
3039    pub fn manifest_set(
3040        &self,
3041        print_id: &str,
3042        reason: &str,
3043        manifest: &JobManifest,
3044    ) -> Result<ManifestAnswer, ClientError> {
3045        crate::reason_given(reason)?;
3046        let target = format!("/v1/prints/{print_id}/manifest");
3047        let asked: Vec<(String, String)> = Vec::new();
3048        let mut sending = serde_json::Map::new();
3049        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3050        sending.insert("manifest".to_owned(), crate::as_value(&manifest)?);
3051        let sending = Some(serde_json::Value::Object(sending));
3052        self.call("PUT", &target, &asked, sending.as_ref())
3053    }
3054
3055    /// Call `spools` on the configured supervisor.
3056    ///
3057    /// # Errors
3058    ///
3059    /// Returns `ClientError` when the supervisor could not be reached, when
3060    /// it answered something this client cannot read.
3061    pub fn spools(&self, include_retired: Option<bool>) -> Result<SpoolsAnswer, ClientError> {
3062        let target = String::from("/v1/spools");
3063        let mut asked: Vec<(String, String)> = Vec::new();
3064        if let Some(value) = include_retired {
3065            asked.push(("include_retired".to_owned(), value.to_string()));
3066        }
3067        let sending: Option<serde_json::Value> = None;
3068        self.call("GET", &target, &asked, sending.as_ref())
3069    }
3070
3071    /// Call `spool` on the configured supervisor.
3072    ///
3073    /// # Errors
3074    ///
3075    /// Returns `ClientError` when the supervisor could not be reached, when
3076    /// it answered something this client cannot read.
3077    pub fn spool(&self, spool_id: &str) -> Result<SpoolAnswer, ClientError> {
3078        let target = format!("/v1/spools/{spool_id}");
3079        let asked: Vec<(String, String)> = Vec::new();
3080        let sending: Option<serde_json::Value> = None;
3081        self.call("GET", &target, &asked, sending.as_ref())
3082    }
3083
3084    /// Call `printer_history` on the configured supervisor.
3085    ///
3086    /// # Errors
3087    ///
3088    /// Returns `ClientError` when the supervisor could not be reached, when
3089    /// it answered something this client cannot read.
3090    pub fn printer_history(
3091        &self,
3092        kinds: Option<Vec<String>>,
3093        since: Option<String>,
3094        until: Option<String>,
3095        spool_id: Option<String>,
3096        before: Option<String>,
3097        limit: Option<i64>,
3098    ) -> Result<TimelineAnswer, ClientError> {
3099        let target = String::from("/v1/history");
3100        let mut asked: Vec<(String, String)> = Vec::new();
3101        for value in kinds.into_iter().flatten() {
3102            asked.push(("kinds".to_owned(), value));
3103        }
3104        if let Some(value) = since {
3105            asked.push(("since".to_owned(), value));
3106        }
3107        if let Some(value) = until {
3108            asked.push(("until".to_owned(), value));
3109        }
3110        if let Some(value) = spool_id {
3111            asked.push(("spool_id".to_owned(), value));
3112        }
3113        if let Some(value) = before {
3114            asked.push(("before".to_owned(), value));
3115        }
3116        if let Some(value) = limit {
3117            asked.push(("limit".to_owned(), value.to_string()));
3118        }
3119        let sending: Option<serde_json::Value> = None;
3120        self.call("GET", &target, &asked, sending.as_ref())
3121    }
3122
3123    /// Call `preflight` on the configured supervisor.
3124    ///
3125    /// # Errors
3126    ///
3127    /// Returns `ClientError` when the supervisor could not be reached, when
3128    /// it answered something this client cannot read.
3129    pub fn preflight(&self, file_name: &str) -> Result<PreflightAnswer, ClientError> {
3130        let target = String::from("/v1/preflight");
3131        let asked: Vec<(String, String)> = vec![("file_name".to_owned(), file_name.to_owned())];
3132        let sending: Option<serde_json::Value> = None;
3133        self.call("GET", &target, &asked, sending.as_ref())
3134    }
3135
3136    /// Call `pause` on the configured supervisor.
3137    ///
3138    /// # Errors
3139    ///
3140    /// Returns `ClientError` when the supervisor could not be reached, when
3141    /// it answered something this client cannot read, when this client
3142    /// refuses the call before making it because the reason is empty, and
3143    /// when the policy refused the action — which arrives as
3144    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3145    /// the range allowed.
3146    pub fn pause(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
3147        crate::reason_given(reason)?;
3148        let target = format!("/v1/prints/{print_id}/actions/pause");
3149        let asked: Vec<(String, String)> = Vec::new();
3150        let mut sending = serde_json::Map::new();
3151        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3152        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3153        let sending = Some(serde_json::Value::Object(sending));
3154        self.call("POST", &target, &asked, sending.as_ref())
3155    }
3156
3157    /// Call `resume` on the configured supervisor.
3158    ///
3159    /// # Errors
3160    ///
3161    /// Returns `ClientError` when the supervisor could not be reached, when
3162    /// it answered something this client cannot read, when this client
3163    /// refuses the call before making it because the reason is empty, and
3164    /// when the policy refused the action — which arrives as
3165    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3166    /// the range allowed.
3167    pub fn resume(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
3168        crate::reason_given(reason)?;
3169        let target = format!("/v1/prints/{print_id}/actions/resume");
3170        let asked: Vec<(String, String)> = Vec::new();
3171        let mut sending = serde_json::Map::new();
3172        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3173        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3174        let sending = Some(serde_json::Value::Object(sending));
3175        self.call("POST", &target, &asked, sending.as_ref())
3176    }
3177
3178    /// Call `cancel` on the configured supervisor.
3179    ///
3180    /// # Errors
3181    ///
3182    /// Returns `ClientError` when the supervisor could not be reached, when
3183    /// it answered something this client cannot read, when this client
3184    /// refuses the call before making it because the reason is empty, and
3185    /// when the policy refused the action — which arrives as
3186    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3187    /// the range allowed.
3188    pub fn cancel(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
3189        crate::reason_given(reason)?;
3190        let target = format!("/v1/prints/{print_id}/actions/cancel");
3191        let asked: Vec<(String, String)> = Vec::new();
3192        let mut sending = serde_json::Map::new();
3193        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3194        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3195        let sending = Some(serde_json::Value::Object(sending));
3196        self.call("POST", &target, &asked, sending.as_ref())
3197    }
3198
3199    /// Call `start_print` on the configured supervisor.
3200    ///
3201    /// # Errors
3202    ///
3203    /// Returns `ClientError` when the supervisor could not be reached, when
3204    /// it answered something this client cannot read, when this client
3205    /// refuses the call before making it because the reason is empty, and
3206    /// when the policy refused the action — which arrives as
3207    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3208    /// the range allowed.
3209    pub fn start_print(
3210        &self,
3211        print_id: &str,
3212        file_name: &FileName,
3213        manifest: &JobManifest,
3214        reason: &str,
3215        override_filament_check: Option<bool>,
3216    ) -> Result<ActionAnswer, ClientError> {
3217        crate::reason_given(reason)?;
3218        let target = format!("/v1/prints/{print_id}/actions/start_print");
3219        let asked: Vec<(String, String)> = Vec::new();
3220        let mut sending = serde_json::Map::new();
3221        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3222        sending.insert("file_name".to_owned(), crate::as_value(&file_name)?);
3223        sending.insert("manifest".to_owned(), crate::as_value(&manifest)?);
3224        if let Some(value) = override_filament_check {
3225            sending.insert(
3226                "override_filament_check".to_owned(),
3227                crate::as_value(&value)?,
3228            );
3229        }
3230        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3231        let sending = Some(serde_json::Value::Object(sending));
3232        self.call("POST", &target, &asked, sending.as_ref())
3233    }
3234
3235    /// Call `set_feedrate_factor` on the configured supervisor.
3236    ///
3237    /// # Errors
3238    ///
3239    /// Returns `ClientError` when the supervisor could not be reached, when
3240    /// it answered something this client cannot read, when this client
3241    /// refuses the call before making it because the reason is empty, and
3242    /// when the policy refused the action — which arrives as
3243    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3244    /// the range allowed.
3245    pub fn set_feedrate_factor(
3246        &self,
3247        print_id: &str,
3248        factor: f64,
3249        reason: &str,
3250        duration_s: Option<i64>,
3251    ) -> Result<ActionAnswer, ClientError> {
3252        crate::reason_given(reason)?;
3253        let target = format!("/v1/prints/{print_id}/actions/set_feedrate_factor");
3254        let asked: Vec<(String, String)> = Vec::new();
3255        let mut sending = serde_json::Map::new();
3256        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3257        if let Some(value) = duration_s {
3258            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
3259        }
3260        sending.insert("factor".to_owned(), crate::as_value(&factor)?);
3261        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3262        let sending = Some(serde_json::Value::Object(sending));
3263        self.call("POST", &target, &asked, sending.as_ref())
3264    }
3265
3266    /// Call `set_flowrate_factor` on the configured supervisor.
3267    ///
3268    /// # Errors
3269    ///
3270    /// Returns `ClientError` when the supervisor could not be reached, when
3271    /// it answered something this client cannot read, when this client
3272    /// refuses the call before making it because the reason is empty, and
3273    /// when the policy refused the action — which arrives as
3274    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3275    /// the range allowed.
3276    pub fn set_flowrate_factor(
3277        &self,
3278        print_id: &str,
3279        factor: f64,
3280        reason: &str,
3281        duration_s: Option<i64>,
3282    ) -> Result<ActionAnswer, ClientError> {
3283        crate::reason_given(reason)?;
3284        let target = format!("/v1/prints/{print_id}/actions/set_flowrate_factor");
3285        let asked: Vec<(String, String)> = Vec::new();
3286        let mut sending = serde_json::Map::new();
3287        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3288        if let Some(value) = duration_s {
3289            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
3290        }
3291        sending.insert("factor".to_owned(), crate::as_value(&factor)?);
3292        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3293        let sending = Some(serde_json::Value::Object(sending));
3294        self.call("POST", &target, &asked, sending.as_ref())
3295    }
3296
3297    /// Call `set_tool_target_c` on the configured supervisor.
3298    ///
3299    /// # Errors
3300    ///
3301    /// Returns `ClientError` when the supervisor could not be reached, when
3302    /// it answered something this client cannot read, when this client
3303    /// refuses the call before making it because the reason is empty, and
3304    /// when the policy refused the action — which arrives as
3305    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3306    /// the range allowed.
3307    pub fn set_tool_target_c(
3308        &self,
3309        print_id: &str,
3310        reason: &str,
3311        target_c: f64,
3312        tool: i64,
3313        duration_s: Option<i64>,
3314    ) -> Result<ActionAnswer, ClientError> {
3315        crate::reason_given(reason)?;
3316        let target = format!("/v1/prints/{print_id}/actions/set_tool_target_c");
3317        let asked: Vec<(String, String)> = Vec::new();
3318        let mut sending = serde_json::Map::new();
3319        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3320        if let Some(value) = duration_s {
3321            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
3322        }
3323        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3324        sending.insert("target_c".to_owned(), crate::as_value(&target_c)?);
3325        sending.insert("tool".to_owned(), crate::as_value(&tool)?);
3326        let sending = Some(serde_json::Value::Object(sending));
3327        self.call("POST", &target, &asked, sending.as_ref())
3328    }
3329
3330    /// Call `set_bed_target_c` on the configured supervisor.
3331    ///
3332    /// # Errors
3333    ///
3334    /// Returns `ClientError` when the supervisor could not be reached, when
3335    /// it answered something this client cannot read, when this client
3336    /// refuses the call before making it because the reason is empty, and
3337    /// when the policy refused the action — which arrives as
3338    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3339    /// the range allowed.
3340    pub fn set_bed_target_c(
3341        &self,
3342        print_id: &str,
3343        reason: &str,
3344        target_c: f64,
3345        duration_s: Option<i64>,
3346    ) -> Result<ActionAnswer, ClientError> {
3347        crate::reason_given(reason)?;
3348        let target = format!("/v1/prints/{print_id}/actions/set_bed_target_c");
3349        let asked: Vec<(String, String)> = Vec::new();
3350        let mut sending = serde_json::Map::new();
3351        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3352        if let Some(value) = duration_s {
3353            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
3354        }
3355        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3356        sending.insert("target_c".to_owned(), crate::as_value(&target_c)?);
3357        let sending = Some(serde_json::Value::Object(sending));
3358        self.call("POST", &target, &asked, sending.as_ref())
3359    }
3360
3361    /// Call `set_fan_percent` on the configured supervisor.
3362    ///
3363    /// # Errors
3364    ///
3365    /// Returns `ClientError` when the supervisor could not be reached, when
3366    /// it answered something this client cannot read, when this client
3367    /// refuses the call before making it because the reason is empty, and
3368    /// when the policy refused the action — which arrives as
3369    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3370    /// the range allowed.
3371    pub fn set_fan_percent(
3372        &self,
3373        print_id: &str,
3374        percent: f64,
3375        reason: &str,
3376        duration_s: Option<i64>,
3377    ) -> Result<ActionAnswer, ClientError> {
3378        crate::reason_given(reason)?;
3379        let target = format!("/v1/prints/{print_id}/actions/set_fan_percent");
3380        let asked: Vec<(String, String)> = Vec::new();
3381        let mut sending = serde_json::Map::new();
3382        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3383        if let Some(value) = duration_s {
3384            sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
3385        }
3386        sending.insert("percent".to_owned(), crate::as_value(&percent)?);
3387        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3388        let sending = Some(serde_json::Value::Object(sending));
3389        self.call("POST", &target, &asked, sending.as_ref())
3390    }
3391
3392    /// Call `acknowledge_failure` on the configured supervisor.
3393    ///
3394    /// # Errors
3395    ///
3396    /// Returns `ClientError` when the supervisor could not be reached, when
3397    /// it answered something this client cannot read, when this client
3398    /// refuses the call before making it because the reason is empty, and
3399    /// when the policy refused the action — which arrives as
3400    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3401    /// the range allowed.
3402    pub fn acknowledge_failure(
3403        &self,
3404        print_id: &str,
3405        disposition: &AcknowledgementDisposition,
3406        event_id: &EventId,
3407        reason: &str,
3408    ) -> Result<ActionAnswer, ClientError> {
3409        crate::reason_given(reason)?;
3410        let target = format!("/v1/prints/{print_id}/actions/acknowledge_failure");
3411        let asked: Vec<(String, String)> = Vec::new();
3412        let mut sending = serde_json::Map::new();
3413        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3414        sending.insert("disposition".to_owned(), crate::as_value(&disposition)?);
3415        sending.insert("event_id".to_owned(), crate::as_value(&event_id)?);
3416        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3417        let sending = Some(serde_json::Value::Object(sending));
3418        self.call("POST", &target, &asked, sending.as_ref())
3419    }
3420
3421    /// Call `close_print` on the configured supervisor.
3422    ///
3423    /// # Errors
3424    ///
3425    /// Returns `ClientError` when the supervisor could not be reached, when
3426    /// it answered something this client cannot read, when this client
3427    /// refuses the call before making it because the reason is empty, and
3428    /// when the policy refused the action — which arrives as
3429    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3430    /// the range allowed.
3431    pub fn close_print(
3432        &self,
3433        print_id: &str,
3434        outcome: &PrintOutcome,
3435        reason: &str,
3436    ) -> Result<ActionAnswer, ClientError> {
3437        crate::reason_given(reason)?;
3438        let target = format!("/v1/prints/{print_id}/actions/close_print");
3439        let asked: Vec<(String, String)> = Vec::new();
3440        let mut sending = serde_json::Map::new();
3441        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3442        sending.insert("outcome".to_owned(), crate::as_value(&outcome)?);
3443        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3444        let sending = Some(serde_json::Value::Object(sending));
3445        self.call("POST", &target, &asked, sending.as_ref())
3446    }
3447
3448    /// Call `register_spool` on the configured supervisor.
3449    ///
3450    /// # Errors
3451    ///
3452    /// Returns `ClientError` when the supervisor could not be reached, when
3453    /// it answered something this client cannot read, when this client
3454    /// refuses the call before making it because the reason is empty, and
3455    /// when the policy refused the action — which arrives as
3456    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3457    /// the range allowed.
3458    // One argument per value the operation declares, as every client takes them; suppressions.toml has the reason.
3459    #[expect(clippy::too_many_arguments)]
3460    pub fn register_spool(
3461        &self,
3462        initial_g: f64,
3463        material: &str,
3464        name: &str,
3465        reason: &str,
3466        color: Option<String>,
3467        density_g_cm3: Option<f64>,
3468        diameter_mm: Option<f64>,
3469        remaining_g: Option<f64>,
3470        tare_g: Option<f64>,
3471    ) -> Result<SpoolCommandAnswer, ClientError> {
3472        crate::reason_given(reason)?;
3473        let target = String::from("/v1/spools/actions/register_spool");
3474        let asked: Vec<(String, String)> = Vec::new();
3475        let mut sending = serde_json::Map::new();
3476        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3477        if let Some(value) = color {
3478            sending.insert("color".to_owned(), crate::as_value(&value)?);
3479        }
3480        if let Some(value) = density_g_cm3 {
3481            sending.insert("density_g_cm3".to_owned(), crate::as_value(&value)?);
3482        }
3483        if let Some(value) = diameter_mm {
3484            sending.insert("diameter_mm".to_owned(), crate::as_value(&value)?);
3485        }
3486        sending.insert("initial_g".to_owned(), crate::as_value(&initial_g)?);
3487        sending.insert("material".to_owned(), crate::as_value(&material)?);
3488        sending.insert("name".to_owned(), crate::as_value(&name)?);
3489        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3490        if let Some(value) = remaining_g {
3491            sending.insert("remaining_g".to_owned(), crate::as_value(&value)?);
3492        }
3493        if let Some(value) = tare_g {
3494            sending.insert("tare_g".to_owned(), crate::as_value(&value)?);
3495        }
3496        let sending = Some(serde_json::Value::Object(sending));
3497        self.call("POST", &target, &asked, sending.as_ref())
3498    }
3499
3500    /// Call `load_spool` on the configured supervisor.
3501    ///
3502    /// # Errors
3503    ///
3504    /// Returns `ClientError` when the supervisor could not be reached, when
3505    /// it answered something this client cannot read, when this client
3506    /// refuses the call before making it because the reason is empty, and
3507    /// when the policy refused the action — which arrives as
3508    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3509    /// the range allowed.
3510    pub fn load_spool(
3511        &self,
3512        spool_id: &str,
3513        reason: &str,
3514        tool: i64,
3515    ) -> Result<SpoolCommandAnswer, ClientError> {
3516        crate::reason_given(reason)?;
3517        let target = format!("/v1/spools/{spool_id}/actions/load_spool");
3518        let asked: Vec<(String, String)> = Vec::new();
3519        let mut sending = serde_json::Map::new();
3520        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3521        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3522        sending.insert("tool".to_owned(), crate::as_value(&tool)?);
3523        let sending = Some(serde_json::Value::Object(sending));
3524        self.call("POST", &target, &asked, sending.as_ref())
3525    }
3526
3527    /// Call `unload_spool` on the configured supervisor.
3528    ///
3529    /// # Errors
3530    ///
3531    /// Returns `ClientError` when the supervisor could not be reached, when
3532    /// it answered something this client cannot read, when this client
3533    /// refuses the call before making it because the reason is empty, and
3534    /// when the policy refused the action — which arrives as
3535    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3536    /// the range allowed.
3537    pub fn unload_spool(
3538        &self,
3539        spool_id: &str,
3540        reason: &str,
3541    ) -> Result<SpoolCommandAnswer, ClientError> {
3542        crate::reason_given(reason)?;
3543        let target = format!("/v1/spools/{spool_id}/actions/unload_spool");
3544        let asked: Vec<(String, String)> = Vec::new();
3545        let mut sending = serde_json::Map::new();
3546        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3547        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3548        let sending = Some(serde_json::Value::Object(sending));
3549        self.call("POST", &target, &asked, sending.as_ref())
3550    }
3551
3552    /// Call `adjust_spool` on the configured supervisor.
3553    ///
3554    /// # Errors
3555    ///
3556    /// Returns `ClientError` when the supervisor could not be reached, when
3557    /// it answered something this client cannot read, when this client
3558    /// refuses the call before making it because the reason is empty, and
3559    /// when the policy refused the action — which arrives as
3560    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3561    /// the range allowed.
3562    pub fn adjust_spool(
3563        &self,
3564        spool_id: &str,
3565        reason: &str,
3566        gross_g: Option<f64>,
3567        remaining_g: Option<f64>,
3568    ) -> Result<SpoolCommandAnswer, ClientError> {
3569        crate::reason_given(reason)?;
3570        let target = format!("/v1/spools/{spool_id}/actions/adjust_spool");
3571        let asked: Vec<(String, String)> = Vec::new();
3572        let mut sending = serde_json::Map::new();
3573        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3574        if let Some(value) = gross_g {
3575            sending.insert("gross_g".to_owned(), crate::as_value(&value)?);
3576        }
3577        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3578        if let Some(value) = remaining_g {
3579            sending.insert("remaining_g".to_owned(), crate::as_value(&value)?);
3580        }
3581        let sending = Some(serde_json::Value::Object(sending));
3582        self.call("POST", &target, &asked, sending.as_ref())
3583    }
3584
3585    /// Call `retire_spool` on the configured supervisor.
3586    ///
3587    /// # Errors
3588    ///
3589    /// Returns `ClientError` when the supervisor could not be reached, when
3590    /// it answered something this client cannot read, when this client
3591    /// refuses the call before making it because the reason is empty, and
3592    /// when the policy refused the action — which arrives as
3593    /// `ClientError::Rejected`, carrying the reason, the value asked for and
3594    /// the range allowed.
3595    pub fn retire_spool(
3596        &self,
3597        spool_id: &str,
3598        reason: &str,
3599    ) -> Result<SpoolCommandAnswer, ClientError> {
3600        crate::reason_given(reason)?;
3601        let target = format!("/v1/spools/{spool_id}/actions/retire_spool");
3602        let asked: Vec<(String, String)> = Vec::new();
3603        let mut sending = serde_json::Map::new();
3604        sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
3605        sending.insert("reason".to_owned(), crate::as_value(&reason)?);
3606        let sending = Some(serde_json::Value::Object(sending));
3607        self.call("POST", &target, &asked, sending.as_ref())
3608    }
3609}