Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ spaghettichef-print-files/**
spaghettichef-camera/**
response.txt
message.txt
*.log


# dataset pictures and snapshots pictures
Expand Down
18 changes: 0 additions & 18 deletions spaghettichef.log

This file was deleted.

22 changes: 22 additions & 0 deletions src/main/resources/dashboard/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,28 @@ export async function getCameraSnapshotJobs(printerId) {
return Array.isArray(data.jobs) ? data.jobs : [];
}

export async function syncCameraStorage(printerId, options = {}) {
const body = {
layout: "runtime-camera-storage",
dryRun: options.dryRun === true,
syncSnapshots: options.syncSnapshots !== false,
syncDeltas: options.syncDeltas !== false,
deleteRowsForMissingFiles: options.deleteRowsForMissingFiles !== false,
reactivateDeletedSnapshotRows: options.reactivateDeletedSnapshotRows !== false,
createMissingCameraJobs: options.createMissingCameraJobs !== false,
createMissingDeltaSets: options.createMissingDeltaSets !== false,
requiredConfirmation: options.requiredConfirmation || "SYNC_CAMERA_DATASET"
};

return requestJson(`/admin/camera/storage/${encodeURIComponent(printerId)}/sync`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
}

export async function getCameraSnapshotJobEntries(jobId, printerId) {
const query = printerId ? `?printerId=${encodeURIComponent(printerId)}` : "";
const data = await requestJson(`/admin/camera/snapshot/jobs/${encodeURIComponent(jobId)}${query}`);
Expand Down
34 changes: 34 additions & 0 deletions src/main/resources/dashboard/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
setPrinterSdFileEnabled,
setPrinterEnabled,
startJob,
syncCameraStorage,
updatePrinter,
uploadPrintFile,
getActiveCameraJob,
Expand Down Expand Up @@ -291,6 +292,32 @@ async function loadAdminCameraTimeline(jobId) {
}
}

async function handleAdminCameraSyncStorage() {
if (!state.adminCameraPrinterId) {
setAdminCameraActionResult({ error: "Select a printer before syncing camera storage." });
return;
}

try {
const result = await syncCameraStorage(state.adminCameraPrinterId, {
dryRun: false,
syncSnapshots: true,
syncDeltas: true,
deleteRowsForMissingFiles: true,
reactivateDeletedSnapshotRows: true,
createMissingCameraJobs: true,
createMissingDeltaSets: true
});
setAdminCameraActionResult({
operation: "camera-storage-sync",
...result
});
await refreshCameraSnapshotJobs();
} catch (error) {
setAdminCameraActionResult({ error: `Failed to sync camera storage: ${error.message}` });
}
}

async function loadAdminCameraAnalysisData(jobId, preferredDeltaSetId = null, preferredCalculationRunId = null) {
if (!state.adminCameraPrinterId || !jobId) {
setAdminCameraAnalysisData([], [], [], [], null, null);
Expand Down Expand Up @@ -1268,6 +1295,13 @@ function bindGlobalListeners() {
return;
}

const adminCameraSyncStorageButton = event.target.closest("[data-admin-camera-sync-storage]");
if (adminCameraSyncStorageButton) {
await handleAdminCameraSyncStorage();
renderApp();
return;
}

const adminCameraSelectEntryButton = event.target.closest("[data-admin-camera-select-entry]");
if (adminCameraSelectEntryButton) {
setAdminCameraSelectedEntry(adminCameraSelectEntryButton.dataset.adminCameraSelectEntry);
Expand Down
20 changes: 20 additions & 0 deletions src/main/resources/dashboard/views/admin-camera-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function renderAdminCameraDataPage(
${renderPrinterOptions(printers, selectedPrinterId)}
</select>
</label>
${renderStorageSyncPanel(selectedPrinterId, actionResult)}
</section>

<section class="two-column-grid">
Expand Down Expand Up @@ -161,6 +162,25 @@ function renderPrinterOptions(printers, selectedPrinterId) {
`).join("");
}

function renderStorageSyncPanel(selectedPrinterId, actionResult) {
return `
<div class="action-row">
<button
type="button"
class="button-primary"
data-admin-camera-sync-storage
${selectedPrinterId ? "" : "disabled"}
>Sync storage</button>
<span class="muted">Scan the selected printer camera storage and import missing snapshot or delta rows.</span>
</div>
${isStorageSyncResult(actionResult) ? renderActionResult(actionResult) : ""}
`;
}

function isStorageSyncResult(result) {
return result && (result.operation === "camera-storage-sync" || result.storageRoot);
}

function renderJobTable(jobs) {
if (!Array.isArray(jobs) || jobs.length === 0) {
return `<p class="muted">No camera snapshot jobs have been recorded yet.</p>`;
Expand Down
26 changes: 26 additions & 0 deletions src/test/java/spaghettichef/local/api/RemoteApiServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3520,10 +3520,36 @@ private String startCameraJobAndWaitForSnapshots(
.resolve(cameraJobId);

waitForCameraSnapshotCount(snapshotsDirectory, expectedSnapshotCount);
waitForLatestCameraSnapshotAvailable(context, printerId);

return cameraJobId;
}

private void waitForLatestCameraSnapshotAvailable(TestContext context, String printerId) throws Exception {
long deadline = System.currentTimeMillis() + 8_000L;
int statusCode = 0;
String responseBody = "";

while (System.currentTimeMillis() < deadline) {
HttpResponse<String> response = context.get("/printers/" + printerId + "/camera/snapshot");
statusCode = response.statusCode();
responseBody = response.body();

if (statusCode == 200) {
return;
}

Thread.sleep(100L);
}

fail("Timed out waiting for latest camera snapshot API for "
+ printerId
+ ". Last status: "
+ statusCode
+ ", last response: "
+ responseBody);
}

private void waitForCameraSnapshotCount(Path snapshotsDirectory, long expectedSnapshotCount) throws Exception {
long deadline = System.currentTimeMillis() + 8_000L;
long count = 0L;
Expand Down