Version 0.3.34 Changed log messages in the daemon. Added yeast_pitchrate field to the product database. Added yeast pitchrate edit field to the yeast tab. Initial value is guessed. There is also a popup window for predefined values. An finally the value can be edited by the user. All popup windows are initialized together.

Sun, 31 May 2020 12:23:01 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 31 May 2020 12:23:01 +0200
changeset 697
367ae7ff52f0
parent 696
f4de55f587c1
child 698
c7b4cb53b37c

Version 0.3.34 Changed log messages in the daemon. Added yeast_pitchrate field to the product database. Added yeast pitchrate edit field to the yeast tab. Initial value is guessed. There is also a popup window for predefined values. An finally the value can be edited by the user. All popup windows are initialized together.

bmsd/websocket.c file | annotate | diff | comparison | revisions
config.status file | annotate | diff | comparison | revisions
configure file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
www/includes/db_product.php file | annotate | diff | comparison | revisions
www/js/global.js file | annotate | diff | comparison | revisions
www/js/prod_edit.js file | annotate | diff | comparison | revisions
www/prod_edit.php file | annotate | diff | comparison | revisions
--- a/bmsd/websocket.c	Fri May 29 09:17:52 2020 +0200
+++ b/bmsd/websocket.c	Sun May 31 12:23:01 2020 +0200
@@ -77,14 +77,13 @@
 
 	case LWS_CALLBACK_ESTABLISHED: {
 		ws_clients++;
-		syslog(LOG_NOTICE, "ws: new connection, total %d", ws_clients);
 		pss->ringbuffer_tail = ringbuffer_head;
                 pss->wsi = wsi;
 		break;
 	}
 
 	case LWS_CALLBACK_PROTOCOL_DESTROY:
-		syslog(LOG_NOTICE, "ws: protocol cleaning up");
+		syslog(LOG_NOTICE, "Websocket: protocol cleaning up");
                 for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
                     if (ringbuffer[n].payload)
                 	free(ringbuffer[n].payload);
@@ -133,7 +132,6 @@
 
 	case LWS_CALLBACK_CLOSED:
 		ws_clients--;
-		syslog(LOG_NOTICE, "ws: connection closed, left %d", ws_clients);
 		break;
 
 	default:
@@ -222,8 +220,9 @@
 
     if (context == NULL) {
 	syslog(LOG_NOTICE, "libwebsocket_create_context() failed");
+	return (void *)1;
     }
-    syslog(LOG_NOTICE, "Websocket server port %d started", info.port);
+    syslog(LOG_NOTICE, "Websocket: server started port %d", info.port);
 
     /*
      * Loop forever until external shutdown variable is set.
@@ -234,8 +233,8 @@
     }
     lws_context_destroy(context);
 
-    syslog(LOG_NOTICE, "Thread ws_loop stopped");
-    return 0;
+    syslog(LOG_NOTICE, "Websocket: server stopped");
+    return (void *)0;
 }
 
 
--- a/config.status	Fri May 29 09:17:52 2020 +0200
+++ b/config.status	Sun May 31 12:23:01 2020 +0200
@@ -621,7 +621,7 @@
 S["CC"]="gcc"
 S["CYEARS"]="2016-2020"
 S["COPYRIGHT"]="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved"
-S["VERSION"]="0.3.33"
+S["VERSION"]="0.3.34"
 S["PACKAGE"]="bms"
 S["SUBDIRS"]="bmsd doc script tools www"
 S["target_alias"]=""
@@ -710,7 +710,7 @@
 D["PACKAGE_STRING"]=" \"\""
 D["PACKAGE_BUGREPORT"]=" \"\""
 D["PACKAGE_URL"]=" \"\""
-D["VERSION"]=" \"0.3.33\""
+D["VERSION"]=" \"0.3.34\""
 D["COPYRIGHT"]=" \"Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved\""
 D["STDC_HEADERS"]=" 1"
 D["HAVE_SYS_TYPES_H"]=" 1"
--- a/configure	Fri May 29 09:17:52 2020 +0200
+++ b/configure	Sun May 31 12:23:01 2020 +0200
@@ -2043,7 +2043,7 @@
 
 
 PACKAGE="bms"
-VERSION="0.3.33"
+VERSION="0.3.34"
 COPYRIGHT="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved"
 CYEARS="2016-2020"
 
--- a/configure.ac	Fri May 29 09:17:52 2020 +0200
+++ b/configure.ac	Sun May 31 12:23:01 2020 +0200
@@ -8,7 +8,7 @@
 dnl General settings
 dnl After changeing the version number, run autoconf!
 PACKAGE="bms"
-VERSION="0.3.33"
+VERSION="0.3.34"
 COPYRIGHT="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved"
 CYEARS="2016-2020"
 AC_SUBST(PACKAGE)
--- a/www/includes/db_product.php	Fri May 29 09:17:52 2020 +0200
+++ b/www/includes/db_product.php	Sun May 31 12:23:01 2020 +0200
@@ -269,7 +269,8 @@
                 $sql .= "', yeast_prod_date=NULL";
         else
                 $sql .= "', yeast_prod_date='" . $_POST['yeast_prod_date'] . "'";
-	$sql .=  ", divide_type='" . $_POST['divide_type'];
+	$sql .=  ", yeast_pitchrate='" . floatval($_POST['yeast_pitchrate']);
+	$sql .= "', divide_type='" . $_POST['divide_type'];
 	$sql .= "', divide_size='" . floatval($_POST['divide_size']);
 	$sql .= "', divide_factor='" . floatval($_POST['divide_factor']);
 	$sql .= "', divide_parts='" . $_POST['divide_parts'];
@@ -881,7 +882,8 @@
 		$brew .= ',"starter_sg":' . $row['starter_sg'];
 		$brew .= ',"starter_viability":' . $row['starter_viability'];
 		$brew .= ',"yeast_prod_date":"' . $row['yeast_prod_date'];
-		$brew .= '","prop1_type":' . $row['prop1_type'];
+		$brew .= '","yeast_pitchrate":' . $row['yeast_pitchrate'];
+		$brew .= ',"prop1_type":' . $row['prop1_type'];
 		$brew .= ',"prop1_volume":' . $row['prop1_volume'];
 		$brew .= ',"prop2_type":' . $row['prop2_type'];
 		$brew .= ',"prop2_volume":' . $row['prop2_volume'];
--- a/www/js/global.js	Fri May 29 09:17:52 2020 +0200
+++ b/www/js/global.js	Sun May 31 12:23:01 2020 +0200
@@ -247,6 +247,22 @@
 },
 StarterTypeAdapter = new $.jqx.dataAdapter(StarterTypeSource),
 
+PitchrateData = [
+ { rate: 0,     nl: ' - Kies factor - ' },
+ { rate: 0.75,  nl: 'Ale, 0.75' },
+ { rate: 1.0,   nl: 'Ale, 1.00 zwaar &gt; 1.060' },
+ { rate: 1.25,  nl: 'Ale, 1.25 zwaar &gt; 1.076' },
+ { rate: 1.5,   nl: 'Lager 1.50' },
+ { rate: 2.0,   nl: 'Lager, 2.00 zwaar &gt; 1.060' },
+ { rate: 0.075, nl: 'Kveik, 0.075 origineel' }
+],
+PitchrateSource = {
+ localdata: PitchrateData,
+ datatype: 'array',
+ datafields: [{ name: 'rate' }, { name: 'nl' }]
+},
+PitchrateAdapter = new $.jqx.dataAdapter(PitchrateSource),
+
 MiscTypeData = [
  { id: 0, en: 'Spice', nl: 'Specerij' },
  { id: 1, en: 'Herb', nl: 'Kruid' },
--- a/www/js/prod_edit.js	Fri May 29 09:17:52 2020 +0200
+++ b/www/js/prod_edit.js	Sun May 31 12:23:01 2020 +0200
@@ -21,7 +21,7 @@
  *****************************************************************************/
 
 
-function createDelElements() {
+function createPopupElements() {
 
  $('#eventWindow').jqxWindow({
   theme: theme,
@@ -40,7 +40,6 @@
    $('#delCancel').focus();
   }
  });
- $('#eventWindow').jqxWindow('hide');
 
  $('#volumeWindow').jqxWindow({
   theme: theme,
@@ -53,7 +52,78 @@
   autoOpen: false,
   cancelButton: $('#volumeReady')
  });
- $('#volumeWindow').jqxWindow('hide');
+
+ $('#pitchrateWindow').jqxWindow({
+  theme: theme,
+  position: { x: 330, y: 210 },
+  width: 600,
+  height: 200,
+  resizable: false,
+  isModal: true,
+  modalOpacity: 0.4,
+  autoOpen: false,
+  cancelButton: $('#pitchrateReady')
+ });
+
+ $('#popupFermentable').jqxWindow({
+  width: 800,
+  height: 300,
+  position: { x: 230, y: 100 },
+  resizable: false,
+  theme: theme,
+  isModal: true,
+  autoOpen: false,
+  cancelButton: $('#FermentableReady'),
+  modalOpacity: 0.40
+ });
+
+ $('#popupHop').jqxWindow({
+  width: 800,
+  height: 300,
+  position: { x: 230, y: 100 },
+  resizable: false,
+  theme: theme,
+  isModal: true,
+  autoOpen: false,
+  cancelButton: $('#HopReady'),
+  modalOpacity: 0.40
+ });
+
+ $('#popupMisc').jqxWindow({
+  width: 800,
+  height: 275,
+  position: { x: 230, y: 100 },
+  resizable: false,
+  theme: theme,
+  isModal: true,
+  autoOpen: false,
+  cancelButton: $('#MiscReady'),
+  modalOpacity: 0.40
+ });
+
+ $('#popupYeast').jqxWindow({
+  width: 800,
+  height: 300,
+  position: { x: 230, y: 100 },
+  resizable: false,
+  theme: theme,
+  isModal: true,
+  autoOpen: false,
+  cancelButton: $('#YeastReady'),
+  modalOpacity: 0.40
+ });
+
+ $('#popupMash').jqxWindow({
+  width: 800,
+  height: 375,
+  position: { x: 230, y: 100 },
+  resizable: false,
+  theme: theme,
+  isModal: true,
+  autoOpen: false,
+  cancelButton: $('#MashReady'),
+  modalOpacity: 0.40
+ });
 }
 
 
@@ -69,7 +139,6 @@
  pcara = 0,           // Percentage cara/crystal malts
  svg = 77,            // Default attenuation
  mashkg = 0,          // Malt in mash weight
- pitchrate = 0.75,    // Yeast pitch rate default
  initcells = 0,       // Initial yeast cell count
 
  ok_fermentables = 1, // Fermentables are in stock
@@ -325,6 +394,7 @@
    { name: 'starter_sg', type: 'float' },
    { name: 'starter_viability', type: 'int' },
    { name: 'yeast_prod_date', type: 'string' },
+   { name: 'yeast_pitchrate', type: 'float' },
    { name: 'prop1_type', type: 'int' },
    { name: 'prop1_volume', type: 'float' },
    { name: 'prop2_type', type: 'int' },
@@ -555,6 +625,7 @@
    $('#starter_sg').val(dataRecord.starter_sg);
    $('#starter_viability').val(dataRecord.starter_viability);
    $('#yeast_prod_date').val(dataRecord.yeast_prod_date);
+   $('#yeast_pitchrate').val(dataRecord.yeast_pitchrate);
    $('#prop1_type').val(dataRecord.prop1_type);
    $('#prop1_volume').val(dataRecord.prop1_volume);
    $('#prop2_type').val(dataRecord.prop2_type);
@@ -2195,7 +2266,7 @@
   if (volume <= 0)
    volume = dataRecord.batch_size - dataRecord.eq_trub_chiller_loss;
 
-  return pitchrate * volume * plato;
+  return dataRecord.yeast_pitchrate * volume * plato;
  }
 
  function hopFlavourContribution(bt, vol, use, amount) {
@@ -2568,6 +2639,7 @@
    return; // no yeast in recipe
 
   var maybe_starter = 0;
+  var pitchrate = 0.75;    // Yeast pitch rate default
   for (i = 0; i < rowscount; i++) {
    row = $('#yeastGrid').jqxGrid('getrowdata', i);
    if (row.y_use == 0) { // primary
@@ -2584,6 +2656,10 @@
      if (dataRecord.est_og > 1.060)
       pitchrate = 1.0;
     }
+    if (dataRecord.yeast_pitchrate < 0.01) {
+     dataRecord.yeast_pitchrate = pitchrate;
+     $('#yeast_pitchrate').val(pitchrate);
+    }
 
     if (row.y_form != 1) { // Not dry yeast
      maybe_starter = 1;
@@ -2591,12 +2667,12 @@
    }
   }
 
-  needed = Round(pitchrate * volume * plato, 1);
+  needed = Round(dataRecord.yeast_pitchrate * volume * plato, 1);
   $('#need_cells').val(needed);
   use_cells = initcells;
   if (needed <= initcells)
    maybe_starter = 0;
-  console.log('calcYeast() pitchrate:' + pitchrate + ' start:' + initcells + ' needed:' + needed + ' volume:' + volume + ' maybe_starter:' + maybe_starter);
+  console.log('calcYeast() pitchrate:' + dataRecord.yeast_pitchrate + ' start:' + initcells + ' needed:' + needed + ' volume:' + volume + ' maybe_starter:' + maybe_starter);
 
   if (maybe_starter != dataRecord.starter_enable) {
    dataRecord.starter_enable = maybe_starter;
@@ -3731,6 +3807,8 @@
    $('#starter_try').jqxButton({ disabled: true });
    $('#starter_sg').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
    $('#yeast_prod_date').jqxDateTimeInput({ disabled: true });
+   $('#yeast_pitchrate').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
+   $('#but_pickpitchrate').jqxButton({ disabled: true });
   }
   if (dataRecord.stage > 3) { // Primary fermentation done
    $('#brew_date_start').jqxDateTimeInput({ disabled: true });
@@ -4620,6 +4698,7 @@
    starter_sg: parseFloat($('#starter_sg').jqxNumberInput('decimal')),
    starter_viability: parseFloat($('#starter_viability').jqxNumberInput('decimal')),
    yeast_prod_date: $('#yeast_prod_date').val(),
+   yeast_pitchrate: parseFloat($('#yeast_pitchrate').jqxNumberInput('decimal')),
    prop1_type: $('#prop1_type').val(),
    prop1_volume: parseFloat($('#prop1_volume').jqxNumberInput('decimal')),
    prop2_type: $('#prop2_type').val(),
@@ -4818,17 +4897,6 @@
   colorRanges: [{ stop: 30, color: '#FF0000' },{ stop: 40, color: '#EB7331' },{ stop: 200, color: '#008C00' }],
   renderText: function(text) { return (parseInt(text) * 2) + ' lintner'; }
  });
- $('#popupFermentable').jqxWindow({
-  width: 800,
-  height: 300,
-  position: { x: 230, y: 100 },
-  resizable: false,
-  theme: theme,
-  isModal: true,
-  autoOpen: false,
-  cancelButton: $('#FermentableReady'),
-  modalOpacity: 0.40
- });
  $('#FermentableReady').jqxButton({ template: 'success', width: '90px', theme: theme });
  $('#FermentableReady').click(function() {
   var row, rowID = $('#fermentableGrid').jqxGrid('getrowid', fermentableRow);
@@ -5084,17 +5152,6 @@
     return 'Zeer veel';
   }
  });
- $('#popupHop').jqxWindow({
-  width: 800,
-  height: 300,
-  position: { x: 230, y: 100 },
-  resizable: false,
-  theme: theme,
-  isModal: true,
-  autoOpen: false,
-  cancelButton: $('#HopReady'),
-  modalOpacity: 0.40
- });
  $('#HopReady').jqxButton({ template: 'success', width: '90px', theme: theme });
  $('#HopReady').click(function() {
   var row, rowID = $('#hopGrid').jqxGrid('getrowid', hopRow);
@@ -5233,17 +5290,6 @@
  });
 
  // Tab 5, Miscs
- $('#popupMisc').jqxWindow({
-  width: 800,
-  height: 275,
-  position: { x: 230, y: 100 },
-  resizable: false,
-  theme: theme,
-  isModal: true,
-  autoOpen: false,
-  cancelButton: $('#MiscReady'),
-  modalOpacity: 0.40
- });
  $('#MiscReady').jqxButton({ template: 'success', width: '90px', theme: theme });
  $('#MiscReady').click(function() {
   var row, rowID = $('#miscGrid').jqxGrid('getrowid', miscRow);
@@ -5345,9 +5391,12 @@
  $('#need_cells').jqxTooltip({ content: 'Het aantal miljard nodige cellen is afhankelijk van het begin SG, biertype en volume.' });
  $('#plato_cells').jqxTooltip({ content: 'De berekende pitchrate in miljard cellen per ml per graad Plato.' });
  $('#yeast_prod_date').jqxTooltip({ content: 'Bij korrelgisten is meestal "best voor" datum op het zakje gedrukt.<br>Gebruik die datum maar dan twee jaar eerder als productie datum.<br>Bij White Labs is de productie datum vier maanden voor de "Best by" datum die geprint op het buisje.<br>Bij Wyeast is dit de "manufacture date" die op het pak geprint is.<br>Voor schuine buis, slurry, opkweek en gedroogd is dit de datum dat je de gist geoogst hebt.' });
+ $('#yeast_pitchrate').jqxTooltip({ content: 'De gewenste pitchrate in miljard cellen per ml per graad Plato voor de vergisting van dit bier.' });
 
  $('#est_fg2,#plato_cells').jqxNumberInput(Show3dec);
+ $('#est_fg2').jqxNumberInput({ width: 70 });
  $('#est_abv2').jqxNumberInput(Show2dec);
+ $('#est_abv2').jqxNumberInput({ width: 70, symbol: '%', symbolPosition: 'right' });
  $('#yeast_cells,#need_cells').jqxNumberInput(Show1dec);
  $('#yeast_prod_date').jqxDateTimeInput(Dateopts);
  $('#yeast_prod_date').on('close', function(event) {
@@ -5355,16 +5404,37 @@
   calcFermentables();
   calcYeast();
  });
- $('#popupYeast').jqxWindow({
-  width: 800,
-  height: 300,
-  position: { x: 230, y: 100 },
-  resizable: false,
+ $('#yeast_pitchrate').jqxNumberInput(Spin3dec);
+ $('#yeast_pitchrate').on('change', function(event) {
+  dataRecord.yeast_pitchrate = parseFloat(event.args.value);
+  calcViability();
+  calcFermentables();
+  calcYeast();
+ });
+ $('#but_pickpitchrate').jqxButton({ template: 'success', width: '23px', height: 23, theme: theme });
+ $('#but_pickpitchrate').bind('click', function() {
+  $('#pick_pitchrate').val(0); // Set default pick incase no perfect match.
+  $('#pick_pitchrate').val(dataRecord.yeast_pitchrate);
+  $('#pitchrateWindow').jqxWindow('open');
+ });
+ $('#pick_pitchrate').jqxDropDownList({
   theme: theme,
-  isModal: true,
-  autoOpen: false,
-  cancelButton: $('#YeastReady'),
-  modalOpacity: 0.40
+  source: PitchrateAdapter,
+  valueMember: 'rate',
+  displayMember: 'nl',
+  width: 275,
+  height: 23,
+  autoDropDownHeight: true,
+  dropDownVerticalAlignment: 'top'
+ });
+ $('#pitchrateReady').jqxButton({ template: 'success', width: '90px', theme: theme });
+ $('#pitchrateReady').click(function() {
+  console.log('pitchrateReady ' + $('#pick_pitchrate').val() );
+  dataRecord.yeast_pitchrate = parseFloat($('#pick_pitchrate').val());
+  $('#yeast_pitchrate').val(dataRecord.yeast_pitchrate);
+  calcViability();
+  calcFermentables();
+  calcYeast();
  });
  $('#YeastReady').jqxButton({ template: 'success', width: '90px', theme: theme });
  $('#YeastReady').click(function() {
@@ -5587,17 +5657,6 @@
  $('#est_mashvol').jqxNumberInput(Show1dec);
  $('#est_mashtime').jqxTooltip({ content: 'De totale tijdsduur van het maischen.' });
  $('#est_mashtime').jqxInput({ theme: theme, width: 70, height: 23 });
- $('#popupMash').jqxWindow({
-  width: 800,
-  height: 375,
-  position: { x: 230, y: 100 },
-  resizable: false,
-  theme: theme,
-  isModal: true,
-  autoOpen: false,
-  cancelButton: $('#MashReady'),
-  modalOpacity: 0.40
- });
  $('#MashReady').jqxButton({ template: 'success', width: '90px', theme: theme });
  $('#MashReady').click(function() {
   calcFermentables();
@@ -6289,6 +6348,6 @@
  $('#Save').bind('click', function() {
   saveRecord(1);
  });
- createDelElements();
+ createPopupElements();
 });
 
--- a/www/prod_edit.php	Fri May 29 09:17:52 2020 +0200
+++ b/www/prod_edit.php	Sun May 31 12:23:01 2020 +0200
@@ -259,8 +259,8 @@
      <div style="overflow: hidden;">
       <table style="width: 100%;">
        <tr>
-        <td style="vertical-align: top; float: right; padding: 3px;">Verwacht eind SG:</td>
-        <td style="padding: 3px;"><div id="est_fg2"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Verwacht FG en ABV:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_fg2"></div><div style="float: left; margin-left: 10px;" id="est_abv2"></div></td>
         <td align="center" colspan="5" rowspan="5">
          <div id="propagator">
           <table style="width: 100%;">
@@ -315,10 +315,6 @@
         </td>
        </tr>
        <tr>
-        <td style="vertical-align: top; float: right; padding: 3px;">Verwacht ABV %:</td>
-        <td style="padding: 3px;"><div id="est_abv2"></div></td>
-       </tr>
-       <tr>
         <td style="vertical-align: top; float: right; padding: 3px;">Gist productie datum:</td>
         <td style="padding: 3px;"><div id="yeast_prod_date"></div></td>
        </tr>
@@ -331,6 +327,10 @@
         <td style="padding: 3px;"><div id="yeast_cells"></div></td>
        </tr>
        <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Pitch rate:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="yeast_pitchrate"></div><input type="button" id="but_pickpitchrate" value="?" style="margin-left: 10px" /></td>
+       </tr>
+       <tr>
         <td style="vertical-align: top; float: right; padding: 3px;">Cellen nodig miljard:</td>
         <td style="padding: 3px;"><div id="need_cells"></div></td>
        </tr>
@@ -1127,6 +1127,25 @@
     </div>
    </div>
 
+   <div id="pitchrateWindow">
+    <div>Kies een voorgedefinieerde pitchrate.</div>
+    <div style="overflow: hidden;">
+     <table style="width: 100%; padding-top: 20px;">
+      <col width="50%">
+      <col width="50%">
+      <tr>
+       <td align="right" style="vertical-align: top;">Pitchrate in miljard cellen/ml/&deg;P:</td>
+       <td style="padding: 3px;"><div id="pick_pitchrate" /></div></td>
+      </tr>
+      <tr>
+       <td style="padding-top: 50px;" colspan="2" align="center">
+        <input id="pitchrateReady" type="button" value="Ok" />
+       </td>
+      </tr>
+     </table>
+    </div>
+   </div>
+
 <?php
 confirm_delete();
 page_footer();

mercurial