Add start_print to the printer command set

Held back until there was a job path to drive it; the queueing work needs it
now. Three things are deliberate about its shape.

It carries a presigned `url`, not the file's bytes. A sliced project runs to
tens of megabytes and this is a JSON control channel, and a URL also keeps
object-store credentials out of every gateway on every farm. The schema says
to presign it at *dispatch* time rather than at job creation, because a queued
job may wait days and a URL minted when it was queued would be long expired.

It carries no filament or AMS field. The gateway reads the mapping out of the
archive's own `Metadata/slice_info.config`, because the archive is the only
thing that knows what each plate was sliced for. Passing one in would
reintroduce the guess that made a real P1P heat to 250C and pause itself at
1%.

And its description is blunt about what accepting it means, which is: very
little. A full end-to-end run on 2026-10-15 was accepted, uploaded
byte-perfect, reached RUNNING, and then the printer paused itself on a
physical broken-filament fault. Job state has to be driven by the telemetry
that follows, never by this reply - so the schema says so where a caller will
read it, rather than leaving it to be rediscovered.
This commit is contained in:
2026-10-15 22:43:03 +00:00
parent 7a9b19f72a
commit b454e30c87
2 changed files with 50 additions and 1 deletions
+24
View File
@@ -75,6 +75,30 @@ export type PrinterTelemetry = Static<typeof PrinterTelemetrySchema>;
* start-an-uploaded-file path has been run against real hardware by hand.
*/
export const PrinterCommandSchema = Type.Union([
Type.Object({
action: Type.Literal("start_print"),
/**
* The cloud's `print_jobs.id`. The gateway does not need it to do the
* work - it is here so a gateway's logs line up with a job.
*/
job_id: Type.String({ format: "uuid" }),
/**
* What to call the file on the printer's own storage. A `.3mf` is a
* sliced project and its filament mapping is read out of the archive;
* anything else is bare gcode and carries no mapping.
*/
file_name: Type.String({ minLength: 1, maxLength: 500 }),
/**
* A presigned, expiring GET for the job file - a URL rather than the
* bytes, because the file is tens of megabytes and this is a JSON
* control channel, and because it keeps object-store credentials out of
* every gateway on every farm.
*
* Presign this at *dispatch* time, never at job creation: a queued job
* may wait days and the URL would be long expired.
*/
url: Type.String({ format: "uri" }),
}),
Type.Object({
action: Type.Literal("set_fan_speed"),
/**