~aleteoryx/muditaos

a607871f76a87b141d0c1db65cb87bc783055874 — Hubert Chrzaniuk 5 years ago 180bfba
[EGD-3839] Cellular - get time zone (#903)

M changelog.md => changelog.md +1 -0
@@ 7,6 7,7 @@
* `[settings][bluetooth]` Add "Phone name" window.
* `[gui]` Add "ButtonOnOff" widget.
* `[cellular]` Add support for modem reset
* `[cellular]` Obtain time zone through network

### Changed


M module-cellular/at/URC_CTZE.hpp => module-cellular/at/URC_CTZE.hpp +8 -1
@@ 21,10 21,17 @@ namespace at::urc

      public:
        explicit CTZE(const std::string &val);

        constexpr static int maxTimezoneOffset = 56;
        constexpr static int minTimezoneOffset = -48;

        ~CTZE() override = default;
        [[nodiscard]] auto what() const noexcept -> std::string final;
        [[nodiscard]] auto isValid() const noexcept -> bool;

        [[nodiscard]] auto getTimeInfo(void) const noexcept -> tm;

        auto getTimeZoneOffset() const -> int;
        auto getTimeZoneString() const -> std::string;
        auto getGMTTime(void) const -> const struct tm;
    };
}; // namespace at::urc

M module-cellular/at/response.cpp => module-cellular/at/response.cpp +15 -5
@@ 32,7 32,11 @@ namespace at
                if (pos != std::string::npos) {
                    LOG_INFO("%s", CSQstring.c_str());
                    CSQstring = CSQstring.substr(0, pos);
                    return utils::toNumeric(CSQstring, result);
                    int parsedVal = 0;
                    if (utils::toNumeric(CSQstring, parsedVal) && parsedVal >= 0) {
                        result = parsedVal;
                        return true;
                    }
                }
            }
            return false;


@@ 59,7 63,11 @@ namespace at
            if (pos != std::string::npos) {
                auto constexpr digitLength = 1;
                resp                       = resp.substr(pos + digitLength, digitLength);
                return utils::toNumeric(resp, result);
                int parsedVal              = 0;
                if (utils::toNumeric(resp, parsedVal) && parsedVal >= 0) {
                    result = parsedVal;
                    return true;
                }
            }
            return false;
        }


@@ 127,7 135,7 @@ namespace at
                utils::findAndReplaceAll(string, " ", "");
                utils::findAndReplaceAll(string, "\"", "");

                uint32_t freq = 0;
                int freq = 0;
                utils::toNumeric(string, freq);
                return freq;
            }


@@ 158,8 166,10 @@ namespace at
                utils::findAndReplaceAll(string, "\"", emptyString);
                utils::findAndReplaceAll(string, toRemove, emptyString);

                uint32_t band;
                utils::toNumeric(string, band);
                int band = 0;
                if (utils::toNumeric(string, band) && band < 0) {
                    return 0;
                }

                auto freq = lteFreqs.find(band);
                if (freq != lteFreqs.end()) {

M module-cellular/at/src/URC_CTZE.cpp => module-cellular/at/src/URC_CTZE.cpp +54 -8
@@ 3,13 3,21 @@

#include "../URC_CTZE.hpp"
#include <log/debug.hpp>
#include <module-utils/time/time_conversion.hpp>

#include <module-utils/date/include/date/date.h>

using namespace at::urc;

CTZE::CTZE(const std::string &val) : Any(val)
{}
{
    if (!is()) {
        return;
    }
    for (auto &t : tokens) {
        utils::findAndReplaceAll(t, "\"", "");
    }
}

auto CTZE::what() const noexcept -> std::string
{


@@ 21,6 29,47 @@ auto CTZE::isValid() const noexcept -> bool
    return tokens.size() == magic_enum::enum_count<Tokens>();
}

int CTZE::getTimeZoneOffset() const
{
    const std::string &tzOffsetToken = tokens[static_cast<uint32_t>(Tokens::GMTDifference)];

    int offsetInQuartersOfHour = 0;
    bool failed                = !utils::toNumeric(tzOffsetToken, offsetInQuartersOfHour);

    if (offsetInQuartersOfHour != std::clamp(offsetInQuartersOfHour, minTimezoneOffset, maxTimezoneOffset)) {
        offsetInQuartersOfHour = 0;
        failed                 = true;
    }

    if (failed) {
        LOG_ERROR("Failed to parse CTZE time zone offset: %s", tzOffsetToken.c_str());
    }

    int offsetInSeconds = offsetInQuartersOfHour * utils::time::minutesInQuarterOfHour * utils::time::secondsInMinute;

    return offsetInSeconds;
}

std::string CTZE::getTimeZoneString() const
{
    std::string timeZoneStr = tokens[static_cast<uint32_t>(Tokens::GMTDifference)] + "," +
                              tokens[static_cast<uint32_t>(Tokens::DaylightSavingsAdjustment)];
    timeZoneStr.erase(remove_if(timeZoneStr.begin(), timeZoneStr.end(), isspace), timeZoneStr.end());
    return timeZoneStr;
}

const struct tm CTZE::getGMTTime(void) const
{
    struct tm timeinfo = {};
    std::stringstream stream(tokens[static_cast<uint32_t>(Tokens::Date)] + "," +
                             tokens[static_cast<uint32_t>(Tokens::Time)]);
    stream >> std::get_time(&timeinfo, "%Y/%m/%d,%H:%M:%S");
    if (stream.fail()) {
        LOG_ERROR("Failed to parse CTZE time");
    }
    return timeinfo;
}

auto CTZE::getTimeInfo() const noexcept -> tm
{
    using namespace std::chrono;


@@ 28,20 77,17 @@ auto CTZE::getTimeInfo() const noexcept -> tm
    std::tm timeinfo{};
    if (isValid()) {
        std::string dateTimeStr(tokens[Tokens::Date] + "," + tokens[Tokens::Time]);
        utils::findAndReplaceAll(dateTimeStr, "\"", "");

        std::stringstream stream(dateTimeStr);
        date::sys_seconds tp;
        stream >> date::parse("%Y/%m/%d,%H:%M:%S", tp);

        auto gmtDifferenceStr = tokens[Tokens::GMTDifference];
        utils::findAndReplaceAll(gmtDifferenceStr, "\"", "");

        int gmtDifference                      = utils::getValue<int>(gmtDifferenceStr);
        constexpr auto minutesInQuartersOfHour = 15;
        constexpr auto secondsInMinutes        = 60;
        auto time = system_clock::to_time_t(tp) + gmtDifference * minutesInQuartersOfHour * secondsInMinutes;
        timeinfo  = *gmtime(&time);
        int gmtDifference = utils::getValue<int>(gmtDifferenceStr);
        auto time         = system_clock::to_time_t(tp) +
                    gmtDifference * utils::time::minutesInQuarterOfHour * utils::time::secondsInMinute;
        timeinfo = *gmtime(&time);
    }
    return timeinfo;
}

M module-cellular/test/unittest_URC.cpp => module-cellular/test/unittest_URC.cpp +33 -0
@@ 8,6 8,7 @@
#define CATCH_CONFIG_MAIN

#include <catch2/catch.hpp>
#include <module-utils/time/time_conversion.hpp>

#include "URC_QIND.hpp"
#include "URC_CUSD.hpp"


@@ 197,6 198,13 @@ TEST_CASE("+CTZE")
        std::stringstream ss;
        ss << std::put_time(&timeInfo, "%Y/%m/%d,%H:%M:%S");
        REQUIRE(ss.str() == "2020/10/21,15:49:57");

        REQUIRE(ctze.getTimeZoneOffset() == 8 * utils::time::minutesInQuarterOfHour * utils::time::secondsInMinute);
        REQUIRE(ctze.getTimeZoneString() == "+08,1");
        ss.str(std::string());
        timeInfo = ctze.getGMTTime();
        ss << std::put_time(&timeInfo, "%Y/%m/%d,%H:%M:%S");
        REQUIRE(ss.str() == "2020/10/21,13:49:57");
    }

    SECTION("ctze -08")


@@ 208,6 216,31 @@ TEST_CASE("+CTZE")
        std::ostringstream ss;
        ss << std::put_time(&timeInfo, "%Y/%m/%d,%H:%M:%S");
        REQUIRE(ss.str() == "2020/10/21,11:49:57");

        REQUIRE(ctze.getTimeZoneOffset() == -8 * utils::time::minutesInQuarterOfHour * utils::time::secondsInMinute);
        REQUIRE(ctze.getTimeZoneString() == "-08,1");
        ss.str(std::string());
        timeInfo = ctze.getGMTTime();
        ss << std::put_time(&timeInfo, "%Y/%m/%d,%H:%M:%S");
        REQUIRE(ss.str() == "2020/10/21,13:49:57");
    }

    SECTION("ctze -00")
    {
        auto ctze = at::urc::CTZE("+CTZE: \"-00\",0,\"2020/10/21,13:49:57\"");
        REQUIRE(ctze.is());
        REQUIRE(ctze.isValid());
        auto timeInfo = ctze.getTimeInfo();
        std::ostringstream ss;
        ss << std::put_time(&timeInfo, "%Y/%m/%d,%H:%M:%S");
        REQUIRE(ss.str() == "2020/10/21,13:49:57");

        REQUIRE(ctze.getTimeZoneOffset() == 0);
        REQUIRE(ctze.getTimeZoneString() == "-00,0");
        ss.str(std::string());
        timeInfo = ctze.getGMTTime();
        ss << std::put_time(&timeInfo, "%Y/%m/%d,%H:%M:%S");
        REQUIRE(ss.str() == "2020/10/21,13:49:57");
    }

    SECTION("too short")

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +13 -6
@@ 187,6 187,12 @@ ServiceCellular::~ServiceCellular()
    LOG_INFO("[ServiceCellular] Cleaning resources");
}

// this static function will be replaced by Settings API
static bool isSettingsAutomaticTimeSyncEnabled()
{
    return true;
}

void ServiceCellular::CallStateTimerHandler()
{
    std::shared_ptr<CellularRequestMessage> msg =


@@ 977,8 983,6 @@ std::optional<std::shared_ptr<CellularMessage>> ServiceCellular::identifyNotific
    std::string str(data.begin(), data.end());

    std::string logStr = utils::removeNewLines(str);
    LOG_DEBUG("Notification:: %s", logStr.c_str());

    if (auto ret = str.find("+CPIN: ") != std::string::npos) {
        /// TODO handle different sim statuses - i.e. no sim, sim error, sim puk, sim pin etc.
        if (str.find("NOT READY", ret) == std::string::npos) {


@@ 1075,8 1079,9 @@ std::optional<std::shared_ptr<CellularMessage>> ServiceCellular::identifyNotific
                                                             *message);
    }
    auto ctze = at::urc::CTZE(str);
    if (ctze.is()) {
        auto msg = std::make_shared<CellularTimeNotificationMessage>(ctze.getTimeInfo());
    if (ctze.is() && isSettingsAutomaticTimeSyncEnabled()) {
        auto msg = std::make_shared<CellularTimeNotificationMessage>(
            ctze.getGMTTime(), ctze.getTimeZoneOffset(), ctze.getTimeZoneString());
        sys::Bus::SendUnicast(msg, service::name::evt_manager, this);
        return std::nullopt;
    }


@@ 1594,8 1599,10 @@ bool ServiceCellular::handle_modem_on()
bool ServiceCellular::handle_URCReady()
{
    auto channel = cmux->get(TS0710::Channel::Commands);
    channel->cmd(at::AT::ENABLE_TIME_ZONE_UPDATE);
    channel->cmd(at::AT::SET_TIME_ZONE_REPORTING);
    if (isSettingsAutomaticTimeSyncEnabled()) {
        channel->cmd(at::AT::ENABLE_TIME_ZONE_UPDATE);
        channel->cmd(at::AT::SET_TIME_ZONE_REPORTING);
    }
    channel->cmd(at::AT::ENABLE_NETWORK_REGISTRATION_URC);
    LOG_DEBUG("%s", state.c_str());
    return true;

M module-services/service-cellular/messages/CellularMessage.hpp => module-services/service-cellular/messages/CellularMessage.hpp +25 -2
@@ 83,17 83,40 @@ class CellularNotificationMessage : public CellularMessage
class CellularTimeNotificationMessage : public CellularMessage
{
  private:
    struct tm time;
    std::optional<struct tm> time;
    std::optional<int> timeZoneGmtOff;
    std::optional<std::string> timeZoneString;

  public:
    CellularTimeNotificationMessage() = delete;
    explicit CellularTimeNotificationMessage(struct tm time, long int timeZoneGmtOff, std::string timeZoneString)
        : CellularMessage(MessageType::CellularTimeUpdated), time(time), timeZoneGmtOff(timeZoneGmtOff),
          timeZoneString(timeZoneString)
    {}

    explicit CellularTimeNotificationMessage(long int timeZoneGmtOff, std::string timeZoneString)
        : CellularMessage(MessageType::CellularTimeUpdated), timeZoneGmtOff(timeZoneGmtOff),
          timeZoneString(timeZoneString)
    {}

    explicit CellularTimeNotificationMessage(struct tm time)
        : CellularMessage(MessageType::CellularTimeUpdated), time(time)
    {}
    struct tm getTime(void)

    std::optional<struct tm> getTime(void)
    {
        return time;
    }

    std::optional<long int> getTimeZoneOffset(void)
    {
        return timeZoneGmtOff;
    }

    std::optional<const std::string> getTimeZoneString(void)
    {
        return timeZoneString;
    }
};
class CellularUSSDMessage : public CellularMessage
{

M module-services/service-db/test/CMakeLists.txt => module-services/service-db/test/CMakeLists.txt +1 -0
@@ 9,4 9,5 @@ add_catch2_executable(
                test-service-db-settings-api.cpp
        LIBS
                module-services
                module-utils
)

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +20 -3
@@ 41,6 41,7 @@
#include "service-audio/messages/AudioMessage.hpp"     // for AudioEventRequest
#include "service-evtmgr/messages/BatteryMessages.hpp" // for BatteryLevelMessage, BatteryPlugMessage
#include "service-evtmgr/messages/KbdMessage.hpp"      // for KbdMessage
#include "module-utils/time/time_conversion.hpp"       // for Time Zone handling

EventManager::EventManager(const std::string &name) : sys::Service(name)
{


@@ 57,6 58,17 @@ EventManager::~EventManager()
    }
}

// those static functions and variables will be replaced by Settings API
static std::string tzSet;
static void setSettingsTimeZone(const std::string &timeZone)
{
    tzSet = timeZone;
}
std::string getSettingsTimeZone()
{
    return tzSet;
}

// Invoked upon receiving data message
sys::Message_t EventManager::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
{


@@ 211,9 223,14 @@ sys::Message_t EventManager::DataReceivedHandler(sys::DataMessage *msgl, sys::Re
    else if (msgl->messageType == MessageType::CellularTimeUpdated) {
        auto msg = dynamic_cast<CellularTimeNotificationMessage *>(msgl);
        if (msg != nullptr) {
            auto time = msg->getTime();
            bsp::rtc_SetDateTime(&time);
            LOG_INFO("RTC set by network time.");
            if (auto time = msg->getTime(); time) {
                LOG_INFO("RTC set by network time.");
                bsp::rtc_SetDateTime(&time.value());
            }
            if (auto timeZoneOffset = msg->getTimeZoneOffset(); timeZoneOffset) {
                setSettingsTimeZone(msg->getTimeZoneString().value());
                utils::time::Time::setTimeZoneOffset(msg->getTimeZoneOffset().value());
            }
            auto notification = std::make_shared<sys::DataMessage>(MessageType::EVMTimeUpdated);
            sys::Bus::SendMulticast(notification, sys::BusChannels::ServiceEvtmgrNotifications, this);
        }

M module-utils/Utils.hpp => module-utils/Utils.hpp +2 -4
@@ 169,18 169,16 @@ namespace utils
        findAndReplaceAll(data, {{toSearch, replaceStr}});
    }

    static inline bool toNumeric(const std::string &text, uint32_t &value)
    static inline bool toNumeric(const std::string &text, int &value)
    {
        try {
            value = std::stoi(text);
            return true;
        }
        catch (const std::exception &e) {
            LOG_ERROR("toNumeric exception %s", e.what());
            return false;
        }

        return false;
        return true;
    }

    static inline uint32_t swapBytes(uint32_t toSwap)

M module-utils/test/unittest_utils.cpp => module-utils/test/unittest_utils.cpp +1 -1
@@ 71,7 71,7 @@ TEST_CASE("Split tests")
TEST_CASE("toNumeric tests")
{
    std::string inputStr1 = "2";
    uint32_t value;
    int value;

    auto ret = utils::toNumeric(inputStr1, value);
    REQUIRE(ret == true);

M module-utils/time/time_conversion.cpp => module-utils/time/time_conversion.cpp +21 -0
@@ 25,6 25,7 @@ namespace utils
    {

        Locale tlocale;
        static int msTimeGmtOff = 4 * utils::time::minutesInQuarterOfHour * utils::time::secondsInMinute;

        UTF8 Localer::get_replacement(Replacements val, const struct tm &timeinfo)
        {


@@ 48,6 49,16 @@ namespace utils
            return retval;
        }

        Timestamp::Timestamp()
        {
            auto err     = bsp::rtc_GetCurrentTimestamp(&time);
            auto curTime = time + utils::time::Time::getTimeZoneOffset();
            if (err) {
                LOG_ERROR("rtc_GetCurrentTimestamp failure!");
            }
            timeinfo = *localtime(&curTime);
        }

        void Timestamp::set_time(time_t newtime)
        {
            time     = newtime;


@@ 225,6 236,16 @@ namespace utils
            }
        }

        void Time::setTimeZoneOffset(int tzOffset)
        {
            msTimeGmtOff = tzOffset;
        }

        int Time::getTimeZoneOffset()
        {
            return msTimeGmtOff;
        };

        Duration::Duration(time_t duration) : duration(duration)
        {
            calculate();

M module-utils/time/time_conversion.hpp => module-utils/time/time_conversion.hpp +10 -11
@@ 16,9 16,10 @@ namespace utils
{
    namespace time
    {
        constexpr auto secondsInMinute = 60;
        constexpr auto minutesInHour   = 60;
        constexpr auto hoursInday      = 24;
        constexpr auto secondsInMinute        = 60;
        constexpr auto minutesInHour          = 60;
        constexpr auto hoursInday             = 24;
        constexpr auto minutesInQuarterOfHour = 15;
        constexpr auto secondsInHour   = minutesInHour * secondsInMinute;
        constexpr auto secondsInDay    = hoursInday * secondsInHour;
        constexpr auto milisecondsInSecond = 1000;


@@ 70,14 71,7 @@ namespace utils
            }

          public:
            Timestamp()
            {
                auto err = bsp::rtc_GetCurrentTimestamp(&time);
                if (err) {
                    LOG_ERROR("rtc_GetCurrentTimestamp failure!");
                }
                timeinfo = *localtime(&time);
            }
            Timestamp();
            Timestamp(time_t newtime) : time(newtime)
            {
                timeinfo = *localtime(&time);


@@ 195,6 189,11 @@ namespace utils
          public:
            Time(time_t val = 0, bool date_format_long = true) : DateTime(val, date_format_long){};
            virtual UTF8 str(std::string format = "") final;

            /// set time zone offset including adjustment for daylight saving
            static void setTimeZoneOffset(int tzOffset);
            // get time zone offset including adjustment for daylight saving
            static int getTimeZoneOffset();
        };

        class Duration