/*
* 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 .
*
*/
#include "mts/MTS_IO_QuectelRadio.h"
#include
#include
#include
using namespace MTS::IO;
QuectelRadio::QuectelRadio(const std::string& sName, const std::string& sRadioPort)
: CellularRadio (sName, sRadioPort)
{
}
bool QuectelRadio::resetRadio(uint32_t iTimeoutMillis) {
printInfo("%s| Rebooting radio", getName().c_str());
if(sendBasicCommand("AT+CFUN=1,1") == SUCCESS) {
if(iTimeoutMillis > 5000) {
MTS::Thread::sleep(5000);
iTimeoutMillis -= 5000;
}
return resetConnection(iTimeoutMillis);
}
return false;
}
ICellularRadio::CODE QuectelRadio::getModel(std::string& sModel) {
printTrace("%s| Get Model", getName().c_str());
//Always returns SUCCESS because the model should be m_sName
sModel = getName();
std::string sCmd("AT+GMM");
std::string sResult = sendCommand(sCmd);
if (sResult.find("OK") == std::string::npos) {
printWarning("%s| Unable to get model from radio. Returning [%s]", getName().c_str(), getName().c_str());
return SUCCESS;
} else {
sModel = extractModelFromResult(sResult);
if(sModel.size() == 0) {
printWarning("%s| Unable to get model from radio. Returning [%s]", getName().c_str(), getName().c_str());
return SUCCESS;
}
}
printDebug("%s| Extracted [%s] from [%s] query", getName().c_str(), sModel.c_str(), sCmd.c_str());
if(sModel != getName()) {
printWarning("%s| Model identified [%s] does not match expected [%s]. Returning [%s]",
getName().c_str(), sModel.c_str(), getName().c_str(), sModel.c_str());
}
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::getIccid(std::string& sIccid) {
printTrace("%s| Get ICCID", getName().c_str());
sIccid = ICellularRadio::VALUE_NOT_SUPPORTED;
// AT+QCCID execution can take up to 300ms according to the datasheet. Setting timeout to 500ms just for sure.
std::string sCmd("AT+QCCID");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 500);
size_t end = sResult.find(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to get ICCID from radio using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
size_t start = sResult.find("+QCCID:");
if(start != std::string::npos) {
start += sizeof("+QCCID:");
sIccid = MTS::Text::trim(sResult.substr(start, end-start));
if(sIccid.size() == 0) {
printWarning("%s| Unable to get ICCID from radio using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
}
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::getService(std::string& sService) {
printTrace("%s| Get Service", getName().c_str());
sService = ICellularRadio::VALUE_NOT_SUPPORTED;
std::string sCmd("AT+COPS?");
std::string sResult = sendCommand(sCmd);
size_t end = sResult.find(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to get Service from radio using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
size_t start = sResult.find(":") + 1; //Position right after "+COPS:"
std::vector vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start, end-start)), ',');
int32_t iAccessTechnology;
// +COPS: [,[,][,]]
if (vParts.size() < 4 || !MTS::Text::parse(iAccessTechnology, vParts[3])) {
printWarning("%s| Unable to get Service from radio using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
switch(iAccessTechnology) {
case 0 : sService = "GPRS" ; break; // GSM
case 2 : sService = "WCDMA" ; break; // UTRAN
case 3 : sService = "EGPRS" ; break; // GSM W/EGPRS
case 4 : sService = "HSDPA" ; break; // UTRAN W/HSDPA
case 5 : sService = "WCDMA" ; break; // UTRAN W/HSUPA
case 6 : sService = "HSDPA" ; break; // UTRAN W/HSDPA and HSUPA
case 7 : sService = "LTE" ; break; // E-UTRAN
case 100 : sService = "CDMA" ; break; // CDMA
default: sService = ICellularRadio::VALUE_UNKNOWN; break;
}
printDebug("%s| Service ID: [%d][%s]", getName().c_str(), iAccessTechnology, sService.c_str());
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::getNetwork(std::string& sNetwork) {
/*
* TODO: Refactor using MccMncTable once it'll be corrected.
*
* The proper way to determine the current network is to do that
* by MCC and MNC fetched from the `getNetworkStatus` and `AT+QENG` command.
* By using MCC and MNC from `AT+QENG` we can fetch the name of the network
* reported by a currently connected base station even if the SIM card is
* not installed or if we are currently working is a roaming mode.
*
* Until MccMncTable implementation is not fixed, we are using the name
* of a currently selected operator (AT+COPS).
*/
printTrace("%s| Get Network", getName().c_str());
sNetwork = ICellularRadio::VALUE_NOT_SUPPORTED;
std::string sCmd("AT+COPS?");
std::string sResult = sendCommand(sCmd);
size_t end = sResult.find(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to get network name from radio using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
// +COPS: [, , ,]
// +COPS: vParts[0],vParts[1],vParts[2],vParts[3]
size_t start = sResult.find(":") + 1; //Position right after "+COPS:"
std::vector vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start)), ",");
if(vParts.size() > 3) {
const std::string sValue = vParts[2];
// +COPS: 0,0,"CHN-UNICOM UNICOM",7
// ^start ^end
// +COPS: 0,0,"AT&T",7
// ^st ^end
size_t start = sValue.find("\"") + 1;
size_t end = sValue.find_first_of(" \"", start);
sNetwork = sValue.substr(start, end-start);
} else {
sNetwork = ""; // Not connected to any network
}
return SUCCESS;
}
/* AT+QENG="servingcell" - Query the information of serving cells
(GSM network)
+QENG:"servingscell",,"GSM",,,,,,,,,,,,,,,,,,,,,,,,
(WCDMA network)
+QENG:"servingcell",,"WCDMA",,,,,,,,,,,,,,
(LTE Network)
+QENG:"servingcell",,"LTE",,,,,,,,,,,,,,,
The following modes are NOT currently handled:
- TD-SCDMA mode;
- CDMA mode;
- HDR mode;
- SRLTE mode.
In the case of TD-SCDMA mode:
+QENG:"servingscell",,"TDSCDMA",,,,,,,,
In the case of CDMA mode or CDMA+HDR mode:
+QENG:"servingscell",,"CDMA",,,,,,,,
[+QENG:"servingscell",,"HDR",,,,,,,,]
In the case of SRLTE mode:
+QENG:"servingscell",,"CDMA",,,,,,,,
+QENG:"servingcell",,"LTE",,,,,,,,,,,,,,
*/
CellularRadio::CODE QuectelRadio::getNetworkStatus(Json::Value& jData) {
const std::string RAT_GSM = "GSM";
const std::string RAT_WCDMA = "WCDMA";
const std::string RAT_LTE = "LTE";
ACTIVEBAND abnd;
SERVICEDOMAIN sd;
std::string sValue;
std::string sRat; // Radio Access Technology which is currently used
printTrace("%s| Get Network Status", getName().c_str());
//Always get common network stats because this should never fail
//This way the basic stats are always returned even if AT+QENG fails below
getCommonNetworkStats(jData);
// IMSI is not provided by AT+QENG. Fetch it separately to keep the same interface
if (getImsi(sValue) == SUCCESS) {
jData[ICellularRadio::KEY_IMSI] = sValue;
}
// Network Name is not explicitly provided by AT+QENG. Fetch it separately to keep the same interface
// TODO: Replace with lookup by MCC and MNC once MccMncTable is fixed.
if (getNetwork(sValue) == SUCCESS) {
jData[ICellularRadio::KEY_NETWORK] = sValue;
}
std::string sCmd;
std::string sResult;
sCmd = "AT+QENG=\"servingcell\"";
sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 200);
if (sResult.find("+QENG: \"servingcell\"") == std::string::npos) {
printDebug("%s| Network Status command returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), sResult.c_str());
printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str());
return SUCCESS; //return SUCCESS because getCommonNetworkStats() succeeded at top of this function
}
size_t start = sResult.find(":") + 1; //Position right after "+QENG:"
size_t end = sResult.rfind(ICellularRadio::RSP_OK);
std::vector vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start, end-start)), ",");
Json::Value jDebug;
Json::Value jQuectelDebug;
if (vParts.size() < 3) {
printDebug("%s| Network Status command reponse is an unknown format: [%s][%s]", getName().c_str(), sCmd.c_str(), sResult.c_str());
printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str());
return SUCCESS; //return SUCCESS because getCommonNetworkStats() succeeded at top of this function
} else {
// UE state and Access technology, Quectel-specific information
jQuectelDebug["state"] = vParts[1];
sRat = MTS::Text::trim(vParts[2], '"');
jQuectelDebug["rat"] = sRat;
}
// +QENG:"servingscell",,"GSM",,,,,,,,,,,,,,,,,,,,,,,,
// +QENG: [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13],[14],[15], [16], [17],[18],[19], [20], [21], [22], [23], [24], [25], [26]
if (sRat == RAT_GSM) {
//Parse as GSM Network Format
jData[ICellularRadio::KEY_MCC] = vParts[3];
jData[ICellularRadio::KEY_MNC] = vParts[4];
jData[ICellularRadio::KEY_LAC] = vParts[5];
jData[ICellularRadio::KEY_CID] = vParts[6];
jQuectelDebug["bsic"] = vParts[7];
jData[ICellularRadio::KEY_CHANNEL] = vParts[8];
if (convertToActiveBand(vParts[9], abnd) == SUCCESS && convertActiveBandToString(abnd, sValue) == SUCCESS) {
jData[ICellularRadio::KEY_ABND] = sValue;
}
jData[ICellularRadio::KEY_RSSIDBM] = vParts[10]; // Values already negative. No need to substract 111 as stated in a datasheet
jData[ICellularRadio::KEY_TXPWR] = vParts[11];
jQuectelDebug["rla"] = vParts[12];
jQuectelDebug["drx"] = vParts[13];
jQuectelDebug["c1"] = vParts[14];
jQuectelDebug["c2"] = vParts[15];
jQuectelDebug["gprs"] = vParts[16];
jQuectelDebug["tch"] = vParts[17];
jQuectelDebug["ts"] = vParts[18];
jQuectelDebug["ta"] = vParts[19];
jQuectelDebug["maio"] = vParts[20];
jQuectelDebug["hsn"] = vParts[21];
jQuectelDebug["rxlevsub"] = vParts[22];
jQuectelDebug["rxlevfull"] = vParts[23];
jQuectelDebug["rxqualsub"] = vParts[24];
jQuectelDebug["rxqualfull"] = vParts[25];
jQuectelDebug["voicecodec"] = vParts[26];
// Service Domain is not provided by AT+QENG. Fetch it separately to keep the same interface
if (getServiceDomain(sd) == SUCCESS && convertServiceDomainToString(sd, sValue) == SUCCESS) {
jData[ICellularRadio::KEY_SD] = sValue;
}
// The following fields can NOT be fetched for Quectel in GSM mode: RAC, MM, RR, NOM
jData["quectelDebug"] = jQuectelDebug;
}
// +QENG:"servingcell",,"WCDMA",,,,,,,,,,,,,,
// +QENG: [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12],[13], [14], [15], [16]
else if(sRat == RAT_WCDMA) {
//Parse as WCDMA Network Format
jData[ICellularRadio::KEY_MCC] = vParts[3];
jData[ICellularRadio::KEY_MNC] = vParts[4];
jData[ICellularRadio::KEY_LAC] = vParts[5];
jData[ICellularRadio::KEY_CID] = vParts[6];
jData[ICellularRadio::KEY_CHANNEL] = vParts[7];
jDebug[ICellularRadio::KEY_PSC] = vParts[8];
jData[ICellularRadio::KEY_RAC] = vParts[9];
jDebug[ICellularRadio::KEY_RSCP] = vParts[10];
jDebug[ICellularRadio::KEY_ECIO] = vParts[11];
jQuectelDebug["phych"] = vParts[12];
jQuectelDebug["sf"] = vParts[13];
jQuectelDebug["slot"] = vParts[14];
jQuectelDebug["speechCode"] = vParts[15];
jQuectelDebug["comMod"] = vParts[16];
// The following fields can NOT be fetched for Quectel in WCDMA mode: TXPWR, DRX, MM, RR, NOM, BLER
// RSSI is not provided by AT+QENG in WCDMA mode. It was filled above by the getCommonNetworkStats
// Service Domain is not provided by AT+QENG. Fetch it separately to keep the same interface
if (getServiceDomain(sd) == SUCCESS && convertServiceDomainToString(sd, sValue) == SUCCESS) {
jDebug[ICellularRadio::KEY_SD] = sValue;
}
// BLER is not provided by AT+QENG. Set to constant
jDebug[ICellularRadio::KEY_BLER] = "000";
// Get the radio band given the channel (UARFCN)
RadioBandMap radioBandMap(vParts[7], ICellularRadio::VALUE_TYPE_CDMA);
jData[ICellularRadio::KEY_ABND] = radioBandMap.getRadioBandName();
jData["quectelDebug"] = jQuectelDebug;
jData[ICellularRadio::KEY_DEBUG] = jDebug;
}
// +QENG:"servingcell",,"LTE",,,,,,,,,,,,,,,
// +QENG: [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17]
else if(sRat == RAT_LTE) {
//Parse as LTE Network Format
jQuectelDebug["isTdd"] = vParts[3];
jData[ICellularRadio::KEY_MCC] = vParts[4];
jData[ICellularRadio::KEY_MNC] = vParts[5];
jData[ICellularRadio::KEY_CID] = vParts[6];
jQuectelDebug["pcid"] = vParts[7];
jData[ICellularRadio::KEY_CHANNEL] = vParts[8];
jQuectelDebug["freqBandInd"] = vParts[9];
jQuectelDebug["ulBandwidth"] = vParts[10];
jQuectelDebug["dlBandwidth"] = vParts[11];
jData["tac"] = vParts[12];
jDebug["rsrp"] = vParts[13];
jDebug["rsrq"] = vParts[14];
jDebug[ICellularRadio::KEY_RSSIDBM] = vParts[15];
jQuectelDebug["sinr"] = vParts[16];
jQuectelDebug["srxlev"] = vParts[17];
// Get the radio band given the channel (EARFCN)
RadioBandMap radioBandMap(vParts[8], ICellularRadio::VALUE_TYPE_LTE);
jData[ICellularRadio::KEY_ABND] = radioBandMap.getRadioBandName();
// Service Domain is not provided by AT+QENG. Fetch it separately to keep the same interface
if (getServiceDomain(sd) == SUCCESS && convertServiceDomainToString(sd, sValue) == SUCCESS) {
jDebug[ICellularRadio::KEY_SD] = sValue;
}
// LAC is not provided by AT+QENG in WCDMA mode. Use another command instead
jData[ICellularRadio::KEY_LAC] = queryLteLac();
jData["quectelDebug"] = jQuectelDebug;
jData[ICellularRadio::KEY_DEBUG] = jDebug;
}
printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str());
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::convertSignalStrengthTodBm(const int32_t& iRssi, int32_t& iDbm) {
int dbmSteps, minValue, maxValue, rssiOffset;
int rawDbm;
if(iRssi >= 0 && iRssi < 99) {
// normal scaling
dbmSteps = 2;
minValue = -113;
maxValue = -51;
rssiOffset = 0;
} else if(iRssi >= 100 && iRssi < 199) {
// TD-SCDMA scaling
dbmSteps = 1;
minValue = -116;
maxValue = -25;
rssiOffset = 100;
} else {
return FAILURE; // invalid, not known or not detectable
}
rawDbm = minValue + ((iRssi - rssiOffset) * dbmSteps);
iDbm = std::min(maxValue, rawDbm);
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::convertdBmToSignalStrength(const int32_t& iDBm, int32_t& iRssi) {
//Quectel Conversion FOR NORMAL SCALING
const int dbmSteps = 2;
const int minValue = -113;
const int rssiOffset = 0;
if (iDBm < -113) {
iRssi = 0;
} else if (iDBm > -51) {
iRssi = 31;
} else {
iRssi = ((iDBm - minValue) / dbmSteps) + rssiOffset;
}
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::setMdn(const Json::Value& jArgs) {
printTrace("%s| Set MDN", getName().c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE QuectelRadio::startOmaDm(ICellularRadio::UpdateCb& stepCb) {
printTrace("%s| Start OMA DM procedure", getName().c_str());
const int32_t iTimeoutOk = 3 * 1000; // 3 seconds
const int32_t iTimeoutStart = 5 * 1000; // 5 seconds
const int32_t iTimeoutEnd = 1 * 60 * 1000; // 1 minute
const std::string sOdmStarted = "DM Start";
const std::string sOdmFinished = "DM End";
const std::vector vOdmStartedStrings{ sOdmStarted };
const std::vector vOdmFinishedStrings{ sOdmFinished };
CODE eCode;
do {
// Send command and expect "OK" in iTimeoutOk milliseconds
eCode = sendBasicCommand("AT+QODM=\"dme\",2,\"ui\"", iTimeoutOk);
if (eCode != SUCCESS) {
printError("%s| OMA DM procedure can not be started", getName().c_str());
if (stepCb) {
stepCb(Json::Value("OMA DM Error: OMA DM can not be started"));
}
break;
}
// Wait for the "Start" response
std::string sResponse = sendCommand("", vOdmStartedStrings, iTimeoutStart, 0x00);
printDebug("%s| Radio returned: [%s]", getName().c_str(), sResponse.c_str());
if (sResponse.find(sOdmStarted) == std::string::npos) {
printError("%s| OMA DM procedure failed due to timeout", getName().c_str());
if (stepCb) {
stepCb(Json::Value("OMA DM Error: OMA DM failed due to timeout"));
}
eCode = FAILURE;
break;
}
// Got "DM Started" message from the radio
printTrace("%s| OMA DM started", getName().c_str());
if (stepCb) {
stepCb(Json::Value("OMA DM Info: OMA DM started"));
}
// Wait for the "End" response
sResponse = sendCommand("", vOdmFinishedStrings, iTimeoutEnd, 0x00);
printDebug("%s| Radio returned: [%s]", getName().c_str(), sResponse.c_str());
if (sResponse.find(sOdmFinished) == std::string::npos) {
printError("%s| OMA DM procedure failed due to timeout", getName().c_str());
if (stepCb) {
stepCb(Json::Value("OMA DM Error: OMA DM failed due to timeout"));
}
eCode = FAILURE;
break;
}
// Got "DM End" message from the radio
printTrace("%s| OMA DM finished", getName().c_str());
if (stepCb) {
stepCb(Json::Value("OMA DM Info: OMA DM finished"));
}
eCode = SUCCESS;
} while (false);
return eCode;
}
ICellularRadio::CODE QuectelRadio::getServiceDomain(ICellularRadio::SERVICEDOMAIN& sd) {
printTrace("%s| Get Service Domain", getName().c_str());
std::string sCmd("AT+QCFG=\"servicedomain\"");
std::string sResult = sendCommand(sCmd);
size_t end = sResult.find(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to get service domain using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
// +QCFG: "servicedomain",
size_t start = sResult.find(",") + 1; // Position right after comma
std::string sServiceDomain = MTS::Text::trim(sResult.substr(start, end-start));
int iValue = -1;
if (!MTS::Text::parse(iValue, sServiceDomain)) {
printWarning("%s| Failed to parse service domain from command output [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
switch (iValue) {
case 0: sd = SERVICEDOMAIN::CS_ONLY; break;
case 1: sd = SERVICEDOMAIN::PS_ONLY; break;
case 2: sd = SERVICEDOMAIN::CSPS; break;
default: return FAILURE; // Unknown
}
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::getIsSimInserted(bool& bData) {
printTrace("%s| Get SIM insertion status", getName().c_str());
// AT+QSIMSTAT? execution can take up to 300ms according to the datasheet. Setting timeout to 500ms just for sure.
std::string sCmd("AT+QSIMSTAT?");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 500);
const std::string sPrefix = "+QSIMSTAT: ";
size_t start = sResult.find(sPrefix);
size_t end = sResult.rfind(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to get SIM insertion status from radio using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
if (start == std::string::npos) {
printDebug("%s| AT+QSIMSTAT? returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), sResult.c_str());
return FAILURE;
}
// +QSIMSTAT: ,
start += sPrefix.size();
std::vector vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start, end-start)), ',');
if(vParts.size() != 2) {
printWarning("%s| Unable to parse SIM insertion status from response [%s]", getName().c_str(), sResult.c_str());
return FAILURE;
}
if (vParts[1] == "1") { // Inserted
bData = true;
} else { // Removed or Unknown, before (U)SIM initialization
bData = false;
}
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk) {
printTrace("%s| Get SIM unlock attempts left", getName().c_str());
// AT+QPINC execution can take more time that expected. Set timeout to 2s just to be sure.
std::string sCmd("AT+QPINC=\"SC\"");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 2000);
const std::string sPrefix = "+QPINC: \"SC\",";
size_t start = sResult.find(sPrefix);
size_t end = sResult.rfind(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to get SIM unlock attempts from radio using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
if (start == std::string::npos) {
printDebug("%s| AT+QPINC returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), sResult.c_str());
return FAILURE;
}
// +QPINC: ,,
// [x] ,[0] ,[1]
start += sPrefix.size();
std::vector vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start, end-start)), ',');
if(vParts.size() != 2) {
printWarning("%s| Unable to parse SIM unlock attempts left from response [%s]", getName().c_str(), sResult.c_str());
return FAILURE;
}
if (!MTS::Text::parse(iAttemptsPin, vParts[0])) {
printWarning("%s| Unable to parse SIM PIM unlock attempts from response [%s]", getName().c_str(), sResult.c_str());
return FAILURE;
}
if (!MTS::Text::parse(iAttemptsPuk, vParts[1])) {
printWarning("%s| Unable to parse SIM PUK unlock attempts from response [%s]", getName().c_str(), sResult.c_str());
return FAILURE;
}
return SUCCESS;
}
ICellularRadio::CODE QuectelRadio::convertToActiveBand(const std::string& sQuectelBand, ICellularRadio::ACTIVEBAND& band) {
int iQuectelBand = -1;
if (!MTS::Text::parse(iQuectelBand, sQuectelBand)) {
return FAILURE; // probably "-", other band
}
switch (iQuectelBand) {
case 0: band = ACTIVEBAND::DCS_1800; break;
case 1: band = ACTIVEBAND::PCS_1900; break;
default: return FAILURE; // actually, this case should never happen
}
return SUCCESS;
}