Files
MuditaOS/module-cellular/Modem/ATCommon.cpp
Adam Dobrowolski 78a52c0705 [EGD-5451] Added SMS Center SMSC check to OS
Extended cmd mechanism to provide class defined responses and
implemented SMS center verification to check if SIM card is properly
cofigured and is able to send SMS messagess at all.
This check is needed because if SMS Center is lost we are not able
to send SMS messages with no verbose error codes.
Added milliseconds and noexcept specifiers for at commands
Fixed Chanel name to Channel
2021-01-31 11:24:37 +01:00

182 lines
6.1 KiB
C++

// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "ATCommon.hpp"
#include "time/ScopedTime.hpp"
#include <functional>
#include <log/log.hpp>
#include <string>
#include <ticks.hpp>
#include <vector>
#include <inttypes.h> // for PRIu32
#include <Utils.hpp>
using namespace at;
const std::string Channel::OK = "OK";
const std::string Channel::ERROR = "ERROR";
const std::string Channel::NO_CARRIER = "NO CARRIER";
const std::string Channel::BUSY = "BUSY";
const std::string Channel::NO_ANSWER = "NO ANSWER";
const std::string Channel::CME_ERROR = "+CME ERROR:";
const std::string Channel::CMS_ERROR = "+CMS ERROR:";
// const std::string Channel::CONNECT = "CONNECT";
// const std::string Channel::RING = "RING";
// const std::string Channel::NO_DIALTONE = "NO DIALTONE";
Result::Code Channel::at_check(const std::vector<std::string> &arr)
{
if (arr.size()) {
for (auto el : arr) {
if (el.compare(0, OK.length(), OK) == 0) {
return Result::Code::OK;
}
else if (el.compare(0, ERROR.length(), ERROR) == 0) {
return Result::Code::ERROR;
}
}
}
return Result::Code::NONE;
}
bool Channel::at_check_cmx_error(const std::string &CMX, const std::vector<std::string> &arr, uint32_t &errcode)
{
if (arr.size()) {
for (auto cmxerr : arr) {
if (cmxerr.compare(0, CMX.length(), CMX) == 0) {
auto serr = utils::trim(cmxerr.substr(CMX.length(), cmxerr.length() - CMX.length()));
int parsedVal = 0;
auto ret = utils::toNumeric(serr, parsedVal);
errcode = parsedVal;
return ret;
}
}
}
return false;
}
void Channel::cmd_log(std::string cmd, const Result &result, uint32_t timeout)
{
cmd.erase(std::remove(cmd.begin(), cmd.end(), '\r'), cmd.end());
cmd.erase(std::remove(cmd.begin(), cmd.end(), '\n'), cmd.end());
switch (result.code) {
case Result::Code::TIMEOUT: {
LOG_ERROR("[AT]: >%s<, timeout %" PRIu32
" - please check the value with Quectel_EC25&EC21_AT_Commands_Manual_V1.3.pdf",
cmd.c_str(),
timeout);
} break;
case Result::Code::ERROR: {
LOG_ERROR("[AT]: >%s<, >%s<", cmd.c_str(), result.response.size() ? result.response.back().c_str() : "");
} break;
default:
LOG_DEBUG("[AT]: >%s<, >%s<", cmd.c_str(), result.response.size() ? result.response.back().c_str() : "");
break;
}
#if DEBUG_MODEM_OUTPUT_RESPONSE
for (auto s : result.response) {
LOG_DEBUG("[AT] > %s", s.c_str());
}
#endif
}
std::string Channel::formatCommand(const std::string &cmd) const
{
bool isTerminatorValid = std::find(validTerm.begin(), validTerm.end(), cmd.back()) != validTerm.end();
if (isTerminatorValid) {
return cmd;
}
return cmd + cmdSeparator;
}
Result Channel::cmd(const std::string &cmd, std::chrono::milliseconds timeout, size_t rxCount)
{
Result result;
blockedTaskHandle = xTaskGetCurrentTaskHandle();
cmd_init();
std::string cmdFixed = formatCommand(cmd);
cmd_send(cmdFixed);
uint32_t currentTime = cpp_freertos::Ticks::GetTicks();
uint32_t timeoutNeeded = ((timeout.count() == UINT32_MAX) ? UINT32_MAX : currentTime + timeout.count());
uint32_t timeElapsed = currentTime;
while (true) {
if (timeoutNeeded != UINT32_MAX && timeElapsed >= timeoutNeeded) {
result.code = Result::Code::TIMEOUT;
break;
}
auto ret = ulTaskNotifyTake(pdTRUE, timeoutNeeded - timeElapsed);
timeElapsed = cpp_freertos::Ticks::GetTicks();
if (ret != 0u) {
std::vector<std::string> ret = cmd_receive();
result.response.insert(std::end(result.response), std::begin(ret), std::end(ret));
uint32_t errcode = 0;
if (at_check_cmx_error(CME_ERROR, ret, errcode)) {
result.code =
Result::Code::ERROR; // setup error but in this case error from +CME ERROR with valid errorCode
auto tmp_ec = magic_enum::enum_cast<EquipmentErrorCode>(errcode);
if (tmp_ec.has_value()) {
LOG_ERROR("%s", utils::enumToString(tmp_ec.value()).c_str());
result.errorCode = tmp_ec.value();
}
else {
LOG_ERROR("Unknow CME error code %" PRIu32, errcode);
result.errorCode = at::EquipmentErrorCode::Unknown;
}
break;
}
if (at_check_cmx_error(CMS_ERROR, ret, errcode)) {
result.code =
Result::Code::ERROR; // setup error but in this case error from +CME ERROR with valid errorCode
auto atmp_ec = magic_enum::enum_cast<NetworkErrorCode>(errcode);
if (atmp_ec.has_value()) {
LOG_ERROR("%s", utils::enumToString(atmp_ec.value()).c_str());
result.errorCode = atmp_ec.value();
}
else {
LOG_ERROR("Unknow CMS error code %" PRIu32, errcode);
result.errorCode = at::NetworkErrorCode::Unknown;
}
break;
}
result.code = at_check(ret);
if (result.code != Result::Code::NONE) {
break;
}
if (rxCount != 0 && result.response.size() >= rxCount) {
result.code = Result::Code::TOKENS;
break;
}
}
}
blockedTaskHandle = nullptr;
cmd_post();
cmd_log(cmdFixed, result, timeout.count());
return result;
}
auto Channel::cmd(const at::Cmd &at) -> Result
{
auto time = utils::time::Scoped("Time to run at command" + at.getCmd());
return cmd(at.getCmd(), at.getTimeout());
}
auto Channel::cmd(const at::AT &at) -> Result
{
auto cmd = at::factory(at);
auto time = utils::time::Scoped("Time to run at command" + cmd.getCmd());
return this->cmd(cmd);
}