Thu, 30 Jun 2022 10:38:03 +0200
Translated to English.
/** * EditProduct.cpp is part of bmsapp. * * Tab 11, package * * bmsapp is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * bmsapp is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ double EditProduct::ResCO2(double T) { double F = T * 1.8 + 32; return round((3.0378 - 0.050062 * F + 0.00026555 * F * F) * 1000000.0) / 1000000.0; } double EditProduct::CarbCO2toS(double CO2, double T, double SFactor) { //var sugar = SFactor * (CO2 - ResCO2(CO2, T)) / 0.286; double sugar = round((SFactor * (CO2 - ResCO2(T)) * 4.014094) * 1000000.0) / 1000000.0; if (sugar < 0) sugar = 0; return sugar; //Round(sugar, 3); } double EditProduct::GetPressure(double CO2, double T1, double T2) { double V = CO2 - ResCO2(T1); V = CO2; // TODO: temp only total pressure, testing if (V < 0) return 0; double P = -1.09145427669121 + 0.00800006989646477 * T2 + 0.000260276315484684 * T2 * T2 + 0.0215142075945119 * T2 * V + 0.674996600795854 * V + -0.00471757220150754 * V * V; if (P < 0) P = 0; P = round((P * 1.01325) * 100.0) / 100.0; // atm to bar qDebug() << " GetPressure(" << CO2 << "," << T1 << "," << T2 << ") V:" << V << "Bar:" << P << "ignored ResCO2:" << ResCO2(T1); return P; } double EditProduct::CarbCO2ToPressure(double CO2, double T) { return (CO2 - (-0.000005594056 * pow(T, 4) + 0.000144357886 * pow(T, 3) + 0.000362999168 * T * T - 0.064872987645 * T + 1.641145175049)) / (0.00000498031 * pow(T, 4) - 0.00024358267 * pow(T, 3) + 0.00385867329 * T * T - 0.05671206825 * T + 1.53801423376); } void EditProduct::calcPack() { int i, j; bool found1, found2; QSqlQuery query; double TSec, SFactor; qDebug() << "calcPack()" << product->package_volume << product->package_abv << "inf" << product->package_infuse_amount << product->package_infuse_abv; if (product->package_volume < 1) { /* * If there is not enough to package, then don't * do any calculations and clear the rest of the form. */ product->package_volume = 0; ui->pack_finalabvShow->setValue(0); return; } double bvol = product->package_volume - (product->package_abv * product->package_volume) / 100.0; double balc = product->package_volume - bvol; double mvol = product->package_infuse_amount - (product->package_infuse_abv * product->package_infuse_amount) / 100.0; double malc = product->package_infuse_amount - mvol; double talc = balc + malc; double tvol = bvol + mvol; product->final_abv = round(talc / (tvol + talc) * 10000.0) / 100.0; ui->pack_finalabvShow->setValue(product->final_abv); TSec = product->secondary_temp; if (TSec < 1) TSec = product->primary_end_temp; if (TSec < 1) TSec = 18; /* * For bottles and kegs use the following complicated procedure to * find the needed priming sugars. * * 1. If we need a priming sugar then first see in the fermentables * table if there is one selected. If not, clear sugars and done. * 2. Then, we update the data from the fermentables inventory. * 3. Set the found sugar in the pulldown menu. * 4. Calculate the sugar in gr/L and total amount need. * 5. Calculate the ABV in the bottles/kegs. * 6. Calculate the pressure build by the refermentation. * * Note that this is all a leftover from the web based application that * did work like this. It's a bit strange but it will allways give * the right data and sugar concentrations. * Sometimes bugs have good things. */ found1 = false; if (product->bottle_amount) { for (i = 0; i < product->fermentables.size(); i++) { if (product->fermentables.at(i).added == FERMENTABLE_ADDED_BOTTLE) { found1 = true; break; } } if (found1) { SFactor = 1 / ((product->fermentables.at(i).yield / 100) * (1 - product->fermentables.at(i).moisture / 100)); qDebug() << " bottle sugar" << product->fermentables.at(i).name << SFactor << TSec; query.prepare("SELECT name,supplier FROM inventory_fermentables WHERE type = '1' OR type = '3' ORDER BY name"); // Sugars or dry extract query.exec(); j = 0; found2 = false; while (query.next()) { j++; if (query.value(0).toString() == product->fermentables.at(i).name && query.value(1).toString() == product->fermentables.at(i).supplier) { ui->bottle_sugarEdit->setCurrentIndex(j); product->bottle_priming_sugar = j; found2 = true; break; } } if (! found2) { ui->bottle_sugarEdit->setCurrentIndex(0); // Make sure not selected product->fermentables.removeAt(i); // Remove false fermentable refreshFermentables(); } else { product->bottle_priming_amount = CarbCO2toS(product->bottle_carbonation, TSec, SFactor); ui->bottle_sug_amountShow->setValue(product->bottle_priming_amount); double total = round(product->bottle_priming_amount * product->bottle_amount * 100.0) / 100000.0; qDebug() << " total" << total << product->fermentables.at(i).amount; if (total != product->fermentables.at(i).amount) { qDebug() << " update priming sugar" << total; product->fermentables[i].amount = total; refreshFermentables(); is_changed(); } ui->bottle_sug_weightShow->setValue(total * 1000); double pabv = product->final_abv + (product->bottle_priming_amount * (1 / SFactor) * 0.47) / 7.907; double pvol = product->bottle_amount - (pabv * product->bottle_amount) / 100; talc = product->bottle_amount - pvol; tvol = pvol + product->bottle_priming_water; product->bottle_abv = talc / (tvol + talc) * 100; product->bottle_bar = GetPressure(product->bottle_carbonation, TSec, product->bottle_carbonation_temp); ui->bottle_abvShow->setValue(product->bottle_abv); ui->bottle_barShow->setValue(product->bottle_bar); } } } if (! found1) { qDebug() << " no bottle priming"; ui->bottle_sug_amountShow->setValue(0); ui->bottle_sug_weightShow->setValue(0); ui->bottle_abvShow->setValue(0); ui->bottle_barShow->setValue(0); product->bottle_abv = 0; product->bottle_bar = 0; } ui->keg_sugarLabel->setEnabled(! product->keg_forced_carb); ui->keg_sugarEdit->setEnabled(! product->keg_forced_carb); ui->keg_sug_weightLabel->setEnabled(! product->keg_forced_carb); ui->keg_sug_weightShow->setEnabled(! product->keg_forced_carb); ui->keg_sug_waterLabel->setEnabled(! product->keg_forced_carb); ui->keg_sug_waterEdit->setEnabled(! product->keg_forced_carb); ui->keg_sug_amountLabel->setEnabled(! product->keg_forced_carb); ui->keg_sug_amountShow->setEnabled(! product->keg_forced_carb); // keg_amount keg_carbonation keg_priming_sugar keg_priming_amount keg_priming_water keg_carbonation_temp keg_forced_carb keg_pressure found1 = false; if (product->keg_amount) { double Pressure = CarbCO2ToPressure(product->keg_carbonation, product->keg_carbonation_temp); if (Pressure < 0) Pressure = 0; product->keg_pressure = Pressure; ui->keg_barShow->setValue(Pressure); if (! product->keg_forced_carb) { for (i = 0; i < product->fermentables.size(); i++) { if (product->fermentables.at(i).added == FERMENTABLE_ADDED_KEGS) { found1 = true; break; } } if (found1) { SFactor = 1 / ((product->fermentables.at(i).yield / 100) * (1 - product->fermentables.at(i).moisture / 100)); qDebug() << " kegs sugar" << product->fermentables.at(i).name << SFactor << TSec; query.prepare("SELECT name,supplier FROM inventory_fermentables WHERE type = '1' OR type = '3' ORDER BY name"); // Sugars or dry extract query.exec(); j = 0; found2 = false; while (query.next()) { j++; if (query.value(0).toString() == product->fermentables.at(i).name && query.value(1).toString() == product->fermentables.at(i).supplier) { ui->keg_sugarEdit->setCurrentIndex(j); product->keg_priming_sugar = j; found2 = true; break; } } if (! found2) { ui->keg_sugarEdit->setCurrentIndex(0); // Make sure not selected product->fermentables.removeAt(i); // Remove false fermentable refreshFermentables(); } else { product->keg_priming_amount = CarbCO2toS(product->keg_carbonation, TSec, SFactor); ui->keg_sug_amountShow->setValue(product->keg_priming_amount); double total = round(product->keg_priming_amount * product->keg_amount * 100.0) / 100000.0; qDebug() << " total" << total << product->fermentables.at(i).amount; if (total != product->fermentables.at(i).amount) { qDebug() << " update priming sugar" << total; product->fermentables[i].amount = total; refreshFermentables(); is_changed(); } ui->keg_sug_weightShow->setValue(total * 1000); double pabv = product->final_abv + (product->keg_priming_amount * (1 / SFactor) * 0.47) / 7.907; double pvol = product->keg_amount - (pabv * product->keg_amount) / 100; talc = product->keg_amount - pvol; tvol = pvol + product->keg_priming_water; product->keg_abv = talc / (tvol + talc) * 100; product->keg_bar = GetPressure(product->keg_carbonation, TSec, product->keg_carbonation_temp); ui->keg_abvShow->setValue(product->keg_abv); ui->keg_barShow->setValue(product->keg_bar); } } // if priming sugar. if (! found1) { qDebug() << " no keg priming"; ui->keg_sug_amountShow->setValue(0); ui->keg_sug_weightShow->setValue(0); ui->keg_abvShow->setValue(0); product->keg_abv = 0; product->keg_bar = 0; } } else { /* * Forced carbonation */ ui->keg_abvShow->setValue(product->final_abv); product->keg_abv = product->final_abv; ui->keg_sug_amountShow->setValue(0); ui->keg_sug_weightShow->setValue(0); } } // if keg_amount } /* * Triggered by writing to ui->pack_abvShow */ void EditProduct::pack_abv_changed(double val) { calcPack(); } void EditProduct::pack_date_changed(QDate val) { qDebug() << "pack_date_changed" << val; product->package_date = ui->pack_dateEdit->nullDate(); is_changed(); setStage(); } void EditProduct::pack_date_button() { ui->pack_dateEdit->setDate(QDate::currentDate()); } void EditProduct::pack_date_ack() { int rc = QMessageBox::warning(this, tr("Confirm package"), tr("Confirm that the beer is packaged and all data is correct"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (rc == QMessageBox::No) return; product->stage = PROD_STAGE_CARBONATION; setStage(); is_changed(); } void EditProduct::pack_volume_changed(double val) { /* * If the total volume is decreased, check if the * bottle and kegs amount need to be lowered as well. */ product->package_volume = val; calcPack(); is_changed(); } void EditProduct::pack_ph_changed(double val) { if (product->package_ph == 0) { product->package_ph = 4.0; const QSignalBlocker blocker1(ui->pack_phEdit); ui->pack_phEdit->setValue(4.0); } else { product->package_ph = val; } is_changed(); } void EditProduct::pack_infusion_vol_changed(double val) { product->package_infuse_amount = val; calcPack(); is_changed(); } void EditProduct::pack_infusion_abv_changed(double val) { product->package_infuse_abv = val; calcPack(); is_changed(); } void EditProduct::pack_infusion_txt_changed(QString val) { product->package_infuse_notes = val; is_changed(); } void EditProduct::bottle_volume_changed(double val) { /* * Check if kegs volume plus this new volume fits in the package volume. */ if ((val + product->keg_amount) > product->package_volume) { double kegs = product->package_volume - val; const QSignalBlocker blocker1(ui->keg_volumeEdit); product->keg_amount = kegs; ui->keg_volumeEdit->setValue(kegs); } product->bottle_amount = val; calcPack(); is_changed(); } void EditProduct::bottle_co2_changed(double val) { product->bottle_carbonation = val; calcPack(); is_changed(); } void EditProduct::bottle_sugar_changed(int val) { Fermentables newf; QSqlQuery query; qDebug() << "bottle_sugar_changed" << product->bottle_priming_sugar << val; for (int i = 0; i < product->fermentables.size(); i++) { if (product->fermentables.at(i).added == FERMENTABLE_ADDED_BOTTLE) { product->fermentables.removeAt(i); refreshFermentables(); break; } } /* * Search the sugar pointed by the index. */ QString sql = "SELECT name,origin,supplier,cost,type,yield,color,coarse_fine_diff,moisture,diastatic_power,protein,dissolved_protein,max_in_batch," "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57,inventory FROM inventory_fermentables " "WHERE type = '1' OR type = '3' ORDER BY name"; query.prepare(sql); query.exec(); query.first(); for (int i = 0; i < (val - 1); i++) { query.next(); } newf.name = query.value(0).toString(); newf.origin = query.value(1).toString(); newf.supplier = query.value(2).toString(); newf.cost = query.value(3).toDouble(); newf.type = query.value(4).toInt(); newf.yield = query.value(5).toDouble(); newf.color = query.value(6).toDouble(); newf.coarse_fine_diff = query.value(7).toDouble(); newf.moisture = query.value(8).toDouble(); newf.diastatic_power = query.value(9).toDouble(); newf.protein = query.value(10).toDouble(); newf.dissolved_protein = query.value(11).toDouble(); newf.max_in_batch = query.value(12).toDouble(); newf.graintype = query.value(13).toInt(); newf.recommend_mash = query.value(14).toInt() ? true:false; newf.add_after_boil = true; newf.di_ph = query.value(16).toDouble(); newf.acid_to_ph_57 = query.value(17).toDouble(); newf.inventory = query.value(18).toDouble(); newf.amount = 0; newf.added = FERMENTABLE_ADDED_BOTTLE; product->fermentables.append(newf); refreshFermentables(); const QSignalBlocker blocker1(ui->bottle_sugarEdit); calcPack(); is_changed(); } void EditProduct::bottle_water_changed(double val) { product->bottle_priming_water = val; calcPack(); is_changed(); } void EditProduct::bottle_temp_changed(double val) { product->bottle_carbonation_temp = val; calcPack(); is_changed(); } void EditProduct::kegs_volume_changed(double val) { /* * Check if bottle volume plus this new volume fits in the package volume. */ if ((val + product->bottle_amount) > product->package_volume) { double bottle = product->package_volume - val; const QSignalBlocker blocker1(ui->bottle_volumeEdit); product->bottle_amount = bottle; ui->bottle_volumeEdit->setValue(bottle); } product->keg_amount = val; calcPack(); is_changed(); } void EditProduct::kegs_co2_changed(double val) { product->keg_carbonation = val; calcPack(); is_changed(); } void EditProduct::kegs_sugar_changed(int val) { Fermentables newf; QSqlQuery query; qDebug() << "kegs_sugar_changed" << product->keg_priming_sugar << val; for (int i = 0; i < product->fermentables.size(); i++) { if (product->fermentables.at(i).added == FERMENTABLE_ADDED_KEGS) { product->fermentables.removeAt(i); refreshFermentables(); break; } } /* * Search the sugar pointed by the index. */ QString sql = "SELECT name,origin,supplier,cost,type,yield,color,coarse_fine_diff,moisture,diastatic_power,protein,dissolved_protein,max_in_batch," "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57,inventory FROM inventory_fermentables " "WHERE type = '1' OR type = '3' ORDER BY name"; query.prepare(sql); query.exec(); query.first(); for (int i = 0; i < (val - 1); i++) { query.next(); } newf.name = query.value(0).toString(); newf.origin = query.value(1).toString(); newf.supplier = query.value(2).toString(); newf.cost = query.value(3).toDouble(); newf.type = query.value(4).toInt(); newf.yield = query.value(5).toDouble(); newf.color = query.value(6).toDouble(); newf.coarse_fine_diff = query.value(7).toDouble(); newf.moisture = query.value(8).toDouble(); newf.diastatic_power = query.value(9).toDouble(); newf.protein = query.value(10).toDouble(); newf.dissolved_protein = query.value(11).toDouble(); newf.max_in_batch = query.value(12).toDouble(); newf.graintype = query.value(13).toInt(); newf.recommend_mash = query.value(14).toInt() ? true:false; newf.add_after_boil = true; newf.di_ph = query.value(16).toDouble(); newf.acid_to_ph_57 = query.value(17).toDouble(); newf.inventory = query.value(18).toDouble(); newf.amount = 0; newf.added = FERMENTABLE_ADDED_KEGS; product->fermentables.append(newf); refreshFermentables(); const QSignalBlocker blocker1(ui->keg_sugarEdit); calcPack(); is_changed(); } void EditProduct::kegs_water_changed(double val) { product->keg_priming_water = val; calcPack(); is_changed(); } void EditProduct::kegs_forced_changed(bool val) { qDebug() << "kegs_forced_changed" << val; product->keg_forced_carb = val; if (val) { /* * Make sure to remove priming sugars. */ for (int i = 0; i < product->fermentables.size(); i++) { if (product->fermentables.at(i).added == FERMENTABLE_ADDED_KEGS) { product->fermentables.removeAt(i); refreshFermentables(); break; } } product->keg_priming_sugar = 0; product->keg_priming_amount = 0; product->keg_priming_water = 0; const QSignalBlocker blocker1(ui->keg_sugarEdit); const QSignalBlocker blocker2(ui->keg_sug_waterEdit); ui->keg_sugarEdit->setCurrentIndex(0); ui->keg_sug_weightShow->setValue(0); ui->keg_sug_waterEdit->setValue(0); ui->keg_sug_amountShow->setValue(0); } calcPack(); is_changed(); } void EditProduct::kegs_temp_changed(double val) { product->keg_carbonation_temp = val; calcPack(); is_changed(); } void EditProduct::carb_log_button() { QSqlQuery query; double timestamp; QDialog* dialog = new QDialog(this); dialog->resize(1024, 600); dialog->setWindowTitle(tr("Carbonation log")); dialog->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); buttonBox->setObjectName(QString::fromUtf8("buttonBox")); buttonBox->setGeometry(QRect(40, 565, 944, 36)); buttonBox->setLayoutDirection(Qt::LeftToRight); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Ok); buttonBox->setCenterButtons(true); QSplineSeries *temperature = new QSplineSeries(); QSplineSeries *pressure = new QSplineSeries(); query.prepare("SELECT * FROM log_co2pressure WHERE code=:code ORDER BY datetime"); query.bindValue(":code", product->code); query.exec(); while (query.next()) { timestamp = query.value("datetime").toDateTime().toSecsSinceEpoch() * 1000; temperature->append(timestamp, query.value("temperature").toDouble()); pressure->append(timestamp, query.value("pressure").toDouble()); } temperature->setName(tr("Temperature °C")); temperature->setColor(QColorConstants::Svg::red); pressure->setName(tr("Pressure bar")); QPen pen(QColorConstants::Svg::navy); pen.setWidth(3); pressure->setPen(pen); QChart *chart = new QChart(); chart->setTitle(QString("%1 \"%2\"").arg(product->code).arg(product->name)); chart->addSeries(temperature); chart->addSeries(pressure); QDateTimeAxis *axisX = new QDateTimeAxis; axisX->setTickCount(10); axisX->setFormat("dd MMM"); axisX->setTitleText(tr("Date")); axisX->setLabelsFont(QFont("Helvetica", 8, QFont::Normal)); chart->addAxis(axisX, Qt::AlignBottom); temperature->attachAxis(axisX); pressure->attachAxis(axisX); QValueAxis *axisYT = new QValueAxis; axisYT->setTickCount(10); axisYT->setLabelFormat("%.1f"); axisYT->setTitleText(tr("Temp °C")); axisYT->setLabelsFont(QFont("Helvetica", 8, QFont::Normal)); chart->addAxis(axisYT, Qt::AlignRight); temperature->attachAxis(axisYT); QValueAxis *axisYP = new QValueAxis; axisYP->setTickCount(10); axisYP->setLabelFormat("%.1f"); axisYP->setTitleText(tr("Bar")); axisYP->setLabelsFont(QFont("Helvetica", 8, QFont::Normal)); chart->addAxis(axisYP, Qt::AlignLeft); pressure->attachAxis(axisYP); QChartView *chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); dialog->setLayout(new QVBoxLayout); dialog->layout()->addWidget(chartView); dialog->layout()->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); dialog->setModal(true); dialog->exec(); }