~aleteoryx/muditaos

30d6ddbe471115dc4e6e2a8b159c7449df5e2e88 — breichel 5 years ago 759bee9
[EGD-4920] Add API to get current operator name

Asynchronous API to get operator name,
also possibility to easy extend solution for
current operator technology and information about
operator selection mode (automatic/hand).
M module-cellular/at/response.cpp => module-cellular/at/response.cpp +45 -1
@@ 51,6 51,7 @@ namespace at
            return parts;
        }

        constexpr std::string_view AT_COPS = "+COPS:";
        bool parseCOPS(const at::Result &resp, std::vector<cops::Operator> &ret)
        {
            /// +COPS: (list of supported <stat>,long alphanumeric <oper>,


@@ 67,7 68,6 @@ namespace at
            constexpr auto minOperatorParams = 4;
            constexpr auto maxOperatorParams = 5;

            constexpr std::string_view AT_COPS = "+COPS:";
            if (auto line = getResponseLineATCommand(resp, AT_COPS); line) {
                const auto &commandLine = *line;



@@ 112,6 112,50 @@ namespace at

            return false;
        }

        bool parseCOPS(const at::Result &resp, cops::CurrentOperatorInfo &ret)
        {
            /// ret as +COPS: <mode>[,<format>[,<oper>][,<Act>]]
            /// parameters could be 1,2,3,4 all optional in documentation !

            constexpr auto minCOPSLength = 1;

            if (auto line = getResponseLineATCommand(resp, AT_COPS); line) {
                const auto &commandLine = *line;

                if (commandLine.length() < minCOPSLength) {
                    return false;
                }

                auto opParams = utils::split(commandLine, ",");
                cops::Operator op;

                switch (opParams.size()) {
                case 4:
                    op.technology = static_cast<cops::AccessTechnology>(utils::getNumericValue<int>(opParams[3]));
                    [[fallthrough]];
                case 3: {
                    ret.setFormat(static_cast<cops::NameFormat>(utils::getNumericValue<int>(opParams[1])));
                    utils::findAndReplaceAll(opParams[2], at::response::StringDelimiter, "");
                    op.setNameByFormat(ret.getFormat(), opParams[2]);
                }
                    ret.setOperator(op);
                    [[fallthrough]];
                case 2:
                    ret.setFormat(static_cast<cops::NameFormat>(utils::getNumericValue<int>(opParams[1])));
                    [[fallthrough]];
                case 1:
                    ret.setMode(static_cast<cops::CopsMode>(utils::getNumericValue<int>(opParams[0])));
                    break;
                default:
                    return false;
                }

                return true;
            }
            return false;
        }

        bool parseQPINC(const at::Result &resp, qpinc::AttemptsCounters &ret)
        {
            /// parse only first result from QPINC

M module-cellular/at/response.hpp => module-cellular/at/response.hpp +51 -1
@@ 55,7 55,6 @@ namespace at
                E_UTRAN                 = 7,
                CDMA                    = 100
            };

            enum class NameFormat
            {
                Long    = 0,


@@ 100,10 99,61 @@ namespace at
                    }
                }
            };

            class CurrentOperatorInfo
            {
                Operator op;
                CopsMode mode     = CopsMode::Automatic;
                NameFormat format = NameFormat::Long;
                bool operatorSet  = false;

              public:
                void setFormat(NameFormat format)
                {
                    this->format = format;
                }
                NameFormat getFormat() const noexcept
                {
                    return this->format;
                }
                void setMode(CopsMode mode)
                {
                    this->mode = mode;
                }
                CopsMode getMode() const noexcept
                {
                    return this->mode;
                }

                void setOperator(Operator op)
                {
                    this->operatorSet = true;
                    this->op          = op;
                }
                std::optional<cops::Operator> getOperator() const
                {
                    if (operatorSet) {
                        return op;
                    }
                    else {
                        return std::nullopt;
                    }
                }
            };
        } // namespace cops

        /**
         * @brief parse for AT+COPS=? from quectel
         *
         */
        bool parseCOPS(const at::Result &resp, std::vector<cops::Operator> &ret);
        using ResponseTokens = std::vector<std::vector<std::string>>;
        /**
         * @brief parse for AT+COPS? from quectel
         *
         */
        bool parseCOPS(const at::Result &resp, cops::CurrentOperatorInfo &ret);

        std::vector<std::string> tokenize(std::string &response, std::string separator = ",");

        /**

M module-cellular/test/unittest_response.cpp => module-cellular/test/unittest_response.cpp +55 -0
@@ 96,4 96,59 @@ TEST_CASE("Response COPS")
        resp.response.push_back("+COPS: (2,\"PLAY\",\"PLAY\", 4)(");
        REQUIRE(at::response::parseCOPS(resp, ret) == false);
    }

    SECTION("OK COPS? - return operator")
    {
        at::Result resp;
        resp.code = at::Result::Code::OK;
        at::response::cops::CurrentOperatorInfo ret;
        resp.response.push_back("+COPS: 0,0,\"PLAY\",2");
        resp.response.push_back("OK");
        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::parseCOPS(resp, ret) == true);
        REQUIRE(ret.getOperator());
        REQUIRE(ret.getMode() == at::response::cops::CopsMode::Automatic);
        REQUIRE(ret.getFormat() == at::response::cops::NameFormat::Long);
        auto op = *ret.getOperator();
        REQUIRE(op.longName == "PLAY");
        REQUIRE(op.technology == at::response::cops::AccessTechnology::UTRAN);
    }

    SECTION("OK COPS? - return operator no act")
    {
        at::Result resp;
        resp.code = at::Result::Code::OK;
        at::response::cops::CurrentOperatorInfo ret;
        resp.response.push_back("+COPS: 0,0,\"PLAY\"");
        resp.response.push_back("OK");
        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::parseCOPS(resp, ret) == true);
        REQUIRE(ret.getOperator());
        REQUIRE(ret.getMode() == at::response::cops::CopsMode::Automatic);
        REQUIRE(ret.getFormat() == at::response::cops::NameFormat::Long);
        auto op = *ret.getOperator();
        REQUIRE(op.longName == "PLAY");
    }

    SECTION("OK COPS? - no operator")
    {
        at::Result resp;
        resp.code = at::Result::Code::OK;
        at::response::cops::CurrentOperatorInfo ret;
        resp.response.push_back("+COPS: 0");
        resp.response.push_back("OK");
        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::parseCOPS(resp, ret) == true);
        REQUIRE(ret.getMode() == at::response::cops::CopsMode::Automatic);
    }
    SECTION("WRONG COPS? - to many")
    {
        at::Result resp;
        resp.code = at::Result::Code::OK;
        at::response::cops::CurrentOperatorInfo ret;
        resp.response.push_back("+COPS: 0,0,\"PLAY\",2, 3");
        resp.response.push_back("OK");
        REQUIRE(resp.code == at::Result::Code::OK);
        REQUIRE(at::response::parseCOPS(resp, ret) == false);
    }
}

M module-services/service-cellular/CellularServiceAPI.cpp => module-services/service-cellular/CellularServiceAPI.cpp +6 -0
@@ 112,6 112,12 @@ void CellularServiceAPI::GetNetworkInfo(sys::Service *serv)
    sys::Bus::SendUnicast(msg, ServiceCellular::serviceName, serv);
}

void CellularServiceAPI::GetCurrentOperator(sys::Service *serv)
{
    std::shared_ptr<CellularGetCurrentOperatorMessage> msg = std::make_shared<CellularGetCurrentOperatorMessage>();
    sys::Bus::SendUnicast(msg, ServiceCellular::serviceName, serv);
}

void CellularServiceAPI::StartOperatorsScan(sys::Service *serv, bool fullInfo)
{
    std::shared_ptr<CellularStartOperatorsScanMessage> msg =

M module-services/service-cellular/NetworkSettings.cpp => module-services/service-cellular/NetworkSettings.cpp +16 -0
@@ 5,6 5,22 @@

#include <unordered_map>

std::string NetworkSettings::getCurrentOperator() const
{
    auto channel = cellularService.cmux->get(TS0710::Channel::Commands);
    if (channel) {
        at::Cmd buildCmd = at::factory(at::AT::COPS) + "?";
        auto resp        = channel->cmd(buildCmd);
        at::response::cops::CurrentOperatorInfo ret;
        if ((resp.code == at::Result::Code::OK) && (at::response::parseCOPS(resp, ret))) {
            if (auto _operator = ret.getOperator(); _operator) {
                return _operator->getNameByFormat(ret.getFormat());
            }
        }
    }

    return {};
}
std::vector<std::string> NetworkSettings::scanOperators(bool fullInfoList)
{
    std::vector<std::string> operatorNames;

M module-services/service-cellular/NetworkSettings.hpp => module-services/service-cellular/NetworkSettings.hpp +1 -0
@@ 26,6 26,7 @@ class NetworkSettings
    std::vector<std::string> scanOperators(bool fullInfoList = false);

    bool setOperatorAutoSelect();
    std::string getCurrentOperator() const;
    bool setOperator(at::response::cops::CopsMode mode, at::response::cops::NameFormat format, const std::string &name);

  private:

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +13 -0
@@ 300,6 300,11 @@ void ServiceCellular::registerMessageHandlers()
    });

    connect(typeid(CellularGetAPNMessage), [&](sys::Message *request) -> sys::MessagePointer {
        connect(typeid(CellularGetCurrentOperatorMessage), [&](sys::Message *request) -> sys::MessagePointer {
            auto msg = static_cast<CellularGetCurrentOperatorMessage *>(request);
            return handleCellularGetCurrentOperator(msg);
        });

        auto msg = static_cast<CellularGetAPNMessage *>(request);
        return handleCellularGetAPNMessage(msg);
    });


@@ 2043,6 2048,14 @@ bool ServiceCellular::handle_apn_conf_procedure()
    return true;
}

std::shared_ptr<CellularGetCurrentOperatorResponse> ServiceCellular::handleCellularGetCurrentOperator(
    CellularGetCurrentOperatorMessage *msg)
{
    LOG_INFO("CellularGetCurrentOperator handled");
    NetworkSettings networkSettings(*this);
    return std::make_shared<CellularGetCurrentOperatorResponse>(networkSettings.getCurrentOperator());
}

std::shared_ptr<CellularGetAPNResponse> ServiceCellular::handleCellularGetAPNMessage(CellularGetAPNMessage *msg)
{
    std::vector<std::shared_ptr<packet_data::APN::Config>> apns;

M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +22 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 88,6 88,13 @@ class CellularNotificationMessage : public CellularMessage
    std::string data;
};

class CellularGetCurrentOperatorMessage : public CellularMessage
{
  public:
    explicit CellularGetCurrentOperatorMessage() : CellularMessage(MessageType::CellularNotification)
    {}
};

class CellularSetOperatorAutoSelectMessage : public sys::Message
{
  public:


@@ 95,6 102,20 @@ class CellularSetOperatorAutoSelectMessage : public sys::Message
    {}
};

class CellularGetCurrentOperatorResponse : public CellularMessage
{
    std::string currentOperatorName;

  public:
    explicit CellularGetCurrentOperatorResponse(std::string currentOperatorName)
        : CellularMessage(MessageType::CellularNotification), currentOperatorName(currentOperatorName)
    {}

    std::string getCurrentOperatorName() const
    {
        return currentOperatorName;
    }
};
class CellularSetOperatorMessage : public sys::Message
{
    at::response::cops::CopsMode mode;

M module-services/service-cellular/service-cellular/CellularServiceAPI.hpp => module-services/service-cellular/service-cellular/CellularServiceAPI.hpp +6 -0
@@ 43,6 43,12 @@ namespace CellularServiceAPI
     * @param serv pointer to caller service.
     */
    void GetNetworkInfo(sys::Service *serv);

    /*
     * @brief Get current operator, result async in
     * CellularGetCurrentOperatorResponse message
     */
    void GetCurrentOperator(sys::Service *serv);
    /*
     * @brief It calls service-cellulat to perform operators scan
     * @param serv pointer to caller service.

M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +3 -0
@@ 269,8 269,11 @@ class ServiceCellular : public sys::Service

    std::shared_ptr<cellular::RawCommandRespAsync> handleCellularStartOperatorsScan(
        CellularStartOperatorsScanMessage *msg);

    std::shared_ptr<CellularSetOperatorAutoSelectResponse> handleCellularSetOperatorAutoSelect(
        CellularSetOperatorAutoSelectMessage *msg);
    std::shared_ptr<CellularGetCurrentOperatorResponse> handleCellularGetCurrentOperator(
        CellularGetCurrentOperatorMessage *msg);
    std::shared_ptr<CellularGetAPNResponse> handleCellularGetAPNMessage(CellularGetAPNMessage *msg);
    std::shared_ptr<CellularSetAPNResponse> handleCellularSetAPNMessage(CellularSetAPNMessage *msg);
    std::shared_ptr<CellularSetOperatorResponse> handleCellularSetOperator(CellularSetOperatorMessage *msg);