From ae2bec04efa3205c74c39ddc245d5310dbd9b9f9 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Thu, 5 Jul 2012 21:11:54 -0500 Subject: [PATCH] Add preliminary FPGA programming client --- clients/tde/configure.in | 1 + clients/tde/src/part/Makefile.am | 2 +- clients/tde/src/part/fpgaprogram/Makefile.am | 9 + clients/tde/src/part/fpgaprogram/layout.ui | 84 ++++ clients/tde/src/part/fpgaprogram/part.cpp | 471 +++++++++++++++++++ clients/tde/src/part/fpgaprogram/part.h | 96 ++++ 6 files changed, 662 insertions(+), 1 deletion(-) create mode 100644 clients/tde/src/part/fpgaprogram/Makefile.am create mode 100644 clients/tde/src/part/fpgaprogram/layout.ui create mode 100644 clients/tde/src/part/fpgaprogram/part.cpp create mode 100644 clients/tde/src/part/fpgaprogram/part.h diff --git a/clients/tde/configure.in b/clients/tde/configure.in index 59b7401..3753714 100644 --- a/clients/tde/configure.in +++ b/clients/tde/configure.in @@ -87,6 +87,7 @@ AC_CONFIG_FILES([ src/app/views/Makefile ]) AC_CONFIG_FILES([ src/dialogs/Makefile ]) AC_CONFIG_FILES([ src/part/Makefile ]) 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/widgets/Makefile ]) AC_OUTPUT diff --git a/clients/tde/src/part/Makefile.am b/clients/tde/src/part/Makefile.am index 900ddb1..b2962ff 100644 --- a/clients/tde/src/part/Makefile.am +++ b/clients/tde/src/part/Makefile.am @@ -1 +1 @@ -SUBDIRS = commanalyzer fpgaview +SUBDIRS = commanalyzer fpgaview fpgaprogram diff --git a/clients/tde/src/part/fpgaprogram/Makefile.am b/clients/tde/src/part/fpgaprogram/Makefile.am new file mode 100644 index 0000000..63fdf0f --- /dev/null +++ b/clients/tde/src/part/fpgaprogram/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/src -I$(top_srcdir)/src/widgets $(KDE_INCLUDES)/tde +KDE_CXXFLAGS = $(USE_EXCEPTIONS) +METASOURCES = AUTO + +# Part +kde_module_LTLIBRARIES = libremotelab_fpgaviewer.la +libremotelab_fpgaviewer_la_LIBADD = ../../widgets/libtracewidget.la ../../widgets/libfloatspinbox.la $(LIB_KFILE) $(LIB_KPARTS) $(LIB_TDEUI) $(LIB_QT) -ltdekrbsocket -ltqtrla +libremotelab_fpgaviewer_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(LIB_TDECORE) $(LIB_TDEUI) -lkio -ltdefx +libremotelab_fpgaviewer_la_SOURCES = part.cpp layout.ui diff --git a/clients/tde/src/part/fpgaprogram/layout.ui b/clients/tde/src/part/fpgaprogram/layout.ui new file mode 100644 index 0000000..985acfe --- /dev/null +++ b/clients/tde/src/part/fpgaprogram/layout.ui @@ -0,0 +1,84 @@ + + FPGAProgramBase + + + FPGAProgramBase + + + + 0 + 0 + 519 + 356 + + + + + unnamed_grid + + + + groupFPGAProgram + + + FPGA Viewer + + + + unnamed_grid + + + + unnamed_label + + + Programming File: + + + + + programmingInputFile + + + 25 + + + *.bit|Bitstream Files (*.bit) + + + + + programRunButton + + + Download Program + + + + + programmingProgressBar + + + + + programmingStatusLabel + + + AlignVCenter|AlignRight + + + + + + + + FPGAProgramBase.ui.h + + + tracewidget.h + floatspinbox.h + part.h + + + + diff --git a/clients/tde/src/part/fpgaprogram/part.cpp b/clients/tde/src/part/fpgaprogram/part.cpp new file mode 100644 index 0000000..bf7e46a --- /dev/null +++ b/clients/tde/src/part/fpgaprogram/part.cpp @@ -0,0 +1,471 @@ +/* + * 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 //::start() +#include +#include +#include +#include +#include +#include +#include //encodeName() +#include //postInit() hack +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //access() +#include + +#include + +#include "tracewidget.h" +#include "floatspinbox.h" +#include "layout.h" + +// RAJA UNCOMMENT ME +//#define SERVER_TIMEOUT_MS 10000 +// RAJA DEBUG ONLY +#define SERVER_TIMEOUT_MS 100000 +#define FPGA_COMM_TIMEOUT_MS 500 +#define FPGA_DATA_PROCESSING_TIMEOUT_MS 2500 + +namespace RemoteLab { + +typedef KParts::GenericFactory Factory; +#define CLIENT_LIBRARY "libremotelab_fpgaprogrammer" +K_EXPORT_COMPONENT_FACTORY(libremotelab_fpgaprogrammer, RemoteLab::Factory) + +FPGAProgramPart::FPGAProgramPart(TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name, const TQStringList&) + : RemoteInstrumentPart( parent, name ), m_socket(0), m_base(0), connToServerConnecting(false), connToServerState(-1), connToServerTimeoutTimer(NULL), + m_commHandlerState(0), m_connectionActiveAndValid(false), m_tickerState(0) +{ + // Initialize mutex + m_connectionMutex = new TQMutex(false); + + // Initialize kpart + setInstance(Factory::instance()); + setWidget(new TQVBox(parentWidget, widgetName)); + + // Create timers + m_updateTimer = new TQTimer(this); + + // Create widgets + m_base = new FPGAProgramBase(widget()); + + // Initialize widgets + connect(m_base->programRunButton, SIGNAL(clicked()), this, SLOT(programRunButtonClicked())); + + TQTimer::singleShot(0, this, TQT_SLOT(postInit())); +} + +FPGAProgramPart::~FPGAProgramPart() { + if (m_connectionMutex->locked()) { + printf("[WARNING] Exiting when data transfer still in progress!\n\r"); fflush(stdout); + } + + disconnectFromServer(); + delete m_connectionMutex; +} + +void FPGAProgramPart::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 ((m_base->programmingInputFile->url() != "") && (m_commHandlerMode == 0) && (m_connectionActiveAndValid == true)) { + m_base->programRunButton->setEnabled(true); + } + else { + m_base->programRunButton->setEnabled(false); + } + + if (m_commHandlerMode == 1) { + m_base->programmingInputFile->setEnabled(false); + } + else { + m_base->programmingInputFile->setEnabled(true); + } + + if ((m_connectionActiveAndValid == true) && (m_commHandlerMode == 0)) { + m_base->programmingStatusLabel->setText(i18n("Ready")); + } +} + +void FPGAProgramPart::resizeToHint() { + resize(widget()->sizeHint()); +} + +void FPGAProgramPart::connectionClosed() { + closeURL(); +} + +void FPGAProgramPart::postInit() { + connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(updateDisplay())); +} + +bool FPGAProgramPart::openURL(const KURL &url) { + int ret; + ret = connectToServer(url.url()); + processLockouts(); + return (ret != 0); +} + +bool FPGAProgramPart::closeURL() { + disconnectFromServer(); + m_url = KURL(); + return true; +} + +void FPGAProgramPart::disconnectFromServer() { + if (m_socket) { + m_socket->clearPendingData(); + m_socket->close(); + delete m_socket; + m_socket = NULL; + } + processLockouts(); +} + +void FPGAProgramPart::finishConnectingToServer() { + if (!m_socket) { + connToServerState = -1; + connToServerConnecting = false; + processLockouts(); + return; + } + + if (connToServerConnecting) { + switch(connToServerState) { + case 0: + if (!connToServerTimeoutTimer) { + connToServerTimeoutTimer = new TQTimer; + connToServerTimeoutTimer->start(SERVER_TIMEOUT_MS, TRUE); + } + if ((m_socket->state() == TQSocket::Connecting) || (m_socket->state() == TQSocket::HostLookup)) { + if (!connToServerTimeoutTimer->isActive()) { + connToServerState = -3; + connToServerConnecting = false; + disconnectFromServer(); + KMessageBox::error(0, i18n("Unable to establish connection to remote server"), i18n("Connection Failed")); + } + } + else { + if (m_socket->state() == TQSocket::Connected) { + printf("[DEBUG] Initial connection established...\n\r"); fflush(stdout); + m_socket->setDataTimeout(SERVER_TIMEOUT_MS); + m_socket->setUsingKerberos(true); + connToServerState = 1; + } + else { + connToServerState = -1; + connToServerConnecting = false; + disconnectFromServer(); + KMessageBox::error(0, i18n("Unable to establish connection to remote server"), i18n("Connection Failed")); + } + } + break; + case 1: + if (m_socket->kerberosStatus() == TDEKerberosClientSocket::KerberosInitializing) { + // Do nothing + } + else { + if (m_socket->kerberosStatus() != TDEKerberosClientSocket::KerberosInUse) { + connToServerState = -1; + connToServerConnecting = false; + disconnectFromServer(); + KMessageBox::error(0, i18n("Unable to establish Kerberos protocol with remote server

Please verify that you currently hold a valid Kerberos ticket"), i18n("Connection Failed")); + } + else { + connToServerState = 2; + } + } + break; + case 2: + // Connection established! + // Read magic number and proto version from server + TQDataStream ds(m_socket); + TQ_UINT32 magicnum; + TQ_UINT32 protover; + ds >> magicnum; + ds >> protover; + printf("[DEBUG] Got magic number %d and protocol version %d\n\r", magicnum, protover); fflush(stdout); + + // Request connection to backend server + TQString response; + ds << TQString("SERV"); + ds << TQString(CLIENT_LIBRARY); + ds >> response; +printf("[RAJA DEBUG 400.0] Got '%s' from the server\n\r", response.ascii()); fflush(stdout); + if (response == "OK") { + connToServerState = 3; + connToServerConnecting = false; + connect(m_socket, SIGNAL(readyRead()), m_socket, SLOT(processPendingData())); + connect(m_socket, SIGNAL(newDataReceived()), this, SLOT(updateDisplay())); + m_tickerState = 0; + m_commHandlerState = 0; + m_commHandlerMode = 0; + m_updateTimer->start(FPGA_COMM_TIMEOUT_MS, FALSE); + processLockouts(); + updateDisplay(); + return; + } + else if (response == "ERRNOCONN") { + connToServerState = -1; + connToServerConnecting = false; + disconnectFromServer(); + KMessageBox::error(0, i18n("Unable to establish connection with backend server

Please verify that you are currently connected to a workspace"), i18n("Connection Failed")); + close(); + return; + } + else if (response == "ERRNOTAVL") { + connToServerState = -1; + connToServerConnecting = false; + disconnectFromServer(); + KMessageBox::error(0, i18n("The backend server is not available at this time

Please try a different workspace, or try again later"), i18n("Connection Failed")); + close(); + return; + } + else if (response == "ERRNOSERV") { + connToServerState = -1; + connToServerConnecting = false; + disconnectFromServer(); + KMessageBox::error(0, i18n("The active laboratory workspace does not support the requested service"), i18n("Service Unavailable")); + close(); + return; + } + else { + connToServerState = -1; + connToServerConnecting = false; + disconnectFromServer(); + KMessageBox::error(0, i18n("Unable to establish connection with remote server"), i18n("Connection Failed")); + close(); + return; + } + break; + } + + TQTimer::singleShot(0, this, SLOT(finishConnectingToServer())); + } +} + +int FPGAProgramPart::connectToServer(TQString server) { + if (m_socket) { + return -1; + } + if (!m_socket) { + m_socket = new TDEKerberosClientSocket(this); + connect(m_socket, TQT_SIGNAL(statusMessageUpdated(const TQString&)), this, TQT_SLOT(setStatusMessage(const TQString&) )); + } + m_socket->setServiceName("remotefpga"); + m_socket->setServerFQDN(server); + m_socket->connectToHost(server, 4004); + + // Finish connecting when appropriate + connToServerState = 0; + connToServerConnecting = true; + TQTimer::singleShot(0, this, SLOT(finishConnectingToServer())); + + return 0; +} + +void FPGAProgramPart::programRunButtonClicked() { + m_commHandlerState = 0; + m_commHandlerMode = 1; + processLockouts(); +} + +#define UPDATEDISPLAY_TIMEOUT m_connectionActiveAndValid = false; \ + m_tickerState = 0; \ + m_commHandlerState = 0; \ + m_commHandlerMode = 0; \ + while (m_socket->bytesAvailable() > 0) { \ + m_socket->readBlock(data, 64); \ + } \ + setStatusMessage(i18n("Debug interface timeout, still waiting for data. Please verify that the FPGA is properly configured.")); \ + m_updateTimer->start(FPGA_COMM_TIMEOUT_MS, FALSE); \ + return; + +void FPGAProgramPart::updateDisplay() { + m_updateTimer->stop(); + TQDataStream ds(m_socket); + + if (m_socket) { + char data[64]; + + if (m_commHandlerMode == 0) { + // Normal operation + switch (m_commHandlerState) { + case 0: + // Get status of remote system + // RAJA FIXME + + ds << TQString("STATUS"); + m_updateTimer->start(FPGA_COMM_TIMEOUT_MS, FALSE); + + m_commHandlerState = 1; + break; + case 1: + // Get all data + if (m_socket->bytesAvailable() > 0) { + // RAJA FIXME + TQStringList statusDetails; + ds >> statusDetails; + + 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(i18n("Running") + TQString("... %1").arg(tickerChar)); + m_tickerState++; + if (m_tickerState > 3) { + m_tickerState = 0; + } + m_updateTimer->start(FPGA_COMM_TIMEOUT_MS, FALSE); + + m_commHandlerState = 0; + } + else { + if (!m_updateTimer->isActive()) { + UPDATEDISPLAY_TIMEOUT + } + } + + break; + } + } + else if (m_commHandlerMode == 1) { + // Program mode! + // RAJA FIXME + if (m_commHandlerState == 0) { + m_base->programmingStatusLabel->setText(i18n("Reading input file") + "..."); + TQFile file(m_base->programmingInputFile->url()); + if (file.open(IO_ReadOnly)) { + m_programmingFileData = file.readAll(); + file.close(); + + m_base->programmingProgressBar->setTotalSteps(0); + m_base->programmingProgressBar->setProgress(0); + m_base->programmingStatusLabel->setText(i18n("Sending data to server") + "..."); + + ds << TQString("FILE"); + // RAJA FIXME + // Transmit file to remote server... + ds << m_programmingFileData; + + m_base->programmingStatusLabel->setText(i18n("Programming device") + "..."); + ds << TQString("PROGRAM"); + + m_commHandlerState = 1; + } + else { + KMessageBox::error(0, i18n("Unable to open selected programming file"), i18n("Program Failed")); + m_commHandlerMode = 0; + m_commHandlerState = 0; + m_base->programmingProgressBar->setTotalSteps(1); + m_base->programmingProgressBar->setProgress(0); + processLockouts(); + } + } + else if (m_commHandlerState == 1) { + // Get response + if (m_socket->bytesAvailable() > 0) { + // RAJA FIXME + TQString result; + ds >> result; + + if (result == "PROGRAMMING") { + // RAJA FIXME + } + else if (result == "DONE") { + int retCode; + TQString log; + + ds >> retCode; + ds >> log; + + // RAJA FIXME + // Handle errors + + // Done! + m_commHandlerMode = 0; + m_commHandlerState = 0; + m_base->programmingProgressBar->setTotalSteps(1); + m_base->programmingProgressBar->setProgress(0); + processLockouts(); + } + } + else { + if (!m_updateTimer->isActive()) { + m_base->programmingProgressBar->setTotalSteps(1); + m_base->programmingProgressBar->setProgress(0); + m_commHandlerMode = 0; + m_commHandlerState = 0; + processLockouts(); + UPDATEDISPLAY_TIMEOUT + } + } + } + } + } + else { + m_commHandlerState = 0; + m_commHandlerMode = 0; + } +} + +KAboutData* FPGAProgramPart::createAboutData() { + return new KAboutData( APP_NAME, I18N_NOOP( APP_PRETTYNAME ), APP_VERSION ); +} + +} //namespace RemoteLab + +#include "part.moc" diff --git a/clients/tde/src/part/fpgaprogram/part.h b/clients/tde/src/part/fpgaprogram/part.h new file mode 100644 index 0000000..a663b11 --- /dev/null +++ b/clients/tde/src/part/fpgaprogram/part.h @@ -0,0 +1,96 @@ +/* + * 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 + */ + +#ifndef REMOTELAB_FPGAPROGRAMPART_H +#define REMOTELAB_FPGAPROGRAMPART_H + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +class KAboutData; +using KParts::StatusBarExtension; +class TraceWidget; +class TQSocket; +class TQTimer; +class TQMutex; +class TQFile; +class FPGAProgramBase; + +namespace RemoteLab +{ + class FPGAProgramPart : public KParts::RemoteInstrumentPart + { + Q_OBJECT + + public: + FPGAProgramPart(TQWidget *, const char *, TQObject *, const char *, const TQStringList&); + ~FPGAProgramPart(); + + virtual bool openFile() { return false; } // pure virtual in the base class + virtual bool closeURL(); + static KAboutData *createAboutData(); + int connectToServer(TQString server); + + public slots: + virtual bool openURL(const KURL &url); + + private slots: + void postInit(); + void resizeToHint(); + void updateDisplay(); + void processLockouts(); + void connectionClosed(); + void disconnectFromServer(); + void finishConnectingToServer(); + + void programRunButtonClicked(); + + private: + TDEKerberosClientSocket* m_socket; + FPGAProgramBase* m_base; + TQMutex* m_connectionMutex; + TQTimer* m_updateTimer; + + bool connToServerConnecting; + int connToServerState; + TQTimer *connToServerTimeoutTimer; + + int m_commHandlerState; + int m_commHandlerMode; + bool m_connectionActiveAndValid; + unsigned char m_tickerState; + TQByteArray m_programmingFileData; + }; +} + +#endif