Skip to content

Commit

Permalink
Added support for explicit current state request
Browse files Browse the repository at this point in the history
  • Loading branch information
OleksandrChaika committed Jan 13, 2023
1 parent f22c749 commit 6995d4a
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 32 deletions.
4 changes: 4 additions & 0 deletions docs/testing/mqtt/current-state-get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"activityId": "random-uuid-as-string",
"timestamp": 123456789
}
38 changes: 38 additions & 0 deletions src/FSM/States/SendCurrentState.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2022 Contributors to the Eclipse Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#include "FSM/States/SendCurrentState.h"
#include "FSM/FSM.h"
#include "Context.h"
#include "FotaEvent.h"

namespace sua {

SendCurrentState::SendCurrentState()
: SendCurrentState("SendCurrentState")
{ }

SendCurrentState::SendCurrentState(const std::string& name)
: State(name)
{ }

void SendCurrentState::onEnter(Context& ctx)
{
send(ctx, IMqttProcessor::TOPIC_STATE, "systemVersion", true);
ctx.stateMachine->handleEvent(FotaEvent::Waiting);
}

} // namespace sua
34 changes: 34 additions & 0 deletions src/FSM/States/SendCurrentState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2022 Contributors to the Eclipse Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef SDV_SUA_SENDCURRENTSTATE_H
#define SDV_SUA_SENDCURRENTSTATE_H

#include "FSM/State.h"

namespace sua {

class SendCurrentState : public State {
public:
SendCurrentState();
SendCurrentState(const std::string& name);

void onEnter(Context& ctx) override;
};

} // namespace sua

#endif
1 change: 1 addition & 0 deletions src/FotaEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace sua {
{ ConnectivityEstablished , "ConnectivityEstablished" },
{ ConnectivityLost , "ConnectivityLost" },
{ Start , "Start" },
{ GetCurrentState , "GetCurrentState" },
{ BundleVersionOK , "BundleVersionOK" },
{ BundleVersionUnchanged , "BundleVersionUnchanged" },
{ BundleVersionInconsistent , "BundleVersionInconsistent" },
Expand Down
1 change: 1 addition & 0 deletions src/FotaEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace sua {
ConnectivityEstablished,
ConnectivityLost,
Start,
GetCurrentState,
BundleVersionOK,
BundleVersionUnchanged,
BundleVersionInconsistent,
Expand Down
2 changes: 2 additions & 0 deletions src/Mqtt/IMqttMessagingProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace sua {

virtual class DesiredState readDesiredState(const std::string & input) = 0;

virtual class DesiredState readCurrentStateRequest(const std::string & input) = 0;

virtual std::string createMessage(const class Context& ctx, const std::string& name) = 0;
};

Expand Down
7 changes: 4 additions & 3 deletions src/Mqtt/IMqttProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ namespace sua {

class IMqttProcessor {
public:
static constexpr const char * TOPIC_START = "selfupdate/desiredstate";
static constexpr const char * TOPIC_FEEDBACK = "selfupdate/desiredstatefeedback";
static constexpr const char * TOPIC_STATE = "selfupdate/currentstate";
static constexpr const char * TOPIC_START = "selfupdate/desiredstate";
static constexpr const char * TOPIC_FEEDBACK = "selfupdate/desiredstatefeedback";
static constexpr const char * TOPIC_STATE = "selfupdate/currentstate";
static constexpr const char * TOPIC_STATE_GET = "selfupdate/currentstate/get";

virtual ~IMqttProcessor() = default;

Expand Down
14 changes: 12 additions & 2 deletions src/Mqtt/MqttMessagingProtocolJSON.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,23 @@ namespace sua {
// clang-format on
}

DesiredState MqttMessagingProtocolJSON::readCurrentStateRequest(const std::string & input)
{
// clang-format off
DesiredState s;
std::stringstream ss(input);
nlohmann::json json = nlohmann::json::parse(ss);
s.activityId = json["activityId"];
return s;
// clang-format on
}

std::string MqttMessagingProtocolJSON::createMessage(const class Context& ctx, const std::string& name)
{
if(name == "systemVersion") {
// clang-format off
const std::string tpl = jsonTemplate(R"(
{
"activityId": "{}",
"timestamp": {},
"payload": {
"domains": [
Expand All @@ -92,7 +102,7 @@ namespace sua {
)");
// clang-format on

return fmt::format(tpl, ctx.desiredState.activityId, epochTime(), ctx.currentState.version);
return fmt::format(tpl, epochTime(), ctx.currentState.version);
}

if(name == "identifying") {
Expand Down
2 changes: 2 additions & 0 deletions src/Mqtt/MqttMessagingProtocolJSON.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ namespace sua {
public:
DesiredState readDesiredState(const std::string & input) override;

DesiredState readCurrentStateRequest(const std::string & input) override;

std::string createMessage(const class Context& ctx, const std::string& name) override;

protected:
Expand Down
6 changes: 6 additions & 0 deletions src/Mqtt/MqttMessagingProtocolYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ namespace sua {
// clang-format on
}

DesiredState MqttMessagingProtocolYAML::readCurrentStateRequest(const std::string & input)
{
// ignored, no activity id needed, simply pass empty result and trigger response
return {};
}

std::string MqttMessagingProtocolYAML::createMessage(const class Context& ctx, const std::string& name)
{
Yaml::Node root;
Expand Down
2 changes: 2 additions & 0 deletions src/Mqtt/MqttMessagingProtocolYAML.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ namespace sua {
public:
DesiredState readDesiredState(const std::string & input) override;

DesiredState readCurrentStateRequest(const std::string & input) override;

std::string createMessage(const class Context& ctx, const std::string& name) override;
};

Expand Down
5 changes: 4 additions & 1 deletion src/Mqtt/MqttProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ namespace {
{
sua::Logger::trace("MqttCallback::connected");
_mqttClient.subscribe(sua::IMqttProcessor::TOPIC_START, QUALITY, nullptr, _actionListener);
_mqttClient.subscribe(sua::IMqttProcessor::TOPIC_STATE_GET, QUALITY, nullptr, _actionListener);
_context.stateMachine->handleEvent(sua::FotaEvent::ConnectivityEstablished);
}

Expand All @@ -100,7 +101,9 @@ namespace {
if(msg->get_topic() == sua::IMqttProcessor::TOPIC_START) {
_context.desiredState = _context.messagingProtocol->readDesiredState(msg->get_payload_str());
_context.stateMachine->handleEvent(sua::FotaEvent::Start);
return;
} if(msg->get_topic() == sua::IMqttProcessor::TOPIC_STATE_GET) {
_context.desiredState = _context.messagingProtocol->readCurrentStateRequest(msg->get_payload_str());
_context.stateMachine->handleEvent(sua::FotaEvent::GetCurrentState);
}
}

Expand Down
56 changes: 31 additions & 25 deletions src/SelfUpdateAgent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "FSM/States/Idle.h"
#include "FSM/States/Installed.h"
#include "FSM/States/Installing.h"
#include "FSM/States/SendCurrentState.h"
#include "FSM/States/Uninitialized.h"
#include "FotaEvent.h"
#include "Logger.h"
Expand All @@ -35,43 +36,48 @@ namespace sua {
{
// clang-format off
auto factory = std::make_shared<StateFactory>();
factory->addStateT<Connected >("Connected" );
factory->addStateT<Downloading >("Downloading" );
factory->addStateT<Failed >("Failed" );
factory->addStateT<Idle >("Idle" );
factory->addStateT<Installed >("Installed" );
factory->addStateT<Installing >("Installing" );
factory->addStateT<Uninitialized>("Uninitialized");
factory->addStateT<Connected >("Connected" );
factory->addStateT<Downloading >("Downloading" );
factory->addStateT<Failed >("Failed" );
factory->addStateT<Idle >("Idle" );
factory->addStateT<Installed >("Installed" );
factory->addStateT<Installing >("Installing" );
factory->addStateT<SendCurrentState>("SendCurrentState");
factory->addStateT<Uninitialized >("Uninitialized" );
_context.stateMachine->setFactory(factory);
// clang-format-on

// clang-format off
_context.stateMachine->setTransitions({
// from "Uninitialized"
{ FotaEvent::ConnectivityEstablished , "Uninitialized", "Connected" },
{ FotaEvent::ConnectivityEstablished, "Uninitialized" , "Connected" },
// from "Connected"
{ FotaEvent::ConnectivityLost , "Connected" , "Uninitialized" },
{ FotaEvent::Start , "Connected" , "Failed" , FotaEvent::BundleVersionUnchanged },
{ FotaEvent::Start , "Connected" , "Downloading" , FotaEvent::BundleVersionOK },
{ FotaEvent::ConnectivityLost , "Connected" , "Uninitialized" },
{ FotaEvent::Start , "Connected" , "Failed" , FotaEvent::BundleVersionUnchanged },
{ FotaEvent::Start , "Connected" , "Downloading" , FotaEvent::BundleVersionOK },
{ FotaEvent::GetCurrentState , "Connected" , "SendCurrentState"},
// from "Failed"
{ FotaEvent::ConnectivityLost , "Failed" , "Uninitialized" },
{ FotaEvent::Waiting , "Failed" , "Idle" },
{ FotaEvent::ConnectivityLost , "Failed" , "Uninitialized" },
{ FotaEvent::Waiting , "Failed" , "Idle" },
// from "Downloading"
{ FotaEvent::ConnectivityLost , "Downloading" , "Uninitialized" },
{ FotaEvent::DownloadStart , "Downloading" , "Installing" , FotaEvent::BundleVersionOK },
{ FotaEvent::DownloadStart , "Downloading" , "Failed" , FotaEvent::BundleVersionInconsistent },
{ FotaEvent::DownloadStart , "Downloading" , "Failed" , FotaEvent::DownloadFailed },
{ FotaEvent::ConnectivityLost , "Downloading" , "Uninitialized" },
{ FotaEvent::DownloadStart , "Downloading" , "Installing" , FotaEvent::BundleVersionOK },
{ FotaEvent::DownloadStart , "Downloading" , "Failed" , FotaEvent::BundleVersionInconsistent },
{ FotaEvent::DownloadStart , "Downloading" , "Failed" , FotaEvent::DownloadFailed },
// from "Installing"
{ FotaEvent::ConnectivityLost , "Installing" , "Uninitialized" },
{ FotaEvent::InstallStart , "Installing" , "Installed" , FotaEvent::InstallCompleted },
{ FotaEvent::InstallStart , "Installing" , "Failed" , FotaEvent::InstallFailed },
{ FotaEvent::ConnectivityLost , "Installing" , "Uninitialized" },
{ FotaEvent::InstallStart , "Installing" , "Installed" , FotaEvent::InstallCompleted },
{ FotaEvent::InstallStart , "Installing" , "Failed" , FotaEvent::InstallFailed },
// from "Installed"
{ FotaEvent::ConnectivityLost , "Installed" , "Uninitialized" },
{ FotaEvent::Waiting , "Installed" , "Idle" },
{ FotaEvent::ConnectivityLost , "Installed" , "Uninitialized" },
{ FotaEvent::Waiting , "Installed" , "Idle" },
// from "Idle"
{ FotaEvent::ConnectivityLost , "Idle" , "Uninitialized" },
{ FotaEvent::Start , "Idle" , "Failed" , FotaEvent::BundleVersionUnchanged },
{ FotaEvent::Start , "Idle" , "Downloading" , FotaEvent::BundleVersionOK },
{ FotaEvent::ConnectivityLost , "Idle" , "Uninitialized" },
{ FotaEvent::Start , "Idle" , "Failed" , FotaEvent::BundleVersionUnchanged },
{ FotaEvent::Start , "Idle" , "Downloading" , FotaEvent::BundleVersionOK },
// from "SendCurrentState"
{ FotaEvent::ConnectivityLost , "SendCurrentState", "Uninitialized" },
{ FotaEvent::Waiting , "SendCurrentState", "Idle" },
});
_context.stateMachine->transitTo("Uninitialized");
// clang-format on
Expand Down
17 changes: 16 additions & 1 deletion utest/TestMqttMessagingProtocolJSON.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,29 @@ namespace {
EXPECT_EQ(s.bundleVersion, "1.1");
}

TEST_F(TestMessagingProtocolJSON, readCurrentStateRequest)
{
// clang-format off
const std::string input = R"(
{
"activityId": "random-uuid-as-string",
"timestamp": 123456789
}
)";
// clang-format on

const sua::DesiredState s = ProtocolJSON().readCurrentStateRequest(input);

EXPECT_EQ(s.activityId, "random-uuid-as-string");
}

TEST_F(TestMessagingProtocolJSON, createMessage_systemVersion)
{
const std::string result = ProtocolJSON().createMessage(ctx, "systemVersion");

// clang-format off
const std::string expected = R"(
{
"activityId": "id",
"timestamp": 42,
"payload": {
"domains": [
Expand Down

0 comments on commit 6995d4a

Please sign in to comment.