/*
* 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
#include
#include
#include
using namespace MTS::IO;
TelitRadio::TelitRadio(const std::string& sName, const std::string& sRadioPort)
: CellularRadio(sName, sRadioPort)
{
}
bool TelitRadio::resetRadio(uint32_t iTimeoutMillis) {
printInfo("%s| Rebooting radio", getName().c_str());
if(sendBasicCommand("AT#REBOOT") == SUCCESS) {
if(iTimeoutMillis > 5000) {
MTS::Thread::sleep(5000);
iTimeoutMillis -= 5000;
}
return resetConnection(iTimeoutMillis);
}
return false;
}
ICellularRadio::CODE TelitRadio::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("ATI4");
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 TelitRadio::getIccid(std::string& sIccid) {
printTrace("%s| Get ICCID", getName().c_str());
sIccid = ICellularRadio::VALUE_NOT_SUPPORTED;
std::string sCmd("AT#CCID");
std::string sResult = CellularRadio::sendCommand(sCmd);
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("#CCID:");
if(start != std::string::npos) {
start += sizeof("#CCID:");
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 TelitRadio::getService(std::string& sService) {
printTrace("%s| Get Service", getName().c_str());
sService = ICellularRadio::VALUE_NOT_SUPPORTED;
std::string sCmd("AT#PSNT?");
std::string sResult = CellularRadio::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(",");
if(start != std::string::npos) {
start += 1; //comma
std::string sPsnt = MTS::Text::trim(sResult.substr(start, end-start));
int32_t iService;
sscanf(sPsnt.c_str(), "%d", &iService);
switch(iService) {
case 0: sService = "GPRS"; break;
case 1: sService = "EGPRS"; break;
case 2: sService = "WCDMA"; break;
case 3: sService = "HSDPA"; break;
case 4: sService = "LTE"; break;
default: sService = ICellularRadio::VALUE_UNKNOWN; break;
}
printDebug("%s| Service ID: [%d][%s]", getName().c_str(), iService, sService.c_str());
}
return SUCCESS;
}
ICellularRadio::CODE TelitRadio::getNetwork(std::string& sNetwork) {
Json::Value jData;
printTrace("%s| Get Network", getName().c_str());
sNetwork = ICellularRadio::VALUE_NOT_SUPPORTED;
if(getNetworkStatus(jData) == SUCCESS) {
if(jData.isMember(ICellularRadio::KEY_NETWORK)) {
sNetwork = jData[ICellularRadio::KEY_NETWORK].asString();
return SUCCESS;
}
}
return FAILURE;
}
/* AT#RFSTS - NETWORK STATUS
(GSM network)
#RFSTS:,,,,,,,,,,,,,
Where:
- Country code and operator code(MCC, MNC)
- GSM Assigned Radio Channel
- Received Signal Strength Indication
- Localization Area Code
- Routing Area Code
- Tx Power
- Mobility Management state
- Radio Resource state
- Network Operator Mode
- Cell ID
- International Mobile Subscriber Identity
- Operator name
- Service Domain
0 - No Service
1 - CS only
2 - PS only
3 - CS+PS
- Active Band
1 - GSM 850
2 - GSM 900
3 - DCS 1800
4 - PCS 1900
(WCDMA network)
#RFSTS:
,,,,, RSSI>,,,,,,,,,,,
,,[,,]
Where:
- Country code and operator code(MCC, MNC)
- UMTS Assigned Radio Channel
- Active PSC(Primary Synchronization Code)
- Active Ec/Io(chip energy per total wideband power in dBm)
- Active RSCP (Received Signal Code Power in dBm)
- Received Signal Strength Indication
- Localization Area Code
- Routing Area Code
- Tx Power
- Discontinuous reception cycle Length (cycle length in ms)
- Mobility Management state
- Radio Resource state
- Network Operator Mode
- Block Error Rate (e.g., 005 means 0.5 %)
- Cell ID
- International Mobile Station ID
- Operator name
- Service Domain (see above)
- Number of Active Set (Maximum 6)
UARFCN of n th active set
PSC of n th active set
Ec/Io of n th active Set
(LTE Network)
#RFSTS:
-
-
-
-
-
-
[] -
-
-
-
-
-
[] -
-
-
*/
ICellularRadio::CODE TelitRadio::getNetworkStatus(Json::Value& jData) {
int32_t iValue;
std::string sValue;
const uint32_t GSM_NETWORK_FORMAT = 14;
const uint32_t WCDMA_NETWORK_FORMAT = 19;
const uint32_t LTE_NETWORK_FORMAT = 16;
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#RFSTS fails below
getCommonNetworkStats(jData);
std::string sCmd;
std::string sResult;
// LE910 radios have a bug where issuing AT#RFSTS with a locked SIM
// will cause the radio to stop responding until a radio power cycle
// Telit Support Portal Case #5069697
// LE910C1-NS is an LE910, so we stop the scan after the 0.
if (getName().find("LE910") != std::string::npos) {
sCmd = "AT+CPIN?";
sResult = sendCommand(sCmd);
if (sResult.find("+CPIN:") == std::string::npos) {
printDebug("%s| AT+CPIN? 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
}
if (sResult.find("SIM PIN") != std::string::npos) {
printError("%s| The SIM is locked and must first be unlocked", getName().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
}
}
sCmd = "AT#RFSTS";
sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 200);
if (sResult.find("#RFSTS:") == std::string::npos) {
//On LTE radios without signal, this case will run because AT#RFSTS just returns "OK"
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 "#RFSTS:"
std::vector vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start)), ",");
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 {
//Country Code and Operator Code
std::vector vPLMN = MTS::Text::split(vParts[0], ' ');
if(vPLMN.size() == 2) {
jData[ICellularRadio::KEY_MCC] = MTS::Text::strip(vPLMN[0], '"');
jData[ICellularRadio::KEY_MNC] = MTS::Text::strip(vPLMN[1], '"');
}
jData[ICellularRadio::KEY_CHANNEL] = vParts[1];
}
if (vParts.size() == GSM_NETWORK_FORMAT ) {
//Parse as GSM Network Format
jData[ICellularRadio::KEY_RSSIDBM] = vParts[2];
jData[ICellularRadio::KEY_LAC] = vParts[3];
jData[ICellularRadio::KEY_RAC] = vParts[4];
jData[ICellularRadio::KEY_TXPWR] = vParts[5];
jData[ICellularRadio::KEY_MM] = vParts[6];
jData[ICellularRadio::KEY_RR] = vParts[7];
jData[ICellularRadio::KEY_NOM] = vParts[8];
jData[ICellularRadio::KEY_CID] = vParts[9];
jData[ICellularRadio::KEY_IMSI] = MTS::Text::strip(vParts[10], '"');
jData[ICellularRadio::KEY_NETWORK] = MTS::Text::strip(vParts[11], '"');
if(MTS::Text::parse(iValue, vParts[12]) && convertServiceDomainToString((SERVICEDOMAIN)iValue, sValue) == SUCCESS) {
jData[ICellularRadio::KEY_SD] = sValue;
}
if(MTS::Text::parse(iValue, vParts[13]) && convertActiveBandToString((ACTIVEBAND)iValue, sValue) == SUCCESS) {
jData[ICellularRadio::KEY_ABND] = sValue;
}
// IN003567 ME910C1 radios have some odd behavior with regards to WCDMA. The ordering of the fields from #RFSTS are
// the same as LTE up to the 16th field (for ME901C1-WW anyway). Drop into LTE parsing for ME910C1-WW.
} else if((vParts.size() >= WCDMA_NETWORK_FORMAT) && (getName().find("ME910C1-WW") == std::string::npos)) {
Json::Value jDebug;
//Parse as WCDMA Network Format
jDebug[ICellularRadio::KEY_PSC] = vParts[2];
jDebug[ICellularRadio::KEY_ECIO] = vParts[3];
jDebug[ICellularRadio::KEY_RSCP] = vParts[4];
jData[ICellularRadio::KEY_RSSIDBM] = vParts[5];
jData[ICellularRadio::KEY_LAC] = vParts[6];
jData[ICellularRadio::KEY_RAC] = vParts[7];
jDebug[ICellularRadio::KEY_TXPWR] = vParts[8];
jDebug[ICellularRadio::KEY_DRX] = vParts[9];
jDebug[ICellularRadio::KEY_MM] = vParts[10];
jDebug[ICellularRadio::KEY_RR] = vParts[11];
jDebug[ICellularRadio::KEY_NOM] = vParts[12];
if(vParts[13].size() != 0) {
jDebug[ICellularRadio::KEY_BLER] = vParts[13];
} else {
jDebug[ICellularRadio::KEY_BLER] = "000";
}
jData[ICellularRadio::KEY_CID] = vParts[14];
jData[ICellularRadio::KEY_IMSI] = MTS::Text::strip(vParts[15], '"');
jData[ICellularRadio::KEY_NETWORK] = MTS::Text::strip(vParts[16], '"');
// Get the radio band given the channel (UARFCN)
RadioBandMap radioBandMap(vParts[1], CellularRadio::ICellularRadio::VALUE_TYPE_CDMA);
jData[ICellularRadio::KEY_ABND] = radioBandMap.getRadioBandName();
if(MTS::Text::parse(iValue, vParts[17]) && convertServiceDomainToString((SERVICEDOMAIN)iValue, sValue) == SUCCESS) {
jDebug[ICellularRadio::KEY_SD] = sValue;
}
//Ignoring Active Set Values
// - Number of Active Set (Maximum 6)
// - UARFCN of n th active set
// - PSC of n th active set
// - Ec/Io of n th active Set
jData[ICellularRadio::KEY_DEBUG] = jDebug;
} else if(vParts.size() >= LTE_NETWORK_FORMAT) {
Json::Value jDebug;
//Parse as LTE Network Format
//
// MD: It is noticed that LTE Network format may vary depending on the firmware version:
//
// ,,,,,,[],,,,,,[],,,
// Ex 1: #RFSTS: "310 260",2300,-98,-63,-14,AA06,,128,19,0,0501D02,"310260754792598","T-Mobile",3,4,197
//
// ,,,,,,,[],,,,,,[],,
// Ex 2: #RFSTS:"310 410",5780,-105,-73,-14,4603,255,,128,19,0,0000098,"310410536498694","AT&T",3,17
// #RFSTS:"311 480",1150,-96,-66,-9.0,bf35,FF,0,0,19,1,"2ED1B0E","311480148817753","Verizon",2,2,720000,10800
// #RFSTS:"310 410",2175,-120,-89,-17.5,4612,FF,0,0,19,1,"4E5E916","310410807276607","AT&T",3,4
//
// Additional parameter in the second example shifts the rest of the parameters. Here we are trying to figure out
// which format is currently produced based on field position which always has double quotation marks.
//
if (vParts[13].find("\"") != std::string::npos) {
// parse the RAC and then remove it from the vector
jData[ICellularRadio::KEY_RAC] = vParts[6];
vParts.erase(vParts.begin() + 6);
}
jDebug["rsrp"] = vParts[2];
jDebug[ICellularRadio::KEY_RSSIDBM] = vParts[3];
jDebug["rsrq"] = vParts[4];
jData["tac"] = vParts[5];
jDebug[ICellularRadio::KEY_TXPWR] = vParts[6];
jData[ICellularRadio::KEY_DRX] = vParts[7];
jDebug[ICellularRadio::KEY_MM] = vParts[8];
jDebug["rrc"] = vParts[9];
jData[ICellularRadio::KEY_CID] = MTS::Text::strip(vParts[10], '"');
jData[ICellularRadio::KEY_IMSI] = MTS::Text::strip(vParts[11], '"');
jData[ICellularRadio::KEY_NETWORK] = MTS::Text::strip(vParts[12], '"');
// Get the radio band given the channel (EARFCN)
RadioBandMap radioBandMap(vParts[1], ICellularRadio::VALUE_TYPE_LTE);
jData[ICellularRadio::KEY_ABND] = radioBandMap.getRadioBandName();
jData[ICellularRadio::KEY_LAC] = queryLteLac();
if(MTS::Text::parse(iValue, vParts[13]) && convertServiceDomainToString((SERVICEDOMAIN)iValue, sValue) == SUCCESS) {
jDebug[ICellularRadio::KEY_SD] = sValue;
}
jData[ICellularRadio::KEY_DEBUG] = jDebug;
}
printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str());
return SUCCESS;
}
ICellularRadio::CODE TelitRadio::convertSignalStrengthTodBm(const int32_t& iRssi, int32_t& iDbm) {
//Telit Conversion
if(iRssi < 0 || iRssi == 99) {
return FAILURE;
}
if(iRssi == 0) {
iDbm = -113;
} else if(iRssi == 1) {
iDbm = -111;
} else if(iRssi <= 30) {
//28 steps between 2 and 30
//54 dbm between 53 and 109
float stepSize = 54.0 / 28.0;
iDbm = -109 + (int)(stepSize * (iRssi-2));
} else {
iDbm = -51;
}
return SUCCESS;
}
ICellularRadio::CODE TelitRadio::convertdBmToSignalStrength(const int32_t& iDBm, int32_t& iRssi) {
//Telit Conversion
if(iDBm <= -113) {
iRssi = 0;
} else if(iDBm <= -111) {
iRssi = 1;
} else if(iDBm <= -53) {
//54 dbm between -109 and -53
//28 steps between 2 and 30
float stepSize = 28.0/54.0;
iRssi = ((iDBm + 109)*stepSize) + 2;
} else {
iRssi = 31;
}
return SUCCESS;
}
ICellularRadio::CODE TelitRadio::setMdn(const Json::Value& jArgs) {
printTrace("%s| Set MDN", getName().c_str());
if(!jArgs["mdn"].isString()) {
return INVALID_ARGS;
}
std::string sCmd("AT#SNUM=1,\"");
sCmd += jArgs["mdn"].asString() + "\"";
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 1000);
size_t end = sResult.find(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to set MDN for radio using command [%s]", getName().c_str(), sCmd.c_str());
return FAILURE;
}
return SUCCESS;
}
bool TelitRadio::getCarrierFromFirmware(const std::string& sFirmware, std::string& sCarrier) {
// Telit Radios
// H.ab.zyx => 3 Main Components
// "H" = Hardware -> 15 = DE910 family, 18 = CE910 family, 12 = HE910 family
// "a" = Hardware version
// "b" = Software Major Version
// "z" = is the product type, i.e. DUAL or SC
// "y" = is the carrier variant
// "x" = is the firmware version
// Telit will do their best to keep the carrier variant as "0" for Sprint, "1" for Aeris, "2" for Verizon, and "3" for U.S. Cellular.
const uint32_t CARRIER_INDEX = 1; //y in [zyx]
bool bResult = false;
std::vector vParts = MTS::Text::split(sFirmware, '.');
if(vParts.size() == 3) {
//CDMA firmware version notation
if(vParts[0] == "15" || vParts[0] == "18") {
//DE910 or CE910 -> Good good
std::string sID = vParts[2];
if(sID.size() == 3) {
char cId = sID[CARRIER_INDEX];
//Good good
if(cId == '0') {
sCarrier = ICellularRadio::VALUE_CARRIER_SPRINT;
bResult = true;
} else
if(cId == '1') {
sCarrier = ICellularRadio::VALUE_CARRIER_AERIS;
bResult = true;
} else
if(cId == '2') {
sCarrier = ICellularRadio::VALUE_CARRIER_VERIZON;
bResult = true;
} else
if(cId == '3') {
sCarrier = ICellularRadio::VALUE_CARRIER_USCELLULAR;
bResult = true;
}
}
}
}
return bResult;
}
bool TelitRadio::getHardwareVersionFromFirmware(const std::string& sFirmware, std::string& sHardware) {
// Telit Radios
// H.ab.zyx => 3 Main Components
// "H" = Hardware -> 15 = DE910 family, 18 = CE910 family, 12 = HE910 family
// "a" = Hardware version
// "b" = Software Major Version
// "z" = is the product type, i.e. DUAL or SC
// "y" = is the carrier variant
// "x" = is the firmware version
// Telit will do their best to keep the carrier variant as "0" for Sprint, "1" for Aeris, and "2" for Verizon.
const uint32_t HARDWARE_INDEX = 0; //a in [ab]
bool bResult = false;
std::vector vParts = MTS::Text::split(sFirmware, '.');
if(vParts.size() == 3) {
//GSM Hardware Version
if(!(vParts[0] == "15" || vParts[0] == "18")) {
//Not DE910 or CE910 -> Good good
std::string sVersion = vParts[1];
if(sVersion.size() == 2) {
sHardware = "1.";
sHardware += sVersion[HARDWARE_INDEX];
bResult = true;
}
}
}
return bResult;
}
ICellularRadio::CODE TelitRadio::getRadioNetworkMode(RADIO_NETWORK_MODE &mode)
{
std::string sCmd("AT+WS46?");
std::string cmdResult = sendCommand(sCmd);
if (cmdResult.find("+WS46:") == std::string::npos) {
printDebug("%s| AT+WS46? returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), cmdResult.c_str());
return FAILURE;
}
switch (stoi(MTS::Text::split(cmdResult, ':')[1])) {
case 12: mode = ICellularRadio::RADIO_NETWORK_MODE_GSM_ONLY; break;
case 22: mode = ICellularRadio::RADIO_NETWORK_MODE_UMTS_ONLY; break;
case 25: mode = ICellularRadio::RADIO_NETWORK_MODE_AUTO; break;
default: mode = ICellularRadio::RADIO_NETWORK_MODE_UNKNOWN; break;
}
return SUCCESS;
}
ICellularRadio::CODE TelitRadio::setRadioNetworkMode(RADIO_NETWORK_MODE mode)
{
std::string value;
switch (mode) {
case ICellularRadio::RADIO_NETWORK_MODE_GSM_ONLY: value = "12"; break;
case ICellularRadio::RADIO_NETWORK_MODE_UMTS_ONLY: value = "22"; break;
case ICellularRadio::RADIO_NETWORK_MODE_AUTO: value = "25"; break;
default: return FAILURE;
}
std::string sCmd("AT+WS46=");
sCmd += value;
std::string cmdResult = sendCommand(sCmd);
if (cmdResult.find(ICellularRadio::RSP_OK) == std::string::npos) {
printDebug("%s| AT+WS46? returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), cmdResult.c_str());
return FAILURE;
}
return SUCCESS;
}
ICellularRadio::CODE TelitRadio::getIsSimInserted(bool& bData) {
printTrace("%s| Get SIM insertion status", getName().c_str());
std::string sCmd("AT#SIMDET?");
std::string sResult = sendCommand(sCmd);
const std::string sPrefix = "#SIMDET: ";
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#SIMDET? returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), sResult.c_str());
return FAILURE;
}
// #SIMDET: ,
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") { //
bData = true;
} else {
bData = false;
}
return SUCCESS;
}
ICellularRadio::CODE TelitRadio::getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk) {
std::string sLockStatus;
ICellularRadio::CODE retCode;
retCode = getSimLockStatus(sLockStatus);
if (retCode != SUCCESS) {
printWarning("%s| Unable determine the number of SIM unlock attempts: SIM lock status is unavailable [%s]", getName().c_str(), sLockStatus.c_str());
return retCode;
}
return getSimLockAttempts(iAttemptsPin, iAttemptsPuk, sLockStatus);
}
ICellularRadio::CODE TelitRadio::getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk, const std::string& sLockStatus) {
printTrace("%s| Get SIM unlock attempts left", getName().c_str());
std::string sCmd("AT#PCT");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 500);
std::string sValue;
int iValue;
const std::string sPrefix = "#PCT: ";
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#PCT? returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), sResult.c_str());
return FAILURE;
}
// #PCT:
start += sPrefix.size();
sValue = MTS::Text::trim(sResult.substr(start, end-start));
if (!MTS::Text::parse(iValue, sValue)) {
printWarning("%s| Unable to parse SIM unlock attempts from response [%s]", getName().c_str(), sResult.c_str());
return FAILURE;
}
if (sLockStatus == "READY" || sLockStatus == "SIM PIN") {
iAttemptsPin = iValue; // Some PIN attempts left, maximum PUK attempts left
iAttemptsPuk = 10;
} else {
iAttemptsPin = 0; // No PIN attempts left
iAttemptsPuk = iValue;
}
return SUCCESS;
}