/*
 * Remote Laboratory GPIB Server
 *
 * 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 <stdio.h>                /* perror() */
#include <stdlib.h>               /* atoi() */
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>               /* read() */
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/types.h>

#include <tqtimer.h>
#include <tqfile.h>

#include <tdelocale.h>

#include <gpib/ib.h>

#include "gpib_functions.h"
#include "scope_functions.h"
#include "signal_functions.h"
#include "commanalyzer_functions.h"

#include "gpib_conn.h"

#define ABORT_SOCKET(s) 		s->close();						\
					s->disconnect();					\
					delete s;						\
					s = NULL;

#define NETWORK_COMM_TIMEOUT_MS 5000

/* exception handling */
struct exit_exception {
	int c;
	exit_exception(int c):c(c) { }
};

/*
  The GPIBSocket class provides a socket that is connected with a client.
  For every client that connects to the server, the server creates a new
  instance of this class.
*/
GPIBSocket::GPIBSocket(int sock, TQObject *parent, const char *name) :
	TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_loopTimer(NULL), m_config(static_cast<GPIBServer*>(parent)->m_config),
	m_serverParent(static_cast<GPIBServer*>(parent)), m_commandLoopState(0)
{

	// Initialize timers
	m_kerberosInitTimer = new TQTimer();
	connect(m_kerberosInitTimer, SIGNAL(timeout()), this, SLOT(finishKerberosHandshake()));
	m_servClientTimeout = new TQTimer();

	setServiceName("ulab");

	line = 0;
	connect(this, SIGNAL(connectionClosed()), SLOT(connectionClosedHandler()));
	connect(this, SIGNAL(connectionClosed()), parent, SLOT(remoteConnectionClosed()));
	setSocket(sock);
}

GPIBSocket::~GPIBSocket() {
	if (m_servClientTimeout) {
		m_servClientTimeout->stop();
		delete m_servClientTimeout;
		m_servClientTimeout = NULL;
	}
	if (m_kerberosInitTimer) {
		m_kerberosInitTimer->stop();
		delete m_kerberosInitTimer;
		m_kerberosInitTimer = NULL;
	}
	if (m_loopTimer) {
		m_loopTimer->stop();
		delete m_loopTimer;
		m_loopTimer = NULL;
	}
}

void GPIBSocket::close() {
	if (state() == TQSocket::Connected) {
		TDEKerberosServerSocket::close();
		connectionClosedHandler();
		TQTimer::singleShot(0, parent(), SLOT(remoteConnectionClosed()));
	}
}

void GPIBSocket::connectionClosedHandler() {
	printf("[DEBUG] Connection from %s closed\n\r", m_remoteHost.ascii());

	if (m_criticalSection > 0) {
		throw exit_exception(-1);
	}
}

void GPIBSocket::initiateKerberosHandshake() {
	setUsingKerberos(true);
	m_kerberosInitTimer->start(100, TRUE);
}

void GPIBSocket::finishKerberosHandshake() {
	if (kerberosStatus() == TDEKerberosServerSocket::KerberosInitializing) {
		m_kerberosInitTimer->start(100, TRUE);
		return;
	}
	if (kerberosStatus() == TDEKerberosServerSocket::KerberosInUse) {
		m_config->setGroup("Security");
		TQString masterUser = m_config->readEntry("masteruser");
		TQString masterRealm = m_config->readEntry("masterrealm");
		if (masterRealm == "") {
			masterRealm = "(NULL)";
		}
		if ((m_authenticatedUserName != masterUser) || (m_authenticatedRealmName != masterRealm)) {
			printf("[DEBUG] Connection from %s closed due to authentication failure (attempted connection as user %s@%s)\n\r", m_remoteHost.ascii(), m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii());
			close();
			return;
		}

		setDataTimeout(NETWORK_COMM_TIMEOUT_MS);

		TQDataStream ds(this);
		ds.setPrintableData(true);
		ds << TQString("OK");
		writeEndOfFrame();

		enterCommandLoop();
		return;
	}
	else {
		printf("[DEBUG] Connection from %s closed due to Kerberos failure\n\r", m_remoteHost.ascii()); fflush(stdout);
		close();
		return;
	}
}

void GPIBSocket::commandLoop() {
	bool transferred_data;

	m_criticalSection++;
	try {
		transferred_data = false;
		if (state() == TQSocket::Connected) {
			if (m_commandLoopState == 0) {
				if (canReadLine()) {
					processPendingData();
				}
				if (canReadFrame()) {
					TQDataStream ds(this);
					ds.setPrintableData(true);
					TQString instrumentRequest;
					ds >> instrumentRequest;
					clearFrameTail();

					m_activeDeviceType = 0;
					if (instrumentRequest == "LIST") {
						TQStringList deviceList;
						if (m_serverParent->m_serialDevice != "") {
							deviceList.append("SERIAL PORT");
						}
						if (m_serverParent->m_scopeType != "") {
							deviceList.append("OSCILLOSCOPE");
						}
						if (m_serverParent->m_funcgenType != "") {
							deviceList.append("FUNCTION GENERATOR");
						}
						if (m_serverParent->m_commanalyzerType != "") {
							deviceList.append("COMMUNICATIONS ANALYZER");
						}
						ds << deviceList;
						writeEndOfFrame();
					}
					else if (instrumentRequest == "SERIAL PORT") {
						m_activeDeviceType = 1;
					}
					else if (instrumentRequest == "OSCILLOSCOPE") {
						m_activeDeviceType = 2;
					}
					else if (instrumentRequest == "FUNCTION GENERATOR") {
						m_activeDeviceType = 3;
					}
					else if (instrumentRequest == "COMMUNICATIONS ANALYZER") {
						m_activeDeviceType = 4;
					}

					if (m_activeDeviceType != 0) {
						ds << TQString("ACK");
						writeEndOfFrame();

						m_servClientTimeout->start(NETWORK_COMM_TIMEOUT_MS, TRUE);
						transferred_data = true;
						m_commandLoopState = 1;
					}
					else {
						ds << TQString("NCK");
						writeEndOfFrame();
					}
				}
			}
			else if (m_commandLoopState == 1) {
				if (canReadLine()) {
					processPendingData();
				}
				if (m_activeDeviceType == 1) {
					// Serial port
					if (canReadFrame()) {
						TQDataStream ds(this);
						ds.setPrintableData(true);
						TQByteArray recData;
						ds >> recData;
						clearFrameTail();
						if (recData.size() > 0) {
							if (write(m_serverParent->m_serialDeviceSocket, recData.data(), recData.size()) < 0) {
								// ERROR
							}
							transferred_data = true;
							printf("[DEBUG] Got %d bytes from the network interface\n\r", recData.size()); fflush(stdout);
						}
					}
					TQByteArray txData;
					txData.resize(10000);
					int cc = read(m_serverParent->m_serialDeviceSocket, txData.data(), txData.size());
					if (cc > 0) {
						TQDataStream ds(this);
						ds.setPrintableData(true);
						ds << txData;
						writeEndOfFrame();
						transferred_data = true;
						printf("[DEBUG] Got %d bytes from the serial port\n\r", cc); fflush(stdout);
					}
				}
				else {
					if (canReadFrame()) {
						TQDataStream ds(this);
						ds.setPrintableData(true);
						ds >> m_instrumentCommand;

						if (m_instrumentCommand != "") {
							if (m_activeDeviceType == 2) {
								// Oscilloscope
								if (m_instrumentCommand == "RESET") {				// Want to reset scope
									if (scope_reset(m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");;
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETHORIZTIMEBASE") {		// Want to change horizontal timebase
									double value;
									ds >> value;
									if (scope_set_timebase(value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if ((m_instrumentCommand == "GETCHANNELTRACE")) {		// Want channel trace
									TQ_INT32 value;
									ds >> value;
									TQ_INT32 traceLength;
									traceLength = scope_get_channel_trace(value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket);
									if (traceLength > 0) {
										long i;
										TQDoubleArray traceData;
										TQDoubleArray positionData;
										traceData.resize(traceLength);
										positionData.resize(traceLength);
										for (i=0; i<traceLength; i++) {
											traceData[i] = scope_raw_trace_data[i];
											positionData[i] = scope_raw_position_data[i];
										}
										ds << TQString("ACK");
										ds << traceData;
										ds << positionData;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETVOLTSDIV") {		// Want to change volts per division
									TQ_INT32 value1;
									ds >> value1;
									double value2;
									ds >> value2;
									if (scope_set_volts_div(value1, value2, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETVOLTSDIV") {		// Want to get volts per division
									double voltsdiv;
									TQ_INT32 value;
									ds >> value;
									if (scope_get_channel_volts_div(&voltsdiv, value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										ds << voltsdiv;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETSECONDSSDIV") {		// Want to get seconds per division
									double secondsdiv;
									TQ_INT32 value;
									ds >> value;
									if (scope_get_channel_seconds_div(&secondsdiv, value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										ds << secondsdiv;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETTRACESAMPLECOUNT") {	// Want to get number of samples in the trace
									unsigned long samples;
									TQ_INT32 value;
									ds >> value;
									if (scope_get_channel_sample_count(&samples, value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										TQ_INT32 safeSamples = samples;
										ds << safeSamples;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETRUNNING") {			// Want to change run status
									TQ_INT16 value;
									ds >> value;
									if (scope_set_acquisition(value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETRUNNING") {		// Want to get run status
									int running;
									if (scope_get_acquisition(&running, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										TQ_INT16 safeRunning;
										safeRunning = running;
										ds << TQString("ACK");
										ds << safeRunning;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETCHANNELACTIVE") {		// Want to change channel enable
									TQ_INT32 value1;
									ds >> value1;
									TQ_INT16 value2;
									ds >> value2;
									if (scope_set_channel_state(value1, value2, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETTRIGGERCHANNEL") {		// Want to change trigger channel
									TQ_INT16 value;
									ds >> value;
									if (scope_set_trigger_channel(value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETTRIGGERCHANNEL") {		// Want to get trigger channel
									int channel;
									if (scope_get_trigger_channel(&channel, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										TQ_INT16 safeChannel = channel;
										ds << TQString("ACK");
										ds << safeChannel;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETTRIGGERLEVEL") {		// Want to change trigger level
									double value;
									ds >> value;
									if (scope_set_trigger_level(value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETTRIGGERLEVEL") {		// Want to get trigger level
									double triggerlevel;
									if (scope_get_trigger_level(&triggerlevel, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										ds << triggerlevel;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETCHANVERTPOS") {		// Want to change channel vertical position
									TQ_INT32 value1;
									ds >> value1;
									float value2;
									ds >> value2;
									if (scope_set_channel_position(value1, value2, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETHORIZONTALDIVCOUNT") {	// Want the number of horizontal divisions available
									TQ_INT16 divisions = scope_get_number_of_vertical_divisions(m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket);
									if (divisions >= 0) {
										ds << TQString("ACK");
										ds << divisions;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETVERTICALDIVCOUNT") {	// Want the number of vertical divisions available
									TQ_INT16 divisions = scope_get_number_of_horizontal_divisions(m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket);
									if (divisions >= 0) {
										ds << TQString("ACK");
										ds << divisions;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETNUMBEROFCHANNELS") {	// Want the number of channels available
									TQ_INT16 divisions = scope_get_number_of_channels(m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket);
									if (divisions >= 0) {
										ds << TQString("ACK");
										ds << divisions;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETCHANNELACTIVE") {		// Want to get channel activity
									int state;
									TQ_INT32 value;
									ds >> value;
									if (scope_get_channel_state(&state, value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										TQ_INT16 safeState = state;
										ds << TQString("ACK");
										ds << safeState;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETPERMITTEDVDIVS") {		// Want to get permitted volts/div settings
									double attenuation_mult;
									double* permitted_array;
									int permitted_count;
									TQ_INT32 value;
									ds >> value;
									if (scope_get_probe_attenuation_multiplier(&attenuation_mult, value, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
										if (scope_get_permitted_volts_div_settings_at_1x(&permitted_count, &permitted_array, m_serverParent->m_scopeType.ascii(), m_serverParent->m_scopeDeviceSocket) == 0) {
											long i;
											TQDoubleList permittedValues;
											for (i=0; i<permitted_count; i++) {
												permittedValues.append(permitted_array[i]/attenuation_mult);
											}
											free(permitted_array);
											ds << TQString("ACK");
											ds << permittedValues;
											writeEndOfFrame();
										}
										else {
											ds << TQString("NCK");
											writeEndOfFrame();
										}
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else {
									printf("[WARNING] Received unknown command %s from host %s\n\r", m_instrumentCommand.ascii(), m_remoteHost.ascii()); fflush(stdout);
									ds << TQString("NCK");
									writeEndOfFrame();
								}
							}
							else if (m_activeDeviceType == 3) {
								// Function generator
								char errorbuf[1000];
								if (m_instrumentCommand == "RESET") {				// Want to reset function generator
									if (signal_reset(m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETFREQUENCY") {		// Want to change frequency
									double value;
									ds >> value;
									if (signal_set_frequency(value, m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETDUTYCYCLE") {		// Want to change duty cycle
									double value;
									ds >> value;
									if (signal_set_duty_cycle(value, m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETSHAPESQUARE") {		// Want to set square wave
									if (signal_set_waveform("SQUARE", m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETSHAPESINE") {		// Want to set sine wave
									if (signal_set_waveform("SINUSOID", m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETSHAPETRIANGLE") {		// Want to set triangle wave
									if (signal_set_waveform("RAMP", m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETSHAPENOISE") {		// Want to set noise wave
									if (signal_set_waveform("NOISE", m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETPEAKPEAKVOLTAGE") {		// Want to change P-P voltage
									double value;
									ds >> value;
									if (signal_set_peak_peak_voltage(value, m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETOFFSETVOLTAGE") {		// Want to change offset voltage
									double value;
									ds >> value;
									if (signal_set_offset_voltage(value, m_serverParent->m_funcgenType.ascii(), m_serverParent->m_funcgenDeviceSocket, errorbuf) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString(errorbuf);
										writeEndOfFrame();
									}
								}
								else {
									printf("[WARNING] Received unknown command %s from host %s\n\r", m_instrumentCommand.ascii(), m_remoteHost.ascii()); fflush(stdout);
								}
							}
							else if (m_activeDeviceType == 4) {
								// Communications analyzer
								if (m_instrumentCommand == "SETMODESPECTRUMANALYZER") {		// Want to set SA mode
									if (commanalyzer_switch_to_spectrum_analyzer_mode(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if ((m_instrumentCommand == "GETSPECTRUMTRACE")) {		// Want SA trace
									if (commanalyzer_get_spectrum_analyzer_trace(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										int i;
										int tracelen = commanalyzerTraceLength(m_serverParent->m_commanalyzerType.ascii());
										TQDoubleArray traceData;
										traceData.resize(tracelen);
										for (i=0; i<tracelen; i++) {
											traceData[i] = commanalyzer_raw_trace_data[i];
										}
										ds << TQString("ACK");
										ds << traceData;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "LOCKSCREEN") {			// Want to lock screen
									if (commanalyzer_lock_screen(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										commanalyzer_set_display_brightness(0, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket);	// Don't burn in the screen
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETGENMODETRACKING") {		// Want to set generator to tracking mode
									if (commanalyzer_spectrum_analyzer_set_generator_mode_tracking(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETGENMODEFIXED") {		// Want to set generator to fixed mode
									if (commanalyzer_spectrum_analyzer_set_generator_mode_fixed(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETCENTERFREQUENCY") {		// Want to change center frequency
									double value;
									ds >> value;
									if (commanalyzer_set_spectrum_analyzer_center_frequency(value, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETFREQUENCYSPAN") {		// Want to change frequency span
									double value;
									ds >> value;
									if (commanalyzer_set_spectrum_analyzer_frequency_span(value, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETRFINPUTDEDICATED") {	// Want to set RF input to dedicated connector
									if (commanalyzer_spectrum_analyzer_set_rf_input_dedicated(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETRFINPUTMULTIPLEXED") {	// Want to set RF input to multiplexed connector
									if (commanalyzer_spectrum_analyzer_set_rf_input_muxed(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETGENOUTPUTDEDICATED") {	// Want to set generator output to dedicated connector
									if (commanalyzer_spectrum_analyzer_set_generator_output_dedicated(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETGENOUTPUTMULTIPLEXED") {	// Want to set generator output to multiplexed connector
									if (commanalyzer_spectrum_analyzer_set_generator_output_muxed(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETINPUTATTENUATION") {	// Want to change input attenuation
									double value;
									ds >> value;
									if (commanalyzer_set_spectrum_analyzer_input_attenuation(value, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETVERTICALSCALE") {		// Want to change scale
									double value;
									ds >> value;
									if (commanalyzer_set_spectrum_analyzer_scale(value, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETINPUTATTENMODEAUTO") {	// Want to set RF input attenuator mode to automatic
									if (commanalyzer_set_spectrum_analyzer_input_attenuator_mode_auto(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETINPUTATTENMODEFIXED") {	// Want to set RF input attenuator mode to fixed
									if (commanalyzer_set_spectrum_analyzer_input_attenuator_mode_fixed(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETGENOUTPUTPOWER") {		// Want to change generator output power
									double value;
									ds >> value;
									if (commanalyzer_set_spectrum_analyzer_generator_power(value, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETGENOUTPUTFREQUENCY") {	// Want to change generator output frequency
									double value;
									ds >> value;
									if (commanalyzer_set_spectrum_analyzer_generator_frequency(value, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETGENSWEEPASCENDING") {	// Want to set generator sweep to ascending
									if (commanalyzer_spectrum_analyzer_set_generator_sweep_ascending(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETGENSWEEPDESCENDING") {	// Want to set generator sweep to descending
									if (commanalyzer_spectrum_analyzer_set_generator_sweep_descending(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETTRACEAVERAGING") {		// Want to set trace averaging
									double value;
									ds >> value;
									if (commanalyzer_set_spectrum_analyzer_trace_averaging(value, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "SETREFERENCEPOWERLEVEL") {	// Want to set reference power level
									double value;
									ds >> value;
									if (commanalyzer_set_spectrum_analyzer_reference_power_level(value, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETHORIZONTALDIVCOUNT") {	// Want the number of horizontal divisions available
									TQ_INT16 divisions = commanalyzer_get_spectrum_analyzer_number_of_horizontal_divisions(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket);
									if (divisions >= 0) {
										ds << TQString("ACK");
										ds << divisions;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETVERTICALDIVCOUNT") {	// Want the number of vertical divisions available
									TQ_INT16 divisions = commanalyzer_get_spectrum_analyzer_number_of_vertical_divisions(m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket);
									if (divisions >= 0) {
										ds << TQString("ACK");
										ds << divisions;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETTRACESAMPLECOUNT") {	// Want the number of samples in a trace
									TQ_INT16 divisions = commanalyzerTraceLength(m_serverParent->m_commanalyzerType.ascii());
									if (divisions >= 0) {
										ds << TQString("ACK");
										ds << divisions;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETREFERENCEPOWERLEVEL") {	// Want the reference power level
									double rpower;
									if (commanalyzer_get_spectrum_analyzer_reference_power_level(&rpower, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										ds << rpower;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETVERTDIVSCALE") {		// Want the vertical division scale
									double scale;
									if (commanalyzer_get_spectrum_analyzer_scale(&scale, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										ds << scale;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETCENTERFREQUENCY") {		// Want to get the center frequency
									double freq;
									if (commanalyzer_get_spectrum_analyzer_center_frequency(&freq, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										ds << freq;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else if (m_instrumentCommand == "GETFREQUENCYSPAN") {		// Want to get the frequency span
									double freq;
									if (commanalyzer_get_spectrum_analyzer_span(&freq, m_serverParent->m_commanalyzerType.ascii(), m_serverParent->m_commanalyzerDeviceSocket) == 0) {
										ds << TQString("ACK");
										ds << freq;
										writeEndOfFrame();
									}
									else {
										ds << TQString("NCK");
										writeEndOfFrame();
									}
								}
								else {
									printf("[WARNING] Received unknown command %s from host %s\n\r", m_instrumentCommand.ascii(), m_remoteHost.ascii()); fflush(stdout);
								}
							}
							else {
								// Unknown
								transferred_data = true;
								m_commandLoopState = 0;
							}

							m_servClientTimeout->start(NETWORK_COMM_TIMEOUT_MS, TRUE);
							transferred_data = true;
							m_commandLoopState = 1;
						}

						clearFrameTail();
					}
				}
			}
		}
		m_criticalSection--;
		if (transferred_data) {
			if (m_loopTimer) m_loopTimer->start(0, TRUE);
		}
		else {
			if (m_loopTimer) m_loopTimer->start(100, TRUE);
		}
		return;
	}
	catch (...) {
		m_criticalSection--;
		return;
	}
}

int GPIBSocket::enterCommandLoop() {
	m_commandLoopState = 0;
	if (!m_loopTimer) {
		m_loopTimer = new TQTimer();
		connect(m_loopTimer, SIGNAL(timeout()), this, SLOT(commandLoop()));
	}
	if (m_loopTimer) m_loopTimer->start(0, TRUE);
	return 0;
}

/*
  The GPIBServer class handles new connections to the server. For every
  client that connects, it creates a new GPIBSocket -- that instance is now
  responsible for the communication with that client.
*/
GPIBServer::GPIBServer(TQObject* parent, int port, KSimpleConfig* config) :
	TQServerSocket( port, 1, parent ), m_config(config), m_numberOfConnections(0),
	m_scopeDeviceSocket(-1), m_funcgenDeviceSocket(-1), m_commanalyzerDeviceSocket(-1) {

	if ( !ok() ) {
		printf("[ERROR] Failed to bind to port %d\n\r", port);
		exit(1);
	}

	if (readConfig() != 0) {
		exit(-1);
	}

	printf("[INFO] Server started on port %d\n\r", port); fflush(stdout);
}

GPIBServer::~GPIBServer() {
	//
}

void GPIBServer::newConnection(int socket) {
	GPIBSocket *s = new GPIBSocket(socket, this);
	s->m_remoteHost = s->peerAddress().toString();
	printf("[DEBUG] New connection from %s\n\r", s->m_remoteHost.ascii());
	connect(s, SIGNAL(connectionClosed()), s, SLOT(deleteLater()));
	s->initiateKerberosHandshake();
	emit newConnect(s);
}

void GPIBServer::remoteConnectionClosed() {
	m_numberOfConnections--;
}

int GPIBServer::readConfig() {
	// Serial port
	m_config->setGroup("Serial Port");
	m_serialDevice = m_config->readEntry("device", "");
	TQString desiredBaudRate = m_config->readEntry("baudrate", "9600");
	if (desiredBaudRate == "1200") {
		m_serialBaud = B1200;
	}
	else if (desiredBaudRate == "9600") {
		m_serialBaud = B9600;
	}
	else if (desiredBaudRate == "19200") {
		m_serialBaud = B19200;
	}
	else if (desiredBaudRate == "115200") {
		m_serialBaud = B115200;
	}
	else {
		printf("[WARNING] Invalid baudrate %s specified, selecting 9600 instead\n\r", desiredBaudRate.ascii()); fflush(stdout);
		desiredBaudRate = "9600";
		m_serialBaud = B9600;
	}

	// Oscilloscope
	m_config->setGroup("Oscilloscope");
	m_scopeType = m_config->readEntry("type", "");
	m_scopeConnection = m_config->readEntry("connection", "gpib");
	m_scopeBoard = m_config->readNumEntry("board", 0);
	m_scopeDevice = m_config->readNumEntry("device", 0);

	// Function generator
	m_config->setGroup("Function Generator");
	m_funcgenType = m_config->readEntry("type", "");
	m_funcgenConnection = m_config->readEntry("connection", "gpib");
	m_funcgenBoard = m_config->readNumEntry("board", 0);
	m_funcgenDevice = m_config->readNumEntry("device", 0);

	// Communications analyzer
	m_config->setGroup("Communications Analyzer");
	m_commanalyzerType = m_config->readEntry("type", "");
	m_commanalyzerConnection = m_config->readEntry("connection", "gpib");
	m_commanalyzerBoard = m_config->readNumEntry("board", 0);
	m_commanalyzerDevice = m_config->readNumEntry("device", 0);

	if (m_serialDevice != "") {
		struct termios oldtio, newtio;

		m_serialDeviceSocket = ::open(m_serialDevice.ascii(), O_RDWR | O_NOCTTY | O_NONBLOCK | O_APPEND);
		if (m_serialDeviceSocket < 0) {
			printf("[FAIL] Unable to open serial device %s\n\r", m_serialDevice.ascii()); fflush(stdout);
			return 1;
		}
	
		tcgetattr(m_serialDeviceSocket, &oldtio);	// Save current port settings

		bzero(&newtio, sizeof(newtio));
		newtio.c_cflag = m_serialBaud | CS8 | CLOCAL | CREAD;
		newtio.c_iflag = IGNPAR;
		newtio.c_oflag = 0;
	
		// Set input mode (non-canonical, no echo,...)
		newtio.c_lflag = 0;
	
		newtio.c_cc[VTIME] = 0;	// Inter-character timer unused
		newtio.c_cc[VMIN]  = 0;	// Blocking read unused
		
		tcflush(m_serialDeviceSocket, TCIFLUSH);
		tcsetattr(m_serialDeviceSocket, TCSANOW, &newtio);

		printf("[INFO] Serial port on node %s activated at %s baud\n\r", m_serialDevice.ascii(), desiredBaudRate.ascii());
	}

	if (m_scopeType != "") {
		printf("[INFO] Oscilloscope conjectured to be on GPIB address %d:%d\n\r", m_scopeBoard, m_scopeDevice);
		m_scopeDeviceSocket = open_gpib_device(m_scopeBoard, m_scopeDevice);
		if (m_scopeDeviceSocket < 0) {
			// return -1;
		}
		else {
			time_t rawtime;
			struct tm * timeinfo;
			char datebuffer [80];
			char timebuffer [80];
			time ( &rawtime );
			timeinfo = localtime ( &rawtime );
			strftime(timebuffer,80,"TIME \"%H:%M:%S\"",timeinfo);
			strftime(datebuffer,80,"DATE \"%Y-%m-%d\"",timeinfo);
			printf("[INFO] Configuring %s oscilloscope\n\r", scopeLongDescription(m_scopeType.ascii()));
			printf("[INFO] %s\n\r", datebuffer);
			printf("[INFO] %s\n\r", timebuffer);
			if (gpib_write(m_scopeDeviceSocket, timebuffer) == 0) {
				gpib_write(m_scopeDeviceSocket, datebuffer);
				scope_perform_initial_setup(m_scopeType.ascii(), m_scopeDeviceSocket);
				printf("[INFO] Communication verified\n\r");
			}
			else {
				printf("[WARN] Communication failed!\n\r");
			}
		}
	}

	if (m_funcgenType != "") {
		printf("[INFO] Function generator conjectured to be on GPIB address %d:%d\n\r", m_funcgenBoard, m_funcgenDevice);
		m_funcgenDeviceSocket = open_gpib_device(m_funcgenBoard, m_funcgenDevice);
		if (m_funcgenDeviceSocket < 0) {
			//return 1;
		}
		else {
			printf("[INFO] Configuring %s function generator\n\r", funcgenLongDescription(m_funcgenType.ascii()));
			if (gpib_write(m_funcgenDeviceSocket, "RESET") == 0) {
				printf("[INFO] Communication verified\n\r");
			}
			else {
				printf("[WARN] Communication failed!\n\r");
			}
		}
	}

	if (m_commanalyzerType != "") {
		printf("[INFO] Communications analyzer conjectured to be on GPIB address %d:%d\n\r", m_commanalyzerBoard, m_commanalyzerDevice);
		m_commanalyzerDeviceSocket = open_gpib_device(m_commanalyzerBoard, m_commanalyzerDevice);
		if (m_commanalyzerDeviceSocket < 0) {
			//return 1;
		}
		else {
			time_t rawtime;
			struct tm * timeinfo;
			time ( &rawtime );
			timeinfo = localtime ( &rawtime );
			printf("[INFO] Configuring %s communications analyzer\n\r", commanalyzerLongDescription(m_commanalyzerType.ascii()));
			if (commanalyzer_set_time(timeinfo, m_commanalyzerType.ascii(), m_commanalyzerDeviceSocket) == 0) {
				commanalyzer_set_date(timeinfo, m_commanalyzerType.ascii(), m_commanalyzerDeviceSocket);
				printf("[INFO] Communication verified\n\r");
			}
			else {
				printf("[WARN] Communication failed!\n\r");
			}
		}
	}

	return 0;
}