From 5d4ae8c1ec03ba409ad3aa37310ca6aa162f454b Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Sat, 12 Sep 2015 19:56:53 -0500 Subject: Add inital component analyzer part Fix a couple of Kerberos socket class threading problems Stop drawing garbage in trace viewer when trace data partially intialized Add FloatSpinBox explicit data accessor --- clients/tde/src/part/Makefile.am | 2 +- clients/tde/src/part/companalyzer/Makefile.am | 12 + .../hi16-action-libremotelab_companalyzer.png | Bin 0 -> 749 bytes clients/tde/src/part/companalyzer/layout.ui | 488 +++++++ clients/tde/src/part/companalyzer/part.cpp | 1406 ++++++++++++++++++++ clients/tde/src/part/companalyzer/part.h | 260 ++++ clients/tde/src/widgets/floatspinbox.cpp | 10 + clients/tde/src/widgets/floatspinbox.h | 3 + clients/tde/src/widgets/sevensegment.cpp | 78 +- clients/tde/src/widgets/tracewidget.cpp | 9 + 10 files changed, 2242 insertions(+), 26 deletions(-) create mode 100644 clients/tde/src/part/companalyzer/Makefile.am create mode 100644 clients/tde/src/part/companalyzer/hi16-action-libremotelab_companalyzer.png create mode 100644 clients/tde/src/part/companalyzer/layout.ui create mode 100755 clients/tde/src/part/companalyzer/part.cpp create mode 100644 clients/tde/src/part/companalyzer/part.h mode change 100644 => 100755 clients/tde/src/widgets/sevensegment.cpp (limited to 'clients') diff --git a/clients/tde/src/part/Makefile.am b/clients/tde/src/part/Makefile.am index 2e4b01a..7c3f426 100644 --- a/clients/tde/src/part/Makefile.am +++ b/clients/tde/src/part/Makefile.am @@ -1 +1 @@ -SUBDIRS = scope logicanalyzer commanalyzer fpgaview fpgaprogram sensormonitor adminconsole adminusermgmt serialconsole prototerminal +SUBDIRS = scope logicanalyzer commanalyzer companalyzer fpgaview fpgaprogram sensormonitor adminconsole adminusermgmt serialconsole prototerminal diff --git a/clients/tde/src/part/companalyzer/Makefile.am b/clients/tde/src/part/companalyzer/Makefile.am new file mode 100644 index 0000000..67e4865 --- /dev/null +++ b/clients/tde/src/part/companalyzer/Makefile.am @@ -0,0 +1,12 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/src -I$(top_srcdir)/src/widgets $(KDE_INCLUDES)/tde +KDE_CXXFLAGS = $(USE_EXCEPTIONS) +METASOURCES = AUTO + +KDE_ICON = libremotelab_companalyzer + +#Part +kde_module_LTLIBRARIES = libremotelab_companalyzer.la +libremotelab_companalyzer_la_LIBADD = ../../widgets/libtracewidget.la ../../widgets/libfloatspinbox.la ../../widgets/libsevensegment.la $(LIB_KFILE) $(LIB_TDEPARTS) $(LIB_TDEUI) $(LIB_QT) +libremotelab_companalyzer_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -ltdecore -ltdeui -ltdeio -ltdefx -ltdekrbsocket -ltqtrla +libremotelab_companalyzer_la_SOURCES = \ + part.cpp layout.ui diff --git a/clients/tde/src/part/companalyzer/hi16-action-libremotelab_companalyzer.png b/clients/tde/src/part/companalyzer/hi16-action-libremotelab_companalyzer.png new file mode 100644 index 0000000..937a414 Binary files /dev/null and b/clients/tde/src/part/companalyzer/hi16-action-libremotelab_companalyzer.png differ diff --git a/clients/tde/src/part/companalyzer/layout.ui b/clients/tde/src/part/companalyzer/layout.ui new file mode 100644 index 0000000..4523b6b --- /dev/null +++ b/clients/tde/src/part/companalyzer/layout.ui @@ -0,0 +1,488 @@ + + CompAnalyzerBase + + + CompAnalyzerBase + + + + 0 + 0 + 519 + 356 + + + + + unnamed + + + + unnamed_layout + + + + + groupParameterADisplay + + + Parameter A: + + + + unnamed_grid + + + + parameterADisplay + + + + + + + groupParameterBDisplay + + + Parameter B: + + + + unnamed_grid + + + + parameterBDisplay + + + + + + + groupFrequencyDisplay + + + Frequency: + + + + unnamed_grid + + + + frequencyDisplay + + + + + + + + + unnamed_layout + + + + + groupMonitorView + + + Trace Viewer + + + + 7 + 7 + 1 + 1 + + + + + unnamed + + + + splitter1 + + + Vertical + + + + traceWidget + + + + 0 + 0 + + + + + + traceZoomWidget + + + + 0 + 0 + + + + + + + + + + + groupControl + + + Controls + + + + unnamed + + + + groupMonitorCaptureControls + + + Capture Controls + + + + + unnamed + + + Parameter A: + + + + + parameterASourceCombo + + + + 1 + 0 + 0 + 0 + + + + + + unnamed + + + Parameter B: + + + + + parameterBSourceCombo + + + + 1 + 0 + 0 + 0 + + + + + + unnamed + + + Frequency: + + + PlainText + + + + + measurementFrequencyBox + + + true + + + + 1 + 0 + 0 + 0 + + + + + 80 + 0 + + + + 1 + + + 1 + + + 1 + + + + + + + groupSweepControls + + + Sweep Configuration + + + + + unnamed + + + Start Frequency: + + + PlainText + + + + + sweepStartFrequencyBox + + + true + + + + 1 + 0 + 0 + 0 + + + + + 80 + 0 + + + + 1 + + + 1 + + + 1 + + + + + unnamed + + + End Frequency: + + + PlainText + + + + + sweepEndFrequencyBox + + + true + + + + 1 + 0 + 0 + 0 + + + + + 80 + 0 + + + + 1 + + + 1 + + + 1 + + + + + unnamed + + + Step Frequency: + + + PlainText + + + + + sweepStepFrequencyBox + + + true + + + + 1 + 0 + 0 + 0 + + + + + 80 + 0 + + + + 1 + + + 1 + + + 1 + + + + + + + groupAcquisitionControls + + + Acquisition Controls + + + + + sweepStartButton + + + Start Sweep + + + + + sweepStopButton + + + Stop Sweep + + + + + waveformSave + + + Save Waveforms + + + + + waveformRecall + + + Recall Waveforms + + + + + autoSave + + + Enable Automatic Saving + + + + + autoSaveFile + + + 25 + + + *.wfm|Waveform Files (*.wfm) + + + + + + + groupTestNotes + + + Notes + + + + 5 + 3 + 0 + 1 + + + + + + userNotes + + + + + + + 5 + 3 + 0 + 1 + + + + + + + + + + + CompAnalyzerBase.ui.h + + + tracewidget.h + floatspinbox.h + sevensegment.h + + + + diff --git a/clients/tde/src/part/companalyzer/part.cpp b/clients/tde/src/part/companalyzer/part.cpp new file mode 100755 index 0000000..66eb188 --- /dev/null +++ b/clients/tde/src/part/companalyzer/part.cpp @@ -0,0 +1,1406 @@ +/* + * 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 - 2015 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; +} + +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); + } + 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); + } + + 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::parameterASourceChanged(int index) { + TQValueList sourceIndexList; + TQString newSource = m_base->parameterASourceCombo->text(index); + TQString source = m_base->parameterBSourceCombo->currentText(); + + AllowedMeasurementInfoList::iterator it2; + for (it2 = m_parameterSourceValues[0].begin(); it2 != m_parameterSourceValues[0].end(); ++it2) { + if ((*it2).second == newSource) { + sourceIndexList.append((*it2).first); + break; + } + } + + 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); + } + + processLockouts(); +} + +void CompAnalyzerPart::parameterBSourceChanged(int index) { + TQValueList sourceIndexList; + TQString newSource = m_base->parameterBSourceCombo->text(index); + TQString source = m_base->parameterASourceCombo->currentText(); + + AllowedMeasurementInfoList::iterator it2; + for (it2 = m_parameterSourceValues[0].begin(); it2 != m_parameterSourceValues[0].end(); ++it2) { + if ((*it2).second == source) { + sourceIndexList.append((*it2).first); + break; + } + } + + for (it2 = m_parameterSourceValues[1].begin(); it2 != m_parameterSourceValues[1].end(); ++it2) { + if ((*it2).second == newSource) { + sourceIndexList.append((*it2).first); + break; + } + } + + if (sourceIndexList.count() >= 2) { + m_worker->setNewParameterSourceList(sourceIndexList); + requestNetworkOperation(CompAnalyzerEvent(ChangeMeasurementSource, TQVariant()), true); + } + + 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; + 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) { + ds >> m_sensorList; + ds >> m_hdivs; + ds >> m_vdivs; + ds >> m_maxNumberOfTraces; + 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]); + } + } + 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); + } + 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" diff --git a/clients/tde/src/part/companalyzer/part.h b/clients/tde/src/part/companalyzer/part.h new file mode 100644 index 0000000..7dfdd32 --- /dev/null +++ b/clients/tde/src/part/companalyzer/part.h @@ -0,0 +1,260 @@ +/* + * 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 - 2015 Timothy Pearson + * Raptor Engineering + * http://www.raptorengineeringinc.com + */ + +#ifndef REMOTELAB_COMPANALYZERPART_H +#define REMOTELAB_COMPANALYZERPART_H + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#define MAXTRACES 255 + +class TDEAboutData; +using KParts::StatusBarExtension; +class TraceWidget; +class TQSocket; +class TQTimer; +class TQMutex; +class TQRectF; +class CompAnalyzerBase; + +namespace RemoteLab +{ + typedef enum CompAnalyzerEventType { + NoEvent = 0, + Initialize = 1, + TxRxSyncPoint = 2, + StateChanged = 3, + ExtendedErrorReceived = 4, + ConfigurationDataReceived = 5, + ChangeMeasurementSource = 6, + GetMeasurement = 7, + GetMaximumFrequency = 8, + GetMinimumFrequency = 9, + SetFrequency = 10, + MeasurementsReceived = 11, + SweepMeasurementsReceived = 12, + StartSweep = 13, + AbortSweep = 14, + OtherEvent = 15 + } CompAnalyzerEventType; + + typedef enum CompAnalyzerStartupState { + StartSelectInstrument = 0, + StartGetMaximumFrequency = 1, + StartGetMinimumFrequency = 2, + StartDone = 3 + } CompAnalyzerStartupState; + + typedef enum CompAnalyzerPartState { + Idle = 0, + Initializing = 1, + FreeRunning = 2, + FrequencySweepWrite = 3, + FrequencySweepRead = 4, + CommunicationFailure = 5 + } CompAnalyzerPartState; + + typedef struct CompAnalyzerMeasurement { + TQ_UINT32 status; + TQ_UINT32 parameter; + TQ_UINT32 type; + double value; + double frequency; + } CompAnalyzerMeasurement; + + typedef TQPair CompAnalyzerEvent; + typedef TQValueVector CompAnalyzerEventQueue; + typedef TQValueList CompAnalyzerMeasurementList; + + typedef TQPair AllowedMeasurementInfo; + typedef TQValueList AllowedMeasurementInfoList; + + typedef struct CompAnalyzerInstrumentLimits { + TQValueList allowedMeasurements; + double maxFrequency; + double minFrequency; + } CompAnalyzerInstrumentLimits; + +#ifndef QT_NO_DATASTREAM + Q_EXPORT TQDataStream &operator<<(TQDataStream &, const CompAnalyzerMeasurement &); + Q_EXPORT TQDataStream &operator>>(TQDataStream &, CompAnalyzerMeasurement &); +#endif + + class CompAnalyzerWorker : public TQObject + { + TQ_OBJECT + + public: + CompAnalyzerWorker(); + ~CompAnalyzerWorker(); + + signals: + void outboundQueueUpdated(); + + public slots: + void run(); + void wake(); + void dataReceived(); + + public: + void resetInboundQueue(); + void appendItemToInboundQueue(CompAnalyzerEvent item, bool syncPoint=false); + bool itemTypeInInboundQueue(CompAnalyzerEventType type); + bool syncPointActive(); + void lockOutboundQueue(); + void unlockOutboundQueue(); + CompAnalyzerEventQueue* outboundQueue(); + CompAnalyzerPartState currentState(); + + CompAnalyzerInstrumentLimits getInstrumentLimits(); + void setNewParameterSourceList(TQValueList); + + void setSweepStartFrequency(double hz); + void setSweepEndFrequency(double hz); + void setSweepStepFrequency(double hz); + double sweepStartFrequency(); + double sweepEndFrequency(); + unsigned int sweepStepNumber(); + + private: + CompAnalyzerEventType nextInboundQueueEvent(); + void clearInboundQueueSyncPoint(); + void eraseNextInboundQueueEvent(bool clearSyncPoint=false); + void setCurrentState(CompAnalyzerPartState state); + + public: + TDEKerberosClientSocket* m_socket; + TQMutex* m_instrumentMutex; + + private: + CompAnalyzerEventQueue m_outboundQueue; + CompAnalyzerEventQueue m_inboundQueue; + TQMutex* m_outboundQueueMutex; + TQMutex* m_inboundQueueMutex; + TQMutex* m_networkDataMutex; + TQMutex* m_currentStateMutex; + TQMutex* m_sweepStepMutex; + CompAnalyzerPartState m_currentState; + CompAnalyzerStartupState m_startupState; + CompAnalyzerEventType m_lastNetworkTransmissionEvent; + bool m_newData; + TQValueList m_sourceList; + CompAnalyzerInstrumentLimits m_instrumentLimits; + + double m_sweepStart; + double m_sweepEnd; + double m_sweepStep; + double m_sweepCurrentFrequency; + TQ_UINT32 m_sweepStepNumber; + }; + + class CompAnalyzerPart : public KParts::RemoteInstrumentPart + { + Q_OBJECT + + public: + CompAnalyzerPart( QWidget *, const char *, TQObject *, const char *, const TQStringList&); + ~CompAnalyzerPart(); + + virtual bool openFile() { return false; } // pure virtual in the base class + virtual bool closeURL(); + static TDEAboutData *createAboutData(); + + signals: + void wakeWorkerThread(); + + public slots: + virtual bool openURL(const KURL &url); + void processOutboundQueue(); + void updateZoomWidgetLimits(const TQRectF& zoomRect); + + private slots: + void postInit(); + void processLockouts(); + void connectionFinishedCallback(); + void disconnectFromServerCallback(); + void connectionStatusChangedCallback(); + void setTickerMessage(TQString message); + void networkTick(); + void networkTimeout(); + void parameterASourceChanged(int); + void parameterBSourceChanged(int); + void frequencyInputChanged(double); + void startSweepClicked(); + void stopSweepClicked(); + + void processAutosave(); + void saveWaveforms(); + void saveWaveforms(TQString fileName); + void recallWaveforms(); + + void updateGraticule(); + + private: + TQString parameterMeasurementUnits(TQ_UINT32 parameter); + TQString parameterNameToMeasurementUnits(TQString name, unsigned int parameter_index); + void requestNetworkOperation(CompAnalyzerEvent item, bool syncPoint=false); + void patWatchDog(); + + private: + int m_commHandlerState; + int m_commHandlerMode; + int m_commHandlerCommandState; + TQTimer* m_updateTimeoutTimer; + bool m_connectionActiveAndValid; + bool m_instrumentSettingsValid; + unsigned char m_tickerState; + CompAnalyzerBase* m_base; + TQMutex* m_instrumentMutex; + TQString m_TextToSend; + TQValueList m_parameterSourceValues; + + TraceWidget* m_traceWidget; + TQGridLayout* m_traceControlWidgetGrid; + + SensorList m_sensorList; + TQ_INT16 m_maxNumberOfTraces; + TQ_INT16 m_hdivs; + TQ_INT16 m_vdivs; + TQ_INT32 m_samplesInTrace[MAXTRACES+1]; + bool m_channelActive[MAXTRACES+1]; + TQString m_traceUnits[MAXTRACES+1]; + + CompAnalyzerWorker* m_worker; + TQEventLoopThread* m_workerThread; + }; +} + +#endif diff --git a/clients/tde/src/widgets/floatspinbox.cpp b/clients/tde/src/widgets/floatspinbox.cpp index 9592968..9bcff87 100644 --- a/clients/tde/src/widgets/floatspinbox.cpp +++ b/clients/tde/src/widgets/floatspinbox.cpp @@ -100,6 +100,16 @@ void FloatSpinBox::setFloatValue(double d) { setValue( ROUND( (value - min) * pow( 10, dec )) ); } +double FloatSpinBox::floatValue() { + return value; +} + +void FloatSpinBox::setEnabled(bool enable) { + if (enable != isEnabled()) { + TQSpinBox::setEnabled(enable); + } +} + void FloatSpinBox::acceptValueChanged(int ival) { Q_UNUSED(ival); emit floatValueChanged( value ); diff --git a/clients/tde/src/widgets/floatspinbox.h b/clients/tde/src/widgets/floatspinbox.h index a577649..102584f 100644 --- a/clients/tde/src/widgets/floatspinbox.h +++ b/clients/tde/src/widgets/floatspinbox.h @@ -44,6 +44,8 @@ class FloatSpinBox : public TQSpinBox void setFloatMax(double max); void setPrecision(int precision); + double floatValue(); + private: int dec; virtual TQString mapValueToText(int value); @@ -51,6 +53,7 @@ class FloatSpinBox : public TQSpinBox public slots: void setFloatValue(double d); + virtual void setEnabled(bool); private slots: void acceptValueChanged(int value); diff --git a/clients/tde/src/widgets/sevensegment.cpp b/clients/tde/src/widgets/sevensegment.cpp old mode 100644 new mode 100755 index d7e7a07..f6d8469 --- a/clients/tde/src/widgets/sevensegment.cpp +++ b/clients/tde/src/widgets/sevensegment.cpp @@ -77,16 +77,23 @@ void Display7Segment::setLitSegments(unsigned char segs) { unsigned char Display7Segment::segmentsForNumericDigit(unsigned char val, bool dp) { unsigned char ret = 0; - if (val == 0) { ret = 0x7e; } - else if (val == 1) { ret = 0x30; } - else if (val == 2) { ret = 0x6d; } - else if (val == 3) { ret = 0x79; } - else if (val == 4) { ret = 0x33; } - else if (val == 5) { ret = 0x5b; } - else if (val == 6) { ret = 0x5f; } - else if (val == 7) { ret = 0x70; } - else if (val == 8) { ret = 0x7f; } - else if (val == 9) { ret = 0x73; } + if (val == 0x0) { ret = 0x7e; } + else if (val == 0x1) { ret = 0x30; } + else if (val == 0x2) { ret = 0x6d; } + else if (val == 0x3) { ret = 0x79; } + else if (val == 0x4) { ret = 0x33; } + else if (val == 0x5) { ret = 0x5b; } + else if (val == 0x6) { ret = 0x5f; } + else if (val == 0x7) { ret = 0x70; } + else if (val == 0x8) { ret = 0x7f; } + else if (val == 0x9) { ret = 0x73; } + else if (val == 0xa) { ret = 0x7d; } + else if (val == 0xb) { ret = 0x1f; } + else if (val == 0xc) { ret = 0x0d; } + else if (val == 0xd) { ret = 0x3d; } + else if (val == 0xe) { ret = 0x4f; } + else if (val == 0xf) { ret = 0x67; } + else if (val == 0x10) { ret = 0x01; } // "-" if (dp) { ret |= 0x80; @@ -496,7 +503,9 @@ void Display7SegmentArray::init() { unsigned int i; // Set up grid layout - m_layout = new TQGridLayout(this, 1, m_numberOfDigits); + m_layout = new TQGridLayout(this, 1, m_numberOfDigits + 1); + TQSpacerItem *spacerItem = new TQSpacerItem(0, 0, TQSizePolicy::Expanding, TQSizePolicy::Fixed); + m_layout->addItem(spacerItem); m_layout->setAutoAdd(true); // Allocate display array @@ -528,7 +537,7 @@ void Display7SegmentArray::init() { m_displayArray[i]->setFrameStyle(TQFrame::NoFrame); } setBackgroundColor(TQt::black); - setFrameStyle(TQFrame::Box | TQFrame::Raised); + setFrameStyle(TQFrame::Box); #endif } @@ -575,6 +584,9 @@ void Display7SegmentArray::setValue(double value, int maxDecimalLength, bool for bool dp; unsigned int i; TQString displayString = TQString("%1").arg(value); + if (displayString.contains("e")) { + maxDecimalLength = -1; + } if (maxDecimalLength >= 0) { int decPos = displayString.find("."); if (decPos >= 0) { @@ -598,23 +610,39 @@ void Display7SegmentArray::setValue(double value, int maxDecimalLength, bool for if (displayString.contains(".")) { current_char--; } - for (i=0; i < m_numberOfDigits; i++) { - if (current_char < 0) { - m_displayArray[i]->setLitSegments(0x0); + if (displayString.length() > m_numberOfDigits) { + // String is too large to display on the specified number of 7-segment digits + for (i=0; i < m_numberOfDigits; i++) { + m_displayArray[i]->setLitSegments(Display7Segment::segmentsForNumericDigit(0x10, false)); } - else { - dp = false; - if (current_char < (displayString.length() - 1)) { - if (displayString[current_char + 1] == '.') { - dp = true; - } + } + else { + for (i=0; i < m_numberOfDigits; i++) { + if (current_char < 0) { + m_displayArray[i]->setLitSegments(0x0); } - if (displayString[current_char] == '.') { - current_char++; + else { + dp = false; + if (current_char < (displayString.length() - 1)) { + if (displayString[current_char + 1] == '.') { + dp = true; + } + } + if (displayString[current_char] == '.') { + current_char++; + } + if (displayString[current_char] == '-') { + m_displayArray[i]->setLitSegments(Display7Segment::segmentsForNumericDigit(0x10, dp)); + } + else if (displayString[current_char] == 'e') { + m_displayArray[i]->setLitSegments(Display7Segment::segmentsForNumericDigit(0xe, dp)); + } + else { + m_displayArray[i]->setLitSegments(Display7Segment::segmentsForNumericDigit(TQString(displayString[current_char]).toInt(NULL, 10), dp)); + } } - m_displayArray[i]->setLitSegments(Display7Segment::segmentsForNumericDigit(TQString(displayString[current_char]).toInt(NULL, 10), dp)); + current_char++; } - current_char++; } } diff --git a/clients/tde/src/widgets/tracewidget.cpp b/clients/tde/src/widgets/tracewidget.cpp index 08cfe42..1d5440c 100644 --- a/clients/tde/src/widgets/tracewidget.cpp +++ b/clients/tde/src/widgets/tracewidget.cpp @@ -695,6 +695,15 @@ void TraceData::drawTrace(TQPainter* p, int graticule_width, int graticule_heigh activeSamples = abs(rightEdgeIndex-leftEdgeIndex); incr = (activeSamples/virtual_width)+1; for (n=leftEdgeIndex; n