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