RTXI  3.0.0
The Real-Time eXperiment Interface Reference Manual
connector.cpp
Go to the documentation of this file.
1 /*
2  The Real-Time eXperiment Interface (RTXI)
3  Copyright (C) 2011 Georgia Institute of Technology, University of Utah,
4  Weill Cornell Medical College
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 
19 */
20 
21 #include "connector.hpp"
22 
23 #include "main_window.hpp"
24 
25 Connector::Panel::Panel(QMainWindow* mw, Event::Manager* ev_manager)
26  : Widgets::Panel(std::string(Connector::MODULE_NAME), mw, ev_manager)
27  , buttonGroup(new QGroupBox)
28  , inputBlock(new QComboBox)
29  , inputChannel(new QComboBox)
30  , outputBlock(new QComboBox)
31  , outputFlag(new QComboBox)
32  , outputChannel(new QComboBox)
33  , connectionBox(new QListWidget)
34 {
35  setWhatsThis(
36  "<p><b>Connector:</b><br>The Connector panel allows you to make "
37  "connections between "
38  "signals and slots in your workspace. Signals are generated by the DAQ "
39  "card (associated "
40  "with input channels) and by user modules. Available signals are listed "
41  "in the \"Output "
42  "Block\" drop-down box and available slots are listed in the \"Input "
43  "Block\" drop-down box. "
44  "The arrow button is a toggle button that turns connections on and off. "
45  "Clicking the toggle "
46  "button immediately makes a connection active or inactive in real-time. "
47  "Current connections "
48  "are listed in the \"Connections\" box.</p>");
49 
50  // Create main layout
51  auto* layout = new QGridLayout;
52 
53  // Create child widget and layout for output block
54  outputGroup = new QGroupBox(tr("Source"));
55  auto* outputLayout = new QVBoxLayout;
56 
57  // Create elements for output
58  outputLayout->addWidget(new QLabel(tr("Block:")), 1, Qt::Alignment());
59  outputLayout->addWidget(outputBlock);
60  outputLayout->addWidget(new QLabel(tr("Flag:")), 2, Qt::Alignment());
61  outputLayout->addWidget(outputFlag);
62  buildOutputFlagList();
63 
64  QObject::connect(outputBlock,
65  SIGNAL(activated(int)),
66  this,
67  SLOT(buildOutputChannelList(void)));
68  QObject::connect(outputBlock,
69  SIGNAL(activated(int)),
70  this,
71  SLOT(updateConnectionButton(void)));
72  QObject::connect(outputFlag,
73  SIGNAL(activated(int)),
74  this,
75  SLOT(buildOutputChannelList(void)));
76  QObject::connect(outputFlag,
77  SIGNAL(activated(int)),
78  this,
79  SLOT(updateConnectionButton(void)));
80 
81  outputLayout->addWidget(new QLabel(tr("Channel:")), 3, Qt::Alignment());
82  outputLayout->addWidget(outputChannel);
83  QObject::connect(outputChannel,
84  SIGNAL(activated(int)),
85  this,
86  SLOT(updateConnectionButton(void)));
87 
88  // Assign layout to child widget
89  outputGroup->setLayout(outputLayout);
90 
91  // Create child widget and layout for connection button
92  auto* buttonLayout = new QVBoxLayout;
93 
94  // Create elements for button
95  connectionButton = new QPushButton("Connect");
96  connectionButton->setCheckable(true);
97  buttonLayout->addWidget(connectionButton);
98  QObject::connect(connectionButton,
99  SIGNAL(clicked(bool)),
100  this,
101  SLOT(toggleConnection(bool)));
102 
103  // Assign layout to child widget
104  buttonGroup->setLayout(buttonLayout);
105 
106  // Create child widget and layout for input block
107  inputGroup = new QGroupBox(tr("Destination"));
108  auto* inputLayout = new QVBoxLayout;
109 
110  // Create elements for output
111  inputLayout->addWidget(new QLabel(tr("Block:")), 1, Qt::Alignment());
112  inputLayout->addWidget(inputBlock);
113  QObject::connect(inputBlock,
114  SIGNAL(activated(int)),
115  this,
116  SLOT(buildInputChannelList(void)));
117  QObject::connect(inputBlock,
118  SIGNAL(activated(int)),
119  this,
120  SLOT(updateConnectionButton(void)));
121 
122  inputLayout->addWidget(new QLabel(tr("Channel:")), 2, Qt::Alignment());
123  inputLayout->addWidget(inputChannel);
124  QObject::connect(inputChannel,
125  SIGNAL(activated(int)),
126  this,
127  SLOT(updateConnectionButton(void)));
128 
129  // Assign layout to child widget
130  inputGroup->setLayout(inputLayout);
131 
132  // Create child widget and layout for connections box
133  connectionGroup = new QGroupBox(tr("Connections"));
134  auto* connectionLayout = new QVBoxLayout;
135 
136  // Create elements for connection box
137  connectionLayout->addWidget(connectionBox);
138  // QObject::connect(connectionBox,
139  // SIGNAL(itemClicked(QListWidgetItem*)),
140  // this,
141  // SLOT(highlightConnectionBox(QListWidgetItem*)));
142 
143  // Assign layout to child widget
144  connectionGroup->setLayout(connectionLayout);
145 
146  // Attach child widget to parent widget
147  layout->addWidget(outputGroup, 1, 0, 1, 2);
148  layout->addWidget(buttonGroup, 2, 0, 1, 4);
149  layout->addWidget(inputGroup, 1, 2, 1, 2);
150  layout->addWidget(connectionGroup, 3, 0, 1, 4);
151 
152  // Set layout so that only connectionGroup expands when resized
153  layout->setRowStretch(0, 0);
154  layout->setRowStretch(2, 0);
155  layout->setRowStretch(3, 1);
156 
157  // Attach layout to widget
158  setLayout(layout);
159  setWindowTitle(QString(this->getName().c_str()));
160 
161  // Set layout to Mdi
162  this->getMdiWindow()->resize(500, this->sizeHint().height());
163 
164  // populate field with block and connection info
165  this->syncBlockInfo();
166 
167  QObject::connect(
168  this, SIGNAL(updateBlockInfo(void)), this, SLOT(syncBlockInfo(void)));
169 
170  // a change to any of the connection parameters should highlight box or not
171  QObject::connect(inputBlock,
172  SIGNAL(currentTextChanged(const QString&)),
173  this,
174  SLOT(highlightConnectionBox(const QString&)));
175  QObject::connect(outputBlock,
176  SIGNAL(currentTextChanged(const QString&)),
177  this,
178  SLOT(highlightConnectionBox(const QString&)));
179  QObject::connect(inputChannel,
180  SIGNAL(currentTextChanged(const QString&)),
181  this,
182  SLOT(highlightConnectionBox(const QString&)));
183  QObject::connect(outputChannel,
184  SIGNAL(currentTextChanged(const QString&)),
185  this,
186  SLOT(highlightConnectionBox(const QString&)));
187  QObject::connect(outputFlag,
188  SIGNAL(currentTextChanged(const QString&)),
189  this,
190  SLOT(highlightConnectionBox(const QString&)));
191 }
192 
193 void Connector::Panel::buildBlockList()
194 {
195  auto prev_input_block = inputBlock->currentData();
196  auto prev_output_block = outputBlock->currentData();
197  inputBlock->clear();
198  outputBlock->clear();
200  this->getRTXIEventManager()->postEvent(&event);
201  this->blocks =
202  std::any_cast<std::vector<IO::Block*>>(event.getParam("blockList"));
203  for (auto* block : this->blocks) {
204  if (block->getName().find("Probe") != std::string::npos
205  || block->getName().find("Recording") != std::string::npos)
206  {
207  continue;
208  }
209  this->inputBlock->addItem(QString(block->getName().c_str()) + " "
210  + QString::number(block->getID()),
211  QVariant::fromValue(block));
212  this->outputBlock->addItem(QString(block->getName().c_str()) + " "
213  + QString::number(block->getID()),
214  QVariant::fromValue(block));
215  }
216  inputBlock->setCurrentIndex(inputBlock->findData(prev_input_block));
217  outputBlock->setCurrentIndex(outputBlock->findData(prev_output_block));
218 }
219 
220 void Connector::Panel::buildConnectionList()
221 {
223  this->getRTXIEventManager()->postEvent(&event);
224  this->links = std::any_cast<std::vector<RT::block_connection_t>>(
225  event.getParam("connections"));
226 }
227 
229 {
230  switch (event->getType()) {
237  this->updatePanelInfo();
238  break;
239  default:
240  break;
241  }
242 }
243 
244 void Connector::Plugin::updatePanelInfo()
245 {
246  dynamic_cast<Connector::Panel*>(this->getPanel())->updateBlockInfo();
247 }
248 
249 // This slot will be called by the plugin to update block list and connection
250 // info after a block has been inserted. The update will show up in the panel
251 void Connector::Panel::syncBlockInfo()
252 {
253  this->buildBlockList();
254  this->buildInputChannelList();
255  this->buildOutputChannelList();
256  this->buildConnectionList();
257 
258  connectionBox->clear();
259  QString temp_list_text;
260  QListWidgetItem* temp_list_item = nullptr;
261  for (auto conn : this->links) {
262  if (conn.dest->getName().find("Probe") != std::string::npos) {
263  continue;
264  }
265  temp_list_text =
266  QString::number(conn.src->getID()) + " "
267  + QString(conn.src->getName().c_str()) + " "
268  + QString(
269  conn.src->getChannelName(conn.src_port_type, conn.src_port).c_str())
270  + " ==> " + QString::number(conn.dest->getID()) + " "
271  + QString(conn.dest->getName().c_str()) + " "
272  + QString(conn.dest->getChannelName(IO::INPUT, conn.dest_port).c_str());
273  temp_list_item = new QListWidgetItem(temp_list_text);
274  temp_list_item->setData(Qt::UserRole, QVariant::fromValue(conn));
275  connectionBox->addItem(temp_list_item);
276  }
277 }
278 
279 void Connector::Panel::buildInputChannelList()
280 {
281  auto prev_channel = inputChannel->currentData();
282  inputChannel->clear();
283  if (inputBlock->count() == 0) {
284  return;
285  }
286 
287  // Get specific block
288  if (!inputBlock->currentData().isValid()) {
289  return;
290  }
291  auto* block = inputBlock->currentData().value<IO::Block*>();
292 
293  // Get list of channels from specific block
294  for (size_t i = 0; i < block->getCount(IO::INPUT); ++i) {
295  inputChannel->addItem(QString(block->getChannelName(IO::INPUT, i).c_str()),
296  QVariant::fromValue(i));
297  }
298  inputChannel->setCurrentIndex(inputChannel->findData(prev_channel));
299  updateConnectionButton();
300 }
301 
302 void Connector::Panel::buildOutputChannelList()
303 {
304  auto prev_channel = outputChannel->currentData();
305  outputChannel->clear();
306  if (outputBlock->count() == 0) {
307  return;
308  }
309 
310  // Get specific block
311  if (!outputBlock->currentData().isValid()
312  || !outputFlag->currentData().isValid())
313  {
314  return;
315  }
316  auto* block = outputBlock->currentData().value<IO::Block*>();
317  auto direction = outputFlag->currentData().value<IO::flags_t>();
318 
319  // Get list of channels from specific block
320  for (size_t i = 0; i < block->getCount(direction); ++i) {
321  outputChannel->addItem(QString(block->getChannelName(direction, i).c_str()),
322  QVariant::fromValue(i));
323  }
324  outputChannel->setCurrentIndex(outputChannel->findData(prev_channel));
325  updateConnectionButton();
326 }
327 
328 void Connector::Panel::buildOutputFlagList()
329 {
330  outputFlag->addItem(QString("OUTPUT"), QVariant::fromValue(IO::OUTPUT));
331  outputFlag->addItem(QString("INPUT"), QVariant::fromValue(IO::INPUT));
332 }
333 
334 void Connector::Panel::highlightConnectionBox(const QString& /*item*/)
335 {
336  // build info in the output group
337  const QVariant src_variant = this->outputBlock->currentData();
338  const QVariant src_type_variant = this->outputFlag->currentData();
339  const QVariant src_chan_variant = this->outputChannel->currentData();
340  const QVariant dest_variant = this->inputBlock->currentData();
341  const QVariant dest_chan_variant = this->inputChannel->currentData();
342  if (!src_variant.isValid() || !src_type_variant.isValid()
343  || !dest_variant.isValid() || !src_chan_variant.isValid()
344  || !dest_chan_variant.isValid())
345  {
346  connectionBox->setCurrentRow(-1);
347  return;
348  }
349 
350  RT::block_connection_t current_connection;
351  current_connection.src = src_variant.value<IO::Block*>();
352  current_connection.src_port_type = src_type_variant.value<IO::flags_t>();
353  current_connection.src_port = src_chan_variant.value<size_t>();
354  current_connection.dest = dest_variant.value<IO::Block*>();
355  current_connection.dest_port = dest_chan_variant.value<size_t>();
356  QVariant temp_conn_variant;
357  // update connection button state
358  for (int row = 0; row < connectionBox->count(); ++row) {
359  temp_conn_variant = this->connectionBox->item(row)->data(Qt::UserRole);
360  if (temp_conn_variant.value<RT::block_connection_t>() == current_connection)
361  {
362  connectionBox->setCurrentRow(row);
363  return;
364  };
365  }
366  connectionBox->setCurrentRow(-1);
367 }
368 
369 void Connector::Panel::reverseHighlightConnectionBox(
370  const QListWidgetItem* item)
371 {
372  const auto connection =
373  item->data(Qt::UserRole).value<RT::block_connection_t>();
374  outputBlock->setCurrentIndex(
375  outputBlock->findData(QVariant::fromValue(connection.src)));
376  outputFlag->setCurrentIndex(
377  outputFlag->findData(QVariant::fromValue(connection.src_port_type)));
378  outputChannel->setCurrentIndex(
379  outputChannel->findData(QVariant::fromValue(connection.src_port)));
380  inputBlock->setCurrentIndex(
381  inputBlock->findData(QVariant::fromValue(connection.dest)));
382  inputChannel->setCurrentIndex(
383  inputChannel->findData(QVariant::fromValue(connection.dest_port)));
384  updateConnectionButton();
385 }
386 
387 void Connector::Panel::toggleConnection(bool down)
388 {
389  RT::block_connection_t connection;
390  const QVariant src_block = outputBlock->currentData();
391  const QVariant src_type = outputFlag->currentData();
392  const QVariant src_port = outputChannel->currentData();
393  const QVariant dest_block = inputBlock->currentData();
394  const QVariant dest_port = inputChannel->currentData();
395  if (!src_block.isValid() || !src_type.isValid() || !src_port.isValid()
396  || !dest_block.isValid() || !dest_port.isValid())
397  {
398  connectionButton->setDown(false);
399  // Somehow the user was able to click the button when it should be
400  // disabled... let's fix that
401  connectionButton->setEnabled(false);
402  return;
403  }
404  connection.src = src_block.value<IO::Block*>();
405  connection.src_port_type = src_type.value<IO::flags_t>();
406  connection.src_port = src_port.value<size_t>();
407  connection.dest = dest_block.value<IO::Block*>();
408  connection.dest_port = dest_port.value<size_t>();
409 
410  if (down) {
412  event.setParam("connection", std::any(connection));
413  this->getRTXIEventManager()->postEvent(&event);
414  } else {
416  event.setParam("connection", std::any(connection));
417  this->getRTXIEventManager()->postEvent(&event);
418  }
419  connectionButton->setDown(down);
420  connectionButton->setChecked(down);
421  syncBlockInfo();
422 }
423 
424 void Connector::Panel::updateConnectionButton()
425 {
426  if (inputChannel->count() == 0 || outputChannel->count() == 0) {
427  connectionButton->setEnabled(false);
428  return;
429  }
430  // QVariant connection_variant =
431  // this->connectionBox->currentItem()->data(Qt::UserRole);
432  const QVariant src_block = outputBlock->currentData();
433  const QVariant src_type = outputFlag->currentData();
434  const QVariant src_port = outputChannel->currentData();
435  const QVariant dest_block = inputBlock->currentData();
436  const QVariant dest_port = inputChannel->currentData();
437  if (!src_block.isValid() || !src_type.isValid() || !src_port.isValid()
438  || !dest_block.isValid() || !dest_port.isValid())
439  {
440  connectionButton->setDown(false);
441  connectionButton->setChecked(false);
442  connectionButton->setEnabled(false);
443  return;
444  }
445  connectionButton->setEnabled(true);
446  RT::block_connection_t connection;
447  connection.src = src_block.value<IO::Block*>();
448  connection.src_port_type = src_type.value<IO::flags_t>();
449  connection.src_port = src_port.value<size_t>();
450  connection.dest = dest_block.value<IO::Block*>();
451  connection.dest_port = dest_port.value<size_t>();
452 
453  QListWidgetItem* temp_item = nullptr;
454  for (int i = 0; i < connectionBox->count(); i++) {
455  temp_item = connectionBox->item(i);
456  if (temp_item->data(Qt::UserRole).value<RT::block_connection_t>()
457  == connection) {
458  connectionButton->setDown(true);
459  connectionButton->setChecked(true);
460  return;
461  }
462  }
463  connectionButton->setDown(false);
464  connectionButton->setChecked(false);
465 }
466 
468  : Widgets::Plugin(ev_manager, std::string(Connector::MODULE_NAME))
469 {
470 }
471 
472 std::unique_ptr<Widgets::Plugin> Connector::createRTXIPlugin(
473  Event::Manager* ev_manager)
474 {
475  return std::make_unique<Connector::Plugin>(ev_manager);
476 }
477 
478 Widgets::Panel* Connector::createRTXIPanel(QMainWindow* main_window,
479  Event::Manager* ev_manager)
480 {
481  return static_cast<Widgets::Panel*>(
482  new Connector::Panel(main_window, ev_manager));
483 }
484 
485 std::unique_ptr<Widgets::Component> Connector::createRTXIComponent(
486  Widgets::Plugin* /*unused*/)
487 {
488  return {nullptr};
489 }
490 
492 {
497  return fact;
498 }
void updateBlockInfo()
Panel(QMainWindow *mw, Event::Manager *ev_manager)
Definition: connector.cpp:25
void receiveEvent(Event::Object *event) override
Definition: connector.cpp:228
Plugin(Event::Manager *ev_manager)
Definition: connector.cpp:467
Event::Type getType() const
Definition: event.cpp:228
Definition: io.hpp:79
QMdiSubWindow * getMdiWindow()
Definition: widgets.hpp:286
std::string getName()
Definition: widgets.hpp:422
std::unique_ptr< Widgets::Component > createRTXIComponent(Widgets::Plugin *host_plugin)
Definition: connector.cpp:485
constexpr std::string_view MODULE_NAME
Definition: connector.hpp:35
std::unique_ptr< Widgets::Plugin > createRTXIPlugin(Event::Manager *ev_manager)
Definition: connector.cpp:472
Widgets::Panel * createRTXIPanel(QMainWindow *main_window, Event::Manager *ev_manager)
Definition: connector.cpp:478
Widgets::FactoryMethods getFactories()
Definition: connector.cpp:491
@ IO_LINK_INSERT_EVENT
Definition: event.hpp:71
@ IO_LINK_REMOVE_EVENT
Definition: event.hpp:72
@ RT_THREAD_INSERT_EVENT
Definition: event.hpp:60
@ RT_DEVICE_INSERT_EVENT
Definition: event.hpp:64
@ RT_THREAD_REMOVE_EVENT
Definition: event.hpp:61
@ IO_BLOCK_QUERY_EVENT
Definition: event.hpp:73
@ IO_ALL_CONNECTIONS_QUERY_EVENT
Definition: event.hpp:76
@ RT_DEVICE_REMOVE_EVENT
Definition: event.hpp:65
flags_t
Definition: io.hpp:52
@ INPUT
Definition: io.hpp:54
@ OUTPUT
Definition: io.hpp:53
Definition: rt.hpp:35
IO::Block * dest
Definition: rt.hpp:190
IO::Block * src
Definition: rt.hpp:187
IO::flags_t src_port_type
Definition: rt.hpp:188
std::unique_ptr< Widgets::Plugin >(* createPlugin)(Event::Manager *)
Function that returns a smart pointer to plugin object.
Definition: widgets.hpp:145
Widgets::Panel *(* createPanel)(QMainWindow *, Event::Manager *)
Definition: widgets.hpp:170
std::unique_ptr< Widgets::Component >(* createComponent)(Widgets::Plugin *)
Definition: widgets.hpp:156