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