CUB-10: Create IMoonrakerClient interface and DTOs

- Expanded IMoonrakerClient interface with 6 strongly-typed methods:
  - GetServerInfoAsync (Moonraker /server/info)
  - IsReachableAsync (connectivity check)
  - GetPrinterInfoAsync (Moonraker /printer/info)
  - GetPrintHistoryAsync (Moonraker /server/history/items)
  - GetPrintStatsAsync (Moonraker /printer/objects/query?print_stats)
  - GetDisplayStatusAsync (Moonraker /printer/objects/query?display_status)
  - GetFilamentUsageAsync (retained for backward compatibility)

- Created Domain/DTOs/Moonraker/ with 7 DTOs:
  - MoonrakerServerInfo, MoonrakerPrinterInfo, MoonrakerPrintJob
  - MoonrakerHistoryResponse, MoonrakerPrintStats
  - MoonrakerDisplayStatus, MoonrakerRequest

- Updated MoonrakerClient implementation to support all new methods
  with proper JSON parsing and mapping helpers

- Full XML doc comments on all public members
This commit is contained in:
2026-04-27 18:42:47 -04:00
committed by dex-bot
parent 6d2634aa6c
commit f668fefbbe
9 changed files with 685 additions and 50 deletions

View File

@@ -0,0 +1,19 @@
namespace Extrudex.Domain.DTOs.Moonraker;
/// <summary>
/// Response DTO for Moonraker /printer/objects/query?display_status endpoint.
/// Contains progress percentage and message for the current print job.
/// Used by the SignalR hub to push real-time progress to connected clients.
/// </summary>
public class MoonrakerDisplayStatus
{
/// <summary>
/// Print progress as a decimal between 0 and 1 (0% to 100%).
/// </summary>
public decimal Progress { get; set; }
/// <summary>
/// Status message displayed on the printer LCD (e.g., "Printing...", "Heating...").
/// </summary>
public string Message { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,20 @@
namespace Extrudex.Domain.DTOs.Moonraker;
/// <summary>
/// Response DTO for the Moonraker /server/history/items endpoint.
/// Wraps the paginated list of print job history items.
/// </summary>
public class MoonrakerHistoryResponse
{
/// <summary>
/// The list of print job history items returned by Moonraker.
/// Most recent jobs appear first (descending by start time).
/// </summary>
public List<MoonrakerPrintJob> Items { get; set; } = [];
/// <summary>
/// Total number of print jobs available on the server
/// (for pagination; the Items list may be a subset).
/// </summary>
public int TotalCount { get; set; }
}

View File

@@ -0,0 +1,56 @@
namespace Extrudex.Domain.DTOs.Moonraker;
/// <summary>
/// Response DTO for a single Moonraker print job history item.
/// Maps to the objects returned by /server/history/items.
/// Contains filament usage, duration, and status for a completed or active print.
/// </summary>
public class MoonrakerPrintJob
{
/// <summary>
/// Unique Moonraker job identifier (e.g., "000001").
/// </summary>
public string JobId { get; set; } = string.Empty;
/// <summary>
/// Filename of the G-code file that was printed.
/// </summary>
public string Filename { get; set; } = string.Empty;
/// <summary>
/// Current status of this print job: "completed", "cancelled", "error", "in_progress".
/// </summary>
public string Status { get; set; } = string.Empty;
/// <summary>
/// Total filament used in millimeters for this print job.
/// This is the primary measurement; grams are derived from this value.
/// </summary>
public decimal FilamentUsedMm { get; set; }
/// <summary>
/// Total print duration in seconds.
/// </summary>
public decimal PrintDurationSeconds { get; set; }
/// <summary>
/// Total print duration including setup and warmup, in seconds.
/// </summary>
public decimal TotalDurationSeconds { get; set; }
/// <summary>
/// Timestamp when the print job started (UTC).
/// </summary>
public DateTime? StartTime { get; set; }
/// <summary>
/// Timestamp when the print job ended (UTC). Null if still in progress.
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// Metadata dictionary from Moonraker. May contain filament_type,
/// filament_name, nozzle_diameter, and other slicer-provided fields.
/// </summary>
public Dictionary<string, object> Metadata { get; set; } = new();
}

View File

@@ -0,0 +1,36 @@
namespace Extrudex.Domain.DTOs.Moonraker;
/// <summary>
/// Response DTO for Moonraker /printer/objects/query?print_stats endpoint.
/// Contains real-time print statistics including current job state,
/// filament consumed, and file being printed.
/// </summary>
public class MoonrakerPrintStats
{
/// <summary>
/// Current print state: "standby", "printing", "paused", "complete", "error", "cancelled".
/// </summary>
public string State { get; set; } = string.Empty;
/// <summary>
/// Total filament used in millimeters for the current print session.
/// </summary>
public decimal FilamentUsedMm { get; set; }
/// <summary>
/// Total print duration in seconds for the current print session.
/// </summary>
public decimal PrintDurationSeconds { get; set; }
/// <summary>
/// Filename of the G-code file currently being printed.
/// Null if no print is active.
/// </summary>
public string? Filename { get; set; }
/// <summary>
/// Detailed message from Klipper about the current print state.
/// May contain error details when state is "error".
/// </summary>
public string? Message { get; set; }
}

View File

@@ -0,0 +1,26 @@
namespace Extrudex.Domain.DTOs.Moonraker;
/// <summary>
/// Response DTO for the Moonraker /printer/info endpoint.
/// Contains the current operational state of the Klipper printer.
/// Used to determine whether the printer is idle, printing, paused, or in error.
/// </summary>
public class MoonrakerPrinterInfo
{
/// <summary>
/// Current Klipper state: "ready", "startup", "shutdown", "error", "cancelled".
/// A "ready" state means the printer is connected and idle.
/// </summary>
public string State { get; set; } = string.Empty;
/// <summary>
/// Detailed state message from Klipper. May contain error details
/// when the state is "error" or "shutdown".
/// </summary>
public string StateMessage { get; set; } = string.Empty;
/// <summary>
/// Whether the Klipper firmware is currently connected and responsive.
/// </summary>
public bool KlippyReady { get; set; }
}

View File

@@ -0,0 +1,25 @@
namespace Extrudex.Domain.DTOs.Moonraker;
/// <summary>
/// Request DTO for querying the Moonraker API.
/// Encapsulates the connection parameters needed to reach a specific
/// Moonraker instance on a Klipper-based printer.
/// </summary>
public class MoonrakerRequest
{
/// <summary>
/// Hostname or IP address of the Moonraker printer.
/// </summary>
public string HostnameOrIp { get; set; } = string.Empty;
/// <summary>
/// Port number for the Moonraker API. Default: 7125.
/// </summary>
public int Port { get; set; } = 7125;
/// <summary>
/// Optional API key for authenticating with Moonraker.
/// Required when the server has API key authentication enabled.
/// </summary>
public string? ApiKey { get; set; }
}

View File

@@ -0,0 +1,44 @@
namespace Extrudex.Domain.DTOs.Moonraker;
/// <summary>
/// Response DTO for the Moonraker /server/info endpoint.
/// Contains server identification and operational state.
/// Used to verify connectivity and determine Moonraker version.
/// </summary>
public class MoonrakerServerInfo
{
/// <summary>
/// The hostname of the Moonraker server (e.g., "mainsail").
/// </summary>
public string Hostname { get; set; } = string.Empty;
/// <summary>
/// Moonraker software version string (e.g., "0.8.0-89ee464").
/// </summary>
public string SoftwareVersion { get; set; } = string.Empty;
/// <summary>
/// CPU model string reported by the host system.
/// </summary>
public string CpuInfo { get; set; } = string.Empty;
/// <summary>
/// Whether Klipper is currently connected to the MCU.
/// </summary>
public bool KlippyConnected { get; set; }
/// <summary>
/// The current Klipper state (e.g., "ready", "startup", "error").
/// </summary>
public string KlippyState { get; set; } = string.Empty;
/// <summary>
/// Whether the Moonraker API requires an authentication token.
/// </summary>
public bool ApiKeyRequired { get; set; }
/// <summary>
/// List of registered Moonraker plugin names.
/// </summary>
public List<string> Plugins { get; set; } = [];
}

View File

@@ -1,23 +1,26 @@
using Extrudex.Domain.DTOs.Moonraker;
namespace Extrudex.Domain.Interfaces;
/// <summary>
/// Client interface for communicating with Moonraker REST API endpoints
/// on Klipper-based printers (e.g., Elegoo Centauri Carbon).
/// Used to retrieve filament usage data, print job status, and
/// remaining spool weight from the printer.
/// Provides strongly-typed methods for server discovery, printer status,
/// print job history, and real-time telemetry.
/// </summary>
public interface IMoonrakerClient
{
/// <summary>
/// Fetches the current filament usage data from the Moonraker server.
/// Returns a dictionary of usage metrics reported by the printer.
/// Checks whether the Moonraker server is reachable and responding.
/// Calls the /server/info endpoint and returns the server information
/// if successful, or null if the server is unreachable.
/// </summary>
/// <param name="hostnameOrIp">The printer's hostname or IP address.</param>
/// <param name="port">The Moonraker API port (default: 7125).</param>
/// <param name="apiKey">Optional API key for authentication.</param>
/// <param name="cancellationToken">Cancellation token for the HTTP request.</param>
/// <returns>A dictionary of usage metric names to their decimal values.</returns>
Task<Dictionary<string, decimal>> GetFilamentUsageAsync(
/// <returns>Server info if reachable; <c>null</c> if unreachable.</returns>
Task<MoonrakerServerInfo?> GetServerInfoAsync(
string hostnameOrIp,
int port,
string? apiKey,
@@ -25,6 +28,8 @@ public interface IMoonrakerClient
/// <summary>
/// Checks whether the Moonraker server is reachable and responding.
/// This is a convenience method equivalent to calling GetServerInfoAsync
/// and checking for a non-null result.
/// </summary>
/// <param name="hostnameOrIp">The printer's hostname or IP address.</param>
/// <param name="port">The Moonraker API port (default: 7125).</param>
@@ -36,4 +41,91 @@ public interface IMoonrakerClient
int port,
string? apiKey,
CancellationToken cancellationToken = default);
/// <summary>
/// Fetches the current printer info from the /printer/info endpoint.
/// Returns the Klipper state and readiness status.
/// </summary>
/// <param name="hostnameOrIp">The printer's hostname or IP address.</param>
/// <param name="port">The Moonraker API port (default: 7125).</param>
/// <param name="apiKey">Optional API key for authentication.</param>
/// <param name="cancellationToken">Cancellation token for the HTTP request.</param>
/// <returns>Printer info if successful; <c>null</c> if the request failed.</returns>
Task<MoonrakerPrinterInfo?> GetPrinterInfoAsync(
string hostnameOrIp,
int port,
string? apiKey,
CancellationToken cancellationToken = default);
/// <summary>
/// Fetches print job history from the /server/history/items endpoint.
/// Returns the most recent print jobs with filament usage data,
/// print duration, and completion status.
/// </summary>
/// <param name="hostnameOrIp">The printer's hostname or IP address.</param>
/// <param name="port">The Moonraker API port (default: 7125).</param>
/// <param name="apiKey">Optional API key for authentication.</param>
/// <param name="limit">Maximum number of history items to return. Default: 50.</param>
/// <param name="cancellationToken">Cancellation token for the HTTP request.</param>
/// <returns>History response with print jobs; empty list if request failed.</returns>
Task<MoonrakerHistoryResponse> GetPrintHistoryAsync(
string hostnameOrIp,
int port,
string? apiKey,
int limit = 50,
CancellationToken cancellationToken = default);
/// <summary>
/// Fetches the current print statistics from the /printer/objects/query endpoint.
/// Returns real-time data including filament used, print duration,
/// and current print state for the active or most recent print.
/// </summary>
/// <param name="hostnameOrIp">The printer's hostname or IP address.</param>
/// <param name="port">The Moonraker API port (default: 7125).</param>
/// <param name="apiKey">Optional API key for authentication.</param>
/// <param name="cancellationToken">Cancellation token for the HTTP request.</param>
/// <returns>Print stats if successful; <c>null</c> if the request failed.</returns>
Task<MoonrakerPrintStats?> GetPrintStatsAsync(
string hostnameOrIp,
int port,
string? apiKey,
CancellationToken cancellationToken = default);
/// <summary>
/// Fetches the current display status from the /printer/objects/query endpoint.
/// Returns progress percentage and status message for the active print.
/// Used by SignalR to push real-time progress updates to connected clients.
/// </summary>
/// <param name="hostnameOrIp">The printer's hostname or IP address.</param>
/// <param name="port">The Moonraker API port (default: 7125).</param>
/// <param name="apiKey">Optional API key for authentication.</param>
/// <param name="cancellationToken">Cancellation token for the HTTP request.</param>
/// <returns>Display status if successful; <c>null</c> if the request failed.</returns>
Task<MoonrakerDisplayStatus?> GetDisplayStatusAsync(
string hostnameOrIp,
int port,
string? apiKey,
CancellationToken cancellationToken = default);
/// <summary>
/// Fetches the current filament usage data from the Moonraker server.
/// Returns a dictionary of usage metrics reported by the printer.
///
/// <para>
/// <b>Prefer GetPrintHistoryAsync or GetPrintStatsAsync for new code.</b>
/// This method is retained for backward compatibility with the
/// FilamentUsageSyncService and returns a dictionary of metric names
/// to their decimal values for callers that don't need typed DTOs.
/// </para>
/// </summary>
/// <param name="hostnameOrIp">The printer's hostname or IP address.</param>
/// <param name="port">The Moonraker API port (default: 7125).</param>
/// <param name="apiKey">Optional API key for authentication.</param>
/// <param name="cancellationToken">Cancellation token for the HTTP request.</param>
/// <returns>A dictionary of usage metric names to their decimal values.</returns>
Task<Dictionary<string, decimal>> GetFilamentUsageAsync(
string hostnameOrIp,
int port,
string? apiKey,
CancellationToken cancellationToken = default);
}