www/js/prod_edit.js

changeset 667
1246550451ca
parent 662
4bb005694ce7
child 669
1a1cf5430f43
--- 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 &deg;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 '<span></span>';
+       var color = '#ffffff';
+       var mvol = mashkg * MaltVolume;
+       if ((rowdata.step_wg_ratio * mashkg + mvol) > dataRecord.eq_tun_volume)
+        color = '#ff4040';
+       return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + dataAdapter.formatNumber(value, 'f1') + '</span>';
+      }
+    },
+    { text: 'Inf/dec &deg;C', datafield: 'step_infuse_temp', width: 90, align: 'right',
+      cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
+       if (rowdata.step_type == 1)
+        return '<span></span>';
+       return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(value, 'f2') + '</span>';
+      }
+    },
+    { 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 '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + dataAdapter.formatNumber(value, 'f2') + '</span>';
+      }
+    },
+    { 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

mercurial