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