PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusClariusOEM.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2  Program: Plus
3  Copyright (c) Verdure Imaging Inc, Stockton, California. All rights reserved.
4  See License.txt for details.
5 
6  We would like to acknowledge Verdure Imaging Inc for generously open-sourcing
7  this support for the Clarius OEM interface to the PLUS & Slicer communities.
8 =========================================================Plus=header=end*/
9 
10 // Local includes
11 #include "PlusConfigure.h"
12 #include "vtkPlusChannel.h"
13 #include "vtkPlusClariusOEM.h"
14 #include "PixelCodec.h"
15 #include "vtkPlusDataSource.h"
17 
18 // Wifi & BLE helpers
19 #include "ClariusBLE.h"
20 #include "ClariusWifi.h"
21 
22 // Clarius Includes
23 #include <solum.h>
24 #include <solum_def.h>
25 
26 // VTK includes
27 #include <vtkImageData.h>
28 #include <vtkInformation.h>
29 #include <vtkMatrix4x4.h>
30 #include <vtkNew.h>
31 #include <vtkSmartPointer.h>
32 
33 // STL includes
34 #include <algorithm>
35 #include <cctype>
36 #include <chrono>
37 #include <future>
38 #include <map>
39 #include <sstream>
40 #include <string>
41 
42 // Clarius enable / disable values
43 #define CLARIUS_TRUE 1
44 #define CLARIUS_FALSE 0
45 #define CLARIUS_RUN 1
46 #define CLARIUS_STOP 0
47 
48 // Clarius connection state values
49 #define CLARIUS_STATE_NOT_INITIALIZED -1
50 #define CLARIUS_STATE_NOT_CONNECTED 0
51 #define CLARIUS_STATE_CONNECTED 1
52 
53 const std::string vtkPlusClariusOEM::OVERLAY_PORT_NAME = "Overlay";
54 
55 //-------------------------------------------------------------------------------------------------
56 // instance memory
57 vtkPlusClariusOEM* vtkPlusClariusOEM::instance;
58 
59 //-------------------------------------------------------------------------------------------------
60 // free helper functions
61 //-------------------------------------------------------------------------------------------------
62 std::string to_string(ClariusAvailability avail)
63 {
64  switch (avail)
65  {
67  return "Available";
69  return "Listen";
71  return "Not Available";
72  default:
73  return "Unknown ClariusAvailability";
74  }
75 }
76 
77 //-------------------------------------------------------------------------------------------------
79 {
80  switch (mode)
81  {
83  return "Access Point";
85  return "Local Area Network";
86  default:
87  return "Unknown ClariusWifiMode";
88  }
89 }
90 
91 //-------------------------------------------------------------------------------------------------
92 namespace
93 {
94  enum class BUTTON_MODE
95  {
96  DISABLED = CusButtonSetting::ButtonDisabled,
97  FREEZE = CusButtonSetting::ButtonFreeze,
98  USER = CusButtonSetting::ButtonUser
99  };
100 
101  static std::map<BUTTON_MODE, std::string> ButtonModeEnumToString{
102  {BUTTON_MODE::FREEZE, "FREEZE"},
103  {BUTTON_MODE::USER, "USER"},
104  {BUTTON_MODE::DISABLED, "DISABLED"}
105  };
106 
107  static std::map<int, std::string> ImagingModeStrings{
108  {CusMode::BMode, "BMode"},
109  {CusMode::Compounding, "Compounding"},
110  {CusMode::MMode, "MMode"},
111  {CusMode::ColorMode, "ColorMode"},
112  {CusMode::PowerMode, "PowerMode"},
113  {CusMode::PwMode, "PwMode"},
114  {CusMode::NeedleEnhance, "NeedleEnhance"},
115  {CusMode::Strain, "Strain"},
116  {CusMode::RfMode, "RfMode"},
117  };
118 
119  static const double CM_TO_MM = 10.0;
120  static const double MM_TO_CM = 0.1;
121  static const double UM_TO_MM = 0.001;
122 
123  static const int CLARIUS_SHORT_DELAY_MS = 10;
124  static const int CLARIUS_LONG_DELAY_MS = 1000;
125 
126  static const std::string BATTERY_FIELD_TAG = "ClariusBattery";
127  static const std::string TEMP_FIELD_TAG = "ClariusTemp";
128  static const std::string FRAME_RATE_FIELD_TAG = "ClariusFrameRate";
129  static const std::string BUTTON_FIELD_TAG = "ClariusButton";
130  static const std::string UP_BUTTON_TAG = "Up";
131  static const std::string DOWN_BUTTON_TAG = "Down";
132  static const std::string NO_BUTTON_TAG = "None";
133  static const std::string NUM_CLICKS_FIELD_TAG = "ClariusNumClicks";
134  static const int SEND_BUTTON_STATE_FOR_N_FRAMES = 20;
135 
136  static const FrameSizeType DEFAULT_FRAME_SIZE = { 512, 512, 1 };
137 
138  static const bool DEFAULT_ENABLE_AUTO_GAIN = false;
139 
140  static const bool DEFAULT_ENABLE_5V_RAIL = false;
141 
142  static const bool DEFAULT_FREEZE_ON_POOR_WIFI_SIGNAL = true;
143 
144  static const bool DEFAULT_KEEP_AWAKE_CHARGING = true;
145 
146  static const bool DEFAULT_POWER_BUTTONS_ENABLED = true;
147 
148  static const bool DEFAULT_SOUND_ENABLED = true;
149 
150  static const int DEFAULT_STATIONARY_TIMEOUT_SEC = 0;
151 
152  static const bool DEFAULT_WAKE_ON_SHAKE = false;
153 
154  static const bool DEFAULT_FORCE_LOG_SEND = false;
155 
156  static const bool DEFAULT_PENETRATION_MODE_ENABLED = false;
157 
158  static const int DEFAULT_CONTACT_DETECTION_TIMEOUT_SEC = 15;
159 
160  static const int DEFAULT_AUTO_FREEZE_TIMEOUT_SEC = 60;
161 
162  static const int DEFAULT_KEEP_AWAKE_TIMEOUT_SEC = 60;
163 
164  static const int DEFAULT_DEEP_SLEEP_TIMEOUT_HR = 0;
165 
166  static const BUTTON_MODE DEFAULT_UP_BUTTON_MODE = BUTTON_MODE::DISABLED;
167 
168  static const BUTTON_MODE DEFAULT_DOWN_BUTTON_MODE = BUTTON_MODE::FREEZE;
169 
170  static const double DEFAULT_DEPTH_MM = 100.0;
171 
172  static const double DEFAULT_GAIN_PERCENT = 80.0;
173 
174  static const double DEFAULT_DYNRANGE_PERCENT = 80.0;
175 
176  static const std::vector<double> DEFAULT_TGC_DB = { 5, 5, 5 };
177 
178  static const bool DEFAULT_ENABLE_AUTO_FOCUS = false;
179 
180  static std::map<int, std::string> ConnectEnumToString{
181  {CusConnection::ProbeConnected, "CONNECT_SUCCESS"},
182  {CusConnection::ProbeDisconnected, "CONNECT_DISCONNECT"},
183  {CusConnection::ConnectionFailed, "CONNECT_FAILED"},
184  {CusConnection::SwUpdateRequired, "CONNECT_SWUPDATE"},
185  {CusConnection::ConnectionError, "CONNECT_ERROR"},
186  };
187 
188  static const std::string TRANSD_PORT_NAME = "Transd";
189 }
190 
191 //-------------------------------------------------------------------------------------------------
192 // vtkInternal
193 //-------------------------------------------------------------------------------------------------
194 
195 class vtkPlusClariusOEM::vtkInternal
196 {
197 public:
198 
199  vtkInternal(vtkPlusClariusOEM* ext);
200 
201  virtual ~vtkInternal()
202  {
203  }
204 
205 protected:
206 
207  friend class vtkPlusClariusOEM;
208 
209  // Clarius callbacks
210  static void ListFn(const char* list, int sz);
211 
212  static void ConnectFn(CusConnection ret, int port, const char* status);
213 
214  static void CertFn(int daysValid);
215 
216  static void PowerDownFn(CusPowerDown ret, int tm);
217 
218  static void SwUpdateFn(CusSwUpdate ret);
219 
220  static void RawImageFn(const void* newImage, const CusRawImageInfo* nfo, int npos, const CusPosInfo* pos);
221 
222  static void ProcessedImageFn(const void* newImage, const CusProcessedImageInfo* nfo, int npos, const CusPosInfo* pos);
223 
224  static void SpectralImageFn(const void* newImage, const CusSpectralImageInfo* nfo);
225 
226  static void ImuDataFn(const CusPosInfo* pos);
227 
228  static void ImagingFn(CusImagingState ready, int imaging);
229 
230  static void ButtonFn(CusButton btn, int clicks);
231 
232  static void ProgressFn(int progress);
233 
234  static void ErrorFn(CusErrorCode errorCode, const char* msg);
235 
236  std::string ImagingModeToString(int mode)
237  {
238  if (ImagingModeStrings.find(mode) != ImagingModeStrings.end())
239  {
240  return ImagingModeStrings[mode];
241  }
242  else
243  {
244  return "Unknown Imaging Mode";
245  }
246  }
247 
248  // log user settings to console
249  void LogUserSettings();
250 
251  //
252  // members
253  //
254 
255  // user configurable params
256  std::string ProbeSerialNum;
257  std::string PathToCert;
258  FrameSizeType FrameSize;
259  std::string ProbeType;
260  std::string ImagingApplication;
261  bool EnableAutoGain;
262  bool Enable5v;
263  bool FreezeOnPoorWifiSignal;
264  bool KeepAwakeCharging;
265  bool PowerButtonsEnabled;
266  bool SoundEnabled;
267  int StationaryTimeoutSec;
268  bool WakeOnShake;
269  bool ForceLogSend;
270  bool EnablePenetrationMode;
271  int ContactDetectionTimeoutSec;
272  int AutoFreezeTimeoutSec;
273  int KeepAwakeTimeoutMin;
274  int DeepSleepTimeoutHr;
275  BUTTON_MODE UpButtonMode;
276  BUTTON_MODE DownButtonMode;
277  int ImagingMode;
278 
279  // parameters retrieved from the probe over BLE
280  std::string Ssid;
281  std::string Password;
282  std::string IpAddress;
283  int TcpPort;
284 
285  // parameters retrieved from the probe using the OEM API
286  std::promise<std::vector<std::string>> PromiseProbes;
287  std::promise<std::vector<std::string>> PromiseApplications;
288  std::promise<void> ConnectionBarrier;
289 
290  // pointers to DataSources
291  std::vector<vtkPlusDataSource*> BModeSources;
292  std::vector<vtkPlusDataSource*> OverlaySources;
293  vtkPlusDataSource* TransdSource;
294 
295  // button press info
296  std::string PressedButton;
297  int ButtonNumClicks;
298  int ButtonSentCount; // button state is sent for SEND_BUTTON_STATE_FOR_N_FRAMES
299  // frames after button is clicked to ensure user can process
300  // it in Slicer even if a frame is skipped
301 
302  CusStatusInfo CurrentStatus;
303 
304  bool EnableAutoFocus;
305 
306  enum class EXPECTED_LIST
307  {
308  PROBES,
309  APPLICATIONS,
310  UNKNOWN
311  } ExpectedList;
312 
313  std::string SoftwareUpdateFilename;
314  int SoftwareUpdateHardwareVersion{ 0 };
315 
316 private:
317  vtkPlusClariusOEM* External;
318 
319  // BLE interface
320  ClariusBLE BleHelper;
321 
322  // Wifi helper class
323  ClariusWifi WifiHelper;
324 };
325 
326 //-------------------------------------------------------------------------------------------------
327 vtkPlusClariusOEM::vtkInternal::vtkInternal(vtkPlusClariusOEM* ext)
328  : External(ext)
329  , ProbeSerialNum("")
330  , PathToCert("")
331  , FrameSize(DEFAULT_FRAME_SIZE)
332  , ProbeType("")
333  , ImagingApplication("")
334  , EnableAutoGain(DEFAULT_ENABLE_AUTO_GAIN)
335  , Enable5v(DEFAULT_ENABLE_5V_RAIL)
336  , FreezeOnPoorWifiSignal(DEFAULT_FREEZE_ON_POOR_WIFI_SIGNAL)
337  , KeepAwakeCharging(DEFAULT_KEEP_AWAKE_CHARGING)
338  , PowerButtonsEnabled(DEFAULT_POWER_BUTTONS_ENABLED)
339  , SoundEnabled(DEFAULT_SOUND_ENABLED)
340  , StationaryTimeoutSec(DEFAULT_STATIONARY_TIMEOUT_SEC)
341  , WakeOnShake(DEFAULT_WAKE_ON_SHAKE)
342  , ForceLogSend(DEFAULT_FORCE_LOG_SEND)
343  , EnablePenetrationMode(DEFAULT_PENETRATION_MODE_ENABLED)
344  , ContactDetectionTimeoutSec(DEFAULT_CONTACT_DETECTION_TIMEOUT_SEC)
345  , AutoFreezeTimeoutSec(DEFAULT_AUTO_FREEZE_TIMEOUT_SEC)
346  , KeepAwakeTimeoutMin(DEFAULT_KEEP_AWAKE_TIMEOUT_SEC)
347  , DeepSleepTimeoutHr(DEFAULT_DEEP_SLEEP_TIMEOUT_HR)
348  , UpButtonMode(DEFAULT_UP_BUTTON_MODE)
349  , DownButtonMode(DEFAULT_DOWN_BUTTON_MODE)
350  , ImagingMode(CusMode::BMode)
351  , IpAddress("")
352  , TcpPort(-1)
353  , TransdSource(nullptr)
354  , PressedButton(NO_BUTTON_TAG)
355  , ButtonNumClicks(0)
356  , ButtonSentCount(0)
357  , EnableAutoFocus(DEFAULT_ENABLE_AUTO_FOCUS)
358 {
359 }
360 
361 //-------------------------------------------------------------------------------------------------
362 void vtkPlusClariusOEM::vtkInternal::ListFn(const char* list, int sz)
363 {
365 
366  std::vector<std::string> vec;
367 
368  std::stringstream ss(list);
369  while (ss.good())
370  {
371  std::string substr;
372  getline(ss, substr, ',');
373  vec.push_back(substr);
374  }
375 
376  if (device->Internal->ExpectedList == EXPECTED_LIST::PROBES)
377  {
378  device->Internal->PromiseProbes.set_value(vec);
379  }
380  else if (device->Internal->ExpectedList == EXPECTED_LIST::APPLICATIONS)
381  {
382  device->Internal->PromiseApplications.set_value(vec);
383  }
384 }
385 
386 //-------------------------------------------------------------------------------------------------
387 void vtkPlusClariusOEM::vtkInternal::ConnectFn(CusConnection ret, int port, const char* status)
388 {
390 
391  switch (ret)
392  {
393  case ConnectionError:
394  LOG_ERROR("Connection status: error - " << status);
395  device->Disconnect();
396  break;
397  case ProbeConnected:
398  LOG_INFO("Connection status: probe connected - " << status);
399  break;
400  case ProbeDisconnected:
401  LOG_INFO("Connection status: probe disconnected - " << status);
402  device->Disconnect();
403  break;
404  case ConnectionFailed:
405  LOG_ERROR("Connection status: connection failed - " << status);
406  device->Disconnect();
407  break;
408  case SwUpdateRequired:
409  LOG_INFO("Connection status: software update required - " << status);
410  device->Disconnect();
411  break;
412  }
413 
414  if (ret == CusConnection::ProbeConnected)
415  {
416  // connection succeeded, set Internal->Connected variable to end busy wait in InternalConnect
417  device->Internal->ConnectionBarrier.set_value();
418  }
419 }
420 
421 //-------------------------------------------------------------------------------------------------
422 void vtkPlusClariusOEM::vtkInternal::CertFn(int daysValid)
423 {
424  if (daysValid <= 0)
425  {
426  LOG_ERROR("Invalid or expired certificate provided for Clarius OEM device");
427  }
428  else
429  {
430  LOG_INFO("Clarius certificate is valid for " << daysValid << " more days");
431  }
432 }
433 
434 //-------------------------------------------------------------------------------------------------
435 void vtkPlusClariusOEM::vtkInternal::PowerDownFn(CusPowerDown ret, int tm)
436 {
437  std::stringstream ss;
438  ss << "Clarius probe will power down ";
439 
440  if (tm == 0)
441  {
442  ss << "immediately as a result of ";
443  }
444  else
445  {
446  ss << "in " << tm << " seconds as a result of ";
447  }
448 
449  if (ret == CusPowerDown::Idle)
450  {
451  ss << "being idle for an extended period of time. ";
452  }
453  else if (ret == CusPowerDown::TooHot)
454  {
455  ss << "overheating. ";
456  }
457  else if (ret == CusPowerDown::LowBattery)
458  {
459  ss << "running out of battery. ";
460  }
461  else if (ret == CusPowerDown::ButtonOff)
462  {
463  ss << "user holding the shutdown button. ";
464  }
465  else
466  {
467  ss << "an unspecified reason. ";
468  }
469 
470  ss << "If Clarius probe has powered off please turn it back on and restart PLUS, if desired.";
471  LOG_ERROR(ss.str());
472 }
473 
474 //-------------------------------------------------------------------------------------------------
475 void vtkPlusClariusOEM::vtkInternal::SwUpdateFn(CusSwUpdate ret)
476 {
478 
479  switch (ret)
480  {
481  case SwUpdateError:
482  LOG_ERROR("Clarius software update failed");
483  break;
484  case SwUpdateSuccess:
485  LOG_INFO("Clarius software update was successful");
486  break;
487  case SwUpdateCurrent:
488  LOG_INFO("Clarius software is current, no update required");
489  break;
490  case SwUpdateBattery:
491  LOG_WARNING("Clarius software update failed due to low battery, please charge the probe and try again");
492  break;
493  case SwUpdateUnsupported:
494  LOG_ERROR("Clarius software update failed because the firmware being sent is not longer supported by the probe");
495  break;
496  case SwUpdateCorrupt:
497  LOG_ERROR("Clarius software update failed because the probe file system may be corrupt, please contact Clarius support for assistance");
498  break;
499  default:
500  LOG_ERROR("Clarius software update callback received an unknown status: " << ret);
501  }
502 
503  device->InternalDisconnect();
504 }
505 
506 //-------------------------------------------------------------------------------------------------
507 void vtkPlusClariusOEM::vtkInternal::RawImageFn(const void* newImage, const CusRawImageInfo* nfo, int npos, const CusPosInfo* pos)
508 {
509  LOG_ERROR("Support for Clarius OEM raw images has not been implemented. If you desire this feature please submit an issue to request it on the PlusToolkit/PlusLib GitHub repository");
510 }
511 
512 //-------------------------------------------------------------------------------------------------
513 void vtkPlusClariusOEM::vtkInternal::SpectralImageFn(const void* newImage, const CusSpectralImageInfo* nfo)
514 {
515  LOG_ERROR("Support for Clarius OEM spectral images has not been implemented. If you desire this feature please submit an issue to request it on the PlusToolkit/PlusLib GitHub repository");
516 }
517 
518 //-------------------------------------------------------------------------------------------------
519 void vtkPlusClariusOEM::vtkInternal::ImuDataFn(const CusPosInfo* pos)
520 {
521  LOG_ERROR("Support for Clarius OEM IMU data has not been implemented. If you desire this feature please submit an issue to request it on the PlusToolkit/PlusLib GitHub repository");
522 }
523 
524 //-------------------------------------------------------------------------------------------------
526 {
527  if (this->UpdateProbeStatus() != PLUS_SUCCESS)
528  {
529  LOG_ERROR("Failed to update probe status");
530  }
531  return PLUS_SUCCESS;
532 }
533 
534 //-------------------------------------------------------------------------------------------------
536 {
537  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
538  {
539  return PLUS_SUCCESS;
540  }
541 
542  if (solumStatusInfo(&this->Internal->CurrentStatus) != 0)
543  {
544  LOG_ERROR("Failed to retrieve solumStatusInfo");
545  return PLUS_FAIL;
546  }
547  return PLUS_SUCCESS;
548 }
549 
550 //-------------------------------------------------------------------------------------------------
551 void vtkPlusClariusOEM::vtkInternal::ProcessedImageFn(const void* oemImage, const CusProcessedImageInfo* nfo, int npos, const CusPosInfo* pos)
552 {
554  if (device == NULL)
555  {
556  LOG_ERROR("vtkPlusClariusOEM::vtkInternal::ProcessedImageFn called when Clarius instance was NULL!");
557  return;
558  }
559 
560  // check if still connected
561  if (!device->Connected)
562  {
563  return;
564  }
565 
566  if (oemImage == NULL)
567  {
568  LOG_ERROR("NULL frame received by the Clarius OEM device");
569  return;
570  }
571 
572  // check if source is valid, if not - nothing to do
573  if (!nfo->overlay && device->Internal->BModeSources.size() == 0)
574  {
575  return;
576  }
577  else if (nfo->overlay && device->Internal->OverlaySources.size() == 0)
578  {
579  return;
580  }
581 
582  // setup source
583  const double unfilteredTimestamp = vtkIGSIOAccurateTimer::GetSystemTime();
584 
585  vtkSmartPointer<vtkImageData> vtkImage = vtkSmartPointer<vtkImageData>::New();
586  vtkNew<vtkInformation> info;
587  vtkImage->SetExtent(0, nfo->width - 1, 0, nfo->height - 1, 0, 1);
588  vtkImage->SetOrigin(0.0, 0.0, 0.0);
589  vtkImage->SetSpacing(1.0, 1.0, 1.0);
590 
591  std::vector<vtkPlusDataSource*> dataSources = device->Internal->BModeSources;
592  if (nfo->overlay)
593  {
594  dataSources = device->Internal->OverlaySources;
595  }
596 
597  for (auto dataSource : dataSources)
598  {
599  if (dataSource->GetImageType() == US_IMG_RGB_COLOR)
600  {
601  vtkImage->AllocateScalars(VTK_UNSIGNED_CHAR, 3);
603  nfo->width,
604  nfo->height,
605  (unsigned char*)oemImage,
606  (unsigned char*)vtkImage->GetScalarPointer()
607  );
608  }
609  else
610  {
611  // convert Clarius RGBA to grayscale
612  // format is:
613  // B = ultrasound value
614  // G = ultrasound value
615  // R = ultrasound value
616  // A = 255
617  vtkImage->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
620  nfo->width,
621  nfo->height,
622  (unsigned char*)oemImage,
623  (unsigned char*)vtkImage->GetScalarPointer()
624  );
625  }
626  }
627 
628 
629  // custom fields (battery & button clicks)
630  igsioFieldMapType customFields;
631  customFields[BATTERY_FIELD_TAG].first = FRAMEFIELD_FORCE_SERVER_SEND;
632  customFields[BATTERY_FIELD_TAG].second = std::to_string(device->Internal->CurrentStatus.battery);
633  customFields[TEMP_FIELD_TAG].first = FRAMEFIELD_FORCE_SERVER_SEND;
634  customFields[TEMP_FIELD_TAG].second = std::to_string(device->Internal->CurrentStatus.temperature);
635  customFields[FRAME_RATE_FIELD_TAG].first = FRAMEFIELD_FORCE_SERVER_SEND;
636  customFields[FRAME_RATE_FIELD_TAG].second = std::to_string(device->Internal->CurrentStatus.frameRate);
637 
638  customFields[BUTTON_FIELD_TAG].first = FRAMEFIELD_FORCE_SERVER_SEND;
639  customFields[BUTTON_FIELD_TAG].second = device->Internal->PressedButton;
640  customFields[NUM_CLICKS_FIELD_TAG].first = FRAMEFIELD_FORCE_SERVER_SEND;
641  customFields[NUM_CLICKS_FIELD_TAG].second = std::to_string(device->Internal->ButtonNumClicks);
642 
643  // reset button state if sent SEND_BUTTON_STATE_FOR_N_FRAMES times
644  if (device->Internal->ButtonSentCount > 0)
645  {
646  --device->Internal->ButtonSentCount;
647  }
648  else
649  {
650  device->Internal->PressedButton = NO_BUTTON_TAG;
651  device->Internal->ButtonNumClicks = 0;
652  }
653 
654  // update the image
655  for (auto dataSource : dataSources)
656  {
657  dataSource->AddItem(
658  vtkImage->GetScalarPointer(),
659  dataSource->GetInputImageOrientation(),
660  dataSource->GetInputFrameSize(),
661  VTK_UNSIGNED_CHAR,
662  dataSource->GetImageType() == US_IMG_RGB_COLOR ? 3 : 1,
663  dataSource->GetImageType(),
664  0,
665  device->FrameNumber,
666  unfilteredTimestamp,
667  unfilteredTimestamp,
668  &customFields
669  );
670  }
671 
672  // if transd source is valid, update the ImageToTransd (or similarly named)
673  // transform which localizes the image to the center of the transducer
674  // and correctly sizes the image
675  if (device->Internal->TransdSource)
676  {
677  int i = nfo->width / 2;
678  double transd[2] = { static_cast<double>(i), 0.0 };
679  for (int j = 0; j < nfo->height; j++)
680  {
681  unsigned char imval = vtkImage->GetScalarComponentAsDouble(i, j, 0, 0);
682  if (imval != 0)
683  {
684  transd[0] = i;
685  transd[1] = j;
686  break;
687  }
688  }
689 
690  vtkSmartPointer<vtkMatrix4x4> tsfm = vtkSmartPointer<vtkMatrix4x4>::New();
691  tsfm->Identity();
692  double mmPerPixel = nfo->micronsPerPixel * UM_TO_MM;
693  tsfm->SetElement(0, 0, mmPerPixel);
694  tsfm->SetElement(1, 1, mmPerPixel);
695  tsfm->SetElement(2, 2, mmPerPixel);
696  tsfm->SetElement(0, 3, -transd[0] * mmPerPixel);
697  tsfm->SetElement(1, 3, -transd[1] * mmPerPixel);
698  device->Internal->TransdSource->AddTimeStampedItem(tsfm, TOOL_OK, device->FrameNumber, unfilteredTimestamp, unfilteredTimestamp, &customFields);
699  }
700 
701  device->FrameNumber++;
702 }
703 
704 //-------------------------------------------------------------------------------------------------
705 void vtkPlusClariusOEM::vtkInternal::ImagingFn(CusImagingState ready, int imaging)
706 {
707  if (ready == CusImagingState::ImagingNotReady)
708  {
709  LOG_WARNING("Clarius imaging is not ready yet...");
710  }
711  else if (ready == CusImagingState::ImagingReady)
712  {
713  LOG_INFO("Clarius imaging is " << (imaging ? "running" : "stopped"));
714  }
715  else if (ready == CusImagingState::CertExpired)
716  {
717  LOG_ERROR("Clarius certificate needs to be updated prior to imaging");
718  }
719  else if (ready == CusImagingState::PoorWifi)
720  {
721  LOG_WARNING("Clarius imaging stopped as a result of a poor Wi-Fi connection");
722  }
723  else if (ready == CusImagingState::NoContact)
724  {
725  LOG_INFO("Clarius imaging stopped as a result of no patient contact for specified timeout duration");
726  }
727  else if (ready == CusImagingState::ChargingChanged)
728  {
729  LOG_WARNING("Clarius imaging " << (imaging ? "started" : "stopped") << " due to a change in charging status");
730  }
731  else if (ready == CusImagingState::LowBandwidth)
732  {
733  LOG_INFO("Clarius low bandwidth was detected, imaging parameters were adjusted");
734  }
735  else if (ready == CusImagingState::MotionSensor)
736  {
737  LOG_INFO("Clarius imaging " << (imaging ? "started" : "stopped") << " due to change in motion sensor");
738  }
739  else
740  {
741  LOG_ERROR("Clarius ImagingFn called with unknown 'ready' state");
742  }
743 }
744 
745 //-------------------------------------------------------------------------------------------------
749 void vtkPlusClariusOEM::vtkInternal::ButtonFn(CusButton btn, int clicks)
750 {
752  if (device == NULL)
753  {
754  LOG_ERROR("vtkPlusClariusOEM::vtkInternal::ButtonFn called when Clarius instance was NULL!");
755  return;
756  }
757 
758  // check if still connected
759  if (!device->Connected)
760  {
761  LOG_ERROR("ClariusOEM device unexpectedly disconnected from Clarius Device. IpAddress = " << device->Internal->IpAddress
762  << " Port = " << device->Internal->TcpPort);
763  return;
764  }
765 
766  // set button state
767  device->Internal->ButtonNumClicks = clicks;
768  if (btn == CusButton::ButtonDown)
769  {
770  device->Internal->PressedButton = DOWN_BUTTON_TAG;
771  }
772  else if (btn == CusButton::ButtonUp)
773  {
774  device->Internal->PressedButton = UP_BUTTON_TAG;
775  }
776  else
777  {
778  LOG_WARNING("Unexpected Clarius button value of " << btn << " received in vtkPlusClariusOEM::vtkInternal::ButtonFn");
779  }
780 
781  // reset number of frames to send new button state for
782  device->Internal->ButtonSentCount = SEND_BUTTON_STATE_FOR_N_FRAMES;
783 }
784 
785 //-------------------------------------------------------------------------------------------------
788 void vtkPlusClariusOEM::vtkInternal::ProgressFn(int progress)
789 {
790  LOG_INFO("Updating: " << progress << "%");
791 }
792 
793 //-------------------------------------------------------------------------------------------------
797 void vtkPlusClariusOEM::vtkInternal::ErrorFn(CusErrorCode errorCode, const char* err)
798 {
799  std::stringstream errorSS;
800  errorSS << "Clarius OEM ";
801  switch (errorCode)
802  {
803  case ErrorGeneric:
804  errorSS << "generic error";
805  break;
806  case ErrorSetup:
807  errorSS << "setup error";
808  break;
809  case ErrorProbe:
810  errorSS << "probe error";
811  break;
812  case ErrorApplication:
813  errorSS << "application load error";
814  break;
815  case ErrorSwUpdate:
816  errorSS << "software update error";
817  break;
818  case ErrorGl:
819  errorSS << "GL error";
820  break;
821  case ErrorRawData:
822  errorSS << "raw data error";
823  break;
824  default:
825  errorSS << "unknown error code" << errorCode;
826  break;
827  }
828  errorSS << ": " << err;
829  LOG_ERROR(errorSS.str());
830 }
831 
832 //-------------------------------------------------------------------------------------------------
833 void vtkPlusClariusOEM::vtkInternal::LogUserSettings()
834 {
835  std::stringstream ss;
836  ss << "ProbeSerialNum: " << this->ProbeSerialNum << std::endl;
837  ss << "PathToCert: " << this->PathToCert << std::endl;
838  ss << "ProbeType: " << this->ProbeType << std::endl;
839  ss << "ImagingApplication: " << this->ImagingApplication << std::endl;
840  ss << "FrameSize: [" << this->FrameSize[0] << ", " << this->FrameSize[1] << ", " << this->FrameSize[2] << "]" << std::endl;
841  ss << "EnableAutoGain: " << (this->EnableAutoGain ? "TRUE" : "FALSE") << std::endl;
842  ss << "EnableAutoFocus: " << (this->EnableAutoFocus ? "TRUE" : "FALSE") << std::endl;
843  ss << "Enable5v: " << (this->Enable5v ? "TRUE" : "FALSE") << std::endl;
844  ss << "EnablePenetrationMode: " << (this->EnablePenetrationMode ? "TRUE" : "FALSE") << std::endl;
845  ss << "FreezeOnPoorWifiSignal: " << (this->FreezeOnPoorWifiSignal ? "TRUE" : "FALSE") << std::endl;
846  ss << "KeepAwakeCharging: " << (this->KeepAwakeCharging ? "TRUE" : "FALSE") << std::endl;
847  ss << "PowerButtonsEnabled: " << (this->PowerButtonsEnabled ? "TRUE" : "FALSE") << std::endl;
848  ss << "SoundEnabled: " << (this->SoundEnabled ? "TRUE" : "FALSE") << std::endl;
849  ss << "StationaryTimeoutSec: " << this->StationaryTimeoutSec << std::endl;
850  ss << "WakeOnShake: " << (this->WakeOnShake ? "TRUE" : "FALSE") << std::endl;
851  ss << "ForceLogSend: " << (this->ForceLogSend ? "TRUE" : "FALSE") << std::endl;
852  ss << "EnablePenetrationMode: " << (this->EnablePenetrationMode ? "TRUE" : "FALSE") << std::endl;
853  ss << "ContactDetectionTimeoutSec: " << this->ContactDetectionTimeoutSec << std::endl;
854  ss << "AutoFreezeTimeoutSec: " << this->AutoFreezeTimeoutSec << std::endl;
855  ss << "KeepAwakeTimeoutMin: " << this->KeepAwakeTimeoutMin << std::endl;
856  ss << "UpButtonMode: " << ButtonModeEnumToString[this->UpButtonMode] << std::endl;
857  ss << "DownButtonMode: " << ButtonModeEnumToString[this->DownButtonMode] << std::endl;
858  ss << "ImagingMode: " << this->ImagingModeToString(this->ImagingMode) << std::endl;
859 
860  LOG_INFO(std::endl << "User settings:" << std::endl << ss.str());
861 }
862 
863 //-------------------------------------------------------------------------------------------------
864 // vtkPlusClariusOEM definitions
865 //-------------------------------------------------------------------------------------------------
867 {
868  if (instance == NULL)
869  {
870  instance = new vtkPlusClariusOEM();
871  }
872  return instance;
873 }
874 
875 //-------------------------------------------------------------------------------------------------
877  : Internal(new vtkInternal(this))
878 {
879  this->StartThreadForInternalUpdates = true;
881 
882  this->ImagingParameters->SetDepthMm(DEFAULT_DEPTH_MM);
883  this->ImagingParameters->SetGainPercent(DEFAULT_GAIN_PERCENT);
884  this->ImagingParameters->SetDynRangeDb(DEFAULT_DYNRANGE_PERCENT);
885  this->ImagingParameters->SetTimeGainCompensation(DEFAULT_TGC_DB);
886 
887  // set dummy values to avoid endless error messages when using SlicerOpenIGTLink US Remote Control
888  this->ImagingParameters->SetPowerDb(-1);
891 
892  instance = this;
893 }
894 
895 //-------------------------------------------------------------------------------------------------
897 {
898  // ensure resources released
899  this->InternalDisconnect();
900 
901  if (this->Internal)
902  {
903  delete this->Internal;
904  this->Internal = nullptr;
905  }
906 
907  this->instance = NULL;
908 }
909 
910 //-------------------------------------------------------------------------------------------------
912 {
913  if (instance != NULL)
914  {
915  return instance;
916  }
917  else
918  {
919  // Instance is null, creating new instance
920  instance = new vtkPlusClariusOEM();
921  return instance;
922  }
923 }
924 
925 //-------------------------------------------------------------------------------------------------
926 void vtkPlusClariusOEM::PrintSelf(ostream& os, vtkIndent indent)
927 {
928  LOG_TRACE("vtkPlusClariusOEM::PrintSelf");
929 
930  this->Superclass::PrintSelf(os, indent);
931  os << indent << "ProbeSerialNum: " << this->Internal->ProbeSerialNum << std::endl;
932  os << indent << "PathToCert: " << this->Internal->PathToCert << std::endl;
933  os << indent << "ProbeType: " << this->Internal->ProbeType << std::endl;
934  os << indent << "ImagingApplication: " << this->Internal->ImagingApplication << std::endl;
935  os << indent << "FrameSize: [" << this->Internal->FrameSize[0] << ", " << this->Internal->FrameSize[1] << ", " << this->Internal->FrameSize[2] << "]" << std::endl;
936  os << indent << "EnableAutoGain: " << (this->Internal->EnableAutoGain ? "TRUE" : "FALSE") << std::endl;
937  os << indent << "Enable5v: " << (this->Internal->Enable5v ? "TRUE" : "FALSE") << std::endl;
938  os << indent << "FreezeOnPoorWifiSignal: " << (this->Internal->FreezeOnPoorWifiSignal ? "TRUE" : "FALSE") << std::endl;
939  os << indent << "KeepAwakeCharging: " << (this->Internal->KeepAwakeCharging ? "TRUE" : "FALSE") << std::endl;
940  os << indent << "PowerButtonsEnabled: " << (this->Internal->PowerButtonsEnabled ? "TRUE" : "FALSE") << std::endl;
941  os << indent << "SoundEnabled: " << (this->Internal->SoundEnabled ? "TRUE" : "FALSE") << std::endl;
942  os << indent << "StationaryTimeoutSec: " << this->Internal->StationaryTimeoutSec << std::endl;
943  os << indent << "WakeOnShake: " << (this->Internal->WakeOnShake ? "TRUE" : "FALSE") << std::endl;
944  os << indent << "ForceLogSend: " << (this->Internal->ForceLogSend ? "TRUE" : "FALSE") << std::endl;
945  os << indent << "EnablePenetrationMode: " << (this->Internal->EnablePenetrationMode ? "TRUE" : "FALSE") << std::endl;
946  os << indent << "ContactDetectionTimeoutSec: " << this->Internal->ContactDetectionTimeoutSec << std::endl;
947  os << indent << "AutoFreezeTimeoutSec: " << this->Internal->AutoFreezeTimeoutSec << std::endl;
948  os << indent << "KeepAwakeTimeoutMin: " << this->Internal->KeepAwakeTimeoutMin << std::endl;
949  os << indent << "UpButtonMode: " << ButtonModeEnumToString[this->Internal->UpButtonMode] << std::endl;
950  os << indent << "DownButtonMode: " << ButtonModeEnumToString[this->Internal->DownButtonMode] << std::endl;
951  os << indent << "ImagingMode: " << this->Internal->ImagingModeToString(this->Internal->ImagingMode) << std::endl;
952 }
953 
954 //-------------------------------------------------------------------------------------------------
955 PlusStatus vtkPlusClariusOEM::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
956 {
957  LOG_TRACE("vtkPlusClariusOEM::ReadConfiguration");
958 
959  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
960 
961  // probe serial number
962  XML_READ_STRING_ATTRIBUTE_NONMEMBER_REQUIRED(
963  ProbeSerialNum, this->Internal->ProbeSerialNum, deviceConfig);
964 
965  // path to Clarius certificate
966  XML_READ_STRING_ATTRIBUTE_NONMEMBER_REQUIRED(
967  PathToCert, this->Internal->PathToCert, deviceConfig);
968 
969  // probe type
970  XML_READ_STRING_ATTRIBUTE_NONMEMBER_REQUIRED(
971  ProbeType, this->Internal->ProbeType, deviceConfig);
972  // force probe type string to be entirely uppercase
973  std::transform(
974  this->Internal->ProbeType.begin(),
975  this->Internal->ProbeType.end(),
976  this->Internal->ProbeType.begin(),
977  [](unsigned char c) { return std::toupper(c); }
978  );
979 
980  // imaging application (msk, abdomen, etc.)
981  XML_READ_STRING_ATTRIBUTE_NONMEMBER_REQUIRED(
982  ImagingApplication, this->Internal->ImagingApplication, deviceConfig);
983  // force imaging application string to be entirely lowercase
984  std::transform(
985  this->Internal->ImagingApplication.begin(),
986  this->Internal->ImagingApplication.end(),
987  this->Internal->ImagingApplication.begin(),
988  [](unsigned char c) { return std::tolower(c); }
989  );
990 
991  // frame size
992  int rfs[2] = { static_cast<int>(DEFAULT_FRAME_SIZE[0]), static_cast<int>(DEFAULT_FRAME_SIZE[1]) };
993  if (deviceConfig->GetVectorAttribute("FrameSize", 2, rfs))
994  {
995  if (rfs[0] < 0 || rfs[1] < 0)
996  {
997  LOG_ERROR("Negative frame size defined in config file. Please define a positive frame size.");
998  return PLUS_FAIL;
999  }
1000  FrameSizeType fs = { static_cast<unsigned int>(rfs[0]), static_cast<unsigned int>(rfs[1]), 1 };
1001  this->Internal->FrameSize = fs;
1002  }
1003 
1004  // enable auto gain
1005  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(EnableAutoGain,
1006  this->Internal->EnableAutoGain, deviceConfig);
1007 
1008  // enable 5v rail
1009  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(Enable5v,
1010  this->Internal->Enable5v, deviceConfig);
1011 
1012  // freeze on poor wifi signal
1013  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(FreezeOnPoorWifiSignal,
1014  this->Internal->FreezeOnPoorWifiSignal, deviceConfig);
1015 
1016  // keep awake when charging
1017  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(KeepAwakeCharging,
1018  this->Internal->KeepAwakeCharging, deviceConfig);
1019 
1020  // power buttons enabled
1021  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(PowerButtonsEnabled,
1022  this->Internal->PowerButtonsEnabled, deviceConfig);
1023 
1024  // sound enabled
1025  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(SoundEnabled,
1026  this->Internal->SoundEnabled, deviceConfig);
1027 
1028  // freeze when probe is stationary for a specified duration
1029  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_OPTIONAL(int, StationaryTimeoutSec,
1030  this->Internal->StationaryTimeoutSec, deviceConfig);
1031 
1032  // wake on shake
1033  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(WakeOnShake,
1034  this->Internal->WakeOnShake, deviceConfig);
1035 
1036  // force log send
1037  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(ForceLogSend,
1038  this->Internal->ForceLogSend, deviceConfig);
1039 
1040  // penetration mode enabled
1041  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(EnablePenetrationMode,
1042  this->Internal->EnablePenetrationMode, deviceConfig);
1043 
1044  // contact detection timeout (seconds)
1045  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_OPTIONAL(int, ContactDetectionTimeoutSec,
1046  this->Internal->ContactDetectionTimeoutSec, deviceConfig);
1047 
1048  // auto freeze timeout (seconds)
1049  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_OPTIONAL(int, AutoFreezeTimeoutSec,
1050  this->Internal->AutoFreezeTimeoutSec, deviceConfig);
1051 
1052  // keep awake timeout (seconds)
1053  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_OPTIONAL(int, KeepAwakeTimeoutMin,
1054  this->Internal->KeepAwakeTimeoutMin, deviceConfig);
1055 
1056  // deep sleep timeout (hours)
1057  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_OPTIONAL(int, DeepSleepTimeoutHr,
1058  this->Internal->DeepSleepTimeoutHr, deviceConfig);
1059 
1060  // up button mode
1061  XML_READ_ENUM3_ATTRIBUTE_NONMEMBER_OPTIONAL(UpButtonMode,
1062  this->Internal->UpButtonMode, deviceConfig,
1063  "FREEZE", BUTTON_MODE::FREEZE,
1064  "USER", BUTTON_MODE::USER,
1065  "DISABLED", BUTTON_MODE::DISABLED
1066  );
1067 
1068  // down button mode
1069  XML_READ_ENUM3_ATTRIBUTE_NONMEMBER_OPTIONAL(DownButtonMode,
1070  this->Internal->DownButtonMode, deviceConfig,
1071  "FREEZE", BUTTON_MODE::FREEZE,
1072  "USER", BUTTON_MODE::USER,
1073  "DISABLED", BUTTON_MODE::DISABLED
1074  );
1075 
1076  // enable auto focus
1077  XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(EnableAutoFocus,
1078  this->Internal->EnableAutoFocus, deviceConfig);
1079 
1080  // imaging mode
1081  XML_READ_ENUM_ATTRIBUTE_NONMEMBER_OPTIONAL(ImagingMode, this->Internal->ImagingMode, deviceConfig, this->Internal->ImagingModeToString, 0, ImagingModeStrings.size());
1082 
1083  // read imaging parameters
1084  this->ImagingParameters->ReadConfiguration(deviceConfig);
1085 
1086  // validate data sources
1087  XML_FIND_NESTED_ELEMENT_REQUIRED(dataSourcesElement, deviceConfig, "DataSources");
1088  int numSources = dataSourcesElement->GetNumberOfNestedElements();
1089  for (int i = 0; i < numSources; ++i)
1090  {
1091  vtkXMLDataElement* element = dataSourcesElement->GetNestedElement(i);
1092  if (!igsioCommon::IsEqualInsensitive(element->GetName(), "DataSource"))
1093  {
1094  // if this is not a data source element, skip it
1095  continue;
1096  }
1097 
1098  // if video source, verify correct orientation (MF) to place the (0, 0) voxel
1099  // of the image in the marked corner adjacent to the probe
1100  const char* type = element->GetAttribute("Type");
1101  if (type && igsioCommon::IsEqualInsensitive(type, "Video"))
1102  {
1103  const char* orientation = element->GetAttribute("PortUsImageOrientation");
1104  if (orientation && !igsioCommon::IsEqualInsensitive(orientation, "UF"))
1105  {
1106  LOG_WARNING("Clarius image source has unexpected value '" << orientation << "' for PortUsImageOrientation. Please confirm your image orientation, expected value for most Clarius probes is 'UF'.");
1107  }
1108  }
1109 
1110  // if tool, verify port name is a valid option for Clarius OEM
1111  if (type && igsioCommon::IsEqualInsensitive(type, "Tool"))
1112  {
1113  const char* toolId = element->GetAttribute("Id");
1114  if (toolId == NULL)
1115  {
1116  LOG_ERROR("Failed to initialize Clarius OEM transform: DataSource Id is missing");
1117  return PLUS_FAIL;
1118  }
1119 
1120  const char* portName = element->GetAttribute("PortName");
1121  if (portName && !igsioCommon::IsEqualInsensitive(portName, TRANSD_PORT_NAME))
1122  {
1123  LOG_ERROR("Clarius OEM device only supports Tool items with PortName=\""
1124  << TRANSD_PORT_NAME << "\"");
1125  return PLUS_FAIL;
1126  }
1127 
1128  // set vtkInternal pointer to transd transform source
1129  igsioTransformName transdTransformName(toolId, this->GetToolReferenceFrameName());
1130  std::string transdSourceId = transdTransformName.GetTransformName();
1131  if (this->GetTool(transdSourceId, this->Internal->TransdSource) != PLUS_SUCCESS || this->Internal->TransdSource == NULL)
1132  {
1133  LOG_ERROR("Failed to get ClariusOEM tool: " << transdSourceId);
1134  return PLUS_FAIL;
1135  }
1136  }
1137  }
1138 
1139  XML_READ_STRING_ATTRIBUTE_NONMEMBER_OPTIONAL(
1140  SoftwareUpdateFilename, this->Internal->SoftwareUpdateFilename, deviceConfig);
1141  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_OPTIONAL(
1142  int, SoftwareUpdateHardwareVersion, this->Internal->SoftwareUpdateHardwareVersion, deviceConfig);
1143 
1144  // set vtkInternal pointer to b-mode data source
1145  this->GetVideoSourcesByPortName(vtkPlusDevice::BMODE_PORT_NAME, this->Internal->BModeSources);
1146  this->GetVideoSourcesByPortName(vtkPlusClariusOEM::OVERLAY_PORT_NAME, this->Internal->OverlaySources);
1147 
1148  return PLUS_SUCCESS;
1149 }
1150 
1151 //-------------------------------------------------------------------------------------------------
1152 PlusStatus vtkPlusClariusOEM::WriteConfiguration(vtkXMLDataElement* rootConfigElement)
1153 {
1154  LOG_TRACE("vtkPlusClariusOEM::WriteConfiguration");
1155 
1156  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
1157 
1158  this->ImagingParameters->WriteConfiguration(deviceConfig);
1159 
1160  return PLUS_SUCCESS;
1161 }
1162 
1163 //-------------------------------------------------------------------------------------------------
1165 {
1166  LOG_TRACE("vtkPlusClariusOEM::NotifyConfigured");
1167 
1169  if (device->OutputChannels.size() > 1)
1170  {
1171  LOG_WARNING("vtkPlusClariusOEM is expecting one output channel and there are " <<
1172  this->OutputChannels.size() << " channels. First output channel will be used.");
1173  }
1174 
1175  if (device->OutputChannels.empty())
1176  {
1177  LOG_ERROR("No output channels defined for vtkPlusClariusOEM. Cannot proceed.");
1178  this->CorrectlyConfigured = false;
1179  return PLUS_FAIL;
1180  }
1181 
1182  std::vector<vtkPlusDataSource*> sources;
1183  sources = device->GetVideoSources();
1184  if (sources.size() > 1)
1185  {
1186  LOG_WARNING("More than one output video source found. First will be used");
1187  }
1188  if (sources.size() == 0)
1189  {
1190  LOG_ERROR("Video source required in configuration. Cannot proceed.");
1191  return PLUS_FAIL;
1192  }
1193 
1194  // Check if output channel has data source
1195  vtkPlusDataSource* aSource(NULL);
1196  if (device->OutputChannels[0]->GetVideoSource(aSource) != PLUS_SUCCESS)
1197  {
1198  LOG_ERROR("Unable to retrieve the video source in the vtkPlusClariusOEM device.");
1199  return PLUS_FAIL;
1200  }
1201 
1202  return PLUS_SUCCESS;
1203 }
1204 
1205 //-------------------------------------------------------------------------------------------------
1207 {
1208  LOG_TRACE("vtkPlusClariusOEM: Probe");
1209  LOG_ERROR("vtkPlusClariusOEM::Probe() is not implemented");
1210 
1211  return PLUS_SUCCESS;
1212 };
1213 
1214 //-------------------------------------------------------------------------------------------------
1216 {
1217  LOG_TRACE("vtkPlusClariusOEM::GetSdkVersion");
1218  return "SDK version not available";
1219 }
1220 
1221 //-------------------------------------------------------------------------------------------------
1223 {
1224  LOG_TRACE("vtkPlusClariusOEM::InitializeBLE");
1225 
1226  // find probe by serial number
1227  if (!this->Internal->BleHelper.FindBySerial(this->Internal->ProbeSerialNum))
1228  {
1229  LOG_ERROR("Failed to find BLE connection for Clarius probe with serial number "
1230  << this->Internal->ProbeSerialNum << ". Please make sure the probe is charged"
1231  ", paired, and in range for BLE.");
1232 
1233  std::vector<std::string> probes = this->Internal->BleHelper.RetrieveFoundProbeIds();
1234 
1235  if (!probes.size())
1236  {
1237  // failed to connect to probe by serial number, no active probes found in BLE range
1238  LOG_ERROR("No active Clarius probes found within BLE range");
1239  return PLUS_FAIL;
1240  }
1241 
1242  // print list of nearby active probes
1243  std::stringstream ss;
1244  ss << "Clarius probes configured with Windows Bluetooth are:" << std::endl;
1245  for (const std::string& probe : probes)
1246  {
1247  ss << "\t" << probe << std::endl;
1248  }
1249  LOG_ERROR(ss.str());
1250  return PLUS_FAIL;
1251  }
1252 
1253  // attempt to connect to probe ble network
1254  LOG_INFO("Connecting to BLE network of Clarius probe with SN: " << this->Internal->ProbeSerialNum);
1255  if (this->Internal->BleHelper.Connect() != PLUS_SUCCESS)
1256  {
1257  LOG_ERROR("Failed to connect to Clarius probe. Last error was: "
1258  << this->Internal->BleHelper.GetLastError());
1259  return PLUS_FAIL;
1260  }
1261 
1262  // double check probe is connected
1263  if (!this->Internal->BleHelper.IsProbeConnected())
1264  {
1265  LOG_ERROR("Failed to connect to Clarius probe. Last error was: "
1266  << this->Internal->BleHelper.GetLastError());
1267  return PLUS_FAIL;
1268  }
1269 
1270  // BLE connection succeeded
1271  return PLUS_SUCCESS;
1272 }
1273 
1274 //-------------------------------------------------------------------------------------------------
1276 {
1277  LOG_TRACE("vtkPlusClariusOEM::InitializeProbe");
1278 
1279  // power on the probe
1280  if (this->Internal->BleHelper.RequestProbeOn() != PLUS_SUCCESS)
1281  {
1282  LOG_ERROR("An error occurred during RequestProbeOn. Last error was: "
1283  << this->Internal->BleHelper.GetLastError());
1284  return PLUS_FAIL;
1285  }
1286 
1287  // wait for probe to fully power on
1288  if (!this->Internal->BleHelper.AwaitWifiInfoReady())
1289  {
1290  LOG_ERROR("Timeout occurred while waiting for Clarius probe to power on."
1291  << " Last error was: " << this->Internal->BleHelper.GetLastError());
1292  return PLUS_FAIL;
1293  }
1294 
1295  // force wifi into AP mode
1296  if (this->Internal->BleHelper.ConfigureWifiAP() != PLUS_SUCCESS)
1297  {
1298  LOG_ERROR("An error occurred during ConfigureWifiAP. Last error was: "
1299  << this->Internal->BleHelper.GetLastError());
1300  return PLUS_FAIL;
1301  }
1302 
1303  // get & print wifi info
1304  std::pair<bool, ClariusWifiInfo> infoPair = this->Internal->BleHelper.GetWifiInfo();
1305  if (!infoPair.first)
1306  {
1307  LOG_ERROR("An error occurred during GetWifiInfo. Last error was: "
1308  << this->Internal->BleHelper.GetLastError());
1309  return PLUS_FAIL;
1310  }
1311 
1312  ClariusWifiInfo info = infoPair.second;
1313  if (!info.Ready)
1314  {
1315  LOG_ERROR("Wifi was not ready when connection info was requested");
1316  return PLUS_FAIL;
1317  }
1318  else
1319  {
1320  std::stringstream infoStream;
1321  infoStream << "\tAvailable: " << to_string(info.Available) << std::endl;
1322  infoStream << "\tWifi Mode: " << to_string(info.WifiMode) << std::endl;
1323  infoStream << "\tSSID: " << info.SSID << std::endl;
1324  infoStream << "\tPassword: " << info.Password << std::endl;
1325  infoStream << "\tIPv4: " << info.IPv4 << std::endl;
1326  infoStream << "\tMac Address: " << info.MacAddress << std::endl;
1327  infoStream << "\tControl Port: " << info.ControlPort << std::endl;
1328  infoStream << "\tCast Port: " << info.CastPort << std::endl;
1329  infoStream << "\tChannel: " << info.Channel << std::endl;
1330 
1331  LOG_INFO("Clarius Wifi Info: " << std::endl << infoStream.str());
1332 
1333  this->Internal->Ssid = info.SSID;
1334  this->Internal->Password = info.Password;
1335  this->Internal->IpAddress = info.IPv4;
1336  this->Internal->TcpPort = info.ControlPort;
1337 
1338  // Remove leading/trailing quotes from SSID
1339  if (this->Internal->Ssid[0] == '"')
1340  {
1341  this->Internal->Ssid = this->Internal->Ssid.substr(1, this->Internal->Ssid.size() - 2);
1342  }
1343  // Remove leading/trailing quotes from password
1344  if (this->Internal->Password[0] == '"')
1345  {
1346  this->Internal->Password = this->Internal->Password.substr(1, this->Internal->Password.size() - 2);
1347  }
1348  // Remove leading/trailing quotes from IP address
1349  if (this->Internal->IpAddress[0] == '"')
1350  {
1351  this->Internal->IpAddress = this->Internal->IpAddress.substr(1, this->Internal->IpAddress.size() - 2);
1352  }
1353 
1354  }
1355 
1356  return PLUS_SUCCESS;
1357 }
1358 
1359 //-------------------------------------------------------------------------------------------------
1361 {
1362  LOG_TRACE("vtkPlusClariusOEM::InitializeWifi");
1363 
1364  if (this->Internal->WifiHelper.Initialize() != PLUS_SUCCESS)
1365  {
1366  LOG_ERROR("Failed to initialize Clarius wifi helper");
1367  return PLUS_FAIL;
1368  }
1369 
1370  PlusStatus res = this->Internal->WifiHelper.ConnectToClariusWifi(
1371  this->Internal->Ssid,
1372  this->Internal->Password
1373  );
1374  if (res != PLUS_SUCCESS)
1375  {
1376  LOG_ERROR("Failed to connect to Clarius probe wifi");
1377  return PLUS_FAIL;
1378  }
1379 
1380  return PLUS_SUCCESS;
1381 }
1382 
1383 //-------------------------------------------------------------------------------------------------
1385 {
1386  LOG_TRACE("vtkPlusClariusOEM::InitializeOEM");
1387 
1388  // placeholder argc / argv arguments
1389  int argc = 1;
1390  char** argv = new char* [1];
1391  argv[0] = new char[4];
1392  strcpy(argv[0], "abc");
1393  const char* certPath = "/Clarius";
1394 
1395  // api callback functions
1396  CusListFn listFnPtr = static_cast<CusListFn>(&vtkPlusClariusOEM::vtkInternal::ListFn);
1397  CusConnectFn connectFnPtr = static_cast<CusConnectFn>(&vtkPlusClariusOEM::vtkInternal::ConnectFn);
1398  CusCertFn certFnPtr = static_cast<CusCertFn>(&vtkPlusClariusOEM::vtkInternal::CertFn);
1399  CusPowerDownFn powerDownFnPtr = static_cast<CusPowerDownFn>(&vtkPlusClariusOEM::vtkInternal::PowerDownFn);
1400  CusNewRawImageFn newRawImageFnPtr = static_cast<CusNewRawImageFn>(&vtkPlusClariusOEM::vtkInternal::RawImageFn);
1401  CusNewProcessedImageFn newProcessedImageFnPtr = static_cast<CusNewProcessedImageFn>(&vtkPlusClariusOEM::vtkInternal::ProcessedImageFn);
1402  CusNewSpectralImageFn newSpectralImageFnPtr = static_cast<CusNewSpectralImageFn>(&vtkPlusClariusOEM::vtkInternal::SpectralImageFn);
1403  CusNewImuDataFn newImuDataFnPtr = static_cast<CusNewImuDataFn>(&vtkPlusClariusOEM::vtkInternal::ImuDataFn);
1404  CusImagingFn imagingFnPtr = static_cast<CusImagingFn>(&vtkPlusClariusOEM::vtkInternal::ImagingFn);
1405  CusButtonFn buttonFnPtr = static_cast<CusButtonFn>(&vtkPlusClariusOEM::vtkInternal::ButtonFn);
1406  CusErrorFn errorFnPtr = static_cast<CusErrorFn>(&vtkPlusClariusOEM::vtkInternal::ErrorFn);
1407 
1408  // no b-mode data sources, disable b mode callback
1409  std::vector<vtkPlusDataSource*> bModeSources;
1411  if (this->Internal->BModeSources.empty() && this->Internal->OverlaySources.empty())
1412  {
1413  newProcessedImageFnPtr = nullptr;
1414  }
1415 
1416  // no RF-mode data sources, disable RF-mode callback
1417  std::vector<vtkPlusDataSource*> rfModeSources;
1419  if (rfModeSources.empty())
1420  {
1421  newRawImageFnPtr = nullptr;
1422  }
1423 
1424  try
1425  {
1426  FrameSizeType fs = this->Internal->FrameSize;
1427  CusInitParams initParams;
1428  initParams.storeDir = certPath;
1429  initParams.connectFn = connectFnPtr;
1430  initParams.certFn = certFnPtr;
1431  initParams.powerDownFn = powerDownFnPtr;
1432  initParams.newRawImageFn = newRawImageFnPtr;
1433  initParams.newProcessedImageFn = newProcessedImageFnPtr;
1434  initParams.newSpectralImageFn = newSpectralImageFnPtr;
1435  initParams.newImuDataFn = newImuDataFnPtr;
1436  initParams.imagingFn = imagingFnPtr;
1437  initParams.buttonFn = buttonFnPtr;
1438  initParams.errorFn = errorFnPtr;
1439  initParams.newImuPortFn = nullptr;
1440  initParams.newImuDataFn = nullptr;
1441  initParams.width = fs[0];
1442  initParams.height = fs[1];
1443 
1444  CusInitParams::Args initArgs;
1445  initArgs.argc = argc;
1446  initArgs.argv = argv;
1447  initParams.args = initArgs;
1448 
1449  int result = solumInit(&initParams);
1450 
1451  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1452 
1453  if (result < 0)
1454  {
1455  LOG_ERROR("Failed to initialize Clarius OEM library");
1456  return PLUS_FAIL;
1457  }
1458  }
1459  catch (const std::runtime_error& re)
1460  {
1461  LOG_ERROR("Runtime error on solumInit. Error text: " << re.what());
1462  return PLUS_FAIL;
1463  }
1464  catch (const std::exception& ex)
1465  {
1466  LOG_ERROR("Exception on solumInit. Error text: " << ex.what());
1467  return PLUS_FAIL;
1468  }
1469  catch (...)
1470  {
1471  LOG_ERROR("Unknown failure occurred on solumInit");
1472  return PLUS_FAIL;
1473  }
1474 
1475  return PLUS_SUCCESS;
1476 
1477 }
1478 
1479 //-------------------------------------------------------------------------------------------------
1481 {
1482  LOG_TRACE("vtkPlusClariusOEM::SetClariusCert");
1483 
1484  // load the cert file
1485  std::string fullCertPath = vtkPlusConfig::GetInstance()->GetDeviceSetConfigurationPath(this->Internal->PathToCert);
1486  std::ifstream certFile(fullCertPath);
1487  if (!certFile.is_open())
1488  {
1489  LOG_ERROR("Failed to open Clarius cert file from " << fullCertPath << ". Please check the PathToCert path in your config.");
1490  return PLUS_FAIL;
1491  }
1492 
1493  std::ostringstream sstr;
1494  sstr << certFile.rdbuf();
1495  std::string certStr = sstr.str();
1496 
1497  // set cert in OEM API
1498  if (solumSetCert(certStr.c_str()) != 0)
1499  {
1500  LOG_ERROR("Failed to set Clarius OEM connection certificate");
1501  return PLUS_FAIL;
1502  }
1503  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1504 
1505  return PLUS_SUCCESS;
1506 }
1507 
1508 //-------------------------------------------------------------------------------------------------
1510 {
1511  const char* ip = this->Internal->IpAddress.c_str();
1512  unsigned int port = this->Internal->TcpPort;
1513  LOG_INFO("Attempting connection to Clarius ultrasound on " << ip << ":" << port << " for 25 seconds:");
1514 
1515  std::future<void> connectionBarrierFuture = this->Internal->ConnectionBarrier.get_future();
1516  try
1517  {
1518  CusConnectionParams connParams;
1519  connParams.ipAddress = ip;
1520  connParams.port = port;
1521  int result = solumConnect(&connParams);
1522  if (result != CusConnection::ProbeConnected)
1523  {
1524  LOG_ERROR("Failed to initiate connection to Clarius probe on " << ip << ":" << port <<
1525  ". Return code: " << ConnectEnumToString[result]);
1526  return PLUS_FAIL;
1527  }
1528  }
1529  catch (const std::runtime_error& re)
1530  {
1531  LOG_ERROR("Runtime error on solumConnect. Error text: " << re.what());
1532  return PLUS_FAIL;
1533  }
1534  catch (const std::exception& ex)
1535  {
1536  LOG_ERROR("Exception on solumConnect. Error text: " << ex.what());
1537  return PLUS_FAIL;
1538  }
1539  catch (...)
1540  {
1541  LOG_ERROR("Unknown failure occurred on solumConnect");
1542  return PLUS_FAIL;
1543  }
1544 
1545  // wait for solumConnected call to complete
1546  if (connectionBarrierFuture.wait_for(std::chrono::seconds(25)) != std::future_status::ready)
1547  {
1548  LOG_ERROR("Connection to Clarius device timed out");
1549  return PLUS_FAIL;
1550  }
1551  LOG_INFO("Connected to Clarius probe on " << ip << ":" << port);
1552 
1553  // get list of available probes
1554  CusListFn listFnPtr = static_cast<CusListFn>(&vtkPlusClariusOEM::vtkInternal::ListFn);
1555  this->Internal->ExpectedList = vtkPlusClariusOEM::vtkInternal::EXPECTED_LIST::PROBES;
1556  std::future<std::vector<std::string>> futureProbes = this->Internal->PromiseProbes.get_future();
1557  if (solumProbes(listFnPtr) != 0)
1558  {
1559  LOG_INFO("Failed to retrieve list of valid probe types");
1560  return PLUS_FAIL;
1561  }
1562  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1563 
1564  // wait for probes list to be populated
1565  if (futureProbes.wait_for(std::chrono::seconds(5)) != std::future_status::ready)
1566  {
1567  LOG_ERROR("Failed to retrieve list of valid Clarius probe names");
1568  return PLUS_FAIL;
1569  }
1570  std::vector<std::string> vProbes = futureProbes.get();
1571 
1572  // validate provided probe type
1573  std::string probeType = this->Internal->ProbeType;
1574  if (std::find(vProbes.begin(), vProbes.end(), probeType) == vProbes.end())
1575  {
1576  std::string vProbesStr;
1577  for (const auto& probe : vProbes)
1578  {
1579  vProbesStr += probe + ", ";
1580  }
1581  vProbesStr.pop_back(); vProbesStr.pop_back(); // remove trailing comma and space
1582  LOG_ERROR("Invalid probe type (" << probeType << ") provided, valid probe types are: " << vProbesStr);
1583  return PLUS_FAIL;
1584  }
1585 
1586  // list available imaging applications
1587  this->Internal->ExpectedList = vtkPlusClariusOEM::vtkInternal::EXPECTED_LIST::APPLICATIONS;
1588  std::future<std::vector<std::string>> futureApplications = this->Internal->PromiseApplications.get_future();
1589  if (solumApplications(probeType.c_str(), listFnPtr) != 0)
1590  {
1591  LOG_ERROR("Failed to retrieve list of valid imaging applications");
1592  return PLUS_FAIL;
1593  }
1594  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1595 
1596  // wait for applications list to be populated
1597  if (futureApplications.wait_for(std::chrono::seconds(5)) != std::future_status::ready)
1598  {
1599  LOG_ERROR("Failed to retrieve list of valid Clarius application names");
1600  return PLUS_FAIL;
1601  }
1602  std::vector<std::string> vApps = futureApplications.get();
1603 
1604  // validate provided imaging application
1605  std::string imagingApplication = this->Internal->ImagingApplication;
1606  if (std::find(vApps.begin(), vApps.end(), imagingApplication) == vApps.end())
1607  {
1608  std::string vAppsStr;
1609  for (const auto& app : vApps)
1610  {
1611  vAppsStr += app + ", ";
1612  }
1613  vAppsStr.pop_back(); vAppsStr.pop_back(); // remove trailing comma and space
1614  LOG_ERROR("Invalid imaging application (" << imagingApplication << ") provided, valid imaging applications are: " << vAppsStr);
1615  return PLUS_FAIL;
1616  }
1617 
1618  // configure probe mode
1619  if (solumLoadApplication(probeType.c_str(), imagingApplication.c_str()) == 0)
1620  {
1621  LOG_INFO("Loaded " << imagingApplication << " application on a " << probeType << " probe");
1622  }
1623  else
1624  {
1625  LOG_ERROR("An error occured on call to solumLoadApplication");
1626  }
1627  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1628 
1629  return PLUS_SUCCESS;
1630 }
1631 
1632 //-------------------------------------------------------------------------------------------------
1634 {
1635  LOG_TRACE("vtkPlusClariusOEM::SetInitialUsParams");
1636 
1637  // set imaging depth (mm)
1638  double depthMm = this->ImagingParameters->GetDepthMm();
1639  if (this->SetDepthMm(depthMm) != PLUS_SUCCESS)
1640  {
1641  LOG_WARNING("Failed to set requested imaging depth (mm) in Clarius OEM device, unknown depth will be used");
1642  }
1643 
1644  // set gain (%)
1645  double gainPercent = this->ImagingParameters->GetGainPercent();
1646  if (this->SetGainPercent(gainPercent) != PLUS_SUCCESS)
1647  {
1648  LOG_WARNING("Failed to set requested imaging gain (%) in Clarius OEM device, unknown gain will be used");
1649  }
1650 
1651  // set dynamic range (%)
1652  double dynRangePercent = this->ImagingParameters->GetDynRangeDb();
1653  if (this->SetDynRangePercent(dynRangePercent) != PLUS_SUCCESS)
1654  {
1655  LOG_WARNING("Failed to set requested imaging dynamic range in Clarius OEM device, unknown dynamic range will be used");
1656  }
1657 
1658  // set time gain compensation
1659  std::vector<double> tgcDb = this->ImagingParameters->GetTimeGainCompensation();
1660  if (this->SetTimeGainCompensationDb(tgcDb) != PLUS_SUCCESS)
1661  {
1662  LOG_WARNING("Failed to set requested imaging time gain compensation in Clarius OEM device, unknown time gain compensation will be used");
1663  }
1664 
1665  if (this->SetEnableAutoFocus(this->Internal->EnableAutoFocus) != PLUS_SUCCESS)
1666  {
1667  LOG_WARNING("Failed to set auto focus");
1668  }
1669 
1670  if (this->SetEnableAutoGain(this->Internal->EnableAutoGain) != PLUS_SUCCESS)
1671  {
1672  LOG_WARNING("Failed to set auto gain");
1673  }
1674 
1675  if (this->SetEnablePenetrationMode(this->Internal->EnablePenetrationMode) != PLUS_SUCCESS)
1676  {
1677  LOG_WARNING("Failed to set penetration mode");
1678  }
1679 
1680  return PLUS_SUCCESS;
1681 }
1682 
1683 //-------------------------------------------------------------------------------------------------
1685 {
1686  LOG_TRACE("vtkPlusClariusOEM::InternalConnect");
1687 
1688  if (this->Connected)
1689  {
1690  // Internal connect already called and completed successfully
1691  return PLUS_SUCCESS;
1692  }
1693 
1694  // log user settings for debugging
1695  this->Internal->LogUserSettings();
1696 
1697  // BLE
1698  if (this->InitializeBLE() != PLUS_SUCCESS)
1699  {
1700  LOG_ERROR("Failed to initialize BLE in Clarius OEM device");
1701  this->InternalDisconnect();
1702  return PLUS_FAIL;
1703  }
1704 
1705  // PROBE (power, etc.)
1706  if (this->InitializeProbe() != PLUS_SUCCESS)
1707  {
1708  LOG_ERROR("Failed to initialize probe (power, wifi settings) in Clarius OEM device");
1709  this->InternalDisconnect();
1710  return PLUS_FAIL;
1711  }
1712 
1713  // WIFI
1714  if (this->InitializeWifi() != PLUS_SUCCESS)
1715  {
1716  LOG_ERROR("Failed to initialize wifi in Clarius OEM device");
1717  this->InternalDisconnect();
1718  return PLUS_FAIL;
1719  }
1720 
1721  // OEM library
1722  if (this->InitializeOEM() != PLUS_SUCCESS)
1723  {
1724  LOG_ERROR("Failed to initialize OEM library in Clarius OEM device");
1725  this->InternalDisconnect();
1726  return PLUS_FAIL;
1727  }
1728 
1729  // SET CERTIFICATE
1730  if (this->SetClariusCert() != PLUS_SUCCESS)
1731  {
1732  LOG_ERROR("Failed to set Clarius certificate. Please check your PathToCert is valid, and contains the correct cert for the probe you're connecting to");
1733  this->InternalDisconnect();
1734  return PLUS_FAIL;
1735  }
1736 
1737  // CONFIGURE PROBE SETTINGS
1738  CusProbeSettings settings;
1739  settings.contactDetection = this->Internal->ContactDetectionTimeoutSec;
1740  settings.autoFreeze = this->Internal->AutoFreezeTimeoutSec;
1741  settings.keepAwake = this->Internal->KeepAwakeTimeoutMin;
1742  settings.deepSleep = this->Internal->DeepSleepTimeoutHr;
1743  settings.stationary = this->Internal->StationaryTimeoutSec;
1744  settings.wifiOptimization = this->Internal->FreezeOnPoorWifiSignal;
1745  // settings.wifiSearch
1746  // settings.htWifi
1747  settings.keepAwakeCharging = this->Internal->KeepAwakeCharging;
1748  settings.powerOn = this->Internal->PowerButtonsEnabled;
1749  settings.sounds = this->Internal->SoundEnabled;
1750  settings.wakeOnShake = this->Internal->WakeOnShake;
1751  // settings.bandwidthOptimization
1752  settings.forceLogSend = this->Internal->ForceLogSend;
1753  settings.up = static_cast<CusButtonSetting>(this->Internal->UpButtonMode);
1754  settings.down = static_cast<CusButtonSetting>(this->Internal->DownButtonMode);
1755  settings.handle = CusButtonSetting::ButtonDisabled;
1756  settings.upHold = CusButtonHoldSetting::ButtonHoldDisabled;
1757  settings.downHold = CusButtonHoldSetting::ButtonHoldShutdown;
1758  if (solumSetProbeSettings(&settings) != 0)
1759  {
1760  LOG_ERROR("Failed to set Clarius OEM probe settings");
1761  return PLUS_FAIL;
1762  }
1763  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1764 
1765  // CONFIGURE PROBE MODE
1766  if (this->ConfigureProbeApplication() != PLUS_SUCCESS)
1767  {
1768  LOG_ERROR("Failed to configure Clarius probe application");
1769  this->InternalDisconnect();
1770  return PLUS_FAIL;
1771  }
1772 
1773  // PRINT DEVICE STATS AND PROBE INFO
1774  this->UpdateProbeStatus();
1775  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1776 
1777  CusProbeInfo probeInfo;
1778  if (solumProbeInfo(&probeInfo) != 0)
1779  {
1780  LOG_WARNING("Failed to retrieve solumProbeInfo");
1781  }
1782  std::stringstream ss;
1783  ss << "Version: " << probeInfo.version << std::endl;
1784  ss << "Battery: " << this->Internal->CurrentStatus.battery << "%" << std::endl;
1785  ss << "Temperature: " << this->Internal->CurrentStatus.temperature << "%" << std::endl;
1786  ss << "Elements: " << probeInfo.elements << std::endl;
1787  ss << "Pitch: " << probeInfo.pitch << std::endl;
1788  ss << "Radius: " << probeInfo.radius << "mm" << std::endl;
1789  LOG_INFO(std::endl << "Probe info: " << std::endl << ss.str());
1790 
1791  char versionStringV1[128] = {};
1792  solumFwVersion(CusPlatform::V1, versionStringV1, 128);
1793  LOG_DEBUG("Current Clarius V1 firmware version:\t" << versionStringV1);
1794  char versionStringHD[128] = {};
1795  solumFwVersion(CusPlatform::HD, versionStringHD, 128);
1796  LOG_DEBUG("Current Clarius HD firmware version:\t" << versionStringHD);
1797  char versionStringHD3[128] = {};
1798  solumFwVersion(CusPlatform::HD3, versionStringHD3, 128);
1799  LOG_DEBUG("Current Clarius HD3 firmware version:\t" << versionStringHD3);
1800 
1801  if (this->Internal->SoftwareUpdateFilename != "")
1802  {
1803  CusSwUpdateFn swUpdateFnPtr = static_cast<CusSwUpdateFn>(&vtkPlusClariusOEM::vtkInternal::SwUpdateFn);
1804  CusProgressFn progressFnPtr = static_cast<CusProgressFn>(&vtkPlusClariusOEM::vtkInternal::ProgressFn);
1805 
1806  // Hardware version set to 0, unless there is a reason to force the version (1, 2, or 3)
1807  if (solumSoftwareUpdate(this->Internal->SoftwareUpdateFilename.c_str(),
1808  swUpdateFnPtr, progressFnPtr, this->Internal->SoftwareUpdateHardwareVersion) != 0)
1809  {
1810  LOG_ERROR("Failed to update Clarius firmware");
1811  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1812  this->InternalDisconnect();
1813  return PLUS_FAIL;
1814  }
1815  return PLUS_SUCCESS;
1816  }
1817 
1818  // enable the 5v rail on the top of the Clarius probe
1819  int enable5v = this->Internal->Enable5v ? 1 : 0;
1820  if (solumEnable5v(enable5v) < 0)
1821  {
1822  std::string enstr = (enable5v ? "TRUE" : "FALSE");
1823  LOG_WARNING("Failed to set the state of the Clarius probe 5v rail, provided enable value was: " << enstr);
1824  }
1825  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1826 
1827  // set imaging parameters
1828  this->SetInitialUsParams();
1829 
1830  CusMode mode = CusMode(this->Internal->ImagingMode);
1831  if (solumSetMode(mode) != 0)
1832  {
1833  LOG_ERROR("Failed to set Clarius OEM imaging mode");
1834  return PLUS_FAIL;
1835  }
1836 
1837  // set separate overlays
1838  if (solumSeparateOverlays(1) != 0)
1839  {
1840  LOG_ERROR("Failed to set Clarius separate overlays");
1841  return PLUS_FAIL;
1842  }
1843 
1844  return PLUS_SUCCESS;
1845 };
1846 
1847 //-------------------------------------------------------------------------------------------------
1849 {
1850  LOG_TRACE("vtkPlusClariusOEM::DeInitializeOEM");
1851 
1852  int oemState = solumIsConnected();
1853  if (oemState == CLARIUS_STATE_NOT_INITIALIZED)
1854  {
1855  return;
1856  }
1857 
1858  if (oemState == CLARIUS_STATE_CONNECTED)
1859  {
1860  if (solumEnable5v(CLARIUS_FALSE) < 0)
1861  {
1862  LOG_WARNING("Failed to disable Clarius 5v");
1863  }
1864 
1865  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1866  if (solumRun(CLARIUS_STOP) < 0)
1867  {
1868  LOG_WARNING("Failed to stop Clarius imaging");
1869  }
1870 
1871  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1872  if (solumDisconnect() < 0)
1873  {
1874  LOG_WARNING("Failed to disconnect from Clarius OEM library");
1875  }
1876  }
1877 
1878  if (solumDestroy() < 0)
1879  {
1880  LOG_WARNING("Failed to destroy Clarius OEM library");
1881  }
1882 }
1883 
1884 //-------------------------------------------------------------------------------------------------
1886 {
1887  LOG_TRACE("vtkPlusClariusOEM::DeInitializeWifi");
1888 
1889  if (!this->Internal->WifiHelper.DisconnectFromClariusWifi())
1890  {
1891  LOG_WARNING("Failed to disconnect from Clarius wifi");
1892  }
1893  if (!this->Internal->WifiHelper.DeInitialize())
1894  {
1895  LOG_WARNING("Failed to de-initialize ClariusWifi");
1896  }
1897 }
1898 
1899 //-------------------------------------------------------------------------------------------------
1901 {
1902  LOG_TRACE("vtkPlusClariusOEM::DeInitializeProbe");
1903 
1904  // power off the probe if powered / connected over BLE
1905  if (!this->Internal->BleHelper.IsProbeConnected())
1906  {
1907  return;
1908  }
1909 
1910  if (this->Internal->BleHelper.RequestProbeOff() != PLUS_SUCCESS)
1911  {
1912  LOG_ERROR("An error occurred during RequestProbeOff. Last error was: "
1913  << this->Internal->BleHelper.GetLastError())
1914  }
1915 }
1916 
1917 //-------------------------------------------------------------------------------------------------
1919 {
1920  LOG_TRACE("vtkPlusClariusOEM::DeInitializeBLE");
1921 
1922  // disconnect from probe BLE
1923  if (this->Internal->BleHelper.Disconnect() != PLUS_SUCCESS)
1924  {
1925  LOG_ERROR("An error occurred during Clarius BLE Disconnect. Last error was: "
1926  << this->Internal->BleHelper.GetLastError());
1927  }
1928 }
1929 
1930 //-------------------------------------------------------------------------------------------------
1932 {
1933  LOG_TRACE("vtkPlusClariusOEM::InternalDisconnect");
1934 
1935  // inverse order to initialization
1936  this->DeInitializeOEM();
1937  this->DeInitializeWifi();
1938  this->DeInitializeProbe();
1939  this->DeInitializeBLE();
1940 
1941  return PLUS_SUCCESS;
1942 };
1943 
1944 //-------------------------------------------------------------------------------------------------
1946 {
1947  LOG_TRACE("vtkPlusClariusOEM::InternalStartRecording");
1948 
1949  this->UpdateFrameSize();
1950 
1951  bool running = false;
1952  for (int i = 0; i < 10; ++i)
1953  {
1954  // Attempt to connect. solumRun may not succeed the first time.
1955  if (solumRun(CLARIUS_RUN) < 0)
1956  {
1957  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1958  continue;
1959  }
1960  running = true;
1961  }
1962 
1963  if (!running)
1964  {
1965  LOG_ERROR("Failed to start Clarius imaging");
1966  return PLUS_FAIL;
1967  }
1968  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
1969 
1970  return PLUS_SUCCESS;
1971 }
1972 
1973 //----------------------------------------------------------------------------
1975 {
1976  vtkPlusDataSource* videoSource(NULL);
1977  this->GetFirstVideoSource(videoSource);
1978  videoSource->SetInputFrameSize(this->Internal->FrameSize);
1979  videoSource->SetPixelType(VTK_UNSIGNED_CHAR);
1980  unsigned int numberOfScalarComponents = (videoSource->GetImageType() == US_IMG_RGB_COLOR ? 3 : 1);
1981  videoSource->SetNumberOfScalarComponents(numberOfScalarComponents);
1982  return PLUS_SUCCESS;
1983 }
1984 
1985 //-------------------------------------------------------------------------------------------------
1987 {
1988  LOG_TRACE("vtkPlusClariusOEM::InternalStopRecording");
1989 
1990  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
1991  {
1992  // Not connected, so recording is already stopped.
1993  return PLUS_SUCCESS;
1994  }
1995 
1996  if (solumRun(CLARIUS_STOP) < 0)
1997  {
1998  LOG_ERROR("Failed to stop Clarius imaging");
1999  return PLUS_FAIL;
2000  }
2001  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_LONG_DELAY_MS));
2002 
2003  return PLUS_SUCCESS;
2004 }
2005 
2006 //-------------------------------------------------------------------------------------------------
2008 {
2009  LOG_TRACE("vtkPlusClariusOEM::InternalApplyImagingParameterChange");
2010 
2011  PlusStatus status = PLUS_SUCCESS;
2012 
2013  // depth (mm), note: Clarius uses cm
2016  {
2017  if (this->SetDepthMm(this->ImagingParameters->GetDepthMm()) != PLUS_SUCCESS)
2018  {
2019  LOG_ERROR("Failed to set depth imaging parameter");
2020  status = PLUS_FAIL;
2021  }
2023  }
2024 
2025  // gain (percent)
2028  {
2030  {
2031  LOG_ERROR("Failed to set gain imaging parameter");
2032  status = PLUS_FAIL;
2033  }
2035  }
2036 
2037  // dynamic range (percent)
2040  {
2042  {
2043  LOG_ERROR("Failed to set dynamic range imaging parameter");
2044  status = PLUS_FAIL;
2045  }
2047  }
2048 
2049  // TGC (time gain compensation)
2052  {
2053  std::vector<double> tgcVec;
2055  if (this->SetTimeGainCompensationDb(tgcVec) != PLUS_SUCCESS)
2056  {
2057  LOG_ERROR("Failed to set time gain compensation imaging parameter");
2058  status = PLUS_FAIL;
2059  }
2061  }
2062 
2063  // FOCUS DEPTH
2066  {
2068  {
2069  LOG_ERROR("Failed to set focus depth percent imaging parameter");
2070  status = PLUS_FAIL;
2071  }
2073  }
2074 
2075  return status;
2076 }
2077 
2078 //-------------------------------------------------------------------------------------------------
2080 {
2081  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2082  {
2083  // Connection has not been established yet, return cached parameter value
2084  return this->ImagingParameters->GetDepthMm(aDepthMm);
2085  }
2086 
2087  double oemVal = solumGetParam(CusParam::ImageDepth);
2088  if (oemVal < 0)
2089  {
2090  aDepthMm = -1;
2091  LOG_ERROR("Failed to get DepthMm parameter");
2092  return PLUS_FAIL;
2093  }
2094  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2095 
2096  aDepthMm = oemVal * CM_TO_MM;
2097 
2098  // ensure ImagingParameters is up to date
2099  this->ImagingParameters->SetDepthMm(aDepthMm);
2100 
2101  return PLUS_SUCCESS;
2102 }
2103 
2104 //-------------------------------------------------------------------------------------------------
2106 {
2107  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2108  {
2109  // Connection has not been established yet, parameter value will be set upon connection
2110  this->ImagingParameters->SetDepthMm(aDepthMm);
2111  LOG_INFO("Cached US parameter DepthMm = " << aDepthMm);
2112  return PLUS_SUCCESS;
2113  }
2114 
2115  // attempt to set parameter value
2116  double depthCm = aDepthMm * MM_TO_CM;
2117  if (solumSetParam(CusParam::ImageDepth, depthCm) < 0)
2118  {
2119  LOG_ERROR("Failed to set DepthMm parameter");
2120  return PLUS_FAIL;
2121  }
2122  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2123 
2124  // update imaging parameters & return successfully
2125  this->ImagingParameters->SetDepthMm(aDepthMm);
2126  LOG_INFO("Set US parameter DepthMm to " << aDepthMm);
2127  return PLUS_SUCCESS;
2128 }
2129 
2130 //-------------------------------------------------------------------------------------------------
2132 {
2133  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2134  {
2135  // Connection has not been established yet, return cached parameter value
2136  return this->ImagingParameters->GetGainPercent(aGainPercent);
2137  }
2138 
2139  double oemVal = solumGetParam(CusParam::Gain);
2140  if (oemVal < 0)
2141  {
2142  aGainPercent = -1;
2143  LOG_ERROR("Failed to get GainPercent parameter");
2144  return PLUS_FAIL;
2145  }
2146  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2147 
2148  aGainPercent = oemVal;
2149 
2150  // ensure ImagingParameters is up to date
2151  this->ImagingParameters->SetGainPercent(aGainPercent);
2152 
2153  return PLUS_SUCCESS;
2154 }
2155 
2156 //-------------------------------------------------------------------------------------------------
2158 {
2159  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2160  {
2161  // Connection has not been established yet, parameter value will be set upon connection
2162  this->ImagingParameters->SetGainPercent(aGainPercent);
2163  LOG_INFO("Cached US parameter GainPercent = " << aGainPercent);
2164  return PLUS_SUCCESS;
2165  }
2166 
2167  // attempt to set parameter value
2168  if (solumSetParam(CusParam::Gain, aGainPercent) < 0)
2169  {
2170  LOG_ERROR("Failed to set GainPercent parameter");
2171  return PLUS_FAIL;
2172  }
2173  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2174 
2175  // update imaging parameters & return successfully
2176  this->ImagingParameters->SetGainPercent(aGainPercent);
2177  LOG_INFO("Set US parameter GainPercent to " << aGainPercent);
2178  return PLUS_SUCCESS;
2179 }
2180 
2181 //-------------------------------------------------------------------------------------------------
2183 {
2184  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2185  {
2186  // Connection has not been established yet, return cached parameter value
2187  return this->ImagingParameters->GetDynRangeDb(aDynRangePercent);
2188  }
2189 
2190  double oemVal = solumGetParam(CusParam::DynamicRange);
2191  if (oemVal < 0)
2192  {
2193  aDynRangePercent = -1;
2194  LOG_ERROR("Failed to get DynRange parameter");
2195  return PLUS_FAIL;
2196  }
2197  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2198 
2199  aDynRangePercent = oemVal;
2200 
2201  // ensure ImagingParameters is up to date
2202  this->ImagingParameters->SetDynRangeDb(aDynRangePercent);
2203 
2204  return PLUS_SUCCESS;
2205 }
2206 
2207 //-------------------------------------------------------------------------------------------------
2209 {
2210  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2211  {
2212  // Connection has not been established yet, parameter value will be set upon connection
2213  this->ImagingParameters->SetDynRangeDb(aDynRangePercent);
2214  LOG_INFO("Cached US parameter DynRangePercent = " << aDynRangePercent);
2215  return PLUS_SUCCESS;
2216  }
2217 
2218  // attempt to set parameter value
2219  if (solumSetParam(CusParam::DynamicRange, aDynRangePercent) < 0)
2220  {
2221  LOG_ERROR("Failed to set DynRange parameter");
2222  return PLUS_FAIL;
2223  }
2224  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2225 
2226  // update imaging parameters & return successfully
2227  this->ImagingParameters->SetDynRangeDb(aDynRangePercent);
2228  LOG_INFO("Set US parameter DynRangePercent to " << aDynRangePercent);
2229  return PLUS_SUCCESS;
2230 }
2231 
2232 //-------------------------------------------------------------------------------------------------
2234 {
2235  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2236  {
2237  // Connection has not been established yet, return cached parameter value
2238  return this->ImagingParameters->GetTimeGainCompensation(aTGC);
2239  }
2240 
2241  CusTgc cTGC;
2242  if (solumGetTgc(&cTGC) < 0)
2243  {
2244  LOG_ERROR("Failed to get time gain compensation parameter");
2245  return PLUS_FAIL;
2246  }
2247  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2248 
2249  aTGC.clear();
2250  aTGC.resize(3);
2251  aTGC[0] = cTGC.top;
2252  aTGC[1] = cTGC.mid;
2253  aTGC[2] = cTGC.bottom;
2254 
2255  // ensure imaging parameters are up to date
2257 
2258  return PLUS_SUCCESS;
2259 }
2260 
2261 //-------------------------------------------------------------------------------------------------
2263 {
2264  if (aTGC.size() != 3)
2265  {
2266  LOG_ERROR("vtkPlusClariusOEM time gain compensation parameter must be provided a vector of exactly 3 doubles [top gain, mid gain, bottom gain]");
2267  return PLUS_FAIL;
2268  }
2269 
2270  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2271  {
2272  // Connection has not been established yet, parameter value will be set upon connection
2274  LOG_INFO("Cached US parameter TGC = [" << aTGC[0] << ", " << aTGC[1] << ", " << aTGC[2] << "]");
2275  return PLUS_SUCCESS;
2276  }
2277 
2278  for (int i = 0; i < 3; ++i)
2279  {
2280  if (std::abs(aTGC[i]) > 20)
2281  {
2282  LOG_ERROR("Invalid time gain compensation parameter at index: " << i << " ["
2283  << aTGC[0] << ", "
2284  << aTGC[1] << ", "
2285  << aTGC[2] << "]. "
2286  << "Valid range is [-20, 20]");
2287  return PLUS_FAIL;
2288  }
2289  }
2290 
2291  CusTgc cTGC;
2292  cTGC.top = aTGC[0];
2293  cTGC.mid = aTGC[1];
2294  cTGC.bottom = aTGC[2];
2295  if (solumSetTgc(&cTGC) < 0)
2296  {
2297  LOG_ERROR("Failed to set time gain compensation parameter");
2298  return PLUS_FAIL;
2299  }
2300  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2301 
2302  // update imaging parameters & return successfully
2304  LOG_INFO("Set US parameter TGC to [" << aTGC[0] << ", " << aTGC[1] << ", " << aTGC[2] << "]");
2305  return PLUS_SUCCESS;
2306 }
2307 
2308 //-------------------------------------------------------------------------------------------------
2310 {
2311  int oemState = solumIsConnected();
2312  if (oemState != CLARIUS_STATE_CONNECTED)
2313  {
2314  // Connection has not been established yet, return cached parameter value
2315  aEnableAutoFocus = this->Internal->EnableAutoFocus;
2316  return PLUS_SUCCESS;
2317  }
2318 
2319  aEnableAutoFocus = solumGetParam(CusParam::AutoFocus) > 0;
2320  return PLUS_SUCCESS;
2321 }
2322 
2323 //-------------------------------------------------------------------------------------------------
2325 {
2326  this->Internal->EnableAutoFocus = aEnableAutoFocus;
2327 
2328  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2329  {
2330  // Connection has not been established yet, parameter value will be set upon connection
2331  LOG_INFO("Cached US parameter AutoFocus = " << aEnableAutoFocus);
2332  return PLUS_SUCCESS;
2333  }
2334 
2335  // attempt to set parameter value
2336  if (solumSetParam(AutoFocus, aEnableAutoFocus ? CLARIUS_TRUE : CLARIUS_FALSE))
2337  {
2338  LOG_ERROR("Failed to set AutoFocus parameter");
2339  return PLUS_FAIL;
2340  }
2341  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2342 
2343  return PLUS_SUCCESS;
2344 }
2345 
2346 //-------------------------------------------------------------------------------------------------
2348 {
2349  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2350  {
2351  // Connection has not been established yet, return cached parameter value
2352  aEnablePenetrationMode = this->Internal->EnablePenetrationMode;
2353  return PLUS_SUCCESS;
2354  }
2355 
2356  aEnablePenetrationMode = solumGetParam(PenetrationMode) > 0;
2357  return PLUS_SUCCESS;
2358 }
2359 
2360 //-------------------------------------------------------------------------------------------------
2362 {
2363  LOG_TRACE("vtkPlusClariusOEM::SetEnablePenetrationMode");
2364 
2365  // attempt to set parameter value
2366  if (solumSetParam(PenetrationMode, aEnablePenetrationMode ? CLARIUS_TRUE : CLARIUS_FALSE))
2367  {
2368  LOG_ERROR("Failed to set PenetrationMode parameter");
2369  return PLUS_FAIL;
2370  }
2371  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2372 
2373  return PLUS_SUCCESS;
2374 }
2375 
2376 //-------------------------------------------------------------------------------------------------
2378 {
2379  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2380  {
2381  // Connection has not been established yet, return cached parameter value
2382  return this->ImagingParameters->GetFocusDepthPercent(aFocusDepthPercent);
2383  }
2384 
2385  double focusDepthCm = solumGetParam(CusParam::FocusDepth);
2386  if (focusDepthCm < 0)
2387  {
2388  aFocusDepthPercent = -1;
2389  LOG_ERROR("Failed to get FocusDepthPercent parameter");
2390  return PLUS_FAIL;
2391  }
2392  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2393 
2394  // ensure ImagingParameters is up to date
2397 
2398  return PLUS_SUCCESS;
2399 }
2400 
2401 //-------------------------------------------------------------------------------------------------
2403 {
2404  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2405  {
2406  // Connection has not been established yet, parameter value will be set upon connection
2407  this->ImagingParameters->SetFocusDepthPercent(aFocusDepthPercent);
2408  LOG_INFO("Cached US parameter FocusDepthPercent = " << aFocusDepthPercent);
2409  return PLUS_SUCCESS;
2410  }
2411 
2412  if (aFocusDepthPercent < 0)
2413  {
2414  this->SetEnableAutoFocus(true);
2415  return PLUS_SUCCESS;
2416  }
2417  this->SetEnableAutoFocus(false);
2418 
2419  double focusDepthCm = this->ConvertDepthPercentToCm(aFocusDepthPercent);
2420 
2421  // attempt to set parameter value
2422  if (solumSetParam(CusParam::FocusDepth, focusDepthCm) < 0)
2423  {
2424  LOG_ERROR("Failed to set FocusDepthPercent parameter");
2425  return PLUS_FAIL;
2426  }
2427  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2428 
2429  // update imaging parameters & return successfully
2430  this->ImagingParameters->SetFocusDepthPercent(aFocusDepthPercent);
2431  LOG_INFO("Set US parameter FocusDepthPercent to " << aFocusDepthPercent);
2432  return PLUS_SUCCESS;
2433 }
2434 
2435 //-------------------------------------------------------------------------------------------------
2437 {
2438  double depthMm = 0.0;
2439  if (this->GetDepthMm(depthMm) != PLUS_SUCCESS)
2440  {
2441  return 0.0;
2442  }
2443  return 100.0 * depthMm / (aFocusDepthCm * CM_TO_MM);
2444 }
2445 
2446 //-------------------------------------------------------------------------------------------------
2447 double vtkPlusClariusOEM::ConvertDepthPercentToCm(double aFocusDepthPercent)
2448 {
2449  double depthMm = 0.0;
2450  if (this->GetDepthMm(depthMm) != PLUS_SUCCESS)
2451  {
2452  return 0.0;
2453  }
2454  return (depthMm * MM_TO_CM) * (aFocusDepthPercent / 100.0);
2455 }
2456 
2457 //-------------------------------------------------------------------------------------------------
2459 {
2460  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2461  {
2462  // Connection has not been established yet, return cached parameter value
2463  aEnableAutoGain = this->Internal->EnableAutoGain;
2464  return PLUS_SUCCESS;
2465  }
2466 
2467  aEnableAutoGain = solumGetParam(CusParam::AutoGain) > 0;
2468  return PLUS_SUCCESS;
2469 }
2470 
2471 //-------------------------------------------------------------------------------------------------
2473 {
2474  this->Internal->EnableAutoGain = aEnableAutoGain;
2475 
2476  if (solumIsConnected() != CLARIUS_STATE_CONNECTED)
2477  {
2478  // Connection has not been established yet, parameter value will be set upon connection
2479  LOG_INFO("Cached US parameter AutoGain = " << aEnableAutoGain);
2480  return PLUS_SUCCESS;
2481  }
2482 
2483  // attempt to set parameter value
2484  if (solumSetParam(AutoGain, aEnableAutoGain ? CLARIUS_TRUE : CLARIUS_FALSE))
2485  {
2486  LOG_ERROR("Failed to set AutoGain parameter");
2487  return PLUS_FAIL;
2488  }
2489  std::this_thread::sleep_for(std::chrono::milliseconds(CLARIUS_SHORT_DELAY_MS));
2490 
2491  return PLUS_SUCCESS;
2492 }
PlusStatus ConfigureProbeApplication()
PlusStatus GetEnablePenetrationMode(bool &aEnablePenetrationMode)
static const std::string RFMODE_PORT_NAME
Definition: vtkPlusDevice.h:68
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *deviceConfig)
#define CLARIUS_RUN
bool IsPending(const std::string &paramName) const
double ConvertDepthCmToPercent(double aFocusDepthMm)
static void BGRA32ToRGB24(int width, int height, unsigned char *s, unsigned char *d)
Definition: PixelCodec.h:260
US_IMAGE_TYPE GetImageType()
PlusStatus UpdateFrameSize()
PlusStatus SetGainPercent(double aGainPercent)
std::string Password
Definition: ClariusBLE.h:46
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
#define CLARIUS_TRUE
PlusStatus InitializeProbe()
Phidget_MeshMode mode
Definition: phidget22.h:1332
igsioStatus PlusStatus
Definition: PlusCommon.h:40
PlusStatus GetFocusDepthPercent(double &aFocusDepthPercent) const
static const std::string BMODE_PORT_NAME
Definition: vtkPlusDevice.h:67
PlusStatus GetDepthMm(double &aDepthMm) const
std::string IPv4
Definition: ClariusBLE.h:47
PlusStatus SetEnableAutoGain(bool aEnableAutoGain)
PlusStatus SetInputFrameSize(unsigned int x, unsigned int y, unsigned int z)
std::vector< vtkPlusDataSource * > GetVideoSources() const
#define CLARIUS_STATE_CONNECTED
bool RequirePortNameInDeviceSetConfiguration
std::string to_string(ClariusAvailability avail)
std::string MacAddress
Definition: ClariusBLE.h:48
PlusStatus UpdateProbeStatus()
vtkPlusUsImagingParameters * ImagingParameters
Store the current imaging parameters.
ClariusAvailability
Definition: ClariusBLE.h:24
for i
PlusStatus SetPending(const std::string &paramName, bool pending)
PlusStatus InternalStartRecording() override
#define PLUS_FAIL
Definition: PlusCommon.h:43
PlusStatus GetGainPercent(double &aGainPercent)
void PrintSelf(ostream &os, vtkIndent indent) override
PlusStatus GetVideoSourcesByPortName(const char *aPortName, std::vector< vtkPlusDataSource * > &sources)
static vtkPlusClariusOEM * New()
PlusStatus NotifyConfigured() override
int port
Definition: phidget22.h:2454
PlusStatus SetPixelType(igsioCommon::VTKScalarPixelType pixelType)
PlusStatus SetTimeGainCompensation(const std::vector< double > &tgc)
#define CLARIUS_STATE_NOT_INITIALIZED
static vtkPlusConfig * GetInstance()
PlusStatus GetGainPercent(double aGainPercent) const
ClariusWifiMode
Definition: ClariusBLE.h:32
PlusStatus GetFirstVideoSource(vtkPlusDataSource *&anImage)
virtual PlusStatus Disconnect()
PlusStatus GetTimeGainCompensation(std::vector< double > &tgc) const
PlusStatus SetPowerDb(double aPower)
PlusStatus SetEnablePenetrationMode(bool aEnablePenetrationMode)
PlusStatus WriteConfiguration(vtkXMLDataElement *config) override
unsigned long FrameNumber
PlusStatus GetDepthMm(double &aDepthMm)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
static const std::string OVERLAY_PORT_NAME
PlusStatus SetFocusDepthPercent(double aFocusDepthPercent)
PlusStatus GetFocusDepthPercent(double &aFocusDepthPercent)
PlusStatus GetDynRangePercent(double &aDynamicRangePercent)
PlusStatus GetDynRangeDb(double &aDynRangeDb) const
PlusStatus SetGainPercent(double aGainPercent)
PlusStatus SetDynRangePercent(double aDynamicRangePercent)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
PlusStatus SetDynRangeDb(double aDynRangeDb)
ClariusAvailability Available
Definition: ClariusBLE.h:43
PlusStatus ReadConfiguration(vtkXMLDataElement *config) override
PlusStatus GetTimeGainCompensationDb(std::vector< double > &aTGC)
#define CLARIUS_FALSE
PlusStatus InternalUpdate() override
PlusStatus GetEnableAutoGain(bool &aEnableAutoGain)
PlusStatus Probe() override
ClariusWifiMode WifiMode
Definition: ClariusBLE.h:44
std::string GetSdkVersion() override
bool StartThreadForInternalUpdates
#define CLARIUS_STOP
static PlusStatus ConvertToGray(int inputCompression, int width, int height, unsigned char *s, unsigned char *d)
Definition: PixelCodec.h:146
PlusStatus SetDepthMm(double aDepthMm)
PlusStatus SetEnableAutoFocus(bool aEnableAutoFocus)
PlusStatus InternalDisconnect() override
PlusStatus SetFrequencyMhz(double aFrequencyMhz)
ChannelContainer OutputChannels
PlusStatus SetNumberOfScalarComponents(unsigned int numberOfScalarComponents)
PlusStatus SetFocusDepthPercent(double aFocusDepthPercent)
std::string GetDeviceSetConfigurationPath(const std::string &subPath)
PlusStatus SetInitialUsParams()
PlusStatus InternalConnect() override
PlusStatus GetEnableAutoFocus(bool &aEnableAutoFocus)
std::string GetToolReferenceFrameName() const
bool IsSet(const std::string &paramName) const
PlusStatus InternalStopRecording() override
PlusStatus GetTool(const char *aToolSourceId, vtkPlusDataSource *&aTool) const
static vtkPlusClariusOEM * GetInstance()
PlusStatus SetDepthMm(double aDepthMm)
PlusStatus SetTimeGainCompensationDb(const std::vector< double > &aTGC)
Interface to Clarius Ultrasound Devices This class talks with a Clarius US Scanner over the Clarius O...
PlusStatus InternalApplyImagingParameterChange() override
std::string SSID
Definition: ClariusBLE.h:45
double ConvertDepthPercentToCm(double aFocusDepthPercent)
bool CorrectlyConfigured
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *deviceConfig)
Interface to a 3D positioning tool, video source, or generalized data stream.