diff --git a/clients/tde/configure.in b/clients/tde/configure.in index 58e7262..072a245 100644 --- a/clients/tde/configure.in +++ b/clients/tde/configure.in @@ -90,6 +90,7 @@ AC_CONFIG_FILES([ src/part/commanalyzer/Makefile ]) AC_CONFIG_FILES([ src/part/fpgaprogram/Makefile ]) AC_CONFIG_FILES([ src/part/fpgaview/Makefile ]) AC_CONFIG_FILES([ src/part/scope/Makefile ]) +AC_CONFIG_FILES([ src/part/sensormonitor/Makefile ]) AC_CONFIG_FILES([ src/widgets/Makefile ]) AC_OUTPUT # Check if KDE_SET_PREFIX was called, and --prefix was passed to configure diff --git a/clients/tde/src/part/scope/part.cpp b/clients/tde/src/part/scope/part.cpp index 03b6b25..7e64592 100644 --- a/clients/tde/src/part/scope/part.cpp +++ b/clients/tde/src/part/scope/part.cpp @@ -396,6 +396,7 @@ void ScopePart::setTickerMessage(TQString message) { } \ else { \ m_commHandlerState = ScopeState_ExternalCommandRequest; \ + EXEC_NEXT_STATE_IMMEDIATELY \ } #define EXEC_NEXT_STATE_IMMEDIATELY m_forcedUpdateTimer->start(0, TRUE); diff --git a/clients/tde/src/part/sensormonitor/part.cpp b/clients/tde/src/part/sensormonitor/part.cpp index 469f24d..31f1213 100644 --- a/clients/tde/src/part/sensormonitor/part.cpp +++ b/clients/tde/src/part/sensormonitor/part.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -58,13 +59,21 @@ #define NETWORK_COMM_TIMEOUT_MS 2500 enum connectionModes { - ModeIdle = 0 + ModeIdle = 0, + ModeInterruptRequested = 1, + ModeGetSample = 2 }; enum connectionStates { - ModeIdle_StateStatusRequest = 0, - ModeIdle_StateProcessStatus = 1, - ModeIdle_StateDelay = 2 + ModeIdle_StateSensorListRequest = 0, + ModeIdle_StateProcessSensorList = 1, + ModeIdle_StateStatusRequest = 2, + ModeIdle_StateProcessStatus = 3, + ModeIdle_StateDelay = 4, + ModeIdle_StatePaused = 5, + ModeIdle_StateExternalRequest = 6, + ModeGetSample_StateSampleRequest = 7, + ModeGetSample_StateProcessSample = 8 }; namespace RemoteLab { @@ -73,8 +82,103 @@ typedef KParts::GenericFactory Factory; #define CLIENT_LIBRARY "libremotelab_sensormonitor" K_EXPORT_COMPONENT_FACTORY(libremotelab_sensormonitor, RemoteLab::Factory) +TQValueTimer::TQValueTimer(TQObject *parent, const char *name) + : TQTimer(parent, name) +{ + connect(this, SIGNAL(timeout()), this, SLOT(timeoutHandler())); +} + +TQValueTimer::~TQValueTimer() { + // +} + +void TQValueTimer::timeoutHandler() { + emit(valueTimeout(m_value)); +} + +int TQValueTimer::value() { + return m_value; +} + +void TQValueTimer::setValue(int value) { + m_value = value; +} + +TraceControlWidget::TraceControlWidget(TQWidget *parent, const char *name) + : TQWidget(parent, name), m_minimumTimeStep(0.0), m_nominalTimeStep(1.0) +{ + TQGridLayout *topGrid = new TQGridLayout(this); + m_groupBox = new TQGroupBox(this); + m_groupBox->setColumnLayout(0, TQt::Vertical); + topGrid->addMultiCellWidget(m_groupBox, 0, 0, 0, 0); + m_groupBox->setTitle(i18n("Unknown Channel")); + m_primaryLayout = new TQGridLayout(m_groupBox->layout(), KDialog::marginHint(), KDialog::spacingHint()); + + m_channelEnabledCheckBox = new TQCheckBox(m_groupBox); + connect(m_channelEnabledCheckBox, SIGNAL(clicked()), this, SLOT(enableClicked())); + m_channelEnabledCheckBox->setText(i18n("Enable")); + m_primaryLayout->addMultiCellWidget(m_channelEnabledCheckBox, 0, 0, 0, 0); + + m_timestepSpinBox = new FloatSpinBox(m_groupBox); + m_timestepSpinBox->setFloatMax(60*60*24); // 1 day + connect(m_timestepSpinBox, SIGNAL(floatValueChanged(double)), this, SLOT(timestepChanged(double))); + m_primaryLayout->addMultiCellWidget(m_timestepSpinBox, 0, 0, 1, 1); + + m_sampleTimer = new TQTimer(); + connect(m_sampleTimer, SIGNAL(timeout()), this, SIGNAL(newSampleDesired())); +} + +TraceControlWidget::~TraceControlWidget() { + m_sampleTimer->stop(); + delete m_sampleTimer; +} + +void TraceControlWidget::startSampleTimer(int msecs) { + if (m_channelEnabledCheckBox->isOn()) { + m_nominalTimeStep = msecs/1.0e3; + m_sampleTimer->start(msecs, FALSE); + } + else { + m_sampleTimer->stop(); + } +} + +void TraceControlWidget::stopSampleTimer() { + m_sampleTimer->stop(); +} + +void TraceControlWidget::setTraceEnabled(bool enabled) { + m_channelEnabledCheckBox->setChecked(enabled); +} + +void TraceControlWidget::setTraceName(TQString name) { + m_groupBox->setTitle(name); +} + +void TraceControlWidget::setTimestep(double seconds) { + m_nominalTimeStep = seconds; + m_timestepSpinBox->setFloatValue(m_nominalTimeStep); + startSampleTimer(m_nominalTimeStep*1.0e3); +} + +void TraceControlWidget::setMinTimestep(double seconds) { + m_minimumTimeStep = seconds; + m_timestepSpinBox->setFloatMin(seconds); +} + +void TraceControlWidget::enableClicked() { + bool enabled = m_channelEnabledCheckBox->isOn(); + emit(enableChanged(enabled)); + startSampleTimer(m_nominalTimeStep*1.0e3); +} + +void TraceControlWidget::timestepChanged(double value) { + m_sampleTimer->stop(); + startSampleTimer(value*1.0e3); +} + SensorMonitorPart::SensorMonitorPart(TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name, const TQStringList&) - : RemoteInstrumentPart( parent, name ), m_base(NULL), m_commHandlerState(0), m_connectionActiveAndValid(false), m_tickerState(0) + : RemoteInstrumentPart( parent, name ), m_base(NULL), m_commHandlerState(0), m_connectionActiveAndValid(false), m_tickerState(0), stopTraceUpdate(false) { // Initialize important base class variables m_clientLibraryName = CLIENT_LIBRARY; @@ -94,12 +198,57 @@ SensorMonitorPart::SensorMonitorPart(TQWidget *parentWidget, const char *widgetN m_pingDelayTimer = new TQTimer(this); connect(m_pingDelayTimer, SIGNAL(timeout()), this, SLOT(mainEventLoop())); + // Initialize data + m_hdivs = 10; + m_vdivs = 8; + m_maxNumberOfTraces = 0; + for (int traceno=0; traceno<=MAXTRACES; traceno++) { + m_samplesInTrace[traceno] = 0; + m_channelActive[traceno] = false; + m_traceUnits[traceno] = ""; + m_traceControlWidgetList[traceno] = NULL; + m_sampleRequestInProgress[traceno] = false; + } + // Create widgets m_base = new SensorMonitorBase(widget()); + m_traceControlWidgetGrid = new TQGridLayout(m_base->traceControlLayoutWidget); + 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); + + m_base->traceZoomWidget->setSizePolicy(TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding)); + connect(m_traceWidget, SIGNAL(zoomBoxChanged(const TQRectF&)), this, SLOT(updateZoomWidgetLimits(const TQRectF&))); // Initialize widgets - connect(m_base->runControlStartButton, SIGNAL(clicked()), this, SLOT(programStartButtonClicked())); - connect(m_base->runControlStopButton, SIGNAL(clicked()), this, SLOT(programStopButtonClicked())); + connect(m_base->runControlStartButton, SIGNAL(clicked()), this, SLOT(acquisitionStartButtonClicked())); + connect(m_base->runControlStopButton, SIGNAL(clicked()), this, SLOT(acquisitionStopButtonClicked())); TQTimer::singleShot(0, this, TQT_SLOT(postInit())); } @@ -123,6 +272,14 @@ void SensorMonitorPart::processLockouts() { mainWidget->setEnabled(false); } } + if (stopTraceUpdate) { + m_base->runControlStartButton->setEnabled(true); + m_base->runControlStopButton->setEnabled(false); + } + else { + m_base->runControlStartButton->setEnabled(false); + m_base->runControlStopButton->setEnabled(true); + } } void SensorMonitorPart::resizeToHint() { @@ -160,7 +317,7 @@ void SensorMonitorPart::connectionFinishedCallback() { m_socket->processPendingData(); connect(m_socket, SIGNAL(newDataReceived()), this, SLOT(mainEventLoop())); m_tickerState = 0; - m_commHandlerState = ModeIdle_StateStatusRequest; + m_commHandlerState = ModeIdle_StateSensorListRequest; m_commHandlerMode = ModeIdle; m_socket->setDataTimeout(NETWORK_COMM_TIMEOUT_MS); m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); @@ -177,6 +334,9 @@ void SensorMonitorPart::connectionStatusChangedCallback() { m_tickerState = 0; \ m_commHandlerState = ModeIdle_StateStatusRequest; \ m_commHandlerMode = ModeIdle; \ + for (int traceno=0; traceno<=MAXTRACES; traceno++) { \ + m_sampleRequestInProgress[traceno] = false; \ + } \ m_socket->clearIncomingData(); \ setStatusMessage(i18n("Server ping timeout. Please verify the status of your network connection.")); \ m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); \ @@ -186,7 +346,14 @@ void SensorMonitorPart::connectionStatusChangedCallback() { #define SET_WATCHDOG_TIMER if (!m_updateTimeoutTimer->isActive()) m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); #define PAT_WATCHDOG_TIMER m_updateTimeoutTimer->stop(); m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); -#define SET_NEXT_STATE(x) m_commHandlerState = x; +#define SET_NEXT_STATE(x) if ((m_commHandlerMode == ModeIdle) || (m_commHandlerMode == ModeGetSample)) { \ + m_commHandlerState = x; \ + } \ + else { \ + m_commHandlerState = ModeIdle_StateExternalRequest; \ + EXEC_NEXT_STATE_IMMEDIATELY \ + } +#define SET_NEXT_STATE_DATA_WAITING(x) m_commHandlerState = x; #define EXEC_NEXT_STATE_IMMEDIATELY m_forcedUpdateTimer->start(0, TRUE); @@ -224,17 +391,42 @@ void SensorMonitorPart::mainEventLoop() { } if (m_socket) { - if (m_commHandlerMode == ModeIdle) { + if ((m_commHandlerMode == ModeIdle) || (m_commHandlerMode == ModeInterruptRequested)) { // Normal operation switch (m_commHandlerState) { + case ModeIdle_StateSensorListRequest: + PAT_WATCHDOG_TIMER + ds << TQString("SENSORS"); + m_socket->writeEndOfFrame(); + SET_NEXT_STATE_DATA_WAITING(ModeIdle_StateProcessSensorList) + break; + case ModeIdle_StateProcessSensorList: + if (m_socket->canReadFrame()) { + PAT_WATCHDOG_TIMER + + ds >> m_sensorList; + m_socket->clearFrameTail(); + + m_maxNumberOfTraces = m_sensorList.count(); + updateTraceControlWidgets(); + + SET_NEXT_STATE(ModeIdle_StateStatusRequest) + EXEC_NEXT_STATE_IMMEDIATELY + } + else { + if (!m_updateTimeoutTimer->isActive()) { + UPDATEDISPLAY_TIMEOUT + } + } + break; case ModeIdle_StateStatusRequest: - // Get status of remote system - // Clear buffers to synchronize frames in case of data corruption - m_socket->clearIncomingData(); - ds << TQString("STATUS"); + PAT_WATCHDOG_TIMER + + // Ping remote system + ds << TQString("PING"); m_socket->writeEndOfFrame(); - SET_NEXT_STATE(ModeIdle_StateProcessStatus) + SET_NEXT_STATE_DATA_WAITING(ModeIdle_StateProcessStatus) break; case ModeIdle_StateProcessStatus: // Get all data @@ -249,16 +441,14 @@ void SensorMonitorPart::mainEventLoop() { // Transfer probably failed UPDATEDISPLAY_TIMEOUT } - else if (status == "IDLE") { + else if (status == "PONG") { // Do nothing } setTickerMessage(i18n("Connected")); - if (m_commHandlerState == ModeIdle_StateProcessStatus) { - m_pingDelayTimer->start(250, TRUE); - SET_NEXT_STATE(ModeIdle_StateDelay); - } + m_pingDelayTimer->start(250, TRUE); + SET_NEXT_STATE(ModeIdle_StateDelay); } else { if (!m_updateTimeoutTimer->isActive()) { @@ -275,6 +465,67 @@ void SensorMonitorPart::mainEventLoop() { } PAT_WATCHDOG_TIMER break; + case ModeIdle_StatePaused: + PAT_WATCHDOG_TIMER + break; + case ModeIdle_StateExternalRequest: + m_commHandlerMode = ModeGetSample; + m_commHandlerState = m_commHandlerNextState; + break; + } + } + else if (m_commHandlerMode == ModeGetSample) { + if (m_commHandlerState == ModeGetSample_StateSampleRequest) { + PAT_WATCHDOG_TIMER + ds << TQString("SAMPLE"); + ds << m_sampleRequestIndex; + m_socket->writeEndOfFrame(); + SET_NEXT_STATE_DATA_WAITING(ModeGetSample_StateProcessSample) + setTickerMessage(i18n("Obtaining new data point for sensor %1").arg(m_sensorList[m_sampleRequestIndex].name)); + } + else if (m_commHandlerState == ModeGetSample_StateProcessSample) { + if (m_socket->canReadFrame()) { + PAT_WATCHDOG_TIMER + + TQString result; + double newValue; + TQDateTime timestamp; + + ds >> result; + if (result == "ACK") { + ds >> newValue; + ds >> timestamp; + + TQDoubleArray sampleArray = m_traceWidget->samples(m_sampleRequestIndex); + TQDoubleArray positionArray = m_traceWidget->positions(m_sampleRequestIndex); + m_samplesInTrace[m_sampleRequestIndex]++; + sampleArray.resize(m_samplesInTrace[m_sampleRequestIndex]); + positionArray.resize(m_samplesInTrace[m_sampleRequestIndex]); + sampleArray[m_samplesInTrace[m_sampleRequestIndex]-1] = newValue; + positionArray[m_samplesInTrace[m_sampleRequestIndex]-1] = (timestamp.toTime_t()+(timestamp.time().msec()*1.0e-3)); + + m_traceWidget->setSamples(m_sampleRequestIndex, sampleArray); + m_traceWidget->setPositions(m_sampleRequestIndex, positionArray); + m_base->traceZoomWidget->setSamples(m_sampleRequestIndex, sampleArray); + m_base->traceZoomWidget->setPositions(m_sampleRequestIndex, positionArray); + + updateGraticule(); + m_traceWidget->repaint(false); + m_base->traceZoomWidget->repaint(false); + } + + m_socket->clearFrameTail(); + m_sampleRequestInProgress[m_sampleRequestIndex] = false; + m_commHandlerMode = ModeIdle; + m_pingDelayTimer->start(250, TRUE); + SET_NEXT_STATE(ModeIdle_StateDelay); + EXEC_NEXT_STATE_IMMEDIATELY + } + else { + if (!m_updateTimeoutTimer->isActive()) { + UPDATEDISPLAY_TIMEOUT + } + } } } @@ -288,6 +539,183 @@ void SensorMonitorPart::mainEventLoop() { m_connectionMutex->unlock(); } +void SensorMonitorPart::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 SensorMonitorPart::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; tracenosetTraceEnabled(traceno, m_channelActive[traceno]); + m_traceWidget->setTraceName(traceno, m_sensorList[traceno].name); + m_traceWidget->setTraceHorizontalUnits(traceno, "s"); + m_traceWidget->setTraceVerticalUnits(traceno, m_sensorList[traceno].units); + + m_base->traceZoomWidget->setTraceEnabled(traceno, m_channelActive[traceno], false); + m_base->traceZoomWidget->setTraceName(traceno, m_sensorList[traceno].name); + m_base->traceZoomWidget->setTraceHorizontalUnits(traceno, "s"); + m_base->traceZoomWidget->setTraceVerticalUnits(traceno, m_sensorList[traceno].units); + + m_traceWidget->setNumberOfSamples(traceno, m_samplesInTrace[traceno]); + m_base->traceZoomWidget->setNumberOfSamples(traceno, m_samplesInTrace[traceno]); + + double starttime = 0.0; + double endtime = 0.0; + if (m_samplesInTrace[traceno] > 0) { + starttime = m_traceWidget->positions(traceno)[0]; + endtime = m_traceWidget->positions(traceno)[m_samplesInTrace[traceno]-1]; + } + m_traceWidget->setDisplayLimits(traceno, TQRectF(starttime, m_sensorList[traceno].max, endtime, m_sensorList[traceno].min)); + if (m_traceControlWidgetList[traceno]) { + m_traceControlWidgetList[traceno]->setTraceEnabled(m_channelActive[traceno]); + } + } + updateZoomWidgetLimits(m_traceWidget->zoomBox()); +} + +void SensorMonitorPart::updateTraceControlWidgets() { + // Add or remove trace control widgets as needed... + int i; + for (i=0; itraceControlLayoutWidget); + connect(m_traceControlWidgetList[i], SIGNAL(enableChanged(bool)), this, SLOT(traceControlEnableChanged(bool))); + connect(m_traceControlWidgetList[i], SIGNAL(newSampleDesired()), this, SLOT(processNewSampleRequest())); + m_traceControlWidgetGrid->addMultiCellWidget(m_traceControlWidgetList[i], i, i, 0, 0); + m_traceControlWidgetList[i]->setTraceName(m_sensorList[i].name); + m_traceControlWidgetList[i]->show(); + // Set sample rate + m_traceControlWidgetList[i]->setMinTimestep(m_sensorList[i].mininterval); + m_traceControlWidgetList[i]->setTimestep(m_sensorList[i].nominalinterval); + } + } + for (i=m_maxNumberOfTraces; iremove(m_traceControlWidgetList[i]); + delete m_traceControlWidgetList[i]; + } + } +} + +void SensorMonitorPart::traceControlEnableChanged(bool enabled) { + int i; + int channel = -1; + const TraceControlWidget* widget = dynamic_cast(sender()); + if (widget) { + for (i=0; i= 0) && (channel <=MAXTRACES)) { + m_channelActive[channel] = enabled; + } + } + + updateGraticule(); + m_traceWidget->repaint(false); + m_base->traceZoomWidget->repaint(false); + updateTraceControlWidgets(); +} + +void SensorMonitorPart::processNewSampleRequest() { + int i; + int channel = -1; + const TraceControlWidget* widget = dynamic_cast(sender()); + if (widget) { + for (i=0; i= 0) && (channel <=MAXTRACES)) { + if (!stopTraceUpdate) { + if (!m_sampleRequestInProgress[channel]) { + m_sampleRequestInProgress[channel] = true; + processNewSampleRequest(channel); + } + else { + printf("[WARNING] Sample request made while previous sample not collected. Some data was not captured (therefore lost)...\n\r"); + } + } + } + } +} + +void SensorMonitorPart::processNewSampleRequest(int channel) { + TQValueTimer* senderTimer = const_cast(dynamic_cast(sender())); + if (senderTimer) { + senderTimer->stop(); + delete senderTimer; + } + if (m_commHandlerMode == ModeIdle) { + // Request a sample + if (m_commHandlerState == ModeIdle_StateDelay) { + m_commHandlerMode = ModeGetSample; + m_commHandlerState = ModeGetSample_StateSampleRequest; + EXEC_NEXT_STATE_IMMEDIATELY + } + else { + m_commHandlerMode = ModeInterruptRequested; + m_commHandlerNextState = ModeGetSample_StateSampleRequest; + } + m_sampleRequestIndex = channel; + } + else { + // The main loop is already getting a sample + // Resubmit the request later on + TQValueTimer* timer = new TQValueTimer; + timer->setValue(channel); + connect(timer, SIGNAL(valueTimeout(int)), this, SLOT(processNewSampleRequest(int))); + timer->start(10, TRUE); + } +} + +void SensorMonitorPart::acquisitionStartButtonClicked() { + stopTraceUpdate = false; + processLockouts(); + if (m_socket) m_socket->clearIncomingData(); + m_commHandlerMode = ModeIdle; + m_commHandlerState = ModeIdle_StateStatusRequest; + EXEC_NEXT_STATE_IMMEDIATELY +} + +void SensorMonitorPart::acquisitionStopButtonClicked() { + stopTraceUpdate = true; + processLockouts(); + for (int i=0; i #include #include -#include #include +#include "floatspinbox.h" + +#define MAXTRACES 255 + class KAboutData; using KParts::StatusBarExtension; class TraceWidget; class TQSocket; class TQTimer; class TQMutex; +class TQRectF; +class TQGridLayout; +class TQCheckBox; +class TQGroupBox; class TQFile; class SensorMonitorBase; namespace RemoteLab { + class Q_EXPORT TQValueTimer : public TQTimer + { + Q_OBJECT + + public: + TQValueTimer(TQObject *parent=0, const char *name=0); + ~TQValueTimer(); + int value(); + void setValue(int); + + signals: + void valueTimeout(int); + + private slots: + void timeoutHandler(); + + private: + int m_value; + }; + + class TraceControlWidget : public TQWidget + { + Q_OBJECT + + public: + TraceControlWidget(TQWidget *parent=0, const char *name=0); + ~TraceControlWidget(); + + public: + void setTraceEnabled(bool enabled); + void setTraceName(TQString name); + void setTimestep(double seconds); + void setMinTimestep(double seconds); + void startSampleTimer(int msecs); + void stopSampleTimer(); + + signals: + void enableChanged(bool enabled); + void newSampleDesired(); + + private slots: + void enableClicked(); + void timestepChanged(double); + + private: + TQGroupBox* m_groupBox; + TQGridLayout* m_primaryLayout; + TQCheckBox* m_channelEnabledCheckBox; + FloatSpinBox* m_timestepSpinBox; + TQTimer* m_sampleTimer; + + double m_minimumTimeStep; + double m_nominalTimeStep; + }; + class SensorMonitorPart : public KParts::RemoteInstrumentPart { Q_OBJECT @@ -62,6 +124,7 @@ namespace RemoteLab public slots: virtual bool openURL(const KURL &url); + void updateZoomWidgetLimits(const TQRectF& zoomRect); private slots: void postInit(); @@ -73,9 +136,19 @@ namespace RemoteLab void disconnectFromServerCallback(); void connectionStatusChangedCallback(); void setTickerMessage(TQString message); + + void updateGraticule(); + void updateTraceControlWidgets(); + void traceControlEnableChanged(bool enabled); + void processNewSampleRequest(); + void processNewSampleRequest(int channel); + void acquisitionStartButtonClicked(); + void acquisitionStopButtonClicked(); private: SensorMonitorBase* m_base; + TraceWidget* m_traceWidget; + TQGridLayout* m_traceControlWidgetGrid; TQMutex* m_connectionMutex; TQTimer* m_pingDelayTimer; TQTimer* m_forcedUpdateTimer; @@ -87,9 +160,18 @@ namespace RemoteLab int m_commHandlerNextMode; bool m_connectionActiveAndValid; unsigned char m_tickerState; - TQByteArray m_programmingFileData; - TQ_ULONG m_programmingFileTotalSize; - TQ_ULONG m_programmingFileTransferredBytes; + + bool stopTraceUpdate; + 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]; + TraceControlWidget* m_traceControlWidgetList[MAXTRACES]; + TQ_UINT32 m_sampleRequestIndex; + bool m_sampleRequestInProgress[MAXTRACES+1]; }; } diff --git a/lib/libtqtrla/src/tqtrla.cpp b/lib/libtqtrla/src/tqtrla.cpp index 711804e..75bae5e 100644 --- a/lib/libtqtrla/src/tqtrla.cpp +++ b/lib/libtqtrla/src/tqtrla.cpp @@ -454,5 +454,47 @@ TQDataStream &operator>>( TQDataStream &s, StationType &st ) s >> st.description; return s; } + +/*! + \relates SensorType + + Writes the SensorType \a str to the stream \a s. + + See also \link datastreamformat.html Format of the TQDataStream operators \endlink +*/ + +TQDataStream &operator<<( TQDataStream &s, const SensorType &st ) +{ + s << st.index; + s << st.name; + s << st.description; + s << st.units; + s << st.min; + s << st.max; + s << st.mininterval; + s << st.nominalinterval; + return s; +} + +/*! + \relates SensorType + + Reads a SensorType from the stream \a s into SensorType \a str. + + See also \link datastreamformat.html Format of the TQDataStream operators \endlink +*/ + +TQDataStream &operator>>( TQDataStream &s, SensorType &st ) +{ + s >> st.index; + s >> st.name; + s >> st.description; + s >> st.units; + s >> st.min; + s >> st.max; + s >> st.mininterval; + s >> st.nominalinterval; + return s; +} #endif // QT_NO_DATASTREAM diff --git a/lib/libtqtrla/src/tqtrla.h b/lib/libtqtrla/src/tqtrla.h index be105b8..e46e2fc 100644 --- a/lib/libtqtrla/src/tqtrla.h +++ b/lib/libtqtrla/src/tqtrla.h @@ -163,4 +163,26 @@ typedef TQValueList StationList; // ============================================================================= +class SensorType +{ + public: + TQ_UINT32 index; + TQString name; + TQString description; + TQString units; + double min; + double max; + double mininterval; + double nominalinterval; +}; + +#ifndef QT_NO_DATASTREAM +Q_EXPORT TQDataStream &operator<<(TQDataStream &, const SensorType &); +Q_EXPORT TQDataStream &operator>>(TQDataStream &, SensorType &); +#endif + +typedef TQValueList SensorList; + +// ============================================================================= + #endif // TQTRLA_H \ No newline at end of file diff --git a/servers/sensor_monitor_server_lin/src/Makefile.am b/servers/sensor_monitor_server_lin/src/Makefile.am index bd6b9ee..7b28ea9 100644 --- a/servers/sensor_monitor_server_lin/src/Makefile.am +++ b/servers/sensor_monitor_server_lin/src/Makefile.am @@ -1,11 +1,11 @@ INCLUDES= $(all_includes) $(KDE_INCLUDES)/tde -I/usr/include/sasl KDE_CXXFLAGS = $(USE_EXCEPTIONS) -bin_PROGRAMS = remotefpga_fpgaprogserver +bin_PROGRAMS = remotefpga_sensormonserver -remotefpga_fpgaprogserver_SOURCES = main.cpp fpga_conn.cpp +remotefpga_sensormonserver_SOURCES = main.cpp sensor_conn.cpp -remotefpga_fpgaprogserver_METASOURCES = AUTO -remotefpga_fpgaprogserver_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_KIO) -lktexteditor -ltdekrbsocket -ltqtrla +remotefpga_sensormonserver_METASOURCES = AUTO +remotefpga_sensormonserver_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_KIO) -lktexteditor -ltdekrbsocket -ltqtrla KDE_OPTIONS = nofinal diff --git a/servers/sensor_monitor_server_lin/src/main.cpp b/servers/sensor_monitor_server_lin/src/main.cpp index 3a5012b..763c6a2 100644 --- a/servers/sensor_monitor_server_lin/src/main.cpp +++ b/servers/sensor_monitor_server_lin/src/main.cpp @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) KSimpleConfig config(TQDir::currentDirPath() + "/remotefpga_sensormonserver.conf", false); config.setGroup("Server"); - SensorServer fpgasvr(0, config.readNumEntry("port", 4014), &config); + SensorServer sensorsvr(0, config.readNumEntry("port", 4014), &config); return app.exec(); } diff --git a/servers/sensor_monitor_server_lin/src/sensor_conn.cpp b/servers/sensor_monitor_server_lin/src/sensor_conn.cpp index b73e480..86b8db0 100644 --- a/servers/sensor_monitor_server_lin/src/sensor_conn.cpp +++ b/servers/sensor_monitor_server_lin/src/sensor_conn.cpp @@ -56,12 +56,7 @@ struct exit_exception { }; enum connectionStates { - StateIdle = 0, - StateGetFileSize = 1, - StateGetFileContents = 2, - StateStartProgramming = 3, - StateCheckProgrammingStatus = 4, - StateProgammingFinished = 5 + StateIdle = 0 }; /* @@ -70,24 +65,37 @@ enum connectionStates { instance of this class. */ SensorSocket::SensorSocket(int sock, TQObject *parent, const char *name) : - TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_loopTimer(NULL), m_config(static_cast(parent)->m_config), m_commandLoopState(StateIdle), - m_progpipe(NULL), m_progpipefd(-1), m_progErrorFlag(false), m_progDoneFlag(false) + TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_loopTimer(NULL), m_config(static_cast(parent)->m_config), m_commandLoopState(StateIdle) { // Initialize timers m_kerberosInitTimer = new TQTimer(); connect(m_kerberosInitTimer, SIGNAL(timeout()), this, SLOT(finishKerberosHandshake())); m_servClientTimeout = new TQTimer(); + + // Initialize data structures + int i; + for (i=0;istop(); + delete m_sensorMinIntervalTimers[j]; + m_sensorMinIntervalTimers[j] = NULL; + } + } if (m_servClientTimeout) { m_servClientTimeout->stop(); delete m_servClientTimeout; @@ -161,6 +169,49 @@ void SensorSocket::finishKerberosHandshake() { } } +void SensorSocket::initializeSensors() { + int i=0; + m_sensorList.clear(); + m_sensorExecInfo.clear(); + m_config->setGroup("Sensors"); + TQStringList sensorNameList = m_config->readListEntry("active"); + for (TQStringList::Iterator it = sensorNameList.begin(); it != sensorNameList.end(); ++it) { + TQString sensorName = *it; + if (m_config->hasGroup(TQString("Sensor %1").arg(sensorName))) { + SensorType st; + m_config->setGroup(TQString("Sensor %1").arg(sensorName)); + st.index = i; + st.name = sensorName; + st.description = m_config->readEntry("name", i18n("")); + st.units = m_config->readEntry("units", i18n("")); + st.min = m_config->readDoubleNumEntry("minvalue", 0.0); + st.max = m_config->readDoubleNumEntry("maxvalue", 1.0); + st.mininterval = m_config->readDoubleNumEntry("mininterval", 1.0); + st.nominalinterval = m_config->readDoubleNumEntry("nominalinterval", 10.0); + m_sensorList.append(st); + m_sensorExecInfo[i] = m_config->readEntry("exec", "echo 0 && exit"); + if (!m_sensorMinIntervalTimers[i]) m_sensorMinIntervalTimers[i] = new TQTimer(); + m_sensorMinIntervalTimers[i]->stop(); + printf("[DEBUG] Added new sensor %s at index %d\n\r", st.name.ascii(), st.index); + i++; + } + else { + printf("[WARNING] Unknown sensor %s specified in sensor list. Ignoring...\n\r", sensorName.ascii()); + } + if (i>=MAX_SENSORS) { + printf("[WARNING] MAX_SENSORS (%d) exceeded. Ignoring any additional sensor definitions...\n\r", MAX_SENSORS); + break; + } + } + for (int j=i;jstop(); + delete m_sensorMinIntervalTimers[j]; + m_sensorMinIntervalTimers[j] = NULL; + } + } +} + void SensorSocket::commandLoop() { bool transferred_data; @@ -168,8 +219,7 @@ void SensorSocket::commandLoop() { try { transferred_data = false; if (state() == TQSocket::Connected) { - if ((m_commandLoopState == StateIdle) || (m_commandLoopState == StateStartProgramming) || (m_commandLoopState == StateCheckProgrammingStatus) || (m_commandLoopState == StateProgammingFinished)) { - // Certain commands can come in at any time during some operations + if (m_commandLoopState == StateIdle) { if (canReadLine()) { processPendingData(); } @@ -178,145 +228,71 @@ void SensorSocket::commandLoop() { ds.setPrintableData(true); TQString command; ds >> command; - clearFrameTail(); - if (command == "STATUS") { - if (m_logMessages != "") { - ds << TQString("LOGMESSAGES"); - writeEndOfFrame(); - ds << m_logMessages; - writeEndOfFrame(); - m_logMessages = ""; - } - else if (m_progErrorFlag) { - ds << TQString("ERROR"); - m_progErrorFlag = false; - writeEndOfFrame(); - } - else if (m_progDoneFlag) { - ds << TQString("DONE"); - ds << m_progRetCode; - m_progDoneFlag = false; - writeEndOfFrame(); - } - else if (m_commandLoopState == StateIdle) { - ds << TQString("IDLE"); - writeEndOfFrame(); - } - else if ((m_commandLoopState == StateStartProgramming) || (m_commandLoopState == StateCheckProgrammingStatus) || (m_commandLoopState == StateProgammingFinished)) { - ds << TQString("PROGRAMMING"); - writeEndOfFrame(); - } - else { - ds << TQString("UNKNOWN"); - writeEndOfFrame(); - } + if (command == "SENSORS") { + clearFrameTail(); + ds << m_sensorList; + writeEndOfFrame(); } - else if (m_commandLoopState == StateIdle) { - if (command == "FILE") { - m_commandLoopState = StateGetFileSize; + else if (command == "SAMPLE") { + TQ_UINT32 sensorIndex; + ds >> sensorIndex; + clearFrameTail(); + printf("[DEBUG] Requested sample from sensor at index %d\n\r", sensorIndex); + if (sensorIndex >= m_sensorList.count()) { + ds << TQString("NCK"); } - else if (command == "PROGRAM") { - m_commandLoopState = StateStartProgramming; + else if (m_sensorMinIntervalTimers[sensorIndex]->isActive()) { + ds << TQString("DLY"); } else { - printf("[WARNING] Received unknown command '%s'\n\r", command.ascii()); + double sampleValue; + bool commandSuccess = true; + long long intervalMsec = (m_sensorList[sensorIndex].mininterval*1.0e3); + m_sensorMinIntervalTimers[sensorIndex]->start(intervalMsec, TRUE); + TQDateTime timestamp = TQDateTime::currentDateTime(TQt::UTC); + TQString command = m_sensorExecInfo[sensorIndex]; + FILE* pipe = popen(command.ascii(), "r"); + if (!pipe) { + commandSuccess = false; + } + else { + char buffer[1024]; + TQString result = ""; + while(!feof(pipe)) { + if (fgets(buffer, 1024, pipe) != NULL) { + result += buffer; + } + } + TQ_INT32 retcode = pclose(pipe); + sampleValue = result.toDouble(); + if (retcode != 0) { + commandSuccess = false; + } + } + if (commandSuccess) { + ds << TQString("ACK"); + ds << sampleValue; + ds << timestamp; + } + else { + ds << TQString("NCK"); + } } + writeEndOfFrame(); } - transferred_data = true; - } - } - if (m_commandLoopState == StateGetFileSize) { - if (canReadLine()) { - processPendingData(); - } - if (canReadFrame()) { - TQDataStream ds(this); - ds.setPrintableData(true); - ds >> m_programmingFileSize; - clearFrameTail(); - m_servClientTimeout->start(NETWORK_COMM_TIMEOUT_MS, TRUE); - m_commandLoopState = StateGetFileContents; - } - } - else if (m_commandLoopState == StateGetFileContents) { - if (canReadLine()) { - m_servClientTimeout->start(NETWORK_COMM_TIMEOUT_MS, TRUE); - processPendingData(); - } - if (bytesAvailable() >= m_programmingFileSize) { - TQByteArray fileContents(m_programmingFileSize); - readBlock(fileContents.data(), fileContents.size()); - m_programmingFileName = TQString("/tmp/%1#%2.dat").arg(m_remoteHost).arg(port()); - TQFile outputFile(m_programmingFileName); - if (outputFile.open(IO_ReadWrite)) { - outputFile.writeBlock(fileContents); - outputFile.flush(); - outputFile.close(); + else if (command == "PING") { + clearFrameTail(); + ds << TQString("PONG"); + writeEndOfFrame(); } - transferred_data = true; - m_commandLoopState = StateIdle; - } - else { - if (!m_servClientTimeout->isActive()) { - m_progErrorFlag = true; - transferred_data = true; - m_commandLoopState = StateIdle; + else { + clearFrameTail(); + printf("[WARNING] Received unknown command %s from host %s\n\r", command.ascii(), m_remoteHost.ascii()); fflush(stdout); + ds << TQString("NCK"); + writeEndOfFrame(); } - } - } - else if (m_commandLoopState == StateStartProgramming) { - // Start programming! - - // Open programming process - m_config->setGroup("Programming"); - TQString programmingScript = m_config->readEntry("script"); - programmingScript.replace("%f", m_programmingFileName); - if (!programmingScript.contains("2>&1")) { - programmingScript.append(" 2>&1"); - } - if ((m_progpipe = popen(programmingScript.ascii(), "r")) == NULL) { - m_logMessages.append(TQString("The system was unable to execute '%1'\nPlease contact your system administrator with this information").arg(programmingScript)); - m_progErrorFlag = true; transferred_data = true; - m_commandLoopState = StateIdle; - } - else { - m_progpipefd = fileno(m_progpipe); - fcntl(m_progpipefd, F_SETFL, O_NONBLOCK); - } - m_commandLoopState = StateCheckProgrammingStatus; - } - else if (m_commandLoopState == StateCheckProgrammingStatus) { - // Check programming status - TQCString buf; - buf.resize(8192); - ssize_t r = read(m_progpipefd, buf.data(), buf.size()); - if ((r == -1) && (errno == EAGAIN)) { - // No data available yet } - else if (r > 0) { - // Data was received - buf.data()[r] = 0; - m_logMessages.append(buf); - } - else { - // Process terminated - m_commandLoopState = StateProgammingFinished; - } - } - else if (m_commandLoopState == StateProgammingFinished) { - // Programming process terminated; get exit code and clean up - if (m_progpipe) { - m_progRetCode = pclose(m_progpipe); - } - else { - m_progRetCode = -1; - } - m_progpipe = NULL; - m_progpipefd = -1; - - m_progDoneFlag = true; - m_commandLoopState = StateIdle; } } m_criticalSection--; diff --git a/servers/sensor_monitor_server_lin/src/sensor_conn.h b/servers/sensor_monitor_server_lin/src/sensor_conn.h index 9486c6f..05c939a 100644 --- a/servers/sensor_monitor_server_lin/src/sensor_conn.h +++ b/servers/sensor_monitor_server_lin/src/sensor_conn.h @@ -38,13 +38,17 @@ #define MAGIC_NUMBER 1 #define PROTOCOL_VERSION 1 -class SensorServer : public TDEKerberosServerSocket +#define MAX_SENSORS 255 + +typedef TQMap SensorStringMap; + +class SensorSocket : public TDEKerberosServerSocket { Q_OBJECT public: - SensorServer(int sock, TQObject *parent=0, const char *name=0); - ~SensorServer(); + SensorSocket(int sock, TQObject *parent=0, const char *name=0); + ~SensorSocket(); public: void close(); @@ -55,9 +59,9 @@ class SensorServer : public TDEKerberosServerSocket void finishKerberosHandshake(); void connectionClosedHandler(); void commandLoop(); + void initializeSensors(); private: - int line; int m_criticalSection; TQString m_remoteHost; @@ -68,26 +72,20 @@ class SensorServer : public TDEKerberosServerSocket KSimpleConfig* m_config; int m_commandLoopState; - TQ_ULONG m_programmingFileSize; - TQString m_programmingFileName; - FILE *m_progpipe; - int m_progpipefd; - - bool m_progErrorFlag; - bool m_progDoneFlag; - TQ_INT32 m_progRetCode; - TQString m_logMessages; + SensorList m_sensorList; + SensorStringMap m_sensorExecInfo; + TQTimer* m_sensorMinIntervalTimers[MAX_SENSORS]; - friend class FPGAServer; + friend class SensorServer; }; -class FPGAServer : public TQServerSocket +class SensorServer : public TQServerSocket { Q_OBJECT public: - FPGAServer(TQObject* parent=0, int port=0, KSimpleConfig* config=0); - ~FPGAServer(); + SensorServer(TQObject* parent=0, int port=0, KSimpleConfig* config=0); + ~SensorServer(); void newConnection(int socket); @@ -95,12 +93,12 @@ class FPGAServer : public TQServerSocket void remoteConnectionClosed(); signals: - void newConnect(SensorServer*); + void newConnect(SensorSocket*); private: KSimpleConfig* m_config; int m_numberOfConnections; - friend class SensorServer; + friend class SensorSocket; }; \ No newline at end of file