413 lines
15 KiB
QML
413 lines
15 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Controls
|
|
import QtQuick.Dialogs
|
|
import ISC
|
|
|
|
// ===== Status Tab =====
|
|
// Shows connection status, wafer info, and activity log.
|
|
// Initially visible — displays connection status and activity log from start.
|
|
ColumnLayout {
|
|
id: root
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.panelPadding
|
|
spacing: Theme.rightPaneGap
|
|
|
|
property alias waferFamilyCode: waferInfoFamily.text
|
|
property alias waferSerialNumber: waferSerial.text
|
|
property alias waferSensorCount: waferSensors.text
|
|
property alias waferRuntime: waferRuntime.text
|
|
property alias waferCycles: waferCycles.text
|
|
property bool waferDetected: false
|
|
|
|
// Data summary after parse
|
|
property int dataRows: 0
|
|
property int dataCols: 0
|
|
property string csvPath: ""
|
|
property bool dataParsed: false
|
|
|
|
function cleanFolderUrl(url) {
|
|
return decodeURIComponent(String(url).replace(/^file:\/\//, ""))
|
|
}
|
|
|
|
function currentFamilyCode() {
|
|
var info = deviceController.lastWaferInfo
|
|
return info && info.length > 0 ? (info[0] || "") : ""
|
|
}
|
|
|
|
function parseAndSavePendingRead() {
|
|
deviceController.parseAndSaveData(root.currentFamilyCode(), deviceController.selectedPort || "")
|
|
}
|
|
|
|
FolderDialog {
|
|
id: saveDirDialog
|
|
title: "Choose a folder to save."
|
|
onAccepted: {
|
|
deviceController.setSaveDataDir(root.cleanFolderUrl(selectedFolder))
|
|
root.parseAndSavePendingRead()
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
spacing: Theme.rightPaneGap
|
|
|
|
// --- Connection Status ---
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 92
|
|
color: Theme.cardBackground
|
|
border.color: {
|
|
if (deviceController.connectionStatus === "Connected") return Theme.statusSuccessColor
|
|
if (deviceController.connectionStatus === "Disconnected") return Theme.statusErrorColor
|
|
return Theme.cardBorder
|
|
}
|
|
radius: Theme.radiusSm
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.panelPadding
|
|
spacing: 4
|
|
|
|
Text {
|
|
text: deviceController.connectionStatus
|
|
color: {
|
|
if (deviceController.connectionStatus === "Connected") return Theme.statusSuccessColor
|
|
if (deviceController.connectionStatus === "Disconnected") return Theme.statusErrorColor
|
|
return Theme.bodyColor
|
|
}
|
|
font.bold: true
|
|
font.pixelSize: 16
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
text: "Port: " + (deviceController.selectedPort || "None selected")
|
|
color: Theme.bodyColor
|
|
font.pixelSize: 14
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
Layout.alignment: Qt.AlignVCenter
|
|
|
|
Text {
|
|
text: "Save dir: " + (deviceController.saveDataDir || "None selected")
|
|
color: Theme.bodyColor
|
|
font.pixelSize: 12
|
|
opacity: 0.7
|
|
elide: Text.ElideMiddle
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
}
|
|
|
|
Button {
|
|
id: browseBtn
|
|
text: "BROWSE..."
|
|
font.pixelSize: 9
|
|
font.weight: Font.Medium
|
|
implicitHeight: 20
|
|
implicitWidth: 65
|
|
hoverEnabled: true
|
|
background: Rectangle {
|
|
color: parent.hovered ? Theme.buttonNeutralHover : Theme.buttonNeutralBackground
|
|
border.color: Theme.cardBorder
|
|
border.width: 1
|
|
radius: Theme.radiusSm
|
|
}
|
|
contentItem: Text {
|
|
text: parent.text
|
|
color: Theme.bodyColor
|
|
font: parent.font
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
onClicked: saveDirDialog.open()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Wafer Info ---
|
|
GroupBox {
|
|
title: "Wafer Information"
|
|
visible: root.waferDetected
|
|
Layout.fillWidth: true
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.panelPadding
|
|
spacing: 8
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Theme.rightPaneGap
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Family Code"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferInfoFamily; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Serial Number"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferSerial; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Sensor Count"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferSensors; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Theme.rightPaneGap
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Runtime"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferRuntime; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Cycles"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferCycles; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Data Summary (shown after parse) ---
|
|
GroupBox {
|
|
title: "Data Summary"
|
|
visible: root.dataParsed
|
|
Layout.fillWidth: true
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.panelPadding
|
|
spacing: Theme.rightPaneGap
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Rows"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { text: String(root.dataRows); color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Sensors"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { text: String(root.dataCols); color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "CSV"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { text: root.csvPath; color: Theme.bodyColor; font.pixelSize: 12; elide: Text.ElideMiddle }
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Activity Log ---
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
color: Theme.cardBackground
|
|
border.color: Theme.cardBorder
|
|
radius: Theme.radiusMd
|
|
clip: true
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
|
|
// Header
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 40
|
|
color: Theme.subtleSectionBackground
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: Theme.panelPadding
|
|
anchors.rightMargin: Theme.panelPadding
|
|
|
|
Label {
|
|
text: "ACTIVITY LOG"
|
|
color: Theme.bodyColor
|
|
font.pixelSize: 10
|
|
font.letterSpacing: 1.8
|
|
font.weight: Font.Medium
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
}
|
|
|
|
Button {
|
|
text: "REFRESH SESSION"
|
|
font.pixelSize: 10
|
|
implicitHeight: 24
|
|
hoverEnabled: true
|
|
background: Rectangle {
|
|
color: parent.hovered ? Theme.buttonNeutralHover : "transparent"
|
|
radius: Theme.radiusSm
|
|
}
|
|
contentItem: Text {
|
|
text: parent.text
|
|
color: Theme.bodyColor
|
|
font: parent.font
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
onClicked: deviceController.clearSession()
|
|
}
|
|
|
|
Button {
|
|
text: "CLEAR LOG"
|
|
font.pixelSize: 10
|
|
implicitHeight: 24
|
|
hoverEnabled: true
|
|
background: Rectangle {
|
|
color: parent.hovered ? Theme.buttonNeutralHover : "transparent"
|
|
radius: Theme.radiusSm
|
|
}
|
|
contentItem: Text {
|
|
text: parent.text
|
|
color: Theme.bodyColor
|
|
font: parent.font
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
onClicked: deviceController.clearActivityLog()
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.bottom: parent.bottom
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
height: 1
|
|
color: Theme.cardBorder
|
|
}
|
|
}
|
|
|
|
ScrollView {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
Layout.margins: Theme.panelPadding
|
|
clip: true
|
|
|
|
TextArea {
|
|
id: activityLog
|
|
width: parent.width
|
|
text: qsTr("Welcome — use the side rail to detect a wafer, or import data to review.")
|
|
readOnly: true
|
|
font.family: "monospace"
|
|
font.pixelSize: 12
|
|
wrapMode: TextArea.WordWrap
|
|
color: Theme.bodyColor
|
|
background: null
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: deviceController
|
|
function onActivityLogUpdated(newLog) {
|
|
activityLog.text = newLog
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Signal Handlers ---
|
|
Connections {
|
|
target: deviceController
|
|
|
|
function onLogMessage(message) {
|
|
if (message == "CHOOSE_SAVE_DIR"){
|
|
saveDirDialog.open()
|
|
}
|
|
}
|
|
|
|
// Any operation start (detect/read/erase/debug) latches the panel on.
|
|
function onPortsUpdated() {
|
|
// Status tab always visible now
|
|
}
|
|
|
|
|
|
function onDetectResult(result) {
|
|
if (result && result.familyCode) {
|
|
root.waferDetected = true
|
|
waferInfoFamily.text = result.familyCode
|
|
waferSerial.text = result.serialNumber
|
|
waferSensors.text = String(result.sensorCount)
|
|
waferRuntime.text = result.runtime + "s"
|
|
waferCycles.text = String(result.cycleCount)
|
|
}
|
|
// On failure, keep the last displayed wafer info and do not change waferDetected.
|
|
}
|
|
|
|
function onStatusRestored() {
|
|
// Show restored wafer info if available
|
|
var info = deviceController.lastWaferInfo
|
|
if (info && info.length > 0) {
|
|
root.waferDetected = true
|
|
waferInfoFamily.text = info[0] || "—"
|
|
waferSerial.text = info[1] || "—"
|
|
waferSensors.text = String(info[2] || 0)
|
|
waferRuntime.text = (info[3] || 0) + "s"
|
|
waferCycles.text = String(info[4] || 0)
|
|
}
|
|
// Show data summary if data was previously parsed
|
|
if (deviceController.dataRowCount > 0) {
|
|
root.dataParsed = true
|
|
root.dataRows = deviceController.dataRowCount
|
|
root.dataCols = deviceController.dataColCount
|
|
root.csvPath = "" // CSV path not persisted as full path, just show it was parsed
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: deviceController
|
|
function onReadResult(result) {
|
|
root.dataParsed = false
|
|
root.dataRows = 0
|
|
root.dataCols = 0
|
|
root.csvPath = ""
|
|
|
|
if (result && result.success === true)
|
|
saveDirDialog.open()
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: deviceController
|
|
function onParsedDataReady(result) {
|
|
if (result && result.success) {
|
|
root.dataParsed = true
|
|
root.dataRows = result.rows
|
|
root.dataCols = result.cols
|
|
root.csvPath = result.csv_path || ""
|
|
} else {
|
|
root.dataParsed = false
|
|
}
|
|
}
|
|
}
|
|
}
|