#include <simgear/props/props_io.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/structure/commands.hxx>
+#include <simgear/structure/SGBinding.hxx>
#include <boost/mem_fn.hpp>
#include <boost/foreach.hpp>
#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
#include <Airports/airport.hxx>
+#include <Scripting/NasalSys.hxx>
#include "AIManager.hxx"
#include "AIAircraft.hxx"
class FGAIManager::Scenario
{
public:
- Scenario(FGAIManager* man, SGPropertyNode* scenarios) :
- _manager(man)
+ Scenario(FGAIManager* man, const std::string& nm, SGPropertyNode* scenarios) :
+ _internalName(nm)
{
BOOST_FOREACH(SGPropertyNode* scEntry, scenarios->getChildren("entry")) {
FGAIBasePtr ai = man->addObject(scEntry);
_objects.push_back(ai);
}
} // of scenario entry iteration
+
+ SGPropertyNode* nasalScripts = scenarios->getChild("nasal");
+ if (!nasalScripts) {
+ return;
+ }
+
+ _unloadScript = nasalScripts->getStringValue("unload");
+ std::string loadScript = nasalScripts->getStringValue("load");
+ if (!loadScript.empty()) {
+ FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
+ std::string moduleName = "scenario_" + _internalName;
+ nasalSys->createModule(moduleName.c_str(), moduleName.c_str(),
+ loadScript.c_str(), loadScript.size(),
+ 0);
+ }
}
~Scenario()
BOOST_FOREACH(FGAIBasePtr ai, _objects) {
ai->setDie(true);
}
+
+ FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
+ if (!nasalSys)
+ return;
+
+ std::string moduleName = "scenario_" + _internalName;
+ if (!_unloadScript.empty()) {
+ nasalSys->createModule(moduleName.c_str(), moduleName.c_str(),
+ _unloadScript.c_str(), _unloadScript.size(),
+ 0);
+ }
+
+ nasalSys->deleteModule(moduleName.c_str());
}
private:
- FGAIManager* _manager;
std::vector<FGAIBasePtr> _objects;
+ std::string _internalName;
+ std::string _unloadScript;
};
///////////////////////////////////////////////////////////////////////////////
wind_from_north_node = fgGetNode("/environment/wind-from-north-fps",true);
user_altitude_agl_node = fgGetNode("/position/altitude-agl-ft", true);
- user_yaw_node = fgGetNode("/orientation/side-slip-deg", true);
user_speed_node = fgGetNode("/velocities/uBody-fps", true);
globals->get_commands()->addCommand("load-scenario", this, &FGAIManager::loadScenarioCommand);
globals->get_commands()->addCommand("unload-scenario", this, &FGAIManager::unloadScenarioCommand);
+ _environmentVisiblity = fgGetNode("/environment/visibility-m");
}
void
-FGAIManager::postinit() {
+FGAIManager::postinit()
+{
// postinit, so that it can access the Nasal subsystem
- if (!root->getBoolValue("scenarios-enabled", true))
- return;
-
// scenarios enabled, AI subsystem required
if (!enabled->getBoolValue())
enabled->setBoolValue(true);
void
FGAIManager::reinit()
{
+ // shutdown scenarios
+ unloadAllScenarios();
+
update(0.0);
std::for_each(ai_list.begin(), ai_list.end(), boost::mem_fn(&FGAIBase::reinit));
+
+ // (re-)load scenarios
+ postinit();
+}
+
+void
+FGAIManager::shutdown()
+{
+ unloadAllScenarios();
+
+ BOOST_FOREACH(FGAIBase* ai, ai_list) {
+ ai->unbind();
+ }
+
+ ai_list.clear();
+ _environmentVisiblity.clear();
+
+ globals->get_commands()->removeCommand("load-scenario");
+ globals->get_commands()->removeCommand("unload-scenario");
}
void
ai_list.erase(ai_list.begin(), firstAlive);
- // every remaining item is alive
+ // every remaining item is alive. update them in turn, but guard for
+ // exceptions, so a single misbehaving AI object doesn't bring down the
+ // entire subsystem.
BOOST_FOREACH(FGAIBase* base, ai_list) {
- if (base->isa(FGAIBase::otThermal)) {
- processThermal(dt, (FGAIThermal*)base);
- } else {
- base->update(dt);
+ try {
+ if (base->isa(FGAIBase::otThermal)) {
+ processThermal(dt, (FGAIThermal*)base);
+ } else {
+ base->update(dt);
+ }
+ } catch (sg_exception& e) {
+ SG_LOG(SG_AI, SG_WARN, "caught exception updating AI model:" << base->_getName()<< ", which will be killed."
+ "\n\tError:" << e.getFormattedMessage());
+ base->setDie(true);
}
} // of live AI objects iteration
p->setBoolValue("valid", true);
}
+bool FGAIManager::isVisible(const SGGeod& pos) const
+{
+ double visibility_meters = _environmentVisiblity->getDoubleValue();
+ return ( dist(globals->get_view_position_cart(), SGVec3d::fromGeod(pos)) ) <= visibility_meters;
+}
+
int
-FGAIManager::getNumAiObjects(void) const
+FGAIManager::getNumAiObjects() const
{
return ai_list.size();
}
void
FGAIManager::fetchUserState( void ) {
- user_yaw = user_yaw_node->getDoubleValue();
globals->get_aircraft_orientation(user_heading, user_pitch, user_roll);
-
user_speed = user_speed_node->getDoubleValue() * 0.592484;
wind_from_east = wind_from_east_node->getDoubleValue();
wind_from_north = wind_from_north_node->getDoubleValue();
bool FGAIManager::loadScenarioCommand(const SGPropertyNode* args)
{
std::string name = args->getStringValue("name");
- return loadScenario(name);
+ if (args->hasChild("load-property")) {
+ // slightly ugly, to simplify life in the dialogs, make load allow
+ // loading or unloading based on a bool property.
+ bool loadIt = fgGetBool(args->getStringValue("load-property"));
+ if (!loadIt) {
+ // user actually wants to unload, fine.
+ return unloadScenario(name);
+ }
+ }
+
+ if (_scenarios.find(name) != _scenarios.end()) {
+ SG_LOG(SG_AI, SG_WARN, "scenario '" << name << "' already loaded");
+ return false;
+ }
+
+ bool ok = loadScenario(name);
+ if (ok) {
+ // create /sim/ai node for consistency
+ int index = 0;
+ for (; root->hasChild("scenario", index); ++index) {}
+
+ SGPropertyNode* scenarioNode = root->getChild("scenario", index, true);
+ scenarioNode->setStringValue(name);
+ }
+
+ return ok;
}
bool FGAIManager::unloadScenarioCommand(const SGPropertyNode* args)
{
std::string name = args->getStringValue("name");
- unloadScenario(name);
- return true;
+ return unloadScenario(name);
}
bool FGAIManager::addObjectCommand(const SGPropertyNode* definition)
}
ai->readFromScenario(const_cast<SGPropertyNode*>(definition));
- attach(ai);
+ if((ai->isValid())){
+ attach(ai);
+ SG_LOG(SG_AI, SG_DEBUG, "attached scenario " << ai->_getName());
+ }
+ else{
+ ai->setDie(true);
+ SG_LOG(SG_AI, SG_ALERT, "killed invalid scenario " << ai->_getName());
+ }
return ai;
}
return false;
}
+FGAIBasePtr FGAIManager::getObjectFromProperty(const SGPropertyNode* aProp) const
+{
+ BOOST_FOREACH(FGAIBase* ai, get_ai_list()) {
+ if (ai->_getProps() == aProp) {
+ return ai;
+ }
+ } // of AI objects iteration
+
+ return NULL;
+}
+
bool
FGAIManager::loadScenario( const string &filename )
{
return false;
}
- _scenarios[filename] = new Scenario(this, scNode);
+ _scenarios[filename] = new Scenario(this, filename, scNode);
return true;
}
-void
+bool
FGAIManager::unloadScenario( const string &filename)
{
ScenarioDict::iterator it = _scenarios.find(filename);
if (it == _scenarios.end()) {
SG_LOG(SG_AI, SG_WARN, "unload scenario: not found:" << filename);
- return;
+ return false;
+ }
+
+// remove /sim/ai node
+ unsigned int index = 0;
+ for (SGPropertyNode* n = NULL; (n = root->getChild("scenario", index)) != NULL; ++index) {
+ if (n->getStringValue() == filename) {
+ root->removeChild("scenario", index);
+ break;
+ }
}
delete it->second;
_scenarios.erase(it);
+ return true;
}
+void
+FGAIManager::unloadAllScenarios()
+{
+ ScenarioDict::iterator it = _scenarios.begin();
+ for (; it != _scenarios.end(); ++it) {
+ delete it->second;
+ } // of scenarios iteration
+
+
+ // remove /sim/ai node
+ root->removeChildren("scenario");
+ _scenarios.clear();
+}
+
+
SGPropertyNode_ptr
FGAIManager::loadScenarioFile(const std::string& filename)
{
int id = (*ai_list_itr)->getID();
- double range = calcRange(cartPos, (*ai_list_itr));
+ double range = calcRangeFt(cartPos, (*ai_list_itr));
//SG_LOG(SG_AI, SG_DEBUG, "AIManager: AI list size "
// << ai_list.size()
}
double
-FGAIManager::calcRange(const SGVec3d& aCartPos, FGAIBase* aObject) const
+FGAIManager::calcRangeFt(const SGVec3d& aCartPos, FGAIBase* aObject) const
{
double distM = dist(aCartPos, aObject->getCartPos());
return distM * SG_METER_TO_FEET;