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