JSBSim Flight Dynamics Model 1.3.0 (09 Apr 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
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
12 Software Foundation; either version 2 of the License, or (at your option) any
13 later 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
21 with this program; if not, write to the Free Software Foundation, Inc., 59
22 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 Further information about the GNU Lesser General Public License can also be
25 found on the world wide web at http://www.gnu.org.
26
27FUNCTIONAL DESCRIPTION
28--------------------------------------------------------------------------------
29
30This class wraps up the simulation scripting routines.
31
32HISTORY
33--------------------------------------------------------------------------------
3412/21/01 JSB Created
35
36%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37COMMENTS, REFERENCES, and NOTES
38%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39
40%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41INCLUDES
42%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
43
44#include <iomanip>
45
46#include "FGScript.h"
47#include "FGFDMExec.h"
48#include "input_output/FGXMLFileRead.h"
49#include "initialization/FGInitialCondition.h"
50#include "models/FGInput.h"
51#include "math/FGCondition.h"
52#include "math/FGFunctionValue.h"
53#include "input_output/string_utilities.h"
54
55using namespace std;
56
57namespace JSBSim {
58
59/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60CLASS IMPLEMENTATION
61%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
62
63// Constructor
64
65FGScript::FGScript(FGFDMExec* fgex) : FDMExec(fgex)
66{
67 PropertyManager=FDMExec->GetPropertyManager();
68
69 Debug(0);
70}
71
72//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73
75{
76 unsigned int i, j;
77
78 for (i=0; i<Events.size(); i++) {
79 delete Events[i].Condition;
80 for (j=0; j<Events[i].Functions.size(); j++)
81 delete Events[i].Functions[j];
82 for (j=0; j<Events[i].NotifyProperties.size(); j++)
83 delete Events[i].NotifyProperties[j];
84 }
85 Events.clear();
86
87 Debug(1);
88}
89
90//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91
92bool FGScript::LoadScript(const SGPath& script, double default_dT,
93 const SGPath& initfile)
94{
95 SGPath initialize;
96 string aircraft="", prop_name="";
97 string notifyPropertyName="";
98 Element *element=0, *run_element=0, *event_element=0;
99 Element *set_element=0;
100 Element *notify_element = 0L, *notify_property_element = 0L;
101 double dt = 0.0, value = 0.0;
102 FGCondition *newCondition;
103
104 FGXMLFileRead XMLFileRead;
105 Element* document = XMLFileRead.LoadXMLDocument(script);
106
107 if (!document) {
108 FGLogging log(LogLevel::ERROR);
109 log << "File: " << script << " could not be loaded.\n";
110 return false;
111 }
112
113 if (document->GetName() != string("runscript")) {
114 FGXMLLogging log(document, LogLevel::ERROR);
115 log << "File: " << script << " is not a script file\n";
116 return false;
117 }
118
119 ScriptName = document->GetAttributeValue("name");
120
121 // First, find "run" element and set delta T
122
123 run_element = document->FindElement("run");
124
125 if (!run_element) {
126 FGXMLLogging log(document, LogLevel::ERROR);
127 log << "No \"run\" element found in script.\n";
128 return false;
129 }
130
131 // Set sim timing
132
133 if (run_element->HasAttribute("start"))
134 StartTime = run_element->GetAttributeValueAsNumber("start");
135 else
136 StartTime = 0.0;
137 FDMExec->Setsim_time(StartTime);
138 if (run_element->HasAttribute("end")) {
139 EndTime = run_element->GetAttributeValueAsNumber("end");
140 } else {
141 FGXMLLogging log(run_element, LogLevel::ERROR);
142 log << "An end time (duration) for the script must be specified in the script <run> element.\n";
143 return false;
144 }
145
146 if (default_dT == 0.0)
147 dt = run_element->GetAttributeValueAsNumber("dt");
148 else {
149 dt = default_dT;
150 FGLogging log(LogLevel::INFO);
151 log << "\nOverriding simulation step size from the command line. New step size is: "
152 << default_dT << " seconds (" << 1/default_dT << " Hz)\n\n";
153 }
154
155 FDMExec->Setdt(dt);
156
157 // Make sure that the desired time is reached and executed.
158 EndTime += 0.99*FDMExec->GetDeltaT();
159
160 // read aircraft and initialization files
161
162 element = document->FindElement("use");
163 if (element) {
164 aircraft = element->GetAttributeValue("aircraft");
165 if (!aircraft.empty()) {
166 if (!FDMExec->LoadModel(aircraft))
167 return false;
168 } else {
169 FGXMLLogging log(element, LogLevel::ERROR);
170 log << "Aircraft must be specified in use element.\n";
171 return false;
172 }
173
174 initialize = SGPath::fromLocal8Bit(element->GetAttributeValue("initialize").c_str());
175 if (initfile.isNull()) {
176 if (initialize.isNull()) {
177 FGXMLLogging log(element, LogLevel::ERROR);
178 log << "Initialization file must be specified in use element.\n";
179 return false;
180 }
181 } else {
182 FGLogging log(LogLevel::INFO);
183 log << "\nThe initialization file specified in the script file ("
184 << initialize << ") has been overridden with a specified file ("
185 << initfile << ").\n";
186 initialize = initfile;
187 }
188
189 } else {
190 FGXMLLogging log(document, LogLevel::ERROR);
191 log << "No \"use\" directives in the script file.\n";
192 return false;
193 }
194
195 auto IC = FDMExec->GetIC();
196 if ( ! IC->Load( initialize )) {
197 FGLogging log(LogLevel::ERROR);
198 log << "Initialization unsuccessful\n";
199 return false;
200 }
201
202 // Now, read input spec if given.
203 element = document->FindElement("input");
204 while (element) {
205 if (!FDMExec->GetInput()->Load(element))
206 return false;
207
208 element = document->FindNextElement("input");
209 }
210
211 // Now, read output spec if given.
212 element = document->FindElement("output");
213 SGPath scriptDir = SGPath(script.dir());
214 if (scriptDir.isNull())
215 scriptDir = SGPath(".");
216
217 while (element) {
218 if (!FDMExec->GetOutput()->Load(element, scriptDir))
219 return false;
220
221 element = document->FindNextElement("output");
222 }
223
224 // Read local property/value declarations
225 int saved_debug_lvl = debug_lvl;
226 debug_lvl = 0; // Disable messages
227 LocalProperties.Load(run_element, PropertyManager.get(), true);
228 debug_lvl = saved_debug_lvl;
229
230 // Read "events" from script
231
232 event_element = run_element->FindElement("event");
233 while (event_element) { // event processing
234
235 // Create the event structure
236 struct event *newEvent = new struct event();
237
238 // Retrieve the event name if given
239 newEvent->Name = event_element->GetAttributeValue("name");
240
241 // Is this event persistent? That is, does it execute every time the
242 // condition triggers to true, or does it execute as a one-shot event, only?
243 if (event_element->GetAttributeValue("persistent") == string("true")) {
244 newEvent->Persistent = true;
245 }
246
247 // Does this event execute continuously when triggered to true?
248 if (event_element->GetAttributeValue("continuous") == string("true")) {
249 newEvent->Continuous = true;
250 }
251
252 // Process the conditions
253 Element* condition_element = event_element->FindElement("condition");
254 if (condition_element) {
255 try {
256 newCondition = new FGCondition(condition_element, PropertyManager);
257 } catch(BaseException& e) {
258 FGXMLLogging log(condition_element, LogLevel::ERROR);
259 log << LogFormat::RED << e.what() << LogFormat::RESET << "\n\n";
260 delete newEvent;
261 return false;
262 }
263 newEvent->Condition = newCondition;
264 } else {
265 FGXMLLogging log(event_element, LogLevel::ERROR);
266 log << "No condition specified in script event " << newEvent->Name << "\n";
267 delete newEvent;
268 return false;
269 }
270
271 // Is there a delay between the time this event is triggered, and when the
272 // event actions are executed?
273
274 Element* delay_element = event_element->FindElement("delay");
275 if (delay_element)
276 newEvent->Delay = event_element->FindElementValueAsNumber("delay");
277 else
278 newEvent->Delay = 0.0;
279
280 // Notify about when this event is triggered?
281 if ((notify_element = event_element->FindElement("notify")) != 0) {
282 if (notify_element->HasAttribute("format")) {
283 if (notify_element->GetAttributeValue("format") == "kml") newEvent->NotifyKML = true;
284 }
285 newEvent->Notify = true;
286 // Check here for new <description> tag that gets echoed
287 string notify_description = notify_element->FindElementValue("description");
288 if (!notify_description.empty()) {
289 newEvent->Description = notify_description;
290 }
291 notify_property_element = notify_element->FindElement("property");
292 while (notify_property_element) {
293 notifyPropertyName = notify_property_element->GetDataLine();
294
295 if (notify_property_element->HasAttribute("apply")) {
296 string function_str = notify_property_element->GetAttributeValue("apply");
297 auto f = FDMExec->GetTemplateFunc(function_str);
298 if (f)
299 newEvent->NotifyProperties.push_back(new FGFunctionValue(notifyPropertyName, PropertyManager, f,
300 notify_property_element));
301 else {
302 FGXMLLogging log(notify_property_element, LogLevel::WARN);
303 log << LogFormat::RED << LogFormat::BOLD << " No function by the name "
304 << function_str << " has been defined. This property will "
305 << "not be logged. You should check your configuration file.\n"
306 << LogFormat::RESET;
307 }
308 }
309 else
310 newEvent->NotifyProperties.push_back(new FGPropertyValue(notifyPropertyName, PropertyManager,
311 notify_property_element));
312
313 string caption_attribute = notify_property_element->GetAttributeValue("caption");
314 if (caption_attribute.empty()) {
315 newEvent->DisplayString.push_back(notifyPropertyName);
316 } else {
317 newEvent->DisplayString.push_back(caption_attribute);
318 }
319
320 notify_property_element = notify_element->FindNextElement("property");
321 }
322 }
323
324 // Read set definitions (these define the actions to be taken when the event
325 // is triggered).
326 set_element = event_element->FindElement("set");
327 while (set_element) {
328 prop_name = set_element->GetAttributeValue("name");
329 if (PropertyManager->HasNode(prop_name)) {
330 newEvent->SetParam.push_back( PropertyManager->GetNode(prop_name) );
331 } else {
332 newEvent->SetParam.push_back( 0L );
333 }
334 newEvent->SetParamName.push_back( prop_name );
335
336 // Todo - should probably do some safety checking here to make sure one or
337 // the other of value or function is specified.
338 if (!set_element->GetAttributeValue("value").empty()) {
339 value = set_element->GetAttributeValueAsNumber("value");
340 newEvent->Functions.push_back(nullptr);
341 } else if (set_element->FindElement("function")) {
342 value = 0.0;
343 newEvent->Functions.push_back(new FGFunction(FDMExec, set_element->FindElement("function")));
344 }
345 newEvent->SetValue.push_back(value);
346 newEvent->OriginalValue.push_back(0.0);
347 newEvent->newValue.push_back(0.0);
348 newEvent->ValueSpan.push_back(0.0);
349 string tempCompare = set_element->GetAttributeValue("type");
350 if (to_lower(tempCompare).find("delta") != string::npos) newEvent->Type.push_back(FG_DELTA);
351 else if (to_lower(tempCompare).find("bool") != string::npos) newEvent->Type.push_back(FG_BOOL);
352 else if (to_lower(tempCompare).find("value") != string::npos) newEvent->Type.push_back(FG_VALUE);
353 else newEvent->Type.push_back(FG_VALUE); // DEFAULT
354 tempCompare = set_element->GetAttributeValue("action");
355 if (to_lower(tempCompare).find("ramp") != string::npos) newEvent->Action.push_back(FG_RAMP);
356 else if (to_lower(tempCompare).find("step") != string::npos) newEvent->Action.push_back(FG_STEP);
357 else if (to_lower(tempCompare).find("exp") != string::npos) newEvent->Action.push_back(FG_EXP);
358 else newEvent->Action.push_back(FG_STEP); // DEFAULT
359
360 if (!set_element->GetAttributeValue("tc").empty())
361 newEvent->TC.push_back(set_element->GetAttributeValueAsNumber("tc"));
362 else
363 newEvent->TC.push_back(1.0); // DEFAULT
364
365 newEvent->Transiting.push_back(false);
366
367 set_element = event_element->FindNextElement("set");
368 }
369 Events.push_back(*newEvent);
370 delete newEvent;
371
372 event_element = run_element->FindNextElement("event");
373 }
374
375 Debug(4);
376
377 return true;
378}
379
380//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
381
382void FGScript::ResetEvents(void)
383{
384 LocalProperties.ResetToIC();
385 FDMExec->Setsim_time(StartTime);
386
387 for (unsigned int i=0; i<Events.size(); i++)
388 Events[i].reset();
389}
390
391//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
392
394{
395 unsigned i, j;
396 unsigned event_ctr = 0;
397
398 double currentTime = FDMExec->GetSimTime();
399 double newSetValue = 0;
400
401 if (currentTime > EndTime) return false;
402
403 // Iterate over all events.
404 for (unsigned int ev_ctr=0; ev_ctr < Events.size(); ev_ctr++) {
405
406 struct event &thisEvent = Events[ev_ctr];
407
408 // Determine whether the set of conditional tests for this condition equate
409 // to true and should cause the event to execute. If the conditions evaluate
410 // to true, then the event is triggered. If the event is not persistent,
411 // then this trigger will remain set true. If the event is persistent, the
412 // trigger will reset to false when the condition evaluates to false.
413 if (thisEvent.Condition->Evaluate()) {
414 if (!thisEvent.Triggered) {
415
416 // The conditions are true, do the setting of the desired Event
417 // parameters
418 for (i=0; i<thisEvent.SetValue.size(); i++) {
419 if (thisEvent.SetParam[i] == 0L) { // Late bind property if necessary
420 if (PropertyManager->HasNode(thisEvent.SetParamName[i])) {
421 thisEvent.SetParam[i] = PropertyManager->GetNode(thisEvent.SetParamName[i]);
422 } else {
423 LogException err;
424 err << "No property, \"" << thisEvent.SetParamName[i] << "\" is defined.\n";
425 throw err;
426 }
427 }
428 thisEvent.OriginalValue[i] = thisEvent.SetParam[i]->getDoubleValue();
429 if (thisEvent.Functions[i] != 0) { // Parameter should be set to a function value
430 try {
431 thisEvent.SetValue[i] = thisEvent.Functions[i]->GetValue();
432 } catch (BaseException& e) {
433 LogException err;
434 err << "\nA problem occurred in the execution of the script. "
435 << e.what() << "\n";
436 throw err;
437 }
438 }
439 switch (thisEvent.Type[i]) {
440 case FG_VALUE:
441 case FG_BOOL:
442 thisEvent.newValue[i] = thisEvent.SetValue[i];
443 break;
444 case FG_DELTA:
445 thisEvent.newValue[i] = thisEvent.OriginalValue[i] + thisEvent.SetValue[i];
446 break;
447 default:
448 FGLogging log(LogLevel::WARN);
449 log << "Invalid Type specified\n";
450 break;
451 }
452 thisEvent.StartTime = currentTime + thisEvent.Delay;
453 thisEvent.ValueSpan[i] = thisEvent.newValue[i] - thisEvent.OriginalValue[i];
454 thisEvent.Transiting[i] = true;
455 }
456 }
457 thisEvent.Triggered = true;
458
459 } else if (thisEvent.Persistent) { // If the event is persistent, reset the trigger.
460 thisEvent.Triggered = false; // Reset the trigger for persistent events
461 thisEvent.Notified = false; // Also reset the notification flag
462 } else if (thisEvent.Continuous) { // If the event is continuous, reset the trigger.
463 thisEvent.Triggered = false; // Reset the trigger for persistent events
464 thisEvent.Notified = false; // Also reset the notification flag
465 }
466
467 if ((currentTime >= thisEvent.StartTime) && thisEvent.Triggered) {
468
469 for (i=0; i<thisEvent.SetValue.size(); i++) {
470 if (thisEvent.Transiting[i]) {
471 thisEvent.TimeSpan = currentTime - thisEvent.StartTime;
472 switch (thisEvent.Action[i]) {
473 case FG_RAMP:
474 if (thisEvent.TimeSpan <= thisEvent.TC[i]) {
475 newSetValue = thisEvent.TimeSpan/thisEvent.TC[i] * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
476 } else {
477 newSetValue = thisEvent.newValue[i];
478 if (thisEvent.Continuous != true) thisEvent.Transiting[i] = false;
479 }
480 break;
481 case FG_STEP:
482 newSetValue = thisEvent.newValue[i];
483
484 // If this is not a continuous event, reset the transiting flag.
485 // Otherwise, it is known that the event is a continuous event.
486 // Furthermore, if the event is to be determined by a function,
487 // then the function will be continuously calculated.
488 if (thisEvent.Continuous != true)
489 thisEvent.Transiting[i] = false;
490 else if (thisEvent.Functions[i] != 0)
491 newSetValue = thisEvent.Functions[i]->GetValue();
492
493 break;
494 case FG_EXP:
495 newSetValue = (1 - exp( -thisEvent.TimeSpan/thisEvent.TC[i] )) * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
496 break;
497 default:
498 FGLogging log(LogLevel::WARN);
499 log << "Invalid Action specified\n";
500 break;
501 }
502 thisEvent.SetParam[i]->setDoubleValue(newSetValue);
503 }
504 }
505
506 // Print notification values after setting them
507 if (thisEvent.Notify && !thisEvent.Notified) {
508 LogLevel level = thisEvent.NotifyKML ? LogLevel::STDOUT : LogLevel::INFO;
509 FGLogging out(level);
510 if (thisEvent.NotifyKML) {
511 out << "\n<Placemark>\n";
512 out << " <name> " << currentTime << " seconds" << " </name>\n";
513 out << " <description>\n";
514 out << " <![CDATA[\n";
515 out << " <b>" << thisEvent.Name << " (Event " << event_ctr << ")"
516 << " executed at time: " << currentTime << "</b><br/>\n";
517 } else {
518 out << "\n" << LogFormat::UNDERLINE_ON << LogFormat::BOLD
519 << thisEvent.Name << LogFormat::NORMAL << LogFormat::UNDERLINE_OFF
520 << " (Event " << event_ctr << ")"
521 << " executed at time: " << LogFormat::BOLD << currentTime << LogFormat::NORMAL
522 << "\n";
523 }
524 if (!thisEvent.Description.empty()) {
525 out << " " << thisEvent.Description << "\n";
526 }
527 for (j=0; j<thisEvent.NotifyProperties.size();j++) {
528 out << " " << thisEvent.DisplayString[j] << " = "
529 << thisEvent.NotifyProperties[j]->getDoubleValue();
530 if (thisEvent.NotifyKML) out << " <br/>";
531 out << "\n";
532 }
533 if (thisEvent.NotifyKML) {
534 out << " ]]>\n";
535 out << " </description>\n";
536 out << " <Point>\n";
537 out << " <altitudeMode> absolute </altitudeMode>\n";
538 out << " <extrude> 1 </extrude>\n";
539 out << " <coordinates>"
540 << FDMExec->GetPropagate()->GetLongitudeDeg() << ","
541 << FDMExec->GetPropagate()->GetGeodLatitudeDeg() << ","
542 << FDMExec->GetPropagate()->GetAltitudeASLmeters()
543 << "</coordinates>\n";
544 out << " </Point>\n";
545 out << "</Placemark>\n";
546 }
547 out << "\n";
548 thisEvent.Notified = true;
549 }
550
551 }
552
553 event_ctr++;
554 }
555 return true;
556}
557
558//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559// The bitmasked value choices are as follows:
560// unset: In this case (the default) JSBSim would only print
561// out the normally expected messages, essentially echoing
562// the config files as they are read. If the environment
563// variable is not set, debug_lvl is set to 1 internally
564// 0: This requests JSBSim not to output any messages
565// whatsoever.
566// 1: This value explicity requests the normal JSBSim
567// startup messages
568// 2: This value asks for a message to be printed out when
569// a class is instantiated
570// 4: When this value is set, a message is displayed when a
571// FGModel object executes its Run() method
572// 8: When this value is set, various runtime state variables
573// are printed out periodically
574// 16: When set various parameters are sanity checked and
575// a message is printed out when they go out of bounds
576
577void FGScript::Debug(int from)
578{
579 if (debug_lvl <= 0) return;
580
581 if (debug_lvl & 1) { // Standard console startup message output
582 if (from == 0) { // Constructor
583 } else if (from == 3) {
584 } else if (from == 4) { // print out script data
585 FGLogging log(LogLevel::DEBUG);
586 log << "\nScript: \"" << ScriptName << "\"\n"
587 << " begins at " << StartTime << " seconds and runs to " << EndTime
588 << " seconds with dt = " << setprecision(6) << FDMExec->GetDeltaT()
589 << " (" << ceil(1.0/FDMExec->GetDeltaT()) << " Hz)\n\n";
590
591 for (auto node: LocalProperties) {
592 log << "Local property: " << node->getNameString()
593 << " = " << node->getDoubleValue() << "\n";
594 }
595
596 if (LocalProperties.empty()) log << "\n";
597
598 auto pm = FDMExec->GetPropertyManager();
599 const SGPropertyNode* root_node = pm->GetNode();
600 const string root_name = GetFullyQualifiedName(root_node) + "/";
601
602 for (unsigned i=0; i<Events.size(); i++) {
603 log << "Event " << i;
604 if (!Events[i].Name.empty()) log << " (" << Events[i].Name << ")";
605 log << ":\n";
606
607 if (Events[i].Persistent)
608 log << " " << "Whenever triggered, executes once";
609 else if (Events[i].Continuous)
610 log << " " << "While true, always executes";
611 else
612 log << " " << "When first triggered, executes once";
613
614 Events[i].Condition->PrintCondition();
615
616 log << "\n Actions taken";
617 if (Events[i].Delay > 0.0)
618 log << " (after a delay of " << Events[i].Delay << " secs)";
619 log << ":\n" << " {";
620 for (unsigned j=0; j<Events[i].SetValue.size(); j++) {
621 if (Events[i].SetValue[j] == 0.0 && Events[i].Functions[j] != 0L) {
622 if (Events[i].SetParam[j] == 0) {
623 if (Events[i].SetParamName[j].empty()) {
624 LogException err;
625 err << " An attempt has been made to access a non-existent property\n"
626 << " in this event. Please check the property names used, spelling, etc.\n";
627 throw err;
628 } else {
629 log << "\n set " << Events[i].SetParamName[j]
630 << " to function value (Late Bound)";
631 }
632 } else {
633 log << "\n set "
634 << GetRelativeName(Events[i].SetParam[j], root_name)
635 << " to function value";
636 }
637 } else {
638 if (Events[i].SetParam[j] == 0) {
639 if (Events[i].SetParamName[j].empty()) {
640 LogException err;
641 err << " An attempt has been made to access a non-existent property\n"
642 << " in this event. Please check the property names used, spelling, etc.\n";
643 throw err;
644 } else {
645 log << "\n set " << Events[i].SetParamName[j]
646 << " to function value (Late Bound)";
647 }
648 } else {
649 log << "\n set "
650 << GetRelativeName(Events[i].SetParam[j], root_name)
651 << " to " << Events[i].SetValue[j];
652 }
653 }
654
655 switch (Events[i].Type[j]) {
656 case FG_VALUE:
657 case FG_BOOL:
658 log << " (constant";
659 break;
660 case FG_DELTA:
661 log << " (delta";
662 break;
663 default:
664 log << " (unspecified type";
665 }
666
667 switch (Events[i].Action[j]) {
668 case FG_RAMP:
669 log << " via ramp";
670 break;
671 case FG_STEP:
672 log << " via step)";
673 break;
674 case FG_EXP:
675 log << " via exponential approach";
676 break;
677 default:
678 log << " via unspecified action)";
679 }
680
681 if (Events[i].Action[j] == FG_RAMP || Events[i].Action[j] == FG_EXP)
682 log << " with time constant " << Events[i].TC[j] << ")";
683 }
684 log << "\n }\n";
685
686 // Print notifications
687 if (Events[i].Notify) {
688 if (!Events[i].NotifyProperties.empty()) {
689 if (Events[i].NotifyKML) {
690 log << " Notifications (KML Format):\n" << " {\n";
691 } else {
692 log << " Notifications:\n" << " {\n";
693 }
694 for (unsigned j=0; j<Events[i].NotifyProperties.size();j++) {
695 log << " "
696 << Events[i].NotifyProperties[j]->GetPrintableName()
697 << "\n";
698 }
699 log << " }" << "\n";
700 }
701 }
702 log << "\n";
703 }
704 }
705 }
706 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
707 FGLogging log(LogLevel::DEBUG);
708 if (from == 0) log << "Instantiated: FGScript\n";
709 if (from == 1) log << "Destroyed: FGScript\n";
710 }
711 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
712 }
713 if (debug_lvl & 8 ) { // Runtime state variables
714 }
715 if (debug_lvl & 16) { // Sanity checking
716 }
717 if (debug_lvl & 64) {
718 if (from == 0) { // Constructor
719 }
720 }
721}
722}
Element * FindElement(const std::string &el="")
Searches for a specified element.
const std::string & GetName(void) const
Retrieves the element name.
double GetAttributeValueAsNumber(const std::string &key)
Retrieves an attribute value as a double precision real number.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
std::string FindElementValue(const std::string &el="")
Searches for the named element and returns the string data belonging to it.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
Encapsulates a condition, which is used in parts of JSBSim including switches.
Definition FGCondition.h:65
Encapsulates the JSBSim simulation executive.
Definition FGFDMExec.h:185
std::shared_ptr< FGInitialCondition > GetIC(void) const
Returns a pointer to the FGInitialCondition object.
Definition FGFDMExec.h:390
std::shared_ptr< FGOutput > GetOutput(void) const
Returns the FGOutput pointer.
double GetDeltaT(void) const
Returns the simulation delta T.
Definition FGFDMExec.h:553
double Setsim_time(double cur_time)
Sets the current sim time.
std::shared_ptr< FGPropagate > GetPropagate(void) const
Returns the FGPropagate pointer.
bool LoadModel(const SGPath &AircraftPath, const SGPath &EnginePath, const SGPath &SystemsPath, const std::string &model, bool addModelToPath=true)
Loads an aircraft model.
double GetSimTime(void) const
Returns the cumulative simulation time in seconds.
Definition FGFDMExec.h:550
void Setdt(double delta_t)
Sets the integration time step for the simulation executive.
Definition FGFDMExec.h:572
std::shared_ptr< FGPropertyManager > GetPropertyManager(void) const
Returns a pointer to the property manager object.
Definition FGFDMExec.h:422
std::shared_ptr< FGInput > GetInput(void) const
Returns the FGInput pointer.
Represents a property value on which a function is applied.
Represents a mathematical function.
Definition FGFunction.h:765
static char reset[5]
resets text properties
Definition FGJSBBase.h:157
Represents a property value which can use late binding.
bool RunScript(void)
This function is called each pass through the executive Run() method IF scripting is enabled.
Definition FGScript.cpp:393
~FGScript()
Default destructor.
Definition FGScript.cpp:74
FGScript(FGFDMExec *exec)
Default constructor.
Definition FGScript.cpp:65
bool LoadScript(const SGPath &script, double default_dT, const SGPath &initfile)
Loads a script to drive JSBSim (usually in standalone mode).
Definition FGScript.cpp:92
A node in a property tree.
Definition props.hxx:747
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:71