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