PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusCommandProcessor.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 // Local includes
8 #include "PlusConfigure.h"
9 #include "vtkIGSIORecursiveCriticalSection.h"
11 
12 // Command includes
13 #include "vtkPlusCommand.h"
14 #include "vtkPlusGetImageCommand.h"
16 #ifdef PLUS_USE_STEALTHLINK
18 #endif
19 #ifdef PLUS_USE_CLARIUS
20 #include "vtkPlusClariusCommand.h"
21 #endif
22 #ifdef PLUS_USE_OPTIMET_CONOPROBE
24 #endif
25 #ifdef PLUS_USE_ATRACSYS
26  #include "vtkPlusAtracsysCommand.h"
27 #endif
28 #ifdef PLUS_USE_CAPISTRANO_VIDEO
30 #endif
31 #ifdef PLUS_USE_WINPROBE_VIDEO
32  #include "vtkPlusWinProbeCommand.h"
33 #endif
34 #ifdef PLUS_USE_USDIGITALENCODERS_TRACKER
36 #endif
37 
38 
47 #include "vtkPlusSendTextCommand.h"
51 #include "vtkPlusVersionCommand.h"
52 
53 // IGTL includes
54 #include "igtl_header.h"
55 
56 // VTK includes
57 #include <vtkImageData.h>
58 #include <vtkMatrix4x4.h>
59 #include <vtkObjectFactory.h>
60 #include <vtkXMLUtilities.h>
61 
63 
64 //----------------------------------------------------------------------------
66  : PlusServer(NULL)
67  , Threader(vtkSmartPointer<vtkMultiThreader>::New())
68  , Mutex(vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New())
69  , CommandExecutionActive(std::make_pair(false, false))
70  , CommandExecutionThreadId(-1)
71 {
72  // Register default commands
73  RegisterPlusCommand(vtkSmartPointer<vtkPlusGetImageCommand>::New());
74  RegisterPlusCommand(vtkSmartPointer<vtkPlusGetPolydataCommand>::New());
75  RegisterPlusCommand(vtkSmartPointer<vtkPlusGetTransformCommand>::New());
76  RegisterPlusCommand(vtkSmartPointer<vtkPlusReconstructVolumeCommand>::New());
77  RegisterPlusCommand(vtkSmartPointer<vtkPlusRequestIdsCommand>::New());
78  RegisterPlusCommand(vtkSmartPointer<vtkPlusSaveConfigCommand>::New());
79  RegisterPlusCommand(vtkSmartPointer<vtkPlusSendTextCommand>::New());
80  RegisterPlusCommand(vtkSmartPointer<vtkPlusStartStopRecordingCommand>::New());
81  RegisterPlusCommand(vtkSmartPointer<vtkPlusUpdateTransformCommand>::New());
82  RegisterPlusCommand(vtkSmartPointer<vtkPlusVersionCommand>::New());
83  RegisterPlusCommand(vtkSmartPointer<vtkPlusSetUsParameterCommand>::New());
84  RegisterPlusCommand(vtkSmartPointer<vtkPlusGetUsParameterCommand>::New());
85  RegisterPlusCommand(vtkSmartPointer<vtkPlusAddRecordingDeviceCommand>::New());
86  RegisterPlusCommand(vtkSmartPointer<vtkPlusGenericSerialCommand>::New());
87  RegisterPlusCommand(vtkSmartPointer<vtkPlusGetFrameRateCommand>::New());
88 #ifdef PLUS_USE_CAPISTRANO_VIDEO
89  RegisterPlusCommand(vtkSmartPointer<vtkPlusCapistranoCommand>::New());
90 #endif
91 #ifdef PLUS_USE_STEALTHLINK
92  RegisterPlusCommand(vtkSmartPointer<vtkPlusStealthLinkCommand>::New());
93 #endif
94 #ifdef PLUS_USE_CLARIUS
95  RegisterPlusCommand(vtkSmartPointer<vtkPlusClariusCommand>::New());
96 #endif
97 #ifdef PLUS_USE_OPTIMET_CONOPROBE
98  RegisterPlusCommand(vtkSmartPointer<vtkPlusConoProbeLinkCommand>::New());
99 #endif
100 #ifdef PLUS_USE_ATRACSYS
101  RegisterPlusCommand(vtkSmartPointer<vtkPlusAtracsysCommand>::New());
102 #endif
103 #ifdef PLUS_USE_WINPROBE_VIDEO
104  RegisterPlusCommand(vtkSmartPointer<vtkPlusWinProbeCommand>::New());
105 #endif
106 #ifdef PLUS_USE_USDIGITALENCODERS_TRACKER
107  RegisterPlusCommand(vtkSmartPointer<vtkPlusUSDigitalEncoderCommand>::New());
108 #endif
109 }
110 
111 //----------------------------------------------------------------------------
113 {
114  SetPlusServer(NULL);
115 
116  for (auto& kv : this->RegisteredCommands)
117  {
118  kv.second->UnRegister(this);
119  }
120 }
121 
122 //----------------------------------------------------------------------------
123 void vtkPlusCommandProcessor::PrintSelf(ostream& os, vtkIndent indent)
124 {
125  this->Superclass::PrintSelf(os, indent);
126  os << indent << "Registered commands: ";
127  for (auto iter = this->RegisteredCommands.begin(); iter != this->RegisteredCommands.end(); ++iter)
128  {
129  os << indent << " " << iter->first << std::endl;
130  }
131 }
132 
133 //----------------------------------------------------------------------------
135 {
136  if (this->CommandExecutionThreadId < 0)
137  {
138  this->CommandExecutionActive.first = true;
139  this->CommandExecutionThreadId = this->Threader->SpawnThread((vtkThreadFunctionType)&CommandExecutionThread, this);
140  }
141  return PLUS_SUCCESS;
142 }
143 
144 //----------------------------------------------------------------------------
146 {
147  // Stop the command execution thread
148  if (this->CommandExecutionThreadId >= 0)
149  {
150  this->CommandExecutionActive.first = false;
151  while (this->CommandExecutionActive.second)
152  {
153  // Wait until the thread stops
154  vtkIGSIOAccurateTimer::Delay(0.2);
155  }
156  this->CommandExecutionThreadId = -1;
157  }
158 
159  LOG_DEBUG("Command execution thread stopped");
160 
161  return PLUS_SUCCESS;
162 }
163 
164 //----------------------------------------------------------------------------
165 void* vtkPlusCommandProcessor::CommandExecutionThread(vtkMultiThreader::ThreadInfo* data)
166 {
168 
169  self->CommandExecutionActive.second = true;
170 
171  // Execute commands until a stop is requested
172  while (self->CommandExecutionActive.first)
173  {
174  self->ExecuteCommands();
175  // no commands in the queue, wait a bit before checking again
176  const double commandQueuePollIntervalSec = 0.010;
177 #ifdef _WIN32
178  Sleep(commandQueuePollIntervalSec * 1000);
179 #else
180  usleep(commandQueuePollIntervalSec * 1000000);
181 #endif
182  }
183 
184  // Close thread
185  self->CommandExecutionThreadId = -1;
186  self->CommandExecutionActive.second = false;
187  return NULL;
188 }
189 
190 //----------------------------------------------------------------------------
192 {
193  // Implemented in a while loop to not block the mutex during command execution, only during management of the queue.
194  int numberOfExecutedCommands(0);
195  while (1)
196  {
197  vtkSmartPointer<vtkPlusCommand> cmd; // next command to be processed
198  {
199  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
200  if (this->CommandQueue.empty())
201  {
202  return numberOfExecutedCommands;
203  }
204  cmd = this->CommandQueue.front();
205  this->CommandQueue.pop_front();
206  }
207 
208  LOG_DEBUG("Executing command");
209  if (cmd->Execute() != PLUS_SUCCESS)
210  {
211  LOG_ERROR("Command execution failed");
212  }
213 
214  // move the response objects from the command to the processor's queue
215  {
216  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
217  cmd->PopCommandResponses(this->CommandResponseQueue);
218  }
219 
220  numberOfExecutedCommands++;
221  }
222 
223  // we never actually reach this point
224  return numberOfExecutedCommands;
225 }
226 
227 //----------------------------------------------------------------------------
229 {
230  if (cmd == NULL)
231  {
232  LOG_ERROR("vtkPlusCommandProcessor::RegisterPlusCommand received an invalid command object");
233  return PLUS_FAIL;
234  }
235 
236  std::list<std::string> cmdNames;
237  cmd->GetCommandNames(cmdNames);
238  if (cmdNames.empty())
239  {
240  LOG_ERROR("Cannot register command: command name is empty");
241  return PLUS_FAIL;
242  }
243 
244  for (std::list<std::string>::iterator nameIt = cmdNames.begin(); nameIt != cmdNames.end(); ++nameIt)
245  {
246  this->RegisteredCommands[*nameIt] = cmd;
247  cmd->Register(this);
248  }
249  return PLUS_SUCCESS;
250 }
251 
252 //----------------------------------------------------------------------------
253 vtkPlusCommand* vtkPlusCommandProcessor::CreatePlusCommand(const std::string& commandName, const std::string& commandStr, const igtl::MessageBase::MetaDataMap& metaData)
254 {
255  vtkSmartPointer<vtkXMLDataElement> cmdElement = vtkSmartPointer<vtkXMLDataElement>::Take(vtkXMLUtilities::ReadElementFromString(commandStr.c_str()));
256  if (cmdElement.GetPointer() == NULL)
257  {
258  LOG_ERROR("failed to parse XML command string (received: " + commandStr + ")");
259  return NULL;
260  }
261 
262  if (STRCASECMP(cmdElement->GetName(), "Command") != 0)
263  {
264  LOG_ERROR("Command element expected (received: " + commandStr + ")");
265  return NULL;
266  }
267 
268  if (this->RegisteredCommands.find(commandName) == this->RegisteredCommands.end())
269  {
270  // unregistered command
271  LOG_ERROR("Unknown command: " << commandName);
272  return NULL;
273  }
274 
275  vtkPlusCommand* cmd = (this->RegisteredCommands[commandName])->Clone();
276  cmd->SetMetaData(metaData);
277  if (cmd->ReadConfiguration(cmdElement) != PLUS_SUCCESS)
278  {
279  cmd->Delete();
280  cmd = NULL;
281  LOG_ERROR("Failed to initialize command from string: " + commandStr);
282  return NULL;
283  }
284  return cmd;
285 }
286 
287 //------------------------------------------------------------------------------
288 PlusStatus vtkPlusCommandProcessor::QueueCommand(bool respondUsingIGTLCommand, unsigned int clientId, const std::string& commandName, const std::string& commandString, const std::string& deviceName, uint32_t uid, const igtl::MessageBase::MetaDataMap& metaData)
289 {
290  if (commandString.empty())
291  {
292  LOG_ERROR("Command string is undefined");
293  return PLUS_FAIL;
294  }
295 
296  if (commandName.empty())
297  {
298  LOG_ERROR("Command name is undefined");
299  return PLUS_FAIL;
300  }
301 
302  vtkSmartPointer<vtkPlusCommand> cmd = vtkSmartPointer<vtkPlusCommand>::Take(CreatePlusCommand(commandName, commandString, metaData));
303  if (cmd.GetPointer() == NULL)
304  {
305  if (!respondUsingIGTLCommand)
306  {
307  this->QueueStringResponse(PLUS_FAIL, deviceName, clientId, std::string("Error attempting to process command."));
308  }
309  else
310  {
311  this->QueueCommandResponse(PLUS_FAIL, deviceName, clientId, commandName, uid, "Error attempting to process command.", "Unknown command type requested.");
312  }
313  return PLUS_FAIL;
314  }
315 
316  cmd->SetCommandProcessor(this);
317  cmd->SetClientId(clientId);
318  cmd->SetDeviceName(deviceName.c_str());
319  cmd->SetId(uid);
320  cmd->SetRespondWithCommandMessage(respondUsingIGTLCommand);
321 
322  // Add command to the execution queue
323  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
324  this->CommandQueue.push_back(cmd);
325 
326  return PLUS_SUCCESS;
327 }
328 
329 //----------------------------------------------------------------------------
330 PlusStatus vtkPlusCommandProcessor::QueueStringResponse(PlusStatus status, const std::string& deviceName, unsigned int clientId, const std::string& replyString)
331 {
332  vtkSmartPointer<vtkPlusCommandStringResponse> response = vtkSmartPointer<vtkPlusCommandStringResponse>::New();
333  response->SetDeviceName(deviceName);
334  std::ostringstream replyStr;
335  replyStr << "<CommandReply";
336  replyStr << " Status=\"" << (status == PLUS_SUCCESS ? "SUCCESS" : "FAIL") << "\"";
337  replyStr << " Message=\"";
338  // Write to XML, encoding special characters, such as " ' \ < > &
339  vtkXMLUtilities::EncodeString(replyString.c_str(), VTK_ENCODING_NONE, replyStr, VTK_ENCODING_NONE, 1 /* encode special characters */);
340  replyStr << "\"";
341  replyStr << " />";
342 
343  response->SetMessage(replyStr.str());
344  response->SetClientId(clientId);
345  response->SetStatus(status);
346 
347  // Add response to the command response queue
348  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
349  this->CommandResponseQueue.push_back(response);
350 
351  return PLUS_SUCCESS;
352 }
353 
354 //----------------------------------------------------------------------------
355 PlusStatus vtkPlusCommandProcessor::QueueCommandResponse(PlusStatus status, const std::string& deviceName, unsigned int clientId, const std::string& commandName, uint32_t uid, const std::string& replyString, const std::string& errorString)
356 {
357  vtkSmartPointer<vtkPlusCommandRTSCommandResponse> response = vtkSmartPointer<vtkPlusCommandRTSCommandResponse>::New();
358  response->SetClientId(clientId);
359  response->SetDeviceName(deviceName);
360  response->SetOriginalId(uid);
361  response->SetRespondWithCommandMessage(true);
362  response->SetErrorString(errorString);
363  response->SetStatus(status);
364 
365  // Add response to the command response queue
366  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
367  this->CommandResponseQueue.push_back(response);
368 
369  return PLUS_SUCCESS;
370 }
371 
372 //------------------------------------------------------------------------------
373 PlusStatus vtkPlusCommandProcessor::QueueGetImageMetaData(unsigned int clientId, const std::string& deviceName)
374 {
375  vtkSmartPointer<vtkPlusGetImageCommand> cmdGetImage = vtkSmartPointer<vtkPlusGetImageCommand>::New();
376  cmdGetImage->SetCommandProcessor(this);
377  cmdGetImage->SetClientId(clientId);
378  cmdGetImage->SetDeviceName(deviceName.c_str());
379  cmdGetImage->SetNameToGetImageMeta();
380  cmdGetImage->SetImageId(deviceName.c_str());
381  {
382  // Add command to the execution queue
383  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
384  this->CommandQueue.push_back(cmdGetImage);
385  }
386  return PLUS_SUCCESS;
387 }
388 
389 //------------------------------------------------------------------------------
390 PlusStatus vtkPlusCommandProcessor::QueueGetImage(unsigned int clientId, const std::string& deviceName)
391 {
392  vtkSmartPointer<vtkPlusGetImageCommand> cmdGetImage = vtkSmartPointer<vtkPlusGetImageCommand>::New();
393  cmdGetImage->SetCommandProcessor(this);
394  cmdGetImage->SetClientId(clientId);
395  cmdGetImage->SetDeviceName(deviceName.c_str());
396  cmdGetImage->SetNameToGetImage();
397  cmdGetImage->SetImageId(deviceName.c_str());
398  {
399  // Add command to the execution queue
400  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
401  this->CommandQueue.push_back(cmdGetImage);
402  }
403  return PLUS_SUCCESS;
404 }
405 
406 //------------------------------------------------------------------------------
408 {
409  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
410  // Add reply to the sending queue
411  // Append this->CommandResponses to 'responses'.
412  // Elements appended to 'responses' are removed from this->CommandResponses.
413  responses.splice(responses.end(), this->CommandResponseQueue, this->CommandResponseQueue.begin(), this->CommandResponseQueue.end());
414 }
415 
416 //------------------------------------------------------------------------------
418 {
419  return this->CommandExecutionActive.second;
420 }
const uint32_t * data
Definition: phidget22.h:3971
vtkPlusCommand * CreatePlusCommand(const std::string &commandName, const std::string &commandStr, const igtl::MessageBase::MetaDataMap &metaData)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
This is an abstract superclass for commands in the OpenIGTLink network interface for Plus.
const char ** errorString
Definition: phidget22.h:1270
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *aConfig)
PlusStatus QueueGetImage(unsigned int clientId, const std::string &deviceName)
Creates a PlusCommand from a string. If the commands are to be executed on the main thread then call ...
static void * CommandExecutionThread(vtkMultiThreader::ThreadInfo *data)
#define PLUS_FAIL
Definition: PlusCommon.h:43
virtual PlusStatus QueueStringResponse(PlusStatus status, const std::string &deviceName, unsigned int clientId, const std::string &replyString)
vtkStandardNewMacro(vtkPlusCommandProcessor)
void SetMetaData(const igtl::MessageBase::MetaDataMap &metaData)
virtual void GetCommandNames(std::list< std::string > &cmdNames)=0
iter
Definition: algo3.m:29
virtual PlusStatus RegisterPlusCommand(vtkPlusCommand *cmd)
const char ** deviceName
Definition: phidget22.h:1316
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual PlusStatus QueueCommandResponse(PlusStatus status, const std::string &deviceName, unsigned int clientId, const std::string &commandName, uint32_t uid, const std::string &replyString, const std::string &errorString)
PlusStatus QueueGetImageMetaData(unsigned int clientId, const std::string &deviceName)
virtual void SetPlusServer(vtkPlusOpenIGTLinkServer *)
std::list< vtkSmartPointer< vtkPlusCommandResponse > > PlusCommandResponseList
virtual PlusStatus QueueCommand(bool respondUsingIGTLCommand, unsigned int clientId, const std::string &commandName, const std::string &commandString, const std::string &deviceName, uint32_t uid, const igtl::MessageBase::MetaDataMap &metaData)
virtual void PopCommandResponses(PlusCommandResponseList &responses)