Commit 846111ea authored by Gertulla, Janis Luca's avatar Gertulla, Janis Luca
Browse files

initial commit

parent e4f5b3a3
QT += core gui network charts
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
client.cpp \
lib.cpp \
main.cpp \
maintab.cpp \
window.cpp
HEADERS += \
client.h \
lib.h \
maintab.h \
window.h
FORMS += \
maintab.ui \
window.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
visuals.qrc
DISTFILES += \
svg/valve_closed.svg \
svg/valve_open.svg \
svg/valve_unknown.svg
This diff is collapsed.
#include "client.h"
#include "window.h"
Client::Client(std::shared_ptr<Config> config) : QTcpSocket(), config(config)
{
connect(this, &QAbstractSocket::readyRead, this, &Client::handleMessage);
pingInterval = std::make_unique<QTimer>(this);
pingInterval->setSingleShot(true);
pingInterval->setInterval(1000);
pingTimeout = std::make_unique<QTimer>(this);
pingTimeout->setSingleShot(true);
pingTimeout->setInterval(3000);
connect(pingInterval.get(), &QTimer::timeout, this, &Client::ping);
connect(this, &QAbstractSocket::connected, [=](){
pingInterval->start();
});
connect(this, &QAbstractSocket::disconnected, [=](){
// make sure these do not trigger after any disconnect
pingInterval->stop();
pingTimeout->stop();
emit log("Client disconnect.");
});
connect(pingTimeout.get(), &QTimer::timeout, [=](){ //
pingInterval->stop();
disconnectFromHost();
emit log("No pong received within 3 seconds, resetting connection.");
});
}
Client::~Client(){
}
void Client::ping() {
emit log("Sending ping");
uint8_t pingData[2] = {0b00000010, 'p'};
write((const char *) pingData, 2);
flush();
pingTimeout->start();
}
void Client::handleMessage() {
QByteArray msg = readAll();
while(msg.length() > 0){
auto msgHeader = msg[0] >> 4;
auto msgLen = msg[0] & 0xF;
auto repr = std::accumulate(msg.begin(), msg.end(), QString(""), [](QString a, int b){
return std::move(a) + QString("\\") + QString::number(b, 16) + QString(" ");
});
if(msgHeader == MsgPing) // pong
{
if(msgLen == 2 && msg[1] == 'p') {
emit log("Received valid pong!: " + repr + "(" + msg + ")");
pingInterval->start();
}
else {
emit log("Received invalid pong-like message!: " + repr + "(" + msg + ")");
}
}
else if(msgHeader == MsgState) // state change
{
if(msgLen == 4) {
auto state = std::make_shared<StateMessage>();
state->operable = msg[1] & (1 << 7);
state->override = msg[1] & (1 << 6);
state->error = msg[1] & (1 << 5);
state->mode = msg[1] & (1 << 4);
state->settingVTdet = msg[2] & (1 << 7);
state->settingVdet = msg[2] & (1 << 6);
state->settingVfoil = msg[2] & (1 << 5);
state->settingVdps = msg[2] & (1 << 4);
state->settingHVmcp = msg[2] & (1 << 3);
state->settingRbeamline = msg[2] & (1 << 2);
state->pumpEnabled = msg[2] & (1 << 1);
emit log("Received new state!: " + repr);
emit newState(state);
} else {
emit log("Received state-like garbage!");
}
}
else if(msgHeader == MsgMeasurement){ // new measurements
if(msgLen == 12) {
// emit log("Received measurements");
char *data = msg.data();
auto meas = std::make_shared<MeasurementMessage>();
meas->pGrat = (((int16_t) msg[1]) << 8) | (((int16_t) msg[2]) & 0xFF);
meas->pMCP = (((int16_t) msg[3]) << 8) | (((int16_t) msg[4]) & 0xFF);
meas->pVV = (((int16_t) msg[5]) << 8) | (((int16_t) msg[6]) & 0xFF);
meas->tDet = (((int16_t) msg[7]) << 8) | (((int16_t) msg[8]) & 0xFF);
meas->tDPS = (((int16_t) msg[9]) << 8) | (((int16_t) msg[10]) & 0xFF);
meas->vTdetHigh = data[11] & (1 << 7);
meas->vdetHigh = data[11] & (1 << 6);
meas->vfoilHigh = data[11] & (1 << 5);
meas->vdpsHigh = data[11] & (1 << 4);
meas->vTdetLow = data[11] & (1 << 3);
meas->vdetLow = data[11] & (1 << 2);
meas->vfoilLow = data[11] & (1 << 1);
meas->vdpsLow = data[11] & (1 << 0);
emit newMeasurements(meas);
} else {
emit log("Error: received measurement-like garbage!");
}
}
else {
emit log("Got garbage: " + repr);
}
msg.remove(0, msgLen);
}
}
void Client::sendState(std::shared_ptr<StateMessage> state){
uint8_t msg[4];
emit log("Writing state...");
msg[0] = 0b00010100;
msg[1] = 0;
msg[1] |= state->operable << 7;
msg[1] |= state->override << 6;
msg[1] |= state->error << 5;
msg[1] |= state->mode << 4;
msg[2] = 0;
msg[2] |= state->settingVTdet << 7;
msg[2] |= state->settingVdet << 6;
msg[2] |= state->settingVfoil << 5;
msg[2] |= state->settingVdps << 4;
msg[2] |= state->settingHVmcp << 3;
msg[2] |= state->settingRbeamline << 2;
msg[2] |= state->pumpEnabled << 1;
write((const char *) msg, 4);
flush();
}
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QTcpSocket>
#include <QTimer>
#include "lib.h"
// what ino/client intend
struct StateMessage { // repr: 1 + 1(header) bytes
bool operable;
bool override;
bool error;
bool mode;
bool settingVTdet;
bool settingVdet;
bool settingVfoil;
bool settingVdps;
bool settingHVmcp;
bool settingRbeamline;
bool pumpEnabled;
};
// what the ino actually measures
struct MeasurementMessage { // repr: 11 + 1(header) bytes
int16_t pGrat;
int16_t pMCP;
int16_t pVV;
int16_t tDet;
int16_t tDPS;
bool vTdetHigh;
bool vdetHigh;
bool vfoilHigh;
bool vdpsHigh;
bool vTdetLow;
bool vdetLow;
bool vfoilLow;
bool vdpsLow;
};
struct PingMessage {
uint8_t p; // redundancy/"checksum"
};
enum MsgType {
MsgPing = 0,
MsgState = 1 << 0,
MsgMeasurement = 1 << 1,
MsgError = 1 << 2, // needed?
};
enum Mode {
ModeWet = 0,
ModeDry = 1,
};
class Client : public QTcpSocket
{
Q_OBJECT
public:
Client(std::shared_ptr<Config> config);
~Client();
signals:
// void new_pong(PingMessage *p);
void newState(std::shared_ptr<StateMessage> state);
void newMeasurements(std::shared_ptr<MeasurementMessage> measurements);
void log(QString msg);
public slots:
void sendState(std::shared_ptr<StateMessage> state);
private:
std::unique_ptr<QTimer> pingInterval;
std::unique_ptr<QTimer> pingTimeout;
std::shared_ptr<Config> config;
void handleMessage();
void ping();
};
#endif // CONNECTION_H
This diff is collapsed.
#include "lib.h"
qreal pressureFromReading(int16_t measure, int type){
qreal baseVal = measure / 1023.0 * 10.0;
switch(type) {
case 0: // PBR
return qPow(10, (baseVal - 7.75) / 0.75);
case 1: // PKR
return qPow(10, 1.667 * baseVal - 11.33);
case 2: // IKR
return qPow(10, 1.25 * baseVal - 12.75);
case 3:
return qPow(10, 1.667 * baseVal - 11.33);
default:
return NAN; // something went terribly wrong in UI or Settings
}
}
void setValue(QLabel *w, qreal value){
w->setText(QString::number(value, 'g', 3));
}
#ifndef LIB_H
#define LIB_H
#include <QtCore>
#include <QLabel>
struct Config {
QString networkIP;
int networkPort;
bool displayModeDynamic;
int pressureUnitType;
int gaugeTypeGrat;
int gaugeTypeMcp;
int gaugeTypeVv;
bool modeWet;
};
qreal pressureFromReading(int16_t measure, int type);
void setValue(QLabel *l, qreal value);
#endif // LIB_H
#include "window.h"
#include <QApplication>
#include <QStyle>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
#include "maintab.h"
#include "ui_maintab.h"
#include "lib.h"
#include <cstdlib>
MainTab::MainTab(std::shared_ptr<Config> config, std::shared_ptr<Client> client, QWidget *parent) :
QWidget(parent), ui(new Ui::MainTab()), client(client), config(config)
{
ui->setupUi(this);
pHvActive = std::make_unique<QPixmap>(":/svg/hv_active.svg");
pHvInactive = std::make_unique<QPixmap>(":/svg/hv_inactive.svg");
pValveClosed = std::make_unique<QPixmap>(":/svg/valve_closed.svg");
pValveClosedV = std::make_unique<QPixmap>(":/svg/valve_closed_v.svg");
pValveOpen = std::make_unique<QPixmap>(":/svg/valve_open.svg");
pValveOpenV = std::make_unique<QPixmap>(":/svg/valve_open_v.svg");
pValveUnknown = std::make_unique<QPixmap>(":/svg/valve_unknown.svg");
pValveUnknownV = std::make_unique<QPixmap>(":/svg/valve_unknown_v.svg");
iHvActive = std::make_unique<QIcon>(*pHvActive);
iHvInactive = std::make_unique<QIcon>(*pHvInactive);
iValveClosed = std::make_unique<QIcon>(*pValveClosed);
iValveClosedV = std::make_unique<QIcon>(*pValveClosedV);
iValveOpen = std::make_unique<QIcon>(*pValveOpen);
iValveOpenV = std::make_unique<QIcon>(*pValveOpenV);
iValveUnknown = std::make_unique<QIcon>(*pValveUnknown);
iValveUnknownV = std::make_unique<QIcon>(*pValveUnknownV);
connect(ui->vdetButton, &QPushButton::clicked, this, &MainTab::onVDetButtonClicked);
connect(ui->vTdetButton, &QPushButton::clicked, this, &MainTab::onVTDetButtonClicked);
connect(ui->vfoilButton, &QPushButton::clicked, this, &MainTab::onVFoilButtonClicked);
connect(ui->vdpsButton, &QPushButton::clicked, this, &MainTab::onVDpsButtonClicked);
connect(ui->pumpStartButton, &QPushButton::clicked, this, &MainTab::onPumpStartButtonClicked);
connect(client.get(), &Client::newMeasurements, this, &MainTab::onNewMeasurements);
connect(client.get(), &Client::newState, this, &MainTab::onNewState);
connect(client.get(), &Client::connected, this, &MainTab::onConnect);
connect(client.get(), &Client::disconnected, this, &MainTab::onDisconnect);
// lastState = std::make_shared<StateMessage>();
// lastState->mode = ModeWet;
}
MainTab::~MainTab()
{}
void MainTab::switchValveIcon(QPushButton *valve, bool low, bool high, bool vertical){
if(low == 0 && high == 1) {
valve->setIcon(vertical?*iValveOpenV:*iValveOpen);
}
else if(low == 1 && high == 0) {
valve->setIcon(vertical?*iValveClosedV:*iValveClosed);
}
else {
valve->setIcon(vertical?*iValveUnknownV:*iValveUnknown);
}
}
void MainTab::onNewMeasurements(std::shared_ptr<MeasurementMessage> msg){
lastMeasurement = msg;
setValue(ui->pGratValueLabel, pressureFromReading(msg->pGrat, 1));
setValue(ui->pMcpValueLabel, pressureFromReading(msg->pMCP, 1));
setValue(ui->pVvValueLabel, pressureFromReading(msg->pVV, 1));
setValue(ui->tDetValueLabel, (msg->tDet) / 1023.0 * 100.0);
setValue(ui->tDPSValueLabel, (msg->tDPS) / 1023.0 * 100.0);
switchValveIcon(ui->vTdetButton, msg->vTdetLow, msg->vTdetHigh, true);
switchValveIcon(ui->vdetButton, msg->vdetLow, msg->vdetHigh, false);
switchValveIcon(ui->vfoilButton, msg->vfoilLow, msg->vfoilHigh, false);
switchValveIcon(ui->vdpsButton, msg->vdpsLow, msg->vdpsHigh, true);
}
void MainTab::onNewState(std::shared_ptr<StateMessage> msg){
lastState = msg;
ui->hvmcpState->setPixmap(lastState->settingHVmcp ? *pHvActive : *pHvInactive);
ui->rBeamlineStateLabel->setPixmap(lastState->settingRbeamline ? *pHvActive : *pHvInactive);
ui->pumpRunningLabel->setText(lastState->pumpEnabled ? "RUNNING" : "STOPPED");
ui->pumpStartButton->setText(lastState->pumpEnabled ? "STOP" : "START");
}
void MainTab::onDisconnect(){
setValue(ui->pGratValueLabel, 0);
setValue(ui->pMcpValueLabel, 0);
setValue(ui->pVvValueLabel, 0);
setValue(ui->tDetValueLabel, 0);
setValue(ui->tDPSValueLabel, 0);
switchValveIcon(ui->vTdetButton, false, false, true);
switchValveIcon(ui->vdetButton, false, false, false);
switchValveIcon(ui->vfoilButton, false, false, false);
switchValveIcon(ui->vdpsButton, false, false, true);
ui->hvmcpState->setPixmap(*pHvInactive);
ui->rBeamlineStateLabel->setPixmap(*pHvInactive);
}
void MainTab::onConnect(){
}
void MainTab::onVDetButtonClicked(){
if((lastState.get() != nullptr) &&
( lastState->operable || lastState->override )){
auto newState = std::make_shared<StateMessage>();
memcpy(newState.get(), lastState.get(), sizeof(StateMessage));
newState->settingVdet = !lastState->settingVdet;
emit log("Toggling vdet.");
client->sendState(newState);
}
else{
emit log("Cannot switch vdet valve as it has no clearance.");
}
}
void MainTab::onVDpsButtonClicked(){
if((lastState.get() != nullptr) &&
( lastState->operable || lastState->override )){
auto newState = std::make_shared<StateMessage>();
memcpy(newState.get(), lastState.get(), sizeof(StateMessage));
newState->settingVdps = !lastState->settingVdps;
emit log("Toggling vdps.");
client->sendState(newState);
}
else{
emit log("Cannot switch vdps valve as it has no clearance.");
}
}
void MainTab::onVFoilButtonClicked(){
if((lastState.get() != nullptr) &&
( lastState->operable || lastState->override )){
auto newState = std::make_shared<StateMessage>();
memcpy(newState.get(), lastState.get(), sizeof(StateMessage));
newState->settingVfoil = !lastState->settingVfoil;
emit log("Toggling vfoil.");
client->sendState(newState);
}
else{
emit log("Cannot switch vfoil valve as it has no clearance.");
}
}
void MainTab::onVTDetButtonClicked(){
if((lastState.get() != nullptr) &&
( lastState->operable || lastState->override )){
auto newState = std::make_shared<StateMessage>();
memcpy(newState.get(), lastState.get(), sizeof(StateMessage));
newState->settingVTdet = !lastState->settingVTdet;
emit log("Toggling vTdet.");
client->sendState(newState);
}
else{
emit log("Cannot switch vTdet valve as it has no clearance.");
}
}
void MainTab::onPumpStartButtonClicked(){
if(lastState.get() != nullptr && (lastState->operable || lastState->override)) {
auto newState = std::make_shared<StateMessage>();
memcpy(newState.get(), lastState.get(), sizeof(StateMessage));
newState->pumpEnabled = !lastState->pumpEnabled;
client->sendState(newState);
}
}
#ifndef MAINTAB_H
#define MAINTAB_H
#include <memory>
#include <QWidget>
#include <QPushButton>
#include "client.h"
namespace Ui {
class MainTab;
}
class MainTab : public QWidget
{
Q_OBJECT
public:
explicit MainTab(std::shared_ptr<Config> config, std::shared_ptr<Client> client, QWidget *parent = nullptr);
~MainTab();
std::shared_ptr<Client> client;
std::shared_ptr<StateMessage> lastState;
std::shared_ptr<MeasurementMessage> lastMeasurement;
private:
Ui::MainTab *ui;
// std::unique_ptr<Ui::MainTab> ui;
std::shared_ptr<Config> config;
std::unique_ptr<QPixmap> pHvActive;
std::unique_ptr<QIcon> iHvActive;
std::unique_ptr<QPixmap> pHvInactive;
std::unique_ptr<QIcon> iHvInactive;
std::unique_ptr<QPixmap> pValveClosed;
std::unique_ptr<QIcon> iValveClosed;
std::unique_ptr<QPixmap> pValveClosedV;
std::unique_ptr<QIcon> iValveClosedV;
std::unique_ptr<QPixmap> pValveOpen;
std::unique_ptr<QIcon> iValveOpen;
std::unique_ptr<QPixmap> pValveOpenV;
std::unique_ptr<QIcon> iValveOpenV;
std::unique_ptr<QPixmap> pValveUnknown;
std::unique_ptr<QIcon> iValveUnknown;
std::unique_ptr<QPixmap> pValveUnknownV;
std::unique_ptr<QIcon> iValveUnknownV;
std::unique_ptr<QTimer> vTdetBlink;
std::unique_ptr<QTimer> vdetBlink;
std::unique_ptr<QTimer> vfoilBlink;
std::unique_ptr<QTimer> vdpsBlink;
void switchValveIcon(QPushButton *valve, bool low, bool high, bool vertical);
signals:
void log(QString msg);
public slots:
void onNewMeasurements(std::shared_ptr<MeasurementMessage> msg);
void onNewState(std::shared_ptr<StateMessage> msg);
private slots:
void onVFoilButtonClicked();
void onVDpsButtonClicked();
void onVTDetButtonClicked();
void onVDetButtonClicked();
void onPumpStartButtonClicked();
void onConnect();