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