RTXI  2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
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, Weill Cornell Medical College
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 
20 #include <connector.h>
21 #include <main_window.h>
22 #include <mutex.h>
23 
25 {
26  QComboBox *blockList0;
27  QComboBox *blockList1;
28  std::vector<IO::Block *> *blocks;
29 };
30 
31 static void buildBlockList(IO::Block *block,void *arg)
32 {
33  block_list_info_t *info = static_cast<block_list_info_t *>(arg);
34  info->blockList0->addItem(QString::fromStdString(block->getName())+" "+QString::number(block->getID()));
35  info->blockList1->addItem(QString::fromStdString(block->getName())+" "+QString::number(block->getID()));
36  info->blocks->push_back(block);
37 }
38 
39 Connector::Panel::Panel(QWidget *parent) : QWidget(parent)
40 {
41 
42  setWhatsThis(
43  "<p><b>Connector:</b><br>The Connector panel allows you to make connections between "
44  "signals and slots in your workspace. Signals are generated by the DAQ card (associated "
45  "with input channels) and by user modules. Available signals are listed in the \"Output "
46  "Block\" drop-down box and available slots are listed in the \"Input Block\" drop-down box. "
47  "The arrow button is a toggle button that turns connections on and off. Clicking the toggle "
48  "button immediately makes a connection active or inactive in real-time. Current connections "
49  "are listed in the \"Connections\" box.</p>");
50 
51  // Make Mdi
52  subWindow = new QMdiSubWindow;
53  subWindow->setWindowIcon(QIcon("/usr/local/share/rtxi/RTXI-widget-icon.png"));
54  subWindow->setAttribute(Qt::WA_DeleteOnClose);
55  subWindow->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint |
56  Qt::WindowMinimizeButtonHint);
58 
59  // Create main layout
60  QGridLayout *layout = new QGridLayout;
61 
62  // Create child widget and layout for output block
63  outputGroup = new QGroupBox(tr("Source"));
64  QVBoxLayout *outputLayout = new QVBoxLayout;
65 
66  // Create elements for output
67  outputLayout->addWidget(new QLabel(tr("Block:")), 1, 0);
68  outputBlock = new QComboBox;
69  outputLayout->addWidget(outputBlock);
70  QObject::connect(outputBlock,SIGNAL(activated(int)),this,SLOT(buildOutputChannelList(void)));
71 
72  outputLayout->addWidget(new QLabel(tr("Channel:")), 2, 0);
73  outputChannel = new QComboBox;
74  outputLayout->addWidget(outputChannel);
75  QObject::connect(outputChannel,SIGNAL(activated(int)),this,SLOT(updateConnectionButton(void)));
76 
77  // Assign layout to child widget
78  outputGroup->setLayout(outputLayout);
79 
80  // Create child widget and layout for connection button
81  buttonGroup = new QGroupBox;
82  QVBoxLayout *buttonLayout = new QVBoxLayout;
83 
84  // Create elements for button
85  connectionButton = new QPushButton("Connect");
86  connectionButton->setCheckable(true);
87  buttonLayout->addWidget(connectionButton);
88  QObject::connect(connectionButton,SIGNAL(toggled(bool)),this,SLOT(toggleConnection(bool)));
89 
90  // Assign layout to child widget
91  buttonGroup->setLayout(buttonLayout);
92 
93  // Create child widget and layout for input block
94  inputGroup = new QGroupBox(tr("Destination"));
95  QVBoxLayout *inputLayout = new QVBoxLayout;
96 
97  // Create elements for output
98  inputLayout->addWidget(new QLabel(tr("Block:")), 1, 0);
99  inputBlock = new QComboBox;
100  inputLayout->addWidget(inputBlock);
101  QObject::connect(inputBlock,SIGNAL(activated(int)),this,SLOT(buildInputChannelList(void)));
102 
103  inputLayout->addWidget(new QLabel(tr("Channel:")), 2, 0);
104  inputChannel = new QComboBox;
105  inputLayout->addWidget(inputChannel);
106  QObject::connect(inputChannel,SIGNAL(activated(int)),this,SLOT(updateConnectionButton(void)));
107 
108  // Assign layout to child widget
109  inputGroup->setLayout(inputLayout);
110 
111  // Create child widget and layout for connections box
112  connectionGroup = new QGroupBox(tr("Connections"));
113  QVBoxLayout *connectionLayout = new QVBoxLayout;
114 
115  // Create elements for connection box
116  connectionBox = new QListWidget;
117  connectionLayout->addWidget(connectionBox);
118  QObject::connect(connectionBox,SIGNAL(itemClicked(QListWidgetItem *)),this,SLOT(highlightConnectionBox(QListWidgetItem *)));
119 
120  // Assign layout to child widget
121  connectionGroup->setLayout(connectionLayout);
122 
123  // Attach child widget to parent widget
124  layout->addWidget(outputGroup, 1, 0, 1, 2);
125  layout->addWidget(buttonGroup, 2, 0, 1, 4);
126  layout->addWidget(inputGroup, 1, 2, 1, 2);
127  layout->addWidget(connectionGroup, 3, 0, 1, 4);
128 
129  // Set layout so that only connectionGroup expands when resized
130  layout->setRowStretch(0, 0);
131  layout->setRowStretch(2, 0);
132  layout->setRowStretch(3, 1);
133 
134  // Attach layout to widget
135  setLayout(layout);
136  setWindowTitle("Connector Panel");
137 
138  // Set layout to Mdi
139  subWindow->setWidget(this);
140  subWindow->resize(500, subWindow->sizeHint().height());
141  show();
142 
144  IO::Connector::getInstance()->foreachBlock(::buildBlockList,&info);
145 
146  if(blocks.size() >= 1)
147  {
150  }
151 
153  for(size_t i = 0, iend = links.size(); i < iend; ++i)
154  {
155  connectionBox->addItem(QString::number(links[i].src->getID())+" "+QString::fromStdString(links[i].src->getName())+" : "+QString::number(links[i].src_idx)+" "+QString::fromStdString(links[i].src->getName(IO::OUTPUT,links[i].src_idx))+" ==> "+
156  QString::number(links[i].dest->getID())+" "+QString::fromStdString(links[i].dest->getName())+" : "+QString::number(links[i].dest_idx)+" "+QString::fromStdString(links[i].dest->getName(IO::INPUT,links[i].dest_idx)));
157  }
158 }
159 
161 {
163 }
164 
166 {
167  if(event->getName() == Event::IO_BLOCK_INSERT_EVENT)
168  {
169  IO::Block *block = reinterpret_cast<IO::Block *>(event->getParam("block"));
170 
171  inputBlock->addItem(QString::fromStdString(block->getName())+QString(" ")+QString::number(block->getID()));
172  outputBlock->addItem(QString::fromStdString(block->getName())+QString(" ")+QString::number(block->getID()));
173  blocks.push_back(block);
174 
175  if(blocks.size() == 1)
176  {
177  buildInputChannelList();
178  buildOutputChannelList();
179  }
180  }
181  else if(event->getName() == Event::IO_BLOCK_REMOVE_EVENT)
182  {
183  IO::Block *block = reinterpret_cast<IO::Block *>(event->getParam("block"));
184 
185  size_t index;
186  for(index = 0; index < blocks.size() && blocks[index] != block; ++index);
187  if(index >= blocks.size())
188  return;
189 
190  size_t current0 = inputBlock->currentIndex();
191  size_t current1 = outputBlock->currentIndex();
192 
193  inputBlock->removeItem(index);
194  outputBlock->removeItem(index);
195  blocks.erase(blocks.begin()+index);
196 
197  if(current0 == index)
198  {
199  inputBlock->setCurrentIndex(0);
200  buildInputChannelList();
201  }
202  if(current1 == index)
203  {
204  outputBlock->setCurrentIndex(0);
205  buildOutputChannelList();
206  }
207  }
208  else if(event->getName() == Event::IO_LINK_INSERT_EVENT)
209  {
210  IO::Block *src = reinterpret_cast<IO::Block *>(event->getParam("src"));
211  size_t src_idx = *reinterpret_cast<size_t *>(event->getParam("src_num"));
212  IO::Block *dest = reinterpret_cast<IO::Block *>(event->getParam("dest"));
213  size_t dest_idx = *reinterpret_cast<size_t *>(event->getParam("dest_num"));
214 
215  connectionBox->addItem(QString::number(src->getID())+" "+QString::fromStdString(src->getName())+" : "+
216  QString::number(src_idx)+" "+QString::fromStdString(src->getName(IO::OUTPUT,src_idx))+" ==> "+
217  QString::number(dest->getID())+" "+QString::fromStdString(dest->getName())+" : "+
218  QString::number(dest_idx)+" "+QString::fromStdString(dest->getName(IO::INPUT,dest_idx)));
219  }
220  else if(event->getName() == Event::IO_LINK_REMOVE_EVENT)
221  {
222  IO::Block *src = reinterpret_cast<IO::Block *>(event->getParam("src"));
223  size_t src_idx = *reinterpret_cast<size_t *>(event->getParam("src_num"));
224  IO::Block *dest = reinterpret_cast<IO::Block *>(event->getParam("dest"));
225  size_t dest_idx = *reinterpret_cast<size_t *>(event->getParam("dest_num"));
226 
227  QString link_name = QString::number(src->getID())+" "+QString::fromStdString(src->getName())+" : "+
228  QString::number(src_idx)+" "+QString::fromStdString(src->getName(IO::OUTPUT,src_idx))+" ==> "+
229  QString::number(dest->getID())+" "+QString::fromStdString(dest->getName())+" : "+
230  QString::number(dest_idx)+" "+QString::fromStdString(dest->getName(IO::INPUT,dest_idx));
231 
232  size_t index;
233  for(index=0; index < connectionBox->count() && connectionBox->item(index)->text() != link_name; ++index);
234  if(index >= connectionBox->count())
235  ERROR_MSG("Connector::Panel::receiveEvent : removing non-existant link.\n");
236  else
237  connectionBox->takeItem(index);
238  }
239 }
240 
242 {
243  inputChannel->clear();
244  if(!inputBlock->count())
245  return;
246 
247  // Get specific block
248  IO::Block *block = blocks[inputBlock->currentIndex()];
249 
250  // Get list of channels from specific block
251  for(size_t i = 0; i < block->getCount(IO::INPUT); ++i)
252  inputChannel->addItem(QString::fromStdString(block->getName(IO::INPUT,i)));
253 
254  updateConnectionButton();
255 }
256 
258 {
259  outputChannel->clear();
260  if(!outputBlock->count())
261  return;
262 
263  // Get specific block
264  IO::Block *block = blocks[outputBlock->currentIndex()];
265 
266  // Get list of channels from specific block
267  for(size_t i = 0; i < block->getCount(IO::OUTPUT); ++i)
268  outputChannel->addItem(QString::fromStdString(block->getName(IO::OUTPUT,i)));
269 
270  updateConnectionButton();
271 }
272 
273 void Connector::Panel::highlightConnectionBox(QListWidgetItem * item)
274 {
275 
276  QString selection = item->text();
277  QString substr;
278  int sep;
279 
280  sep = selection.indexOf(' ');
281  substr = selection.left(sep);
282  selection = selection.right(selection.length()-sep-1);
283  Settings::Object::ID src_id = substr.toULong();
284 
285  sep = selection.indexOf(':');
286  selection = selection.right(selection.length()-sep-2);
287 
288  sep = selection.indexOf(' ');
289  substr = selection.left(sep);
290  selection = selection.right(selection.length()-sep-1);
291  size_t src_idx = substr.toULong();
292 
293  sep = selection.indexOf("==>");
294  selection = selection.right(selection.length()-sep-4);
295 
296  sep = selection.indexOf(' ');
297  substr = selection.left(sep);
298  selection = selection.right(selection.length()-sep-1);
299  Settings::Object::ID dest_id = substr.toULong();
300 
301  sep = selection.indexOf(':');
302  selection = selection.right(selection.length()-sep-2);
303 
304  sep = selection.indexOf(' ');
305  substr = selection.left(sep);
306  selection = selection.right(selection.length()-sep-1);
307  size_t dest_idx = substr.toULong();
308 
309  IO::Block *src = dynamic_cast<IO::Block *>(Settings::Manager::getInstance()->getObject(src_id));
310 
311  size_t index;
312  for(index = 0; index < blocks.size() && blocks[index] != src; ++index);
313  if(index >= blocks.size())
314  ERROR_MSG("Connector::Panel::highlightConnectionBox : highlighted source does not exist.\n");
315 
316  outputBlock->setCurrentIndex(index);
317  buildOutputChannelList();
318  outputChannel->setCurrentIndex(src_idx);
319 
320  IO::Block *dest = dynamic_cast<IO::Block *>(Settings::Manager::getInstance()->getObject(dest_id));
321  for(index = 0; index < blocks.size() && blocks[index] != dest; ++index);
322  if(index >= blocks.size())
323  ERROR_MSG("Connector::Panel::highlightConnectionBox : highlighted destination does not exist.\n");
324 
325  inputBlock->setCurrentIndex(index);
326  buildInputChannelList();
327  inputChannel->setCurrentIndex(dest_idx);
328 
329  updateConnectionButton();
330 }
331 
333 {
334  IO::Block *src = blocks[outputBlock->currentIndex()];
335  IO::Block *dest = blocks[inputBlock->currentIndex()];
336  size_t src_num = outputChannel->currentIndex();
337  size_t dest_num = inputChannel->currentIndex();
338 
339  if(IO::Connector::getInstance()->connected(src,src_num,dest,dest_num) == on)
340  return;
341 
342  if(on)
343  IO::Connector::getInstance()->connect(src,src_num,dest,dest_num);
344  else
345  IO::Connector::getInstance()->disconnect(src,src_num,dest,dest_num);
346 }
347 
349 {
350 
351  if(!inputChannel->count() || !outputChannel->count())
352  {
353  connectionButton->setEnabled(false);
354  }
355  else
356  {
357  connectionButton->setEnabled(true);
358  IO::Block *src = blocks[outputBlock->currentIndex()];
359  IO::Block *dest = blocks[inputBlock->currentIndex()];
360  size_t src_num = outputChannel->currentIndex();
361  size_t dest_num = inputChannel->currentIndex();
362 
363  connectionButton->setChecked(IO::Connector::getInstance()->connected(src,src_num,dest,dest_num));
364  }
365 }
366 
367 void Connector::Panel::buildConnectionList(IO::Block *src,size_t src_num,IO::Block *dest,size_t dest_num,void *arg)
368 {
369  std::vector<link_t> &list = *reinterpret_cast<std::vector<link_t> *>(arg);
370 
371  link_t link =
372  {
373  src,
374  src_num,
375  dest,
376  dest_num,
377  };
378 
379  list.push_back(link);
380 }
381 
382 extern "C" Plugin::Object *createRTXIPlugin(void *)
383 {
385 }
386 
388 {
389  MainWindow::getInstance()->createSystemMenuItem("Connector",this,SLOT(showConnectorPanel(void)));
390 }
391 
393 {
394  if(panel)
395  delete panel;
396  instance = 0;
397 }
398 
400 {
401  if(!panel)
402  panel = new Panel(MainWindow::getInstance()->centralWidget());
403  panel->show();
404 }
405 
407 {
408  if(p == panel)
409  panel = NULL;
410 }
411 
412 static Mutex mutex;
414 
416 {
417  if(instance)
418  return instance;
419 
420  /*************************************************************************
421  * Seems like alot of hoops to jump through, but allocation isn't *
422  * thread-safe. So effort must be taken to ensure mutual exclusion. *
423  *************************************************************************/
424 
425  Mutex::Locker lock(&::mutex);
426  if(!instance)
427  instance = new Plugin();
428 
429  return instance;
430 }
void removeConnectorPanel(Panel *)
Definition: connector.cpp:406
static MainWindow * getInstance(void)
void disconnect(IO::Block *outputBlock, size_t outputChannel, IO::Block *inputBlock, size_t inputChannel)
Definition: io.cpp:293
void showConnectorPanel(void)
Definition: connector.cpp:399
Plugin::Object * createRTXIPlugin(void *)
Definition: connector.cpp:382
void buildOutputChannelList(void)
Definition: connector.cpp:257
QComboBox * outputChannel
Definition: connector.h:71
Definition: io.h:187
const char * IO_LINK_REMOVE_EVENT
Definition: event.cpp:34
QGroupBox * inputGroup
Definition: connector.h:67
void highlightConnectionBox(QListWidgetItem *)
Definition: connector.cpp:273
void buildInputChannelList(void)
Definition: connector.cpp:241
static Connector * getInstance(void)
Definition: io.cpp:421
QGroupBox * outputGroup
Definition: connector.h:66
std::vector< IO::Block * > * blocks
Definition: connector.cpp:28
void receiveEvent(const Event::Object *)
Definition: connector.cpp:165
Definition: mutex.h:28
double arg(const complex _z)
Definition: complex.h:133
unsigned long ID
Definition: settings.h:53
Object * getObject(Object::ID) const
Definition: settings.cpp:212
const char * IO_BLOCK_REMOVE_EVENT
Definition: event.cpp:32
QGroupBox * connectionGroup
Definition: connector.h:64
QComboBox * inputChannel
Definition: connector.h:69
#define ERROR_MSG(fmt, args...)
Definition: debug.h:41
QGroupBox * buttonGroup
Definition: connector.h:65
void connect(IO::Block *outputBlock, size_t outputChannel, IO::Block *inputBlock, size_t inputChannel)
Definition: io.cpp:274
const char * IO_LINK_INSERT_EVENT
Definition: event.cpp:33
static Plugin * instance
Definition: connector.h:97
void createMdi(QMdiSubWindow *)
QListWidget * connectionBox
Definition: connector.h:72
Panel(QWidget *)
Definition: connector.cpp:39
QComboBox * inputBlock
Definition: connector.h:68
void foreachBlock(void(*callback)(Block *, void *), void *param)
Definition: io.cpp:257
QMdiSubWindow * subWindow
Definition: connector.h:62
std::string getName(void) const
Definition: io.h:214
const char * IO_BLOCK_INSERT_EVENT
Definition: event.cpp:31
QComboBox * blockList1
Definition: connector.cpp:27
QComboBox * blockList0
Definition: connector.cpp:26
static void buildConnectionList(IO::Block *, size_t, IO::Block *, size_t, void *)
Definition: connector.cpp:367
static Plugin * getInstance(void)
Definition: connector.cpp:415
ID getID(void) const
Definition: settings.h:131
virtual size_t getCount(flags_t type) const
Definition: io.cpp:82
std::vector< IO::Block * > blocks
Definition: connector.h:74
QComboBox * outputBlock
Definition: connector.h:70
static Manager * getInstance(void)
Definition: settings.cpp:534
void toggleConnection(bool)
Definition: connector.cpp:332
void foreachConnection(void(*callback)(Block *, size_t, Block *, size_t, void *), void *param)
Definition: io.cpp:264
QAction * createSystemMenuItem(const QString &label, const QObject *handler, const char *slot)
void updateConnectionButton(void)
Definition: connector.cpp:348
std::vector< link_t > links
Definition: connector.h:75
Classes associated with the loading/unloading of binaries at run-time.
Definition: plugin.h:35
const char * getName(void) const
Definition: event.h:136
QPushButton * connectionButton
Definition: connector.h:73