Expose FlightPlan delegates to Nasal, finally.
This will permit Nasal (e.g., FMS) to update nicely when the FlightPlan is modified from anywhere else.
This commit is contained in:
parent
acadbdeee1
commit
ce92730ef6
6 changed files with 203 additions and 18 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -55,6 +55,9 @@ using std::fstream;
|
|||
|
||||
namespace flightgear {
|
||||
|
||||
typedef std::vector<FlightPlan::DelegateFactory*> 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)
|
||||
{
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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 <type>, nil on error
|
||||
// navaids sorted by ascending distance
|
||||
// navinfo([<lat>,<lon>],[<type>],[<id>])
|
||||
|
@ -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);
|
||||
|
|
|
@ -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--;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue