49 #include "FGFDMExec.h" 50 #include "input_output/FGXMLElement.h" 51 #include "input_output/FGXMLFileRead.h" 52 #include "initialization/FGInitialCondition.h" 53 #include "models/FGInput.h" 54 #include "math/FGCondition.h" 55 #include "math/FGFunction.h" 61 IDENT(IdSrc,
"$Id: FGScript.cpp,v 1.65 2017/02/25 14:23:18 bcoconni Exp $");
62 IDENT(IdHdr,ID_FGSCRIPT);
87 for (i=0; i<Events.size(); i++) {
88 delete Events[i].Condition;
89 for (j=0; j<Events[i].Functions.size(); j++)
90 delete Events[i].Functions[j];
100 const SGPath& initfile)
103 string aircraft=
"", prop_name=
"";
104 string notifyPropertyName=
"";
105 Element *element=0, *run_element=0, *event_element=0;
107 Element *notify_element = 0L, *notify_property_element = 0L;
108 double dt = 0.0, value = 0.0;
112 Element* document = XMLFileRead.LoadXMLDocument(script);
115 cerr <<
"File: " << script <<
" could not be loaded." << endl;
119 if (document->
GetName() != string(
"runscript")) {
120 cerr <<
"File: " << script <<
" is not a script file" << endl;
131 cerr <<
"No \"run\" element found in script." << endl;
137 if (run_element->HasAttribute(
"start"))
138 StartTime = run_element->GetAttributeValueAsNumber(
"start");
142 if (run_element->HasAttribute(
"end")) {
143 EndTime = run_element->GetAttributeValueAsNumber(
"end");
145 cerr <<
"An end time (duration) for the script must be specified in the script <run> element." << endl;
152 if (default_dT == 0.0)
153 dt = run_element->GetAttributeValueAsNumber(
"dt");
156 cout << endl <<
"Overriding simulation step size from the command line. New step size is: " 157 << default_dT <<
" seconds (" << 1/default_dT <<
" Hz)" << endl << endl;
167 if (!aircraft.empty()) {
171 cerr <<
"Aircraft must be specified in use element." << endl;
175 initialize = SGPath::fromLocal8Bit(element->
GetAttributeValue(
"initialize").c_str());
176 if (initfile.isNull()) {
177 if (initialize.isNull()) {
178 cerr <<
"Initialization file must be specified in use element." << endl;
182 cout << endl <<
"The initialization file specified in the script file (" << initialize
183 <<
") has been overridden with a specified file (" << initfile <<
")." << endl;
184 initialize = initfile;
188 cerr <<
"No \"use\" directives in the script file." << endl;
193 if ( ! IC->
Load( initialize )) {
194 cerr <<
"Initialization unsuccessful" << endl;
217 int saved_debug_lvl = debug_lvl;
219 LocalProperties.Load(run_element, PropertyManager,
true);
220 debug_lvl = saved_debug_lvl;
224 event_element = run_element->FindElement(
"event");
225 while (event_element) {
228 struct event *newEvent =
new struct event();
231 newEvent->Name = event_element->GetAttributeValue(
"name");
235 if (event_element->GetAttributeValue(
"persistent") == string(
"true")) {
236 newEvent->Persistent =
true;
240 if (event_element->GetAttributeValue(
"continuous") == string(
"true")) {
241 newEvent->Continuous =
true;
246 if (condition_element != 0) {
248 newCondition =
new FGCondition(condition_element, PropertyManager);
249 }
catch(
string& str) {
250 cout << endl <<
fgred << str <<
reset << endl << endl;
254 newEvent->Condition = newCondition;
256 cerr <<
"No condition specified in script event " << newEvent->Name << endl;
268 newEvent->Delay = 0.0;
271 if ((notify_element = event_element->
FindElement(
"notify")) != 0) {
273 if (notify_element->
GetAttributeValue(
"format") ==
"kml") newEvent->NotifyKML =
true;
275 newEvent->Notify =
true;
277 string notify_description = notify_element->
FindElementValue(
"description");
278 if (!notify_description.empty()) {
279 newEvent->Description = notify_description;
281 notify_property_element = notify_element->
FindElement(
"property");
282 while (notify_property_element) {
283 notifyPropertyName = notify_property_element->
GetDataLine();
285 newEvent->NotifyPropertyNames.push_back(notifyPropertyName);
286 newEvent->NotifyProperties.push_back(0);
287 string caption_attribute = notify_property_element->GetAttributeValue(
"caption");
288 if (caption_attribute.empty()) {
289 newEvent->DisplayString.push_back(notifyPropertyName);
291 newEvent->DisplayString.push_back(caption_attribute);
300 while (set_element) {
302 if (PropertyManager->HasNode(prop_name)) {
303 newEvent->SetParam.push_back( PropertyManager->GetNode(prop_name) );
305 newEvent->SetParam.push_back( 0L );
307 newEvent->SetParamName.push_back( prop_name );
313 newEvent->Functions.push_back((
FGFunction*)0L);
316 newEvent->Functions.push_back(
new FGFunction(PropertyManager, set_element->
FindElement(
"function")));
318 newEvent->SetValue.push_back(value);
319 newEvent->OriginalValue.push_back(0.0);
320 newEvent->newValue.push_back(0.0);
321 newEvent->ValueSpan.push_back(0.0);
323 if (to_lower(tempCompare).find(
"delta") != string::npos) newEvent->Type.push_back(FG_DELTA);
324 else if (to_lower(tempCompare).find(
"bool") != string::npos) newEvent->Type.push_back(FG_BOOL);
325 else if (to_lower(tempCompare).find(
"value") != string::npos) newEvent->Type.push_back(FG_VALUE);
326 else newEvent->Type.push_back(FG_VALUE);
328 if (to_lower(tempCompare).find(
"ramp") != string::npos) newEvent->Action.push_back(FG_RAMP);
329 else if (to_lower(tempCompare).find(
"step") != string::npos) newEvent->Action.push_back(FG_STEP);
330 else if (to_lower(tempCompare).find(
"exp") != string::npos) newEvent->Action.push_back(FG_EXP);
331 else newEvent->Action.push_back(FG_STEP);
336 newEvent->TC.push_back(1.0);
338 newEvent->Transiting.push_back(
false);
342 Events.push_back(*newEvent);
345 event_element = run_element->FindNextElement(
"event");
355 void FGScript::ResetEvents(
void)
357 LocalProperties.ResetToIC();
360 for (
unsigned int i=0; i<Events.size(); i++)
369 unsigned event_ctr = 0;
372 double newSetValue = 0;
374 if (currentTime > EndTime)
return false;
377 for (
unsigned int ev_ctr=0; ev_ctr < Events.size(); ev_ctr++) {
379 struct event &thisEvent = Events[ev_ctr];
386 if (thisEvent.Condition->Evaluate()) {
387 if (!thisEvent.Triggered) {
390 for (i=0; i<thisEvent.SetValue.size(); i++) {
391 if (thisEvent.SetParam[i] == 0L) {
392 if (PropertyManager->HasNode(thisEvent.SetParamName[i])) {
393 thisEvent.SetParam[i] = PropertyManager->GetNode(thisEvent.SetParamName[i]);
395 throw(
"No property, \""+thisEvent.SetParamName[i]+
"\" is defined.");
398 thisEvent.OriginalValue[i] = thisEvent.SetParam[i]->getDoubleValue();
399 if (thisEvent.Functions[i] != 0) {
401 thisEvent.SetValue[i] = thisEvent.Functions[i]->GetValue();
402 }
catch (
string& msg) {
403 std::cerr << std::endl <<
"A problem occurred in the execution of the script. " << msg << endl;
407 switch (thisEvent.Type[i]) {
410 thisEvent.newValue[i] = thisEvent.SetValue[i];
413 thisEvent.newValue[i] = thisEvent.OriginalValue[i] + thisEvent.SetValue[i];
416 cerr <<
"Invalid Type specified" << endl;
419 thisEvent.StartTime = currentTime + thisEvent.Delay;
420 thisEvent.ValueSpan[i] = thisEvent.newValue[i] - thisEvent.OriginalValue[i];
421 thisEvent.Transiting[i] =
true;
424 thisEvent.Triggered =
true;
426 }
else if (thisEvent.Persistent) {
427 thisEvent.Triggered =
false;
428 thisEvent.Notified =
false;
429 }
else if (thisEvent.Continuous) {
430 thisEvent.Triggered =
false;
431 thisEvent.Notified =
false;
434 if ((currentTime >= thisEvent.StartTime) && thisEvent.Triggered) {
436 for (i=0; i<thisEvent.SetValue.size(); i++) {
437 if (thisEvent.Transiting[i]) {
438 thisEvent.TimeSpan = currentTime - thisEvent.StartTime;
439 switch (thisEvent.Action[i]) {
441 if (thisEvent.TimeSpan <= thisEvent.TC[i]) {
442 newSetValue = thisEvent.TimeSpan/thisEvent.TC[i] * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
444 newSetValue = thisEvent.newValue[i];
445 if (thisEvent.Continuous !=
true) thisEvent.Transiting[i] =
false;
449 newSetValue = thisEvent.newValue[i];
455 if (thisEvent.Continuous !=
true)
456 thisEvent.Transiting[i] =
false;
457 else if (thisEvent.Functions[i] != 0)
458 newSetValue = thisEvent.Functions[i]->GetValue();
462 newSetValue = (1 - exp( -thisEvent.TimeSpan/thisEvent.TC[i] )) * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
465 cerr <<
"Invalid Action specified" << endl;
468 thisEvent.SetParam[i]->setDoubleValue(newSetValue);
473 if (thisEvent.Notify && !thisEvent.Notified) {
474 if (thisEvent.NotifyKML) {
475 cout << endl <<
"<Placemark>" << endl;
476 cout <<
" <name> " << currentTime <<
" seconds" <<
" </name>" << endl;
477 cout <<
" <description>" << endl;
478 cout <<
" <![CDATA[" << endl;
479 cout <<
" <b>" << thisEvent.Name <<
" (Event " << event_ctr <<
")" <<
" executed at time: " << currentTime <<
"</b><br/>" << endl;
483 <<
" (Event " << event_ctr <<
")" 484 <<
" executed at time: " <<
highint << currentTime <<
normint << endl;
486 if (!thisEvent.Description.empty()) {
487 cout <<
" " << thisEvent.Description << endl;
489 for (j=0; j<thisEvent.NotifyProperties.size();j++) {
490 if (thisEvent.NotifyProperties[j] == 0) {
491 if (PropertyManager->HasNode(thisEvent.NotifyPropertyNames[j])) {
492 thisEvent.NotifyProperties[j] = PropertyManager->GetNode(thisEvent.NotifyPropertyNames[j]);
494 throw(
"Could not find property named "+thisEvent.NotifyPropertyNames[j]+
" in script.");
497 cout <<
" " << thisEvent.DisplayString[j] <<
" = " << thisEvent.NotifyProperties[j]->getDoubleValue();
498 if (thisEvent.NotifyKML) cout <<
" <br/>";
501 if (thisEvent.NotifyKML) {
502 cout <<
" ]]>" << endl;
503 cout <<
" </description>" << endl;
504 cout <<
" <Point>" << endl;
505 cout <<
" <altitudeMode> absolute </altitudeMode>" << endl;
506 cout <<
" <extrude> 1 </extrude>" << endl;
507 cout <<
" <coordinates>" << FDMExec->
GetPropagate()->GetLongitudeDeg()
510 cout <<
" </Point>" << endl;
511 cout <<
"</Placemark>" << endl;
514 thisEvent.Notified =
true;
543 void FGScript::Debug(
int from)
545 if (debug_lvl <= 0)
return;
549 }
else if (from == 3) {
550 }
else if (from == 4) {
552 cout <<
"Script: \"" << ScriptName <<
"\"" << endl;
553 cout <<
" begins at " << StartTime <<
" seconds and runs to " << EndTime
554 <<
" seconds with dt = " << setprecision(6) << FDMExec->
GetDeltaT() <<
" (" <<
555 ceil(1.0/FDMExec->
GetDeltaT()) <<
" Hz)" << endl;
559 for (it = LocalProperties.begin(); it != LocalProperties.end(); ++it) {
561 cout <<
"Local property: " << node->
GetName()
562 <<
" = " << node->getDoubleValue()
566 if (LocalProperties.empty()) cout << endl;
568 for (
unsigned i=0; i<Events.size(); i++) {
569 cout <<
"Event " << i;
570 if (!Events[i].Name.empty()) cout <<
" (" << Events[i].Name <<
")";
573 if (Events[i].Persistent)
574 cout <<
" " <<
"Whenever triggered, executes once";
575 else if (Events[i].Continuous)
576 cout <<
" " <<
"While true, always executes";
578 cout <<
" " <<
"When first triggered, executes once";
580 Events[i].Condition->PrintCondition();
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) {
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." 596 cout << endl <<
" set " << Events[i].SetParamName[j]
597 <<
" to function value (Late Bound)";
600 cout << endl <<
" set " << Events[i].SetParam[j]->GetRelativeName(
"/fdm/jsbsim/")
601 <<
" to function value";
604 if (Events[i].SetParam[j] == 0) {
605 if (Events[i].SetParamName[j].size() == 0) {
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." 612 cout << endl <<
" set " << Events[i].SetParamName[j]
613 <<
" to function value (Late Bound)";
616 cout << endl <<
" set " << Events[i].SetParam[j]->GetRelativeName(
"/fdm/jsbsim/")
617 <<
" to " << Events[i].SetValue[j];
621 switch (Events[i].Type[j]) {
624 cout <<
" (constant";
630 cout <<
" (unspecified type";
633 switch (Events[i].Action[j]) {
638 cout <<
" via step)";
641 cout <<
" via exponential approach";
644 cout <<
" via unspecified action)";
647 if (Events[i].Action[j] == FG_RAMP || Events[i].Action[j] == FG_EXP)
648 cout <<
" with time constant " << Events[i].TC[j] <<
")";
650 cout << endl <<
" }" << endl;
653 if (Events[i].Notify) {
654 if (Events[i].NotifyProperties.size() > 0) {
655 if (Events[i].NotifyKML) {
656 cout <<
" Notifications (KML Format):" << endl <<
" {" << endl;
658 cout <<
" Notifications:" << endl <<
" {" << endl;
660 for (
unsigned j=0; j<Events[i].NotifyPropertyNames.size();j++) {
662 << Events[i].NotifyPropertyNames[j]
665 cout <<
" }" << endl;
672 if (debug_lvl & 2 ) {
673 if (from == 0) cout <<
"Instantiated: FGScript" << endl;
674 if (from == 1) cout <<
"Destroyed: FGScript" << endl;
676 if (debug_lvl & 4 ) {
678 if (debug_lvl & 8 ) {
680 if (debug_lvl & 16) {
682 if (debug_lvl & 64) {
684 cout << IdSrc << endl;
685 cout << IdHdr << endl;
FGInitialCondition * GetIC(void)
Returns a pointer to the FGInitialCondition object.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
FGOutput * GetOutput(void)
Returns the FGOutput pointer.
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
std::string GetName(void) const
Get the name of a node.
Class wrapper for property handling.
static char reset[5]
resets text properties
double GetAltitudeASLmeters(void) const
Returns the current altitude above sea level.
FGInput * GetInput(void)
Returns the FGInput pointer.
bool LoadScript(const SGPath &script, double default_dT, const SGPath &initfile)
Loads a script to drive JSBSim (usually in standalone mode).
Element * FindElement(const std::string &el="")
Searches for a specified element.
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
static char normint[6]
normal intensity text
FGPropertyManager * GetPropertyManager(void)
Returns a pointer to the property manager object.
static char fgred[6]
red text
double Setsim_time(double cur_time)
Sets the current sim time.
void Setdt(double delta_t)
Sets the integration time step for the simulation executive.
bool RunScript(void)
This function is called each pass through the executive Run() method IF scripting is enabled...
double GetAttributeValueAsNumber(const std::string &key)
Retrieves an attribute value as a double precision real number.
const std::string & GetName(void) const
Retrieves the element name.
std::string FindElementValue(const std::string &el="")
Searches for the named element and returns the string data belonging to it.
~FGScript()
Default destructor.
Represents a mathematical function.
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
bool Load(const SGPath &rstname, bool useStoredPath=true)
Loads the initial conditions.
static char underoff[6]
underline off
bool Load(Element *el)
Load the output directives and adds a new output instance to the Output Manager list.
Initializes the simulation run.
double GetSimTime(void) const
Returns the cumulative simulation time in seconds.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
static char highint[5]
highlights text
double GetDeltaT(void) const
Returns the simulation delta T.
Encapsulates a condition, which is used in parts of JSBSim including switches.
Encapsulates the JSBSim simulation executive.
bool LoadModel(const SGPath &AircraftPath, const SGPath &EnginePath, const SGPath &SystemsPath, const std::string &model, bool addModelToPath=true)
Loads an aircraft model.
static char underon[5]
underlines text
FGPropagate * GetPropagate(void)
Returns the FGPropagate pointer.