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