Skip to content

Commit 40ac89f

Browse files
authored
Information Service (#6)
InformationService to broadcast list of tasks and their objects Add an information service that collects the list of objects published by all the tasks and make it available to clients. There are 2 binaries : qcInfoService and qcInfoServiceDump. The latter is to check what is being produced by the Information Service. The information service can use a file as input if there are no tasks running but one wants to simulate a normal use. QC-57
1 parent 1d1c54d commit 40ac89f

17 files changed

Lines changed: 776 additions & 20 deletions

Framework/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ set(SRCS
7474
src/SpyDevice.cxx
7575
src/SpyMainFrame.cxx
7676
src/CcdbDatabase.cxx
77+
src/InformationService.cxx
78+
src/InformationServiceDump.cxx
7779
)
7880

7981
set(HEADERS # needed for the dictionary generation
@@ -134,6 +136,20 @@ O2_GENERATE_EXECUTABLE(
134136
BUCKET_NAME ${BUCKET_NAME}
135137
)
136138

139+
O2_GENERATE_EXECUTABLE(
140+
EXE_NAME qcInfoService
141+
SOURCES src/runInformationService.cxx
142+
MODULE_LIBRARY_NAME ${LIBRARY_NAME}
143+
BUCKET_NAME ${BUCKET_NAME}
144+
)
145+
146+
O2_GENERATE_EXECUTABLE(
147+
EXE_NAME qcInfoServiceDump
148+
SOURCES src/runInformationServiceDump.cxx
149+
MODULE_LIBRARY_NAME ${LIBRARY_NAME}
150+
BUCKET_NAME ${BUCKET_NAME}
151+
)
152+
137153
if (FAIRROOT_FOUND)
138154
O2_GENERATE_EXECUTABLE(
139155
EXE_NAME alfaTestReceiver

Framework/alfa.json

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@
1616
"rateLogging": 0
1717
}
1818
]
19+
},
20+
{
21+
"name": "information-service-out",
22+
"sockets": [
23+
{
24+
"type": "pub",
25+
"method": "connect",
26+
"address": "tcp://localhost:5560",
27+
"sndBufSize": 10,
28+
"rcvBufSize": 10,
29+
"rateLogging": 0
30+
}
31+
]
1932
}
2033
]
2134
},
@@ -28,9 +41,22 @@
2841
{
2942
"type": "pub",
3043
"method": "bind",
31-
"address": "tcp://*:5556",
32-
"sndBufSize": 100,
33-
"rcvBufSize": 100,
44+
"address": "tcp://*:5557",
45+
"sndBufSize": 1,
46+
"rcvBufSize": 1,
47+
"rateLogging": 0
48+
}
49+
]
50+
},
51+
{
52+
"name": "information-service-out",
53+
"sockets": [
54+
{
55+
"type": "pub",
56+
"method": "connect",
57+
"address": "tcp://localhost:5560",
58+
"sndBufSize": 1,
59+
"rcvBufSize": 1,
3460
"rateLogging": 0
3561
}
3662
]

Framework/include/QualityControl/ObjectsManager.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,10 @@ class ObjectsManager
2929

3030
public:
3131
ObjectsManager(TaskConfig &taskConfig);
32-
33-
/// Destructor
3432
virtual ~ObjectsManager();
35-
3633
void startPublishing(TObject *obj, std::string objectName = "");
37-
34+
// todo stoppublishing
3835
void setQuality(std::string objectName, Quality quality);
39-
// todo stop publishing
40-
4136
Quality getQuality(std::string objectName);
4237

4338
/// \brief Add a check to the object defined by objectName.
@@ -71,6 +66,9 @@ class ObjectsManager
7166
iterator end()
7267
{ return mMonitorObjects.end(); }
7368

69+
std::string getObjectsListString()
70+
{ return mObjectsList.GetString().Data(); }
71+
7472
private:
7573
void UpdateIndex(const std::string &nonEmptyName) ;
7674

Framework/include/QualityControl/TaskDevice.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class TaskDevice : public FairMQDevice
5656
void monitorCycle();
5757
unsigned long publish();
5858
static void CustomCleanupTMessage(void *data, void *object);
59+
void sendToInformationService(std::string objectsListString);
5960

6061
private:
6162
std::string mTaskName;
@@ -65,6 +66,8 @@ class TaskDevice : public FairMQDevice
6566
std::unique_ptr<AliceO2::DataSampling::SamplerInterface> mSampler;
6667
o2::quality_control::core::TaskInterface *mTask;
6768
std::shared_ptr<ObjectsManager> mObjectsManager;
69+
// InformationServiceSender infoServiceSender;
70+
std::string lastListSent;
6871

6972
// stats
7073
int mTotalNumberObjectsPublished;
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
//
11+
12+
///
13+
/// \author bvonhall
14+
/// \file InformationService.cxx
15+
///
16+
17+
#include "InformationService.h"
18+
#include "QualityControl/QcInfoLogger.h"
19+
#include <options/FairMQProgOptions.h>
20+
21+
using namespace std;
22+
typedef boost::tokenizer<boost::char_separator<char> > t_tokenizer;
23+
using namespace o2::quality_control::core;
24+
25+
int timeOutIntervals = 5; // in seconds
26+
27+
InformationService::InformationService() : th(nullptr), mFakeDataIndex(0)
28+
{
29+
OnData("tasks_input", &InformationService::handleTaskInputData);
30+
OnData("request_data", &InformationService::handleRequestData);
31+
}
32+
33+
void InformationService::Init()
34+
{
35+
string fakeDataFile = fConfig->GetValue<string>("fake-data-file");
36+
37+
// todo put this in a method
38+
if (fakeDataFile != "") {
39+
readFakeDataFile(fakeDataFile);
40+
}
41+
}
42+
43+
InformationService::~InformationService()
44+
{
45+
}
46+
47+
void InformationService::checkTimedOut()
48+
{
49+
string line = mFakeData[mFakeDataIndex % mFakeData.size()];
50+
handleTaskInputData(line);
51+
mFakeDataIndex++;
52+
53+
// restart timer
54+
mTimer->expires_at(mTimer->expires_at() + boost::posix_time::seconds(timeOutIntervals));
55+
mTimer->async_wait(boost::bind(&InformationService::checkTimedOut, this));
56+
}
57+
58+
bool InformationService::handleRequestData(FairMQMessagePtr &request, int /*index*/)
59+
{
60+
string requestParam = string(static_cast<char *>(request->GetData()), request->GetSize());
61+
LOG(INFO) << "Received request from client: \"" << requestParam << "\"";
62+
63+
string *result = nullptr;
64+
if (requestParam == "all") {
65+
result = new string(produceJsonAll());
66+
} else {
67+
if (mCacheTasksData.count(requestParam) > 0) {
68+
result = new string(produceJson(requestParam));
69+
} else {
70+
result = new string("{\"error\": \"no such task\"}");
71+
}
72+
}
73+
74+
LOG(INFO) << "Sending reply to client.";
75+
FairMQMessagePtr reply(NewMessage(const_cast<char *>(result->c_str()), // data
76+
result->length(), // size
77+
[](void * /*data*/,
78+
void *object) { delete static_cast<string *>(object); }, // deletion callback
79+
result)); // object that manages the data
80+
if (Send(reply, "request_data") <= 0) {
81+
LOG(ERROR) << "error sending reply";
82+
}
83+
return true; // keep running
84+
}
85+
86+
bool InformationService::handleTaskInputData(FairMQMessagePtr &msg, int /*index*/)
87+
{
88+
string *receivedData = new std::string(static_cast<char *>(msg->GetData()), msg->GetSize());
89+
LOG(INFO) << "Received data, processing...";
90+
LOG(INFO) << " " << *receivedData;
91+
92+
handleTaskInputData(*receivedData);
93+
94+
return true; // keep running
95+
}
96+
97+
bool InformationService::handleTaskInputData(std::string receivedData)
98+
{
99+
std::string taskName = getTaskName(&receivedData);
100+
LOG(DEBUG) << "task : " << taskName;
101+
102+
// check if new data
103+
boost::hash<std::string> string_hash;
104+
size_t hash = string_hash(receivedData);
105+
if (mCacheTasksObjectsHash.count(taskName) > 0) {
106+
if (mCacheTasksObjectsHash.count(taskName) > 0 && hash == mCacheTasksObjectsHash[taskName]) {
107+
LOG(INFO) << "Data already known, we skip it";
108+
return true;
109+
}
110+
}
111+
mCacheTasksObjectsHash[taskName] = hash;
112+
113+
// parse
114+
vector<string> objects = getObjects(&receivedData);
115+
116+
// store
117+
mCacheTasksData[taskName] = objects;
118+
119+
// json
120+
string *json = new std::string(produceJson(taskName));
121+
122+
// publish
123+
sendJson(json);
124+
}
125+
126+
void InformationService::readFakeDataFile(std::string fakeDataFile)
127+
{
128+
std::string line;
129+
std::ifstream myfile(fakeDataFile);
130+
if (!myfile) //Always test the file open.
131+
{
132+
LOG(ERROR) << "Error opening fake data file";
133+
return;
134+
}
135+
mFakeData.clear();
136+
while (std::getline(myfile, line)) {
137+
mFakeData.push_back(line);
138+
}
139+
140+
// start a timer to use the fake data
141+
mTimer = new boost::asio::deadline_timer(io, boost::posix_time::seconds(timeOutIntervals));
142+
mTimer->async_wait(boost::bind(&InformationService::checkTimedOut, this));
143+
th = new thread([&] { io.run(); });
144+
}
145+
146+
vector<string> InformationService::getObjects(string *receivedData)
147+
{
148+
vector<string> objects;
149+
std::string objectsString = receivedData->substr(receivedData->find(":") + 1, receivedData->length());
150+
LOG(DEBUG) << "objects : " << objectsString;
151+
boost::char_separator<char> sep(",");
152+
t_tokenizer tok(objectsString, sep);
153+
for (t_tokenizer::iterator beg = tok.begin(); beg != tok.end(); ++beg) {
154+
objects.push_back(*beg);
155+
}
156+
return objects;
157+
}
158+
159+
std::string InformationService::getTaskName(std::string *receivedData)
160+
{
161+
return receivedData->substr(0, receivedData->find(":"));
162+
}
163+
164+
pt::ptree InformationService::buildTaskNode(std::string taskName)
165+
{
166+
pt::ptree task_node;
167+
task_node.put("name", taskName);
168+
pt::ptree objects_node;
169+
for (auto &object : mCacheTasksData[taskName]) {
170+
pt::ptree object_node;
171+
object_node.put("id", object);
172+
objects_node.push_back(std::make_pair("", object_node));
173+
}
174+
task_node.add_child("objects", objects_node);
175+
return task_node;
176+
}
177+
178+
std::string InformationService::produceJson(std::string taskName)
179+
{
180+
pt::ptree taskNode = buildTaskNode(taskName);
181+
182+
std::stringstream ss;
183+
pt::json_parser::write_json(ss, taskNode);
184+
LOG(DEBUG) << "json : " << endl << ss.str();
185+
// QcInfoLogger::GetInstance() << infologger::Debug << "json : \n" << *json << infologger::endm;
186+
return ss.str();
187+
}
188+
189+
std::string InformationService::produceJsonAll()
190+
{
191+
string result;
192+
pt::ptree main_node;
193+
194+
pt::ptree tasksListNode;
195+
for (const auto &taskTuple : mCacheTasksData) {
196+
pt::ptree taskNode = buildTaskNode(taskTuple.first);
197+
tasksListNode.push_back(std::make_pair("", taskNode));
198+
}
199+
main_node.add_child("tasks", tasksListNode);
200+
201+
std::stringstream ss;
202+
pt::json_parser::write_json(ss, main_node);
203+
LOG(DEBUG) << "json : " << endl << ss.str();
204+
return ss.str();
205+
}
206+
207+
void InformationService::sendJson(std::string *json)
208+
{
209+
FairMQMessagePtr msg2(NewMessage(const_cast<char *>(json->c_str()),
210+
json->length(),
211+
[](void * /*data*/, void *object) { delete static_cast<string *>(object); },
212+
json));
213+
int ret = Send(msg2, "updates_output");
214+
if (ret < 0) {
215+
LOG(ERROR) << "Error sending update";
216+
}
217+
}

0 commit comments

Comments
 (0)