/* * Remote Laboratory FPGA Programming 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) 2012 Timothy Pearson * Raptor Engineering * http://www.raptorengineeringinc.com */ #include "define.h" #include "part.h" #include //::createAboutData() #include #include #include #include #include //::start() #include #include #include #include #include #include #include //encodeName() #include //postInit() hack #include #include #include #include #include #include #include #include #include #include #include //access() #include #include #include "tracewidget.h" #include "floatspinbox.h" #include "layout.h" #define NETWORK_COMM_TIMEOUT_MS 2500 enum connectionModes { ModeIdle = 0, ModeInterruptRequested = 1, ModeGetSample = 2 }; enum connectionStates { 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 { 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), stopTraceUpdate(false) { // Initialize important base class variables m_clientLibraryName = CLIENT_LIBRARY; // Initialize mutex m_connectionMutex = new TQMutex(false); // Initialize kpart setInstance(Factory::instance()); setWidget(new TQVBox(parentWidget, widgetName)); // Create timers m_forcedUpdateTimer = new TQTimer(this); connect(m_forcedUpdateTimer, SIGNAL(timeout()), this, SLOT(mainEventLoop())); m_updateTimeoutTimer = new TQTimer(this); connect(m_updateTimeoutTimer, SIGNAL(timeout()), this, SLOT(mainEventLoop())); 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(acquisitionStartButtonClicked())); connect(m_base->runControlStopButton, SIGNAL(clicked()), this, SLOT(acquisitionStopButtonClicked())); TQTimer::singleShot(0, this, TQT_SLOT(postInit())); } SensorMonitorPart::~SensorMonitorPart() { if (m_connectionMutex->locked()) { printf("[WARNING] Exiting when data transfer still in progress!\n\r"); fflush(stdout); } disconnectFromServer(); delete m_connectionMutex; } void SensorMonitorPart::processLockouts() { TQWidget* mainWidget = widget(); if (mainWidget) { if ((m_socket) && (m_socket->state() == TQSocket::Connected) && (connToServerState > 0) && (connToServerConnecting == false)) { mainWidget->setEnabled(true); } else { 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() { resize(widget()->sizeHint()); } void SensorMonitorPart::connectionClosed() { closeURL(); } void SensorMonitorPart::postInit() { setUsingFixedSize(false); } bool SensorMonitorPart::openURL(const KURL &url) { int ret; ret = connectToServer(url.url()); processLockouts(); return (ret != 0); } bool SensorMonitorPart::closeURL() { disconnectFromServer(); m_url = KURL(); return true; } void SensorMonitorPart::disconnectFromServerCallback() { m_forcedUpdateTimer->stop(); m_updateTimeoutTimer->stop(); } void SensorMonitorPart::connectionFinishedCallback() { connect(m_socket, SIGNAL(readyRead()), m_socket, SLOT(processPendingData())); m_socket->processPendingData(); connect(m_socket, SIGNAL(newDataReceived()), this, SLOT(mainEventLoop())); m_tickerState = 0; m_commHandlerState = ModeIdle_StateSensorListRequest; m_commHandlerMode = ModeIdle; m_socket->setDataTimeout(NETWORK_COMM_TIMEOUT_MS); m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE); processLockouts(); mainEventLoop(); return; } void SensorMonitorPart::connectionStatusChangedCallback() { processLockouts(); } #define UPDATEDISPLAY_TIMEOUT m_connectionActiveAndValid = false; \ 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); \ m_connectionMutex->unlock(); \ return; #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) 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); void SensorMonitorPart::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 SensorMonitorPart::mainEventLoop() { TQDataStream ds(m_socket); ds.setPrintableData(true); if (!m_connectionMutex->tryLock()) { EXEC_NEXT_STATE_IMMEDIATELY return; } if (m_socket) { 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: PAT_WATCHDOG_TIMER // Ping remote system ds << TQString("PING"); m_socket->writeEndOfFrame(); SET_NEXT_STATE_DATA_WAITING(ModeIdle_StateProcessStatus) break; case ModeIdle_StateProcessStatus: // Get all data if (m_socket->canReadFrame()) { PAT_WATCHDOG_TIMER TQString status; ds >> status; m_socket->clearFrameTail(); if (status == "") { // Transfer probably failed UPDATEDISPLAY_TIMEOUT } else if (status == "PONG") { // Do nothing } setTickerMessage(i18n("Connected")); m_pingDelayTimer->start(250, TRUE); SET_NEXT_STATE(ModeIdle_StateDelay); } else { if (!m_updateTimeoutTimer->isActive()) { UPDATEDISPLAY_TIMEOUT } } break; case ModeIdle_StateDelay: // Let the client and server rest for a bit to lower CPU/network overhead if (!m_pingDelayTimer->isActive()) { EXEC_NEXT_STATE_IMMEDIATELY // Execute query on next event loop SET_NEXT_STATE(ModeIdle_StateStatusRequest); } 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 } } } } SET_WATCHDOG_TIMER } else { SET_NEXT_STATE(ModeIdle_StateStatusRequest); m_commHandlerMode = ModeIdle; } 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