src/EditProductTab11.cpp

Fri, 18 Nov 2022 16:57:02 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 18 Nov 2022 16:57:02 +0100
changeset 443
3c195eb4e7a1
parent 442
d8c110d91b1f
child 447
9b5acb1f5776
permissions
-rw-r--r--

Details CO2 monitor shows the style limits for the specific beer. Adjust the scale of the pressure widget to the beer limits. Moved more functions to the global Utils. Fix expected pressure in the package screen for other priming sugars. Disabled some debug log messages.

/**
 * 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;
    }

    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 = 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) {
	val = product->package_volume;
	const QSignalBlocker blocker2(ui->bottle_volumeEdit);
	ui->bottle_volumeEdit->setValue(val);
    }

    if ((val + product->keg_amount) > product->package_volume) {
        double kegs = product->package_volume - 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) {
        val = product->package_volume;
	const QSignalBlocker blocker2(ui->keg_volumeEdit);
	ui->keg_volumeEdit->setValue(val);
    }

    if ((val + product->bottle_amount) > product->package_volume) {
	double bottle = product->package_volume - 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);
}

mercurial