JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
FGCondition.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGCondition.cpp
4  Author: Jon S. Berndt
5  Date started: 1/2/2003
6 
7  -------------- Copyright (C) 2003 Jon S. Berndt (jon@jsbsim.org) --------------
8 
9  This program is free software; you can redistribute it and/or modify it under
10  the terms of the GNU Lesser General Public License as published by the Free
11  Software Foundation; either version 2 of the License, or (at your option) any
12  later version.
13 
14  This program is distributed in the hope that it will be useful, but WITHOUT
15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17  details.
18 
19  You should have received a copy of the GNU Lesser General Public License along
20  with this program; if not, write to the Free Software Foundation, Inc., 59
21  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 
23  Further information about the GNU Lesser General Public License can also be
24  found on the world wide web at http://www.gnu.org.
25 
26 HISTORY
27 --------------------------------------------------------------------------------
28 
29 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30 COMMENTS, REFERENCES, and NOTES
31 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32 
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 INCLUDES
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
36 
37 #include <iostream>
38 #include <cstdlib>
39 #include <stdexcept>
40 
41 #include "FGCondition.h"
42 #include "FGPropertyValue.h"
43 #include "input_output/FGXMLElement.h"
44 #include "input_output/FGPropertyManager.h"
45 #include "FGParameterValue.h"
46 
47 using namespace std;
48 
49 namespace JSBSim {
50 
51 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 CLASS IMPLEMENTATION
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
54 
55 // This constructor is called when tests are inside an element
56 FGCondition::FGCondition(Element* element, FGPropertyManager* PropertyManager)
57  : Logic(elUndef), TestParam1(nullptr), TestParam2(nullptr),
58  Comparison(ecUndef)
59 {
60  InitializeConditionals();
61 
62  string logic = element->GetAttributeValue("logic");
63  if (!logic.empty()) {
64  if (logic == "OR") Logic = eOR;
65  else if (logic == "AND") Logic = eAND;
66  else { // error
67  cerr << element->ReadFrom()
68  << "Unrecognized LOGIC token " << logic << endl;
69  throw std::invalid_argument("FGCondition: unrecognized logic value:'" + logic + "'");
70  }
71  } else {
72  Logic = eAND; // default
73  }
74 
75  for (unsigned int i=0; i<element->GetNumDataLines(); i++) {
76  string data = element->GetDataLine(i);
77  conditions.push_back(new FGCondition(data, PropertyManager, element));
78  }
79 
80  Element* condition_element = element->GetElement();
81  const string& elName = element->GetName();
82 
83  while (condition_element) {
84  string tagName = condition_element->GetName();
85 
86  if (tagName != elName) {
87  cerr << condition_element->ReadFrom()
88  << "Unrecognized tag <" << tagName << "> in the condition statement."
89  << endl;
90  throw std::invalid_argument("FGCondition: unrecognized tag:'" + tagName + "'");
91  }
92 
93  conditions.push_back(new FGCondition(condition_element, PropertyManager));
94  condition_element = element->GetNextElement();
95  }
96 
97  Debug(0);
98 }
99 
100 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101 // This constructor is called when there are no nested test groups inside the
102 // condition
103 
104 FGCondition::FGCondition(const string& test, FGPropertyManager* PropertyManager,
105  Element* el)
106  : Logic(elUndef), TestParam1(nullptr), TestParam2(nullptr),
107  Comparison(ecUndef)
108 {
109  InitializeConditionals();
110 
111  vector<string> test_strings = split(test, ' ');
112 
113  if (test_strings.size() == 3) {
114  TestParam1 = new FGPropertyValue(test_strings[0], PropertyManager);
115  conditional = test_strings[1];
116  TestParam2 = new FGParameterValue(test_strings[2], PropertyManager);
117  } else {
118  cerr << el->ReadFrom()
119  << " Conditional test is invalid: \"" << test
120  << "\" has " << test_strings.size() << " elements in the "
121  << "test condition." << endl;
122  throw std::invalid_argument("FGCondition: incorrect number of test elements:" + std::to_string(test_strings.size()));
123  }
124 
125  Comparison = mComparison[conditional];
126  if (Comparison == ecUndef) {
127  throw std::invalid_argument("FGCondition: Comparison operator: \""+conditional
128  +"\" does not exist. Please check the conditional.");
129  }
130 }
131 
132 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133 
134 void FGCondition::InitializeConditionals(void)
135 {
136  mComparison["EQ"] = eEQ;
137  mComparison["NE"] = eNE;
138  mComparison["GT"] = eGT;
139  mComparison["GE"] = eGE;
140  mComparison["LT"] = eLT;
141  mComparison["LE"] = eLE;
142  mComparison["eq"] = eEQ;
143  mComparison["ne"] = eNE;
144  mComparison["gt"] = eGT;
145  mComparison["ge"] = eGE;
146  mComparison["lt"] = eLT;
147  mComparison["le"] = eLE;
148  mComparison["=="] = eEQ;
149  mComparison["!="] = eNE;
150  mComparison[">"] = eGT;
151  mComparison[">="] = eGE;
152  mComparison["<"] = eLT;
153  mComparison["<="] = eLE;
154 }
155 
156 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157 
158 FGCondition::~FGCondition(void)
159 {
160  for (auto cond: conditions) delete cond;
161 
162  Debug(1);
163 }
164 
165 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166 
167 bool FGCondition::Evaluate(void )
168 {
169  bool pass = false;
170 
171  if (!TestParam1) {
172 
173  if (Logic == eAND) {
174 
175  pass = true;
176  for (auto cond: conditions) {
177  if (!cond->Evaluate()) pass = false;
178  }
179 
180  } else { // Logic must be eOR
181 
182  pass = false;
183  for (auto cond: conditions) {
184  if (cond->Evaluate()) pass = true;
185  }
186 
187  }
188 
189  } else {
190 
191  double compareValue = TestParam2->GetValue();
192 
193  switch (Comparison) {
194  case ecUndef:
195  cerr << "Undefined comparison operator." << endl;
196  break;
197  case eEQ:
198  pass = TestParam1->getDoubleValue() == compareValue;
199  break;
200  case eNE:
201  pass = TestParam1->getDoubleValue() != compareValue;
202  break;
203  case eGT:
204  pass = TestParam1->getDoubleValue() > compareValue;
205  break;
206  case eGE:
207  pass = TestParam1->getDoubleValue() >= compareValue;
208  break;
209  case eLT:
210  pass = TestParam1->getDoubleValue() < compareValue;
211  break;
212  case eLE:
213  pass = TestParam1->getDoubleValue() <= compareValue;
214  break;
215  default:
216  cerr << "Unknown comparison operator." << endl;
217  }
218  }
219 
220  return pass;
221 }
222 
223 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224 
225 void FGCondition::PrintCondition(string indent)
226 {
227  string scratch;
228 
229  if (!conditions.empty()) {
230 
231  switch(Logic) {
232  case (elUndef):
233  scratch = " UNSET";
234  cerr << "unset logic for test condition" << endl;
235  break;
236  case (eAND):
237  scratch = indent + "if all of the following are true: {";
238  break;
239  case (eOR):
240  scratch = indent + "if any of the following are true: {";
241  break;
242  default:
243  scratch = " UNKNOWN";
244  cerr << "Unknown logic for test condition" << endl;
245  }
246  cout << scratch << endl;
247 
248  for (auto cond: conditions) {
249  cond->PrintCondition(indent + " ");
250  cout << endl;
251  }
252 
253  cout << indent << "}";
254 
255  } else {
256  cout << indent << TestParam1->GetName() << " " << conditional
257  << " " << TestParam2->GetName();
258  }
259 }
260 
261 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
262 // The bitmasked value choices are as follows:
263 // unset: In this case (the default) JSBSim would only print
264 // out the normally expected messages, essentially echoing
265 // the config files as they are read. If the environment
266 // variable is not set, debug_lvl is set to 1 internally
267 // 0: This requests JSBSim not to output any messages
268 // whatsoever.
269 // 1: This value explicity requests the normal JSBSim
270 // startup messages
271 // 2: This value asks for a message to be printed out when
272 // a class is instantiated
273 // 4: When this value is set, a message is displayed when a
274 // FGModel object executes its Run() method
275 // 8: When this value is set, various runtime state variables
276 // are printed out periodically
277 // 16: When set various parameters are sanity checked and
278 // a message is printed out when they go out of bounds
279 
280 void FGCondition::Debug(int from)
281 {
282  if (debug_lvl <= 0) return;
283 
284  if (debug_lvl & 1) { // Standard console startup message output
285  if (from == 0) { // Constructor
286 
287  }
288  }
289  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
290  if (from == 0) cout << "Instantiated: FGCondition" << endl;
291  if (from == 1) cout << "Destroyed: FGCondition" << endl;
292  }
293  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
294  }
295  if (debug_lvl & 8 ) { // Runtime state variables
296  }
297  if (debug_lvl & 16) { // Sanity checking
298  }
299  if (debug_lvl & 64) {
300  if (from == 0) { // Constructor
301  }
302  }
303 }
304 
305 } //namespace JSBSim