From cee68d98eb76a5e757ff90398eca8ab6484a7341 Mon Sep 17 00:00:00 2001 From: Jakub Pyszczak Date: Tue, 2 Feb 2021 15:41:51 +0100 Subject: [PATCH] [EGD-5529] Added CLCC parser New cmd mechanism implementation of CLCC parser. Unit tests implementation for the parser. Refactor of searching for active outgoing call in service cellular. --- enabled_unittests | 9 + module-cellular/CMakeLists.txt | 1 + module-cellular/at/Cmd.hpp | 2 + module-cellular/at/Result.hpp | 1 + module-cellular/at/cmd/CLCC.hpp | 92 +++++++++ module-cellular/at/cmd/src/CLCC.cpp | 103 ++++++++++ module-cellular/at/src/Cmd.cpp | 47 ++++- module-cellular/test/CMakeLists.txt | 1 - .../test/mock/AtCommon_channel.hpp | 81 ++++++++ .../test/unittest_parse_result.cpp | 189 ++++++++++++++++++ module-db/Interface/CalllogRecord.cpp | 5 +- module-db/Interface/CalllogRecord.hpp | 4 +- .../service-cellular/CellularCall.cpp | 83 -------- .../service-cellular/CellularUrcHandler.hpp | 4 +- .../service-cellular/ServiceCellular.cpp | 45 ++--- .../service-cellular/CellularCall.hpp | 57 ------ .../service-cellular/CellularMessage.hpp | 3 - 17 files changed, 544 insertions(+), 183 deletions(-) create mode 100644 module-cellular/at/cmd/CLCC.hpp create mode 100644 module-cellular/at/cmd/src/CLCC.cpp diff --git a/enabled_unittests b/enabled_unittests index b288e4baa5eb31f667284375ce3993a21faacc61..997008e8edf1192218f0c901f74e658dad966c25 100644 --- a/enabled_unittests +++ b/enabled_unittests @@ -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; " diff --git a/module-cellular/CMakeLists.txt b/module-cellular/CMakeLists.txt index 87498b8a243c604c0664aa0198e1124eafa7644b..9288eaa7f38d9e5921960243c11aaadbf4f65a65 100644 --- a/module-cellular/CMakeLists.txt +++ b/module-cellular/CMakeLists.txt @@ -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") diff --git a/module-cellular/at/Cmd.hpp b/module-cellular/at/Cmd.hpp index 0cc2262a4f6e799b73fcc2d886818cd35e89806f..0b7686f5e0267009007defdbd5d6903f31f75d51 100644 --- a/module-cellular/at/Cmd.hpp +++ b/module-cellular/at/Cmd.hpp @@ -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; /// 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 diff --git a/module-cellular/at/Result.hpp b/module-cellular/at/Result.hpp index 7b8b6f946221ca3f141f728bc6f1aad655af8ac4..74b50442ae73faa36a319c7a4243aea31c8f9077 100644 --- a/module-cellular/at/Result.hpp +++ b/module-cellular/at/Result.hpp @@ -42,6 +42,7 @@ namespace at std::variant errorCode = at::EquipmentErrorCode::NoInformation; std::vector response; + std::vector> tokens; virtual explicit operator bool() const { diff --git a/module-cellular/at/cmd/CLCC.hpp b/module-cellular/at/cmd/CLCC.hpp new file mode 100644 index 0000000000000000000000000000000000000000..037779542842bee1b5d034a3daca7f75d1fbb8d9 --- /dev/null +++ b/module-cellular/at/cmd/CLCC.hpp @@ -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 +#include + +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; + friend cmd::CLCC; + + public: + explicit CLCC(const Result &); + [[nodiscard]] auto getData() const noexcept -> const std::vector & + { + 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 [[nodiscard]] static auto toEnum(const std::string &text) -> std::optional; + + public: + CLCC() noexcept; + explicit CLCC(at::cmd::Modifier mod) noexcept; + [[nodiscard]] auto parse(Result &base_result) -> result::CLCC & final; + }; + } // namespace cmd +} // namespace at diff --git a/module-cellular/at/cmd/src/CLCC.cpp b/module-cellular/at/cmd/src/CLCC.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ed114cc49aea29e60d7eb482366ecae06e91aea --- /dev/null +++ b/module-cellular/at/cmd/src/CLCC.cpp @@ -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 +#include +#include +#include +#include + +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::max()) { + throw std::runtime_error("Out of range value"); + } + return static_cast(value); + } + + template auto CLCC::toEnum(const std::string &text) -> std::optional + { + static_assert(std::is_enum_v); + int ret = -1; + if (!utils::toNumeric(text, ret) || ret == -1) { + return std::nullopt; + } + + return magic_enum::enum_cast(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(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(result->tokens.size())); + p->code = result::CLCC::Code::PARSING_ERROR; + return *p; + } + + try { + const auto callDir = toEnum(tokens[1]); + const auto callState = toEnum(tokens[2]); + const auto callMode = toEnum(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 diff --git a/module-cellular/at/src/Cmd.cpp b/module-cellular/at/src/Cmd.cpp index 97da90660c5805b336ceadfbe072cd50509eb78b..48703ed15497d42fafaf88657f8b17e907c5a1f0 100644 --- a/module-cellular/at/src/Cmd.cpp +++ b/module-cellular/at/src/Cmd.cpp @@ -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 +#include "Cmd.hpp" +#include 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 = std::make_unique(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 diff --git a/module-cellular/test/CMakeLists.txt b/module-cellular/test/CMakeLists.txt index 4ac978a9cf9ec37827ad4245a49900e24c61edf1..1f511d799212a06dfb21fa36a43d07738a94a614 100644 --- a/module-cellular/test/CMakeLists.txt +++ b/module-cellular/test/CMakeLists.txt @@ -28,7 +28,6 @@ add_catch2_executable( module-cellular ) - add_catch2_executable( NAME unittest_ATStream diff --git a/module-cellular/test/mock/AtCommon_channel.hpp b/module-cellular/test/mock/AtCommon_channel.hpp index 94ae99b00c0dd7a38db2d550cd1a846d9f66082e..b18717c397fab707f2a812fcb1da6662f155c61e 100644 --- a/module-cellular/test/mock/AtCommon_channel.hpp +++ b/module-cellular/test/mock/AtCommon_channel.hpp @@ -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 diff --git a/module-cellular/test/unittest_parse_result.cpp b/module-cellular/test/unittest_parse_result.cpp index b59da8720c5c66bf470dedd635ce57e2ad8c3c9e..26904076a5daec7194dd06c164bac47c554543da 100644 --- a/module-cellular/test/unittest_parse_result.cpp +++ b/module-cellular/test/unittest_parse_result.cpp @@ -6,6 +6,7 @@ #include +#include #include #include @@ -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 [[nodiscard]] static auto toEnum(const std::string &text) -> std::optional + { + return CLCC::toEnum(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::max()); + REQUIRE_THROWS(at::cmd::CLCCStub::toUInt("256")); + } + SECTION("String to enum") + { + SECTION("Call dir") + { + REQUIRE(at::cmd::CLCCStub::toEnum("-1") == std::nullopt); + REQUIRE(at::cmd::CLCCStub::toEnum("0").value() == ModemCall::CallDir::MO); + REQUIRE(at::cmd::CLCCStub::toEnum("1").value() == ModemCall::CallDir::MT); + REQUIRE(at::cmd::CLCCStub::toEnum("2") == std::nullopt); + } + SECTION("Call state") + { + REQUIRE(at::cmd::CLCCStub::toEnum("-1") == std::nullopt); + REQUIRE(at::cmd::CLCCStub::toEnum("0").value() == ModemCall::CallState::Active); + REQUIRE(at::cmd::CLCCStub::toEnum("5").value() == ModemCall::CallState::Waiting); + REQUIRE(at::cmd::CLCCStub::toEnum("6") == std::nullopt); + } + SECTION("Call mode") + { + REQUIRE(at::cmd::CLCCStub::toEnum("-1") == std::nullopt); + REQUIRE(at::cmd::CLCCStub::toEnum("0").value() == ModemCall::CallMode::Voice); + REQUIRE(at::cmd::CLCCStub::toEnum("2").value() == ModemCall::CallMode::FAX); + REQUIRE(at::cmd::CLCCStub::toEnum("3") == std::nullopt); + } + } +} diff --git a/module-db/Interface/CalllogRecord.cpp b/module-db/Interface/CalllogRecord.cpp index 04f2b5b65325344d7028e5b6969aa70e7cb4d7d2..5303d042dc50bf609faf486dc48426b5b5b01c40 100644 --- a/module-db/Interface/CalllogRecord.cpp +++ b/module-db/Interface/CalllogRecord.cpp @@ -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); diff --git a/module-db/Interface/CalllogRecord.hpp b/module-db/Interface/CalllogRecord.hpp index 538ec1697e0e75c4bf3562c20df1d43042af6dfa..b31a26bfb10fb4e299209ad4d083f4ac7b29c3b6 100644 --- a/module-db/Interface/CalllogRecord.hpp +++ b/module-db/Interface/CalllogRecord.hpp @@ -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 #include -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 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(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(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(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(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 << " " << call.idx << " " << static_cast(call.dir) << " " - << static_cast(call.state) << " " << static_cast(call.mode) << " " - << static_cast(call.isConferenceCall) << " " << call.phoneNumber << " " - << static_cast(call.type); - - if (!call.phoneBookName.empty()) { - out << " " << call.phoneBookName; - } - - return out; - } -} // namespace ModemCall - namespace CellularCall { bool CellularCall::startCall(const utils::PhoneNumber::View &number, const CallType type) diff --git a/module-services/service-cellular/CellularUrcHandler.hpp b/module-services/service-cellular/CellularUrcHandler.hpp index 13e9c9c38446331571ea749c163e51917b8678cf..d48f411f3fac479ac743d1235ccf2aa6c880156b 100644 --- a/module-services/service-cellular/CellularUrcHandler.hpp +++ b/module-services/service-cellular/CellularUrcHandler.hpp @@ -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; diff --git a/module-services/service-cellular/ServiceCellular.cpp b/module-services/service-cellular/ServiceCellular.cpp index b2eb2db751bb63257924c474ebd5f8cf062826b7..5800fc7b05bb91539bdab108b207914e7dea4695 100644 --- a/module-services/service-cellular/ServiceCellular.cpp +++ b/module-services/service-cellular/ServiceCellular.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -225,9 +226,8 @@ static bool isSettingsAutomaticTimeSyncEnabled() void ServiceCellular::CallStateTimerHandler() { LOG_DEBUG("CallStateTimerHandler"); - std::shared_ptr msg = - std::make_shared(MessageType::CellularListCurrentCalls); - bus.sendUnicast(msg, ServiceCellular::serviceName); + auto msg = std::make_shared(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::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::Type::CallActive); + bus.sendMulticast(std::move(msg), sys::BusChannel::ServiceCellularNotifications); + callStateTimer->stop(); + responseMsg = std::make_shared(true); + break; } - responseMsg = std::make_shared(retVal); - } - else { - responseMsg = std::make_shared(false); } + responseMsg = std::make_shared(false); } break; case MessageType::CellularHangupCall: { diff --git a/module-services/service-cellular/service-cellular/CellularCall.hpp b/module-services/service-cellular/service-cellular/CellularCall.hpp index 356c55bccb8b8de9a068bae0d91c8b9b36a07458..09b11412604179da04411a9cdefd8555f85212a1 100644 --- a/module-services/service-cellular/service-cellular/CellularCall.hpp +++ b/module-services/service-cellular/service-cellular/CellularCall.hpp @@ -15,61 +15,6 @@ #include #include -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 callAction) { startCallAction = callAction; diff --git a/module-services/service-cellular/service-cellular/CellularMessage.hpp b/module-services/service-cellular/service-cellular/CellularMessage.hpp index a07c673fef039fe4a3e48ee63799a6942baf4461..fd3a7602704be7be9038a3f92f091290aa0eed17 100644 --- a/module-services/service-cellular/service-cellular/CellularMessage.hpp +++ b/module-services/service-cellular/service-cellular/CellularMessage.hpp @@ -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; };