Skip to content

Commit

Permalink
[WASimModule] Add ability to specify/set a default L var value and un…
Browse files Browse the repository at this point in the history
…it type in `GetCreate` command to use if the variable needs to be created; `GetCreate` and `SetCreate` commands for non-L types now silently fall back to `Get` and `Set` respectively; Fixed that command response for `GetCreate` was always sent as if responding to a `Get` command.
  • Loading branch information
mpaperno committed Nov 1, 2023
1 parent e16049a commit 61a5267
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 18 deletions.
47 changes: 32 additions & 15 deletions src/WASimModule/WASimModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ int getVariableId(char varType, const char *name, bool createLocal = false)
// Parse a command string to find a variable name/unit/index and populates the respective reference params.
// Lookups are done on var names, depending on varType, and unit strings, to attempt conversion to IDs.
// Used by setVariable() and getVariable(). Only handles A/L/T var types (not needed for others).
bool parseVariableString(const char varType, const char *data, ID &varId, bool createLocal, ENUM *unitId = nullptr, uint8_t *varIndex = nullptr, string *varName = nullptr)
bool parseVariableString(const char varType, const char *data, ID &varId, bool createLocal, ENUM *unitId = nullptr, uint8_t *varIndex = nullptr, string *varName = nullptr, bool *existed = nullptr)
{
string_view svVar(data, strlen(data)), svUnit{};

Expand All @@ -784,13 +784,23 @@ bool parseVariableString(const char varType, const char *data, ID &varId, bool c
if (svVar.empty())
return false;

if (varName)
*varName = string(svVar);
// Try to parse the remaining var name string as a numeric ID
auto result = from_chars(svVar.data(), svVar.data() + svVar.size(), varId);
// if number conversion failed, look up variable id
if (result.ec != errc())
varId = getVariableId(varType, string(svVar).c_str(), createLocal);
if (result.ec != errc()) {
const std::string vname(svVar);
if (createLocal && !!existed) {
varId = check_named_variable(vname.c_str());
*existed = varId > -1;
if (!*existed)
varId = register_named_variable(vname.c_str());
}
else {
varId = getVariableId(varType, vname.c_str(), createLocal);
}
if (!!varName)
*varName = vname;
}
// failed to find a numeric id, whole input was invalid
if (varId < 0)
return false;
Expand Down Expand Up @@ -970,29 +980,39 @@ void getVariable(const Client *c, const Command *const cmd)
const char varType = char(cmd->uData);
const char *data = cmd->sData;
LOG_TRC << "getVariable(" << varType << ", " << quoted(data) << ") for client " << c->name;
if (cmd->commandId == CommandId::GetCreate && varType != 'L') {
logAndNak(c, *cmd, ostringstream() << "The GetCreate command only supports the 'L' variable type.");
return;
}

// Anything besides L/A/T type vars just gets converted to calc code.
if (!Utilities::isIndexedVariableType(varType)) {
const ostringstream codeStr = ostringstream() << "(" << varType << ':' << data << ')';
const Command execCmd(cmd->commandId, +CalcResultType::Double, codeStr.str().c_str(), 0.0, cmd->token);
return execCalculatorCode(c, &execCmd);
}

ID varId{-1};
ENUM unitId{-1};
uint8_t varIndex{0};
string varName;
if (!parseVariableString(varType, data, varId, (cmd->commandId == CommandId::GetCreate), &unitId, &varIndex, &varName)) {
bool existed = true;
if (!parseVariableString(varType, data, varId, (cmd->commandId == CommandId::GetCreate && varType == 'L'), &unitId, &varIndex, &varName, &existed)) {
logAndNak(c, *cmd, ostringstream() << "Could not resolve Variable ID for Get command from string " << quoted(data));
return;
}

// !existed can only happen for an L var if we just created it. In that case it has a default value and unit type (0.0, number).
if (!existed) {
if (unitId > -1)
set_named_variable_typed_value(varId, cmd->fData, unitId);
else if (cmd->fData != 0.0)
set_named_variable_value(varId, cmd->fData);
sendResponse(c, Command(CommandId::Ack, (uint32_t)CommandId::GetCreate, nullptr, cmd->fData, cmd->token));
LOG_DBG << "getVariable(" << quoted(data) << ") created new variable for client " << c->name;
return;
}

calcResult_t res = calcResult_t { CalcResultType::Double, STRSZ_CMD, varId, unitId, varIndex, varName.c_str() };
if (!getNamedVariableValue(varType, res))
return logAndNak(c, *cmd, ostringstream() << "getNamedVariableValue() returned error result for code " << quoted(data));
Command resp(CommandId::Ack, (uint32_t)CommandId::Get);
Command resp(CommandId::Ack, (uint32_t)cmd->commandId);
resp.token = cmd->token;
switch (res.resultMemberIndex) {
case 0: resp.fData = res.fVal; break;
Expand All @@ -1010,10 +1030,7 @@ void setVariable(const Client *c, const Command *const cmd)
const char *data = cmd->sData;
const double value = cmd->fData;
LOG_TRC << "setVariable(" << varType << ", " << quoted(data) << ", " << value << ") for client " << c->name;
if (cmd->commandId == CommandId::SetCreate && varType != 'L') {
logAndNak(c, *cmd, ostringstream() << "The SetCreate command only supports the 'L' variable type.");
return;
}

// Anything besides an L var just gets converted to calc code.
if (varType != 'L') {
const ostringstream codeStr = ostringstream() << fixed << setprecision(7) << value << " (>" << varType << ':' << data << ')';
Expand Down
9 changes: 6 additions & 3 deletions src/include/enums_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,16 @@ namespace WSMCMND_ENUM_NAMESPACE
/// For example, a SimVar: ```uData = 'A'; sData = "PROP BETA:2,degrees";``` \n
/// Other variables types can also be requested ('B', 'E', 'M', etc) but such requests are simply converted to a calculator string and processed as an `Exec` type command (using an `Exec` command directly may be more efficient).\n
/// Result is returned with the `Ack` response in `fData` as a double-precision value. In case of failure a `Nak` is returned with possible error message in `sData`.
GetCreate, ///< Same as `Get` but creates the variable if it doesn't already exist (with `register_named_variable()` _Gauge API_). The returned value will be `0.0` (see `SetCreate` to assign a default value and `Lookup` to check if a variable exists).
/// **This only works with `L` (local) type variables.**
GetCreate, ///< Same as `Get` but creates a local 'L' variable if it doesn't already exist (with `register_named_variable()` _Gauge API_). Use `Lookup` command to check if a variable exists.
/// \n **Since v1.2:** If a variable is created, the value provided in `fData` will be used as the initial value of the variable, and will be returned as the result
/// (essentially providing a default value in this case). Previous versions would _not_ set a value (or unit type) on the variable after creating it and would return the default of `0.0`. \n
/// **Creating variables only works with `L` (local) types.** Since v1.2, for all other types this command will be handled the same as `Get`. Previous versions would return a `Nak`.
Set, ///< Set a named local variable with optional unit type. `uData` is a char of the variable type, with default of 'L' for local vars.
/// `sData` is the variable name or numeric ID (for local vars only), optionally followed by comma (`,`) and unit name (or numeric unit ID for local vars) (**no spaces**). The value to set is passed in `fData` member.\n
/// For example, a SimVar: ```uData = 'A'; sData = "PROP RPM:2,rpm"; fData = 2200;```\n
/// Other variables types can also be set this way ('A', 'H", 'K', etc) but such requests are simply converted to a calculator string and processed as an `Exec` type command (using an `Exec` command directly may be slightly more efficient).
SetCreate, ///< Same as `Set` but creates the variable first if it doesn't already exist (with `register_named_variable()` _Gauge API_). Use the `Lookup` command to check if a variable exists. **This only works with `L` (local) type variables.**
SetCreate, ///< Same as `Set` but creates a local 'L' variable if it doesn't already exist (with `register_named_variable()` _Gauge API_). Use the `Lookup` command to check if a variable exists. \n
/// **Creating variables only works with `L` (local) types.** Since v1.2, for all other types this command will be handled the same as `Get`. Previous versions would return a `Nak`.
Exec, ///< Run calculator code contained in `sData` with `WASimCommander::CalcResultType` in `uData`. Result, if any, is returned with the `Ack` response, numeric types in `fData` and strings in `sData`.
/// (Due to the way the _Gauge API_ calculator function works, a string result is typically also returned even when only a numeric result is requested.)\n\n
/// In case of failure a `Nak` is returned with possible error message in `sData`. Note however that the _Gauge API_ functions often happily return a "success" status even when the actual thing you're trying to do fails. The only feedback
Expand Down

0 comments on commit 61a5267

Please sign in to comment.