RTXI  2.4
The Real-Time eXperiment Interface Documentation
oscilloscope.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  This class creates and controls the drawing parameters
21  A control panel is instantiated for all the active channels/modules
22  and user selection is enabled to change color, style, width and other
23  oscilloscope properties.
24  */
25 
26 #include <qwt_plot_renderer.h>
27 
28 #include <debug.h>
29 #include <main_window.h>
30 #include <rt.h>
31 #include <workspace.h>
32 #include <cmath>
33 #include <sstream>
34 
35 #include "oscilloscope.h"
36 
37 namespace
38 {
39 class SyncEvent : public RT::Event
40 {
41 public:
42  int callback(void)
43  {
44  return 0;
45  };
46 }; // SyncEvent
47 
48 struct channel_info
49 {
50  QString name;
51  IO::Block *block;
52  IO::flags_t type;
53  size_t index;
54  double previous; // stores previous value for trigger and downsample buffer
55 }; // channel_info
56 } // namespace
57 
59 extern "C" Plugin::Object * createRTXIPlugin(void *)
60 {
62 }
63 
64 // Create and insert scope into menu
65 Oscilloscope::Plugin::Plugin(void)
66 {
67  MainWindow::getInstance()->createSystemMenuItem("Oscilloscope",this,SLOT(createOscilloscopePanel(void)));
68 }
69 
70 // Kill me
71 Oscilloscope::Plugin::~Plugin(void)
72 {
73  while (panelList.size())
74  delete panelList.front();
75  instance = 0;
76 }
77 
78 // Create oscilloscope(s) and puts them in the list
80 {
81  Panel *d_panel = new Panel(MainWindow::getInstance()->centralWidget());
82  panelList.push_back(d_panel);
83 }
84 
85 void Oscilloscope::Plugin::removeOscilloscopePanel(Panel *d_panel)
86 {
87  panelList.remove(d_panel);
88 }
89 
91 {
92  size_t i = 0;
93  for (std::list<Panel *>::iterator j = panelList.begin(), end = panelList.end(); j != end; ++j)
94  (*j)->deferred(s.loadState(QString::number(i++).toStdString()));
95 }
96 
98 {
99  for (size_t i = 0; i < static_cast<size_t> (s.loadInteger("Num Panels")); ++i)
100  {
101  Panel *d_panel = new Panel(MainWindow::getInstance()->centralWidget());
102  panelList.push_back(d_panel);
103  d_panel->load(s.loadState(QString::number(i).toStdString()));
104  }
105 }
106 
108 {
109  s.saveInteger("Num Panels", panelList.size());
110  size_t n = 0;
111  for (std::list<Panel *>::const_iterator i = panelList.begin(), end = panelList.end(); i != end; ++i)
112  s.saveState(QString::number(n++).toStdString(), (*i)->save());
113 }
114 
115 void Oscilloscope::Panel::receiveEvent(const ::Event::Object *event)
116 {
117  if (event->getName() == Event::IO_BLOCK_INSERT_EVENT)
118  {
119  IO::Block *block = reinterpret_cast<IO::Block *> (event->getParam("block"));
120  if (block)
121  {
122  // Update the list of blocks
123  blocksList->addItem(QString::fromStdString(block->getName())+" "+QString::number(block->getID()));
124  blocks.push_back(block);
125  if (blocksList->count() == 1)
126  buildChannelList();
127  }
128  }
129  else if (event->getName() == Event::IO_BLOCK_REMOVE_EVENT)
130  {
131  IO::Block *block = reinterpret_cast<IO::Block *> (event->getParam("block"));
132  if (block)
133  {
134  // Find the index of the block in the blocks vector
135  size_t index;
136  for (index = 0; index < blocks.size() && blocks[index]
137  != block; ++index);
138 
139  if (index < blocks.size())
140  {
141  // Stop displaying channels coming from the removed block
142  for (std::list<Scope::Channel>::iterator i = scopeWindow->getChannelsBegin(), end = scopeWindow->getChannelsEnd(); i != end;)
143  {
144  struct channel_info *info = reinterpret_cast<struct channel_info *> (i->getInfo());
145  if (info->block == block)
146  {
147  // If triggering on this channel disable triggering
148  if(trigsChanList->currentText() != "<None>")
149  if (i->getLabel() == scopeWindow->getTriggerChannel()->getLabel())
150  {
151  scopeWindow->setTrigger(Scope::NONE, scopeWindow->getTriggerThreshold(),
152  scopeWindow->getChannelsEnd(), scopeWindow->getTriggerWindow());
153  showDisplayTab();
154  }
155 
156  struct channel_info *info = reinterpret_cast<struct channel_info *> (i->getInfo());
157  std::list<Scope::Channel>::iterator chan = i++;
158  bool active = setInactiveSync();
159  scopeWindow->removeChannel(chan);
160  flushFifo();
161  setActive(active);
162  delete info;
163  }
164  else
165  ++i;
166  }
167 
168  // Update the list of blocks
169  size_t current = blocksList->currentIndex();
170  blocksList->removeItem(index);
171  blocks.erase(blocks.begin() + index);
172 
173  if (current == index)
174  {
175  blocksList->setCurrentIndex(0);
176  buildChannelList();
177  }
178  showTab(0);
179  }
180  else
181  std::cout << "Oscilloscope::Panel::receiveEvent : removed block never inserted\n";
182  }
183  }
184  else if (event->getName() == Event::RT_POSTPERIOD_EVENT)
185  {
186  scopeWindow->setPeriod(RT::System::getInstance()->getPeriod() * 1e-6);
187  adjustDataSize();
188  showTab(0);
189  }
190 }
191 
192 // Slot for enabling user specified channel
193 void Oscilloscope::Panel::activateChannel(bool active)
194 {
195  bool enable = active && blocksList->count() && channelsList->count();
196  scalesList->setEnabled(enable);
197  offsetsEdit->setEnabled(enable);;
198  offsetsList->setEnabled(enable);
199  colorsList->setEnabled(enable);
200  widthsList->setEnabled(enable);
201  stylesList->setEnabled(enable);
202 }
203 
204 void Oscilloscope::Panel::apply(void)
205 {
206  switch (tabWidget->currentIndex())
207  {
208  case 0:
209  applyChannelTab();
210  break;
211  case 1:
212  applyDisplayTab();
213  break;
214  default:
215  ERROR_MSG("Oscilloscope::Panel::showTab : invalid tab\n");
216  }
217 }
218 
219 void Oscilloscope::Panel::buildChannelList(void)
220 {
221 
222  channelsList->clear();
223  if (!blocksList->count())
224  return;
225 
226  if (blocksList->currentIndex() < 0)
227  blocksList->setCurrentIndex(0);
228 
229  IO::Block *block = blocks[blocksList->currentIndex()];
230  IO::flags_t type;
231  switch (typesList->currentIndex())
232  {
233  case 0:
234  type = Workspace::INPUT;
235  break;
236  case 1:
237  type = Workspace::OUTPUT;
238  break;
239  case 2:
240  type = Workspace::PARAMETER;
241  break;
242  case 3:
243  type = Workspace::STATE;
244  break;
245  default:
246  ERROR_MSG("Oscilloscope::Panel::buildChannelList : invalid type selection\n");
247  type = Workspace::INPUT;
248  }
249 
250  for (size_t i = 0; i < block->getCount(type); ++i)
251  channelsList->addItem(QString::fromStdString(block->getName(type, i)));
252 
253  showChannelTab();
254 }
255 
256 void Oscilloscope::Panel::showTab(int index)
257 {
258  switch (index)
259  {
260  case 0:
261  showChannelTab();
262  break;
263  case 1:
264  showDisplayTab();
265  break;
266  default:
267  ERROR_MSG("Oscilloscope::Panel::showTab : invalid tab\n");
268  }
269 }
270 
271 void Oscilloscope::Panel::applyChannelTab(void)
272 {
273  if (blocksList->count() <= 0 || channelsList->count() <= 0)
274  return;
275 
276  IO::Block *block = blocks[blocksList->currentIndex()];
277  IO::flags_t type;
278  switch (typesList->currentIndex())
279  {
280  case 0:
281  type = Workspace::INPUT;
282  break;
283  case 1:
284  type = Workspace::OUTPUT;
285  break;
286  case 2:
287  type = Workspace::PARAMETER;
288  break;
289  case 3:
290  type = Workspace::STATE;
291  break;
292  default:
293  ERROR_MSG("Oscilloscope::Panel::applyChannelTab : invalid type\n");
294  typesList->setCurrentIndex(0);
295  type = Workspace::INPUT;
296  }
297 
298  struct channel_info *info;
299  std::list<Scope::Channel>::iterator i = scopeWindow->getChannelsBegin();
300  for (std::list<Scope::Channel>::iterator end = scopeWindow->getChannelsEnd(); i
301  != end; ++i)
302  {
303  info = reinterpret_cast<struct channel_info *> (i->getInfo());
304  if (info->block == block && info->type == type && info->index
305  == static_cast<size_t> (channelsList->currentIndex()))
306  break;
307  }
308 
309  if (!activateButton->isChecked())
310  {
311  if (i != scopeWindow->getChannelsEnd())
312  {
313  // If triggering on this channel disable triggering
314  if(trigsChanList->currentText() != "<None>")
315  if (i->getLabel() == scopeWindow->getTriggerChannel()->getLabel())
316  scopeWindow->setTrigger(Scope::NONE, scopeWindow->getTriggerThreshold(),
317  scopeWindow->getChannelsEnd(), scopeWindow->getTriggerWindow());
318 
319  bool active = setInactiveSync();
320  scopeWindow->removeChannel(i);
321  flushFifo();
322  setActive(active);
323  delete info;
324  }
325  }
326  else
327  {
328  if (i == scopeWindow->getChannelsEnd())
329  {
330  info = new struct channel_info;
331 
332  info->block = block;
333  info->type = type;
334  info->index = channelsList->currentIndex();
335  info->previous = 0.0;
336  info->name = QString::number(block->getID())+" "+QString::fromStdString(block->getName(type, channelsList->currentIndex()));
337 
338  bool active = setInactiveSync();
339  QwtPlotCurve *curve = new QwtPlotCurve(info->name);
340  i = scopeWindow->insertChannel(info->name + " 50 mV/div", 2.0, 0.0, QPen(QColor(255,0,16,255), 1, Qt::SolidLine), curve, info);
341  flushFifo();
342  setActive(active);
343  }
344 
345  double scale;
346  switch (scalesList->currentIndex() % 4)
347  {
348  case 0:
349  scale = pow(10, 1 - scalesList->currentIndex() / 4);
350  break;
351  case 1:
352  scale = 5 * pow(10, -scalesList->currentIndex() / 4);
353  break;
354  case 2:
355  scale = 2.5 * pow(10, -scalesList->currentIndex() / 4);
356  break;
357  case 3:
358  scale = 2 * pow(10, -scalesList->currentIndex() / 4);
359  break;
360  default:
361  ERROR_MSG("Oscilloscope::Panel::applyChannelTab : invalid scale selection\n");
362  scale = 1.0;
363  }
364  if (scale != i->getScale())
365  {
366  scopeWindow->setChannelScale(i, scale);
367  scopeWindow->setChannelLabel(i, info->name + " - " + scalesList->currentText().simplified());
368  }
369  scopeWindow->setChannelOffset(i, offsetsEdit->text().toDouble() * pow(10, -3 * offsetsList->currentIndex()));
370 
371  QPen pen;
372  switch (colorsList->currentIndex())
373  {
374  case 0:
375  pen.setColor(QColor(255,0,16,255));
376  break;
377  case 1:
378  pen.setColor(QColor(255,164,5,255));
379  break;
380  case 2:
381  pen.setColor(QColor(43,206,72,255));
382  break;
383  case 3:
384  pen.setColor(QColor(0,117,220,255));
385  break;
386  case 4:
387  pen.setColor(QColor(178,102,255,255));
388  break;
389  case 5:
390  pen.setColor(QColor(0,153,143,255));
391  break;
392  case 6:
393  pen.setColor(QColor(83,81,84,255));
394  break;
395  default:
396  ERROR_MSG("Oscilloscope::Panel::applyChannelTab : invalid color selection\n");
397  pen.setColor(QColor(255,0,16,255));
398  }
399  pen.setWidth(widthsList->currentIndex() + 1);
400  switch (stylesList->currentIndex())
401  {
402  case 0:
403  pen.setStyle(Qt::SolidLine);
404  break;
405  case 1:
406  pen.setStyle(Qt::DashLine);
407  break;
408  case 2:
409  pen.setStyle(Qt::DotLine);
410  break;
411  case 3:
412  pen.setStyle(Qt::DashDotLine);
413  break;
414  case 4:
415  pen.setStyle(Qt::DashDotDotLine);
416  break;
417  default:
418  ERROR_MSG("Oscilloscope::Panel::applyChannelTab : invalid style selection\n");
419  pen.setStyle(Qt::SolidLine);
420  }
421  scopeWindow->setChannelPen(i, pen);
422  }
423  scopeWindow->replot();
424  showChannelTab();
425 }
426 
427 void Oscilloscope::Panel::applyDisplayTab(void)
428 {
429  // Update downsample rate
430  downsample_rate = ratesSpin->value();
431 
432  // Update refresh rate for oscillscope
433  scopeWindow->setRefresh(refreshsSpin->value());
434 
435  // Update X divisions
436  double divT;
437  if (timesList->currentIndex() % 3 == 1)
438  divT = 2 * pow(10, 3 - timesList->currentIndex() / 3);
439  else if (timesList->currentIndex() % 3 == 2)
440  divT = pow(10, 3 - timesList->currentIndex() / 3);
441  else
442  divT = 5 * pow(10, 3 - timesList->currentIndex() / 3);
443  scopeWindow->setDivT(divT);
444  scopeWindow->setPeriod(RT::System::getInstance()->getPeriod() * 1e-6);
445  adjustDataSize();
446 
447  // Update trigger direction
448  Scope::trig_t trigDirection = static_cast<Scope::trig_t> (trigsGroup->id(trigsGroup->checkedButton()));
449 
450  // Update trigger threshold
451  double trigThreshold = trigsThreshEdit->text().toDouble() * pow(10, -3 * trigsThreshList->currentIndex());
452 
453  // Update pre-trigger window for displaying
454  double trigWindow = trigWindowEdit->text().toDouble() * pow(10, -3 * trigWindowList->currentIndex());
455 
456  std::list<Scope::Channel>::iterator trigChannel = scopeWindow->getChannelsEnd();
457  for (std::list<Scope::Channel>::iterator i = scopeWindow->getChannelsBegin(), end = scopeWindow->getChannelsEnd(); i != end; ++i)
458  if (i->getLabel() == trigsChanList->currentText())
459  {
460  trigChannel = i;
461  break;
462  }
463  if (trigChannel == scopeWindow->getChannelsEnd())
464  trigDirection = Scope::NONE;
465 
466  scopeWindow->setTrigger(trigDirection, trigThreshold, trigChannel, trigWindow);
467 
468  adjustDataSize();
469  scopeWindow->replot();
470  showDisplayTab();
471 }
472 
473 struct block_list_info_t
474 {
475  QComboBox *blockList;
476  std::vector<IO::Block *> *blocks;
477 };
478 
479 static void buildBlockList(IO::Block *block, void *arg)
480 {
481  block_list_info_t *info = static_cast<block_list_info_t *> (arg);
482  info->blockList->addItem(QString::fromStdString(block->getName())+" "+QString::number(block->getID()));
483  info->blocks->push_back(block);
484 }
485 
486 QWidget * Oscilloscope::Panel::createChannelTab(QWidget *parent)
487 {
488 
489  setWhatsThis("<p><b>Oscilloscope: Channel Options</b><br>"
490  "Use the dropdown boxes to select the signal streams you want to plot from "
491  "any loaded modules or your DAQ device. You may change the plotting scale for "
492  "the signal, apply a DC offset, and change the color and style of the line.</p>");
493 
494  QWidget *page = new QWidget(parent);
495 
496  // Create group and layout for buttons at bottom of scope
497  QGridLayout *bttnLayout = new QGridLayout(page);
498 
499  // Create Channel box
500  QHBoxLayout *row1Layout = new QHBoxLayout;
501  QLabel *channelLabel = new QLabel(tr("Channel:"),page);
502  row1Layout->addWidget(channelLabel);
503  blocksList = new QComboBox(page);
504  blocksList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
505  blocksList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
506  block_list_info_t info = {blocksList, &this->blocks};
507  IO::Connector::getInstance()->foreachBlock(::buildBlockList, &info);
508  QObject::connect(blocksList,SIGNAL(activated(int)),this,SLOT(buildChannelList(void)));
509  row1Layout->addWidget(blocksList);
510 
511  // Create Type box
512  typesList = new QComboBox(page);
513  typesList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
514  typesList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
515  typesList->addItem("Input");
516  typesList->addItem("Output");
517  typesList->addItem("Parameter");
518  typesList->addItem("State");
519  QObject::connect(typesList,SIGNAL(activated(int)),this,SLOT(buildChannelList(void)));
520  row1Layout->addWidget(typesList);
521 
522  // Create Channels box
523  channelsList = new QComboBox(page);
524  channelsList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
525  channelsList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
526  QObject::connect(channelsList,SIGNAL(activated(int)),this,SLOT(showChannelTab(void)));
527  row1Layout->addWidget(channelsList);
528 
529  // Create elements for display box
530  row1Layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
531  QLabel *scaleLabel = new QLabel(tr("Scale:"),page);
532  row1Layout->addWidget(scaleLabel);
533  scalesList = new QComboBox(page);
534  row1Layout->addWidget(scalesList);
535  QFont scalesListFont("DejaVu Sans Mono");
536  scalesList->setFont(scalesListFont);
537  scalesList->addItem("10 V/div"); // 0 case 0
538  scalesList->addItem("5 V/div"); // 1 case 1
539  scalesList->addItem("2.5 V/div"); // 2 case 2
540  scalesList->addItem("2 V/div"); // 3 case 3
541  scalesList->addItem("1 V/div"); // 4 case 0
542  scalesList->addItem("500 mV/div"); // 5 case 1
543  scalesList->addItem("250 mV/div"); // 6 case 2
544  scalesList->addItem("200 mV/div"); // 7 case 3
545  scalesList->addItem("100 mV/div"); // 8 case 0
546  scalesList->addItem("50 mV/div"); // 9 case 1
547  scalesList->addItem("25 mV/div");
548  scalesList->addItem("20 mV/div");
549  scalesList->addItem("10 mV/div");
550  scalesList->addItem("5 mV/div");
551  scalesList->addItem("2.5 mV/div");
552  scalesList->addItem("2 mV/div");
553  scalesList->addItem("1 mV/div");
554  scalesList->addItem(QString::fromUtf8("500 µV/div"));
555  scalesList->addItem(QString::fromUtf8("250 µV/div"));
556  scalesList->addItem(QString::fromUtf8("200 µV/div"));
557  scalesList->addItem(QString::fromUtf8("100 µV/div"));
558  scalesList->addItem(QString::fromUtf8("50 µV/div"));
559  scalesList->addItem(QString::fromUtf8("25 µV/div"));
560  scalesList->addItem(QString::fromUtf8("20 µV/div"));
561  scalesList->addItem(QString::fromUtf8("10 µV/div"));
562  scalesList->addItem(QString::fromUtf8("5 µV/div"));
563  scalesList->addItem(QString::fromUtf8("2.5 µV/div"));
564  scalesList->addItem(QString::fromUtf8("2 µV/div"));
565  scalesList->addItem(QString::fromUtf8("1 µV/div"));
566  scalesList->addItem("500 nV/div");
567  scalesList->addItem("250 nV/div");
568  scalesList->addItem("200 nV/div");
569  scalesList->addItem("100 nV/div");
570  scalesList->addItem("50 nV/div");
571  scalesList->addItem("25 nV/div");
572  scalesList->addItem("20 nV/div");
573  scalesList->addItem("10 nV/div");
574  scalesList->addItem("5 nV/div");
575  scalesList->addItem("2.5 nV/div");
576  scalesList->addItem("2 nV/div");
577  scalesList->addItem("1 nV/div");
578  scalesList->addItem("500 pV/div");
579  scalesList->addItem("250 pV/div");
580  scalesList->addItem("200 pV/div");
581  scalesList->addItem("100 pV/div");
582  scalesList->addItem("50 pV/div");
583  scalesList->addItem("25 pV/div");
584  scalesList->addItem("20 pV/div");
585  scalesList->addItem("10 pV/div");
586  scalesList->addItem("5 pV/div");
587  scalesList->addItem("2.5 pV/div");
588  scalesList->addItem("2 pV/div");
589  scalesList->addItem("1 pV/div");
590  scalesList->addItem("500 fV/div");
591  scalesList->addItem("250 fV/div");
592  scalesList->addItem("200 fV/div");
593  scalesList->addItem("100 fV/div");
594  scalesList->addItem("50 fV/div");
595  scalesList->addItem("25 fV/div");
596  scalesList->addItem("20 fV/div");
597  scalesList->addItem("10 fV/div");
598  scalesList->addItem("5 fV/div");
599  scalesList->addItem("2.5 fV/div");
600  scalesList->addItem("2 fV/div");
601  scalesList->addItem("1 fV/div");
602 
603  // Offset items
604  QLabel *offsetLabel = new QLabel(tr("Offset:"),page);
605  row1Layout->addWidget(offsetLabel);
606  offsetsEdit = new QLineEdit(page);
607  offsetsEdit->setMaximumWidth(offsetsEdit->minimumSizeHint().width()*2);
608  offsetsEdit->setValidator(new QDoubleValidator(offsetsEdit));
609  row1Layout->addWidget(offsetsEdit);//, Qt::AlignRight);
610  offsetsList = new QComboBox(page);
611  row1Layout->addWidget(offsetsList);//, Qt::AlignRight);
612  offsetsList->addItem("V");
613  offsetsList->addItem("mV");
614  offsetsList->addItem(QString::fromUtf8("µV"));
615  offsetsList->addItem("nV");
616  offsetsList->addItem("pV");
617 
618  // Create elements for graphic
619  QHBoxLayout *row2Layout = new QHBoxLayout;//(page);
620  row2Layout->setAlignment(Qt::AlignLeft);
621  QLabel *colorLabel = new QLabel(tr("Color:"), page);
622  row2Layout->addWidget(colorLabel);
623  colorsList = new QComboBox(page);
624  colorsList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
625  colorsList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
626  row2Layout->addWidget(colorsList);
627  QPixmap tmp(25, 25);
628  tmp.fill(QColor(255,0,16,255));
629  colorsList->addItem(tmp, " Red");
630  tmp.fill(QColor(255,164,5,255));
631  colorsList->addItem(tmp, " Orange");
632  tmp.fill(QColor(43,206,72,255));
633  colorsList->addItem(tmp, " Green");
634  tmp.fill(QColor(0,117,220,255));
635  colorsList->addItem(tmp, " Blue");
636  tmp.fill(QColor(178,102,255,255));
637  colorsList->addItem(tmp, " Purple");
638  tmp.fill(QColor(0,153,143,255));
639  colorsList->addItem(tmp, " Teal");
640  tmp.fill(QColor(83,81,84,255));
641  colorsList->addItem(tmp, " Black");
642 
643  QLabel *widthLabel = new QLabel(tr("Width:"),page);
644  row2Layout->addWidget(widthLabel);
645  widthsList = new QComboBox(page);
646  widthsList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
647  widthsList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
648  row2Layout->addWidget(widthsList);
649  tmp.fill(Qt::white);
650  QPainter painter(&tmp);
651  for (int i = 1; i < 6; i++)
652  {
653  painter.setPen(QPen(QColor(83,81,84,255), i));
654  painter.drawLine(0, 12, 25, 12);
655  widthsList->addItem(tmp, QString::number(i) + QString(" Pixels"));
656  }
657 
658  // Create styles list
659  QLabel *styleLabel = new QLabel(tr("Style:"),page);
660  row2Layout->addWidget(styleLabel);
661  stylesList = new QComboBox(page);
662  stylesList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
663  stylesList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
664  row2Layout->addWidget(stylesList);
665  tmp.fill(Qt::white);
666  painter.setPen(QPen(QColor(83,81,84,255), 3, Qt::SolidLine));
667  painter.drawLine(0, 12, 25, 12);
668  stylesList->addItem(tmp, QString(" Solid"));
669  tmp.fill(Qt::white);
670  painter.setPen(QPen(QColor(83,81,84,255), 3, Qt::DashLine));
671  painter.drawLine(0, 12, 25, 12);
672  stylesList->addItem(tmp, QString(" Dash"));
673  tmp.fill(Qt::white);
674  painter.setPen(QPen(QColor(83,81,84,255), 3, Qt::DotLine));
675  painter.drawLine(0, 12, 25, 12);
676  stylesList->addItem(tmp, QString(" Dot"));
677  tmp.fill(Qt::white);
678  painter.setPen(QPen(QColor(83,81,84,255), 3, Qt::DashDotLine));
679  painter.drawLine(0, 12, 25, 12);
680  stylesList->addItem(tmp, QString(" Dash Dot"));
681  tmp.fill(Qt::white);
682  painter.setPen(QPen(QColor(83,81,84,255), 3, Qt::DashDotDotLine));
683  painter.drawLine(0, 12, 25, 12);
684  stylesList->addItem(tmp, QString(" Dash Dot Dot"));
685 
686  // Activate button
687  row2Layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
688  activateButton = new QPushButton("Enable Channel",page);
689  row2Layout->addWidget(activateButton);
690  activateButton->setCheckable(true);
691  activateButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
692  QObject::connect(activateButton, SIGNAL(toggled(bool)), this, SLOT(activateChannel(bool)));
693 
694  bttnLayout->addLayout(row1Layout, 0, 0);
695  bttnLayout->addLayout(row2Layout, 1, 0);
696 
697  return page;
698 }
699 
700 QWidget *Oscilloscope::Panel::createDisplayTab(QWidget *parent)
701 {
702 
703  setWhatsThis("<p><b>Oscilloscope: Display Options</b><br>"
704  "Use the dropdown box to select the time scale for the Oscilloscope. This "
705  "scaling is applied to all signals plotted in the same window. You may also "
706  "set a trigger on any signal that is currently plotted in the window. A yellow "
707  "line will appear at the trigger threshold.</p>");
708 
709  QWidget *page = new QWidget(parent);
710 
711  // Scope properties
712  QGridLayout *displayTabLayout = new QGridLayout(page);
713 
714  // Create elements for time settings
715  QHBoxLayout *row1Layout = new QHBoxLayout;
716  row1Layout->addWidget(new QLabel(tr("Time/Div:"), page));
717  timesList = new QComboBox(page);
718  row1Layout->addWidget(timesList);
719  QFont timeListFont("DejaVu Sans Mono");
720  timesList->setFont(timeListFont);
721  timesList->addItem("5 s/div");
722  timesList->addItem("2 s/div");
723  timesList->addItem("1 s/div");
724  timesList->addItem("500 ms/div");
725  timesList->addItem("200 ms/div");
726  timesList->addItem("100 ms/div");
727  timesList->addItem("50 ms/div");
728  timesList->addItem("20 ms/div");
729  timesList->addItem("10 ms/div");
730  timesList->addItem("5 ms/div");
731  timesList->addItem("2 ms/div");
732  timesList->addItem("1 ms/div");
733  timesList->addItem(QString::fromUtf8("500 µs/div"));
734  timesList->addItem(QString::fromUtf8("200 µs/div"));
735  timesList->addItem(QString::fromUtf8("100 µs/div"));
736  timesList->addItem(QString::fromUtf8("50 µs/div"));
737  timesList->addItem(QString::fromUtf8("20 µs/div"));
738  timesList->addItem(QString::fromUtf8("10 µs/div"));
739  timesList->addItem(QString::fromUtf8("5 µs/div"));
740  timesList->addItem(QString::fromUtf8("2 µs/div"));
741  timesList->addItem(QString::fromUtf8("1 µs/div"));
742  timesList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
743  timesList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
744 
745  QLabel *refreshLabel = new QLabel(tr("Refresh:"),page);
746  row1Layout->addWidget(refreshLabel);
747  refreshsSpin = new QSpinBox(page);
748  row1Layout->addWidget(refreshsSpin);
749  refreshsSpin->setRange(100,10000);
750  refreshsSpin->setValue(250);
751 
752  QLabel *downsampleLabel = new QLabel(tr("Downsample:"),page);
753  row1Layout->addWidget(downsampleLabel);
754  ratesSpin = new QSpinBox(page);
755  row1Layout->addWidget(ratesSpin);
756  ratesSpin->setValue(downsample_rate);
757  ratesSpin->setEnabled(true);
758  ratesSpin->setRange(1,2);
759  ratesSpin->setValue(1);
760 
761  // Display box for Buffer bit. Push it to the right.
762  row1Layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
763  QLabel *bufferLabel = new QLabel(tr("Buffer Size (MB):"),page);
764  row1Layout->addWidget(bufferLabel);
765  sizesEdit = new QLineEdit(page);
766  sizesEdit->setMaximumWidth(sizesEdit->minimumSizeHint().width()*3);
767  sizesEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
768  row1Layout->addWidget(sizesEdit);
769  sizesEdit->setText(QString::number(scopeWindow->getDataSize()));
770  sizesEdit->setEnabled(false);
771 
772  // Trigger box
773  QHBoxLayout *row2Layout = new QHBoxLayout;
774  row2Layout->addWidget(new QLabel(tr("Edge:"),page));
775  trigsGroup = new QButtonGroup(page);
776 
777  QRadioButton *off = new QRadioButton(tr("Off"),page);
778  trigsGroup->addButton(off, Scope::NONE);
779  row2Layout->addWidget(off);
780  QRadioButton *plus = new QRadioButton(tr("+"),page);
781  trigsGroup->addButton(plus, Scope::POS);
782  row2Layout->addWidget(plus);
783  QRadioButton *minus = new QRadioButton(tr("-"),page);
784  trigsGroup->addButton(minus, Scope::NEG);
785  row2Layout->addWidget(minus);
786 
787  row2Layout->addWidget(new QLabel(tr("Channel:"),page));
788  trigsChanList = new QComboBox(page);
789  trigsChanList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
790  trigsChanList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
791  row2Layout->addWidget(trigsChanList);
792 
793  row2Layout->addWidget(new QLabel(tr("Threshold:"),page));
794  trigsThreshEdit = new QLineEdit(page);
795  trigsThreshEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
796  trigsThreshEdit->setMaximumWidth(trigsThreshEdit->minimumSizeHint().width()*3);
797  row2Layout->addWidget(trigsThreshEdit);
798  trigsThreshEdit->setValidator(new QDoubleValidator(trigsThreshEdit));
799  trigsThreshList = new QComboBox(page);
800  trigsThreshList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
801  row2Layout->addWidget(trigsThreshList);
802  trigsThreshList->addItem("V");
803  trigsThreshList->addItem("mV");
804  trigsThreshList->addItem(QString::fromUtf8("µV"));
805  trigsThreshList->addItem("nV");
806  trigsThreshList->addItem("pV");
807 
808  row2Layout->addWidget(new QLabel(tr("Window:"),page));
809  trigWindowEdit = new QLineEdit(page);
810  trigWindowEdit->setText(QString::number(scopeWindow->getTriggerWindow()));
811  trigWindowEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
812  trigWindowEdit->setMaximumWidth(trigWindowEdit->minimumSizeHint().width()*3);
813  trigWindowEdit->setValidator(new QDoubleValidator(trigWindowEdit));
814  row2Layout->addWidget(trigWindowEdit);
815  trigWindowList = new QComboBox(page);
816  trigWindowList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
817  row2Layout->addWidget(trigWindowList);
818  trigWindowList->addItem("s");
819  trigWindowList->addItem("ms");
820  trigWindowList->addItem(QString::fromUtf8("µs"));
821  trigWindowList->setCurrentIndex(1);
822 
823  displayTabLayout->addLayout(row1Layout, 0, 0);
824  displayTabLayout->addLayout(row2Layout, 1, 0);
825 
826  return page;
827 }
828 
829 // Aggregates all channel information to show for configuration
830 // in the display tab
831 void Oscilloscope::Panel::showChannelTab(void)
832 {
833 
834  IO::flags_t type;
835  switch (typesList->currentIndex())
836  {
837  case 0:
838  type = Workspace::INPUT;
839  break;
840  case 1:
841  type = Workspace::OUTPUT;
842  break;
843  case 2:
844  type = Workspace::PARAMETER;
845  break;
846  case 3:
847  type = Workspace::STATE;
848  break;
849  default:
850  ERROR_MSG("Oscilloscope::Panel::showChannelTab : invalid type\n");
851  typesList->setCurrentIndex(0);
852  type = Workspace::OUTPUT;
853  }
854 
855  bool found = false;
856 
857  for (std::list<Scope::Channel>::iterator i = scopeWindow->getChannelsBegin(), end = scopeWindow->getChannelsEnd(); i != end; ++i)
858  {
859  struct channel_info *info =
860  reinterpret_cast<struct channel_info *> (i->getInfo());
861  if (!info)
862  continue;
863  if (info->block && info->block == blocks[blocksList->currentIndex()]
864  && info->type == type && info->index == static_cast<size_t> (channelsList->currentIndex()))
865  {
866  found = true;
867 
868  scalesList->setCurrentIndex(static_cast<int> (round(4 * (log10(1/i->getScale()) + 1))));
869 
870  double offset = i->getOffset();
871  int offsetUnits = 0;
872  if (offset)
873  while (fabs(offset) < 1)
874  {
875  offset *= 1000;
876  offsetUnits++;
877  }
878  offsetsEdit->setText(QString::number(offset));
879  offsetsList->setCurrentIndex(offsetUnits);
880 
881  if (i->getPen().color() == QColor(255,0,16,255))
882  colorsList->setCurrentIndex(0);
883  else if (i->getPen().color() == QColor(255,164,5,255))
884  colorsList->setCurrentIndex(1);
885  else if (i->getPen().color() == QColor(43,206,72,255))
886  colorsList->setCurrentIndex(2);
887  else if (i->getPen().color() == QColor(0,117,220,255))
888  colorsList->setCurrentIndex(3);
889  else if (i->getPen().color() == QColor(178,102,255,255))
890  colorsList->setCurrentIndex(4);
891  else if (i->getPen().color() == QColor(0,153,143,255))
892  colorsList->setCurrentIndex(5);
893  else if (i->getPen().color() == QColor(83,81,84,255))
894  colorsList->setCurrentIndex(6);
895  else
896  {
897  ERROR_MSG("Oscilloscope::Panel::displayChannelTab : invalid color selection\n");
898  colorsList->setCurrentIndex(0);
899  }
900 
901  switch (i->getPen().width())
902  {
903  case 1:
904  widthsList->setCurrentIndex(0);
905  break;
906  case 2:
907  widthsList->setCurrentIndex(1);
908  break;
909  case 3:
910  widthsList->setCurrentIndex(2);
911  break;
912  case 4:
913  widthsList->setCurrentIndex(3);
914  break;
915  case 5:
916  widthsList->setCurrentIndex(4);
917  break;
918  default:
919  ERROR_MSG("Oscilloscope::Panel::displayChannelTab : invalid width selection\n");
920  widthsList->setCurrentIndex(0);
921  }
922 
923  switch (i->getPen().style())
924  {
925  case Qt::SolidLine:
926  stylesList->setCurrentIndex(0);
927  break;
928  case Qt::DashLine:
929  stylesList->setCurrentIndex(1);
930  break;
931  case Qt::DotLine:
932  stylesList->setCurrentIndex(2);
933  break;
934  case Qt::DashDotLine:
935  stylesList->setCurrentIndex(3);
936  break;
937  case Qt::DashDotDotLine:
938  stylesList->setCurrentIndex(4);
939  break;
940  default:
941  ERROR_MSG("Oscilloscope::Panel::displayChannelTab : invalid style selection\n");
942  stylesList->setCurrentIndex(0);
943  }
944  break;
945  }
946  }
947 
948  activateButton->setChecked(found);
949  scalesList->setEnabled(found);
950  offsetsEdit->setEnabled(found);
951  offsetsList->setEnabled(found);
952  colorsList->setEnabled(found);
953  widthsList->setEnabled(found);
954  stylesList->setEnabled(found);
955  if (!found)
956  {
957  scalesList->setCurrentIndex(9);
958  offsetsEdit->setText(QString::number(0));
959  offsetsList->setCurrentIndex(0);
960  colorsList->setCurrentIndex(0);
961  widthsList->setCurrentIndex(0);
962  stylesList->setCurrentIndex(0);
963  }
964 }
965 
966 void Oscilloscope::Panel::showDisplayTab(void)
967 {
968  timesList->setCurrentIndex(static_cast<int> (round(3 * log10(1/scopeWindow->getDivT()) + 11)));
969 
970  refreshsSpin->setValue(scopeWindow->getRefresh());
971 
972  // Find current trigger value and update gui
973  static_cast<QRadioButton *>(trigsGroup->button(static_cast<int>(scopeWindow->getTriggerDirection())))->setChecked(true);
974 
975  trigsChanList->clear();
976  for (std::list<Scope::Channel>::iterator i = scopeWindow->getChannelsBegin(), end = scopeWindow->getChannelsEnd(); i != end; ++i)
977  {
978  trigsChanList->addItem(i->getLabel());
979  if (i == scopeWindow->getTriggerChannel())
980  trigsChanList->setCurrentIndex(trigsChanList->count() - 1);
981  }
982  trigsChanList->addItem("<None>");
983  if (scopeWindow->getTriggerChannel() == scopeWindow->getChannelsEnd())
984  trigsChanList->setCurrentIndex(trigsChanList->count() - 1);
985 
986  int trigThreshUnits = 0;
987  double trigThresh = scopeWindow->getTriggerThreshold();
988  if (trigThresh != 0.0)
989  while (fabs(trigThresh) < 1)
990  {
991  trigThresh *= 1000;
992  ++trigThreshUnits;
993  }
994  trigsThreshList->setCurrentIndex(trigThreshUnits);
995  trigsThreshEdit->setText(QString::number(trigThresh));
996 
997  sizesEdit->setText(QString::number(scopeWindow->getDataSize()));
998 }
999 
1001 Oscilloscope::Panel::Panel(QWidget *parent) : QWidget(parent), RT::Thread(0), fifo(25 * 1048576)
1002 {
1003 
1004  // Make Mdi
1005  subWindow = new QMdiSubWindow;
1006  subWindow->setWindowIcon(QIcon("/usr/local/share/rtxi/RTXI-widget-icon.png"));
1007  subWindow->setAttribute(Qt::WA_DeleteOnClose);
1008  subWindow->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint |
1009  Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint);
1010  MainWindow::getInstance()->createMdi(subWindow);
1011 
1012  setWhatsThis("<p><b>Oscilloscope:</b><br>The Oscilloscope allows you to plot any signal "
1013  "in your workspace in real-time, including signals from your DAQ card and those "
1014  "generated by user modules. Multiple signals are overlaid in the window and "
1015  "different line colors and styles can be selected. When a signal is added, a legend "
1016  "automatically appears in the bottom of the window. Multiple oscilloscopes can "
1017  "be instantiated to give you multiple data windows. To select signals for plotting, "
1018  "use the right-click context \"Panel\" menu item. After selecting a signal, you must "
1019  "click the \"Active\" button for it to appear in the window. To change signal settings, "
1020  "you must click the \"Apply\" button. The right-click context \"Pause\" menu item "
1021  "allows you to start and stop real-time plotting.</p>");
1022 
1023  // Create tab widget
1024  tabWidget = new QTabWidget;
1025  tabWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
1026  QObject::connect(tabWidget,SIGNAL(currentChanged(int)),this,SLOT(showTab(int)));
1027 
1028  // Create main layout
1029  layout = new QVBoxLayout;
1030 
1031  // Create scope group
1032  scopeGroup = new QWidget(this);
1033  QHBoxLayout *scopeLayout = new QHBoxLayout(this);
1034 
1035  // Create scope
1036  scopeWindow = new Scope(this);
1037 
1038  // Attach scope to layout
1039  scopeLayout->addWidget(scopeWindow);
1040 
1041  // Attach to layout
1042  scopeGroup->setLayout(scopeLayout);
1043 
1044  // Create group
1045  setBttnGroup = new QGroupBox(this);
1046  QHBoxLayout *setBttnLayout = new QHBoxLayout(this);
1047 
1048  // Creat buttons
1049  pauseButton = new QPushButton("Pause");
1050  pauseButton->setCheckable(true);
1051  QObject::connect(pauseButton,SIGNAL(released(void)),this,SLOT(togglePause(void)));
1052  setBttnLayout->addWidget(pauseButton);
1053  applyButton = new QPushButton("Apply");
1054  QObject::connect(applyButton,SIGNAL(released(void)),this,SLOT(apply(void)));
1055  setBttnLayout->addWidget(applyButton);
1056  settingsButton = new QPushButton("Screenshot");
1057  QObject::connect(settingsButton,SIGNAL(released(void)),this,SLOT(screenshot(void)));
1058  setBttnLayout->addWidget(settingsButton);
1059 
1060  // Attach layout
1061  setBttnGroup->setLayout(setBttnLayout);
1062 
1063  // Create tabs
1064  tabWidget->setTabPosition(QTabWidget::North);
1065  tabWidget->addTab(createChannelTab(this), "Channel");
1066  tabWidget->addTab(createDisplayTab(this), "Display");
1067 
1068  // Setup main layout
1069  layout->addWidget(scopeGroup);
1070  layout->addWidget(tabWidget);
1071  layout->addWidget(setBttnGroup);
1072 
1073  // Set
1074  setLayout(layout);
1075 
1076  // Show stuff
1077  adjustDataSize();
1078  buildChannelList();
1079  showDisplayTab();
1080  subWindow->setWidget(this);
1081  subWindow->setMinimumSize(subWindow->minimumSizeHint().width(),450);
1082  subWindow->resize(subWindow->minimumSizeHint().width()+50,600);
1083 
1084  // Initialize vars
1085  counter = 0;
1086  downsample_rate = 1;
1087  setActive(true);
1088  setWindowTitle(QString::number(getID()) + " Oscilloscope");
1089 
1090  QTimer *otimer = new QTimer;
1091  otimer->setTimerType(Qt::PreciseTimer);
1092  QObject::connect(otimer,SIGNAL(timeout(void)),this,SLOT(timeoutEvent(void)));
1093  otimer->start(25);
1094 
1095  scopeWindow->replot();
1096  show();
1097 }
1098 
1100 {
1101  while (scopeWindow->getChannelsBegin() != scopeWindow->getChannelsEnd())
1102  delete reinterpret_cast<struct channel_info *> (scopeWindow->removeChannel(scopeWindow->getChannelsBegin()));
1103 
1104  Oscilloscope::Plugin::getInstance()->removeOscilloscopePanel(this);
1105 }
1106 
1108 {
1109  size_t nchans = scopeWindow->getChannelCount();
1110 
1111  if (nchans)
1112  {
1113  size_t idx = 0;
1114  size_t token = nchans;
1115  double data[nchans];
1116 
1117  if (!counter++)
1118  {
1119  for (std::list<Scope::Channel>::iterator i = scopeWindow->getChannelsBegin(), end = scopeWindow->getChannelsEnd(); i != end; ++i)
1120  {
1121  struct channel_info *info =
1122  reinterpret_cast<struct channel_info *> (i->getInfo());
1123 
1124  double value = info->block->getValue(info->type, info->index);
1125 
1126  if (i == scopeWindow->getTriggerChannel())
1127  {
1128  double thresholdValue = scopeWindow->getTriggerThreshold();
1129 
1130  if ((thresholdValue > value && thresholdValue
1131  < info->previous) || (thresholdValue < value
1132  && thresholdValue > info->previous))
1133  {
1135  int direction = (thresholdValue > value) ? 1 : -1;
1136 
1137  event.setParam("block", info->block);
1138  event.setParam("type", &info->type);
1139  event.setParam("index", &info->index);
1140  event.setParam("direction", &direction);
1141  event.setParam("threshold", &thresholdValue);
1142 
1144  }
1145  }
1146  info->previous = value; // automatically buffers a single value
1147  data[idx++] = value; // sample from DAQ
1148  }
1149  fifo.write(&token, sizeof(token));
1150  fifo.write(data, sizeof(data));
1151  }
1152  else
1153  {
1154  double prevdata[nchans];
1155  for (std::list<Scope::Channel>::iterator i = scopeWindow->getChannelsBegin(), end = scopeWindow->getChannelsEnd(); i != end; ++i)
1156  {
1157  struct channel_info *info =
1158  reinterpret_cast<struct channel_info *> (i->getInfo());
1159  prevdata[idx++] = info->previous;
1160  }
1161  fifo.write(&token, sizeof(token));
1162  fifo.write(prevdata, sizeof(prevdata));
1163  }
1164  }
1165  counter %= downsample_rate;
1166 }
1167 
1168 void Oscilloscope::Panel::screenshot()
1169 {
1170  QwtPlotRenderer renderer;
1171  renderer.exportTo(scopeWindow,"screenshot.pdf");
1172 }
1173 
1175 {
1176  scopeWindow->isPaused = !(scopeWindow->isPaused);
1177 }
1178 
1180 {
1181  bool active = getActive();
1182  setActive(false);
1183  SyncEvent event;
1185  return active;
1186 }
1187 
1189 {
1190  char yogi;
1191  while (fifo.read(&yogi, sizeof(yogi), false))
1192  ;
1193 }
1194 
1196 {
1197  double period = RT::System::getInstance()->getPeriod() * 1e-6; // ms
1198  size_t size = ceil(scopeWindow->getDivT() * scopeWindow->getDivX() / period) + 1;
1199  scopeWindow->setDataSize(size);
1200  sizesEdit->setText(QString::number(scopeWindow->getDataSize()));
1201 }
1202 
1204 {
1205  size_t size;
1206  while (fifo.read(&size, sizeof(size), false))
1207  {
1208  double data[size];
1209  if (fifo.read(data, sizeof(data)))
1210  scopeWindow->setData(data, size);
1211  }
1212 }
1213 
1215 {
1216  bool active = setInactiveSync();
1217 
1218  for (size_t i = 0, nchans = s.loadInteger("Num Channels"); i < nchans; ++i)
1219  {
1220  std::ostringstream str;
1221  str << i;
1222 
1223  IO::Block
1224  *block = dynamic_cast<IO::Block *> (Settings::Manager::getInstance()->getObject(s.loadInteger(str.str() + " ID")));
1225  if (!block)
1226  continue;
1227 
1228  struct channel_info *info = new struct channel_info;
1229 
1230  info->block = block;
1231  info->type = s.loadInteger(str.str() + " type");
1232  info->index = s.loadInteger(str.str() + " index");
1233  info->name = QString::number(block->getID())+" "+QString::fromStdString(block->getName(info->type, info->index));
1234  info->previous = 0.0;
1235 
1236  QwtPlotCurve *curve = new QwtPlotCurve(info->name);
1237 
1238  std::list<Scope::Channel>::iterator chan = scopeWindow->insertChannel(info->name, s.loadDouble(str.str() + " scale"),
1239  s.loadDouble(str.str() + " offset"), QPen(QColor(QString::fromStdString(s.loadString(str.str() + " pen color"))),
1240  s.loadInteger(str.str() + " pen width"), Qt::PenStyle(s.loadInteger(str.str() + " pen style"))), curve, info);
1241 
1242  scopeWindow->setChannelLabel(chan, info->name + " - " + scalesList->itemText(static_cast<int> (round(4 * (log10(1/chan->getScale()) + 1)))).simplified());
1243  }
1244 
1245  flushFifo();
1246  setActive(active);
1247 }
1248 
1250 {
1251  scopeWindow->setDataSize(s.loadInteger("Size"));
1252  scopeWindow->setDivT(s.loadDouble("DivT"));
1253 
1254  if (s.loadInteger("Maximized"))
1255  scopeWindow->showMaximized();
1256  else if (s.loadInteger("Minimized"))
1257  scopeWindow->showMinimized();
1258 
1259  if (scopeWindow->paused() != s.loadInteger("Paused"))
1260  togglePause();
1261 
1262  scopeWindow->setRefresh(s.loadInteger("Refresh"));
1263 
1264  subWindow->resize(s.loadInteger("W"), s.loadInteger("H"));
1265  parentWidget()->move(s.loadInteger("X"), s.loadInteger("Y"));
1266 }
1267 
1269 {
1270  s.saveInteger("Size", scopeWindow->getDataSize());
1271  s.saveInteger("DivX", scopeWindow->getDivX());
1272  s.saveInteger("DivY", scopeWindow->getDivY());
1273  s.saveDouble("DivT", scopeWindow->getDivT());
1274 
1275  if (isMaximized())
1276  s.saveInteger("Maximized", 1);
1277  else if (isMinimized())
1278  s.saveInteger("Minimized", 1);
1279 
1280  s.saveInteger("Paused", scopeWindow->paused());
1281  s.saveInteger("Refresh", scopeWindow->getRefresh());
1282 
1283  s.saveInteger("X", parentWidget()->pos().x());
1284  s.saveInteger("Y", parentWidget()->pos().y());
1285  s.saveInteger("W", width());
1286  s.saveInteger("H", height());
1287 
1288  s.saveInteger("Num Channels", scopeWindow->getChannelCount());
1289  size_t n = 0;
1290  for (std::list<Scope::Channel>::const_iterator i = scopeWindow->getChannelsBegin(), end = scopeWindow->getChannelsEnd(); i != end; ++i)
1291  {
1292  std::ostringstream str;
1293  str << n++;
1294 
1295  const struct channel_info *info = reinterpret_cast<const struct channel_info *> (i->getInfo());
1296  s.saveInteger(str.str() + " ID", info->block->getID());
1297  s.saveInteger(str.str() + " type", info->type);
1298  s.saveInteger(str.str() + " index", info->index);
1299 
1300  s.saveDouble(str.str() + " scale", i->getScale());
1301  s.saveDouble(str.str() + " offset", i->getOffset());
1302 
1303  s.saveString(str.str() + " pen color", i->getPen().color().name().toStdString());
1304  s.saveInteger(str.str() + " pen style", i->getPen().style());
1305  s.saveInteger(str.str() + " pen width", i->getPen().width());
1306  }
1307 }
1308 
1309 static Mutex mutex;
1310 Oscilloscope::Plugin *Oscilloscope::Plugin::instance = 0;
1311 
1313 {
1314  if (instance)
1315  return instance;
1316 
1317  /*************************************************************************
1318  * Seems like alot of hoops to jump through, but allocation isn't *
1319  * thread-safe. So effort must be taken to ensure mutual exclusion. *
1320  *************************************************************************/
1321 
1322  Mutex::Locker lock(&::mutex);
1323  if (!instance)
1324  instance = new Plugin();
1325 
1326  return instance;
1327 }
block_list_info_t
Definition: connector.cpp:24
MainWindow::createSystemMenuItem
QAction * createSystemMenuItem(const QString &label, const QObject *handler, const char *slot)
Definition: main_window.cpp:253
Scope::POS
@ POS
Definition: scope.h:139
Oscilloscope::Plugin::doSave
virtual void doSave(Settings::Object::State &) const
Definition: oscilloscope.cpp:107
RT::Event
Definition: rt.h:82
Oscilloscope::Panel::setInactiveSync
bool setInactiveSync(void)
Definition: oscilloscope.cpp:1179
Event::RT_POSTPERIOD_EVENT
const char * RT_POSTPERIOD_EVENT
Definition: event.cpp:26
ERROR_MSG
void ERROR_MSG(const std::string &errmsg,...)
Definition: debug.cpp:27
Settings::Manager::getInstance
static Manager * getInstance(void)
Definition: settings.cpp:534
IO::flags_t
unsigned long flags_t
Definition: io.h:40
IO::Connector::getInstance
static Connector * getInstance(void)
Definition: io.cpp:421
Scope::NEG
@ NEG
Definition: scope.h:140
block_list_info_t::blocks
std::vector< IO::Block * > * blocks
Definition: connector.cpp:28
Event::Manager::getInstance
static Manager * getInstance(void)
Definition: event.cpp:149
Event::IO_BLOCK_INSERT_EVENT
const char * IO_BLOCK_INSERT_EVENT
Definition: event.cpp:31
Plugin::Object
Definition: plugin.h:145
MainWindow::createMdi
void createMdi(QMdiSubWindow *)
Definition: main_window.cpp:230
Oscilloscope::Panel::Panel
Panel(QWidget *=NULL)
Definition: oscilloscope.cpp:1001
Settings::Object::State::loadState
State loadState(const std::string &name) const
Definition: settings.cpp:124
workspace.h
RT::System::postEvent
int postEvent(Event *event, bool blocking=true)
Definition: rt.cpp:218
Settings::Manager::getObject
Object * getObject(Object::ID) const
Definition: settings.cpp:212
Settings::Object::State::loadDouble
double loadDouble(const std::string &name) const
Definition: settings.cpp:61
Oscilloscope::Plugin::createOscilloscopePanel
void createOscilloscopePanel(void)
Definition: oscilloscope.cpp:79
RT::Thread::setActive
void setActive(bool)
Definition: rt.cpp:152
IO::Block::getCount
virtual size_t getCount(flags_t type) const
Definition: io.cpp:82
Scope::NONE
@ NONE
Definition: scope.h:138
RT
Realtime Oriented Classes.
Definition: rt.h:34
oscilloscope.h
Event::Manager::postEventRT
void postEventRT(const Object *event)
Definition: event.cpp:118
RT::Event::callback
virtual int callback(void)=0
Settings::Object::State::saveDouble
void saveDouble(const std::string &name, double)
Definition: settings.cpp:105
RT::System::getPeriod
long long getPeriod(void) const
Definition: rt.h:404
Settings::Object::State::loadString
std::string loadString(const std::string &name) const
Definition: settings.cpp:93
block_list_info_t::blockList
QComboBox * blockList
Definition: oscilloscope.cpp:475
RT::System::getInstance
static System * getInstance(void)
Definition: rt.cpp:361
IO::Block::getName
std::string getName(void) const
Definition: io.h:215
Mutex
Definition: mutex.h:28
Oscilloscope::Panel::timeoutEvent
void timeoutEvent(void)
Definition: oscilloscope.cpp:1203
Settings::Object::State::saveString
void saveString(const std::string &name, const std::string &value)
Definition: settings.cpp:119
Oscilloscope::Panel::receiveEvent
void receiveEvent(const ::Event::Object *)
Definition: oscilloscope.cpp:115
Oscilloscope::Panel::doLoad
void doLoad(const Settings::Object::State &)
Definition: oscilloscope.cpp:1249
createRTXIPlugin
Plugin::Object * createRTXIPlugin(void *)
Definition: oscilloscope.cpp:59
IO::Block
Definition: io.h:188
Oscilloscope::Panel::Scope
friend class Scope
Definition: oscilloscope.h:86
IO::Connector::foreachBlock
void foreachBlock(void(*callback)(Block *, void *), void *param)
Definition: io.cpp:257
Oscilloscope::Panel::adjustDataSize
void adjustDataSize(void)
Definition: oscilloscope.cpp:1195
Settings::Object::State::loadInteger
int loadInteger(const std::string &name) const
Definition: settings.cpp:77
Oscilloscope::Plugin::getInstance
static Plugin * getInstance(void)
Definition: oscilloscope.cpp:1312
Oscilloscope::Plugin
Definition: oscilloscope.h:48
Oscilloscope::Panel::execute
void execute(void)
Definition: oscilloscope.cpp:1107
Oscilloscope::Panel::togglePause
void togglePause(void)
Definition: oscilloscope.cpp:1174
rt.h
Oscilloscope::Panel::doSave
void doSave(Settings::Object::State &) const
Definition: oscilloscope.cpp:1268
Oscilloscope::Panel::flushFifo
void flushFifo(void)
Definition: oscilloscope.cpp:1188
Scope::trig_t
trig_t
Definition: scope.h:136
Settings::Object::State
Definition: settings.h:62
Plugin
Classes associated with the loading/unloading of binaries at run-time.
Definition: plugin.h:35
Settings::Object::State::saveInteger
void saveInteger(const std::string &name, int)
Definition: settings.cpp:112
Mutex::Locker
Definition: mutex.h:36
Event::IO_BLOCK_REMOVE_EVENT
const char * IO_BLOCK_REMOVE_EVENT
Definition: event.cpp:32
MainWindow::getInstance
static MainWindow * getInstance(void)
Definition: main_window.cpp:454
Event::THRESHOLD_CROSSING_EVENT
const char * THRESHOLD_CROSSING_EVENT
Definition: event.cpp:44
Settings::Object::State::saveState
void saveState(const std::string &name, const State &value)
Definition: settings.cpp:136
Oscilloscope::Plugin::doDeferred
virtual void doDeferred(const Settings::Object::State &)
Definition: oscilloscope.cpp:90
Oscilloscope::Panel::~Panel
virtual ~Panel(void)
Definition: oscilloscope.cpp:1099
Event::Object
Definition: event.h:128
Oscilloscope::Panel::doDeferred
void doDeferred(const Settings::Object::State &)
Definition: oscilloscope.cpp:1214
Settings::Object::getID
ID getID(void) const
Definition: settings.h:131
Oscilloscope::Plugin::doLoad
virtual void doLoad(const Settings::Object::State &)
Definition: oscilloscope.cpp:97
Oscilloscope::Panel
Definition: oscilloscope.h:81
debug.h
Settings::Object::load
void load(const State &)
Definition: settings.cpp:195
main_window.h