# HG changeset patch # User Michiel Broek # Date 1651764006 -7200 # Node ID 9887278c4fbef7af8a19eeac55289d076cb6135b # Parent ea8cce5e7eb93baef9119a8697e6d05ec72d035f The framework to calculate yeast starters added. diff -r ea8cce5e7eb9 -r 9887278c4fbe src/EditProduct.cpp --- a/src/EditProduct.cpp Wed May 04 13:49:37 2022 +0200 +++ b/src/EditProduct.cpp Thu May 05 17:20:06 2022 +0200 @@ -807,9 +807,7 @@ ui->est_ibu2Edit->setValue(product->est_ibu); // Tab yeasts. - ui->est_og4Edit->setValue(product->est_og); - ui->est_fg3Edit->setValue(product->est_fg); - ui->est_abv2Edit->setValue(product->est_abv); + initYeast(); // Tab mashs. ui->mash_nameEdit->setText(product->mash_name); @@ -1045,8 +1043,9 @@ connect(ui->addMisc, SIGNAL(clicked()), this, SLOT(addMiscRow_clicked())); // All signals from tab "Yeasts" - ui->yeastsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); connect(ui->addYeast, SIGNAL(clicked()), this, SLOT(addYeastRow_clicked())); + connect(ui->stmethodEdit, QOverload::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_method_changed); + connect(ui->startersgEdit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::yeast_starter_sg_changed); // All signals from tab "Mash" ui->mashsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); diff -r ea8cce5e7eb9 -r 9887278c4fbe src/EditProduct.h --- a/src/EditProduct.h Wed May 04 13:49:37 2022 +0200 +++ b/src/EditProduct.h Thu May 05 17:20:06 2022 +0200 @@ -15,6 +15,14 @@ #include "global.h" +struct StepResult { + double svol; + double irate; + double ncells; + double totcells; + double growf; +}; + namespace Ui { class EditProduct; } @@ -72,6 +80,11 @@ void misc_select_changed(int val); void misc_instock_changed(bool val); void misc_useat_changed(int val); + void yeast_prod_date_clicked(); + void yeast_method_changed(int val); + void yeast_starter_sg_changed(double val); + void yeast_starter_edit_clicked(); + void yeast_pitchrate_button_clicked(); void yeast_amount_changed(double val); void yeast_select_changed(int val); void yeast_instock_changed(bool val); @@ -181,6 +194,10 @@ void calcSparge(); double GetBUGU(); double GetOptSO4Clratio(); + double getGrowthRate(int stype, double totcells, double egrams); + StepResult calcStep(double svol, int stype, double start); + void calcSteps(int stype, double start, double needed); + void initYeast(); void calcYeast(); void adjustYeasts(double factor); double infusionVol(double step_infused, double step_mashkg, double infuse_temp, double step_temp, double last_temp); diff -r ea8cce5e7eb9 -r 9887278c4fbe src/EditProductTab6.cpp --- a/src/EditProductTab6.cpp Wed May 04 13:49:37 2022 +0200 +++ b/src/EditProductTab6.cpp Thu May 05 17:20:06 2022 +0200 @@ -89,22 +89,24 @@ item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->yeastsTable->setItem(i, 8, item); - if (product->yeasts.at(i).y_form == 0) + if (product->yeasts.at(i).y_form == YEAST_FORMS_LIQUID) item = new QTableWidgetItem(QString("%1 pack").arg(product->yeasts.at(i).y_amount, 1, 'f', 0, '0')); - else if (product->yeasts.at(i).y_form == 1) + else if (product->yeasts.at(i).y_form == YEAST_FORMS_DRY || product->yeasts.at(i).y_form == YEAST_FORMS_DRIED) item = new QTableWidgetItem(QString("%1 gr").arg(product->yeasts.at(i).y_amount * 1000.0, 3, 'f', 2, '0')); else item = new QTableWidgetItem(QString("%1 ml").arg(product->yeasts.at(i).y_amount * 1000.0, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->yeastsTable->setItem(i, 9, item); - if (product->yeasts.at(i).y_form == 0) + if (product->yeasts.at(i).y_form == YEAST_FORMS_LIQUID) item = new QTableWidgetItem(QString("%1 pack").arg(product->yeasts.at(i).y_inventory, 1, 'f', 0, '0')); - else if (product->yeasts.at(i).y_form == 1) + else if (product->yeasts.at(i).y_form == YEAST_FORMS_DRY || product->yeasts.at(i).y_form == YEAST_FORMS_DRIED) item = new QTableWidgetItem(QString("%1 gr").arg(product->yeasts.at(i).y_inventory * 1000.0, 3, 'f', 2, '0')); else item = new QTableWidgetItem(QString("%1 ml").arg(product->yeasts.at(i).y_inventory * 1000.0, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + if (product->yeasts.at(i).y_inventory < product->yeasts.at(i).y_amount) + item->setForeground(QBrush(QColor(Qt::red))); ui->yeastsTable->setItem(i, 10, item); pWidget = new QWidget(); @@ -132,27 +134,62 @@ } +void EditProduct::initYeast() +{ + ui->est_og4Edit->setValue(product->est_og); + ui->est_fg3Edit->setValue(product->est_fg); + ui->est_abv2Edit->setValue(product->est_abv); + ui->productionEdit->setText(product->yeast_prod_date.toString("dd MMM yyyy")); + ui->conditionShow->setValue(product->starter_viability); + ui->startersgEdit->setValue(product->starter_sg); + ui->pitchrateEdit->setValue(product->yeast_pitchrate); + + ui->yeastsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + + for (int i = 0; i < starters.size(); i++) { + ui->stmethodEdit->addItem(starters[i]); + } + ui->stmethodEdit->setCurrentIndex(product->starter_type); +} + + /* - * The results are not stored line in EditProduct. This is just a hint. + * Calculate the needed yeast for this batch. */ void EditProduct::calcYeast() { - double sg = product->est_og; - double plato = Utils::sg_to_plato(sg); - double volume = product->batch_size * 0.9; // Volume min trub chiller loss. + double sg = product->brew_fermenter_sg; + double use_cells; + double needed = 0; + double initcells = 0; bool maybe_starter = false; - double pitchrate = 0.75; - double initcells = 0; qDebug() << "calcYeast()"; ui->yeastProcedure->setCurrentIndex(0); + if (sg <= 1.0001 && product->fg > 1.000) + sg = product->fg; + else if (sg <= 1.0001) + sg = product->est_og; + double plato = Utils::sg_to_plato(sg); + + double volume = product->brew_fermenter_volume; + if (volume > 0) { + if (product->brew_fermenter_extrawater > 0) + volume += product->brew_fermenter_extrawater; + } else { + volume = product->batch_size - product->eq_trub_chiller_loss; + } + + // Also in calcFermentables() + //$('#yeast_cells').val(initcells); + if (product->yeasts.size() == 0) return; // No yeast in product. for (int i = 0; i < product->yeasts.size(); i++) { - if (product->yeasts.at(i).y_use == 0) { // Primary - if (product->yeasts.at(i).y_form == 1) { + if (product->yeasts.at(i).y_use == YEAST_USE_PRIMARY) { // Primary + if (product->yeasts.at(i).y_form == YEAST_FORMS_DRY) { /* * Dry yeast, build the formule with the yeast parameters. * Based on https://www.lallemandbrewing.com/en/canada/brewers-corner/brewing-tools/pitching-rate-calculator/ @@ -180,39 +217,446 @@ * and http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/ */ ui->yeastProcedure->setCurrentIndex(1); - if (product->yeasts.at(i).y_type == 0) { // Lager yeast - pitchrate = 1.5; - if (sg > 1.060) - pitchrate = 2.0; - } else if (product->yeasts.at(i).y_type == 6) { // Real Kveik - pitchrate = 0.075; - } else { - pitchrate = 0.75; - if (sg > 1.060) - pitchrate = 1.0; + if (product->yeast_pitchrate == 0) { + /* + * No pitchrate yet, do a educated guess .. + */ + if (product->yeasts.at(i).y_type == YEAST_TYPES_LAGER) { + product->yeast_pitchrate = 1.5; + if (sg > 1.060) + product->yeast_pitchrate = 2.0; + } else if (product->yeasts.at(i).y_type == YEAST_TYPES_KVEIK) { // Real Kveik + product->yeast_pitchrate = 0.075; + } else { + product->yeast_pitchrate = 0.75; + if (sg > 1.060) + product->yeast_pitchrate = 1.0; + } + is_changed(); + ui->pitchrateEdit->setValue(product->yeast_pitchrate); } - if (product->yeasts.at(i).y_form == 0) + + initcells = (product->yeasts.at(i).y_cells / 1000000) * product->yeasts.at(i).y_amount * 0.97; // cells / ml. + if (product->yeasts.at(i).y_form == YEAST_FORMS_LIQUID) initcells = (product->yeasts.at(i).y_cells / 1000000000) * product->yeasts.at(i).y_amount * 0.97; // 97% viability assumed. - else - initcells = (product->yeasts.at(i).y_cells / 1000000) * product->yeasts.at(i).y_amount * 0.97; - double needed = round(pitchrate * volume * plato * 10.0) / 10.0; - double starter = 0; + needed = round(product->yeast_pitchrate * volume * plato * 10.0) / 10.0; if (needed > initcells) { maybe_starter = true; - starter = round(needed / 2.0) / 100.0; // A very rough starter size estimate. } - ui->pitchrateEdit->setValue(pitchrate); - ui->initcellsEdit->setValue(initcells); - ui->targetcellsEdit->setValue(needed); - ui->starterEdit->setValue(starter); - - qDebug() << " pitchrate:" << pitchrate << "needed:" << needed << "initcells:" << initcells << "starter" << maybe_starter << "size" << starter; + qDebug() << " pitchrate:" << product->yeast_pitchrate << "needed:" << needed << "initcells:" << initcells << "starter" << maybe_starter; } break; } } + + if (maybe_starter != product->starter_enable) { + product->starter_enable = maybe_starter; + qDebug() << " Set starter enable" << maybe_starter; + is_changed(); + } + + if (product->starter_enable) { + qDebug() << " Starter calculate.."; + + const QStringList labels({tr("Method"), tr("Volume"), tr("Inj. factor"), tr("New cells"), tr("Total cells"), tr("Grow factor"), "" }); + ui->starterTable->show(); + ui->starterTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->starterTable->clear(); + ui->starterTable->setColumnCount(7); + ui->starterTable->setRowCount(0); + ui->starterTable->setColumnWidth(0, 130); /* Method */ + ui->starterTable->setColumnWidth(1, 90); /* Volume */ + ui->starterTable->setColumnWidth(2, 90); /* Inj. factor */ + ui->starterTable->setColumnWidth(3, 90); /* New cells */ + ui->starterTable->setColumnWidth(4, 90); /* Total cells */ + ui->starterTable->setColumnWidth(5, 90); /* Grow factor */ + ui->starterTable->setColumnWidth(6, 30); /* Edit button */ + ui->starterTable->setHorizontalHeaderLabels(labels); + calcSteps(product->starter_type, initcells, needed); + } else { + ui->starterTable->hide(); + } +} + + +/* + * http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/ + * + * stype: 0=stirred, 1=shaken, 2=simple + * totcells: initial cells + * egrams: gram extract + */ +double EditProduct::getGrowthRate(int stype, double totcells, double egrams) +{ + /* Cells per grams extract (B/g) */ + double cpe = totcells / egrams; + + if (cpe > 3.5) + return 0; // no growth + if (stype == STARTERS_SIMPLE) + return 0.4; // simple starter + if (stype == STARTERS_SHAKEN) + return 0.62; // shaken starter + if (cpe <= 1.4) // stirred starter + return 1.4; + return 2.33 - (.67 * cpe); +}; + + +StepResult EditProduct::calcStep(double svol, int stype, double start) +{ + StepResult res; + double gperpoint = 2.72715; //number of grams of extract per point of starter gravity per liter + double irate = round(start / svol * 10000.0) / 10.0; + double egrams = (product->starter_sg - 1) * svol * gperpoint; + double grate = getGrowthRate(stype, start, egrams); + double ncells = round(egrams * grate * 10.0) / 10.0; + double totcells = ncells + start; + + res.svol = svol; + res.irate = irate; + res.ncells = ncells; + res.totcells = totcells; + res.growf = round((ncells / start) * 100.0) / 100.0; + qDebug() << " calcStep(" << svol << "," << stype << "," << start << ") irate" << irate + << "ncells" << res.ncells << "totcells" << res.totcells << "growf" << res.growf; + return res; +} + + +/* + * Calculate all starter steps. + * stype: final starter type: 0 = stirred, 1 = shaked, 2 = simple. + * start: initial cells in billions + * needed: needed cells in billions + * + * result: all values updated. + */ +void EditProduct::calcSteps(int stype, double start, double needed) +{ + int i, step, svol; + int lasti = 0; + /* Erlenmeyer sizes */ + const int uvols[] { 20, 40, 60, 80, 100, 150, 200, 250, 375, 500, 625, 750, 875, 1000, 1250, 1500, 2000, 2500, 3000, 4000, 5000 }; + int mvols = sizeof(uvols); + StepResult result; + QTableWidgetItem *item; + QWidget* pWidget; + QHBoxLayout* pLayout; + double tcells = start; + QIcon iconT; + + iconT.addFile(QString::fromUtf8(":/icons/silk/pencil.png"), QSize(), QIcon::Normal, QIcon::Off); + + if ((product->prop1_volume + product->prop2_volume + product->prop3_volume + product->prop4_volume) == 0) { + /* + * Auto calculate the starter. + */ + qDebug() << " calcSteps() auto"; + + if (start > needed) + return; + + for (step = 1; step < 5; step++) { + qDebug() << " step" << step; + + for (i = lasti; i <= mvols; i++) { + lasti = i; + svol = uvols[lasti]; + result = calcStep(svol, stype, tcells); + if (result.irate < 25) { + // inocculation rate too low, backup one step and break out. + lasti = i - 1; + svol = uvols[lasti]; + result = calcStep(svol, stype, tcells); + break; + } + if (result.totcells > needed || i == mvols) { // hit the target or loops done + break; + } + } + ui->starterTable->setRowCount(step); + item = new QTableWidgetItem(starters[stype]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->starterTable->setItem(step -1, 0, item); + + item = new QTableWidgetItem(QString("%1").arg(result.svol / 1000.0, 4, 'f', 3, '0')); // To liters + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(step -1, 1, item); + + item = new QTableWidgetItem(QString("%1").arg(result.irate, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(step -1, 2, item); + + item = new QTableWidgetItem(QString("%1").arg(result.ncells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(step -1, 3, item); + + item = new QTableWidgetItem(QString("%1").arg(result.totcells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(step -1, 4, item); + + item = new QTableWidgetItem(QString("%1").arg(result.growf, 3, 'f', 2, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(step -1, 5, item); + + pWidget = new QWidget(); + QToolButton* btn_edit = new QToolButton(); + btn_edit->setObjectName(QString("%1").arg(step - 1)); /* Send row with the button */ + btn_edit->setIcon(iconT); + connect(btn_edit, SIGNAL(clicked()), this, SLOT(yeast_starter_edit_clicked())); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(btn_edit); + pLayout->setContentsMargins(5, 0, 5, 0); + pWidget->setLayout(pLayout); + ui->starterTable->setCellWidget(step -1, 6, pWidget); + + if (step == 1) { + product->prop1_type = product->starter_type; + product->prop1_volume = result.svol / 1000.0; + } else if (step == 2) { + product->prop2_type = product->starter_type; + product->prop2_volume = result.svol / 1000.0; + } else if (step == 3) { + product->prop3_type = product->starter_type; + product->prop3_volume = result.svol / 1000.0; + } else if (step == 4) { + product->prop4_type = product->starter_type; + product->prop4_volume = result.svol / 1000.0; + } + + tcells = result.totcells; + if (result.totcells > needed) // Hit the target + return; + } + + } else { + /* + * Recalculate the starter. + */ + qDebug() << " calcSteps() recalculate"; + + if (product->prop1_volume > 0) { + result = calcStep(product->prop1_volume * 1000, product->prop1_type, tcells); + ui->starterTable->setRowCount(1); + item = new QTableWidgetItem(starters[product->prop1_type]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->starterTable->setItem(0, 0, item); + + item = new QTableWidgetItem(QString("%1").arg(result.svol / 1000.0, 4, 'f', 3, '0')); // To liters + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(0, 1, item); + + item = new QTableWidgetItem(QString("%1").arg(result.irate, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(0, 2, item); + + item = new QTableWidgetItem(QString("%1").arg(result.ncells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(0, 3, item); + + item = new QTableWidgetItem(QString("%1").arg(result.totcells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(0, 4, item); + + item = new QTableWidgetItem(QString("%1").arg(result.growf, 3, 'f', 2, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(0, 5, item); + + pWidget = new QWidget(); + QToolButton* btn_edit = new QToolButton(); + btn_edit->setObjectName(QString("0")); /* Send row with the button */ + btn_edit->setIcon(iconT); + connect(btn_edit, SIGNAL(clicked()), this, SLOT(yeast_starter_edit_clicked())); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(btn_edit); + pLayout->setContentsMargins(5, 0, 5, 0); + pWidget->setLayout(pLayout); + ui->starterTable->setCellWidget(0, 6, pWidget); + + tcells = result.totcells; + if (result.totcells > needed) { // Hit the target + product->prop2_volume = product->prop3_volume = product->prop4_volume = 0; + product->prop2_type = product->prop3_type = product->prop4_type = 0; + return; + } else if (product->prop2_volume == 0) { // Extra step needed, start with the same size. + product->prop2_volume = product->prop1_volume; + product->prop2_type = product->prop1_type; + } + } + if (product->prop2_volume > 0) { + result = calcStep(product->prop2_volume * 1000, product->prop2_type, tcells); + ui->starterTable->setRowCount(2); + item = new QTableWidgetItem(starters[product->prop2_type]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->starterTable->setItem(1, 0, item); + + item = new QTableWidgetItem(QString("%1").arg(result.svol / 1000.0, 4, 'f', 3, '0')); // To liters + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(1, 1, item); + + item = new QTableWidgetItem(QString("%1").arg(result.irate, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(1, 2, item); + + item = new QTableWidgetItem(QString("%1").arg(result.ncells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(1, 3, item); + + item = new QTableWidgetItem(QString("%1").arg(result.totcells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(1, 4, item); + + item = new QTableWidgetItem(QString("%1").arg(result.growf, 3, 'f', 2, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(1, 5, item); + + pWidget = new QWidget(); + QToolButton* btn_edit = new QToolButton(); + btn_edit->setObjectName(QString("1")); /* Send row with the button */ + btn_edit->setIcon(iconT); + connect(btn_edit, SIGNAL(clicked()), this, SLOT(yeast_starter_edit_clicked())); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(btn_edit); + pLayout->setContentsMargins(5, 0, 5, 0); + pWidget->setLayout(pLayout); + ui->starterTable->setCellWidget(1, 6, pWidget); + + tcells = result.totcells; + if (result.totcells > needed) { // Hit the target + product->prop3_volume = product->prop4_volume = 0; + product->prop3_type = product->prop4_type = 0; + return; + } else if (product->prop3_volume == 0) { // Extra step needed, start with the same size. + product->prop3_volume = product->prop2_volume; + product->prop3_type = product->prop2_type; + } + } + if (product->prop3_volume > 0) { + result = calcStep(product->prop3_volume * 1000, product->prop3_type, tcells); + ui->starterTable->setRowCount(3); + item = new QTableWidgetItem(starters[product->prop3_type]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->starterTable->setItem(2, 0, item); + + item = new QTableWidgetItem(QString("%1").arg(result.svol / 1000.0, 4, 'f', 3, '0')); // To liters + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(2, 1, item); + + item = new QTableWidgetItem(QString("%1").arg(result.irate, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(2, 2, item); + + item = new QTableWidgetItem(QString("%1").arg(result.ncells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(2, 3, item); + + item = new QTableWidgetItem(QString("%1").arg(result.totcells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(2, 4, item); + + item = new QTableWidgetItem(QString("%1").arg(result.growf, 3, 'f', 2, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(2, 5, item); + + pWidget = new QWidget(); + QToolButton* btn_edit = new QToolButton(); + btn_edit->setObjectName(QString("2")); /* Send row with the button */ + btn_edit->setIcon(iconT); + connect(btn_edit, SIGNAL(clicked()), this, SLOT(yeast_starter_edit_clicked())); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(btn_edit); + pLayout->setContentsMargins(5, 0, 5, 0); + pWidget->setLayout(pLayout); + ui->starterTable->setCellWidget(2, 6, pWidget); + + tcells = result.totcells; + if (result.totcells > needed) { // Hit the target + product->prop4_volume = 0; + product->prop4_type = 0; + return; + } else if (product->prop4_volume == 0) { // Extra step needed, start with the same size. + product->prop4_volume = product->prop3_volume; + product->prop4_type = product->prop3_type; + } + } + if (product->prop4_volume > 0) { + result = calcStep(product->prop4_volume * 1000, product->prop4_type, tcells); + ui->starterTable->setRowCount(4); + item = new QTableWidgetItem(starters[product->prop4_type]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->starterTable->setItem(3, 0, item); + + item = new QTableWidgetItem(QString("%1").arg(result.svol / 1000.0, 4, 'f', 3, '0')); // To liters + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(3, 1, item); + + item = new QTableWidgetItem(QString("%1").arg(result.irate, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(3, 2, item); + + item = new QTableWidgetItem(QString("%1").arg(result.ncells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(3, 3, item); + + item = new QTableWidgetItem(QString("%1").arg(result.totcells, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(3, 4, item); + + item = new QTableWidgetItem(QString("%1").arg(result.growf, 3, 'f', 2, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->starterTable->setItem(3, 5, item); + + pWidget = new QWidget(); + QToolButton* btn_edit = new QToolButton(); + btn_edit->setObjectName(QString("3")); /* Send row with the button */ + btn_edit->setIcon(iconT); + connect(btn_edit, SIGNAL(clicked()), this, SLOT(yeast_starter_edit_clicked())); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(btn_edit); + pLayout->setContentsMargins(5, 0, 5, 0); + pWidget->setLayout(pLayout); + ui->starterTable->setCellWidget(3, 6, pWidget); + } + } +} + + +void EditProduct::yeast_prod_date_clicked() +{ +} + + +void EditProduct::yeast_method_changed(int val) +{ + qDebug() << "yeast_method_changed" << val; + product->starter_type = val; + calcYeast(); + is_changed(); +} + + +void EditProduct::yeast_starter_sg_changed(double val) +{ + qDebug() << "yeast_starter_sg_changed" << val; + product->starter_sg = val; + calcYeast(); + is_changed(); +} + + +void EditProduct::yeast_pitchrate_button_clicked() +{ +} + + +void EditProduct::yeast_starter_edit_clicked() +{ + QToolButton *pb = qobject_cast(QObject::sender()); + int row = pb->objectName().toInt(); + qDebug() << "yeast_starter_edit_clicked" << row; } @@ -594,7 +1038,7 @@ return; for (int i = 0; i < product->yeasts.size(); i++) { - if (product->yeasts.at(i).y_form == 1) { // Only adjust dry yeast + if (product->yeasts.at(i).y_form == YEAST_FORMS_DRY) { // Only adjust dry yeast amount = product->yeasts.at(i).y_amount * factor; product->yeasts[i].y_amount = amount; } diff -r ea8cce5e7eb9 -r 9887278c4fbe src/Utils.cpp --- a/src/Utils.cpp Wed May 04 13:49:37 2022 +0200 +++ b/src/Utils.cpp Thu May 05 17:20:06 2022 +0200 @@ -214,6 +214,7 @@ double Utils::sg_to_plato(double sg) { + // On stChiellus: return ((135.997 * sg - 630.272) * sg + 1111.14) * sg - 616.868; return -668.962 + (1262.45 * sg) - (776.43 * sg * sg) + (182.94 * sg * sg * sg); } diff -r ea8cce5e7eb9 -r 9887278c4fbe src/global.h --- a/src/global.h Wed May 04 13:49:37 2022 +0200 +++ b/src/global.h Thu May 05 17:20:06 2022 +0200 @@ -662,9 +662,47 @@ }; extern const QStringList misc_uses; + +enum YeastTypes { + YEAST_TYPES_LAGER, + YEAST_TYPES_ALE, + YEAST_TYPES_WHEAT, + YEAST_TYPES_WINE, + YEAST_TYPES_CHAMPAGNE, + YEAST_TYPES_BRETT, + YEAST_TYPES_KVEIK, + YEAST_TYPES_HYBRID +}; + extern const QStringList yeast_types; + +enum YeastForms { + YEAST_FORMS_LIQUID, + YEAST_FORMS_DRY, + YEAST_FORMS_SLANT, + YEAST_FORMS_CULTURE, + YEAST_FORMS_FROZEN, + YEAST_FORMS_BOTTLE, + YEAST_FORMS_DRIED +}; + extern const QStringList yeast_forms; + +enum YeastUse { + YEAST_USE_PRIMARY, + YEAST_USE_SECONDARY, + YEAST_USE_TERTIARY, + YEAST_USE_BOTTLE +}; + extern const QStringList yeast_use; + +enum Starters { + STARTERS_STIRRED, + STARTERS_SHAKEN, + STARTERS_SIMPLE +}; + extern const QStringList starters; extern const QStringList step_types; extern const QStringList tun_materials; diff -r ea8cce5e7eb9 -r 9887278c4fbe ui/EditProduct.ui --- a/ui/EditProduct.ui Wed May 04 13:49:37 2022 +0200 +++ b/ui/EditProduct.ui Thu May 05 17:20:06 2022 +0200 @@ -95,7 +95,7 @@ QTabWidget::Rounded - 0 + 5 Qt::ElideNone @@ -2723,7 +2723,7 @@ - 50 + 30 10 131 20 @@ -2739,7 +2739,7 @@ - 190 + 170 10 71 24 @@ -2770,7 +2770,7 @@ - 50 + 30 40 131 20 @@ -2786,7 +2786,7 @@ - 190 + 170 40 71 24 @@ -2817,7 +2817,7 @@ - 50 + 30 70 131 20 @@ -2833,7 +2833,7 @@ - 190 + 170 70 71 24 @@ -2870,7 +2870,7 @@ - 30 + 10 100 151 20 @@ -2886,7 +2886,7 @@ - 190 + 170 100 71 24 @@ -2924,19 +2924,19 @@ 10 - 250 + 280 1101 - 211 + 181 - 290 + 320 10 - 821 - 231 + 791 + 261 @@ -2944,31 +2944,11 @@ - - - - 200 - 20 - 341 - 20 - - - - - 11 - 75 - true - - - - Liquid yeast advice - - - 200 - 60 + 610 + 10 91 24 @@ -2995,8 +2975,8 @@ - 10 - 60 + 420 + 10 181 20 @@ -3008,139 +2988,119 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + - 10 - 90 + 230 + 10 + 101 + 23 + + + + + + + 40 + 10 181 20 - Initial billion cells: + Starter method: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + - 10 - 120 + 40 + 40 181 20 - Target billion cells: + Starter SG: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + - 200 - 90 - 91 + 230 + 40 + 71 24 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - true - - QAbstractSpinBox::NoButtons - - - false - - - 0 - - - 100000.000000000000000 - - - - - - 200 - 120 - 91 - 24 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - QAbstractSpinBox::NoButtons + QAbstractSpinBox::UpDownArrows - false - - - 0 - - - 100000.000000000000000 - - - - - - 10 - 150 - 181 - 20 - - - - Starter volume L: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - 200 - 150 - 91 - 24 - - - - A very rough starter volume estimate. - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - true - - QAbstractSpinBox::NoButtons - - - false - 3 + + 0.980000000000000 + - 100000.000000000000000 + 2.000000000000000 + + + 0.001000000000000 + + + + + + 710 + 10 + 28 + 22 + + + + Set or clear date + + + ... + + + + :/icons/bms/erlenmeyer.png:/icons/bms/erlenmeyer.png + + + + + + 80 + 100 + 636 + 146 + + + + + 0 + 0 + + + + + 0 + 0 + @@ -3425,8 +3385,8 @@ - 180 - 210 + 90 + 240 80 23 @@ -3439,6 +3399,99 @@ :/icons/bms/erlenmeyer.png:/icons/bms/erlenmeyer.png + + + + 10 + 130 + 151 + 20 + + + + Production date: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 10 + 160 + 151 + 20 + + + + Yeast condition: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 280 + 130 + 28 + 22 + + + + Set or clear date + + + ... + + + + :/icons/silk/date.png:/icons/silk/date.png + + + + + + 170 + 130 + 101 + 23 + + + + End of primary fermentation, start secondary. + + + true + + + + + + 170 + 160 + 71 + 24 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + QAbstractSpinBox::NoButtons + + + false + + + % + +