--- a/src/EditRecipe.cpp Sat Apr 02 23:01:13 2022 +0200 +++ b/src/EditRecipe.cpp Sun Apr 03 17:43:45 2022 +0200 @@ -97,7 +97,7 @@ ui->est_abvShow->setMarkerTextIsValue(true); ui->est_abvShow->setValue(query.value(30).toDouble()); - QColor color = Utils::ebc_to_color(query.value(31).toInt()); + //QColor color = Utils::ebc_to_color(query.value(31).toInt()); ui->est_colorEdit->setValue(query.value(31).toDouble()); ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(query.value(31).toInt())); ui->est_color2Edit->setValue(query.value(31).toDouble()); @@ -173,46 +173,11 @@ // 82 wa_acid_perc // 83 wa_base_name - /* - * Progress bars. - * perc_maltShow pmalts = mashkg / (dataRecord.boil_size / 3) * 100; - * perc_sugarsShow if (row.f_type == 1 && row.f_added < 4) // Sugar - * psugar += row.f_percentage; - * perc_caraShow if (row.f_graintype == 2 && row.f_added < 4) // Crystal - * pcara += row.f_percentage; - * lintner if (row.f_added == 0 && (row.f_type == 0 || row.f_type == 4) && row.f_color < 50) - * lintner += row.f_diastatic_power * row.f_amount; - * lintner = parseFloat(lintner / mashkg) - * - * perc_malts range(0, 120) - * stop: 90, color: '#008C00' - * stop: 100, color: '#EB7331' - * stop: 120, color: '#FF0000' - * - * perc_sugars range(0, 50) - * stop: 20, color: '#008C00' - * stop: 50, color: '#FF0000' - * perc_cara range(0, 50) - * stop: 25, color: '#008C00' - * stop: 50, color: '#FF0000' - * lintner range(0, 200) - * stop: 30, color: '#FF0000' red - * stop: 40, color: '#EB7331' orange - * stop: 200, color: '#008C00' green - */ - -// ui->lintnerShow->setValue(52); - if (ui->lintnerShow->value() < 30) - ui->lintnerShow->setStyleSheet(bar_red); - else if (ui->lintnerShow->value() < 40) - ui->lintnerShow->setStyleSheet(bar_orange); - else - ui->lintnerShow->setStyleSheet(bar_green); QJsonParseError parseError; - const auto& json = query.value(84).toString(); - if (!json.trimmed().isEmpty()) { - const auto& formattedJson = QString("%1").arg(json); + const auto& f_json = query.value(84).toString(); + if (!f_json.trimmed().isEmpty()) { + const auto& formattedJson = QString("%1").arg(f_json); this->fermentables = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError) qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; @@ -220,10 +185,45 @@ qDebug() << "empty fermentables"; } - // 85 json_hops - // 86 json_miscs - // 87 json_yeasts - // 88 json_mashs + const auto& h_json = query.value(85).toString(); + if (!h_json.trimmed().isEmpty()) { + const auto& formattedJson = QString("%1").arg(h_json); + this->hops = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); + if (parseError.error != QJsonParseError::NoError) + qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; + } else { + qDebug() << "empty hops"; + } + + const auto& m_json = query.value(86).toString(); + if (!m_json.trimmed().isEmpty()) { + const auto& formattedJson = QString("%1").arg(m_json); + this->miscs = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); + if (parseError.error != QJsonParseError::NoError) + qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; + } else { + qDebug() << "empty miscs"; + } + + const auto& y_json = query.value(87).toString(); + if (!y_json.trimmed().isEmpty()) { + const auto& formattedJson = QString("%1").arg(y_json); + this->yeasts = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); + if (parseError.error != QJsonParseError::NoError) + qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; + } else { + qDebug() << "empty yeasts"; + } + + const auto& ma_json = query.value(88).toString(); + if (!ma_json.trimmed().isEmpty()) { + const auto& formattedJson = QString("%1").arg(ma_json); + this->mashs = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); + if (parseError.error != QJsonParseError::NoError) + qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; + } else { + qDebug() << "empty mashs"; + } } else { /* Set some defaults */ @@ -245,11 +245,15 @@ connect(ui->efficiencyEdit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed); connect(ui->beerstyleEdit, &QComboBox::currentTextChanged, this, &EditRecipe::style_changed); connect(ui->est_ogEdit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed); - connect(ui->color_methodEdit, &QComboBox::currentTextChanged, this, &EditRecipe::is_changed); + connect(ui->color_methodEdit, &QComboBox::currentTextChanged, this, &EditRecipe::colormethod_changed); connect(ui->ibu_methodEdit, &QComboBox::currentTextChanged, this, &EditRecipe::is_changed); // All signals from tab "Fermentables" connect(ui->est_og2Edit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed); + connect(ui->perc_mashShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_mash_valueChanged); + connect(ui->perc_sugarsShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_sugars_valueChanged); + connect(ui->perc_caraShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_cara_valueChanged); + connect(ui->lintnerShow, &QProgressBar::valueChanged, this, &EditRecipe::on_lintner_valueChanged); // connect(ui->fermentablesTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int))); // All signals from tab "Hops" @@ -445,7 +449,7 @@ { int i; bool my_100 = false; - double psugar = 0, pcara = 0, d, s = 0, x; + double psugar = 0, pcara = 0, d, s = 0, x, color; double vol = 0; // Volume sugars after boil double addedS = 0; // Added sugars after boil double addedmass = 0; // Added mass after boil @@ -455,11 +459,36 @@ double sugarsf = 0; // fermentable sugars mash + boil double sugarsm = 0; // fermentable sugars in mash double sugardensity = 1.611; // kg/l in solution + double mashtime = 0; // Total mash time + double mashtemp = 0; // Average mash temperature + double mashinfuse = 0; // Mash infuse amount + double colort = 0; // Colors srm * vol totals + double colorh = 0; // Colors ebc * vol * kt + double colorn = 0; // Colors ebc * pt * pct QJsonObject obj; qDebug() << "calcFermentables()"; - // Get mashtemp and mashtime from the Mash schedule. + /* + * Get average mashtemp and mashtime from the Mash schedule. + * It is possible that the schedule is not (yet) present. + */ + if (this->mashs.array().size() > 0) { + for (i = 0; i < this->mashs.array().size(); i++) { + obj = this->mashs.array().at(i).toObject(); + if (obj["step_type"].toInt() == 0) // Infusion + mashinfuse += obj["step_infuse_amount"].toDouble(); + if (obj["step_temp"].toDouble() < 75) { // Ignore mashout + mashtime += obj["step_time"].toDouble(); + mashtemp += obj["step_time"].toDouble() * obj["step_temp"].toDouble(); + } + } + mashtemp = mashtemp / mashtime; + mvol = mashinfuse; + qDebug() << " mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse; + } else { + qDebug() << " no mash schedule"; + } if (this->fermentables.array().size() < 1) { qDebug() << " no fermentables, return."; @@ -497,12 +526,139 @@ lintner += obj["f_diastatic_power"].toDouble() * obj["f_amount"].toDouble(); } if (obj["f_added"].toInt() < 4) { - // colors + colort += obj["f_amount"].toDouble() * Utils::ebc_to_srm(obj["f_color"].toDouble()); + colorh += obj["f_amount"].toDouble() * obj["f_color"].toDouble() * Utils::get_kt(obj["f_color"].toDouble()); + colorn += (obj["f_percentage"].toDouble() / 100) * obj["f_color"].toDouble(); // For 8.6 Pt wort. } } + qDebug() << " colort" << colort << "colorh" << colorh << "colorn" << colorn; + qDebug() << " psugar" << psugar << "pcara" << pcara << "mvol" << mvol; + qDebug() << " sugarsf" << sugarsf << "sugarsm" << sugarsm; + double og = Utils::estimate_sg(sugarsf + addedS, ui->batch_sizeEdit->value()); + qDebug() << " OG" << ui->est_ogEdit->value() << og; + ui->est_ogEdit->setValue(og); + ui->est_og2Edit->setValue(og); + ui->est_og3Edit->setValue(og); + ui->est_ogShow->setValue(og); + + double preboil_sg = Utils::estimate_sg(sugarsm, ui->boil_sizeEdit->value()); + qDebug() << " preboil SG" << preboil_sg; + + /* + * Color of the wort + */ + if (ui->color_methodEdit->currentIndex() == 4) { // Naudts + color = round(((Utils::sg_to_plato(og) / 8.6) * colorn) + (ui->boil_timeEdit->value() / 60)); + } else if (ui->color_methodEdit->currentIndex() == 3) { // Hans Halberstadt + double bv = 0.925; // Beer loss efficiency + double sr = 0.95; // Mash and sparge efficiency + color = round((4.46 * bv * sr) / ui->batch_sizeEdit->value() * colorh); + } else { + double cw = colort / ui->batch_sizeEdit->value() * 8.34436; + color = Utils::kw_to_ebc(ui->color_methodEdit->currentIndex(), cw); + } + qDebug() << " color" << ui->est_colorEdit->value() << color; + ui->est_colorEdit->setValue(color); + ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(color)); + ui->est_color2Edit->setValue(color); + ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(color)); + ui->est_colorShow->setValue(color); + + /* + * We don't have a equipment profile in recipes, + * so we assume a certain guessed mashtun size. + */ + ui->perc_mashShow->setValue(round(mashkg / (ui->boil_sizeEdit->value() / 3) * 100)); + ui->perc_sugarsShow->setValue(round(psugar)); + ui->perc_caraShow->setValue(round(pcara)); qDebug() << " lintner" << lintner << " mashkg" << mashkg << "final" << round(lintner / mashkg); ui->lintnerShow->setValue(round(lintner / mashkg)); + + /* + * Calculate the apparant attenuation. + */ + double svg = 0; + if (this->yeasts.array().size() > 0) { + for (i = 0; i < this->yeasts.array().size(); i++) { + obj = this->yeasts.array().at(i).toObject(); + if (obj["y_use"].toInt() == 0) { // Used in primary + if (obj["y_attenuation"].toDouble() > svg) + svg = obj["y_attenuation"].toDouble(); // Take the highest if multiple yeasts. + } + // TODO: brett or others in secondary. + } + qDebug() << " SVG" << svg; + } + if (svg == 0) + svg = 77.0; + + double fg; + if (mashkg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0) + fg = Utils::estimate_fg(psugar, pcara, mashinfuse / mashkg, mashtime, mashtemp, svg, og); + else + fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og); + qDebug() << " FG" << ui->est_fgEdit->value() << fg; + ui->est_fgEdit->setValue(fg); + ui->est_fg3Edit->setValue(fg); + ui->est_fgShow->setValue(fg); + + double abv = Utils::abvol(og, fg); + qDebug() << " ABV" << ui->est_abvEdit->value() << abv; + ui->est_abvEdit->setValue(abv); + ui->est_abv2Edit->setValue(abv); + ui->est_abvShow->setValue(abv); + + /* + * Calculate kilocalories/liter. Formula from brouwhulp. + * Take the alcohol and sugar parts and then combine. + */ + double alc = 1881.22 * fg * (og - fg) / (1.775 - og); + double sug = 3550 * fg * (0.1808 * og + 0.8192 * fg - 1.0004); + qDebug() << " kcal" << round((alc + sug) / (12 * 0.0295735296)); + + // If to_100 then make all amount fields t/o and percent fields r/w + +} + + +void EditRecipe::on_perc_mash_valueChanged(int value) +{ + if (value < 90) + ui->perc_mashShow->setStyleSheet(bar_green); + else if (value < 100) + ui->perc_mashShow->setStyleSheet(bar_orange); + else + ui->perc_mashShow->setStyleSheet(bar_red); +} + + +void EditRecipe::on_perc_sugars_valueChanged(int value) +{ + if (value < 20) + ui->perc_sugarsShow->setStyleSheet(bar_green); + else + ui->perc_sugarsShow->setStyleSheet(bar_red); +} + + +void EditRecipe::on_perc_cara_valueChanged(int value) +{ + if (value < 25) + ui->perc_caraShow->setStyleSheet(bar_green); + else + ui->perc_caraShow->setStyleSheet(bar_red); +} + + +void EditRecipe::on_lintner_valueChanged(int value) +{ + if (value < 30) + ui->lintnerShow->setStyleSheet(bar_red); + else if (value < 40) + ui->lintnerShow->setStyleSheet(bar_orange); + else + ui->lintnerShow->setStyleSheet(bar_green); } @@ -637,6 +793,13 @@ } +void EditRecipe::colormethod_changed() +{ + calcFermentables(); + is_changed(); +} + + void EditRecipe::time_changed() { is_changed();