~scottydelicious666/brewtarget/brewtarget

« back to all changes in this revision

Viewing changes to src/equipment.cpp

  • Committer: Philip Greggory Lee
  • Date: 2009-08-23 16:53:43 UTC
  • Revision ID: git-v1:f8d1a25135bd92f06c46c562293800e4faa42c61
Made a src/ and ui/ directory and moved everything.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * equipment.cpp is part of Brewtarget, and is Copyright Philip G. Lee
 
3
 * (rocketman768@gmail.com), 2009.
 
4
 *
 
5
 * Brewtarget is free software: you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation, either version 3 of the License, or
 
8
 * (at your option) any later version.
 
9
 
 
10
 * Brewtarget is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#include <iostream>
 
20
#include <string>
 
21
#include <vector>
 
22
#include "xmlnode.h"
 
23
#include "stringparsing.h"
 
24
#include "equipment.h"
 
25
#include "brewtarget.h"
 
26
#include <QDomElement>
 
27
#include <QDomText>
 
28
 
 
29
bool operator<(Equipment &e1, Equipment &e2)
 
30
{
 
31
   return e1.name < e2.name;
 
32
}
 
33
 
 
34
bool operator==(Equipment &e1, Equipment &e2)
 
35
{
 
36
   return e1.name == e2.name;
 
37
}
 
38
 
 
39
void Equipment::toXml(QDomDocument& doc, QDomNode& parent)
 
40
{
 
41
   QDomElement equipNode;
 
42
   QDomElement tmpNode;
 
43
   QDomText tmpText;
 
44
   
 
45
   equipNode = doc.createElement("EQUIPMENT");
 
46
   
 
47
   tmpNode = doc.createElement("NAME");
 
48
   tmpText = doc.createTextNode(name.c_str());
 
49
   tmpNode.appendChild(tmpText);
 
50
   equipNode.appendChild(tmpNode);
 
51
   
 
52
   tmpNode = doc.createElement("VERSION");
 
53
   tmpText = doc.createTextNode(text(version));
 
54
   tmpNode.appendChild(tmpText);
 
55
   equipNode.appendChild(tmpNode);
 
56
   
 
57
   tmpNode = doc.createElement("BOIL_SIZE");
 
58
   tmpText = doc.createTextNode(text(boilSize_l));
 
59
   tmpNode.appendChild(tmpText);
 
60
   equipNode.appendChild(tmpNode);
 
61
   
 
62
   tmpNode = doc.createElement("BATCH_SIZE");
 
63
   tmpText = doc.createTextNode(text(batchSize_l));
 
64
   tmpNode.appendChild(tmpText);
 
65
   equipNode.appendChild(tmpNode);
 
66
   
 
67
   tmpNode = doc.createElement("TUN_VOLUME");
 
68
   tmpText = doc.createTextNode(text(tunVolume_l));
 
69
   tmpNode.appendChild(tmpText);
 
70
   equipNode.appendChild(tmpNode);
 
71
   
 
72
   tmpNode = doc.createElement("TUN_WEIGHT");
 
73
   tmpText = doc.createTextNode(text(tunWeight_kg));
 
74
   tmpNode.appendChild(tmpText);
 
75
   equipNode.appendChild(tmpNode);
 
76
   
 
77
   tmpNode = doc.createElement("TUN_SPECIFIC_HEAT");
 
78
   tmpText = doc.createTextNode(text(tunSpecificHeat_calGC));
 
79
   tmpNode.appendChild(tmpText);
 
80
   equipNode.appendChild(tmpNode);
 
81
   
 
82
   tmpNode = doc.createElement("TOP_UP_WATER");
 
83
   tmpText = doc.createTextNode(text(topUpWater_l));
 
84
   tmpNode.appendChild(tmpText);
 
85
   equipNode.appendChild(tmpNode);
 
86
   
 
87
   tmpNode = doc.createElement("TRUB_CHILLER_LOSS");
 
88
   tmpText = doc.createTextNode(text(trubChillerLoss_l));
 
89
   tmpNode.appendChild(tmpText);
 
90
   equipNode.appendChild(tmpNode);
 
91
   
 
92
   tmpNode = doc.createElement("EVAP_RATE");
 
93
   tmpText = doc.createTextNode(text(evapRate_pctHr));
 
94
   tmpNode.appendChild(tmpText);
 
95
   equipNode.appendChild(tmpNode);
 
96
   
 
97
   tmpNode = doc.createElement("BOIL_TIME");
 
98
   tmpText = doc.createTextNode(text(boilTime_min));
 
99
   tmpNode.appendChild(tmpText);
 
100
   equipNode.appendChild(tmpNode);
 
101
   
 
102
   tmpNode = doc.createElement("CALC_BOIL_VOLUME");
 
103
   tmpText = doc.createTextNode(text(calcBoilVolume));
 
104
   tmpNode.appendChild(tmpText);
 
105
   equipNode.appendChild(tmpNode);
 
106
   
 
107
   tmpNode = doc.createElement("LAUTER_DEADSPACE");
 
108
   tmpText = doc.createTextNode(text(lauterDeadspace_l));
 
109
   tmpNode.appendChild(tmpText);
 
110
   equipNode.appendChild(tmpNode);
 
111
   
 
112
   tmpNode = doc.createElement("TOP_UP_KETTLE");
 
113
   tmpText = doc.createTextNode(text(topUpKettle_l));
 
114
   tmpNode.appendChild(tmpText);
 
115
   equipNode.appendChild(tmpNode);
 
116
   
 
117
   tmpNode = doc.createElement("HOP_UTILIZATION");
 
118
   tmpText = doc.createTextNode(text(hopUtilization_pct));
 
119
   tmpNode.appendChild(tmpText);
 
120
   equipNode.appendChild(tmpNode);
 
121
   
 
122
   tmpNode = doc.createElement("NOTES");
 
123
   tmpText = doc.createTextNode(notes.c_str());
 
124
   tmpNode.appendChild(tmpText);
 
125
   equipNode.appendChild(tmpNode);
 
126
   
 
127
   parent.appendChild(equipNode);
 
128
}
 
129
 
 
130
//=============================CONSTRUCTORS=====================================
 
131
 
 
132
void Equipment::setDefaults()
 
133
{
 
134
   name = "";
 
135
   boilSize_l = 0.0;
 
136
   batchSize_l = 0.0;
 
137
   tunVolume_l = 0.0;
 
138
   tunWeight_kg = 0.0;
 
139
   tunSpecificHeat_calGC = 0.0;
 
140
   topUpWater_l = 0.0;
 
141
   trubChillerLoss_l = 0.0;
 
142
   evapRate_pctHr = 0.0;
 
143
   boilTime_min = 0.0;
 
144
   calcBoilVolume = false;
 
145
   lauterDeadspace_l = 0.0;
 
146
   topUpKettle_l = 0.0;
 
147
   hopUtilization_pct = 0.0;
 
148
   notes = "";
 
149
}
 
150
 
 
151
Equipment::Equipment()
 
152
{
 
153
   setDefaults();
 
154
}
 
155
 
 
156
Equipment::Equipment(XmlNode *node)
 
157
{
 
158
   std::vector<XmlNode *> children;
 
159
   std::vector<XmlNode *> tmpVec;
 
160
   std::string tag;
 
161
   std::string leafText;
 
162
   XmlNode* leaf;
 
163
   unsigned int i, childrenSize;
 
164
   bool hasName=false, hasVersion=false, hasBoilSize=false, hasBatchSize=false;
 
165
   
 
166
   setDefaults();
 
167
   
 
168
   if( node->getTag() != "EQUIPMENT" )
 
169
      throw EquipmentException("initializer not passed an EQUIPMENT node.");
 
170
   
 
171
   node->getChildren( children );
 
172
   childrenSize = children.size();
 
173
   
 
174
   for( i = 0; i < childrenSize; ++i )
 
175
   {
 
176
      tag = children[i]->getTag();
 
177
      children[i]->getChildren( tmpVec );
 
178
      
 
179
      // All valid children of EQUIPMENT only have one or fewer children.
 
180
      if( tmpVec.size() > 1 )
 
181
         throw EquipmentException("Tag \""+tag+"\" has more than one child.");
 
182
      
 
183
      leaf = tmpVec[0];
 
184
      // It must be a leaf if it is a valid BeerXML entry.
 
185
      if( ! leaf->isLeaf() )
 
186
         throw EquipmentException("Should have been a leaf but is not.");
 
187
      
 
188
      leafText = leaf->getLeafText();
 
189
 
 
190
      // TODO: in all elements, make sure that an exception thrown by a parse
 
191
      // method gets handled when the element is not required.
 
192
      if( tag == "NAME" )
 
193
      {
 
194
         setName(leafText);
 
195
         hasName = true;
 
196
      }
 
197
      else if( tag == "VERSION" )
 
198
      {
 
199
         if( parseInt(leafText) != version )
 
200
            std::cerr << "Warning: XML EQUIPMENT version is not " << version << std::endl;
 
201
         hasVersion = true;
 
202
      }
 
203
      else if( tag == "BOIL_SIZE" )
 
204
      {
 
205
         setBoilSize_l(parseDouble(leafText));
 
206
         hasBoilSize=true;
 
207
      }
 
208
      else if( tag == "BATCH_SIZE" )
 
209
      {
 
210
         setBatchSize_l(parseDouble(leafText));
 
211
         hasBatchSize=true;
 
212
      }
 
213
      else if( tag == "TUN_VOLUME" )
 
214
         setTunVolume_l(parseDouble(leafText));
 
215
      else if( tag == "TUN_WEIGHT" )
 
216
         setTunWeight_kg(parseDouble(leafText));
 
217
      else if( tag == "TUN_SPECIFIC_HEAT" )
 
218
         setTunSpecificHeat_calGC(parseDouble(leafText));
 
219
      else if( tag == "TOP_UP_WATER" )
 
220
         setTopUpWater_l(parseDouble(leafText));
 
221
      else if( tag == "TRUB_CHILLER_LOSS" )
 
222
         setTrubChillerLoss_l(parseDouble(leafText));
 
223
      else if( tag == "EVAP_RATE" )
 
224
         setEvapRate_pctHr(parseDouble(leafText));
 
225
      else if( tag == "BOIL_TIME" )
 
226
         setBoilTime_min(parseDouble(leafText));
 
227
      else if( tag == "CALC_BOIL_VOLUME" )
 
228
         setCalcBoilVolume(parseBool(leafText));
 
229
      else if( tag == "LAUTER_DEADSPACE" )
 
230
         setLauterDeadspace_l(parseDouble(leafText));
 
231
      else if( tag == "TOP_UP_KETTLE" )
 
232
         setTopUpKettle_l(parseDouble(leafText));
 
233
      else if( tag == "HOP_UTILIZATION" )
 
234
         setHopUtilization_pct(parseDouble(leafText));
 
235
      else if( tag == "NOTES" )
 
236
         setNotes(leafText);
 
237
      else
 
238
         std::cerr << "Warning: Unsupported EQUIPMENT tag: " << tag << std::endl;
 
239
   } // end for
 
240
   
 
241
   if( !hasName || !hasVersion || !hasBoilSize || !hasBatchSize )
 
242
      throw EquipmentException("missing required tag.");
 
243
} // end Equipment()
 
244
 
 
245
Equipment::Equipment(const QDomNode& equipmentNode)
 
246
{
 
247
   QDomNode node, child;
 
248
   QDomText textNode;
 
249
   QString property, value;
 
250
 
 
251
   setDefaults();
 
252
 
 
253
   for( node = equipmentNode.firstChild(); ! node.isNull(); node = node.nextSibling() )
 
254
   {
 
255
      if( ! node.isElement() )
 
256
      {
 
257
         Brewtarget::log(Brewtarget::WARNING, QString("Node at line %1 is not an element.").arg(textNode.lineNumber()) );
 
258
         continue;
 
259
      }
 
260
 
 
261
      child = node.firstChild();
 
262
      if( child.isNull() || ! child.isText() )
 
263
         continue;
 
264
 
 
265
      property = node.nodeName();
 
266
      textNode = child.toText();
 
267
      value = textNode.nodeValue();
 
268
 
 
269
      if( property == "NAME" )
 
270
      {
 
271
         name = value.toStdString();
 
272
      }
 
273
      else if( property == "VERSION" )
 
274
      {
 
275
         if( version != getInt(textNode) )
 
276
            Brewtarget::log(Brewtarget::ERROR, QString("EQUIPMENT says it is not version %1. Line %2").arg(version).arg(textNode.lineNumber()) );
 
277
      }
 
278
      else if( property == "BOIL_SIZE" )
 
279
      {
 
280
         setBoilSize_l(getDouble(textNode));
 
281
      }
 
282
      else if( property == "BATCH_SIZE" )
 
283
      {
 
284
         setBatchSize_l(getDouble(textNode));
 
285
      }
 
286
      else if( property == "TUN_VOLUME" )
 
287
      {
 
288
         setTunVolume_l(getDouble(textNode));
 
289
      }
 
290
      else if( property == "TUN_WEIGHT" )
 
291
      {
 
292
         setTunWeight_kg(getDouble(textNode));
 
293
      }
 
294
      else if( property == "TUN_SPECIFIC_HEAT" )
 
295
      {
 
296
         setTunSpecificHeat_calGC(getDouble(textNode));
 
297
      }
 
298
      else if( property == "TOP_UP_WATER" )
 
299
      {
 
300
         setTopUpWater_l(getDouble(textNode));
 
301
      }
 
302
      else if( property == "TRUB_CHILLER_LOSS" )
 
303
      {
 
304
         setTrubChillerLoss_l(getDouble(textNode));
 
305
      }
 
306
      else if( property == "EVAP_RATE" )
 
307
      {
 
308
         setEvapRate_pctHr(getDouble(textNode));
 
309
      }
 
310
      else if( property == "BOIL_TIME" )
 
311
      {
 
312
         setBoilTime_min(getDouble(textNode));
 
313
      }
 
314
      else if( property == "CALC_BOIL_VOLUME" )
 
315
      {
 
316
         setCalcBoilVolume(getBool(textNode));
 
317
      }
 
318
      else if( property == "LAUTER_DEADSPACE" )
 
319
      {
 
320
         setLauterDeadspace_l(getDouble(textNode));
 
321
      }
 
322
      else if( property == "TOP_UP_KETTLE" )
 
323
      {
 
324
         setTopUpKettle_l(getDouble(textNode));
 
325
      }
 
326
      else if( property == "HOP_UTILIZATION" )
 
327
      {
 
328
         setHopUtilization_pct(getDouble(textNode));
 
329
      }
 
330
      else if( property == "NOTES" )
 
331
      {
 
332
         setNotes(value.toStdString());
 
333
      }
 
334
      else
 
335
         Brewtarget::log(Brewtarget::WARNING, QString("Unsupported EQUIPMENT property: %1. Line %2").arg(property).arg(node.lineNumber()) );
 
336
   }
 
337
}
 
338
 
 
339
//============================"SET" METHODS=====================================
 
340
 
 
341
void Equipment::setName( const std::string &var )
 
342
{
 
343
   name = std::string(var);
 
344
   hasChanged();
 
345
}
 
346
 
 
347
void Equipment::setBoilSize_l( double var )
 
348
{
 
349
   if( var < 0.0 )
 
350
      throw EquipmentException("boil size cannot be negative: " + doubleToString(var) );
 
351
   else
 
352
   {
 
353
      boilSize_l = var;
 
354
      hasChanged();
 
355
   }
 
356
}
 
357
 
 
358
void Equipment::setBatchSize_l( double var )
 
359
{
 
360
   if( var < 0.0 )
 
361
      throw EquipmentException( "batch size cannot be negative: " + doubleToString(var) );
 
362
   else
 
363
   {
 
364
      batchSize_l = var;
 
365
      hasChanged();
 
366
      doCalculations();
 
367
   }
 
368
}
 
369
 
 
370
void Equipment::setTunVolume_l( double var )
 
371
{
 
372
   if( var < 0.0 )
 
373
      throw EquipmentException( "tun volume cannot be negative: " + doubleToString(var) );
 
374
   else
 
375
   {
 
376
      tunVolume_l = var;
 
377
      hasChanged();
 
378
   }
 
379
}
 
380
 
 
381
void Equipment::setTunWeight_kg( double var )
 
382
{
 
383
   if( var < 0.0 )
 
384
      throw EquipmentException( "tun weight cannot be negative: " + doubleToString(var) );
 
385
   else
 
386
   {
 
387
      tunWeight_kg = var;
 
388
      hasChanged();
 
389
   }
 
390
}
 
391
 
 
392
void Equipment::setTunSpecificHeat_calGC( double var )
 
393
{
 
394
   if( var < 0.0 )
 
395
      throw EquipmentException( "tun specific heat cannot be negative: " + doubleToString(var) );
 
396
   else
 
397
   {
 
398
      tunSpecificHeat_calGC = var;
 
399
      hasChanged();
 
400
   }
 
401
}
 
402
 
 
403
void Equipment::setTopUpWater_l( double var )
 
404
{
 
405
   if( var < 0.0 )
 
406
      throw EquipmentException( "top up water cannot be negative: " + doubleToString(var) );
 
407
   else
 
408
   {
 
409
      topUpWater_l = var;
 
410
      hasChanged();
 
411
      doCalculations();
 
412
   }
 
413
}
 
414
 
 
415
void Equipment::setTrubChillerLoss_l( double var )
 
416
{
 
417
   if( var < 0.0 )
 
418
      throw EquipmentException( "trub chiller loss cannot be negative: " + doubleToString(var) );
 
419
   else
 
420
   {
 
421
      trubChillerLoss_l = var;
 
422
      hasChanged();
 
423
      doCalculations();
 
424
   }
 
425
}
 
426
 
 
427
void Equipment::setEvapRate_pctHr( double var )
 
428
{
 
429
   if( var < 0.0 || var > 100.0)
 
430
      throw EquipmentException( "evap rate must be a percent: " + doubleToString(var) );
 
431
   else
 
432
   {
 
433
      evapRate_pctHr = var;
 
434
      hasChanged();
 
435
      doCalculations();
 
436
   }
 
437
}
 
438
 
 
439
void Equipment::setBoilTime_min( double var )
 
440
{
 
441
   if( var < 0.0 )
 
442
      throw EquipmentException( "boil time cannot be negative: " + doubleToString(var) );
 
443
   else
 
444
   {
 
445
      boilTime_min = var;
 
446
      hasChanged();
 
447
      doCalculations();
 
448
   }
 
449
}
 
450
 
 
451
void Equipment::setCalcBoilVolume( bool var )
 
452
{
 
453
   calcBoilVolume = var;
 
454
   hasChanged();
 
455
   if( var )
 
456
      doCalculations();
 
457
}
 
458
 
 
459
void Equipment::setLauterDeadspace_l( double var )
 
460
{
 
461
   if( var < 0.0 )
 
462
      throw EquipmentException( "lauter deadspace cannot be negative: " + doubleToString(var) );
 
463
   else
 
464
   {
 
465
      lauterDeadspace_l = var;
 
466
      hasChanged();
 
467
   }
 
468
}
 
469
 
 
470
void Equipment::setTopUpKettle_l( double var )
 
471
{
 
472
   if( var < 0.0 )
 
473
      throw EquipmentException( "top up kettle cannot be negative: " + doubleToString(var) );
 
474
   else
 
475
   {
 
476
      topUpKettle_l = var;
 
477
      hasChanged();
 
478
   }
 
479
}
 
480
 
 
481
void Equipment::setHopUtilization_pct( double var )
 
482
{
 
483
   if( var < 0.0 || var > 100.0 )
 
484
      throw EquipmentException( "hop utilization must be a percentage: " + doubleToString(var) );
 
485
   else
 
486
   {
 
487
      hopUtilization_pct = var;
 
488
      hasChanged();
 
489
   }
 
490
}
 
491
 
 
492
void Equipment::setNotes( const std::string &var )
 
493
{
 
494
   notes = std::string(var);
 
495
   hasChanged();
 
496
}
 
497
 
 
498
//============================"GET" METHODS=====================================
 
499
 
 
500
std::string Equipment::getName() const
 
501
{
 
502
   return name;
 
503
}
 
504
 
 
505
double Equipment::getBoilSize_l() const
 
506
{
 
507
   return boilSize_l;
 
508
}
 
509
 
 
510
double Equipment::getBatchSize_l() const
 
511
{
 
512
   return batchSize_l;
 
513
}
 
514
 
 
515
double Equipment::getTunVolume_l() const
 
516
{
 
517
   return tunVolume_l;
 
518
}
 
519
 
 
520
double Equipment::getTunWeight_kg() const
 
521
{
 
522
   return tunWeight_kg;
 
523
}
 
524
 
 
525
double Equipment::getTunSpecificHeat_calGC() const
 
526
{
 
527
   return tunSpecificHeat_calGC;
 
528
}
 
529
 
 
530
double Equipment::getTopUpWater_l() const
 
531
{
 
532
   return topUpWater_l;
 
533
}
 
534
 
 
535
double Equipment::getTrubChillerLoss_l() const
 
536
{
 
537
   return trubChillerLoss_l;
 
538
}
 
539
 
 
540
double Equipment::getEvapRate_pctHr() const
 
541
{
 
542
   return evapRate_pctHr;
 
543
}
 
544
 
 
545
double Equipment::getBoilTime_min() const
 
546
{
 
547
   return boilTime_min;
 
548
}
 
549
 
 
550
bool Equipment::getCalcBoilVolume() const
 
551
{
 
552
   return calcBoilVolume;
 
553
}
 
554
 
 
555
double Equipment::getLauterDeadspace_l() const
 
556
{
 
557
   return lauterDeadspace_l;
 
558
}
 
559
 
 
560
double Equipment::getTopUpKettle_l() const
 
561
{
 
562
   return topUpKettle_l;
 
563
}
 
564
 
 
565
double Equipment::getHopUtilization_pct() const
 
566
{
 
567
   return hopUtilization_pct;
 
568
}
 
569
 
 
570
std::string Equipment::getNotes() const
 
571
{
 
572
   return notes;
 
573
}
 
574
 
 
575
//TODO: take a look at evapRate_pctHr.
 
576
void Equipment::doCalculations()
 
577
{
 
578
   // Only do the calculation if we're asked to.
 
579
   if( ! calcBoilVolume )
 
580
      return;
 
581
   
 
582
   /* The equation given the BeerXML 1.0 spec was way wrong. */
 
583
   boilSize_l =
 
584
      (batchSize_l - topUpWater_l + trubChillerLoss_l)
 
585
      / (1 - (boilTime_min/(double)60) * (evapRate_pctHr/(double)100) );
 
586
 
 
587
   hasChanged();
 
588
}
 
589
 
 
590
double Equipment::wortEndOfBoil_l( double kettleWort_l ) const
 
591
{
 
592
   return kettleWort_l * (1 - (boilTime_min/(double)60) * (evapRate_pctHr/(double)100) );
 
593
}