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.
397 lines
12 KiB
397 lines
12 KiB
//Author: Timothy Pearson <kb9vqf@pearsoncomputing.net>, (C) 2012
|
|
//Copyright: See COPYING file that comes with this distribution
|
|
|
|
#include "debug.h"
|
|
#include "define.h"
|
|
#include "part.h"
|
|
|
|
#include <kaboutdata.h> //::createAboutData()
|
|
#include <kaction.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h> //::start()
|
|
#include <kparts/genericfactory.h>
|
|
#include <kstatusbar.h>
|
|
#include <kstdaction.h>
|
|
#include <tqfile.h> //encodeName()
|
|
#include <tqtimer.h> //postInit() hack
|
|
#include <tqvbox.h>
|
|
#include <tqsocket.h>
|
|
#include <tqmutex.h>
|
|
#include <tqeventloop.h>
|
|
#include <tqapplication.h>
|
|
#include <unistd.h> //access()
|
|
#include <stdint.h>
|
|
|
|
#include "tracewidget.h"
|
|
#include "floatspinbox.h"
|
|
#include "layout.h"
|
|
|
|
namespace RemoteLab {
|
|
|
|
typedef KParts::GenericFactory<RemoteLab::CommAnalyzerPart> Factory;
|
|
K_EXPORT_COMPONENT_FACTORY( libremotelab_commanalyzer, RemoteLab::Factory )
|
|
|
|
|
|
CommAnalyzerPart::CommAnalyzerPart( TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name, const TQStringList& )
|
|
: ReadOnlyPart( parent, name ), m_traceWidget(0), m_socket(0), m_base(0), stopTraceUpdate(false)
|
|
{
|
|
// Initialize mutex
|
|
m_instrumentMutex = new TQMutex(false);
|
|
|
|
// Initialize kpart
|
|
setInstance(Factory::instance());
|
|
setWidget(new TQVBox(parentWidget, widgetName));
|
|
|
|
// Create widgets
|
|
m_base = new CommAnalyzerBase(widget());
|
|
m_traceWidget = m_base->traceWidget;
|
|
m_base->saRefLevel->setFloatMin(-128);
|
|
m_base->saRefLevel->setFloatMax(128);
|
|
m_base->saRefLevel->setLineStep(1);
|
|
|
|
connect(m_base->saRefLevel, SIGNAL(floatValueChanged(double)), this, SLOT(saRefLevelChanged(double)));
|
|
|
|
TQTimer::singleShot(0, this, TQT_SLOT(postInit()));
|
|
}
|
|
|
|
CommAnalyzerPart::~CommAnalyzerPart() {
|
|
if (m_traceWidget) {
|
|
delete m_traceWidget;
|
|
}
|
|
if (m_socket) {
|
|
m_socket->close();
|
|
while (m_socket->state() == TQSocket::Closing) {
|
|
tqApp->processEvents();
|
|
}
|
|
delete m_socket;
|
|
}
|
|
|
|
delete m_instrumentMutex;
|
|
}
|
|
|
|
void CommAnalyzerPart::postInit() {
|
|
m_updateTimer = new TQTimer(this);
|
|
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(updateTrace()));
|
|
}
|
|
|
|
bool CommAnalyzerPart::openURL(const KURL &url) {
|
|
connectToServer(url.url());
|
|
}
|
|
|
|
bool CommAnalyzerPart::closeURL() {
|
|
m_socket->close();
|
|
|
|
while (m_socket->state() != TQSocket::Idle) {
|
|
tqApp->processEvents();
|
|
}
|
|
|
|
m_url = KURL();
|
|
|
|
return true;
|
|
}
|
|
|
|
TQString CommAnalyzerPart::callServerMethod(int command) {
|
|
if (m_instrumentMutex->locked() == true) {
|
|
printf("[WARN] An attempt was made to access the instrument asynchronously, and was rejected to prevent a lockup\n\r"); fflush(stdout);
|
|
return TQString::null;
|
|
}
|
|
m_instrumentMutex->lock();
|
|
if (m_socket->state() == TQSocket::Connected) {
|
|
TQString cmd = TQChar(command);
|
|
cmd.append('\r');
|
|
m_socket->writeBlock(cmd.latin1(), cmd.length());
|
|
// Read from the server
|
|
TQString serverRet;
|
|
while ((!serverRet.contains('\r')) && (m_socket->state() == TQSocket::Connected)) {
|
|
char data[1];
|
|
if( m_socket->readBlock(data, 1) > 0) {
|
|
serverRet.append(data[0]);
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
m_instrumentMutex->unlock();
|
|
return serverRet;
|
|
}
|
|
else {
|
|
m_instrumentMutex->unlock();
|
|
return TQString::null;
|
|
}
|
|
}
|
|
|
|
int16_t CommAnalyzerPart::callServerMethodInt16(int command) {
|
|
if (m_instrumentMutex->locked() == true) {
|
|
printf("[WARN] An attempt was made to access the instrument asynchronously, and was rejected to prevent a lockup\n\r"); fflush(stdout);
|
|
return 0;
|
|
}
|
|
m_instrumentMutex->lock();
|
|
if (m_socket->state() == TQSocket::Connected) {
|
|
TQString cmd = TQChar(command);
|
|
cmd.append('\r');
|
|
m_socket->writeBlock(cmd.latin1(), cmd.length());
|
|
// Read from the server
|
|
int bytesread = 0;
|
|
int16_t data[1];
|
|
while ((bytesread < 2) && (m_socket->state() == TQSocket::Connected)) {
|
|
int ret = m_socket->readBlock(((char*)data)+bytesread, 1);
|
|
if (ret > 0) {
|
|
bytesread += ret;
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
TQString serverRet;
|
|
while ((!serverRet.contains('\r')) && (m_socket->state() == TQSocket::Connected)) {
|
|
char data[1];
|
|
if( m_socket->readBlock(data, 1) > 0) {
|
|
serverRet.append(data[0]);
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
m_instrumentMutex->unlock();
|
|
return data[0];
|
|
}
|
|
else {
|
|
m_instrumentMutex->unlock();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
double CommAnalyzerPart::callServerMethodDouble(int command) {
|
|
if (m_instrumentMutex->locked() == true) {
|
|
printf("[WARN] An attempt was made to access the instrument asynchronously, and was rejected to prevent a lockup\n\r"); fflush(stdout);
|
|
return 0;
|
|
}
|
|
m_instrumentMutex->lock();
|
|
if (m_socket->state() == TQSocket::Connected) {
|
|
TQString cmd = TQChar(command);
|
|
cmd.append('\r');
|
|
m_socket->writeBlock(cmd.latin1(), cmd.length());
|
|
// Read from the server
|
|
unsigned int bytesread = 0;
|
|
double data[1];
|
|
while ((bytesread < sizeof(double)) && (m_socket->state() == TQSocket::Connected)) {
|
|
int ret = m_socket->readBlock(((char*)data)+bytesread, 1);
|
|
if (ret > 0) {
|
|
bytesread += ret;
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
TQString serverRet;
|
|
while ((!serverRet.contains('\r')) && (m_socket->state() == TQSocket::Connected)) {
|
|
char data[1];
|
|
if( m_socket->readBlock(data, 1) > 0) {
|
|
serverRet.append(data[0]);
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
m_instrumentMutex->unlock();
|
|
return data[0];
|
|
}
|
|
else {
|
|
m_instrumentMutex->unlock();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void CommAnalyzerPart::sendServerCommandWithParameter(int command, TQString param) {
|
|
if (m_instrumentMutex->locked() == true) {
|
|
printf("[WARN] An attempt was made to access the instrument asynchronously, and was rejected to prevent a lockup\n\r"); fflush(stdout);
|
|
return;
|
|
}
|
|
m_instrumentMutex->lock();
|
|
if (m_socket->state() == TQSocket::Connected) {
|
|
TQString cmd = TQChar(command);
|
|
param = TQString("%1%2%3").arg(param).arg(TQChar('°')).arg(TQChar('\r'));
|
|
cmd += param;
|
|
m_socket->writeBlock(cmd.ascii(), cmd.length());
|
|
// Read from the server
|
|
TQString serverRet;
|
|
while ((!serverRet.contains('\r')) && (m_socket->state() == TQSocket::Connected)) {
|
|
char data[1];
|
|
if( m_socket->readBlock(data, 1) > 0) {
|
|
serverRet.append(data[0]);
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
}
|
|
m_instrumentMutex->unlock();
|
|
}
|
|
|
|
void CommAnalyzerPart::sendServerCommand(int command) {
|
|
if (m_instrumentMutex->locked() == true) {
|
|
printf("[WARN] An attempt was made to access the instrument asynchronously, and was rejected to prevent a lockup\n\r"); fflush(stdout);
|
|
return;
|
|
}
|
|
m_instrumentMutex->lock();
|
|
if (m_socket->state() == TQSocket::Connected) {
|
|
TQString cmd = TQChar(command);
|
|
cmd.append('\r');
|
|
m_socket->writeBlock(cmd.latin1(), cmd.length());
|
|
// Read from the server
|
|
TQString serverRet;
|
|
while ((!serverRet.contains('\r')) && (m_socket->state() == TQSocket::Connected)) {
|
|
char data[1];
|
|
if( m_socket->readBlock(data, 1) > 0) {
|
|
serverRet.append(data[0]);
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
}
|
|
m_instrumentMutex->unlock();
|
|
}
|
|
|
|
void CommAnalyzerPart::callServerMethodDoubleArray(int command, double * array, int arrayLen) {
|
|
if (m_instrumentMutex->locked() == true) {
|
|
printf("[WARN] An attempt was made to access the instrument asynchronously, and was rejected to prevent a lockup\n\r"); fflush(stdout);
|
|
return;
|
|
}
|
|
m_instrumentMutex->lock();
|
|
if (m_socket->state() == TQSocket::Connected) {
|
|
TQString cmd = TQChar(command);
|
|
cmd.append('\r');
|
|
m_socket->writeBlock(cmd.latin1(), cmd.length());
|
|
// Read from the server
|
|
TQString serverRet;
|
|
while ((!serverRet.contains('\r')) && (m_socket->state() == TQSocket::Connected)) {
|
|
char data[1];
|
|
if( m_socket->readBlock(data, 1) > 0) {
|
|
serverRet.append(data[0]);
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
unsigned int bytesread = 0;
|
|
int16_t data[1];
|
|
while ((bytesread < 2) && (m_socket->state() == TQSocket::Connected)) {
|
|
int ret = m_socket->readBlock(((char*)data)+bytesread, 1);
|
|
if (ret > 0) {
|
|
bytesread += ret;
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
serverRet = "";
|
|
while ((!serverRet.contains('\r')) && (m_socket->state() == TQSocket::Connected)) {
|
|
char data[1];
|
|
if( m_socket->readBlock(data, 1) > 0) {
|
|
serverRet.append(data[0]);
|
|
}
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
bytesread = 0;
|
|
int elementsread = 0;
|
|
for (elementsread=0;elementsread<arrayLen;elementsread++) {
|
|
bytesread = 0;
|
|
while ((bytesread < sizeof(double)) && (m_socket->state() == TQSocket::Connected)) {
|
|
if (m_socket->size() < 1) {
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
int ret = m_socket->readBlock(((char*)array)+bytesread+(elementsread*sizeof(double)), 1);
|
|
if (ret > 0) {
|
|
bytesread += ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_instrumentMutex->unlock();
|
|
}
|
|
|
|
int CommAnalyzerPart::connectToServer(TQString server) {
|
|
if (!m_socket) {
|
|
m_socket = new TQSocket(this);
|
|
// connect(m_socket, SIGNAL(connected()), SLOT(socketConnected()));
|
|
// connect(m_socket, SIGNAL(connectionClosed()), SLOT(socketConnectionClosed()));
|
|
// connect(m_socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
|
|
// connect(m_socket, SIGNAL(error(int)), SLOT(socketError(int)));
|
|
}
|
|
m_socket->connectToHost(server, 4002);
|
|
while ((m_socket->state() != TQSocket::Connected) && (m_socket->state() != TQSocket::Idle)) {
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::AllEvents);
|
|
}
|
|
if (m_socket->state() != TQSocket::Connected) {
|
|
return -1;
|
|
}
|
|
|
|
// Gather information from the server
|
|
if (callServerMethod(41) == "NCK") {
|
|
// FIXME
|
|
// Display message and exit
|
|
return -1;
|
|
}
|
|
sendServerCommand(40); // Set spectrum analyzer mode
|
|
m_samplesInTrace = callServerMethodInt16(63); // Get number of samples in trace
|
|
m_traceWidget->setNumberOfSamples(m_samplesInTrace);
|
|
m_hdivs = callServerMethodInt16(62); // Get number of horizontal divisions
|
|
m_traceWidget->setNumberOfHorizontalDivisions(m_hdivs);
|
|
m_vdivs = callServerMethodInt16(64); // Get number of vertical divisions
|
|
m_traceWidget->setNumberOfVerticalDivisions(m_vdivs);
|
|
|
|
m_rpower = callServerMethodDouble(65); // Get reference power level
|
|
m_vscale = callServerMethodDouble(66); // Get vertical division scale
|
|
|
|
m_centerfreq = callServerMethodDouble(67); // Get center frequency
|
|
m_spanfreq = callServerMethodDouble(68); // Get frequency span
|
|
|
|
updateGraticule();
|
|
|
|
// Start trace update timer
|
|
m_updateTimer->start(10, FALSE);
|
|
}
|
|
|
|
void CommAnalyzerPart::postProcessTrace() {
|
|
return;
|
|
}
|
|
|
|
void CommAnalyzerPart::updateTrace() {
|
|
m_updateTimer->stop();
|
|
callServerMethodDoubleArray(42, m_traceWidget->samples(), m_samplesInTrace);
|
|
postProcessTrace();
|
|
m_traceWidget->repaint();
|
|
if (m_socket->state() == TQSocket::Connected) {
|
|
if (stopTraceUpdate == true) {
|
|
stopTraceUpdate = false;
|
|
}
|
|
else {
|
|
m_updateTimer->start(10, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CommAnalyzerPart::updateGraticule() {
|
|
m_leftFrequency = m_centerfreq - (m_spanfreq/2.0);
|
|
m_rightFrequency = m_centerfreq + (m_spanfreq/2.0);
|
|
m_traceWidget->setDisplayLimits(m_leftFrequency, m_rpower, m_rightFrequency, m_rpower-(m_vscale*m_hdivs));
|
|
|
|
// Also update controls
|
|
m_base->saRefLevel->blockSignals(true);
|
|
m_base->saRefLevel->setFloatValue(m_rpower);
|
|
m_base->saRefLevel->blockSignals(false);
|
|
}
|
|
|
|
void CommAnalyzerPart::saRefLevelChanged(double newval) {
|
|
// We cannot directly send data to the remote instrument because the GUI event may have ocurred during a remote instrument transaction
|
|
// This "flaw" is a direct result of maximizing performance by processing GUI events during network transfers, as well as the fact that this client is a multithreaded application
|
|
m_rpower = newval;
|
|
stopTraceUpdate = true;
|
|
TQTimer::singleShot(0, this, SLOT(changeSaRefLevel()));
|
|
}
|
|
|
|
void CommAnalyzerPart::changeSaRefLevel() {
|
|
// Keep trying to set the new power level
|
|
if (m_instrumentMutex->locked() == false) {
|
|
sendServerCommandWithParameter(61, TQString("%1").arg(m_rpower, 0, 'E')); // Set reference power level
|
|
m_rpower = callServerMethodDouble(65); // Get reference power level
|
|
updateGraticule(); // Update the display grid
|
|
m_updateTimer->start(10, FALSE); // Restart trace update timer
|
|
}
|
|
else {
|
|
tqApp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput);
|
|
TQTimer::singleShot(0, this, SLOT(changeSaRefLevel()));
|
|
}
|
|
}
|
|
|
|
KAboutData* CommAnalyzerPart::createAboutData() {
|
|
return new KAboutData( APP_NAME, I18N_NOOP( APP_PRETTYNAME ), APP_VERSION );
|
|
}
|
|
|
|
} //namespace RemoteLab
|
|
|
|
#include "part.moc"
|