PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusGenericSensorTracker.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 #include "PlusConfigure.h"
9 
10 // Local includes
11 #include "vtkPlusChannel.h"
12 #include "vtkPlusDataSource.h"
13 
14 // VTK includes
15 #include <vtkMatrix4x4.h>
16 #include <vtkTransform.h>
17 
18 // Windows sensor api includes
19 #include <SensorsApi.h>
20 #include <Sensors.h>
21 #include <PortableDeviceTypes.h>
22 #include <stringapiset.h>
23 
24 // Stl includes
25 #include <string>
26 #include <memory>
27 #include <vector>
28 #include <bitset>
29 #include <array>
30 #include <algorithm>
31 
32 //----------------------------------------------------------------------------
34 
35 namespace
36 {
37  //
38  // C++ memory management helpers
39  //
40 
41  template <typename T>
42  struct DefaultDeletor
43  {
44  void
45  operator()(T* t)
46  {
47  if (t)
48  {
49  t->Release();
50  }
51  }
52  };
53 
54  template <typename T>
55  using UniquePtrType = std::unique_ptr<T, DefaultDeletor<T>>;
56 
57  template <typename T>
58  auto createSafePointer(T* ptr)
59  {
60  UniquePtrType<T> safePtr;
61  safePtr.reset(ptr);
62 
63  return safePtr;
64  }
65 
66  // Rely on the fact the compiler is not allowed to optimize away
67  // an automatic object whose dtors or ctors has side effects
68  struct SafeComInitializer
69  {
70  SafeComInitializer()
71  {
72  CoInitialize(nullptr);
73  }
74  ~SafeComInitializer()
75  {
76  CoUninitialize();
77  }
78  };
79 
80  //
81  // Base internal types
82  //
83 
84  enum class SensorType
85  {
86  Accelerometer = 0,
87  Gyrometer,
88  Magnetometer // Enable in the future
89  };
90 
91  struct SensorStreamConfig
92  {
93  SensorType Type;
94  std::wstring FriendlyName;
95  std::wstring SerialNumber;
96  std::wstring Manufacturer;
97  std::wstring Description;
98  bool TrackerTimeToSystemTimeComputed{false};
99  double TrackerTimeToSystemTimeSec{};
100  UniquePtrType<ISensor> SensorHandle{nullptr};
101  IStream* UpdateThreadSensorMarshallingStream{nullptr};
102  UniquePtrType<ISensor> UpdateThreadSensorRef{nullptr};
103  vtkPlusDataSource* Tool{nullptr};
104  };
105 
106  struct SensorUserConfig
107  {
108  std::string ToolId;
109  std::string SerialNumber;
110  };
111 
112  //
113  // Windows API helpers
114  //
115 
116  std::string GetLastErrorAsString()
117  {
118  // Get the error message ID, if any.
119  DWORD errorMessageID = ::GetLastError();
120  if (errorMessageID == 0)
121  {
122  return std::string("No error message available");
123  }
124 
125  LPSTR messageBuffer{nullptr};
126  auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
127  NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
128 
129  std::string message(messageBuffer, size);
130  LocalFree(messageBuffer);
131 
132  return message;
133  }
134 
135  uint64_t GetSystemTimeInMs(SYSTEMTIME* systemTime)
136  {
137  FILETIME filetime;
138  SystemTimeToFileTime(systemTime, &filetime);
139 
140  return (static_cast<uint64_t>(filetime.dwLowDateTime) + (static_cast<uint64_t>(filetime.dwHighDateTime) << 32LL)) * 100 / 1000000;
141  }
142 
143  std::string ConvertWideStr2Str(const std::wstring& wstr)
144  {
145  auto size = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
146  std::vector<char> buf(size);
147  WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &buf[0], size, nullptr, nullptr);
148  return std::string(&buf[0]);
149  }
150 
151  //
152  // Type conversion
153  //
154 
155  REFSENSOR_TYPE_ID ConvertCustomTypeToWindowsType(SensorType type)
156  {
157  switch (type)
158  {
159  case SensorType::Accelerometer:
160  return SENSOR_TYPE_ACCELEROMETER_3D;
161  case SensorType::Gyrometer:
162  return SENSOR_TYPE_GYROMETER_3D;
163  case SensorType::Magnetometer:
164  return SENSOR_TYPE_COMPASS_3D;
165  default:
166  break;
167  }
168 
169  return SENSOR_TYPE_UNKNOWN;
170  }
171 
172  std::string ToString(SensorType type)
173  {
174  switch (type)
175  {
176  case SensorType::Accelerometer:
177  return "Accelerometer";
178  case SensorType::Gyrometer:
179  return "Gyrometer";
180  case SensorType::Magnetometer:
181  return "Magnetometer";
182  default:
183  break;
184  }
185 
186  return "";
187  }
188 
189  std::string ToString(SensorState state)
190  {
191  switch (state)
192  {
193  case SensorState::SENSOR_STATE_READY:
194  return "Ready to send sensor data";
195  case SensorState::SENSOR_STATE_NOT_AVAILABLE:
196  return "The sensor is not available for use";
197  case SensorState::SENSOR_STATE_NO_DATA:
198  return "The sensor is available but does not have data";
199  case SensorState::SENSOR_STATE_INITIALIZING:
200  return "The sensor is available, but performing initialization. Try again later.";
201  case SensorState::SENSOR_STATE_ACCESS_DENIED:
202  return "The sensor is available, but the user account does not have permission to access the sensor data";
203  case SensorState::SENSOR_STATE_ERROR:
204  return "The sensor has raised an error";
205  default:
206  break;
207  }
208 
209  return "";
210  }
211 }
212 
213 //----------------------------------------------------------------------------
214 class vtkPlusGenericSensorTracker::vtkInternal
215 {
216 public:
217  vtkPlusGenericSensorTracker* External;
218  UniquePtrType<ISensorManager> SensorManager{nullptr};
219  std::vector<SensorStreamConfig> SensorStreams;
220  std::vector<SensorUserConfig> SensorUserConfigs;
221  bool UseReportedTimestamp{false};
222 
223  vtkInternal(vtkPlusGenericSensorTracker* external)
224  : External(external)
225  {
226  }
227 
228  vtkInternal::~vtkInternal()
229  {
230  this->ReleaseStreams();
231  this->ReleaseManager();
232  }
233 
234  PlusStatus RetrieveSensorManager()
235  {
236  // Initializing the appartment with COINIT_MULTITHREADED is useless
237  // as the calling thread might be of type APTTYPE_MAINSTA. Thus,
238  // we rely later on COM interface inter-thread marshalling
239  CoInitialize(nullptr);
240 
241  ISensorManager* sensorManager{nullptr};
242  auto hr = CoCreateInstance(CLSID_SensorManager,
243  nullptr, CLSCTX_INPROC_SERVER,
244  IID_PPV_ARGS(&sensorManager));
245 
246  if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY))
247  {
248  // Unable to retrieve sensor manager due to
249  // group policy settings. Alert the user.
250  LOG_ERROR("Group policiy settings prevent access to sensor manager: " << GetLastErrorAsString());
251  return PLUS_FAIL;
252  }
253 
254  if (FAILED(hr))
255  {
256  LOG_ERROR("Failed to retrieve sensor manager: " << GetLastErrorAsString());
257  return PLUS_FAIL;
258  }
259 
260  this->SensorManager.reset(sensorManager);
261  return PLUS_SUCCESS;
262  }
263 
264  PlusStatus PopulateStreams()
265  {
266  std::set<SensorType> sensorTypes = {SensorType::Accelerometer, SensorType::Gyrometer};
267 
268  for (const auto& sensorType : sensorTypes)
269  {
270  vtkPlusDataSource* tool{nullptr};
271  this->External->GetToolByPortName(ToString(sensorType), tool);
272 
273  if (tool)
274  {
275  SensorStreamConfig config;
276  config.Type = sensorType;
277  config.Tool = tool;
278  this->SensorStreams.emplace_back(std::move(config));
279  }
280  }
281 
282  if (this->SensorStreams.empty())
283  {
284  LOG_ERROR("No data source available: required at least one Accelerometer or Gyrometer source");
285  return PLUS_FAIL;
286  }
287 
288  return PLUS_SUCCESS;
289  }
290 
291  PlusStatus InitializeStream(SensorStreamConfig& sensorStream)
292  {
293  ISensorCollection* collection{nullptr};
294  auto hr = this->SensorManager->GetSensorsByType(ConvertCustomTypeToWindowsType(sensorStream.Type), &collection);
295 
296  LOG_INFO("Initializing stream for sensor of type: " << ToString(sensorStream.Type));
297 
298  if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
299  {
300  LOG_ERROR("No sensor found");
301  return PLUS_FAIL;
302  }
303 
304  if (FAILED(hr))
305  {
306  LOG_ERROR("Failed to retrieve sensor collection: " << GetLastErrorAsString());
307  return PLUS_FAIL;
308  }
309 
310  auto safeCollection = createSafePointer(collection);
311 
312  ULONG collectionSize{};
313  hr = safeCollection->GetCount(&collectionSize);
314  if (FAILED(hr) || collectionSize == 0)
315  {
316  LOG_ERROR("Failed to retrieve sensor: " << GetLastErrorAsString());
317  return PLUS_FAIL;
318  }
319 
320  auto userConfig = std::find_if(this->SensorUserConfigs.cbegin(), this->SensorUserConfigs.cend(), [&sensorStream](const SensorUserConfig & userConfig)
321  {
322  auto referencedToolId = sensorStream.Tool->GetId();
323  auto refIdx = referencedToolId.find("To");
324  if (refIdx != std::string::npos)
325  {
326  referencedToolId = referencedToolId.substr(0, refIdx);
327  }
328  return userConfig.ToolId == referencedToolId;
329  });
330 
331  if (collectionSize > 1 && userConfig == this->SensorUserConfigs.cend())
332  {
333  LOG_WARNING("Found more than one sensor of this type. Connecting to the first one.");
334  }
335  else if (userConfig != this->SensorUserConfigs.cend())
336  {
337  LOG_INFO("Searching for device with serial number: " << userConfig->SerialNumber);
338  }
339 
340  bool found{false};
341  for (ULONG i = 0; i < collectionSize; i++)
342  {
343  ISensor* sensor{nullptr};
344  auto hr = safeCollection->GetAt(i, &sensor);
345  if (FAILED(hr))
346  {
347  LOG_ERROR("Failed to retrieve sensor: " << GetLastErrorAsString());
348  return PLUS_FAIL;
349  }
350  sensorStream.SensorHandle.reset(sensor);
351  InitializeProperties(sensorStream);
352 
353  if ((userConfig == this->SensorUserConfigs.cend()) || ConvertWideStr2Str(sensorStream.SerialNumber) == userConfig->SerialNumber)
354  {
355  found = true;
356  break;
357  }
358  }
359 
360  if (!found)
361  {
362  LOG_ERROR("Failed to find a matching device");
363  return PLUS_FAIL;
364  }
365 
366  // Needs some marshalling to access com interface from another thread as we cannot ensure
367  // an MTA is used
368  IStream* sensorMarshallingStream{nullptr};
369  hr = CoMarshalInterThreadInterfaceInStream(IID_ISensor, sensorStream.SensorHandle.get(), &sensorMarshallingStream);
370 
371  if (FAILED(hr))
372  {
373  LOG_ERROR("Failed to marshal interface for multithreading: " << GetLastErrorAsString());
374  return PLUS_FAIL;
375  }
376 
377  sensorStream.UpdateThreadSensorMarshallingStream = sensorMarshallingStream;
378  sensorStream.SensorHandle->SetEventSink(nullptr);
379  return CheckForSupportedData(sensorStream);
380  }
381 
382  void InitializeProperties(SensorStreamConfig& sensorStream)
383  {
384  const PROPERTYKEY SensorProperties[] =
385  {
386  SENSOR_PROPERTY_MANUFACTURER,
387  SENSOR_PROPERTY_SERIAL_NUMBER,
388  SENSOR_PROPERTY_DESCRIPTION,
389  SENSOR_PROPERTY_FRIENDLY_NAME
390  };
391 
392  sensorStream.Manufacturer = L"";
393  sensorStream.SerialNumber = L"";
394  sensorStream.FriendlyName = L"";
395  sensorStream.Description = L"";
396 
397  IPortableDeviceKeyCollection* keys{nullptr};
398 
399  LOG_DEBUG("Retrieving metadata for sensor of type: " << ToString(sensorStream.Type));
400 
401  auto hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
402  NULL,
403  CLSCTX_INPROC_SERVER,
404  IID_PPV_ARGS(&keys));
405 
406  if (FAILED(hr))
407  {
408  LOG_WARNING("Failed to retrieve metadata: " << GetLastErrorAsString());
409  return;
410  }
411 
412  auto safeKeys = createSafePointer(keys);
413 
414  for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(SensorProperties); dwIndex++)
415  {
416  safeKeys->Add(SensorProperties[dwIndex]);
417  }
418 
419  IPortableDeviceValues* values{nullptr};
420  hr = sensorStream.SensorHandle->GetProperties(safeKeys.get(), &values);
421 
422  if (FAILED(hr))
423  {
424  LOG_WARNING("Failed to retrieve metadata: " << GetLastErrorAsString());
425  return;
426  }
427 
428  auto safeValues = createSafePointer(values);
429 
430  DWORD valsCount{};
431  safeValues->GetCount(&valsCount);
432 
433  PROPERTYKEY pk;
434  PROPVARIANT pv = {};
435  for (DWORD i = 0; i < valsCount; i++)
436  {
437  // Get the value at the current index.
438  hr = safeValues->GetAt(i, &pk, &pv);
439 
440  if (SUCCEEDED(hr))
441  {
442  // Find and print the property.
443  if (IsEqualPropertyKey(pk, SENSOR_PROPERTY_MANUFACTURER))
444  {
445  sensorStream.Manufacturer = pv.pwszVal;
446  }
447  else if (IsEqualPropertyKey(pk, SENSOR_PROPERTY_SERIAL_NUMBER))
448  {
449  sensorStream.SerialNumber = pv.pwszVal;
450  }
451  else if (IsEqualPropertyKey(pk, SENSOR_PROPERTY_FRIENDLY_NAME))
452  {
453  sensorStream.FriendlyName = pv.pwszVal;
454  }
455  else if (IsEqualPropertyKey(pk, SENSOR_PROPERTY_DESCRIPTION))
456  {
457  sensorStream.Description = pv.pwszVal;
458  }
459  }
460 
461  PropVariantClear(&pv);
462  }
463 
464  LOG_DEBUG("Manufacturer: " << ConvertWideStr2Str(sensorStream.Manufacturer));
465  LOG_DEBUG("Serial Number: " << ConvertWideStr2Str(sensorStream.SerialNumber));
466  LOG_DEBUG("FriendlyName: " << ConvertWideStr2Str(sensorStream.FriendlyName));
467  LOG_DEBUG("Description: " << ConvertWideStr2Str(sensorStream.Description));
468  }
469 
470  PlusStatus
471  CheckForSupportedData(SensorStreamConfig& sensorStream)
472  {
473  LOG_INFO("Checking for supported data for sensor of type: " << ToString(sensorStream.Type));
474 
475  IPortableDeviceKeyCollection* keys{nullptr};
476  auto hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
477  NULL,
478  CLSCTX_INPROC_SERVER,
479  IID_PPV_ARGS(&keys));
480 
481  if (FAILED(hr))
482  {
483  LOG_ERROR("Failed to retrieve supported data: " << GetLastErrorAsString());
484  return PLUS_FAIL;
485  }
486 
487  hr = sensorStream.SensorHandle->GetSupportedDataFields(&keys);
488  if (FAILED(hr))
489  {
490  LOG_ERROR("Failed to retrieve supported data: " << GetLastErrorAsString());
491  return PLUS_FAIL;
492  }
493 
494  auto safeKeys = createSafePointer(keys);
495 
496  DWORD keysCount{};
497  hr = safeKeys->GetCount(&keysCount);
498  if (FAILED(hr))
499  {
500  LOG_ERROR("Failed to retrieve supported data: " << GetLastErrorAsString());
501  return PLUS_FAIL;
502  }
503 
504  if (sensorStream.Type == SensorType::Accelerometer && !CheckForSupportedDataAcc(safeKeys.get(), keysCount))
505  {
506  LOG_ERROR("The accelerometer sensor cannot provide the expected data");
507  return PLUS_FAIL;
508  }
509  else if (sensorStream.Type == SensorType::Gyrometer && !CheckForSupportedDataGyr(safeKeys.get(), keysCount))
510  {
511  LOG_ERROR("The gyrometer sensor cannot provide the expected data");
512  return PLUS_FAIL;
513  }
514 
515  return PLUS_SUCCESS;
516  }
517 
518  void ReleaseStreams()
519  {
520  for (auto& sensorStream : this->SensorStreams)
521  {
522  if (sensorStream.UpdateThreadSensorRef)
523  {
524  sensorStream.UpdateThreadSensorRef.reset(nullptr);
525  }
526  else if (sensorStream.UpdateThreadSensorMarshallingStream)
527  {
528  sensorStream.UpdateThreadSensorMarshallingStream->Release();
529  }
530  sensorStream.SensorHandle.reset(nullptr);
531  }
532  }
533 
534  void ReleaseManager()
535  {
536  this->SensorManager.reset(nullptr);
537  CoUninitialize();
538  }
539 
540  PlusStatus CheckSensorState(const SensorType& sensorType, ISensor* sensorHandle)
541  {
542  SensorState state;
543  auto hr = sensorHandle->GetState(&state);
544 
545  LOG_TRACE("Retrieving state for sensor of type: " << ToString(sensorType));
546 
547  if (FAILED(hr))
548  {
549  LOG_ERROR("Failed to retrieve state for sensor: " << GetLastErrorAsString());
550  return PLUS_FAIL;
551  }
552 
553  if (state == SensorState::SENSOR_STATE_READY || state == SensorState::SENSOR_STATE_NO_DATA)
554  {
555  LOG_TRACE("Sensor state: " << ToString(state));
556  }
557  else
558  {
559  LOG_ERROR("Sensor is not ready: " << ToString(state));
560  return PLUS_FAIL;
561  }
562  return PLUS_SUCCESS;
563  }
564 
565  //
566  // Accelerometer specific callback
567  //
568 
569  bool CheckForSupportedDataAcc(IPortableDeviceKeyCollection* keys, DWORD keysCount)
570  {
571  PROPERTYKEY pk;
572  std::bitset<3> supported = 0b000;
573 
574  for (DWORD i = 0; i < keysCount; i++)
575  {
576  auto hr = keys->GetAt(i, &pk);
577 
578  if (FAILED(hr))
579  {
580  continue;
581  }
582 
583  if (IsEqualPropertyKey(pk, SENSOR_DATA_TYPE_ACCELERATION_X_G))
584  {
585  supported[0] = 1;
586  }
587  else if (IsEqualPropertyKey(pk, SENSOR_DATA_TYPE_ACCELERATION_Y_G))
588  {
589  supported[1] = 1;
590  }
591  else if (IsEqualPropertyKey(pk, SENSOR_DATA_TYPE_ACCELERATION_Z_G))
592  {
593  supported[2] = 1;
594  }
595  }
596 
597  return supported.all();
598  }
599 
600  static PlusStatus RetrieveAccData(ISensorDataReport* report, vtkSmartPointer<vtkMatrix4x4> matrix)
601  {
602  std::array<const PROPERTYKEY, 3> fields = {SENSOR_DATA_TYPE_ACCELERATION_X_G, SENSOR_DATA_TYPE_ACCELERATION_Y_G, SENSOR_DATA_TYPE_ACCELERATION_Z_G};
603  matrix->Identity();
604  for (int i = 0; i < 3; ++i)
605  {
606  PROPVARIANT var = {};
607  auto hr = report->GetSensorValue(fields[i], &var);
608 
609  if (!SUCCEEDED(hr) || var.vt != VT_R8)
610  {
611  PropVariantClear(&var);
612  LOG_ERROR("Failed to retrieve acceleration in " << ((i == 0) ? "x" : ((i == 1) ? "y" : "z")));
613  return PLUS_FAIL;
614  }
615 
616  matrix->SetElement(i, 3, var.dblVal);
617  LOG_TRACE("Retrieve acceleration component " << ((i == 0) ? "x" : ((i == 1) ? "y" : "z")) << ": " << var.dblVal);
618  PropVariantClear(&var);
619  }
620 
621  return PLUS_SUCCESS;
622  }
623 
624  //
625  // Gyrometer specific callback
626  //
627 
628  bool CheckForSupportedDataGyr(IPortableDeviceKeyCollection* keys, DWORD keysCount)
629  {
630  PROPERTYKEY pk;
631  std::bitset<3> supported = 0b000;
632 
633  for (DWORD i = 0; i < keysCount; i++)
634  {
635  auto hr = keys->GetAt(i, &pk);
636 
637  if (FAILED(hr))
638  {
639  continue;
640  }
641 
642  if (IsEqualPropertyKey(pk, SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND))
643  {
644  supported[0] = 1;
645  }
646  else if (IsEqualPropertyKey(pk, SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND))
647  {
648  supported[1] = 1;
649  }
650  else if (IsEqualPropertyKey(pk, SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND))
651  {
652  supported[2] = 1;
653  }
654  }
655 
656  return supported.all();
657  }
658 
659  static PlusStatus RetrieveGyrData(ISensorDataReport* report, vtkSmartPointer<vtkMatrix4x4> matrix)
660  {
661  std::array<const PROPERTYKEY, 3> fields = {SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND, SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND, SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND};
662  matrix->Identity();
663  for (int i = 0; i < 3; ++i)
664  {
665  PROPVARIANT var = {};
666  auto hr = report->GetSensorValue(fields[i], &var);
667 
668  if (!SUCCEEDED(hr) || var.vt != VT_R8)
669  {
670  PropVariantClear(&var);
671  LOG_ERROR("Failed to retrieve angular velocity in " << ((i == 0) ? "x" : ((i == 1) ? "y" : "z")));
672  return PLUS_FAIL;
673  }
674 
675  matrix->SetElement(i, 3, var.dblVal);
676  LOG_TRACE("Retrieve angular velocity component " << ((i == 0) ? "x" : ((i == 1) ? "y" : "z")) << ": " << var.dblVal);
677  PropVariantClear(&var);
678  }
679 
680  return PLUS_SUCCESS;
681  }
682 
683  //
684  // TODO: Compass, etc. implementations
685  //
686 };
687 
688 //----------------------------------------------------------------------------
690  : Internal(new vtkInternal(this))
691 {
692  this->StartThreadForInternalUpdates = true;
693 }
694 
695 //----------------------------------------------------------------------------
697 {
698  delete this->Internal;
699  this->Internal = nullptr;
700 }
701 
702 //----------------------------------------------------------------------------
703 void vtkPlusGenericSensorTracker::PrintSelf(ostream& os, vtkIndent indent)
704 {
705  this->Superclass::PrintSelf(os, indent);
706 
707  os << indent << "Generic Sensor Configuration:" << std::endl;
708  os << indent << "UseReportedTimestamp: " << this->Internal->UseReportedTimestamp << std::endl;
709  for (auto& sensorStream : this->Internal->SensorStreams)
710  {
711  os << "Sensor type: " << ToString(sensorStream.Type);
712  os << indent << "Serial Number: " << ConvertWideStr2Str(sensorStream.SerialNumber);
713  os << indent << "Serial Manufaturer: " << ConvertWideStr2Str(sensorStream.Manufacturer);
714  os << indent << "Serial Friendly Name: " << ConvertWideStr2Str(sensorStream.FriendlyName);
715  os << indent << "Serial Description: " << ConvertWideStr2Str(sensorStream.Description);
716  }
717 }
718 
719 //-----------------------------------------------------------------------------
720 PlusStatus vtkPlusGenericSensorTracker::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
721 {
722  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
723  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(UseReportedTimestamp, this->Internal->UseReportedTimestamp, deviceConfig);
724  XML_FIND_NESTED_ELEMENT_REQUIRED(dataSourcesElement, deviceConfig, "DataSources");
725 
726  LOG_TRACE("Reading custom configuration fields in " << dataSourcesElement->GetNumberOfNestedElements() << " nested elements");
727  for (int nestedElementIndex = 0; nestedElementIndex < dataSourcesElement->GetNumberOfNestedElements(); ++nestedElementIndex)
728  {
729  vtkXMLDataElement* dataElement = dataSourcesElement->GetNestedElement(nestedElementIndex);
730  if (STRCASECMP(dataElement->GetName(), "DataSource") != 0)
731  {
732  continue;
733  }
734 
735  LOG_TRACE("Found a new data source");
736 
737  if (dataElement->GetAttribute("Type") != NULL && STRCASECMP(dataElement->GetAttribute("Type"), "Tool") == 0)
738  {
739  const char* toolId = dataElement->GetAttribute("Id");
740  if (toolId == nullptr)
741  {
742  // tool doesn't have ID needed to generate transform
743  LOG_ERROR("Failed to initialize tool: Id is missing");
744  continue;
745  }
746 
747  LOG_TRACE("Data source name: " << toolId);
748  SensorUserConfig config;
749  config.ToolId = toolId;
750  XML_READ_STRING_ATTRIBUTE_NONMEMBER_OPTIONAL(SerialNumber, config.SerialNumber, dataElement);
751 
752  if (!config.SerialNumber.empty())
753  {
754  this->Internal->SensorUserConfigs.emplace_back(config);
755  }
756  }
757  else
758  {
759  LOG_ERROR("DataSource with unknown Type.");
760  return PLUS_FAIL;
761  }
762  }
763  return PLUS_SUCCESS;
764 }
765 
766 //----------------------------------------------------------------------------
768 {
769  if (this->Internal->PopulateStreams() != PLUS_SUCCESS ||
770  this->Internal->RetrieveSensorManager() != PLUS_SUCCESS)
771  {
772  return PLUS_FAIL;
773  }
774 
775  return PLUS_SUCCESS;
776 }
777 
778 //----------------------------------------------------------------------------
780 {
781  this->Internal->ReleaseManager();
782  return PLUS_SUCCESS;
783 }
784 
785 //----------------------------------------------------------------------------
787 {
788  for (const auto& sensorStream : this->Internal->SensorStreams)
789  {
790  if (this->Internal->CheckSensorState(sensorStream.Type, sensorStream.SensorHandle.get()) != PLUS_SUCCESS)
791  {
792  return PLUS_FAIL;
793  }
794  }
795  return PLUS_SUCCESS;
796 }
797 
798 //----------------------------------------------------------------------------
800 {
801  for (auto& sensorStream : this->Internal->SensorStreams)
802  {
803  if (this->Internal->InitializeStream(sensorStream) != PLUS_SUCCESS)
804  {
805  return PLUS_FAIL;
806  }
807  }
808 
809  this->FrameNumber = 0;
810  return PLUS_SUCCESS;
811 }
812 
813 //----------------------------------------------------------------------------
815 {
816  this->Internal->ReleaseStreams();
817  return PLUS_SUCCESS;
818 }
819 
820 //----------------------------------------------------------------------------
822 {
823  static SafeComInitializer safeComInitializer;
824  for (auto& sensorStream : this->Internal->SensorStreams)
825  {
826  LOG_TRACE("Retrieving sensor data for sensor of type: " << ToString(sensorStream.Type));
827 
828  if (!sensorStream.UpdateThreadSensorRef)
829  {
830  ISensor* sensorRef{nullptr};
831  auto hr = CoGetInterfaceAndReleaseStream(sensorStream.UpdateThreadSensorMarshallingStream, IID_ISensor, (LPVOID*)&sensorRef);
832 
833  if (FAILED(hr))
834  {
835  LOG_ERROR("Failed to retrieve sensor handle: " << GetLastErrorAsString());
836  return PLUS_FAIL;
837  }
838 
839  sensorStream.UpdateThreadSensorRef.reset(sensorRef);
840  sensorStream.UpdateThreadSensorMarshallingStream = nullptr;
841  }
842 
843  if (Internal->CheckSensorState(sensorStream.Type, sensorStream.UpdateThreadSensorRef.get()) != PLUS_SUCCESS)
844  {
845  LOG_ERROR("Sensor state not valid");
846  return PLUS_FAIL;
847  }
848 
849  ISensorDataReport* report{nullptr};
850  auto hr = sensorStream.UpdateThreadSensorRef->GetData(&report);
851 
852  if (hr == HRESULT_FROM_WIN32(ERROR_NO_DATA))
853  {
854  LOG_ERROR("No data is available yet");
855  continue;
856  }
857 
858  if (FAILED(hr))
859  {
860  LOG_ERROR("Failed to retrieve data: " << GetLastErrorAsString() << hr);
861  return PLUS_FAIL;
862  }
863 
864  auto safeReport = createSafePointer(report);
865 
866  vtkNew<vtkMatrix4x4> transform;
867  if (sensorStream.Type == SensorType::Accelerometer)
868  {
869  if (vtkInternal::RetrieveAccData(safeReport.get(), transform) != PLUS_SUCCESS)
870  {
871  LOG_ERROR("Failed to get accelerometer data: " << GetLastErrorAsString());
872  return PLUS_FAIL;
873  }
874  }
875  else if (sensorStream.Type == SensorType::Gyrometer)
876  {
877  if (vtkInternal::RetrieveGyrData(safeReport.get(), transform) != PLUS_SUCCESS)
878  {
879  LOG_ERROR("Failed to get gyrometer data: " << GetLastErrorAsString());
880  return PLUS_FAIL;
881  }
882  }
883 
884  if (this->Internal->UseReportedTimestamp)
885  {
886  SYSTEMTIME sysTime;
887  hr = safeReport->GetTimestamp(&sysTime);
888 
889  double timestamp{};
890  if (FAILED(hr))
891  {
892  LOG_TRACE("Failed to get timestamp. Falling back on current system time.");
893  timestamp = vtkIGSIOAccurateTimer::GetSystemTime();
894  }
895  else
896  {
897  auto sensorTimestampMs = GetSystemTimeInMs(&sysTime);
898  LOG_TRACE("Sensor reported timestamp is ms: " << sensorTimestampMs);
899  if (!sensorStream.TrackerTimeToSystemTimeComputed)
900  {
901  const double timeSystemSec = vtkIGSIOAccurateTimer::GetSystemTime();
902  LOG_TRACE("System time in s: " << timeSystemSec);
903  sensorStream.TrackerTimeToSystemTimeSec = timeSystemSec - sensorTimestampMs / 1000.0;
904  sensorStream.TrackerTimeToSystemTimeComputed = true;
905 
906  LOG_TRACE("Timestamp offset for this sensor: " << sensorStream.TrackerTimeToSystemTimeSec);
907  }
908 
909  timestamp = sensorTimestampMs / 1000.0 + sensorStream.TrackerTimeToSystemTimeSec;
910  }
911 
912  LOG_TRACE("Final timestamp in s: " << timestamp);
913  ToolTimeStampedUpdateWithoutFiltering(sensorStream.Tool->GetId(), transform, TOOL_OK, timestamp, timestamp);
914  }
915  else
916  {
917  ToolTimeStampedUpdate(sensorStream.Tool->GetId(), transform, TOOL_OK, this->FrameNumber, vtkIGSIOAccurateTimer::GetSystemTime());
918  }
919  }
920  this->FrameNumber++;
921 
922  return PLUS_SUCCESS;
923 }
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
double * timestamp
Definition: phidget22.h:3432
Interface class to collect sensor data in a generic wayFor now, the following sensor types are suppor...
virtual PlusStatus ToolTimeStampedUpdateWithoutFiltering(const std::string &aToolSourceId, vtkMatrix4x4 *matrix, ToolStatus status, double unfilteredtimestamp, double filteredtimestamp, const igsioFieldMapType *customFields=NULL)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual PlusStatus ToolTimeStampedUpdate(const std::string &aToolSourceId, vtkMatrix4x4 *matrix, ToolStatus status, unsigned long frameNumber, double unfilteredtimestamp, const igsioFieldMapType *customFields=NULL)
void PrintSelf(ostream &os, vtkIndent indent) override
for i
#define PLUS_FAIL
Definition: PlusCommon.h:43
unsigned long DWORD
Definition: ATC3DGm.h:451
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
PlusStatus GetToolByPortName(const char *aPortName, vtkPlusDataSource *&aSource)
int * state
Definition: phidget22.h:3207
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
unsigned long ULONG
Definition: ATC3DGm.h:432
bool StartThreadForInternalUpdates
vtkStandardNewMacro(vtkPlusGenericSensorTracker)
PlusStatus ReadConfiguration(vtkXMLDataElement *config) override
const char * message
Definition: phidget22.h:2457
PhidgetVoltageRatioInput_SensorType sensorType
Definition: phidget22.h:3149
for t
Definition: exploreFolders.m:9
Interface to a 3D positioning tool, video source, or generalized data stream.