use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::{Client, ClientError};
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const OPERATION_NAMES: [&str; 29] = [
"prints",
"status",
"context",
"image",
"history",
"events",
"manifest_get",
"look",
"manifest_set",
"spools",
"spool",
"printer_history",
"preflight",
"pause",
"resume",
"cancel",
"start_print",
"set_feedrate_factor",
"set_flowrate_factor",
"set_tool_target_c",
"set_bed_target_c",
"set_fan_percent",
"acknowledge_failure",
"close_print",
"register_spool",
"load_spool",
"unload_spool",
"adjust_spool",
"retire_spool",
];
fn present<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: Deserialize<'de>,
{
T::deserialize(deserializer).map(Some)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AcknowledgementDisposition {
#[serde(rename = "continue")]
Continue,
#[serde(rename = "watch")]
Watch,
#[serde(rename = "stop")]
Stop,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ActionAnswer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub intervention: Option<Intervention>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub printer_refusal: Option<String>,
pub record: ActionRecord,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ActionExecutedPayload {
pub action_id: ActionId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub intervention_id: Option<InterventionId>,
}
pub type ActionId = String;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ActionKind {
#[serde(rename = "pause")]
Pause,
#[serde(rename = "resume")]
Resume,
#[serde(rename = "cancel")]
Cancel,
#[serde(rename = "start_print")]
StartPrint,
#[serde(rename = "set_feedrate_factor")]
SetFeedrateFactor,
#[serde(rename = "set_flowrate_factor")]
SetFlowrateFactor,
#[serde(rename = "set_tool_target_c")]
SetToolTargetC,
#[serde(rename = "set_bed_target_c")]
SetBedTargetC,
#[serde(rename = "set_fan_percent")]
SetFanPercent,
#[serde(rename = "acknowledge_failure")]
AcknowledgeFailure,
#[serde(rename = "close_print")]
ClosePrint,
#[serde(rename = "register_spool")]
RegisterSpool,
#[serde(rename = "load_spool")]
LoadSpool,
#[serde(rename = "unload_spool")]
UnloadSpool,
#[serde(rename = "adjust_spool")]
AdjustSpool,
#[serde(rename = "retire_spool")]
RetireSpool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ActionRecord {
pub decision: PolicyDecision,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub executed_at: Option<Timestamp>,
pub id: ActionId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome: Option<ExecutionOutcome>,
pub print_id: PrintId,
pub request: ActionRequest,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ActionRejectedPayload {
pub action_id: ActionId,
pub decision: PolicyDecision,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ActionRequest {
pub action: PrintAction,
pub actor: Actor,
pub requested_at: Timestamp,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ActionRequestedPayload {
pub action: PrintAction,
pub action_id: ActionId,
pub actor: Actor,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Actor {
#[serde(rename = "agent")]
Agent {
session_name: String,
},
#[serde(rename = "operator")]
Operator,
#[serde(rename = "system")]
System,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ActorClass {
#[serde(rename = "agent")]
Agent,
#[serde(rename = "operator")]
Operator,
#[serde(rename = "system")]
System,
}
pub type Adjustable = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AgentAssessment {
pub confidence: Confidence,
pub did: String,
pub escalating: bool,
pub should_continue: bool,
pub summary: String,
pub why: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AgentAssessmentPayload {
pub assessment: AgentAssessment,
pub session_name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CameraDegradedPayload {
pub attempts: i64,
pub cause: FrameCause,
pub detail: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CameraLookPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delivered: Option<Vec<EventId>>,
pub waited_s: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "key")]
pub enum ChargeKey {
#[serde(rename = "print")]
Print {
print_id: PrintId,
segment: i64,
tool: i64,
},
#[serde(rename = "unobserved")]
Unobserved {
file_name: String,
generation: i64,
origin: String,
print_number: i64,
tool: i64,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChargeMethod {
#[serde(rename = "slicer_estimate")]
SlicerEstimate,
#[serde(rename = "gcode_extrusion")]
GcodeExtrusion,
#[serde(rename = "file_fraction")]
FileFraction,
#[serde(rename = "unobserved_estimate")]
UnobservedEstimate,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChargePendedPayload {
pub pending: PendingCharge,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChargedFilament {
pub density_g_cm3: f64,
pub diameter_mm: f64,
pub grams: f64,
pub remaining_after_g: f64,
pub remaining_before_g: f64,
pub spool_id: SpoolId,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClearedMark {
pub reason: UnverifiedReason,
pub spool_id: SpoolId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Confidence {
#[serde(rename = "low")]
Low,
#[serde(rename = "medium")]
Medium,
#[serde(rename = "high")]
High,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContextAnswer {
pub context: PrintContext,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image_path: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "status")]
pub enum DetectorHealth {
#[serde(rename = "unchecked")]
Unchecked {
since: (),
},
#[serde(rename = "available")]
Available {
since: Timestamp,
},
#[serde(rename = "unavailable")]
Unavailable {
since: Timestamp,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DetectorHealthChangedPayload {
pub detail: String,
pub status: DetectorStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DetectorStatus {
#[serde(rename = "available")]
Available,
#[serde(rename = "unavailable")]
Unavailable,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EffectiveBounds {
pub allowed: BTreeMap<String, Range>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ErrorAnswer {
pub error: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ErrorSource {
#[serde(rename = "printer")]
Printer,
#[serde(rename = "host")]
Host,
}
pub type EventId = String;
pub type EventKind = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EventRecord {
pub id: EventId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image: Option<ImageRef>,
pub kind: EventKind,
pub payload: serde_json::Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub print_id: Option<PrintId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub raw: Option<RawBytes>,
pub received_at: Timestamp,
pub source: EventSource,
}
pub type EventSource = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EventsAnswer {
pub cursor: Option<EventId>,
pub events: Vec<EventRecord>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "present"
)]
pub print_ended: Option<Option<PrintEnded>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ExecutionOutcome {
#[serde(rename = "succeeded")]
Succeeded,
#[serde(rename = "failed")]
Failed {
reason: String,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FilamentChangeSignalledPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub position: Option<SignalPosition>,
pub source_kind: EventKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub spool_id: Option<SpoolId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FilamentConsumedPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub analysed_length_mm: Option<f64>,
pub charge_key: ChargeKey,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub charged: Option<ChargedFilament>,
pub length_mm: f64,
pub method: ChargeMethod,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub position: Option<FilePosition>,
pub tool: i64,
}
pub type FileName = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FilePosition {
pub bytes: i64,
pub observed_at: Timestamp,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub size_bytes: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FileTallyRebaselinedPayload {
pub disposition: RebaselineDisposition,
pub file_name: String,
pub new_failures: i64,
pub new_generation: i64,
pub new_successes: i64,
pub old_failures: i64,
pub old_generation: i64,
pub old_successes: i64,
pub origin: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FrameCause {
#[serde(rename = "blank")]
Blank,
#[serde(rename = "undecodable")]
Undecodable,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HeatedTool {
pub target_c: f64,
pub tool: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HeaterSnapshot {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub actual_c: Option<Reported>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub offset_c: Option<Reported>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_c: Option<Reported>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HistoryAnswer {
pub events: Vec<EventRecord>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImageAnswer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
pub record: ImageRecord,
}
pub type ImageId = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImageRecord {
pub byte_len: i64,
pub content_type: String,
pub event_id: EventId,
pub fetched_at: Timestamp,
pub id: ImageId,
pub print_id: PrintId,
pub relative_path: String,
pub sha256: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImageRef {
pub id: ImageId,
pub sha256: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Intervention {
pub action_id: ActionId,
pub adjustable: Adjustable,
pub applied_at: Timestamp,
pub applied_value: f64,
pub expires_at: Timestamp,
pub id: InterventionId,
pub outcome: InterventionOutcome,
pub print_id: PrintId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub prior_value: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub restored_at: Option<Timestamp>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InterventionExpiredPayload {
pub adjustable: Adjustable,
pub intervention_id: InterventionId,
pub outcome: InterventionOutcome,
}
pub type InterventionId = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum InterventionOutcome {
#[serde(rename = "still_active")]
StillActive,
#[serde(rename = "restored")]
Restored,
#[serde(rename = "restore_unavailable")]
RestoreUnavailable,
#[serde(rename = "restore_failed")]
RestoreFailed {
reason: String,
},
#[serde(rename = "superseded")]
Superseded {
by: InterventionId,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JobError {
pub source: ErrorSource,
pub text: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JobManifest {
pub allowed: BTreeMap<String, Range>,
pub file_name: String,
pub material: String,
pub metadata: BTreeMap<String, String>,
pub nozzle_diameter_mm: f64,
pub slicer_profile: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JobSnapshot {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub completion: Option<Reported>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<JobError>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub estimated_print_time_s: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filament: Option<Vec<ToolFilament>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_origin: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_position_bytes: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub print_time_left_s: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub print_time_s: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub size_bytes: Option<i64>,
pub state: PrinterState,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LoadedSpool {
pub spool_id: SpoolId,
pub tool: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Look {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arrived: Option<Vec<EventRecord>>,
pub detector_paused: bool,
pub event: EventRecord,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub frame: Option<ImageRef>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub job: Option<JobSnapshot>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub printer: Option<PrinterSnapshot>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MalformedExternalEventPayload {
pub detail: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ManifestAnswer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub manifest: Option<JobManifest>,
pub narrowings: Vec<ManifestNarrowing>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ManifestNarrowing {
pub adjustable: Adjustable,
pub applied: Range,
pub requested: Range,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ObicoFailureAlertPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ended_at: Option<Timestamp>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
pub is_warning: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub obico_print_id: Option<i64>,
pub print_paused: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub started_at: Option<Timestamp>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ObicoNotificationType {
#[serde(rename = "started")]
Started,
#[serde(rename = "done")]
Done,
#[serde(rename = "cancelled")]
Cancelled,
#[serde(rename = "paused")]
Paused,
#[serde(rename = "resumed")]
Resumed,
#[serde(rename = "filament_change")]
FilamentChange,
#[serde(rename = "heater_cooled")]
HeaterCooled,
#[serde(rename = "heater_target")]
HeaterTarget,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ObicoPrinterNotificationPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ended_at: Option<Timestamp>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
pub notification_type: ObicoNotificationType,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub obico_print_id: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub started_at: Option<Timestamp>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OperatorAcknowledgementPayload {
pub acknowledged_event_id: EventId,
pub disposition: AcknowledgementDisposition,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PendingCharge {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub analysed_length_mm: Option<f64>,
pub charge_key: PrintSegmentKey,
pub length_mm: f64,
pub method: ChargeMethod,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
pub pended_at: Timestamp,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub position: Option<FilePosition>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PendingTaken {
pub charge_key: ChargeKey,
pub print_id: PrintId,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PeriodicObservationPayload {
pub interval_s: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub job: Option<JobSnapshot>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub printer: Option<PrinterSnapshot>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PolicyDecision {
#[serde(rename = "accepted")]
Accepted,
#[serde(rename = "rejected")]
Rejected(RejectionReason),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PortFailurePayload {
pub detail: String,
pub event_id: EventId,
pub site: PortFailureSite,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PortFailureSite {
#[serde(rename = "printer_snapshot")]
PrinterSnapshot,
#[serde(rename = "printer_job")]
PrinterJob,
#[serde(rename = "image_write")]
ImageWrite,
#[serde(rename = "supervision_turn")]
SupervisionTurn,
#[serde(rename = "camera_look")]
CameraLook,
#[serde(rename = "detector_acknowledgement")]
DetectorAcknowledgement,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PositionFreshness {
#[serde(rename = "read_at_signal")]
ReadAtSignal,
#[serde(rename = "last_recorded")]
LastRecorded,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Preflight {
pub checked_at: Timestamp,
pub file_name: String,
pub tools: Vec<PreflightTool>,
pub verdict: PreflightVerdict,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PreflightAnswer {
pub preflight: Preflight,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PreflightCheckedPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub action_id: Option<ActionId>,
pub preflight: Preflight,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PreflightNeed {
pub buffer_g: f64,
pub required_g: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "verdict")]
pub enum PreflightObjection {
#[serde(rename = "insufficient")]
Insufficient {
#[serde(default, skip_serializing_if = "Option::is_none")]
marks: Option<Vec<UnverifiedMark>>,
need: PreflightNeed,
remaining_g: f64,
spool: PreflightSpool,
},
#[serde(rename = "unverified_spool")]
UnverifiedSpool {
marks: Vec<UnverifiedMark>,
#[serde(default, skip_serializing_if = "Option::is_none")]
need: Option<PreflightNeed>,
remaining_g: f64,
spool: PreflightSpool,
},
#[serde(rename = "no_analysis")]
NoAnalysis {
remaining_g: f64,
spool: PreflightSpool,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PreflightSpool {
pub id: SpoolId,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PreflightTool {
pub result: PreflightToolResult,
pub tool: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "verdict")]
pub enum PreflightToolResult {
#[serde(rename = "passed")]
Passed {
need: PreflightNeed,
remaining_g: f64,
spool: PreflightSpool,
},
#[serde(rename = "unchecked")]
Unchecked,
#[serde(rename = "insufficient")]
Insufficient {
#[serde(default, skip_serializing_if = "Option::is_none")]
marks: Option<Vec<UnverifiedMark>>,
need: PreflightNeed,
remaining_g: f64,
spool: PreflightSpool,
},
#[serde(rename = "unverified_spool")]
UnverifiedSpool {
marks: Vec<UnverifiedMark>,
#[serde(default, skip_serializing_if = "Option::is_none")]
need: Option<PreflightNeed>,
remaining_g: f64,
spool: PreflightSpool,
},
#[serde(rename = "no_analysis")]
NoAnalysis {
remaining_g: f64,
spool: PreflightSpool,
},
#[serde(rename = "overridden")]
Overridden {
objection: PreflightObjection,
},
#[serde(rename = "warned")]
Warned {
objection: PreflightObjection,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PreflightVerdict {
#[serde(rename = "passed")]
Passed,
#[serde(rename = "unchecked")]
Unchecked,
#[serde(rename = "insufficient")]
Insufficient,
#[serde(rename = "unverified_spool")]
UnverifiedSpool,
#[serde(rename = "no_analysis")]
NoAnalysis,
#[serde(rename = "overridden")]
Overridden,
#[serde(rename = "warned")]
Warned,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "action")]
pub enum PrintAction {
#[serde(rename = "pause")]
Pause {
actor: Actor,
reason: String,
},
#[serde(rename = "resume")]
Resume {
actor: Actor,
reason: String,
},
#[serde(rename = "cancel")]
Cancel {
actor: Actor,
reason: String,
},
#[serde(rename = "start_print")]
StartPrint {
actor: Actor,
file_name: FileName,
manifest: JobManifest,
#[serde(default, skip_serializing_if = "Option::is_none")]
override_filament_check: Option<bool>,
reason: String,
},
#[serde(rename = "set_feedrate_factor")]
SetFeedrateFactor {
actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
duration_s: Option<i64>,
factor: f64,
reason: String,
},
#[serde(rename = "set_flowrate_factor")]
SetFlowrateFactor {
actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
duration_s: Option<i64>,
factor: f64,
reason: String,
},
#[serde(rename = "set_tool_target_c")]
SetToolTargetC {
actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
duration_s: Option<i64>,
reason: String,
target_c: f64,
tool: i64,
},
#[serde(rename = "set_bed_target_c")]
SetBedTargetC {
actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
duration_s: Option<i64>,
reason: String,
target_c: f64,
},
#[serde(rename = "set_fan_percent")]
SetFanPercent {
actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
duration_s: Option<i64>,
percent: f64,
reason: String,
},
#[serde(rename = "acknowledge_failure")]
AcknowledgeFailure {
actor: Actor,
disposition: AcknowledgementDisposition,
event_id: EventId,
reason: String,
},
#[serde(rename = "close_print")]
ClosePrint {
actor: Actor,
outcome: PrintOutcome,
reason: String,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrintContext {
pub bounds: EffectiveBounds,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detector: Option<DetectorHealth>,
pub interventions: Vec<Intervention>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub job: Option<JobSnapshot>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub latest_image: Option<ImageRef>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub manifest: Option<JobManifest>,
pub print: PrintRecord,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub printer: Option<PrinterSnapshot>,
pub recent_events: Vec<EventRecord>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrintEnded {
pub event_id: EventId,
pub outcome: PrintOutcome,
pub received_at: Timestamp,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrintEndedPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_position: Option<FilePosition>,
pub outcome: PrintOutcome,
pub reason: String,
pub state: PrinterState,
}
pub type PrintId = String;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PrintOutcome {
#[serde(rename = "completed")]
Completed,
#[serde(rename = "cancelled")]
Cancelled,
#[serde(rename = "failed")]
Failed,
#[serde(rename = "interrupted")]
Interrupted,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrintRecord {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end_reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ended_at: Option<Timestamp>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
pub id: PrintId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub job_print_time_s: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub job_started_at: Option<Timestamp>,
pub narrowings: Vec<ManifestNarrowing>,
pub opened_at: Timestamp,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_print_id: Option<i64>,
pub state: PrinterState,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrintSegmentKey {
pub print_id: PrintId,
pub segment: i64,
pub tool: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrintStartedPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
pub loaded: Vec<LoadedSpool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub preflight: Option<Preflight>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub started_by: Option<ActionId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrintUntrackedPayload {
pub reason: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrinterConnectionChangedPayload {
pub detail: String,
pub from: PrinterState,
pub to: PrinterState,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrinterResetPayload {
pub detail: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub during_print: Option<PrintId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrinterSnapshot {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bed: Option<HeaterSnapshot>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chamber: Option<HeaterSnapshot>,
pub connection: PrinterState,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub fan_percent: Option<Reported>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub feedrate_factor: Option<Reported>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub flowrate_factor: Option<Reported>,
pub observed_at: Timestamp,
pub tools: Vec<HeaterSnapshot>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PrinterState {
#[serde(rename = "operational")]
Operational,
#[serde(rename = "paused")]
Paused,
#[serde(rename = "printing")]
Printing,
#[serde(rename = "cancelling")]
Cancelling,
#[serde(rename = "error")]
Error,
#[serde(rename = "offline")]
Offline,
#[serde(rename = "unknown")]
Unknown(String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrintsAnswer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub active: Option<PrintId>,
pub prints: Vec<PrintRecord>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Range {
pub max: f64,
pub min: f64,
}
pub type RawBytes = String;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RebaselineDisposition {
#[serde(rename = "accounted")]
Accounted,
#[serde(rename = "unverified")]
Unverified,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum RejectionReason {
#[serde(rename = "out_of_bounds")]
OutOfBounds {
adjustable: Adjustable,
allowed: Range,
requested: f64,
},
#[serde(rename = "actor_may_not_request")]
ActorMayNotRequest {
action: ActionKind,
actor_class: ActorClass,
},
#[serde(rename = "no_active_print")]
NoActivePrint,
#[serde(rename = "invalid_from_state")]
InvalidFromState {
state: PrinterState,
},
#[serde(rename = "min_interval_not_elapsed")]
MinIntervalNotElapsed {
interval_s: i64,
since_last_s: i64,
},
#[serde(rename = "unsupported_adjustable")]
UnsupportedAdjustable {
adjustable: Adjustable,
},
#[serde(rename = "insufficient_filament")]
InsufficientFilament {
buffer_g: f64,
remaining_g: f64,
required_g: f64,
spool_id: SpoolId,
spool_name: String,
tool: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
unverified: Option<UnverifiedReason>,
},
#[serde(rename = "unverified_spool")]
UnverifiedSpool {
reason: UnverifiedReason,
spool_id: SpoolId,
spool_name: String,
tool: i64,
},
#[serde(rename = "analysis_unavailable")]
AnalysisUnavailable {
file_name: String,
waited_s: i64,
},
#[serde(rename = "print_already_ended")]
PrintAlreadyEnded,
#[serde(rename = "print_still_active")]
PrintStillActive {
state: PrinterState,
},
#[serde(rename = "intervention_still_active")]
InterventionStillActive {
intervention_id: InterventionId,
},
#[serde(rename = "spool_refused")]
SpoolRefused {
cause: SpoolRefusalCause,
message: String,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Reported {
pub out_of_range: bool,
pub value: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SignalPosition {
pub freshness: PositionFreshness,
pub position: FilePosition,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolAdjustedPayload {
pub actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cleared_marks: Option<Vec<ClearedMark>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gross_g: Option<f64>,
pub reason: String,
pub remaining_after_g: f64,
pub remaining_before_g: f64,
pub spool_id: SpoolId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub took_pending: Option<Vec<PendingTaken>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolAnswer {
pub recent: Vec<EventRecord>,
pub spool: SpoolRecord,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "command")]
pub enum SpoolCommand {
#[serde(rename = "register_spool")]
RegisterSpool {
actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
color: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
density_g_cm3: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
diameter_mm: Option<f64>,
initial_g: f64,
material: String,
name: String,
reason: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
remaining_g: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
tare_g: Option<f64>,
},
#[serde(rename = "load_spool")]
LoadSpool {
actor: Actor,
reason: String,
spool_id: SpoolId,
tool: i64,
},
#[serde(rename = "unload_spool")]
UnloadSpool {
actor: Actor,
reason: String,
spool_id: SpoolId,
},
#[serde(rename = "adjust_spool")]
AdjustSpool {
actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
gross_g: Option<f64>,
reason: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
remaining_g: Option<f64>,
spool_id: SpoolId,
},
#[serde(rename = "retire_spool")]
RetireSpool {
actor: Actor,
reason: String,
spool_id: SpoolId,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolCommandAnswer {
pub decision: PolicyDecision,
pub events: Vec<EventRecord>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub spool: Option<SpoolRecord>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolCommandRejectedPayload {
pub command: SpoolCommand,
pub decision: PolicyDecision,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SpoolField {
#[serde(rename = "name")]
Name,
#[serde(rename = "material")]
Material,
#[serde(rename = "diameter_mm")]
DiameterMm,
#[serde(rename = "density_g_cm3")]
DensityGCm3,
#[serde(rename = "initial_g")]
InitialG,
#[serde(rename = "remaining_g")]
RemainingG,
#[serde(rename = "tare_g")]
TareG,
#[serde(rename = "gross_g")]
GrossG,
}
pub type SpoolId = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "state")]
pub enum SpoolLifecycle {
#[serde(rename = "shelved")]
Shelved,
#[serde(rename = "loaded")]
Loaded {
tool: i64,
},
#[serde(rename = "retired")]
Retired {
at: Timestamp,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolLoadedPayload {
pub actor: Actor,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cleared_marks: Option<Vec<ClearedMark>>,
pub reason: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub replaced: Option<SpoolId>,
pub spool_id: SpoolId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub took_pending: Option<Vec<PendingTaken>>,
pub tool: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolRecord {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub color: Option<String>,
pub density_g_cm3: f64,
pub diameter_mm: f64,
pub id: SpoolId,
pub initial_g: f64,
pub lifecycle: SpoolLifecycle,
pub material: String,
pub name: String,
pub registered_at: Timestamp,
pub remaining_g: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tare_g: Option<f64>,
pub trust: SpoolTrust,
pub updated_at: Timestamp,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "cause")]
pub enum SpoolRefusalCause {
#[serde(rename = "unknown_spool")]
UnknownSpool {
spool_id: SpoolId,
},
#[serde(rename = "retired_spool")]
RetiredSpool {
spool_id: SpoolId,
},
#[serde(rename = "nothing_to_confirm")]
NothingToConfirm {
tool: i64,
},
#[serde(rename = "invalid_adjustment_figure")]
InvalidAdjustmentFigure {
field: SpoolField,
value: f64,
},
#[serde(rename = "tare_required")]
TareRequired {
spool_id: SpoolId,
},
#[serde(rename = "density_required")]
DensityRequired {
material: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
spool_id: Option<SpoolId>,
},
#[serde(rename = "missing_field")]
MissingField {
field: SpoolField,
},
#[serde(rename = "conflicting_fields")]
ConflictingFields {
fields: Vec<SpoolField>,
},
#[serde(rename = "unknown_tool")]
UnknownTool {
tool: i64,
},
#[serde(rename = "not_loaded")]
NotLoaded {
spool_id: SpoolId,
},
#[serde(rename = "ledger_refused")]
LedgerRefused,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolRegisteredPayload {
pub actor: Actor,
pub reason: String,
pub spool: SpoolRecord,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolRetiredPayload {
pub actor: Actor,
pub reason: String,
pub remaining_g: f64,
pub spool_id: SpoolId,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "state")]
pub enum SpoolTrust {
#[serde(rename = "verified")]
Verified,
#[serde(rename = "unverified")]
Unverified {
marks: Vec<UnverifiedMark>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolUnloadedPayload {
pub actor: Actor,
pub reason: String,
pub spool_id: SpoolId,
pub tool: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolUnverifiedPayload {
pub reason: UnverifiedReason,
pub spool_id: SpoolId,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpoolsAnswer {
pub at_a_glance: Vec<String>,
pub pending: Vec<PendingCharge>,
pub spools: Vec<SpoolRecord>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum StartupOutcome {
#[serde(rename = "print_adopted")]
PrintAdopted,
#[serde(rename = "session_resumed")]
SessionResumed {
session_name: String,
},
#[serde(rename = "intervention_expired")]
InterventionExpired {
adjustable: Adjustable,
intervention_id: InterventionId,
outcome: InterventionOutcome,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StartupReconciliationPayload {
pub outcome: StartupOutcome,
pub print_id: PrintId,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StatusAnswer {
pub detector: DetectorHealth,
pub interventions: Vec<Intervention>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub job: Option<JobSnapshot>,
pub print: PrintRecord,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub printer: Option<PrinterSnapshot>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session: Option<SupervisionSession>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SupervisionSession {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub close_reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub closed_at: Option<Timestamp>,
pub created_at: Timestamp,
pub harness_identity: String,
pub last_turn_at: Timestamp,
pub print_id: PrintId,
pub session_name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SupervisionSessionClosedPayload {
pub close_reason: String,
pub session_name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SupervisionSessionOpenedPayload {
pub harness_identity: String,
pub session_name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TimelineAnswer {
pub events: Vec<EventRecord>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next: Option<EventId>,
}
pub type Timestamp = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolFilament {
pub length_mm: f64,
pub tool: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub volume_cm3: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UnaccountedPrintPayload {
pub charged: bool,
pub file_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub printed_at: Option<Timestamp>,
pub success: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UnattendedPrinterActivityPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bed_target_c: Option<f64>,
pub detail: String,
pub last_print_id: PrintId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub moved: Option<bool>,
pub tools: Vec<HeatedTool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UnverifiedMark {
pub reason: UnverifiedReason,
pub since: Timestamp,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "reason")]
pub enum UnverifiedReason {
#[serde(rename = "uncharged_print")]
UnchargedPrint {
print_id: PrintId,
},
#[serde(rename = "unaccounted_print")]
UnaccountedPrint {
file_name: String,
printed_at: Timestamp,
},
#[serde(rename = "filament_change_unconfirmed")]
FilamentChangeUnconfirmed {
print_id: PrintId,
},
#[serde(rename = "filament_change_unconfirmed_position")]
FilamentChangeUnconfirmedPosition {
print_id: PrintId,
},
#[serde(rename = "tally_reset")]
TallyReset {
file_name: String,
origin: String,
},
}
pub trait EventPayloadKind: serde::de::DeserializeOwned {
const KIND: &'static str;
}
impl EventPayloadKind for ActionExecutedPayload {
const KIND: &'static str = "action_executed";
}
impl EventPayloadKind for ActionRejectedPayload {
const KIND: &'static str = "action_rejected";
}
impl EventPayloadKind for ActionRequestedPayload {
const KIND: &'static str = "action_requested";
}
impl EventPayloadKind for AgentAssessmentPayload {
const KIND: &'static str = "agent_assessment";
}
impl EventPayloadKind for CameraDegradedPayload {
const KIND: &'static str = "camera_degraded";
}
impl EventPayloadKind for CameraLookPayload {
const KIND: &'static str = "camera_look";
}
impl EventPayloadKind for ChargePendedPayload {
const KIND: &'static str = "charge_pended";
}
impl EventPayloadKind for DetectorHealthChangedPayload {
const KIND: &'static str = "detector_health_changed";
}
impl EventPayloadKind for FilamentChangeSignalledPayload {
const KIND: &'static str = "filament_change_signalled";
}
impl EventPayloadKind for FilamentConsumedPayload {
const KIND: &'static str = "filament_consumed";
}
impl EventPayloadKind for FileTallyRebaselinedPayload {
const KIND: &'static str = "file_tally_rebaselined";
}
impl EventPayloadKind for InterventionExpiredPayload {
const KIND: &'static str = "intervention_expired";
}
impl EventPayloadKind for MalformedExternalEventPayload {
const KIND: &'static str = "malformed_external_event";
}
impl EventPayloadKind for ObicoFailureAlertPayload {
const KIND: &'static str = "obico_failure_alert";
}
impl EventPayloadKind for ObicoPrinterNotificationPayload {
const KIND: &'static str = "obico_printer_notification";
}
impl EventPayloadKind for OperatorAcknowledgementPayload {
const KIND: &'static str = "operator_acknowledgement";
}
impl EventPayloadKind for PeriodicObservationPayload {
const KIND: &'static str = "periodic_observation";
}
impl EventPayloadKind for PortFailurePayload {
const KIND: &'static str = "port_failure";
}
impl EventPayloadKind for PreflightCheckedPayload {
const KIND: &'static str = "preflight_checked";
}
impl EventPayloadKind for PrintEndedPayload {
const KIND: &'static str = "print_ended";
}
impl EventPayloadKind for PrintStartedPayload {
const KIND: &'static str = "print_started";
}
impl EventPayloadKind for PrintUntrackedPayload {
const KIND: &'static str = "print_untracked";
}
impl EventPayloadKind for PrinterConnectionChangedPayload {
const KIND: &'static str = "printer_connection_changed";
}
impl EventPayloadKind for PrinterResetPayload {
const KIND: &'static str = "printer_reset";
}
impl EventPayloadKind for SpoolAdjustedPayload {
const KIND: &'static str = "spool_adjusted";
}
impl EventPayloadKind for SpoolCommandRejectedPayload {
const KIND: &'static str = "spool_command_rejected";
}
impl EventPayloadKind for SpoolLoadedPayload {
const KIND: &'static str = "spool_loaded";
}
impl EventPayloadKind for SpoolRegisteredPayload {
const KIND: &'static str = "spool_registered";
}
impl EventPayloadKind for SpoolRetiredPayload {
const KIND: &'static str = "spool_retired";
}
impl EventPayloadKind for SpoolUnloadedPayload {
const KIND: &'static str = "spool_unloaded";
}
impl EventPayloadKind for SpoolUnverifiedPayload {
const KIND: &'static str = "spool_unverified";
}
impl EventPayloadKind for StartupReconciliationPayload {
const KIND: &'static str = "startup_reconciliation";
}
impl EventPayloadKind for SupervisionSessionClosedPayload {
const KIND: &'static str = "supervision_session_closed";
}
impl EventPayloadKind for SupervisionSessionOpenedPayload {
const KIND: &'static str = "supervision_session_opened";
}
impl EventPayloadKind for UnaccountedPrintPayload {
const KIND: &'static str = "unaccounted_print";
}
impl EventPayloadKind for UnattendedPrinterActivityPayload {
const KIND: &'static str = "unattended_printer_activity";
}
pub const EVENT_KINDS: [&str; 36] = [
"action_executed",
"action_rejected",
"action_requested",
"agent_assessment",
"camera_degraded",
"camera_look",
"charge_pended",
"detector_health_changed",
"filament_change_signalled",
"filament_consumed",
"file_tally_rebaselined",
"intervention_expired",
"malformed_external_event",
"obico_failure_alert",
"obico_printer_notification",
"operator_acknowledgement",
"periodic_observation",
"port_failure",
"preflight_checked",
"print_ended",
"print_started",
"print_untracked",
"printer_connection_changed",
"printer_reset",
"spool_adjusted",
"spool_command_rejected",
"spool_loaded",
"spool_registered",
"spool_retired",
"spool_unloaded",
"spool_unverified",
"startup_reconciliation",
"supervision_session_closed",
"supervision_session_opened",
"unaccounted_print",
"unattended_printer_activity",
];
impl EventRecord {
#[must_use]
pub fn payload_as<P: EventPayloadKind>(&self) -> Option<Result<P, serde_json::Error>> {
(self.kind == P::KIND).then(|| serde_json::from_value(self.payload.clone()))
}
}
impl Client {
pub fn prints(&self) -> Result<PrintsAnswer, ClientError> {
let target = String::from("/v1/prints");
let asked: Vec<(String, String)> = Vec::new();
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn status(&self, print_id: &str) -> Result<StatusAnswer, ClientError> {
let target = format!("/v1/prints/{print_id}/status");
let asked: Vec<(String, String)> = Vec::new();
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn context(&self, print_id: &str) -> Result<ContextAnswer, ClientError> {
let target = format!("/v1/prints/{print_id}/context");
let asked: Vec<(String, String)> = Vec::new();
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn image(&self, image_id: &str) -> Result<ImageAnswer, ClientError> {
let target = format!("/v1/images/{image_id}");
let asked: Vec<(String, String)> = Vec::new();
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn history(
&self,
print_id: &str,
limit: Option<i64>,
) -> Result<HistoryAnswer, ClientError> {
let target = format!("/v1/prints/{print_id}/history");
let mut asked: Vec<(String, String)> = Vec::new();
if let Some(value) = limit {
asked.push(("limit".to_owned(), value.to_string()));
}
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn events(
&self,
after: Option<String>,
print_id: Option<String>,
kinds: Option<Vec<String>>,
exclude_kinds: Option<Vec<String>>,
wait_s: Option<i64>,
limit: Option<i64>,
) -> Result<EventsAnswer, ClientError> {
let target = String::from("/v1/events");
let mut asked: Vec<(String, String)> = Vec::new();
if let Some(value) = after {
asked.push(("after".to_owned(), value));
}
if let Some(value) = print_id {
asked.push(("print_id".to_owned(), value));
}
for value in kinds.into_iter().flatten() {
asked.push(("kinds".to_owned(), value));
}
for value in exclude_kinds.into_iter().flatten() {
asked.push(("exclude_kinds".to_owned(), value));
}
if let Some(value) = wait_s {
asked.push(("wait_s".to_owned(), value.to_string()));
}
if let Some(value) = limit {
asked.push(("limit".to_owned(), value.to_string()));
}
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn manifest_get(&self, print_id: &str) -> Result<ManifestAnswer, ClientError> {
let target = format!("/v1/prints/{print_id}/manifest");
let asked: Vec<(String, String)> = Vec::new();
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn look(&self, print_id: &str, wait_s: Option<i64>) -> Result<Look, ClientError> {
let target = format!("/v1/prints/{print_id}/look");
let mut asked: Vec<(String, String)> = Vec::new();
if let Some(value) = wait_s {
asked.push(("wait_s".to_owned(), value.to_string()));
}
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn manifest_set(
&self,
print_id: &str,
reason: &str,
manifest: &JobManifest,
) -> Result<ManifestAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/manifest");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
sending.insert("manifest".to_owned(), crate::as_value(&manifest)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("PUT", &target, &asked, sending.as_ref())
}
pub fn spools(&self, include_retired: Option<bool>) -> Result<SpoolsAnswer, ClientError> {
let target = String::from("/v1/spools");
let mut asked: Vec<(String, String)> = Vec::new();
if let Some(value) = include_retired {
asked.push(("include_retired".to_owned(), value.to_string()));
}
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn spool(&self, spool_id: &str) -> Result<SpoolAnswer, ClientError> {
let target = format!("/v1/spools/{spool_id}");
let asked: Vec<(String, String)> = Vec::new();
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn printer_history(
&self,
kinds: Option<Vec<String>>,
since: Option<String>,
until: Option<String>,
spool_id: Option<String>,
before: Option<String>,
limit: Option<i64>,
) -> Result<TimelineAnswer, ClientError> {
let target = String::from("/v1/history");
let mut asked: Vec<(String, String)> = Vec::new();
for value in kinds.into_iter().flatten() {
asked.push(("kinds".to_owned(), value));
}
if let Some(value) = since {
asked.push(("since".to_owned(), value));
}
if let Some(value) = until {
asked.push(("until".to_owned(), value));
}
if let Some(value) = spool_id {
asked.push(("spool_id".to_owned(), value));
}
if let Some(value) = before {
asked.push(("before".to_owned(), value));
}
if let Some(value) = limit {
asked.push(("limit".to_owned(), value.to_string()));
}
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn preflight(&self, file_name: &str) -> Result<PreflightAnswer, ClientError> {
let target = String::from("/v1/preflight");
let asked: Vec<(String, String)> = vec![("file_name".to_owned(), file_name.to_owned())];
let sending: Option<serde_json::Value> = None;
self.call("GET", &target, &asked, sending.as_ref())
}
pub fn pause(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/pause");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn resume(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/resume");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn cancel(&self, print_id: &str, reason: &str) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/cancel");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn start_print(
&self,
print_id: &str,
file_name: &FileName,
manifest: &JobManifest,
reason: &str,
override_filament_check: Option<bool>,
) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/start_print");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("file_name".to_owned(), crate::as_value(&file_name)?);
sending.insert("manifest".to_owned(), crate::as_value(&manifest)?);
if let Some(value) = override_filament_check {
sending.insert(
"override_filament_check".to_owned(),
crate::as_value(&value)?,
);
}
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn set_feedrate_factor(
&self,
print_id: &str,
factor: f64,
reason: &str,
duration_s: Option<i64>,
) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/set_feedrate_factor");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
if let Some(value) = duration_s {
sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
}
sending.insert("factor".to_owned(), crate::as_value(&factor)?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn set_flowrate_factor(
&self,
print_id: &str,
factor: f64,
reason: &str,
duration_s: Option<i64>,
) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/set_flowrate_factor");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
if let Some(value) = duration_s {
sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
}
sending.insert("factor".to_owned(), crate::as_value(&factor)?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn set_tool_target_c(
&self,
print_id: &str,
reason: &str,
target_c: f64,
tool: i64,
duration_s: Option<i64>,
) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/set_tool_target_c");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
if let Some(value) = duration_s {
sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
}
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
sending.insert("target_c".to_owned(), crate::as_value(&target_c)?);
sending.insert("tool".to_owned(), crate::as_value(&tool)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn set_bed_target_c(
&self,
print_id: &str,
reason: &str,
target_c: f64,
duration_s: Option<i64>,
) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/set_bed_target_c");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
if let Some(value) = duration_s {
sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
}
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
sending.insert("target_c".to_owned(), crate::as_value(&target_c)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn set_fan_percent(
&self,
print_id: &str,
percent: f64,
reason: &str,
duration_s: Option<i64>,
) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/set_fan_percent");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
if let Some(value) = duration_s {
sending.insert("duration_s".to_owned(), crate::as_value(&value)?);
}
sending.insert("percent".to_owned(), crate::as_value(&percent)?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn acknowledge_failure(
&self,
print_id: &str,
disposition: &AcknowledgementDisposition,
event_id: &EventId,
reason: &str,
) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/acknowledge_failure");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("disposition".to_owned(), crate::as_value(&disposition)?);
sending.insert("event_id".to_owned(), crate::as_value(&event_id)?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn close_print(
&self,
print_id: &str,
outcome: &PrintOutcome,
reason: &str,
) -> Result<ActionAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/prints/{print_id}/actions/close_print");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("outcome".to_owned(), crate::as_value(&outcome)?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
#[expect(clippy::too_many_arguments)]
pub fn register_spool(
&self,
initial_g: f64,
material: &str,
name: &str,
reason: &str,
color: Option<String>,
density_g_cm3: Option<f64>,
diameter_mm: Option<f64>,
remaining_g: Option<f64>,
tare_g: Option<f64>,
) -> Result<SpoolCommandAnswer, ClientError> {
crate::reason_given(reason)?;
let target = String::from("/v1/spools/actions/register_spool");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
if let Some(value) = color {
sending.insert("color".to_owned(), crate::as_value(&value)?);
}
if let Some(value) = density_g_cm3 {
sending.insert("density_g_cm3".to_owned(), crate::as_value(&value)?);
}
if let Some(value) = diameter_mm {
sending.insert("diameter_mm".to_owned(), crate::as_value(&value)?);
}
sending.insert("initial_g".to_owned(), crate::as_value(&initial_g)?);
sending.insert("material".to_owned(), crate::as_value(&material)?);
sending.insert("name".to_owned(), crate::as_value(&name)?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
if let Some(value) = remaining_g {
sending.insert("remaining_g".to_owned(), crate::as_value(&value)?);
}
if let Some(value) = tare_g {
sending.insert("tare_g".to_owned(), crate::as_value(&value)?);
}
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn load_spool(
&self,
spool_id: &str,
reason: &str,
tool: i64,
) -> Result<SpoolCommandAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/spools/{spool_id}/actions/load_spool");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
sending.insert("tool".to_owned(), crate::as_value(&tool)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn unload_spool(
&self,
spool_id: &str,
reason: &str,
) -> Result<SpoolCommandAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/spools/{spool_id}/actions/unload_spool");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn adjust_spool(
&self,
spool_id: &str,
reason: &str,
gross_g: Option<f64>,
remaining_g: Option<f64>,
) -> Result<SpoolCommandAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/spools/{spool_id}/actions/adjust_spool");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
if let Some(value) = gross_g {
sending.insert("gross_g".to_owned(), crate::as_value(&value)?);
}
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
if let Some(value) = remaining_g {
sending.insert("remaining_g".to_owned(), crate::as_value(&value)?);
}
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
pub fn retire_spool(
&self,
spool_id: &str,
reason: &str,
) -> Result<SpoolCommandAnswer, ClientError> {
crate::reason_given(reason)?;
let target = format!("/v1/spools/{spool_id}/actions/retire_spool");
let asked: Vec<(String, String)> = Vec::new();
let mut sending = serde_json::Map::new();
sending.insert("actor".to_owned(), crate::as_value(&self.actor())?);
sending.insert("reason".to_owned(), crate::as_value(&reason)?);
let sending = Some(serde_json::Value::Object(sending));
self.call("POST", &target, &asked, sending.as_ref())
}
}