~aleteoryx/muditaos

232d0b4b2ee681043b7340f39c98236f02280fb2 — Wojtek Rzepecki 5 years ago 3afcbd8
[EGD-6101] Add low battery start prevention

Device will be shutdown after
startup if SOC will be below
shutdown level
23 files changed, 303 insertions(+), 206 deletions(-)

M module-services/service-appmgr/CMakeLists.txt
A module-services/service-appmgr/messages/StartAllowedMessage.cpp
M module-services/service-appmgr/model/ApplicationManager.cpp
A module-services/service-appmgr/service-appmgr/StartupType.hpp
M module-services/service-appmgr/service-appmgr/messages/BaseMessage.hpp
M module-services/service-appmgr/service-appmgr/messages/Message.hpp
A module-services/service-appmgr/service-appmgr/messages/StartAllowedMessage.hpp
M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp
M module-services/service-cellular/doc/cellular_gsm_onoff_flow.puml
M module-services/service-cellular/doc/cellular_gsm_onoff_flow.svg
M module-services/service-evtmgr/EventManager.cpp
M module-services/service-evtmgr/WorkerEvent.cpp
M module-services/service-evtmgr/api/EventManagerServiceAPI.cpp
M module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.cpp
M module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.hpp
M module-services/service-evtmgr/doc/battery_level_check_state_machine.puml
M module-services/service-evtmgr/doc/battery_level_check_state_machine.svg
M module-services/service-evtmgr/service-evtmgr/BatteryMessages.hpp
M module-services/service-evtmgr/service-evtmgr/EventManagerServiceAPI.hpp
M module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp
M module-sys/SystemManager/SystemManager.cpp
M module-sys/SystemManager/SystemManager.hpp
M module-utils/common_data/EventStore.hpp
M module-services/service-appmgr/CMakeLists.txt => module-services/service-appmgr/CMakeLists.txt +2 -1
@@ 26,7 26,8 @@ set(SOURCES
    messages/SwitchRequest.cpp
    messages/UpdateInProgress.cpp
    messages/DOMRequest.cpp
    messages/SetOsUpdateVersion.cpp)
    messages/SetOsUpdateVersion.cpp
    messages/StartAllowedMessage.cpp)

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


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

#include <service-appmgr/messages/StartAllowedMessage.hpp>

namespace app::manager
{
    StartAllowedMessage::StartAllowedMessage(StartupType startupType) : BaseMessage(), startupType(startupType)
    {}

    [[nodiscard]] StartupType StartAllowedMessage::getStartupType() const noexcept
    {
        return startupType;
    }

} // namespace app::manager

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +31 -4
@@ 21,6 21,7 @@
#include <service-eink/ServiceEink.hpp>
#include <service-gui/Common.hpp>
#include <service-desktop/DesktopMessages.hpp>
#include <service-appmgr/StartupType.hpp>

#include <algorithm>
#include <limits>


@@ 158,13 159,32 @@ namespace app::manager
            ::settings::SettingsScope::Global);

        startBackgroundApplications();
        if (auto app = getApplication(rootApplicationName); app != nullptr) {
            Controller::sendAction(this, actions::Home);
        }
        bus.sendUnicast(std::make_unique<CheckIfStartAllowedMessage>(), service::name::system_manager);

        return sys::ReturnCodes::Success;
    }

    void ApplicationManager::handleStart(StartAllowedMessage *msg)
    {
        switch (msg->getStartupType()) {
        case StartupType::Regular:
            if (auto app = getApplication(rootApplicationName); app != nullptr) {
                Controller::sendAction(this, actions::Home);
            }
            break;
        case StartupType::LowBattery:
            handleSwitchApplication(
                std::make_unique<SwitchRequest>(ServiceName, app::name_desktop, window::name::dead_battery, nullptr)
                    .get());
            break;
        case StartupType::LowBatteryCharging:
            handleSwitchApplication(
                std::make_unique<SwitchRequest>(ServiceName, app::name_desktop, window::name::charging_battery, nullptr)
                    .get());
            break;
        }
    }

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


@@ 192,11 212,13 @@ namespace app::manager
        ActionRequest act = ActionRequest{this->GetName(), app::manager::actions::DisplayLogoAtExit, nullptr};
        switch (closeReason) {
        case sys::CloseReason::SystemBrownout:
            [[fallthrough]];
        case sys::CloseReason::LowBattery:
            act = ActionRequest{this->GetName(), app::manager::actions::SystemBrownout, nullptr};
            break;
        case sys::CloseReason::RegularPowerDown:
            [[fallthrough]];
        case sys::CloseReason::Reboot:
        case sys::CloseReason::LowBattery:
            break;
        }
        handleActionRequest(&act);


@@ 217,6 239,11 @@ namespace app::manager
                LOG_INFO("Phone mode changed.");
            });

        connect(typeid(StartAllowedMessage), [this](sys::Message *request) {
            auto msg = static_cast<StartAllowedMessage *>(request);
            handleStart(msg);
            return sys::MessageNone{};
        });
        connect(typeid(ApplicationStatusRequest), [this](sys::Message *request) {
            auto msg = static_cast<ApplicationStatusRequest *>(request);
            return std::make_shared<ApplicationStatusResponse>(msg->checkAppName,

A module-services/service-appmgr/service-appmgr/StartupType.hpp => module-services/service-appmgr/service-appmgr/StartupType.hpp +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

#pragma once

namespace app::manager
{
    enum class StartupType
    {
        Regular,
        LowBattery,
        LowBatteryCharging
    };
} // namespace app::manager

M module-services/service-appmgr/service-appmgr/messages/BaseMessage.hpp => module-services/service-appmgr/service-appmgr/messages/BaseMessage.hpp +1 -0
@@ 14,6 14,7 @@ namespace app::manager
      public:
        BaseMessage(MessageType type, ApplicationName sender);
        explicit BaseMessage(ApplicationName sender);
        BaseMessage() = default;

        [[nodiscard]] auto getSenderName() const noexcept -> const ApplicationName &;


M module-services/service-appmgr/service-appmgr/messages/Message.hpp => module-services/service-appmgr/service-appmgr/messages/Message.hpp +1 -0
@@ 10,6 10,7 @@
#include "SwitchConfirmation.hpp"
#include "ApplicationCloseRequest.hpp"
#include "CloseConfirmation.hpp"
#include "StartAllowedMessage.hpp"
#include "ApplicationInitialised.hpp"
#include "ApplicationStatus.hpp"
#include "LanguageChangeRequest.hpp"

A module-services/service-appmgr/service-appmgr/messages/StartAllowedMessage.hpp => module-services/service-appmgr/service-appmgr/messages/StartAllowedMessage.hpp +22 -0
@@ 0,0 1,22 @@
// 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 "BaseMessage.hpp"
#include <service-appmgr/StartupType.hpp>
namespace app::manager
{
    class CheckIfStartAllowedMessage : public BaseMessage
    {};
    class StartAllowedMessage : public BaseMessage
    {
      public:
        explicit StartAllowedMessage(StartupType startupType);

        [[nodiscard]] StartupType getStartupType() const noexcept;

      private:
        StartupType startupType;
    };
} // namespace app::manager

M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp +1 -0
@@ 137,6 137,7 @@ namespace app::manager
        auto handleMessageAsAction(sys::Message *request) -> std::shared_ptr<sys::ResponseMessage>;
        /// handles dom request by passing this request to application which should provide the dom
        auto handleDOMRequest(sys::Message *request) -> std::shared_ptr<sys::ResponseMessage>;
        void handleStart(StartAllowedMessage *msg);

        void requestApplicationClose(ApplicationHandle &app, bool isCloseable);
        void onApplicationSwitch(ApplicationHandle &app,

M module-services/service-cellular/doc/cellular_gsm_onoff_flow.puml => module-services/service-cellular/doc/cellular_gsm_onoff_flow.puml +2 -10
@@ 11,10 11,6 @@ participant "ApplicaionDesktop" as appdsktp
    activate cell
    cell -> sysmgr : CellularCheckIfStartAllowedMessage
    activate sysmgr
    sysmgr -> batt : BatteryLevelCriticalCheckMessage
    activate batt
    batt -> sysmgr : BatteryLevelCriticalMessage
    deactivate batt
    sysmgr -> cell : CellularPowerStateChange(Off)
    cell -> cell : handle_power_state_change
    sysmgr -> appmgr : CriticalBatteryLevelNotification(true)


@@ 27,10 23,6 @@ alt
    activate cell
    cell -> sysmgr : CellularCheckIfStartAllowedMessage
    activate sysmgr
    sysmgr -> batt : BatteryLevelNormalCheckMessage
    activate batt
    batt -> sysmgr : BatteryLevelCriticalMessage
    deactivate batt
    sysmgr -> cell : CellularPowerStateChange(On)
    cell -> cell : handle_power_state_change
    sysmgr -> appmgr : CriticalBatteryLevelNotification(false)


@@ 39,7 31,7 @@ alt
end

== Normal flow ==
    batt -> sysmgr : BatteryLevelCriticalMessage
    batt -> sysmgr : BatteryStateChangeMessage
    activate sysmgr
    sysmgr -> cell : CellularPowerStateChange(Off)
    activate cell


@@ 50,7 42,7 @@ end
    appmgr -> appdsktp : actions::DisplayLowBatteryNotification
    deactivate appmgr
alt
    batt -> sysmgr : BatteryLevelNormalMessage
    batt -> sysmgr : BatteryStateChangeMessage
    activate sysmgr
    sysmgr -> cell : CellularPowerStateChange(On)
    activate cell

M module-services/service-cellular/doc/cellular_gsm_onoff_flow.svg => module-services/service-cellular/doc/cellular_gsm_onoff_flow.svg +4 -12
@@ 1,4 1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="945px" preserveAspectRatio="none" style="width:1168px;height:945px;" version="1.1" viewBox="0 0 1168 945" width="1168px" zoomAndPan="magnify"><defs><filter height="300%" id="f17c9soi04i4ed" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="89" y="143.6953"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="89" y="384.7578"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="158.6641" style="stroke:#A80036;stroke-width:1.0;" width="10" x="341" y="114.5625"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="158.6641" style="stroke:#A80036;stroke-width:1.0;" width="10" x="341" y="355.625"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="299.1953" style="stroke:#A80036;stroke-width:1.0;" width="10" x="341" y="593.5547"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="116.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="346" y="776.3516"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="222.9297" style="stroke:#A80036;stroke-width:1.0;" width="10" x="600" y="50.2969"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="211.9297" style="stroke:#A80036;stroke-width:1.0;" width="10" x="600" y="302.3594"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="71.2656" style="stroke:#A80036;stroke-width:1.0;" width="10" x="600" y="622.6875"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="71.2656" style="stroke:#A80036;stroke-width:1.0;" width="10" x="600" y="805.4844"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="806" y="273.2266"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="806" y="693.9531"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="204.9297" style="stroke:#000000;stroke-width:2.0;" width="889.5" x="10" y="317.3594"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="146.6641" style="stroke:#000000;stroke-width:2.0;" width="889.5" x="10" y="738.0859"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="94" x2="94" y1="40.2969" y2="901.75"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="345.5" x2="345.5" y1="40.2969" y2="901.75"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="605" x2="605" y1="40.2969" y2="901.75"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="810.5" x2="810.5" y1="40.2969" y2="901.75"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="1082.5" x2="1082.5" y1="40.2969" y2="901.75"/><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="144" x="20" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="130" x="27" y="24.9951">Battery level check</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="144" x="20" y="900.75"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="130" x="27" y="920.7451">Battery level check</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="129" x="279.5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="115" x="286.5" y="24.9951">System Manager</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="129" x="279.5" y="900.75"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="115" x="286.5" y="920.7451">System Manager</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="118" x="544" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="104" x="551" y="24.9951">Service Cellular</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="118" x="544" y="900.75"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="104" x="551" y="920.7451">Service Cellular</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="153" x="732.5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="139" x="739.5" y="24.9951">Application Manager</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="153" x="732.5" y="900.75"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="139" x="739.5" y="920.7451">Application Manager</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="143" x="1009.5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="129" x="1016.5" y="24.9951">ApplicaionDesktop</text><rect fill="#FEFECE" filter="url(#f17c9soi04i4ed)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="143" x="1009.5" y="900.75"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="129" x="1016.5" y="920.7451">ApplicaionDesktop</text><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="89" y="143.6953"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="89" y="384.7578"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="158.6641" style="stroke:#A80036;stroke-width:1.0;" width="10" x="341" y="114.5625"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="158.6641" style="stroke:#A80036;stroke-width:1.0;" width="10" x="341" y="355.625"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="299.1953" style="stroke:#A80036;stroke-width:1.0;" width="10" x="341" y="593.5547"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="116.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="346" y="776.3516"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="222.9297" style="stroke:#A80036;stroke-width:1.0;" width="10" x="600" y="50.2969"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="211.9297" style="stroke:#A80036;stroke-width:1.0;" width="10" x="600" y="302.3594"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="71.2656" style="stroke:#A80036;stroke-width:1.0;" width="10" x="600" y="622.6875"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="71.2656" style="stroke:#A80036;stroke-width:1.0;" width="10" x="600" y="805.4844"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="806" y="273.2266"/><rect fill="#FFFFFF" filter="url(#f17c9soi04i4ed)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="806" y="693.9531"/><rect fill="#EEEEEE" filter="url(#f17c9soi04i4ed)" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1161.5" x="0" y="70.8633"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1161.5" y1="70.8633" y2="70.8633"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1161.5" y1="73.8633" y2="73.8633"/><rect fill="#EEEEEE" filter="url(#f17c9soi04i4ed)" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="107" x="527.25" y="60.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="88" x="533.25" y="76.3638">Initial check</text><polygon fill="#A80036" points="362,110.5625,352,114.5625,362,118.5625,358,114.5625" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="356" x2="599" y1="114.5625" y2="114.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="230" x="368" y="109.4966">CellularCheckIfStartAllowedMessage</text><polygon fill="#A80036" points="110,139.6953,100,143.6953,110,147.6953,106,143.6953" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="104" x2="340" y1="143.6953" y2="143.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="219" x="116" y="138.6294">BatteryLevelCriticalCheckMessage</text><polygon fill="#A80036" points="329,168.8281,339,172.8281,329,176.8281,333,172.8281" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="94" x2="335" y1="172.8281" y2="172.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="180" x="101" y="167.7622">BatteryLevelCriticalMessage</text><polygon fill="#A80036" points="588,197.9609,598,201.9609,588,205.9609,592,201.9609" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="351" x2="594" y1="201.9609" y2="201.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="197" x="358" y="196.895">CellularPowerStateChange(Off)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="610" x2="652" y1="231.0938" y2="231.0938"/><line style="stroke:#A80036;stroke-width:1.0;" x1="652" x2="652" y1="231.0938" y2="244.0938"/><line style="stroke:#A80036;stroke-width:1.0;" x1="611" x2="652" y1="244.0938" y2="244.0938"/><polygon fill="#A80036" points="621,240.0938,611,244.0938,621,248.0938,617,244.0938" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="182" x="617" y="226.0278">handle_power_state_change</text><polygon fill="#A80036" points="794,269.2266,804,273.2266,794,277.2266,798,273.2266" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="346" x2="800" y1="273.2266" y2="273.2266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="231" x="353" y="268.1606">CriticalBatteryLevelNotification(true)</text><polygon fill="#A80036" points="1071,298.3594,1081,302.3594,1071,306.3594,1075,302.3594" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="811" x2="1077" y1="302.3594" y2="302.3594"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="243" x="818" y="297.2935">actions::DisplayLowBatteryNotification</text><path d="M10,317.3594 L74,317.3594 L74,324.3594 L64,334.3594 L10,334.3594 L10,317.3594 " fill="#EEEEEE" style="stroke:#000000;stroke-width:1.0;"/><rect fill="none" height="204.9297" style="stroke:#000000;stroke-width:2.0;" width="889.5" x="10" y="317.3594"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="19" x="25" y="330.4263">alt</text><polygon fill="#A80036" points="362,351.625,352,355.625,362,359.625,358,355.625" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="356" x2="599" y1="355.625" y2="355.625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="230" x="368" y="350.5591">CellularCheckIfStartAllowedMessage</text><polygon fill="#A80036" points="110,380.7578,100,384.7578,110,388.7578,106,384.7578" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="104" x2="340" y1="384.7578" y2="384.7578"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="223" x="116" y="379.6919">BatteryLevelNormalCheckMessage</text><polygon fill="#A80036" points="329,409.8906,339,413.8906,329,417.8906,333,413.8906" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="94" x2="335" y1="413.8906" y2="413.8906"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="180" x="101" y="408.8247">BatteryLevelCriticalMessage</text><polygon fill="#A80036" points="588,439.0234,598,443.0234,588,447.0234,592,443.0234" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="351" x2="594" y1="443.0234" y2="443.0234"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="197" x="358" y="437.9575">CellularPowerStateChange(On)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="610" x2="652" y1="472.1563" y2="472.1563"/><line style="stroke:#A80036;stroke-width:1.0;" x1="652" x2="652" y1="472.1563" y2="485.1563"/><line style="stroke:#A80036;stroke-width:1.0;" x1="611" x2="652" y1="485.1563" y2="485.1563"/><polygon fill="#A80036" points="621,481.1563,611,485.1563,621,489.1563,617,485.1563" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="182" x="617" y="467.0903">handle_power_state_change</text><polygon fill="#A80036" points="799,510.2891,809,514.2891,799,518.2891,803,514.2891" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="346" x2="805" y1="514.2891" y2="514.2891"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="235" x="353" y="509.2231">CriticalBatteryLevelNotification(false)</text><rect fill="#EEEEEE" filter="url(#f17c9soi04i4ed)" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1161.5" x="0" y="549.8555"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1161.5" y1="549.8555" y2="549.8555"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1161.5" y1="552.8555" y2="552.8555"/><rect fill="#EEEEEE" filter="url(#f17c9soi04i4ed)" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="108" x="526.75" y="539.2891"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="89" x="532.75" y="555.356">Normal flow</text><polygon fill="#A80036" points="329,589.5547,339,593.5547,329,597.5547,333,593.5547" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="94" x2="335" y1="593.5547" y2="593.5547"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="180" x="101" y="588.4888">BatteryLevelCriticalMessage</text><polygon fill="#A80036" points="588,618.6875,598,622.6875,588,626.6875,592,622.6875" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="351" x2="594" y1="622.6875" y2="622.6875"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="197" x="358" y="617.6216">CellularPowerStateChange(Off)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="610" x2="652" y1="651.8203" y2="651.8203"/><line style="stroke:#A80036;stroke-width:1.0;" x1="652" x2="652" y1="651.8203" y2="664.8203"/><line style="stroke:#A80036;stroke-width:1.0;" x1="611" x2="652" y1="664.8203" y2="664.8203"/><polygon fill="#A80036" points="621,660.8203,611,664.8203,621,668.8203,617,664.8203" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="182" x="617" y="646.7544">handle_power_state_change</text><polygon fill="#A80036" points="794,689.9531,804,693.9531,794,697.9531,798,693.9531" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="351" x2="800" y1="693.9531" y2="693.9531"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="231" x="358" y="688.8872">CriticalBatteryLevelNotification(true)</text><polygon fill="#A80036" points="1071,719.0859,1081,723.0859,1071,727.0859,1075,723.0859" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="811" x2="1077" y1="723.0859" y2="723.0859"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="243" x="818" y="718.02">actions::DisplayLowBatteryNotification</text><path d="M10,738.0859 L74,738.0859 L74,745.0859 L64,755.0859 L10,755.0859 L10,738.0859 " fill="#EEEEEE" style="stroke:#000000;stroke-width:1.0;"/><rect fill="none" height="146.6641" style="stroke:#000000;stroke-width:2.0;" width="889.5" x="10" y="738.0859"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="19" x="25" y="751.1528">alt</text><polygon fill="#A80036" points="334,772.3516,344,776.3516,334,780.3516,338,776.3516" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="94" x2="340" y1="776.3516" y2="776.3516"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="184" x="101" y="771.2856">BatteryLevelNormalMessage</text><polygon fill="#A80036" points="588,801.4844,598,805.4844,588,809.4844,592,805.4844" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="356" x2="594" y1="805.4844" y2="805.4844"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="197" x="363" y="800.4185">CellularPowerStateChange(On)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="610" x2="652" y1="834.6172" y2="834.6172"/><line style="stroke:#A80036;stroke-width:1.0;" x1="652" x2="652" y1="834.6172" y2="847.6172"/><line style="stroke:#A80036;stroke-width:1.0;" x1="611" x2="652" y1="847.6172" y2="847.6172"/><polygon fill="#A80036" points="621,843.6172,611,847.6172,621,851.6172,617,847.6172" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="182" x="617" y="829.5513">handle_power_state_change</text><polygon fill="#A80036" points="799,872.75,809,876.75,799,880.75,803,876.75" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="356" x2="805" y1="876.75" y2="876.75"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="235" x="363" y="871.6841">CriticalBatteryLevelNotification(false)</text><!--MD5=[4d4302522169880a6f8cbf95a6a95f20]
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="828px" preserveAspectRatio="none" style="width:1133px;height:828px;" version="1.1" viewBox="0 0 1133 828" width="1133px" zoomAndPan="magnify"><defs><filter height="300%" id="f11zk64p704qoz" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="100.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="306" y="114.5625"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="100.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="306" y="297.3594"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="299.1953" style="stroke:#A80036;stroke-width:1.0;" width="10" x="306" y="477.0234"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="116.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="311" y="659.8203"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="164.6641" style="stroke:#A80036;stroke-width:1.0;" width="10" x="565" y="50.2969"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="153.6641" style="stroke:#A80036;stroke-width:1.0;" width="10" x="565" y="244.0938"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="71.2656" style="stroke:#A80036;stroke-width:1.0;" width="10" x="565" y="506.1563"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="71.2656" style="stroke:#A80036;stroke-width:1.0;" width="10" x="565" y="688.9531"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="771" y="214.9609"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="771" y="577.4219"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="146.6641" style="stroke:#000000;stroke-width:2.0;" width="630" x="234.5" y="259.0938"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="146.6641" style="stroke:#000000;stroke-width:2.0;" width="854.5" x="10" y="621.5547"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="94" x2="94" y1="40.2969" y2="785.2188"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="310.5" x2="310.5" y1="40.2969" y2="785.2188"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="570" x2="570" y1="40.2969" y2="785.2188"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="775.5" x2="775.5" y1="40.2969" y2="785.2188"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="1047.5" x2="1047.5" y1="40.2969" y2="785.2188"/><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="144" x="20" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="130" x="27" y="24.9951">Battery level check</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="144" x="20" y="784.2188"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="130" x="27" y="804.2139">Battery level check</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="129" x="244.5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="115" x="251.5" y="24.9951">System Manager</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="129" x="244.5" y="784.2188"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="115" x="251.5" y="804.2139">System Manager</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="118" x="509" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="104" x="516" y="24.9951">Service Cellular</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="118" x="509" y="784.2188"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="104" x="516" y="804.2139">Service Cellular</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="153" x="697.5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="139" x="704.5" y="24.9951">Application Manager</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="153" x="697.5" y="784.2188"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="139" x="704.5" y="804.2139">Application Manager</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="143" x="974.5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="129" x="981.5" y="24.9951">ApplicaionDesktop</text><rect fill="#FEFECE" filter="url(#f11zk64p704qoz)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="143" x="974.5" y="784.2188"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="129" x="981.5" y="804.2139">ApplicaionDesktop</text><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="100.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="306" y="114.5625"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="100.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="306" y="297.3594"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="299.1953" style="stroke:#A80036;stroke-width:1.0;" width="10" x="306" y="477.0234"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="116.3984" style="stroke:#A80036;stroke-width:1.0;" width="10" x="311" y="659.8203"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="164.6641" style="stroke:#A80036;stroke-width:1.0;" width="10" x="565" y="50.2969"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="153.6641" style="stroke:#A80036;stroke-width:1.0;" width="10" x="565" y="244.0938"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="71.2656" style="stroke:#A80036;stroke-width:1.0;" width="10" x="565" y="506.1563"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="71.2656" style="stroke:#A80036;stroke-width:1.0;" width="10" x="565" y="688.9531"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="771" y="214.9609"/><rect fill="#FFFFFF" filter="url(#f11zk64p704qoz)" height="29.1328" style="stroke:#A80036;stroke-width:1.0;" width="10" x="771" y="577.4219"/><rect fill="#EEEEEE" filter="url(#f11zk64p704qoz)" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1126.5" x="0" y="70.8633"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1126.5" y1="70.8633" y2="70.8633"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1126.5" y1="73.8633" y2="73.8633"/><rect fill="#EEEEEE" filter="url(#f11zk64p704qoz)" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="107" x="509.75" y="60.2969"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="88" x="515.75" y="76.3638">Initial check</text><polygon fill="#A80036" points="327,110.5625,317,114.5625,327,118.5625,323,114.5625" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="321" x2="564" y1="114.5625" y2="114.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="230" x="333" y="109.4966">CellularCheckIfStartAllowedMessage</text><polygon fill="#A80036" points="553,139.6953,563,143.6953,553,147.6953,557,143.6953" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="316" x2="559" y1="143.6953" y2="143.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="197" x="323" y="138.6294">CellularPowerStateChange(Off)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="575" x2="617" y1="172.8281" y2="172.8281"/><line style="stroke:#A80036;stroke-width:1.0;" x1="617" x2="617" y1="172.8281" y2="185.8281"/><line style="stroke:#A80036;stroke-width:1.0;" x1="576" x2="617" y1="185.8281" y2="185.8281"/><polygon fill="#A80036" points="586,181.8281,576,185.8281,586,189.8281,582,185.8281" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="182" x="582" y="167.7622">handle_power_state_change</text><polygon fill="#A80036" points="759,210.9609,769,214.9609,759,218.9609,763,214.9609" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="311" x2="765" y1="214.9609" y2="214.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="231" x="318" y="209.895">CriticalBatteryLevelNotification(true)</text><polygon fill="#A80036" points="1036,240.0938,1046,244.0938,1036,248.0938,1040,244.0938" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="776" x2="1042" y1="244.0938" y2="244.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="243" x="783" y="239.0278">actions::DisplayLowBatteryNotification</text><path d="M234.5,259.0938 L298.5,259.0938 L298.5,266.0938 L288.5,276.0938 L234.5,276.0938 L234.5,259.0938 " fill="#EEEEEE" style="stroke:#000000;stroke-width:1.0;"/><rect fill="none" height="146.6641" style="stroke:#000000;stroke-width:2.0;" width="630" x="234.5" y="259.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="19" x="249.5" y="272.1606">alt</text><polygon fill="#A80036" points="327,293.3594,317,297.3594,327,301.3594,323,297.3594" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="321" x2="564" y1="297.3594" y2="297.3594"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="230" x="333" y="292.2935">CellularCheckIfStartAllowedMessage</text><polygon fill="#A80036" points="553,322.4922,563,326.4922,553,330.4922,557,326.4922" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="316" x2="559" y1="326.4922" y2="326.4922"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="197" x="323" y="321.4263">CellularPowerStateChange(On)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="575" x2="617" y1="355.625" y2="355.625"/><line style="stroke:#A80036;stroke-width:1.0;" x1="617" x2="617" y1="355.625" y2="368.625"/><line style="stroke:#A80036;stroke-width:1.0;" x1="576" x2="617" y1="368.625" y2="368.625"/><polygon fill="#A80036" points="586,364.625,576,368.625,586,372.625,582,368.625" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="182" x="582" y="350.5591">handle_power_state_change</text><polygon fill="#A80036" points="764,393.7578,774,397.7578,764,401.7578,768,397.7578" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="311" x2="770" y1="397.7578" y2="397.7578"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="235" x="318" y="392.6919">CriticalBatteryLevelNotification(false)</text><rect fill="#EEEEEE" filter="url(#f11zk64p704qoz)" height="3" style="stroke:#EEEEEE;stroke-width:1.0;" width="1126.5" x="0" y="433.3242"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1126.5" y1="433.3242" y2="433.3242"/><line style="stroke:#000000;stroke-width:1.0;" x1="0" x2="1126.5" y1="436.3242" y2="436.3242"/><rect fill="#EEEEEE" filter="url(#f11zk64p704qoz)" height="23.1328" style="stroke:#000000;stroke-width:2.0;" width="108" x="509.25" y="422.7578"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="89" x="515.25" y="438.8247">Normal flow</text><polygon fill="#A80036" points="294,473.0234,304,477.0234,294,481.0234,298,477.0234" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="94" x2="300" y1="477.0234" y2="477.0234"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="188" x="101" y="471.9575">BatteryStateChangeMessage</text><polygon fill="#A80036" points="553,502.1563,563,506.1563,553,510.1563,557,506.1563" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="316" x2="559" y1="506.1563" y2="506.1563"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="197" x="323" y="501.0903">CellularPowerStateChange(Off)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="575" x2="617" y1="535.2891" y2="535.2891"/><line style="stroke:#A80036;stroke-width:1.0;" x1="617" x2="617" y1="535.2891" y2="548.2891"/><line style="stroke:#A80036;stroke-width:1.0;" x1="576" x2="617" y1="548.2891" y2="548.2891"/><polygon fill="#A80036" points="586,544.2891,576,548.2891,586,552.2891,582,548.2891" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="182" x="582" y="530.2231">handle_power_state_change</text><polygon fill="#A80036" points="759,573.4219,769,577.4219,759,581.4219,763,577.4219" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="316" x2="765" y1="577.4219" y2="577.4219"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="231" x="323" y="572.356">CriticalBatteryLevelNotification(true)</text><polygon fill="#A80036" points="1036,602.5547,1046,606.5547,1036,610.5547,1040,606.5547" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="776" x2="1042" y1="606.5547" y2="606.5547"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="243" x="783" y="601.4888">actions::DisplayLowBatteryNotification</text><path d="M10,621.5547 L74,621.5547 L74,628.5547 L64,638.5547 L10,638.5547 L10,621.5547 " fill="#EEEEEE" style="stroke:#000000;stroke-width:1.0;"/><rect fill="none" height="146.6641" style="stroke:#000000;stroke-width:2.0;" width="854.5" x="10" y="621.5547"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacing" textLength="19" x="25" y="634.6216">alt</text><polygon fill="#A80036" points="299,655.8203,309,659.8203,299,663.8203,303,659.8203" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="94" x2="305" y1="659.8203" y2="659.8203"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="188" x="101" y="654.7544">BatteryStateChangeMessage</text><polygon fill="#A80036" points="553,684.9531,563,688.9531,553,692.9531,557,688.9531" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="321" x2="559" y1="688.9531" y2="688.9531"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="197" x="328" y="683.8872">CellularPowerStateChange(On)</text><line style="stroke:#A80036;stroke-width:1.0;" x1="575" x2="617" y1="718.0859" y2="718.0859"/><line style="stroke:#A80036;stroke-width:1.0;" x1="617" x2="617" y1="718.0859" y2="731.0859"/><line style="stroke:#A80036;stroke-width:1.0;" x1="576" x2="617" y1="731.0859" y2="731.0859"/><polygon fill="#A80036" points="586,727.0859,576,731.0859,586,735.0859,582,731.0859" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="182" x="582" y="713.02">handle_power_state_change</text><polygon fill="#A80036" points="764,756.2188,774,760.2188,764,764.2188,768,760.2188" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="321" x2="770" y1="760.2188" y2="760.2188"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="235" x="328" y="755.1528">CriticalBatteryLevelNotification(false)</text><!--MD5=[a9b40611bd703dfc937c2afa52a20f92]
@startuml

participant "Battery level check" as batt


@@ 12,10 12,6 @@ participant "ApplicaionDesktop" as appdsktp
    activate cell
    cell -> sysmgr : CellularCheckIfStartAllowedMessage
    activate sysmgr
    sysmgr -> batt : BatteryLevelCriticalCheckMessage
    activate batt
    batt -> sysmgr : BatteryLevelCriticalMessage
    deactivate batt
    sysmgr -> cell : CellularPowerStateChange(Off)
    cell -> cell : handle_power_state_change
    sysmgr -> appmgr : CriticalBatteryLevelNotification(true)


@@ 28,10 24,6 @@ alt
    activate cell
    cell -> sysmgr : CellularCheckIfStartAllowedMessage
    activate sysmgr
    sysmgr -> batt : BatteryLevelNormalCheckMessage
    activate batt
    batt -> sysmgr : BatteryLevelCriticalMessage
    deactivate batt
    sysmgr -> cell : CellularPowerStateChange(On)
    cell -> cell : handle_power_state_change
    sysmgr -> appmgr : CriticalBatteryLevelNotification(false)


@@ 40,7 32,7 @@ alt
end

== Normal flow ==
    batt -> sysmgr : BatteryLevelCriticalMessage
    batt -> sysmgr : BatteryStateChangeMessage
    activate sysmgr
    sysmgr -> cell : CellularPowerStateChange(Off)
    activate cell


@@ 51,7 43,7 @@ end
    appmgr -> appdsktp : actions::DisplayLowBatteryNotification
    deactivate appmgr
alt
    batt -> sysmgr : BatteryLevelNormalMessage
    batt -> sysmgr : BatteryStateChangeMessage
    activate sysmgr
    sysmgr -> cell : CellularPowerStateChange(On)
    activate cell


@@ 62,7 54,7 @@ end

@enduml

PlantUML version 1.2020.22(Sun Dec 06 10:36:27 CET 2020)
PlantUML version 1.2021.00(Sun Jan 10 11:25:05 CET 2021)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +0 -5
@@ 243,11 243,6 @@ sys::ReturnCodes EventManager::InitHandler()
        return std::make_shared<sys::ResponseMessage>();
    });

    connect(sevm::BatteryLevelCriticalCheckMessage(), [&](sys::Message *msgl) {
        EventWorker->checkBatteryLevelCritical();
        return std::make_shared<sys::ResponseMessage>();
    });

    connect(sevm::ScreenLightControlMessage(), [&](sys::Message *msgl) {
        auto *m = dynamic_cast<sevm::ScreenLightControlMessage *>(msgl);
        screenLightControl->processRequest(m->action, m->parameters);

M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +3 -8
@@ 97,7 97,7 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
                bsp::battery_charger::getChargeStatus();
                auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                battery_level_check::checkBatteryLevelCritical();
                battery_level_check::checkBatteryLevel();
                bsp::battery_charger::clearAllChargerIRQs();
            }
            if (topINT & static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::FG_INT)) {


@@ 112,7 112,7 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
                    bsp::battery_charger::getBatteryLevel();
                    auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                    service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                    battery_level_check::checkBatteryLevelCritical();
                    battery_level_check::checkBatteryLevel();
                }
                if (status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::maxTemp) ||
                    status & static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minTemp)) {


@@ 123,7 123,7 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
                    bsp::battery_charger::getChargeStatus();
                    auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
                    service->bus.sendUnicast(std::move(message), service::name::evt_manager);
                    battery_level_check::checkBatteryLevelCritical();
                    battery_level_check::checkBatteryLevel();
                }
            }
        }


@@ 300,8 300,3 @@ void WorkerEvent::processKeyEvent(bsp::KeyEvents event, bsp::KeyCodes code)
    }
    service->bus.sendUnicast(message, service::name::evt_manager);
}

void WorkerEvent::checkBatteryLevelCritical()
{
    battery_level_check::checkBatteryLevelCriticalWithConfirmation();
}

M module-services/service-evtmgr/api/EventManagerServiceAPI.cpp => module-services/service-evtmgr/api/EventManagerServiceAPI.cpp +0 -6
@@ 35,12 35,6 @@ bsp::Board EventManagerServiceAPI::GetBoard(sys::Service *serv)
    return bsp::Board::none;
}

void EventManagerServiceAPI::checkBatteryLevelCriticalState(sys::Service *serv)
{
    auto msg = std::make_shared<sevm::BatteryLevelCriticalCheckMessage>();
    serv->bus.sendUnicast(msg, service::name::evt_manager);
}

/*
 * @brief Call single vibra pulse
 */

M module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.cpp => module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.cpp +39 -51
@@ 23,30 23,12 @@ namespace battery_level_check

        std::shared_ptr<settings::Settings> settings = nullptr;

        void sendCriticalLevelMessage(bool charging)
        {
            auto levelCriticalMessage = std::make_shared<sevm::BatteryLevelCriticalMessage>(charging);
            parentService->bus.sendUnicast(std::move(levelCriticalMessage), service::name::system_manager);
        }

        void sendShutdownLevelMessage()
        {
            auto shutdownMessage = std::make_shared<sevm::BatteryShutdownLevelMessage>();
            parentService->bus.sendUnicast(std::move(shutdownMessage), service::name::system_manager);
        }

        void sendNormalLevelMessage()
        {
            auto levelNormalMessage = std::make_shared<sevm::BatteryLevelNormalMessage>();
            parentService->bus.sendUnicast(std::move(levelNormalMessage), service::name::system_manager);
        }

        namespace sml = boost::sml;

        // events
        struct criticalLevelCheck
        struct initialLevelCheck
        {};
        struct confirmState
        struct criticalLevelCheck
        {};

        // guards


@@ 82,34 64,43 @@ namespace battery_level_check
        } isShutdown;

        // actions
        struct sendCriticalNotCharging
        struct setCriticalNotCharging
        {
            void operator()()
            {
                sendCriticalLevelMessage(false);
                Store::Battery::modify().levelState = Store::Battery::LevelState::CriticalNotCharging;
            }
        } sendCriticalNotCharging;
        struct sendCriticalCharging
        } setCriticalNotCharging;
        struct setCriticalCharging
        {
            void operator()()
            {
                sendCriticalLevelMessage(true);
                Store::Battery::modify().levelState = Store::Battery::LevelState::CriticalCharging;
            }
        } sendCriticalCharging;
        struct sendNormal
        } setCriticalCharging;
        struct setNormal
        {
            void operator()()
            {
                sendNormalLevelMessage();
                Store::Battery::modify().levelState = Store::Battery::LevelState::Normal;
            }
        } sendNormal;
        struct sendShutdown
        } setNormal;
        struct setShutdown
        {
            void operator()()
            {
                sendShutdownLevelMessage();
                Store::Battery::modify().levelState = Store::Battery::LevelState::Shutdown;
            }
        } sendShutdown;
        } setShutdown;

        struct sendStateChange
        {
            void operator()()
            {
                auto stateChangeMessage = std::make_shared<sevm::BatteryStateChangeMessage>();
                parentService->bus.sendUnicast(std::move(stateChangeMessage), service::name::system_manager);
            }
        } sendStateChange;

        struct StateMachine
        {


@@ 118,17 109,19 @@ namespace battery_level_check
                using namespace sml;
                // clang-format off
                return make_transition_table(
                *"LevelNormal"_s + event<criticalLevelCheck> [ isCriticalNotCharging ] / sendCriticalNotCharging = "LevelCriticalNotCharging"_s,
                "LevelNormal"_s + event<criticalLevelCheck> [ isCriticalCharging ] / sendCriticalCharging = "LevelCriticalCharging"_s,
                "LevelCriticalNotCharging"_s + event<criticalLevelCheck> [ isCriticalCharging ] / sendCriticalCharging = "LevelCriticalCharging"_s,
                "LevelCriticalCharging"_s + event<criticalLevelCheck> [ isCriticalNotCharging ] / sendCriticalNotCharging = "LevelCriticalNotCharging"_s,
                "LevelCriticalNotCharging"_s + event<criticalLevelCheck> [ isNormal ] / sendNormal = "LevelNormal"_s,
                "LevelCriticalCharging"_s + event<criticalLevelCheck> [ isNormal ] / sendNormal = "LevelNormal"_s,
                "LevelCriticalNotCharging"_s + event<criticalLevelCheck> [ isShutdown ] / sendShutdown = "Shutdown"_s,
                "LevelNormal"_s + event<confirmState> / sendNormal = "LevelNormal"_s,
                "LevelCriticalNotCharging"_s + event<confirmState> / sendCriticalNotCharging = "LevelCriticalNotCharging"_s,
                "LevelCriticalCharging"_s + event<confirmState> / sendCriticalCharging = "LevelCriticalCharging"_s,
                "Shutdown"_s = X
                *"InitialCheck"_s + event<initialLevelCheck> [ isCriticalNotCharging && !isShutdown ] / setCriticalNotCharging = "LevelCriticalNotCharging"_s,
                "InitialCheck"_s + event<initialLevelCheck> [ isCriticalNotCharging && isShutdown ] / setShutdown = "Shutdown"_s,
                "InitialCheck"_s + event<initialLevelCheck> [ isCriticalCharging ] / setCriticalCharging = "LevelCriticalCharging"_s,
                "InitialCheck"_s + event<initialLevelCheck> [ isNormal ]  / setNormal = "LevelNormal"_s,
                "LevelNormal"_s + event<criticalLevelCheck> [ isCriticalNotCharging && !isShutdown ] / (setCriticalNotCharging, sendStateChange) = "LevelCriticalNotCharging"_s,
                "LevelNormal"_s + event<criticalLevelCheck> [ isCriticalNotCharging && isShutdown ] / (setShutdown, sendStateChange) = "Shutdown"_s,
                "LevelNormal"_s + event<criticalLevelCheck> [ isCriticalCharging ] / (setCriticalCharging, sendStateChange) = "LevelCriticalCharging"_s,
                "LevelCriticalNotCharging"_s + event<criticalLevelCheck> [ isNormal ] / (setNormal, sendStateChange) = "LevelNormal"_s,
                "LevelCriticalNotCharging"_s + event<criticalLevelCheck> [ isCriticalCharging ] / (setCriticalCharging, sendStateChange) = "LevelCriticalCharging"_s,
                "LevelCriticalNotCharging"_s + event<criticalLevelCheck> [ isShutdown ] / (setShutdown, sendStateChange) = "Shutdown"_s,
                "LevelCriticalCharging"_s + event<criticalLevelCheck> [ isCriticalNotCharging && !isShutdown ] / (setCriticalNotCharging, sendStateChange) = "LevelCriticalNotCharging"_s,
                "LevelCriticalCharging"_s + event<criticalLevelCheck> [ isNormal ] / (setNormal, sendStateChange) = "LevelNormal"_s,
                "LevelCriticalCharging"_s + event<criticalLevelCheck> [ isCriticalNotCharging && isShutdown ] / (setShutdown, sendStateChange) = "Shutdown"_s
                );
                // clang-format on
            }


@@ 146,7 139,7 @@ namespace battery_level_check
            settings::Battery::batteryCriticalLevel,
            [&](const std::string &value) { batteryLevelCritical = utils::getNumericValue<unsigned int>(value); },
            settings::SettingsScope::Global);
        checkBatteryLevelCritical();
        sm->process_event(initialLevelCheck{});
    }

    void deinit()


@@ 156,22 149,17 @@ namespace battery_level_check
        sm.reset();
    }

    void checkBatteryLevelCritical()
    void checkBatteryLevel()
    {
        sm->process_event(criticalLevelCheck{});
    }

    void checkBatteryLevelCriticalWithConfirmation()
    {
        sm->process_event(confirmState{});
    }

    void setBatteryCriticalLevel(unsigned int level)
    {
        batteryLevelCritical = level;
        settings->setValue(
            settings::Battery::batteryCriticalLevel, utils::to_string(level), settings::SettingsScope::Global);
        checkBatteryLevelCritical();
        checkBatteryLevel();
    }

} // namespace battery_level_check

M module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.hpp => module-services/service-evtmgr/battery-level-check/BatteryLevelCheck.hpp +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

#pragma once


@@ 17,9 17,7 @@ namespace battery_level_check

    void deinit();

    void checkBatteryLevelCritical();

    void checkBatteryLevelCriticalWithConfirmation();
    void checkBatteryLevel();

    void setBatteryCriticalLevel(unsigned int level);
} // namespace battery_level_check

M module-services/service-evtmgr/doc/battery_level_check_state_machine.puml => module-services/service-evtmgr/doc/battery_level_check_state_machine.puml +13 -13
@@ 1,18 1,18 @@
@startuml

[*] --> InitialCheck
InitialCheck --> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging
InitialCheck --> LevelCriticalCharging : criticalLevelCheck [isCriticalCharging] / sendCriticalCharging
InitialCheck --> LevelNormal : criticalLevelCheck [isNormal] / sendNormal
LevelNormal --> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging
LevelCriticalNotCharging --> LevelCriticalCharging : criticalLevelCheck [isCriticalCharging] / sendCriticalCharging
LevelCriticalCharging --> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging
LevelCriticalNotCharging --> LevelNormal : criticalLevelCheck [isNormal] / sendNormal
LevelCriticalCharging --> LevelNormal : criticalLevelCheck [isNormal] / sendNormal
LevelCriticalNotCharging --> Shutdown : criticalLevelCheck [isShutdown] / sendShutdown
LevelNormal --> LevelNormal : confirmState / sendNormal
LevelCriticalNotCharging --> LevelCriticalNotCharging : confirmState / sendCriticalNotCharging
LevelCriticalCharging --> LevelCriticalCharging : confirmState / sendCriticalCharging
Shutdown --> terminate
InitialCheck --> LevelCriticalNotCharging : initialLevelCheck [isCriticalNotCharging && !isShutdown]
InitialCheck --> Shutdown : initialLevelCheck [isCriticalNotCharging && isShutdown]
InitialCheck --> LevelCriticalCharging : initialLevelCheck [isCriticalCharging]
InitialCheck --> LevelNormal : initialLevelCheck [isNormal]
LevelNormal --> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging && !isShutdown] /\n (setCriticalNotCharging, sendStateChange)
LevelNormal --> Shutdown : criticalLevelCheck [isCriticalNotCharging && isShutdown] /\n (setShutdown, sendStateChange)
LevelNormal --> LevelCriticalCharging : criticalLevelCheck [isCriticalCharging] /\n (setCriticalCharging, sendStateChange)
LevelCriticalNotCharging --> LevelNormal : criticalLevelCheck [isNormal] /\n (setNormal, sendStateChange)
LevelCriticalNotCharging --> LevelCriticalCharging : criticalLevelCheck [isCriticalCharging] /\n (setCriticalCharging, sendStateChange)
LevelCriticalNotCharging --> Shutdown : criticalLevelCheck [isShutdown] / (sendShutdown, sendStateChange)
LevelCriticalCharging --> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging && !isShutdown] /\n (setCriticalNotCharging, sendStateChange)
LevelCriticalCharging --> LevelNormal : criticalLevelCheck [isNormal] /\n (setNormal, sendStateChange)
LevelCriticalCharging --> Shutdown : criticalLevelCheck [isCriticalNotCharging && isShutdown] / \n(setShutdown, sendStateChange)

@enduml

M module-services/service-evtmgr/doc/battery_level_check_state_machine.svg => module-services/service-evtmgr/doc/battery_level_check_state_machine.svg +28 -28
@@ 1,34 1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="532px" preserveAspectRatio="none" style="width:2304px;height:532px;" version="1.1" viewBox="0 0 2304 532" width="2304px" zoomAndPan="magnify"><defs><filter height="300%" id="fxto8wylu90j9" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="1660" cy="16" fill="#000000" filter="url(#fxto8wylu90j9)" rx="10" ry="10" style="stroke:none;stroke-width:1.0;"/><g id="InitialCheck"><rect fill="#FEFECE" filter="url(#fxto8wylu90j9)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="98" x="1611" y="87"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1611" x2="1709" y1="113.2969" y2="113.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="78" x="1621" y="104.9951">InitialCheck</text></g><g id="LevelCriticalNotCharging"><rect fill="#FEFECE" filter="url(#fxto8wylu90j9)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="186" x="665" y="214"/><line style="stroke:#A80036;stroke-width:1.5;" x1="665" x2="851" y1="240.2969" y2="240.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="166" x="675" y="231.9951">LevelCriticalNotCharging</text></g><g id="LevelCriticalCharging"><rect fill="#FEFECE" filter="url(#fxto8wylu90j9)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="162" x="1192" y="341"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1192" x2="1354" y1="367.2969" y2="367.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="142" x="1202" y="358.9951">LevelCriticalCharging</text></g><g id="LevelNormal"><rect fill="#FEFECE" filter="url(#fxto8wylu90j9)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="103" x="949.5" y="468"/><line style="stroke:#A80036;stroke-width:1.5;" x1="949.5" x2="1052.5" y1="494.2969" y2="494.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="83" x="959.5" y="485.9951">LevelNormal</text></g><g id="Shutdown"><rect fill="#FEFECE" filter="url(#fxto8wylu90j9)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="90" x="7" y="341"/><line style="stroke:#A80036;stroke-width:1.5;" x1="7" x2="97" y1="367.2969" y2="367.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="70" x="17" y="358.9951">Shutdown</text></g><g id="terminate"><rect fill="#FEFECE" filter="url(#fxto8wylu90j9)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="86" x="9" y="468"/><line style="stroke:#A80036;stroke-width:1.5;" x1="9" x2="95" y1="494.2969" y2="494.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="66" x="19" y="485.9951">terminate</text></g><!--MD5=[d4999903ff2dd24919f2b1538b17fdaa]
link *start to InitialCheck--><path d="M1660,26.01 C1660,38.7 1660,62.41 1660,81.57 " fill="none" id="*start-to-InitialCheck" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1660,86.84,1664,77.84,1660,81.84,1656,77.84,1660,86.84" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[92dce6cefb7a02d934e3cfca3b958627]
link InitialCheck to LevelCriticalNotCharging--><path d="M1610.88,118.21 C1527.5,127.2 1353.02,146.61 1206,167 C1085.43,183.72 947.12,206.13 856.46,221.27 " fill="none" id="InitialCheck-to-LevelCriticalNotCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="851.37,222.12,860.9086,224.5725,856.3008,221.2911,859.5823,216.6833,851.37,222.12" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="426" x="1207" y="180.0669">criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging</text><!--MD5=[7cf26f7c5c06732917a715b46f14b13e]
link InitialCheck to LevelCriticalCharging--><path d="M1664.04,137.08 C1669.53,178.87 1673.89,264.75 1627,311 C1590.18,347.31 1451.73,359.23 1359.28,363.13 " fill="none" id="InitialCheck-to-LevelCriticalCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1354.09,363.34,1363.253,366.951,1359.0854,363.1259,1362.9105,358.9583,1354.09,363.34" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="380" x="1667" y="243.5669">criticalLevelCheck [isCriticalCharging] / sendCriticalCharging</text><!--MD5=[c538625450f55e3fab0d1c9574b1c463]
link InitialCheck to LevelNormal--><path d="M1709.34,114.11 C1834.24,118.72 2146.77,142.38 2056,264 C1934.28,427.09 1259.67,477.56 1057.97,489.11 " fill="none" id="InitialCheck-to-LevelNormal" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1052.72,489.41,1061.9335,492.89,1057.7119,489.1248,1061.4771,484.9031,1052.72,489.41" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="274" x="2022" y="307.0669">criticalLevelCheck [isNormal] / sendNormal</text><!--MD5=[29a0cbaa3e7031085e7c32fba5460bf1]
link LevelNormal to LevelCriticalNotCharging--><path d="M949.33,488.03 C889.16,480.33 792.23,457.66 747,391 C722.41,354.77 734.48,301.75 745.91,269.13 " fill="none" id="LevelNormal-to-LevelCriticalNotCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="747.74,264.08,740.9242,271.1895,746.0439,268.7835,748.4498,273.9032,747.74,264.08" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="426" x="748" y="370.5669">criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging</text><!--MD5=[f5c482fa1c56c0203ea304ee6ca6a7f3]
link LevelCriticalNotCharging to LevelNormal--><path d="M664.87,251.54 C550.89,269.41 380.81,310.43 448,391 C510.81,466.32 815.79,485.62 944.02,490.45 " fill="none" id="LevelCriticalNotCharging-to-LevelNormal" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="949.25,490.64,940.4051,486.3078,944.2535,490.4539,940.1073,494.3022,949.25,490.64" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="274" x="449" y="370.5669">criticalLevelCheck [isNormal] / sendNormal</text><!--MD5=[bb16dae49642355397c65a9ba7bb3e8f]
link LevelCriticalNotCharging to LevelCriticalCharging--><path d="M851.11,242.72 C966.47,247.5 1155.24,260.34 1216,294 C1233.42,303.65 1247.5,320.99 1257.3,336.12 " fill="none" id="LevelCriticalNotCharging-to-LevelCriticalCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1260.21,340.74,1258.7996,330.9927,1257.5459,336.5088,1252.0297,335.2552,1260.21,340.74" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="380" x="1238" y="307.0669">criticalLevelCheck [isCriticalCharging] / sendCriticalCharging</text><!--MD5=[b4c2a83ad0cf32e6fb8562f37b395c46]
link LevelCriticalCharging to LevelCriticalNotCharging--><path d="M1191.85,345.68 C1182.82,343.92 1173.74,342.3 1165,341 C1122.56,334.66 813.52,337.78 780,311 C767.61,301.1 762.04,284.34 759.61,269.59 " fill="none" id="LevelCriticalCharging-to-LevelCriticalNotCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="758.85,264.26,756.1477,273.7309,759.5491,269.2109,764.0691,272.6123,758.85,264.26" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="426" x="781" y="307.0669">criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging</text><!--MD5=[38f0fa53fd35c1a33d34344416b0d935]
link LevelCriticalCharging to LevelNormal--><path d="M1220.56,391.1 C1173.75,412.61 1105.38,444.03 1057.4,466.08 " fill="none" id="LevelCriticalCharging-to-LevelNormal" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1052.82,468.19,1062.6678,468.0444,1057.3585,466.092,1059.311,460.7828,1052.82,468.19" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="274" x="1154" y="434.0669">criticalLevelCheck [isNormal] / sendNormal</text><!--MD5=[743e923f215b3613f3f57ec27ffb5d84]
link LevelCriticalNotCharging to Shutdown--><path d="M664.79,240.21 C481.87,241.59 90.01,249.75 49,294 C38.84,304.96 38.97,321.44 41.92,335.77 " fill="none" id="LevelCriticalNotCharging-to-Shutdown" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="43.12,340.94,44.9793,331.2682,41.9884,336.0697,37.1869,333.0788,43.12,340.94" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="306" x="50" y="307.0669">criticalLevelCheck [isShutdown] / sendShutdown</text><!--MD5=[0301a2f87f8ca24f4e57abf82bd26793]
link LevelNormal to LevelNormal--><path d="M1052.51,481.4 C1071.7,481.02 1087.5,484.89 1087.5,493 C1087.5,500.35 1074.52,504.22 1057.8,504.59 " fill="none" id="LevelNormal-to-LevelNormal" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1052.51,504.6,1061.5214,508.5743,1057.51,504.5857,1061.4985,500.5743,1052.51,504.6" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="173" x="1093.5" y="497.5669">confirmState / sendNormal</text><!--MD5=[812772c8344e0faa39bad966b02f4d9a]
link LevelCriticalNotCharging to LevelCriticalNotCharging--><path d="M851.2,227.72 C871.37,228.82 886,232.58 886,239 C886,244.87 873.78,248.51 856.27,249.94 " fill="none" id="LevelCriticalNotCharging-to-LevelCriticalNotCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="851.2,250.28,860.4436,253.6795,856.1892,249.9512,859.9175,245.6968,851.2,250.28" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="249" x="892" y="243.5669">confirmState / sendCriticalNotCharging</text><!--MD5=[116352c5a36105c1a0ba6f320acea20f]
link LevelCriticalCharging to LevelCriticalCharging--><path d="M1354.04,354.55 C1374.07,355.37 1389,359.19 1389,366 C1389,372.23 1376.52,375.95 1359.08,377.18 " fill="none" id="LevelCriticalCharging-to-LevelCriticalCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1354.04,377.45,1363.2436,380.9563,1359.0326,377.179,1362.8099,372.968,1354.04,377.45" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="226" x="1395" y="370.5669">confirmState / sendCriticalCharging</text><!--MD5=[37b33c3e4312a7df9ac254896c7d9da3]
link Shutdown to terminate--><path d="M52,391.1 C52,411.59 52,441.08 52,462.88 " fill="none" id="Shutdown-to-terminate" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="52,467.97,56,458.97,52,462.97,48,458.97,52,467.97" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[600677706eb106810a650089e6f085b5]
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="704px" preserveAspectRatio="none" style="width:1935px;height:704px;" version="1.1" viewBox="0 0 1935 704" width="1935px" zoomAndPan="magnify"><defs><filter height="300%" id="fg98y5t5z8q6i" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="574" cy="16" fill="#000000" filter="url(#fg98y5t5z8q6i)" rx="10" ry="10" style="stroke:none;stroke-width:1.0;"/><g id="InitialCheck"><rect fill="#FEFECE" filter="url(#fg98y5t5z8q6i)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="98" x="525" y="87"/><line style="stroke:#A80036;stroke-width:1.5;" x1="525" x2="623" y1="113.2969" y2="113.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="78" x="535" y="104.9951">InitialCheck</text></g><g id="LevelCriticalNotCharging"><rect fill="#FEFECE" filter="url(#fg98y5t5z8q6i)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="186" x="661" y="214"/><line style="stroke:#A80036;stroke-width:1.5;" x1="661" x2="847" y1="240.2969" y2="240.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="166" x="671" y="231.9951">LevelCriticalNotCharging</text></g><g id="Shutdown"><rect fill="#FEFECE" filter="url(#fg98y5t5z8q6i)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="90" x="374" y="640"/><line style="stroke:#A80036;stroke-width:1.5;" x1="374" x2="464" y1="666.2969" y2="666.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="70" x="384" y="657.9951">Shutdown</text></g><g id="LevelCriticalCharging"><rect fill="#FEFECE" filter="url(#fg98y5t5z8q6i)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="162" x="599" y="356"/><line style="stroke:#A80036;stroke-width:1.5;" x1="599" x2="761" y1="382.2969" y2="382.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="142" x="609" y="373.9951">LevelCriticalCharging</text></g><g id="LevelNormal"><rect fill="#FEFECE" filter="url(#fg98y5t5z8q6i)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="103" x="1124.5" y="498"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1124.5" x2="1227.5" y1="524.2969" y2="524.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="83" x="1134.5" y="515.9951">LevelNormal</text></g><!--MD5=[d4999903ff2dd24919f2b1538b17fdaa]
link *start to InitialCheck--><path d="M574,26.01 C574,38.7 574,62.41 574,81.57 " fill="none" id="*start-to-InitialCheck" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="574,86.84,578,77.84,574,81.84,570,77.84,574,86.84" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[92dce6cefb7a02d934e3cfca3b958627]
link InitialCheck to LevelCriticalNotCharging--><path d="M608.71,137.1 C639.21,158.28 683.55,189.08 715.21,211.07 " fill="none" id="InitialCheck-to-LevelCriticalNotCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="719.4,213.97,714.2695,205.563,715.2865,211.1277,709.7218,212.1446,719.4,213.97" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="360" x="675" y="180.0669">initialLevelCheck [isCriticalNotCharging &amp;&amp; !isShutdown]</text><!--MD5=[48d29b19653630c4b14787fb9e51dcbb]
link InitialCheck to Shutdown--><path d="M524.59,113.41 C386.4,115.86 6,132.23 6,238 C6,238 6,238 6,524 C6,599.74 258.26,642.6 368.48,657.72 " fill="none" id="InitialCheck-to-Shutdown" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="373.62,658.42,365.2383,653.2482,368.6652,657.7489,364.1646,661.1758,373.62,658.42" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="355" x="7" y="385.5669">initialLevelCheck [isCriticalNotCharging &amp;&amp; isShutdown]</text><!--MD5=[7cf26f7c5c06732917a715b46f14b13e]
link InitialCheck to LevelCriticalCharging--><path d="M524.83,128.94 C486.76,144.19 436.64,171.27 413,214 C402.24,233.45 406.72,242.68 413,264 C422.27,295.46 426.23,307.06 453,326 C493.92,354.95 548.31,368.53 593.59,374.83 " fill="none" id="InitialCheck-to-LevelCriticalCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="598.79,375.53,590.4083,370.3582,593.8352,374.8589,589.3346,378.2858,598.79,375.53" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="229" x="414" y="243.5669">initialLevelCheck [isCriticalCharging]</text><!--MD5=[c538625450f55e3fab0d1c9574b1c463]
link InitialCheck to LevelNormal--><path d="M623.27,114.72 C831.9,123.13 1638.64,167.62 1785,356 C1798.63,373.55 1799.17,388.88 1785,406 C1715.05,490.53 1370.26,513.99 1232.76,520.04 " fill="none" id="InitialCheck-to-LevelNormal" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1227.56,520.27,1236.723,523.881,1232.5554,520.0559,1236.3805,515.8883,1227.56,520.27" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="176" x="1751" y="314.5669">initialLevelCheck [isNormal]</text><!--MD5=[29a0cbaa3e7031085e7c32fba5460bf1]
link LevelNormal to LevelCriticalNotCharging--><path d="M1227.57,506.95 C1305.4,481.56 1438.68,426.08 1388,356 C1326.5,270.96 1014.71,248.26 852.45,242.2 " fill="none" id="LevelNormal-to-LevelCriticalNotCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="847.22,242.01,856.0649,246.3422,852.2165,242.1961,856.3627,238.3478,847.22,242.01" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="376" x="1400" y="378.0669">criticalLevelCheck [isCriticalNotCharging &amp;&amp; !isShutdown] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="276" x="1452" y="393.1997">(setCriticalNotCharging, sendStateChange)</text><!--MD5=[f5c482fa1c56c0203ea304ee6ca6a7f3]
link LevelCriticalNotCharging to LevelNormal--><path d="M847.17,242.63 C944.66,247.21 1089.37,259.74 1132,294 C1173.44,327.3 1141.87,363.64 1174,406 C1187.65,423.99 1204.78,415.86 1215,436 C1224.57,454.87 1214.58,476.57 1202.32,493.45 " fill="none" id="LevelCriticalNotCharging-to-LevelNormal" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1199.16,497.64,1207.7791,492.8745,1202.176,493.652,1201.3984,488.0489,1199.16,497.64" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="192" x="1181" y="378.0669">criticalLevelCheck [isNormal] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="200" x="1179" y="393.1997">(setNormal, sendStateChange)</text><!--MD5=[f0fd995ca85f74627a924678c0d1d6ba]
link LevelNormal to Shutdown--><path d="M1124.27,533.57 C985.52,559.23 606.4,629.34 469.31,654.7 " fill="none" id="LevelNormal-to-Shutdown" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="464.14,655.65,473.7198,657.9363,469.0557,654.7355,472.2565,650.0713,464.14,655.65" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="371" x="866" y="591.0669">criticalLevelCheck [isCriticalNotCharging &amp;&amp; isShutdown] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="216" x="945.5" y="606.1997">(setShutdown, sendStateChange)</text><!--MD5=[5660824d2304c2c05fa5f23238c324d7]
link LevelNormal to LevelCriticalCharging--><path d="M1124.34,512.57 C1067.91,501.74 981.6,483.47 952,468 C933.03,458.08 935.03,445.81 916,436 C869.5,412.04 812.43,398.61 766.06,391.14 " fill="none" id="LevelNormal-to-LevelCriticalCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="761.03,390.35,769.3055,395.69,765.9702,391.121,770.5392,387.7857,761.03,390.35" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="245" x="959" y="449.0669">criticalLevelCheck [isCriticalCharging] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="253" x="957" y="464.1997">(setCriticalCharging, sendStateChange)</text><!--MD5=[38f0fa53fd35c1a33d34344416b0d935]
link LevelCriticalCharging to LevelNormal--><path d="M679.14,406.32 C679.87,426.07 684.39,453.02 702,468 C758.76,516.29 962.1,488.36 1036,498 C1063.51,501.59 1093.88,506.66 1119.28,511.21 " fill="none" id="LevelCriticalCharging-to-LevelNormal" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1124.29,512.12,1116.1422,506.5871,1119.3693,511.2331,1114.7232,514.4602,1124.29,512.12" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="192" x="709" y="449.0669">criticalLevelCheck [isNormal] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="200" x="707" y="464.1997">(setNormal, sendStateChange)</text><!--MD5=[bb16dae49642355397c65a9ba7bb3e8f]
link LevelCriticalNotCharging to LevelCriticalCharging--><path d="M660.78,256.35 C579.52,270.83 474.12,290.22 471,294 C431.62,341.61 519.76,363.19 593.45,372.74 " fill="none" id="LevelCriticalNotCharging-to-LevelCriticalCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="598.73,373.41,590.3047,368.3096,593.7697,372.781,589.2983,376.2461,598.73,373.41" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="245" x="478" y="307.0669">criticalLevelCheck [isCriticalCharging] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="253" x="476" y="322.1997">(setCriticalCharging, sendStateChange)</text><!--MD5=[b4c2a83ad0cf32e6fb8562f37b395c46]
link LevelCriticalCharging to LevelCriticalNotCharging--><path d="M709.95,355.86 C718.87,347.29 727.88,337.01 734,326 C743.64,308.63 748.65,286.81 751.24,269.49 " fill="none" id="LevelCriticalCharging-to-LevelCriticalNotCharging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="751.97,264.27,746.7475,272.6202,751.2689,269.2206,754.6685,273.742,751.97,264.27" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="376" x="747" y="307.0669">criticalLevelCheck [isCriticalNotCharging &amp;&amp; !isShutdown] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="276" x="799" y="322.1997">(setCriticalNotCharging, sendStateChange)</text><!--MD5=[743e923f215b3613f3f57ec27ffb5d84]
link LevelCriticalNotCharging to Shutdown--><path d="M660.96,262.22 C657.94,262.84 654.95,263.43 652,264 C564.17,280.92 529.52,247.69 453,294 C400.22,325.94 423.68,373.9 371,406 C297.62,450.72 235.85,370.69 180,436 C108.23,519.92 279.64,606.59 369.01,644.39 " fill="none" id="LevelCriticalNotCharging-to-Shutdown" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="373.73,646.37,366.9875,639.1909,369.1218,644.4297,363.8831,646.564,373.73,646.37" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="439" x="181" y="456.5669">criticalLevelCheck [isShutdown] / (sendShutdown, sendStateChange)</text><!--MD5=[1f88bde0fbb3174b34231d4b5c203335]
link LevelCriticalCharging to Shutdown--><path d="M668.06,406.17 C658.65,424.09 644.56,448.66 629,468 C574.98,535.13 498.22,600.93 454.02,636.61 " fill="none" id="LevelCriticalCharging-to-Shutdown" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="450.11,639.76,459.6332,637.2485,454.0102,636.6313,454.6273,631.0082,450.11,639.76" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="371" x="601" y="520.0669">criticalLevelCheck [isCriticalNotCharging &amp;&amp; isShutdown] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="216" x="680.5" y="535.1997">(setShutdown, sendStateChange)</text><!--MD5=[f147cc7dc8dd5976e6f190e94b133957]
@startuml

[*] - -> InitialCheck
InitialCheck - -> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging
InitialCheck - -> LevelCriticalCharging : criticalLevelCheck [isCriticalCharging] / sendCriticalCharging
InitialCheck - -> LevelNormal : criticalLevelCheck [isNormal] / sendNormal
LevelNormal - -> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging
LevelCriticalNotCharging - -> LevelCriticalCharging : criticalLevelCheck [isCriticalCharging] / sendCriticalCharging
LevelCriticalCharging - -> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging] / sendCriticalNotCharging
LevelCriticalNotCharging - -> LevelNormal : criticalLevelCheck [isNormal] / sendNormal
LevelCriticalCharging - -> LevelNormal : criticalLevelCheck [isNormal] / sendNormal
LevelCriticalNotCharging - -> Shutdown : criticalLevelCheck [isShutdown] / sendShutdown
LevelNormal - -> LevelNormal : confirmState / sendNormal
LevelCriticalNotCharging - -> LevelCriticalNotCharging : confirmState / sendCriticalNotCharging
LevelCriticalCharging - -> LevelCriticalCharging : confirmState / sendCriticalCharging
Shutdown - -> terminate
InitialCheck - -> LevelCriticalNotCharging : initialLevelCheck [isCriticalNotCharging && !isShutdown]
InitialCheck - -> Shutdown : initialLevelCheck [isCriticalNotCharging && isShutdown]
InitialCheck - -> LevelCriticalCharging : initialLevelCheck [isCriticalCharging]
InitialCheck - -> LevelNormal : initialLevelCheck [isNormal]
LevelNormal - -> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging && !isShutdown] /\n (setCriticalNotCharging, sendStateChange)
LevelNormal - -> Shutdown : criticalLevelCheck [isCriticalNotCharging && isShutdown] /\n (setShutdown, sendStateChange)
LevelNormal - -> LevelCriticalCharging : criticalLevelCheck [isCriticalCharging] /\n (setCriticalCharging, sendStateChange)
LevelCriticalNotCharging - -> LevelNormal : criticalLevelCheck [isNormal] /\n (setNormal, sendStateChange)
LevelCriticalNotCharging - -> LevelCriticalCharging : criticalLevelCheck [isCriticalCharging] /\n (setCriticalCharging, sendStateChange)
LevelCriticalNotCharging - -> Shutdown : criticalLevelCheck [isShutdown] / (sendShutdown, sendStateChange)
LevelCriticalCharging - -> LevelCriticalNotCharging : criticalLevelCheck [isCriticalNotCharging && !isShutdown] /\n (setCriticalNotCharging, sendStateChange)
LevelCriticalCharging - -> LevelNormal : criticalLevelCheck [isNormal] /\n (setNormal, sendStateChange)
LevelCriticalCharging - -> Shutdown : criticalLevelCheck [isCriticalNotCharging && isShutdown] / \n(setShutdown, sendStateChange)

@enduml


M module-services/service-evtmgr/service-evtmgr/BatteryMessages.hpp => module-services/service-evtmgr/service-evtmgr/BatteryMessages.hpp +7 -27
@@ 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,41 9,21 @@

namespace sevm
{
    class BatteryStatusChangeMessage : public sys::Message
    class BatteryStatusChangeMessage : public Message
    {};

    class BatterySetCriticalLevel : public sys::Message
    class BatterySetCriticalLevel : public Message
    {
      public:
        BatterySetCriticalLevel(std::uint8_t level) : Message(), criticalLevel(level)
        BatterySetCriticalLevel(std::uint8_t level) : criticalLevel(level)
        {}
        unsigned int criticalLevel = 0;
        const unsigned int criticalLevel = 0;
    };

    class BatteryLevelCriticalCheckMessage : public sys::Message
    {};
    class BatteryLevelCriticalMessage : public sys::Message
    {
      public:
        explicit BatteryLevelCriticalMessage(bool charging) : charging(charging)
        {}

        bool isCharging() const noexcept
        {
            return charging;
        }

      private:
        bool charging;
    };

    class BatteryShutdownLevelMessage : public sys::Message
    {};

    class BatteryLevelNormalMessage : public sys::Message
    class BatteryStateChangeMessage : public Message
    {};

    class BatteryBrownoutMessage : public sys::Message
    class BatteryBrownoutMessage : public Message
    {};

} // namespace sevm

M module-services/service-evtmgr/service-evtmgr/EventManagerServiceAPI.hpp => module-services/service-evtmgr/service-evtmgr/EventManagerServiceAPI.hpp +0 -2
@@ 22,8 22,6 @@ namespace EventManagerServiceAPI
     */
    bsp::Board GetBoard(sys::Service *serv);

    void checkBatteryLevelCriticalState(sys::Service *serv);

    /*
     * @brief Call single vibra pulse
     */

M module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp => module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp +0 -1
@@ 84,5 84,4 @@ class WorkerEvent : public sys::Worker
     */
    bool handleMessage(uint32_t queueID) override final;

    void checkBatteryLevelCritical();
};

M module-sys/SystemManager/SystemManager.cpp => module-sys/SystemManager/SystemManager.cpp +101 -33
@@ 29,6 29,7 @@
#include "messages/TetheringStateRequest.hpp"
#include <time/ScopedTime.hpp>
#include "Timers/TimerFactory.hpp"
#include <service-appmgr/StartupType.hpp>

const inline size_t systemManagerStack = 4096 * 2;



@@ 41,7 42,8 @@ namespace sys
            {bsp::KeyCodes::SSwitchMid, phone_modes::PhoneMode::DoNotDisturb},
            {bsp::KeyCodes::SSwitchDown, phone_modes::PhoneMode::Offline}};

        constexpr auto preShutdownRoutineTimeout = 1500;
        constexpr std::chrono::milliseconds preShutdownRoutineTimeout{1500};
        constexpr std::chrono::milliseconds lowBatteryShutdownDelayTime{5000};
    } // namespace

    using namespace cpp_freertos;


@@ 59,6 61,10 @@ namespace sys
    {
        // Specify list of channels which System Manager is registered to
        bus.channels = {BusChannel::SystemManagerRequests};
        lowBatteryShutdownDelay = sys::TimerFactory::createPeriodicTimer(
            this, "lowBatteryShutdownDelay", lowBatteryShutdownDelayTime, [this](sys::Timer &) {
                CloseSystemHandler(CloseReason::LowBattery);
            });
    }

    SystemManager::~SystemManager()


@@ 152,6 158,8 @@ namespace sys
                throw SystemInitialisationError{"System startup failed: unable to start a system service."};
            }
        });

        postStartRoutine();
    }

    void SystemManager::StartSystem(InitFunction sysInit, InitFunction appSpaceInit)


@@ 308,14 316,54 @@ namespace sys
            readyForCloseRegister.push_back(service->GetName());
        }

        servicesPreShutdownRoutineTimeout =
            sys::TimerFactory::createPeriodicTimer(this,
                                                   "servicesPreShutdownRoutine",
                                                   std::chrono::milliseconds{preShutdownRoutineTimeout},
                                                   [this](sys::Timer &) { CloseServices(); });
        servicesPreShutdownRoutineTimeout = sys::TimerFactory::createPeriodicTimer(
            this, "servicesPreShutdownRoutine", preShutdownRoutineTimeout, [this](sys::Timer &) { CloseServices(); });
        servicesPreShutdownRoutineTimeout.start();
    }

    void SystemManager::postStartRoutine()
    {
        connect(sevm::BatteryStateChangeMessage(), [&](Message *) {
            switch (Store::Battery::get().levelState) {
            case Store::Battery::LevelState::Normal:
                batteryNormalLevelAction();
                break;
            case Store::Battery::LevelState::Shutdown:
                batteryShutdownLevelAction();
                break;
            case Store::Battery::LevelState::CriticalCharging:
                batteryCriticalLevelAction(true);
                break;
            case Store::Battery::LevelState::CriticalNotCharging:
                batteryCriticalLevelAction(false);
                break;
            }
            return MessageNone{};
        });
    }

    void SystemManager::batteryCriticalLevelAction(bool charging)
    {
        LOG_INFO("Battery Critical Level reached!");
        CellularServiceAPI::ChangeModulePowerState(this, cellular::State::PowerState::Off);
        auto msg = std::make_shared<CriticalBatteryLevelNotification>(true, charging);
        bus.sendUnicast(std::move(msg), app::manager::ApplicationManager::ServiceName);
    }

    void SystemManager::batteryShutdownLevelAction()
    {
        LOG_INFO("Battery level too low - shutting down the system...");
        CloseSystemHandler(CloseReason::LowBattery);
    }

    void SystemManager::batteryNormalLevelAction()
    {
        LOG_INFO("Battery level normal.");
        CellularServiceAPI::ChangeModulePowerState(this, cellular::State::PowerState::On);
        auto battNormalMsg = std::make_shared<CriticalBatteryLevelNotification>(false);
        bus.sendUnicast(std::move(battNormalMsg), app::manager::ApplicationManager::ServiceName);
    }

    void SystemManager::readyToCloseHandler(Message *msg)
    {
        if (!readyForCloseRegister.empty() && servicesPreShutdownRoutineTimeout.isActive()) {


@@ 341,6 389,11 @@ namespace sys
            LOG_DEBUG("deinit handler: %s", c_str(ret));
        }
        toKill->CloseHandler();

        servicesList.erase(
            std::find_if(servicesList.begin(), servicesList.end(), [&toKill](std::shared_ptr<Service> const &s) {
                return s->GetName() == toKill->GetName();
            }));
    }

    ReturnCodes SystemManager::InitHandler()


@@ 389,34 442,19 @@ namespace sys
            return MessageNone{};
        });

        connect(sevm::BatteryShutdownLevelMessage(), [&](Message *) {
            LOG_INFO("Battery level too low - shutting down the system");
            CloseSystemHandler(CloseReason::LowBattery);
            return MessageNone{};
        });

        connect(CellularCheckIfStartAllowedMessage(), [&](Message *) {
            EventManagerServiceAPI::checkBatteryLevelCriticalState(this);
            return MessageNone{};
        });

        connect(sevm::BatteryLevelCriticalMessage(bool{}), [&](Message *req) {
            LOG_INFO("Battery Critical Level reached!");
            CellularServiceAPI::ChangeModulePowerState(this, cellular::State::PowerState::Off);

            auto request = static_cast<sevm::BatteryLevelCriticalMessage *>(req);
            auto msg     = std::make_shared<CriticalBatteryLevelNotification>(true, request->isCharging());
            bus.sendUnicast(msg, app::manager::ApplicationManager::ServiceName);

            return MessageNone{};
        });

        connect(sevm::BatteryLevelNormalMessage(), [&](Message *) {
            LOG_INFO("Battery level normal.");
            CellularServiceAPI::ChangeModulePowerState(this, cellular::State::PowerState::On);

            auto msg = std::make_shared<CriticalBatteryLevelNotification>(false);
            bus.sendUnicast(msg, app::manager::ApplicationManager::ServiceName);
            switch (Store::Battery::get().levelState) {
            case Store::Battery::LevelState::Normal:
                CellularServiceAPI::ChangeModulePowerState(this, cellular::State::PowerState::On);
                break;
            case Store::Battery::LevelState::CriticalCharging:
                [[fallthrough]];
            case Store::Battery::LevelState::CriticalNotCharging:
                CellularServiceAPI::ChangeModulePowerState(this, cellular::State::PowerState::Off);
                break;
            case Store::Battery::LevelState::Shutdown:
                break;
            }
            return MessageNone{};
        });



@@ 483,6 521,31 @@ namespace sys
            return handleTetheringStateRequest(request);
        });

        connect(typeid(app::manager::CheckIfStartAllowedMessage), [this](sys::Message *) -> sys::MessagePointer {
            switch (Store::Battery::get().levelState) {
            case Store::Battery::LevelState::Normal:
                bus.sendUnicast(std::make_unique<app::manager::StartAllowedMessage>(app::manager::StartupType::Regular),
                                app::manager::ApplicationManager::ServiceName);
                break;
            case Store::Battery::LevelState::Shutdown:
                if (!lowBatteryShutdownDelay.isActive()) {
                    lowBatteryShutdownDelay.start();
                }
                [[fallthrough]];
            case Store::Battery::LevelState::CriticalNotCharging:
                bus.sendUnicast(
                    std::make_unique<app::manager::StartAllowedMessage>(app::manager::StartupType::LowBattery),
                    app::manager::ApplicationManager::ServiceName);
                break;
            case Store::Battery::LevelState::CriticalCharging:
                bus.sendUnicast(
                    std::make_unique<app::manager::StartAllowedMessage>(app::manager::StartupType::LowBatteryCharging),
                    app::manager::ApplicationManager::ServiceName);
                break;
            }
            return sys::MessageNone{};
        });

        deviceManager->RegisterNewDevice(powerManager->getExternalRamDevice());

        return ReturnCodes::Success;


@@ 497,6 560,9 @@ namespace sys
    {
        LOG_DEBUG("Invoking closing procedure...");

        // In case if other power down request arrive in the meantime
        lowBatteryShutdownDelay.stop();

        // We are going to remove services in reversed order of creation
        CriticalSection::Enter();
        std::reverse(servicesList.begin(), servicesList.end());


@@ 510,6 576,8 @@ namespace sys
        for (const auto &element : readyForCloseRegister) {
            LOG_INFO("Service: %s did not reported before timeout", element.c_str());
        }
        // All delayed messages will be ignored
        readyForCloseRegister.clear();

        for (bool retry{};; retry = false) {
            for (auto &service : servicesList) {

M module-sys/SystemManager/SystemManager.hpp => module-sys/SystemManager/SystemManager.hpp +7 -0
@@ 161,6 161,8 @@ namespace sys

        void preCloseRoutine(CloseReason closeReason);

        void postStartRoutine();

        void readyToCloseHandler(Message *msg);

        void UpdateSystemHandler();


@@ 181,11 183,16 @@ namespace sys
        MessagePointer handlePhoneModeRequest(PhoneModeRequest *request);
        MessagePointer handleTetheringStateRequest(TetheringStateRequest *request);

        void batteryCriticalLevelAction(bool charging);
        void batteryNormalLevelAction();
        void batteryShutdownLevelAction();

        bool cpuStatisticsTimerInit{false};

        std::vector<std::unique_ptr<BaseServiceCreator>> systemServiceCreators;
        sys::TimerHandle cpuStatisticsTimer;
        sys::TimerHandle servicesPreShutdownRoutineTimeout;
        sys::TimerHandle lowBatteryShutdownDelay;
        std::unique_ptr<phone_modes::Subject> phoneModeSubject;
        InitFunction userInit;
        InitFunction systemInit;

M module-utils/common_data/EventStore.hpp => module-utils/common_data/EventStore.hpp +9 -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


@@ 21,6 21,14 @@ namespace Store
{
    struct Battery
    {
        enum class LevelState
        {
            Normal,
            Shutdown,
            CriticalCharging,
            CriticalNotCharging
        } levelState = LevelState::Normal;

        enum class State
        {
            Discharging,