~aleteoryx/muditaos

65119cb495f3417d8f5ef4f028990cd09ab8fbc9 — Piotr Tański 5 years ago e1400ae
[EGD-5027] Services synchronization introduced

Dependencies between services are now supported.
50 files changed, 1048 insertions(+), 253 deletions(-)

M module-apps/ApplicationLauncher.hpp
M module-apps/application-settings-new/windows/AddDeviceWindow.cpp
M module-apps/application-settings/windows/BtScanWindow.cpp
M module-db/Database/Database.cpp
M module-services/service-antenna/ServiceAntenna.cpp
M module-services/service-antenna/api/AntennaServiceAPI.cpp
M module-services/service-antenna/service-antenna/ServiceAntenna.hpp
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp
M module-services/service-audio/AudioServiceAPI.cpp
M module-services/service-audio/ServiceAudio.cpp
M module-services/service-audio/service-audio/ServiceAudio.hpp
M module-services/service-bluetooth/CMakeLists.txt
M module-services/service-bluetooth/ServiceBluetooth.cpp
A module-services/service-bluetooth/service-bluetooth/BluetoothMessage.cpp
M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp
M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp
M module-services/service-cellular/service-cellular/ServiceCellular.hpp
M module-services/service-db/ServiceDB.cpp
M module-services/service-db/ServiceDB.hpp
M module-services/service-db/test/test-settings/test-service-db-settings-api.cpp
M module-services/service-desktop/service-desktop/ServiceDesktop.hpp
M module-services/service-eink/ServiceEink.hpp
M module-services/service-evtmgr/service-evtmgr/EventManager.hpp
M module-services/service-fileindexer/Constants.hpp
M module-services/service-fileindexer/ServiceFileIndexer.hpp
M module-services/service-fota/FotaServiceAPI.cpp
M module-services/service-fota/ServiceFota.cpp
M module-services/service-fota/service-fota/ServiceFota.hpp
M module-services/service-gui/ServiceGUI.hpp
M module-services/service-lwip/ServiceLwIP.cpp
M module-services/service-lwip/service-lwip/ServiceLwIP.hpp
M module-services/service-time/ServiceTime.hpp
M module-sys/CMakeLists.txt
M module-sys/Service/Service.hpp
A module-sys/Service/ServiceCreator.hpp
A module-sys/Service/ServiceManifest.hpp
A module-sys/SystemManager/DependencyGraph.cpp
A module-sys/SystemManager/DependencyGraph.hpp
M module-sys/SystemManager/SystemManager.cpp
M module-sys/SystemManager/SystemManager.hpp
A module-sys/SystemManager/doc/ServicesSynchronization.md
A module-sys/SystemManager/doc/services_synchronization.puml
A module-sys/SystemManager/graph/TopologicalSort.cpp
A module-sys/SystemManager/graph/TopologicalSort.hpp
A module-sys/SystemManager/tests/CMakeLists.txt
A module-sys/SystemManager/tests/test-DependencyGraph.cpp
A module-sys/SystemManager/tests/tests-main.cpp
A services_synchronization.png
M source/main.cpp
M module-apps/ApplicationLauncher.hpp => module-apps/ApplicationLauncher.hpp +3 -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


@@ 79,14 79,14 @@ namespace app
        {
            parent = (caller == nullptr ? "" : caller->GetName());
            handle = std::make_shared<T>(name, parent);
            return sys::SystemManager::CreateService(handle, caller);
            return sys::SystemManager::RunService(handle, caller);
        }

        bool runBackground(sys::Service *caller) override
        {
            parent = (caller == nullptr ? "" : caller->GetName());
            handle = std::make_shared<T>(name, parent, true);
            return sys::SystemManager::CreateService(handle, caller);
            return sys::SystemManager::RunService(handle, caller);
        }
    };


M module-apps/application-settings-new/windows/AddDeviceWindow.cpp => module-apps/application-settings-new/windows/AddDeviceWindow.cpp +5 -0
@@ 9,6 9,11 @@
#include <i18n/i18n.hpp>
#include <utility>

extern "C"
{
#include <module-bluetooth/lib/btstack/src/btstack_util.h>
}

namespace gui
{


M module-apps/application-settings/windows/BtScanWindow.cpp => module-apps/application-settings/windows/BtScanWindow.cpp +5 -0
@@ 16,6 16,11 @@

#include <Style.hpp>

extern "C"
{
#include <module-bluetooth/lib/btstack/src/btstack_util.h>
}

namespace gui
{


M module-db/Database/Database.cpp => module-db/Database/Database.cpp +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

#include "Database.hpp"


@@ 70,7 70,7 @@ Database::Database(const char *name)
{
    LOG_INFO("Creating database: %s", dbName.c_str());
    if (const auto rc = sqlite3_open(name, &dbConnection); rc != SQLITE_OK) {
        LOG_ERROR("SQLITE INITIALIZATION ERROR! rc=%d dbName=%s", rc, name);
        LOG_ERROR("SQLITE INITIALIZATION ERROR! rc=%d ( %s ) dbName=%s", rc, sqlite3_errstr(rc), name);
        throw DatabaseInitialisationError{"Failed to initialize the sqlite db"};
    }


M module-services/service-antenna/ServiceAntenna.cpp => module-services/service-antenna/ServiceAntenna.cpp +4 -6
@@ 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 "service-antenna/ServiceAntenna.hpp"


@@ 25,8 25,6 @@
#include <string>
#include <vector>

const char *ServiceAntenna::serviceName = "ServiceAntenna";

namespace antenna
{
    const char *c_str(antenna::State state)


@@ 55,9 53,9 @@ namespace antenna
    }
} // namespace antenna

ServiceAntenna::ServiceAntenna() : sys::Service(serviceName)
ServiceAntenna::ServiceAntenna() : sys::Service(service::name::antenna)
{
    LOG_INFO("[%s] Initializing", serviceName);
    LOG_INFO("[%s] Initializing", service::name::antenna);

    timer = std::make_unique<sys::Timer>("Antena", this, 5000, sys::Timer::Type::Periodic);
    timer->connect([&](sys::Timer &) {


@@ 80,7 78,7 @@ ServiceAntenna::ServiceAntenna() : sys::Service(serviceName)

ServiceAntenna::~ServiceAntenna()
{
    LOG_INFO("[%s] Cleaning resources", serviceName);
    LOG_INFO("[%s] Cleaning resources", service::name::antenna);
}

// Invoked upon receiving data message

M module-services/service-antenna/api/AntennaServiceAPI.cpp => module-services/service-antenna/api/AntennaServiceAPI.cpp +3 -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

#include <service-antenna/AntennaServiceAPI.hpp>


@@ 29,7 29,7 @@ namespace AntennaServiceAPI
    bool LockRequest(sys::Service *serv, antenna::lockState request)
    {
        auto msg = std::make_shared<AntennaLockRequestMessage>(MessageType::AntennaLockService, request);
        auto ret = serv->bus.sendUnicast(msg, ServiceAntenna::serviceName, 5000);
        auto ret = serv->bus.sendUnicast(msg, service::name::antenna, 5000);
        if (ret.first == sys::ReturnCodes::Success) {

            return true;


@@ 40,7 40,7 @@ namespace AntennaServiceAPI
    bool GetLockState(sys::Service *serv, antenna::lockState &response)
    {
        auto msg = std::make_shared<AntennaLockRequestMessage>(MessageType::AntennaGetLockState);
        auto ret = serv->bus.sendUnicast(msg, ServiceAntenna::serviceName, 5000);
        auto ret = serv->bus.sendUnicast(msg, service::name::antenna, 5000);
        if (ret.first == sys::ReturnCodes::Success) {
            auto responseMsg = dynamic_cast<AntennaLockRequestResponse *>(ret.second.get());
            if (responseMsg != nullptr) {

M module-services/service-antenna/service-antenna/ServiceAntenna.hpp => module-services/service-antenna/service-antenna/ServiceAntenna.hpp +21 -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


@@ 10,6 10,7 @@
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <Service/Worker.hpp>
#include <service-db/DBServiceName.hpp>

#include <algorithm>
#include <cassert>


@@ 28,6 29,11 @@ namespace utils
    } // namespace state
} // namespace utils

namespace service::name
{
    constexpr inline auto antenna = "ServiceAntenna";
} // namespace service::name

namespace antenna
{
    enum class State


@@ 76,8 82,6 @@ class ServiceAntenna : public sys::Service
    bool suspended = false;

  public:
    static const char *serviceName;

    ServiceAntenna();
    ~ServiceAntenna();



@@ 102,3 106,17 @@ class ServiceAntenna : public sys::Service
    bool csqChangeStateHandler(void);
    bool lockedStateHandler(void);
};

namespace sys
{
    template <> struct ManifestTraits<ServiceAntenna>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::antenna;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +0 -15
@@ 157,7 157,6 @@ namespace app::manager
            },
            ::settings::SettingsScope::Global);

        startSystemServices();
        startBackgroundApplications();
        if (auto app = getApplication(rootApplicationName); app != nullptr) {
            Controller::sendAction(this, actions::Home);


@@ 166,20 165,6 @@ namespace app::manager
        return sys::ReturnCodes::Success;
    }

    void ApplicationManager::startSystemServices()
    {
        if (bool ret = sys::SystemManager::CreateService(
                std::make_shared<service::gui::ServiceGUI>(service::name::gui, GetName()), this);
            !ret) {
            LOG_ERROR("Failed to initialize GUI service");
        }
        if (bool ret = sys::SystemManager::CreateService(
                std::make_shared<service::eink::ServiceEink>(service::name::eink, GetName()), this);
            !ret) {
            LOG_ERROR("Failed to initialize EInk service");
        }
    }

    void ApplicationManager::suspendSystemServices()
    {
        sys::SystemManager::SuspendService(service::name::gui, this);

M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp +17 -1
@@ 22,7 22,10 @@
#include <string_view>
#include <vector>

#include <service-db/DBServiceName.hpp>
#include <service-db/Settings.hpp>
#include <service-gui/Common.hpp>
#include <service-eink/Common.hpp>

namespace app
{


@@ 100,7 103,6 @@ namespace app::manager

      private:
        auto startApplication(ApplicationHandle &app) -> bool;
        void startSystemServices();
        void startBackgroundApplications();
        void rebuildActiveApplications();
        void suspendSystemServices();


@@ 158,3 160,17 @@ namespace app::manager
        std::string displayLanguage;
    };
} // namespace app::manager

namespace sys
{
    template <> struct ManifestTraits<app::manager::ApplicationManager>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = app::manager::ApplicationManager::ServiceName;
            manifest.dependencies = {service::name::db, service::name::gui, service::name::eink};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-audio/AudioServiceAPI.cpp => module-services/service-audio/AudioServiceAPI.cpp +13 -13
@@ 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 "service-audio/AudioServiceAPI.hpp"


@@ 25,7 25,7 @@ namespace AudioServiceAPI
        {
            auto msgType = static_cast<int>(msg->type);
            LOG_DEBUG("Msg type %d", msgType);
            auto ret = serv->bus.sendUnicast(msg, ServiceAudio::serviceName, sys::BusProxy::defaultTimeout);
            auto ret = serv->bus.sendUnicast(msg, service::name::audio, sys::BusProxy::defaultTimeout);
            if (ret.first == sys::ReturnCodes::Success) {
                if (auto resp = std::dynamic_pointer_cast<AudioResponseMessage>(ret.second)) {
                    LOG_DEBUG("Msg type %d done", msgType);


@@ 42,19 42,19 @@ namespace AudioServiceAPI
    bool PlaybackStart(sys::Service *serv, const audio::PlaybackType &playbackType, const std::string &fileName)
    {
        auto msg = std::make_shared<AudioStartPlaybackRequest>(fileName, playbackType);
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool RecordingStart(sys::Service *serv, const std::string &fileName)
    {
        auto msg = std::make_shared<AudioStartRecorderRequest>(fileName);
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool RoutingStart(sys::Service *serv)
    {
        auto msg = std::make_shared<AudioStartRoutingRequest>();
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool Stop(sys::Service *serv, const std::vector<audio::PlaybackType> &stopVec)


@@ 63,43 63,43 @@ namespace AudioServiceAPI
            return true;
        }
        auto msg = std::make_shared<AudioStopRequest>(stopVec);
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool Stop(sys::Service *serv, const audio::Token &token)
    {
        auto msg = std::make_shared<AudioStopRequest>(token);
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool StopAll(sys::Service *serv)
    {
        auto msg = std::make_shared<AudioStopRequest>();
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool Pause(sys::Service *serv, const audio::Token &token)
    {
        auto msg = std::make_shared<AudioPauseRequest>(token);
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool Resume(sys::Service *serv, const audio::Token &token)
    {
        auto msg = std::make_shared<AudioResumeRequest>(token);
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool SendEvent(sys::Service *serv, std::shared_ptr<audio::Event> evt)
    {
        auto msg = std::make_shared<AudioEventRequest>(std::move(evt));
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool SendEvent(sys::Service *serv, audio::EventType eType, audio::Event::DeviceState state)
    {
        auto msg = std::make_shared<AudioEventRequest>(eType, state);
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    template <typename T>


@@ 164,7 164,7 @@ namespace AudioServiceAPI
    bool KeyPressed(sys::Service *serv, const int step)
    {
        auto msg = std::make_shared<AudioKeyPressedRequest>(step);
        return serv->bus.sendUnicast(msg, ServiceAudio::serviceName);
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

} // namespace AudioServiceAPI

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +2 -4
@@ 13,12 13,10 @@

#include <type_traits>

const char *ServiceAudio::serviceName = "ServiceAudio";

using namespace audio;

ServiceAudio::ServiceAudio()
    : sys::Service(serviceName, "", 4096 * 2, sys::ServicePriority::Idle),
    : sys::Service(service::name::audio, "", 4096 * 2, sys::ServicePriority::Idle),
      audioMux([this](auto... params) { return this->AudioServicesCallback(params...); }),
      settingsProvider(std::make_unique<settings::Settings>(this))
{


@@ 426,7 424,7 @@ void ServiceAudio::HandleNotification(const AudioNotificationMessage::Type &type
        }
        else {
            auto newMsg = std::make_shared<AudioStopRequest>(token);
            bus.sendUnicast(newMsg, ServiceAudio::serviceName);
            bus.sendUnicast(newMsg, service::name::audio);
        }
        return;
    }

M module-services/service-audio/service-audio/ServiceAudio.hpp => module-services/service-audio/service-audio/ServiceAudio.hpp +21 -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


@@ 12,6 12,7 @@
#include <Utils.hpp>

#include <service-db/DBServiceAPI.hpp>
#include <service-db/DBServiceName.hpp>
#include <service-db/QueryMessage.hpp>

#include <functional>


@@ 21,6 22,11 @@ namespace settings
    class Settings;
}

namespace service::name
{
    constexpr inline auto audio = "ServiceAudio";
} // namespace service::name

class ServiceAudio : public sys::Service
{
  public:


@@ 36,8 42,6 @@ class ServiceAudio : public sys::Service

    sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override final;

    static const char *serviceName;

  private:
    enum class VibrationType
    {


@@ 95,3 99,17 @@ class ServiceAudio : public sys::Service

    void settingsChanged(const std::string &name, std::string value);
};

namespace sys
{
    template <> struct ManifestTraits<ServiceAudio>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::audio;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-bluetooth/CMakeLists.txt => module-services/service-bluetooth/CMakeLists.txt +1 -0
@@ 6,6 6,7 @@ set(SOURCES
    ServiceBluetooth.cpp
    service-bluetooth/SettingsHolder.cpp
    service-bluetooth/SettingsSerializer.cpp
    service-bluetooth/BluetoothMessage.cpp
)

add_library(${PROJECT_NAME} STATIC ${SOURCES})

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +0 -1
@@ 1,7 1,6 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Constants.hpp"
#include "service-bluetooth/ServiceBluetooth.hpp"
#include "service-bluetooth/BluetoothMessage.hpp"


A module-services/service-bluetooth/service-bluetooth/BluetoothMessage.cpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.cpp +14 -0
@@ 0,0 1,14 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BluetoothMessage.hpp"

extern "C"
{
#include <module-bluetooth/lib/btstack/src/btstack_util.h>
}

BluetoothAddrMessage::BluetoothAddrMessage(std::string addr) : sys::DataMessage(MessageType::BluetoothAddrResult)
{
    sscanf_bd_addr(addr.c_str(), this->addr);
}

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp +1 -9
@@ 14,11 14,6 @@
#include <utility>
#include <vector>

extern "C"
{
#include <module-bluetooth/lib/btstack/src/btstack_util.h>
};

struct BluetoothStatus
{
    enum class State


@@ 80,10 75,7 @@ class BluetoothAddrMessage : public sys::DataMessage
{
  public:
    bd_addr_t addr;
    explicit BluetoothAddrMessage(std::string addr) : sys::DataMessage(MessageType::BluetoothAddrResult)
    {
        sscanf_bd_addr(addr.c_str(), this->addr);
    };
    explicit BluetoothAddrMessage(std::string addr);
    ~BluetoothAddrMessage() override = default;
};


M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +19 -2
@@ 1,13 1,16 @@
// 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

#include <Bluetooth/BluetoothWorker.hpp>
#include "Constants.hpp"
#include <Service/Common.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include "service-bluetooth/SettingsHolder.hpp"
#include <service-db/DBServiceName.hpp>
#include <service-audio/ServiceAudio.hpp>
#include <module-bluetooth/Bluetooth/CommandHandler.hpp>
#include "BluetoothMessage.hpp"

#include <memory> // for unique_ptr


@@ 36,3 39,17 @@ class ServiceBluetooth : public sys::Service
  private:
    std::unique_ptr<BluetoothWorker> worker;
};

namespace sys
{
    template <> struct ManifestTraits<ServiceBluetooth>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::bluetooth;
            manifest.dependencies = {service::name::db, service::name::audio};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +15 -0
@@ 27,6 27,7 @@
#include <vector>   // for vector
#include <service-db/Settings.hpp>
#include <module-services/service-db/agents/settings/SystemSettings.hpp>
#include <service-db/DBServiceName.hpp>

#include <cstdint>
#include <memory>


@@ 310,3 311,17 @@ class ServiceCellular : public sys::Service
    void apnListChanged(const std::string &value);
    bool volteOn = false;
};

namespace sys
{
    template <> struct ManifestTraits<ServiceCellular>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = ServiceCellular::serviceName;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-db/ServiceDB.cpp => module-services/service-db/ServiceDB.cpp +1 -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

#include "ServiceDB.hpp"


@@ 11,7 11,6 @@
#include "service-db/DBSMSMessage.hpp"
#include "service-db/DBSMSTemplateMessage.hpp"
#include "service-db/DBServiceMessage.hpp"
#include "service-db/DBServiceName.hpp"
#include "service-db/DBThreadMessage.hpp"
#include "service-db/QueryMessage.hpp"
#include "service-db/DatabaseAgent.hpp"

M module-services/service-db/ServiceDB.hpp => module-services/service-db/ServiceDB.hpp +20 -1
@@ 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


@@ 7,6 7,9 @@
#include "agents/settings/SettingsAgent.hpp"
#include "service-db/DatabaseAgent.hpp"
#include "service-db/DBNotificationMessage.hpp"
#include "service-db/DBServiceName.hpp"

#include "service-fileindexer/Constants.hpp"

#include <Common/Query.hpp>
#include <Interface/AlarmsRecord.hpp>


@@ 91,3 94,19 @@ class ServiceDB : public sys::Service
    bool StoreIntoBackup(const std::string &backupPath);
    void sendUpdateNotification(db::Interface::Name interface, db::Query::Type type);
};

namespace sys
{
    template <> struct ManifestTraits<ServiceDB>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name = service::name::db;
#if ENABLE_FILEINDEXER_SERVICE
            manifest.dependencies = {service::name::file_indexer.data()};
#endif
            return manifest;
        }
    };
} // namespace sys

M module-services/service-db/test/test-settings/test-service-db-settings-api.cpp => module-services/service-db/test/test-settings/test-service-db-settings-api.cpp +64 -62
@@ 26,7 26,7 @@ TEST_CASE("SettingsApi")

    SECTION("variable/profile/mode register/set/get/unregister")
    {
        auto manager = std::make_shared<sys::SystemManager>();
        auto manager = std::make_shared<sys::SystemManager>(std::vector<std::unique_ptr<sys::BaseServiceCreator>>{});
        std::shared_ptr<settings::MyService> varWritter;
        std::shared_ptr<settings::MyService> varReader;
        std::shared_ptr<settings::AppTest> testVar;


@@ 38,67 38,69 @@ TEST_CASE("SettingsApi")
        std::shared_ptr<settings::AppTestProfileMode> testMode;
        std::shared_ptr<std::mutex> testStart;

        manager->StartSystem([manager,
                              &varWritter,
                              &varReader,
                              &testVar,
                              &profWritter,
                              &profReader,
                              &testProf,
                              &modeWritter,
                              &modeReader,
                              &testMode,
                              &testStart]() {
            // preliminary
            testStart = std::make_shared<std::mutex>();
            testStart->lock();
            std::cout << "start thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
            auto ret = sys::SystemManager::CreateService(std::make_shared<EventManager>(service::name::evt_manager),
                                                         manager.get());
            ret &= sys::SystemManager::CreateService(std::make_shared<ServiceDB>(), manager.get());

            varWritter = std::make_shared<settings::MyService>("writterVar");
            varReader  = std::make_shared<settings::MyService>("readerVar");

            ret &= sys::SystemManager::CreateService(varWritter, manager.get());
            ret &= sys::SystemManager::CreateService(varReader, manager.get());

            testVar = std::make_shared<settings::AppTest>("appTest", varWritter, varReader, testStart);
            ret &= sys::SystemManager::CreateService(testVar, manager.get());

            profWritter = std::make_shared<settings::ServiceProfile>("writterProf");
            profReader  = std::make_shared<settings::ServiceProfile>("readerProf");

            ret &= sys::SystemManager::CreateService(profWritter, manager.get());
            ret &= sys::SystemManager::CreateService(profReader, manager.get());

            testProf =
                std::make_shared<settings::AppTestProfileMode>("appTestProfile", profWritter, profReader, testStart);
            ret &= sys::SystemManager::CreateService(testProf, manager.get());

            modeWritter = std::make_shared<settings::ServiceMode>("writterMode");
            modeReader  = std::make_shared<settings::ServiceMode>("readerMode");

            ret &= sys::SystemManager::CreateService(modeWritter, manager.get());
            ret &= sys::SystemManager::CreateService(modeReader, manager.get());

            testMode =
                std::make_shared<settings::AppTestProfileMode>("appTestMode", modeWritter, modeReader, testStart);
            ret &= sys::SystemManager::CreateService(testMode, manager.get());

            std::cout << "koniec start thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
            testStart->unlock();
            auto msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
            manager->bus.sendUnicast(std::move(msgStart), "appTest");

            msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
            manager->bus.sendUnicast(std::move(msgStart), "appTestProfile");

            msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
            manager->bus.sendUnicast(std::move(msgStart), "appTestMode");

            return ret;
        });
        manager->StartSystem(
            nullptr,
            [manager,
             &varWritter,
             &varReader,
             &testVar,
             &profWritter,
             &profReader,
             &testProf,
             &modeWritter,
             &modeReader,
             &testMode,
             &testStart]() {
                // preliminary
                testStart = std::make_shared<std::mutex>();
                testStart->lock();
                std::cout << "start thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
                auto ret = sys::SystemManager::RunService(std::make_shared<EventManager>(service::name::evt_manager),
                                                          manager.get());
                ret &= sys::SystemManager::RunService(std::make_shared<ServiceDB>(), manager.get());

                varWritter = std::make_shared<settings::MyService>("writterVar");
                varReader  = std::make_shared<settings::MyService>("readerVar");

                ret &= sys::SystemManager::RunService(varWritter, manager.get());
                ret &= sys::SystemManager::RunService(varReader, manager.get());

                testVar = std::make_shared<settings::AppTest>("appTest", varWritter, varReader, testStart);
                ret &= sys::SystemManager::RunService(testVar, manager.get());

                profWritter = std::make_shared<settings::ServiceProfile>("writterProf");
                profReader  = std::make_shared<settings::ServiceProfile>("readerProf");

                ret &= sys::SystemManager::RunService(profWritter, manager.get());
                ret &= sys::SystemManager::RunService(profReader, manager.get());

                testProf = std::make_shared<settings::AppTestProfileMode>(
                    "appTestProfile", profWritter, profReader, testStart);
                ret &= sys::SystemManager::RunService(testProf, manager.get());

                modeWritter = std::make_shared<settings::ServiceMode>("writterMode");
                modeReader  = std::make_shared<settings::ServiceMode>("readerMode");

                ret &= sys::SystemManager::RunService(modeWritter, manager.get());
                ret &= sys::SystemManager::RunService(modeReader, manager.get());

                testMode =
                    std::make_shared<settings::AppTestProfileMode>("appTestMode", modeWritter, modeReader, testStart);
                ret &= sys::SystemManager::RunService(testMode, manager.get());

                std::cout << "koniec start thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
                testStart->unlock();
                auto msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
                manager->bus.sendUnicast(std::move(msgStart), "appTest");

                msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
                manager->bus.sendUnicast(std::move(msgStart), "appTestProfile");

                msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
                manager->bus.sendUnicast(std::move(msgStart), "appTestMode");

                return ret;
            });

        // start application
        cpp_freertos::Thread::StartScheduler();

M module-services/service-desktop/service-desktop/ServiceDesktop.hpp => module-services/service-desktop/service-desktop/ServiceDesktop.hpp +16 -5
@@ 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


@@ 13,10 13,7 @@
#include "Constants.hpp"
#include "WorkerDesktop.hpp"
#include <endpoints/update/UpdateMuditaOS.hpp>
#include <Service/Common.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <memory>
#include <service-db/DBServiceName.hpp>

namespace settings
{


@@ 50,3 47,17 @@ class ServiceDesktop : public sys::Service
  private:
    std::unique_ptr<settings::Settings> settings;
};

namespace sys
{
    template <> struct ManifestTraits<ServiceDesktop>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::service_desktop;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-eink/ServiceEink.hpp => module-services/service-eink/ServiceEink.hpp +19 -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


@@ 9,6 9,9 @@

#include "EinkDisplay.hpp"

#include <service-db/DBServiceName.hpp>
#include <service-gui/Common.hpp>

#include <chrono>
#include <cstdint>
#include <string>


@@ 18,7 21,7 @@ namespace service::eink
    class ServiceEink : public sys::Service
    {
      public:
        explicit ServiceEink(const std::string &name, std::string parent = {});
        explicit ServiceEink(const std::string &name = service::name::eink, std::string parent = {});

        sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *response) override;
        sys::ReturnCodes InitHandler() override;


@@ 60,3 63,17 @@ namespace service::eink
         */
    };
} // namespace service::eink

namespace sys
{
    template <> struct ManifestTraits<service::eink::ServiceEink>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::eink;
            manifest.dependencies = {service::name::db, service::name::gui};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-evtmgr/service-evtmgr/EventManager.hpp => module-services/service-evtmgr/service-evtmgr/EventManager.hpp +20 -2
@@ 1,8 1,10 @@
// 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

#include "Constants.hpp"

#include <MessageType.hpp>
#include <Service/Common.hpp>
#include <Service/Message.hpp>


@@ 13,6 15,8 @@
#include <bsp/keypad_backlight/keypad_backlight.hpp>
#include <screen-light-control/ScreenLightControl.hpp>

#include <service-db/DBServiceName.hpp>

#include <cstdint>
#include <memory>
#include <string>


@@ 44,7 48,7 @@ class EventManager : public sys::Service
    std::unique_ptr<screen_light_control::ScreenLightControl> screenLightControl;

  public:
    EventManager(const std::string &name);
    EventManager(const std::string &name = service::name::evt_manager);
    ~EventManager();

    sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;


@@ 62,3 66,17 @@ class EventManager : public sys::Service
     */
    static bool messageSetApplication(sys::Service *sender, const std::string &applicationName);
};

namespace sys
{
    template <> struct ManifestTraits<EventManager>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::evt_manager;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-fileindexer/Constants.hpp => module-services/service-fileindexer/Constants.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


@@ 7,4 7,4 @@
namespace service::name
{
    inline constexpr std::string_view file_indexer = "FileIndexer";
} // namespace service::name
\ No newline at end of file
} // namespace service::name

M module-services/service-fileindexer/ServiceFileIndexer.hpp => module-services/service-fileindexer/ServiceFileIndexer.hpp +17 -2
@@ 1,9 1,11 @@
// 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

#include <Service/Service.hpp>

#include "Constants.hpp"
#include "StartupIndexer.hpp"

namespace service


@@ 12,7 14,7 @@ namespace service
    class ServiceFileIndexer final : public sys::Service
    {
      public:
        ServiceFileIndexer(const std::string_view name);
        ServiceFileIndexer(const std::string_view name = service::name::file_indexer);
        virtual ~ServiceFileIndexer()                  = default;
        ServiceFileIndexer(const ServiceFileIndexer &) = delete;
        ServiceFileIndexer &operator=(const ServiceFileIndexer &) = delete;


@@ 30,3 32,16 @@ namespace service
    };

}; // namespace service

namespace sys
{
    template <> struct ManifestTraits<service::ServiceFileIndexer>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name = service::name::file_indexer;
            return manifest;
        }
    };
} // namespace sys

M module-services/service-fota/FotaServiceAPI.cpp => module-services/service-fota/FotaServiceAPI.cpp +7 -7
@@ 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 "service-fota/FotaMessages.hpp"


@@ 19,21 19,21 @@ namespace FotaService
    {
        LOG_DEBUG("FOTA - Internet Config called");
        std::shared_ptr<ConfigureAPNMessage> msg = std::make_shared<ConfigureAPNMessage>(config);
        return serv->bus.sendUnicast(std::move(msg), FotaService::Service::serviceName);
        return serv->bus.sendUnicast(std::move(msg), service::name::fota);
    }

    bool API::Connect(sys::Service *serv)
    {
        LOG_DEBUG("FOTA - Internet connection called");
        auto msg = std::make_shared<ConnectMessage>();
        return serv->bus.sendUnicast(std::move(msg), FotaService::Service::serviceName);
        return serv->bus.sendUnicast(std::move(msg), service::name::fota);
    }

    bool API::Disconnect(sys::Service *serv)
    {
        std::shared_ptr<InternetRequestMessage> msg =
            std::make_shared<InternetRequestMessage>(MessageType::FotaInternetDisconnect);
        return serv->bus.sendUnicast(std::move(msg), FotaService::Service::serviceName);
        return serv->bus.sendUnicast(std::move(msg), service::name::fota);
    }

    void API::HTTPGET(sys::Service *serv, const std::string &url)


@@ 42,7 42,7 @@ namespace FotaService
        std::shared_ptr<HTTPRequestMessage> msg = std::make_shared<HTTPRequestMessage>();
        msg->url                                = url;
        msg->method                             = FotaService::HTTPMethod::GET;
        serv->bus.sendUnicast(std::move(msg), FotaService::Service::serviceName);
        serv->bus.sendUnicast(std::move(msg), service::name::fota);
    }

    void API::FotaStart(sys::Service *serv, const std::string &url)


@@ 52,7 52,7 @@ namespace FotaService

        msg->url = url;

        serv->bus.sendUnicast(std::move(msg), FotaService::Service::serviceName);
        serv->bus.sendUnicast(std::move(msg), service::name::fota);
    }

    void API::sendRawProgress(sys::Service *serv, const std::string &rawQind)


@@ 60,7 60,7 @@ namespace FotaService
        LOG_DEBUG("Fota sending Raw progress");
        std::shared_ptr<FotaService::FOTARawProgress> msg = std::make_shared<FotaService::FOTARawProgress>();
        msg->qindRaw                                      = rawQind;
        serv->bus.sendUnicast(std::move(msg), FotaService::Service::serviceName);
        serv->bus.sendUnicast(std::move(msg), service::name::fota);
    }

    std::string APN::toString(APN::AuthMethod authMethod)

M module-services/service-fota/ServiceFota.cpp => module-services/service-fota/ServiceFota.cpp +4 -6
@@ 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 "service-fota/FotaMessages.hpp"


@@ 34,15 34,13 @@

namespace FotaService
{

    const char *Service::serviceName     = "ServiceFota";
    const TickType_t defaultTimer        = 1000;
    const uint32_t QIDEACTTimeout        = 40000;
    const uint32_t QIACTTimeout          = 150000;
    const char *httpErrorCode200         = "200";
    const uint32_t httpErrorCodeValue200 = 200;

    Service::Service() : sys::Service(serviceName)
    Service::Service() : sys::Service(service::name::fota)
    {
        LOG_INFO("[ServiceFota] Initializing");



@@ 53,7 51,7 @@ namespace FotaService
        connectionTimer->connect([&](sys::Timer &) {
            std::shared_ptr<InternetRequestMessage> msg =
                std::make_shared<InternetRequestMessage>(MessageType::CellularListCurrentCalls);
            bus.sendUnicast(msg, Service::serviceName);
            bus.sendUnicast(msg, service::name::fota);
        });
        registerMessageHandlers();
    }


@@ 598,7 596,7 @@ namespace FotaService
        std::shared_ptr<sys::ResponseMessage> responseMsg;

        LOG_DEBUG("%s: DataRecieve: bus:%d | message:%d",
                  serviceName,
                  service::name::fota,
                  static_cast<int>(msgl->channel),
                  static_cast<int>(msgl->messageType));
        return (responseMsg ? responseMsg : std::make_shared<sys::ResponseMessage>());

M module-services/service-fota/service-fota/ServiceFota.hpp => module-services/service-fota/service-fota/ServiceFota.hpp +22 -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


@@ 13,6 13,8 @@
#include <Service/Service.hpp>
#include <service-cellular/ServiceCellular.hpp>

#include <service-db/DBServiceName.hpp>

#include <cstdint>
#include <memory>
#include <sstream>


@@ 26,6 28,11 @@ namespace sys
    class Timer;
} // namespace sys

namespace service::name
{
    constexpr inline auto fota = "ServiceFota";
} // namespace service::name

namespace FotaService
{



@@ 60,8 67,6 @@ namespace FotaService

        void registerMessageHandlers();

        static const char *serviceName;

      private:
        /** Get access to data channel
         */


@@ 121,3 126,17 @@ namespace FotaService
    };

} // namespace FotaService

namespace sys
{
    template <> struct ManifestTraits<FotaService::Service>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::fota;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-gui/ServiceGUI.hpp => module-services/service-gui/ServiceGUI.hpp +19 -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


@@ 9,10 9,13 @@
#include <Service/Service.hpp>
#include <Service/Timer.hpp>

#include <service-db/DBServiceName.hpp>

#include "messages/RenderingFinished.hpp"

#include "ContextPool.hpp"
#include "DrawCommandsQueue.hpp"
#include "Common.hpp"

#include <cstdint>
#include <memory>


@@ 33,7 36,7 @@ namespace service::gui
        friend WorkerGUI;

      public:
        explicit ServiceGUI(const std::string &name, std::string parent = {});
        explicit ServiceGUI(const std::string &name = service::name::gui, std::string parent = {});
        ~ServiceGUI() noexcept override;

        sys::ReturnCodes InitHandler() override;


@@ 88,3 91,17 @@ namespace service::gui
        State currentState;
    };
} // namespace service::gui

namespace sys
{
    template <> struct ManifestTraits<service::gui::ServiceGUI>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::gui;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-lwip/ServiceLwIP.cpp => module-services/service-lwip/ServiceLwIP.cpp +2 -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 "service-lwip/ServiceLwIP.hpp"


@@ 51,9 51,7 @@ sys::ReturnCodes message_lwip(sys::Service *app, LwIP_message::Request req)
    return ret.first;
}

const char *ServiceLwIP::serviceName = "ServiceLwIP";

ServiceLwIP::ServiceLwIP() : sys::Service(serviceName)
ServiceLwIP::ServiceLwIP() : sys::Service(service::name::lwip)
{
    LOG_INFO("[ServiceLwIP] Initializing");
    tcpip_init(nullptr, nullptr);

M module-services/service-lwip/service-lwip/ServiceLwIP.hpp => module-services/service-lwip/service-lwip/ServiceLwIP.hpp +22 -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

#pragma once


@@ 8,8 8,15 @@
#include <Service/Message.hpp>
#include <Service/Service.hpp>

#include <service-db/DBServiceName.hpp>

#include <memory>

namespace service::name
{
    constexpr inline auto lwip = "ServiceLwIP";
} // namespace service::name

class LwIP_message : public sys::DataMessage
{
  public:


@@ 36,7 43,18 @@ class ServiceLwIP : public sys::Service
    sys::ReturnCodes InitHandler() override;
    sys::ReturnCodes DeinitHandler() override;
    virtual sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override;

  private:
    static const char *serviceName;
};

namespace sys
{
    template <> struct ManifestTraits<ServiceLwIP>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::lwip;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-services/service-time/ServiceTime.hpp => module-services/service-time/ServiceTime.hpp +17 -1
@@ 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


@@ 6,6 6,8 @@
#include "service-time/CalendarTimeEvents.hpp"
#include "service-time/ServiceTime.hpp"

#include <service-db/DBServiceName.hpp>

#include <MessageType.hpp>
#include <Service/Common.hpp>
#include <Service/Message.hpp>


@@ 41,3 43,17 @@ namespace stm
    };

} /* namespace stm */

namespace sys
{
    template <> struct ManifestTraits<stm::ServiceTime>
    {
        static auto GetManifest() -> ServiceManifest
        {
            ServiceManifest manifest;
            manifest.name         = service::name::service_time;
            manifest.dependencies = {service::name::db};
            return manifest;
        }
    };
} // namespace sys

M module-sys/CMakeLists.txt => module-sys/CMakeLists.txt +5 -0
@@ 14,9 14,11 @@ set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Service.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Timer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/SystemManager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/DependencyGraph.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/PowerManager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/CpuStatistics.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/DeviceManager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/graph/TopologicalSort.cpp

        )



@@ 67,3 69,6 @@ target_include_directories(${PROJECT_NAME}
        ${CMAKE_CURRENT_SOURCE_DIR}
)

if (${ENABLE_TESTS})
    add_subdirectory(SystemManager/tests)
endif()

M module-sys/Service/Service.hpp => module-sys/Service/Service.hpp +2 -1
@@ 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


@@ 7,6 7,7 @@
#include "Common.hpp"  // for ReturnCodes, ServicePriority, BusChannels
#include "Mailbox.hpp" // for Mailbox
#include "Message.hpp" // for MessagePointer
#include "ServiceManifest.hpp"
#include "thread.hpp"  // for Thread
#include <algorithm>   // for find, max
#include <cstdint>     // for uint32_t, uint64_t

A module-sys/Service/ServiceCreator.hpp => module-sys/Service/ServiceCreator.hpp +57 -0
@@ 0,0 1,57 @@
// 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 "ServiceManifest.hpp"

#include <memory>

namespace sys
{
    class Service;

    class BaseServiceCreator
    {
      public:
        explicit BaseServiceCreator(ServiceManifest &&manifest) noexcept : manifest{std::move(manifest)}
        {}
        virtual ~BaseServiceCreator() noexcept = default;

        [[nodiscard]] virtual std::shared_ptr<Service> create() const = 0;

        [[nodiscard]] auto getName() const noexcept -> const ServiceManifest::ServiceName &
        {
            return manifest.name;
        }

        [[nodiscard]] auto getDependencies() const noexcept -> const std::vector<ServiceManifest::ServiceName> &
        {
            return manifest.dependencies;
        }

        [[nodiscard]] auto getStartTimeout() const noexcept -> ServiceManifest::Timeout
        {
            return manifest.timeout;
        }

      private:
        ServiceManifest manifest;
    };

    template <typename T> class ServiceCreator : public BaseServiceCreator
    {
      public:
        using BaseServiceCreator::BaseServiceCreator;

        [[nodiscard]] auto create() const -> std::shared_ptr<Service> override
        {
            return std::make_shared<T>();
        }
    };

    template <typename T> std::unique_ptr<BaseServiceCreator> CreatorFor() noexcept
    {
        return std::make_unique<ServiceCreator<T>>(ManifestOf<T>());
    }
} // namespace sys

A module-sys/Service/ServiceManifest.hpp => module-sys/Service/ServiceManifest.hpp +45 -0
@@ 0,0 1,45 @@
// 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 <chrono>
#include <string>
#include <vector>

namespace sys
{
    class Service; // Forward declaration

    struct ServiceManifest
    {
        using ServiceName                    = std::string;
        using Timeout                        = std::chrono::milliseconds;
        static constexpr auto DefaultTimeout = Timeout{5000};

        std::vector<ServiceName> dependencies;
        ServiceName name;
        Timeout timeout = DefaultTimeout;
    };

    /// Type traits pattern used to enforce user-defined types to implement "GetManifest" function.
    template <class T> struct ManifestTraits;

    template <class, class = void> struct HasManifest : std::false_type
    {};

    /// Checks whether T implements "GetManifest" static method.
    /// Provides the member constant "value" that is equal to true if T implements "GetManifest" static method.
    /// Otherwise, "value" is equal to false.
    template <class T>
    struct HasManifest<T, std::void_t<decltype(&ManifestTraits<T>::GetManifest)>>
        : std::is_same<ServiceManifest, decltype(ManifestTraits<T>::GetManifest())>
    {};

    /// Retrieves the manifest of T, if T implements ManifestTraits.
    /// Otherwise, reports an error during compile time.
    template <class T, std::enable_if_t<HasManifest<T>::value, int> = 0> auto ManifestOf() -> ServiceManifest
    {
        return ManifestTraits<T>::GetManifest();
    }
} // namespace sys

A module-sys/SystemManager/DependencyGraph.cpp => module-sys/SystemManager/DependencyGraph.cpp +32 -0
@@ 0,0 1,32 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DependencyGraph.hpp"

#include <algorithm>
#include <cassert>

namespace sys
{
    namespace graph
    {
        Nodes nodesFrom(const std::vector<std::unique_ptr<BaseServiceCreator>> &services)
        {
            Nodes nodes;
            nodes.reserve(services.size());
            std::transform(services.begin(), services.end(), std::back_inserter(nodes), [](const auto &service) {
                return std::ref(*service);
            });
            return nodes;
        }
    } // namespace graph

    DependencyGraph::DependencyGraph(graph::Nodes nodes, std::unique_ptr<DependencySortingStrategy> &&strategy)
        : nodes{std::move(nodes)}, strategy{std::move(strategy)}
    {}

    auto DependencyGraph::sort() const -> graph::Nodes
    {
        return strategy->sort(nodes);
    }
} // namespace sys

A module-sys/SystemManager/DependencyGraph.hpp => module-sys/SystemManager/DependencyGraph.hpp +42 -0
@@ 0,0 1,42 @@
// 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 "Service/ServiceCreator.hpp"

#include <memory>
#include <vector>

namespace sys
{
    class BaseServiceCreator; // Forward declaration

    namespace graph
    {
        using Node  = std::reference_wrapper<BaseServiceCreator>;
        using Nodes = std::vector<Node>;

        Nodes nodesFrom(const std::vector<std::unique_ptr<BaseServiceCreator>> &services);
    } // namespace graph

    class DependencySortingStrategy
    {
      public:
        virtual ~DependencySortingStrategy() noexcept = default;

        [[nodiscard]] virtual auto sort(const graph::Nodes &nodes) -> graph::Nodes = 0;
    };

    class DependencyGraph
    {
      public:
        DependencyGraph(graph::Nodes nodes, std::unique_ptr<DependencySortingStrategy> &&strategy);

        [[nodiscard]] auto sort() const -> graph::Nodes;

      private:
        graph::Nodes nodes;
        std::unique_ptr<DependencySortingStrategy> strategy;
    };
} // namespace sys

M module-sys/SystemManager/SystemManager.cpp => module-sys/SystemManager/SystemManager.cpp +46 -14
@@ 3,6 3,9 @@

#include "SystemManager.hpp"

#include "DependencyGraph.hpp"
#include "graph/TopologicalSort.hpp"

#include <common_data/EventStore.hpp>
#include "thread.hpp"
#include "ticks.hpp"


@@ 18,6 21,7 @@
#include <service-appmgr/model/ApplicationManager.hpp>
#include "messages/CpuFrequencyMessage.hpp"
#include "messages/DeviceRegistrationMessage.hpp"
#include <time/ScopedTime.hpp>

const inline size_t systemManagerStack = 4096 * 2;



@@ 34,7 38,8 @@ namespace sys
        this->state = state;
    }

    SystemManager::SystemManager() : Service(service::name::system_manager, "", systemManagerStack)
    SystemManager::SystemManager(std::vector<std::unique_ptr<BaseServiceCreator>> &&creators)
        : Service(service::name::system_manager, "", systemManagerStack), systemServiceCreators{std::move(creators)}
    {
        // Specify list of channels which System Manager is registered to
        bus.channels = {BusChannel::SystemManagerRequests};


@@ 47,12 52,7 @@ namespace sys

    void SystemManager::Run()
    {

        InitHandler();

        if (userInit) {
            userInit();
        }
        initialize();

        // in shutdown we need to wait till event manager tells us that it's ok to stfu
        while (state == State::Running) {


@@ 103,13 103,48 @@ namespace sys
        };
    }

    void SystemManager::StartSystem(InitFunction init)
    void SystemManager::initialize()
    {
        utils::time::Scoped timer{"Initialize"};
        InitHandler();
        if (systemInit) {
            systemInit();
        }

        StartSystemServices();
        if (userInit) {
            userInit();
        }
    }

    void SystemManager::StartSystemServices()
    {
        DependencyGraph depGraph{graph::nodesFrom(systemServiceCreators), std::make_unique<graph::TopologicalSort>()};
        const auto &sortedServices = [&depGraph]() {
            utils::time::Scoped timer{"DependencyGraph"};
            return depGraph.sort();
        }();

        LOG_INFO("Order of system services initialization:");
        for (const auto &service : sortedServices) {
            LOG_INFO("\t> %s", service.get().getName().c_str());
        }
        std::for_each(sortedServices.begin(), sortedServices.end(), [this](const auto &service) {
            const auto startTimeout = service.get().getStartTimeout().count();
            if (const auto success = RunService(service.get().create(), this, startTimeout); !success) {
                LOG_FATAL("Unable to start service: %s", service.get().getName().c_str());
            }
        });
    }

    void SystemManager::StartSystem(InitFunction sysInit, InitFunction appSpaceInit)
    {
        powerManager  = std::make_unique<PowerManager>();
        cpuStatistics = std::make_unique<CpuStatistics>();
        deviceManager = std::make_unique<DeviceManager>();

        userInit = init;
        systemInit = std::move(sysInit);
        userInit   = std::move(appSpaceInit);

        // Start System manager
        StartService();


@@ 157,9 192,8 @@ namespace sys
        return true;
    }

    bool SystemManager::CreateService(std::shared_ptr<Service> service, Service *caller, TickType_t timeout)
    bool SystemManager::RunService(std::shared_ptr<Service> service, Service *caller, TickType_t timeout)
    {

        CriticalSection::Enter();
        servicesList.push_back(service);
        CriticalSection::Exit();


@@ 173,9 207,7 @@ namespace sys
        if (ret.first == ReturnCodes::Success && (resp->retCode == ReturnCodes::Success)) {
            return true;
        }
        else {
            return false;
        }
        return false;
    }

    bool SystemManager::DestroyService(const std::string &name, Service *caller, TickType_t timeout)

M module-sys/SystemManager/SystemManager.hpp => module-sys/SystemManager/SystemManager.hpp +13 -8
@@ 17,12 17,13 @@
#include "mutex.hpp"
#include "Service/Mailbox.hpp"
#include "Service/Service.hpp"
#include "Service/Message.hpp"
#include "Service/ServiceCreator.hpp"
#include "PowerManager.hpp"
#include "Constants.hpp"
#include "CpuStatistics.hpp"
#include "DeviceManager.hpp"
#include <chrono>
#include <vector>

namespace sys
{


@@ 63,12 64,14 @@ namespace sys
            Reboot,
        } state = State::Running;

        SystemManager();
        explicit SystemManager(std::vector<std::unique_ptr<BaseServiceCreator>> &&creators);
        ~SystemManager() override;

        void set(enum State state);

        void StartSystem(InitFunction init);
        void initialize();

        void StartSystem(InitFunction sysInit, InitFunction appSpaceInit);

        // Invoke system close procedure
        static bool CloseSystem(Service *s);


@@ 79,8 82,8 @@ namespace sys

        static bool ResumeService(const std::string &name, Service *caller);

        // Create new service
        static bool CreateService(std::shared_ptr<Service> service, Service *caller, TickType_t timeout = 5000);
        /// Runs a service
        static bool RunService(std::shared_ptr<Service> service, Service *caller, TickType_t timeout = 5000);

        /// Destroy existing service
        /// @note there is no fallback


@@ 113,6 116,8 @@ namespace sys

        void Run() override;

        void StartSystemServices();

        /// Sysmgr stores list of all active services but some of them are under control of parent services.
        /// Parent services ought to manage lifetime of child services hence we are sending DestroyRequests only to
        /// parents.


@@ 135,9 140,10 @@ namespace sys

        bool cpuStatisticsTimerInit{false};

        InitFunction userInit;

        std::vector<std::unique_ptr<BaseServiceCreator>> systemServiceCreators;
        std::unique_ptr<sys::Timer> cpuStatisticsTimer;
        InitFunction userInit;
        InitFunction systemInit;

        static std::vector<std::shared_ptr<Service>> servicesList;
        static cpp_freertos::MutexStandard destroyMutex;


@@ 145,7 151,6 @@ namespace sys
        static std::unique_ptr<CpuStatistics> cpuStatistics;
        static std::unique_ptr<DeviceManager> deviceManager;
    };

} // namespace sys

inline const char *c_str(sys::SystemManager::State state)

A module-sys/SystemManager/doc/ServicesSynchronization.md => module-sys/SystemManager/doc/ServicesSynchronization.md +25 -0
@@ 0,0 1,25 @@
# Services synchronization

If a service has dependencies to other services, it should be started after its dependencies are started.

For example, a ServiceA may be dependent on the ServiceDB. It means that the ServiceA can not be started unless the ServiceDB successfully starts.

It is a part of the Services Synchronization feature to order services based on their dependencies.

## Service Manifest

Each service should export its manifest. The manifest may contain the following information:
- dependencies to other services
- start limit
- start timeout
- on failure strategy

The System Manager needs to read the manifests to run the services in the correct order.

## Synchronization algorithm

The [topological sorting](https://en.wikipedia.org/wiki/Topological_sorting) algorithm may be used to build a linear ordering of services that are to be started by the System Manager.

![](./services_synchronization.png)

**Important note: The Dependency Graph implementation handles Directed Acyclic Graphs only.**

A module-sys/SystemManager/doc/services_synchronization.puml => module-sys/SystemManager/doc/services_synchronization.puml +30 -0
@@ 0,0 1,30 @@
@startuml

actor Driver
participant "System Manager" as sysmgr
participant "Dependency Graph" as graph
participant Service as srv

Driver -> sysmgr: Pass a list\nof services
sysmgr -> graph **: create

|||
sysmgr -> graph: addServices()

|||
sysmgr -> graph: topologicalSort()

|||
activate graph
graph --> sysmgr: sorted services
deactivate graph
destroy graph
|||

loop for each service in sorted services
sysmgr -> srv: Start
srv --> sysmgr: Confirmation
end
|||

@enduml

A module-sys/SystemManager/graph/TopologicalSort.cpp => module-sys/SystemManager/graph/TopologicalSort.cpp +75 -0
@@ 0,0 1,75 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "TopologicalSort.hpp"

#include <algorithm>
#include <cassert>

namespace sys::graph
{
    auto TopologicalSort::sort(const graph::Nodes &nodes) -> graph::Nodes
    {
        std::vector<std::string_view> sortedNames;
        auto edges   = createEdges(nodes);
        auto visited = createVisitedMap(nodes);
        auto layer   = 1;
        for (const auto &node : nodes) {
            const auto &nodeName = node.get().getName();
            if (visited[nodeName] == false) {
                visit(nodeName, edges, visited, layer, sortedNames);
            }
        }
        return prepareOutput(nodes, sortedNames);
    }

    void TopologicalSort::visit(
        std::string_view node, Edges &edges, VisitedMap &visited, int layer, std::vector<std::string_view> &out)
    {
        visited[node] = true;
        for (const auto &dependency : edges[node]) {
            if (visited[dependency] == false) {
                layer += 1;
                visit(dependency, edges, visited, layer, out);
            }
        }
        out.emplace_back(node);
    }

    auto TopologicalSort::createEdges(const graph::Nodes &nodes) const -> Edges
    {
        Edges edges;
        for (const auto &node : nodes) {
            const auto &nodeName     = node.get().getName();
            const auto &dependencies = node.get().getDependencies();
            for (const auto &dependency : dependencies) {
                edges[nodeName].push_back(dependency);
            }
        }
        return edges;
    }

    auto TopologicalSort::createVisitedMap(const graph::Nodes &nodes) const -> VisitedMap
    {
        VisitedMap visited;
        std::for_each(
            nodes.begin(), nodes.end(), [&visited](const auto &node) { visited[node.get().getName()] = false; });
        return visited;
    }

    auto TopologicalSort::prepareOutput(const graph::Nodes &nodes, const std::vector<std::string_view> &sorted) const
        -> graph::Nodes
    {
        graph::Nodes output;
        output.reserve(sorted.size());
        for (const auto &name : sorted) {
            const auto it = std::find_if(
                nodes.cbegin(), nodes.cend(), [&name](const auto &node) { return node.get().getName() == name; });

            // Either nodes have been modified by mistake or there's no particular dependency in the nodes container.
            assert(it != nodes.end());
            output.emplace_back(*it);
        }
        return output;
    }
} // namespace sys::graph

A module-sys/SystemManager/graph/TopologicalSort.hpp => module-sys/SystemManager/graph/TopologicalSort.hpp +32 -0
@@ 0,0 1,32 @@
// 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 "SystemManager/DependencyGraph.hpp"

#include <list>
#include <string_view>
#include <unordered_map>
#include <vector>

namespace sys::graph
{
    class TopologicalSort : public DependencySortingStrategy
    {
      public:
        [[nodiscard]] auto sort(const graph::Nodes &nodes) -> graph::Nodes override;

      private:
        using Edges      = std::unordered_map<std::string_view, std::list<std::string_view>>;
        using VisitedMap = std::unordered_map<std::string_view, bool>;

        [[nodiscard]] auto createEdges(const graph::Nodes &nodes) const -> Edges;
        [[nodiscard]] auto createVisitedMap(const graph::Nodes &nodes) const -> VisitedMap;
        void visit(
            std::string_view node, Edges &edges, VisitedMap &visited, int layer, std::vector<std::string_view> &out);

        [[nodiscard]] auto prepareOutput(const graph::Nodes &nodes, const std::vector<std::string_view> &sorted) const
            -> graph::Nodes;
    };
} // namespace sys::graph

A module-sys/SystemManager/tests/CMakeLists.txt => module-sys/SystemManager/tests/CMakeLists.txt +9 -0
@@ 0,0 1,9 @@
add_catch2_executable(
    NAME
        dependency-graph-tests
    SRCS
        tests-main.cpp
        test-DependencyGraph.cpp
    LIBS
        module-sys
)

A module-sys/SystemManager/tests/test-DependencyGraph.cpp => module-sys/SystemManager/tests/test-DependencyGraph.cpp +175 -0
@@ 0,0 1,175 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>

#include "Service/ServiceCreator.hpp"
#include "SystemManager/DependencyGraph.hpp"
#include "SystemManager/graph/TopologicalSort.hpp"

using namespace sys;
using graph::TopologicalSort;

class MockedServiceCreator : public BaseServiceCreator
{
  public:
    using BaseServiceCreator::BaseServiceCreator;

    std::shared_ptr<Service> create() const override
    {
        return nullptr;
    }
};

ServiceManifest createManifest(ServiceManifest::ServiceName name, std::vector<ServiceManifest::ServiceName> deps)
{
    ServiceManifest manifest;
    manifest.name         = std::move(name);
    manifest.dependencies = std::move(deps);
    return manifest;
}

TEST_CASE("Given Dependency Graph When topological sort empty graph then verify if result is empty")
{
    DependencyGraph graph{{}, std::make_unique<TopologicalSort>()};

    const auto &sorted = graph.sort();

    REQUIRE(sorted.empty());
}

TEST_CASE("Given Dependency Graph When topological sort without dependencies then verify order")
{
    std::vector<std::unique_ptr<BaseServiceCreator>> services;
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S1", {})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S2", {})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S3", {})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S4", {})));

    // Graph:
    // S1 S2 S3 S4
    DependencyGraph graph{graph::nodesFrom(services), std::make_unique<TopologicalSort>()};

    const auto &sorted = graph.sort();

    REQUIRE(sorted[0].get().getName() == "S1");
    REQUIRE(sorted[1].get().getName() == "S2");
    REQUIRE(sorted[2].get().getName() == "S3");
    REQUIRE(sorted[3].get().getName() == "S4");
}

TEST_CASE("Given Dependency Graph When topological sort simple case then verify order")
{
    std::vector<std::unique_ptr<BaseServiceCreator>> services;
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S1", {"S2"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S2", {"S3"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S3", {"S4"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S4", {})));

    // Graph:
    // S1 -> S2 -> S3 -> S4
    DependencyGraph graph{graph::nodesFrom(services), std::make_unique<TopologicalSort>()};

    const auto &sorted = graph.sort();

    REQUIRE(sorted[0].get().getName() == "S4");
    REQUIRE(sorted[1].get().getName() == "S3");
    REQUIRE(sorted[2].get().getName() == "S2");
    REQUIRE(sorted[3].get().getName() == "S1");
}

TEST_CASE("Given Dependency Graph When topological sort all depending on one then verify order")
{
    std::vector<std::unique_ptr<BaseServiceCreator>> services;
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S1", {"S2"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S2", {})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S3", {"S2"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S4", {"S2"})));

    // Graph:
    // S1 -> S2
    //     / ^
    //   S3  |
    //      S4
    DependencyGraph graph{graph::nodesFrom(services), std::make_unique<TopologicalSort>()};

    const auto &sorted = graph.sort();

    REQUIRE(sorted[0].get().getName() == "S2");
    REQUIRE(sorted[1].get().getName() == "S1");
    REQUIRE(sorted[2].get().getName() == "S3");
    REQUIRE(sorted[3].get().getName() == "S4");
}

TEST_CASE("Given Dependency Graph When topological sort advanced case then verify order 1")
{
    std::vector<std::unique_ptr<BaseServiceCreator>> services;
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S1", {"S2", "S3"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S2", {"S4"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S3", {"S5"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S4", {"S6"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S5", {"S6"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S6", {})));

    // Graph:
    //    -> S2 -> S4 -->
    // S1 -> S3 -> S5 -> S6
    DependencyGraph graph{graph::nodesFrom(services), std::make_unique<TopologicalSort>()};

    const auto &sorted = graph.sort();

    REQUIRE(sorted[0].get().getName() == "S6");
    REQUIRE(sorted[1].get().getName() == "S4");
    REQUIRE(sorted[2].get().getName() == "S2");
    REQUIRE(sorted[3].get().getName() == "S5");
    REQUIRE(sorted[4].get().getName() == "S3");
    REQUIRE(sorted[5].get().getName() == "S1");
}

TEST_CASE("Given Dependency Graph When topological sort advanced case then verify order 2")
{
    std::vector<std::unique_ptr<BaseServiceCreator>> services;
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S1", {"S2", "S3", "S4"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S2", {})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S3", {"S5", "S6"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S4", {"S6"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S5", {})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S6", {})));

    // Graph:
    //    --> S4 ---\>
    //   /     ----> S6
    //  /     /
    // S1 -> S3 -> S5
    //   \-> S2
    DependencyGraph graph{graph::nodesFrom(services), std::make_unique<TopologicalSort>()};

    const auto &sorted = graph.sort();

    REQUIRE(sorted[0].get().getName() == "S2");
    REQUIRE(sorted[1].get().getName() == "S5");
    REQUIRE(sorted[2].get().getName() == "S6");
    REQUIRE(sorted[3].get().getName() == "S3");
    REQUIRE(sorted[4].get().getName() == "S4");
    REQUIRE(sorted[5].get().getName() == "S1");
}

TEST_CASE("Given Dependency Graph When topological sort on directed cyclic graph then verify order")
{
    std::vector<std::unique_ptr<BaseServiceCreator>> services;
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S1", {"S2"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S2", {"S3"})));
    services.push_back(std::make_unique<MockedServiceCreator>(createManifest("S3", {"S1"})));

    // Graph:
    // S1 -> S2 -> S3
    //  \---------/
    DependencyGraph graph{graph::nodesFrom(services), std::make_unique<TopologicalSort>()};

    const auto &sorted = graph.sort();

    // The order is not correct. Dependency Graph doesn't handle cyclic graphs properly
    REQUIRE(sorted[0].get().getName() == "S3");
    REQUIRE(sorted[1].get().getName() == "S2");
    REQUIRE(sorted[2].get().getName() == "S1");
}

A module-sys/SystemManager/tests/tests-main.cpp => module-sys/SystemManager/tests/tests-main.cpp +5 -0
@@ 0,0 1,5 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <catch2/catch.hpp>

A services_synchronization.png => services_synchronization.png +0 -0
M source/main.cpp => source/main.cpp +57 -60
@@ 29,6 29,10 @@
#include <service-evtmgr/EventManager.hpp>
#include <service-lwip/ServiceLwIP.hpp>
#include <service-time/ServiceTime.hpp>
#include <Service/ServiceCreator.hpp>
#include <module-services/service-gui/ServiceGUI.hpp>
#include <module-services/service-gui/Common.hpp>
#include <module-services/service-eink/ServiceEink.hpp>
#include <service-fileindexer/Constants.hpp>
#include <service-fileindexer/ServiceFileIndexer.hpp>



@@ 67,103 71,96 @@ int main()

    bsp::BoardInit();

    auto sysmgr = std::make_shared<sys::SystemManager>();

    sysmgr->StartSystem([sysmgr]() {
        /// force initialization of PhonenumberUtil because of its stack usage
        /// otherwise we would end up with an init race and PhonenumberUtil could
        /// be initiated in a task with stack not big enough to handle it
        i18n::phonenumbers::PhoneNumberUtil::GetInstance();

        vfs.Init();

        auto ret = true;

        ret &=
            sys::SystemManager::CreateService(std::make_shared<EventManager>(service::name::evt_manager), sysmgr.get());
    std::vector<std::unique_ptr<sys::BaseServiceCreator>> systemServices;
    systemServices.emplace_back(sys::CreatorFor<EventManager>());
#if ENABLE_FILEINDEXER_SERVICE
        ret &= sys::SystemManager::CreateService(
            std::make_shared<service::ServiceFileIndexer>(service::name::file_indexer), sysmgr.get());
    systemServices.emplace_back(sys::CreatorFor<service::ServiceFileIndexer>());
#endif
        ret &= sys::SystemManager::CreateService(std::make_shared<ServiceDB>(), sysmgr.get());

    systemServices.emplace_back(sys::CreatorFor<ServiceDB>());
#if ENABLE_GSM == 0
        // For now disable pernamenlty Service cellular when there is no GSM configured
        LOG_INFO("ServiceCellular (GSM) - Disabled");
    // For now disable permanently Service cellular when there is no GSM configured
    LOG_INFO("ServiceCellular (GSM) - Disabled");
#else
        LOG_INFO("ServiceCellular (GSM) - Enabling");
        ret &= sys::SystemManager::CreateService(std::make_shared<ServiceAntenna>(), sysmgr.get());
        ret &= sys::SystemManager::CreateService(std::make_shared<ServiceCellular>(), sysmgr.get());
        ret &= sys::SystemManager::CreateService(std::make_shared<FotaService::Service>(), sysmgr.get());
#endif

        ret &= sys::SystemManager::CreateService(std::make_shared<ServiceAudio>(), sysmgr.get());
        ret &= sys::SystemManager::CreateService(std::make_shared<ServiceBluetooth>(), sysmgr.get());
        ret &= sys::SystemManager::CreateService(std::make_shared<ServiceLwIP>(), sysmgr.get());
        ret &= sys::SystemManager::CreateService(std::make_shared<ServiceDesktop>(), sysmgr.get());

        ret &= sys::SystemManager::CreateService(std::make_shared<stm::ServiceTime>(), sysmgr.get());

        // vector with launchers to applications
        std::vector<std::unique_ptr<app::ApplicationLauncher>> applications;
    systemServices.emplace_back(sys::CreatorFor<ServiceAntenna>());
    systemServices.emplace_back(sys::CreatorFor<ServiceCellular>());
    systemServices.emplace_back(sys::CreatorFor<FotaService::Service>());
#endif
    systemServices.emplace_back(sys::CreatorFor<ServiceAudio>());
    systemServices.emplace_back(sys::CreatorFor<ServiceBluetooth>());
    systemServices.emplace_back(sys::CreatorFor<ServiceLwIP>());
    systemServices.emplace_back(sys::CreatorFor<ServiceDesktop>());
    systemServices.emplace_back(sys::CreatorFor<stm::ServiceTime>());
    systemServices.emplace_back(sys::CreatorFor<service::eink::ServiceEink>());
    systemServices.emplace_back(sys::CreatorFor<service::gui::ServiceGUI>());

    auto sysmgr = std::make_shared<sys::SystemManager>(std::move(systemServices));
    sysmgr->StartSystem(
        []() {
            vfs.Init();
            return true;
        },
        [sysmgr]() {
            /// force initialization of PhonenumberUtil because of its stack usage
            /// otherwise we would end up with an init race and PhonenumberUtil could
            /// be initiated in a task with stack not big enough to handle it
            i18n::phonenumbers::PhoneNumberUtil::GetInstance();

            // vector with launchers to applications
            std::vector<std::unique_ptr<app::ApplicationLauncher>> applications;
#ifdef ENABLE_APP_DESKTOP
        applications.push_back(app::CreateLauncher<app::ApplicationDesktop>(app::name_desktop, false));
            applications.push_back(app::CreateLauncher<app::ApplicationDesktop>(app::name_desktop, false));
#endif
#ifdef ENABLE_APP_CALL
        applications.push_back(app::CreateLauncher<app::ApplicationCall>(app::name_call, false));
            applications.push_back(app::CreateLauncher<app::ApplicationCall>(app::name_call, false));
#endif
#ifdef ENABLE_APP_SETTINGS
        applications.push_back(app::CreateLauncher<app::ApplicationSettings>(app::name_settings));
            applications.push_back(app::CreateLauncher<app::ApplicationSettings>(app::name_settings));
#endif
#ifdef ENABLE_APP_SETTINGS_NEW
        applications.push_back(app::CreateLauncher<app::ApplicationSettingsNew>(app::name_settings_new));
            applications.push_back(app::CreateLauncher<app::ApplicationSettingsNew>(app::name_settings_new));
#endif
#ifdef ENABLE_APP_NOTES
        applications.push_back(app::CreateLauncher<app::ApplicationNotes>(app::name_notes));
            applications.push_back(app::CreateLauncher<app::ApplicationNotes>(app::name_notes));
#endif
#ifdef ENABLE_APP_CALLLOG
        applications.push_back(app::CreateLauncher<app::ApplicationCallLog>(app::CallLogAppStr));
            applications.push_back(app::CreateLauncher<app::ApplicationCallLog>(app::CallLogAppStr));
#endif
#ifdef ENABLE_APP_PHONEBOOK
        applications.push_back(app::CreateLauncher<app::ApplicationPhonebook>(app::name_phonebook));
            applications.push_back(app::CreateLauncher<app::ApplicationPhonebook>(app::name_phonebook));
#endif
#ifdef ENABLE_APP_MESSAGES
        applications.push_back(app::CreateLauncher<app::ApplicationMessages>(app::name_messages));
            applications.push_back(app::CreateLauncher<app::ApplicationMessages>(app::name_messages));
#endif
#ifdef ENABLE_APP_SPECIAL_INPUT
        applications.push_back(app::CreateLauncher<app::ApplicationSpecialInput>(app::special_input, false));
            applications.push_back(app::CreateLauncher<app::ApplicationSpecialInput>(app::special_input, false));
#endif
#ifdef ENABLE_APP_ANTENNA
        applications.push_back(app::CreateLauncher<app::ApplicationAntenna>(app::name_antenna));
            applications.push_back(app::CreateLauncher<app::ApplicationAntenna>(app::name_antenna));
#endif
#ifdef ENABLE_APP_CALENDAR
        applications.push_back(app::CreateLauncher<app::ApplicationCalendar>(app::name_calendar));
            applications.push_back(app::CreateLauncher<app::ApplicationCalendar>(app::name_calendar));
#endif
#ifdef ENABLE_APP_MUSIC_PLAYER
        applications.push_back(app::CreateLauncher<app::ApplicationMusicPlayer>(app::name_music_player));
            applications.push_back(app::CreateLauncher<app::ApplicationMusicPlayer>(app::name_music_player));
#endif
#ifdef ENABLE_APP_MEDITATION
        applications.push_back(app::CreateLauncher<app::ApplicationMeditation>(app::name_meditation));
            applications.push_back(app::CreateLauncher<app::ApplicationMeditation>(app::name_meditation));
#endif
#ifdef ENABLE_APP_CALCULATOR
        applications.push_back(app::CreateLauncher<app::ApplicationCalculator>(app::name_calculator));
            applications.push_back(app::CreateLauncher<app::ApplicationCalculator>(app::name_calculator));
#endif
#ifdef ENABLE_APP_ALARM_CLOCK
        applications.push_back(app::CreateLauncher<app::ApplicationAlarmClock>(app::name_alarm_clock));
            applications.push_back(app::CreateLauncher<app::ApplicationAlarmClock>(app::name_alarm_clock));
#endif
            // start application manager
            return sysmgr->RunService(
                std::make_shared<app::manager::ApplicationManager>(
                    app::manager::ApplicationManager::ServiceName, std::move(applications), app::name_desktop),
                sysmgr.get());
        });

        // start application manager
        ret &= sysmgr->CreateService(
            std::make_shared<app::manager::ApplicationManager>(
                app::manager::ApplicationManager::ServiceName, std::move(applications), app::name_desktop),
            sysmgr.get());

        return ret;
    });
    LOG_PRINTF("Launching PurePhone \n");
    LOG_PRINTF("commit: %s tag: %s branch: %s\n", GIT_REV, GIT_TAG, GIT_BRANCH);

    cpp_freertos::Thread::StartScheduler();

    return 0;
}