PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusGenericSerialDevice.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 "PlusSerialLine.h"
10 #include "vtkPlusDataSource.h"
12 
13 // VTK includes
14 #include <vtkObjectFactory.h>
15 #include <vtkXMLDataElement.h>
16 #include <vtkXMLUtilities.h>
17 #include <vtksys/SystemTools.hxx>
18 
19 // STL includes
20 #include <deque>
21 
22 //----------------------------------------------------------------------------
23 
25 
26 //----------------------------------------------------------------------------
27 // Define command strings
31 
32 //----------------------------------------------------------------------------
33 void bin2hex(const std::string& inputBinary, std::string& outputHexEncoded)
34 {
35  std::stringstream ss;
36  ss << std::hex << std::setfill('0');
37  for (unsigned int i = 0; i < inputBinary.size(); i++)
38  {
39  if (i > 0)
40  {
41  ss << " ";
42  }
43  ss << std::setw(2) << int(inputBinary[i]);
44  }
45  outputHexEncoded = ss.str();
46 }
47 
48 //----------------------------------------------------------------------------
49 void hex2bin(const std::string& inputHexEncoded, std::string& outputBinary)
50 {
51  outputBinary.clear();
52  int i;
53  std::stringstream ss(inputHexEncoded);
54  while (ss >> std::hex >> i)
55  {
56  outputBinary += static_cast<char>(i);
57  }
58 }
59 
60 //----------------------------------------------------------------------------
62  : Serial(new SerialLine())
63  , SerialPort(1)
64  , BaudRate(9600)
65  , DTR(false)
66  , RTS(false)
67  , MaximumReplyDelaySec(0.100)
68  , MaximumReplyDurationSec(0.300)
69  , Mutex(vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New())
70  , FrameNumber(0)
71  , FieldDataSource(nullptr)
72 {
73  // By default use CR as line ending (13, 0x0D)
74  this->SetLineEnding("0d");
75 
76  // No callback function provided by the device, so the data capture thread will be used to poll the hardware and add new items to the buffer
77  this->StartThreadForInternalUpdates = true;
78  this->AcquisitionRate = 10;
79 }
80 
81 //-------------------------------------------------------------------------
83 {
84  if (this->Recording)
85  {
86  this->StopRecording();
87  }
88 
89  if (this->Serial->IsHandleAlive())
90  {
91  this->Serial->Close();
92  delete this->Serial;
93  this->Serial = NULL;
94  }
95 }
96 
97 //-------------------------------------------------------------------------
98 void vtkPlusGenericSerialDevice::PrintSelf(ostream& os, vtkIndent indent)
99 {
100  Superclass::PrintSelf(os, indent);
101 }
102 
103 //-------------------------------------------------------------------------
105 {
106  LOG_TRACE("vtkPlusGenericSerialDevice::Connect");
107 
108  if (this->Serial->IsHandleAlive())
109  {
110  LOG_ERROR("Already connected to serial port");
111  return PLUS_FAIL;
112  }
113 
114  // COM port name format is different for port number under/over 10 (see Microsoft KB115831)
115  // Port number<10: COMn
116  // Port number>=10: \\.\COMn
117  std::ostringstream strComPort;
118  if (this->SerialPort < 10)
119  {
120  strComPort << "COM" << this->SerialPort;
121  }
122  else
123  {
124  strComPort << "\\\\.\\COM" << this->SerialPort;
125  }
126  this->Serial->SetPortName(strComPort.str());
127 
128  this->Serial->SetSerialPortSpeed(this->BaudRate);
129 
130  this->Serial->SetMaxReplyTime(50); // msec
131 
132  if (!this->Serial->Open())
133  {
134  LOG_ERROR("Cannot open serial port " << strComPort.str());
135  return PLUS_FAIL;
136  }
137 
138  if (!this->Serial->IsHandleAlive())
139  {
140  LOG_ERROR("COM port handle is not alive " << strComPort.str());
141  return PLUS_FAIL;
142  }
143 
144  if (!this->SetDTR(this->DTR)) //re-set it in case we are reconnecting
145  {
146  LOG_ERROR("Could not re-establish DTR (data-terminal-ready) line ");
147  return PLUS_FAIL;
148  }
149 
150  if (!this->SetRTS(this->RTS)) //re-set it in case we are reconnecting
151  {
152  LOG_ERROR("Could not re-establish RTS (request-to-send) line ");
153  return PLUS_FAIL;
154  }
155 
156  return PLUS_SUCCESS;
157 }
158 
159 //-------------------------------------------------------------------------
161 {
162  LOG_TRACE("vtkPlusGenericSerialDevice::Disconnect");
163  this->StopRecording();
164 
165  this->Serial->Close();
166 
167  return PLUS_SUCCESS;
168 }
169 
170 //-------------------------------------------------------------------------
172 {
173  // Either update or send commands - but not simultaneously
174  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
175 
176  // Determine the maximum time to spend in the loop (acquisition time period, but maximum 1 sec)
177  double maxReadTimeSec = (this->AcquisitionRate < 1.0) ? 1.0 : 1 / this->AcquisitionRate;
178  double startTime = vtkIGSIOAccurateTimer::GetSystemTime();
179  while (this->Serial->GetNumberOfBytesAvailableForReading() > 0)
180  {
181  std::string textReceived;
182  ReceiveResponse(textReceived);
183  LOG_DEBUG("Received from serial device without request: " << textReceived);
184  if (vtkIGSIOAccurateTimer::GetSystemTime() - startTime > maxReadTimeSec)
185  {
186  // force exit from the loop if continuously receiving data
187  break;
188  }
189  }
190  return PLUS_SUCCESS;
191 }
192 
193 //-------------------------------------------------------------------------
195 {
196  // Either update or send commands - but not simultaneously
197  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
198 
199  PlusStatus retval;
200 
201  if (onOff == this->DTR)
202  {
203  return PLUS_SUCCESS; //already the desired value
204  }
205  else
206  {
207  retval = this->Serial->SetDTR(onOff);
208  if (retval == PLUS_SUCCESS)
209  {
210  this->DTR = onOff;
211  }
212  }
213 
214  return retval;
215 }
216 
217 //-------------------------------------------------------------------------
219 {
220  // Either update or send commands - but not simultaneously
221  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
222 
223  PlusStatus retval;
224 
225  if (onOff == this->RTS)
226  {
227  return PLUS_SUCCESS; //already the desired value
228  }
229  else
230  {
231  retval = this->Serial->SetRTS(onOff);
232  if (retval == PLUS_SUCCESS)
233  {
234  this->RTS = onOff;
235  }
236  }
237 
238  return retval;
239 }
240 
241 //-------------------------------------------------------------------------
243 {
244  // Either update or send commands - but not simultaneously
245  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
246  PlusStatus retval = this->Serial->GetDSR(onOff);
247  return retval;
248 }
249 
250 //-------------------------------------------------------------------------
252 {
253  // Either update or send commands - but not simultaneously
254  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
255  PlusStatus retval = this->Serial->GetCTS(onOff);
256  return retval;
257 }
258 
259 //-------------------------------------------------------------------------
260 PlusStatus vtkPlusGenericSerialDevice::SendText(const std::string& textToSend, std::string* textReceived, ReplyTermination acceptReply)
261 {
262  LOG_DEBUG("Send to Serial device: " << textToSend);
263 
264  // Either update or send commands - but not simultaneously
265  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
266 
267  // Write text
268  unsigned char packetLength = textToSend.size();
269  for (int i = 0; i < packetLength; i++)
270  {
271  this->Serial->Write(textToSend[i]);
272  }
273  // Write line ending
274  for (std::string::iterator lineEndingIt = this->LineEndingBin.begin(); lineEndingIt != this->LineEndingBin.end(); ++lineEndingIt)
275  {
276  this->Serial->Write(*lineEndingIt);
277  }
278  // Get response
279  if (textReceived != NULL)
280  {
281  textReceived->clear();
282  this->WaitForResponse();
283  while (this->Serial->GetNumberOfBytesAvailableForReading() > 0)
284  {
285  // a response is expected
286  std::string line;
287  if (this->ReceiveResponse(line, acceptReply) != PLUS_SUCCESS)
288  {
289  *textReceived += line;
290  break;
291  }
292  *textReceived += line;
293  }
294  LOG_DEBUG("Received from serial device: " << (*textReceived));
295  }
296  return PLUS_SUCCESS;
297 }
298 
299 //-------------------------------------------------------------------------
301 {
302  const int waitPeriodSec = 0.010;
303 
304  double startTime = vtkIGSIOAccurateTimer::GetSystemTime();
305  while (this->Serial->GetNumberOfBytesAvailableForReading() == 0)
306  {
307  if (vtkIGSIOAccurateTimer::GetSystemTime() - startTime > this->MaximumReplyDelaySec)
308  {
309  // waiting time expired
310  return false;
311  }
312  vtksys::SystemTools::Delay(waitPeriodSec * 1000);
313  }
314  // data available for reading
315  return true;
316 }
317 
318 //-------------------------------------------------------------------------
319 PlusStatus vtkPlusGenericSerialDevice::ReceiveResponse(std::string& textReceived, ReplyTermination acceptReply/*=REQUIRE_LINE_ENDING*/)
320 {
321  textReceived.clear();
322  double startTime = vtkIGSIOAccurateTimer::GetSystemTime();
323 
324  // Read the the response (until line ending is found or timeout)
325  unsigned int lineEndingLength = this->LineEndingBin.size();
326  unsigned char d = 0;
327  bool lineEndingFound = false;
328  do
329  {
330  while (!this->Serial->Read(d))
331  {
332  if (vtkIGSIOAccurateTimer::GetSystemTime() - startTime > this->MaximumReplyDurationSec)
333  {
334  // waiting time expired
335  if (acceptReply == REQUIRE_LINE_ENDING)
336  {
337  static vtkIGSIOLogHelper logHelper(60.0, 1e6);
338  CUSTOM_RETURN_WITH_FAIL_IF(true, "Failed to get a proper response within configured time (" << this->MaximumReplyDurationSec << " sec)");
339  }
340  else if (acceptReply == REQUIRE_NOT_EMPTY && textReceived.empty())
341  {
342  RETURN_WITH_FAIL_IF(true, "Failed to read a complete line from serial device. Received: " << textReceived);
343  }
344  else
345  {
346  return PLUS_SUCCESS;
347  }
348  }
349  }
350  textReceived.push_back(d);
351  lineEndingFound = textReceived.size() >= lineEndingLength
352  && (this->LineEndingBin.compare(textReceived.substr(textReceived.size() - lineEndingLength, lineEndingLength)) == 0);
353 
354  }
355  while (!lineEndingFound);
356 
357  // Remove line ending
358  textReceived.erase(textReceived.size() - lineEndingLength, lineEndingLength);
359 
360  // Store in frame
361  if (this->FieldDataSource != nullptr)
362  {
363  igsioFieldMapType fieldMap;
364  fieldMap[this->FieldDataSource->GetId()].first = FRAMEFIELD_NONE;
365  fieldMap[this->FieldDataSource->GetId()].second = textReceived;
366  this->FieldDataSource->AddItem(fieldMap, this->FrameNumber);
367  this->FrameNumber++;
368  }
369 
370  return PLUS_SUCCESS;
371 }
372 
373 //----------------------------------------------------------------------------
375 {
377  {
378  this->FieldDataSource = this->Fields.begin()->second;
379  }
380 
381  return PLUS_SUCCESS;
382 }
383 
384 //----------------------------------------------------------------------------
385 PlusStatus vtkPlusGenericSerialDevice::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
386 {
387  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
388  XML_READ_SCALAR_ATTRIBUTE_REQUIRED(unsigned long, SerialPort, deviceConfig);
389  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(unsigned long, BaudRate, deviceConfig);
390  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, MaximumReplyDelaySec, deviceConfig);
391  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, MaximumReplyDurationSec, deviceConfig);
392  XML_READ_CSTRING_ATTRIBUTE_OPTIONAL(LineEnding, deviceConfig);
393  return PLUS_SUCCESS;
394 }
395 
396 //----------------------------------------------------------------------------
397 PlusStatus vtkPlusGenericSerialDevice::WriteConfiguration(vtkXMLDataElement* rootConfigElement)
398 {
399  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
400  deviceConfig->SetUnsignedLongAttribute("SerialPort", this->SerialPort);
401  deviceConfig->SetUnsignedLongAttribute("BaudRate", this->BaudRate);
402  deviceConfig->SetDoubleAttribute("MaximumReplyDelaySec", this->MaximumReplyDelaySec);
403  deviceConfig->SetDoubleAttribute("MaximumReplyDurationSec", this->MaximumReplyDurationSec);
404  deviceConfig->SetAttribute("LineEnding", this->LineEnding.c_str());
405  return PLUS_SUCCESS;
406 }
407 
408 //----------------------------------------------------------------------------
409 void vtkPlusGenericSerialDevice::SetLineEnding(const char* lineEndingHex)
410 {
411  this->LineEnding = lineEndingHex;
412  hex2bin(this->LineEnding, this->LineEndingBin);
413 }
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
PlusStatus SetRTS(bool onOff)
int Write(const BYTE *data, int numberOfBytesToWrite)
const char int line
Definition: phidget22.h:2458
static const char * SERIAL_COMMAND_GET_CTS
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
void SetMaxReplyTime(int maxreply)
int
Definition: phidget22.h:3069
igsioStatus PlusStatus
Definition: PlusCommon.h:40
int Read(BYTE *data, int maxNumberOfBytesToRead)
virtual PlusStatus AddItem(vtkImageData *frame, US_IMAGE_ORIENTATION usImageOrientation, US_IMAGE_TYPE imageType, long frameNumber, double unfilteredTimestamp=UNDEFINED_TIMESTAMP, double filteredTimestamp=UNDEFINED_TIMESTAMP, const igsioFieldMapType *customFields=NULL)
for i
double AcquisitionRate
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
void bin2hex(const std::string &inputBinary, std::string &outputHexEncoded)
#define PLUS_FAIL
Definition: PlusCommon.h:43
void SetLineEnding(const char *lineEndingHex)
Class for reading and writing data through the serial (RS-232) port.
DataSourceContainer Fields
void hex2bin(const std::string &inputHexEncoded, std::string &outputBinary)
void PrintSelf(ostream &os, vtkIndent indent)
static const char * SERIAL_COMMAND_GET_RTS
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
vtkStandardNewMacro(vtkPlusGenericSerialDevice)
virtual PlusStatus StopRecording()
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
PlusStatus SetDTR(bool onOff)
PlusStatus GetCTS(bool &onOff)
bool StartThreadForInternalUpdates
unsigned int GetNumberOfBytesAvailableForReading() const
vtkSmartPointer< vtkIGSIORecursiveCriticalSection > Mutex
static const char * SERIAL_COMMAND_SET_RTS
virtual PlusStatus ReceiveResponse(std::string &textReceived, ReplyTermination acceptReply=REQUIRE_LINE_ENDING)
virtual PlusStatus SendText(const std::string &textToSend, std::string *textReceived=NULL) VTK_OVERRIDE
DataSourceContainerConstIterator GetFieldDataSourcessIteratorEnd() const
Generic interface for communicating with a serial device.
DataSourceContainerConstIterator GetFieldDataSourcessIteratorBegin() const
PlusStatus GetDSR(bool &onOff)
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)
void SetSerialPortSpeed(DWORD speed)
bool IsHandleAlive() const