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.

444 lines
13 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"
/* exception handling */
struct exit_exception {
int c;
exit_exception(int c):c(c) { }
};
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() {
if (m_socket) {
m_socket->close();
while (m_socket->state() != TQSocket::Idle) {
tqApp->processEvents();
}
}
m_url = KURL();
if (m_instrumentMutex->locked()) {
throw exit_exception(-1);
}
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;
}
try {
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;
}
}
catch (exit_exception& e) {
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;
}
try {
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;
}
}
catch (exit_exception& e) {
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;
}
try {
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;
}
}
catch (exit_exception& e) {
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;
}
try {
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();
return;
}
catch (exit_exception& e) {
m_instrumentMutex->unlock();
return;
}
}
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;
}
try {
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;
}
catch (exit_exception& e) {
m_instrumentMutex->unlock();
return;
}
}
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;
}
try {
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();
return;
}
catch (exit_exception& e) {
m_instrumentMutex->unlock();
return;
}
}
int CommAnalyzerPart::connectToServer(TQString server) {
if (!m_socket) {
m_socket = new TQSocket(this);
}
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"