--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/EditProductTab8.cpp Thu Apr 28 22:49:13 2022 +0200 @@ -0,0 +1,797 @@ +/** + * EditProduct.cpp is part of bmsapp. + * + * tab 8, water. + * + * 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/>. + */ + + +void EditProduct::refreshWaters() +{ + + // calc_acid + ui->mw_phEdit->setValue(product->mash_ph); + // mash_name + //ui->mw_acidpercEdit->setValue(product-> + +} + + +/* + * Z alkalinity is the amount of acid (in mEq/l) needed to bring water to the target pH (Z pH) + */ +double EditProduct::ZAlkalinity(double pHZ) +{ + double C43 = Utils::Charge(4.3); + double Cw = Utils::Charge(product->wg_ph); + double Cz = Utils::Charge(pHZ); + double DeltaCNaught = -C43 + Cw; + double CT = product->wg_total_alkalinity / 50 / DeltaCNaught; + double DeltaCZ = -Cz + Cw; + return CT * DeltaCZ; +} + + +/* + * Z Residual alkalinity is the amount of acid (in mEq/l) needed + * to bring the water in the mash to the target pH (Z pH). + */ +double EditProduct::ZRA(double pHZ) +{ + double Calc = product->wg_calcium / (MMCa / 2); + double Magn = product->wg_magnesium / (MMMg / 2); + double Z = ZAlkalinity(pHZ); + return Z - (Calc / 3.5 + Magn / 7); +} + + +double EditProduct::BufferCapacity(Fermentables F) +{ + double C1 = 0; + + if ((F.f_di_ph != 5.7) && ((F.f_acid_to_ph_57 < - 0.1) || (F.f_acid_to_ph_57 > 0.1))) { + C1 = F.f_acid_to_ph_57 / (F.f_di_ph - 5.7); + } else { + /* + * If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid. + */ + switch (F.f_graintype) { + case 0: // Base, Special, Kilned + case 3: + case 5: C1 = 0.014 * F.f_color - 34.192; + break; + case 2: C1 = -0.0597 * F.f_color - 32.457; // Crystal + break; + case 1: C1 = 0.0107 * F.f_color - 54.768; // Roast + break; + case 4: C1 = -149; // Sour malt + break; + } + } + return C1; +} + + +double EditProduct::AcidRequired(double ZpH, Fermentables F) +{ + double C1 = BufferCapacity(F); + double x = F.f_di_ph; + return C1 * (ZpH - x); +} + + +double EditProduct::ProtonDeficit(double pHZ) +{ + double C1, x; + int i, error_count = 0; + double Result = ZRA(pHZ) * product->wg_amount; + Fermentables F; + + /* + * proton deficit for the grist + */ + if (product->fermentables.size()) { + for (i = 0; i < product->fermentables.size(); i++) { + F = product->fermentables.at(i); + if (F.f_added == 0 && F.f_graintype != 6) { // Added == Mash && graintype != No Malt + x = AcidRequired(pHZ, F) * F.f_amount; + Result += x; + } + } + } else { + error_count++; + if (error_count < 5) + qDebug() << " ProtonDeficit" << pHZ << "invalid grist, return" << Result; + } + return Result; +} + + +double EditProduct::MashpH() +{ + int n = 0; + double pH = 5.4; + double deltapH = 0.001; + double deltapd = 0.1; + double pd = ProtonDeficit(pH); + + while (((pd < -deltapd) || (pd > deltapd)) && (n < 2000)) { + n++; + if (pd < -deltapd) + pH -= deltapH; + else if (pd > deltapd) + pH += deltapH; + pd = ProtonDeficit(pH); + } + pH = round(pH * 1000000) / 1000000.0; + qDebug() << " MashpH() n:" << n << "pH:" << pH; + return pH; +} + + +void EditProduct::calcWater() +{ + double liters = 0; + double calcium = 0; + double magnesium = 0; + double sodium = 0; + double total_alkalinity = 0; + double bicarbonate = 0; + double chloride = 0; + double sulfate = 0; + double ph = 0; + double TpH = 0; + double frac, RA; + double protonDeficit = 0; + double Acid = 0, Acidmg = 0; + int AT; + + qDebug() << "calcWater()"; + + /* + * If there is a dilute water source, mix the waters. + */ + if (product->w2_name != "") { + liters = product->w1_amount + product->w2_amount; + calcium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_calcium, product->w2_calcium); + magnesium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_magnesium, product->w2_magnesium); + sodium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_sodium, product->w2_sodium); + chloride = Utils::mix(product->w1_amount, product->w2_amount, product->w1_chloride, product->w2_chloride); + sulfate = Utils::mix(product->w1_amount, product->w2_amount, product->w1_sulfate, product->w2_sulfate); + total_alkalinity = Utils::mix(product->w1_amount, product->w2_amount, product->w1_total_alkalinity, product->w2_total_alkalinity); + ph = -log10(((pow(10, -product->w1_ph) * product->w1_amount) + (pow(10, -product->w2_ph) * product->w2_amount)) / liters); + } else { + liters = product->w1_amount; + calcium = product->w1_calcium; + magnesium = product->w1_magnesium; + sodium = product->w1_sodium; + chloride = product->w1_chloride; + sulfate = product->w1_sulfate; + total_alkalinity = product->w1_total_alkalinity; + ph = product->w1_ph; + } + + product->wg_amount = liters; + product->wg_calcium = round(calcium * 10.0) / 10.0; + product->wg_magnesium = round(magnesium * 10.0) / 10.0; + product->wg_sodium = round(sodium * 10.0) / 10.0; + product->wg_chloride = round(chloride * 10.0) / 10.0; + product->wg_sulfate = round(sulfate * 10.0) / 10.0; + product->wg_total_alkalinity = round(total_alkalinity * 10.0) / 10.0; + product->wg_ph = ph; + + ui->wg_volEdit->setValue(liters); + ui->wg_caEdit->setValue(calcium); + ui->wg_mgEdit->setValue(magnesium); + ui->wg_hco3Edit->setValue(total_alkalinity * 1.22); + ui->wg_caco3Edit->setValue(total_alkalinity); + ui->wg_naEdit->setValue(sodium); + ui->wg_clEdit->setValue(chloride); + ui->wg_so4Edit->setValue(sulfate); + ui->wg_phEdit->setValue(ph); + bicarbonate = total_alkalinity * 1.22; + + /* Save mixed water ions for later */ + double wg_calcium = calcium; + double wg_sodium = sodium; + double wg_total_alkalinity = total_alkalinity; + double wg_chloride = chloride; + double wg_sulfate = sulfate; + double wg_bicarbonate = bicarbonate; + + double mash_ph = MashpH(); + qDebug() << " Distilled water mash pH:" << mash_ph; + + /* Calculate Salt additions */ + if (liters > 0) { + calcium += ( ui->bs_cacl2Edit->value() * MMCa / MMCaCl2 * 1000 + ui->bs_caso4Edit->value() * MMCa / MMCaSO4 * 1000 + + ui->bs_caco3Edit->value() * MMCa / MMCaCO3 * 1000) / liters; + magnesium += (ui->bs_mgso4Edit->value() * MMMg / MMMgSO4 * 1000 + ui->bs_mgcl2Edit->value() * MMMg / MMMgCl2 * 1000) / liters; + sodium += (ui->bs_naclEdit->value() * MMNa / MMNaCl * 1000 + ui->bs_nahco3Edit->value() * MMNa / MMNaHCO3 * 1000) / liters; + sulfate += (ui->bs_caso4Edit->value() * MMSO4 / MMCaSO4 * 1000 + ui->bs_mgso4Edit->value() * MMSO4 / MMMgSO4 * 1000) / liters; + chloride += (2 * ui->bs_cacl2Edit->value() * MMCl / MMCaCl2 * 1000 + ui->bs_naclEdit->value() * MMCl / MMNaCl * 1000 + + ui->bs_mgcl2Edit->value() * MMCl / MMMgCl2 * 1000) / liters; + bicarbonate += (ui->bs_nahco3Edit->value() * MMHCO3 / MMNaHCO3 * 1000 + ui->bs_caco3Edit->value() / 3 * MMHCO3 / MMCaCO3 * 1000) / liters; + } + + const QSignalBlocker blocker1(ui->mw_acidPick); + const QSignalBlocker blocker2(ui->mw_acidpercEdit); + const QSignalBlocker blocker3(ui->mw_acidvolEdit); + const QSignalBlocker blocker4(ui->wb_phEdit); + const QSignalBlocker blocker5(ui->mw_phEdit); + + if (product->wa_acid_name < 0 || product->wa_acid_name >= my_acids.size()) { + product->wa_acid_name = 0; + product->wa_acid_perc = my_acids.at(0).AcidPrc; + ui->mw_acidPick->setCurrentIndex(0); + ui->mw_acidpercEdit->setValue(my_acids.at(0).AcidPrc); + } + AT = product->wa_acid_name; + + /* + * Note that the next calculations do not correct the pH change by the added salts. + * This pH change is at most 0.1 pH and is a minor difference in Acid amount. + */ + if (product->calc_acid) { + /* + * Auto calculate the needed acid. + */ + TpH = product->mash_ph; + protonDeficit = ProtonDeficit(TpH); + qDebug() << " calc_acid tgt:" << TpH << "protonDeficit:" << protonDeficit; + if (protonDeficit > 0) { + frac = Utils::CalcFrac(TpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3); + Acid = protonDeficit / frac; + Acid *= my_acids[AT].MolWt; // mg. + Acidmg = Acid; + Acid = Acid / my_acids[AT].AcidSG; + Acid = round((Acid / (product->wa_acid_perc / 100.0)) * 100.0) / 100.0; + qDebug() << " Mash auto Acid final ml:" << Acid; + + QString w = my_acids[AT].name_en + ' ' + my_acids[AT].name_nl; + brewing_salt_sub(w, Acid); + ui->mw_acidvolEdit->setValue(Acid); + + bicarbonate = bicarbonate - protonDeficit * frac / liters; + total_alkalinity = bicarbonate * 50 / 61; + } + ph = TpH; + ui->wb_phEdit->setValue(ph); + product->mash_ph = ph; + } else { // Manual + /* + * Manual adjust acid, calculate resulting pH. + */ + double pHa = ph; // Mixed water pH. + // Then calculate the new pH with added acids and malts + qDebug() << " Mash pH:" << pHa; + Acid = my_acids[AT].AcidSG * (product->wa_acid_perc / 100.0); // ml + Acid *= ui->mw_acidvolEdit->value(); + Acid /= my_acids[AT].MolWt; // mg; + Acidmg = Acid; + + //find the pH where the protondeficit = protondeficit by the acid + frac = Utils::CalcFrac(pHa, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3); + protonDeficit = Acid * frac; + //qDebug() << " protonDeficit Acid:" << protonDeficit << "frac:" << frac << "pH:" << pHa; + + double deltapH = 0.001; + double deltapd = 0.1; + double pd = round(ProtonDeficit(pHa) * 1000000.0) / 1000000.0; + int n = 0; + while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 4000)) { + n++; + if (pd < (protonDeficit - deltapd)) + pHa -= deltapH; + else if (pd > (protonDeficit + deltapd)) + pHa += deltapH; + frac = Utils::CalcFrac(pHa, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3); + protonDeficit = Acid * frac; + pd = ProtonDeficit(pHa); + } + //qDebug() << " n:" << n << "pd:" << pd << "protonDeficit:" << protonDeficit << "frac:" << frac << "pHa:" << pHa; + + bicarbonate = wg_bicarbonate - protonDeficit * frac / liters; + total_alkalinity = bicarbonate * 50 / 61; + ph = pHa; + ui->wb_phEdit->setValue(ph); + ui->mw_phEdit->setValue(ph); + product->mash_ph = ph; + } + + if ((AT == 3) && (liters > 0)) { // Sulfuctic / Zwavelzuur + RA = ui->bs_caso4Edit->value() * MMSO4 / MMCaSO4 + ui->bs_mgso4Edit->value() * MMSO4 / MMMgSO4 + Acidmg / 1000 * MMSO4 / (MMSO4 + 2); + RA = 1000 * RA / liters; + sulfate = wg_sulfate + RA; // Not add to sulfate?? + } else if ((AT == 1) && (liters > 0)) { // Hydrochloric, Zoutzuur + RA = ui->bs_cacl2Edit->value() * MMCl / MMCaCl2 + ui->bs_naclEdit->value() * MMCl / MMNaCl + Acidmg / 1000 * MMCl / (MMCl + 1); + RA = 1000 * RA / liters; + chloride = wg_chloride + RA; + } + + double BUGU = GetBUGU(); + ui->buguEdit->setValue(BUGU); + if (BUGU < 0.32) + ui->buguResult->setText(tr("Very malty and sweet")); + else if (BUGU < 0.43) + ui->buguResult->setText(tr("Malty, sweet")); + else if (BUGU < 0.52) + ui->buguResult->setText(tr("Balanced")); + else if (BUGU < 0.63) + ui->buguResult->setText(tr("Hoppy, bitter")); + else + ui->buguResult->setText(tr("Very hoppy, very bitter")); + + double OptSO4Clratio = GetOptSO4Clratio(); + ui->so4clEdit->setValue(OptSO4Clratio); + ui->cur_so4clResult->setRange(0.7 * OptSO4Clratio, 1.3 * OptSO4Clratio); + if (OptSO4Clratio < 0.4) + ui->so4clResult->setText(tr("Too malty")); + else if (OptSO4Clratio < 0.6) + ui->so4clResult->setText(tr("Very malty")); + else if (OptSO4Clratio < 0.8) + ui->so4clResult->setText(tr("Malty")); + else if (OptSO4Clratio < 1.5) + ui->so4clResult->setText(tr("Balanced")); + else if (OptSO4Clratio < 2.0) + ui->so4clResult->setText(tr("Little bitter")); + else if (OptSO4Clratio < 4.0) + ui->so4clResult->setText(tr("Bitter")); + else if (OptSO4Clratio < 9.0) + ui->so4clResult->setText(tr("Very bitter")); + else + ui->so4clResult->setText(tr("Too bitter")); + if (chloride > 0) + RA = sulfate / chloride; + else + RA = 10; + ui->cur_so4clEdit->setValue(RA); + ui->cur_so4clResult->setValue(RA); + + product->wb_calcium = calcium; + product->wb_magnesium = magnesium; + product->wb_total_alkalinity = total_alkalinity; + product->wb_sodium = sodium; + product->wb_chloride = chloride; + product->wb_sulfate = sulfate; + product->wb_ph = ph; + + ui->wb_caEdit->setValue(calcium); + ui->wb_mgEdit->setValue(magnesium); + ui->wb_hco3Edit->setValue(bicarbonate); + ui->wb_caco3Edit->setValue(total_alkalinity); + ui->wb_naEdit->setValue(sodium); + ui->wb_clEdit->setValue(chloride); + ui->wb_so4Edit->setValue(sulfate); + + ui->wb_caEdit->setStyleSheet((calcium < 40 || calcium > 150) ? "background-color: red":"background-color: green"); + ui->wb_mgEdit->setStyleSheet((magnesium < 5 || magnesium > 40) ? "background-color: red":"background-color: green"); + ui->wb_naEdit->setStyleSheet((sodium > 150) ? "background-color: red":"background-color: green"); + /* + * Both chloride and sulfate should be above 50 according to + * John Palmer. So the Cl/SO4 ratio calculation will work. + */ + ui->wb_clEdit->setStyleSheet((chloride <= 50 || chloride > 150) ? "background-color: red":"background-color: green"); + ui->wb_so4Edit->setStyleSheet((sulfate <= 50 || sulfate > 400) ? "background-color: red":"background-color: green"); + /* + * (cloride + sulfate) > 500 is too high + */ + if ((chloride + sulfate) > 500) { + ui->wb_clEdit->setStyleSheet("background-color: red"); + ui->wb_so4Edit->setStyleSheet("background-color: red"); + } + ui->wb_phEdit->setStyleSheet((ph < 5.2 || ph > 5.6) ? "background-color: red":"background-color: green"); + ui->wb_hco3Edit->setStyleSheet((bicarbonate > 250) ? "background-color: red":"background-color: green"); + ui->wb_caco3Edit->setStyleSheet((bicarbonate > 250) ? "background-color: red":"background-color: green"); + + calcSparge(); +} + + +/* + * Based on the work of ajDeLange. + */ +void EditProduct::calcSparge() +{ + double TargetpH = product->sparge_ph; + double Source_pH = product->w1_ph; + double Source_alkalinity = product->w1_total_alkalinity; + + qDebug() << "calcSparge()"; + + const QSignalBlocker blocker1(ui->sp_sourceEdit); + + // Select watersource or fallback to the first source. + if (product->sparge_source == 1) { // Source 2 + if (product->w2_ph > 0.0 && product->w2_amount > 0) { + Source_pH = product->w2_ph; + Source_alkalinity = product->w2_total_alkalinity; + } else { + product->sparge_source = 0; // Source 1 + ui->sp_sourceEdit->setCurrentIndex(0); + } + } else if (product->sparge_source == 2) { // Mixed + if (product->w2_ph > 0.0 && product->w2_amount > 0) { + Source_pH = product->wg_ph; + Source_alkalinity = product->wg_total_alkalinity; + } else { + product->sparge_source = 0; // Source 1 + ui->sp_sourceEdit->setCurrentIndex(0); + } + } + + // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH + double r1 = pow(10, Source_pH - 6.35); + double r2 = pow(10, Source_pH - 10.33); + double d = 1 + r1 + r1 * r2; + double f1 = 1 / d; + double f3 = r1 * r2 / d; + + // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) + double r143 = pow(10, 4.3 - 6.35); + double r243 = pow(10, 4.3 - 10.33); + double d43 = 1 + r143 + r143 * r243; + double f143 = 1 / d43; + double f343 = r143 * r243 / d43; + + // Step 4. Solve + //double Ct = (Source_alkalinity - 1000 * (pow(10, -4.3) - pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343)); + double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); + + // Step 5. Compute mole fractions at desired pH + double r1g = pow(10, TargetpH - 6.35); + double r2g = pow(10, TargetpH - 10.33); + double dg = 1 + r1g + r1g * r2g; + double f1g = 1 / dg; + double f3g = r1g * r2g / dg; + + // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) + double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH); //mEq/l + Acid += 0.01; // Add acid that would be required for distilled water. + + //Step 8. Get the acid data. + int AT = product->sparge_acid_type; + if (AT < 0 || AT >= my_acids.size()) { + AT = 0; + product->sparge_acid_type = 0; + ui->sp_acidtypeEdit->setCurrentIndex(0); + product->sparge_acid_perc = my_acids[0].AcidPrc; + ui->sp_acidpercEdit->setValue(product->sparge_acid_perc); + } + double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3); + + // Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid. + Acid /= fract; + + // Step 10. Multiply by molecular weight of the acid + Acid *= my_acids[AT].MolWt; //mg + + // Step 11. Divide by Specific Gravity and Percentage to get the final ml. + Acid = Acid / my_acids[AT].AcidSG / (product->sparge_acid_perc / 100); //ml + Acid *= product->sparge_volume; //ml acid total + Acid = round(Acid * 100.0) / 100.0; + product->sparge_acid_amount = Acid / 1000; + ui->sp_acidvolEdit->setValue(Acid); +} + + +void EditProduct::sp_source_changed(int val) +{ + product->sparge_source = val; + calcSparge(); + is_changed(); +} + + +void EditProduct::sp_type_changed(int val) +{ + product->sparge_acid_type = val; + product->sparge_acid_perc = my_acids[val].AcidPrc; + ui->sp_acidpercEdit->setValue(product->sparge_acid_perc); + calcSparge(); + is_changed(); +} + + +void EditProduct::sp_ph_changed(double val) +{ + product->sparge_ph = val; + calcSparge(); + is_changed(); +} + + +double EditProduct::GetBUGU() +{ + double gu = (product->est_og - 1) * 1000; + if (gu > 0) + return product->est_ibu / gu; + return 0.5; +} + + +double EditProduct::GetOptSO4Clratio() +{ + if (ui->wt_so4Edit->value() > 0 && ui->wt_clEdit->value()) { + /* If target water is selected .. */ + return (ui->wt_so4Edit->value() / ui->wt_clEdit->value()); + } + double BUGU = GetBUGU(); + return (-1.2 * BUGU + 1.4); +} + + +void EditProduct::mw_calc_acid_clicked() +{ + product->calc_acid = ! product->calc_acid; + ui->mw_autoEdit->setChecked(product->calc_acid); + ui->mw_phEdit->setReadOnly(! product->calc_acid); + ui->mw_phEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons); + ui->mw_acidvolEdit->setReadOnly(product->calc_acid); + ui->mw_acidvolEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows); + is_changed(); + calcWater(); +} + + +void EditProduct::mw_ph_changed(double val) +{ + if (! product->calc_acid) + return; + + if (product->mash_ph != val) { + qDebug() << "mw_ph_changed" << val << product->mash_ph; + product->mash_ph = val; + is_changed(); + calcWater(); + } +} + + +void EditProduct::mw_acid_changed(double val) +{ + if (product->calc_acid) + return; + + qDebug() << "on_mw_acid_changed" << val; + QString w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl; + set_brewing_salt(w, val); +} + + +void EditProduct::mw_type_changed(int val) +{ + if (val == product->wa_acid_name) + return; + + qDebug() << "on_mw_type_changed" << val << "old" << product->wa_acid_name; + /* + * First remove current acid. + */ + QString w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl; + brewing_salt_sub(w, 0); + + product->wa_acid_name = val; + w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl; + + product->wa_acid_perc = my_acids.at(val).AcidPrc; + ui->mw_acidpercEdit->setValue(my_acids.at(val).AcidPrc); + brewing_salt_sub(w, ui->mw_acidvolEdit->value()); // For now, set old amount. + + is_changed(); + calcWater(); +} + + +void EditProduct::w2_volume_changed(double val) +{ + qDebug() << "w2_vol_changed" << val; + + if (product->w2_total_alkalinity && product->w2_sulfate) { + /* + * Seems a valid water, but don't go over the total. + */ + if (val < (product->w1_amount + product->w2_amount)) { + product->w1_amount -= val - product->w2_amount; + product->w2_amount = val; + ui->w1_volEdit->setValue(product->w1_amount); + } + } else { + /* + * Invalid water, block changes. + */ + product->w2_amount = 0; + } + ui->w2_volEdit->setValue(product->w2_amount); + + calcWater(); + is_changed(); +} + + +void EditProduct::w1_name_changed(int val) +{ + QSqlQuery query; + + qDebug() << "w1_name_changed" << val; + const QSignalBlocker blocker1(ui->w1_nameEdit); + if (val == 0) { + /* + * If no water is selected, take the default water. + */ + val = my_default_water; + ui->w1_nameEdit->setCurrentIndex(val); + } + + query.prepare("SELECT * FROM inventory_waters ORDER BY record"); + query.exec(); + query.first(); + for (int i = 0; i < (val - 1); i++) { + query.next(); + } + qDebug() << "set water" << query.value(1).toString(); + + product->w1_name = query.value(1).toString(); + product->w1_calcium = query.value(3).toDouble(); + product->w1_magnesium = query.value(8).toDouble(); + product->w1_total_alkalinity = query.value(11).toDouble(); + product->w1_sodium = query.value(7).toDouble(); + product->w1_chloride = query.value(6).toDouble(); + product->w1_sulfate = query.value(5).toDouble(); + product->w1_ph = query.value(9).toDouble(); + + ui->w1_caEdit->setValue(product->w1_calcium); + ui->w1_mgEdit->setValue(product->w1_magnesium); + ui->w1_hco3Edit->setValue(product->w1_total_alkalinity * 1.22); + ui->w1_caco3Edit->setValue(product->w1_total_alkalinity); + ui->w1_naEdit->setValue(product->w1_sodium); + ui->w1_clEdit->setValue(product->w1_chloride); + ui->w1_so4Edit->setValue(product->w1_sulfate); + ui->w1_phEdit->setValue(product->w1_ph); + + is_changed(); + calcWater(); +} + + +void EditProduct::w2_name_changed(int val) +{ + QSqlQuery query; + + qDebug() << "w2_name_changed" << val; + + if (val == 0) { // Clear water 2. + product->w2_name = ""; + product->w2_calcium = 0; + product->w2_magnesium = 0; + product->w2_total_alkalinity = 0; + product->w2_sodium = 0; + product->w2_chloride = 0; + product->w2_sulfate = 0; + product->w2_ph = 0; + product->w1_amount += product->w2_amount; + product->w2_amount = 0; + } else { + query.prepare("SELECT * FROM inventory_waters ORDER BY record"); + query.exec(); + query.first(); + for (int i = 0; i < (val - 1); i++) { + query.next(); + } + qDebug() << "set water" << query.value(1).toString(); + + product->w2_name = query.value(1).toString(); + product->w2_calcium = query.value(3).toDouble(); + product->w2_magnesium = query.value(8).toDouble(); + product->w2_total_alkalinity = query.value(11).toDouble(); + product->w2_sodium = query.value(7).toDouble(); + product->w2_chloride = query.value(6).toDouble(); + product->w2_sulfate = query.value(5).toDouble(); + product->w2_ph = query.value(9).toDouble(); + } + ui->w1_volEdit->setValue(product->w1_amount); + ui->w2_volEdit->setValue(product->w2_amount); + ui->w2_caEdit->setValue(product->w2_calcium); + ui->w2_mgEdit->setValue(product->w2_magnesium); + ui->w2_hco3Edit->setValue(product->w2_total_alkalinity * 1.22); + ui->w2_caco3Edit->setValue(product->w2_total_alkalinity); + ui->w2_naEdit->setValue(product->w2_sodium); + ui->w2_clEdit->setValue(product->w2_chloride); + ui->w2_so4Edit->setValue(product->w2_sulfate); + ui->w2_phEdit->setValue(product->w2_ph); + + is_changed(); + calcWater(); +} + + +void EditProduct::wt_target_changed(int val) +{ + QSqlQuery query; + + if (val == 0) { + /* Clear values */ + ui->wt_caEdit->setValue(0); + ui->wt_mgEdit->setValue(0); + ui->wt_hco3Edit->setValue(0); + ui->wt_caco3Edit->setValue(0); + ui->wt_naEdit->setValue(0); + ui->wt_clEdit->setValue(0); + ui->wt_so4Edit->setValue(0); + } else { + query.prepare("SELECT * FROM profile_water ORDER BY name"); + query.exec(); + query.first(); + for (int i = 0; i < (val - 1); i++) { + query.next(); + } + ui->wt_caEdit->setValue(query.value(2).toDouble()); + ui->wt_mgEdit->setValue(query.value(7).toDouble()); + ui->wt_hco3Edit->setValue(query.value(3).toDouble()); + ui->wt_caco3Edit->setValue(query.value(10).toDouble()); + ui->wt_naEdit->setValue(query.value(6).toDouble()); + ui->wt_clEdit->setValue(query.value(5).toDouble()); + ui->wt_so4Edit->setValue(query.value(4).toDouble()); + } + calcWater(); +} + + +void EditProduct::adjustWaters(double factor) +{ + int i; + double amount; + + if (product->mashs.size() == 0) + return; + + double mash_infuse = 0; + for (i = 0; i < product->mashs.size(); i++) { + if (product->mashs.at(i).step_type == 0) { // Infusion + amount = round(product->mashs.at(i).step_infuse_amount * factor * 1000000.0) / 1000000.0; + product->mashs[i].step_infuse_amount = amount; + mash_infuse += amount; + product->mashs[i].step_volume = mash_infuse; + } + } + + const QSignalBlocker blocker1(ui->w1_volEdit); + const QSignalBlocker blocker2(ui->w2_volEdit); + + if (product->w2_amount == 0) { + product->w1_amount = mash_infuse; + ui->w1_volEdit->setValue(mash_infuse); + } else { + double w1 = (product->w1_amount / (product->w1_amount + product->w2_amount)) * mash_infuse; + double w2 = (product->w2_amount / (product->w1_amount + product->w2_amount)) * mash_infuse; + product->w1_amount = w1; + product->w2_amount = w2; + ui->w1_volEdit->setValue(product->w1_amount); + ui->w2_volEdit->setValue(product->w2_amount); + } + product->wg_amount = mash_infuse; + ui->wg_volEdit->setValue(mash_infuse); +} + + +void EditProduct::wb_cacl2_changed(double val) { set_brewing_salt("CaCl2", val); } +void EditProduct::wb_caso4_changed(double val) { set_brewing_salt("CaSO4", val); } +void EditProduct::wb_mgso4_changed(double val) { set_brewing_salt("MgSO4", val); } +void EditProduct::wb_nacl_changed(double val) { set_brewing_salt("NaCl", val); } +void EditProduct::wb_mgcl2_changed(double val) { set_brewing_salt("MgCl2", val); } +void EditProduct::wb_nahco3_changed(double val) { set_brewing_salt("NaHCO3", val); } +void EditProduct::wb_caco3_changed(double val) { set_brewing_salt("CaCO3", val); } + +