diff --git a/network/RemoteControl.cpp b/network/RemoteControl.cpp index 6499532a..fefc3ea8 100644 --- a/network/RemoteControl.cpp +++ b/network/RemoteControl.cpp @@ -33,6 +33,7 @@ #include "dmr/Control.h" #include "p25/Control.h" #include "nxdn/Control.h" +#include "modem/Modem.h" #include "host/Host.h" #include "network/UDPSocket.h" #include "RemoteControl.h" @@ -74,6 +75,8 @@ using namespace modem; #define RCD_KILL "mdm-kill" #define RCD_FORCE_KILL "mdm-force-kill" +#define RCD_PERMIT_TG "permit-tg" + #define RCD_RID_WLIST "rid-whitelist" #define RCD_RID_BLIST "rid-blacklist" @@ -199,9 +202,9 @@ void RemoteControl::setLookups(lookups::RadioIdLookup* ridLookup, lookups::Talkg /// Process remote network command data. /// /// Instance of the Host class. -/// Instance of the Control class. -/// Instance of the Control class. -/// Instance of the Control class. +/// Instance of the DMR Control class. +/// Instance of the P25 Control class. +/// Instance of the NXDN Control class. void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn) { std::vector args = std::vector(); @@ -289,133 +292,10 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx reply = displayHelp(); } else if (rcom == RCD_GET_STATUS) { - reply = ""; - yaml::Node systemConf = host->m_conf["system"]; - { - yaml::Node modemConfig = host->m_conf["system"]["modem"]; - std::string type = modemConfig["protocol"]["type"].as(); - - yaml::Node uartConfig = modemConfig["protocol"]["uart"]; - std::string modemPort = uartConfig["port"].as(); - uint32_t portSpeed = uartConfig["speed"].as(115200U); - - reply += string_format("Host State: %u, DMR: %u, P25: %u, NXDN: %u, Port Type: %s, Modem Port: %s, Port Speed: %u, Proto Ver: %u", host->m_state, - dmr != nullptr, p25 != nullptr, nxdn != nullptr, type.c_str(), modemPort.c_str(), portSpeed, host->m_modem->getVersion()); - } - - { - if (!host->m_modem->isHotspot()) { - reply += string_format("\r\nPTT Invert: %s, RX Invert: %s, TX Invert: %s, DC Blocker: %s", - host->m_modem->m_pttInvert ? "yes" : "no", host->m_modem->m_rxInvert ? "yes" : "no", host->m_modem->m_txInvert ? "yes" : "no", host->m_modem->m_dcBlocker ? "yes" : "no"); - } - reply += string_format("\r\nRX Level: %.1f%%, CW TX Level: %.1f%%, DMR TX Level: %.1f%%, P25 TX Level: %.1f%%, NXDN TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d", - host->m_modem->m_rxLevel, host->m_modem->m_cwIdTXLevel, host->m_modem->m_dmrTXLevel, host->m_modem->m_p25TXLevel, host->m_modem->m_nxdnTXLevel, host->m_modem->m_txDCOffset, host->m_modem->m_rxDCOffset); - if (!host->m_modem->isHotspot()) { - reply += string_format("\r\nDMR Symbol +/- 3 Level Adj.: %d, DMR Symbol +/- 1 Level Adj.: %d, P25 Symbol +/- 3 Level Adj.: %d, P25 Symbol +/- 1 Level Adj.: %d", - host->m_modem->m_dmrSymLevel3Adj, host->m_modem->m_dmrSymLevel1Adj, host->m_modem->m_p25SymLevel3Adj, host->m_modem->m_p25SymLevel1Adj); - - // are we on a protocol version 3 firmware? - if (host->m_modem->getVersion() >= 3U) { - reply += string_format("\r\nNXDN Symbol +/- 3 Level Adj.: %d, NXDN Symbol +/- 1 Level Adj.: %d", - host->m_modem->m_nxdnSymLevel3Adj, host->m_modem->m_nxdnSymLevel1Adj); - } - } - if (host->m_modem->isHotspot()) { - reply += string_format("\r\nDMR Disc. BW: %d, P25 Disc. BW: %d, DMR Post Demod BW: %d, P25 Post Demod BW: %d", - host->m_modem->m_dmrDiscBWAdj, host->m_modem->m_p25DiscBWAdj, host->m_modem->m_dmrPostBWAdj, host->m_modem->m_p25PostBWAdj); - - // are we on a protocol version 3 firmware? - if (host->m_modem->getVersion() >= 3U) { - reply += string_format("\r\nNXDN Disc. BW: %d, NXDN Post Demod BW: %d", - host->m_modem->m_nxdnDiscBWAdj, host->m_modem->m_nxdnPostBWAdj); - - reply += string_format("\r\nAFC Enabled: %u, AFC KI: %u, AFC KP: %u, AFC Range: %u", - host->m_modem->m_afcEnable, host->m_modem->m_afcKI, host->m_modem->m_afcKP, host->m_modem->m_afcRange); - } - - switch (host->m_modem->m_adfGainMode) { - case ADF_GAIN_AUTO_LIN: - reply += "\r\nADF7021 Gain Mode: Auto High Linearity"; - break; - case ADF_GAIN_LOW: - reply += "\r\nADF7021 Gain Mode: Low"; - break; - case ADF_GAIN_HIGH: - reply += "\r\nADF7021 Gain Mode: High"; - break; - case ADF_GAIN_AUTO: - default: - reply += "\r\nADF7021 Gain Mode: Auto"; - break; - } - } - reply += string_format("\r\nFDMA Preambles: %u (%.1fms), DMR Rx Delay: %u (%.1fms), P25 Corr. Count: %u (%.1fms)", host->m_modem->m_fdmaPreamble, float(host->m_modem->m_fdmaPreamble) * 0.2222F, - host->m_modem->m_dmrRxDelay, float(host->m_modem->m_dmrRxDelay) * 0.0416666F, host->m_modem->m_p25CorrCount, float(host->m_modem->m_p25CorrCount) * 0.667F); - reply += string_format("\r\nRx Freq: %uHz, Tx Freq: %uHz, Rx Offset: %dHz, Tx Offset: %dHz", host->m_modem->m_rxFrequency, host->m_modem->m_txFrequency, host->m_modem->m_rxTuning, host->m_modem->m_txTuning); - reply += string_format("\r\nRx Effective Freq: %uHz, Tx Effective Freq: %uHz", host->m_modem->m_rxFrequency + host->m_modem->m_rxTuning, host->m_modem->m_txFrequency + host->m_modem->m_txTuning); - } + reply = rcdGetStatus(host, dmr, p25, nxdn); } else if (rcom == RCD_MODE && argCnt >= 1U) { - std::string mode = getArgString(args, 0U); - if (mode == RCD_MODE_OPT_IDLE) { - host->m_fixedMode = false; - host->setState(STATE_IDLE); - - reply = string_format("Dynamic mode, mode %u", host->m_state); - LogInfoEx(LOG_RCON, reply.c_str()); - } - else if (mode == RCD_MODE_OPT_LCKOUT) { - host->m_fixedMode = false; - host->setState(HOST_STATE_LOCKOUT); - reply = string_format("Lockout mode, mode %u", host->m_state); - LogInfoEx(LOG_RCON, reply.c_str()); - } -#if defined(ENABLE_DMR) - else if (mode == RCD_MODE_OPT_FDMR) { - if (dmr != nullptr) { - host->m_fixedMode = true; - host->setState(STATE_DMR); - reply = string_format("Fixed mode, mode %u", host->m_state); - LogInfoEx(LOG_RCON, reply.c_str()); - } - else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } - } -#endif // defined(ENABLE_DMR) -#if defined(ENABLE_P25) - else if (mode == RCD_MODE_OPT_FP25) { - if (p25 != nullptr) { - host->m_fixedMode = true; - host->setState(STATE_P25); - reply = string_format("Fixed mode, mode %u", host->m_state); - LogInfoEx(LOG_RCON, reply.c_str()); - } - else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } - } -#endif // defined(ENABLE_P25) -#if defined(ENABLE_NXDN) - else if (mode == RCD_MODE_OPT_FNXDN) { - if (p25 != nullptr) { - host->m_fixedMode = true; - host->setState(STATE_NXDN); - reply = string_format("Fixed mode, mode %u", host->m_state); - LogInfoEx(LOG_RCON, reply.c_str()); - } - else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } - } -#endif // defined(ENABLE_NXDN) - else { - reply = INVALID_OPT_STR "invalid mode!"; - LogError(LOG_RCON, reply.c_str()); - } + reply = rcdMode(args, host, dmr, p25, nxdn); } else if (rcom == RCD_KILL) { g_killed = true; @@ -424,6 +304,9 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx g_killed = true; host->setState(HOST_STATE_QUIT); // ensures immediate cessation of service } + else if (rcom == RCD_PERMIT_TG && argCnt >= 1U) { + reply = rcdPermitTG(args, host, dmr, p25, nxdn); + } else if (rcom == RCD_RID_WLIST && argCnt >= 1U) { uint32_t srcId = getArgUInt32(args, 0U); if (srcId != 0U) { @@ -978,187 +861,17 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx #endif // defined(ENABLE_NXDN) #if defined(ENABLE_DMR) else if (rcom == RCD_DMRD_MDM_INJ && argCnt >= 1U) { - if (dmr != nullptr) { - uint8_t slot = getArgUInt32(args, 0U); - std::string argString = getArgString(args, 1U); - const char* fileName = argString.c_str(); - if (fileName != nullptr) { - FILE* file = ::fopen(fileName, "r"); - if (file != nullptr) { - uint8_t* buffer = nullptr; - int32_t fileSize = 0; - - // obtain file size - ::fseek(file, 0, SEEK_END); - fileSize = ::ftell(file); - ::rewind(file); - - // allocate a buffer and read file - buffer = new uint8_t[fileSize]; - if (buffer != nullptr) { - int32_t bytes = ::fread(buffer, 1U, fileSize, file); - if (bytes == fileSize) { - uint8_t sync[dmr::DMR_SYNC_LENGTH_BYTES]; - ::memcpy(sync, buffer, dmr::DMR_SYNC_LENGTH_BYTES); - - // count data sync errors - uint8_t dataErrs = 0U; - for (uint8_t i = 0U; i < dmr::DMR_SYNC_LENGTH_BYTES; i++) - dataErrs += Utils::countBits8(sync[i] ^ dmr::DMR_MS_DATA_SYNC_BYTES[i]); - - // count voice sync errors - uint8_t voiceErrs = 0U; - for (uint8_t i = 0U; i < dmr::DMR_SYNC_LENGTH_BYTES; i++) - voiceErrs += Utils::countBits8(sync[i] ^ dmr::DMR_MS_VOICE_SYNC_BYTES[i]); - - if ((dataErrs <= 4U) || (voiceErrs <= 4U)) { - if (slot == 0U) { - host->m_modem->injectDMRData1(buffer, fileSize); - } - else if (slot == 1U) { - host->m_modem->injectDMRData2(buffer, fileSize); - } - else { - reply = CMD_FAILED_STR "invalid DMR slot!"; - LogError(LOG_RCON, reply.c_str()); - } - } - else { - reply = CMD_FAILED_STR "DMR data has too many errors!"; - LogError(LOG_RCON, reply.c_str()); - } - } - else { - reply = CMD_FAILED_STR "DMR failed to open DMR data!"; - LogError(LOG_RCON, reply.c_str()); - } - - delete[] buffer; - } - - ::fclose(file); - } - } - } - else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } + reply = rcdDMRModemInj(args, host, dmr); } #endif // defined(ENABLE_DMR) #if defined(ENABLE_P25) else if (rcom == RCD_P25D_MDM_INJ && argCnt >= 1U) { - if (p25 != nullptr) { - std::string argString = getArgString(args, 0U); - const char* fileName = argString.c_str(); - if (fileName != nullptr) { - FILE* file = ::fopen(fileName, "r"); - if (file != nullptr) { - uint8_t* buffer = nullptr; - int32_t fileSize = 0; - - // obtain file size - ::fseek(file, 0, SEEK_END); - fileSize = ::ftell(file); - ::rewind(file); - - // allocate a buffer and read file - buffer = new uint8_t[fileSize]; - if (buffer != nullptr) { - int32_t bytes = ::fread(buffer, 1U, fileSize, file); - if (bytes == fileSize) { - uint8_t sync[p25::P25_SYNC_LENGTH_BYTES]; - ::memcpy(sync, buffer, p25::P25_SYNC_LENGTH_BYTES); - - uint8_t errs = 0U; - for (uint8_t i = 0U; i < p25::P25_SYNC_LENGTH_BYTES; i++) - errs += Utils::countBits8(sync[i] ^ p25::P25_SYNC_BYTES[i]); - - if (errs <= 4U) { - bool valid = p25->nid().decode(buffer); - if (valid) { - host->m_modem->injectP25Data(buffer, fileSize); - } - else { - reply = CMD_FAILED_STR "P25 data did not contain a valid NID!"; - LogError(LOG_RCON, reply.c_str()); - } - } - else { - reply = CMD_FAILED_STR "P25 data has too many errors!"; - LogError(LOG_RCON, reply.c_str()); - } - } - else { - reply = CMD_FAILED_STR "P25 failed to open P25 data!"; - LogError(LOG_RCON, reply.c_str()); - } - - delete[] buffer; - } - - ::fclose(file); - } - } - } - else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } + reply = rcdP25ModemInj(args, host, p25); } #endif // defined(ENABLE_P25) #if defined(ENABLE_NXDN) else if (rcom == RCD_NXDD_MDM_INJ && argCnt >= 1U) { - if (p25 != nullptr) { - std::string argString = getArgString(args, 0U); - const char* fileName = argString.c_str(); - if (fileName != nullptr) { - FILE* file = ::fopen(fileName, "r"); - if (file != nullptr) { - uint8_t* buffer = nullptr; - int32_t fileSize = 0; - - // obtain file size - ::fseek(file, 0, SEEK_END); - fileSize = ::ftell(file); - ::rewind(file); - - // allocate a buffer and read file - buffer = new uint8_t[fileSize]; - if (buffer != nullptr) { - int32_t bytes = ::fread(buffer, 1U, fileSize, file); - if (bytes == fileSize) { - uint8_t sync[nxdn::NXDN_FSW_BYTES_LENGTH]; - ::memcpy(sync, buffer, nxdn::NXDN_FSW_BYTES_LENGTH); - - uint8_t errs = 0U; - for (uint8_t i = 0U; i < nxdn::NXDN_FSW_BYTES_LENGTH; i++) - errs += Utils::countBits8(sync[i] ^ nxdn::NXDN_FSW_BYTES[i]); - - if (errs <= 4U) { - host->m_modem->injectNXDNData(buffer, fileSize); - } - else { - reply = CMD_FAILED_STR "NXDN data has too many errors!"; - LogError(LOG_RCON, reply.c_str()); - } - } - else { - reply = CMD_FAILED_STR "NXDN failed to open NXDN data!"; - LogError(LOG_RCON, reply.c_str()); - } - - delete[] buffer; - } - - ::fclose(file); - } - } - } - else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } + reply = rcdNXDNModemInj(args, host, nxdn); } #endif // defined(ENABLE_NXDN) else { @@ -1273,6 +986,7 @@ void RemoteControl::writeResponse(std::string reply, sockaddr_storage address, u /// /// Helper to print the remote control help. /// +/// std::string RemoteControl::displayHelp() { std::string reply = ""; @@ -1286,6 +1000,8 @@ std::string RemoteControl::displayHelp() reply += " mdm-kill Causes the host to quit\r\n"; reply += " mdm-force-kill Causes the host to quit immediately\r\n"; reply += "\r\n"; + reply += " permit-tg Causes the host to permit the specified destination ID if non-authoritative\r\n"; + reply += "\r\n"; reply += " rid-whitelist Whitelists the specified RID in the host ACL tables\r\n"; reply += " rid-blacklist Blacklists the specified RID in the host ACL tables\r\n"; reply += "\r\n"; @@ -1339,84 +1055,468 @@ std::string RemoteControl::displayHelp() } /// -/// +/// /// -/// -/// +/// Instance of the Host class. +/// Instance of the DMR Control class. +/// Instance of the P25 Control class. +/// Instance of the NXDN Control class. /// -std::string RemoteControl::getArgString(std::vector args, uint32_t n) const +std::string RemoteControl::rcdGetStatus(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn) { - n += 1; - if (n >= args.size()) - return ""; + std::string reply = ""; + yaml::Node systemConf = host->m_conf["system"]; + { + yaml::Node modemConfig = host->m_conf["system"]["modem"]; + std::string type = modemConfig["protocol"]["type"].as(); - return args.at(n); -} + yaml::Node uartConfig = modemConfig["protocol"]["uart"]; + std::string modemPort = uartConfig["port"].as(); + uint32_t portSpeed = uartConfig["speed"].as(115200U); -/// -/// -/// -/// -/// -/// -uint64_t RemoteControl::getArgUInt64(std::vector args, uint32_t n) const -{ - return (uint64_t)::atol(getArgString(args, n).c_str()); + reply += string_format("Host State: %u, DMR: %u, P25: %u, NXDN: %u, Port Type: %s, Modem Port: %s, Port Speed: %u, Proto Ver: %u", host->m_state, + dmr != nullptr, p25 != nullptr, nxdn != nullptr, type.c_str(), modemPort.c_str(), portSpeed, host->m_modem->getVersion()); + } + + { + if (!host->m_modem->isHotspot()) { + reply += string_format("\r\nPTT Invert: %s, RX Invert: %s, TX Invert: %s, DC Blocker: %s", + host->m_modem->m_pttInvert ? "yes" : "no", host->m_modem->m_rxInvert ? "yes" : "no", host->m_modem->m_txInvert ? "yes" : "no", host->m_modem->m_dcBlocker ? "yes" : "no"); + } + reply += string_format("\r\nRX Level: %.1f%%, CW TX Level: %.1f%%, DMR TX Level: %.1f%%, P25 TX Level: %.1f%%, NXDN TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d", + host->m_modem->m_rxLevel, host->m_modem->m_cwIdTXLevel, host->m_modem->m_dmrTXLevel, host->m_modem->m_p25TXLevel, host->m_modem->m_nxdnTXLevel, host->m_modem->m_txDCOffset, host->m_modem->m_rxDCOffset); + if (!host->m_modem->isHotspot()) { + reply += string_format("\r\nDMR Symbol +/- 3 Level Adj.: %d, DMR Symbol +/- 1 Level Adj.: %d, P25 Symbol +/- 3 Level Adj.: %d, P25 Symbol +/- 1 Level Adj.: %d", + host->m_modem->m_dmrSymLevel3Adj, host->m_modem->m_dmrSymLevel1Adj, host->m_modem->m_p25SymLevel3Adj, host->m_modem->m_p25SymLevel1Adj); + + // are we on a protocol version 3 firmware? + if (host->m_modem->getVersion() >= 3U) { + reply += string_format("\r\nNXDN Symbol +/- 3 Level Adj.: %d, NXDN Symbol +/- 1 Level Adj.: %d", + host->m_modem->m_nxdnSymLevel3Adj, host->m_modem->m_nxdnSymLevel1Adj); + } + } + if (host->m_modem->isHotspot()) { + reply += string_format("\r\nDMR Disc. BW: %d, P25 Disc. BW: %d, DMR Post Demod BW: %d, P25 Post Demod BW: %d", + host->m_modem->m_dmrDiscBWAdj, host->m_modem->m_p25DiscBWAdj, host->m_modem->m_dmrPostBWAdj, host->m_modem->m_p25PostBWAdj); + + // are we on a protocol version 3 firmware? + if (host->m_modem->getVersion() >= 3U) { + reply += string_format("\r\nNXDN Disc. BW: %d, NXDN Post Demod BW: %d", + host->m_modem->m_nxdnDiscBWAdj, host->m_modem->m_nxdnPostBWAdj); + + reply += string_format("\r\nAFC Enabled: %u, AFC KI: %u, AFC KP: %u, AFC Range: %u", + host->m_modem->m_afcEnable, host->m_modem->m_afcKI, host->m_modem->m_afcKP, host->m_modem->m_afcRange); + } + + switch (host->m_modem->m_adfGainMode) { + case ADF_GAIN_AUTO_LIN: + reply += "\r\nADF7021 Gain Mode: Auto High Linearity"; + break; + case ADF_GAIN_LOW: + reply += "\r\nADF7021 Gain Mode: Low"; + break; + case ADF_GAIN_HIGH: + reply += "\r\nADF7021 Gain Mode: High"; + break; + case ADF_GAIN_AUTO: + default: + reply += "\r\nADF7021 Gain Mode: Auto"; + break; + } + } + reply += string_format("\r\nFDMA Preambles: %u (%.1fms), DMR Rx Delay: %u (%.1fms), P25 Corr. Count: %u (%.1fms)", host->m_modem->m_fdmaPreamble, float(host->m_modem->m_fdmaPreamble) * 0.2222F, + host->m_modem->m_dmrRxDelay, float(host->m_modem->m_dmrRxDelay) * 0.0416666F, host->m_modem->m_p25CorrCount, float(host->m_modem->m_p25CorrCount) * 0.667F); + reply += string_format("\r\nRx Freq: %uHz, Tx Freq: %uHz, Rx Offset: %dHz, Tx Offset: %dHz", host->m_modem->m_rxFrequency, host->m_modem->m_txFrequency, host->m_modem->m_rxTuning, host->m_modem->m_txTuning); + reply += string_format("\r\nRx Effective Freq: %uHz, Tx Effective Freq: %uHz", host->m_modem->m_rxFrequency + host->m_modem->m_rxTuning, host->m_modem->m_txFrequency + host->m_modem->m_txTuning); + } + + return reply; } /// -/// +/// /// /// -/// +/// Instance of the Host class. +/// Instance of the DMR Control class. +/// Instance of the P25 Control class. +/// Instance of the NXDN Control class. /// -uint32_t RemoteControl::getArgUInt32(std::vector args, uint32_t n) const +std::string RemoteControl::rcdMode(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn) { - return (uint32_t)::atoi(getArgString(args, n).c_str()); + std::string reply = ""; + std::string mode = getArgString(args, 0U); + if (mode == RCD_MODE_OPT_IDLE) { + host->m_fixedMode = false; + host->setState(STATE_IDLE); + + reply = string_format("Dynamic mode, mode %u", host->m_state); + LogInfoEx(LOG_RCON, reply.c_str()); + } + else if (mode == RCD_MODE_OPT_LCKOUT) { + host->m_fixedMode = false; + host->setState(HOST_STATE_LOCKOUT); + reply = string_format("Lockout mode, mode %u", host->m_state); + LogInfoEx(LOG_RCON, reply.c_str()); + } +#if defined(ENABLE_DMR) + else if (mode == RCD_MODE_OPT_FDMR) { + if (dmr != nullptr) { + host->m_fixedMode = true; + host->setState(STATE_DMR); + reply = string_format("Fixed mode, mode %u", host->m_state); + LogInfoEx(LOG_RCON, reply.c_str()); + } + else { + reply = CMD_FAILED_STR "DMR mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } +#endif // defined(ENABLE_DMR) +#if defined(ENABLE_P25) + else if (mode == RCD_MODE_OPT_FP25) { + if (p25 != nullptr) { + host->m_fixedMode = true; + host->setState(STATE_P25); + reply = string_format("Fixed mode, mode %u", host->m_state); + LogInfoEx(LOG_RCON, reply.c_str()); + } + else { + reply = CMD_FAILED_STR "P25 mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } +#endif // defined(ENABLE_P25) +#if defined(ENABLE_NXDN) + else if (mode == RCD_MODE_OPT_FNXDN) { + if (nxdn != nullptr) { + host->m_fixedMode = true; + host->setState(STATE_NXDN); + reply = string_format("Fixed mode, mode %u", host->m_state); + LogInfoEx(LOG_RCON, reply.c_str()); + } + else { + reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } +#endif // defined(ENABLE_NXDN) + else { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } + + return reply; } /// -/// +/// /// /// -/// +/// Instance of the Host class. +/// Instance of the DMR Control class. +/// Instance of the P25 Control class. +/// Instance of the NXDN Control class. /// -int32_t RemoteControl::getArgInt32(std::vector args, uint32_t n) const +std::string RemoteControl::rcdPermitTG(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn) { - return ::atoi(getArgString(args, n).c_str()); + std::string reply = ""; + if (host->m_authoritative) { + DVM_STATE state = (DVM_STATE)getArgInt32(args, 0U); + uint32_t dstId = getArgInt32(args, 1U); + if (dstId == 0U) { + reply = string_format(INVALID_OPT_STR "illegal TGID permitted (%u)", dstId); + LogError(LOG_RCON, reply.c_str()); + } + else { + switch (state) { + case STATE_DMR: +#if defined(ENABLE_DMR) + { + if (dmr != nullptr) { + // TODO TODO TODO -- handle permitting destination IDs + } + else { + reply = CMD_FAILED_STR "DMR mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } +#else + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } +#endif // defined(ENABLE_DMR) + break; + case STATE_P25: +#if defined(ENABLE_P25) + { + if (p25 != nullptr) { + // TODO TODO TODO -- handle permitting destination IDs + } + else { + reply = CMD_FAILED_STR "P25 mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } +#else + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } +#endif // defined(ENABLE_P25) + break; + case STATE_NXDN: +#if defined(ENABLE_NXDN) + { + if (nxdn != nullptr) { + // TODO TODO TODO -- handle permitting destination IDs + } + else { + reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } +#else + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } +#endif // defined(ENABLE_NXDN) + break; + default: + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + break; + } + } + } + else { + reply = CMD_FAILED_STR "Host is authoritative, cannot permit TG!"; + LogError(LOG_RCON, reply.c_str()); + } + + return reply; } /// -/// +/// /// /// -/// +/// Instance of the Host class. +/// Instance of the DMR Control class. +/// Instance of the P25 Control class. +/// Instance of the NXDN Control class. /// -uint16_t RemoteControl::getArgUInt16(std::vector args, uint32_t n) const +std::string RemoteControl::rcdDMRModemInj(std::vector args, Host* host, dmr::Control* dmr) { - return (uint16_t)::atoi(getArgString(args, n).c_str()); + std::string reply = ""; + + if (dmr != nullptr) { + uint8_t slot = getArgUInt32(args, 0U); + std::string argString = getArgString(args, 1U); + const char* fileName = argString.c_str(); + if (fileName != nullptr) { + FILE* file = ::fopen(fileName, "r"); + if (file != nullptr) { + uint8_t* buffer = nullptr; + int32_t fileSize = 0; + + // obtain file size + ::fseek(file, 0, SEEK_END); + fileSize = ::ftell(file); + ::rewind(file); + + // allocate a buffer and read file + buffer = new uint8_t[fileSize]; + if (buffer != nullptr) { + int32_t bytes = ::fread(buffer, 1U, fileSize, file); + if (bytes == fileSize) { + uint8_t sync[dmr::DMR_SYNC_LENGTH_BYTES]; + ::memcpy(sync, buffer, dmr::DMR_SYNC_LENGTH_BYTES); + + // count data sync errors + uint8_t dataErrs = 0U; + for (uint8_t i = 0U; i < dmr::DMR_SYNC_LENGTH_BYTES; i++) + dataErrs += Utils::countBits8(sync[i] ^ dmr::DMR_MS_DATA_SYNC_BYTES[i]); + + // count voice sync errors + uint8_t voiceErrs = 0U; + for (uint8_t i = 0U; i < dmr::DMR_SYNC_LENGTH_BYTES; i++) + voiceErrs += Utils::countBits8(sync[i] ^ dmr::DMR_MS_VOICE_SYNC_BYTES[i]); + + if ((dataErrs <= 4U) || (voiceErrs <= 4U)) { + if (slot == 0U) { + host->m_modem->injectDMRData1(buffer, fileSize); + } + else if (slot == 1U) { + host->m_modem->injectDMRData2(buffer, fileSize); + } + else { + reply = CMD_FAILED_STR "invalid DMR slot!"; + LogError(LOG_RCON, reply.c_str()); + } + } + else { + reply = CMD_FAILED_STR "DMR data has too many errors!"; + LogError(LOG_RCON, reply.c_str()); + } + } + else { + reply = CMD_FAILED_STR "DMR failed to open DMR data!"; + LogError(LOG_RCON, reply.c_str()); + } + + delete[] buffer; + } + + ::fclose(file); + } + } + } + else { + reply = CMD_FAILED_STR "DMR mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + + return reply; } /// -/// +/// /// /// -/// +/// Instance of the Host class. +/// Instance of the P25 Control class. /// -int16_t RemoteControl::getArgInt16(std::vector args, uint32_t n) const +std::string RemoteControl::rcdP25ModemInj(std::vector args, Host* host, p25::Control* p25) { - return (int16_t)::atoi(getArgString(args, n).c_str()); + std::string reply = ""; + + if (p25 != nullptr) { + std::string argString = getArgString(args, 0U); + const char* fileName = argString.c_str(); + if (fileName != nullptr) { + FILE* file = ::fopen(fileName, "r"); + if (file != nullptr) { + uint8_t* buffer = nullptr; + int32_t fileSize = 0; + + // obtain file size + ::fseek(file, 0, SEEK_END); + fileSize = ::ftell(file); + ::rewind(file); + + // allocate a buffer and read file + buffer = new uint8_t[fileSize]; + if (buffer != nullptr) { + int32_t bytes = ::fread(buffer, 1U, fileSize, file); + if (bytes == fileSize) { + uint8_t sync[p25::P25_SYNC_LENGTH_BYTES]; + ::memcpy(sync, buffer, p25::P25_SYNC_LENGTH_BYTES); + + uint8_t errs = 0U; + for (uint8_t i = 0U; i < p25::P25_SYNC_LENGTH_BYTES; i++) + errs += Utils::countBits8(sync[i] ^ p25::P25_SYNC_BYTES[i]); + + if (errs <= 4U) { + bool valid = p25->nid().decode(buffer); + if (valid) { + host->m_modem->injectP25Data(buffer, fileSize); + } + else { + reply = CMD_FAILED_STR "P25 data did not contain a valid NID!"; + LogError(LOG_RCON, reply.c_str()); + } + } + else { + reply = CMD_FAILED_STR "P25 data has too many errors!"; + LogError(LOG_RCON, reply.c_str()); + } + } + else { + reply = CMD_FAILED_STR "P25 failed to open P25 data!"; + LogError(LOG_RCON, reply.c_str()); + } + + delete[] buffer; + } + + ::fclose(file); + } + } + } + else { + reply = CMD_FAILED_STR "P25 mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + + return reply; } /// -/// +/// /// /// -/// +/// Instance of the Host class. +/// Instance of the NXDN Control class. /// -uint8_t RemoteControl::getArgUInt8(std::vector args, uint32_t n) const +std::string RemoteControl::rcdNXDNModemInj(std::vector args, Host* host, nxdn::Control* nxdn) { - return (uint8_t)::atoi(getArgString(args, n).c_str()); + std::string reply = ""; + + if (nxdn != nullptr) { + std::string argString = getArgString(args, 0U); + const char* fileName = argString.c_str(); + if (fileName != nullptr) { + FILE* file = ::fopen(fileName, "r"); + if (file != nullptr) { + uint8_t* buffer = nullptr; + int32_t fileSize = 0; + + // obtain file size + ::fseek(file, 0, SEEK_END); + fileSize = ::ftell(file); + ::rewind(file); + + // allocate a buffer and read file + buffer = new uint8_t[fileSize]; + if (buffer != nullptr) { + int32_t bytes = ::fread(buffer, 1U, fileSize, file); + if (bytes == fileSize) { + uint8_t sync[nxdn::NXDN_FSW_BYTES_LENGTH]; + ::memcpy(sync, buffer, nxdn::NXDN_FSW_BYTES_LENGTH); + + uint8_t errs = 0U; + for (uint8_t i = 0U; i < nxdn::NXDN_FSW_BYTES_LENGTH; i++) + errs += Utils::countBits8(sync[i] ^ nxdn::NXDN_FSW_BYTES[i]); + + if (errs <= 4U) { + host->m_modem->injectNXDNData(buffer, fileSize); + } + else { + reply = CMD_FAILED_STR "NXDN data has too many errors!"; + LogError(LOG_RCON, reply.c_str()); + } + } + else { + reply = CMD_FAILED_STR "NXDN failed to open NXDN data!"; + LogError(LOG_RCON, reply.c_str()); + } + + delete[] buffer; + } + + ::fclose(file); + } + } + } + else { + reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + + return reply; } /// @@ -1425,7 +1525,11 @@ uint8_t RemoteControl::getArgUInt8(std::vector args, uint32_t n) co /// /// /// -int8_t RemoteControl::getArgInt8(std::vector args, uint32_t n) const +std::string RemoteControl::getArgString(std::vector args, uint32_t n) const { - return (int8_t)::atoi(getArgString(args, n).c_str()); + n += 1; + if (n >= args.size()) + return ""; + + return args.at(n); } diff --git a/network/RemoteControl.h b/network/RemoteControl.h index fd8500c3..c196db4b 100644 --- a/network/RemoteControl.h +++ b/network/RemoteControl.h @@ -89,23 +89,37 @@ private: /// Helper to print the remote control help. std::string displayHelp(); + /// + std::string rcdGetStatus(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); + /// + std::string rcdMode(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); + /// + std::string rcdPermitTG(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); + + /// + std::string rcdDMRModemInj(std::vector args, Host* host, dmr::Control* dmr); + /// + std::string rcdP25ModemInj(std::vector args, Host* host, p25::Control* p25); + /// + std::string rcdNXDNModemInj(std::vector args, Host* host, nxdn::Control* nxdn); + /// std::string getArgString(std::vector args, uint32_t n) const; /// - uint64_t getArgUInt64(std::vector args, uint32_t n) const; + __forceinline uint64_t getArgUInt64(std::vector args, uint32_t n) const { return (uint64_t)::atol(getArgString(args, n).c_str()); } /// - uint32_t getArgUInt32(std::vector args, uint32_t n) const; + __forceinline uint32_t getArgUInt32(std::vector args, uint32_t n) const { return (uint32_t)::atoi(getArgString(args, n).c_str()); } /// - int32_t getArgInt32(std::vector args, uint32_t n) const; + __forceinline int32_t getArgInt32(std::vector args, uint32_t n) const { return ::atoi(getArgString(args, n).c_str()); } /// - uint16_t getArgUInt16(std::vector args, uint32_t n) const; + __forceinline uint16_t getArgUInt16(std::vector args, uint32_t n) const { return (uint16_t)::atoi(getArgString(args, n).c_str()); } /// - int16_t getArgInt16(std::vector args, uint32_t n) const; + __forceinline int16_t getArgInt16(std::vector args, uint32_t n) const { return (int16_t)::atoi(getArgString(args, n).c_str()); } /// - uint8_t getArgUInt8(std::vector args, uint32_t n) const; + __forceinline uint8_t getArgUInt8(std::vector args, uint32_t n) const { return (uint8_t)::atoi(getArgString(args, n).c_str()); } /// - int8_t getArgInt8(std::vector args, uint32_t n) const; + __forceinline int8_t getArgInt8(std::vector args, uint32_t n) const { return (int8_t)::atoi(getArgString(args, n).c_str()); } }; #endif // __REMOTE_CONTROL_H__