// 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 #include #include #include #include #include // for PRIu32 #include using namespace at; const std::string Chanel::OK = "OK"; const std::string Chanel::ERROR = "ERROR"; const std::string Chanel::NO_CARRIER = "NO CARRIER"; const std::string Chanel::BUSY = "BUSY"; const std::string Chanel::NO_ANSWER = "NO ANSWER"; const std::string Chanel::CME_ERROR = "+CME ERROR:"; const std::string Chanel::CMS_ERROR = "+CMS ERROR:"; // const std::string Chanel::CONNECT = "CONNECT"; // const std::string Chanel::RING = "RING"; // const std::string Chanel::NO_DIALTONE = "NO DIALTONE"; Result::Code Chanel::at_check(const std::vector &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 Chanel::at_check_cmx_error(const std::string &CMX, const std::vector &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 Chanel::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 Chanel::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; } class Result Chanel::cmd(const std::string cmd, uint32_t 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 == UINT32_MAX) ? UINT32_MAX : currentTime + timeout); uint32_t timeElapsed = currentTime; while (1) { 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) { std::vector 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(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(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); return result; } auto Chanel::cmd(at::Cmd &at) -> Result { at.last.requested = cpp_freertos::Ticks::GetTicks(); Result result = this->cmd(at.cmd, at.timeout); at.last.response = cpp_freertos::Ticks::GetTicks(); at.last.status = result.code; return result; } auto Chanel::cmd(const at::AT at) -> Result { auto cmd = at::factory(at); return this->cmd(cmd); }