diff -r d053ffbbf3e9 -r c1bb6b197763 src/DetailFermenter.cpp --- a/src/DetailFermenter.cpp Thu Jun 30 21:05:30 2022 +0200 +++ b/src/DetailFermenter.cpp Fri Jul 01 19:52:11 2022 +0200 @@ -20,6 +20,12 @@ #include "MainWindow.h" +/* + * Results are available via MySQL and websockets. Because we initialize using + * MySQL we only use that for the results and up to date status. + * Commands are send via websockets only. + */ + DetailFermenter::DetailFermenter(int id, QWidget *parent) : QDialog(parent), ui(new Ui::DetailFermenter) { QSqlQuery query; @@ -57,12 +63,23 @@ ui->codePick->addItem(query.value("code").toString()+" - "+query.value("name").toString()); } + ui->profilePick->addItem(tr("Erase profile")); + query.exec("SELECT name FROM profile_fermentation ORDER BY name"); + while (query.next()) { + ui->profilePick->addItem(query.value("name").toString()); + } + connect(ui->loEdit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &DetailFermenter::lo_changed); connect(ui->hiEdit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &DetailFermenter::hi_changed); connect(ui->heatSwitch, SIGNAL(clicked()), this, SLOT(heat_switched())); connect(ui->coolSwitch, SIGNAL(clicked()), this, SLOT(cool_switched())); connect(ui->fanSwitch, SIGNAL(clicked()), this, SLOT(fan_switched())); + connect(ui->modeButton1, SIGNAL(clicked()), this, SLOT(button1_pressed())); + connect(ui->modeButton2, SIGNAL(clicked()), this, SLOT(button2_pressed())); connect(ui->modeEdit, QOverload::of(&QComboBox::currentIndexChanged), this, &DetailFermenter::mode_changed); + connect(ui->stageEdit, QOverload::of(&QComboBox::currentIndexChanged), this, &DetailFermenter::stage_changed); + connect(ui->codePick, QOverload::of(&QComboBox::currentIndexChanged), this, &DetailFermenter::code_changed); + connect(ui->profilePick, QOverload::of(&QComboBox::currentIndexChanged), this, &DetailFermenter::profile_changed); connect(parent, SIGNAL(updateFermenter(QString)), this, SLOT(refreshFermenter(QString))); emit refreshTable(); } @@ -72,7 +89,14 @@ { QSqlQuery query; - qDebug() << "refreshTable fermenter" << this->recno; + qDebug() << "refreshTable fermenter rec:" << this->recno; + + QIcon icon_done, icon_start, icon_abort, icon_pause, icon_cont; + icon_done.addFile(QString::fromUtf8(":icons/silk/accept.png"), QSize(), QIcon::Normal, QIcon::Off); + icon_start.addFile(QString::fromUtf8(":icons/silk/resultset_next.png"), QSize(), QIcon::Normal, QIcon::Off); + icon_abort.addFile(QString::fromUtf8(":icons/silk/bomb.png"), QSize(), QIcon::Normal, QIcon::Off); + icon_pause.addFile(QString::fromUtf8(":icons/silk/cup.png"), QSize(), QIcon::Normal, QIcon::Off); + icon_cont.addFile(QString::fromUtf8(":icons/silk/cup_go.png"), QSize(), QIcon::Normal, QIcon::Off); query.prepare("SELECT * FROM mon_fermenters WHERE record = :recno"); query.bindValue(":recno", this->recno); @@ -81,6 +105,9 @@ const QSignalBlocker blocker1(ui->codePick); const QSignalBlocker blocker2(ui->modeEdit); + const QSignalBlocker blocker3(ui->loEdit); + const QSignalBlocker blocker4(ui->hiEdit); + const QSignalBlocker blocker5(ui->stageEdit); _node = query.value("node").toString(); _alias = query.value("alias").toString(); @@ -156,8 +183,10 @@ ui->hiEdit->setReadOnly(true); ui->hiEdit->setButtonSymbols(QAbstractSpinBox::NoButtons); } - ui->loEdit->setValue(query.value("setpoint_low").toDouble()); - ui->hiEdit->setValue(query.value("setpoint_high").toDouble()); + lo_set = query.value("setpoint_low").toDouble(); + hi_set = query.value("setpoint_high").toDouble(); + ui->loEdit->setValue(lo_set); + ui->hiEdit->setValue(hi_set); ui->switchBox->show(); ui->heatLED->setChecked((query.value("heater_state").toInt() != 0) ? true:false); @@ -168,13 +197,73 @@ ui->heatSwitch->show(); ui->coolSwitch->show(); ui->fanSwitch->show(); + heat_state = (query.value("heater_state").toInt()) ? true:false; + cool_state = (query.value("cooler_state").toInt()) ? true:false; + fan_state = (query.value("fan_state").toInt()) ? true:false; + ui->heatSwitch->setChecked(heat_state); + ui->coolSwitch->setChecked(cool_state); + ui->fanSwitch->setChecked(fan_state); + // Copy state values to variables and set the switches. } else { ui->heatSwitch->hide(); ui->coolSwitch->hide(); ui->fanSwitch->hide(); + heat_state = cool_state = fan_state = false; } - //webSocket->sendTextMessage(QString("dd")); + if (query.value("profile_name").toString() == "") { + qobject_cast(ui->modeEdit->model())->item(4)->setEnabled(false); + } else { + qobject_cast(ui->modeEdit->model())->item(4)->setEnabled(true); + } + ui->profileEdit->setText(query.value("profile_name").toString()); + + if (query.value("mode").toString() == "PROFILE") { + _profile = query.value("profile_state").toString(); // So we know the profile state anywhere. + qDebug() << "profile state" << query.value("profile_state").toString(); + if (query.value("profile_state").toString() == "OFF") { + ui->profilePick->show(); + ui->profileShow->hide(); // Both on the same location. + ui->modeButton1->show(); + ui->modeButton1->setText(tr("Start")); + ui->modeButton1->setIcon(icon_start); + ui->modeButton2->hide(); + } else if (query.value("profile_state").toString() == "RUN") { + ui->profilePick->hide(); + ui->profileShow->show(); + ui->profileShow->setText(QString(tr("Profile active %1% done")).arg(query.value("profile_percent").toDouble())); + ui->modeButton1->show(); + ui->modeButton1->setText(tr("Abort")); + ui->modeButton1->setIcon(icon_abort); + ui->modeButton2->show(); + ui->modeButton2->setText(tr("Pause")); + ui->modeButton2->setIcon(icon_pause); + } else if (query.value("profile_state").toString() == "PAUSE") { + ui->profilePick->hide(); + ui->profileShow->show(); + ui->profileShow->setText(QString(tr("Profile paused %1% done")).arg(query.value("profile_percent").toDouble())); + ui->modeButton1->show(); + ui->modeButton1->setText(tr("Abort")); + ui->modeButton1->setIcon(icon_abort); + ui->modeButton2->show(); + ui->modeButton2->setText(tr("Continue")); + ui->modeButton2->setIcon(icon_cont); + } else if (query.value("profile_state").toString() == "DONE") { + ui->profilePick->hide(); + ui->profileShow->show(); + ui->profileShow->setText(QString(tr("Profile ready"))); + ui->modeButton1->show(); + ui->modeButton1->setText(tr("Profile Ok")); + ui->modeButton1->setIcon(icon_done); + ui->modeButton2->hide(); + } + } else { + ui->profilePick->show(); + ui->profileShow->hide(); // Both on the same location. + ui->modeButton1->hide(); + ui->modeButton2->hide(); + _profile = QString(""); + } ui->thermoBox->show(); if (query.value("air_state").toString() == "OK") { @@ -231,40 +320,175 @@ void DetailFermenter::lo_changed(double val) { - qDebug() << "lo_changed" << val; + double hi = ui->hiEdit->value(); + + if (val >= hi) + hi = val + 0.1; + QString msg = QString("{\"device\":\"fermenters\",\"node\":\""+_node+"\",\"unit\":\""+_alias+"\",\"setpoint_low\":%1,\"setpoint_high\":%2}") + .arg(val, 2, 'f', 1, '0').arg(hi, 2, 'f', 1, '0'); + qDebug() << "lo_changed" << val << msg; + webSocket->sendTextMessage(msg); } void DetailFermenter::hi_changed(double val) { - qDebug() << "hi_changed" << val; + double lo = ui->loEdit->value(); + + if (val <= lo) + lo = val - 0.1; + QString msg = QString("{\"device\":\"fermenters\",\"node\":\""+_node+"\",\"unit\":\""+_alias+"\",\"setpoint_low\":%1,\"setpoint_high\":%2}") + .arg(lo, 2, 'f', 1, '0').arg(val, 2, 'f', 1, '0'); + qDebug() << "hi_changed" << val << msg; + webSocket->sendTextMessage(msg); +} + + +void DetailFermenter::send_switches() +{ + QString msg=QString("{\"device\":\"fermenters\",\"node\":\""+_node+"\",\"unit\":\""+_alias+"\",\"heater_state\":%1,\"cooler_state\":%2,\"fan_state\":%3}") + .arg((heat_state)?100:0).arg((cool_state)?100:0).arg((fan_state)?100:0); + qDebug() << msg; + webSocket->sendTextMessage(msg); } void DetailFermenter::heat_switched() { - qDebug() << "heat_switched" << heat_state; + heat_state = !heat_state; + cool_state = false; + send_switches(); } void DetailFermenter::cool_switched() { - qDebug() << "cool_switched" << cool_state; + cool_state = !cool_state; + heat_state = false; + send_switches(); } void DetailFermenter::fan_switched() { - qDebug() << "fan_switched" << fan_state; + fan_state = !fan_state; + send_switches(); +} + + +void DetailFermenter::button1_pressed() +{ + qDebug() << "button1" << _profile; + if (_profile == "OFF") { + QString msg = QString("{\"device\":\"fermenters\",\"node\":\"" + _node + "\",\"unit\":\"" + _alias + "\",\"profile\":{\"command\":\"start\"}}"); + qDebug() << msg; + webSocket->sendTextMessage(msg); + return; + } + + if ((_profile == "RUN") || (_profile == "PAUSE")) { + // use popup + int rc = QMessageBox::warning(this, tr("Profile running"), tr("Profile is active, really abort?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (rc == QMessageBox::Yes) { + QString msg = QString("{\"device\":\"fermenters\",\"node\":\"" + _node + "\",\"unit\":\"" + _alias + "\",\"profile\":{\"command\":\"abort\"}}"); + qDebug() << msg; + webSocket->sendTextMessage(msg); + } + return; + } + + if (_profile == "DONE") { + QString msg = QString("{\"device\":\"fermenters\",\"node\":\"" + _node + "\",\"unit\":\"" + _alias + "\",\"profile\":{\"command\":\"done\"}}"); + qDebug() << msg; + webSocket->sendTextMessage(msg); + } +} + + +void DetailFermenter::button2_pressed() +{ + qDebug() << "button2" << _profile; + + if ((_profile == "RUN") || (_profile == "PAUSE")) { + QString msg = QString("{\"device\":\"fermenters\",\"node\":\"" + _node + "\",\"unit\":\"" + _alias + "\",\"profile\":{\"command\":\"pause\"}}"); + qDebug() << msg; + webSocket->sendTextMessage(msg); + } } void DetailFermenter::mode_changed(int val) { - qDebug() << "mode_changed" << val; - QStringList mode ({ "OFF", "NONE", "FRIDGE", "BEER", "PROFiLE" }); + QStringList mode ({ "OFF", "NONE", "FRIDGE", "BEER", "PROFILE" }); QString msg = QString("{\"device\":\"fermenters\",\"node\":\"" + _node + "\",\"unit\":\"" + _alias + "\",\"mode\":\"" + mode[val] + "\"}"); - qDebug() << msg; + qDebug() << "mode_changed" << val << msg; + webSocket->sendTextMessage(msg); +} + + +void DetailFermenter::stage_changed(int val) +{ + QStringList stage ({ "PRIMARY", "SECONDARY", "TERTIARY", "CARBONATION" }); + QString msg = QString("{\"device\":\"fermenters\",\"node\":\"" + _node + "\",\"unit\":\"" + _alias + "\",\"stage\":\"" + stage[val] + "\"}"); + qDebug() << "stage_changed" << val << msg; webSocket->sendTextMessage(msg); } + +void DetailFermenter::code_changed(int val) +{ + QString msg = QString(""); + qDebug() << "code_changed" << val << msg; +} + + +void DetailFermenter::profile_changed(int val) +{ + QString payload; + QSqlQuery query; + QJsonParseError parseError; + + if (val == 0) { + payload = QString("\"profile\":null"); + } else { + query.exec("SELECT * FROM profile_fermentation ORDER BY name"); + for (int i = 0; i < val; i++) { + query.next(); + } + payload = QString("\"profile\":{\"uuid\":\""+query.value("uuid").toString()+"\",\"name\":\""+query.value("name").toString()); + payload.append(QString("\",\"inittemp\":{\"low\":") + QString("%1").arg(query.value("inittemp_lo").toDouble(), 2, 'f', 1, '0' )); + payload.append(QString(",\"high\":") + QString("%1").arg(query.value("inittemp_hi").toDouble(), 2, 'f', 1, '0' ) + QString("},")); + payload.append(QString("\"fridgemode\":") + QString("%1").arg(query.value("fridgemode").toInt()) ); + payload.append(QString(",\"steps\":[")); + + const auto& s_json = query.value("steps").toString(); + if (! s_json.trimmed().isEmpty()) { + const auto& formattedJson = QString("%1").arg(s_json); + QJsonDocument steps = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + qWarning() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; + } else if (steps.isArray()) { + for (int i = 0; i < steps.array().size(); i++) { + QJsonObject obj = steps.array().at(i).toObject(); + if (i > 0) + payload.append(QString(",")); + payload.append(QString("{\"steptime\":%1").arg(obj["steptime"].toString().toDouble())); + payload.append(QString(",\"resttime\":%1").arg(obj["resttime"].toString().toDouble())); + payload.append(QString(",\"target_lo\":%1").arg(obj["target_lo"].toString().toDouble())); + payload.append(QString(",\"target_hi\":%1").arg(obj["target_hi"].toString().toDouble())); + payload.append(QString(",\"fridgemode\":%1").arg(obj["fridgemode"].toString().toInt())); + payload.append(QString(",\"name\":\"") + obj["name"].toString() + QString("\"}")); + } + } + } + + payload.append(QString("]}")); +// qDebug() << query.value("steps").toString(); + } + QString msg = QString("{\"device\":\"fermenters\",\"node\":\"" + _node + "\",\"unit\":\"" + _alias + "\",%1}").arg(payload); + qDebug() << "profile_changed" << val << msg; + webSocket->sendTextMessage(msg); +} + +