1
0
Fork 0

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:
James Turner 2012-05-12 17:23:17 +01:00
parent acadbdeee1
commit ce92730ef6
6 changed files with 203 additions and 18 deletions

View file

@ -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();

View file

@ -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)
{

View file

@ -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:

View file

@ -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);

View file

@ -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--;

View file

@ -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;