Mon, 30 Jan 2023 17:05:13 +0100
Added yeastpack editor. Expanded the database upgrade. On startup, recount the yeastpack used fields.
/** * 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::GetPressure(double CO2, double T) { if (CO2 < 0) return 0; double P = -1.09145427669121 + 0.00800006989646477 * T + 0.000260276315484684 * T * T + 0.0215142075945119 * T * CO2 + 0.674996600795854 * CO2 + -0.00471757220150754 * CO2 * CO2; P = round((P * 1.01325) * 100.0) / 100.0; // atm to bar qDebug() << " GetPressure(" << CO2 << "," << T << ") CO2:" << CO2 << "Bar:" << P; 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; } if ((product->stage >= PROD_STAGE_TERTIARY) && (product->fg >= 0.990)) { /* Make sure we have the final value. */ product->package_abv = Utils::abvol(product->brew_fermenter_sg, product->fg); ui->pack_abvLabel->setText(tr("Package ABV %:")); ui->pack_finalabvLabel->setText(tr("Final ABV %:")); ui->pack_finalibuLabel->setText(tr("Final IBU:")); } else { product->package_abv = product->est_abv; } 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_abvShow->setValue(product->package_abv); ui->pack_finalabvShow->setValue(product->final_abv); double pack_color = product->brew_fermenter_color * (product->package_volume / (product->package_volume + product->package_infuse_amount)); double pack_ibu = product->brew_fermenter_ibu * (product->package_volume / (product->package_volume + product->package_infuse_amount)); ui->pack_finalcolorShow->setValue(pack_color); ui->pack_finalcolorShow->setStyleSheet(Utils::ebc_to_style(pack_color)); ui->pack_finalibuShow->setValue(pack_ibu); 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 = Utils::CarbCO2toS(product->bottle_carbonation, TSec, SFactor); //qDebug() << " priming CarbCO2toS(" << product->bottle_carbonation << TSec << SFactor << ") =" << product->bottle_priming_amount; ui->bottle_sug_amountShow->setValue(product->bottle_priming_amount); double total = round(product->bottle_priming_amount * product->bottle_amount * 100.0) / 100000.0; if (total != product->fermentables.at(i).amount) { qDebug() << " total" << 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 = Utils::GetPressureBar(product->bottle_priming_amount * (1 / SFactor), 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->est_bottle_co2Edit->setValue(product->bottle_carbonation); ui->est_bottle_co2Show->setValue(product->bottle_carbonation); 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); 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 = Utils::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, 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); } ui->est_kegs_co2Edit->setValue(product->keg_carbonation); ui->est_kegs_co2Show->setValue(product->keg_carbonation); } // 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. */ const QSignalBlocker blocker1(ui->keg_volumeEdit); if (product->keg_amount < 0) product->keg_amount = 0; /* Failsafe - bugfix */ if (val > (product->package_volume + product->package_infuse_amount)) { val = product->package_volume + product->package_infuse_amount; const QSignalBlocker blocker2(ui->bottle_volumeEdit); ui->bottle_volumeEdit->setValue(val); } if ((val + product->keg_amount) > (product->package_volume + product->package_infuse_amount)) { double kegs = product->package_volume + product->package_infuse_amount - val; 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("name").toString(); newf.origin = query.value("origin").toString(); newf.supplier = query.value("supplier").toString(); newf.cost = query.value("cost").toDouble(); newf.type = query.value("type").toInt(); newf.yield = query.value("yield").toDouble(); newf.color = query.value("color").toDouble(); newf.coarse_fine_diff = query.value("coarse_fine_diff").toDouble(); newf.moisture = query.value("moisture").toDouble(); newf.diastatic_power = query.value("diastatic_power").toDouble(); newf.protein = query.value("protein").toDouble(); newf.dissolved_protein = query.value("dissolved_protein").toDouble(); newf.max_in_batch = query.value("max_in_batch").toDouble(); newf.graintype = query.value("graintype").toInt(); newf.recommend_mash = query.value("recommend_mash").toInt() ? true:false; newf.add_after_boil = true; newf.di_ph = query.value("di_ph").toDouble(); newf.acid_to_ph_57 = query.value("acid_to_ph_57").toDouble(); newf.inventory = query.value("inventory").toDouble(); newf.adjust_to_total_100 = false; newf.percentage = 0; 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. */ const QSignalBlocker blocker1(ui->bottle_volumeEdit); if (product->bottle_amount < 0) product->bottle_amount = 0; /* Failsafe - bugfix */ if (val > (product->package_volume + product->package_infuse_amount)) { val = product->package_volume + product->package_infuse_amount; const QSignalBlocker blocker2(ui->keg_volumeEdit); ui->keg_volumeEdit->setValue(val); } if ((val + product->bottle_amount) > (product->package_volume + product->package_infuse_amount)) { double bottle = product->package_volume + product->package_infuse_amount - val; 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("name").toString(); newf.origin = query.value("origin").toString(); newf.supplier = query.value("supplier").toString(); newf.cost = query.value("cost").toDouble(); newf.type = query.value("type").toInt(); newf.yield = query.value("yield").toDouble(); newf.color = query.value("color").toDouble(); newf.coarse_fine_diff = query.value("coarse_fine_diff").toDouble(); newf.moisture = query.value("moisture").toDouble(); newf.diastatic_power = query.value("diastatic_power").toDouble(); newf.protein = query.value("protein").toDouble(); newf.dissolved_protein = query.value("dissolved_protein").toDouble(); newf.max_in_batch = query.value("max_in_batch").toDouble(); newf.graintype = query.value("graintype").toInt(); newf.recommend_mash = query.value("recommend_mash").toInt() ? true:false; newf.add_after_boil = true; newf.di_ph = query.value("di_ph").toDouble(); newf.acid_to_ph_57 = query.value("acid_to_ph_57").toDouble(); newf.inventory = query.value("inventory").toDouble(); newf.adjust_to_total_100 = false; newf.percentage = 0; 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() { ChartCarbonate dialog(product->code, product->name, this); }