fix: gate DTW comparisons on wafer type + recording category; surface load errors
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.impl
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import ISC
|
||||
@@ -30,11 +31,23 @@ Item {
|
||||
// into the trend chart's data property. The slot parseJsonToData() is defined
|
||||
// on the Python TrendChartItem; we use it so malformed payloads are logged
|
||||
// there instead of crashing the QML binding.
|
||||
property string loadErrorMessage: ""
|
||||
Connections {
|
||||
target: streamController
|
||||
function onTrendData(avgsJson) {
|
||||
trendChart.setDataFromJson(avgsJson);
|
||||
}
|
||||
// A file was picked from the rail (any tab) — loadFile() switches the
|
||||
// backend to review mode; mirror that in the toolbar toggle so a
|
||||
// successful load is never hidden behind a stale "Live" selection.
|
||||
function onLoadedFileChanged() {
|
||||
root.loadErrorMessage = "";
|
||||
if (streamController.mode === "review")
|
||||
modeBar.currentIndex = 0;
|
||||
}
|
||||
function onLoadFileError(message) {
|
||||
root.loadErrorMessage = message;
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -118,12 +131,13 @@ Item {
|
||||
RowLayout {
|
||||
visible: streamController.mode === "live"
|
||||
spacing: 5
|
||||
// WiFi icon (Unicode approximation; swap for SVG if available)
|
||||
Label {
|
||||
// Live status dot (matches the connection-flow indicator dot elsewhere)
|
||||
Rectangle {
|
||||
id: liveIndicator
|
||||
text: "◉"
|
||||
width: 8; height: 8
|
||||
radius: 4
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: (deviceController.connectionStatus === "Connected" && streamController.state !== "idle") ? Theme.liveColor : Theme.bodyColor
|
||||
font.pixelSize: Theme.fontSm
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
running: deviceController.connectionStatus === "Connected" && streamController.state !== "idle"
|
||||
@@ -250,6 +264,41 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Load error banner ───────────────────────────────────────────
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: errorRow.implicitHeight + 16
|
||||
visible: root.loadErrorMessage !== ""
|
||||
color: Theme.errorSurface
|
||||
border.color: Theme.statusErrorColor
|
||||
border.width: 1
|
||||
radius: Theme.radiusSm
|
||||
|
||||
RowLayout {
|
||||
id: errorRow
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
spacing: 8
|
||||
|
||||
IconImage {
|
||||
source: "icons/triangle-alert.svg"
|
||||
width: 16; height: 16
|
||||
sourceSize.width: 16
|
||||
sourceSize.height: 16
|
||||
color: Theme.errorTextSoft
|
||||
Layout.alignment: Qt.AlignTop
|
||||
}
|
||||
|
||||
Label {
|
||||
text: root.loadErrorMessage
|
||||
color: Theme.errorTextSoft
|
||||
font.pixelSize: Theme.fontSm
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Body: 2 columns (wafer + trend + transport | readout) ──────────
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -122,10 +122,12 @@ ColumnLayout {
|
||||
}
|
||||
contentItem: RowLayout {
|
||||
spacing: 6
|
||||
Label {
|
||||
text: "▤"
|
||||
IconImage {
|
||||
source: "../icons/folder.svg"
|
||||
width: 14; height: 14
|
||||
sourceSize.width: 14
|
||||
sourceSize.height: 14
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: Theme.fontSm
|
||||
}
|
||||
Label {
|
||||
text: file_browser.currentDirectory.split("/").pop() || file_browser.currentDirectory
|
||||
@@ -204,7 +206,7 @@ ColumnLayout {
|
||||
onClicked: streamController.loadFile(modelData.fileName)
|
||||
|
||||
background: Rectangle {
|
||||
color: fileItem.isActive ? Qt.rgba(1, 1, 1, 0.06) : fileItem.hovered ? Qt.rgba(1, 1, 1, 0.04) : "transparent"
|
||||
color: fileItem.isActive ? Theme.listActiveBackground : fileItem.hovered ? Theme.listHoverBackground : "transparent"
|
||||
radius: Theme.radiusSm
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.durationFast }
|
||||
@@ -225,11 +227,11 @@ ColumnLayout {
|
||||
color: {
|
||||
var t = modelData.waferType;
|
||||
if (t === "A" || t === "E" || t === "P")
|
||||
return "#3B82F6";
|
||||
return Theme.familyBlueAccent;
|
||||
if (t === "B" || t === "C" || t === "D")
|
||||
return "#10B981";
|
||||
return Theme.familyGreenAccent;
|
||||
if (t === "Z")
|
||||
return "#8B5CF6";
|
||||
return Theme.familyVioletAccent;
|
||||
return Theme.headingColor;
|
||||
}
|
||||
}
|
||||
@@ -249,19 +251,36 @@ ColumnLayout {
|
||||
color: {
|
||||
var t = modelData.waferType;
|
||||
if (t === "A" || t === "E" || t === "P")
|
||||
return "#1D4ED8";
|
||||
return Theme.familyBlueFill;
|
||||
if (t === "B" || t === "C" || t === "D")
|
||||
return "#065F46";
|
||||
return Theme.familyGreenFill;
|
||||
if (t === "Z")
|
||||
return "#7C3AED";
|
||||
return "#374151";
|
||||
return Theme.familyVioletFill;
|
||||
return Theme.familyNeutralFill;
|
||||
}
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
text: modelData.waferType || "?"
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.weight: Font.Bold
|
||||
color: "#FFFFFF"
|
||||
color: Theme.textOnColor
|
||||
}
|
||||
|
||||
// Recording indicator dot — distinguishes a live-recorded CSV
|
||||
// (SessionController.startRecording) from a read-memory dump
|
||||
// (DeviceController.parseAndSaveData).
|
||||
Rectangle {
|
||||
visible: modelData.isRecording === true
|
||||
width: 10
|
||||
height: 10
|
||||
radius: 5
|
||||
color: Theme.recordColor
|
||||
border.color: Theme.subtleSectionBackground
|
||||
border.width: 1.5
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: -2
|
||||
anchors.rightMargin: -2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,14 +289,38 @@ ColumnLayout {
|
||||
spacing: 1
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Primary: wafer type + serial number
|
||||
Label {
|
||||
text: modelData.serialNumber ? (modelData.waferType + modelData.serialNumber) : modelData.baseName
|
||||
color: Theme.headingColor
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.weight: Font.Bold
|
||||
elide: Text.ElideRight
|
||||
// Primary: wafer type + serial number, plus a REC badge for recordings
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 6
|
||||
|
||||
Label {
|
||||
text: modelData.serialNumber ? (modelData.waferType + modelData.serialNumber) : modelData.baseName
|
||||
color: Theme.headingColor
|
||||
font.pixelSize: Theme.fontMd
|
||||
font.weight: Font.Bold
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: modelData.isRecording === true
|
||||
radius: 4
|
||||
color: Qt.alpha(Theme.recordColor, 0.18)
|
||||
implicitWidth: recLabel.implicitWidth + 10
|
||||
implicitHeight: recLabel.implicitHeight + 4
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Label {
|
||||
id: recLabel
|
||||
anchors.centerIn: parent
|
||||
text: "REC"
|
||||
color: Theme.recordColor
|
||||
font.pixelSize: Theme.font2xs
|
||||
font.bold: true
|
||||
font.letterSpacing: 0.6
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary: date · time
|
||||
|
||||
@@ -119,6 +119,11 @@ class FileBrowser(QObject):
|
||||
return
|
||||
|
||||
for csv_path in sorted(self._current_directory.glob("*.csv")):
|
||||
# Recorded-live CSVs are named "live_<serial>_<timestamp>.csv" by
|
||||
# SessionController.startRecording (see WaferMapTab's Record button),
|
||||
# distinct from the "<serial>-<timestamp>.csv" convention used by
|
||||
# DeviceController.parseAndSaveData for read-memory dumps.
|
||||
is_recording = csv_path.stem.lower().startswith("live_")
|
||||
try:
|
||||
metadata = self._load_metadata(csv_path)
|
||||
wafer_type = metadata.get_wafer_type().upper() or (csv_path.stem[0].upper() if csv_path.stem else "")
|
||||
@@ -146,6 +151,7 @@ class FileBrowser(QObject):
|
||||
"masterType": master_state.get(str(csv_path), "") or metadata.master_type,
|
||||
"fileName": str(csv_path),
|
||||
"highlight": wafer_type in {"A", "B", "C"},
|
||||
"isRecording": is_recording,
|
||||
}
|
||||
)
|
||||
except Exception:
|
||||
@@ -169,6 +175,7 @@ class FileBrowser(QObject):
|
||||
"masterType": master_state.get(str(csv_path), ""),
|
||||
"fileName": str(csv_path),
|
||||
"highlight": False,
|
||||
"isRecording": is_recording,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user