SDK: Add sync ChangeState and add msg to its result

This commit is contained in:
Dennis Klein 2019-07-24 10:41:08 +02:00
parent a93840b240
commit d70a203449
No known key found for this signature in database
GPG Key ID: 08E62D23FA0ECBBC
5 changed files with 96 additions and 41 deletions

View File

@ -22,43 +22,50 @@
namespace fair { namespace fair {
namespace mq { namespace mq {
auto operator<<(std::ostream& os, AsyncOpResult v) -> std::ostream& auto operator<<(std::ostream& os, AsyncOpResultCode v) -> std::ostream&
{ {
switch (v) { switch (v) {
case AsyncOpResult::Aborted: case AsyncOpResultCode::Aborted:
return os << "Aborted"; return os << "Aborted";
case AsyncOpResult::Timeout: case AsyncOpResultCode::Timeout:
return os << "Timeout"; return os << "Timeout";
case AsyncOpResult::Error: case AsyncOpResultCode::Error:
return os << "Error"; return os << "Error";
case AsyncOpResult::Ok: case AsyncOpResultCode::Ok:
default: default:
return os << "Ok"; return os << "Ok";
} }
} }
auto operator<<(std::ostream& os, AsyncOpResult v) -> std::ostream&
{
(void)(os << "[" << v.code << "]");
if (v.msg.empty()) {
(void)(os << " " << v.msg);
}
return os;
}
namespace sdk { namespace sdk {
const std::unordered_map<DeviceTransition, DeviceState, tools::HashEnum<DeviceTransition>> Topology::fkExpectedState = const std::unordered_map<DeviceTransition, DeviceState, tools::HashEnum<DeviceTransition>>
{ expectedState = {{Transition::InitDevice, DeviceState::InitializingDevice},
{ Transition::InitDevice, DeviceState::InitializingDevice }, {Transition::CompleteInit, DeviceState::Initialized},
{ Transition::CompleteInit, DeviceState::Initialized }, {Transition::Bind, DeviceState::Bound},
{ Transition::Bind, DeviceState::Bound }, {Transition::Connect, DeviceState::DeviceReady},
{ Transition::Connect, DeviceState::DeviceReady }, {Transition::InitTask, DeviceState::InitializingTask},
{ Transition::InitTask, DeviceState::InitializingTask }, {Transition::Run, DeviceState::Running},
{ Transition::Run, DeviceState::Running }, {Transition::Stop, DeviceState::Ready},
{ Transition::Stop, DeviceState::Ready }, {Transition::ResetTask, DeviceState::DeviceReady},
{ Transition::ResetTask, DeviceState::DeviceReady }, {Transition::ResetDevice, DeviceState::Idle},
{ Transition::ResetDevice, DeviceState::Idle }, {Transition::End, DeviceState::Exiting}};
{ Transition::End, DeviceState::Exiting }
};
Topology::Topology(DDSTopology topo, DDSSession session) Topology::Topology(DDSTopology topo, DDSSession session)
: fDDSSession(std::move(session)) : fDDSSession(std::move(session))
, fDDSTopo(std::move(topo)) , fDDSTopo(std::move(topo))
, fTopologyState()
, fStateChangeOngoing(false) , fStateChangeOngoing(false)
, fExecutionThread() , fTargetState(DeviceState::Idle)
, fStateChangeTimeout(0)
, fShutdown(false) , fShutdown(false)
{ {
std::vector<uint64_t> deviceList = fDDSTopo.GetDeviceList(); std::vector<uint64_t> deviceList = fDDSTopo.GetDeviceList();
@ -73,7 +80,6 @@ Topology::Topology(DDSTopology topo, DDSSession session)
for (unsigned int i = 0; i < parts.size(); ++i) { for (unsigned int i = 0; i < parts.size(); ++i) {
boost::trim(parts.at(i)); boost::trim(parts.at(i));
LOG(info) << "parts[" << i << "]: " << parts.at(i);
} }
if (parts[0] == "state-change") { if (parts[0] == "state-change") {
@ -95,7 +101,7 @@ Topology::Topology(DDSTopology topo, DDSSession session)
fExecutionThread = std::thread(&Topology::WaitForState, this); fExecutionThread = std::thread(&Topology::WaitForState, this);
} }
auto Topology::ChangeState(fair::mq::Transition transition, ChangeStateCallback cb, std::chrono::milliseconds timeout) -> void auto Topology::ChangeState(TopologyTransition transition, ChangeStateCallback cb, Duration timeout) -> void
{ {
{ {
std::lock_guard<std::mutex> guard(fMtx); std::lock_guard<std::mutex> guard(fMtx);
@ -103,17 +109,32 @@ auto Topology::ChangeState(fair::mq::Transition transition, ChangeStateCallback
LOG(error) << "State change already in progress, concurrent requested not yet supported"; LOG(error) << "State change already in progress, concurrent requested not yet supported";
return; // TODO call the callback with error msg return; // TODO call the callback with error msg
} }
LOG(info) << "Initiating ChangeState with " << transition << " to " << fkExpectedState.at(transition); LOG(info) << "Initiating ChangeState with " << transition << " to " << expectedState.at(transition);
fStateChangeOngoing = true; fStateChangeOngoing = true;
fChangeStateCallback = cb; fChangeStateCallback = cb;
fStateChangeTimeout = timeout; fStateChangeTimeout = timeout;
fTargetState = fkExpectedState.at(transition); fTargetState = expectedState.at(transition);
fDDSSession.SendCommand(GetTransitionName(transition)); fDDSSession.SendCommand(GetTransitionName(transition));
} }
fExecutionCV.notify_one(); fExecutionCV.notify_one();
} }
auto Topology::ChangeState(TopologyTransition t, Duration timeout) -> ChangeStateResult
{
fair::mq::tools::Semaphore blocker;
ChangeStateResult res;
ChangeState(
t,
[&blocker, &res](Topology::ChangeStateResult _res) {
res = _res;
blocker.Signal();
},
timeout);
blocker.Wait();
return res;
}
void Topology::WaitForState() void Topology::WaitForState()
{ {
while (!fShutdown) { while (!fShutdown) {
@ -150,10 +171,10 @@ void Topology::WaitForState()
} }
} catch(std::exception& e) { } catch(std::exception& e) {
LOG(error) << "Error while processing state request: " << e.what(); LOG(error) << "Error while processing state request: " << e.what();
fChangeStateCallback(ChangeStateResult{AsyncOpResult::Error, fTopologyState}); fChangeStateCallback({{AsyncOpResultCode::Error, ""}, fTopologyState});
} }
fChangeStateCallback(ChangeStateResult{AsyncOpResult::Ok, fTopologyState}); fChangeStateCallback({{AsyncOpResultCode::Ok, ""}, fTopologyState});
} else { } else {
std::unique_lock<std::mutex> lock(fExecutionMtx); std::unique_lock<std::mutex> lock(fExecutionMtx);
fExecutionCV.wait(lock); fExecutionCV.wait(lock);

View File

@ -26,13 +26,21 @@
namespace fair { namespace fair {
namespace mq { namespace mq {
// TODO make this a struct with a readable string error msg enum class AsyncOpResultCode {
enum class AsyncOpResult {
Ok, Ok,
Timeout, Timeout,
Error, Error,
Aborted Aborted
}; };
auto operator<<(std::ostream& os, AsyncOpResultCode v) -> std::ostream&;
using AsyncOpResultMessage = std::string;
struct AsyncOpResult {
AsyncOpResultCode code;
AsyncOpResultMessage msg;
operator AsyncOpResultCode() const { return code; }
};
auto operator<<(std::ostream& os, AsyncOpResult v) -> std::ostream&; auto operator<<(std::ostream& os, AsyncOpResult v) -> std::ostream&;
namespace sdk { namespace sdk {
@ -59,6 +67,12 @@ class Topology
/// @brief (Re)Construct a FairMQ topology from an existing DDS topology /// @brief (Re)Construct a FairMQ topology from an existing DDS topology
/// @param topo Initialized DDS CTopology /// @param topo Initialized DDS CTopology
explicit Topology(DDSTopology topo, DDSSession session = DDSSession()); explicit Topology(DDSTopology topo, DDSSession session = DDSSession());
explicit Topology(const Topology&) = delete;
Topology& operator=(const Topology&) = delete;
explicit Topology(Topology&&) = delete;
Topology& operator=(Topology&&) = delete;
~Topology(); ~Topology();
struct ChangeStateResult { struct ChangeStateResult {
@ -67,13 +81,20 @@ class Topology
friend auto operator<<(std::ostream& os, ChangeStateResult v) -> std::ostream&; friend auto operator<<(std::ostream& os, ChangeStateResult v) -> std::ostream&;
}; };
using ChangeStateCallback = std::function<void(ChangeStateResult)>; using ChangeStateCallback = std::function<void(ChangeStateResult)>;
using Duration = std::chrono::milliseconds;
/// @brief Initiate state transition on all FairMQ devices in this topology /// @brief Initiate state transition on all FairMQ devices in this topology
/// @param t FairMQ device state machine transition /// @param t FairMQ device state machine transition
/// @param cb Completion callback /// @param cb Completion callback
auto ChangeState(TopologyTransition t, ChangeStateCallback cb, std::chrono::milliseconds timeout = std::chrono::milliseconds(0)) -> void; /// @param timeout Timeout in milliseconds, 0 means no timeout
auto ChangeState(TopologyTransition t, ChangeStateCallback cb, Duration timeout = std::chrono::milliseconds(0)) -> void;
/// @brief Perform a state transition on all FairMQ devices in this topology
/// @param t FairMQ device state machine transition
/// @param timeout Timeout in milliseconds, 0 means no timeout
/// @return The result of the state transition
auto ChangeState(TopologyTransition t, Duration timeout = std::chrono::milliseconds(0)) -> ChangeStateResult;
static const std::unordered_map<DeviceTransition, DeviceState, tools::HashEnum<DeviceTransition>> fkExpectedState;
private: private:
DDSSession fDDSSession; DDSSession fDDSSession;

View File

@ -6,8 +6,7 @@
* copied verbatim in the file "LICENSE" * * copied verbatim in the file "LICENSE" *
********************************************************************************/ ********************************************************************************/
#include <fairmq/sdk/Topology.h> #include <fairmq/States.h>
#include <fairmq/StateMachine.h>
#include <iostream> #include <iostream>
int main(int /*argc*/, char ** /*argv*/) int main(int /*argc*/, char ** /*argv*/)

View File

@ -21,27 +21,39 @@ TEST_F(Topology, Construction)
fair::mq::sdk::Topology topo(mDDSTopo, mDDSSession); fair::mq::sdk::Topology topo(mDDSTopo, mDDSSession);
} }
TEST_F(Topology, ChangeState) TEST_F(Topology, ChangeState_async1)
{ {
using fair::mq::sdk::Topology; using fair::mq::sdk::Topology;
using fair::mq::sdk::TopologyTransition; using fair::mq::sdk::TopologyTransition;
Topology topo(mDDSTopo, mDDSSession); Topology topo(mDDSTopo, mDDSSession);
Topology::ChangeStateResult r;
fair::mq::tools::Semaphore blocker; fair::mq::tools::Semaphore blocker;
topo.ChangeState(TopologyTransition::Stop, [&](Topology::ChangeStateResult result) { topo.ChangeState(TopologyTransition::Stop, [&blocker](Topology::ChangeStateResult result) {
LOG(info) << result; LOG(info) << result;
r = result; EXPECT_EQ(result.rc, fair::mq::AsyncOpResultCode::Ok);
// TODO add the helper to check state consistency
for (const auto& e : result.state) {
EXPECT_EQ(e.second.state, fair::mq::sdk::DeviceState::Ready);
}
blocker.Signal(); blocker.Signal();
}); });
blocker.Wait(); blocker.Wait();
EXPECT_EQ(r.rc, fair::mq::AsyncOpResult::Ok); }
TEST_F(Topology, ChangeState_sync)
{
using fair::mq::sdk::Topology;
using fair::mq::sdk::TopologyTransition;
Topology topo(mDDSTopo, mDDSSession);
auto result(topo.ChangeState(TopologyTransition::Stop));
EXPECT_EQ(result.rc, fair::mq::AsyncOpResultCode::Ok);
// TODO add the helper to check state consistency // TODO add the helper to check state consistency
for (const auto& e : r.state) { for (const auto& e : result.state) {
EXPECT_EQ(e.second.state, fair::mq::sdk::DeviceState::Ready); EXPECT_EQ(e.second.state, fair::mq::sdk::DeviceState::Ready);
} }
} }
// TEST_F(Topology, Timeout) // TEST_F(Topology, Timeout)
// { // {
// using fair::mq::sdk::Topology; // using fair::mq::sdk::Topology;

View File

@ -16,7 +16,7 @@
</decltask> </decltask>
<decltask name="Sink"> <decltask name="Sink">
<exe reachable="true">fairmq-sink --id sink --color false --channel-config name=data,type=pull,method=connect -P dds</exe> <exe reachable="true">fairmq-sink --id sink_%taskIndex% --color false --channel-config name=data,type=pull,method=connect -P dds</exe>
<requirements> <requirements>
<name>SinkWorker</name> <name>SinkWorker</name>
</requirements> </requirements>
@ -27,7 +27,9 @@
<main name="main"> <main name="main">
<task>Sampler</task> <task>Sampler</task>
<task>Sink</task> <group name="SinkGroup" n="50">
<task>Sink</task>
</group>
</main> </main>
</topology> </topology>