diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | include/mts/MTS_IO_CellularRadio.h | 6 | ||||
-rw-r--r-- | include/mts/MTS_IO_CellularRadioFactory.h | 3 | ||||
-rw-r--r-- | include/mts/MTS_IO_ICellularRadio.h | 4 | ||||
-rw-r--r-- | include/mts/MTS_IO_LE910SV1Radio.h (renamed from include/mts/MTS_IO_ME910C1NARadio.h) | 27 | ||||
-rw-r--r-- | include/mts/MTS_IO_ME910C1NVRadio.h | 77 | ||||
-rw-r--r-- | include/mts/MTS_IO_ME910C1WWRadio.h | 23 | ||||
-rw-r--r-- | src/MTS_IO_CellularRadio.cpp | 122 | ||||
-rw-r--r-- | src/MTS_IO_CellularRadioFactory.cpp | 18 | ||||
-rw-r--r-- | src/MTS_IO_ICellularRadio.cpp | 26 | ||||
-rw-r--r-- | src/MTS_IO_LE910SV1Radio.cpp | 36 | ||||
-rw-r--r-- | src/MTS_IO_ME910C1NARadio.cpp | 127 | ||||
-rw-r--r-- | src/MTS_IO_ME910C1NVRadio.cpp | 623 | ||||
-rw-r--r-- | src/MTS_IO_ME910C1WWRadio.cpp | 593 |
14 files changed, 789 insertions, 899 deletions
@@ -27,13 +27,12 @@ OBJS += \ src/MTS_IO_LE910C4NFRadio.o \ src/MTS_IO_LE910NA1Radio.o \ src/MTS_IO_LE910SVGRadio.o \ + src/MTS_IO_LE910SV1Radio.o \ src/MTS_IO_LE910EUGRadio.o \ src/MTS_IO_LE910C4EURadio.o \ src/MTS_IO_LE910EU1Radio.o \ src/MTS_IO_LE910C1NSRadio.o \ src/MTS_IO_LE910C1APRadio.o \ - src/MTS_IO_ME910C1NARadio.o \ - src/MTS_IO_ME910C1NVRadio.o \ src/MTS_IO_ME910C1WWRadio.o \ src/MTS_IO_ME910Radio.o \ src/MTS_IO_LockFile.o \ diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index 1e80274..56506af 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -78,6 +78,8 @@ namespace MTS { CODE getRegistration(REGISTRATION& eRegistration) override; CODE convertRegistrationToString(REGISTRATION eRegistration, std::string& sRegistration) override; + CODE convertCellModesToString(CELLULAR_MODES eCellModes, std::string& sCellModes) override; + CODE unlockSimCard(const Json::Value& jArgs) override; CODE getMipProfile(Json::Value& jMipProfile) override; @@ -168,6 +170,10 @@ namespace MTS { virtual std::string queryCGREGstring(); virtual void setCGREG(std::string value); + const std::vector<std::string> getRegistrationCommands(); + REGISTRATION parseRegResponse(std::string sResult); + CODE getRegistration(REGISTRATION& eRegistration, const std::string& sType); + class RadioBandMap : public MTS::NonCopyable { public: RadioBandMap() diff --git a/include/mts/MTS_IO_CellularRadioFactory.h b/include/mts/MTS_IO_CellularRadioFactory.h index c9cb56f..e57ca5e 100644 --- a/include/mts/MTS_IO_CellularRadioFactory.h +++ b/include/mts/MTS_IO_CellularRadioFactory.h @@ -43,13 +43,12 @@ namespace MTS { ICellularRadio* createLE910C4NF(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createLE910NA1(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createLE910SVG(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; + ICellularRadio* createLE910SV1(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createLE910EUG(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createLE910C4EU(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createLE910EU1(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createLE910C1NS(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createLE910C1AP(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; - ICellularRadio* createME910C1NA(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; - ICellularRadio* createME910C1NV(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createME910C1WW(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createGE910(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; ICellularRadio* createDE910(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; diff --git a/include/mts/MTS_IO_ICellularRadio.h b/include/mts/MTS_IO_ICellularRadio.h index 57d6f53..58d5076 100644 --- a/include/mts/MTS_IO_ICellularRadio.h +++ b/include/mts/MTS_IO_ICellularRadio.h @@ -126,12 +126,14 @@ namespace MTS { static const char *KEY_CARRIER; //!< Cellular Service Provider (Home Network) static const char *KEY_ICCID; //!< Integrated Circuit Card Identifier static const char *KEY_MSL; //!< Master Subsidy Lock + static const char *KEY_SUPPORTED_CELL_MODES; //!< Comma-separated list of all supported cellular modes (2g,3g,4g) //Network Status Data static const char *KEY_ROAMING; //!< Indicates whether or not using Home Network static const char *KEY_DATETIME; //!< Date and Time from tower static const char *KEY_SERVICE; //!< Service Connection Type [GPRS, EGPRS, WCDMA, HSDPA, 1xRTT, EVDO] + static const char *KEY_CELL_MODE; //!< Specifies the cellular mode that is currently used by the modem [2g, 3g, 4g] static const char *KEY_NETWORK; //!< Cellular Service Provider static const char *KEY_NETWORK_REG; //!< Network Registration static const char *KEY_CID; //!< Cellular ID (Tower) in HEX @@ -266,6 +268,8 @@ namespace MTS { virtual CODE getRegistration(REGISTRATION& eRegistration) = 0; virtual CODE convertRegistrationToString(REGISTRATION eRegistration, std::string& sRegistration) = 0; + virtual CODE convertCellModesToString(CELLULAR_MODES eCellModes, std::string& sCellModes) = 0; + /** * @brief unlockSimCard - unlock the SIM card using PIN code provided * diff --git a/include/mts/MTS_IO_ME910C1NARadio.h b/include/mts/MTS_IO_LE910SV1Radio.h index e8120bd..80536c1 100644 --- a/include/mts/MTS_IO_ME910C1NARadio.h +++ b/include/mts/MTS_IO_LE910SV1Radio.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 by Multi-Tech Systems + * Copyright (C) 2019 by Multi-Tech Systems * * This file is part of libmts-io. * @@ -18,33 +18,24 @@ * */ -/*! - \file MTS_IO_ME910C1NARadio.h - \brief A brief description - \date Jan 19, 2015 - \author sgodinez - A more elaborate description -*/ -#ifndef MTS_IO_ME910C1NARADIO_H_ -#define MTS_IO_ME910C1NARADIO_H_ +#ifndef MTS_IO_LE910SV1RADIO_H_ +#define MTS_IO_LE910SV1RADIO_H_ -#include <mts/MTS_IO_ME910Radio.h> +#include "mts/MTS_IO_LE910Radio.h" namespace MTS { namespace IO { - class ME910C1NARadio : public ME910Radio { + class LE910SV1Radio : public LE910Radio { public: static const std::string MODEL_NAME; - ME910C1NARadio(const std::string& sPort); - virtual ~ME910C1NARadio(){}; + LE910SV1Radio(const std::string& sPort); + virtual ~LE910SV1Radio(){}; - virtual CODE setActiveFirmware(const Json::Value& jArgs); - - virtual CODE getActiveFirmware(std::string& sFwId); + virtual CODE getCarrier(std::string& sCarrier); protected: @@ -54,4 +45,4 @@ namespace MTS { } } -#endif /* MTS_IO_ME910C1NARADIO_H_ */ +#endif /* MTS_IO_LE910SV1RADIO_H_ */ diff --git a/include/mts/MTS_IO_ME910C1NVRadio.h b/include/mts/MTS_IO_ME910C1NVRadio.h deleted file mode 100644 index 3d0b3b0..0000000 --- a/include/mts/MTS_IO_ME910C1NVRadio.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2018 by Multi-Tech Systems - * - * This file is part of libmts-io. - * - * libmts-io is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * libmts-io is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/*! - \file MTS_IO_ME910C1NVRadio.h - \brief A brief description - \date May 1, 2018 - \author mykyta.dorokhin - - A more elaborate description -*/ -#ifndef MTS_IO_ME910C1NVRadio_H_ -#define MTS_IO_ME910C1NVRadio_H_ - -#include <mts/MTS_IO_ME910Radio.h> - -namespace MTS { - namespace IO { - - class ME910C1NVRadio : public ME910Radio { - - public: - static const std::string MODEL_NAME; - - ME910C1NVRadio(const std::string& sPort); - virtual ~ME910C1NVRadio(){}; - - virtual CODE updateFumo(const Json::Value& jArgs, UpdateCb& stepCb); - virtual CODE getCarrier(std::string& sCarrier); - - protected: - - CODE doGetFirmwareNumbers(std::string &sFirmware, std::string &sFirmwareBuild); - - private: - static const std::string KEY_FUMO_PDPID; //!< PDP context id (default 3) - static const std::string KEY_FUMO_PDPTYPE; //!< PDP context type (default IPV4V6) - static const std::string KEY_FUMO_APN; //!< APN (default empty) - static const std::string KEY_FUMO_ADDRESS; //!< FTP server address - static const std::string KEY_FUMO_DIR; //!< Directory - static const std::string KEY_FUMO_FILE; //!< Name of the upgrade file - static const std::string KEY_FUMO_USER; //!< Username - static const std::string KEY_FUMO_PASSWORD; //!< Password - static const std::string KEY_FUMO_DRYRUN; //!< If set, do not apply the downloaded firmware - - CODE doFumoPerform(const Json::Value &jConfig, UpdateCb& stepCb); - CODE doFumoReadConfig(const Json::Value& jArgs, Json::Value &jConfig); - CODE doFumoSetup(const Json::Value &jConfig, UpdateCb& stepCb); - CODE doFumoFtp(const Json::Value &jConfig, UpdateCb& stepCb); - CODE doFumoCleanup(const Json::Value &jConfig, UpdateCb& stepCb); - CODE doFumoApplyFirmware(const Json::Value &jConfig, UpdateCb& stepCb); - CODE doFumoWaitNewFirmware(const Json::Value &jConfig, UpdateCb& stepCb); - - std::string m_sFw; - std::string m_sFwBuild; - }; - } -} - -#endif /* MTS_IO_ME910C1NVRadio_H_ */ diff --git a/include/mts/MTS_IO_ME910C1WWRadio.h b/include/mts/MTS_IO_ME910C1WWRadio.h index d45d86a..9605862 100644 --- a/include/mts/MTS_IO_ME910C1WWRadio.h +++ b/include/mts/MTS_IO_ME910C1WWRadio.h @@ -36,10 +36,33 @@ namespace MTS { ICellularRadio::CODE setActiveFirmware(const Json::Value& jArgs); ICellularRadio::CODE getActiveFirmware(std::string& sFwId); + virtual CODE updateFumo(const Json::Value& jArgs, UpdateCb& stepCb); + protected: + CODE doGetFirmwareNumbers(std::string &sFirmware, std::string &sFirmwareBuild); + private: + static const std::string KEY_FUMO_PDPID; //!< PDP context id (default 3) + static const std::string KEY_FUMO_PDPTYPE; //!< PDP context type (default IPV4V6) + static const std::string KEY_FUMO_APN; //!< APN (default empty) + static const std::string KEY_FUMO_ADDRESS; //!< FTP server address + static const std::string KEY_FUMO_DIR; //!< Directory + static const std::string KEY_FUMO_FILE; //!< Name of the upgrade file + static const std::string KEY_FUMO_USER; //!< Username + static const std::string KEY_FUMO_PASSWORD; //!< Password + static const std::string KEY_FUMO_DRYRUN; //!< If set, do not apply the downloaded firmware + + CODE doFumoPerform(const Json::Value &jConfig, UpdateCb& stepCb); + CODE doFumoReadConfig(const Json::Value& jArgs, Json::Value &jConfig); + CODE doFumoSetup(const Json::Value &jConfig, UpdateCb& stepCb); + CODE doFumoFtp(const Json::Value &jConfig, UpdateCb& stepCb); + CODE doFumoCleanup(const Json::Value &jConfig, UpdateCb& stepCb); + CODE doFumoApplyFirmware(const Json::Value &jConfig, UpdateCb& stepCb); + CODE doFumoWaitNewFirmware(const Json::Value &jConfig, UpdateCb& stepCb); + std::string m_sFw; + std::string m_sFwBuild; }; } } diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index 6f45da4..50fdf5c 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -306,7 +306,7 @@ ICellularRadio::CODE CellularRadio::getSimStatusSummary(Json::Value& jData) { } if (!bIsSimInserted) { - // There is no left much to do. Return one field only. + // There is not much left to do. Return one field only. jData[KEY_IS_SIM_INSERTED] = bIsSimInserted; break; } @@ -314,6 +314,16 @@ ICellularRadio::CODE CellularRadio::getSimStatusSummary(Json::Value& jData) { // The following code assumes that the SIM card is inserted retCode = getSimLockStatus(sSimLockStatus); if (retCode != SUCCESS) { + /* IN:4033: + * + * On some devices #SIMDET reports "inserted" but +CPIN? returns ERROR when there is + * no SIM card in the slot. It's also the case when only plastic holder is inserted + * instead of the SIM itself. + * + * Interpret this error as "SIM card not detected" for such cases. + */ + jData[KEY_IS_SIM_INSERTED] = false; + retCode = SUCCESS; break; } @@ -694,6 +704,14 @@ void CellularRadio::getCommonNetworkStats(Json::Value& jData) { jData[ICellularRadio::KEY_NETWORK_REG] = sNetworkReg; } } + + std::string sCurrentCellMode; + CELLULAR_MODES eModes; + if (getCellularMode(eModes) == SUCCESS) { + if (convertCellModesToString(eModes, sCurrentCellMode) == SUCCESS) { + jData[ICellularRadio::KEY_CELL_MODE] = sCurrentCellMode; + } + } } ICellularRadio::CODE CellularRadio::getSimLockStatus(std::string& sData) @@ -736,41 +754,79 @@ void CellularRadio::initMipProfile(Json::Value& jData) { jData[ICellularRadio::KEY_MIP_MNHASS] = false; } -ICellularRadio::CODE CellularRadio::getRegistration(REGISTRATION& eRegistration) { - std::string sCmd; - std::string sResp; +const std::vector<std::string> CellularRadio::getRegistrationCommands() { + std::string sType; + convertModelToType(getName(), sType); - // LE910C1-NS is an LE910, so we stop the scan after the 0. - // NOTE: Eventually this may need to be changed to try all three of CREG, CGREG, and CEREG - if ((m_sName.find("LE910") != std::string::npos) || (m_sName.find("ME910") != std::string::npos)) { - // use AT+CGREG instead for LE910 models - sCmd = "AT+CGREG?"; - sResp = "+CGREG: "; - } - else { - sCmd = "AT+CREG?"; - sResp = "+CREG: "; + if (sType == VALUE_TYPE_LTE) { + return { "CREG", "CGREG", "CEREG" }; + } else { + return { "CREG", "CGREG" }; } +} +ICellularRadio::REGISTRATION CellularRadio::parseRegResponse(std::string sResult) { + size_t start = sResult.find(','); + size_t stop = sResult.find(' ', start); + std::string sRegStat = sResult.substr(start + 1, stop - start - 1); + int32_t value; + sscanf(sRegStat.c_str(), "%d", &value); + + return (ICellularRadio::REGISTRATION)value; +} + +ICellularRadio::CODE CellularRadio::getRegistration(REGISTRATION& eRegistration, const std::string& sType) { + std::string sCmd = "AT+" + sType + "?"; + std::string sResp = "+" + sType + ": "; std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 5000); if (sResult.find(sResp) == std::string::npos) { if(sResult.size() == 0) { - printDebug("%s| Registration command returned no response: [%s]", m_sName.c_str()); + printDebug("%s| Registration command returned no response", m_sName.c_str()); return NO_RESPONSE; } printDebug("%s| Registration command returned unexpected response: [%s]", m_sName.c_str(), sResult.c_str()); return FAILURE; } - size_t start = sResult.find(','); - size_t stop = sResult.find(' ', start); - std::string sRegStat = sResult.substr(start + 1, stop - start - 1); - int32_t value; - sscanf(sRegStat.c_str(), "%d", &value); - eRegistration = (REGISTRATION)value; + eRegistration = parseRegResponse(sResult); return SUCCESS; } +ICellularRadio::CODE CellularRadio::getRegistration(REGISTRATION& eRegistration) { + /* REGISTRATION_PRIORITY: + * REGISTERED = 0 + * ROAMING = 1 + * DENIED = 2 + * SEARCHING = 3 + * NOT_REGISTERED = 4 + * UNKNOWN = 5 + */ + uint8_t uRegPriority[] = { 4, 0, 3, 2, 5, 1 }; + ICellularRadio::CODE ret = ERROR; + REGISTRATION eReg = UNKNOWN; + eRegistration = UNKNOWN; + + // We need to check CREG, CGREG, and CEREG for possible success. + // Depending on the radio, carrier, roaming, sim card, cellular mode and some other factors the registration + // will come back differently depending on the type of connection that is possible. + + const auto & commands = getRegistrationCommands(); + for (const auto & cmd : commands) { + ret = getRegistration(eReg, cmd); + if (ret != SUCCESS) { + break; + } + + if (eReg == REGISTERED || eReg == ROAMING) { + eRegistration = eReg; + break; + } + eRegistration = (uRegPriority[eRegistration] > uRegPriority[eReg]) ? eReg : eRegistration; + } + + return ret; +} + ICellularRadio::CODE CellularRadio::getCellularMode(CELLULAR_MODES &networks) { networks = CELLULAR_MODE_NA; std::string cmdResult = sendCommand("AT+COPS?"); @@ -813,6 +869,30 @@ ICellularRadio::CODE CellularRadio::convertRegistrationToString(REGISTRATION eRe return eCode; } +ICellularRadio::CODE CellularRadio::convertCellModesToString(ICellularRadio::CELLULAR_MODES eCellModes, std::string& sCellModes) { + std::string sResult; + + if (eCellModes & CELLULAR_MODE_2G) { + sResult += "2g,"; + } + if (eCellModes & CELLULAR_MODE_3G) { + sResult += "3g,"; + } + if (eCellModes & CELLULAR_MODE_4G) { + sResult += "4g,"; + } + if (eCellModes & CELLULAR_MODE_5G) { + sResult += "5g,"; + } + + if (!sResult.empty()) { + sResult.pop_back(); // remove trailing comma + } + + sCellModes = sResult; + return SUCCESS; +} + ICellularRadio::CODE CellularRadio::unlockSimCard(const Json::Value& jArgs) { printTrace("%s| Unlock the SIM card using PIN code", m_sName.c_str()); diff --git a/src/MTS_IO_CellularRadioFactory.cpp b/src/MTS_IO_CellularRadioFactory.cpp index 4ee8756..08c6315 100644 --- a/src/MTS_IO_CellularRadioFactory.cpp +++ b/src/MTS_IO_CellularRadioFactory.cpp @@ -25,13 +25,12 @@ #include <mts/MTS_IO_LE910C4NFRadio.h> #include <mts/MTS_IO_LE910NA1Radio.h> #include <mts/MTS_IO_LE910SVGRadio.h> +#include <mts/MTS_IO_LE910SV1Radio.h> #include <mts/MTS_IO_LE910EUGRadio.h> #include <mts/MTS_IO_LE910C4EURadio.h> #include <mts/MTS_IO_LE910EU1Radio.h> #include <mts/MTS_IO_LE910C1NSRadio.h> #include <mts/MTS_IO_LE910C1APRadio.h> -#include <mts/MTS_IO_ME910C1NARadio.h> -#include <mts/MTS_IO_ME910C1NVRadio.h> #include <mts/MTS_IO_LE866A1JSRadio.h> #include <mts/MTS_IO_ME910C1WWRadio.h> #include <mts/MTS_IO_GE910Radio.h> @@ -49,13 +48,12 @@ CellularRadioFactory::CellularRadioFactory() { m_mCreationMap[LE910C4NFRadio::MODEL_NAME] = &CellularRadioFactory::createLE910C4NF; m_mCreationMap[LE910NA1Radio::MODEL_NAME] = &CellularRadioFactory::createLE910NA1; m_mCreationMap[LE910SVGRadio::MODEL_NAME] = &CellularRadioFactory::createLE910SVG; + m_mCreationMap[LE910SV1Radio::MODEL_NAME] = &CellularRadioFactory::createLE910SV1; m_mCreationMap[LE910EUGRadio::MODEL_NAME] = &CellularRadioFactory::createLE910EUG; m_mCreationMap[LE910C4EURadio::MODEL_NAME] = &CellularRadioFactory::createLE910C4EU; m_mCreationMap[LE910EU1Radio::MODEL_NAME] = &CellularRadioFactory::createLE910EU1; m_mCreationMap[LE910C1NSRadio::MODEL_NAME] = &CellularRadioFactory::createLE910C1NS; m_mCreationMap[LE910C1APRadio::MODEL_NAME] = &CellularRadioFactory::createLE910C1AP; - m_mCreationMap[ME910C1NARadio::MODEL_NAME] = &CellularRadioFactory::createME910C1NA; - m_mCreationMap[ME910C1NVRadio::MODEL_NAME] = &CellularRadioFactory::createME910C1NV; m_mCreationMap[ME910C1WWRadio::MODEL_NAME] = &CellularRadioFactory::createME910C1WW; m_mCreationMap[GE910Radio::MODEL_NAME] = &CellularRadioFactory::createGE910; m_mCreationMap[DE910Radio::MODEL_NAME] = &CellularRadioFactory::createDE910; @@ -149,6 +147,10 @@ ICellularRadio* CellularRadioFactory::createLE910SVG(const std::string& sPort) c return new LE910SVGRadio(sPort); } +ICellularRadio *CellularRadioFactory::createLE910SV1(const std::string& sPort) const { + return new LE910SV1Radio(sPort); +} + ICellularRadio* CellularRadioFactory::createLE910EUG(const std::string& sPort) const { return new LE910EUGRadio(sPort); } @@ -169,14 +171,6 @@ ICellularRadio* CellularRadioFactory::createLE910C1AP(const std::string& sPort) return new LE910C1APRadio(sPort); } -ICellularRadio* CellularRadioFactory::createME910C1NA(const std::string& sPort) const { - return new ME910C1NARadio(sPort); -} - -ICellularRadio* CellularRadioFactory::createME910C1NV(const std::string& sPort) const { - return new ME910C1NVRadio(sPort); -} - ICellularRadio* CellularRadioFactory::createME910C1WW(const std::string& sPort) const { return new ME910C1WWRadio(sPort); } diff --git a/src/MTS_IO_ICellularRadio.cpp b/src/MTS_IO_ICellularRadio.cpp index 4e7809e..53c8faa 100644 --- a/src/MTS_IO_ICellularRadio.cpp +++ b/src/MTS_IO_ICellularRadio.cpp @@ -49,11 +49,13 @@ const char *MTS::IO::ICellularRadio::KEY_MSID = "msid"; //!< Mobil Stat const char *MTS::IO::ICellularRadio::KEY_MDN = "mdn"; //!< Mobile Directory Number : Actual phone number dialed to reach radio const char *MTS::IO::ICellularRadio::KEY_ICCID = "iccid"; //!< Integrated Circuit Card Identifier const char *MTS::IO::ICellularRadio::KEY_MSL = "msl"; //!< Master Subsidy Lock +const char *MTS::IO::ICellularRadio::KEY_SUPPORTED_CELL_MODES = "supportedCellularModes"; //!< Comma-separated list of all supported cellular modes (2g,3g,4g) //Dynamic Data const char *MTS::IO::ICellularRadio::KEY_ROAMING = "roaming"; //!< Indicates whether or not using Home Network const char *MTS::IO::ICellularRadio::KEY_DATETIME = "datetime"; //!< Date and Time from tower const char *MTS::IO::ICellularRadio::KEY_SERVICE = "service"; //!< Service Connection Type [GPRS, EGPRS, WCDMA, HSDPA, 1xRTT, EVDO] +const char *MTS::IO::ICellularRadio::KEY_CELL_MODE = "cellularMode"; //!< Specifies the cellular mode that is currently used by the modem [2g, 3g, 4g] const char *MTS::IO::ICellularRadio::KEY_NETWORK = "network"; //!< Cellular Service Provider const char *MTS::IO::ICellularRadio::KEY_NETWORK_REG = "netreg"; //!< Network Registration const char *MTS::IO::ICellularRadio::KEY_CID = "cid"; //!< Cellular ID = Tower in HEX @@ -154,18 +156,15 @@ MTS::IO::ICellularRadio::CODE MTS::IO::ICellularRadio::convertModelToMtsShortCod } else if (sModel.find("LE910-SVG") == 0) { sCode = "LVW2"; eCode = SUCCESS; + } else if (sModel.find("LE910-SV1") == 0) { + sCode = "LVW3"; + eCode = SUCCESS; } else if (sModel.find("LE910C1-NS") == 0) { sCode = "LSP3"; eCode = SUCCESS; } else if (sModel.find("LE910C1-AP") == 0) { sCode = "LAP3"; eCode = SUCCESS; - } else if (sModel.find("ME910C1-NA") == 0) { - sCode = "MAT1"; - eCode = SUCCESS; - } else if (sModel.find("ME910C1-NV") == 0) { - sCode = "MVW1"; - eCode = SUCCESS; } else if (sModel.find("ME910C1-WW") == 0) { sCode = "MNG2"; eCode = SUCCESS; @@ -268,6 +267,9 @@ MTS::IO::ICellularRadio::CODE MTS::IO::ICellularRadio::convertModelToType(const } else if (sModel.find("LE910-SVG") == 0) { sType = VALUE_TYPE_LTE; eCode = SUCCESS; + } else if (sModel.find("LE910-SV1") == 0) { + sType = VALUE_TYPE_LTE; + eCode = SUCCESS; } else if (sModel.find("LE910-EUG") == 0) { sType = VALUE_TYPE_LTE; eCode = SUCCESS; @@ -283,12 +285,6 @@ MTS::IO::ICellularRadio::CODE MTS::IO::ICellularRadio::convertModelToType(const } else if (sModel.find("LE910C1-AP") == 0) { sType = VALUE_TYPE_LTE; eCode = SUCCESS; - } else if (sModel.find("ME910C1-NA") == 0) { - sType = VALUE_TYPE_LTE; - eCode = SUCCESS; - } else if (sModel.find("ME910C1-NV") == 0) { - sType = VALUE_TYPE_LTE; - eCode = SUCCESS; } else if (sModel.find("ME910C1-WW") == 0) { sType = VALUE_TYPE_LTE; eCode = SUCCESS; @@ -425,14 +421,12 @@ std::string MTS::IO::ICellularRadio::extractModelFromResult(const std::string& s sModel = "LE910C4-NF"; } else if(sResult.find("LE910-NA1") != std::string::npos) { sModel = "LE910-NA1"; - } else if(sResult.find("ME910C1-NA") != std::string::npos) { - sModel = "ME910C1-NA"; - } else if(sResult.find("ME910C1-NV") != std::string::npos) { - sModel = "ME910C1-NV"; } else if(sResult.find("ME910C1-WW") != std::string::npos) { sModel = "ME910C1-WW"; } else if(sResult.find("LE910-SVG") != std::string::npos) { sModel = "LE910-SVG"; + } else if(sResult.find("LE910-SV1") != std::string::npos) { + sModel = "LE910-SV1"; } else if(sResult.find("LE910-EUG") != std::string::npos) { sModel = "LE910-EUG"; } else if(sResult.find("LE910C4-EU") != std::string::npos) { diff --git a/src/MTS_IO_LE910SV1Radio.cpp b/src/MTS_IO_LE910SV1Radio.cpp new file mode 100644 index 0000000..8f0d116 --- /dev/null +++ b/src/MTS_IO_LE910SV1Radio.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 by Multi-Tech Systems + * + * This file is part of libmts-io. + * + * libmts-io is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libmts-io is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <mts/MTS_IO_LE910SV1Radio.h> + +using namespace MTS::IO; + +const std::string LE910SV1Radio::MODEL_NAME("LE910-SV1"); + +LE910SV1Radio::LE910SV1Radio(const std::string& sPort) +: LE910Radio(MODEL_NAME, sPort) +{ + +} + +ICellularRadio::CODE LE910SV1Radio::getCarrier(std::string& sCarrier) { + sCarrier = "Verizon"; + return SUCCESS; +} diff --git a/src/MTS_IO_ME910C1NARadio.cpp b/src/MTS_IO_ME910C1NARadio.cpp deleted file mode 100644 index 8c9992e..0000000 --- a/src/MTS_IO_ME910C1NARadio.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2017 by Multi-Tech Systems - * - * This file is part of libmts-io. - * - * libmts-io is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * libmts-io is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/*! - \file MTS_IO_ME910C1NARadio.cpp - \brief A brief description - - A more elaborate description -*/ -#include <mts/MTS_Text.h> -#include <mts/MTS_Logger.h> -#include <mts/MTS_IO_ME910C1NARadio.h> - -using namespace MTS::IO; - -const std::string ME910C1NARadio::MODEL_NAME("ME910C1-NA"); - -ME910C1NARadio::ME910C1NARadio(const std::string& sPort) -: ME910Radio(MODEL_NAME, sPort) -{ - -} - -CellularRadio::CODE ME910C1NARadio::setActiveFirmware(const Json::Value& jArgs) { - CellularRadio::CODE rc; - - // Set command allows enabling a specific firmware image on products - // embedding 2 different firmware images: - // - // "AT#FWSWITCH=<image_number>[,<storage_conf>]" - // <image_number> - Firmware Image To Be Enabled - // 0 – Image 1 (Default) - // 1 – Image 2 - // <storage_conf> - Setting Storage Configuration - // 0 – Save the <image_number> value in RAM - // 1 – Save the <image_number> value in NVM - - printTrace("%s| Set Active Firmware Image Number", getName().c_str()); - - if(!jArgs["fwid"].isString()) { - return INVALID_ARGS; - } - - if (jArgs["fwid"].asString() != "1" && jArgs["fwid"].asString() != "0") { - return INVALID_ARGS; - } - - // LE910-NA1 and LE910-NA V2 is orderable in single image or dual image (single SKU) - // configuration. So issue the test command first - rc = sendBasicCommand("AT#FWSWITCH=?"); - if (rc == ERROR) { - printTrace("%s| FWSWITCH is not supported", getName().c_str()); - return NOT_APPLICABLE; - } - else if (rc != SUCCESS) { - return rc; - } - - std::string sCmd = "AT#FWSWITCH="; - sCmd += jArgs["fwid"].asString(); - sCmd += ",1"; - - printTrace("%s| Issuing %s command", getName().c_str(), sCmd.c_str()); - - return sendBasicCommand(sCmd, 5000); -} - -CellularRadio::CODE ME910C1NARadio::getActiveFirmware(std::string& sFwId) { - std::string sCmd; - CellularRadio::CODE rc; - // - // Read command reports the current active firmware image: - // AT#FWSWITCH? - // #FWSWITCH: 1 - // - // OK - // - printTrace("%s| Get Active Firmware Image Number", getName().c_str()); - - // LE910-NA1 and LE910-NA V2 is orderable in single image or dual image (single SKU) - // configuration. So issue the test command first - sCmd = "AT#FWSWITCH=?"; - rc = sendBasicCommand(sCmd); - if (rc == ERROR) { - printTrace("%s| FWSWITCH is not supported", getName().c_str()); - return NOT_APPLICABLE; - } - else if (rc != SUCCESS) { - return rc; - } - - sCmd = "AT#FWSWITCH?"; - std::string sResult = sendCommand(sCmd); - size_t end = sResult.find(RSP_OK); - if (end == std::string::npos) { - printWarning("%s| Unable to get active image number from radio using command [%s]", - getName().c_str(), - sCmd.c_str()); - return FAILURE; - } - - size_t start = sResult.find("#FWSWITCH:") + sizeof("#FWSWITCH:"); - sFwId = MTS::Text::trim(sResult.substr(start, end-start)); - if(sFwId.size() == 0) { - printWarning("%s| Firmware Image Number is empty", getName().c_str()); - return FAILURE; - } - - return SUCCESS; -} diff --git a/src/MTS_IO_ME910C1NVRadio.cpp b/src/MTS_IO_ME910C1NVRadio.cpp deleted file mode 100644 index 0af5f24..0000000 --- a/src/MTS_IO_ME910C1NVRadio.cpp +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright (C) 2018 by Multi-Tech Systems - * - * This file is part of libmts-io. - * - * libmts-io is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * libmts-io is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/*! - \file MTS_IO_ME910C1NVRadio.cpp - \brief A brief description - \date May 1, 2018 - \author mykyta.dorokhin - - A more elaborate description -*/ -#include <fstream> -#include <mts/MTS_Text.h> -#include <mts/MTS_Logger.h> -#include <mts/MTS_Thread.h> -#include <mts/MTS_Timer.h> -#include <mts/MTS_IO_ME910C1NVRadio.h> - -using namespace MTS::IO; - -const std::string ME910C1NVRadio::MODEL_NAME("ME910C1-NV"); - -const std::string ME910C1NVRadio::KEY_FUMO_PDPID("pdpid"); // optional (default : "3") -const std::string ME910C1NVRadio::KEY_FUMO_PDPTYPE("pdptype"); // optional (default : "IPV4V6") -const std::string ME910C1NVRadio::KEY_FUMO_APN("apn"); // optional (default : "") -const std::string ME910C1NVRadio::KEY_FUMO_ADDRESS("address"); -const std::string ME910C1NVRadio::KEY_FUMO_DIR("dir"); -const std::string ME910C1NVRadio::KEY_FUMO_FILE("file"); -const std::string ME910C1NVRadio::KEY_FUMO_USER("user"); -const std::string ME910C1NVRadio::KEY_FUMO_PASSWORD("password"); -const std::string ME910C1NVRadio::KEY_FUMO_DRYRUN("dryrun"); - -ME910C1NVRadio::ME910C1NVRadio(const std::string& sPort) -: ME910Radio(MODEL_NAME, sPort) -{ - -} - -ICellularRadio::CODE ME910C1NVRadio::getCarrier(std::string& sCarrier) { - sCarrier = "Verizon"; - return SUCCESS; -} - -ICellularRadio::CODE ME910C1NVRadio::doGetFirmwareNumbers(std::string &sFirmware, std::string &sFirmwareBuild) { - ICellularRadio::CODE rc = FAILURE; - - rc = getFirmware(sFirmware); - if (rc != SUCCESS){ - return rc; - } - - rc = getFirmwareBuild(sFirmwareBuild); - if (rc != SUCCESS){ - return rc; - } - - return rc; -} - -ICellularRadio::CODE ME910C1NVRadio::doFumoReadConfig(const Json::Value& jArgs, Json::Value &jConfig) -{ - ICellularRadio::CODE rc = INVALID_ARGS; - std::string sPath; - - do - { - if (!jArgs["config-file"].isString()) { - rc = INVALID_ARGS; - break; - } - - sPath = jArgs["config-file"].asString(); - - std::ifstream file(sPath.c_str()); - if (!file.is_open()) { - printError("Failed to open file [%s]", sPath.c_str()); - break; - } - - file.seekg(0, std::ios::end); - size_t size = file.tellg(); - std::string buffer(size, ' '); - file.seekg(0); - file.read(&buffer[0], size); - file.close(); - - Json::Features features = Json::Features::strictMode(); - Json::Reader reader(features); - if (!reader.parse(buffer, jConfig)) { - printError("Error parsing FOTA configuration file"); - break; - } - - // - // set default values if missing - // - if (!jConfig.isMember(KEY_FUMO_PDPID)) { - jConfig[KEY_FUMO_PDPID] = std::string("3"); - } - - if (!jConfig.isMember(KEY_FUMO_PDPTYPE)) { - jConfig[KEY_FUMO_PDPTYPE] = std::string("IPV4V6"); - } - - if (!jConfig.isMember(KEY_FUMO_APN)) { - jConfig[KEY_FUMO_APN] = std::string(""); - } - - // - // validate - // - if (!jConfig[KEY_FUMO_PDPID].isString()) { - printError("Error loading FOTA configuration: PDP context id is not set"); - break; - } - - if (jConfig[KEY_FUMO_PDPID].asString().empty()) { - printError("Error loading FOTA configuration: context id is empty"); - break; - } - - if (!jConfig[KEY_FUMO_PDPTYPE].isString()) { - printError("Error loading FOTA configuration: PDP type is not set"); - break; - } - - if (jConfig[KEY_FUMO_PDPTYPE].asString().empty()) { - printError("Error loading FOTA configuration: PDP type is empty"); - break; - } - - // Note : allow empty APN - if (!jConfig[KEY_FUMO_APN].isString()) { - printError("Error loading FOTA configuration: APN is not set"); - break; - } - - if (!jConfig[KEY_FUMO_ADDRESS].isString()) { - printError("Error loading FOTA configuration: address is not set"); - break; - } - - if (jConfig[KEY_FUMO_ADDRESS].asString().empty()) { - printError("Error loading FOTA configuration: address is empty"); - break; - } - - // Note: allow empty dir - if (!jConfig[KEY_FUMO_DIR].isString()) { - printError("Error loading FOTA configuration: directory is not set"); - break; - } - - if (!jConfig[KEY_FUMO_FILE].isString()) { - printError("Error loading FOTA configuration: filename is not set"); - break; - } - - if (jConfig[KEY_FUMO_FILE].asString().empty()) { - printError("Error loading FOTA configuration: filename is empty"); - break; - } - - // Note: allow empty username/password - if (!jConfig[KEY_FUMO_USER].isString()) { - printError("Error loading FOTA configuration: username is not set"); - break; - } - - if (!jConfig[KEY_FUMO_PASSWORD].isString()) { - printError("Error loading FOTA configuration: password is not set"); - break; - } - - rc = SUCCESS; - } - while(0); - - return rc; -} - -ICellularRadio::CODE ME910C1NVRadio::doFumoSetup(const Json::Value &jConfig, UpdateCb& stepCb) -{ - ICellularRadio::CODE rc = FAILURE; - std::string sCmd; - - std::string sContextId = jConfig[KEY_FUMO_PDPID].asString(); - std::string sApn = jConfig[KEY_FUMO_APN].asString(); - std::string sPdpType = jConfig[KEY_FUMO_PDPTYPE].asString(); - - - do - { - // - // Execution command is used to activate or deactivate either the GSM - // context or the specified PDP context. - // - // AT#SGACT=<cid>,<stat>[,<userId>,<pwd>] - // - sCmd = "AT#SGACT=" + sContextId + ",0"; - rc = sendBasicCommand(sCmd); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: Failed to deactivate PDP context")); - } - break; - } - - // - // Read current Firmware numbers (let it be after AT#SGACT not to confuse users) - // - rc = doGetFirmwareNumbers(m_sFw, m_sFwBuild); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: Failed to obtain current firmware version")); - } - break; - } - - // - // Set command specifies PDP context parameter values for a PDP context identified by - // the (local) context identification parameter <cid>. - // - // AT+CGDCONT= [<cid>[,<PDP_type>[,<APN>[,<PDP_addr>[,<d_comp>[,<h_comp>[,<pd1>[,...[,pdN]]]]]]]]] - // - sCmd = "AT+CGDCONT=" + sContextId + ",\"" + sPdpType + "\""; - if (!sApn.empty()) { - sCmd += ",\"" + sApn + "\""; - } - rc = sendBasicCommand(sCmd, 1000); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: Failed to setup PDP context")); - } - break; - } - - // - // Set command sets the socket configuration parameters. - // - // AT#SCFG==<connId>,<cid>,<pktSz>,<maxTo>,<connTo>,<txTo> - // <connId> - socket connection identifier - // <cid> - PDP context identifier - // <pktSz> - packet size to be used by the TCP/UDP/IP stack for data sending. - // <maxTo> - exchange timeout (or socket inactivity timeout); if there’s no - // data exchange within this timeout period the connection is closed (timeout value in seconds). - // <connTo> - connection timeout; if we can’t establish a connection to the - // remote within this timeout period, an error is raised timeout value in hundreds of milliseconds. - // <txTo> - data sending timeout; after this period data are sent also if they’re - // less than max packet size (timeout value in hundreds of milliseconds). - // - sCmd = "AT#SCFG=1," + sContextId + ",300,90,600,50"; - rc = sendBasicCommand(sCmd); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: Failed to set connection configuration parameters")); - } - break; - } - - // - // Activate PDP context - // - sCmd = "AT#SGACT=" + sContextId + ",1"; - rc = sendBasicCommand(sCmd, 60 * 1000); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: Failed to activate PDP context")); - } - break; - } - } - while (0); - - return rc; -} - -ICellularRadio::CODE ME910C1NVRadio::doFumoFtp(const Json::Value &jConfig, UpdateCb& stepCb) -{ - ICellularRadio::CODE rc = FAILURE; - std::string sCmd; - std::string sResult; - - // - // Set command sets the time-out used when opening either the FTP control - // channel or the FTP traffic channel. - // - // AT#FTPTO= [<tout>] - // <tout> - time-out in 100 ms units - // - rc = sendBasicCommand("AT#FTPTO=2400"); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: Failed to setup connection timeout")); - } - return rc; - } - - // - // Execution command opens an FTP connection toward the FTP server. - // - // AT#FTPOPEN=[<server:port>,<username>,<password>[,<mode>]] - // - sCmd = "AT#FTPOPEN="; - sCmd += "\"" + jConfig[KEY_FUMO_ADDRESS].asString() + "\","; - sCmd += "\"" + jConfig[KEY_FUMO_USER].asString() + "\","; - sCmd += "\"" + jConfig[KEY_FUMO_PASSWORD].asString() + "\",1"; - rc = sendBasicCommand(sCmd, 60 * 1000); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: Failed to open connection")); - } - return rc; - } - - if (stepCb) { - stepCb(Json::Value("FUMO Info: connection opened")); - } - - do - { - // - // Set command, issued during an FTP connection, sets the file transfer type. - // - // AT#FTPTYPE=[<type>] - // <type> - file transfer type: - // 0 - binary - // 1 - ascii - // - rc = sendBasicCommand("AT#FTPTYPE=0", 1000); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: failed to set file transfer type")); - } - break; - } - - // - // Execution command, issued during an FTP connection, changes the - // working directory on FTP server. - // - // AT#FTPCWD=[<dirname>] - // - sCmd = "AT#FTPCWD=\"/"; - if (!jConfig[KEY_FUMO_DIR].asString().empty()) { - sCmd += jConfig[KEY_FUMO_DIR].asString() + "/"; - } - sCmd += "\""; - rc = sendBasicCommand(sCmd, 60 * 1000); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: failed to change working directory on the server")); - } - break; - } - - if (stepCb) { - stepCb(Json::Value("FUMO Info: downloading the firmware")); - } - - // - // Start FTP transfer - // - sCmd = "AT#FTPGETOTA="; - sCmd += "\"" + jConfig[KEY_FUMO_FILE].asString() + "\",1,1"; - sendBasicCommand(sCmd); - - // - // Noticed that after successful AT#FTPGETOTA the radio resets the connection. - // and the response code (OK, ERROR.. ) not always reach the host. Therefore - // we send the AT#FTPGETOTA with relatively small timeout and then poll with AT - // until we get valid response. After that, using AT#FTPMSG we can check the - // result of the last FTP command (which is AT#FTPGETOTA in our case). - MTS::Timer oTimer; - - oTimer.start(); - - while (oTimer.getSeconds() < (30 * 60)) // 30 min - { - MTS::Thread::sleep(5000); - - rc = sendBasicCommand("AT"); - if (rc == SUCCESS) { - break; - } - - resetConnection(1); - } - oTimer.stop(); - - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: unable to obtain radio after reset")); - } - break; - } - - // - // Now check the FTP status - // - std::string sResult = sendCommand("AT#FTPMSG"); - - printTrace("RADIO| AT#FTPMSG result [%s]", sResult.c_str()); - - if (sResult.find(ICellularRadio::RSP_OK) == std::string::npos) { - rc = FAILURE; - if(stepCb) { - stepCb(Json::Value("FUMO Error: failed to download the firmware file")); - } - break; - } - if (sResult.find("#FTPMSG: 550") != std::string::npos) { - // FTP(550) : File not found - rc = FAILURE; - if(stepCb) { - stepCb(Json::Value("FUMO Error: file not found")); - } - break; - } - if (sResult.find("#FTPMSG: 226") == std::string::npos) { - // FTP(226) : Successfully transferred - rc = FAILURE; - if(stepCb) { - stepCb(Json::Value("FUMO Error: failed to download the firmware file")); - } - break; - } - } - while (0); - - // - // Execution command closes an FTP connection. - // - // AT#FTPCLOSE - // - ICellularRadio::CODE rcclose = sendBasicCommand("AT#FTPCLOSE", 60 * 1000); - if (rcclose != SUCCESS && rc == SUCCESS) { - if(stepCb) { - // Only one "FUMO Error" message should be sent - stepCb(Json::Value("FUMO Error: Failed to close FTP connection")); - } - rc = rcclose; - } - - if (rc == SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Info: firmware downloaded successfully")); - } - } - - return rc; -} - -ICellularRadio::CODE ME910C1NVRadio::doFumoCleanup(const Json::Value &jConfig, UpdateCb& stepCb) -{ - ICellularRadio::CODE rc = FAILURE; - std::string sCmd; - - std::string sContextId = jConfig[KEY_FUMO_PDPID].asString(); - - // - // Deactivate PDP context - // - sCmd = "AT#SGACT=" + sContextId + ",0"; - rc = sendBasicCommand(sCmd, 10000); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: Failed to deactivate PDP context")); - } - } - return rc; -} - -ICellularRadio::CODE ME910C1NVRadio::doFumoApplyFirmware(const Json::Value &jConfig, UpdateCb& stepCb) -{ - ICellularRadio::CODE rc = FAILURE; - - if (jConfig.isMember(KEY_FUMO_DRYRUN)) { - if(stepCb) { - stepCb(Json::Value("FUMO Info: applying the radio firmware")); - } - return SUCCESS; - } - - rc = sendBasicCommand("AT#OTAUP=0", 10000); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: failed to apply the firmware")); - } - return rc; - } - - if(stepCb) { - stepCb(Json::Value("FUMO Info: applying the radio firmware")); - } - - return rc; -} - -ICellularRadio::CODE ME910C1NVRadio::doFumoWaitNewFirmware(const Json::Value &jConfig, UpdateCb& stepCb) -{ - std::string sFirmware; - std::string sFirmwareBuild; - ICellularRadio::CODE rc = FAILURE; - - if (jConfig.isMember(KEY_FUMO_DRYRUN)) { - if(stepCb) { - stepCb(Json::Value("FUMO done: radio firmware applied successfully")); - } - return SUCCESS; - } - - // The radio is expected to send "#OTAEV: Module Upgraded To New Fw" unsolicited message - // on success. However, for some reason, we do not see this message. - - MTS::Timer oTimer; - oTimer.start(); - - while (oTimer.getSeconds() < (5 * 60)) { // 5 minutes - - MTS::Thread::sleep(10000); - - if (doGetFirmwareNumbers(sFirmware, sFirmwareBuild) != SUCCESS) { - // The radio is probably unavailable - resetConnection(100); - continue; - } - - if (sFirmware == m_sFw && sFirmwareBuild == m_sFwBuild) { - // Have the same firmware. The radio resets several time - // before the firmware is actually get upgraded. So keep polling. - continue; - } - - // The firmware numbers have changed - rc = SUCCESS; - break; - } - oTimer.stop(); - - if (rc == SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO done: radio firmware applied successfully")); - } - } - else { - if(stepCb) { - stepCb(Json::Value("FUMO error: radio firmware has not been updated")); - } - } - - return rc; -} - - -ICellularRadio::CODE ME910C1NVRadio::doFumoPerform(const Json::Value &jConfig, UpdateCb& stepCb) -{ - ICellularRadio::CODE rc = FAILURE; - - UpdateCb dummyCb; - - // Set the PDP context for the FOTA - rc = doFumoSetup(jConfig, stepCb); - if (rc != SUCCESS) { - return rc; - } - - // Download FW over FTP - rc = doFumoFtp(jConfig, stepCb); - if (rc != SUCCESS) { - doFumoCleanup(jConfig, dummyCb); - return rc; - } - - // Clean up before applying the FW file - rc = doFumoCleanup(jConfig, stepCb); - if (rc != SUCCESS) { - return rc; - } - - // Apply the FW file - rc = doFumoApplyFirmware(jConfig, stepCb); - if (rc != SUCCESS) { - return rc; - } - - rc = doFumoWaitNewFirmware(jConfig, stepCb); - - return rc; -} - -ICellularRadio::CODE ME910C1NVRadio::updateFumo(const Json::Value& jArgs, UpdateCb& stepCb) -{ - Json::Value jConfig(Json::objectValue); - ICellularRadio::CODE rc = FAILURE; - - rc = doFumoReadConfig(jArgs, jConfig); - if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: bad configuration parameters")); - } - return rc; - } - - return doFumoPerform(jConfig, stepCb); -} diff --git a/src/MTS_IO_ME910C1WWRadio.cpp b/src/MTS_IO_ME910C1WWRadio.cpp index 0469d5d..32c64b3 100644 --- a/src/MTS_IO_ME910C1WWRadio.cpp +++ b/src/MTS_IO_ME910C1WWRadio.cpp @@ -17,15 +17,27 @@ * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. * */ - +#include <fstream> #include <mts/MTS_Text.h> #include <mts/MTS_Logger.h> +#include <mts/MTS_Thread.h> +#include <mts/MTS_Timer.h> #include <mts/MTS_IO_ME910C1WWRadio.h> using namespace MTS::IO; const std::string ME910C1WWRadio::MODEL_NAME("ME910C1-WW"); +const std::string ME910C1WWRadio::KEY_FUMO_PDPID("pdpid"); // optional (default : "3") +const std::string ME910C1WWRadio::KEY_FUMO_PDPTYPE("pdptype"); // optional (default : "IPV4V6") +const std::string ME910C1WWRadio::KEY_FUMO_APN("apn"); // optional (default : "") +const std::string ME910C1WWRadio::KEY_FUMO_ADDRESS("address"); +const std::string ME910C1WWRadio::KEY_FUMO_DIR("dir"); +const std::string ME910C1WWRadio::KEY_FUMO_FILE("file"); +const std::string ME910C1WWRadio::KEY_FUMO_USER("user"); +const std::string ME910C1WWRadio::KEY_FUMO_PASSWORD("password"); +const std::string ME910C1WWRadio::KEY_FUMO_DRYRUN("dryrun"); + ME910C1WWRadio::ME910C1WWRadio(const std::string& sPort) : ME910Radio(MODEL_NAME, sPort) { @@ -119,3 +131,582 @@ ICellularRadio::CODE ME910C1WWRadio::getActiveFirmware(std::string& sFwId) { return SUCCESS; } +ICellularRadio::CODE ME910C1WWRadio::doGetFirmwareNumbers(std::string &sFirmware, std::string &sFirmwareBuild) { + ICellularRadio::CODE rc = FAILURE; + + rc = getFirmware(sFirmware); + if (rc != SUCCESS){ + return rc; + } + + rc = getFirmwareBuild(sFirmwareBuild); + if (rc != SUCCESS){ + return rc; + } + + return rc; +} + +ICellularRadio::CODE ME910C1WWRadio::doFumoReadConfig(const Json::Value& jArgs, Json::Value &jConfig) { + ICellularRadio::CODE rc = INVALID_ARGS; + std::string sPath; + + do + { + if (!jArgs["config-file"].isString()) { + rc = INVALID_ARGS; + break; + } + + sPath = jArgs["config-file"].asString(); + + std::ifstream file(sPath.c_str()); + if (!file.is_open()) { + printError("Failed to open file [%s]", sPath.c_str()); + break; + } + + file.seekg(0, std::ios::end); + size_t size = file.tellg(); + std::string buffer(size, ' '); + file.seekg(0); + file.read(&buffer[0], size); + file.close(); + + Json::Features features = Json::Features::strictMode(); + Json::Reader reader(features); + if (!reader.parse(buffer, jConfig)) { + printError("Error parsing FOTA configuration file"); + break; + } + + // + // set default values if missing + // + if (!jConfig.isMember(KEY_FUMO_PDPID)) { + jConfig[KEY_FUMO_PDPID] = std::string("3"); + } + + if (!jConfig.isMember(KEY_FUMO_PDPTYPE)) { + jConfig[KEY_FUMO_PDPTYPE] = std::string("IPV4V6"); + } + + if (!jConfig.isMember(KEY_FUMO_APN)) { + jConfig[KEY_FUMO_APN] = std::string(""); + } + + // + // validate + // + if (!jConfig[KEY_FUMO_PDPID].isString()) { + printError("Error loading FOTA configuration: PDP context id is not set"); + break; + } + + if (jConfig[KEY_FUMO_PDPID].asString().empty()) { + printError("Error loading FOTA configuration: context id is empty"); + break; + } + + if (!jConfig[KEY_FUMO_PDPTYPE].isString()) { + printError("Error loading FOTA configuration: PDP type is not set"); + break; + } + + if (jConfig[KEY_FUMO_PDPTYPE].asString().empty()) { + printError("Error loading FOTA configuration: PDP type is empty"); + break; + } + + // Note : allow empty APN + if (!jConfig[KEY_FUMO_APN].isString()) { + printError("Error loading FOTA configuration: APN is not set"); + break; + } + + if (!jConfig[KEY_FUMO_ADDRESS].isString()) { + printError("Error loading FOTA configuration: address is not set"); + break; + } + + if (jConfig[KEY_FUMO_ADDRESS].asString().empty()) { + printError("Error loading FOTA configuration: address is empty"); + break; + } + + // Note: allow empty dir + if (!jConfig[KEY_FUMO_DIR].isString()) { + printError("Error loading FOTA configuration: directory is not set"); + break; + } + + if (!jConfig[KEY_FUMO_FILE].isString()) { + printError("Error loading FOTA configuration: filename is not set"); + break; + } + + if (jConfig[KEY_FUMO_FILE].asString().empty()) { + printError("Error loading FOTA configuration: filename is empty"); + break; + } + + // Note: allow empty username/password + if (!jConfig[KEY_FUMO_USER].isString()) { + printError("Error loading FOTA configuration: username is not set"); + break; + } + + if (!jConfig[KEY_FUMO_PASSWORD].isString()) { + printError("Error loading FOTA configuration: password is not set"); + break; + } + + rc = SUCCESS; + } + while(0); + + return rc; +} + +ICellularRadio::CODE ME910C1WWRadio::doFumoSetup(const Json::Value &jConfig, UpdateCb& stepCb) { + ICellularRadio::CODE rc = FAILURE; + std::string sCmd; + + std::string sContextId = jConfig[KEY_FUMO_PDPID].asString(); + std::string sApn = jConfig[KEY_FUMO_APN].asString(); + std::string sPdpType = jConfig[KEY_FUMO_PDPTYPE].asString(); + + + do + { + // + // Execution command is used to activate or deactivate either the GSM + // context or the specified PDP context. + // + // AT#SGACT=<cid>,<stat>[,<userId>,<pwd>] + // + sCmd = "AT#SGACT=" + sContextId + ",0"; + rc = sendBasicCommand(sCmd); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: Failed to deactivate PDP context")); + } + break; + } + + // + // Read current Firmware numbers (let it be after AT#SGACT not to confuse users) + // + rc = doGetFirmwareNumbers(m_sFw, m_sFwBuild); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: Failed to obtain current firmware version")); + } + break; + } + + // + // Set command specifies PDP context parameter values for a PDP context identified by + // the (local) context identification parameter <cid>. + // + // AT+CGDCONT= [<cid>[,<PDP_type>[,<APN>[,<PDP_addr>[,<d_comp>[,<h_comp>[,<pd1>[,...[,pdN]]]]]]]]] + // + sCmd = "AT+CGDCONT=" + sContextId + ",\"" + sPdpType + "\""; + if (!sApn.empty()) { + sCmd += ",\"" + sApn + "\""; + } + rc = sendBasicCommand(sCmd, 1000); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: Failed to setup PDP context")); + } + break; + } + + // + // Set command sets the socket configuration parameters. + // + // AT#SCFG==<connId>,<cid>,<pktSz>,<maxTo>,<connTo>,<txTo> + // <connId> - socket connection identifier + // <cid> - PDP context identifier + // <pktSz> - packet size to be used by the TCP/UDP/IP stack for data sending. + // <maxTo> - exchange timeout (or socket inactivity timeout); if there’s no + // data exchange within this timeout period the connection is closed (timeout value in seconds). + // <connTo> - connection timeout; if we can’t establish a connection to the + // remote within this timeout period, an error is raised timeout value in hundreds of milliseconds. + // <txTo> - data sending timeout; after this period data are sent also if they’re + // less than max packet size (timeout value in hundreds of milliseconds). + // + sCmd = "AT#SCFG=1," + sContextId + ",300,90,600,50"; + rc = sendBasicCommand(sCmd); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: Failed to set connection configuration parameters")); + } + break; + } + + // + // Activate PDP context + // + sCmd = "AT#SGACT=" + sContextId + ",1"; + rc = sendBasicCommand(sCmd, 60 * 1000); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: Failed to activate PDP context")); + } + break; + } + } + while (0); + + return rc; +} + +ICellularRadio::CODE ME910C1WWRadio::doFumoFtp(const Json::Value &jConfig, UpdateCb& stepCb) { + ICellularRadio::CODE rc = FAILURE; + std::string sCmd; + std::string sResult; + + // + // Set command sets the time-out used when opening either the FTP control + // channel or the FTP traffic channel. + // + // AT#FTPTO= [<tout>] + // <tout> - time-out in 100 ms units + // + rc = sendBasicCommand("AT#FTPTO=2400"); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: Failed to setup connection timeout")); + } + return rc; + } + + // + // Execution command opens an FTP connection toward the FTP server. + // + // AT#FTPOPEN=[<server:port>,<username>,<password>[,<mode>]] + // + sCmd = "AT#FTPOPEN="; + sCmd += "\"" + jConfig[KEY_FUMO_ADDRESS].asString() + "\","; + sCmd += "\"" + jConfig[KEY_FUMO_USER].asString() + "\","; + sCmd += "\"" + jConfig[KEY_FUMO_PASSWORD].asString() + "\",1"; + rc = sendBasicCommand(sCmd, 60 * 1000); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: Failed to open connection")); + } + return rc; + } + + if (stepCb) { + stepCb(Json::Value("FUMO Info: connection opened")); + } + + do + { + // + // Set command, issued during an FTP connection, sets the file transfer type. + // + // AT#FTPTYPE=[<type>] + // <type> - file transfer type: + // 0 - binary + // 1 - ascii + // + rc = sendBasicCommand("AT#FTPTYPE=0", 1000); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to set file transfer type")); + } + break; + } + + // + // Execution command, issued during an FTP connection, changes the + // working directory on FTP server. + // + // AT#FTPCWD=[<dirname>] + // + sCmd = "AT#FTPCWD=\"/"; + if (!jConfig[KEY_FUMO_DIR].asString().empty()) { + sCmd += jConfig[KEY_FUMO_DIR].asString() + "/"; + } + sCmd += "\""; + rc = sendBasicCommand(sCmd, 60 * 1000); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to change working directory on the server")); + } + break; + } + + if (stepCb) { + stepCb(Json::Value("FUMO Info: downloading the firmware")); + } + + // + // Start FTP transfer + // + sCmd = "AT#FTPGETOTA="; + sCmd += "\"" + jConfig[KEY_FUMO_FILE].asString() + "\",1,1"; + sendBasicCommand(sCmd); + + // + // Noticed that after successful AT#FTPGETOTA the radio resets the connection. + // and the response code (OK, ERROR.. ) not always reach the host. Therefore + // we send the AT#FTPGETOTA with relatively small timeout and then poll with AT + // until we get valid response. After that, using AT#FTPMSG we can check the + // result of the last FTP command (which is AT#FTPGETOTA in our case). + MTS::Timer oTimer; + + oTimer.start(); + + while (oTimer.getSeconds() < (30 * 60)) // 30 min + { + MTS::Thread::sleep(5000); + + rc = sendBasicCommand("AT"); + if (rc == SUCCESS) { + break; + } + + resetConnection(1); + } + oTimer.stop(); + + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: unable to obtain radio after reset")); + } + break; + } + + // + // Now check the FTP status + // + std::string sResult = sendCommand("AT#FTPMSG"); + + printTrace("RADIO| AT#FTPMSG result [%s]", sResult.c_str()); + + if (sResult.find(ICellularRadio::RSP_OK) == std::string::npos) { + rc = FAILURE; + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to download the firmware file")); + } + break; + } + if (sResult.find("#FTPMSG: 550") != std::string::npos) { + // FTP(550) : File not found + rc = FAILURE; + if(stepCb) { + stepCb(Json::Value("FUMO Error: file not found")); + } + break; + } + if (sResult.find("#FTPMSG: 226") == std::string::npos) { + // FTP(226) : Successfully transferred + rc = FAILURE; + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to download the firmware file")); + } + break; + } + } + while (0); + + // + // Execution command closes an FTP connection. + // + // AT#FTPCLOSE + // + ICellularRadio::CODE rcclose = sendBasicCommand("AT#FTPCLOSE", 60 * 1000); + if (rcclose != SUCCESS && rc == SUCCESS) { + if(stepCb) { + // Only one "FUMO Error" message should be sent + stepCb(Json::Value("FUMO Error: Failed to close FTP connection")); + } + rc = rcclose; + } + + if (rc == SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Info: firmware downloaded successfully")); + } + } + + return rc; +} + +ICellularRadio::CODE ME910C1WWRadio::doFumoCleanup(const Json::Value &jConfig, UpdateCb& stepCb) { + ICellularRadio::CODE rc = FAILURE; + std::string sCmd; + + std::string sContextId = jConfig[KEY_FUMO_PDPID].asString(); + + // + // Deactivate PDP context + // + sCmd = "AT#SGACT=" + sContextId + ",0"; + rc = sendBasicCommand(sCmd, 10000); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: Failed to deactivate PDP context")); + } + } + return rc; +} + +ICellularRadio::CODE ME910C1WWRadio::doFumoApplyFirmware(const Json::Value &jConfig, UpdateCb& stepCb) { + ICellularRadio::CODE rc = FAILURE; + + if (jConfig.isMember(KEY_FUMO_DRYRUN)) { + if(stepCb) { + stepCb(Json::Value("FUMO Info: applying the radio firmware")); + } + return SUCCESS; + } + + rc = sendBasicCommand("AT#OTAUP=0", 10000); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to apply the firmware")); + } + return rc; + } + + if(stepCb) { + stepCb(Json::Value("FUMO Info: applying the radio firmware")); + } + + return rc; +} + +ICellularRadio::CODE ME910C1WWRadio::doFumoWaitNewFirmware(const Json::Value &jConfig, UpdateCb& stepCb) { + std::string sFirmware; + std::string sFirmwareBuild; + ICellularRadio::CODE rc = FAILURE; + + if (jConfig.isMember(KEY_FUMO_DRYRUN)) { + if(stepCb) { + stepCb(Json::Value("FUMO done: radio firmware applied successfully")); + } + return SUCCESS; + } + + // The radio is expected to send "#OTAEV: Module Upgraded To New Fw" unsolicited message + // on success. However, for some reason, we do not see this message. + + MTS::Timer oTimer; + oTimer.start(); + + while (oTimer.getSeconds() < (5 * 60)) { // 5 minutes + + MTS::Thread::sleep(10000); + + if (doGetFirmwareNumbers(sFirmware, sFirmwareBuild) != SUCCESS) { + // The radio is probably unavailable + resetConnection(100); + continue; + } + + if (sFirmware == m_sFw && sFirmwareBuild == m_sFwBuild) { + // Have the same firmware. The radio resets several time + // before the firmware is actually get upgraded. So keep polling. + continue; + } + + // The firmware numbers have changed + rc = SUCCESS; + break; + } + oTimer.stop(); + + if (rc == SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO done: radio firmware applied successfully")); + } + } + else { + if(stepCb) { + stepCb(Json::Value("FUMO error: radio firmware has not been updated")); + } + } + + return rc; +} + + +ICellularRadio::CODE ME910C1WWRadio::doFumoPerform(const Json::Value &jConfig, UpdateCb& stepCb) { + ICellularRadio::CODE rc = FAILURE; + + UpdateCb dummyCb; + + // Set the PDP context for the FOTA + rc = doFumoSetup(jConfig, stepCb); + if (rc != SUCCESS) { + return rc; + } + + // Download FW over FTP + rc = doFumoFtp(jConfig, stepCb); + if (rc != SUCCESS) { + doFumoCleanup(jConfig, dummyCb); + return rc; + } + + // Clean up before applying the FW file + rc = doFumoCleanup(jConfig, stepCb); + if (rc != SUCCESS) { + return rc; + } + + // Apply the FW file + rc = doFumoApplyFirmware(jConfig, stepCb); + if (rc != SUCCESS) { + return rc; + } + + rc = doFumoWaitNewFirmware(jConfig, stepCb); + + return rc; +} + +ICellularRadio::CODE ME910C1WWRadio::updateFumo(const Json::Value& jArgs, UpdateCb& stepCb) { + Json::Value jConfig(Json::objectValue); + ICellularRadio::CODE rc = FAILURE; + std::string sFwId; + + do + { + rc = getActiveFirmware(sFwId); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to obtain current active firmware id")); + } + break; + } + + // For Verizon Only + if (sFwId != "1") { + if(stepCb) { + stepCb(Json::Value("FUMO Error: fumo is not supported")); + } + break; + } + + rc = doFumoReadConfig(jArgs, jConfig); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: bad configuration parameters")); + } + break; + } + + rc = doFumoPerform(jConfig, stepCb); + } + while(0); + + return rc; +} + |