Overlord.cxx 16.3 KB
Newer Older
Floris Berendsen's avatar
Floris Berendsen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*=========================================================================
 *
 *  Copyright Leiden University Medical Center, Erasmus University Medical 
 *  Center and contributors
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

20
#include "Overlord.h"
21
#include "selxKeys.h"
22
23
24

namespace selx
{
25
  Overlord::Overlord() : m_isConfigured(false)
26
  {
27
    this->m_RunRegistrationComponents = ComponentsContainerType::New();
28
    this->m_AfterRegistrationComponents = ComponentsContainerType::New();   
29
  }
30

31
32
  bool Overlord::Configure()
  {
33
34
35
36
    //TODO: make a while loop until stable:
    // - for each unique component propagate the required interfaces to neighboring components as added criterion   
    
    
37
    if (!this->m_isConfigured)
38
    {
39

40
      std::cout << "Applying Component Criteria" << std::endl;
41
      this->ApplyNodeConfiguration();
42

43
      auto nonUniqueComponentNames = this->GetNonUniqueComponentNames();
44
      std::cout << nonUniqueComponentNames.size() << " out of " << m_Blueprint->GetComponentNames().size() << " Components could not be uniquely selected" << std::endl << std::endl;
45

46
47
48
49
      std::cout << "Applying Connection Criteria" << std::endl;
      this->ApplyConnectionConfiguration();
      nonUniqueComponentNames = this->GetNonUniqueComponentNames();
      std::cout << nonUniqueComponentNames.size() << " out of " << m_Blueprint->GetComponentNames().size() << " Components could not be uniquely selected" << std::endl << std::endl;
50

51
52
53
54
55
56
57
      this->m_isConfigured = true;
    }
    auto nonUniqueComponentNames = this->GetNonUniqueComponentNames();
    if (nonUniqueComponentNames.size() > 0)
    {
      std::cout << std::endl << "These Nodes need more criteria: " << std::endl; 
      for (const auto & nonUniqueComponentName : nonUniqueComponentNames)
58
      {
59
        std::cout << nonUniqueComponentName<< std::endl;
60
      }
61
      return false;
62
    }
63
    return true;
64
65
  }

66
  Overlord::ComponentNamesType Overlord::GetNonUniqueComponentNames()
67
  {
68
    ComponentNamesType nonUniqueComponentNames;
69
    const Blueprint::ComponentNamesType componentNames = m_Blueprint->GetComponentNames();
70
    for (auto const & name : componentNames)
71
72
    {    
     
73
74
75
76
      // The current idea of the configuration setup is that the number of 
      // possible components at a node can only be reduced by adding criteria.
      // If a node has 0 possible components, the configuration is aborted (with an exception)
      // If all nodes have exactly 1 possible component, no more criteria are needed.
77
78

      if (this->m_ComponentSelectorContainer[name]->HasMultipleComponents()==true)
79
      {
80
81
        //std::cout << "To select a component for blueprint node " << name << " more critatia are required" << std::endl;
        nonUniqueComponentNames.push_back(name);
82
83
      }
    }
84
    return nonUniqueComponentNames;
85
86
87
  }

  void Overlord::ApplyNodeConfiguration()
88
  {
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    // At the overlord we store all components selectors in a mapping based  
    // on the keys we find in the graph. This is a flexible solution, but is
    // fragile as well since correspondence is implicit.
    // We might consider copying the blueprint graph to a component selector
    // graph, such that all graph operations correspond
    //
    // This could be in line with the idea of maintaining 2 graphs: 
    // 1 descriptive (= const blueprint) and 1 internal holding to realized 
    // components.
    // Manipulating the internal graph (combining component nodes into a 
    // hybrid node, duplicating sub graphs, etc) is possible then.
    //
    // Additional redesign consideration: the final graph should hold the 
    // realized components at each node and not the ComponentSelectors that, 
    // in turn, hold 1 (or more) component.

105
    Blueprint::ComponentNamesType componentNames = this->m_Blueprint->GetComponentNames();
106
    
107
    for (auto const & name : componentNames)
108
    {
109
      std::cout << " Blueprint Node: " << name << std::endl;
110
      ComponentSelectorPointer currentComponentSelector = ComponentSelector::New();
111
      Blueprint::ParameterMapType currentProperty = this->m_Blueprint->GetComponent(name);
112
113
114
115
116
117
118
119
120
121
122
123
124
      for (auto const & criterion : currentProperty)
      {
        std::cout << "  " << criterion.first << ": " << criterion.second[0] << std::endl;
        currentComponentSelector->AddCriterion(criterion);
      }

      if ((currentComponentSelector->HasMultipleComponents() == false) && (currentComponentSelector->GetComponent().IsNull()))
      {
        std::stringstream msg;
        msg << "Too many criteria for Component " << name << std::endl;
        std::runtime_error::runtime_error(msg.str());
      }

125
      // insert new element
126
      this->m_ComponentSelectorContainer[name] = currentComponentSelector;
127
    }
128
    return;
129
  }
130

131
  void Overlord::ApplyConnectionConfiguration()
132
  {
133
134
    Blueprint::ComponentNamesType componentNames = this->m_Blueprint->GetComponentNames();
    for (auto const & name : componentNames)
135
    {
136
      for (auto const & outgoingName : this->m_Blueprint->GetOutputNames(name))
137
138
      {
        //TODO check direction upstream/downstream input/output source/target
139
140
        Blueprint::ParameterMapType connectionProperties = this->m_Blueprint->GetConnection(name, outgoingName);
        if (connectionProperties.count("NameOfInterface") > 0)
141
        {
142
143
144
145
146
          
          this->m_ComponentSelectorContainer[name]->AddCriterion({ keys::HasProvidingInterface, connectionProperties[keys::NameOfInterface] });
          this->m_ComponentSelectorContainer[outgoingName]->AddCriterion({ keys::HasAcceptingInterface, connectionProperties[keys::NameOfInterface] });
          std::cout << " Blueprint Node: " << name << std::endl << "  HasProvidingInterface " << connectionProperties[keys::NameOfInterface][0] << std::endl;
          std::cout << " Blueprint Node: " << outgoingName << std::endl << "  HasAcceptingInterface " << connectionProperties[keys::NameOfInterface][0] << std::endl;
147
148
        }
      }
149
150
151
152
153
154
      if ((this->m_ComponentSelectorContainer[name]->HasMultipleComponents() == false) && (this->m_ComponentSelectorContainer[name]->GetComponent().IsNull()))
      {
        std::stringstream msg;
        msg << "Too many criteria for Component " << name << std::endl;
        std::runtime_error::runtime_error(msg.str());
      }
155
156
    }

157
    return;
158
  }
159
160
  bool Overlord::ConnectComponents()
  {
161
    bool isAllSuccess = true;
162

163
164
    Blueprint::ComponentNamesType componentNames = this->m_Blueprint->GetComponentNames();
    for (auto const & name : componentNames)
165
    {
166
      for (auto const & outgoingName : this->m_Blueprint->GetOutputNames(name))
167
168
169
      {
        //TODO check direction upstream/downstream input/output source/target
        //TODO GetComponent returns NULL if possible components !=1 we can check for that, but Overlord::UpdateSelectors() does something similar.
170
171
172
173
174
        ComponentBase::Pointer sourceComponent = this->m_ComponentSelectorContainer[name]->GetComponent();
        ComponentBase::Pointer targetComponent = this->m_ComponentSelectorContainer[outgoingName]->GetComponent();

        Blueprint::ParameterMapType connectionProperties = this->m_Blueprint->GetConnection(name, outgoingName);
        int numberOfConnections = 0;
175
        if (connectionProperties.count(keys::NameOfInterface) > 0)
176
177
        {
          // connect only via interfaces provided by user configuration
178
          for (auto const & interfaceName : connectionProperties[keys::NameOfInterface])
179
          {
180
            numberOfConnections += (targetComponent->AcceptConnectionFrom(interfaceName.c_str(), sourceComponent) == ComponentBase::interfaceStatus::success ? 1: 0);
181
182
183
184
185
186
187
188
          }
        }
        else
        {
          // connect via all possible interfaces
          numberOfConnections = targetComponent->AcceptConnectionFrom(sourceComponent);
        }

189
190
        if (numberOfConnections == 0)
        {
191
          isAllSuccess = false;
192
          std::cout << "Warning: a connection from " << name << " to " << outgoingName << " was specified, but no compatible interfaces were found." << std::endl;
193
        }
194
195
      }
    }
196
    return isAllSuccess;
197
  }
198
199
200
201
202
  Overlord::SourceInterfaceMapType Overlord::GetSourceInterfaces()
  {
    /** Scans all Components to find those with Sourcing capability and store them in SourceComponents list */
    
    SourceInterfaceMapType sourceInterfaceMap;
203
    for (const auto & componentSelector : this->m_ComponentSelectorContainer)
204
205
    {
      ComponentBase::Pointer component = componentSelector.second->GetComponent();
206
      if (component->MeetsCriterionBase({ keys::HasProvidingInterface, { keys::SourceInterface } }))
207
      {
208
        SourceInterface* provingSourceInterface = dynamic_cast<SourceInterface*> (component.GetPointer());
209
210
        if (provingSourceInterface == nullptr) // is actually a double-check for sanity: based on criterion cast should be successful
        {
211
          std::runtime_error::runtime_error("dynamic_cast<SourceInterface*> fails, but based on component criterion it shouldn't");
212
213
214
215
216
217
218
219
220
221
222
        }
        sourceInterfaceMap[componentSelector.first]=provingSourceInterface;
        
      }
    }
    return sourceInterfaceMap;
  }

  Overlord::SinkInterfaceMapType Overlord::GetSinkInterfaces()
  {
    /** Scans all Components to find those with Sinking capability and store them in SinkComponents list */
223
   
224
    SinkInterfaceMapType sinkInterfaceMap;
225
    for (auto const & componentSelector : this->m_ComponentSelectorContainer)
226
227
    {
      ComponentBase::Pointer component = componentSelector.second->GetComponent();
228
      if (component->MeetsCriterionBase({ keys::HasProvidingInterface, { keys::SinkInterface } }))
229
      {
230
        SinkInterface* provingSinkInterface = dynamic_cast<SinkInterface*> (component.GetPointer());
231
232
        if (provingSinkInterface == nullptr) // is actually a double-check for sanity: based on criterion cast should be successful
        {
233
          std::runtime_error::runtime_error("dynamic_cast<SinkInterface*> fails, but based on component criterion it shouldn't");
234
235
236
237
238
239
240
        }
        sinkInterfaceMap[componentSelector.first] = provingSinkInterface;
      }
    }
    return sinkInterfaceMap;
  }

241
  void Overlord::FindRunRegistration()
242
  {
243
    /** Scans all Components to find those with FindRunRegistration capability and store them in m_RunRegistrationComponents list */
244
    const CriterionType runRegistrationCriterion = CriterionType(keys::HasProvidingInterface, { "RunRegistrationInterface" });
245

246
    for (auto const & componentSelector : this->m_ComponentSelectorContainer)
247
    {
248
      ComponentBase::Pointer component = componentSelector.second->GetComponent();
249
      if (component->MeetsCriterionBase(runRegistrationCriterion))
250
251
252
253
254
      {
        this->m_RunRegistrationComponents->push_back(component);
      }
    }
  }
255

256
  void Overlord::FindAfterRegistration()
257
  {
258
    /** Scans all Components to find those with FindAfterRegistration capability and store them in m_AfterRegistrationComponents list */
259
    const CriterionType afterRegistrationCriterion = CriterionType(keys::HasProvidingInterface, { "AfterRegistrationInterface" });
260

261
    for (auto const & componentSelector : this->m_ComponentSelectorContainer)
262
    {
263
      ComponentBase::Pointer component = componentSelector.second->GetComponent();
264
      if (component->MeetsCriterionBase(afterRegistrationCriterion))
265
266
267
268
269
270
271
272
      {
        this->m_AfterRegistrationComponents->push_back(component);
      }
    }
  }

  

273
  void Overlord::RunRegistrations()
274
275
276
277
  {

    for (auto const & runRegistrationComponent : *(this->m_RunRegistrationComponents)) // auto&& preferred?
    {
278
      RunRegistrationInterface* providingRunRegistrationInterface = dynamic_cast<RunRegistrationInterface*> (runRegistrationComponent.GetPointer());
279
      if (providingRunRegistrationInterface == nullptr) // is actually a double-check for sanity: based on criterion cast should be successful
280
      {
281
        std::runtime_error::runtime_error("dynamic_cast<RunRegistrationInterface*> fails, but based on component criterion it shouldn't");
282
283
      }
      // For testing purposes, all Sources are connected to an ImageWriter
284
285
286
287
      providingRunRegistrationInterface->RunRegistration();
    }
  }

288
  void Overlord::AfterRegistrations()
289
290
291
292
  {

    for (auto const & afterRegistrationComponent : *(this->m_AfterRegistrationComponents)) // auto&& preferred?
    {
293
      AfterRegistrationInterface* providingAfterRegistrationInterface = dynamic_cast<AfterRegistrationInterface*> (afterRegistrationComponent.GetPointer());
294
295
      if (providingAfterRegistrationInterface == nullptr) // is actually a double-check for sanity: based on criterion cast should be successful
      {
296
        std::runtime_error::runtime_error("dynamic_cast<AfterRegistrationInterface*> fails, but based on component criterion it shouldn't");
297
298
299
      }
      // For testing purposes, all Sources are connected to an ImageWriter
      providingAfterRegistrationInterface->AfterRegistration();
300
301
    }
  }
302

303
  void Overlord::ReconnectTransforms()
304
305
  {
    /** Scans all Components to find those with ReconnectTransform capability and call them */
306
    const CriterionType criterion = CriterionType(keys::HasProvidingInterface, { "ReconnectTransformInterface" });
307

308
    for (auto const & componentSelector : this->m_ComponentSelectorContainer)
309
310
    {
      ComponentBase::Pointer component = componentSelector.second->GetComponent();
311
      if (component->MeetsCriterionBase(criterion))
312
313
314
315
      {
        ReconnectTransformInterface* providingInterface = dynamic_cast<ReconnectTransformInterface*> (component.GetPointer());
        if (providingInterface == nullptr) // is actually a double-check for sanity: based on criterion cast should be successful
        {
316
          std::runtime_error::runtime_error("dynamic_cast<ReconnectTransformInterface*> fails, but based on component criterion it shouldn't");
317
318
319
320
321
322
323
324
        }
        // For testing purposes, all Sources are connected to an ImageWriter
        providingInterface->ReconnectTransform();

      }
    }
  }

325
  void Overlord::Execute()
326
  {
327
    
328
329
330
331
    // TODO make one "update button" for the overlord
    this->FindRunRegistration();
    this->FindAfterRegistration();

332
333
334
335
    // RunRegistrations is a simple execution model
    // E.g.if the components are true itk Process Object, the don't need an 'Update' call. 
    // The container of RunRegistrationsInterfaces will therefore be empty, since they will not be added if they don't expose this interface.
    // If components need RunIterations() or RunResolution() we can explicitly 'Update' them here and control the flow.
336
337
    // TODO: see if signals-and-slots paradigm is appropriate here.

338
    this->RunRegistrations();
339
    this->ReconnectTransforms();
340
    this->AfterRegistrations();
341
    //update all writers...
342
    
343
  }
344
345
346
347
348
349

  AnyFileReader::Pointer Overlord::GetInputFileReader(const Overlord::ComponentNameType& inputName)
  {
    SourceInterfaceMapType sources = this->GetSourceInterfaces();
    if (sources.count(inputName) != 1)
    {
350
351
352
      std::stringstream msg;
      msg << "No Source component found by name:" << inputName;
      std::runtime_error::runtime_error(msg.str());
353
354
    }

355
    return sources[inputName]->GetInputFileReader();
356
357
358
359
360
361
362
  }
  
  AnyFileWriter::Pointer Overlord::GetOutputFileWriter(const Overlord::ComponentNameType& outputName)
  {
    SinkInterfaceMapType sinks = this->GetSinkInterfaces();
    if (sinks.count(outputName) != 1)
    {
363
364
365
      std::stringstream msg;
      msg << "No Sink component found by name : " << outputName;
      std::runtime_error::runtime_error(msg.str());
366
367
368
369
    }

    return sinks[outputName]->GetOutputFileWriter();
  }
370
371
372
373
374
375

  SinkInterface::DataObjectPointer Overlord::GetInitializedOutput(const Overlord::ComponentNameType& outputName)
  {
    SinkInterfaceMapType sinks = this->GetSinkInterfaces();
    if (sinks.count(outputName) != 1)
    {
376
377
378
      std::stringstream msg;
      msg << "No Sink component found by name : " << outputName;
      std::runtime_error::runtime_error(msg.str());
379
380
381
382
383
    }

    return sinks[outputName]->GetInitializedOutput();
  }

384
385
} // end namespace selx