www/js/fermenter.js

changeset 678
cc49115e769e
child 680
8b3c86124a08
equal deleted inserted replaced
677:c867eb3f7fc1 678:cc49115e769e
1 /*****************************************************************************
2 * Copyright (C) 2024
3 *
4 * Michiel Broek <mbroek at mbse dot eu>
5 *
6 * This file is part of mbsePi-apps thermferm
7 *
8 * This is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * Brewery Management System istributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with ThermFerm; see the file COPYING. If not, write to the Free
20 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *****************************************************************************/
22
23 function createAbortElements() {
24 $('#eventWindow').jqxWindow({
25 theme: theme,
26 position: { x: 440, y: 210 },
27 width: 400,
28 height: 200,
29 resizable: false,
30 isModal: true,
31 modalOpacity: 0.4,
32 okButton: $('#delOk'),
33 cancelButton: $('#delCancel'),
34 initContent: function() {
35 $('#delOk').jqxButton({ template: 'danger', width: '65px', theme: theme });
36 $('#delCancel').jqxButton({ template: 'success', width: '65px', theme: theme });
37 $('#delCancel').focus();
38 }
39 });
40 $('#eventWindow').jqxWindow('hide');
41 }
42
43
44 $(document).ready(function() {
45
46 var record = {},
47 global = {},
48 blank = {},
49 ppayload = '',
50 yl = 12, // Normal yeast temp range
51 yh = 24,
52
53 gaugeoptions = {
54 min: 0, max: 45, width: 375, height: 375,
55 ranges: [{ startValue: 0, endValue: yl, style: { fill: '#3399FF', stroke: '#3399FF' }, endWidth: 10, startWidth: 10 },
56 { startValue: yl, endValue: yh, style: { fill: '#00CC33', stroke: '#00CC33' }, endWidth: 10, startWidth: 10 },
57 { startValue: yh, endValue: 45, style: { fill: '#FC6A6A', stroke: '#FC6A6A' }, endWidth: 10, startWidth: 10 }],
58 ticksMinor: { interval: 1, size: '5%' },
59 ticksMajor: { interval: 5, size: '9%' },
60 labels: { interval: 5 },
61 style: { fill: '#eeeeee', stroke: '#666666' },
62 value: 0,
63 colorScheme: 'scheme05'
64 },
65 gaugeSmalloptions = {
66 min: -15, max: 25, width: 190, height: 190,
67 ranges: [{ startValue: -15, endValue: 0, startWidth: 5, endWidth: 5, style: { fill: '#3399FF', stroke: '#3399FF' }},
68 { startValue: 0, endValue: 10, startWidth: 5, endWidth: 5, style: { fill: '#00CC33', stroke: '#00CC33' }},
69 { startValue: 10, endValue: 25, startWidth: 5, endWidth: 5, style: { fill: '#FC6A6A', stroke: '#FC6A6A' }}],
70 ticksMinor: { interval: 1, size: '5%' },
71 ticksMajor: { interval: 5, size: '9%' },
72 labels: { interval: 5 },
73 style: { fill: '#eeeeee', stroke: '#666666' },
74 value: 0,
75 colorScheme: 'scheme05',
76 caption: { value: 'Chiller', position: 'bottom', offset: [0, 10] }
77 },
78 switchoptions = {
79 height: 68,
80 width: 35,
81 onLabel: 'ON',
82 offLabel: 'OFF',
83 theme: theme,
84 thumbSize: '50%',
85 orientation: 'vertical'
86 },
87 targetoptions = { inputMode: 'simple', theme: theme, width: 70, min: 0, max: 45, decimalDigits: 1, spinButtons: true },
88
89 globalSource = {
90 datatype: 'json',
91 cache: false,
92 datafields: [
93 { name: 'type' },
94 { name: 'name' },
95 { name: 'node' },
96 { name: 'os' },
97 { name: 'os_version' },
98 { name: 'FW' },
99 { name: 'room_temp', map: 'THB>temperature', type: 'float' },
100 { name: 'room_hum', map: 'THB>humidity', type: 'float' }
101 ],
102 id: 'name',
103 url: 'getglobal.php'
104 },
105 globalData = new $.jqx.dataAdapter(globalSource, {
106 loadComplete: function(records) {
107 global = globalData.records[0];
108 updateScreen();
109 }
110 }),
111 url = 'getfermenter.php?uuid=' + my_uuid,
112 source = {
113 datatype: 'json',
114 datafields: [
115 { name: 'type' },
116 { name: 'unit' },
117 { name: 'beercode', map: 'metric>product>code' },
118 { name: 'beername', map: 'metric>product>name' },
119 { name: 'yeast_lo', map: 'metric>product>yeast_lo' },
120 { name: 'yeast_hi', map: 'metric>product>yeast_hi' },
121 { name: 'air_state', map: 'metric>air>state' },
122 { name: 'air_temperature', map: 'metric>air>temperature' },
123 { name: 'beer_state', map: 'metric>beer>state' },
124 { name: 'beer_temperature', map: 'metric>beer>temperature' },
125 { name: 'chiller_state', map: 'metric>chiller>state' },
126 { name: 'chiller_temperature', map: 'metric>chiller>temperature' },
127 { name: 'heater_state', map: 'metric>heater>state' },
128 { name: 'heater_usage', map: 'metric>heater>usage' },
129 { name: 'cooler_state', map: 'metric>cooler>state' },
130 { name: 'cooler_usage', map: 'metric>cooler>usage' },
131 { name: 'fan_state', map: 'metric>fan>state' },
132 { name: 'fan_usage', map: 'metric>fan>usage' },
133 { name: 'light_address', map: 'metric>light>address' },
134 { name: 'light_state', map: 'metric>light>state' },
135 { name: 'light_usage', map: 'metric>light>usage' },
136 { name: 'door_address', map: 'metric>door>address' },
137 { name: 'door_state', map: 'metric>door>state' },
138 { name: 'psu_address', map: 'metric>psu>address' },
139 { name: 'psu_state', map: 'metric>psu>state' },
140 { name: 'mode', map: 'metric>mode' },
141 { name: 'alarm', map: 'metric>alarm', type: 'int' },
142 { name: 'setpoint_high', map: 'metric>setpoint>high' },
143 { name: 'setpoint_low', map: 'metric>setpoint>low' },
144 { name: 'profile_uuid', type: 'string' },
145 { name: 'profile_name', type: 'string' },
146 { name: 'profile_state', type: 'string' },
147 { name: 'profile_percent', type: 'int' },
148 { name: 'profile_inittemp_high', type: 'float' },
149 { name: 'profile_inittemp_low', type: 'float' },
150 { name: 'profile_steps', type: 'string' },
151 { name: 'stage', map: 'metric>stage', type: 'string' },
152 { name: 'beeruuid', map: 'metric>product>uuid' }
153 ],
154 id: 'alias',
155 url: url
156 },
157 dataAdapter = new $.jqx.dataAdapter(source, {
158 loadComplete: function(records) {
159 record = dataAdapter.records[0];
160 updateScreen();
161 }
162 });
163
164 function updateScreen() {
165 $('#room_thb').html(global.room_temp + '&deg;C&nbsp;&nbsp;' + global.room_hum + '% humidity');
166 $('#info_system').html(record.unit);
167 $('#info_beer').html(record.beercode + ' - ' + record.beername);
168 $('#info_mode').jqxDropDownList('selectItem', record.mode);
169 $('#info_stage').jqxDropDownList('selectItem', record.stage);
170 if (record.door_address) {
171 if (record.door_state != '0') {
172 $('#fermenter_doorled').html('<div class="LEDyellow_on"></div>Door');
173 } else {
174 $('#fermenter_doorled').html('<div class="LEDyellow_off"></div>Door');
175 }
176 }
177 if (record.light_address) {
178 if (record.light_state != '0') {
179 $('#fermenter_lightled').html('<div class="LEDyellow_on"></div>Light');
180 } else {
181 $('#fermenter_lightled').html('<div class="LEDyellow_off"></div>Light');
182 }
183 }
184 if (record.mode != 'OFF') {
185 $('#fermenter_powerled').html('<div class="LEDblue_on"></div>Power');
186 } else {
187 $('#fermenter_powerled').html('<div class="LEDblue_off"></div>Power');
188 }
189 if (record.alarm != '0') {
190 $('#fermenter_alarmled').html('<div class="LEDred_on"></div>Alarm');
191 } else {
192 $('#fermenter_alarmled').html('<div class="LEDred_off"></div>Alarm');
193 }
194
195 $('#target_lo').val(record.setpoint_low);
196 $('#target_hi').val(record.setpoint_high);
197 if ((record.mode == 'FRIDGE') || (record.mode == 'BEER')) {
198 $('#target_lo').jqxNumberInput({ readOnly: false, Width: 70, spinButtons: true });
199 $('#target_hi').jqxNumberInput({ readOnly: false, Width: 70, spinButtons: true });
200 } else {
201 $('#target_lo').jqxNumberInput({ readOnly: true, Width: 50, spinButtons: false });
202 $('#target_hi').jqxNumberInput({ readOnly: true, Width: 50, spinButtons: false });
203 }
204
205 $('.f_control_leds').show();
206 if (record.heater_state != '0') {
207 $('#fermenter_led1').html('<div class="LEDgreen_on"></div>Heat');
208 } else {
209 $('#fermenter_led1').html('<div class="LEDgreen_off"></div>Heat');
210 }
211 if (record.cooler_state != '0') {
212 $('#fermenter_led2').html('<div class="LEDgreen_on"></div>Cool');
213 } else {
214 $('#fermenter_led2').html('<div class="LEDgreen_off"></div>Cool');
215 }
216 if (record.fan_state != '0') {
217 $('#fermenter_led3').html('<div class="LEDgreen_on"></div>Fan');
218 } else {
219 $('#fermenter_led3').html('<div class="LEDgreen_off"></div>Fan');
220 }
221
222 if (record.mode == 'NONE') {
223 $('.f_control_switches').show();
224 } else {
225 $('.f_control_switches').hide();
226 }
227 if ((record.heater_state != '0') != $('#fermenter_toggle1').jqxSwitchButton('val'))
228 $('#fermenter_toggle1').val(record.heater_state != '0');
229 if ((record.cooler_state != '0') != $('#fermenter_toggle2').jqxSwitchButton('val'))
230 $('#fermenter_toggle2').val(record.cooler_state != '0');
231 if ((record.fan_state != '0') != $('#fermenter_toggle3').jqxSwitchButton('val'))
232 $('#fermenter_toggle3').val(record.fan_state != '0');
233
234 $('#info_profile').html(record.profile_name);
235 if (record.profile_name == '')
236 $('#info_mode').jqxDropDownList('disableItem', 'PROFILE');
237 else
238 $('#info_mode').jqxDropDownList('enableItem', 'PROFILE');
239
240 if (record.mode == 'PROFILE') {
241 if (record.profile_state == 'OFF') {
242 $('#info_mode').jqxDropDownList({ disabled: false });
243 $('#Profile1').jqxButton({ template: 'success', value: 'Starten' });
244 $('#Profile1').show();
245 $('#Profile2').hide();
246 $('#status_profile').html('');
247 } else if (record.profile_state == 'RUN') {
248 $('#info_mode').jqxDropDownList({ disabled: true });
249 $('#Profile1').jqxButton({ template: 'danger', value: 'Afbreken' });
250 $('#Profile2').jqxButton({ template: 'primary', value: 'Pauze' });
251 $('#Profile1').show();
252 $('#Profile2').show();
253 $('#status_profile').html('Profiel actief, ' + record.profile_percent + '% gereed');
254 } else if (record.profile_state == 'PAUSE') {
255 $('#info_mode').jqxDropDownList({ disabled: true });
256 $('#Profile1').jqxButton({ template: 'danger', value: 'Afbreken' });
257 $('#Profile2').jqxButton({ template: 'success', value: 'Doorgaan' });
258 $('#Profile1').show();
259 $('#Profile2').show();
260 $('#status_profile').html('Profiel pauze, ' + record.profile_percent + '% gereed');
261 } else if (record.profile_state == 'DONE') {
262 $('#info_mode').jqxDropDownList({ disabled: true });
263 $('#Profile1').jqxButton({ template: 'primary', value: 'Profiel Ok' });
264 $('#Profile1').show();
265 $('#Profile2').hide();
266 $('#status_profile').html('Profiel is gereed');
267 }
268 } else {
269 $('#info_mode').jqxDropDownList({ disabled: false });
270 $('#Profile1').hide();
271 $('#Profile2').hide();
272 $('#status_profile').html('');
273 }
274
275 var yl = record.yeast_lo;
276 var yh = record.yeast_hi;
277 var range = { ranges: [{ startValue: 0, endValue: yl, style: { fill: '#3399FF', stroke: '#3399FF' }, endWidth: 10, startWidth: 10 },
278 { startValue: yl, endValue: yh, style: { fill: '#00CC33', stroke: '#00CC33' }, endWidth: 10, startWidth: 10 },
279 { startValue: yh, endValue: 45, style: { fill: '#FC6A6A', stroke: '#FC6A6A' }, endWidth: 10, startWidth: 10 }]};
280 $('#gaugeContainer_air').jqxGauge(range);
281 $('#gaugeContainer_beer').jqxGauge(range);
282
283 if (record.air_temperature !== undefined) {
284 $('#gaugeContainer_air').jqxGauge({ caption: { value: 'Air: ' + record.air_temperature.toFixed(3) }});
285 $('#gaugeContainer_air').jqxGauge({ value: record.air_temperature });
286 }
287 if (record.air_state == 'OK') {
288 $('#gaugeContainer_air').jqxGauge({ disabled: false });
289 } else {
290 $('#gaugeContainer_air').jqxGauge({ disabled: true });
291 }
292 if (record.beer_temperature !== undefined) {
293 $('#gaugeContainer_beer').jqxGauge({ caption: { value: 'Beer: ' + record.beer_temperature.toFixed(3) }});
294 $('#gaugeContainer_beer').jqxGauge({ value: record.beer_temperature });
295 }
296 if (record.beer_state == 'OK') {
297 $('#gaugeContainer_beer').jqxGauge({ disabled: false });
298 } else {
299 $('#gaugeContainer_beer').jqxGauge({ disabled: true });
300 }
301 if (record.chiller_temperature !== undefined) {
302 $('#gaugeContainer_chiller').jqxGauge({ value: record.chiller_temperature });
303 }
304 if (record.chiller_state == 'OK') {
305 $('#gaugeContainer_chiller').jqxGauge({ disabled: false });
306 } else {
307 $('#gaugeContainer_chiller').jqxGauge({ disabled: true });
308 }
309 }
310
311 $('#gaugeContainer_air').jqxGauge(gaugeoptions);
312 $('#gaugeContainer_air').jqxGauge({ caption: { value: 'Air: 00.000' }});
313 $('#gaugeContainer_beer').jqxGauge(gaugeoptions);
314 $('#gaugeContainer_beer').jqxGauge({ caption: { value: 'Beer: 00.000' }});
315 $('#gaugeContainer_chiller').jqxGauge(gaugeSmalloptions);
316
317 $('#fermenter_toggle1').jqxSwitchButton(switchoptions);
318 $('#fermenter_toggle2').jqxSwitchButton(switchoptions);
319 $('#fermenter_toggle3').jqxSwitchButton(switchoptions);
320
321 srcMode = ['OFF', 'NONE', 'FRIDGE', 'BEER', 'PROFILE'];
322 srcStage = ['PRIMARY', 'SECONDARY', 'TERTIARY', 'CARBONATION'];
323 $('#info_mode').jqxDropDownList({ theme: theme, source: srcMode, width: 100, height: 24, dropDownHeight: 156 });
324 $('#info_stage').jqxDropDownList({ theme: theme, source: srcStage, width: 150, height: 24, dropDownHeight: 125 });
325
326 $('#target_lo').jqxNumberInput(targetoptions);
327 $('#target_hi').jqxNumberInput(targetoptions);
328
329 $('#Profile1').jqxButton({ template: 'info', width: '150px', height: 24, theme: theme });
330 $('#Profile2').jqxButton({ template: 'info', width: '150px', height: 24, theme: theme });
331 $('#Profile1').hide(); // Hide these until they are needed.
332 $('#Profile2').hide();
333
334 // Get the data immediatly and then at regular intervals to refresh.
335 dataAdapter.dataBind();
336 globalData.dataBind();
337
338 $('#info_mode').on('select', function(event) {
339 if (event.args && event.args.item.value != record.mode) {
340 record.mode = event.args.item.value;
341 console.log('set mode ' + record.mode);
342 var msg = '{"type":"fermenter","unit":"' + record.unit + '","mode":"' + record.mode + '"}';
343 websocket.send(msg);
344 }
345 });
346 $('#info_stage').on('select', function(event) {
347 if (event.args && event.args.item.value != record.stage) {
348 record.stage = event.args.item.value;
349 console.log('set stage ' + record.stage);
350 var msg = '{"type":"fermenter","unit":"' + record.unit + '","stage":"' + record.stage + '"}';
351 websocket.send(msg);
352 }
353 });
354
355 $('#target_lo').on('change', function(event) {
356 record.setpoint_low = parseFloat(event.args.value);
357 // Keep the high target above the low.
358 if (record.setpoint_low > record.setpoint_high) {
359 record.setpoint_high = record.setpoint_low;
360 $('#target_hi').val(record.setpoint_high);
361 }
362 console.log('set setpoints ' + record.setpoint_low + ' ' + record.setpoint_high);
363 websocket.send('{"type":"fermenter","unit":"' + record.unit +
364 '","setpoint_low":' + record.setpoint_low + ',"setpoint_high":' + record.setpoint_high + '}');
365 });
366 $('#target_hi').on('change', function(event) {
367 record.setpoint_high = parseFloat(event.args.value);
368 // Keep the low target below the high.
369 if (record.setpoint_high < record.setpoint_low) {
370 record.setpoint_low = record.setpoint_high;
371 $('#target_lo').val(record.setpoint_low);
372 }
373 console.log('set setpoints ' + record.setpoint_low + ' ' + record.setpoint_high);
374 websocket.send('{"type":"fermenter","unit":"' + record.unit +
375 '","setpoint_low":' + record.setpoint_low + ',"setpoint_high":' + record.setpoint_high + '}');
376 });
377
378 $('#fermenter_toggle1').on('checked', function(event) {
379 if (record.mode == 'NONE' && record.heater_state != 0) {
380 console.log('set heater ' + $("#fermenter_toggle1").jqxSwitchButton('val'));
381 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","heater_state":0}');
382 }
383 });
384 $('#fermenter_toggle1').on('unchecked', function(event) {
385 if (record.mode == 'NONE' && record.heater_state == 0) {
386 console.log('set heater ' + $("#fermenter_toggle1").jqxSwitchButton('val'));
387 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","heater_state":100,"cooler_state":0}');
388 }
389 });
390 $('#fermenter_toggle2').on('checked', function(event) {
391 if (record.mode == 'NONE' && record.cooler_state != 0) {
392 console.log('set cooler ' + $("#fermenter_toggle2").jqxSwitchButton('val'));
393 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","cooler_state":0}');
394 }
395 });
396 $('#fermenter_toggle2').on('unchecked', function(event) {
397 if (record.mode == 'NONE' & record.cooler_state == 0) {
398 console.log('set cooler ' + $("#fermenter_toggle2").jqxSwitchButton('val'));
399 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","cooler_state":100,"heater_state":0}');
400 }
401 });
402 $('#fermenter_toggle3').on('checked', function(event) {
403 if (record.mode == 'NONE' && record.fan_state != 0) {
404 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","fan_state":0}');
405 }
406 });
407 $('#fermenter_toggle3').on('unchecked', function(event) {
408 if (record.mode == 'NONE' && record.fan_state == 0) {
409 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","fan_state":100}');
410 }
411 });
412 $('#Profile1').click(function() {
413 if (record.mode == 'PROFILE') {
414 if (record.profile_state == 'OFF') {
415 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","profile":{"command":"start"}}');
416 } else if ((record.profile_state == 'RUN') || (record.profile_state == 'PAUSE')) {
417 // Open a popup to confirm this action.
418 $('#eventWindow').jqxWindow('open');
419 $('#delOk').click(function() {
420 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","profile":{"command":"abort"}}');
421 });
422 } else if (record.profile_state == 'DONE') {
423 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","profile":{"command":"done"}}');
424 }
425 }
426 });
427 $('#Profile2').click(function() {
428 if (record.mode == 'PROFILE') {
429 if ((record.profile_state == 'RUN') || (record.profile_state == 'PAUSE')) {
430 websocket.send('{"type":"fermenter","unit":"' + record.unit + '","profile":{"command":"pause"}}');
431 }
432 }
433 });
434
435 createAbortElements();
436
437 websocket.onmessage = function(evt) {
438 var msg = evt.data;
439 var obj = JSON.parse(msg);
440
441 console.log('ws got ' + msg);
442
443 if (obj.ping == 1) {
444 console.log('ws got ping');
445 websocket.send('{"pong":1}');
446 }
447
448 if (obj.type == 'fermenter' && obj.unit == record.unit) {
449 console.log('ws got this device ' + msg);
450 record.beeruuid = obj.metric.product.uuid;
451 record.beercode = obj.metric.product.code;
452 record.beername = obj.metric.product.name;
453 record.yeast_lo = obj.metric.product.yeast_lo;
454 record.yeast_hi = obj.metric.product.yeast_hi;
455 record.air_state = obj.metric.air.state;
456 record.air_temperature = obj.metric.air.temperature;
457 record.beer_state = obj.metric.beer.state;
458 record.beer_temperature = obj.metric.beer.temperature;
459 record.chiller_state = obj.metric.chiller.state;
460 record.chiller_temperature = obj.metric.chiller.temperature;
461 if (obj.metric.heater.state !== undefined)
462 record.heater_state = obj.metric.heater.state;
463 if (obj.metric.cooler.state !== undefined)
464 record.cooler_state = obj.metric.cooler.state;
465 if (obj.metric.fan.state !== undefined)
466 record.fan_state = obj.metric.fan.state;
467 if (obj.metric.door)
468 record.door_state = obj.metric.door.state;
469 if (obj.metric.light)
470 record.light_state = obj.metric.light.state;
471 if (obj.metric.psu)
472 record.psu_state = obj.metric.psu.state;
473 record.mode = obj.metric.mode;
474 record.stage = obj.metric.stage;
475 record.alarm = obj.metric.alarm;
476 record.setpoint_low = obj.metric.setpoint.low;
477 record.setpoint_high = obj.metric.setpoint.high;
478 if (obj.profile) {
479 record.profile_uuid = obj.profile_uuid;
480 record.profile_name = obj.profile_name;
481 record.profile_state = obj.profile_state;
482 record.profile_percent = obj.profile_percent;
483 record.profile_inittemp_high = obj.profile_inittemp_high;
484 record.profile_inittemp_low = obj.profile_inittemp_low;
485 } else {
486 record.profile_uuid = '';
487 record.profile_name = '';
488 record.profile_state = '';
489 record.profile_percent = 0;
490 }
491 updateScreen();
492 }
493 }
494 });

mercurial