diff -r 029e65ca3678 -r 1246550451ca www/js/prod_edit.js --- a/www/js/prod_edit.js Fri May 01 21:37:23 2020 +0200 +++ b/www/js/prod_edit.js Wed May 06 14:14:14 2020 +0200 @@ -121,6 +121,9 @@ dataRecord = {}, url = 'includes/db_product.php', MaltVolume = 0.87, // l/kg 0.688 volgens internetbronnen, gemeten 0.874 l/kg, na enige tijd maischen 0,715 l/kg (A3 Otten). + SpecificHeatWater = 1.0, + SpecificHeatMalt = 0.399, //cal/g.°C + SlakingHeat = 10.318, //cal/g.°C // Prepare the data source = { @@ -1473,10 +1476,12 @@ datafields: [ { name: 'step_name', type: 'string' }, { name: 'step_type', type: 'int' }, + { name: 'step_volume', type: 'float' }, { name: 'step_infuse_amount', type: 'float' }, + { name: 'step_infuse_temp', type: 'float' }, { name: 'step_temp', type: 'float' }, { name: 'step_time', type: 'float' }, - { name: 'step_thickness', type: 'float' }, + { name: 'step_wg_ratio', type: 'float' }, { name: 'ramp_time', type: 'float' }, { name: 'end_temp', type: 'float' } ], @@ -1491,7 +1496,7 @@ row = records[i]; if (row.step_type == 0) // Infusion mash_infuse += parseFloat(row.step_infuse_amount); - row.step_thickness = 0; // Init this field. + row.step_wg_ratio = 0; // Init this field. data.push(row); } }, @@ -1514,16 +1519,21 @@ row['step_name'] = 'Stap ' + (rowscount + 1); if (rowscount > 0) { row['step_type'] = 1; + row['step_infuse_amount'] = 0; + row['step_volume'] = mash_infuse; } else { row['step_type'] = 0; row['step_infuse_amount'] = 15; + row['step_volume'] = 15; } + row['step_infuse_temp'] = 0; row['step_temp'] = 62.0; row['step_time'] = 20.0; - row['step_thickness'] = 0; + row['step_wg_ratio'] = 0; row['ramp_time'] = 1.0; row['end_temp'] = 62.0; $('#mashGrid').jqxGrid('addrow', null, row); + calcMash(); }); // delete selected step. $('#sdeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150, disabled: (dataRecord.stage > 3) }); @@ -1533,6 +1543,7 @@ if (selectedrowindex >= 0 && selectedrowindex < rowscount) { id = $('#mashGrid').jqxGrid('getrowid', selectedrowindex); $('#mashGrid').jqxGrid('deleterow', id); + calcMash(); } }); }, @@ -1563,9 +1574,57 @@ { text: 'Eind °C', datafield: 'end_temp', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' }, { text: 'Rust min.', datafield: 'step_time', width: 90, align: 'right', cellsalign: 'right' }, { text: 'Stap min.', datafield: 'ramp_time', width: 90, align: 'right', cellsalign: 'right' }, - { text: 'Infuse L.', datafield: 'step_infuse_amount', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' }, - { text: 'L/Kg.', datafield: 'step_thickness', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f2' }, - { text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center', + { text: 'Inf/dec L.', datafield: 'step_infuse_amount', width: 90, align: 'right', + cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) { + if (rowdata.step_type == 1) + return ''; + var color = '#ffffff'; + var mvol = mashkg * MaltVolume; + if ((rowdata.step_wg_ratio * mashkg + mvol) > dataRecord.eq_tun_volume) + color = '#ff4040'; + return '' + dataAdapter.formatNumber(value, 'f1') + ''; + } + }, + { text: 'Inf/dec °C', datafield: 'step_infuse_temp', width: 90, align: 'right', + cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) { + if (rowdata.step_type == 1) + return ''; + return '' + dataAdapter.formatNumber(value, 'f2') + ''; + } + }, + { text: 'L/Kg.', datafield: 'step_wg_ratio', width: 90, align: 'right', + cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) { + var color = '#ffffff'; + if (value < 2.0 || value > 6.0) + color = '#ff4040'; + return '' + dataAdapter.formatNumber(value, 'f2') + ''; + } + }, + { text: '', columntype: 'button', width: 15, align: 'center', + cellsrenderer: function(row) { + if (row < 2) + return ' '; + return '▴'; + }, buttonclick: function(row) { + if (row >= 2) { + swapMash(row, row-1); + } + } + }, + { text: '', columntype: 'button', width: 15, align: 'center', + cellsrenderer: function(row) { + rowscount = $('#mashGrid').jqxGrid('getdatainformation').rowscount; + if (row < 1 || row > (rowscount -2)) + return ' '; + return '▾'; + }, buttonclick: function(row) { + rowscount = $('#mashGrid').jqxGrid('getdatainformation').rowscount; + if (row >= 1 && row <= (rowscount -2)) { + swapMash(row, row+1); + } + } + }, + { text: '', datafield: 'Edit', columntype: 'button', width: 80, align: 'center', cellsrenderer: function() { return 'Wijzig'; }, buttonclick: function(row) { @@ -1574,19 +1633,30 @@ } else { mashRow = row; mashData = $('#mashGrid').jqxGrid('getrowdata', mashRow); + if (mashRow == 0) + $("#wstep_type").jqxDropDownList('disableAt', 2); + else + $("#wstep_type").jqxDropDownList('enableAt', 2); $('#wstep_name').val(mashData.step_name); $('#wstep_type').val(mashData.step_type); $('#wstep_infuse_amount').val(mashData.step_infuse_amount); + $('#wstep_infuse_temp').val(mashData.step_infuse_temp); $('#wstep_temp').val(mashData.step_temp); $('#wend_temp').val(mashData.end_temp); $('#wstep_time').val(mashData.step_time); $('#wramp_time').val(mashData.ramp_time); + $('#wstep_infuse_amount').hide(); // Hide all untile we need it. + $('#wstep_infuse_temp').hide(); + $('#wstep_pmpt_amount').hide(); + $('#wstep_pmpt_temp').hide(); if (mashData.step_type == 0) { - $('#wstep_infuse_amount').show(); - $('#wstep_pmpt').show(); - } else { - $('#wstep_infuse_amount').hide(); - $('#wstep_pmpt').hide(); + if (mashRow == 0) { + $('#wstep_infuse_amount').show(); + $('#wstep_pmpt_amount').show(); + } else { + $('#wstep_infuse_temp').show(); + $('#wstep_pmpt_temp').show(); + } } // show the popup window. $('#popupMash').jqxWindow('open'); @@ -1619,6 +1689,41 @@ } } + function swapMash(r1, r2) { + + console.log('swap mash rows ' + r1 + ' ' + r2); + var row1 = $('#mashGrid').jqxGrid('getrowdata', r1); + var row2 = $('#mashGrid').jqxGrid('getrowdata', r2); + var obj1 = { step_name: row1.step_name, step_type: row1.step_type, step_volume: row1.step_volume, step_infuse_amount: row1.step_infuse_amount, + step_infuse_temp: row1.step_infuse_temp, step_temp: row1.step_temp, step_time: row1.step_time, + ramp_time: row1.ramp_time, end_temp: row1.end_temp, step_wg_ratio: row1.step_wg_ratio }; + var obj2 = { step_name: row2.step_name, step_type: row2.step_type, step_volume: row2.step_volume, step_infuse_amount: row2.step_infuse_amount, + step_infuse_temp: row2.step_infuse_temp, step_temp: row2.step_temp, step_time: row2.step_time, + ramp_time: row2.ramp_time, end_temp: row2.end_temp, step_wg_ratio: row2.step_wg_ratio }; + $("#mashGrid").jqxGrid('updaterow', r1, obj2); + $("#mashGrid").jqxGrid('updaterow', r2, obj1); + } + + function infusionVol(step_infused, step_mashkg, infuse_temp, step_temp, last_temp) { + var a = last_temp * (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt); + var b = step_temp * (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt); + //console.log('b - a: ' + (b - a) + ' t: ' + ((infuse_temp - step_temp) * SpecificHeatWater)); + //console.log('res: ' + ((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater))); + var vol = Round(((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)), 2); + console.log('infusionVol(' + step_infused + ', ' + step_mashkg + ', ' + infuse_temp + ', ' + step_temp + ', ' + last_temp + '): ' + vol); + return vol; + } + + function decoctionVol(step_volume, step_temp, prev_temp) { + var a = (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + step_volume * SpecificHeatWater) * (step_temp - prev_temp); + var b = SpecificHeatWater * (99 - step_temp); + var vol = 0; + if (b > 0) + vol = Round(a / b, 6); + console.log('decoctionVol(' + step_volume + ', ' + step_temp + ', ' + prev_temp + '): ' + vol); + return vol; + } + function calcViability() { var vpm = 1.00; var max = 100; @@ -1985,15 +2090,52 @@ function calcMash() { - var h, m, infused = 0, mashtime = 0, mashvol = 0, i, row; + var h, m, infused = 0, mashtime = 0, mashvol = 0, vol, i, j, n, a, b, row, temp; + var lasttemp = 18.0; + var graintemp = 18.0; + var tuntemp = 18.0; if ((rows = $('#mashGrid').jqxGrid('getrows')) && (mashkg > 0)) { + console.log('calcMash()'); for (i = 0; i < rows.length; i++) { row = $('#mashGrid').jqxGrid('getrowdata', i); - if (row.step_type == 0) // Infusion + if (row.step_type == 0) { // Infusion + if (i == 0) { + // First mash step, temperature from the mashtun and malt. + n = 20; // tun is preheated. + tuntemp = row.step_temp; + for (j = 0; j < n; j++) { + a = mashkg * graintemp * SpecificHeatMalt + dataRecord.eq_tun_weight * tuntemp * dataRecord.eq_tun_specific_heat; + b = row.step_temp * (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + row.step_infuse_amount * SpecificHeatWater + mashkg * SpecificHeatMalt) - SlakingHeat * mashkg; + if (row.step_infuse_amount > 0) { + temp = (b - a) / (row.step_infuse_amount * SpecificHeatWater); + } else { + temp = 99; + } + tuntemp += (temp - tuntemp) / 2; + row.step_infuse_temp = Round(temp, 6); + } + console.log('init infuse temp: ' + row.step_infuse_temp); + } else { + // Calculate amount of infusion water. + row.step_infuse_amount = infusionVol(infused, mashkg, row.step_infuse_temp, row.step_temp, lasttemp); + //console.log('vol: ' + row.step_infuse_amount + ' temp: ' + row.step_infuse_temp); + } infused += row.step_infuse_amount; + } else if (row.step_type == 1) { // Temperature + if (i > 0) + row.step_infuse_amount = 0; + row.step_infuse_temp = 0; + } else if (row.step_type == 2) { // Decoction + row.step_infuse_amount = decoctionVol(infused, row.step_temp, lasttemp); + row.step_infuse_temp = 99; + } + row.step_volume = infused; + //console.log(i + ' type: ' + row.step_type + ' volume: ' + row.step_infuse_amount + ' temp: ' + row.step_infuse_temp); + lasttemp = row.step_temp; mashtime += row.step_time + row.ramp_time; - $('#mashGrid').jqxGrid('setcellvalue', i, 'step_thickness', infused / mashkg); + row.step_wg_ratio = Round(infused / mashkg, 6); + $('#mashGrid').jqxGrid('updaterow', i, row); } } mashvol = mashkg * MaltVolume + infused; @@ -5399,7 +5541,7 @@ }); $('#mash_select').on('select', function(event) { if (event.args) { - var data, datarecord, rowIDs, rows, i, row, index = event.args.index; + var infused = 0, data, datarecord, rowIDs, rows, i, row, index = event.args.index; // First delete all current steps rowIDs = new Array(); rows = $('#mashGrid').jqxGrid('getdisplayrows'); @@ -5415,22 +5557,29 @@ data = datarecord.steps[i]; row = {}; row['step_name'] = data.step_name; - row['step_type'] = data.step_type; - // For now, but this must be smarter. - if (mash_infuse == 0 && dataRecord.w1_amount > 0) - mash_infuse = dataRecord.w1_amount; - if (i == 0) - row['step_infuse_amount'] = mash_infuse; + row['step_type'] = parseInt(data.step_type); + row['step_temp'] = parseFloat(data.step_temp); + row['end_temp'] = parseFloat(data.end_temp); + row['step_time'] = parseFloat(data.step_time); + row['ramp_time'] = parseFloat(data.ramp_time); + row['step_infuse_temp'] = 0.0; + row['step_infuse_amount'] = 0.0; + if (mash_infuse == 0 && dataRecord.wg_amount > 0) + mash_infuse = dataRecord.wg_amount; + if (data.step_type == 0) { // Infusion + if (i == 0) { + row['step_infuse_amount'] = parseFloat(mash_infuse); + } else { + row['step_infuse_temp'] = 99.0; + } + } + //console.log(i + ' type: ' + row['step_type'] + ' start infusion: ' + parseFloat(row['step_infuse_amount']) + ' mash_infuse: ' + mash_infuse); + infused += parseFloat(row['step_infuse_amount']); + row['step_volume'] = infused; + if (mashkg > 0) + row['step_wg_ratio'] = Round(parseFloat(mash_infuse / mashkg), 2); else - row['step_infuse_amount'] = 0; - if (mashkg > 0) - row['step_thickness'] = parseFloat(mash_infuse / mashkg); - else - row['step_thickness'] = 0; - row['step_temp'] = data.step_temp; - row['end_temp'] = data.end_temp; - row['step_time'] = data.step_time; - row['ramp_time'] = data.ramp_time; + row['step_wg_ratio'] = 0; $('#mashGrid').jqxGrid('addrow', null, row); } calcMash(); @@ -5442,7 +5591,7 @@ $('#est_mashtime').jqxInput({ theme: theme, width: 70, height: 23 }); $('#popupMash').jqxWindow({ width: 800, - height: 350, + height: 375, position: { x: 230, y: 100 }, resizable: false, theme: theme, @@ -5453,12 +5602,15 @@ }); $('#MashReady').jqxButton({ template: 'success', width: '90px', theme: theme }); $('#MashReady').click(function() { + calcFermentables(); + calcWater(); + calcSparge(); calcMash(); }); $('#wstep_name').jqxInput({ theme: theme, width: 320, height: 23 }); $('#wstep_name').on('change', function(event) { var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow); - rowdata.step_name = event.args.value; + rowdata.step_name = $('#wstep_name').val(); }); $('#wstep_type').jqxDropDownList({ theme: theme, @@ -5473,29 +5625,62 @@ if (event.args) { var rowdata, i, rows, row, index = event.args.index; rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow); - rowdata.step_type = index; - if (index == 0) { - $('#wstep_infuse_amount').show(); - $('#wstep_pmpt').show(); - } else { - rowdata.step_infuse_amount = 0; + if (rowdata.step_type != index) { + rowdata.step_type = index; $('#wstep_infuse_amount').hide(); - $('#wstep_pmpt').hide(); - } - mash_infuse = 0; - rows = $('#mashGrid').jqxGrid('getrows'); - for (i = 0; i < rows.length; i++) { - row = rows[i]; - if (row.step_type == 0) // Infusion - mash_infuse += parseFloat(row.step_infuse_amount); + $('#wstep_infuse_temp').hide(); + $('#wstep_pmpt_amount').hide(); + $('#wstep_pmpt_temp').hide(); + if (index == 0) { // Infusion + if (mashRow == 0) { + $('#wstep_infuse_amount').show(); + $('#wstep_pmpt_amount').show(); + } else { + $('#wstep_infuse_temp').show(); + $('#wstep_pmpt_temp').show(); + } + } + if (index == 1) { // Temperature + if (mashRow > 0) + rowdata.step_infuse_amount = 0; + rowdata.step_infuse_temp = 0; + } + if (index == 2) { // Decoction + var rowprev = $('#mashGrid').jqxGrid('getrowdata', mashRow-1); + rowdata.step_infuse_temp = 99; + rowdata.step_infuse_amount = decoctionVol(rowdata.step_volume, rowdata.step_temp, rowprev.end_temp); + console.log('decoction: ' + rowdata.step_infuse_amount + '/' + rowdata.step_infuse_temp); + } + $('#mashGrid').jqxGrid('updaterow', mashRow, rowdata); + mash_infuse = 0; + rows = $('#mashGrid').jqxGrid('getrows'); + for (i = 0; i < rows.length; i++) { + row = rows[i]; + if (row.step_type == 0) // Infusion + mash_infuse += parseFloat(row.step_infuse_amount); + } + calcMash(); } } }); $('#wstep_temp').on('change', function(event) { var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow); - rowdata.step_temp = parseFloat(event.args.value); + if (rowdata.step_type == 2) { // Decoction + var rowprev = $('#mashGrid').jqxGrid('getrowdata', mashRow-1); + var a = (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + rowdata.step_volume * SpecificHeatWater) * + (parseFloat(event.args.value) - rowprev.end_temp); + var b = SpecificHeatWater * (99 - parseFloat(event.args.value)); + if (b > 0) { + rowdata.step_temp = parseFloat(event.args.value); + rowdata.step_infuse_amount = Round(a / b, 2); + } else + rowdata.step_infuse_amount = 0; + console.log('change temp ' + rowdata.step_temp + ' decoction: ' + rowdata.step_infuse_amount + '/' + rowdata.step_infuse_temp); + } else { + rowdata.step_temp = parseFloat(event.args.value); + } }); - $('#wstep_temp,#wend_temp,#wstep_infuse_amount').jqxNumberInput(Spin1dec); + $('#wstep_temp,#wend_temp,#wstep_infuse_amount,#wstep_infuse_temp').jqxNumberInput(Spin1dec); $('#wend_temp').on('change', function(event) { var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow); rowdata.end_temp = parseFloat(event.args.value); @@ -5514,29 +5699,40 @@ $('#wstep_infuse_amount').on('change', function(event) { var row, i, rows, rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow); rowdata.step_infuse_amount = parseFloat(event.args.value); - mash_infuse = 0; - rows = $('#mashGrid').jqxGrid('getrows'); - for (i = 0; i < rows.length; i++) { - row = rows[i]; - if (row.step_type == 0) // Infusion - mash_infuse += parseFloat(row.step_infuse_amount); + if (mashRow == 0) { + rowdata.step_infuse_amount = parseFloat(event.args.value); + mash_infuse = 0; + rows = $('#mashGrid').jqxGrid('getrows'); + for (i = 0; i < rows.length; i++) { + row = rows[i]; + if (row.step_type == 0) // Infusion + mash_infuse += parseFloat(row.step_infuse_amount); + } + if (dataRecord.w2_amount == 0) { + dataRecord.w1_amount = mash_infuse; + $('#w1_amount').val(mash_infuse); + } else { + var w1_amount = (dataRecord.w1_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse; + var w2_amount = (dataRecord.w2_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse; + dataRecord.w1_amount = Round(w1_amount, 3); + dataRecord.w2_amount = Round(w2_amount, 3); + $('#w1_amount').val(dataRecord.w1_amount); + $('#w2_amount').val(dataRecord.w2_amount); + } + $('#wg_amount').val(mash_infuse); + console.log('new infuse amount: ' + mash_infuse); + calcWater(); +// calcMash(); } - if (dataRecord.w2_amount == 0) { - dataRecord.w1_amount = mash_infuse; - $('#w1_amount').val(mash_infuse); - } else { - var w1_amount = (dataRecord.w1_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse; - var w2_amount = (dataRecord.w2_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse; - dataRecord.w1_amount = Round(w1_amount, 3); - dataRecord.w2_amount = Round(w2_amount, 3); - $('#w1_amount').val(dataRecord.w1_amount); - $('#w2_amount').val(dataRecord.w2_amount); - } - $('#wg_amount').val(mash_infuse); - calcFermentables(); - calcWater(); - calcSparge(); - calcMash(); + }); + $('#wstep_infuse_temp').on('change', function(event) { + var prevdata = $('#mashGrid').jqxGrid('getrowdata', mashRow-1); + var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow); + rowdata.step_infuse_temp = parseFloat(event.args.value); + var vol = infusionVol(prevdata.step_volume, mashkg, rowdata.step_infuse_temp, rowdata.step_temp, prevdata.end_temp); + console.log('new vol: ' + vol); + rowdata.step_infuse_amount = vol; + $('#wstep_infuse_amount').val(vol); }); // Tab 8, Water