Skip to content

Port printer monitoring to backend-agnostic printer.Printer interface#41

Open
kennhung wants to merge 2 commits into
mainfrom
feat/printer-interface
Open

Port printer monitoring to backend-agnostic printer.Printer interface#41
kennhung wants to merge 2 commits into
mainfrom
feat/printer-interface

Conversation

@kennhung

Copy link
Copy Markdown
Member

Summary

  • Make moonraker.Monitor implement the new internal/printer.Printer (and printer.Thumbnailer) interface, dropping its duplicate PrinterState/MonitorConfig types in favor of the shared ones.
  • Switch main.go, internal/web, and internal/controller to depend on map[string]printer.Printer instead of the concrete *moonraker.Monitor, so a future second backend can be added without touching these layers.
  • Rework the web API's Printer DTO to embed the neutral printer.Job/printer.PrinterState/printer.ErrorInfo types instead of Moonraker-raw structs; regenerate Swagger/OpenAPI + the frontend axios client accordingly.
  • Update frontend/src/types.ts, utils.ts, PrinterCard.tsx, App.tsx to match the new DTO shape (drops the loadedFile/latestJob UUID-matching hack in favor of job.status === "in_progress").

Test plan

  • go build ./... and go vet ./...
  • npx tsc -b && vite build in frontend/
  • npm run lint in frontend/ (only pre-existing generated-client warnings remain)
  • Manual smoke test of /api/v1/printers and the dashboard against a live printer (blocked locally by another dev server already bound to :8080)

🤖 Generated with Claude Code

Make moonraker.Monitor implement internal/printer's Printer and
Thumbnailer interfaces, and switch main.go/internal/web/internal/controller
to depend on printer.Printer instead of the concrete *moonraker.Monitor,
paving the way for additional backends. Regenerate the Swagger/OpenAPI
client and update the frontend to match the resulting neutral DTO shape.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@kennhung

Copy link
Copy Markdown
Member Author

Review

Overview: this PR extracts a backend-agnostic printer.Printer interface (plus optional Thumbnailer/RawReporter capability interfaces) into a new internal/printer package, and migrates moonraker.Monitor, main.go, internal/web, and internal/controller to depend on that interface instead of the concrete Moonraker type. The web DTO and frontend types are reworked to use the new neutral Job/PrinterState/ErrorInfo shapes. go build/go vet/gofmt are all clean, and there's no leftover Moonraker-specific leakage in web/controller/main.

Issues found

1. EstimatedRemaining (remaining_sec) is now computed incorrectly — regression (internal/moonraker/moonraker_monitor.go:141-151)

printDuration := printer.Seconds(m.printerObjects.PrintStats.GetPrintDuration().Seconds())
totalDuration := printer.Seconds(m.printerObjects.PrintStats.GetTotalDuration().Seconds())
...
remaining := totalDuration - printDuration

Klipper's print_stats.total_duration is wall-clock time since the job started including time spent paused, while print_duration excludes paused time. So total_duration - print_duration is the accumulated pause duration, not a completion ETA — for an unpaused print this is ~0 regardless of actual progress.

The previous code computed a real ETA from the slicer's estimated_time in gcode metadata, or extrapolated from virtualSD.progress: totalTime = printDuration / progress; estRemainSec = totalTime - printDuration. That logic isn't reproduced in Monitor.Job(), so the dashboard will show ~0s remaining for actively-printing jobs. Worth fixing before merge since it's a silent, user-facing regression rather than a pure refactor.

2. PrinterObjects(), LatestJob(), LoadedFile() are now dead code outside the package (internal/moonraker/moonraker_monitor.go:97-107)

Nothing outside internal/moonraker calls these anymore since web/controller now go through printer.Printer. Worth deleting if there's no plan to use them.

Minor / non-blocking

  • moonraker_monitor.go:371-378: wire state "startup" used to map to a distinct KlippyStartup (shown as "info"/blue in the frontend); it now collapses into printer.Unknown (shown as "secondary"/gray). Likely an intentional simplification given the smaller printer.PrinterState enum, but it's a small user-visible change — worth confirming it's intended.
  • types.ts:262: errorMessage: errorDetail?.message ?? printer.message — when a backend sets both Message() and ErrorDetail.Message, the frontend now prefers the specific error message over the generic status text. Worth double-checking this precedence matches intent.
  • No unit test covers Monitor.Job()'s remaining-time logic — a test on the paused/non-paused/no-metadata cases would have caught issue Bump ejs from 3.1.9 to 3.1.10 in /frontend #1.
  • PR description notes manual smoke testing was blocked locally (port conflict already in use) — worth doing before merge since Bump ejs from 3.1.9 to 3.1.10 in /frontend #1 produces plausible-but-wrong values rather than a crash, so it wouldn't surface via go vet/build.

No security concerns; import paths and internal/ layout match project conventions.

Job()'s EstimatedRemaining was computed as total_duration -
print_duration (Klipper's accumulated pause time, not an ETA),
regressing the pre-port frontend logic. Restore the original two-path
estimate: slicer's estimated_time scaled by progress, falling back to
extrapolating from print_duration/progress, with total_duration kept
separate for the print/total display fields as before.

Also remove PrinterObjects()/LatestJob()/LoadedFile(), which have had
no external callers since web/controller moved onto printer.Printer.

Addresses review feedback on PR #41.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@kennhung

Copy link
Copy Markdown
Member Author

Thanks for the review — pushed 1dc2b83.

Fixed:

  1. EstimatedRemaining regression — restored the original two-path estimate from main's frontend logic: slicer's estimated_time * (1 - progress) when gcode metadata has it, else extrapolate print_duration/progress - print_duration. Kept TotalDuration as print_stats.total_duration (the separate display-only "elapsed since start incl. pauses" field) — that part wasn't actually wrong, only the ETA derivation from it was.
  2. Removed the now-dead PrinterObjects()/LatestJob()/LoadedFile() — confirmed zero callers repo-wide.

Confirmed intentional (no change):

  • Klippy startupUnknown collapse — matches controller.Connector's pre-existing status mapping (startup already fell to the default/Unknown case there); the frontend color shift is an accepted side effect of unifying the enum.
  • errorMessage: errorDetail?.message ?? printer.message — deliberately prefers the specific error detail over the generic status message.

Still haven't been able to smoke-test locally (port 8080 occupied by an unrelated already-running dev server), so this is verified by go build/go vet plus manually tracing the three Job() cases (slicer estimate, extrapolation fallback, neither available) rather than a live run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant