139 lines
5.1 KiB
C#
139 lines
5.1 KiB
C#
using Extrudex.Domain.Enums;
|
|
using Extrudex.Domain.Interfaces;
|
|
using Extrudex.Infrastructure.Data;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Extrudex.Infrastructure.Configuration;
|
|
|
|
/// <summary>
|
|
/// Service that syncs filament usage data from Moonraker printers into the
|
|
/// Extrudex database. Queries all active Moonraker printers, fetches their
|
|
/// current filament usage metrics, and updates spool remaining weights and
|
|
/// print job records.
|
|
/// </summary>
|
|
public class FilamentUsageSyncService : IFilamentUsageSyncService
|
|
{
|
|
private readonly ExtrudexDbContext _dbContext;
|
|
private readonly IMoonrakerClient _moonrakerClient;
|
|
private readonly ILogger<FilamentUsageSyncService> _logger;
|
|
|
|
/// <summary>
|
|
/// Creates a new FilamentUsageSyncService.
|
|
/// </summary>
|
|
/// <param name="dbContext">The EF Core database context for persisting updates.</param>
|
|
/// <param name="moonrakerClient">The Moonraker HTTP client for fetching printer data.</param>
|
|
/// <param name="logger">Logger for diagnostic output.</param>
|
|
public FilamentUsageSyncService(
|
|
ExtrudexDbContext dbContext,
|
|
IMoonrakerClient moonrakerClient,
|
|
ILogger<FilamentUsageSyncService> logger)
|
|
{
|
|
_dbContext = dbContext;
|
|
_moonrakerClient = moonrakerClient;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<int> SyncAllAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
_logger.LogInformation("Starting filament usage sync cycle");
|
|
|
|
var printers = await _dbContext.Printers
|
|
.Where(p => p.IsActive && p.ConnectionType == ConnectionType.Moonraker)
|
|
.Include(p => p.AmsUnits)
|
|
.ThenInclude(u => u.Slots)
|
|
.ThenInclude(s => s.Spool)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
if (printers.Count == 0)
|
|
{
|
|
_logger.LogInformation("No active Moonraker printers found — skipping sync");
|
|
return 0;
|
|
}
|
|
|
|
_logger.LogInformation("Found {PrinterCount} active Moonraker printer(s) to sync", printers.Count);
|
|
|
|
var syncedCount = 0;
|
|
|
|
foreach (var printer in printers)
|
|
{
|
|
try
|
|
{
|
|
var usageData = await _moonrakerClient.GetFilamentUsageAsync(
|
|
printer.HostnameOrIp,
|
|
printer.Port,
|
|
printer.ApiKey,
|
|
cancellationToken);
|
|
|
|
if (usageData.Count == 0)
|
|
{
|
|
_logger.LogWarning(
|
|
"No usage data returned from printer {PrinterName} ({Host}:{Port})",
|
|
printer.Name, printer.HostnameOrIp, printer.Port);
|
|
continue;
|
|
}
|
|
|
|
// Update spool remaining weights from AMS data
|
|
UpdateSpoolWeights(printer, usageData);
|
|
|
|
// Mark printer as seen and idle (reachable = idle, not printing)
|
|
printer.LastSeenAt = DateTime.UtcNow;
|
|
printer.Status = PrinterStatus.Idle;
|
|
|
|
syncedCount++;
|
|
_logger.LogInformation(
|
|
"Successfully synced filament usage from printer {PrinterName}",
|
|
printer.Name);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex,
|
|
"Error syncing filament usage from printer {PrinterName} ({Host}:{Port})",
|
|
printer.Name, printer.HostnameOrIp, printer.Port);
|
|
}
|
|
}
|
|
|
|
await _dbContext.SaveChangesAsync(cancellationToken);
|
|
|
|
_logger.LogInformation(
|
|
"Filament usage sync cycle complete — {SyncedCount}/{TotalCount} printers synced",
|
|
syncedCount, printers.Count);
|
|
|
|
return syncedCount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates spool remaining weights based on usage data received from Moonraker.
|
|
/// For printers with AMS units, updates the remaining weight on each slot's spool.
|
|
/// </summary>
|
|
private void UpdateSpoolWeights(
|
|
Domain.Entities.Printer printer,
|
|
Dictionary<string, decimal> usageData)
|
|
{
|
|
// Update AMS slot remaining weights if available
|
|
foreach (var amsUnit in printer.AmsUnits)
|
|
{
|
|
foreach (var slot in amsUnit.Slots)
|
|
{
|
|
if (slot.Spool != null && slot.RemainingWeightG.HasValue)
|
|
{
|
|
// Sync the AMS-reported remaining weight to the spool
|
|
slot.Spool.WeightRemainingGrams = slot.RemainingWeightG.Value;
|
|
|
|
_logger.LogDebug(
|
|
"Updated spool {SpoolSerial} remaining weight to {Weight}g",
|
|
slot.Spool.SpoolSerial, slot.RemainingWeightG.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If usage data contains extruded mm, log it for observability
|
|
if (usageData.TryGetValue("mm_extruded", out var mmExtruded) && mmExtruded > 0)
|
|
{
|
|
_logger.LogInformation(
|
|
"Printer {PrinterName} reports {MmExtruded}mm filament extruded in latest job",
|
|
printer.Name, mmExtruded);
|
|
}
|
|
}
|
|
} |