From: curt Date: Sat, 22 Dec 2001 17:39:21 +0000 (+0000) Subject: Initial revision. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=3f1e9cdc371dfec71eba6fa029b1b940191a0349;p=flightgear.git Initial revision. --- diff --git a/src/FDM/JSBSim/FGScript.cpp b/src/FDM/JSBSim/FGScript.cpp new file mode 100644 index 000000000..690d5abb3 --- /dev/null +++ b/src/FDM/JSBSim/FGScript.cpp @@ -0,0 +1,409 @@ +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Module: FGScript.cpp + Author: Jon S. Berndt + Date started: 12/21/01 + Purpose: Loads and runs JSBSim scripts. + + ------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) ------------- + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 Temple + Place - Suite 330, Boston, MA 02111-1307, USA. + + Further information about the GNU General Public License can also be found on + the world wide web at http://www.gnu.org. + +FUNCTIONAL DESCRIPTION +-------------------------------------------------------------------------------- + +This class wraps up the simulation scripting routines. + +HISTORY +-------------------------------------------------------------------------------- +12/21/01 JSB Created + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +COMMENTS, REFERENCES, and NOTES +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +INCLUDES +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +#ifdef FGFS +# include +# include STL_IOSTREAM +# include STL_ITERATOR +#else +# if defined(sgi) && !defined(__GNUC__) +# include +# else +# include +# endif +# include +#endif + +#include "FGScript.h" +#include "FGConfigFile.h" + +static const char *IdSrc = "$Id$"; +static const char *IdHdr = ID_FGSCRIPT; + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +GLOBAL DECLARATIONS +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CLASS IMPLEMENTATION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +// Constructor + +FGScript::FGScript(FGFDMExec* fgex) : FDMExec(fgex) +{ + State = FDMExec->GetState(); + Debug(0); +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +FGScript::~FGScript() +{ + Debug(1); +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +bool FGScript::LoadScript(string script) +{ + FGConfigFile Script(script); + string token=""; + string aircraft=""; + string initialize=""; + bool result = false; + double dt = 0.0; + unsigned i; + struct condition *newCondition; + + if (!Script.IsOpen()) return false; + + Script.GetNextConfigLine(); + if (Script.GetValue("runscript").length() <= 0) { + cerr << "File: " << script << " is not a script file" << endl; + delete FDMExec; + return false; + } + ScriptName = Script.GetValue("name"); + Scripted = true; + + if (debug_lvl > 0) cout << "Reading Script File " << ScriptName << endl; + + while (Script.GetNextConfigLine() != string("EOF") && Script.GetValue() != string("/runscript")) { + token = Script.GetValue(); + if (token == "use") { + if ((token = Script.GetValue("aircraft")) != string("")) { + aircraft = token; + if (debug_lvl > 0) cout << " Use aircraft: " << token << endl; + } else if ((token = Script.GetValue("initialize")) != string("")) { + initialize = token; + if (debug_lvl > 0) cout << " Use reset file: " << token << endl; + } else { + cerr << "Unknown 'use' keyword: \"" << token << "\"" << endl; + } + } else if (token == "run") { + StartTime = strtod(Script.GetValue("start").c_str(), NULL); + State->Setsim_time(StartTime); + EndTime = strtod(Script.GetValue("end").c_str(), NULL); + dt = strtod(Script.GetValue("dt").c_str(), NULL); + State->Setdt(dt); + Script.GetNextConfigLine(); + token = Script.GetValue(); + while (token != string("/run")) { + + if (token == "when") { + Script.GetNextConfigLine(); + token = Script.GetValue(); + newCondition = new struct condition(); + while (token != string("/when")) { + if (token == "parameter") { + newCondition->TestParam.push_back(State->GetParameterIndex(Script.GetValue("name"))); + newCondition->TestValue.push_back(strtod(Script.GetValue("value").c_str(), NULL)); + newCondition->Comparison.push_back(Script.GetValue("comparison")); + } else if (token == "set") { + newCondition->SetParam.push_back(State->GetParameterIndex(Script.GetValue("name"))); + newCondition->SetValue.push_back(strtod(Script.GetValue("value").c_str(), NULL)); + newCondition->Triggered.push_back(false); + newCondition->OriginalValue.push_back(0.0); + newCondition->newValue.push_back(0.0); + newCondition->StartTime.push_back(0.0); + newCondition->EndTime.push_back(0.0); + string tempCompare = Script.GetValue("type"); + if (tempCompare == "FG_DELTA") newCondition->Type.push_back(FG_DELTA); + else if (tempCompare == "FG_BOOL") newCondition->Type.push_back(FG_BOOL); + else if (tempCompare == "FG_VALUE") newCondition->Type.push_back(FG_VALUE); + else newCondition->Type.push_back((eType)0); + tempCompare = Script.GetValue("action"); + if (tempCompare == "FG_RAMP") newCondition->Action.push_back(FG_RAMP); + else if (tempCompare == "FG_STEP") newCondition->Action.push_back(FG_STEP); + else if (tempCompare == "FG_EXP") newCondition->Action.push_back(FG_EXP); + else newCondition->Action.push_back((eAction)0); + + if (Script.GetValue("persistent") == "true") + newCondition->Persistent.push_back(true); + else + newCondition->Persistent.push_back(false); + + newCondition->TC.push_back(strtod(Script.GetValue("tc").c_str(), NULL)); + + } else { + cerr << "Unrecognized keyword in script file: \" [when] " << token << "\"" << endl; + } + Script.GetNextConfigLine(); + token = Script.GetValue(); + } + Conditions.push_back(*newCondition); + Script.GetNextConfigLine(); + token = Script.GetValue(); + + } else { + cerr << "Error reading script file: expected \"when\", got \"" << token << "\"" << endl; + } + + } + } else if (token.empty()) { + // do nothing + } else { + cerr << "Unrecognized keyword in script file: \"" << token << "\" [runscript] " << endl; + } + } + + if (aircraft == "") { + cerr << "Aircraft file not loaded in script" << endl; + exit(-1); + } + + Debug(4); + + result = FDMExec->LoadModel("aircraft", "engine", aircraft); + if (!result) { + cerr << "Aircraft file " << aircraft << " was not found" << endl; + exit(-1); + } + + FGInitialCondition IC(FDMExec); + if ( ! IC.Load("aircraft", aircraft, initialize)) { + cerr << "Initialization unsuccessful" << endl; + exit(-1); + } + + return true; +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +bool FGScript::RunScript(void) +{ + vector ::iterator iC = Conditions.begin(); + bool truth = false; + bool WholeTruth = false; + unsigned i; + + double currentTime = State->Getsim_time(); + double newSetValue = 0; + + if (currentTime > EndTime) return false; + + while (iC < Conditions.end()) { + // determine whether the set of conditional tests for this condition equate + // to true + for (i=0; iTestValue.size(); i++) { + if (iC->Comparison[i] == "lt") + truth = State->GetParameter(iC->TestParam[i]) < iC->TestValue[i]; + else if (iC->Comparison[i] == "le") + truth = State->GetParameter(iC->TestParam[i]) <= iC->TestValue[i]; + else if (iC->Comparison[i] == "eq") + truth = State->GetParameter(iC->TestParam[i]) == iC->TestValue[i]; + else if (iC->Comparison[i] == "ge") + truth = State->GetParameter(iC->TestParam[i]) >= iC->TestValue[i]; + else if (iC->Comparison[i] == "gt") + truth = State->GetParameter(iC->TestParam[i]) > iC->TestValue[i]; + else if (iC->Comparison[i] == "ne") + truth = State->GetParameter(iC->TestParam[i]) != iC->TestValue[i]; + else + cerr << "Bad comparison" << endl; + + if (i == 0) WholeTruth = truth; + else WholeTruth = WholeTruth && truth; + + if (!truth && iC->Persistent[i] && iC->Triggered[i]) iC->Triggered[i] = false; + } + + // if the conditions are true, do the setting of the desired parameters + + if (WholeTruth) { + for (i=0; iSetValue.size(); i++) { + if ( ! iC->Triggered[i]) { + iC->OriginalValue[i] = State->GetParameter(iC->SetParam[i]); + switch (iC->Type[i]) { + case FG_VALUE: + iC->newValue[i] = iC->SetValue[i]; + break; + case FG_DELTA: + iC->newValue[i] = iC->OriginalValue[i] + iC->SetValue[i]; + break; + case FG_BOOL: + iC->newValue[i] = iC->SetValue[i]; + break; + default: + cerr << "Invalid Type specified" << endl; + break; + } + iC->Triggered[i] = true; + iC->StartTime[i] = currentTime; + } + + switch (iC->Action[i]) { + case FG_RAMP: + newSetValue = (currentTime - iC->StartTime[i])/(iC->TC[i]) + * (iC->newValue[i] - iC->OriginalValue[i]) + iC->OriginalValue[i]; + if (newSetValue > iC->newValue[i]) newSetValue = iC->newValue[i]; + break; + case FG_STEP: + newSetValue = iC->newValue[i]; + break; + case FG_EXP: + newSetValue = (1 - exp(-(currentTime - iC->StartTime[i])/(iC->TC[i]))) + * (iC->newValue[i] - iC->OriginalValue[i]) + iC->OriginalValue[i]; + break; + default: + cerr << "Invalid Action specified" << endl; + break; + } + State->SetParameter(iC->SetParam[i], newSetValue); + } + } + iC++; + } + return true; +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// The bitmasked value choices are as follows: +// unset: In this case (the default) JSBSim would only print +// out the normally expected messages, essentially echoing +// the config files as they are read. If the environment +// variable is not set, debug_lvl is set to 1 internally +// 0: This requests JSBSim not to output any messages +// whatsoever. +// 1: This value explicity requests the normal JSBSim +// startup messages +// 2: This value asks for a message to be printed out when +// a class is instantiated +// 4: When this value is set, a message is displayed when a +// FGModel object executes its Run() method +// 8: When this value is set, various runtime state variables +// are printed out periodically +// 16: When set various parameters are sanity checked and +// a message is printed out when they go out of bounds + +void FGScript::Debug(int from) +{ + unsigned int i; + + if (debug_lvl <= 0) return; + + if (debug_lvl & 1) { // Standard console startup message output + if (from == 0) { // Constructor + } else if (from == 3) { + } else if (from == 4) { // print out script data + vector ::iterator iterConditions = Conditions.begin(); + int count=0; + + cout << "\n Script goes from " << StartTime << " to " << EndTime + << " with dt = " << State->Getdt() << endl << endl; + + while (iterConditions < Conditions.end()) { + cout << " Condition: " << count++ << endl; + cout << " if ("; + + for (i=0; iTestValue.size(); i++) { + if (i>0) cout << " and" << endl << " "; + cout << "(" << State->paramdef[iterConditions->TestParam[i]] + << iterConditions->Comparison[i] << " " + << iterConditions->TestValue[i] << ")"; + } + cout << ") then {"; + + for (i=0; iSetValue.size(); i++) { + cout << endl << " set" << State->paramdef[iterConditions->SetParam[i]] + << "to " << iterConditions->SetValue[i]; + + switch (iterConditions->Type[i]) { + case FG_VALUE: + cout << " (constant"; + break; + case FG_DELTA: + cout << " (delta"; + break; + case FG_BOOL: + cout << " (boolean"; + break; + default: + cout << " (unspecified type"; + } + + switch (iterConditions->Action[i]) { + case FG_RAMP: + cout << " via ramp"; + break; + case FG_STEP: + cout << " via step"; + break; + case FG_EXP: + cout << " via exponential approach"; + break; + default: + cout << " via unspecified action"; + } + + if (!iterConditions->Persistent[i]) cout << endl + << " once"; + else cout << endl + << " repeatedly"; + + if (iterConditions->Action[i] == FG_RAMP || + iterConditions->Action[i] == FG_EXP) cout << endl + << " with time constant " + << iterConditions->TC[i]; + } + cout << ")" << endl << " }" << endl << endl; + + iterConditions++; + } + + cout << endl; + } + } + if (debug_lvl & 2 ) { // Instantiation/Destruction notification + if (from == 0) cout << "Instantiated: FGScript" << endl; + if (from == 1) cout << "Destroyed: FGScript" << endl; + } + if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects + } + if (debug_lvl & 8 ) { // Runtime state variables + } + if (debug_lvl & 16) { // Sanity checking + } +} + diff --git a/src/FDM/JSBSim/FGScript.h b/src/FDM/JSBSim/FGScript.h new file mode 100644 index 000000000..df39f1eb3 --- /dev/null +++ b/src/FDM/JSBSim/FGScript.h @@ -0,0 +1,199 @@ +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Header: FGScript.h + Author: Jon Berndt + Date started: 12/21/2001 + + ------------- Copyright (C) 2001 Jon S. Berndt (jsb@hal-pc.org) ------------- + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 Temple + Place - Suite 330, Boston, MA 02111-1307, USA. + + Further information about the GNU General Public License can also be found on + the world wide web at http://www.gnu.org. + +HISTORY +-------------------------------------------------------------------------------- +12/21/01 JSB Created + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SENTRY +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +#ifndef FGSCRIPT_HEADER_H +#define FGSCRIPT_HEADER_H + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +INCLUDES +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +#include "FGJSBBase.h" +#include "FGState.h" +#include "FGFDMExec.h" +#include + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DEFINITIONS +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +#define ID_FGSCRIPT "$Id$" + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FORWARD DECLARATIONS +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +COMMENTS, REFERENCES, and NOTES [use "class documentation" below for API docs] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CLASS DOCUMENTATION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +/** Encapsulates the JSBSim scripting capability. + @author Jon S. Berndt + @version $Id$ + +

Scripting support provided via FGScript.

+ +

There is simple scripting support provided in the FGScript + class. Commands are specified using the Simple Scripting + Directives for JSBSim (SSDJ). The script file is in XML + format. A test condition (or conditions) can be set up in the + script and when the condition evaluates to true, the specified + action[s] is/are taken. A test condition can be persistent, + meaning that if a test condition evaluates to true, then passes + and evaluates to false, the condition is reset and may again be + triggered. When the set of tests evaluates to true for a given + condition, an item may be set to another value. This value might + be a boolean, a value, or a delta value, and the change from the + current value to the new value can be either via a step function, + a ramp, or an exponential approach. The speed of a ramp or + approach is specified via the time constant. Here is the format + of the script file:

+ +
<?xml version="1.0"?>
+    <runscript name="C172-01A">
+
+    <!--
+    This run is for testing C172 runs
+    -->
+
+    <use aircraft="c172">
+    <use initialize="reset00">
+
+    <run start="0.0" end="4.5" dt="0.05">
+      <when>
+        <parameter name="FG_TIME" comparison="ge" value="0.25">
+        <parameter name="FG_TIME" comparison="le" value="0.50">
+        <set name="FG_AILERON_CMD" type="FG_VALUE" value="0.25"
+        action="FG_STEP" persistent="false" tc ="0.25">
+      </when>
+      <when>
+        <parameter name="FG_TIME" comparison="ge" value="0.5">
+        <parameter name="FG_TIME" comparison="le" value="1.5">
+        <set name="FG_AILERON_CMD" type="FG_DELTA" value="0.5"
+        action="FG_EXP" persistent="false" tc ="0.5">
+      </when>
+      <when>
+        <parameter name="FG_TIME" comparison="ge" value="1.5">
+        <parameter name="FG_TIME" comparison="le" value="2.5">
+        <set name="FG_RUDDER_CMD" type="FG_DELTA" value="0.5"
+        action="FG_RAMP" persistent="false" tc ="0.5">
+      </when>
+    </run>
+
+    </runscript>
+ +

The first line must always be present. The second line + identifies this file as a script file, and gives a descriptive + name to the script file. Comments are next, delineated by the + <!-- and --> symbols. The aircraft and initialization files + to be used are specified in the "use" lines. Next, + comes the "run" section, where the conditions are + described in "when" clauses.

+ +*/ + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CLASS DECLARATION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +class FGScript : public FGJSBBase +{ +public: + /// Default constructor + FGScript(FGFDMExec* exec); + + /// Default destructor + ~FGScript(); + + /** Loads a script to drive JSBSim (usually in standalone mode). + The language is the Simple Script Directives for JSBSim (SSDJ). + @param script the filename (including path name, if any) for the script. + @return true if successful */ + bool LoadScript(string script); + + /** This function is called each pass through the executive Run() method IF + scripting is enabled. + @return false if script should exit (i.e. if time limits are violated */ + bool RunScript(void); + +private: + enum eAction { + FG_RAMP = 1, + FG_STEP = 2, + FG_EXP = 3 + }; + + enum eType { + FG_VALUE = 1, + FG_DELTA = 2, + FG_BOOL = 3 + }; + + struct condition { + vector TestParam; + vector SetParam; + vector TestValue; + vector SetValue; + vector Comparison; + vector TC; + vector Persistent; + vector Action; + vector Type; + vector Triggered; + vector newValue; + vector OriginalValue; + vector StartTime; + vector EndTime; + + condition() { + } + }; + + bool Scripted; + + string ScriptName; + double StartTime; + double EndTime; + vector Conditions; + + FGFDMExec* FDMExec; + FGState* State; + void Debug(int from); +}; + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#endif +