Skip to content

Commit d96aa4f

Browse files
committed
fix: telemetry issues
1 parent c6f966d commit d96aa4f

5 files changed

Lines changed: 154 additions & 58 deletions

File tree

‎MakerPrompt.Shared/Infrastructure/ConnectionComponentBase.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ protected virtual void HandleConnectionChanged(object? sender, bool connected)
7676
PrinterServiceFactory.Current.TelemetryUpdated -= OnTelemetryUpdated;
7777
}
7878
}
79-
StateHasChanged();
79+
InvokeAsync(StateHasChanged);
8080
}
8181

8282
public async ValueTask DisposeAsync()

‎MakerPrompt.Shared/Pages/Fleet.razor‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
<i class="bi bi-x-circle me-1"></i>@localizer[Resources.NavConnection_Disconnect]
4040
</button>
4141
</div>
42-
<ControlPanel />
42+
<ControlPanel @key="_selectedPrinterId" />
4343
</div>
4444
}
4545
else
@@ -361,9 +361,14 @@ else
361361
// Keep selected printer reference fresh
362362
if (_selectedPrinterId.HasValue)
363363
{
364-
_selectedPrinter = ConnectionManager.Printers.FirstOrDefault(p => p.Definition.Id == _selectedPrinterId);
364+
_selectedPrinter = ConnectionManager.Printers.ToList().FirstOrDefault(p => p.Definition.Id == _selectedPrinterId);
365365
if (_selectedPrinter == null)
366366
_selectedPrinterId = null;
367+
// When the selected printer just became connected (e.g. user hit Connect
368+
// while its card was selected), sync factory.Current before the re-render
369+
// creates the dashboard — otherwise the StorageExplorer reads stale data.
370+
else if (_selectedPrinter.Service?.IsConnected == true && !_selectedPrinter.IsActive)
371+
ConnectionManager.SetActivePrinter(_selectedPrinter.Definition.Id);
367372
}
368373

369374
await RefreshCameraUrlsAsync();
@@ -400,7 +405,7 @@ else
400405
// ── Camera ──
401406
private async Task RefreshCameraUrlsAsync()
402407
{
403-
foreach (var printer in ConnectionManager.Printers)
408+
foreach (var printer in ConnectionManager.Printers.ToList())
404409
{
405410
if (printer.Service == null || !printer.Service.IsConnected)
406411
{

‎MakerPrompt.Shared/Pages/Settings.razor‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99
<LocalizedTitle TitleKey="Settings" />
1010

1111
<div class="container-fluid">
12-
<h3 class="mb-4">Settings</h3>
13-
1412
<div class="row">
15-
<div class="col-md-6">
13+
<div class="col-md-6 col-sm-12">
1614
<div class="card mb-4">
1715
<div class="card-header">
1816
<strong>General</strong>
@@ -58,13 +56,18 @@
5856
</div>
5957
</div>
6058

59+
</div>
60+
<div class="col-md-6 col-sm-12">
61+
<StorageExplorer Title="App storage" Provider="AppStorage" EnableUpload="true" EnableDelete="true" EnableOpen="true" EnableCopy="false" HiddenPrefixes="_hiddenPrefixes" />
62+
</div>
63+
</div>
64+
<div class="row">
65+
<div class="col">
6166
<button class="btn btn-primary" @onclick="SaveSettingsAsync">Save Settings</button>
6267
</div>
6368
</div>
6469
</div>
6570

66-
<StorageExplorer Title="App storage" Provider="AppStorage" EnableUpload="true" EnableDelete="true" EnableOpen="true" EnableCopy="false" HiddenPrefixes="_hiddenPrefixes" />
67-
6871
@code {
6972
private AppConfiguration _config = new();
7073
private static readonly string[] _hiddenPrefixes = ["MakerPrompt.PrinterConnections", "MakerPrompt.PrintProjects"];

‎MakerPrompt.Shared/Services/MoonrakerApiService.cs‎

Lines changed: 82 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -121,53 +121,96 @@ public async Task<PrinterTelemetry> GetPrinterTelemetryAsync()
121121
if (!IsConnected) return LastTelemetry;
122122

123123
// Get temperature data
124-
var tempResponse = await Client.GetAsync("/printer/objects/query?heater_bed&extruder", _cts.Token);
125-
if (tempResponse.IsSuccessStatusCode)
124+
try
126125
{
127-
var json = await tempResponse.Content.ReadAsStringAsync();
128-
using var doc = JsonDocument.Parse(json);
129-
var root = doc.RootElement.GetProperty("result").GetProperty("status");
130-
131-
LastTelemetry.BedTemp = root.GetProperty("heater_bed").GetProperty("temperature").GetDouble();
132-
LastTelemetry.BedTarget = root.GetProperty("heater_bed").GetProperty("target").GetDouble();
133-
LastTelemetry.HotendTemp = root.GetProperty("extruder").GetProperty("temperature").GetDouble();
134-
LastTelemetry.HotendTarget = root.GetProperty("extruder").GetProperty("target").GetDouble();
126+
var tempResponse = await Client.GetAsync("/printer/objects/query?heater_bed&extruder", _cts.Token);
127+
if (tempResponse.IsSuccessStatusCode)
128+
{
129+
var json = await tempResponse.Content.ReadAsStringAsync();
130+
using var doc = JsonDocument.Parse(json);
131+
var root = doc.RootElement.GetProperty("result").GetProperty("status");
132+
133+
LastTelemetry.BedTemp = root.GetProperty("heater_bed").GetProperty("temperature").GetDouble();
134+
LastTelemetry.BedTarget = root.GetProperty("heater_bed").GetProperty("target").GetDouble();
135+
LastTelemetry.HotendTemp = root.GetProperty("extruder").GetProperty("temperature").GetDouble();
136+
LastTelemetry.HotendTarget = root.GetProperty("extruder").GetProperty("target").GetDouble();
137+
}
135138
}
139+
catch { }
136140

137-
var motionResponse = await Client.GetAsync("/printer/objects/query?gcode_move,fan", _cts.Token);
138-
if (motionResponse.IsSuccessStatusCode)
141+
// Get motion and fan data (each object must be a separate query parameter)
142+
try
139143
{
140-
var json = await motionResponse.Content.ReadAsStringAsync();
141-
using var doc = JsonDocument.Parse(json);
142-
var status = doc.RootElement.GetProperty("result").GetProperty("status");
143-
144-
// Position data
145-
var position = status.GetProperty("gcode_move").GetProperty("position");
146-
LastTelemetry.Position = new Vector3(
147-
(float)position[0].GetDecimal(),
148-
(float)position[1].GetDecimal(),
149-
(float)position[2].GetDecimal()
150-
);
151-
// Speed and flow data
152-
LastTelemetry.FeedRate = (int)status.GetProperty("gcode_move")
153-
.GetProperty("speed").GetDecimal();
154-
LastTelemetry.FlowRate = (int)(status.GetProperty("gcode_move")
155-
.GetProperty("extrude_factor").GetDecimal() * 100);
156-
157-
// Fan speed
158-
LastTelemetry.FanSpeed = (int)(status.GetProperty("fan")
159-
.GetProperty("speed").GetDecimal() * 100);
144+
var motionResponse = await Client.GetAsync("/printer/objects/query?gcode_move&fan", _cts.Token);
145+
if (motionResponse.IsSuccessStatusCode)
146+
{
147+
var json = await motionResponse.Content.ReadAsStringAsync();
148+
using var doc = JsonDocument.Parse(json);
149+
var status = doc.RootElement.GetProperty("result").GetProperty("status");
150+
151+
// Position data
152+
var position = status.GetProperty("gcode_move").GetProperty("position");
153+
LastTelemetry.Position = new Vector3(
154+
(float)position[0].GetDecimal(),
155+
(float)position[1].GetDecimal(),
156+
(float)position[2].GetDecimal()
157+
);
158+
// Speed and flow data
159+
LastTelemetry.FeedRate = (int)status.GetProperty("gcode_move")
160+
.GetProperty("speed").GetDecimal();
161+
LastTelemetry.FlowRate = (int)(status.GetProperty("gcode_move")
162+
.GetProperty("extrude_factor").GetDecimal() * 100);
163+
164+
// Fan speed
165+
LastTelemetry.FanSpeed = (int)(status.GetProperty("fan")
166+
.GetProperty("speed").GetDecimal() * 100);
167+
}
160168
}
169+
catch { }
161170

162-
// Get printer status
163-
var statusResponse = await Client.GetAsync("/printer/objects/query?print_stats", _cts.Token);
164-
if (statusResponse.IsSuccessStatusCode)
171+
// Get printer status, print progress, and job info
172+
try
165173
{
166-
var json = await statusResponse.Content.ReadAsStringAsync();
167-
LastTelemetry.Status = json.Contains("printing") ?
168-
PrinterStatus.Printing :
169-
PrinterStatus.Connected;
174+
var statusResponse = await Client.GetAsync("/printer/objects/query?print_stats&virtual_sdcard", _cts.Token);
175+
if (statusResponse.IsSuccessStatusCode)
176+
{
177+
var json = await statusResponse.Content.ReadAsStringAsync();
178+
using var doc = JsonDocument.Parse(json);
179+
var status = doc.RootElement.GetProperty("result").GetProperty("status");
180+
181+
if (status.TryGetProperty("print_stats", out var printStats))
182+
{
183+
var state = printStats.TryGetProperty("state", out var stateProp)
184+
? stateProp.GetString() ?? ""
185+
: "";
186+
LastTelemetry.Status = state switch
187+
{
188+
"printing" => PrinterStatus.Printing,
189+
"paused" => PrinterStatus.Paused,
190+
"error" => PrinterStatus.Error,
191+
"complete" or "cancelled" or "standby" => PrinterStatus.Connected,
192+
_ => PrinterStatus.Connected
193+
};
194+
LastTelemetry.SDCard.Printing = state == "printing";
195+
196+
if (printStats.TryGetProperty("filename", out var filenameProp))
197+
LastTelemetry.PrintJobName = filenameProp.GetString() ?? "";
198+
199+
if (printStats.TryGetProperty("print_duration", out var durationProp))
200+
LastTelemetry.PrintDuration = TimeSpan.FromSeconds(durationProp.GetDouble());
201+
202+
if (printStats.TryGetProperty("filament_used", out var filamentProp))
203+
LastTelemetry.FilamentUsed = filamentProp.GetDouble();
204+
}
205+
206+
if (status.TryGetProperty("virtual_sdcard", out var sdcard))
207+
{
208+
if (sdcard.TryGetProperty("progress", out var progressProp))
209+
LastTelemetry.SDCard.Progress = progressProp.GetDouble() * 100;
210+
}
211+
}
170212
}
213+
catch { }
171214

172215
RaiseTelemetryUpdated();
173216
return LastTelemetry;

‎MakerPrompt.Shared/Services/PrusaLinkApiService.cs‎

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ public async Task<PrinterTelemetry> GetPrinterTelemetryAsync()
121121
if (status.Job != null)
122122
{
123123
LastTelemetry.SDCard.Printing = status.Job.State?.Equals("PRINTING", StringComparison.OrdinalIgnoreCase) == true;
124+
LastTelemetry.SDCard.Progress = status.Job.Progress ?? LastTelemetry.SDCard.Progress;
125+
126+
if (status.Job.TimePrinting.HasValue)
127+
LastTelemetry.PrintDuration = TimeSpan.FromSeconds(status.Job.TimePrinting.Value);
124128
}
125129

126130
if (status.Storage != null)
@@ -149,15 +153,36 @@ public async Task<List<FileEntry>> GetFilesAsync()
149153
return [];
150154
}
151155

152-
return folder.Children.Select(child => new FileEntry
156+
var storagePath = firstStorage.Path.TrimEnd('/');
157+
var result = new List<FileEntry>();
158+
CollectFiles(folder.Children, storagePath, result);
159+
return result;
160+
}
161+
162+
private static void CollectFiles(List<PrusaFileSystemEntry> entries, string parentPath, List<FileEntry> result)
163+
{
164+
foreach (var child in entries)
153165
{
154-
FullPath = $"{firstStorage.Path}{child.Path ?? string.Empty}",
155-
Size = child.Size ?? 0,
156-
ModifiedDate = child.Timestamp.HasValue
157-
? DateTimeOffset.FromUnixTimeSeconds(child.Timestamp.Value).DateTime
158-
: null,
159-
IsAvailable = !child.ReadOnly
160-
}).ToList();
166+
if (string.Equals(child.Type, "FOLDER", StringComparison.OrdinalIgnoreCase))
167+
{
168+
if (child.Children != null)
169+
{
170+
var folderPath = $"{parentPath}/{child.DisplayName ?? child.Name}";
171+
CollectFiles(child.Children, folderPath, result);
172+
}
173+
continue;
174+
}
175+
176+
result.Add(new FileEntry
177+
{
178+
FullPath = $"{parentPath}/{child.DisplayName ?? child.Name}",
179+
Size = child.Size ?? 0,
180+
ModifiedDate = child.Timestamp.HasValue
181+
? DateTimeOffset.FromUnixTimeSeconds(child.Timestamp.Value).DateTime
182+
: null,
183+
IsAvailable = !child.ReadOnly
184+
});
185+
}
161186
}
162187

163188
public Task SetHotendTemp(int targetTemp = 0) =>
@@ -298,6 +323,12 @@ private void ConfigureClient(ApiConnectionSettings settings)
298323
return await GetAsync<PrusaFileSystemEntry>(requestPath, cancellationToken);
299324
}
300325

326+
private static readonly JsonSerializerOptions s_jsonOptions = new()
327+
{
328+
PropertyNameCaseInsensitive = true,
329+
NumberHandling = JsonNumberHandling.AllowReadingFromString,
330+
};
331+
301332
private async Task<T?> GetAsync<T>(string path, CancellationToken cancellationToken)
302333
{
303334
var request = new HttpRequestMessage(HttpMethod.Get, path);
@@ -308,7 +339,7 @@ private void ConfigureClient(ApiConnectionSettings settings)
308339
}
309340

310341
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
311-
return await JsonSerializer.DeserializeAsync<T>(stream, cancellationToken: cancellationToken);
342+
return await JsonSerializer.DeserializeAsync<T>(stream, s_jsonOptions, cancellationToken);
312343
}
313344

314345
private async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage request, CancellationToken cancellationToken)
@@ -403,12 +434,15 @@ public sealed class PrusaStatusPrinter
403434
public double? TargetBed { get; set; }
404435

405436
[JsonPropertyName("axis_x")]
437+
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
406438
public float? AxisX { get; set; }
407439

408440
[JsonPropertyName("axis_y")]
441+
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
409442
public float? AxisY { get; set; }
410443

411444
[JsonPropertyName("axis_z")]
445+
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
412446
public float? AxisZ { get; set; }
413447

414448
[JsonPropertyName("flow")]
@@ -477,11 +511,22 @@ public sealed class PrusaFileSystemEntry
477511
[JsonPropertyName("name")]
478512
public string Name { get; set; } = string.Empty;
479513

514+
[JsonPropertyName("display_name")]
515+
public string? DisplayName { get; set; }
516+
480517
[JsonPropertyName("path")]
481518
public string? Path { get; set; }
482519

520+
// Some firmware versions use "ro", others use "read_only".
521+
// PropertyNameCaseInsensitive + both properties covers all variants.
522+
[JsonPropertyName("ro")]
523+
public bool? Ro { get; set; }
524+
483525
[JsonPropertyName("read_only")]
484-
public bool ReadOnly { get; set; }
526+
public bool? ReadOnlyFlag { get; set; }
527+
528+
[JsonIgnore]
529+
public bool ReadOnly => Ro ?? ReadOnlyFlag ?? false;
485530

486531
[JsonPropertyName("size")]
487532
public long? Size { get; set; }

0 commit comments

Comments
 (0)