mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 05:27:50 +00:00
temp: doc for intended change
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
# Cloud print job: MQTT-native design (not yet implemented)
|
||||
|
||||
`OrcaPrinterAgent::start_print` currently finalizes a cloud print job over HTTP
|
||||
(`OrcaCloudServiceAgent::start_cloud_print_job`, `POST
|
||||
/api/v1/printers/<dev_id>/print-jobs/<job_id>/start`). This document records
|
||||
the MQTT-native alternative that was designed as the intended replacement, the
|
||||
gap that blocks it today, and why it should not be built by adding fields to
|
||||
`print.gcode_file`.
|
||||
|
||||
## Current (implemented) flow
|
||||
|
||||
1. `OrcaCloudServiceAgent::upload_gcode_via_cloud`
|
||||
- `POST print-jobs/uploads` -> `{job_id, upload_url, expires_at, max_bytes}`
|
||||
- `PUT upload_url` -> raw G-code straight to R2 (presigned, PUT-only, no
|
||||
bearer token; must not go through the `http_put` helper, which always
|
||||
prefixes `api_base_url` and attaches the cloud session's Authorization
|
||||
header).
|
||||
2. `OrcaCloudServiceAgent::start_cloud_print_job`
|
||||
- `POST print-jobs/<job_id>/start` with `{filename, start}`.
|
||||
- The gateway HEAD-verifies the R2 object landed, mints a short-lived
|
||||
signed *download* URL (`createPrintJobDownloadToken` in
|
||||
`apps/gateway/src/services/printer-print-jobs.ts`), and relays a
|
||||
`print.project_file` command carrying that URL to the printer -
|
||||
**over the gateway's own relay connection to OrcaSonar, not
|
||||
OrcaSlicer's MQTT session.** OrcaSonar's `executeProjectFile`
|
||||
(`internal/cloud/printfile.go`) downloads from that URL and starts the
|
||||
print.
|
||||
|
||||
This works today and requires no changes to OrcaCloud or OrcaSonar.
|
||||
|
||||
## Why an MQTT-native version is desirable
|
||||
|
||||
The HTTP finalize call means `start_print`'s cloud path depends on the
|
||||
client's REST session (auth token, network path to the gateway's HTTP API)
|
||||
in addition to its MQTT session. An MQTT-native version would let OrcaSlicer
|
||||
trigger the download-and-start entirely over the connection it already
|
||||
maintains for every other printer command.
|
||||
|
||||
## The key finding: OrcaSonar already supports this, transport-agnostically
|
||||
|
||||
`print.project_file` is not tied to the HTTP `/start` route. OrcaSonar's
|
||||
cloud MQTT message handler intercepts it purely by command name, before
|
||||
routing to the generic per-namespace dispatcher:
|
||||
|
||||
```go
|
||||
// internal/cloud/client.go, makeRequestHandler
|
||||
if cmd.Namespace == "print" && cmd.Name == "project_file" {
|
||||
go c.handleProjectFile(ctx, cmd) // downloads `url`, then starts if start=true
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
This fires for **any** message that reaches OrcaSonar's own outbound cloud
|
||||
MQTT session on `device/<dev_id>/request` - regardless of whether it was
|
||||
published there by the gateway's internal relay (today's `/start` path) or
|
||||
by a client publishing directly. OrcaSlicer's cloud MQTT connection already
|
||||
publishes to that same topic for every other cloud command
|
||||
(`OrcaPrinterAgent::route_send(is_lan=false, ...)` - `set_bed_temp`,
|
||||
`ams_change_filament`, etc.), via the same relay-shard mechanism
|
||||
(`apps/gateway/src/lib/relay-router.ts`). So **OrcaSlicer publishing
|
||||
`print.project_file` itself, over its existing cloud MQTT connection, would
|
||||
already reach `handleProjectFile` and trigger the identical download-then-
|
||||
start behavior - with zero new code on OrcaSonar.**
|
||||
|
||||
(This only works over the cloud relay session. OrcaSonar's LAN-side/generic
|
||||
dispatcher, `internal/bridge/klipper/adapter.go`, treats `print.project_file`
|
||||
as an unmapped macro call - a no-op in practice. That's fine: R2 upload is a
|
||||
cloud-only feature to begin with.)
|
||||
|
||||
## The one real gap: no GET-signed download URL is exposed to the client
|
||||
|
||||
`handleProjectFile` needs a URL it can `GET`. The `upload_url` returned by
|
||||
`POST print-jobs/uploads` is presigned for `PUT` only - S3 SigV4 signatures
|
||||
are bound to the HTTP method, so it cannot be reused for a download.
|
||||
|
||||
Minting a download URL/token already exists as a function
|
||||
(`createPrintJobDownloadToken` in
|
||||
`apps/gateway/src/services/printer-print-jobs.ts`) and the exact URL
|
||||
template is already built in `dispatchPrintFileCommand`
|
||||
(`apps/gateway/src/routes/printer.ts`) - it is simply never returned to the
|
||||
API caller today, only used server-side when `/start` builds the relay
|
||||
payload itself.
|
||||
|
||||
**Required OrcaCloud change:** have `POST print-jobs/uploads` (or a small
|
||||
follow-up call) also mint and return a signed download URL alongside
|
||||
`upload_url`, reusing `createPrintJobDownloadToken` + the existing URL
|
||||
template. This is on the order of ~10 lines in an existing handler, not a new
|
||||
permission model - the caller is already an authenticated, authorized user of
|
||||
that printer, identically to who is authorized to call `/start` today.
|
||||
|
||||
No OrcaSonar change is required at all.
|
||||
|
||||
## Why this should NOT be built into `print.gcode_file` / `start_sdcard_print`
|
||||
|
||||
`print.gcode_file` (sent by `OrcaPrinterAgent::start_sdcard_print`) is the
|
||||
generic "start this file that is already on the printer" primitive. It is
|
||||
used by the LAN `start_local_print` path today, is meant to stay usable for
|
||||
starting any file already on the SD card by filename alone, and is expected
|
||||
to grow parameters unrelated to cloud upload (e.g. filament mapping) over
|
||||
time.
|
||||
|
||||
Making `gcode_file` download-aware would require either:
|
||||
- adding cloud-specific fields (`job_id`, a download `url`, ...) to a command
|
||||
that has nothing to do with cloud jobs in the LAN case, forcing all of them
|
||||
to be optional/unused most of the time, or
|
||||
- giving OrcaSonar a side-channel registry of "filenames currently being
|
||||
downloaded" that `gcode_file`'s handler consults - solvable, but an
|
||||
orthogonal change with its own design questions (see "decoupled two-step
|
||||
option" below).
|
||||
|
||||
Neither is necessary: `print.project_file` already exists as a fully
|
||||
separate, fully-working command for exactly the "not yet on the printer,
|
||||
fetch it first" case, so the cloud upload flow does not need to touch
|
||||
`gcode_file` at all.
|
||||
|
||||
## Intended MQTT-native design, once the gap above is closed
|
||||
|
||||
Replace the HTTP finalize step (`start_cloud_print_job`) with: build and
|
||||
publish, over the cloud MQTT connection (`route_send(is_lan=false, ...)`),
|
||||
|
||||
```json
|
||||
{"print": {"command": "project_file", "sequence_id": "...",
|
||||
"url": "<signed download URL from the uploads response>",
|
||||
"param": "<remote_gcode_name(params)>",
|
||||
"start": true}}
|
||||
```
|
||||
|
||||
`start_sdcard_print` / `print.gcode_file` remains untouched and fully
|
||||
decoupled.
|
||||
|
||||
### Decoupled two-step option
|
||||
|
||||
If a use case ever needs "download now, start later" as an explicit user
|
||||
action (rather than upload-and-immediately-print), the same
|
||||
`print.project_file` command already supports it via `start: false` (download
|
||||
and store only - see `executeProjectFile`'s `start` handling in
|
||||
`internal/cloud/printfile.go`). The later "start" action would then be a
|
||||
perfectly ordinary `print.gcode_file` with `param: <filename>`, going through
|
||||
the existing, generic `start_sdcard_print` unmodified. This still requires no
|
||||
protocol changes beyond the download-URL gap above.
|
||||
|
||||
## Summary of gaps
|
||||
|
||||
| Component | Change needed |
|
||||
|---|---|
|
||||
| OrcaCloud (gateway) | Return a signed download URL from `POST print-jobs/uploads` (or a small sibling endpoint), reusing existing `createPrintJobDownloadToken` logic. |
|
||||
| OrcaSonar | None. `print.project_file` handling already does exactly what's needed, transport-agnostically, on the cloud MQTT session. |
|
||||
| OrcaSlicer (this repo) | Once the above lands: replace `start_cloud_print_job`'s HTTP call with a `print.project_file` publish over the cloud MQTT connection. `start_sdcard_print` stays untouched either way. |
|
||||
Reference in New Issue
Block a user