fw4spl
SActivitySequencer.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2017.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "activities/SActivitySequencer.hpp"
8 
9 #include <fwActivities/IActivityValidator.hpp>
10 #include <fwActivities/registry/Activities.hpp>
11 
12 #include <fwCom/Signal.hxx>
13 #include <fwCom/Signals.hpp>
14 #include <fwCom/Slot.hpp>
15 #include <fwCom/Slots.hpp>
16 #include <fwCom/Slots.hxx>
17 
18 #include <fwData/Composite.hpp>
19 
20 #include <fwGui/dialog/MessageDialog.hpp>
21 
22 #include <fwMedData/ActivitySeries.hpp>
23 #include <fwMedData/SeriesDB.hpp>
24 
25 #include <fwMedDataTools/helper/SeriesDB.hpp>
26 
27 #include <fwRuntime/operations.hpp>
28 
29 #include <fwServices/macros.hpp>
30 
31 #include <fwTools/dateAndTime.hpp>
32 #include <fwTools/UUID.hpp>
33 
34 #include <boost/foreach.hpp>
35 
36 namespace activities
37 {
38 
39 //------------------------------------------------------------------------------
40 
41 fwServicesRegisterMacro( ::fwServices::IController, ::activities::SActivitySequencer );
42 
43 //------------------------------------------------------------------------------
44 
45 const ::fwCom::Slots::SlotKeyType s_NEXT_SLOT = "next";
46 const ::fwCom::Slots::SlotKeyType s_PREVIOUS_SLOT = "previous";
47 const ::fwCom::Slots::SlotKeyType s_SEND_INFO_SLOT = "sendInfo";
48 
49 const ::fwCom::Signals::SignalKeyType s_ACTIVITY_CREATED_SIG = "activityCreated";
50 const ::fwCom::Signals::SignalKeyType s_ENABLED_PREVIOUS_SIG = "enabledPrevious";
51 const ::fwCom::Signals::SignalKeyType s_ENABLED_NEXT_SIG = "enabledNext";
52 
53 const ::fwServices::IService::KeyType s_SERIESDB_INOUT = "seriesDB";
54 const ::fwServices::IService::KeyType s_OVERRIDES_INOUT = "requirementOverrides";
55 
56 //------------------------------------------------------------------------------
57 
59  m_currentActivity(-1)
60 {
61  newSlot(s_NEXT_SLOT, &SActivitySequencer::next, this);
62  newSlot(s_PREVIOUS_SLOT, &SActivitySequencer::previous, this);
63  newSlot(s_SEND_INFO_SLOT, &SActivitySequencer::sendInfo, this);
64 
65  m_sigActivityCreated = newSignal< ActivityCreatedSignalType >(s_ACTIVITY_CREATED_SIG);
66  m_sigEnabledPrevious = newSignal< EnabledPreviousSignalType >(s_ENABLED_PREVIOUS_SIG);
67  m_sigEnabledNext = newSignal< EnabledNextSignalType >(s_ENABLED_NEXT_SIG);
68 }
69 
70 //------------------------------------------------------------------------------
71 
73 {
74 }
75 
76 //------------------------------------------------------------------------------
77 
79 {
80  const ::fwServices::IService::ConfigType config = this->getConfigTree();
81  BOOST_FOREACH( const ::fwServices::IService::ConfigType::value_type &v, config.equal_range("activity") )
82  {
83  m_activityIds.push_back(v.second.get<std::string>(""));
84  }
85 }
86 
87 //------------------------------------------------------------------------------
88 
90 {
91 }
92 
93 //------------------------------------------------------------------------------
94 
96 {
97 }
98 
99 //------------------------------------------------------------------------------
100 
102 {
103  ::fwMedData::SeriesDB::sptr seriesDB = this->getInOut< ::fwMedData::SeriesDB >(s_SERIESDB_INOUT);
104  SLM_ASSERT("Missing '" + s_SERIESDB_INOUT +"' seriesDB", seriesDB);
105 
106  m_currentActivity = -1;
107 
108  for (const auto& series: seriesDB->getContainer())
109  {
110  ::fwMedData::ActivitySeries::sptr activity = ::fwMedData::ActivitySeries::dynamicCast(series);
111 
112  if (!activity)
113  {
114  // Remove the wrong data
115  SLM_ERROR("The series DB must only contain 'ActivitySeries'. The series of type '" +
116  series->getClassname() + "' will be removed");
117 
118  ::fwMedDataTools::helper::SeriesDB helper(seriesDB);
119  helper.remove(series);
120  helper.notify();
121  }
122  else if (std::find(m_activityIds.begin(), m_activityIds.end(),
123  activity->getActivityConfigId()) == m_activityIds.end())
124  {
125  // Remove the wrong data
126  SLM_ERROR("The activity '" +activity->getActivityConfigId() + "' is unknown, it will be removed");
127 
128  ::fwMedDataTools::helper::SeriesDB helper(seriesDB);
129  helper.remove(activity);
130  helper.notify();
131  }
132  else
133  {
134  ++m_currentActivity;
135  this->storeActivityData();
136  }
137  }
138 
139  if (m_currentActivity >= 0)
140  {
141  // launch the last series
142  const size_t index = static_cast<size_t>(m_currentActivity);
143  ::fwMedData::ActivitySeries::sptr lastActivity = this->getActivity(index);
144 
145  m_sigActivityCreated->asyncEmit(lastActivity);
146  }
147 }
148 
149 //------------------------------------------------------------------------------
150 
151 void SActivitySequencer::next()
152 {
153  if (m_currentActivity >= 0)
154  {
155  this->storeActivityData();
156  }
157 
158  const size_t newIdx = static_cast<size_t>(m_currentActivity + 1);
159  if (newIdx >= m_activityIds.size())
160  {
161  OSLM_ERROR("no activity to launch, the current activity '"+ m_activityIds.back() + "' is the last one.");
162  return;
163  }
164 
165  ::fwMedData::ActivitySeries::sptr activity = this->getActivity(newIdx);
166 
167  if(this->checkValidity(activity, true))
168  {
169  m_sigActivityCreated->asyncEmit(activity);
170 
171  ++m_currentActivity;
172  }
173 }
174 
175 //------------------------------------------------------------------------------
176 
177 void SActivitySequencer::previous()
178 {
179  if (m_currentActivity <= 0)
180  {
181  OSLM_ERROR("no activity to launch, the current activity '"+ m_activityIds[0] + "' is the first one.");
182  return;
183  }
184  this->storeActivityData();
185  const size_t newIdx = static_cast<size_t>(m_currentActivity - 1);
186 
187  ::fwMedData::ActivitySeries::sptr activity = this->getActivity(newIdx);
188 
189  if(this->checkValidity(activity, true))
190  {
191  m_sigActivityCreated->asyncEmit(activity);
192 
193  --m_currentActivity;
194  }
195 }
196 
197 //------------------------------------------------------------------------------
198 
199 void SActivitySequencer::sendInfo() const
200 {
201  const bool previousEnabled = (m_currentActivity > 0);
202  m_sigEnabledPrevious->asyncEmit(previousEnabled);
203 
204  const bool nextEnabled = (m_currentActivity < static_cast<int>(m_activityIds.size()) -1);
205  m_sigEnabledNext->asyncEmit(nextEnabled);
206 }
207 
208 //------------------------------------------------------------------------------
209 
210 void SActivitySequencer::storeActivityData()
211 {
212  ::fwMedData::SeriesDB::sptr seriesDB = this->getInOut< ::fwMedData::SeriesDB >(s_SERIESDB_INOUT);
213  SLM_ASSERT("Missing '" + s_SERIESDB_INOUT +"' seriesDB", seriesDB);
214 
215  // Retrives the current activity data
216  const size_t currentIdx = static_cast<size_t>(m_currentActivity);
217  SLM_ASSERT("SeriesDB does not contain enough series.", seriesDB->size() > currentIdx);
218  ::fwMedData::Series::sptr series = seriesDB->getContainer()[currentIdx];
219  ::fwMedData::ActivitySeries::sptr activity = ::fwMedData::ActivitySeries::dynamicCast(series);
220  SLM_ASSERT("seriesDB contains an unknown series : " + series->getClassname(), activity);
221  ::fwData::Composite::sptr composite = activity->getData();
222  ::fwData::Composite::csptr overrides = this->getInput< ::fwData::Composite>(s_OVERRIDES_INOUT);
223 
224  if(overrides)
225  {
226  // Do not store overriden requirements
227  auto overridesContainer = overrides->getContainer();
228  for (const auto& elt : composite->getContainer())
229  {
230  if(overridesContainer.count(elt.first) == 0)
231  {
232  m_requirements[elt.first] = elt.second;
233  }
234  }
235  }
236  else
237  {
238  for (const auto& elt : composite->getContainer())
239  {
240  m_requirements[elt.first] = elt.second;
241  }
242  }
243 }
244 
245 //------------------------------------------------------------------------------
246 
247 ::fwMedData::ActivitySeries::sptr SActivitySequencer::getActivity(size_t index)
248 {
249  ::fwMedData::SeriesDB::sptr seriesDB = this->getInOut< ::fwMedData::SeriesDB >(s_SERIESDB_INOUT);
250  SLM_ASSERT("Missing '" + s_SERIESDB_INOUT +"' seriesDB", seriesDB);
251 
252  ::fwMedData::ActivitySeries::sptr activity;
253  ::fwData::Composite::csptr overrides = this->getInput< ::fwData::Composite>(s_OVERRIDES_INOUT);
254  if (seriesDB->size() > index) // The activity already exists, update the data
255  {
256  ::fwMedData::Series::sptr series = seriesDB->getContainer()[index];
257  activity = ::fwMedData::ActivitySeries::dynamicCast(series);
258  SLM_ASSERT("seriesDB contains an unknown series : " + series->getClassname(), activity);
259  ::fwData::Composite::sptr composite = activity->getData();
260 
261  // FIXME: update all the data or only the requirement ?
262  if(overrides)
263  {
264  auto overridesContainer = overrides->getContainer();
265  for (const auto& elt : composite->getContainer())
266  {
267  composite->getContainer()[elt.first] = overridesContainer.count(elt.first) == 0 ?
268  m_requirements[elt.first] : overridesContainer[elt.first];
269  }
270  }
271  else
272  {
273  for (const auto& elt : composite->getContainer())
274  {
275  composite->getContainer()[elt.first] = m_requirements[elt.first];
276  }
277  }
278  }
279  else // create a new activity series
280  {
281  const std::string activityId = m_activityIds[index];
282  const ::fwActivities::registry::ActivityInfo& info =
284 
285  activity = ::fwMedData::ActivitySeries::New();
286 
287  activity->setModality("OT");
288  activity->setInstanceUID("fwActivities." + ::fwTools::UUID::generateUUID() );
289 
290  const ::boost::posix_time::ptime now = ::boost::posix_time::second_clock::local_time();
291  activity->setDate(::fwTools::getDate(now));
292  activity->setTime(::fwTools::getTime(now));
293 
294  activity->setActivityConfigId(info.id);
295  activity->setDescription(info.description);
296 
297  ::fwData::Composite::sptr composite = activity->getData();
298 
299  for (const auto& req : info.requirements)
300  {
301  if (overrides)
302  {
303  auto overridesContainer = overrides->getContainer();
304  if(overridesContainer.count(req.name) != 0)
305  {
306  composite->getContainer()[req.name] = overridesContainer[req.name];
307  }
308  }
309  else if (m_requirements.find(req.name) != m_requirements.end())
310  {
311  composite->getContainer()[req.name] = m_requirements[req.name];
312  }
313  else if (req.create == true || (req.minOccurs == 0 && req.maxOccurs == 0))
314  {
315  // create the new data
316  ::fwData::Object::sptr newObj = ::fwData::factory::New(req.type);
317  composite->getContainer()[req.name] = newObj;
318  m_requirements[req.name] = newObj;
319  }
320  else if (req.minOccurs == 0)
321  {
322  // create empty composite for optional data
323  ::fwData::Composite::sptr optionalDataComposite = ::fwData::Composite::New();
324  composite->getContainer()[req.name] = optionalDataComposite;
325  m_requirements[req.name] = optionalDataComposite;
326  }
327  }
328 
329  ::fwMedDataTools::helper::SeriesDB helper(seriesDB);
330  helper.add(activity);
331  {
332  auto sig = seriesDB->signal< ::fwMedData::SeriesDB::AddedSeriesSignalType >(
334  ::fwCom::Connection::Blocker block(sig->getConnection( this->slot(s_UPDATE_SLOT)));
335  helper.notify();
336  }
337  }
338  return activity;
339 }
340 
341 //------------------------------------------------------------------------------
342 
343 bool SActivitySequencer::checkValidity(const fwMedData::ActivitySeries::csptr& activity, bool showDialog) const
344 {
345  bool ok = true;
346  std::string errorMsg;
347 
348  SLM_ASSERT("activity is not defined", activity);
349 
350  const ::fwActivities::registry::ActivityInfo& info =
351  ::fwActivities::registry::Activities::getDefault()->getInfo(activity->getActivityConfigId());
352 
353  // load activity bundle to register validator factory
354  std::shared_ptr< ::fwRuntime::Bundle > bundle = ::fwRuntime::findBundle(info.bundleId, info.bundleVersion);
355  if (!bundle->isStarted())
356  {
357  bundle->start();
358  }
359 
360  for (std::string validatorImpl : info.validatorsImpl)
361  {
362  ::fwActivities::IValidator::sptr validator = ::fwActivities::validator::factory::New(validatorImpl);
363 
364  ::fwActivities::IActivityValidator::sptr activityValidator =
365  ::fwActivities::IActivityValidator::dynamicCast(validator);
366 
367  SLM_ERROR_IF("Validator '" + validatorImpl + "' instantiation failed", !activityValidator);
368 
369  if (activityValidator)
370  {
371 
372  ::fwActivities::IValidator::ValidationType validation = activityValidator->validate(activity);
373  if(!validation.first)
374  {
375  ok = false;
376  errorMsg += "\n" + validation.second;
377  }
378  }
379  }
380 
381  if (!ok && showDialog)
382  {
383  ::fwGui::dialog::MessageDialog::showMessageDialog("Activity not valid", "The activity '" + info.title + "' can "
384  "not be launched: \n" + errorMsg);
385  }
386  return ok;
387 }
388 
389 //------------------------------------------------------------------------------
390 
392 {
393  KeyConnectionsMap connections;
394  connections.push( s_SERIESDB_INOUT, ::fwMedData::SeriesDB::s_ADDED_SERIES_SIG, s_UPDATE_SLOT );
395  connections.push( s_SERIESDB_INOUT, ::fwMedData::SeriesDB::s_MODIFIED_SIG, s_UPDATE_SLOT );
396 
397  return connections;
398 }
399 
400 //------------------------------------------------------------------------------
401 
402 } // namespace activities
virtual ACTIVITIES_API ~SActivitySequencer() noexcept
Destructor. Do nothing.
This class is a helper to define the connections of a service and its data.
Definition: IService.hpp:454
std::pair< bool, std::string > ValidationType
Defines validation result of an activity. First element tells if the activity is validated or not by ...
Definition: IValidator.hpp:39
Class allowing to block a Connection.
Definition: Connection.hpp:20
static FWGUI_API IMessageDialog::Buttons showMessageDialog(const std::string &title, const std::string &message,::fwGui::dialog::IMessageDialog::Icons icon=INFO)
static FWACTIVITIES_API Activities::sptr getDefault()
Return the default global instance of Activities.
Definition: Activities.cpp:226
ACTIVITIES_API SActivitySequencer() noexcept
Constructor. Do nothing.
virtual void configuring() override
Parse XML configuration.
FWMEDDATATOOLS_API void add(::fwMedData::Series::sptr newSeries)
Add a Series in the SeriesDB.
Defines an helper to modify an fwMedData::SeriesDB and create in parallel the message to announce thi...
virtual void starting() override
Do nothing.
virtual void updating() override
Analyse the series contained in the current seriesDB.
#define SLM_ERROR(message)
Definition: spyLog.hpp:272
static FWMEDDATA_APIconst::fwCom::Signals::SignalKeyType s_ADDED_SERIES_SIG
Type of signal when series are added.
#define OSLM_ERROR(message)
Definition: spyLog.hpp:274
virtual ACTIVITIES_API KeyConnectionsMap getAutoConnections() const override
Connect the service to the SeriesDB signals.
This interface defines control service API. Does nothing particularly, can be considered as a default...
Definition: IController.hpp:23
#define SLM_ERROR_IF(message, cond)
Definition: spyLog.hpp:276
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
The namespace activities contains helpers and services allowing to launch activities.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_MODIFIED_SIG
Key in m_signals map of signal m_sigModified.
FWTOOLS_API std::string getDate(const ::boost::posix_time::ptime &dateAndTime)
Convert a boost time to a string date.
FWMEDDATATOOLS_API void notify()
Send the signal of modification.
virtual void stopping() override
Do nothing.
FWTOOLS_API std::string getTime(const ::boost::posix_time::ptime &dateAndTime)
Convert a boost time to a string time.
static FWSERVICES_APIconst::fwCom::Slots::SlotKeyType s_UPDATE_SLOT
Slot to call start method.
Definition: IService.hpp:177
virtual FWSERVICES_API void info(std::ostream &_sstream)
Write information in a stream.
Definition: IService.cpp:74
This service allows to launch activities sequentially.
static FWTOOLS_API UUIDType generateUUID()
Return a new extended UUID;.
Definition: UUID.cpp:114
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.
Definition: IService.cpp:247