Browse Source

Fix AMS history chart showing wrong time range (#535)

The chart X axis used dataMin/dataMax domain, compressing the view to
only the data present. A 6h view with 1.5h of data looked like 1.5h.
Pin the X axis to the full selected time range, pad data edges so the
line extends to both ends, and connect through nulls for a continuous
line.
maziggy 2 months ago
parent
commit
94d0f55e1c

+ 1 - 0
CHANGELOG.md

@@ -5,6 +5,7 @@ All notable changes to Bambuddy will be documented in this file.
 ## [0.2.1] - Unreleased
 ## [0.2.1] - Unreleased
 
 
 ### Fixed
 ### Fixed
+- **AMS History Chart Shows Wrong Time Range** ([#535](https://github.com/maziggy/bambuddy/issues/535)) — The AMS temperature/humidity chart X axis was fitted to only the data points present (`dataMin`/`dataMax`), not the selected time window. When the printer was offline for part of the period, shorter views (e.g., 6h) appeared compressed to only the portion with data (e.g., 1.5h). Now pins the X axis domain to the full requested time range (e.g., now−6h to now), pads the data edges so the line extends across the full window, and connects through null values so the chart always shows a continuous line.
 - **"Clear Plate & Start Next" Ignores Filament Override Color** ([#486](https://github.com/maziggy/bambuddy/issues/486)) — When a print was queued to "any printer" with a filament color override (e.g., white PETG), the "Clear Plate & Start Next" button appeared on all printers of the matching model that had the correct filament *type*, regardless of *color*. A printer with blue PETG would show the button for a white PETG job. The backend scheduler already correctly rejected color mismatches, but the frontend `PrinterQueueWidget` only checked `required_filament_types` (type only) and ignored `filament_overrides` (type + color). Now passes loaded filament type+color pairs from AMS/vt_tray status to the widget and filters queue items against override colors, mirroring the backend's `_count_override_color_matches()` logic.
 - **"Clear Plate & Start Next" Ignores Filament Override Color** ([#486](https://github.com/maziggy/bambuddy/issues/486)) — When a print was queued to "any printer" with a filament color override (e.g., white PETG), the "Clear Plate & Start Next" button appeared on all printers of the matching model that had the correct filament *type*, regardless of *color*. A printer with blue PETG would show the button for a white PETG job. The backend scheduler already correctly rejected color mismatches, but the frontend `PrinterQueueWidget` only checked `required_filament_types` (type only) and ignored `filament_overrides` (type + color). Now passes loaded filament type+color pairs from AMS/vt_tray status to the widget and filters queue items against override colors, mirroring the backend's `_count_override_color_matches()` logic.
 - **Queue Empty After Container Restart Due to Uncheckpointed WAL** ([#523](https://github.com/maziggy/bambuddy/issues/523)) — The print queue appeared empty after a Docker container restart until a filter was applied. SQLite WAL mode keeps uncommitted data in a separate `-wal` file, but the shutdown handler never checkpointed the WAL back into the main database or disposed of engine connections. If the container was stopped or crashed, the WAL could contain partial schema migrations or uncommitted data, causing inconsistent query results on restart. Deleting the `-wal` and `-shm` files was the only workaround. Now runs `PRAGMA wal_checkpoint(TRUNCATE)` and disposes the engine on shutdown, ensuring all data is flushed to the main database file before exit.
 - **Queue Empty After Container Restart Due to Uncheckpointed WAL** ([#523](https://github.com/maziggy/bambuddy/issues/523)) — The print queue appeared empty after a Docker container restart until a filter was applied. SQLite WAL mode keeps uncommitted data in a separate `-wal` file, but the shutdown handler never checkpointed the WAL back into the main database or disposed of engine connections. If the container was stopped or crashed, the WAL could contain partial schema migrations or uncommitted data, causing inconsistent query results on restart. Deleting the `-wal` and `-shm` files was the only workaround. Now runs `PRAGMA wal_checkpoint(TRUNCATE)` and disposes the engine on shutdown, ensuring all data is flushed to the main database file before exit.
 - **Virtual Printer Queue Sends Wrong Plate ID and Ignores AMS Mapping** ([#529](https://github.com/maziggy/bambuddy/issues/529)) — Files sent to a virtual printer in queue mode had two issues. First, `plate_id` was always `1`, generating the wrong MQTT gcode path for multi-plate 3MF files (HMS error 0500_4003). Now extracts the plate index from the 3MF's `slice_info.config`. Second, `ams_mapping` was never computed for printer-specific queue items (VP assigned to a particular printer), so the printer always used the first AMS slot regardless of which filament the 3MF required. The scheduler now computes AMS mapping for all queue items that lack one, not just model-based assignments.
 - **Virtual Printer Queue Sends Wrong Plate ID and Ignores AMS Mapping** ([#529](https://github.com/maziggy/bambuddy/issues/529)) — Files sent to a virtual printer in queue mode had two issues. First, `plate_id` was always `1`, generating the wrong MQTT gcode path for multi-plate 3MF files (HMS error 0500_4003). Now extracts the plate index from the 3MF's `slice_info.config`. Second, `ams_mapping` was never computed for printer-specific queue items (VP assigned to a particular printer), so the printer always used the first AMS slot regardless of which filament the 3MF required. The scheduler now computes AMS mapping for all queue items that lack one, not just model-based assignments.

+ 18 - 8
frontend/src/components/AMSHistoryModal.tsx

@@ -87,21 +87,30 @@ export function AMSHistoryModal({
   if (!isOpen) return null;
   if (!isOpen) return null;
 
 
   // Format data for chart
   // Format data for chart
-  const chartData = data?.data.map(point => {
+  const rawPoints = data?.data.map(point => {
     const date = parseUTCDate(point.recorded_at) || new Date();
     const date = parseUTCDate(point.recorded_at) || new Date();
-    const timeOptions: Intl.DateTimeFormatOptions = {
-      hour: '2-digit',
-      minute: '2-digit',
-      ...(hours > 24 ? { day: 'numeric', month: 'short' } : {}),
-    };
     return {
     return {
       time: date.getTime(),
       time: date.getTime(),
       humidity: point.humidity,
       humidity: point.humidity,
       temperature: point.temperature,
       temperature: point.temperature,
-      timeLabel: date.toLocaleTimeString([], applyTimeFormat(timeOptions, timeFormat)),
     };
     };
   }) || [];
   }) || [];
 
 
+  // Pad edges so the line extends across the full time window
+  const domainStart = Date.now() - hours * 60 * 60 * 1000;
+  const domainEnd = Date.now();
+  const chartData = [...rawPoints];
+  if (chartData.length > 0) {
+    const first = chartData[0];
+    if (first.time > domainStart) {
+      chartData.unshift({ ...first, time: domainStart });
+    }
+    const last = chartData[chartData.length - 1];
+    if (last.time < domainEnd) {
+      chartData.push({ ...last, time: domainEnd });
+    }
+  }
+
   // Get thresholds
   // Get thresholds
   const humidityGood = thresholds?.humidityGood || 40;
   const humidityGood = thresholds?.humidityGood || 40;
   const humidityFair = thresholds?.humidityFair || 60;
   const humidityFair = thresholds?.humidityFair || 60;
@@ -313,7 +322,7 @@ export function AMSHistoryModal({
                   <XAxis
                   <XAxis
                     dataKey="time"
                     dataKey="time"
                     type="number"
                     type="number"
-                    domain={['dataMin', 'dataMax']}
+                    domain={[Date.now() - hours * 60 * 60 * 1000, Date.now()]}
                     tickFormatter={(ts) => {
                     tickFormatter={(ts) => {
                       const date = new Date(ts);
                       const date = new Date(ts);
                       if (hours > 24) {
                       if (hours > 24) {
@@ -372,6 +381,7 @@ export function AMSHistoryModal({
                     strokeWidth={2}
                     strokeWidth={2}
                     dot={false}
                     dot={false}
                     activeDot={{ r: 4 }}
                     activeDot={{ r: 4 }}
+                    connectNulls={true}
                   />
                   />
                 </LineChart>
                 </LineChart>
               </ResponsiveContainer>
               </ResponsiveContainer>

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-CxFtC4Kb.js


+ 1 - 1
static/index.html

@@ -23,7 +23,7 @@
 
 
     <!-- Splash screens for iOS -->
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-B19w8vas.js"></script>
+    <script type="module" crossorigin src="/assets/index-CxFtC4Kb.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-DJjXosw8.css">
     <link rel="stylesheet" crossorigin href="/assets/index-DJjXosw8.css">
   </head>
   </head>
   <body>
   <body>

Some files were not shown because too many files changed in this diff