fw4spl
Config.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2016.
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 "fwServices/helper/Config.hpp"
8 #include "fwServices/registry/ServiceConfig.hpp"
9 
10 #include "fwServices/registry/Proxy.hpp"
11 
12 #include <fwCom/HasSignals.hpp>
13 #include <fwCom/HasSlots.hpp>
14 #include <fwCom/helper/SigSlotConnection.hpp>
15 
16 #include <fwData/Object.hpp>
17 
18 #include <fwRuntime/ConfigurationElement.hpp>
19 
20 #include <fwTools/Object.hpp>
21 
22 #include <boost/regex.hpp>
23 
24 #include <string>
25 #include <vector>
26 
27 namespace fwServices
28 {
29 
30 namespace helper
31 {
32 
34 const std::array< std::string, 3 > s_DATA_KEYWORDS = {{ "in", "out", "inout" }};
35 
36 //-----------------------------------------------------------------------------
37 
38 void Config::createConnections( const ::fwRuntime::ConfigurationElement::csptr& connectionCfg,
39  ::fwCom::helper::SigSlotConnection& connections,
40  const CSPTR(::fwTools::Object)& obj)
41 {
42  ConnectionInfo info = parseConnections(connectionCfg, obj);
43 
44  ::fwTools::Object::sptr sigSource = ::fwTools::fwID::getObject(info.m_signal.first);
45  ::fwCom::HasSignals::sptr hasSignals = std::dynamic_pointer_cast< ::fwCom::HasSignals >(sigSource);
46 
47  SLM_ASSERT("Signal source not found '" + info.m_signal.first + "'", sigSource);
48  SLM_ASSERT("invalid signal source '" + info.m_signal.first + "'", hasSignals);
49 
50  for(SlotInfoType slotInfo : info.m_slots)
51  {
52  ::fwTools::Object::sptr obj = ::fwTools::fwID::getObject(slotInfo.first);
53  SLM_ASSERT("Failed to retrieve object '" + slotInfo.first + "'", obj);
54  ::fwCom::HasSlots::sptr hasSlots = std::dynamic_pointer_cast< ::fwCom::HasSlots >(obj);
55  SLM_ASSERT("invalid slot owner " << slotInfo.first, hasSlots);
56 
57  connections.connect(hasSignals, info.m_signal.second, hasSlots, slotInfo.second);
58  }
59 }
60 
61 //-----------------------------------------------------------------------------
62 
63 Config::ConnectionInfo Config::parseConnections( const ::fwRuntime::ConfigurationElement::csptr& connectionCfg,
64  const CSPTR(::fwTools::Object)& obj)
65 {
66  ConnectionInfo info;
67 
68  ::boost::regex re("(.*)/(.*)");
69  ::boost::smatch match;
70  std::string src, uid, key;
71 
72  for(::fwRuntime::ConfigurationElement::csptr elem : connectionCfg->getElements())
73  {
74  src = elem->getValue();
75  if( ::boost::regex_match(src, match, re) )
76  {
77  OSLM_ASSERT("Wrong value for attribute src: "<<src, match.size() >= 3);
78  uid.assign(match[1].first, match[1].second);
79  key.assign(match[2].first, match[2].second);
80 
81  OSLM_ASSERT(src << " configuration is not correct for "<< elem->getName(),
82  !uid.empty() && !key.empty());
83 
84  if (elem->getName() == "signal")
85  {
86  SLM_ASSERT("There must be only one signal by connection",
87  info.m_signal.first.empty() && info.m_signal.second.empty());
88  info.m_signal = std::make_pair(uid, key);
89  }
90  else if (elem->getName() == "slot")
91  {
92  info.m_slots.push_back( std::make_pair(uid, key) );
93  }
94  }
95  else
96  {
97  SLM_ASSERT("Object uid is not defined, object used to retrieve signal must be present.", obj);
98  uid = obj->getID();
99  key = src;
100  SLM_ASSERT("Element must be a signal or must be written as <fwID/Key>", elem->getName() == "signal");
101  SLM_ASSERT("There must be only one signal by connection",
102  info.m_signal.first.empty() && info.m_signal.second.empty());
103  info.m_signal = std::make_pair(uid, key);
104  }
105  }
106 
107  // This is ok to return like this, thanks to C++11 rvalue there will be no copy of the vectors inside the struct
108  return info;
109 }
110 
111 //-----------------------------------------------------------------------------
112 
113 ProxyConnections Config::parseConnections2(const ::fwRuntime::ConfigurationElement::csptr& connectionCfg,
114  const std::string& errMsgHead,
115  std::function<std::string ()> generateChannelNameFn)
116 {
117 
118  ::boost::regex re("(.*)/(.*)");
119  ::boost::smatch match;
120  std::string src, uid, key;
121 
122  std::string channel;
123  if(connectionCfg->hasAttribute("channel"))
124  {
125  channel = connectionCfg->getAttributeValue("channel");
126  SLM_ASSERT(errMsgHead + "Empty 'channel' attribute", !channel.empty());
127  }
128  else
129  {
130  // Generate an UID
131  channel = generateChannelNameFn();
132  }
133 
134  ProxyConnections proxyCnt(channel);
135 
136  for(::fwRuntime::ConfigurationElement::csptr elem : connectionCfg->getElements())
137  {
138  src = elem->getValue();
139  if( ::boost::regex_match(src, match, re) )
140  {
141  OSLM_ASSERT("errMsgHead + Wrong value for attribute src: "<<src, match.size() >= 3);
142  uid.assign(match[1].first, match[1].second);
143  key.assign(match[2].first, match[2].second);
144 
145  OSLM_ASSERT(errMsgHead + src << " configuration is not correct for "<< elem->getName(),
146  !uid.empty() && !key.empty());
147 
148  if (elem->getName() == "signal")
149  {
150  proxyCnt.addSignalConnection(uid, key);
151  }
152  else if (elem->getName() == "slot")
153  {
154  proxyCnt.addSlotConnection(uid, key);
155  }
156  }
157  else
158  {
159  SLM_ASSERT(errMsgHead + "Signal or slot must be written as <signal>fwID/Key</signal> or "
160  "<slot>fwID/Key</slot>", false);
161  }
162  }
163 
164  // This is ok to return like this, thanks to C++11 rvalue there will be no copy of the vectors inside the struct
165  return proxyCnt;
166 }
167 
168 //-----------------------------------------------------------------------------
169 
170 void Config::createProxy( const std::string& objectKey,
172  Config::ProxyConnectionsMapType& proxyMap,
173  const CSPTR(::fwData::Object)& obj)
174 {
175  ::fwServices::registry::Proxy::sptr proxy = ::fwServices::registry::Proxy::getDefault();
176 
177  SLM_ASSERT("Missing 'channel' attribute", cfg->hasAttribute("channel"));
178  const std::string channel = cfg->getExistingAttributeValue("channel");
179  ProxyConnections proxyCnt(channel);
180 
181  ::boost::regex re("(.*)/(.*)");
182  ::boost::smatch match;
183  std::string src, uid, key;
184  for(::fwRuntime::ConfigurationElement::csptr elem : cfg->getElements())
185  {
186  src = elem->getValue();
187  if( ::boost::regex_match(src, match, re) )
188  {
189  SLM_ASSERT("Wrong value for attribute src: " + src, match.size() >= 3);
190  uid.assign(match[1].first, match[1].second);
191  key.assign(match[2].first, match[2].second);
192 
193  SLM_ASSERT(src + " configuration is not correct for " + elem->getName(), !uid.empty() && !key.empty());
194 
195  ::fwTools::Object::sptr obj = ::fwTools::fwID::getObject(uid);
196 
197  if (elem->getName() == "signal")
198  {
199  ::fwCom::HasSignals::sptr hasSignals = std::dynamic_pointer_cast< ::fwCom::HasSignals >(obj);
200  SLM_ASSERT("Can't find the holder of signal '" + key + "'", hasSignals);
201  ::fwCom::SignalBase::sptr sig = hasSignals->signal(key);
202  proxy->connect(channel, sig);
203  proxyCnt.addSignalConnection(uid, key);
204  }
205  else if (elem->getName() == "slot")
206  {
207  ::fwCom::HasSlots::sptr hasSlots = std::dynamic_pointer_cast< ::fwCom::HasSlots >(obj);
208  SLM_ASSERT("Can't find the holder of slot '" + key + "'", hasSlots);
209  ::fwCom::SlotBase::sptr slot = hasSlots->slot(key);
210  proxy->connect(channel, slot);
211  proxyCnt.addSlotConnection(uid, key);
212  }
213  }
214  else
215  {
216  uid = obj->getID();
217  key = src;
218  SLM_ASSERT("Element must be a signal or must be written as <fwID/Key>", elem->getName() == "signal");
219  ::fwCom::SignalBase::sptr sig = obj->signal(key);
220  proxy->connect(channel, sig);
221  proxyCnt.addSignalConnection(uid, key);
222  }
223  }
224  proxyMap[objectKey].push_back(proxyCnt);
225 }
226 
227 //-----------------------------------------------------------------------------
228 
229 void Config::disconnectProxies(const std::string& objectKey, Config::ProxyConnectionsMapType& proxyMap)
230 {
231  ProxyConnectionsMapType::iterator iter = proxyMap.find(objectKey);
232  if (iter != proxyMap.end())
233  {
234  ::fwServices::registry::Proxy::sptr proxy = ::fwServices::registry::Proxy::getDefault();
235 
236  ProxyConnectionsVectType vectProxyConnections = iter->second;
237 
238  for(ProxyConnectionsVectType::value_type proxyConnections : vectProxyConnections)
239  {
240  for(ProxyConnections::ProxyEltType signalElt : proxyConnections.m_signals)
241  {
242  ::fwTools::Object::sptr obj = ::fwTools::fwID::getObject(signalElt.first);
243  ::fwCom::HasSignals::sptr hasSignals = std::dynamic_pointer_cast< ::fwCom::HasSignals >(obj);
244  ::fwCom::SignalBase::sptr sig = hasSignals->signal(signalElt.second);
245  proxy->disconnect(proxyConnections.m_channel, sig);
246  }
247  for(ProxyConnections::ProxyEltType slotElt : proxyConnections.m_slots)
248  {
249  ::fwTools::Object::sptr obj = ::fwTools::fwID::getObject(slotElt.first);
250  ::fwCom::HasSlots::sptr hasSlots = std::dynamic_pointer_cast< ::fwCom::HasSlots >(obj);
251  ::fwCom::SlotBase::sptr slot = hasSlots->slot(slotElt.second);
252  proxy->disconnect(proxyConnections.m_channel, slot);
253  }
254  }
255  vectProxyConnections.clear();
256  proxyMap.erase(objectKey);
257  }
258 }
259 
260 //-----------------------------------------------------------------------------
261 
262 ::fwServices::IService::Config Config::parseService(const ::fwRuntime::ConfigurationElement::csptr& srvElem,
263  const std::string& errMsgHead)
264 {
265  SLM_ASSERT("Configuration element is not a \"service\" node.", srvElem->getName() == "service");
266 
267  // Get attributes
269 
270  // Uid
271  if (srvElem->hasAttribute("uid"))
272  {
273  srvConfig.m_uid = srvElem->getAttributeValue("uid");
274  SLM_ASSERT(errMsgHead + "'uid' attribute is empty.", !srvConfig.m_uid.empty());
275  }
276 
277  std::string errMsgTail = " when parsing service '" + srvConfig.m_uid + "'.";
278 
279  // Config
280  std::string config;
281  if (srvElem->hasAttribute("config"))
282  {
283  config = srvElem->getAttributeValue("config");
284  SLM_ASSERT(errMsgHead + "\"config\" attribute is empty" + errMsgTail, !config.empty());
285  }
286 
287  // Type
288  SLM_ASSERT(errMsgHead + "'type'' attribute is empty" + errMsgTail, srvElem->hasAttribute("type"));
289  srvConfig.m_type = srvElem->getAttributeValue("type");
290  SLM_ASSERT(errMsgHead + "Attribute \"type\" is required " + errMsgTail,
291  !srvConfig.m_type.empty());
292 
293  // AutoConnect
294  srvConfig.m_globalAutoConnect = false;
295  const ::fwRuntime::ConfigurationElement::AttributePair attribAutoConnect =
296  srvElem->getSafeAttributeValue("autoConnect");
297  if(attribAutoConnect.first)
298  {
299  SLM_ASSERT("'autoConnect' attribute must be either 'yes' or 'no'" + errMsgTail,
300  (!attribAutoConnect.first) || attribAutoConnect.second == "yes" || attribAutoConnect.second == "no");
301  srvConfig.m_globalAutoConnect = (attribAutoConnect.second == "yes");
302  }
303 
304  // Worker key
305  srvConfig.m_worker = srvElem->getAttributeValue("worker");
306 
307  // Get service configuration
308  ::fwRuntime::ConfigurationElement::csptr cfgElem = srvElem;
309  if (!config.empty())
310  {
311  const auto srvCfgFactory = ::fwServices::registry::ServiceConfig::getDefault();
312  cfgElem = srvCfgFactory->getServiceConfig(config, srvConfig.m_type);
313  }
314  srvConfig.m_config = cfgElem;
315 
316  // Check if user did not bind a service to another service
317  for(::fwRuntime::ConfigurationElement::csptr elem : cfgElem->getElements())
318  {
319  SLM_ASSERT(errMsgHead + "Cannot bind a service to another service" + errMsgTail,
320  elem->getName() != "service" &&
321  elem->getName() != "serviceList");
322  }
323 
324  // Check first if we can create this service
325  // If there is a missing object in its data list, then it is not possible
326  auto cfgConstElem = ::fwRuntime::ConfigurationElement::constCast(srvElem);
327 
328  // Collect all input/output configurations
329  std::vector< ::fwRuntime::ConfigurationElement::sptr > objectCfgs;
330  for(const auto& dataKeyword : s_DATA_KEYWORDS)
331  {
332  auto objCfgs = cfgConstElem->find(dataKeyword);
333  std::move(objCfgs.begin(), objCfgs.end(), std::back_inserter(objectCfgs));
334  }
335 
336  // Parse input/output configurations
337  for(const auto& cfg : objectCfgs)
338  {
339  // Access type
341  if(cfg->getName() == "in")
342  {
343  objConfig.m_access = ::fwServices::IService::AccessType::INPUT;
344  }
345  else if(cfg->getName() == "out")
346  {
347  objConfig.m_access = ::fwServices::IService::AccessType::OUTPUT;
348  }
349  else if(cfg->getName() == "inout")
350  {
351  objConfig.m_access = ::fwServices::IService::AccessType::INOUT;
352  }
353  else
354  {
355  SLM_FATAL("Unreachable code");
356  }
357 
358  // AutoConnect
359  auto autoConnect = cfg->getSafeAttributeValue("autoConnect");
360  objConfig.m_autoConnect = false;
361  if(autoConnect.first)
362  {
363  SLM_ASSERT(errMsgHead + "'autoConnect' attribute must be either 'yes' or 'no'" + errMsgTail,
364  autoConnect.second == "yes" || autoConnect.second == "no" );
365  objConfig.m_autoConnect = (autoConnect.second == "yes");
366  }
367 
368  // Optional
369  if(objConfig.m_access != ::fwServices::IService::AccessType::OUTPUT)
370  {
371  const std::string optionalStr = cfg->getAttributeValue("optional");
372 
373  objConfig.m_optional = false;
374  if(!optionalStr.empty())
375  {
376  SLM_ASSERT("'optional' attribute must be either 'yes' or 'no'" + errMsgTail,
377  optionalStr=="" || optionalStr == "yes" || optionalStr == "no");
378  objConfig.m_optional = optionalStr == "yes" ? true : false;
379  }
380  }
381  else
382  {
383  objConfig.m_optional = true;
384  }
385 
386  // Do we use groups ?
387  std::string group = cfg->getAttributeValue("group");
388  if(!group.empty())
389  {
390  std::vector< ::fwRuntime::ConfigurationElement::sptr > keyCfgs = cfg->find("key");
391 
392  size_t count = 0;
393  for(const auto& groupCfg : keyCfgs)
394  {
395  ::fwServices::IService::ObjectServiceConfig grouObjConfig = objConfig;
396 
397  // Identifier
398  grouObjConfig.m_uid = groupCfg->getAttributeValue("uid");
399  SLM_ASSERT(errMsgHead + "\"uid\" attribute is empty" + errMsgTail, !grouObjConfig.m_uid.empty());
400 
401  grouObjConfig.m_key = KEY_GROUP_NAME(group, count++);
402 
403  // AutoConnect
404  auto autoConnectPeyKey = groupCfg->getSafeAttributeValue("autoConnect");
405  if(autoConnectPeyKey.first)
406  {
407  SLM_ASSERT(errMsgHead + "'autoConnect' attribute must be either 'yes' or 'no'" + errMsgTail,
408  autoConnectPeyKey.second == "yes" || autoConnectPeyKey.second == "no" );
409  grouObjConfig.m_autoConnect = (autoConnectPeyKey.second == "yes");
410  }
411  // Optional
412  if(grouObjConfig.m_access != ::fwServices::IService::AccessType::OUTPUT)
413  {
414  const std::string optionalStr = groupCfg->getAttributeValue("optional");
415 
416  if(!optionalStr.empty())
417  {
418  SLM_ASSERT("'optional' attribute must be either 'yes' or 'no'" + errMsgTail,
419  optionalStr=="" || optionalStr == "yes" || optionalStr == "no");
420  grouObjConfig.m_optional = optionalStr == "yes" ? true : false;
421  }
422  }
423 
424  srvConfig.m_objects.emplace_back(grouObjConfig);
425  }
426  srvConfig.m_groupSize[group] = count;
427  }
428  else
429  {
430  // Identifier
431  objConfig.m_uid = cfg->getAttributeValue("uid");
432  SLM_ASSERT(errMsgHead + "\"uid\" attribute is empty" + errMsgTail, !objConfig.m_uid.empty());
433 
434  // Key inside the service
435  objConfig.m_key = cfg->getAttributeValue("key");
436  SLM_ASSERT(errMsgHead + "Missing object attribute 'key'" + errMsgTail, !objConfig.m_key.empty());
437 
438  srvConfig.m_objects.emplace_back(objConfig);
439  }
440  }
441 
442  return srvConfig;
443 }
444 
445 // ----------------------------------------------------------------------------
446 
447 } // namespace helper
448 } // namespace fwServices
449 
#define CSPTR(_cls_)
Define Base class for FW4SPL objects and services.
#define OSLM_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:310
static FWSERVICES_API::fwServices::IService::Config parseService(const std::shared_ptr< const ::fwRuntime::ConfigurationElement > &srvElem, const std::string &errMsgHead)
Parse a service and return a service configuration.
Definition: Config.cpp:262
static FWSERVICES_API Proxy::sptr getDefault()
Returns an instance of Proxy.
Definition: Proxy.cpp:33
Namespace fwServices is dedicated to (mimic) the dynamic affectation of methods to (pure data) object...
This class proposes a mapping between a SlotKeyType and a SlotBase.
Definition: HasSlots.hpp:22
FWRUNTIME_API bool hasAttribute(const std::string &name) const noexcept
Tells if the specified attributes exists.
Defines the configuration element class.
FWRUNTIME_API std::vector< ConfigurationElement::sptr > find(std::string name="", std::string attribute="", std::string attributeValue="", int depth=1)
Find recursively all the corresponding configuration elements.
FWRUNTIME_API const std::string getExistingAttributeValue(const std::string &name) const
Retrieves the value of an attribute for the specified name.
FWRUNTIME_API const std::string getAttributeValue(const std::string &name) const noexcept
Retrieves the value of an attribute for the specified name.
FWRUNTIME_API const std::string getName() const noexcept
Retrieves the name of the configuration element.
Used to store object configuration in a service.
Definition: IService.hpp:90
This class provides few tools to ease connect/disconnect between a signal emitter and a slot receiver...
FWTOOLS_API IDType getID(Policy policy=GENERATE) const
Returns the id of the object. If it is not set and the policy value is.
Definition: fwID.cpp:78
#define SLM_FATAL(message)
Definition: spyLog.hpp:283
static FWSERVICES_API ConnectionInfo parseConnections(const std::shared_ptr< const ::fwRuntime::ConfigurationElement > &cfg, const std::shared_ptr< const ::fwTools::Object > &obj=std::shared_ptr< const ::fwTools::Object >())
Parses "<connect>" tags from given configuration and return a structure containing the signal and slo...
Definition: Config.cpp:63
static FWSERVICES_API void createProxy(const std::string &objectKey, const std::shared_ptr< const ::fwRuntime::ConfigurationElement > &cfg, ProxyConnectionsMapType &proxyMap, const std::shared_ptr< const ::fwData::Object > &obj=std::shared_ptr< const ::fwData::Object >())
Parses "<proxy>" tags from given configuration to connect signals and slots using proxies...
Definition: Config.cpp:170
FWRUNTIME_API const AttributePair getSafeAttributeValue(const std::string &name) const noexcept
Retrieves the value of an existing attribute for the specified name.
#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
Helper class to register proxy connections.
Base class for each data object.
FWRUNTIME_API const Container & getElements() const
Returns the configuration element container.
static FWSERVICES_API void createConnections(const std::shared_ptr< const ::fwRuntime::ConfigurationElement > &cfg,::fwCom::helper::SigSlotConnection &helper, const std::shared_ptr< const ::fwTools::Object > &obj=std::shared_ptr< const ::fwTools::Object >())
Parses "<connect>" tags from given configuration to connect signals and slots using given helper...
Definition: Config.cpp:38
static FWSERVICES_API ServiceConfig::sptr getDefault()
Return the default global instance of ServiceConfig.
FWCOM_API void connect(const ::fwCom::HasSignals::csptr &hasSignals,::fwCom::Signals::SignalKeyType signalKey, const ::fwCom::HasSlots::csptr &hasSlots,::fwCom::Slots::SlotKeyType slotKey)
Connect signal to slot, and register this new connection in m_connections.
static FWSERVICES_API ProxyConnections parseConnections2(const std::shared_ptr< const ::fwRuntime::ConfigurationElement > &connectionCfg, const std::string &errMsgHead, std::function< std::string()> generateChannelNameFn)
Parses "<connect>" tags from given configuration and return a structure containing the signal and slo...
Definition: Config.cpp:113
static FWSERVICES_API void disconnectProxies(const std::string &objectKey, Config::ProxyConnectionsMapType &proxyMap)
Disconnects all proxies associated to objectKey.
Definition: Config.cpp:229
Used to store a service configuration.
Definition: IService.hpp:100
This class proposes a mapping between a SignalKeyType and a SignalBase.
Definition: HasSignals.hpp:21
static FWTOOLS_API std::shared_ptr< ::fwTools::Object > getObject(IDType requestID)
Retrieve the object attached to the given id. Return a null sptr if no correspondence exist...
Definition: fwID.cpp:117