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