M enabled_unittests => enabled_unittests +9 -0
@@ 77,6 77,15 @@ TESTS_LIST["catch2-cellular-URC"]="
+Qiurc: TCP Context and connection message;
"
#---------
+TESTS_LIST["catch2-cellular-parse-result"]="
+ CSCA parser test;
+ CSCA set data;
+ QECCNUM parser;
+ CLCC parser;
+ CLCC set data;
+ CLCC conversion methods;
+"
+#---------
TESTS_LIST["catch2-commands-queue-tests"]="
DrawCommandsQueueTests;
"
M module-cellular/CMakeLists.txt => module-cellular/CMakeLists.txt +1 -0
@@ 41,6 41,7 @@ set(SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/at/response.cpp
${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/CSCA.cpp
${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/QECCNUM.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/CLCC.cpp
)
if(NOT ${PROJECT_TARGET} STREQUAL "TARGET_Linux")
M module-cellular/at/Cmd.hpp => module-cellular/at/Cmd.hpp +2 -0
@@ 33,6 33,8 @@ namespace at
private:
std::string cmd; /// command head to run (AT, CLCC etc...)
std::chrono::milliseconds timeout = default_timeout; /// timeout for this command
+ void split(const std::string &str, Result &result) const;
+
protected:
std::unique_ptr<Result> result; /// lifetime result storage to be able to return reference to it
cmd::Modifier mod = cmd::Modifier::None; /// modifier responsible to define action we want to perform
M module-cellular/at/Result.hpp => module-cellular/at/Result.hpp +1 -0
@@ 42,6 42,7 @@ namespace at
std::variant<at::EquipmentErrorCode, at::NetworkErrorCode> errorCode = at::EquipmentErrorCode::NoInformation;
std::vector<std::string> response;
+ std::vector<std::vector<std::string>> tokens;
virtual explicit operator bool() const
{
A module-cellular/at/cmd/CLCC.hpp => module-cellular/at/cmd/CLCC.hpp +92 -0
@@ 0,0 1,92 @@
+// 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 "Result.hpp"
+#include <at/Cmd.hpp>
+#include <PhoneNumber.hpp>
+
+namespace ModemCall
+{
+ enum class CallState : uint8_t
+ {
+ Active = 0, // 0 active: call in progress (setup was successful)
+ Held, // 1 held: call on hold
+ Dialing, // 2 dialing (MO call): number dialed
+ Alerting, // 3 alerting (MO call): number dialed and the called party is alerted
+ Incoming, // 4 incoming (MT call): incoming call, ringtone played (AT RING notification)
+ Waiting // 5 waiting (MT call): call waiting notification while another call is active (if call waiting feature
+ // enabled)
+ };
+
+ enum class CallDir : uint8_t
+ {
+ MO = 0, // Mobile originated (MO) call
+ MT = 1 // Mobile terminated (MT) call
+ };
+
+ enum class CallMode : uint8_t
+ {
+ Voice = 0,
+ Data,
+ FAX
+ };
+} // namespace ModemCall
+
+namespace at
+{
+ namespace cmd
+ {
+ class CLCC;
+ } // namespace cmd
+
+ namespace result
+ {
+ /// please see documentation:
+ /// QuectelEC2526EC21ATCommandsManualV13.1100970659
+ /// page: 101 for more information
+ struct CLCC : public Result
+ {
+ private:
+ struct Data
+ {
+ const std::uint8_t idx;
+ const ModemCall::CallDir dir;
+ const ModemCall::CallState stateOfCall;
+ const ModemCall::CallMode mode;
+ const bool multiparty;
+ const utils::PhoneNumber::View number;
+ const std::string type;
+ const std::string alpha;
+ const std::size_t tokens;
+ };
+
+ std::vector<Data> data;
+ friend cmd::CLCC;
+
+ public:
+ explicit CLCC(const Result &);
+ [[nodiscard]] auto getData() const noexcept -> const std::vector<Data> &
+ {
+ return data;
+ };
+ };
+ } // namespace result
+
+ namespace cmd
+ {
+ class CLCC : public Cmd
+ {
+ protected:
+ [[nodiscard]] static auto toBool(const std::string &text) -> bool;
+ [[nodiscard]] static auto toUInt(const std::string &text) -> std::uint8_t;
+ template <typename T>[[nodiscard]] static auto toEnum(const std::string &text) -> std::optional<T>;
+
+ public:
+ CLCC() noexcept;
+ explicit CLCC(at::cmd::Modifier mod) noexcept;
+ [[nodiscard]] auto parse(Result &base_result) -> result::CLCC & final;
+ };
+ } // namespace cmd
+} // namespace at
A module-cellular/at/cmd/src/CLCC.cpp => module-cellular/at/cmd/src/CLCC.cpp +103 -0
@@ 0,0 1,103 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <at/cmd/CLCC.hpp>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <service-cellular/CellularCall.hpp>
+
+namespace at
+{
+ namespace cmd
+ {
+ CLCC::CLCC(at::cmd::Modifier mod) noexcept : Cmd{"AT+CLCC", mod}
+ {}
+
+ CLCC::CLCC() noexcept : CLCC{at::cmd::Modifier::None}
+ {}
+
+ auto CLCC::toBool(const std::string &text) -> bool
+ {
+ int ret = -1;
+ if (!utils::toNumeric(text, ret) || ret == -1) {
+ throw std::runtime_error("String to bool conversion failed");
+ }
+ return ret == 1 ? true : false;
+ }
+
+ auto CLCC::toUInt(const std::string &text) -> std::uint8_t
+ {
+ auto value = std::stoul(text);
+ if (value > std::numeric_limits<std::uint8_t>::max()) {
+ throw std::runtime_error("Out of range value");
+ }
+ return static_cast<std::uint8_t>(value);
+ }
+
+ template <typename T> auto CLCC::toEnum(const std::string &text) -> std::optional<T>
+ {
+ static_assert(std::is_enum_v<T>);
+ int ret = -1;
+ if (!utils::toNumeric(text, ret) || ret == -1) {
+ return std::nullopt;
+ }
+
+ return magic_enum::enum_cast<T>(ret);
+ }
+
+ result::CLCC &CLCC::parse(Result &base_result)
+ {
+ using namespace std::literals;
+ auto &baseResult = Cmd::parse(base_result);
+ auto p = new result::CLCC(baseResult);
+ result = std::unique_ptr<result::CLCC>(p);
+
+ auto parseErrorHandler = [&p]() -> result::CLCC & {
+ LOG_ERROR("Parsing error - invalid parameter passed");
+ p->code = result::CLCC::Code::PARSING_ERROR;
+ return *p;
+ };
+ if (p && p->tokens.size() > 0) {
+ for (const auto &tokens : p->tokens) {
+ auto numberOfTokens = tokens.size();
+ if (numberOfTokens != 7 && numberOfTokens != 8) {
+ LOG_ERROR("Can't parse - number of tokens %u is not valid",
+ static_cast<unsigned int>(result->tokens.size()));
+ p->code = result::CLCC::Code::PARSING_ERROR;
+ return *p;
+ }
+
+ try {
+ const auto callDir = toEnum<ModemCall::CallDir>(tokens[1]);
+ const auto callState = toEnum<ModemCall::CallState>(tokens[2]);
+ const auto callMode = toEnum<ModemCall::CallMode>(tokens[3]);
+ if (!callDir.has_value() || !callState.has_value() || !callMode.has_value()) {
+ return parseErrorHandler();
+ }
+ p->data.push_back(result::CLCC::Data{toUInt(tokens[0]),
+ callDir.value(),
+ callState.value(),
+ callMode.value(),
+ toBool(tokens[4]),
+ utils::PhoneNumber(tokens[5]).getView(),
+ tokens[6],
+ (numberOfTokens == 8) ? tokens[7] : "",
+ numberOfTokens});
+ }
+ catch (...) {
+ return parseErrorHandler();
+ }
+ }
+ }
+ return *p;
+ }
+ } // namespace cmd
+
+ namespace result
+ {
+ CLCC::CLCC(const Result &result) : Result{result}
+ {}
+
+ } // namespace result
+} // namespace at
M module-cellular/at/src/Cmd.cpp => module-cellular/at/src/Cmd.cpp +45 -2
@@ 1,7 1,8 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
-#include <Cmd.hpp>
+#include "Cmd.hpp"
+#include <algorithm>
namespace at
{
@@ 22,6 23,20 @@ namespace at
*this = operator=(p);
}
+ void Cmd::split(const std::string &str, Result &result) const
+ {
+ constexpr char tokenDelimiter = ',';
+
+ result.tokens.push_back(utils::split(str, tokenDelimiter));
+ constexpr auto urcStringDelimiter = "\"";
+ for (auto &arr : result.tokens) {
+ for (auto &t : arr) {
+ utils::findAndReplaceAll(t, urcStringDelimiter, "");
+ t = utils::trim(t);
+ }
+ }
+ }
+
auto Cmd::operator=(const Cmd &p) noexcept -> Cmd &
{
if (&p == this) {
@@ 48,7 63,35 @@ namespace at
Result &Cmd::parse(Result &that)
{
- result = std::unique_ptr<Result>();
+ result = std::make_unique<Result>(that);
+ if (result->code != Result::Code::OK) {
+ return *result;
+ }
+ else if (result->response.empty()) {
+ LOG_ERROR("Can't parse - empty response");
+ result->code = Result::Code::PARSING_ERROR;
+ return *result;
+ }
+ else if (result->response.size() == 1) {
+ if (result->response[0] == "OK") {
+ LOG_INFO("OK response");
+ result->code = Result::Code::OK;
+ return *result;
+ }
+ LOG_ERROR("Can't parse - response not valid");
+ result->code = Result::Code::ERROR;
+ return *result;
+ }
+ const char headDelimiter = ':';
+ const auto atResponse = result->response;
+ const auto lastPosition = atResponse.end() - 1;
+ for (auto it = atResponse.begin(); it != lastPosition; it++) {
+ auto prefixIter = std::find(it->begin(), it->end(), headDelimiter);
+ auto head = std::string(it->begin(), prefixIter);
+ auto body = std::string(prefixIter == it->end() ? it->begin() : prefixIter + 1, it->end());
+ split(body, *result);
+ }
+
return *result;
}
} // namespace at
M module-cellular/test/CMakeLists.txt => module-cellular/test/CMakeLists.txt +0 -1
@@ 28,7 28,6 @@ add_catch2_executable(
module-cellular
)
-
add_catch2_executable(
NAME
unittest_ATStream
M module-cellular/test/mock/AtCommon_channel.hpp => module-cellular/test/mock/AtCommon_channel.hpp +81 -0
@@ 111,4 111,85 @@ namespace at
return r;
}
};
+
+ /// provides proper CLCC response with one call on the list
+ class CLCC_successChannel_oneCall : public ChannelMock
+ {
+ public:
+ const std::string idx = "1";
+ const std::string dir = "0";
+ const std::string stateOfCall = "0";
+ const std::string mode = "0";
+ const std::string multiparty = "1";
+ const std::string number = "10086";
+ const std::string type = "129";
+
+ auto ResultMock() -> Result final
+ {
+ auto result = Result();
+ result.code = Result::Code::OK;
+ result.response = {"+CLCC: " + idx + "," + dir + "," + stateOfCall + "," + mode + "," + multiparty + "," +
+ number + "," + type,
+ "OK"};
+ return result;
+ }
+ };
+
+ /// provides proper CLCC response with two calls on the list
+ class CLCC_successChannel_twoCalls : public ChannelMock
+ {
+ public:
+ const std::string idx1{"1"};
+ const std::string dir1{"0"};
+ const std::string stateOfCall1{"0"};
+ const std::string mode1{"1"};
+ const std::string multiparty1{"0"};
+ const std::string number1{""};
+ const std::string type1{"129"};
+
+ const std::string idx2{"2"};
+ const std::string dir2{"0"};
+ const std::string stateOfCall2{"0"};
+ const std::string mode2{"0"};
+ const std::string multiparty2{"0"};
+ const std::string number2{"10086"};
+ const std::string type2{"129"};
+
+ const std::string tokensLTEMode{"1,0,0,1,0,\"\",129"};
+ const std::string tokensEstablishCall{"2,0,0,0,0,\"10086\",129"};
+
+ auto ResultMock() -> Result final
+ {
+ auto result = Result();
+ result.code = Result::Code::OK;
+ result.response = {"+CLCC: " + tokensLTEMode, "+CLCC: " + tokensEstablishCall, "OK"};
+ return result;
+ }
+ };
+
+ // Provides succesfull 'OK' response
+ class OK_Channel : public ChannelMock
+ {
+ public:
+ auto ResultMock() -> Result final
+ {
+ auto result = Result();
+ result.code = Result::Code::OK;
+ result.response = {"OK"};
+ return result;
+ }
+ };
+
+ // Provides bad 'OG' response
+ class OG_Channel : public ChannelMock
+ {
+ public:
+ auto ResultMock() -> Result final
+ {
+ auto result = Result();
+ result.code = Result::Code::OK;
+ result.response = {"OG"};
+ return result;
+ }
+ };
} // namespace at
M module-cellular/test/unittest_parse_result.cpp => module-cellular/test/unittest_parse_result.cpp +189 -0
@@ 6,6 6,7 @@
#include <catch2/catch.hpp>
+#include <at/cmd/CLCC.hpp>
#include <at/cmd/CSCA.hpp>
#include <at/cmd/QECCNUM.hpp>
@@ 13,6 14,26 @@
#include "PhoneNumber.hpp"
#include "Result.hpp"
+namespace at::cmd
+{
+ struct CLCCStub : public CLCC
+ {
+ [[nodiscard]] static auto toBool(const std::string &text) -> bool
+ {
+ return CLCC::toBool(text);
+ }
+ [[nodiscard]] static auto toUInt(const std::string &text) -> std::uint8_t
+ {
+ return CLCC::toUInt(text);
+ }
+
+ template <typename T>[[nodiscard]] static auto toEnum(const std::string &text) -> std::optional<T>
+ {
+ return CLCC::toEnum<T>(text);
+ }
+ };
+} // namespace at::cmd
+
TEST_CASE("CSCA parser test")
{
SECTION("empty failed data")
@@ 149,3 170,171 @@ TEST_CASE("QECCNUM parser")
REQUIRE(cmdAddSim.getCmd() == "AT+QECCNUM=1,0,\"600800900\",\"112\"");
}
}
+
+TEST_CASE("CLCC parser")
+{
+ using namespace std::string_literals;
+ SECTION("Empty data")
+ {
+ at::cmd::CLCC cmd;
+ at::Result result;
+ auto response = cmd.parse(result);
+ REQUIRE(!response);
+ }
+ SECTION("Failing channel")
+ {
+ at::cmd::CLCC cmd;
+ at::FailingChannel channel;
+ auto base = channel.cmd(cmd);
+ auto response = cmd.parse(base);
+ REQUIRE(!response);
+ REQUIRE(response.code == at::Result::Code::ERROR);
+ }
+ SECTION("Success - one call")
+ {
+ at::cmd::CLCC cmd;
+ at::CLCC_successChannel_oneCall channel;
+ auto base = channel.cmd(cmd);
+ auto response = cmd.parse(base);
+ REQUIRE(response);
+ auto [idx, dir, stateOfCall, mode, multiparty, number, type, alpha, tokens] = response.getData()[0];
+ REQUIRE(idx == 1);
+ REQUIRE(dir == ModemCall::CallDir::MO);
+ REQUIRE(stateOfCall == ModemCall::CallState::Active);
+ REQUIRE(mode == ModemCall::CallMode::Voice);
+ REQUIRE(multiparty == true);
+ REQUIRE(number == utils::PhoneNumber(channel.number).getView());
+ REQUIRE(type == "129"s);
+ REQUIRE(alpha == "");
+ }
+
+ SECTION("Success - two calls")
+ {
+ at::cmd::CLCC cmd;
+ at::CLCC_successChannel_twoCalls channel;
+ auto base = channel.cmd(cmd);
+ auto response = cmd.parse(base);
+ REQUIRE(response);
+ SECTION("First entry")
+ {
+ auto [idx, dir, stateOfCall, mode, multiparty, number, type, alpha, tokens] = response.getData()[0];
+ REQUIRE(idx == 1);
+ REQUIRE(dir == ModemCall::CallDir::MO);
+ REQUIRE(stateOfCall == ModemCall::CallState::Active);
+ REQUIRE(mode == ModemCall::CallMode::Data);
+ REQUIRE(multiparty == false);
+ REQUIRE(number == utils::PhoneNumber(channel.number1).getView());
+ REQUIRE(type == "129"s);
+ REQUIRE(alpha == "");
+ }
+
+ SECTION("Second entry")
+ {
+ auto [idx, dir, stateOfCall, mode, multiparty, number, type, alpha, tokens] = response.getData()[1];
+ REQUIRE(idx == 2);
+ REQUIRE(dir == ModemCall::CallDir::MO);
+ REQUIRE(stateOfCall == ModemCall::CallState::Active);
+ REQUIRE(mode == ModemCall::CallMode::Voice);
+ REQUIRE(multiparty == false);
+ REQUIRE(number == utils::PhoneNumber(channel.number2).getView());
+ REQUIRE(type == "129"s);
+ REQUIRE(alpha == "");
+ }
+ }
+
+ SECTION("Failed - bad channel result")
+ {
+ at::cmd::CLCC cmd;
+ at::CSCS_badChannel channel;
+ auto base = channel.cmd(cmd);
+ auto resp = cmd.parse(base);
+ REQUIRE(!resp);
+ REQUIRE(resp.code == at::Result::Code::ERROR);
+ }
+
+ SECTION("Success - only OK data")
+ {
+ at::cmd::CLCC cmd;
+ at::OK_Channel channel;
+ auto base = channel.cmd(cmd);
+ auto response = cmd.parse(base);
+ REQUIRE(response);
+ }
+
+ SECTION("Failed - only OG data")
+ {
+ at::cmd::CLCC cmd;
+ at::OG_Channel channel;
+ auto base = channel.cmd(cmd);
+ auto response = cmd.parse(base);
+ REQUIRE(response.code == at::Result::Code::ERROR);
+ }
+}
+
+TEST_CASE("CLCC set data")
+{
+ using namespace std::literals;
+ at::cmd::CLCC cmd;
+ SECTION("None modifier set")
+ {
+ constexpr auto expectedResult = "AT+CLCC"sv;
+ REQUIRE(cmd.getCmd() == expectedResult);
+ }
+
+ SECTION("Get modifier set")
+ {
+ constexpr auto expectedResult = "AT+CLCC?"sv;
+ cmd.setModifier(at::cmd::Modifier::Get);
+ REQUIRE(cmd.getCmd() == expectedResult);
+ }
+}
+
+TEST_CASE("CLCC conversion methods")
+{
+ SECTION("String to bool")
+ {
+ SECTION("Proper data - true")
+ {
+ REQUIRE(at::cmd::CLCCStub::toBool("1") == true);
+ }
+ SECTION("Proper data - true")
+ {
+ REQUIRE(at::cmd::CLCCStub::toBool("0") == false);
+ }
+ SECTION("Invaild data")
+ {
+ REQUIRE_THROWS(at::cmd::CLCCStub::toBool("-1") == false);
+ }
+ }
+ SECTION("String to uint32")
+ {
+ REQUIRE(at::cmd::CLCCStub::toUInt("1") == 1);
+ REQUIRE(at::cmd::CLCCStub::toUInt("32") == 32);
+ REQUIRE(at::cmd::CLCCStub::toUInt("255") == std::numeric_limits<std::uint8_t>::max());
+ REQUIRE_THROWS(at::cmd::CLCCStub::toUInt("256"));
+ }
+ SECTION("String to enum")
+ {
+ SECTION("Call dir")
+ {
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallDir>("-1") == std::nullopt);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallDir>("0").value() == ModemCall::CallDir::MO);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallDir>("1").value() == ModemCall::CallDir::MT);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallDir>("2") == std::nullopt);
+ }
+ SECTION("Call state")
+ {
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallState>("-1") == std::nullopt);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallState>("0").value() == ModemCall::CallState::Active);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallState>("5").value() == ModemCall::CallState::Waiting);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallState>("6") == std::nullopt);
+ }
+ SECTION("Call mode")
+ {
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallMode>("-1") == std::nullopt);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallMode>("0").value() == ModemCall::CallMode::Voice);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallMode>("2").value() == ModemCall::CallMode::FAX);
+ REQUIRE(at::cmd::CLCCStub::toEnum<ModemCall::CallMode>("3") == std::nullopt);
+ }
+ }
+}
M module-db/Interface/CalllogRecord.cpp => module-db/Interface/CalllogRecord.cpp +1 -4
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "CalllogRecord.hpp"
@@ 45,9 45,6 @@ CalllogRecordInterface::CalllogRecordInterface(CalllogDB *calllogDb, ContactsDB
: calllogDB(calllogDb), contactsDB(contactsDb)
{}
-CalllogRecordInterface::~CalllogRecordInterface()
-{}
-
bool CalllogRecordInterface::Add(const CalllogRecord &rec)
{
ContactRecordInterface contactInterface(contactsDB);
M module-db/Interface/CalllogRecord.hpp => module-db/Interface/CalllogRecord.hpp +1 -3
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 29,7 29,6 @@ struct CalllogRecord : public Record
friend std::ostream &operator<<(std::ostream &out, const CalllogRecord &point);
CalllogRecord() = default;
- ~CalllogRecord() = default;
CalllogRecord(const CalllogTableRow &tableRow);
uint32_t getContactId() const;
@@ 45,7 44,6 @@ class CalllogRecordInterface : public RecordInterface<CalllogRecord, CalllogReco
{
public:
CalllogRecordInterface(CalllogDB *CalllogDb, ContactsDB *contactsDb);
- virtual ~CalllogRecordInterface();
bool Add(const CalllogRecord &rec) override final;
bool RemoveByID(uint32_t id) override final;
M module-services/service-cellular/CellularCall.cpp => module-services/service-cellular/CellularCall.cpp +0 -83
@@ 16,89 16,6 @@
#include <string>
#include <vector>
-namespace ModemCall
-{
- ModemCall::ModemCall(const std::string str)
- {
- const std::string prefix = "+CLCC: ";
- std::string callEntry = str;
-
- // Check for a valid prefix
- if (callEntry.rfind(prefix, 0) != 0) {
-
- throw std::runtime_error("No valid prefix");
- }
- else {
- // remove the prefix
- callEntry.erase(0, prefix.length());
- }
-
- std::vector<std::string> tokens = utils::split(callEntry, ",");
-
- auto numberOfTokens = tokens.size();
- if (numberOfTokens != 7 && numberOfTokens != 8) {
- throw std::runtime_error("Wrong number of tokens" + std::to_string(numberOfTokens));
- }
-
- idx = std::stoul(tokens[0]);
- auto conv_val = std::stoul(tokens[1]);
- auto tmp_dir = magic_enum::enum_cast<CallDir>(conv_val);
- if (tmp_dir.has_value()) {
- dir = tmp_dir.value();
- }
- else {
- throw std::runtime_error("dir value out of range CallDir enum - " + tokens[1]);
- }
-
- conv_val = std::stoul(tokens[2]);
- auto tmp_state = magic_enum::enum_cast<CallState>(conv_val);
- if (tmp_state.has_value()) {
- state = tmp_state.value();
- }
- else {
- throw std::runtime_error("state value out of range CallState enum - " + tokens[2]);
- }
-
- conv_val = std::stoul(tokens[3]);
- auto tmp_mode = magic_enum::enum_cast<CallMode>(conv_val);
- if (tmp_mode.has_value()) {
- mode = tmp_mode.value();
- }
- else {
- throw std::runtime_error("mode value out of range CallMode enum - " + tokens[3]);
- }
-
- isConferenceCall = static_cast<bool>(std::stoul(tokens[4]));
- phoneNumber = tokens[5];
-
- try {
- conv_val = std::stoul(tokens[6]);
- }
- catch (const std::logic_error &) {
- conv_val = 0;
- }
- type = conv_val;
-
- if (numberOfTokens == 8) {
- phoneBookName = tokens[7];
- }
- }
-
- std::ostream &operator<<(std::ostream &out, const ModemCall &call)
- {
- out << " <idx> " << call.idx << " <dir> " << static_cast<uint32_t>(call.dir) << " <stat> "
- << static_cast<uint32_t>(call.state) << " <mode> " << static_cast<uint32_t>(call.mode) << " <mpty> "
- << static_cast<uint32_t>(call.isConferenceCall) << " <number> " << call.phoneNumber << " <type> "
- << static_cast<uint32_t>(call.type);
-
- if (!call.phoneBookName.empty()) {
- out << " <alpha> " << call.phoneBookName;
- }
-
- return out;
- }
-} // namespace ModemCall
-
namespace CellularCall
{
bool CellularCall::startCall(const utils::PhoneNumber::View &number, const CallType type)
M module-services/service-cellular/CellularUrcHandler.hpp => module-services/service-cellular/CellularUrcHandler.hpp +2 -2
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 24,7 24,7 @@
class CellularUrcHandler : public at::urc::UrcHandler
{
public:
- CellularUrcHandler(ServiceCellular &cellularService) : cellularService(cellularService)
+ explicit CellularUrcHandler(ServiceCellular &cellularService) : cellularService(cellularService)
{}
void Handle(at::urc::Clip &urc) final;
M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +17 -28
@@ 36,6 36,7 @@
#include <Tables/CalllogTable.hpp>
#include <Tables/Record.hpp>
#include <Utils.hpp>
+#include <at/cmd/CLCC.hpp>
#include <at/UrcClip.hpp>
#include <at/UrcCmti.hpp>
#include <at/UrcCreg.hpp>
@@ 225,9 226,8 @@ static bool isSettingsAutomaticTimeSyncEnabled()
void ServiceCellular::CallStateTimerHandler()
{
LOG_DEBUG("CallStateTimerHandler");
- std::shared_ptr<CellularRequestMessage> msg =
- std::make_shared<CellularRequestMessage>(MessageType::CellularListCurrentCalls);
- bus.sendUnicast(msg, ServiceCellular::serviceName);
+ auto msg = std::make_shared<CellularRequestMessage>(MessageType::CellularListCurrentCalls);
+ bus.sendUnicast(std::move(msg), ServiceCellular::serviceName);
}
sys::ReturnCodes ServiceCellular::InitHandler()
@@ 822,33 822,22 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
break;
}
case MessageType::CellularListCurrentCalls: {
- auto ret = cmux->get(TS0710::Channel::Commands)->cmd(at::AT::CLCC);
- auto size = ret.response.size();
- if (ret && size > 1) {
- bool retVal = true;
- // sometimes there is additional active data connection, sometimes not
- auto callEntry = ret.response[size == 2 ? 0 : 1];
-
- try {
- ModemCall::ModemCall call(callEntry);
- LOG_DEBUG("%s", utils::to_string(call).c_str());
- // If call changed to "Active" state stop callStateTimer(used for polling for call state)
- if (call.state == ModemCall::CallState::Active) {
- auto msg =
- std::make_shared<CellularNotificationMessage>(CellularNotificationMessage::Type::CallActive);
- bus.sendMulticast(msg, sys::BusChannel::ServiceCellularNotifications);
- callStateTimer->stop();
- }
- }
- catch (const std::exception &e) {
- LOG_ERROR("exception \"%s\" was thrown", e.what());
- retVal = false;
+ at::cmd::CLCC cmd;
+ auto base = cmux->get(TS0710::Channel::Commands)->cmd(cmd);
+ if (auto response = cmd.parse(base); response) {
+ const auto &data = response.getData();
+ auto it = std::find_if(std::begin(data), std::end(data), [&](const auto &entry) {
+ return entry.stateOfCall == ModemCall::CallState::Active && entry.mode == ModemCall::CallMode::Voice;
+ });
+ if (it != std::end(data)) {
+ auto msg = std::make_shared<CellularNotificationMessage>(CellularNotificationMessage::Type::CallActive);
+ bus.sendMulticast(std::move(msg), sys::BusChannel::ServiceCellularNotifications);
+ callStateTimer->stop();
+ responseMsg = std::make_shared<CellularResponseMessage>(true);
+ break;
}
- responseMsg = std::make_shared<CellularResponseMessage>(retVal);
- }
- else {
- responseMsg = std::make_shared<CellularResponseMessage>(false);
}
+ responseMsg = std::make_shared<CellularResponseMessage>(false);
} break;
case MessageType::CellularHangupCall: {
M module-services/service-cellular/service-cellular/CellularCall.hpp => module-services/service-cellular/service-cellular/CellularCall.hpp +0 -57
@@ 15,61 15,6 @@
#include <string>
#include <sys/types.h>
-namespace ModemCall
-{
- enum class CallState : uint8_t
- {
- Active = 0, // 0 active: call in progress (setup was successful)
- Held, // 1 held: call on hold
- Dialing, // 2 dialing (MO call): number dialed
- Alerting, // 3 alerting (MO call): number dialed and the called party is alerted
- Incoming, // 4 incoming (MT call): incoming call, ringtone played (AT RING notification)
- Waiting // 5 waiting (MT call): call waiting notification while another call is active (if call waiting feature
- // enabled)
- };
-
- enum class CallDir : uint8_t
- {
- MO = 0, // Mobile originated (MO) call
- MT = 1, // Mobile terminated (MT) call
- };
-
- enum class CallMode : uint8_t
- {
- Voice = 0,
- Data = 1,
- FAX = 2,
- };
-
- /// Usually contains one of defined values
- /// More details in 3GPP TS 24.008 subclause 10.5.4.7
- enum class CallType : uint8_t
- {
- UknownType = 129,
- InternationType = 145, // contains the "+" character
- NationalType = 161,
- };
-
- struct ModemCall
- {
- int8_t idx;
- CallDir dir;
- CallState state;
- CallMode mode;
- bool isConferenceCall;
- std::string phoneNumber;
- uint8_t type; /// Usually contains on of values defined in CallType
- std::string phoneBookName; /// This field is defined in the AT+CLCC command response but our modem is
- /// not returning it.
-
- ModemCall() = delete;
- ~ModemCall() = default;
- ModemCall(const std::string str);
-
- friend std::ostream &operator<<(std::ostream &out, const ModemCall &call);
- };
-} // namespace ModemCall
-
namespace CellularCall
{
enum class Forced : bool
@@ 118,8 63,6 @@ namespace CellularCall
this->call.contactId = 0;
}
- ~CellularCall() = default;
-
void setStartCallAction(const std::function<CalllogRecord(const CalllogRecord &rec)> callAction)
{
startCallAction = callAction;
M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +0 -3
@@ 28,7 28,6 @@ class CellularMessage : public sys::DataMessage
{
public:
CellularMessage(MessageType messageType) : sys::DataMessage(messageType){};
- virtual ~CellularMessage(){};
};
class CellularCallMessage : public CellularMessage
@@ 257,8 256,6 @@ class CellularRequestMessage : public CellularMessage
public:
CellularRequestMessage(MessageType messageType, std::string data = "") : CellularMessage(messageType), data(data)
{}
- ~CellularRequestMessage()
- {}
std::string data;
};