You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

995 lines
32 KiB

/*
* Remote Laboratory Sensor Monitor 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-2019 Timothy Pearson
* Raptor Engineering
* http://www.raptorengineeringinc.com
*/
#include "define.h"
#include "part.h"
#include <tdeaboutdata.h> //::createAboutData()
#include <tdeaction.h>
#include <tdelocale.h>
#include <ktextedit.h>
#include <tdefiledialog.h>
#include <tdemessagebox.h> //::start()
#include <tdeparts/genericfactory.h>
#include <kstatusbar.h>
#include <kstdaction.h>
#include <knuminput.h>
#include <kmdcodec.h>
#include <kurlrequester.h>
#include <tqfile.h> //encodeName()
#include <tqtimer.h> //postInit() hack
#include <tqvbox.h>
#include <tqsocket.h>
#include <tqmutex.h>
#include <tqlayout.h>
#include <tqeventloop.h>
#include <tqapplication.h>
#include <tqgroupbox.h>
#include <tqcheckbox.h>
#include <tqpushbutton.h>
#include <tqprogressbar.h>
#include <unistd.h> //access()
#include <stdint.h>
#include <tqpainter.h>
#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_StateResetRequest = 4,
ModeIdle_StateProcessReset = 5,
ModeIdle_StateDelay = 6,
ModeIdle_StatePaused = 7,
ModeIdle_StateExternalRequest = 8,
ModeGetSample_StateSampleRequest = 9,
ModeGetSample_StateProcessSample = 10
};
namespace RemoteLab {
typedef KParts::GenericFactory<RemoteLab::SensorMonitorPart> 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(), 1, 1, 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, 2);
m_valueLabel = new TQLabel(m_groupBox);
m_valueLabel->setText(i18n("<qt><nobr>Value: <b>%1</b></qt>").arg(i18n("<unknown>")));
m_primaryLayout->addMultiCellWidget(m_valueLabel, 1, 1, 0, 2);
m_valueLabel->hide();
m_timestampLabel = new TQLabel(m_groupBox);
m_timestampLabel->setText(i18n("<qt><nobr>Sampled: <b>%1</b></qt>").arg(i18n("<unknown>")));
m_primaryLayout->addMultiCellWidget(m_timestampLabel, 2, 2, 0, 2);
m_timestampLabel->hide();
m_timestepLabel1 = new TQLabel(m_groupBox);
m_timestepLabel1->setText(i18n("<qt><nobr>Sample every:</qt>"));
m_primaryLayout->addMultiCellWidget(m_timestepLabel1, 3, 3, 0, 0);
m_timestepLabel1->hide();
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, 3, 3, 1, 1);
m_timestepSpinBox->hide();
m_timestepLabel2 = new TQLabel(m_groupBox);
m_timestepLabel2->setText(i18n("<qt><nobr>seconds<qt>"));
m_primaryLayout->addMultiCellWidget(m_timestepLabel2, 3, 3, 2, 2);
m_timestepLabel2->hide();
m_clearPushButton = new TQPushButton(m_groupBox);
m_clearPushButton->setText(i18n("Erase Existing Data"));
connect(m_clearPushButton, SIGNAL(clicked()), this, SLOT(clearTraceDataClicked()));
m_primaryLayout->addMultiCellWidget(m_clearPushButton, 5, 5, 0, 2);
m_clearPushButton->setSizePolicy(TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Minimum));
m_clearPushButton->hide();
m_sampleTimer = new TQTimer();
connect(m_sampleTimer, SIGNAL(timeout()), this, SIGNAL(newSampleDesired()));
}
TraceControlWidget::~TraceControlWidget() {
m_sampleTimer->stop();
delete m_sampleTimer;
}
void TraceControlWidget::clearTraceDataClicked() {
emit(clearTraceRequested());
}
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);
if (enabled) {
m_timestepLabel1->show();
m_timestepLabel2->show();
m_timestepSpinBox->show();
m_valueLabel->show();
m_timestampLabel->show();
m_clearPushButton->show();
}
else {
m_timestepLabel1->hide();
m_timestepLabel2->hide();
m_timestepSpinBox->hide();
m_valueLabel->hide();
m_timestampLabel->hide();
m_clearPushButton->hide();
}
}
void TraceControlWidget::setTraceName(TQString name) {
m_groupBox->setTitle(name);
}
double TraceControlWidget::timestep() {
return m_nominalTimeStep;
}
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);
if (seconds < 0.001) {
m_timestepSpinBox->setPrecision(3);
}
else if (seconds < 0.01) {
m_timestepSpinBox->setPrecision(2);
}
else if (seconds < 0.1) {
m_timestepSpinBox->setPrecision(1);
}
else {
m_timestepSpinBox->setPrecision(0);
}
}
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);
}
void TraceControlWidget::setCurrentSampleValue(double value, TQString units) {
m_valueLabel->setText(i18n("<qt><nobr>Value: <b>%1%2</b></qt>").arg(value).arg(units));
}
void TraceControlWidget::setCurrentSampleTimestamp(TQDateTime timestamp) {
m_timestampLabel->setText(i18n("<qt><nobr>Sampled: <b>%1 UTC</b></qt>").arg(timestamp.toString()));
}
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; trace<MAXTRACES; trace++) {
activeTraces.append(trace);
}
m_traceWidget->setCursorActiveTraceList(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()));
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()));
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);
m_base->waveformSave->setEnabled(true);
m_base->waveformRecall->setEnabled(true);
m_base->autoSave->setEnabled(true);
if (m_base->autoSave->isOn()) {
m_base->autoSaveFile->setEnabled(true);
}
else {
m_base->autoSaveFile->setEnabled(false);
}
}
else {
m_base->runControlStartButton->setEnabled(false);
m_base->runControlStopButton->setEnabled(true);
m_base->waveformSave->setEnabled(false);
m_base->waveformRecall->setEnabled(false);
m_base->autoSave->setEnabled(false);
m_base->autoSaveFile->setEnabled(false);
}
}
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; \
if (m_commHandlerState != ModeIdle_StateProcessReset) { \
m_commHandlerState = ModeIdle_StateResetRequest; \
} \
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_StateResetRequest:
// Reset remote system
ds << TQString("RESET");
m_socket->writeEndOfFrame();
SET_NEXT_STATE_DATA_WAITING(ModeIdle_StateProcessReset)
break;
case ModeIdle_StateProcessReset:
// Get all data
if (m_socket->canReadFrame()) {
PAT_WATCHDOG_TIMER
TQString status;
ds >> status;
m_socket->clearFrameTail();
if (status == "RESET") {
SET_NEXT_STATE(ModeIdle_StateStatusRequest)
EXEC_NEXT_STATE_IMMEDIATELY
}
}
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);
m_traceControlWidgetList[m_sampleRequestIndex]->setCurrentSampleValue(newValue, m_sensorList[m_sampleRequestIndex].units);
m_traceControlWidgetList[m_sampleRequestIndex]->setCurrentSampleTimestamp(timestamp);
updateGraticule();
m_traceWidget->repaint(false);
m_base->traceZoomWidget->repaint(false);
}
processAutosave();
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; traceno<m_maxNumberOfTraces; traceno++) {
TQRectF fullZoomRect = m_traceWidget->displayLimits(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; traceno<m_maxNumberOfTraces; traceno++) {
m_traceWidget->setTraceEnabled(traceno, m_channelActive[traceno]);
m_traceWidget->setTraceName(traceno, i18n("Sensor %1").arg(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], TraceWidget::SummaryText);
m_base->traceZoomWidget->setTraceName(traceno, i18n("Sensor %1").arg(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; i<m_maxNumberOfTraces;i++) {
if (!m_traceControlWidgetList[i]) {
m_traceControlWidgetList[i] = new TraceControlWidget(m_base->traceControlLayoutWidget);
connect(m_traceControlWidgetList[i], SIGNAL(enableChanged(bool)), this, SLOT(traceControlEnableChanged(bool)));
connect(m_traceControlWidgetList[i], SIGNAL(newSampleDesired()), this, SLOT(processNewSampleRequest()));
connect(m_traceControlWidgetList[i], SIGNAL(clearTraceRequested()), this, SLOT(traceControlClearRequested()));
m_traceControlWidgetGrid->addMultiCellWidget(m_traceControlWidgetList[i], i, i, 0, 0);
m_traceControlWidgetList[i]->setTraceName(i18n("Sensor %1").arg(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; i<MAXTRACES;i++) {
if (m_traceControlWidgetList[i]) {
m_traceControlWidgetGrid->remove(m_traceControlWidgetList[i]);
delete m_traceControlWidgetList[i];
}
}
}
void SensorMonitorPart::traceControlEnableChanged(bool enabled) {
int i;
int channel = -1;
const TraceControlWidget* widget = dynamic_cast<const TraceControlWidget*>(sender());
if (widget) {
for (i=0; i<MAXTRACES;i++) {
if (m_traceControlWidgetList[i] == widget) {
channel = i;
break;
}
}
if ((channel >= 0) && (channel <=MAXTRACES)) {
m_channelActive[channel] = enabled;
}
}
updateGraticule();
m_traceWidget->repaint(false);
m_base->traceZoomWidget->repaint(false);
updateTraceControlWidgets();
}
void SensorMonitorPart::traceControlClearRequested() {
int i;
int channel = -1;
const TraceControlWidget* widget = dynamic_cast<const TraceControlWidget*>(sender());
if (widget) {
for (i=0; i<MAXTRACES;i++) {
if (m_traceControlWidgetList[i] == widget) {
channel = i;
break;
}
}
if ((channel >= 0) && (channel <=MAXTRACES)) {
m_samplesInTrace[channel] = 0;
TQDoubleArray sampleArray;
TQDoubleArray positionArray;
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);
m_traceControlWidgetList[m_sampleRequestIndex]->setCurrentSampleValue(0, m_sensorList[channel].units);
m_traceControlWidgetList[m_sampleRequestIndex]->setCurrentSampleTimestamp(TQDateTime());
}
}
updateGraticule();
m_traceWidget->repaint(false);
m_base->traceZoomWidget->repaint(false);
updateTraceControlWidgets();
}
void SensorMonitorPart::processNewSampleRequest() {
int i;
int channel = -1;
const TraceControlWidget* widget = dynamic_cast<const TraceControlWidget*>(sender());
if (widget) {
for (i=0; i<MAXTRACES;i++) {
if (m_traceControlWidgetList[i] == widget) {
channel = i;
break;
}
}
if ((channel >= 0) && (channel <=MAXTRACES)) {
if ((!stopTraceUpdate) && (m_commHandlerState != ModeIdle_StateProcessReset) && (m_commHandlerState != ModeIdle_StateResetRequest)) {
if (!m_sampleRequestInProgress[channel]) {
m_sampleRequestInProgress[channel] = true;
processNewSampleRequest(channel);
}
else {
setStatusMessage(i18n("Sample request made while previous sample not collected. Some data was not captured (therefore lost) for sensor %1").arg(m_sensorList[channel].name));
}
}
}
}
}
void SensorMonitorPart::processNewSampleRequest(int channel) {
TQValueTimer* senderTimer = const_cast<TQValueTimer*>(dynamic_cast<const TQValueTimer*>(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
if ((!stopTraceUpdate) && (m_commHandlerState != ModeIdle_StateProcessReset) && (m_commHandlerState != ModeIdle_StateResetRequest)) {
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<MAXTRACES;i++) {
m_sampleRequestInProgress[i] = false;
}
m_commHandlerMode = ModeIdle;
m_commHandlerState = ModeIdle_StatePaused;
setStatusMessage(i18n("Acquisition stopped"));
mainEventLoop();
}
void SensorMonitorPart::processAutosave() {
if (m_base->autoSave->isOn()) {
if (m_base->autoSaveFile->url() != "") {
saveWaveforms(m_base->autoSaveFile->url());
}
}
}
#define WAVEFORM_MAGIC_NUMBER 2
#define WAVEFORM_FILE_VERSION 1
void SensorMonitorPart::saveWaveforms() {
saveWaveforms(TQString::null);
}
void SensorMonitorPart::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; traceno<m_maxNumberOfTraces; traceno++) {
TQ_UINT8 boolValue;
boolValue = m_channelActive[traceno];
ds << boolValue;
ds << m_traceControlWidgetList[traceno]->timestep();
ds << m_samplesInTrace[traceno];
ds << m_traceUnits[traceno];
ds << m_traceWidget->samples(traceno);
ds << m_traceWidget->positions(traceno);
}
for (int cursorno=0; cursorno<4; cursorno++) {
ds << m_traceWidget->cursorPosition(cursorno);
}
}
}
void SensorMonitorPart::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;
updateTraceControlWidgets();
for (int traceno=0; traceno<m_maxNumberOfTraces; traceno++) {
TQ_UINT8 boolValue;
ds >> boolValue;
m_channelActive[traceno] = (boolValue!=0)?true:false;
double timestep;
ds >> timestep;
m_traceControlWidgetList[traceno]->setTimestep(timestep);
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);
if (m_samplesInTrace[traceno] > 0) {
m_traceControlWidgetList[traceno]->setCurrentSampleValue(values[m_samplesInTrace[traceno]-1], m_sensorList[traceno].units);
TQDateTime lastSampleTime;
lastSampleTime.setTime_t(positions[m_samplesInTrace[traceno]-1]);
m_traceControlWidgetList[traceno]->setCurrentSampleTimestamp(lastSampleTime);
}
}
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);
updateTraceControlWidgets();
}
else {
KMessageBox::error(0, i18n("<qt>The selected waveform file version does not match this client</qt>"), i18n("Invalid File"));
}
}
else {
KMessageBox::error(0, i18n("<qt>Invalid waveform file selected</qt>"), i18n("Invalid File"));
}
}
}
TDEAboutData* SensorMonitorPart::createAboutData() {
return new TDEAboutData( APP_NAME, I18N_NOOP( APP_PRETTYNAME ), APP_VERSION );
}
} //namespace RemoteLab
#include "part.moc"