/* * Remote Laboratory Component Analyzer Part * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * (c) 2014 - 2019 Timothy Pearson * Raptor Engineering * http://www.raptorengineeringinc.com */ #include "define.h" #include "part.h" #include //::createAboutData() #include #include #include //::start() #include #include #include #include #include #include //encodeName() #include #include #include #include #include #include #include #include #include #include #include #include //access() #include #include #include "layout.h" #include "tracewidget.h" #include "floatspinbox.h" #define NETWORK_COMM_TIMEOUT_MS 15000 /* exception handling */ struct exit_exception { int c; exit_exception(int c):c(c) { } }; namespace RemoteLab { typedef KParts::GenericFactory Factory; #define CLIENT_LIBRARY "libremotelab_companalyzer" K_EXPORT_COMPONENT_FACTORY( libremotelab_companalyzer, RemoteLab::Factory ) #ifndef QT_NO_DATASTREAM TQDataStream &operator<<( TQDataStream &s, const CompAnalyzerMeasurement &data ) { s << data.status; s << data.parameter; s << data.type; s << data.value; s << data.frequency; return s; } TQDataStream &operator>>( TQDataStream &s, CompAnalyzerMeasurement &data ) { s >> data.status; s >> data.parameter; s >> data.type; s >> data.value; s >> data.frequency; return s; } #endif CompAnalyzerWorker::CompAnalyzerWorker() : TQObject() { m_sweepStepMutex = new TQMutex(false); m_currentStateMutex = new TQMutex(false); m_networkDataMutex = new TQMutex(false); m_outboundQueueMutex = new TQMutex(false); m_inboundQueueMutex = new TQMutex(false); m_newData = false; m_currentState = Initializing; m_startupState = StartSelectInstrument; m_lastNetworkTransmissionEvent = NoEvent; } CompAnalyzerWorker::~CompAnalyzerWorker() { delete m_sweepStepMutex; m_sweepStepMutex = NULL; delete m_currentStateMutex; m_currentStateMutex = NULL; delete m_networkDataMutex; m_networkDataMutex = NULL; delete m_inboundQueueMutex; m_inboundQueueMutex = NULL; delete m_outboundQueueMutex; m_outboundQueueMutex = NULL; } void CompAnalyzerWorker::run() { TQEventLoop* eventLoop = TQApplication::eventLoop(); if (!eventLoop) { return; } while (1) { m_instrumentMutex->lock(); CompAnalyzerPartState state = currentState(); CompAnalyzerEventType lastTxEvent = m_lastNetworkTransmissionEvent; // Handle inbound queue m_inboundQueueMutex->lock(); if (m_inboundQueue.count() > 0) { TQDataStream ds(m_socket); ds.setPrintableData(true); CompAnalyzerEventQueue::iterator it; for (it = m_inboundQueue.begin(); it != m_inboundQueue.end(); ++it) { if ((*it).first == TxRxSyncPoint) { break; } else if ((*it).first == Initialize) { setCurrentState(Initializing); m_lastNetworkTransmissionEvent = OtherEvent; ds << TQString("COMPONENT ANALYZER"); m_socket->writeEndOfFrame(); it = m_inboundQueue.erase(it); } else if ((*it).first == GetMeasurement) { m_lastNetworkTransmissionEvent = GetMeasurement; ds << TQString("GETMEASUREMENT"); m_socket->writeEndOfFrame(); it = m_inboundQueue.erase(it); } else if ((*it).first == GetMaximumFrequency) { m_lastNetworkTransmissionEvent = GetMaximumFrequency; ds << TQString("GETMAXMEASUREMENTFREQUENCY"); m_socket->writeEndOfFrame(); it = m_inboundQueue.erase(it); } else if ((*it).first == GetMinimumFrequency) { m_lastNetworkTransmissionEvent = GetMinimumFrequency; ds << TQString("GETMINMEASUREMENTFREQUENCY"); m_socket->writeEndOfFrame(); it = m_inboundQueue.erase(it); } else if ((*it).first == SetFrequency) { m_lastNetworkTransmissionEvent = SetFrequency; ds << TQString("SETMEASUREMENTFREQUENCY"); ds << (*it).second.toDouble(); m_socket->writeEndOfFrame(); it = m_inboundQueue.erase(it); } else if ((*it).first == ChangeMeasurementSource) { m_lastNetworkTransmissionEvent = ChangeMeasurementSource; TQ_UINT8 number_of_parameters = 2; ds << TQString("SETMEASUREDPARAMETERS"); ds << number_of_parameters; ds << m_sourceList[0]; ds << m_sourceList[1]; m_socket->writeEndOfFrame(); it = m_inboundQueue.erase(it); } // If the next command is a sync point stop command list execution if ((*it).first == TxRxSyncPoint) { break; } } m_socket->flush(); } m_inboundQueueMutex->unlock(); // Handle outbound queue if (m_newData) { bool queue_modified = false; m_networkDataMutex->lock(); m_newData = false; // Receive data if (m_socket->canReadFrame()) { TQDataStream ds(m_socket); ds.setPrintableData(true); while (!ds.atEnd() && m_socket->canReadFrame(false)) { // Get command status TQString input; ds >> input; if (input == "") { continue; } // Response received clearInboundQueueSyncPoint(); if (state == Initializing) { if (input == "ACK") { if (m_startupState == StartSelectInstrument) { m_startupState = StartGetMaximumFrequency; appendItemToInboundQueue(CompAnalyzerEvent(GetMaximumFrequency, TQVariant()), true); } else if (m_startupState == StartGetMaximumFrequency) { ds >> m_instrumentLimits.maxFrequency; m_startupState = StartGetMinimumFrequency; appendItemToInboundQueue(CompAnalyzerEvent(GetMinimumFrequency, TQVariant()), true); } else if (m_startupState == StartGetMinimumFrequency) { ds >> m_instrumentLimits.minFrequency; // TODO // This should be loaded from the instrument // Add requisite functionality to the GPIB server and then // update this routine to use it.... m_instrumentLimits.allowedMeasurements.clear(); AllowedMeasurementInfoList parameterASourceValues; parameterASourceValues.append(AllowedMeasurementInfo(0, i18n("Resistance"))); parameterASourceValues.append(AllowedMeasurementInfo(2, i18n("Conductance"))); parameterASourceValues.append(AllowedMeasurementInfo(4, i18n("Inductance"))); parameterASourceValues.append(AllowedMeasurementInfo(5, i18n("Capacitance"))); parameterASourceValues.append(AllowedMeasurementInfo(8, i18n("Impedance"))); parameterASourceValues.append(AllowedMeasurementInfo(9, i18n("Admittance"))); parameterASourceValues.append(AllowedMeasurementInfo(10, i18n("Reflection Coefficient (Absolute)"))); parameterASourceValues.append(AllowedMeasurementInfo(11, i18n("Reflection Coefficient (X)"))); AllowedMeasurementInfoList parameterBSourceValues; parameterBSourceValues.append(AllowedMeasurementInfo(0, i18n("Resistance"))); parameterBSourceValues.append(AllowedMeasurementInfo(2, i18n("Conductance"))); parameterBSourceValues.append(AllowedMeasurementInfo(6, i18n("Dissipation Factor"))); parameterBSourceValues.append(AllowedMeasurementInfo(7, i18n("Quality Factor"))); parameterBSourceValues.append(AllowedMeasurementInfo(13, i18n("Phase Angle (°)"))); parameterBSourceValues.append(AllowedMeasurementInfo(14, i18n("Phase Angle (radians)"))); m_instrumentLimits.allowedMeasurements.append(parameterASourceValues); m_instrumentLimits.allowedMeasurements.append(parameterBSourceValues); m_startupState = StartDone; setCurrentState(FreeRunning); // Request first measurement appendItemToInboundQueue(CompAnalyzerEvent(GetMeasurement, TQVariant()), true); // Notify GUI that new configuration data is available m_outboundQueueMutex->lock(); m_outboundQueue.push_back(CompAnalyzerEvent(ConfigurationDataReceived, TQVariant())); m_outboundQueueMutex->unlock(); } } else { setCurrentState(CommunicationFailure); } queue_modified = true; } else if ((state == FreeRunning) || (state == FrequencySweepRead)) { if (input == "ACK") { if (lastTxEvent == GetMeasurement) { int i; CompAnalyzerMeasurement measurement; CompAnalyzerMeasurementList measurements; TQ_UINT8 number_of_parameters; ds >> number_of_parameters; for (i=0; i < number_of_parameters; i++) { ds >> measurement.status; ds >> measurement.parameter; ds >> measurement.type; ds >> measurement.value; ds >> measurement.frequency; measurements.append(measurement); } if (nextInboundQueueEvent() == StartSweep) { eraseNextInboundQueueEvent(true); // Set initial sweep frequency m_sweepCurrentFrequency = m_sweepStart; m_sweepStepMutex->lock(); m_sweepStepNumber = 0; m_sweepStepMutex->unlock(); appendItemToInboundQueue(CompAnalyzerEvent(SetFrequency, TQVariant(m_sweepCurrentFrequency)), true); setCurrentState(FrequencySweepWrite); } else if (nextInboundQueueEvent() == AbortSweep) { eraseNextInboundQueueEvent(true); // Exit sweep mode setCurrentState(FreeRunning); // Request measurement appendItemToInboundQueue(CompAnalyzerEvent(GetMeasurement, TQVariant()), true); } else { if (state == FrequencySweepRead) { // Set up next measurement frequency m_sweepCurrentFrequency += m_sweepStep; m_sweepStepMutex->lock(); m_sweepStepNumber++; m_sweepStepMutex->unlock(); if (m_sweepCurrentFrequency <= m_sweepEnd) { // Set next sweep frequency step appendItemToInboundQueue(CompAnalyzerEvent(SetFrequency, TQVariant(m_sweepCurrentFrequency)), true); setCurrentState(FrequencySweepWrite); } else { // Exit sweep mode setCurrentState(FreeRunning); // Request measurement appendItemToInboundQueue(CompAnalyzerEvent(GetMeasurement, TQVariant()), true); } } else { // Request another measurement appendItemToInboundQueue(CompAnalyzerEvent(GetMeasurement, TQVariant()), true); } } // Send data to GUI TQByteArray measurementStreamData; { TQDataStream measurementStream(measurementStreamData, IO_WriteOnly); measurementStream << measurements; measurementStream << m_sweepStepNumber - 1; } m_outboundQueueMutex->lock(); if (state == FrequencySweepRead) { m_outboundQueue.push_back(CompAnalyzerEvent(SweepMeasurementsReceived, TQVariant(measurementStreamData))); } else { m_outboundQueue.push_back(CompAnalyzerEvent(MeasurementsReceived, TQVariant(measurementStreamData))); } m_outboundQueueMutex->unlock(); } } else if (input.startsWith("EXT")) { // Extended error TQString extendedError = input.remove(0, 3); m_outboundQueue.push_back(CompAnalyzerEvent(ExtendedErrorReceived, TQVariant(extendedError))); setCurrentState(CommunicationFailure); } else { setCurrentState(CommunicationFailure); } queue_modified = true; } else if ((state == FreeRunning) || (state == FrequencySweepWrite)) { // Request another measurement appendItemToInboundQueue(CompAnalyzerEvent(GetMeasurement, TQVariant()), true); setCurrentState(FrequencySweepRead); } m_socket->clearFrameTail(); } } m_networkDataMutex->unlock(); if (queue_modified) { emit(outboundQueueUpdated()); } } m_instrumentMutex->unlock(); // Wait for queue status change or new network activity if (!eventLoop->processEvents(TQEventLoop::ExcludeUserInput)) { eventLoop->processEvents(TQEventLoop::ExcludeUserInput | TQEventLoop::WaitForMore); } } eventLoop->exit(0); } void CompAnalyzerWorker::resetInboundQueue() { m_inboundQueueMutex->lock(); m_inboundQueue.clear(); m_inboundQueueMutex->unlock(); } void CompAnalyzerWorker::appendItemToInboundQueue(CompAnalyzerEvent item, bool syncPoint) { m_inboundQueueMutex->lock(); m_inboundQueue.push_back(item); if (syncPoint) { m_inboundQueue.push_back(CompAnalyzerEvent(TxRxSyncPoint, TQVariant())); } m_inboundQueueMutex->unlock(); } bool CompAnalyzerWorker::itemTypeInInboundQueue(CompAnalyzerEventType type) { bool ret = false; m_inboundQueueMutex->lock(); CompAnalyzerEventQueue::iterator it; for (it = m_inboundQueue.begin(); it != m_inboundQueue.end(); ++it) { if ((*it).first == type) { ret = true; } } m_inboundQueueMutex->unlock(); return ret; } bool CompAnalyzerWorker::syncPointActive() { bool active = false; m_inboundQueueMutex->lock(); CompAnalyzerEventQueue::iterator it = m_inboundQueue.begin(); if ((it) && (it != m_inboundQueue.end())) { if ((*it).first == TxRxSyncPoint) { active = true; } } m_inboundQueueMutex->unlock(); return active; } void CompAnalyzerWorker::wake() { // Do nothing -- the main event loop will wake when this is called } void CompAnalyzerWorker::dataReceived() { if (!m_networkDataMutex->tryLock()) { TQTimer::singleShot(0, this, TQT_SLOT(dataReceived())); } else { m_newData = true; m_networkDataMutex->unlock(); } } void CompAnalyzerWorker::lockOutboundQueue() { m_outboundQueueMutex->lock(); } void CompAnalyzerWorker::unlockOutboundQueue() { m_outboundQueueMutex->unlock(); } CompAnalyzerEventQueue* CompAnalyzerWorker::outboundQueue() { return &m_outboundQueue; } CompAnalyzerEventType CompAnalyzerWorker::nextInboundQueueEvent() { CompAnalyzerEventType ret = NoEvent; m_inboundQueueMutex->lock(); CompAnalyzerEventQueue::iterator it = m_inboundQueue.begin(); if ((it) && (it != m_inboundQueue.end())) { ret = (*it).first; } m_inboundQueueMutex->unlock(); return ret; } void CompAnalyzerWorker::clearInboundQueueSyncPoint() { m_inboundQueueMutex->lock(); CompAnalyzerEventQueue::iterator it = m_inboundQueue.begin(); if ((it) && (it != m_inboundQueue.end())) { if ((*it).first == TxRxSyncPoint) { m_inboundQueue.erase(it); } } m_inboundQueueMutex->unlock(); } void CompAnalyzerWorker::eraseNextInboundQueueEvent(bool clearSyncPoint) { m_inboundQueueMutex->lock(); CompAnalyzerEventQueue::iterator it = m_inboundQueue.begin(); if ((it) && (it != m_inboundQueue.end())) { m_inboundQueue.erase(it); } if (clearSyncPoint) { it = m_inboundQueue.begin(); if ((it) && (it != m_inboundQueue.end())) { if ((*it).first == TxRxSyncPoint) { m_inboundQueue.erase(it); } } } m_inboundQueueMutex->unlock(); } CompAnalyzerInstrumentLimits CompAnalyzerWorker::getInstrumentLimits() { return m_instrumentLimits; } void CompAnalyzerWorker::setNewParameterSourceList(TQValueList list) { m_sourceList = list; } CompAnalyzerPartState CompAnalyzerWorker::currentState() { CompAnalyzerPartState ret; m_currentStateMutex->lock(); ret = m_currentState; m_currentStateMutex->unlock(); return ret; } void CompAnalyzerWorker::setCurrentState(CompAnalyzerPartState state) { CompAnalyzerPartState prevState = m_currentState; m_currentStateMutex->lock(); m_currentState = state; m_currentStateMutex->unlock(); if (m_currentState != prevState) { m_outboundQueueMutex->lock(); m_outboundQueue.push_back(CompAnalyzerEvent(StateChanged, TQVariant())); m_outboundQueueMutex->unlock(); } } void CompAnalyzerWorker::setSweepStartFrequency(double hz) { m_sweepStart = hz; } void CompAnalyzerWorker::setSweepEndFrequency(double hz) { m_sweepEnd = hz; } double CompAnalyzerWorker::sweepStartFrequency() { return m_sweepStart; } double CompAnalyzerWorker::sweepEndFrequency() { return m_sweepEnd; } double CompAnalyzerWorker::sweepStepFrequency() { return m_sweepStep; } void CompAnalyzerWorker::setSweepStepFrequency(double hz) { m_sweepStep = hz; } unsigned int CompAnalyzerWorker::sweepStepNumber() { unsigned int ret; m_sweepStepMutex->lock(); ret = m_sweepStepNumber; m_sweepStepMutex->unlock(); return ret; } CompAnalyzerPart::CompAnalyzerPart( TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name, const TQStringList& ) : RemoteInstrumentPart( parent, name ), m_commHandlerState(-1), m_commHandlerMode(0), m_commHandlerCommandState(0), m_connectionActiveAndValid(false), m_instrumentSettingsValid(false), m_base(0) { // Initialize important base class variables m_clientLibraryName = CLIENT_LIBRARY; // Initialize mutex m_instrumentMutex = new TQMutex(false); // Initialize kpart setInstance(Factory::instance()); setWidget(new TQVBox(parentWidget, widgetName)); // Set up worker m_worker = new CompAnalyzerWorker(); m_workerThread = new TQEventLoopThread(); m_worker->moveToThread(m_workerThread); TQObject::connect(this, TQT_SIGNAL(wakeWorkerThread()), m_worker, TQT_SLOT(wake())); TQObject::connect(m_worker, TQT_SIGNAL(outboundQueueUpdated()), this, TQT_SLOT(processOutboundQueue())); // Create timers m_updateTimeoutTimer = new TQTimer(this); connect(m_updateTimeoutTimer, SIGNAL(timeout()), this, SLOT(networkTimeout())); // Create widgets m_base = new CompAnalyzerBase(widget()); // Initialize widgets m_base->setMinimumSize(500, 350); m_base->parameterADisplay->setNumberOfDigits(12); m_base->parameterBDisplay->setNumberOfDigits(12); m_base->frequencyDisplay->setNumberOfDigits(12); m_traceWidget = m_base->traceWidget; m_traceWidget->setSizePolicy(TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding)); m_traceWidget->setNumberOfCursors(4); m_traceWidget->setZoomCursorStartIndex(0); m_traceWidget->setCursorOrientation(0, TQt::Horizontal); m_traceWidget->setCursorOrientation(1, TQt::Horizontal); m_traceWidget->setCursorOrientation(2, TQt::Vertical); m_traceWidget->setCursorOrientation(3, TQt::Vertical); m_traceWidget->setCursorEnabled(0, true); m_traceWidget->setCursorEnabled(1, true); m_traceWidget->setCursorEnabled(2, true); m_traceWidget->setCursorEnabled(3, true); m_traceWidget->setCursorName(0, "Cursor H1"); m_traceWidget->setCursorName(1, "Cursor H2"); m_traceWidget->setCursorName(2, "Cursor V1"); m_traceWidget->setCursorName(3, "Cursor V2"); m_traceWidget->setCursorPosition(0, 25); m_traceWidget->setCursorPosition(1, 75); m_traceWidget->setCursorPosition(2, 25); m_traceWidget->setCursorPosition(3, 75); TraceNumberList activeTraces; for (uint trace=0; tracesetCursorActiveTraceList(0, activeTraces); m_traceWidget->setCursorActiveTraceList(1, activeTraces); m_traceWidget->setCursorActiveTraceList(2, activeTraces); m_traceWidget->setCursorActiveTraceList(3, activeTraces); m_traceWidget->setZoomBoxEnabled(true); connect(m_base->parameterASourceCombo, SIGNAL(activated(int)), this, SLOT(parameterASourceChanged(int))); connect(m_base->parameterBSourceCombo, SIGNAL(activated(int)), this, SLOT(parameterBSourceChanged(int))); connect(m_base->measurementFrequencyBox, SIGNAL(floatValueChanged(double)), this, SLOT(frequencyInputChanged(double))); connect(m_base->sweepStartFrequencyBox, SIGNAL(floatValueChanged(double)), this, SLOT(processLockouts())); connect(m_base->sweepEndFrequencyBox, SIGNAL(floatValueChanged(double)), this, SLOT(processLockouts())); connect(m_base->sweepStepFrequencyBox, SIGNAL(floatValueChanged(double)), this, SLOT(processLockouts())); m_base->traceZoomWidget->setSizePolicy(TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding)); connect(m_traceWidget, SIGNAL(zoomBoxChanged(const TQRectF&)), this, SLOT(updateZoomWidgetLimits(const TQRectF&))); connect(m_base->sweepStartButton, SIGNAL(clicked()), this, SLOT(startSweepClicked())); connect(m_base->sweepStopButton, SIGNAL(clicked()), this, SLOT(stopSweepClicked())); connect(m_base->waveformSave, SIGNAL(clicked()), this, SLOT(saveWaveforms())); connect(m_base->waveformRecall, SIGNAL(clicked()), this, SLOT(recallWaveforms())); connect(m_base->autoSave, SIGNAL(clicked()), this, SLOT(processLockouts())); // Initialize data m_hdivs = 10; m_vdivs = 8; m_maxNumberOfTraces = 2; for (int traceno=0; traceno<=MAXTRACES; traceno++) { m_samplesInTrace[traceno] = 0; m_channelActive[traceno] = false; m_traceUnits[traceno] = ""; } updateGraticule(); TQTimer::singleShot(0, this, TQT_SLOT(postInit())); } CompAnalyzerPart::~CompAnalyzerPart() { if (m_instrumentMutex->locked()) { printf("[WARNING] Exiting when data transfer still in progress!\n\r"); fflush(stdout); } disconnectFromServer(); delete m_instrumentMutex; if (m_workerThread) { m_workerThread->terminate(); m_workerThread->wait(); delete m_workerThread; m_workerThread = NULL; delete m_worker; m_worker = NULL; } } void CompAnalyzerPart::postInit() { setUsingFixedSize(false); } bool CompAnalyzerPart::openURL(const KURL &url) { int ret; m_connectionActiveAndValid = false; ret = connectToServer(url.url()); processLockouts(); return (ret != 0); } bool CompAnalyzerPart::closeURL() { disconnectFromServer(); m_url = KURL(); return true; } void CompAnalyzerPart::processLockouts() { CompAnalyzerPartState state = m_worker->currentState(); if (m_connectionActiveAndValid) { m_base->setEnabled(true); } else { m_base->setEnabled(false); } if ((state == FrequencySweepWrite) || (state == FrequencySweepRead)) { m_base->sweepStartButton->setEnabled(false); if (!m_worker->itemTypeInInboundQueue(AbortSweep)) { m_base->sweepStopButton->setEnabled(true); } else { m_base->sweepStopButton->setEnabled(false); } m_base->parameterASourceCombo->setEnabled(false); m_base->parameterBSourceCombo->setEnabled(false); m_base->measurementFrequencyBox->setEnabled(false); m_base->sweepStartFrequencyBox->setEnabled(false); m_base->sweepEndFrequencyBox->setEnabled(false); m_base->sweepStepFrequencyBox->setEnabled(false); m_base->waveformRecall->setEnabled(false); } else { if (m_base->sweepEndFrequencyBox->floatValue() > m_base->sweepStartFrequencyBox->floatValue()) { if (!m_worker->itemTypeInInboundQueue(StartSweep)) { m_base->sweepStartButton->setEnabled(true); } else { m_base->sweepStartButton->setEnabled(true); } } else { m_base->sweepStartButton->setEnabled(false); } m_base->sweepStopButton->setEnabled(false); if (m_instrumentSettingsValid) { m_base->parameterASourceCombo->setEnabled(true); m_base->parameterBSourceCombo->setEnabled(true); m_base->measurementFrequencyBox->setEnabled(true); } else { m_base->parameterASourceCombo->setEnabled(false); m_base->parameterBSourceCombo->setEnabled(false); m_base->measurementFrequencyBox->setEnabled(false); } m_base->sweepStartFrequencyBox->setEnabled(true); m_base->sweepEndFrequencyBox->setEnabled(true); m_base->sweepStepFrequencyBox->setEnabled(true); m_base->waveformRecall->setEnabled(true); } if (m_base->autoSave->isOn()) { m_base->autoSaveFile->setEnabled(true); } else { m_base->autoSaveFile->setEnabled(false); } } void CompAnalyzerPart::disconnectFromServerCallback() { m_updateTimeoutTimer->stop(); m_connectionActiveAndValid = false; } void CompAnalyzerPart::connectionFinishedCallback() { // Finish worker setup m_worker->m_socket = m_socket; m_worker->m_instrumentMutex = m_instrumentMutex; m_socket->moveToThread(m_workerThread); m_worker->appendItemToInboundQueue(CompAnalyzerEvent(Initialize, TQVariant()), true); connect(m_socket, SIGNAL(readyRead()), m_socket, SLOT(processPendingData())); m_socket->processPendingData(); connect(m_socket, SIGNAL(newDataReceived()), m_worker, SLOT(dataReceived())); m_tickerState = 0; m_commHandlerState = 0; m_commHandlerMode = 0; m_socket->setDataTimeout(NETWORK_COMM_TIMEOUT_MS); m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); // Start worker m_workerThread->start(); TQTimer::singleShot(0, m_worker, SLOT(run())); processLockouts(); networkTick(); return; } void CompAnalyzerPart::connectionStatusChangedCallback() { processLockouts(); } void CompAnalyzerPart::setTickerMessage(TQString message) { m_connectionActiveAndValid = true; TQString tickerChar; switch (m_tickerState) { case 0: tickerChar = "-"; break; case 1: tickerChar = "\\"; break; case 2: tickerChar = "|"; break; case 3: tickerChar = "/"; break; } setStatusMessage(message + TQString("... %1").arg(tickerChar)); m_tickerState++; if (m_tickerState > 3) { m_tickerState = 0; } } void CompAnalyzerPart::patWatchDog() { m_updateTimeoutTimer->stop(); } void CompAnalyzerPart::requestNetworkOperation(CompAnalyzerEvent item, bool syncPoint) { m_updateTimeoutTimer->stop(); m_worker->appendItemToInboundQueue(item, syncPoint); m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); emit(wakeWorkerThread()); } void CompAnalyzerPart::processOutboundQueue() { bool had_events = false; m_worker->lockOutboundQueue(); CompAnalyzerEventQueue* eventQueue = m_worker->outboundQueue(); CompAnalyzerEventQueue::iterator it; for (it = eventQueue->begin(); it != eventQueue->end(); ++it) { patWatchDog(); if ((*it).first == StateChanged) { CompAnalyzerPartState state = m_worker->currentState(); if (m_connectionActiveAndValid) { if (state == CommunicationFailure) { networkTimeout(); } } } else if ((*it).first == ExtendedErrorReceived) { m_updateTimeoutTimer->stop(); m_socket->clearIncomingData(); setStatusMessage((*it).second.toString()); m_connectionActiveAndValid = false; processLockouts(); // Try to recover m_worker->resetInboundQueue(); requestNetworkOperation(CompAnalyzerEvent(Initialize, TQVariant()), true); } else if ((*it).first == ConfigurationDataReceived) { // Get configuration data CompAnalyzerInstrumentLimits instrumentLimits = m_worker->getInstrumentLimits(); m_parameterSourceValues = instrumentLimits.allowedMeasurements; m_base->measurementFrequencyBox->setLineStep(1); m_base->measurementFrequencyBox->setFloatMax(instrumentLimits.maxFrequency / 1000000.0); m_base->measurementFrequencyBox->setFloatMin(instrumentLimits.minFrequency / 1000000.0); m_base->measurementFrequencyBox->setFloatValue(instrumentLimits.minFrequency / 1000000.0); m_base->sweepStartFrequencyBox->setLineStep(1); m_base->sweepStartFrequencyBox->setFloatMax(instrumentLimits.maxFrequency / 1000000.0); m_base->sweepStartFrequencyBox->setFloatMin(instrumentLimits.minFrequency / 1000000.0); m_base->sweepStartFrequencyBox->setFloatValue(instrumentLimits.minFrequency / 1000000.0); m_base->sweepEndFrequencyBox->setLineStep(1); m_base->sweepEndFrequencyBox->setFloatMax(instrumentLimits.maxFrequency / 1000000.0); m_base->sweepEndFrequencyBox->setFloatMin(instrumentLimits.minFrequency / 1000000.0); m_base->sweepEndFrequencyBox->setFloatValue(instrumentLimits.minFrequency / 1000000.0); m_base->sweepStepFrequencyBox->setLineStep(1); m_base->sweepStepFrequencyBox->setFloatMax((instrumentLimits.maxFrequency - instrumentLimits.minFrequency) / 1000000.0); m_base->sweepStepFrequencyBox->setFloatMin(0.000001); // 1Hz if (instrumentLimits.maxFrequency >= 1.0) { m_base->sweepStepFrequencyBox->setFloatValue(1.0); // 1MHz } else { // Fallback... m_base->sweepStepFrequencyBox->setFloatValue(instrumentLimits.minFrequency); } m_instrumentSettingsValid = false; // Update GUI unsigned int parameter_number = 0; TQValueList::iterator it; AllowedMeasurementInfoList::iterator it2; for (it = m_parameterSourceValues.begin(); it != m_parameterSourceValues.end(); ++it) { AllowedMeasurementInfoList allowedValuePairs = *it; if (parameter_number == 0) { m_base->parameterASourceCombo->clear(); for (it2 = allowedValuePairs.begin(); it2 != allowedValuePairs.end(); ++it2) { m_base->parameterASourceCombo->insertItem((*it2).second, -1); } } else if (parameter_number == 1) { m_base->parameterBSourceCombo->clear(); for (it2 = allowedValuePairs.begin(); it2 != allowedValuePairs.end(); ++it2) { m_base->parameterBSourceCombo->insertItem((*it2).second, -1); } } parameter_number++; } m_connectionActiveAndValid = true; } else if (((*it).first == MeasurementsReceived) || ((*it).first == SweepMeasurementsReceived)) { TQ_UINT32 sample_number; unsigned int parameter_number; CompAnalyzerMeasurementList measurements; TQByteArray measurementStreamData = (*it).second.toByteArray(); TQDataStream measurementStream(measurementStreamData, IO_ReadOnly); measurementStream >> measurements; measurementStream >> sample_number; // If frequency sweep is in progress, then add sample points to graph if ((*it).first == SweepMeasurementsReceived) { unsigned int traceno = 0; CompAnalyzerMeasurementList::iterator it; for (it = measurements.begin(); it != measurements.end(); ++it) { TQDoubleArray sampleArray = m_traceWidget->samples(traceno); TQDoubleArray positionArray = m_traceWidget->positions(traceno); if (sampleArray.count() < (sample_number + 1)) { sampleArray.resize(sample_number + 1); } if (positionArray.count() < (sample_number + 1)) { positionArray.resize(sample_number + 1); } sampleArray[sample_number] = (*it).value; positionArray[sample_number] = (*it).frequency; if (sample_number == 0) { m_sensorList[traceno].max = (*it).value; m_sensorList[traceno].min = (*it).value; } else { if ((*it).value > m_sensorList[traceno].max) { m_sensorList[traceno].max = (*it).value; } if ((*it).value < m_sensorList[traceno].min) { m_sensorList[traceno].min = (*it).value; } } m_traceWidget->setSamples(traceno, sampleArray); m_traceWidget->setPositions(traceno, positionArray); m_base->traceZoomWidget->setSamples(traceno, sampleArray); m_base->traceZoomWidget->setPositions(traceno, positionArray); traceno++; } updateGraticule(); m_traceWidget->repaint(false); m_base->traceZoomWidget->repaint(false); processAutosave(); } // Update displays parameter_number = 0; CompAnalyzerMeasurementList::iterator it; for (it = measurements.begin(); it != measurements.end(); ++it) { if (parameter_number == 0) { m_base->parameterADisplay->setValue((*it).value, 5, true); } else if (parameter_number == 1) { m_base->parameterBDisplay->setValue((*it).value, 5, true); } m_base->frequencyDisplay->setValue((*it).frequency / 1000000.0, 2, true); // Update instrument control selectors if (m_parameterSourceValues.count() < (parameter_number + 1)) { continue; } AllowedMeasurementInfoList::iterator it2; for (it2 = m_parameterSourceValues[parameter_number].begin(); it2 != m_parameterSourceValues[parameter_number].end(); ++it2) { if ((*it2).first == (*it).parameter) { if (parameter_number == 0) { m_base->parameterASourceCombo->setCurrentText((*it2).second); } if (parameter_number == 1) { m_base->parameterBSourceCombo->setCurrentText((*it2).second); } } } parameter_number++; } m_instrumentSettingsValid = true; m_connectionActiveAndValid = true; } had_events = true; } if (had_events) { if (m_connectionActiveAndValid) { networkTick(); } eventQueue->clear(); } m_worker->unlockOutboundQueue(); processLockouts(); } void CompAnalyzerPart::networkTick() { setTickerMessage(i18n("Connected")); m_connectionActiveAndValid = true; processLockouts(); } void CompAnalyzerPart::networkTimeout() { m_updateTimeoutTimer->stop(); m_socket->clearIncomingData(); setStatusMessage(i18n("Server ping timeout. Please verify the status of your network connection.")); m_connectionActiveAndValid = false; processLockouts(); // Try to recover m_worker->resetInboundQueue(); requestNetworkOperation(CompAnalyzerEvent(Initialize, TQVariant()), true); } void CompAnalyzerPart::updateZoomWidgetLimits(const TQRectF& zoomRect) { for (int traceno=0; tracenodisplayLimits(traceno); double widthSpan = fullZoomRect.width()-fullZoomRect.x(); double heightSpan = fullZoomRect.height()-fullZoomRect.y(); TQRectF zoomLimitsRect((fullZoomRect.x()+(widthSpan*(zoomRect.x()/100.0))), (fullZoomRect.y()+(heightSpan*(zoomRect.y()/100.0))), (fullZoomRect.x()+(widthSpan*((zoomRect.x()/100.0)+(zoomRect.width()/100.0)))), (fullZoomRect.y()+(heightSpan*((zoomRect.y()/100.0)+(zoomRect.height()/100.0))))); m_base->traceZoomWidget->setDisplayLimits(traceno, zoomLimitsRect); } } void CompAnalyzerPart::updateGraticule() { m_traceWidget->setNumberOfHorizontalDivisions(m_hdivs); m_traceWidget->setNumberOfVerticalDivisions(m_vdivs); m_base->traceZoomWidget->setNumberOfHorizontalDivisions(m_hdivs); m_base->traceZoomWidget->setNumberOfVerticalDivisions(m_vdivs); if (m_maxNumberOfTraces > 0) m_traceWidget->setTraceColor(0, TQColor(255, 255, 255)); if (m_maxNumberOfTraces > 1) m_traceWidget->setTraceColor(1, TQColor(128, 255, 128)); if (m_maxNumberOfTraces > 2) m_traceWidget->setTraceColor(2, TQColor(255, 255, 128)); if (m_maxNumberOfTraces > 3) m_traceWidget->setTraceColor(3, TQColor(128, 128, 255)); if (m_maxNumberOfTraces > 0) m_base->traceZoomWidget->setTraceColor(0, TQColor(255, 255, 255)); if (m_maxNumberOfTraces > 1) m_base->traceZoomWidget->setTraceColor(1, TQColor(128, 255, 128)); if (m_maxNumberOfTraces > 2) m_base->traceZoomWidget->setTraceColor(2, TQColor(255, 255, 128)); if (m_maxNumberOfTraces > 3) m_base->traceZoomWidget->setTraceColor(3, TQColor(128, 128, 255)); for (int traceno=0; tracenoparameterASourceCombo->currentText(); } else if (traceno == 1) { m_sensorList[traceno].name = m_base->parameterBSourceCombo->currentText(); } m_sensorList[traceno].units = parameterNameToMeasurementUnits(m_sensorList[traceno].name, traceno); m_traceWidget->setTraceEnabled(traceno, m_channelActive[traceno]); m_traceWidget->setTraceName(traceno, m_sensorList[traceno].name); m_traceWidget->setTraceHorizontalUnits(traceno, "Hz"); m_traceWidget->setTraceVerticalUnits(traceno, m_sensorList[traceno].units); m_base->traceZoomWidget->setTraceEnabled(traceno, m_channelActive[traceno], TraceWidget::SummaryText); m_base->traceZoomWidget->setTraceName(traceno, m_sensorList[traceno].name); m_base->traceZoomWidget->setTraceHorizontalUnits(traceno, "Hz"); m_base->traceZoomWidget->setTraceVerticalUnits(traceno, m_sensorList[traceno].units); double startfreq = 0.0; double endfreq = 0.0; if (m_samplesInTrace[traceno] > 0) { startfreq = m_worker->sweepStartFrequency(); endfreq = m_worker->sweepEndFrequency(); } m_traceWidget->setDisplayLimits(traceno, TQRectF(startfreq, m_sensorList[traceno].max, endfreq, m_sensorList[traceno].min)); } updateZoomWidgetLimits(m_traceWidget->zoomBox()); } void CompAnalyzerPart::frequencyInputChanged(double value) { double frequency = value * 1000000.0; requestNetworkOperation(CompAnalyzerEvent(SetFrequency, TQVariant(frequency)), true); processLockouts(); } void CompAnalyzerPart::parameterSourceChanged() { TQValueList sourceIndexList; AllowedMeasurementInfoList::iterator it2; TQString source; source = m_base->parameterASourceCombo->currentText(); for (it2 = m_parameterSourceValues[0].begin(); it2 != m_parameterSourceValues[0].end(); ++it2) { if ((*it2).second == source) { sourceIndexList.append((*it2).first); break; } } source = m_base->parameterBSourceCombo->currentText(); for (it2 = m_parameterSourceValues[1].begin(); it2 != m_parameterSourceValues[1].end(); ++it2) { if ((*it2).second == source) { sourceIndexList.append((*it2).first); break; } } if (sourceIndexList.count() >= 2) { m_worker->setNewParameterSourceList(sourceIndexList); requestNetworkOperation(CompAnalyzerEvent(ChangeMeasurementSource, TQVariant()), true); } } void CompAnalyzerPart::parameterASourceChanged(int) { parameterSourceChanged(); processLockouts(); } void CompAnalyzerPart::parameterBSourceChanged(int) { parameterSourceChanged(); processLockouts(); } void CompAnalyzerPart::startSweepClicked() { int traceno; double start = m_base->sweepStartFrequencyBox->floatValue() * 1000000.0; double end = m_base->sweepEndFrequencyBox->floatValue() * 1000000.0; double step = m_base->sweepStepFrequencyBox->floatValue() * 1000000.0; if (end <= start) { return; } m_worker->setSweepStartFrequency(start); m_worker->setSweepEndFrequency(end); m_worker->setSweepStepFrequency(step); m_sensorList.clear(); for ( traceno=0; tracenosetNumberOfSamples(traceno, m_samplesInTrace[traceno]); m_base->traceZoomWidget->setNumberOfSamples(traceno, m_samplesInTrace[traceno]); // Clear graph for (int traceno=0; tracenosamples(traceno); TQDoubleArray positionArray = m_traceWidget->positions(traceno); if (sampleArray.count() != (unsigned int)m_samplesInTrace[traceno]) { sampleArray.resize(m_samplesInTrace[traceno]); } if (positionArray.count() != (unsigned int)m_samplesInTrace[traceno]) { positionArray.resize(m_samplesInTrace[traceno]); } sampleArray.fill(NAN); positionArray.fill(NAN); m_traceWidget->setSamples(traceno, sampleArray); m_traceWidget->setPositions(traceno, positionArray); m_base->traceZoomWidget->setSamples(traceno, sampleArray); m_base->traceZoomWidget->setPositions(traceno, positionArray); } updateGraticule(); requestNetworkOperation(CompAnalyzerEvent(StartSweep, TQVariant()), true); processLockouts(); } void CompAnalyzerPart::stopSweepClicked() { requestNetworkOperation(CompAnalyzerEvent(AbortSweep, TQVariant()), true); processLockouts(); } void CompAnalyzerPart::processAutosave() { if (m_base->autoSave->isOn()) { if (m_base->autoSaveFile->url() != "") { saveWaveforms(m_base->autoSaveFile->url()); } } } #define WAVEFORM_MAGIC_NUMBER 3 #define WAVEFORM_FILE_VERSION 1 void CompAnalyzerPart::saveWaveforms() { saveWaveforms(TQString::null); } void CompAnalyzerPart::saveWaveforms(TQString fileName) { TQString saveFileName; if (fileName != "") { saveFileName = fileName; } else { saveFileName = KFileDialog::getSaveFileName(TQString::null, "*.wfm|Waveform Files (*.wfm)", 0, i18n("Save waveforms...")); } if (saveFileName != "") { TQFile file(saveFileName); file.open(IO_WriteOnly); TQDataStream ds(&file); TQ_INT32 magicNumber = WAVEFORM_MAGIC_NUMBER; TQ_INT32 version = WAVEFORM_FILE_VERSION; ds << magicNumber; ds << version; ds << m_sensorList; ds << m_hdivs; ds << m_vdivs; ds << m_maxNumberOfTraces; ds << m_worker->sweepStartFrequency(); ds << m_worker->sweepEndFrequency(); ds << m_worker->sweepStepFrequency(); for (int traceno=0; tracenosamples(traceno); ds << m_traceWidget->positions(traceno); } for (int cursorno=0; cursorno<4; cursorno++) { ds << m_traceWidget->cursorPosition(cursorno); } ds << m_base->userNotes->text(); } processLockouts(); } void CompAnalyzerPart::recallWaveforms() { TQString openFileName = KFileDialog::getOpenFileName(TQString::null, "*.wfm|Waveform Files (*.wfm)", 0, i18n("Open waveforms...")); if (openFileName != "") { TQFile file(openFileName); file.open(IO_ReadOnly); TQDataStream ds(&file); TQ_INT32 magicNumber; TQ_INT32 version; ds >> magicNumber; if (magicNumber == WAVEFORM_MAGIC_NUMBER) { ds >> version; if (version == WAVEFORM_FILE_VERSION) { double sweepStartFrequency; double sweepEndFrequency; double sweepStepFrequency; ds >> m_sensorList; ds >> m_hdivs; ds >> m_vdivs; ds >> m_maxNumberOfTraces; ds >> sweepStartFrequency; ds >> sweepEndFrequency; ds >> sweepStepFrequency; for (int traceno=0; traceno> boolValue; m_channelActive[traceno] = (boolValue!=0)?true:false; ds >> m_samplesInTrace[traceno]; ds >> m_traceUnits[traceno]; TQDoubleArray values; TQDoubleArray positions; ds >> values; ds >> positions; m_traceWidget->setNumberOfSamples(traceno, m_samplesInTrace[traceno], true); m_traceWidget->setSamples(traceno, values); m_traceWidget->setPositions(traceno, positions); m_base->traceZoomWidget->setSamples(traceno, values); m_base->traceZoomWidget->setPositions(traceno, positions); m_traceWidget->setDisplayLimits(traceno, TQRectF(positions[0], m_sensorList[traceno].max, positions[positions.count() - 1], m_sensorList[traceno].min)); if (traceno == 0) { m_worker->setSweepStartFrequency(positions[0]); m_worker->setSweepEndFrequency(positions[positions.count() - 1]); m_base->parameterASourceCombo->setCurrentText(m_sensorList[traceno].name); } else if (traceno == 1) { m_base->parameterBSourceCombo->setCurrentText(m_sensorList[traceno].name); } } for (int cursorno=0; cursorno<4; cursorno++) { double cursorPos; ds >> cursorPos; m_traceWidget->setCursorPosition(cursorno, cursorPos); } updateGraticule(); m_traceWidget->repaint(false); m_base->traceZoomWidget->repaint(false); TQString notes; ds >> notes; m_base->userNotes->setText(notes); m_base->sweepStartFrequencyBox->setFloatValue(sweepStartFrequency / 1000000.0); m_base->sweepEndFrequencyBox->setFloatValue(sweepEndFrequency / 1000000.0); m_base->sweepStepFrequencyBox->setFloatValue(sweepStepFrequency / 1000000.0); parameterSourceChanged(); } else { KMessageBox::error(0, i18n("The selected waveform file version does not match this client"), i18n("Invalid File")); } } else { KMessageBox::error(0, i18n("Invalid waveform file selected"), i18n("Invalid File")); } } processLockouts(); } TQString CompAnalyzerPart::parameterMeasurementUnits(TQ_UINT32 parameter) { TQString ret; switch (parameter) { case 0: // Resistance ret = i18n("Ω"); break; case 1: // Reactance ret = i18n("Ω"); break; case 2: // Conductance ret = i18n("S"); break; case 3: // Susceptance ret = i18n("S"); break; case 4: // Inductance ret = i18n("H"); break; case 5: // Capacitance ret = i18n("F"); break; case 6: // Dissipation Factor ret = TQString::null; break; case 7: // Quality Factor ret = TQString::null; break; case 8: // Impedance ret = i18n("Ω"); break; case 9: // Admittance ret = i18n("S"); break; case 10: // Reflection (absolute) ret = TQString::null; break; case 11: // Reflection (X) ret = TQString::null; break; case 12: // Reflection (Y) ret = TQString::null; break; case 13: // Phase angle (degrees) ret = i18n("°"); break; case 14: // Phase angle (radians) ret = i18n("rad"); break; } return ret; } TQString CompAnalyzerPart::parameterNameToMeasurementUnits(TQString name, unsigned int parameter_index) { TQString ret; AllowedMeasurementInfoList::iterator it2; for (it2 = m_parameterSourceValues[parameter_index].begin(); it2 != m_parameterSourceValues[parameter_index].end(); ++it2) { if ((*it2).second == name) { ret = parameterMeasurementUnits((*it2).first); } } return ret; } TDEAboutData* CompAnalyzerPart::createAboutData() { return new TDEAboutData( APP_NAME, I18N_NOOP( APP_PRETTYNAME ), APP_VERSION ); } } //namespace RemoteLab #include "part.moc"