Date started: 12/21/01
Purpose: Loads and runs JSBSim scripts.
- ------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
+ ------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
-#ifdef FGFS
-# include <simgear/compiler.h>
-# include STL_IOSTREAM
-# include STL_ITERATOR
-#else
-# if defined(sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
-# include <iostream.h>
-# else
-# include <iostream>
-# endif
-# include <iterator>
-#endif
-
#include "FGScript.h"
-#include <input_output/FGXMLParse.h>
-#include <initialization/FGTrim.h>
+#include "input_output/FGXMLElement.h"
+#include "input_output/FGXMLParse.h"
+#include "initialization/FGTrim.h"
+
+#include <iostream>
+#include <cstdlib>
+#include <iomanip>
+
+using namespace std;
namespace JSBSim {
-static const char *IdSrc = "$Id$";
+static const char *IdSrc = "$Id: FGScript.cpp,v 1.46 2011/02/18 12:44:16 jberndt Exp $";
static const char *IdHdr = ID_FGSCRIPT;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGScript::FGScript(FGFDMExec* fgex) : FDMExec(fgex)
{
- State = FDMExec->GetState();
PropertyManager=FDMExec->GetPropertyManager();
+
Debug(0);
}
FGScript::~FGScript()
{
- unsigned int i;
- for (i=0; i<local_properties.size(); i++)
- PropertyManager->Untie(local_properties[i]->title);
-
- for (i=0; i<local_properties.size(); i++)
- delete local_properties[i];
+ unsigned int i, j;
+ for (i=0; i<local_properties.size(); i++) {
+ delete local_properties[i]->value;
+ delete local_properties[i];
+ }
local_properties.clear();
+
+ for (i=0; i<Events.size(); i++) {
+ delete Events[i].Condition;
+ for (j=0; j<Events[i].Functions.size(); j++)
+ delete Events[i].Functions[j];
+ }
+ Events.clear();
+
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-bool FGScript::LoadScript( string script )
+bool FGScript::LoadScript(string script, double deltaT)
{
string aircraft="", initialize="", comparison = "", prop_name="";
string notifyPropertyName="";
Element *condition_element=0, *set_element=0, *delay_element=0;
Element *notify_element = 0L, *notify_property_element = 0L;
Element *property_element = 0L;
+ Element *output_element = 0L;
+ Element *input_element = 0L;
bool result = false;
double dt = 0.0, value = 0.0;
struct event *newEvent;
document = LoadXMLDocument(script);
+ if (!document) {
+ cerr << "File: " << script << " could not be loaded." << endl;
+ return false;
+ }
+
+ // Set up input and output files if specified
+
+ output_element = document->FindElement("output");
+ input_element = document->FindElement("input");
+
if (document->GetName() != string("runscript")) {
cerr << "File: " << script << " is not a script file" << endl;
return false;
// Set sim timing
StartTime = run_element->GetAttributeValueAsNumber("start");
- State->Setsim_time(StartTime);
+ FDMExec->Setsim_time(StartTime);
EndTime = run_element->GetAttributeValueAsNumber("end");
- dt = run_element->GetAttributeValueAsNumber("dt");
- State->Setdt(dt);
+ // Make sure that the desired time is reached and executed.
+ EndTime += 0.99*FDMExec->GetDeltaT();
+
+ if (deltaT == 0.0)
+ dt = run_element->GetAttributeValueAsNumber("dt");
+ else {
+ dt = deltaT;
+ cout << endl << "Overriding simulation step size from the command line. New step size is: "
+ << deltaT << " seconds (" << 1/deltaT << " Hz)" << endl << endl;
+ }
+ FDMExec->Setdt(dt);
+
// read aircraft and initialization files
element = document->FindElement("use");
return false;
}
- // Read local property declarations
+ // Now, read input spec if given.
+ if (input_element > 0) {
+ FDMExec->GetInput()->Load(input_element);
+ }
+
+ // Now, read output spec if given.
+ if (output_element > 0) {
+ string output_file = output_element->GetAttributeValue("file");
+ if (output_file.empty()) {
+ cerr << "No logging directives file was specified." << endl;
+ } else {
+ if (!FDMExec->SetOutputDirectives(output_file)) return false;
+ }
+ }
+
+ // Read local property/value declarations
property_element = run_element->FindElement("property");
while (property_element) {
- LocalProps *localProp = new LocalProps();
- localProp->title = property_element->GetDataLine();
+
+ double value=0.0;
+ string title="";
+
+ title = property_element->GetDataLine();
+ if ( ! property_element->GetAttributeValue("value").empty())
+ value = property_element->GetAttributeValueAsNumber("value");
+
+ LocalProps *localProp = new LocalProps(value);
+ localProp->title = title;
local_properties.push_back(localProp);
- PropertyManager->Tie(localProp->title, (local_properties.back())->value);
+ if (PropertyManager->HasNode(title)) {
+ PropertyManager->GetNode(title)->setDoubleValue(value);
+ } else {
+ PropertyManager->Tie(localProp->title, localProp->value);
+ }
property_element = run_element->FindNextElement("property");
}
// Retrieve the event name if given
newEvent->Name = event_element->GetAttributeValue("name");
- // Is this event persistent? That is, does it execute repeatedly as long as the
- // condition is true, or does it execute as a one-shot event, only?
+ // Is this event persistent? That is, does it execute every time the
+ // condition triggers to true, or does it execute as a one-shot event, only?
if (event_element->GetAttributeValue("persistent") == string("true")) {
newEvent->Persistent = true;
}
+ // Does this event execute continuously when triggered to true?
+ if (event_element->GetAttributeValue("continuous") == string("true")) {
+ newEvent->Continuous = true;
+ }
+
// Process the conditions
condition_element = event_element->FindElement("condition");
if (condition_element != 0) {
- newCondition = new FGCondition(condition_element, PropertyManager);
+ try {
+ newCondition = new FGCondition(condition_element, PropertyManager);
+ } catch(string str) {
+ cout << endl << fgred << str << reset << endl << endl;
+ delete newEvent;
+ return false;
+ }
newEvent->Condition = newCondition;
} else {
cerr << "No condition specified in script event " << newEvent->Name << endl;
+ delete newEvent;
return false;
}
// Notify about when this event is triggered?
if ((notify_element = event_element->FindElement("notify")) != 0) {
newEvent->Notify = true;
+ // Check here for new <description> tag that gets echoed
+ string notify_description = notify_element->FindElementValue("description");
+ if (!notify_description.empty()) {
+ newEvent->Description = notify_description;
+ }
notify_property_element = notify_element->FindElement("property");
while (notify_property_element) {
notifyPropertyName = notify_property_element->GetDataLine();
- newEvent->NotifyProperties.push_back( PropertyManager->GetNode(notifyPropertyName) );
+ if (PropertyManager->GetNode(notifyPropertyName)) {
+ newEvent->NotifyProperties.push_back( PropertyManager->GetNode(notifyPropertyName) );
+ string caption_attribute = notify_property_element->GetAttributeValue("caption");
+ if (caption_attribute.empty()) {
+ newEvent->DisplayString.push_back(notifyPropertyName);
+ } else {
+ newEvent->DisplayString.push_back(caption_attribute);
+ }
+ } else {
+ cout << endl << fgred << " Could not find the property named "
+ << notifyPropertyName << " in script" << endl << " \""
+ << ScriptName << "\". Execution is aborted. Please recheck "
+ << "your input files and scripts." << reset << endl;
+ delete newEvent->Condition;
+ delete newEvent;
+ return false;
+ }
notify_property_element = notify_element->FindNextElement("property");
}
}
while (set_element) {
prop_name = set_element->GetAttributeValue("name");
newEvent->SetParam.push_back( PropertyManager->GetNode(prop_name) );
- value = set_element->GetAttributeValueAsNumber("value");
+ //Todo - should probably do some safety checking here to make sure one or the other
+ //of value or function is specified.
+ if (!set_element->GetAttributeValue("value").empty()) {
+ value = set_element->GetAttributeValueAsNumber("value");
+ newEvent->Functions.push_back((FGFunction*)0L);
+ } else if (set_element->FindElement("function")) {
+ value = 0.0;
+ newEvent->Functions.push_back(new FGFunction(PropertyManager, set_element->FindElement("function")));
+ }
newEvent->SetValue.push_back(value);
newEvent->OriginalValue.push_back(0.0);
newEvent->newValue.push_back(0.0);
newEvent->ValueSpan.push_back(0.0);
string tempCompare = set_element->GetAttributeValue("type");
- if (tempCompare == "FG_DELTA") newEvent->Type.push_back(FG_DELTA);
- else if (tempCompare == "FG_BOOL") newEvent->Type.push_back(FG_BOOL);
- else if (tempCompare == "FG_VALUE") newEvent->Type.push_back(FG_VALUE);
+ if (to_lower(tempCompare).find("delta") != string::npos) newEvent->Type.push_back(FG_DELTA);
+ else if (to_lower(tempCompare).find("bool") != string::npos) newEvent->Type.push_back(FG_BOOL);
+ else if (to_lower(tempCompare).find("value") != string::npos) newEvent->Type.push_back(FG_VALUE);
else newEvent->Type.push_back(FG_VALUE); // DEFAULT
tempCompare = set_element->GetAttributeValue("action");
- if (tempCompare == "FG_RAMP") newEvent->Action.push_back(FG_RAMP);
- else if (tempCompare == "FG_STEP") newEvent->Action.push_back(FG_STEP);
- else if (tempCompare == "FG_EXP") newEvent->Action.push_back(FG_EXP);
+ if (to_lower(tempCompare).find("ramp") != string::npos) newEvent->Action.push_back(FG_RAMP);
+ else if (to_lower(tempCompare).find("step") != string::npos) newEvent->Action.push_back(FG_STEP);
+ else if (to_lower(tempCompare).find("exp") != string::npos) newEvent->Action.push_back(FG_EXP);
else newEvent->Action.push_back(FG_STEP); // DEFAULT
if (!set_element->GetAttributeValue("tc").empty())
set_element = event_element->FindNextElement("set");
}
Events.push_back(*newEvent);
+ delete newEvent;
+
event_element = run_element->FindNextElement("event");
}
bool FGScript::RunScript(void)
{
- vector <struct event>::iterator iEvent = Events.begin();
unsigned i, j;
unsigned event_ctr = 0;
- double currentTime = State->Getsim_time();
+ double currentTime = FDMExec->GetSimTime();
double newSetValue = 0;
- if (currentTime > EndTime) return false; //Script done!
+ if (currentTime > EndTime) return false;
// Iterate over all events.
- while (iEvent < Events.end()) {
- iEvent->PrevTriggered = iEvent->Triggered;
+ for (unsigned int ev_ctr=0; ev_ctr < Events.size(); ev_ctr++) {
// Determine whether the set of conditional tests for this condition equate
- // to true and should cause the event to execute.
- if (iEvent->Condition->Evaluate()) {
- if (!iEvent->Triggered) {
+ // to true and should cause the event to execute. If the conditions evaluate
+ // to true, then the event is triggered. If the event is not persistent,
+ // then this trigger will remain set true. If the event is persistent,
+ // the trigger will reset to false when the condition evaluates to false.
+ if (Events[ev_ctr].Condition->Evaluate()) {
+ if (!Events[ev_ctr].Triggered) {
// The conditions are true, do the setting of the desired Event parameters
- for (i=0; i<iEvent->SetValue.size(); i++) {
- iEvent->OriginalValue[i] = iEvent->SetParam[i]->getDoubleValue();
- switch (iEvent->Type[i]) {
+ for (i=0; i<Events[ev_ctr].SetValue.size(); i++) {
+ Events[ev_ctr].OriginalValue[i] = Events[ev_ctr].SetParam[i]->getDoubleValue();
+ if (Events[ev_ctr].Functions[i] != 0) { // Parameter should be set to a function value
+ Events[ev_ctr].SetValue[i] = Events[ev_ctr].Functions[i]->GetValue();
+ }
+ switch (Events[ev_ctr].Type[i]) {
case FG_VALUE:
case FG_BOOL:
- iEvent->newValue[i] = iEvent->SetValue[i];
+ Events[ev_ctr].newValue[i] = Events[ev_ctr].SetValue[i];
break;
case FG_DELTA:
- iEvent->newValue[i] = iEvent->OriginalValue[i] + iEvent->SetValue[i];
+ Events[ev_ctr].newValue[i] = Events[ev_ctr].OriginalValue[i] + Events[ev_ctr].SetValue[i];
break;
default:
cerr << "Invalid Type specified" << endl;
break;
}
- iEvent->StartTime = currentTime + iEvent->Delay;
- iEvent->ValueSpan[i] = iEvent->newValue[i] - iEvent->OriginalValue[i];
- iEvent->Transiting[i] = true;
+ Events[ev_ctr].StartTime = currentTime + Events[ev_ctr].Delay;
+ Events[ev_ctr].ValueSpan[i] = Events[ev_ctr].newValue[i] - Events[ev_ctr].OriginalValue[i];
+ Events[ev_ctr].Transiting[i] = true;
}
}
- iEvent->Triggered = true;
- } else if (iEvent->Persistent) {
- iEvent->Triggered = false; // Reset the trigger for persistent events
- iEvent->Notified = false; // Also reset the notification flag
+ Events[ev_ctr].Triggered = true;
+
+ } else if (Events[ev_ctr].Persistent) { // If the event is persistent, reset the trigger.
+ Events[ev_ctr].Triggered = false; // Reset the trigger for persistent events
+ Events[ev_ctr].Notified = false; // Also reset the notification flag
+ } else if (Events[ev_ctr].Continuous) { // If the event is continuous, reset the trigger.
+ Events[ev_ctr].Triggered = false; // Reset the trigger for persistent events
+ Events[ev_ctr].Notified = false; // Also reset the notification flag
}
- if ((currentTime >= iEvent->StartTime) && iEvent->Triggered) {
+ if ((currentTime >= Events[ev_ctr].StartTime) && Events[ev_ctr].Triggered) {
- for (i=0; i<iEvent->SetValue.size(); i++) {
- if (iEvent->Transiting[i]) {
- iEvent->TimeSpan = currentTime - iEvent->StartTime;
- switch (iEvent->Action[i]) {
+ for (i=0; i<Events[ev_ctr].SetValue.size(); i++) {
+ if (Events[ev_ctr].Transiting[i]) {
+ Events[ev_ctr].TimeSpan = currentTime - Events[ev_ctr].StartTime;
+ switch (Events[ev_ctr].Action[i]) {
case FG_RAMP:
- if (iEvent->TimeSpan <= iEvent->TC[i]) {
- newSetValue = iEvent->TimeSpan/iEvent->TC[i] * iEvent->ValueSpan[i] + iEvent->OriginalValue[i];
+ if (Events[ev_ctr].TimeSpan <= Events[ev_ctr].TC[i]) {
+ newSetValue = Events[ev_ctr].TimeSpan/Events[ev_ctr].TC[i] * Events[ev_ctr].ValueSpan[i] + Events[ev_ctr].OriginalValue[i];
} else {
- newSetValue = iEvent->newValue[i];
- iEvent->Transiting[i] = false;
+ newSetValue = Events[ev_ctr].newValue[i];
+ if (Events[ev_ctr].Continuous != true) Events[ev_ctr].Transiting[i] = false;
}
break;
case FG_STEP:
- newSetValue = iEvent->newValue[i];
- iEvent->Transiting[i] = false;
+ newSetValue = Events[ev_ctr].newValue[i];
+
+ // If this is not a continuous event, reset the transiting flag.
+ // Otherwise, it is known that the event is a continuous event.
+ // Furthermore, if the event is to be determined by a function,
+ // then the function will be continuously calculated.
+ if (Events[ev_ctr].Continuous != true)
+ Events[ev_ctr].Transiting[i] = false;
+ else if (Events[ev_ctr].Functions[i] != 0)
+ newSetValue = Events[ev_ctr].Functions[i]->GetValue();
+
break;
case FG_EXP:
- newSetValue = (1 - exp( -iEvent->TimeSpan/iEvent->TC[i] )) * iEvent->ValueSpan[i] + iEvent->OriginalValue[i];
+ newSetValue = (1 - exp( -Events[ev_ctr].TimeSpan/Events[ev_ctr].TC[i] )) * Events[ev_ctr].ValueSpan[i] + Events[ev_ctr].OriginalValue[i];
break;
default:
cerr << "Invalid Action specified" << endl;
break;
}
- iEvent->SetParam[i]->setDoubleValue(newSetValue);
+ Events[ev_ctr].SetParam[i]->setDoubleValue(newSetValue);
}
}
// Print notification values after setting them
- if (iEvent->Notify && !iEvent->Notified) {
- cout << endl << " Event " << event_ctr << " (" << iEvent->Name << ")"
+ if (Events[ev_ctr].Notify && !Events[ev_ctr].Notified) {
+ cout << endl << " Event " << event_ctr << " (" << Events[ev_ctr].Name << ")"
<< " executed at time: " << currentTime << endl;
- for (j=0; j<iEvent->NotifyProperties.size();j++) {
- cout << " " << iEvent->NotifyProperties[j]->GetName()
- << " = " << iEvent->NotifyProperties[j]->getDoubleValue() << endl;
+ if (!Events[ev_ctr].Description.empty()) {
+ cout << " " << Events[ev_ctr].Description << endl;
+ }
+ for (j=0; j<Events[ev_ctr].NotifyProperties.size();j++) {
+// cout << " " << Events[ev_ctr].NotifyProperties[j]->GetRelativeName()
+ cout << " " << Events[ev_ctr].DisplayString[j]
+ << " = " << Events[ev_ctr].NotifyProperties[j]->getDoubleValue() << endl;
}
cout << endl;
- iEvent->Notified = true;
+ Events[ev_ctr].Notified = true;
}
}
- iEvent++;
event_ctr++;
}
return true;
cout << endl;
cout << "Script: \"" << ScriptName << "\"" << endl;
cout << " begins at " << StartTime << " seconds and runs to " << EndTime
- << " seconds with dt = " << State->Getdt() << endl;
+ << " seconds with dt = " << setprecision(6) << FDMExec->GetDeltaT() << " (" <<
+ ceil(1.0/FDMExec->GetDeltaT()) << " Hz)" << endl;
cout << endl;
+ for (unsigned int i=0; i<local_properties.size(); i++) {
+ cout << "Local property: " << local_properties[i]->title
+ << " = " << PropertyManager->GetNode(local_properties[i]->title)->getDoubleValue()
+ << endl;
+ }
+
+ if (local_properties.size() > 0) cout << endl;
+
for (unsigned i=0; i<Events.size(); i++) {
cout << "Event " << i;
if (!Events[i].Name.empty()) cout << " (" << Events[i].Name << ")";
cout << ":" << endl;
if (Events[i].Persistent)
- cout << " " << "Always executes";
+ cout << " " << "Whenever triggered, executes once";
+ else if (Events[i].Continuous)
+ cout << " " << "While true, always executes";
else
- cout << " " << "Executes once";
+ cout << " " << "When first triggered, executes once";
Events[i].Condition->PrintCondition();
- cout << endl << " Actions taken:" << endl << " {";
+ cout << endl << " Actions taken";
+ if (Events[i].Delay > 0.0)
+ cout << " (after a delay of " << Events[i].Delay << " secs)";
+ cout << ":" << endl << " {";
for (unsigned j=0; j<Events[i].SetValue.size(); j++) {
- cout << endl << " set " << Events[i].SetParam[j]->GetName()
- << " to " << Events[i].SetValue[j];
+ if (Events[i].SetValue[j] == 0.0 && Events[i].Functions[j] != 0L) {
+ if (Events[i].SetParam[j] == 0) {
+ cerr << fgred << highint << endl
+ << " An attempt has been made to access a non-existent property" << endl
+ << " in this event. Please check the property names used, spelling, etc."
+ << reset << endl;
+ exit(-1);
+ }
+ cout << endl << " set " << Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
+ << " to function value";
+ } else {
+ if (Events[i].SetParam[j] == 0) {
+ cerr << fgred << highint << endl
+ << " An attempt has been made to access a non-existent property" << endl
+ << " in this event. Please check the property names used, spelling, etc."
+ << reset << endl;
+ exit(-1);
+ }
+ cout << endl << " set " << Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
+ << " to " << Events[i].SetValue[j];
+ }
switch (Events[i].Type[j]) {
case FG_VALUE:
if (Events[i].Action[j] == FG_RAMP || Events[i].Action[j] == FG_EXP)
cout << " with time constant " << Events[i].TC[j] << ")";
}
- cout << endl << " }" << endl << endl;
-
+ cout << endl << " }" << endl;
+
+ // Print notifications
+ if (Events[i].Notify) {
+ if (Events[i].NotifyProperties.size() > 0) {
+ cout << " Notifications" << ":" << endl << " {" << endl;
+ for (unsigned j=0; j<Events[i].NotifyProperties.size();j++) {
+ cout << " "
+ << Events[i].NotifyProperties[j]->GetRelativeName("/fdm/jsbsim/")
+ << endl;
+ }
+ cout << " }" << endl;
+ }
+ }
+ cout << endl;
}
}
}