File indexing completed on 2024-04-21 05:43:41

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "oscilloscope.h"
0012 #include "ktechlab.h"
0013 #include "oscilloscopedata.h"
0014 #include "oscilloscopeview.h"
0015 #include "probe.h"
0016 #include "probepositioner.h"
0017 #include "simulator.h"
0018 
0019 #include <KConfigGroup>
0020 #include <KLocalizedString>
0021 #include <KSharedConfig>
0022 #include <cmath>
0023 
0024 // #include <q3button.h>
0025 
0026 #include <QLabel>
0027 #include <QScrollBar>
0028 #include <QSlider>
0029 #include <QTimer>
0030 #include <QToolButton>
0031 
0032 #include <cassert>
0033 
0034 #include <ktechlab_debug.h>
0035 
0036 // BEGIN Oscilloscope Class
0037 QColor probeColors[9] =
0038     {QColor(0x52, 0x22, 0x00), QColor(0xB5, 0x00, 0x2F), QColor(0xF9, 0xBA, 0x07), QColor(0x53, 0x93, 0x16), QColor(0x00, 0x66, 0x2F), QColor(0x00, 0x41, 0x88), QColor(0x1B, 0x2D, 0x83), QColor(0x55, 0x12, 0x7B), QColor(0x7B, 0x0C, 0x82)};
0039 
0040 Oscilloscope *Oscilloscope::m_pSelf = nullptr;
0041 
0042 Oscilloscope *Oscilloscope::self(KateMDI::ToolView *parent)
0043 {
0044     if (!m_pSelf) {
0045         assert(parent);
0046         m_pSelf = new Oscilloscope(parent);
0047     }
0048     return m_pSelf;
0049 }
0050 
0051 Oscilloscope::Oscilloscope(KateMDI::ToolView *parent)
0052     : QWidget(parent)
0053 {
0054     setupUi(this);
0055 
0056     if (parent->layout()) {
0057         parent->layout()->addWidget(this);
0058         qCDebug(KTL_LOG) << " added item selector to parent's layout " << parent;
0059     } else {
0060         qCWarning(KTL_LOG) << " unexpected null layout on parent " << parent;
0061     }
0062 
0063     m_nextColor = 0;
0064     m_nextId = 1;
0065     m_oldestId = -1;
0066     m_oldestProbe = nullptr;
0067     //  b_isPaused = false;
0068     m_zoomLevel = 0.5;
0069     m_pSimulator = Simulator::self();
0070 
0071     horizontalScroll->setSingleStep(32);
0072     horizontalScroll->setPageStep(oscilloscopeView->width());
0073 
0074     connect(resetBtn, &QPushButton::clicked, this, &Oscilloscope::reset);
0075     connect(zoomSlider, &QSlider::valueChanged, this, &Oscilloscope::slotZoomSliderChanged);
0076     connect(horizontalScroll, &QScrollBar::valueChanged, this, &Oscilloscope::slotSliderValueChanged);
0077 
0078     //  connect( pauseBtn, SIGNAL(clicked()), this, SLOT(slotTogglePause()));
0079 
0080     QTimer *updateScrollTmr = new QTimer(this);
0081     connect(updateScrollTmr, &QTimer::timeout, this, &Oscilloscope::updateScrollbars);
0082     updateScrollTmr->start(20);
0083 
0084     // KGlobal::config()->setGroup("Oscilloscope");
0085     KConfigGroup grOscill(KSharedConfig::openConfig(), "Oscilloscope");
0086     setZoomLevel(grOscill.readEntry("ZoomLevel", 0.5));
0087 
0088     connect(this, SIGNAL(probeRegistered(int, ProbeData *)), probePositioner, SLOT(slotProbeDataRegistered(int, ProbeData *)));
0089     /*TODO  fix error: 'slotProbeDataRegistered(int, ProbeData*)’ is protected within this context
0090     connect(this, &Oscilloscope::probeRegistered,
0091             probePositioner, &ProbePositioner::slotProbeDataRegistered);*/
0092     connect(this, SIGNAL(probeUnregistered(int)), probePositioner, SLOT(slotProbeDataUnregistered(int)));
0093     /*TODO the same problem
0094     connect(this, &Oscilloscope::probeUnregistered,
0095             probePositioner, &ProbePositioner::slotProbeDataUnregistered);*/
0096 }
0097 
0098 Oscilloscope::~Oscilloscope()
0099 {
0100     m_pSelf = nullptr;
0101 }
0102 
0103 bool Oscilloscope::isInstantiated()
0104 {
0105     return m_pSelf != nullptr;
0106 }
0107 
0108 void Oscilloscope::slotTogglePause()
0109 {
0110     //  b_isPaused = !b_isPaused;
0111     //  pauseBtn->setText( b_isPaused ? i18n("Resume") : i18n("Pause"));
0112     //  const ProbeDataMap::iterator end = m_probeDataMap.end();
0113     //  for( ProbeDataMap::iterator it = m_probeDataMap.begin(); it != end; ++it)
0114     //      (*it)->setPaused(b_isPaused);
0115 }
0116 
0117 int Oscilloscope::sliderTicksPerSecond() const
0118 {
0119     return int(1e4);
0120 }
0121 
0122 void Oscilloscope::setZoomLevel(double zoomLevel)
0123 {
0124     if (zoomLevel < 0.0)
0125         zoomLevel = 0.0;
0126 
0127     else if (zoomLevel > 1.0)
0128         zoomLevel = 1.0;
0129 
0130     // KGlobal::config()->setGroup("Oscilloscope");
0131     KConfigGroup grOscill = KSharedConfig::openConfig()->group("Oscilloscope");
0132     // KGlobal::config()->writeEntry( "ZoomLevel", zoomLevel);
0133     grOscill.writeEntry("ZoomLevel", zoomLevel);
0134 
0135     // We want to maintain the position of the *center* of the view, not the
0136     // left edge, so have to record time at center of view... We also have to
0137     // handle the case where the scroll is at the end separately.
0138     bool wasAtUpperEnd = horizontalScroll->maximum() == horizontalScroll->value();
0139     int pageLength = int(oscilloscopeView->width() * sliderTicksPerSecond() / pixelsPerSecond());
0140     int at_ticks = horizontalScroll->value() + (pageLength / 2);
0141 
0142     m_zoomLevel = zoomLevel;
0143     zoomSlider->setValue(int((double(zoomSlider->maximum()) * zoomLevel) + 0.5));
0144     updateScrollbars();
0145 
0146     // And restore the center position of the slider
0147     if (!wasAtUpperEnd) {
0148         int pageLength = int(oscilloscopeView->width() * sliderTicksPerSecond() / pixelsPerSecond());
0149         horizontalScroll->setValue(at_ticks - (pageLength / 2));
0150         oscilloscopeView->updateView();
0151     }
0152 }
0153 
0154 void Oscilloscope::slotZoomSliderChanged(int value)
0155 {
0156     setZoomLevel(double(value) / double(zoomSlider->maximum()));
0157 }
0158 
0159 ProbeData *Oscilloscope::registerProbe(Probe *probe)
0160 {
0161     if (!probe)
0162         return nullptr;
0163 
0164     const uint id = m_nextId++;
0165 
0166     ProbeData *probeData = nullptr;
0167 
0168     if (dynamic_cast<LogicProbe *>(probe)) {
0169         probeData = new LogicProbeData(id);
0170         m_logicProbeDataMap[id] = static_cast<LogicProbeData *>(probeData);
0171     } else {
0172         probeData = new FloatingProbeData(id);
0173         m_floatingProbeDataMap[id] = static_cast<FloatingProbeData *>(probeData);
0174     }
0175 
0176     m_probeDataMap[id] = probeData;
0177 
0178     if (!m_oldestProbe) {
0179         m_oldestProbe = probeData;
0180         m_oldestId = id;
0181     }
0182 
0183     probeData->setColor(probeColors[m_nextColor]);
0184     m_nextColor = (m_nextColor + 1) % 9;
0185     //  probeData->setPaused(b_isPaused);
0186 
0187     emit probeRegistered(id, probeData);
0188     return probeData;
0189 }
0190 
0191 void Oscilloscope::unregisterProbe(int id)
0192 {
0193     ProbeDataMap::iterator it = m_probeDataMap.find(id);
0194 
0195     if (it == m_probeDataMap.end())
0196         return;
0197 
0198     m_logicProbeDataMap.remove(id);
0199     m_floatingProbeDataMap.remove(id);
0200 
0201     bool oldestDestroyed = it.value() == m_oldestProbe;
0202 
0203     if (it != m_probeDataMap.end())
0204         m_probeDataMap.erase(it);
0205 
0206     if (oldestDestroyed)
0207         getOldestProbe();
0208 
0209     emit probeUnregistered(id);
0210 }
0211 
0212 ProbeData *Oscilloscope::probeData(int id) const
0213 {
0214     const ProbeDataMap::const_iterator bit = m_probeDataMap.find(id);
0215     if (bit != m_probeDataMap.end())
0216         return bit.value();
0217 
0218     return nullptr;
0219 }
0220 
0221 int Oscilloscope::probeNumber(int id) const
0222 {
0223     const ProbeDataMap::const_iterator end = m_probeDataMap.end();
0224     int i = 0;
0225     for (ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it) {
0226         if (it.key() == id)
0227             return i;
0228         i++;
0229     }
0230     return -1;
0231 }
0232 
0233 int Oscilloscope::numberOfProbes() const
0234 {
0235     return m_probeDataMap.size();
0236 }
0237 
0238 void Oscilloscope::getOldestProbe()
0239 {
0240     if (m_probeDataMap.isEmpty()) {
0241         m_oldestProbe = nullptr;
0242         m_oldestId = -1;
0243         return;
0244     }
0245 
0246     m_oldestProbe = m_probeDataMap.begin().value();
0247     m_oldestId = m_probeDataMap.begin().key();
0248 }
0249 
0250 void Oscilloscope::reset()
0251 {
0252     const ProbeDataMap::iterator end = m_probeDataMap.end();
0253     for (ProbeDataMap::iterator it = m_probeDataMap.begin(); it != end; ++it)
0254         (*it)->eraseData();
0255 
0256     oscilloscopeView->updateView();
0257 }
0258 
0259 void Oscilloscope::slotSliderValueChanged(int value)
0260 {
0261     Q_UNUSED(value);
0262     oscilloscopeView->updateView();
0263 }
0264 
0265 void Oscilloscope::updateScrollbars()
0266 {
0267     bool wasAtUpperEnd = horizontalScroll->maximum() == horizontalScroll->value();
0268 
0269     const float pps = pixelsPerSecond();
0270 
0271     int pageLength = int(oscilloscopeView->width() * sliderTicksPerSecond() / pps);
0272     int64_t timeAsTicks = time() * sliderTicksPerSecond() / LOGIC_UPDATE_RATE;
0273     int64_t upper = (timeAsTicks > pageLength) ? (timeAsTicks - pageLength) : 0;
0274     horizontalScroll->setRange(0, upper);
0275 
0276     horizontalScroll->setPageStep(uint64_t(oscilloscopeView->width() * sliderTicksPerSecond() / pps));
0277 
0278     if (wasAtUpperEnd) {
0279         horizontalScroll->setValue(horizontalScroll->maximum());
0280         oscilloscopeView->updateView();
0281     }
0282 }
0283 
0284 uint64_t Oscilloscope::time() const
0285 {
0286     if (!m_oldestProbe)
0287         return 0;
0288 
0289     return uint64_t(m_pSimulator->time() - m_oldestProbe->resetTime());
0290 }
0291 
0292 int64_t Oscilloscope::scrollTime() const
0293 {
0294     //  if( b_isPaused || numberOfProbes() == 0)
0295     //      return 0;
0296 
0297     if (numberOfProbes() == 0)
0298         return 0;
0299 
0300     if (horizontalScroll->maximum() == 0) {
0301         int64_t lengthAsTime = int64_t(oscilloscopeView->width() * LOGIC_UPDATE_RATE / pixelsPerSecond());
0302         int64_t ret = m_pSimulator->time() - lengthAsTime;
0303         if (ret < 0)
0304             return 0;
0305         return ret;
0306     } else
0307         return int64_t(m_oldestProbe->resetTime() + (int64_t(horizontalScroll->value()) * LOGIC_UPDATE_RATE / sliderTicksPerSecond()));
0308 }
0309 
0310 double Oscilloscope::pixelsPerSecond() const
0311 {
0312     return 2 * MIN_BITS_PER_S * std::pow(2.0, m_zoomLevel * MIN_MAX_LOG_2_DIFF);
0313 }
0314 // END Oscilloscope Class
0315 
0316 void addOscilloscopeAsToolView(KTechlab *ktechlab)
0317 {
0318     KateMDI::ToolView *tv;
0319     tv = ktechlab->createToolView(Oscilloscope::toolViewIdentifier(), KMultiTabBar::Bottom, QIcon::fromTheme("oscilloscope"), i18n("Oscilloscope"));
0320 
0321     Oscilloscope::self(tv);
0322 }
0323 
0324 ProbeData *registerProbe(Probe *probe)
0325 {
0326     if (!Oscilloscope::isInstantiated()) {
0327         qCDebug(KTL_LOG) << "no oscilloscope to register to, doing nothing";
0328         return nullptr;
0329     }
0330     return Oscilloscope::self()->registerProbe(probe);
0331 }
0332 
0333 void unregisterProbe(int id)
0334 {
0335     if (!Oscilloscope::isInstantiated()) {
0336         qCDebug(KTL_LOG) << "no oscilloscope to unregister from, doing nothing";
0337         return;
0338     }
0339     Oscilloscope::self()->unregisterProbe(id);
0340 }
0341 
0342 #include "moc_oscilloscope.cpp"