@@ -38,9 +38,6 @@ namespace o2::utilities
3838Dispatcher::Dispatcher (std::string name, const std::string reconfigurationSource)
3939 : mName (name), mReconfigurationSource (reconfigurationSource)
4040{
41- header::DataDescription timerDescription;
42- timerDescription.runtimeInit ((" TIMER-" + name).substr (0 , 16 ).c_str ());
43- inputs.emplace_back (InputSpec{" timer-stats" , " DS" , timerDescription, 0 , Lifetime::Timer});
4441}
4542
4643Dispatcher::~Dispatcher () = default ;
@@ -55,15 +52,16 @@ void Dispatcher::init(InitContext& ctx)
5552 std::unique_ptr<ConfigurationInterface> cfg = ConfigurationFactory::getConfiguration (mReconfigurationSource );
5653 policiesTree = cfg->getRecursive (" dataSamplingPolicies" );
5754 mPolicies .clear ();
58- } else {
55+ } else if (ctx. options (). isSet ( " sampling-config-ptree " )) {
5956 policiesTree = ctx.options ().get <boost::property_tree::ptree>(" sampling-config-ptree" );
6057 mPolicies .clear ();
61- }
58+ } else
59+ ; // we use policies declared during workflow init.
6260
6361 for (auto && policyConfig : policiesTree) {
6462 // we don't want the Dispatcher to exit due to one faulty Policy
6563 try {
66- mPolicies .emplace_back (std::make_shared<DataSamplingPolicy>(policyConfig.second ));
64+ mPolicies .emplace_back (std::make_shared<DataSamplingPolicy>(DataSamplingPolicy::fromConfiguration ( policyConfig.second ) ));
6765 } catch (std::exception& ex) {
6866 LOG (WARN) << " Could not load the Data Sampling Policy '"
6967 << policyConfig.second .get_optional <std::string>(" id" ).value_or (" " ) << " ', because: " << ex.what ();
@@ -188,22 +186,9 @@ void Dispatcher::sendFairMQ(FairMQDevice* device, const DataRef& inputData, cons
188186 int64_t bytesSent = device->Send (message, fairMQChannel);
189187}
190188
191- void Dispatcher::registerPath ( const std::pair<InputSpec, OutputSpec>& path )
189+ void Dispatcher::registerPolicy ( std::unique_ptr<DataSamplingPolicy>&& policy )
192190{
193- // todo: take care of inputs inclusive in others, when subSpec matchers are supported
194- auto cmp = [a = path.first ](const InputSpec b) {
195- return a.matcher == b.matcher && a.lifetime == b.lifetime ;
196- };
197-
198- if (std::find_if (inputs.begin (), inputs.end (), cmp) == inputs.end ()) {
199- inputs.push_back (path.first );
200- LOG (DEBUG) << " Registering input " << DataSpecUtils::describe (path.first );
201- } else {
202- LOG (DEBUG) << " Input " << DataSpecUtils::describe (path.first )
203- << " already registered" ;
204- }
205-
206- outputs.push_back (path.second );
191+ mPolicies .emplace_back (std::move (policy));
207192}
208193
209194const std::string& Dispatcher::getName ()
@@ -213,12 +198,66 @@ const std::string& Dispatcher::getName()
213198
214199Inputs Dispatcher::getInputSpecs ()
215200{
216- return inputs;
201+ Inputs declaredInputs;
202+
203+ // Add data inputs. Avoid duplicates and inputs which include others (e.g. "TST/DATA" includes "TST/DATA/1".
204+ for (const auto & policy : mPolicies ) {
205+ for (const auto & [potentiallyNewInput, _policyOutput] : policy->getPathMap ()) {
206+ (void )_policyOutput;
207+
208+ // The idea is that we remove all existing inputs which are covered by the potentially new input.
209+ // If there are none which are broader than the new one, then we add it.
210+ // I hope this is enough for all corner cases, but I am not 100% sure.
211+ auto newInputIsBroader = [&potentiallyNewInput](const InputSpec& other) {
212+ return DataSpecUtils::includes (potentiallyNewInput, other);
213+ };
214+ declaredInputs.erase (std::remove_if (declaredInputs.begin (), declaredInputs.end (), newInputIsBroader), declaredInputs.end ());
215+
216+ auto declaredInputIsBroader = [&potentiallyNewInput](const InputSpec& other) {
217+ return DataSpecUtils::includes (other, potentiallyNewInput);
218+ };
219+ if (std::none_of (declaredInputs.begin (), declaredInputs.end (), declaredInputIsBroader)) {
220+ declaredInputs.push_back (potentiallyNewInput);
221+ }
222+ }
223+ }
224+
225+ // add timer input
226+ header::DataDescription timerDescription;
227+ timerDescription.runtimeInit ((" TIMER-" + mName ).substr (0 , 16 ).c_str ());
228+ declaredInputs.emplace_back (InputSpec{" timer-stats" , " DS" , timerDescription, 0 , Lifetime::Timer});
229+
230+ return declaredInputs;
217231}
218232
219233Outputs Dispatcher::getOutputSpecs ()
220234{
221- return outputs;
235+ Outputs declaredOutputs;
236+ for (const auto & policy : mPolicies ) {
237+ for (const auto & [_policyInput, policyOutput] : policy->getPathMap ()) {
238+ (void )_policyInput;
239+ // In principle Data Sampling Policies should have different outputs.
240+ // We may add a check to be very gentle with users.
241+ declaredOutputs.push_back (policyOutput);
242+ }
243+ }
244+ return declaredOutputs;
245+ }
246+ framework::Options Dispatcher::getOptions ()
247+ {
248+ o2::framework::Options options;
249+ for (const auto & policy : mPolicies ) {
250+ if (!policy->getFairMQOutputChannel ().empty ()) {
251+ if (!options.empty ()) {
252+ throw std::runtime_error (" Maximum one policy with raw FairMQ channel is allowed, more have been declared." );
253+ }
254+ options.push_back ({" channel-config" , VariantType::String, policy->getFairMQOutputChannel ().c_str (), {" Out-of-band channel config" }});
255+ LOG (DEBUG) << " - registering output FairMQ channel '" << policy->getFairMQOutputChannel () << " '" ;
256+ }
257+ }
258+ options.push_back ({" period-timer-stats" , framework::VariantType::Int, 10 * 1000000 , {" Dispatcher's stats timer period" }});
259+
260+ return options;
222261}
223262
224263} // namespace o2::utilities
0 commit comments