From ce92730ef6e3e1ac95e3748b1f4f8216c437e58a Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 12 May 2012 17:23:17 +0100 Subject: [PATCH] Expose FlightPlan delegates to Nasal, finally. This will permit Nasal (e.g., FMS) to update nicely when the FlightPlan is modified from anywhere else. --- src/Autopilot/route_mgr.cxx | 6 +- src/Navaids/FlightPlan.cxx | 46 +++++++++++- src/Navaids/FlightPlan.hxx | 18 ++++- src/Scripting/NasalPositioned.cxx | 114 ++++++++++++++++++++++++++++++ src/Scripting/NasalSys.cxx | 10 ++- src/Scripting/NasalSys.hxx | 27 ++++--- 6 files changed, 203 insertions(+), 18 deletions(-) diff --git a/src/Autopilot/route_mgr.cxx b/src/Autopilot/route_mgr.cxx index dedb68aaa..ffb9dc306 100644 --- a/src/Autopilot/route_mgr.cxx +++ b/src/Autopilot/route_mgr.cxx @@ -326,12 +326,13 @@ void FGRouteMgr::init() { wpn->getChild("eta", 0, true); _pathNode = fgGetNode(RM "file-path", 0, true); - setFlightPlan(new FlightPlan()); } void FGRouteMgr::postinit() { + setFlightPlan(new FlightPlan()); + SGPath path(_pathNode->getStringValue()); if (!path.isNull()) { SG_LOG(SG_AUTOPILOT, SG_INFO, "loading flight-plan from: " << path.str()); @@ -402,12 +403,13 @@ void FGRouteMgr::setFlightPlan(FlightPlan* plan) } if (_plan) { + _plan->removeDelegate(this); delete _plan; active->setBoolValue(false); } _plan = plan; - _plan->setDelegate(this); + _plan->addDelegate(this); _flightplanChanged->fireValueChanged(); diff --git a/src/Navaids/FlightPlan.cxx b/src/Navaids/FlightPlan.cxx index 7d7233be4..5630bea67 100644 --- a/src/Navaids/FlightPlan.cxx +++ b/src/Navaids/FlightPlan.cxx @@ -55,6 +55,9 @@ using std::fstream; namespace flightgear { +typedef std::vector FPDelegateFactoryVec; +static FPDelegateFactoryVec static_delegateFactories; + FlightPlan::FlightPlan() : _currentIndex(-1), _departureRunway(NULL), @@ -64,12 +67,26 @@ FlightPlan::FlightPlan() : _approach(NULL), _delegate(NULL) { - + BOOST_FOREACH(DelegateFactory* factory, static_delegateFactories) { + Delegate* d = factory->createFlightPlanDelegate(this); + if (d) { // factory might not always create a delegate + d->_deleteWithPlan = true; + addDelegate(d); + } + } } FlightPlan::~FlightPlan() { - +// delete all delegates which we own. + Delegate* d = _delegate; + while (d) { + Delegate* cur = d; + d = d->_inner; + if (cur->_deleteWithPlan) { + delete cur; + } + } } FlightPlan* FlightPlan::clone(const string& newIdent) const @@ -986,7 +1003,29 @@ void FlightPlan::rebuildLegData() } // of legs iteration } -void FlightPlan::setDelegate(Delegate* d) +void FlightPlan::registerDelegateFactory(DelegateFactory* df) +{ + FPDelegateFactoryVec::iterator it = std::find(static_delegateFactories.begin(), + static_delegateFactories.end(), df); + if (it != static_delegateFactories.end()) { + throw sg_exception("duplicate delegate factory registration"); + } + + static_delegateFactories.push_back(df); +} + +void FlightPlan::unregisterDelegateFactory(DelegateFactory* df) +{ + FPDelegateFactoryVec::iterator it = std::find(static_delegateFactories.begin(), + static_delegateFactories.end(), df); + if (it == static_delegateFactories.end()) { + return; + } + + static_delegateFactories.erase(it); +} + +void FlightPlan::addDelegate(Delegate* d) { // wrap any existing delegate(s) in the new one d->_inner = _delegate; @@ -1003,6 +1042,7 @@ void FlightPlan::removeDelegate(Delegate* d) } FlightPlan::Delegate::Delegate() : + _deleteWithPlan(false), _inner(NULL) { diff --git a/src/Navaids/FlightPlan.hxx b/src/Navaids/FlightPlan.hxx index b46dae03c..4558d16dd 100644 --- a/src/Navaids/FlightPlan.hxx +++ b/src/Navaids/FlightPlan.hxx @@ -100,7 +100,7 @@ public: { public: virtual ~Delegate(); - + virtual void departureChanged() { } virtual void arrivalChanged() { } virtual void waypointsChanged() { } @@ -120,6 +120,7 @@ public: friend class FlightPlan; + bool _deleteWithPlan; Delegate* _inner; }; @@ -207,7 +208,20 @@ public: */ WayptRef waypointFromString(const std::string& target); - void setDelegate(Delegate* d); + /** + * abstract interface for creating delegates automatically when a + * flight-plan is created or loaded + */ + class DelegateFactory + { + public: + virtual Delegate* createFlightPlanDelegate(FlightPlan* fp) = 0; + }; + + static void registerDelegateFactory(DelegateFactory* df); + static void unregisterDelegateFactory(DelegateFactory* df); + + void addDelegate(Delegate* d); void removeDelegate(Delegate* d); private: diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index ff7546bfc..1baf07d04 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -1308,6 +1308,16 @@ static naRef f_airport_getApproach(naContext c, naRef me, int argc, naRef* args) return ghostForProcedure(c, apt->findApproachWithIdent(ident)); } +static naRef f_airport_toString(naContext c, naRef me, int argc, naRef* args) +{ + FGAirport* apt = airportGhost(me); + if (!apt) { + naRuntimeError(c, "airport.tostring called on non-airport object"); + } + + return stringToNasal(c, "an airport " + apt->ident()); +} + // Returns vector of data hash for navaid of a , nil on error // navaids sorted by ascending distance // navinfo([,],[],[]) @@ -1617,6 +1627,108 @@ static naRef f_route(naContext c, naRef me, int argc, naRef* args) return naNil(); } +class NasalFPDelegate : public FlightPlan::Delegate +{ +public: + NasalFPDelegate(FlightPlan* fp, FGNasalSys* sys, naRef ins) : + _nasal(sys), + _plan(fp), + _instance(ins) + { + SG_LOG(SG_NASAL, SG_INFO, "created Nasal delegate for " << fp); + _gcSaveKey = _nasal->gcSave(ins); + } + + virtual ~NasalFPDelegate() + { + SG_LOG(SG_NASAL, SG_INFO, "destroying Nasal delegate for " << _plan); + _nasal->gcRelease(_gcSaveKey); + } + + virtual void departureChanged() + { + callDelegateMethod("departureChanged"); + } + + virtual void arrivalChanged() + { + callDelegateMethod("arrivalChanged"); + } + + virtual void waypointsChanged() + { + callDelegateMethod("waypointsChanged"); + } + + virtual void currentWaypointChanged() + { + callDelegateMethod("currentWaypointChanged"); + } +private: + + void callDelegateMethod(const char* method) + { + naRef f; + naMember_cget(_nasal->context(), _instance, method, &f); + if (naIsNil(f)) { + return; // no method on the delegate + } + + naRef arg[1]; + arg[0] = ghostForFlightPlan(_nasal->context(), _plan); + _nasal->callMethod(f, _instance, 1, arg, naNil()); + } + + FGNasalSys* _nasal; + FlightPlan* _plan; + naRef _instance; + int _gcSaveKey; +}; + +class NasalFPDelegateFactory : public FlightPlan::DelegateFactory +{ +public: + NasalFPDelegateFactory(naRef code) + { + _nasal = (FGNasalSys*) globals->get_subsystem("nasal"); + _func = code; + _gcSaveKey = _nasal->gcSave(_func); + } + + ~NasalFPDelegateFactory() + { + _nasal->gcRelease(_gcSaveKey); + } + + virtual FlightPlan::Delegate* createFlightPlanDelegate(FlightPlan* fp) + { + naRef args[1]; + args[0] = ghostForFlightPlan(_nasal->context(), fp); + naRef instance = _nasal->call(_func, 1, args, naNil()); + if (naIsNil(instance)) { + return NULL; + } + + return new NasalFPDelegate(fp, _nasal, instance); + } +private: + FGNasalSys* _nasal; + naRef _func; + int _gcSaveKey; +}; + +static naRef f_registerFPDelegate(naContext c, naRef me, int argc, naRef* args) +{ + if ((argc < 1) || !naIsFunc(args[0])) { + naRuntimeError(c, "non-function argument to registerFlightPlanDelegate"); + } + + NasalFPDelegateFactory* factory = new NasalFPDelegateFactory(args[0]); + FlightPlan::registerDelegateFactory(factory); + + return naNil(); +} + static WayptRef wayptFromArg(naRef arg) { WayptRef r = wayptGhost(arg); @@ -2077,6 +2189,7 @@ static struct { const char* name; naCFunction func; } funcs[] = { { "findNavaidsByID", f_findNavaidsByIdent }, { "findFixesByID", f_findFixesByIdent }, { "flightplan", f_route }, + { "registerFlightPlanDelegate", f_registerFPDelegate }, { "createWP", f_createWP }, { "createWPFrom", f_createWPFrom }, { "airwaysRoute", f_airwaySearch }, @@ -2104,6 +2217,7 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave) hashset(c, airportPrototype, "getSid", naNewFunc(c, naNewCCode(c, f_airport_getSid))); hashset(c, airportPrototype, "getStar", naNewFunc(c, naNewCCode(c, f_airport_getStar))); hashset(c, airportPrototype, "getIAP", naNewFunc(c, naNewCCode(c, f_airport_getApproach))); + hashset(c, airportPrototype, "tostring", naNewFunc(c, naNewCCode(c, f_airport_toString))); flightplanPrototype = naNewHash(c); hashset(c, gcSave, "flightplanProto", flightplanPrototype); diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 73bb2e25a..1c3ec31cb 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -99,18 +99,24 @@ FGNasalSys::FGNasalSys() _callCount = 0; } +naRef FGNasalSys::call(naRef code, int argc, naRef* args, naRef locals) +{ + return callMethod(code, naNil(), argc, args, locals); +} + // Does a naCall() in a new context. Wrapped here to make lock // tracking easier. Extension functions are called with the lock, but // we have to release it before making a new naCall(). So rather than // drop the lock in every extension function that might call back into // Nasal, we keep a stack depth counter here and only unlock/lock // around the naCall if it isn't the first one. -naRef FGNasalSys::call(naRef code, int argc, naRef* args, naRef locals) + +naRef FGNasalSys::callMethod(naRef code, naRef self, int argc, naRef* args, naRef locals) { naContext ctx = naNewContext(); if(_callCount) naModUnlock(); _callCount++; - naRef result = naCall(ctx, code, argc, args, naNil(), locals); + naRef result = naCall(ctx, code, argc, args, self, locals); if(naGetError(ctx)) logError(ctx); _callCount--; diff --git a/src/Scripting/NasalSys.hxx b/src/Scripting/NasalSys.hxx index b564bb354..5ca64f109 100644 --- a/src/Scripting/NasalSys.hxx +++ b/src/Scripting/NasalSys.hxx @@ -111,11 +111,29 @@ public: void deleteModule(const char* moduleName); naRef call(naRef code, int argc, naRef* args, naRef locals); + + naRef callMethod(naRef code, naRef self, int argc, naRef* args, naRef locals); + naRef propNodeGhost(SGPropertyNode* handle); void registerToLoad(FGNasalModelData* data) { _loadList.push(data);} void registerToUnload(FGNasalModelData* data) { _unloadList.push(data);} + // can't call this 'globals' due to naming clash + naRef nasalGlobals() const + { return _globals; } + + naContext context() const + { return _context; } + + // This mechanism is here to allow naRefs to be passed to + // locations "outside" the interpreter. Normally, such a + // reference would be garbage collected unexpectedly. By passing + // it to gcSave and getting a key/handle, it can be cached in a + // globals.__gcsave hash. Be sure to release it with gcRelease + // when done. + int gcSave(naRef r); + void gcRelease(int key); private: friend class FGNasalScript; friend class FGNasalListener; @@ -151,15 +169,6 @@ private: naRef parse(const char* filename, const char* buf, int len); naRef genPropsModule(); - // This mechanism is here to allow naRefs to be passed to - // locations "outside" the interpreter. Normally, such a - // reference would be garbage collected unexpectedly. By passing - // it to gcSave and getting a key/handle, it can be cached in a - // globals.__gcsave hash. Be sure to release it with gcRelease - // when done. - int gcSave(naRef r); - void gcRelease(int key); - naContext _context; naRef _globals;