M enabled_unittests => enabled_unittests +4 -0
@@ 222,6 222,10 @@ TESTS_LIST["catch2-unittest_parse_CSCA"]="
CSCA set data;
"
#---------
+TESTS_LIST["catch2-unittest_ATStream"]="
+ Channel Test- AT return parser;
+"
+#---------
TESTS_LIST["catch2-utils"]="
Split tests;
toNumeric tests;
M module-cellular/CMakeLists.txt => module-cellular/CMakeLists.txt +1 -0
@@ 7,6 7,7 @@ include(SerialPort)
set(SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/Modem/ATParser.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Modem/ATStream.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Modem/ATCommon.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Modem/TS0710/DLC_channel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Modem/TS0710/TS0710.cpp
M module-cellular/Modem/ATCommon.cpp => module-cellular/Modem/ATCommon.cpp +11 -76
@@ 10,6 10,7 @@
#include <vector>
#include <inttypes.h> // for PRIu32
#include <Utils.hpp>
+#include "ATStream.hpp"
using namespace at;
@@ 24,37 25,6 @@ const std::string Channel::CMS_ERROR = "+CMS ERROR:";
// 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());
@@ 97,64 67,29 @@ Result Channel::cmd(const std::string &cmd, std::chrono::milliseconds timeout, s
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;
+ ATStream atStream(rxCount);
+
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 taskUnlocked = ulTaskNotifyTake(pdTRUE, timeoutNeeded - timeElapsed);
+ timeElapsed = cpp_freertos::Ticks::GetTicks();
- auto atmp_ec = magic_enum::enum_cast<NetworkErrorCode>(errcode);
+ if (taskUnlocked) {
+ atStream.write(cmd_receive());
- 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;
+ if (atStream.isReady()) {
+ result = atStream.getResult();
break;
}
}
@@ 175,7 110,7 @@ auto Channel::cmd(const at::Cmd &at) -> Result
auto Channel::cmd(const at::AT &at) -> Result
{
- auto cmd = at::factory(at);
+ auto cmd = at::factory(at);
auto time = utils::time::Scoped("Time to run at command" + cmd.getCmd());
return this->cmd(cmd);
}
M module-cellular/Modem/ATCommon.hpp => module-cellular/Modem/ATCommon.hpp +2 -4
@@ 16,6 16,8 @@ namespace sys
namespace at
{
+ constexpr auto delimiter = "\r\n"; /// use std::strlen()
+
class Channel : public BaseChannel
{
protected:
@@ 45,10 47,6 @@ namespace at
size_t rxCount = 0) -> Result final;
virtual auto cmd(const at::AT &at) -> Result final;
virtual auto cmd(const at::Cmd &at) -> Result final;
- /// check for OK, ERROR in string in last token
- virtual Result::Code at_check(const std::vector<std::string> &arr);
- /// check for +CME ERROR: errors if exists in last token
- virtual bool at_check_cmx_error(const std::string &CMX, const std::vector<std::string> &arr, uint32_t &errcode);
virtual void cmd_log(std::string cmd, const Result &result, uint32_t timeout) final;
virtual auto ProcessNewData(sys::Service *service) -> int
{
M module-cellular/Modem/ATParser.cpp => module-cellular/Modem/ATParser.cpp +2 -2
@@ 113,10 113,10 @@ void ATParser::cmd_send(std::string cmd)
cellular->Write(const_cast<char *>(cmd.c_str()), cmd.size());
}
-std::vector<std::string> ATParser::cmd_receive()
+std::string ATParser::cmd_receive()
{
cpp_freertos::LockGuard lock(mutex);
- return utils::split(responseBuffer, "\r\n");
+ return responseBuffer;
}
void ATParser::cmd_post()
M module-cellular/Modem/ATParser.hpp => module-cellular/Modem/ATParser.hpp +1 -1
@@ 38,7 38,7 @@ class ATParser : public at::Channel
virtual void cmd_init() override final;
virtual void cmd_send(std::string cmd) override final;
- virtual std::vector<std::string> cmd_receive() override final;
+ virtual std::string cmd_receive() override final;
virtual void cmd_post() override final;
private:
A module-cellular/Modem/ATStream.cpp => module-cellular/Modem/ATStream.cpp +214 -0
@@ 0,0 1,214 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "ATStream.hpp"
+
+#include "ATCommon.hpp"
+namespace at
+{
+
+ Result::Code ATStream::parseState(const std::string &state, uint32_t &errcode)
+ {
+ const std::map<std::string, Result::Code> convert_map = {
+ {Channel::OK, Result::Code::OK},
+ {Channel::ERROR, Result::Code::ERROR},
+ {Channel::CME_ERROR, Result::Code::CME_ERROR},
+ {Channel::CMS_ERROR, Result::Code::CMS_ERROR},
+
+ };
+
+ auto it = convert_map.find(state);
+ if (it != convert_map.end()) {
+ if ((it->second == Result::Code::CME_ERROR) || (it->second == Result::Code::CMS_ERROR)) {
+ return Result::Code::PARSING_ERROR;
+ }
+ return it->second;
+ }
+
+ auto cmxErrorTest = state.substr(0, Channel::CME_ERROR.length()); /// same length for CMS
+
+ it = convert_map.find(cmxErrorTest);
+ if ((it != convert_map.end()) &&
+ ((it->second == Result::Code::CME_ERROR) || (it->second == Result::Code::CMS_ERROR))) {
+ auto serr =
+ utils::trim(state.substr(Channel::CME_ERROR.length(), state.length() - Channel::CME_ERROR.length()));
+ if (serr.length() == 0) {
+ return Result::Code::PARSING_ERROR;
+ }
+ int parsedVal = 0;
+ auto ret = utils::toNumeric(serr, parsedVal);
+ if (!ret) {
+ return Result::Code::PARSING_ERROR;
+ }
+ errcode = parsedVal;
+ return it->second;
+ }
+
+ return Result::Code::NONE;
+ }
+
+ void ATStream::checkError()
+ {
+ if (result.code == Result::Code::CME_ERROR) {
+ 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;
+ }
+ }
+
+ if (result.code == Result::Code::CMS_ERROR) {
+ 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;
+ }
+ }
+ }
+
+ bool ATStream::checkATBegin()
+ {
+ auto pos = atBuffer.find(at::delimiter, std::strlen(at::delimiter));
+ if (pos != std::string::npos) {
+ beginChecked = true;
+ std::string rr = atBuffer.substr(std::strlen(at::delimiter), pos - std::strlen(at::delimiter)).c_str();
+ auto code = parseState(rr, errcode);
+ if (code != Result::Code::NONE) {
+ result.code = code;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool ATStream::checkATEnd()
+ {
+ auto pos = atBuffer.rfind(at::delimiter);
+ if (pos != std::string::npos) {
+ auto pos2 = atBuffer.rfind(at::delimiter, pos - std::strlen(at::delimiter));
+ if (pos2 != std::string::npos) {
+ std::string rr =
+ atBuffer.substr(pos2 + std::strlen(at::delimiter), pos - pos2 - std::strlen(at::delimiter)).c_str();
+ auto code = parseState(rr, errcode);
+ if (code != Result::Code::NONE) {
+ result.code = code;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void ATStream::countLines()
+ {
+ if (rxCount != 0) {
+ auto pos = atBuffer.find(at::delimiter, lastPos);
+ while (pos != std::string::npos) {
+ /// do not count empty lines, see implementation of utils:split
+ if ((lastPos) != pos) {
+ lineCounter++;
+ }
+ lastPos = pos + std::strlen(at::delimiter);
+ pos = atBuffer.find(at::delimiter, lastPos);
+ }
+ }
+ }
+
+ bool ATStream::parse()
+ {
+ /// check for return at the begin eg <cr<lf>X<cr><lf>
+ if ((!beginChecked) && (atBuffer.length() > minATCmdRet) && (atBuffer[0] == '\r') && (atBuffer[1] == '\n')) {
+ if (checkATBegin()) {
+ atAtTheBegin = true;
+ /**
+ * This is a compromise between the certainty of getting all the elements and the waiting time.
+ * In case if we always waited, the waiting time would be equal timeout.
+ * In this case, we wait for a specified number of commands,
+ * the wrong one may result in returning incorrect / incomplete values.
+ * It only applies to a group of specific commands like AT + QPING, in which the result (OK)
+ * is returned at the beginning, followed by information. This could happen only if we not fit
+ * in one return of cmd_receive return.
+ */
+ if (rxCount == 0) {
+ return true;
+ }
+ }
+ }
+ countLines();
+ /**
+ * Check for pattern <cr><lf>XXX<cr><lf> at the end or check counted lines
+ * XXX must be one of AT return codes
+ */
+ if (atAtTheBegin) {
+ if (rxCount <= (lineCounter)) {
+ return true;
+ }
+ }
+ else {
+ if (checkATEnd()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ *
+ * @param buffer
+ * @return false if could not write (mean ready). Reset to write again
+ */
+ bool ATStream::write(const std::string &buffer)
+ {
+ if (isATReady) {
+ return false;
+ }
+ atBuffer += buffer;
+ if (parse()) {
+ isATReady = true;
+
+ auto fullRet = utils::split(atBuffer, at::delimiter);
+ result.response.insert(std::end(result.response), std::begin(fullRet), std::end(fullRet));
+
+ checkError();
+
+ if (rxCount != 0 && result.response.size() > rxCount) {
+ result.code = Result::Code::TOKENS;
+ }
+ return false;
+ }
+ else {
+
+ return true;
+ }
+ }
+
+ void ATStream::reset()
+ {
+ result.code = Result::Code::NONE;
+ result.response.clear();
+ result.errorCode = at::EquipmentErrorCode::NoInformation;
+ atAtTheBegin = false;
+ beginChecked = false;
+ lastPos = 0;
+ lineCounter = 0;
+ atBuffer = {};
+ isATReady = false;
+ errcode = 0;
+ }
+} // namespace at<
\ No newline at end of file
A module-cellular/Modem/ATStream.hpp => module-cellular/Modem/ATStream.hpp +52 -0
@@ 0,0 1,52 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <at/ErrorCode.hpp>
+#include <at/Result.hpp>
+
+namespace at
+{
+ constexpr auto minATCmdRet = 5; /// minimum return from AT in command mode "<cr<lf>X<cr><lf>"
+
+ class ATStream
+ {
+
+ private:
+ at::Result result;
+ bool atAtTheBegin = false; /// case for commands like QPING
+ bool beginChecked = false;
+ size_t lastPos = 0;
+ size_t lineCounter = 0;
+ size_t rxCount = 0;
+ std::string atBuffer = {};
+ bool isATReady = false;
+ uint32_t errcode = 0;
+
+ protected:
+ bool checkATBegin();
+ bool checkATEnd();
+ void checkError();
+ bool parse();
+ void countLines();
+
+ public:
+ ATStream(size_t rxCount = 0) : rxCount(rxCount)
+ {}
+
+ bool write(const std::string &buffer);
+ bool isReady()
+ {
+ return isATReady;
+ }
+ void reset();
+ at::Result getResult()
+ {
+ return result;
+ }
+
+ Result::Code parseState(const std::string &state, uint32_t &errcode);
+ };
+
+} // namespace at
M module-cellular/Modem/BaseChannel.hpp => module-cellular/Modem/BaseChannel.hpp +1 -1
@@ 30,7 30,7 @@ namespace at
{}
virtual void cmd_init() = 0;
virtual void cmd_send(std::string cmd) = 0;
- virtual std::vector<std::string> cmd_receive() = 0;
+ virtual std::string cmd_receive() = 0;
virtual void cmd_post() = 0;
/// }
};
M module-cellular/Modem/TS0710/DLC_channel.cpp => module-cellular/Modem/TS0710/DLC_channel.cpp +2 -2
@@ 99,7 99,7 @@ void DLC_channel::cmd_send(std::string cmd)
SendData(data);
}
-std::vector<std::string> DLC_channel::cmd_receive()
+std::string DLC_channel::cmd_receive()
{
cpp_freertos::LockGuard lock(mutex);
TS0710_Frame::frame_t frame;
@@ 128,7 128,7 @@ std::vector<std::string> DLC_channel::cmd_receive()
deserialisedData += str;
}
mFrames.clear();
- return utils::split(deserialisedData, "\r\n");
+ return deserialisedData;
}
void DLC_channel::cmd_post()
M module-cellular/Modem/TS0710/DLC_channel.h => module-cellular/Modem/TS0710/DLC_channel.h +1 -1
@@ 69,7 69,7 @@ class DLC_channel : public at::Channel
virtual void cmd_init() override final;
virtual void cmd_send(std::string cmd) override final;
- virtual std::vector<std::string> cmd_receive() override final;
+ virtual std::string cmd_receive() override final;
virtual void cmd_post() override final;
std::vector<std::string> SendCommandPrompt(const char *cmd, size_t rxCount, uint32_t timeout = 300);
A module-cellular/Modem/doc/ATStream.md => module-cellular/Modem/doc/ATStream.md +36 -0
@@ 0,0 1,36 @@
+
+
+# Channel implementation
+
+## Table of contents
+1. [History](#history)
+2. [Scope](#scope)
+3. [Current implementation](#currentimpl)
+
+
+## History <a name="history"></a>
+
+| Authors | Change description | Status | Modification date |
+| ----------------- | ------------------------- | ------ | ----------------- |
+| Bartosz Reichel | Initial version | Draft | 2021.02.10 |
+
+
+## Scope <a name="scope"></a>
+Description of dependencies for channel and ATStream usage
+
+
+
+## Current implementation <a name="currentimpl"></a>
+
+
+
+
+When initializing the modem, communication is direct via the UART channel through the ATParser channel (AT commands).
+At CMUX configuration, the channel for AT commands is represented by DLC_Channel (see Channel::Commands and also
+Channel::Notifications and Channel::Data).
+
+Both ATParser and DLC_Channel use ATStream (in Channel) for parsing AT command. ATStream parse AT commands in terms of correctness /return (not the selected command)
+
+below way of how ATStream parse command (especially such strange like QPING)
+
+<
\ No newline at end of file
A module-cellular/Modem/doc/Images/atstream.png => module-cellular/Modem/doc/Images/atstream.png +0 -0
A module-cellular/Modem/doc/Images/class_channel.png => module-cellular/Modem/doc/Images/class_channel.png +0 -0
A module-cellular/Modem/doc/Images/current_volte_on.png => module-cellular/Modem/doc/Images/current_volte_on.png +0 -0
A module-cellular/Modem/doc/Images/mudita_logo.png => module-cellular/Modem/doc/Images/mudita_logo.png +0 -0
A module-cellular/Modem/doc/Images/volte_on.png => module-cellular/Modem/doc/Images/volte_on.png +0 -0
A module-cellular/Modem/doc/scripts/atstream.pu => module-cellular/Modem/doc/scripts/atstream.pu +49 -0
@@ 0,0 1,49 @@
+@startuml
+
+start
+
+
+:pass data to ATStream via write;
+
+if (isReady) then
+ :return error (could be call reset, to get new data);
+ Stop;
+endif
+
+:add new data to buffer;
+
+
+ if (AT Return at the begin) then (yes)
+ :Set "Return at the begin" flag;
+ if (Expected line count= 0) then (yes)
+ :(isReady=true);
+ Stop;
+ endif
+ else (no)
+ endif
+
+ :Count lines from last position;
+
+ if ("Return at the begin" flag is set) then (yes)
+ if (More lines then expected) then (yes)
+ :(isReady=true);
+ Stop;
+ endif
+ else (no)
+ if (AT Return at the end) then (yes)
+ :(isReady=true);
+ Stop;
+ endif
+ endif
+
+ :Stream still wait for data (isReady=false);
+ Stop;
+
+
+
+
+
+
+
+
+@enduml<
\ No newline at end of file
A module-cellular/Modem/doc/scripts/class_channel.pu => module-cellular/Modem/doc/scripts/class_channel.pu +12 -0
@@ 0,0 1,12 @@
+@startuml
+
+abstract class BaseChannel
+
+BaseChannel <|-- Channel
+
+Channel ..> ATStream: Channel use ATStream
+
+Channel <|-- DLC_channel
+Channel <|-- ATParser
+
+@enduml<
\ No newline at end of file
M module-cellular/at/Result.hpp => module-cellular/at/Result.hpp +3 -1
@@ 17,7 17,9 @@ namespace at
enum class Code
{
OK, /// at OK
- ERROR, /// at ERROR
+ ERROR, /// at ERROR For compatibility also for CME_ERROR and CMS_ERROR (details in errorCode)
+ CME_ERROR, /// In case CME error see errorCode
+ CMS_ERROR, /// In case CMS error see errorCode
TIMEOUT, /// at Timeout
TOKENS, /// at numbers of tokens needed met
NONE, /// no code
M module-cellular/test/CMakeLists.txt => module-cellular/test/CMakeLists.txt +9 -0
@@ 28,3 28,12 @@ add_catch2_executable(
module-cellular
)
+
+add_catch2_executable(
+ NAME
+ unittest_ATStream
+ SRCS
+ unittest_ATStream.cpp
+ LIBS
+ module-cellular
+)
M module-cellular/test/mock/AtCommon_channel.hpp => module-cellular/test/mock/AtCommon_channel.hpp +2 -2
@@ 3,11 3,11 @@
#pragma once
+#include "Modem/ATCommon.hpp"
#include "Modem/BaseChannel.hpp"
namespace at
{
-
class ChannelMock : public BaseChannel
{
public:
@@ 40,7 40,7 @@ namespace at
void cmd_post() override
{}
- std::vector<std::string> cmd_receive() override
+ std::string cmd_receive() override
{
return {};
}
A module-cellular/test/unittest_ATStream.cpp => module-cellular/test/unittest_ATStream.cpp +95 -0
@@ 0,0 1,95 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <string>
+#define CATCH_CONFIG_MAIN
+
+#include <catch2/catch.hpp>
+#include "Modem/ATStream.hpp"
+#include "Result.hpp"
+
+TEST_CASE("Channel Test- AT return parser")
+{
+ SECTION("Parse AT - OK")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("OK", errcode);
+ REQUIRE(code == at::Result::Code::OK);
+ }
+
+ SECTION("Parse AT - ERROR")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("ERROR", errcode);
+ REQUIRE(code == at::Result::Code::ERROR);
+ }
+
+ SECTION("Parse AT - +CME ERROR: Valid")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("+CME ERROR: 33", errcode);
+ REQUIRE(code == at::Result::Code::CME_ERROR);
+ REQUIRE(errcode == 33);
+ }
+
+ SECTION("Parse AT - +CMS ERROR: Valid")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("+CMS ERROR: 33", errcode);
+ REQUIRE(code == at::Result::Code::CMS_ERROR);
+ REQUIRE(errcode == 33);
+ }
+
+ SECTION("Parse AT - +CMS ERROR: Invalid")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("+CMS ERROR: ssss", errcode);
+ REQUIRE(code == at::Result::Code::PARSING_ERROR);
+ }
+
+ SECTION("Parse AT - +CMS ERROR: Invalid empty")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("+CMS ERROR:", errcode);
+ REQUIRE(code == at::Result::Code::PARSING_ERROR);
+ }
+
+ SECTION("Parse AT - +CME ERROR: Valid")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("+CME ERROR: 33", errcode);
+ REQUIRE(code == at::Result::Code::CME_ERROR);
+ REQUIRE(errcode == 33);
+ }
+
+ SECTION("Parse AT - +CME ERROR: Invalid")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("+CME ERROR: ssss", errcode);
+ REQUIRE(code == at::Result::Code::PARSING_ERROR);
+ }
+
+ SECTION("Parse AT - +CME ERROR: Invalid empty")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("+CME ERROR:", errcode);
+ REQUIRE(code == at::Result::Code::PARSING_ERROR);
+ }
+
+ SECTION("Parse AT - Wrong data")
+ {
+ at::ATStream stream;
+ uint32_t errcode = 0;
+ auto code = stream.parseState("+XME ERROR:", errcode);
+ REQUIRE(code == at::Result::Code::NONE);
+ }
+}