PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusServerTest.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 "igsioCommon.h"
10 #include "vtkPlusDataSource.h"
12 #include "vtkPlusDataCollector.h"
14 #include "vtkIGSIOTransformRepository.h"
15 
16 // VTK includes
17 #include <vtkSmartPointer.h>
18 #include <vtksys/CommandLineArguments.hxx>
19 
20 // -------------------------------------------------
21 PlusStatus ConnectClients(int listeningPort, std::vector< vtkSmartPointer<vtkPlusOpenIGTLinkVideoSource> >& testClientList, int numberOfClientsToConnect, vtkSmartPointer<vtkXMLDataElement> configRootElement)
22 {
23  if (configRootElement == NULL)
24  {
25  LOG_ERROR("PlusServer client configuration is missing");
26  return PLUS_FAIL;
27  }
28 
29  int numberOfErrors = 0;
30 
31  // Clear test client list
32  testClientList.clear();
33 
34  for (int i = 0; i < numberOfClientsToConnect; ++i)
35  {
36  vtkSmartPointer<vtkPlusOpenIGTLinkVideoSource> client = vtkSmartPointer<vtkPlusOpenIGTLinkVideoSource>::New();
37  client->SetDeviceId("OpenIGTLinkVideoReceiveDevice");
38  client->ReadConfiguration(configRootElement);
39  client->SetDeviceId(std::string("OpenIGTLinkVideoReceiveDevice") + igsioCommon::ToString<int>(i));
40  client->SetServerAddress("127.0.0.1");
41  client->SetServerPort(listeningPort);
42  if (client->OutputChannelCount() == 0)
43  {
44  LOG_ERROR("No output channels in openIGTLink client.");
45  ++numberOfErrors;
46  continue;
47  }
48 
49  vtkPlusChannel* aChannel = *(client->GetOutputChannelsStart());
50  vtkPlusDataSource* aSource(NULL);
51  if (aChannel->GetVideoSource(aSource) != PLUS_SUCCESS)
52  {
53  LOG_ERROR("Unable to retrieve the video source.");
54  continue;
55  }
56  client->SetBufferSize(*aChannel, 10);
57  client->SetMessageType("TrackedFrame");
58  igsioTransformName name("Image", "Reference");
59  client->SetImageMessageEmbeddedTransformName(name);
60  aSource->SetInputImageOrientation(US_IMG_ORIENT_MF);
61 
62  if (client->Connect() != PLUS_SUCCESS)
63  {
64  LOG_ERROR("Client #" << i + 1 << " couldn't connect to server.");
65  ++numberOfErrors;
66  continue;
67  }
68 
69  LOG_DEBUG("Client #" << i + 1 << " successfully connected to server!");
70 
71  if (client->StartRecording() != PLUS_SUCCESS)
72  {
73  LOG_ERROR("Client #" << i + 1 << " couldn't start recording frames.");
74  client->Disconnect();
75  ++numberOfErrors;
76  continue;
77  }
78 
79  // Add connected client to list
80  testClientList.push_back(client);
81  }
82 
83  return (numberOfErrors == 0 ? PLUS_SUCCESS : PLUS_FAIL);
84 }
85 
86 // -------------------------------------------------
87 PlusStatus DisconnectClients(std::vector< vtkSmartPointer<vtkPlusOpenIGTLinkVideoSource> >& testClientList)
88 {
89  int numberOfErrors = 0;
90  for (unsigned int i = 0; i < testClientList.size(); ++i)
91  {
92  if (testClientList[i]->StopRecording() != PLUS_SUCCESS)
93  {
94  LOG_ERROR("Client #" << i + 1 << " failed to stop recording");
95  ++numberOfErrors;
96  }
97 
98  if (testClientList[i]->Disconnect() != PLUS_SUCCESS)
99  {
100  LOG_ERROR("Client #" << i + 1 << " failed to disconnect from server");
101  ++numberOfErrors;
102  continue;
103  }
104 
105  LOG_DEBUG("Client #" << i + 1 << " successfully disconnected from server!");
106  }
107 
108  return (numberOfErrors == 0 ? PLUS_SUCCESS : PLUS_FAIL);
109 }
110 
111 // -------------------------------------------------
112 vtkSmartPointer<vtkPlusOpenIGTLinkServer> StartServer(const std::string& inputConfigFileName)
113 {
114  // Read main configuration file
115  std::string configFilePath = inputConfigFileName;
116  if (!vtksys::SystemTools::FileExists(configFilePath.c_str(), true))
117  {
118  configFilePath = vtkPlusConfig::GetInstance()->GetDeviceSetConfigurationPath(inputConfigFileName);
119  if (!vtksys::SystemTools::FileExists(configFilePath.c_str(), true))
120  {
121  LOG_ERROR("Reading device set configuration file failed: " << inputConfigFileName << " does not exist in the current directory or in " << vtkPlusConfig::GetInstance()->GetDeviceSetConfigurationDirectory());
122  exit(EXIT_FAILURE);
123  }
124  }
125  vtkSmartPointer<vtkXMLDataElement> configRootElement = vtkSmartPointer<vtkXMLDataElement>::Take(vtkXMLUtilities::ReadElementFromFile(configFilePath.c_str()));
126  if (configRootElement == NULL)
127  {
128  LOG_ERROR("Reading device set configuration file failed: syntax error in " << inputConfigFileName);
129  exit(EXIT_FAILURE);
130  }
131 
134 
135  // Print configuration file contents for debugging purposes
136  LOG_DEBUG("Device set configuration is read from file: " << inputConfigFileName);
137  std::ostringstream xmlFileContents;
138  igsioCommon::XML::PrintXML(xmlFileContents, vtkIndent(1), configRootElement);
139  LOG_DEBUG("Device set configuration file contents: " << std::endl << xmlFileContents.str());
140 
141  LOG_INFO("Server status: Reading configuration.");
142  // Create data collector instance
143  vtkSmartPointer<vtkPlusDataCollector> dataCollector = vtkSmartPointer<vtkPlusDataCollector>::New();
144  if (dataCollector->ReadConfiguration(configRootElement) != PLUS_SUCCESS)
145  {
146  LOG_ERROR("Datacollector failed to read configuration");
147  return nullptr;
148  }
149 
150  // Create transform repository instance
151  vtkSmartPointer<vtkIGSIOTransformRepository> transformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
152  if (transformRepository->ReadConfiguration(configRootElement) != PLUS_SUCCESS)
153  {
154  LOG_ERROR("Transform repository failed to read configuration");
155  return nullptr;
156  }
157 
158  LOG_INFO("Server status: Connecting to devices.");
159  if (dataCollector->Connect() != PLUS_SUCCESS)
160  {
161  LOG_ERROR("Datacollector failed to connect to devices");
162  return nullptr;
163  }
164 
165  if (dataCollector->Start() != PLUS_SUCCESS)
166  {
167  LOG_ERROR("Datacollector failed to start");
168  return nullptr;
169  }
170 
171  LOG_INFO("Server status: Starting servers.");
172  std::vector<vtkPlusOpenIGTLinkServer*> serverList;
173  for (int i = 0; i < configRootElement->GetNumberOfNestedElements(); ++i)
174  {
175  vtkXMLDataElement* serverElement = configRootElement->GetNestedElement(i);
176  if (STRCASECMP(serverElement->GetName(), "PlusOpenIGTLinkServer") != 0)
177  {
178  continue;
179  }
180 
181  // This is a PlusServer tag, let's create it
182  vtkSmartPointer<vtkPlusOpenIGTLinkServer> server = vtkSmartPointer<vtkPlusOpenIGTLinkServer>::New();
183  LOG_DEBUG("Initializing Plus OpenIGTLink server... ");
184  if (server->Start(dataCollector, transformRepository, serverElement, configFilePath) != PLUS_SUCCESS)
185  {
186  LOG_ERROR("Failed to start OpenIGTLink server");
187  break;
188  }
189  return server;
190  }
191 
192  return nullptr;
193 }
194 
195 //-----------------------------------------------------------------------------
196 int main(int argc, char** argv)
197 {
198  // Check command line arguments.
199  bool printHelp(false);
200  std::string inputConfigFileName;
201  std::string testingConfigFileName;
202  int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
203 
204  const double WAIT_TIME_SEC = 5.0;
205  const int NUM_TEST_CLIENTS = 5; // only if testing is enabled S
206 
207  vtksys::CommandLineArguments args;
208  args.Initialize(argc, argv);
209 
210  args.AddArgument("--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp, "Print this help.");
211  args.AddArgument("--server-config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputConfigFileName, "Name of the server configuration file.");
212  args.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
213  args.AddArgument("--testing-config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &testingConfigFileName, "Name of the testing configuration file");
214 
215  if (!args.Parse())
216  {
217  std::cerr << "Problem parsing arguments." << std::endl;
218  std::cout << "Help: " << args.GetHelp() << std::endl;
219  exit(EXIT_FAILURE);
220  }
221 
222  if (printHelp)
223  {
224  std::cout << args.GetHelp() << std::endl;
225  exit(EXIT_SUCCESS);
226  }
227 
228  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
229 
230  if (inputConfigFileName.empty())
231  {
232  LOG_ERROR("--server-config-file argument is required!");
233  std::cout << "Help: " << args.GetHelp() << std::endl;
234  exit(EXIT_FAILURE);
235  }
236 
237  if (testingConfigFileName.empty())
238  {
239  LOG_ERROR("--testing-config-file argument is required!");
240  std::cout << "Help: " << args.GetHelp() << std::endl;
241  exit(EXIT_FAILURE);
242  }
243 
244  LOG_INFO("Logging at level " << vtkPlusLogger::Instance()->GetLogLevel() << " (" << vtkPlusLogger::Instance()->GetLogLevelString() << ") to file: " << vtkPlusLogger::Instance()->GetLogFileName());
245 
246  // Start a server
247  vtkSmartPointer<vtkPlusOpenIGTLinkServer> server = StartServer(inputConfigFileName);
248  if (server == nullptr)
249  {
250  LOG_ERROR("Unable to start server.");
251  exit(EXIT_FAILURE);
252  }
253 
254  vtkSmartPointer<vtkXMLDataElement> configRootElement = vtkSmartPointer<vtkXMLDataElement>::New();
255  if (PlusXmlUtils::ReadDeviceSetConfigurationFromFile(configRootElement, testingConfigFileName.c_str()) == PLUS_FAIL)
256  {
257  LOG_ERROR("Unable to read test configuration from file " << testingConfigFileName.c_str());
258  exit(EXIT_FAILURE);
259  }
260 
261  std::vector< vtkSmartPointer<vtkPlusOpenIGTLinkVideoSource> > outTestClients;
262  if (ConnectClients(server->GetListeningPort(), outTestClients, NUM_TEST_CLIENTS, configRootElement) != PLUS_SUCCESS)
263  {
264  LOG_ERROR("Unable to connect clients to PlusServer!");
265  DisconnectClients(outTestClients);
266  exit(EXIT_FAILURE);
267  }
268  LOG_INFO("Clients are connected");
269 
270  const double commandQueuePollIntervalSec = 0.010;
271  const double startTime = vtkIGSIOAccurateTimer::GetSystemTime();
272  while (vtkIGSIOAccurateTimer::GetSystemTime() < startTime + WAIT_TIME_SEC)
273  {
274  server->ProcessPendingCommands();
275 
276  // Need to process messages while waiting because some devices (such as the vtkPlusWin32VideoSource2) require event processing
277  vtkIGSIOAccurateTimer::DelayWithEventProcessing(commandQueuePollIntervalSec);
278  }
279 
280  LOG_INFO("Requested testing time elapsed");
281 
282  // Make sure all the clients are still connected
283  unsigned int numOfActuallyConnectedClients = server->GetNumberOfConnectedClients();
284  if (numOfActuallyConnectedClients != outTestClients.size())
285  {
286  LOG_ERROR("Number of connected clients to PlusServer doesn't match the requirements ("
287  << numOfActuallyConnectedClients << " out of " << outTestClients.size() << ").");
288  DisconnectClients(outTestClients);
289  exit(EXIT_FAILURE);
290  }
291 
292  // Disconnect clients from server
293  LOG_INFO("Disconnecting clients...");
294  if (DisconnectClients(outTestClients) != PLUS_SUCCESS)
295  {
296  LOG_ERROR("Unable to disconnect clients from PlusServer!");
297  exit(EXIT_FAILURE);
298  }
299  LOG_INFO("Clients are disconnected");
300 
301  return EXIT_SUCCESS;
302 }
PlusStatus ConnectClients(int listeningPort, std::vector< vtkSmartPointer< vtkPlusOpenIGTLinkVideoSource > > &testClientList, int numberOfClientsToConnect, vtkSmartPointer< vtkXMLDataElement > configRootElement)
int main(int argc, char **argv)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
for i
#define PLUS_FAIL
Definition: PlusCommon.h:43
virtual PlusStatus SetInputImageOrientation(US_IMAGE_ORIENTATION imageOrientation)
static vtkPlusConfig * GetInstance()
PlusStatus DisconnectClients(std::vector< vtkSmartPointer< vtkPlusOpenIGTLinkVideoSource > > &testClientList)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
vtkSmartPointer< vtkPlusOpenIGTLinkServer > StartServer(const std::string &inputConfigFileName)
static vtkIGSIOLogger * Instance()
Contains an optional timestamped circular buffer containing the video images and a number of timestam...
std::string GetDeviceSetConfigurationPath(const std::string &subPath)
void SetDeviceSetConfigurationFileName(const std::string &aFilePath)
PlusStatus GetVideoSource(vtkPlusDataSource *&aVideoSource) const
void SetDeviceSetConfigurationData(vtkXMLDataElement *deviceSetConfigurationData)
static PlusStatus ReadDeviceSetConfigurationFromFile(vtkXMLDataElement *config, const char *filename)
Definition: PlusXmlUtils.h:23
vtkNew< vtkPlusOpenIGTLinkServer > server
Interface to a 3D positioning tool, video source, or generalized data stream.