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