mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-24 13:58:00 -05:00
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
182 lines
6.1 KiB
C++
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);
|
|
}
|