]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/input_output/FGScript.cpp
Update to the latest version of JSBSim which supports Lighter Than Air craft
[flightgear.git] / src / FDM / JSBSim / input_output / FGScript.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGScript.cpp
4  Author:       Jon S. Berndt
5  Date started: 12/21/01
6  Purpose:      Loads and runs JSBSim scripts.
7
8  ------------- Copyright (C) 1999  Jon S. Berndt (jsb@hal-pc.org) -------------
9
10  This program is free software; you can redistribute it and/or modify it under
11  the terms of the GNU Lesser General Public License as published by the Free Software
12  Foundation; either version 2 of the License, or (at your option) any later
13  version.
14
15  This program is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
18  details.
19
20  You should have received a copy of the GNU Lesser General Public License along with
21  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22  Place - Suite 330, Boston, MA  02111-1307, USA.
23
24  Further information about the GNU Lesser General Public License can also be found on
25  the world wide web at http://www.gnu.org.
26
27 FUNCTIONAL DESCRIPTION
28 --------------------------------------------------------------------------------
29
30 This class wraps up the simulation scripting routines.
31
32 HISTORY
33 --------------------------------------------------------------------------------
34 12/21/01   JSB   Created
35
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 COMMENTS, REFERENCES,  and NOTES
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 INCLUDES
42 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
43
44 #ifdef FGFS
45 #  include <simgear/compiler.h>
46 #  include STL_IOSTREAM
47 #  include STL_ITERATOR
48 #else
49 #  if defined(sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
50 #    include <iostream.h>
51 #  else
52 #    include <iostream>
53 #  endif
54 #  include <iterator>
55 #endif
56
57 #include "FGScript.h"
58 #include <input_output/FGXMLParse.h>
59 #include <initialization/FGTrim.h>
60
61 namespace JSBSim {
62
63 static const char *IdSrc = "$Id$";
64 static const char *IdHdr = ID_FGSCRIPT;
65
66 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
67 GLOBAL DECLARATIONS
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
69
70 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71 CLASS IMPLEMENTATION
72 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
73
74 // Constructor
75
76 FGScript::FGScript(FGFDMExec* fgex) : FDMExec(fgex)
77 {
78   State = FDMExec->GetState();
79   PropertyManager=FDMExec->GetPropertyManager();
80   Debug(0);
81 }
82
83 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84
85 FGScript::~FGScript()
86 {
87   unsigned int i;
88
89   for (i=0; i<local_properties.size(); i++) delete local_properties[i];
90   local_properties.clear();
91
92   for (i=0; i<Events.size(); i++) delete Events[i].Condition;
93   Events.clear();
94
95   Debug(1);
96 }
97
98 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99
100 bool FGScript::LoadScript( string script )
101 {
102   string aircraft="", initialize="", comparison = "", prop_name="";
103   string notifyPropertyName="";
104   Element *element=0, *run_element=0, *event_element=0;
105   Element *condition_element=0, *set_element=0, *delay_element=0;
106   Element *notify_element = 0L, *notify_property_element = 0L;
107   Element *property_element = 0L;
108   Element *output_element = 0L;
109   Element *input_element = 0L;
110   bool result = false;
111   double dt = 0.0, value = 0.0;
112   struct event *newEvent;
113   FGCondition *newCondition;
114
115   document = LoadXMLDocument(script);
116
117   if (!document) {
118     cerr << "File: " << script << " could not be loaded." << endl;
119     return false;
120   }
121
122   // Set up input and output files if specified
123   
124   output_element = document->FindElement("output");
125   input_element = document->FindElement("input");
126
127   if (document->GetName() != string("runscript")) {
128     cerr << "File: " << script << " is not a script file" << endl;
129     return false;
130   }
131
132   ScriptName = document->GetAttributeValue("name");
133
134  // First, find "run" element and set delta T
135
136   run_element = document->FindElement("run");
137
138   if (!run_element) {
139     cerr << "No \"run\" element found in script." << endl;
140     return false;
141   }
142
143   // Set sim timing
144
145   StartTime = run_element->GetAttributeValueAsNumber("start");
146   State->Setsim_time(StartTime);
147   EndTime   = run_element->GetAttributeValueAsNumber("end");
148   dt        = run_element->GetAttributeValueAsNumber("dt");
149   State->Setdt(dt);
150   
151   // read aircraft and initialization files
152
153   element = document->FindElement("use");
154   if (element) {
155     aircraft = element->GetAttributeValue("aircraft");
156     if (!aircraft.empty()) {
157       result = FDMExec->LoadModel(aircraft);
158       if (!result) return false;
159     } else {
160       cerr << "Aircraft must be specified in use element." << endl;
161       return false;
162     }
163
164     initialize = element->GetAttributeValue("initialize");
165     if (initialize.empty()) {
166       cerr << "Initialization file must be specified in use element." << endl;
167       return false;
168     }
169
170   } else {
171     cerr << "No \"use\" directives in the script file." << endl;
172     return false;
173   }
174
175   // Now, read input spec if given.
176   if (input_element > 0) {
177     FDMExec->GetInput()->Load(input_element);
178   }
179
180   // Now, read output spec if given.
181   if (output_element > 0) {
182     string output_file = output_element->GetAttributeValue("file");
183     if (output_file.empty()) {
184       cerr << "No logging directives file was specified." << endl;
185     } else {
186       FDMExec->SetOutputDirectives(output_file);
187     }
188   }
189
190   // Read local property/value declarations
191   property_element = run_element->FindElement("property");
192   while (property_element) {
193
194     double value=0.0;
195     if ( ! property_element->GetAttributeValue("value").empty())
196       value = property_element->GetAttributeValueAsNumber("value");
197
198     LocalProps *localProp = new LocalProps(value);
199     localProp->title = property_element->GetDataLine();
200     local_properties.push_back(localProp);
201
202     PropertyManager->Tie(localProp->title, (local_properties.back())->value);
203     property_element = run_element->FindNextElement("property");
204   }
205
206   // Read "events" from script
207
208   event_element = run_element->FindElement("event");
209   while (event_element) { // event processing
210
211     // Create the event structure
212     newEvent = new struct event();
213
214     // Retrieve the event name if given
215     newEvent->Name = event_element->GetAttributeValue("name");
216
217     // Is this event persistent? That is, does it execute repeatedly as long as the
218     // condition is true, or does it execute as a one-shot event, only?
219     if (event_element->GetAttributeValue("persistent") == string("true")) {
220       newEvent->Persistent = true;
221     }
222
223     // Process the conditions
224     condition_element = event_element->FindElement("condition");
225     if (condition_element != 0) {
226       newCondition = new FGCondition(condition_element, PropertyManager);
227       newEvent->Condition = newCondition;
228     } else {
229       cerr << "No condition specified in script event " << newEvent->Name << endl;
230       return false;
231     }
232
233     // Is there a delay between the time this event is triggered, and when the event
234     // actions are executed?
235
236     delay_element = event_element->FindElement("delay");
237     if (delay_element) newEvent->Delay = event_element->FindElementValueAsNumber("delay");
238     else newEvent->Delay = 0.0;
239
240     // Notify about when this event is triggered?
241     if ((notify_element = event_element->FindElement("notify")) != 0) {
242       newEvent->Notify = true;
243       notify_property_element = notify_element->FindElement("property");
244       while (notify_property_element) {
245         notifyPropertyName = notify_property_element->GetDataLine();
246         if (PropertyManager->GetNode(notifyPropertyName)) {
247           newEvent->NotifyProperties.push_back( PropertyManager->GetNode(notifyPropertyName) );
248         } else {
249           cout << endl << fgred << "  Could not find the property named "
250                << notifyPropertyName << " in script" << endl << "  \""
251                << ScriptName << "\". This unknown property will not be "
252                << "echoed for notification." << reset << endl;
253         }
254         notify_property_element = notify_element->FindNextElement("property");
255       }
256     }
257
258     // Read set definitions (these define the actions to be taken when the event is triggered).
259     set_element = event_element->FindElement("set");
260     while (set_element) {
261       prop_name = set_element->GetAttributeValue("name");
262       newEvent->SetParam.push_back( PropertyManager->GetNode(prop_name) );
263       //Todo - should probably do some safety checking here to make sure one or the other
264       //of value or function is specified.
265       if (!set_element->GetAttributeValue("value").empty()) {
266         value = set_element->GetAttributeValueAsNumber("value");
267         newEvent->Functions.push_back((FGFunction*)0L);
268       } else if (set_element->FindElement("function")) {
269         value = 0.0;
270         newEvent->Functions.push_back(new FGFunction(PropertyManager, set_element->FindElement("function")));
271       }
272       newEvent->SetValue.push_back(value);
273       newEvent->OriginalValue.push_back(0.0);
274       newEvent->newValue.push_back(0.0);
275       newEvent->ValueSpan.push_back(0.0);
276       string tempCompare = set_element->GetAttributeValue("type");
277       if      (tempCompare == "FG_DELTA") newEvent->Type.push_back(FG_DELTA);
278       else if (tempCompare == "FG_BOOL")  newEvent->Type.push_back(FG_BOOL);
279       else if (tempCompare == "FG_VALUE") newEvent->Type.push_back(FG_VALUE);
280       else                                newEvent->Type.push_back(FG_VALUE); // DEFAULT
281       tempCompare = set_element->GetAttributeValue("action");
282       if      (tempCompare == "FG_RAMP") newEvent->Action.push_back(FG_RAMP);
283       else if (tempCompare == "FG_STEP") newEvent->Action.push_back(FG_STEP);
284       else if (tempCompare == "FG_EXP")  newEvent->Action.push_back(FG_EXP);
285       else                               newEvent->Action.push_back(FG_STEP); // DEFAULT
286
287       if (!set_element->GetAttributeValue("tc").empty())
288         newEvent->TC.push_back(set_element->GetAttributeValueAsNumber("tc"));
289       else
290         newEvent->TC.push_back(1.0); // DEFAULT
291
292       newEvent->Transiting.push_back(false);
293
294       set_element = event_element->FindNextElement("set");
295     }
296     Events.push_back(*newEvent);
297     delete newEvent;
298
299     event_element = run_element->FindNextElement("event");
300   }
301
302   Debug(4);
303
304   FGInitialCondition *IC=FDMExec->GetIC();
305   if ( ! IC->Load( initialize )) {
306     cerr << "Initialization unsuccessful" << endl;
307     exit(-1);
308   }
309
310   return true;
311 }
312
313 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
314
315 bool FGScript::RunScript(void)
316 {
317   vector <struct event>::iterator iEvent = Events.begin();
318   unsigned i, j;
319   unsigned event_ctr = 0;
320
321   double currentTime = State->Getsim_time();
322   double newSetValue = 0;
323
324   if (currentTime > EndTime) return false; //Script done!
325
326   // Iterate over all events.
327   while (iEvent < Events.end()) {
328     iEvent->PrevTriggered = iEvent->Triggered;
329     // Determine whether the set of conditional tests for this condition equate
330     // to true and should cause the event to execute.
331     if (iEvent->Condition->Evaluate()) {
332       if (!iEvent->Triggered) {
333
334         // The conditions are true, do the setting of the desired Event parameters
335         for (i=0; i<iEvent->SetValue.size(); i++) {
336           iEvent->OriginalValue[i] = iEvent->SetParam[i]->getDoubleValue();
337           if (iEvent->Functions[i] != 0) { // Parameter should be set to a function value
338             iEvent->SetValue[i] = iEvent->Functions[i]->GetValue();
339           }
340           switch (iEvent->Type[i]) {
341           case FG_VALUE:
342           case FG_BOOL:
343             iEvent->newValue[i] = iEvent->SetValue[i];
344             break;
345           case FG_DELTA:
346             iEvent->newValue[i] = iEvent->OriginalValue[i] + iEvent->SetValue[i];
347             break;
348           default:
349             cerr << "Invalid Type specified" << endl;
350             break;
351           }
352           iEvent->StartTime = currentTime + iEvent->Delay;
353           iEvent->ValueSpan[i] = iEvent->newValue[i] - iEvent->OriginalValue[i];
354           iEvent->Transiting[i] = true;
355         }
356       }
357       iEvent->Triggered = true;
358     } else if (iEvent->Persistent) {
359       iEvent->Triggered = false; // Reset the trigger for persistent events
360       iEvent->Notified = false;  // Also reset the notification flag
361     }
362
363     if ((currentTime >= iEvent->StartTime) && iEvent->Triggered) {
364
365       for (i=0; i<iEvent->SetValue.size(); i++) {
366         if (iEvent->Transiting[i]) {
367           iEvent->TimeSpan = currentTime - iEvent->StartTime;
368           if (iEvent->Functions[i] == 0) {
369             switch (iEvent->Action[i]) {
370             case FG_RAMP:
371               if (iEvent->TimeSpan <= iEvent->TC[i]) {
372                 newSetValue = iEvent->TimeSpan/iEvent->TC[i] * iEvent->ValueSpan[i] + iEvent->OriginalValue[i];
373               } else {
374                 newSetValue = iEvent->newValue[i];
375                 iEvent->Transiting[i] = false;
376               }
377               break;
378             case FG_STEP:
379               newSetValue = iEvent->newValue[i];
380               iEvent->Transiting[i] = false;
381               break;
382             case FG_EXP:
383               newSetValue = (1 - exp( -iEvent->TimeSpan/iEvent->TC[i] )) * iEvent->ValueSpan[i] + iEvent->OriginalValue[i];
384               break;
385             default:
386               cerr << "Invalid Action specified" << endl;
387               break;
388             }
389           } else { // Set the new value based on a function
390             newSetValue = iEvent->Functions[i]->GetValue();
391           }
392           iEvent->SetParam[i]->setDoubleValue(newSetValue);
393         }
394       }
395
396       // Print notification values after setting them
397       if (iEvent->Notify && !iEvent->Notified) {
398         cout << endl << "  Event " << event_ctr << " (" << iEvent->Name << ")"
399              << " executed at time: " << currentTime << endl;
400         for (j=0; j<iEvent->NotifyProperties.size();j++) {
401           cout << "    " << iEvent->NotifyProperties[j]->GetName()
402                << " = " << iEvent->NotifyProperties[j]->getDoubleValue() << endl;
403         }
404         cout << endl;
405         iEvent->Notified = true;
406       }
407
408     }
409
410     iEvent++;
411     event_ctr++;
412   }
413   return true;
414 }
415
416 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
417 //    The bitmasked value choices are as follows:
418 //    unset: In this case (the default) JSBSim would only print
419 //       out the normally expected messages, essentially echoing
420 //       the config files as they are read. If the environment
421 //       variable is not set, debug_lvl is set to 1 internally
422 //    0: This requests JSBSim not to output any messages
423 //       whatsoever.
424 //    1: This value explicity requests the normal JSBSim
425 //       startup messages
426 //    2: This value asks for a message to be printed out when
427 //       a class is instantiated
428 //    4: When this value is set, a message is displayed when a
429 //       FGModel object executes its Run() method
430 //    8: When this value is set, various runtime state variables
431 //       are printed out periodically
432 //    16: When set various parameters are sanity checked and
433 //       a message is printed out when they go out of bounds
434
435 void FGScript::Debug(int from)
436 {
437   if (debug_lvl <= 0) return;
438
439   if (debug_lvl & 1) { // Standard console startup message output
440     if (from == 0) { // Constructor
441     } else if (from == 3) {
442     } else if (from == 4)  { // print out script data
443       cout << endl;
444       cout << "Script: \"" << ScriptName << "\"" << endl;
445       cout << "  begins at " << StartTime << " seconds and runs to " << EndTime
446            << " seconds with dt = " << State->Getdt() << endl;
447       cout << endl;
448
449       for (unsigned i=0; i<Events.size(); i++) {
450         cout << "Event " << i;
451         if (!Events[i].Name.empty()) cout << " (" << Events[i].Name << ")";
452         cout << ":" << endl;
453
454         if (Events[i].Persistent)
455           cout << "  " << "Always executes";
456         else
457           cout << "  " << "Executes once";
458
459         Events[i].Condition->PrintCondition();
460
461         cout << endl << "  Actions taken:" << endl << "    {";
462         for (unsigned j=0; j<Events[i].SetValue.size(); j++) {
463           if (Events[i].SetValue[j] == 0.0 && Events[i].Functions[j] != 0L) {
464             if (Events[i].SetParam[j] == 0) {
465               cerr << fgred << highint << endl
466                    << "  An attempt has been made to access a non-existent property" << endl
467                    << "  in this event. Please check the property names used, spelling, etc."
468                    << reset << endl;
469               exit(-1);
470             }
471             cout << endl << "      set " << Events[i].SetParam[j]->GetName()
472                  << " to function value";
473           } else {
474             if (Events[i].SetParam[j] == 0) {
475               cerr << fgred << highint << endl
476                    << "  An attempt has been made to access a non-existent property" << endl
477                    << "  in this event. Please check the property names used, spelling, etc."
478                    << reset << endl;
479               exit(-1);
480             }
481             cout << endl << "      set " << Events[i].SetParam[j]->GetName()
482                  << " to " << Events[i].SetValue[j];
483           }
484
485           switch (Events[i].Type[j]) {
486           case FG_VALUE:
487           case FG_BOOL:
488             cout << " (constant";
489             break;
490           case FG_DELTA:
491             cout << " (delta";
492             break;
493           default:
494             cout << " (unspecified type";
495           }
496
497           switch (Events[i].Action[j]) {
498           case FG_RAMP:
499             cout << " via ramp";
500             break;
501           case FG_STEP:
502             cout << " via step)";
503             break;
504           case FG_EXP:
505             cout << " via exponential approach";
506             break;
507           default:
508             cout << " via unspecified action)";
509           }
510
511           if (Events[i].Action[j] == FG_RAMP || Events[i].Action[j] == FG_EXP)
512             cout << " with time constant " << Events[i].TC[j] << ")";
513         }
514         cout << endl << "    }" << endl << endl;
515
516       }
517     }
518   }
519   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
520     if (from == 0) cout << "Instantiated: FGScript" << endl;
521     if (from == 1) cout << "Destroyed:    FGScript" << endl;
522   }
523   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
524   }
525   if (debug_lvl & 8 ) { // Runtime state variables
526   }
527   if (debug_lvl & 16) { // Sanity checking
528   }
529   if (debug_lvl & 64) {
530     if (from == 0) { // Constructor
531       cout << IdSrc << endl;
532       cout << IdHdr << endl;
533     }
534   }
535 }
536 }