import QtQuick import QtQuick.Controls import QtQuick.Layouts import ISC import ISC.Wafer // ===== Graph Tab ===== // Displays sensor temperature line charts using GraphQuickItem. // Gets data from deviceController.getChartData() (parsed CSV, batch read) // or from streamController.trendData (live streaming trend). Item { id: root anchors.fill: parent property var _chartData: null // cached result from getChartData() ColumnLayout { anchors.fill: parent anchors.margins: Theme.panelPadding spacing: Theme.rightPaneGap // ── Toolbar ─────────────────────────────────────────────────────────── RowLayout { Layout.fillWidth: true spacing: 10 Label { text: "Line Chart" color: Theme.headingColor font.pixelSize: 13 font.bold: true Layout.alignment: Qt.AlignVCenter } Item { Layout.fillWidth: true } // Refresh button — pulls data from deviceController Button { id: refreshBtn text: "REFRESH" font.pixelSize: 10 font.weight: Font.Medium implicitHeight: 26 implicitWidth: 72 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.headingColor font: parent.font horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } onClicked: reloadData() } } // ── Mode selector (Trend / Full chart) ───────────────────────────── RowLayout { Layout.fillWidth: true spacing: 8 Label { text: "Source:" color: Theme.bodyColor font.pixelSize: 10 Layout.alignment: Qt.AlignVCenter } ComboBox { id: sourceSelector model: ["Parsed Data", "Live Trend"] currentIndex: 0 implicitHeight: 26 font.pixelSize: 11 background: Rectangle { color: Theme.fieldBackground border.color: Theme.fieldBorder border.width: 1 radius: Theme.radiusSm } contentItem: Text { text: sourceSelector.displayText color: Theme.fieldText font: sourceSelector.font horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter leftPadding: 8 } onCurrentIndexChanged: reloadData() } Item { Layout.fillWidth: true } Label { id: statusLabel text: "Ready" color: Theme.bodyColor font.pixelSize: 10 font.italic: true Layout.alignment: Qt.AlignVCenter } } // ── Chart Area ───────────────────────────────────────────────────────── Rectangle { Layout.fillWidth: true Layout.fillHeight: true color: Theme.cardBackground border.color: Theme.cardBorder border.width: 1 radius: Theme.radiusMd clip: true GraphQuickItem { id: graph anchors.fill: parent anchors.margins: 2 backgroundColor: Theme.cardBackground gridColor: Theme.softBorder axisColor: Theme.bodyColor textColor: Theme.headingColor seriesColors: [Theme.sensorLow, Theme.sensorHigh, Theme.sensorInRange, "#FFA726", "#AB47BC", "#00BCD4"] showLegend: true } } } // ── Data Loading ──────────────────────────────────────────────────────────── function reloadData() { if (sourceSelector.currentIndex === 0) { // Parsed data from DeviceController loadParsedData() } else { // Live trend from SessionController loadLiveTrend() } } function loadParsedData() { statusLabel.text = "Loading..." var result = deviceController.getChartData() if (!result || !result.success) { graph.seriesData = [] graph.sensorNames = [] graph.title = "Sensor Temperature Over Time" statusLabel.text = "No data — read a wafer first" return } graph.seriesData = result.series || [] graph.sensorNames = result.sensor_names || [] graph.title = "Sensor Temperature Over Time" statusLabel.text = (result.series ? result.series.length : 0) + " sensors loaded" } function loadLiveTrend() { // Live trend: connects to streamController.trendData // The trend data comes as a JSON array of per-frame averages statusLabel.text = "Connecting to live trend..." // We need at least one data point if (streamController.stats && streamController.stats.avg !== undefined) { // Build a single-series graph from the trend buffer // For now show a placeholder — the trendData signal handles updates graph.title = "Live Average Temperature" graph.yLabel = "Avg Temperature (°C)" graph.xLabel = "Time (s)" statusLabel.text = "Live trend — waiting for data..." } else { statusLabel.text = "No live data — start a stream on the Wafer Map tab" } } // ── Connections ───────────────────────────────────────────────────────────── Connections { target: deviceController function onParsedDataReady(result) { if (sourceSelector.currentIndex === 0) { root.reloadData() } } } Connections { target: streamController function onTrendData(avgsJson) { if (sourceSelector.currentIndex === 1) { try { var avgs = JSON.parse(avgsJson) if (avgs && avgs.length > 0) { graph.seriesData = [avgs] graph.sensorNames = ["Average"] graph.title = "Live Average Temperature" graph.yLabel = "Avg Temperature (°C)" graph.xLabel = "Frame" statusLabel.text = "Live: " + avgs.length + " data points" } } catch (e) { // ignore parse errors } } } } // On startup, try to load parsed data if available Component.onCompleted: { if (deviceController.dataRowCount > 0) { reloadData() } } }