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