]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/input_output/FGScript.cpp
Fix for bug 1304 - crash loading XML route
[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 <iostream>
45 #include <cstdlib>
46 #include <iomanip>
47
48 #include "FGScript.h"
49 #include "input_output/FGXMLElement.h"
50 #include "input_output/FGXMLFileRead.h"
51 #include "initialization/FGTrim.h"
52 #include "models/FGInput.h"
53
54 using namespace std;
55
56 namespace JSBSim {
57
58 IDENT(IdSrc,"$Id: FGScript.cpp,v 1.56 2014/01/13 10:46:01 ehofman Exp $");
59 IDENT(IdHdr,ID_FGSCRIPT);
60
61 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62 GLOBAL DECLARATIONS
63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
64
65 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 CLASS IMPLEMENTATION
67 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
68
69 // Constructor
70
71 FGScript::FGScript(FGFDMExec* fgex) : FDMExec(fgex)
72 {
73   PropertyManager=FDMExec->GetPropertyManager();
74
75   Debug(0);
76 }
77
78 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79
80 FGScript::~FGScript()
81 {
82   unsigned int i, j;
83
84   for (i=0; i<Events.size(); i++) {
85     delete Events[i].Condition;
86     for (j=0; j<Events[i].Functions.size(); j++)
87       delete Events[i].Functions[j];
88   }
89   Events.clear();
90
91   Debug(1);
92 }
93
94 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95
96 bool FGScript::LoadScript(string script, double deltaT, const string initfile)
97 {
98   string aircraft="", initialize="", comparison = "", prop_name="";
99   string notifyPropertyName="";
100   Element *element=0, *run_element=0, *event_element=0;
101   Element *condition_element=0, *set_element=0, *delay_element=0;
102   Element *notify_element = 0L, *notify_property_element = 0L;
103   Element *output_element = 0L;
104   Element *input_element = 0L;
105   bool result = false;
106   double dt = 0.0, value = 0.0;
107   struct event *newEvent;
108   FGCondition *newCondition;
109
110   FGXMLFileRead XMLFileRead;
111   Element* document = XMLFileRead.LoadXMLDocument(script);
112
113   if (!document) {
114     cerr << "File: " << script << " could not be loaded." << endl;
115     return false;
116   }
117
118   // Set up input and output files if specified
119   
120   output_element = document->FindElement("output");
121   input_element = document->FindElement("input");
122
123   if (document->GetName() != string("runscript")) {
124     cerr << "File: " << script << " is not a script file" << endl;
125     return false;
126   }
127
128   ScriptName = document->GetAttributeValue("name");
129
130  // First, find "run" element and set delta T
131
132   run_element = document->FindElement("run");
133
134   if (!run_element) {
135     cerr << "No \"run\" element found in script." << endl;
136     return false;
137   }
138
139   // Set sim timing
140
141   StartTime = run_element->GetAttributeValueAsNumber("start");
142   FDMExec->Setsim_time(StartTime);
143   EndTime   = run_element->GetAttributeValueAsNumber("end");
144   // Make sure that the desired time is reached and executed.
145   EndTime += 0.99*FDMExec->GetDeltaT();
146
147   if (deltaT == 0.0)
148     dt = run_element->GetAttributeValueAsNumber("dt");
149   else {
150     dt = deltaT;
151     cout << endl << "Overriding simulation step size from the command line. New step size is: "
152          << deltaT << " seconds (" << 1/deltaT << " Hz)" << endl << endl;
153   }
154
155   FDMExec->Setdt(dt);
156   
157   // read aircraft and initialization files
158
159   element = document->FindElement("use");
160   if (element) {
161     aircraft = element->GetAttributeValue("aircraft");
162     if (!aircraft.empty()) {
163       result = FDMExec->LoadModel(aircraft);
164       if (!result) return false;
165     } else {
166       cerr << "Aircraft must be specified in use element." << endl;
167       return false;
168     }
169
170     initialize = element->GetAttributeValue("initialize");
171     if (initfile.empty()) {
172     if (initialize.empty()) {
173       cerr << "Initialization file must be specified in use element." << endl;
174       return false;
175       }
176     } else {
177       cout << endl << "The initialization file specified in the script file (" << initialize
178                    << ") has been overridden with a specified file (" << initfile << ")." << endl;
179       initialize = initfile;
180     }
181
182   } else {
183     cerr << "No \"use\" directives in the script file." << endl;
184     return false;
185   }
186
187   // Now, read input spec if given.
188   if (input_element > 0) {
189     FDMExec->GetInput()->Load(input_element);
190   }
191
192   // Now, read output spec if given.
193   if (output_element > 0) {
194     string output_file = output_element->GetAttributeValue("file");
195     if (output_file.empty()) {
196       cerr << "No logging directives file was specified." << endl;
197     } else {
198       if (!FDMExec->SetOutputDirectives(output_file)) return false;
199     }
200   }
201
202   // Read local property/value declarations
203   int saved_debug_lvl = debug_lvl;
204   debug_lvl = 0; // Disable messages
205   LoadProperties(run_element, PropertyManager, true);
206   debug_lvl = saved_debug_lvl;
207
208   // Read "events" from script
209
210   event_element = run_element->FindElement("event");
211   while (event_element) { // event processing
212
213     // Create the event structure
214     newEvent = new struct event();
215
216     // Retrieve the event name if given
217     newEvent->Name = event_element->GetAttributeValue("name");
218
219     // Is this event persistent? That is, does it execute every time the
220     // condition triggers to true, or does it execute as a one-shot event, only?
221     if (event_element->GetAttributeValue("persistent") == string("true")) {
222       newEvent->Persistent = true;
223     }
224
225     // Does this event execute continuously when triggered to true?
226     if (event_element->GetAttributeValue("continuous") == string("true")) {
227       newEvent->Continuous = true;
228     }
229
230     // Process the conditions
231     condition_element = event_element->FindElement("condition");
232     if (condition_element != 0) {
233       try {
234         newCondition = new FGCondition(condition_element, PropertyManager);
235       } catch(string str) {
236         cout << endl << fgred << str << reset << endl << endl;
237         delete newEvent;
238         return false;
239       }
240       newEvent->Condition = newCondition;
241     } else {
242       cerr << "No condition specified in script event " << newEvent->Name << endl;
243       delete newEvent;
244       return false;
245     }
246
247     // Is there a delay between the time this event is triggered, and when the event
248     // actions are executed?
249
250     delay_element = event_element->FindElement("delay");
251     if (delay_element) newEvent->Delay = event_element->FindElementValueAsNumber("delay");
252     else newEvent->Delay = 0.0;
253
254     // Notify about when this event is triggered?
255     if ((notify_element = event_element->FindElement("notify")) != 0) {
256       if (notify_element->HasAttribute("format")) {
257         if (notify_element->GetAttributeValue("format") == "kml") newEvent->NotifyKML = true;
258       }
259       newEvent->Notify = true;
260       // Check here for new <description> tag that gets echoed
261       string notify_description = notify_element->FindElementValue("description");
262       if (!notify_description.empty()) {
263         newEvent->Description = notify_description;
264       }
265       notify_property_element = notify_element->FindElement("property");
266       while (notify_property_element) {
267         notifyPropertyName = notify_property_element->GetDataLine();
268
269         newEvent->NotifyPropertyNames.push_back(notifyPropertyName);
270         newEvent->NotifyProperties.push_back(0);
271           string caption_attribute = notify_property_element->GetAttributeValue("caption");
272           if (caption_attribute.empty()) {
273             newEvent->DisplayString.push_back(notifyPropertyName);
274           } else {
275             newEvent->DisplayString.push_back(caption_attribute);
276           }
277
278         notify_property_element = notify_element->FindNextElement("property");
279       }
280     }
281
282     // Read set definitions (these define the actions to be taken when the event is triggered).
283     set_element = event_element->FindElement("set");
284     while (set_element) {
285       prop_name = set_element->GetAttributeValue("name");
286       if (PropertyManager->HasNode(prop_name)) {
287       newEvent->SetParam.push_back( PropertyManager->GetNode(prop_name) );
288       } else {
289         newEvent->SetParam.push_back( 0L );
290       }
291       newEvent->SetParamName.push_back( prop_name );
292
293       //Todo - should probably do some safety checking here to make sure one or the other
294       //of value or function is specified.
295       if (!set_element->GetAttributeValue("value").empty()) {
296         value = set_element->GetAttributeValueAsNumber("value");
297         newEvent->Functions.push_back((FGFunction*)0L);
298       } else if (set_element->FindElement("function")) {
299         value = 0.0;
300         newEvent->Functions.push_back(new FGFunction(PropertyManager, set_element->FindElement("function")));
301       }
302       newEvent->SetValue.push_back(value);
303       newEvent->OriginalValue.push_back(0.0);
304       newEvent->newValue.push_back(0.0);
305       newEvent->ValueSpan.push_back(0.0);
306       string tempCompare = set_element->GetAttributeValue("type");
307       if      (to_lower(tempCompare).find("delta") != string::npos) newEvent->Type.push_back(FG_DELTA);
308       else if (to_lower(tempCompare).find("bool") != string::npos)  newEvent->Type.push_back(FG_BOOL);
309       else if (to_lower(tempCompare).find("value") != string::npos) newEvent->Type.push_back(FG_VALUE);
310       else                                newEvent->Type.push_back(FG_VALUE); // DEFAULT
311       tempCompare = set_element->GetAttributeValue("action");
312       if      (to_lower(tempCompare).find("ramp") != string::npos) newEvent->Action.push_back(FG_RAMP);
313       else if (to_lower(tempCompare).find("step") != string::npos) newEvent->Action.push_back(FG_STEP);
314       else if (to_lower(tempCompare).find("exp") != string::npos) newEvent->Action.push_back(FG_EXP);
315       else                               newEvent->Action.push_back(FG_STEP); // DEFAULT
316
317       if (!set_element->GetAttributeValue("tc").empty())
318         newEvent->TC.push_back(set_element->GetAttributeValueAsNumber("tc"));
319       else
320         newEvent->TC.push_back(1.0); // DEFAULT
321
322       newEvent->Transiting.push_back(false);
323
324       set_element = event_element->FindNextElement("set");
325     }
326     Events.push_back(*newEvent);
327     delete newEvent;
328
329     event_element = run_element->FindNextElement("event");
330   }
331
332   Debug(4);
333
334   FGInitialCondition *IC=FDMExec->GetIC();
335   if ( ! IC->Load( initialize )) {
336     cerr << "Initialization unsuccessful" << endl;
337     exit(-1);
338   }
339
340   return true;
341 }
342
343 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344
345 void FGScript::ResetEvents(void)
346 {
347   //ResetToIC();
348
349   for (unsigned int i=0; i<Events.size(); i++)
350     Events[i].reset();
351 }
352
353 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
354
355 bool FGScript::RunScript(void)
356 {
357   unsigned i, j;
358   unsigned event_ctr = 0;
359
360   double currentTime = FDMExec->GetSimTime();
361   double newSetValue = 0;
362
363   if (currentTime > EndTime) return false;
364
365   // Iterate over all events.
366   for (unsigned int ev_ctr=0; ev_ctr < Events.size(); ev_ctr++) {
367
368     struct event &thisEvent = Events[ev_ctr];
369
370     // Determine whether the set of conditional tests for this condition equate
371     // to true and should cause the event to execute. If the conditions evaluate 
372     // to true, then the event is triggered. If the event is not persistent,
373     // then this trigger will remain set true. If the event is persistent,
374     // the trigger will reset to false when the condition evaluates to false.
375     if (thisEvent.Condition->Evaluate()) {
376       if (!thisEvent.Triggered) {
377
378         // The conditions are true, do the setting of the desired Event parameters
379         for (i=0; i<thisEvent.SetValue.size(); i++) {
380           if (thisEvent.SetParam[i] == 0L) { // Late bind property if necessary
381             if (PropertyManager->HasNode(thisEvent.SetParamName[i])) {
382               thisEvent.SetParam[i] = PropertyManager->GetNode(thisEvent.SetParamName[i]);
383             } else {
384               throw("No property, \""+thisEvent.SetParamName[i]+"\" is defined.");
385             }
386           }
387           thisEvent.OriginalValue[i] = thisEvent.SetParam[i]->getDoubleValue();
388           if (thisEvent.Functions[i] != 0) { // Parameter should be set to a function value
389             try {
390               thisEvent.SetValue[i] = thisEvent.Functions[i]->GetValue();
391             } catch (string msg) {
392               std::cerr << std::endl << "A problem occurred in the execution of the script. " << msg << endl;
393               throw;
394             }
395           }
396           switch (thisEvent.Type[i]) {
397           case FG_VALUE:
398           case FG_BOOL:
399             thisEvent.newValue[i] = thisEvent.SetValue[i];
400             break;
401           case FG_DELTA:
402             thisEvent.newValue[i] = thisEvent.OriginalValue[i] + thisEvent.SetValue[i];
403             break;
404           default:
405             cerr << "Invalid Type specified" << endl;
406             break;
407           }
408           thisEvent.StartTime = currentTime + thisEvent.Delay;
409           thisEvent.ValueSpan[i] = thisEvent.newValue[i] - thisEvent.OriginalValue[i];
410           thisEvent.Transiting[i] = true;
411         }
412       }
413       thisEvent.Triggered = true;
414
415     } else if (thisEvent.Persistent) { // If the event is persistent, reset the trigger.
416       thisEvent.Triggered = false; // Reset the trigger for persistent events
417       thisEvent.Notified = false;  // Also reset the notification flag
418     } else if (thisEvent.Continuous) { // If the event is continuous, reset the trigger.
419       thisEvent.Triggered = false; // Reset the trigger for persistent events
420       thisEvent.Notified = false;  // Also reset the notification flag
421     }
422
423     if ((currentTime >= thisEvent.StartTime) && thisEvent.Triggered) {
424
425       for (i=0; i<thisEvent.SetValue.size(); i++) {
426         if (thisEvent.Transiting[i]) {
427           thisEvent.TimeSpan = currentTime - thisEvent.StartTime;
428           switch (thisEvent.Action[i]) {
429           case FG_RAMP:
430             if (thisEvent.TimeSpan <= thisEvent.TC[i]) {
431               newSetValue = thisEvent.TimeSpan/thisEvent.TC[i] * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
432             } else {
433               newSetValue = thisEvent.newValue[i];
434               if (thisEvent.Continuous != true) thisEvent.Transiting[i] = false;
435             }
436             break;
437           case FG_STEP:
438             newSetValue = thisEvent.newValue[i];
439
440             // If this is not a continuous event, reset the transiting flag.
441             // Otherwise, it is known that the event is a continuous event.
442             // Furthermore, if the event is to be determined by a function,
443             // then the function will be continuously calculated.
444             if (thisEvent.Continuous != true)
445               thisEvent.Transiting[i] = false;
446             else if (thisEvent.Functions[i] != 0)
447               newSetValue = thisEvent.Functions[i]->GetValue();
448
449             break;
450           case FG_EXP:
451             newSetValue = (1 - exp( -thisEvent.TimeSpan/thisEvent.TC[i] )) * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
452             break;
453           default:
454             cerr << "Invalid Action specified" << endl;
455             break;
456           }
457           thisEvent.SetParam[i]->setDoubleValue(newSetValue);
458         }
459       }
460
461       // Print notification values after setting them
462       if (thisEvent.Notify && !thisEvent.Notified) {
463         if (thisEvent.NotifyKML) {
464           cout << endl << "<Placemark>" << endl;
465           cout << "  <name> " << currentTime << " seconds" << " </name>" << endl;
466           cout << "  <description>" << endl;
467           cout << "  <![CDATA[" << endl;
468           cout << "  <b>" << thisEvent.Name << " (Event " << event_ctr << ")" << " executed at time: " << currentTime << "</b><br/>" << endl;
469         } else  {
470           cout << endl << underon
471                << highint << thisEvent.Name << normint << underoff
472                << " (Event " << event_ctr << ")" 
473                << " executed at time: " << highint << currentTime << normint << endl;
474         }
475         if (!thisEvent.Description.empty()) {
476           cout << "    " << thisEvent.Description << endl;
477         }
478         for (j=0; j<thisEvent.NotifyProperties.size();j++) {
479           if (thisEvent.NotifyProperties[j] == 0) {
480             if (PropertyManager->HasNode(thisEvent.NotifyPropertyNames[j])) {
481               thisEvent.NotifyProperties[j] = PropertyManager->GetNode(thisEvent.NotifyPropertyNames[j]);
482             } else {
483               throw("Could not find property named "+thisEvent.NotifyPropertyNames[j]+" in script.");
484             }
485           }
486           cout << "    " << thisEvent.DisplayString[j] << " = " << thisEvent.NotifyProperties[j]->getDoubleValue();
487           if (thisEvent.NotifyKML) cout << " <br/>";
488           cout << endl;
489         }
490         if (thisEvent.NotifyKML) {
491           cout << "  ]]>" << endl;
492           cout << "  </description>" << endl;
493           cout << "  <Point>" << endl;
494           cout << "    <altitudeMode> absolute </altitudeMode>" << endl;
495           cout << "    <extrude> 1 </extrude>" << endl;
496           cout << "    <coordinates>" << FDMExec->GetPropagate()->GetLongitudeDeg()
497             << "," << FDMExec->GetPropagate()->GetGeodLatitudeDeg()
498             << "," << FDMExec->GetPropagate()->GetAltitudeASLmeters() << "</coordinates>" << endl;
499           cout << "  </Point>" << endl;
500           cout << "</Placemark>" << endl;
501         }
502         cout << endl;
503         thisEvent.Notified = true;
504       }
505
506     }
507
508     event_ctr++;
509   }
510   return true;
511 }
512
513 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514 //    The bitmasked value choices are as follows:
515 //    unset: In this case (the default) JSBSim would only print
516 //       out the normally expected messages, essentially echoing
517 //       the config files as they are read. If the environment
518 //       variable is not set, debug_lvl is set to 1 internally
519 //    0: This requests JSBSim not to output any messages
520 //       whatsoever.
521 //    1: This value explicity requests the normal JSBSim
522 //       startup messages
523 //    2: This value asks for a message to be printed out when
524 //       a class is instantiated
525 //    4: When this value is set, a message is displayed when a
526 //       FGModel object executes its Run() method
527 //    8: When this value is set, various runtime state variables
528 //       are printed out periodically
529 //    16: When set various parameters are sanity checked and
530 //       a message is printed out when they go out of bounds
531
532 void FGScript::Debug(int from)
533 {
534   if (debug_lvl <= 0) return;
535
536   if (debug_lvl & 1) { // Standard console startup message output
537     if (from == 0) { // Constructor
538     } else if (from == 3) {
539     } else if (from == 4)  { // print out script data
540       cout << endl;
541       cout << "Script: \"" << ScriptName << "\"" << endl;
542       cout << "  begins at " << StartTime << " seconds and runs to " << EndTime
543         << " seconds with dt = " << setprecision(6) << FDMExec->GetDeltaT() << " (" <<
544         ceil(1.0/FDMExec->GetDeltaT()) << " Hz)" << endl;
545       cout << endl;
546
547       map<FGPropertyNode_ptr, double>::iterator it = interface_prop_initial_value.begin();
548       for (; it != interface_prop_initial_value.end(); ++it) {
549         FGPropertyNode* node = it->first;
550         cout << "Local property: " << node->GetName()
551              << " = " << node->getDoubleValue()
552              << endl;
553       }
554       
555       if (!interface_prop_initial_value.empty()) cout << endl;
556
557       for (unsigned i=0; i<Events.size(); i++) {
558         cout << "Event " << i;
559         if (!Events[i].Name.empty()) cout << " (" << Events[i].Name << ")";
560         cout << ":" << endl;
561
562         if (Events[i].Persistent)
563           cout << "  " << "Whenever triggered, executes once";
564         else if (Events[i].Continuous)
565           cout << "  " << "While true, always executes";
566         else
567           cout << "  " << "When first triggered, executes once";
568
569         Events[i].Condition->PrintCondition();
570
571         cout << endl << "  Actions taken";
572         if (Events[i].Delay > 0.0)
573           cout << " (after a delay of " << Events[i].Delay << " secs)";
574         cout << ":" << endl << "    {";
575         for (unsigned j=0; j<Events[i].SetValue.size(); j++) {
576           if (Events[i].SetValue[j] == 0.0 && Events[i].Functions[j] != 0L) {
577             if (Events[i].SetParam[j] == 0) {
578               if (Events[i].SetParamName[j].size() == 0) {
579               cerr << fgred << highint << endl
580                    << "  An attempt has been made to access a non-existent property" << endl
581                    << "  in this event. Please check the property names used, spelling, etc."
582                    << reset << endl;
583               exit(-1);
584               } else {
585                 cout << endl << "      set " << Events[i].SetParamName[j]
586                      << " to function value (Late Bound)";
587             }
588             } else {
589             cout << endl << "      set " << Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
590                  << " to function value";
591             }
592           } else {
593             if (Events[i].SetParam[j] == 0) {
594               if (Events[i].SetParamName[j].size() == 0) {
595               cerr << fgred << highint << endl
596                    << "  An attempt has been made to access a non-existent property" << endl
597                    << "  in this event. Please check the property names used, spelling, etc."
598                    << reset << endl;
599               exit(-1);
600               } else {
601                 cout << endl << "      set " << Events[i].SetParamName[j]
602                      << " to function value (Late Bound)";
603             }
604             } else {
605             cout << endl << "      set " << Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
606                  << " to " << Events[i].SetValue[j];
607           }
608           }
609
610           switch (Events[i].Type[j]) {
611           case FG_VALUE:
612           case FG_BOOL:
613             cout << " (constant";
614             break;
615           case FG_DELTA:
616             cout << " (delta";
617             break;
618           default:
619             cout << " (unspecified type";
620           }
621
622           switch (Events[i].Action[j]) {
623           case FG_RAMP:
624             cout << " via ramp";
625             break;
626           case FG_STEP:
627             cout << " via step)";
628             break;
629           case FG_EXP:
630             cout << " via exponential approach";
631             break;
632           default:
633             cout << " via unspecified action)";
634           }
635
636           if (Events[i].Action[j] == FG_RAMP || Events[i].Action[j] == FG_EXP)
637             cout << " with time constant " << Events[i].TC[j] << ")";
638         }
639         cout << endl << "    }" << endl;
640
641         // Print notifications
642         if (Events[i].Notify) {
643           if (Events[i].NotifyProperties.size() > 0) {
644             if (Events[i].NotifyKML) {
645               cout << "  Notifications (KML Format):" << endl << "    {" << endl;
646             } else {
647               cout << "  Notifications:" << endl << "    {" << endl;
648             }
649             for (unsigned j=0; j<Events[i].NotifyPropertyNames.size();j++) {
650               cout << "      "
651                    << Events[i].NotifyPropertyNames[j]
652                    << endl;
653             }
654             cout << "    }" << endl;
655           }
656         }
657         cout << endl;
658       }
659     }
660   }
661   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
662     if (from == 0) cout << "Instantiated: FGScript" << endl;
663     if (from == 1) cout << "Destroyed:    FGScript" << endl;
664   }
665   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
666   }
667   if (debug_lvl & 8 ) { // Runtime state variables
668   }
669   if (debug_lvl & 16) { // Sanity checking
670   }
671   if (debug_lvl & 64) {
672     if (from == 0) { // Constructor
673       cout << IdSrc << endl;
674       cout << IdHdr << endl;
675     }
676   }
677 }
678 }