]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/input_output/FGScript.cpp
Sync. w. JSB CVS as of 15/01/2007
[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   int i;
88   for (i=0; i<local_properties.size(); i++)
89     PropertyManager->Untie(local_properties[i]->title);
90   
91   for (i=0; i<local_properties.size(); i++)
92     delete local_properties[i];
93
94   local_properties.clear();
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 *document=0, *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   bool result = false;
109   double dt = 0.0, value = 0.0;
110   FGXMLParse script_file_parser;
111   struct event *newEvent;
112   FGCondition *newCondition;
113   ifstream script_file(script.c_str());
114
115   if ( !script_file ) return false;
116
117   readXML(script_file, script_file_parser);
118   document = script_file_parser.GetDocument();
119
120   if (document->GetName() != string("runscript")) {
121     cerr << "File: " << script << " is not a script file" << endl;
122     return false;
123   }
124
125   ScriptName = document->GetAttributeValue("name");
126
127  // First, find "run" element and set delta T
128
129   run_element = document->FindElement("run");
130
131   if (!run_element) {
132     cerr << "No \"run\" element found in script." << endl;
133     return false;
134   }
135
136   // Set sim timing
137
138   StartTime = run_element->GetAttributeValueAsNumber("start");
139   State->Setsim_time(StartTime);
140   EndTime   = run_element->GetAttributeValueAsNumber("end");
141   dt        = run_element->GetAttributeValueAsNumber("dt");
142   State->Setdt(dt);
143
144   // read aircraft and initialization files
145
146   element = document->FindElement("use");
147   if (element) {
148     aircraft = element->GetAttributeValue("aircraft");
149     if (!aircraft.empty()) {
150       result = FDMExec->LoadModel(aircraft);
151       if (!result) return false;
152     } else {
153       cerr << "Aircraft must be specified in use element." << endl;
154       return false;
155     }
156
157     initialize = element->GetAttributeValue("initialize");
158     if (initialize.empty()) {
159       cerr << "Initialization file must be specified in use element." << endl;
160       return false;
161     }
162
163   } else {
164     cerr << "No \"use\" directives in the script file." << endl;
165     return false;
166   }
167
168   // Read local property declarations
169   property_element = run_element->FindElement("property");
170   while (property_element) {
171     LocalProps *localProp = new LocalProps();
172     localProp->title = property_element->GetDataLine();
173     local_properties.push_back(localProp);
174     PropertyManager->Tie(localProp->title, (local_properties.back())->value);
175     property_element = run_element->FindNextElement("property");
176   }
177
178   // Read "events" from script
179
180   event_element = run_element->FindElement("event");
181   while (event_element) { // event processing
182
183     // Create the event structure
184     newEvent = new struct event();
185
186     // Retrieve the event name if given
187     newEvent->Name = event_element->GetAttributeValue("name");
188
189     // Is this event persistent? That is, does it execute repeatedly as long as the
190     // condition is true, or does it execute as a one-shot event, only?
191     if (event_element->GetAttributeValue("persistent") == string("true")) {
192       newEvent->Persistent = true;
193     }
194
195     // Process the conditions
196     condition_element = event_element->FindElement("condition");
197     if (condition_element != 0) {
198       newCondition = new FGCondition(condition_element, PropertyManager);
199       newEvent->Condition = newCondition;
200     } else {
201       cerr << "No condition specified in script event " << newEvent->Name << endl;
202       return false;
203     }
204
205     // Is there a delay between the time this event is triggered, and when the event
206     // actions are executed?
207
208     delay_element = event_element->FindElement("delay");
209     if (delay_element) newEvent->Delay = event_element->FindElementValueAsNumber("delay");
210     else newEvent->Delay = 0.0;
211
212     // Notify about when this event is triggered?
213     if ((notify_element = event_element->FindElement("notify")) != 0) {
214       newEvent->Notify = true;
215       notify_property_element = notify_element->FindElement("property");
216       while (notify_property_element) {
217         notifyPropertyName = notify_property_element->GetDataLine();
218         newEvent->NotifyProperties.push_back( PropertyManager->GetNode(notifyPropertyName) );
219         notify_property_element = notify_element->FindNextElement("property");
220       }
221     }
222
223     // Read set definitions (these define the actions to be taken when the event is triggered).
224     set_element = event_element->FindElement("set");
225     while (set_element) {
226       prop_name = set_element->GetAttributeValue("name");
227       newEvent->SetParam.push_back( PropertyManager->GetNode(prop_name) );
228       value = set_element->GetAttributeValueAsNumber("value");
229       newEvent->SetValue.push_back(value);
230       newEvent->OriginalValue.push_back(0.0);
231       newEvent->newValue.push_back(0.0);
232       newEvent->ValueSpan.push_back(0.0);
233       string tempCompare = set_element->GetAttributeValue("type");
234       if      (tempCompare == "FG_DELTA") newEvent->Type.push_back(FG_DELTA);
235       else if (tempCompare == "FG_BOOL")  newEvent->Type.push_back(FG_BOOL);
236       else if (tempCompare == "FG_VALUE") newEvent->Type.push_back(FG_VALUE);
237       else                                newEvent->Type.push_back(FG_VALUE); // DEFAULT
238       tempCompare = set_element->GetAttributeValue("action");
239       if      (tempCompare == "FG_RAMP") newEvent->Action.push_back(FG_RAMP);
240       else if (tempCompare == "FG_STEP") newEvent->Action.push_back(FG_STEP);
241       else if (tempCompare == "FG_EXP")  newEvent->Action.push_back(FG_EXP);
242       else                               newEvent->Action.push_back(FG_STEP); // DEFAULT
243
244       if (!set_element->GetAttributeValue("tc").empty())
245         newEvent->TC.push_back(set_element->GetAttributeValueAsNumber("tc"));
246       else
247         newEvent->TC.push_back(1.0); // DEFAULT
248
249       newEvent->Transiting.push_back(false);
250
251       set_element = event_element->FindNextElement("set");
252     }
253     Events.push_back(*newEvent);
254     event_element = run_element->FindNextElement("event");
255   }
256
257   Debug(4);
258
259   FGInitialCondition *IC=FDMExec->GetIC();
260   if ( ! IC->Load( initialize )) {
261     cerr << "Initialization unsuccessful" << endl;
262     exit(-1);
263   }
264
265   return true;
266 }
267
268 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269
270 bool FGScript::RunScript(void)
271 {
272   vector <struct event>::iterator iEvent = Events.begin();
273   unsigned i, j;
274   unsigned event_ctr = 0;
275
276   double currentTime = State->Getsim_time();
277   double newSetValue = 0;
278
279   if (currentTime > EndTime) return false; //Script done!
280
281   // Iterate over all events.
282   while (iEvent < Events.end()) {
283     iEvent->PrevTriggered = iEvent->Triggered;
284     // Determine whether the set of conditional tests for this condition equate
285     // to true and should cause the event to execute.
286     if (iEvent->Condition->Evaluate()) {
287       if (!iEvent->Triggered) {
288
289         // The conditions are true, do the setting of the desired Event parameters
290         for (i=0; i<iEvent->SetValue.size(); i++) {
291           iEvent->OriginalValue[i] = iEvent->SetParam[i]->getDoubleValue();
292           switch (iEvent->Type[i]) {
293           case FG_VALUE:
294           case FG_BOOL:
295             iEvent->newValue[i] = iEvent->SetValue[i];
296             break;
297           case FG_DELTA:
298             iEvent->newValue[i] = iEvent->OriginalValue[i] + iEvent->SetValue[i];
299             break;
300           default:
301             cerr << "Invalid Type specified" << endl;
302             break;
303           }
304           iEvent->StartTime = currentTime + iEvent->Delay;
305           iEvent->ValueSpan[i] = iEvent->newValue[i] - iEvent->OriginalValue[i];
306           iEvent->Transiting[i] = true;
307         }
308       }
309       iEvent->Triggered = true;
310     } else if (iEvent->Persistent) {
311       iEvent->Triggered = false; // Reset the trigger for persistent events
312     }
313
314     if ((currentTime >= iEvent->StartTime) && iEvent->Triggered) {
315
316       if (iEvent->Notify && iEvent->PrevTriggered != iEvent->Triggered) {
317         cout << endl << "  Event " << event_ctr << " (" << iEvent->Name << ")"
318              << " executed at time: " << currentTime << endl;
319         for (j=0; j<iEvent->NotifyProperties.size();j++) {
320           cout << "    " << iEvent->NotifyProperties[j]->GetName()
321                << " = " << iEvent->NotifyProperties[j]->getDoubleValue() << endl;
322         }
323         cout << endl;
324       }
325
326       for (i=0; i<iEvent->SetValue.size(); i++) {
327         if (iEvent->Transiting[i]) {
328           iEvent->TimeSpan = currentTime - iEvent->StartTime;
329           switch (iEvent->Action[i]) {
330           case FG_RAMP:
331             if (iEvent->TimeSpan <= iEvent->TC[i]) {
332               newSetValue = iEvent->TimeSpan/iEvent->TC[i] * iEvent->ValueSpan[i] + iEvent->OriginalValue[i];
333             } else {
334               newSetValue = iEvent->newValue[i];
335               iEvent->Transiting[i] = false;
336             }
337             break;
338           case FG_STEP:
339             newSetValue = iEvent->newValue[i];
340             iEvent->Transiting[i] = false;
341             break;
342           case FG_EXP:
343             newSetValue = (1 - exp( -iEvent->TimeSpan/iEvent->TC[i] )) * iEvent->ValueSpan[i] + iEvent->OriginalValue[i];
344             break;
345           default:
346             cerr << "Invalid Action specified" << endl;
347             break;
348           }
349           iEvent->SetParam[i]->setDoubleValue(newSetValue);
350         }
351       }
352     }
353
354     iEvent++;
355     event_ctr++;
356   }
357   return true;
358 }
359
360 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361 //    The bitmasked value choices are as follows:
362 //    unset: In this case (the default) JSBSim would only print
363 //       out the normally expected messages, essentially echoing
364 //       the config files as they are read. If the environment
365 //       variable is not set, debug_lvl is set to 1 internally
366 //    0: This requests JSBSim not to output any messages
367 //       whatsoever.
368 //    1: This value explicity requests the normal JSBSim
369 //       startup messages
370 //    2: This value asks for a message to be printed out when
371 //       a class is instantiated
372 //    4: When this value is set, a message is displayed when a
373 //       FGModel object executes its Run() method
374 //    8: When this value is set, various runtime state variables
375 //       are printed out periodically
376 //    16: When set various parameters are sanity checked and
377 //       a message is printed out when they go out of bounds
378
379 void FGScript::Debug(int from)
380 {
381   if (debug_lvl <= 0) return;
382
383   if (debug_lvl & 1) { // Standard console startup message output
384     if (from == 0) { // Constructor
385     } else if (from == 3) {
386     } else if (from == 4)  { // print out script data
387       cout << endl;
388       cout << "Script: \"" << ScriptName << "\"" << endl;
389       cout << "  begins at " << StartTime << " seconds and runs to " << EndTime
390            << " seconds with dt = " << State->Getdt() << endl;
391       cout << endl;
392
393       for (unsigned i=0; i<Events.size(); i++) {
394         cout << "Event " << i;
395         if (!Events[i].Name.empty()) cout << " (" << Events[i].Name << ")";
396         cout << ":" << endl;
397
398         if (Events[i].Persistent)
399           cout << "  " << "Always executes";
400         else
401           cout << "  " << "Executes once";
402
403         Events[i].Condition->PrintCondition();
404
405         cout << endl << "  Actions taken:" << endl << "    {";
406         for (unsigned j=0; j<Events[i].SetValue.size(); j++) {
407           cout << endl << "      set " << Events[i].SetParam[j]->GetName()
408                << " to " << Events[i].SetValue[j];
409
410           switch (Events[i].Type[j]) {
411           case FG_VALUE:
412           case FG_BOOL:
413             cout << " (constant";
414             break;
415           case FG_DELTA:
416             cout << " (delta";
417             break;
418           default:
419             cout << " (unspecified type";
420           }
421
422           switch (Events[i].Action[j]) {
423           case FG_RAMP:
424             cout << " via ramp";
425             break;
426           case FG_STEP:
427             cout << " via step)";
428             break;
429           case FG_EXP:
430             cout << " via exponential approach";
431             break;
432           default:
433             cout << " via unspecified action)";
434           }
435
436           if (Events[i].Action[j] == FG_RAMP || Events[i].Action[j] == FG_EXP)
437             cout << " with time constant " << Events[i].TC[j] << ")";
438         }
439         cout << endl << "    }" << endl << endl;
440
441       }
442     }
443   }
444   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
445     if (from == 0) cout << "Instantiated: FGScript" << endl;
446     if (from == 1) cout << "Destroyed:    FGScript" << endl;
447   }
448   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
449   }
450   if (debug_lvl & 8 ) { // Runtime state variables
451   }
452   if (debug_lvl & 16) { // Sanity checking
453   }
454   if (debug_lvl & 64) {
455     if (from == 0) { // Constructor
456       cout << IdSrc << endl;
457       cout << IdHdr << endl;
458     }
459   }
460 }
461 }