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.

425 lines
13 KiB

//Author: Timothy Pearson <kb9vqf@pearsoncomputing.net>, (C) 2012
//Copyright: See COPYING file that comes with this distribution
// TDE MDI interface based on a (passable) tutorial by Andrea Bergia et al.
#include "remotemdi.h"
#include <cassert>
using namespace std;
#include <kapplication.h>
#include <klocale.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kmessagebox.h>
#include <tqlabel.h>
#include <tqtimer.h>
#include <tqlayout.h>
#include <kiconloader.h>
#include <kstdaction.h>
#include <kstatusbar.h>
#include <kmdichildview.h>
#include <klistbox.h>
#include <kactionclasses.h>
#include <kedittoolbar.h>
#include <kkeydialog.h>
#include "views/instrumentview.h"
#include "dialogs/selectserverdlg.h"
RemoteMDI::RemoteMDI()
: KMdiMainFrm(0, "RemoteMDI", KMdi::ChildframeMode), m_children(0), m_rsvSvrSocket(NULL)
{
setXMLFile("remotelabui.rc");
// Create some actions
KStdAction::close(this, SLOT(closeCurrent()), actionCollection());
KStdAction::quit(this, SLOT(close()), actionCollection());
KActionCollection *const ac = actionCollection();
setStandardToolBarMenuEnabled(true);
KStdAction::quit(TQT_TQOBJECT(this), TQT_SLOT(close()), ac);
KStdAction::configureToolbars(TQT_TQOBJECT(this), TQT_SLOT(configToolbars()), ac);
KStdAction::keyBindings(TQT_TQOBJECT(this), TQT_SLOT(configKeys()), ac);
connect_action = new KAction(i18n("Connect to Server"), "connect_creating", KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(connectToServer()), ac, "connect_server");
disconnect_action = new KAction(i18n("Disconnect from Server"), "connect_no", KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(disconnectFromServer()), ac, "disconnect_server");
// Add Window menu
if ( !isFakingSDIApplication() ) {
menuBar()->insertItem(i18n("&Window"), windowMenu());
}
createGUI( 0 );
// When we change view, change the status bar text
connect(this, SIGNAL(viewActivated(KMdiChildView*)), this, SLOT(currentChanged(KMdiChildView*)));
ac->setHighlightingEnabled(true);
connect(ac, TQT_SIGNAL(actionStatusText(const TQString&)), statusBar(), TQT_SLOT(message(const TQString&) ));
connect(ac, TQT_SIGNAL(clearStatusText()), statusBar(), TQT_SLOT(clear()));
// Create the status bar
statusBar()->message(i18n("No view!"));
processActions();
processLockouts();
}
RemoteMDI::~RemoteMDI()
{
while (m_pCurrentWindow) {
closeCurrent();
}
if (m_rsvSvrSocket) {
m_rsvSvrSocket->close();
while (m_rsvSvrSocket->state() == TQSocket::Closing) {
tqApp->processEvents();
}
}
}
void RemoteMDI::processActions() {
// Add dynamic actions
// RAJA FIXME
KActionCollection *const ac = actionCollection();
TQPtrList<KAction> dut_actions;
inst_fpgaviewer_menu = new KAction(i18n("Launch FPGA Viewer"), "remote", KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(startModule()), ac, "libremotelab_fpgaviewer");
dut_actions.append(inst_fpgaviewer_menu);
unplugActionList("dutMenu_actionlist");
unplugActionList("dutToolBar_actionlist");
plugActionList("dutMenu_actionlist", dut_actions);
plugActionList("dutToolBar_actionlist", dut_actions);
TQPtrList<KAction> instrument_actions;
inst_sa_menu = new KAction(i18n("Launch Spectrum Analyzer"), "remote", KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(startModule()), ac, "libremotelab_commanalyzer");
instrument_actions.append(inst_sa_menu);
unplugActionList("instrumentMenu_actionlist");
unplugActionList("instrumentToolBar_actionlist");
plugActionList("instrumentMenu_actionlist", instrument_actions);
plugActionList("instrumentToolBar_actionlist", instrument_actions);
TQPtrList<KAction> service_actions;
// Nothing here yet!
unplugActionList("serviceMenu_actionlist");
unplugActionList("serviceToolBar_actionlist");
plugActionList("serviceMenu_actionlist", service_actions);
plugActionList("serviceToolBar_actionlist", service_actions);
}
void RemoteMDI::startModule() {
const KAction* sendingAction = dynamic_cast<const KAction*>(sender());
if (sendingAction) {
// RAJA FIXME
bool serviceFound = false;
ServiceType st;
for (ServiceList::Iterator it(m_activeStation.services.begin()); it != m_activeStation.services.end(); ++it) {
st = *it;
if (st.clientLibrary == sendingAction->name()) {
serviceFound = true;
}
}
if (!serviceFound) {
KMessageBox::error(this, i18n("<qt>The active laboratory workspace does not support the requested service</qt>"), i18n("Service Unavailable"));
return;
}
RemoteLab::InstrumentView* view = new RemoteLab::InstrumentView(sendingAction->name(), st.name, (mdiMode() == KMdi::ToplevelMode) ? 0 : this);
openNewWindow(view);
if (m_serverHost != "") {
view->connectServer(m_serverHost);
}
}
}
void RemoteMDI::connectToServer() {
if (m_rsvSvrSocket) {
if (m_rsvSvrSocket->state() != TQSocket::Idle) {
printf("[DEBUG] Not connecting because the socket is still in state %d\n\r", m_rsvSvrSocket->state()); fflush(stdout);
return;
}
}
connect_action->setEnabled(false);
disconnect_action->setEnabled(true);
// Connect to the central reservation/control server
if (!m_rsvSvrSocket) {
m_rsvSvrSocket = new TDEKerberosClientSocket(this);
connect(m_rsvSvrSocket, SIGNAL(connectionClosed()), this, SLOT(connectionClosedHandler()));
}
m_rsvSvrSocket->setServiceName("remotefpga");
if (m_serverHost != "") {
m_rsvSvrSocket->setServerFQDN(m_serverHost);
m_rsvSvrSocket->connectToHost(m_serverHost, 4004);
TQTimer connectionTimeout;
connectionTimeout.start(5000, TRUE);
while ((m_rsvSvrSocket->state() == TQSocket::Connecting) || (m_rsvSvrSocket->state() == TQSocket::HostLookup)) {
tqApp->processEvents();
if (!connectionTimeout.isActive()) {
break;
}
}
connectionTimeout.stop();
if (m_rsvSvrSocket->state() == TQSocket::Connected) {
printf("[DEBUG] Initial connection established...\n\r"); fflush(stdout);
if (m_rsvSvrSocket->setUsingKerberos(true) != 0) {
disconnectFromServer();
KMessageBox::error(this, i18n("<qt>Unable to establish Kerberos protocol with remote server<p>Please verify that you currently hold a valid Kerberos ticket</qt>"), i18n("Connection Failed"));
}
else {
// Connection established!
// Read magic number and proto version from server
TQDataStream* ds = new TQDataStream(m_rsvSvrSocket);
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);
delete ds;
if ((magicnum == MAGIC_NUMBER) && (protover == PROTOCOL_VERSION)) {
disconnect_action->setEnabled(true);
promptForStationType();
}
else {
disconnectFromServer();
KMessageBox::error(this, i18n("<qt>The remote server is not compatible with this client</qt>"), i18n("Connection Failed"));
}
}
}
else {
disconnectFromServer();
KMessageBox::error(this, i18n("<qt>Unable to establish connection to remote server</qt>"), i18n("Connection Failed"));
}
}
else {
disconnectFromServer();
KMessageBox::error(this, i18n("<qt>The address of the remote server has not been specified</qt>"), i18n("Connection Failed"));
}
processLockouts();
}
void RemoteMDI::promptForStationType() {
if (!m_rsvSvrSocket) {
return;
}
if (m_rsvSvrSocket->state() != TQSocket::Connected) {
return;
}
TQDataStream ds(m_rsvSvrSocket);
// Request list of laboratory stations
StationList slist;
ds << TQString("LIST");
ds >> slist;
printf("[RAJA DEBUG 200.2] Got list of stations, count is %d\n\r", slist.count()); fflush(stdout);
SelectServerDialog select(this, 0, slist);
const int ret = select.exec();
if (ret == KDialog::Accepted) {
TQString result;
ds << TQString("BIND");
ds << select.m_selectedStation;
ds >> result;
printf("[RAJA DEBUG 100.0] '%s'\n\r", result.ascii()); fflush(stdout);
if (result == "OK") {
// Success!
m_activeStation = select.m_selectedStation;
}
else if (result == "ERRUNAVAL") {
KMessageBox::error(this, i18n("<qt>No stations of the specified type are currently available<p>Please try again later</qt>"), i18n("Insufficient Laboratory Resources"));
disconnectFromServer();
}
else if (result == "ERRPREVCN") {
KMessageBox::error(this, i18n("<qt>You are already connected to a laboratory station<p>Please disconnect and try again</qt>"), i18n("Multiple Connections Detected"));
disconnectFromServer();
}
else {
KMessageBox::error(this, i18n("<qt>Unknown server error<p>Please reconnect and try again</qt>"), i18n("Internal Error"));
disconnectFromServer();
}
}
else {
disconnectFromServer();
}
}
void RemoteMDI::disconnectFromServer() {
connect_action->setEnabled(false);
disconnect_action->setEnabled(false);
12 years ago
// Close all windows
KMdiIterator<KMdiChildView*> *it = createIterator();
while (it->currentItem()) {
KMdiChildView *c = dynamic_cast<KMdiChildView*>(it->currentItem());
if (c) {
closeSpecifiedWindow(c);
}
it->next();
}
12 years ago
deleteIterator(it);
if (m_rsvSvrSocket) {
m_rsvSvrSocket->clearPendingData();
m_rsvSvrSocket->close();
delete m_rsvSvrSocket;
m_rsvSvrSocket = NULL;
}
connect_action->setEnabled(true);
processLockouts();
}
void RemoteMDI::connectionClosedHandler() {
disconnectFromServer();
KMessageBox::error(this, i18n("<qt>The remote server has closed the connection</qt>"), i18n("Connection Terminated"));
}
void RemoteMDI::processLockouts() {
bool connected = false;
if (m_rsvSvrSocket) {
connected = (m_rsvSvrSocket->state() == TQSocket::Connected);
}
printf("[RAJA DEBUG 600.0] connected: %d\n\r", connected); fflush(stdout);
connect_action->setEnabled(!connected);
disconnect_action->setEnabled(connected);
inst_fpgaviewer_menu->setEnabled(connected);
inst_sa_menu->setEnabled(connected);
}
void RemoteMDI::configToolbars() {
KEditToolbar dialog(factory(), this);
dialog.showButtonApply(false);
if (dialog.exec()) {
applyMainWindowSettings(kapp->config(), "window");
}
}
void RemoteMDI::configKeys() {
KKeyDialog::configure(actionCollection(), this);
}
void RemoteMDI::setServerHost(TQString server) {
m_serverHost = server;
}
void RemoteMDI::openNewWindow(KMdiChildView *view) {
// Add a child view
m_children++;
// The child view will be our child only if we aren't in Toplevel mode
if (!view) {
view = new KMdiChildView(i18n("View %1").arg(m_children), (mdiMode() == KMdi::ToplevelMode) ? 0 : this);
}
(new TQHBoxLayout(view))->setAutoAdd( true );
// Add to the MDI and set as current
if (mdiMode() == KMdi::ToplevelMode) {
addWindow(view, KMdi::Detach);
}
else {
addWindow(view);
}
currentChanged(view);
// Handle termination
connect(view, SIGNAL(childWindowCloseRequest(KMdiChildView*)), this, SLOT(childClosed(KMdiChildView*)));
}
void RemoteMDI::childWindowCloseRequest(KMdiChildView *pWnd) {
RemoteLab::InstrumentView* iview = dynamic_cast<RemoteLab::InstrumentView*>(pWnd);
if (iview) {
// Give the child a chance to finish what it was doing and exit cleanly (i.e. without crashing!)
iview->closeConnections();
iview->hide();
// RAJA FIXME
// Executing KMdiMainFrm::childWindowCloseRequest(pWnd) here will probably cause a crash
// We need to call this AFTER control has been returned to the main event loop at least once
// This is related to my lack of proper returning to the event loop, which MUST BE FIXED
// KMdiMainFrm::childWindowCloseRequest(pWnd); // RAJA UNCOMMENT ME
}
}
void RemoteMDI::currentChanged(KMdiChildView *current) {
// Update status bar and list box
statusBar()->message(i18n( "%1 activated").arg(current->tabCaption()));
}
void RemoteMDI::closeCurrent() {
// If there's a current view, close it
if (m_pCurrentWindow) {
closeSpecifiedWindow(m_pCurrentWindow);
}
}
void RemoteMDI::closeSpecifiedWindow(KMdiChildView *window) {
if (window) {
// Notify the status bar of the removal of the window
statusBar()->message(i18n("%1 removed").arg(window->tabCaption()));
// We could also call removeWindowFromMdi, but it doesn't delete the
// pointer. This way, we're sure that the view will get deleted.
closeWindow(window);
// Synchronize combo box
if (m_pCurrentWindow) {
currentChanged(m_pCurrentWindow);
}
}
}
void RemoteMDI::childClosed(KMdiChildView * w) {
assert(w);
// Set as active
w->activate();
assert(w == m_pCurrentWindow);
// Notify the status bar of the removal of the window
statusBar()->message(i18n("%1 removed").arg(w->tabCaption()));
// Remove the view from MDI, BUT DO NOT DELETE IT! It is automatically deleted by TQt since it was closed.
removeWindowFromMdi(w);
}
bool RemoteMDI::queryClose() {
// Close all open connections
12 years ago
KMdiIterator<KMdiChildView*> *it = createIterator();
while (it->currentItem()) {
KMdiChildView *c = dynamic_cast<KMdiChildView*>(it->currentItem());
if (c) {
RemoteLab::InstrumentView* iview = dynamic_cast<RemoteLab::InstrumentView*>(c);
if (iview) {
iview->closeConnections();
}
}
12 years ago
it->next();
}
12 years ago
deleteIterator(it);
// Save current MDI settings (window positions, etc.)
KConfig *c = kapp->config();
// RAJA FIXME
c->sync();
// Allow this window to close
return true;
}
#include "remotemdi.moc"