CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxPlusConnectWidget.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxPlusConnectWidget.h"
13 
14 #include <QHBoxLayout>
15 #include <QVBoxLayout>
16 #include <QPushButton>
17 #include <QProcess>
18 #include <QComboBox>
19 #include <QLabel>
20 #include <QFileDialog>
21 #include <QSpacerItem>
22 #include "cxLogger.h"
24 #include "cxVisServices.h"
25 #include "cxVideoService.h"
26 #include "cxBoolProperty.h"
27 #include "cxProfile.h"
28 #include "cxProcessWrapper.h"
29 #include "cxSettings.h"
30 #include "cxDataLocations.h"
31 #include "cxFileSelectWidget.h"
32 #include "cxCheckBoxWidget.h"
33 #include "cxFilePreviewWidget.h"
34 
35 #define START_TEXT "Start PlusServer and connect tracking and streaming"
36 #define STOP_TEXT "Stop PlusServer and disconnect tracking and streaming"
37 
38 namespace cx
39 {
41  BaseWidget(parent, "plus_connect_widget" ,"Connect to PlusServer"),
42  mServices(services),
43  mPlusRunning(false),
44  mExternalProcess(new ProcessWrapper("Plus")),
45  mFilePreviewWidget(NULL),
46  mPlusPathComboBox(NULL)
47 {
48  QVBoxLayout* toplayout = new QVBoxLayout(this);
49 
50  QGridLayout* layout = new QGridLayout();
51  toplayout->addLayout(layout);
52 // layout->setMargin(0);
53  int line = 0;
54 
55  // PlusServer path
56  mPlusPath = settings()->value("plus/Path").toString();
57  if(!QFile::exists(mPlusPath))
58  this->searchForPlus();
59 
60  QLabel* plusPathLabel = new QLabel(tr("PlusServer path:"));
61  mPlusPathComboBox = new QComboBox();
62  mPlusPathComboBox->addItem(mPlusPath);
63  QAction* browsePlusPathAction = new QAction(QIcon(":/icons/open.png"), tr("Browse for PlusServer..."), this);
64  connect(browsePlusPathAction, &QAction::triggered, this, &PlusConnectWidget::browsePlusPathSlot);
65  QToolButton* browsePlusPathButton = new QToolButton(this);
66  browsePlusPathButton->setDefaultAction(browsePlusPathAction);
67 
68  layout->addWidget(plusPathLabel, line, 0);
69  layout->addWidget(mPlusPathComboBox, line, 1);
70  layout->addWidget(browsePlusPathButton, line, 2);
71  ++line;
72 
73  layout->addWidget(new QLabel("Plus config File:", this), line, 0);
74  mPlusConfigFileWidget = new FileSelectWidget(this);
75  mPlusConfigFileWidget->setNameFilter(QStringList("*.xml"));
76  mPlusConfigFileWidget->setPaths(this->getPlusConfigFilePaths());
77  mPlusConfigFileWidget->setFilename("Select Plus config file...");
78  connect(mPlusConfigFileWidget, &FileSelectWidget::fileSelected, this, &PlusConnectWidget::configFileFileSelected);
79  layout->addWidget(mPlusConfigFileWidget, line, 1, 1, 2);
80  ++line;
81 
82 
83  mShowPlusOutput = BoolProperty::initialize("showPlusOutput",
84  "Show PlusServer output",
85  "Display all output from PlusServer",
86  false);
87  layout->addWidget(new CheckBoxWidget(this, mShowPlusOutput), line, 0, 1, 3);
88  ++line;
89 
90  QString warningText;
91  warningText = "<font color=red>Note! The start/stop tracking button is not correctly synchronized <br> when using the plus server.</font><br>";
92  QLabel* warningInfoLabel = new QLabel(warningText);
93  layout->addWidget(warningInfoLabel, line, 0, 1, 3);
94  ++line;
95 
96  mConnectButton = new QPushButton(START_TEXT);
97  mConnectButton->setToolTip("Remove all saved clip planes from the selected volume");
98  connect(mConnectButton, &QPushButton::clicked, this, &PlusConnectWidget::connectButtonClickedSlot);
99  layout->addWidget(mConnectButton, line, 0, 1, 3);
100  ++line;
101 
102  mFilePreviewWidget = new FilePreviewWidget(this);
103  mFilePreviewWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
104  layout->addWidget(mFilePreviewWidget, line, 0, 1, 3);
105  ++line;
106 
107 
108 // toplayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
109 
110  connect(mExternalProcess.get(), &ProcessWrapper::stateChanged, this, &PlusConnectWidget::plusAppStateChanged);
111 }
112 
113 void PlusConnectWidget::searchForPlus()
114 {
115  QString possiblePath = QDir::homePath() + "/dev/plus-2.6/PlusB-bin/bin/PlusServer";
116 
117  if(QFile::exists(possiblePath))
118  {
119  CX_LOG_DEBUG() << "Found PlusServer in: " << possiblePath;
120  mPlusPath = possiblePath;
121  }
122 }
123 
124 QStringList PlusConnectWidget::getPlusConfigFilePaths()
125 {
126  QStringList retval;
127  retval << DataLocations::getRootConfigPath() + "/plus"; //Installed
128  retval << QString(CX_OPTIONAL_CONFIG_ROOT) + "/plus"; //CustusS build tree
129  return retval;
130 }
131 
132 void PlusConnectWidget::searchForPlusConfigFile()
133 {
134  // CustusX app folder config/tool
135 // QString possiblePath = QDir::homePath() + "/dev/plus-2.6/PlusB-bin/bin/PlusServer";
136  QString possiblePath = DataLocations::getRootConfigPath() + "/plus/PlusDeviceSet_Server_Bk_Polaris.xml";
137 
138  if(QFile::exists(possiblePath))
139  {
140  CX_LOG_DEBUG() << "Found Plus config file in: " << possiblePath;
141  mPlusConfigFile = possiblePath;
142  }
143 }
144 
145 void PlusConnectWidget::browsePlusPathSlot()
146 {
147  QFileInfo fileInfo(mPlusPath);
148  mPlusPath = QFileDialog::getOpenFileName(this, tr("Find PlusServer executable"), fileInfo.absolutePath());
149 
150  if(!mPlusPath.isEmpty())
151  {
152  mPlusPathComboBox->addItem( mPlusPath );
153  mPlusPathComboBox->setCurrentIndex( mPlusPathComboBox->currentIndex() + 1 );
154  }
155 }
156 
157 void PlusConnectWidget::configFileFileSelected(QString filename)
158 {
159  mPlusConfigFile = filename;
160 
161  QFileInfo fileinfo(filename);
162  mPlusConfigFileWidget->setPath(fileinfo.absolutePath());
163 
164  mFilePreviewWidget->previewFileSlot(filename);
165 }
166 
167 
168 void PlusConnectWidget::connectButtonClickedSlot()
169 {
170  if(mPlusRunning)
171  {
172  CX_LOG_INFO() << "Stopping PlusServer and disconnecting";
173  if(this->stopPlus())
174  {
175  mConnectButton->setText(START_TEXT);
176  mPlusRunning = false;
177  }
178  else
179  CX_LOG_WARNING() << "Failed to stop/disconnect PlusServer";
180  }
181  else
182  {
183  CX_LOG_INFO() << "Starting PlusServer and connecting";
184  if(this->startPlus())
185  {
186  mConnectButton->setText(STOP_TEXT);
187  mPlusRunning = true;
188  }
189  else
190  CX_LOG_WARNING() << "Failed to start/connect PlusServer";
191  }
192 }
193 bool PlusConnectWidget::stopPlus()
194 {
195  StreamerServicePtr streamerService = this->getStreamerService();
196  if(!streamerService)
197  return false;
198 
199  //Trigger OpenIGTLinkStreamerService::stopTrackingAndOpenIGTLinkClientIfStartedFromThisObject
200  streamerService->stop();
201 
202  if(mExternalProcess->isRunning())
203  mExternalProcess->getProcess()->close();
204 
205  //Stop video streaming
206  mServices->video()->closeConnection();
207 
208  //Stop output
209  disconnect(mExternalProcess->getProcess(), &QProcess::readyRead, this, &PlusConnectWidget::processReadyRead);
210 
211 
212  return mExternalProcess->waitForFinished(1000);
213 // mExternalProcess.reset();
214 }
215 
216 bool PlusConnectWidget::startPlus()
217 {
218  StreamerServicePtr streamerService = this->getStreamerService();
219  if(!streamerService)
220  return false;
221 
222  if(!this->startExternalPlusServer())
223  return false;
224 
225  this->changeOpenIGTLinkStreamerParameter(streamerService, QString(OPENIGTLINK3_STREAMER_START_TRACKING), QVariant("true"));
226  this->changeOpenIGTLinkStreamerParameter(streamerService, QString(OPENIGTLINK3_STREAMER_IP), QVariant("127.0.0.1"));
227  this->startOpenIGTLink3VideoStreaming();
228 
229  return true;
230 }
231 
232 void PlusConnectWidget::startOpenIGTLink3VideoStreaming()
233 {
234  mServices->video()->setConnectionMethod(OPENIGTLINK3_STREAMER);
235  mServices->video()->openConnection();
236 }
237 
238 void PlusConnectWidget::plusAppStateChanged()
239 {
240  if(mExternalProcess->getProcess()->state() == QProcess::Starting)
241  {
242  mConnectButton->setText("PlusServer starting...");
243  return;
244  }
245 
246  if(!mExternalProcess->isRunning())
247  {
248  CX_LOG_DEBUG() << "plusAppStateChanged not running. Stopping...";
249  mPlusRunning = false;
250  mConnectButton->setText(START_TEXT);
251  this->stopPlus();
252  }
253  else
254  {
255  mPlusRunning = true;
256  mConnectButton->setText(STOP_TEXT);
257  }
258 
259 }
260 
261 bool PlusConnectWidget::startExternalPlusServer()
262 {
263  if(!this->configFileIsValid())
264  return false;
265 
266  QStringList arguments;
267 
268  arguments << QString("--config-file=").append(mPlusConfigFile);
269 
270  //Show Plus output
271  if(mShowPlusOutput->getValue())
272  {
273  QProcess *process = mExternalProcess->getProcess();
274  connect(process, &QProcess::readyRead, this, &PlusConnectWidget::processReadyRead);
275  process->setProcessChannelMode(QProcess::MergedChannels);
276  process->setReadChannel(QProcess::StandardOutput);
277  }
278  else
279  {
280  //Turn off Plus Warnings if Plus output is disabled
281  arguments << QString("--verbose=1");
282  }
283 
284  mExternalProcess->launch(mPlusPath, arguments);
285  return mExternalProcess->waitForStarted(1000);
286 }
287 
288 bool PlusConnectWidget::configFileIsValid()
289 {
290  QFileInfo fileInfo(mPlusConfigFile);
291  if(fileInfo.exists())
292  return true;
293  else
294  {
295  CX_LOG_WARNING() << "Plus config file is not existing: " << mPlusConfigFile;
296  return false;
297  }
298 }
299 
300 void PlusConnectWidget::processReadyRead()
301 {
302  report(QString(mExternalProcess->getProcess()->readAllStandardOutput()));
303 }
304 
305 QDomElement PlusConnectWidget::getXmlVideoElement()
306 {
307  XmlOptionFile xmlFile = profile()->getXmlSettings().descend("video");
308  QDomElement element = xmlFile.getElement("video");
309  return element;
310 }
311 
312 void PlusConnectWidget::changeOpenIGTLinkStreamerParameter(StreamerServicePtr streamerService, QString parameterName, QVariant value)
313 {
314  QDomElement element = this->getXmlVideoElement();
315 
316  std::vector<PropertyPtr> settings = streamerService->getSettings(element);
317  for(unsigned i = 0; i < settings.size(); ++i)
318  {
319  if (settings[i]->getUid().contains(parameterName))
320  {
321  CX_LOG_DEBUG() << "Changing parameter " << parameterName << " to: " << value.toString() << " in OpenIGTLinkStreamer";
322  settings[i]->setValueFromVariant(value);
323  }
324  }
325 }
326 
327 StreamerServicePtr PlusConnectWidget::getStreamerService()
328 {
329  StreamerServicePtr streamer = mServices->video()->getStreamerService(OPENIGTLINK3_STREAMER);
330  if(!streamer)
331  CX_LOG_WARNING() << "PlusConnectWidget::getOpenIGTLinkStreamerService(): Cannot get StreamerServicePtr";
332 
333  return streamer;
334 }
335 
336 }//namespace cx
void fileSelected(QString name)
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:160
static BoolPropertyPtr initialize(const QString &uid, QString name, QString help, bool value, QDomNode root=QDomNode())
void setNameFilter(QStringList filter)
#define STOP_TEXT
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:40
Widget for the BoolPropertyBase.
#define CX_LOG_INFO
Definition: cxLogger.h:96
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: cxSettings.cpp:66
QDomElement getElement()
return the current element
virtual void previewFileSlot(const QString &absoluteFilePath)
View a xml document.
void setFilename(QString name)
void setPath(QString path)
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:21
PlusConnectWidget(VisServicesPtr services, QWidget *parent)
void setPaths(QStringList paths)
static QString getRootConfigPath()
return path to root config folder. May be replaced with getExistingConfigPath()
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:88
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
void report(QString msg)
Definition: cxLogger.cpp:69
#define CX_LOG_WARNING
Definition: cxLogger.h:98
boost::shared_ptr< class StreamerService > StreamerServicePtr
#define START_TEXT
Helper class for xml files used to store ssc/cx data.
Widget for displaying and selecting a single file.
Namespace for all CustusX production code.