mirror of
https://github.com/FairRootGroup/FairMQ.git
synced 2025-10-15 17:41:45 +00:00
Port DDS plugin to the new plugin system.
This commit is contained in:
committed by
Mohammad Al-Turany
parent
2db114bc5c
commit
01327426c3
@@ -8,8 +8,6 @@
|
||||
|
||||
#include "Control.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <termios.h> // for the interactive mode
|
||||
#include <poll.h> // for the interactive mode
|
||||
|
||||
@@ -79,7 +77,6 @@ auto Control::InteractiveMode() -> void
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::InitDevice);
|
||||
while (WaitForNextState() != DeviceState::DeviceReady) {}
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::InitTask);
|
||||
while (WaitForNextState() != DeviceState::Ready) {}
|
||||
ChangeDeviceState(DeviceStateTransition::Run);
|
||||
|
18
fairmq/plugins/DDS/CMakeLists.txt
Normal file
18
fairmq/plugins/DDS/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2012-2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# #
|
||||
# This software is distributed under the terms of the #
|
||||
# GNU Lesser General Public Licence (LGPL) version 3, #
|
||||
# copied verbatim in the file "LICENSE" #
|
||||
################################################################################
|
||||
|
||||
set(plugin FairMQPlugin_dds)
|
||||
|
||||
add_library(${plugin} SHARED ${CMAKE_CURRENT_SOURCE_DIR}/DDS.cxx ${CMAKE_CURRENT_SOURCE_DIR}/DDS.h)
|
||||
target_link_libraries(${plugin} FairMQ ${DDS_INTERCOM_LIBRARY_SHARED} ${DDS_PROTOCOL_LIBRARY_SHARED} ${DDS_USER_DEFAULTS_LIBRARY_SHARED})
|
||||
target_include_directories(${plugin} PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${DDS_INCLUDE_DIR})
|
||||
set_target_properties(${plugin} PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
|
||||
add_executable(fairmq-dds-command-ui ${CMAKE_CURRENT_SOURCE_DIR}/runDDSCommandUI.cxx)
|
||||
target_link_libraries(fairmq-dds-command-ui FairMQ ${DDS_INTERCOM_LIBRARY_SHARED} ${DDS_PROTOCOL_LIBRARY_SHARED} ${DDS_USER_DEFAULTS_LIBRARY_SHARED})
|
||||
target_include_directories(fairmq-dds-command-ui PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${DDS_INCLUDE_DIR})
|
260
fairmq/plugins/DDS/DDS.cxx
Normal file
260
fairmq/plugins/DDS/DDS.cxx
Normal file
@@ -0,0 +1,260 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include "DDS.h"
|
||||
|
||||
#include <termios.h> // for the interactive mode
|
||||
#include <poll.h> // for the interactive mode
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fair
|
||||
{
|
||||
namespace mq
|
||||
{
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
DDS::DDS(const string name, const Plugin::Version version, const string maintainer, const string homepage, PluginServices* pluginServices)
|
||||
: Plugin(name, version, maintainer, homepage, pluginServices)
|
||||
, fService()
|
||||
, fDDSCustomCmd(fService)
|
||||
, fDDSKeyValue(fService)
|
||||
, fBindingChans()
|
||||
, fConnectingChans()
|
||||
, fStopMutex()
|
||||
, fStopCondition()
|
||||
, fCommands({ "INIT DEVICE", "INIT TASK", "PAUSE", "RUN", "STOP", "RESET TASK", "RESET DEVICE" })
|
||||
, fControllerThread()
|
||||
, fEvents()
|
||||
, fEventsMutex()
|
||||
, fNewEvent()
|
||||
{
|
||||
try
|
||||
{
|
||||
TakeDeviceControl();
|
||||
fControllerThread = thread(&DDS::HandleControl, this);
|
||||
}
|
||||
catch (PluginServices::DeviceControlError& e)
|
||||
{
|
||||
LOG(DEBUG) << e.what();
|
||||
}
|
||||
catch (exception& e)
|
||||
{
|
||||
LOG(ERROR) << "Error in plugin initialization: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
auto DDS::HandleControl() -> void
|
||||
{
|
||||
try
|
||||
{
|
||||
// subscribe for DDS service errors.
|
||||
fService.subscribeOnError([](const dds::intercom_api::EErrorCode errorCode, const string& errorMsg) {
|
||||
LOG(ERROR) << "DDS Error received: error code: " << errorCode << ", error message: " << errorMsg << endl;
|
||||
});
|
||||
|
||||
// subscribe to device state changes, pushing new state chenges into the event queue
|
||||
SubscribeToDeviceStateChange([&](DeviceState newState)
|
||||
{
|
||||
{
|
||||
lock_guard<mutex> lock{fEventsMutex};
|
||||
fEvents.push(newState);
|
||||
}
|
||||
fNewEvent.notify_one();
|
||||
});
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::InitDevice);
|
||||
while (WaitForNextState() != DeviceState::InitializingDevice) {}
|
||||
|
||||
// in the Initializing state subscribe to receive addresses of connecting channels from DDS
|
||||
// and propagate addresses of bound channels to DDS.
|
||||
FillChannelContainers();
|
||||
|
||||
if (fConnectingChans.size() > 0)
|
||||
{
|
||||
LOG(DEBUG) << "Subscribing for DDS properties.";
|
||||
|
||||
SubscribeForConnectingChannels();
|
||||
}
|
||||
|
||||
// subscribe for state changes from DDS (subscriptions start firing after fService.start() is called)
|
||||
SubscribeForStateChanges();
|
||||
|
||||
// start DDS service - subscriptions will only start firing after this step
|
||||
fService.start();
|
||||
|
||||
// publish bound addresses via DDS at keys corresponding to the channel prefixes, e.g. 'data' in data[i]
|
||||
PublishBoundChannels();
|
||||
|
||||
while (WaitForNextState() != DeviceState::DeviceReady) {}
|
||||
|
||||
ChangeDeviceState(DeviceStateTransition::InitTask);
|
||||
while (WaitForNextState() != DeviceState::Ready) {}
|
||||
ChangeDeviceState(DeviceStateTransition::Run);
|
||||
|
||||
// wait until stop signal
|
||||
unique_lock<mutex> lock(fStopMutex);
|
||||
while (!DeviceTerminated())
|
||||
{
|
||||
fStopCondition.wait_for(lock, chrono::seconds(1));
|
||||
}
|
||||
LOG(DEBUG) << "Stopping DDS control plugin";
|
||||
}
|
||||
catch (exception& e)
|
||||
{
|
||||
LOG(ERROR) << "Error: " << e.what() << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
fDDSKeyValue.unsubscribe();
|
||||
fDDSCustomCmd.unsubscribe();
|
||||
|
||||
UnsubscribeFromDeviceStateChange();
|
||||
ReleaseDeviceControl();
|
||||
}
|
||||
|
||||
auto DDS::FillChannelContainers() -> void
|
||||
{
|
||||
unordered_map<string, int> channelInfo(GetChannelInfo());
|
||||
for (const auto& c : channelInfo)
|
||||
{
|
||||
string methodKey{"chans." + c.first + "." + to_string(c.second - 1) + ".method"};
|
||||
string addressKey{"chans." + c.first + "." + to_string(c.second - 1) + ".address"};
|
||||
if (GetProperty<string>(methodKey) == "bind")
|
||||
{
|
||||
fBindingChans.insert(make_pair(c.first, vector<string>()));
|
||||
for (unsigned int i = 0; i < c.second; ++i)
|
||||
{
|
||||
fBindingChans.at(c.first).push_back(GetProperty<string>(addressKey));
|
||||
}
|
||||
}
|
||||
else if (GetProperty<string>(methodKey) == "connect")
|
||||
{
|
||||
fConnectingChans.insert(make_pair(c.first, DDSConfig()));
|
||||
LOG(DEBUG) << "preparing to connect: " << c.first << " with " << c.second << " sub-channels.";
|
||||
for (unsigned int i = 0; i < c.second; ++i)
|
||||
{
|
||||
fConnectingChans.at(c.first).fSubChannelAddresses.push_back(string());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(ERROR) << "Cannot update address configuration. Channel method (bind/connect) not specified.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto DDS::SubscribeForConnectingChannels() -> void
|
||||
{
|
||||
fDDSKeyValue.subscribe([&] (const string& propertyId, const string& key, const string& value)
|
||||
{
|
||||
LOG(DEBUG) << "Received update for " << propertyId << ": key=" << key << " value=" << value;
|
||||
fConnectingChans.at(propertyId).fDDSValues.insert(make_pair<string, string>(key.c_str(), value.c_str()));
|
||||
|
||||
// update channels and remove them from unfinished container
|
||||
for (auto mi = fConnectingChans.begin(); mi != fConnectingChans.end(); /* no increment */)
|
||||
{
|
||||
if (mi->second.fSubChannelAddresses.size() == mi->second.fDDSValues.size())
|
||||
{
|
||||
// when multiple subChannels are used, their order on every device should be the same, irregardless of arrival order from DDS.
|
||||
sort(mi->second.fSubChannelAddresses.begin(), mi->second.fSubChannelAddresses.end());
|
||||
auto it = mi->second.fDDSValues.begin();
|
||||
for (unsigned int i = 0; i < mi->second.fSubChannelAddresses.size(); ++i)
|
||||
{
|
||||
string k = "chans." + mi->first + "." + to_string(i) + ".address";
|
||||
SetProperty<string>(k, it->second);
|
||||
++it;
|
||||
}
|
||||
fConnectingChans.erase(mi++);
|
||||
}
|
||||
else
|
||||
{
|
||||
++mi;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto DDS::PublishBoundChannels() -> void
|
||||
{
|
||||
for (const auto& chan : fBindingChans)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
for (const auto& i : chan.second)
|
||||
{
|
||||
LOG(DEBUG) << "Publishing " << chan.first << "[" << index << "] address to DDS under '" << chan.first << "' property name.";
|
||||
fDDSKeyValue.putValue(chan.first, i);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto DDS::SubscribeForStateChanges() -> void
|
||||
{
|
||||
string id = GetProperty<string>("id");
|
||||
string pid(to_string(getpid()));
|
||||
|
||||
fDDSCustomCmd.subscribe([id, pid, this](const string& cmd, const string& cond, uint64_t senderId)
|
||||
{
|
||||
LOG(INFO) << "Received command: " << cmd;
|
||||
|
||||
if (cmd == "check-state")
|
||||
{
|
||||
fDDSCustomCmd.send(id + ": " + ToStr(GetCurrentDeviceState()) + " (pid: " + pid + ")", to_string(senderId));
|
||||
}
|
||||
else if (fCommands.find(cmd) != fCommands.end())
|
||||
{
|
||||
fDDSCustomCmd.send(id + ": attempting to " + cmd, to_string(senderId));
|
||||
ChangeDeviceState(ToDeviceStateTransition(cmd));
|
||||
}
|
||||
else if (cmd == "END")
|
||||
{
|
||||
fDDSCustomCmd.send(id + ": attempting to " + cmd, to_string(senderId));
|
||||
ChangeDeviceState(ToDeviceStateTransition(cmd));
|
||||
fDDSCustomCmd.send(id + ": " + ToStr(GetCurrentDeviceState()), to_string(senderId));
|
||||
if (ToStr(GetCurrentDeviceState()) == "EXITING")
|
||||
{
|
||||
unique_lock<mutex> lock(fStopMutex);
|
||||
fStopCondition.notify_one();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(WARN) << "Unknown command: " << cmd;
|
||||
LOG(WARN) << "Origin: " << senderId;
|
||||
LOG(WARN) << "Destination: " << cond;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto DDS::WaitForNextState() -> DeviceState
|
||||
{
|
||||
unique_lock<mutex> lock{fEventsMutex};
|
||||
while (fEvents.empty())
|
||||
{
|
||||
fNewEvent.wait(lock);
|
||||
}
|
||||
|
||||
auto result = fEvents.front();
|
||||
fEvents.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
DDS::~DDS()
|
||||
{
|
||||
if (fControllerThread.joinable())
|
||||
{
|
||||
fControllerThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace plugins */
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
92
fairmq/plugins/DDS/DDS.h
Normal file
92
fairmq/plugins/DDS/DDS.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_PLUGINS_DDS
|
||||
#define FAIR_MQ_PLUGINS_DDS
|
||||
|
||||
#include <fairmq/Plugin.h>
|
||||
|
||||
#include <dds_intercom.h>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
namespace fair
|
||||
{
|
||||
namespace mq
|
||||
{
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
struct DDSConfig
|
||||
{
|
||||
DDSConfig()
|
||||
: fSubChannelAddresses()
|
||||
, fDDSValues()
|
||||
{}
|
||||
|
||||
// container of sub channel addresses
|
||||
std::vector<std::string> fSubChannelAddresses;
|
||||
// dds values for the channel
|
||||
std::unordered_map<std::string, std::string> fDDSValues;
|
||||
};
|
||||
|
||||
class DDS : public Plugin
|
||||
{
|
||||
public:
|
||||
DDS(const std::string name, const Plugin::Version version, const std::string maintainer, const std::string homepage, PluginServices* pluginServices);
|
||||
|
||||
~DDS();
|
||||
|
||||
private:
|
||||
auto HandleControl() -> void;
|
||||
auto WaitForNextState() -> DeviceState;
|
||||
|
||||
auto FillChannelContainers() -> void;
|
||||
auto SubscribeForConnectingChannels() -> void;
|
||||
auto PublishBoundChannels() -> void;
|
||||
auto SubscribeForStateChanges() -> void;
|
||||
|
||||
dds::intercom_api::CIntercomService fService;
|
||||
dds::intercom_api::CCustomCmd fDDSCustomCmd;
|
||||
dds::intercom_api::CKeyValue fDDSKeyValue;
|
||||
|
||||
std::unordered_map<std::string, std::vector<std::string>> fBindingChans;
|
||||
std::unordered_map<std::string, DDSConfig> fConnectingChans;
|
||||
|
||||
std::mutex fStopMutex;
|
||||
std::condition_variable fStopCondition;
|
||||
|
||||
const std::set<std::string> fCommands;
|
||||
|
||||
std::thread fControllerThread;
|
||||
std::queue<DeviceState> fEvents;
|
||||
std::mutex fEventsMutex;
|
||||
std::condition_variable fNewEvent;
|
||||
};
|
||||
|
||||
REGISTER_FAIRMQ_PLUGIN(
|
||||
DDS, // Class name
|
||||
dds, // Plugin name (string, lower case chars only)
|
||||
(Plugin::Version{1,0,0}), // Version
|
||||
"FairRootGroup <fairroot@gsi.de>", // Maintainer
|
||||
"https://github.com/FairRootGroup/FairRoot", // Homepage
|
||||
fair::mq::Plugin::NoProgramOptions // custom program options for the plugin
|
||||
)
|
||||
|
||||
} /* namespace plugins */
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#endif /* FAIR_MQ_PLUGINS_DDS */
|
136
fairmq/plugins/DDS/runDDSCommandUI.cxx
Normal file
136
fairmq/plugins/DDS/runDDSCommandUI.cxx
Normal file
@@ -0,0 +1,136 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <dds_intercom.h>
|
||||
|
||||
#include <termios.h> // raw mode console input
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dds::intercom_api;
|
||||
|
||||
void PrintControlsHelp()
|
||||
{
|
||||
cout << "Use keys to control the devices:" << endl;
|
||||
cout << "[c] check states, [h] help, [p] pause, [r] run, [s] stop, [t] reset task, [d] reset device, [q] end, [j] init task, [i] init device" << endl;
|
||||
cout << "To quit press Ctrl+C" << endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
CIntercomService service;
|
||||
CCustomCmd ddsCustomCmd(service);
|
||||
|
||||
service.subscribeOnError([](const EErrorCode errorCode, const string& errorMsg)
|
||||
{
|
||||
cout << "DDS error received: error code: " << errorCode << ", error message: " << errorMsg << endl;
|
||||
});
|
||||
|
||||
// subscribe to receive messages from DDS
|
||||
ddsCustomCmd.subscribe([](const string& msg, const string& /*condition*/, uint64_t /*senderId*/)
|
||||
{
|
||||
cout << "Received: \"" << msg << "\"" << endl;
|
||||
});
|
||||
|
||||
service.start();
|
||||
|
||||
char c;
|
||||
|
||||
// setup reading from cin (enable raw mode)
|
||||
struct termios t;
|
||||
tcgetattr(STDIN_FILENO, &t); // get the current terminal I/O structure
|
||||
t.c_lflag &= ~ICANON; // disable canonical input
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t); // apply the new settings
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
PrintControlsHelp();
|
||||
}
|
||||
else
|
||||
{
|
||||
cin.putback(argv[1][0]);
|
||||
}
|
||||
|
||||
while (cin >> c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'c':
|
||||
cout << " > checking state of the devices" << endl;
|
||||
ddsCustomCmd.send("check-state", "");
|
||||
break;
|
||||
case 'i':
|
||||
cout << " > init devices" << endl;
|
||||
ddsCustomCmd.send("INIT DEVICE", "");
|
||||
break;
|
||||
case 'j':
|
||||
cout << " > init tasks" << endl;
|
||||
ddsCustomCmd.send("INIT TASK", "");
|
||||
break;
|
||||
case 'p':
|
||||
cout << " > pause devices" << endl;
|
||||
ddsCustomCmd.send("PAUSE", "");
|
||||
break;
|
||||
case 'r':
|
||||
cout << " > run tasks" << endl;
|
||||
ddsCustomCmd.send("RUN", "");
|
||||
break;
|
||||
case 's':
|
||||
cout << " > stop devices" << endl;
|
||||
ddsCustomCmd.send("STOP", "");
|
||||
break;
|
||||
case 't':
|
||||
cout << " > reset tasks" << endl;
|
||||
ddsCustomCmd.send("RESET TASK", "");
|
||||
break;
|
||||
case 'd':
|
||||
cout << " > reset devices" << endl;
|
||||
ddsCustomCmd.send("RESET DEVICE", "");
|
||||
break;
|
||||
case 'h':
|
||||
cout << " > help" << endl;
|
||||
PrintControlsHelp();
|
||||
break;
|
||||
case 'q':
|
||||
cout << " > end" << endl;
|
||||
ddsCustomCmd.send("END", "");
|
||||
break;
|
||||
default:
|
||||
cout << "Invalid input: [" << c << "]" << endl;
|
||||
PrintControlsHelp();
|
||||
break;
|
||||
}
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
usleep(50000);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// disable raw mode
|
||||
tcgetattr(STDIN_FILENO, &t); // get the current terminal I/O structure
|
||||
t.c_lflag |= ICANON; // re-enable canonical input
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t); // apply the new settings
|
||||
}
|
||||
catch (exception& e)
|
||||
{
|
||||
cerr << "Error: " << e.what() << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Reference in New Issue
Block a user