Initial calibration page

- Fetch spectrometer status upon connection
- Display appropriate page depending on the status

TODO:
- Write calibration instructions
- Timer for periodic status fetch
This commit is contained in:
2024-02-15 22:48:31 -03:00
parent 56c16b8908
commit c3776f5908
11 changed files with 202 additions and 91 deletions

View File

@@ -9,8 +9,15 @@
*
*/
#include <QJsonDocument>
#include "Backend.hpp"
enum MsgType {
MSG_STATUS,
MSG_UNKNOWN,
};
void Backend::registerTypes(const char *uri) {
qmlRegisterType<Backend>(uri, 0, 1, "Backend");
}
@@ -25,6 +32,8 @@ Backend::Backend(QObject *parent) : QObject(parent) {
&Backend::brokerStateChanged);
connect(this->m_client, &QMqttClient::stateChanged, this,
&Backend::onBrokerStateChanged);
connect(this->m_client, &QMqttClient::messageReceived, this,
&Backend::onMqttMessage);
}
void Backend::connectToBroker() { this->m_client->connectToHost(); }
@@ -56,8 +65,63 @@ void Backend::onBrokerStateChanged(const QMqttClient::ClientState &state) {
return;
this->m_client->subscribe(QMqttTopicFilter("/spec"), 1);
this->m_client->publish(QMqttTopicName("/status"));
}
const Spec::SpecStatus Backend::specStatus() const {
return this->m_specStatus;
}
const QString Backend::specStatusReason() const {
return this->m_specStatusReason;
}
void Backend::onMqttMessage(const QByteArray &message,
const QMqttTopicName &topic) {
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(message, &err);
if (err.error != err.NoError) {
qWarning() << "Failed to parse json message, ignoring."
<< err.errorString();
return;
} else if (doc.isObject() == false) {
qWarning() << "Wrong json format received, ignoring.";
return;
}
QJsonObject o = doc.object();
if (o.contains("type") == false) {
qWarning() << "Wrong json message received, ignoring.";
return;
}
MsgType type = (MsgType)o["type"].toInt();
switch (type) {
case MSG_STATUS:
this->handleStatus(o);
break;
default:
qWarning() << "Unknown message type received, ignoring.";
return;
}
}
void Backend::handleStatus(QJsonObject o) {
if (o.contains("status") == false) {
qWarning() << "Invalid status message received, ignoring.";
return;
}
this->m_specStatus = (Spec::SpecStatus)o["status"].toInt();
if (o.contains("reason") == false)
this->m_specStatusReason = "";
else
this->m_specStatusReason = o["reason"].toString();
emit this->specStatusChanged();
}

View File

@@ -52,6 +52,8 @@ class Backend : public QObject {
// Spectrometer properties
Q_PROPERTY(
Spec::SpecStatus specStatus READ specStatus NOTIFY specStatusChanged)
Q_PROPERTY(
QString specStatusReason READ specStatusReason NOTIFY specStatusChanged)
QML_EXTENDED_NAMESPACE(Spec)
@@ -77,6 +79,7 @@ public:
const QMqttClient::ClientState brokerState() const;
const Spec::SpecStatus specStatus() const;
const QString specStatusReason() const;
signals:
void brokerHostChanged();
@@ -86,9 +89,13 @@ signals:
private slots:
void onBrokerStateChanged(const QMqttClient::ClientState &state);
void onMqttMessage(const QByteArray &message, const QMqttTopicName &topic);
private:
Q_DISABLE_COPY(Backend)
QMqttClient *m_client;
Spec::SpecStatus m_specStatus = Spec::SPEC_UNKNOWN;
QString m_specStatusReason = "";
void handleStatus(QJsonObject o);
};

5
src/Gui/AppPage.qml Normal file
View File

@@ -0,0 +1,5 @@
import QtQuick.Controls
Page {
}

View File

@@ -0,0 +1,40 @@
import QtQuick
import QtQuick.Layouts
import "../PStyle"
import ".."
ColumnLayout {
spacing: 64
ColumnLayout {
PText {
text: qsTr("Spectrometer needs calibration")
wrapMode: Text.WordWrap
font: PStyle.getFont(24)
}
PText {
text: qsTr("Reason: ") + App.specStatusReason
}
}
PText {
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: 886
Layout.preferredWidth: mainWindow.width * 0.7
font: PStyle.getFont(10)
text: qsTr(
"This is needed so the system knows where the diffraction grating " +
"is pointing at.\naaaa"
)
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignJustify
}
PTextButton {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Continue")
}
}

29
src/Gui/Loading.qml Normal file
View File

@@ -0,0 +1,29 @@
import QtQuick.Layouts
import "PStyle"
ColumnLayout {
property string title: ""
property string description: ""
spacing: 64
PText {
Layout.alignment: Qt.AlignHCenter
visible: title
text: title
font: PStyle.getFont(24)
}
PBusyIndicator {
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: 48
running: true
}
PText {
Layout.alignment: Qt.AlignHCenter
visible: description
text: description
}
}

View File

@@ -72,7 +72,7 @@ ColumnLayout {
PTextButton {
id: btn
text: qsTr("Connect")
text: qsTr("Next")
Layout.fillWidth: true
enabled: false

View File

@@ -1,20 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import "../PStyle"
ColumnLayout {
spacing: 24
PBusyIndicator {
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: 48
running: true
}
PText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Trying to connect...")
}
}

View File

@@ -6,34 +6,8 @@ import QtQml
import "../PStyle"
import ".."
PStackView {
id: login
initialItem: brokerInput
property var instanceInfo
Component {
id: brokerInput
PBackground {
BrokerInput {
anchors.centerIn: parent
BrokerInput {
onTryConnect: (url, user, pass) => {
App.tryConnect(url, user, pass)
login.push(loadInstance)
}
}
}
}
Component {
id: loadInstance
PBackground {
LoadingInfo {
anchors.centerIn: parent
}
}
}
}

View File

@@ -2,25 +2,52 @@ import QtQuick
import QtQuick.Window
import QtQuick.Layouts
import QtQuick.Controls
import QtQml
import "PStyle"
import "Login"
import "."
ApplicationWindow {
id: mainWindow
visible: true
width: 640
height: 480
title: Qt.application.displayName
minimumWidth: 840
minimumHeight: 640
property string loadingTitle: qsTr("Loading application...")
property string loadingDesc
Component.onCompleted: {
App.setup()
loadingTimer.running = true
}
App.brokerStateChanged.connect(() => {
if (App.brokerState == 2)
connectTimer.running = true
})
Connections {
target: App
function onBrokerStateChanged() {
switch (App.brokerState) {
case App.BROKER_CONNECTING:
showLoading("Connecting to broker...")
break;
case App.BROKER_CONNECTED:
showLoading("Getting spectrometer...")
break;
}
}
function onSpecStatusChanged() {
switch (App.specStatus) {
case App.SPEC_NEEDS_CALIBRATION:
loader.setSource(
"qrc:/Calibration/Calibration.qml",
)
break;
}
}
}
Timer {
@@ -41,49 +68,34 @@ ApplicationWindow {
running: false
repeat: false
interval: 750
interval: 500
onTriggered: loader.sourceComponent = mainPane
}
Loader {
id: loader
anchors.fill: parent
anchors.centerIn: parent
sourceComponent: loadingComponent
}
Component {
id: loadingComponent
PBackground {
ColumnLayout {
anchors.centerIn: parent
spacing: 24
PBusyIndicator {
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: 48
running: true
}
PText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Loading application...")
}
}
Loading {
id: loading
title: loadingTitle
description: loadingDesc
}
}
Component {
id: mainPane
function showLoading(title, desc = "") {
loadingTitle = title
loadingDesc = desc
loader.sourceComponent = loadingComponent
}
PBackground {
PText {
anchors.centerIn: parent
text: qsTr("YAAAAY I AM CONNECTED")
}
}
}
background: PBackground {}
footer: ToolBar {
visible: false
@@ -101,7 +113,7 @@ ApplicationWindow {
RowLayout {
PText {
font: PStyle.getFont(10)
text: qsTr("Connection status:")
text: qsTr("Connection:")
}
Rectangle {
@@ -128,7 +140,7 @@ ApplicationWindow {
PText {
font: PStyle.getFont(10)
text: qsTr("Spectrometer status:")
text: qsTr("Spectrometer:")
}
Rectangle {

View File

@@ -11,10 +11,7 @@ QtObject {
property double animationDuration: 100
property font font: Qt.font({
family: "HackGen Console",
pointSize: 12,
})
property font font: getFont()
function getFont(size = 12) {
return Qt.font({

View File

@@ -2,15 +2,18 @@
<qresource prefix="/">
<file alias="Splash.qml">Gui/Splash.qml</file>
<file alias="App.qml">Gui/App.qml</file>
<file alias="Loading.qml">Gui/Loading.qml</file>
<file alias="qmldir">Gui/qmldir</file>
<!-- login -->
<file alias="Login/Login.qml">Gui/Login/Login.qml</file>
<file alias="Login/BrokerInput.qml">Gui/Login/BrokerInput.qml</file>
<file alias="Login/LoadingInfo.qml">Gui/Login/LoadingInfo.qml</file>
<!-- settings -->
<file alias="Settings/Settings.qml">Gui/Settings/Settings.qml</file>
<file alias="Settings/qmldir">Gui/Settings/qmldir</file>
<!-- calibration -->
<file alias="Calibration/Calibration.qml">Gui/Calibration/Calibration.qml</file>
</qresource>
</RCC>