1
0
Fork 0

Improve creating VIA segments

Improve argument parsing for createViaTo, createViaFromTo to
handle Airway ghosts as well as strings. As part of this, allow
specification of the airway level explicitly when looking
up an airway.

SF-ID: https://sourceforge.net/p/flightgear/codetickets/2686/
This commit is contained in:
James Turner 2022-01-05 15:44:50 +00:00
parent a7ae3bf6cd
commit c1e5cc3074
3 changed files with 156 additions and 32 deletions
src/Scripting
test_suite
FGTestApi
unit_tests/Navaids

View file

@ -134,6 +134,33 @@ static naRef wayptFlagToNasal(naContext c, unsigned int flags)
return naNil();
}
static std::optional<Airway::Level> airwayLevelFromNasal(naRef na)
{
if (naIsString(na)) {
const char* s = naStr_data(na);
if (!strcmp(s, "both")) return {Airway::Both};
if (!strcmp(s, "high")) return {Airway::HighLevel};
if (!strcmp(s, "low")) return {Airway::LowLevel};
return {};
}
if (!naIsNum(na)) {
return {};
}
const int num = static_cast<int>(na.num);
switch (num) {
case Airway::HighLevel: return {Airway::HighLevel};
case Airway::LowLevel: return {Airway::LowLevel};
case Airway::Both: return {Airway::Both};
default:
break;
// fall through
}
return {}; // fail
}
Waypt* wayptGhost(naRef r)
{
if (naGhost_type(r) == &WayptGhostType)
@ -1028,6 +1055,8 @@ static const char* airwayGhostGetMember(naContext c, void* g, naRef field, naRef
} else if (!strcmp(fieldName, "id"))
*out = stringToNasal(c, awy->ident());
else if (!strcmp(fieldName, "level")) {
// James was dumb, returning a string here since we didn't have the
// constant values exposed.
const auto level = awy->level();
switch (level) {
case Airway::HighLevel: *out = stringToNasal(c, "high"); break;
@ -1035,6 +1064,10 @@ static const char* airwayGhostGetMember(naContext c, void* g, naRef field, naRef
case Airway::Both: *out = stringToNasal(c, "both"); break;
default: *out = naNil();
}
} else if (!strcmp(fieldName, "level_code")) {
// so expose the numerical values here
const auto level = awy->level();
*out = naNum(static_cast<int>(level));
} else {
return 0;
}
@ -1341,9 +1374,18 @@ static naRef f_findAirway(naContext c, naRef me, int argc, naRef* args)
FGPositionedRef pos;
Airway::Level level = Airway::Both;
if (argc >= 2) {
pos = positionedFromArg(args[1]);
if (naIsString(args[1])) {
// level spec,
int posArgIndex = 1;
// try next arg as a level specifier first: this means you can't use
// a string navaid ident which is 'high', 'low' or 'both'.
auto maybeLevel = airwayLevelFromNasal(args[1]);
if (maybeLevel.has_value()) {
level = maybeLevel.value_or(Airway::Both);
++posArgIndex; // worked, so increment index
}
if (argc > posArgIndex) {
pos = positionedFromArg(args[posArgIndex]);
}
}
@ -1419,28 +1461,49 @@ static naRef f_createWPFrom(naContext c, naRef me, int argc, naRef* args)
static naRef f_createViaTo(naContext c, naRef me, int argc, naRef* args)
{
if (argc != 2) {
naRuntimeError(c, "createViaTo: needs exactly two arguments");
if ((argc < 2) || (argc > 3)) {
naRuntimeError(c, "createViaTo: needs two or three arguments");
}
AirwayRef airway = airwayGhost(args[0]);
naRef toArg = args[1];
if (!airway && naIsString(args[0])) {
std::string airwayName = naStr_data(args[0]);
auto level = Airway::Both;
if (argc == 3) {
// this means second arg is high / low select
auto l = airwayLevelFromNasal(args[1]);
toArg = args[2];
if (!l) {
naRuntimeError(c, "createViaTo: level argument is not accepted");
}
level = l.value_or(Airway::Both);
}
airway = Airway::findByIdent(airwayName, level);
if (!airway) {
naRuntimeError(c, "createViaTo: couldn't find airway with provided name: %s",
naStr_data(args[0]));
}
}
std::string airwayName = naStr_data(args[0]);
AirwayRef airway = Airway::findByIdent(airwayName, Airway::Both);
if (!airway) {
naRuntimeError(c, "createViaTo: couldn't find airway with provided name: %s",
naStr_data(args[0]));
naRuntimeError(c, "createViaTo: invalid airway");
}
FGPositionedRef nav;
if (naIsString(args[1])) {
WayptRef enroute = airway->findEnroute(naStr_data(args[1]));
if (naIsString(toArg)) {
WayptRef enroute = airway->findEnroute(naStr_data(toArg));
if (!enroute) {
naRuntimeError(c, "unknown waypoint on airway %s: %s",
naStr_data(args[0]), naStr_data(args[1]));
naStr_data(args[0]), naStr_data(toArg));
}
nav = enroute->source();
} else {
nav = positionedGhost(args[1]);
nav = positionedGhost(toArg);
if (!nav) {
naRuntimeError(c, "createViaTo: arg[1] is not a navaid");
}
@ -1456,8 +1519,8 @@ static naRef f_createViaTo(naContext c, naRef me, int argc, naRef* args)
static naRef f_createViaFromTo(naContext c, naRef me, int argc, naRef* args)
{
if (argc != 3) {
naRuntimeError(c, "createViaFromTo: needs exactly three arguments");
if ((argc < 3) || (argc > 4)) {
naRuntimeError(c, "createViaFromTo: needs three or four arguments");
}
auto from = positionedFromArg(args[0]);
@ -1465,27 +1528,47 @@ static naRef f_createViaFromTo(naContext c, naRef me, int argc, naRef* args)
naRuntimeError(c, "createViaFromTo: from wp not found");
}
std::string airwayName = naStr_data(args[1]);
AirwayRef airway = Airway::findByIdentAndNavaid(airwayName, from);
AirwayRef airway = airwayGhost(args[1]);
naRef toArg = args[2];
if (!airway && naIsString(args[1])) {
std::string airwayName = naStr_data(args[1]);
auto level = Airway::Both;
if (argc == 4) {
// this means third arg is high / low select
auto l = airwayLevelFromNasal(args[2]);
toArg = args[3];
if (!l) {
naRuntimeError(c, "createViaFromTo: level argument is not accepted");
}
level = l.value_or(Airway::Both);
}
airway = Airway::findByIdent(airwayName, level);
if (!airway) {
naRuntimeError(c, "createViaFromTo: couldn't find airway with provided name: %s",
naStr_data(args[1]));
}
}
if (!airway) {
naRuntimeError(c, "createViaFromTo: couldn't find airway with provided name: %s from wp %s",
naStr_data(args[0]),
from->ident().c_str());
naRuntimeError(c, "createViaFromTo: invalid airway");
}
FGPositionedRef nav;
if (naIsString(args[2])) {
WayptRef enroute = airway->findEnroute(naStr_data(args[2]));
if (naIsString(toArg)) {
WayptRef enroute = airway->findEnroute(naStr_data(toArg));
if (!enroute) {
naRuntimeError(c, "unknown waypoint on airway %s: %s",
naStr_data(args[1]), naStr_data(args[2]));
naStr_data(args[1]), naStr_data(toArg));
}
nav = enroute->source();
} else {
nav = positionedFromArg(args[2]);
nav = positionedFromArg(toArg);
if (!nav) {
naRuntimeError(c, "createViaFromTo: arg[2] is not a navaid");
naRuntimeError(c, "createViaFromTo: final arg is not a navaid");
}
}
@ -2149,6 +2232,11 @@ naRef initNasalFlightPlan(naRef globals, naContext c)
airwayPrototype = naNewHash(c);
naSave(c, airwayPrototype);
hashset(c, airwayPrototype, "contains", naNewFunc(c, naNewCCode(c, f_airway_contains)));
hashset(c, airwayPrototype, "HIGH", naNum(Airway::HighLevel));
hashset(c, airwayPrototype, "LOW", naNum(Airway::LowLevel));
hashset(c, airwayPrototype, "BOTH", naNum(Airway::Both));
hashset(c, globals, "Airway", airwayPrototype);
for (int i = 0; funcs[i].name; i++) {
hashset(c, globals, funcs[i].name,

View file

@ -434,11 +434,20 @@ bool executeNasal(const std::string& code)
if (!nasal) {
throw sg_exception("Nasal not available");
}
std::string output, errors;
bool ok = nasal->parseAndRunWithOutput(code, output, errors);
nasal->getAndClearErrorList();
std::string output, dummyErrors;
bool ok = nasal->parseAndRunWithOutput(code, output, dummyErrors);
if (!dummyErrors.empty()) {
SG_LOG(SG_NASAL, SG_ALERT, "Errors running Nasal:" << dummyErrors);
return false;
}
auto errors = nasal->getAndClearErrorList();
if (!errors.empty()) {
SG_LOG(SG_NASAL, SG_ALERT, "Errors running Nasal:" << errors);
for (auto err : errors) {
SG_LOG(SG_NASAL, SG_ALERT, "Errors running Nasal:" << err);
}
return false;
}

View file

@ -403,16 +403,19 @@ void FPNasalTests::testAirwaysAPI()
{
bool ok = FGTestApi::executeNasal(R"(
var airwayIdent = "L620";
var airwayStore = airway(airwayIdent);
var airwayStore = airway(airwayIdent, "low");
unitTest.assert(airwayStore != nil, "Airway " ~ airwayIdent ~ " not found");
unitTest.assert(airwayStore.id == airwayIdent, "Incorrect airway found");
unitTest.assert_equal(airwayStore.level, 'low', "Incorrect airway found");
unitTest.assert_equal(airwayStore.level_code, Airway.LOW, "Incorrect airway found");
airwayIdent = "UL620";
var cln = findNavaidsByID("CLN", "VOR")[0];
airwayStore = airway(airwayIdent, cln);
unitTest.assert(airwayStore != nil, "Airway " ~ airwayIdent ~ " not found");
unitTest.assert(airwayStore.id == airwayIdent, "Incorrect airway found");
unitTest.assert_equal(airwayStore.level_code, Airway.HIGH, "Incorrect airway found");
airwayIdent = "J547";
airwayStore = airway(airwayIdent);
unitTest.assert(airwayStore != nil, "Airway " ~ airwayIdent ~ " not found");
@ -420,6 +423,30 @@ void FPNasalTests::testAirwaysAPI()
)");
CPPUNIT_ASSERT(ok);
ok = FGTestApi::executeNasal(R"(
var airwayIdent = "L620";
var airwayStore = airway(airwayIdent, Airway.LOW);
var cln = findNavaidsByID("CLN", "VOR")[0];
var v1 = createViaTo(airwayIdent, "CLN");
unitTest.assert_equal(v1.wp_type, "via");
unitTest.assert_equal(v1.airway.id, 'L620');
unitTest.assert_equal(v1.airway.level_code, Airway.LOW);
var v2 = createViaTo(airwayStore, "TULIP");
unitTest.assert_equal(v2.wp_type, "via");
unitTest.assert_equal(v2.airway.id, airwayIdent);
var v3 = createViaFromTo(cln, "L620", 'low', "TULIP");
unitTest.assert_equal(v3.airway.id, 'L620');
var v4 = createViaFromTo(cln, "L620", "REDFA");
unitTest.assert_equal(v4.airway.level_code, Airway.LOW);
)");
CPPUNIT_ASSERT(ok);
}
void FPNasalTests::testTotalDistanceAPI()