/*
 * EdgeTechHandler.cpp
 *
 *  Last modified: January 18th, 2013
 *  Author: ericjmartin
 *
 *  This class implements a system level control and communication
 *  of an Edgetech FSDW system. Number of subsystems to be controlled
 *  is
 */

#include "EdgeTechHandler.h"

/*
 * Default Constructor
 * 	etSockIP: Textual address for resolution IP address or dynamic name acceptable
 * 	sonarCmdSockPort: integer of target tcp port for sonar command server
 * 	sonarDataSockPort: integer of target tcp port for sonar data server
 */
EdgeTechHandler::EdgeTechHandler(char * etSockIP, int sonarCmdSockPort, int sonarDataSockPort, FILE * logfds) {

	m_ulMaxPulseFiles = ulMaxPulseFiles_c;

// Register the file descriptor for all messages.
	this->logfds = logfds;

//	Create Subsystem Objects
	for (int i = 0; i < ET_SUBSYSTEMS; i++) {
		switch (i) {
		case 0:
			etsubsys[i].setId(SUBSYSID_SB);
			break;
		case 1:
			etsubsys[i].setId(SUBSYSID_SSLF);
			break;
		case 2:
			etsubsys[i].setId(SUBSYSID_SSHF);
			break;
		}
	}
	c_netCmdConnected = false;

//	Establish Socket Client Connections for command and data ports
	strcpy(c_sockAddress, etSockIP);
	c_cmdSockPort = sonarCmdSockPort;
	c_dataSockPort = sonarDataSockPort;

	fprintf(logfds, "EdgeTechHandler: Establishing Command Connection to the server at %s:%i\n", c_sockAddress, c_cmdSockPort);

	if (Connect() == true) {
//		Read State For the System
		fprintf(logfds, "EdgeTechHandler: Reading State for System Variables\n");
//		Call Initialization routine Moved out for better API control on front end.
//		if (Initialize()) {
//			Finished Gathering information, system is ready to respond to requests.
		fprintf(logfds, "EdgeTechHandler: FSDW Client Connected, ready for requests.\n");
//		}
	}

}

/*
 * Class Destructor
 */
EdgeTechHandler::~EdgeTechHandler() {
//	Disconnect TCP Clients
	fprintf(logfds, "EdgeTechHandler::~EdgeTechHandler(), destructing...\n");
	Disconnect();
}

/*
 *  EdgeTechHandler::SetTxPower() - transmits a message to set projector power
 *  	id: subsystem id for message assembly
 *  	maxChannels: number of channels within the given id
 *  	rfTxPower: float value of percent full power
 *
 */
int EdgeTechHandler::SetTxPower(subsysId_t subsys, const float &rfTxPower, const bool &bUpdate, const long &lTimeOutMS) {
	int errcount = 0;
	if (!(rfTxPower <= 100))
		return -1;

	long lTxGain = (long) (fabs(floor(10.0 * rfTxPower + 0.05))); //round

	if (subsys == SUBSYSID_NONE)
		return -1;

	int maxChannels = (subsys == SUBSYSID_SB) ? 1 : 2;

	for (int iChannel = 0; iChannel < maxChannels; iChannel++) {

		if (!SendCommand(SONAR_MESSAGE_PING_GAIN, SONAR_COMMAND_SET, subsys, (unsigned char) iChannel, (unsigned char *) &lTxGain, sizeof(lTxGain))) {
			errcount++;
			continue;
		}

		if (bUpdate) {
			if (SendCommand(SONAR_MESSAGE_PING_GAIN, SONAR_COMMAND_GET, subsys, (unsigned char) iChannel)) {
				if (!ReceiveMessage(lTimeOutMS)) {
					errcount++;
				}
			} else
				errcount++;
		}
	}
	return (-errcount);
}

/*
 * EdgeTechHandler::EnablePing() - enable (true) or disable (false) on given subsys
 */
int EdgeTechHandler::EnablePing(subsysId_t subsys, bool bEnable, const bool &bUpdate, const long &lTimeOutMS) {
	int errcount = 0;
	long lPingMode = bEnable ? 1 : 0;
	if (!SendCommand(SONAR_MESSAGE_PING, SONAR_COMMAND_SET, subsys, 0, (unsigned char *) &lPingMode, (unsigned long) sizeof(lPingMode))) {
		errcount++;
	}

//	Update if requested
	if (bUpdate) {
		if (SendCommand(SONAR_MESSAGE_PING, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS)) {
				errcount++;
			}
		} else
			errcount++;
	}

	return (-errcount);
}

/*
 * EdgeTechHandler::PingOnce() - conduct a singe ping for testing purposes
 */
int EdgeTechHandler::PingOnce(subsysId_t subsys, const long &lTimeOutMS) {
	const long lPingSingle = 2L;
	int errcount = 0;

	if (SendCommand(SONAR_MESSAGE_PING, SONAR_COMMAND_SET, subsys, 0, (unsigned char *) &lPingSingle, sizeof(lPingSingle)) == 0) {
		errcount++;
	}

	return (-errcount);
}

/*
 * EdgeTechHandler::HandleSonarCommand()
 */
bool EdgeTechHandler::HandleSonarCommand(const EdgeTechMessage& rMessage) {

// Sonar command channel data receive handler for this subsystem.
// Note: this routine is called by a thread in the context of the sonar command channel
// so access this subsystem's state data in a thread safe manner.

	const unsigned short unMessage = rMessage.SonarMessage();
	char *pszVersionName;
	char *pszFileName;
	unsigned long ulNumPulses, ulminNumPulses;
	TimestampType * timeSt;
	SonarMessagePingType *psPingType;
	int subidx = -1;
	unsigned long ulPulse;
	DataLoggingStatusType * sLogStatus;

//	fprintf(logfds,
//			"EdgeTechHandler::HandleSonarCommand(), Message %u received @ %lu\n",
//			unMessage, rulTimeStamp);

//ESTATUSUPDATE eStatus = statusUpdateNone;

	switch (unMessage) {
	case SONAR_MESSAGE_NONE:
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_NONE received\n");
		// Null message - can be used to test communications.
		break;

	case SONAR_MESSAGE_SYSTEM_VERSION:

		// Get the software version string (SonarMessageStringType)
		// Note:  This is a system command, the subsystem and channel numbers
		// must be 0.  The version string consists of an alphabetic string
		// followed by a version number of the form N.M where N and M are both
		// integers.
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_SYSTEM_VERSION received");

		pszVersionName = (char *) rMessage;
		strncpy(c_cSystemVersion, pszVersionName, ulMaxVersionStringLength_c - 1);
		fprintf(logfds, " [%s]\n", pszVersionName);
		break;

	case SONAR_MESSAGE_SYSTEM_TIME:

		// Get or set the time (TimestampType)
		// Note that because of the nagle algorithm on sockets, the actual
		// message can be delayed.  To set the time with greater accuracy, say,
		// within 10 ms, the nagle algorithm should be disabled by the sender.
		// Or a subsequent message bigger than the maximum network packet size
		// should be sent following this message (usually about 1600 bytes).
		// Note:  This is a system command, the subsystem and channel numbers
		// must be 0.
		// NOTE: The SONAR_MESSAGE_NONE message can be any size desired.
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_SYSTEM_TIME received");

		timeSt = (TimestampType *) rMessage;
		fprintf(logfds, "[%li\'' %li''']\n", timeSt->time, timeSt->milliseconds);
		c_lServerMillisecs = timeSt->milliseconds;
		c_lServerSecs = timeSt->time;

		break;

	case SONAR_MESSAGE_OVERRIDE:

		// Override data lockout caused by suspected system failure in the
		// sonar system.  Once an override message is sent, the sonar system
		// will not stop the data flow because of potential data quality
		// problems.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_OVERRIDE received\n");
		break;

	case SONAR_MESSAGE_RUN_POST_DIAGNOSTICS:

		// Run all of the power on self test diagnostics.  This will normally
		// cause an audible chirp on the subbottom (if present) and side scan
		// low (if present) and other internal diagnostics.  This can either
		// cause OR CLEAR a POST error code.  If a post error code is detected
		// sonar data WILL NOT be returned to the topside unless an override
		// message is sent.  The subsystem and channel should be set to 0 for
		// this message.  The sonarCommand field in the header should be set to
		// SONAR_COMMAND_GET as this command returns the post status bit field
		// as the lsb 8 bits of its return value (SonarMessageLongType).  See
		// the SonarMessageStatusType and the serviceNeeded field for a list
		// of the possible return values.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_RUN_POST_DIAGNOSTICS received\n");
		break;

	case SONAR_MESSAGE_DATA_NETWORK_WINDOW:

		// Window for data transmission (SonarMessageWindowType)
		// Subsystem and channel must be valid.

//		m_sStatus.m_sDataNetworkWindow = *((SonarMessageWindowType *) rMessage);
//		eStatus = statusUpdateDataNetworkWindow;

		break;

	case SONAR_MESSAGE_DATA_ACTIVE:

		// Activate / Deactivate return data type.  The data for the subsystem
		// / channel specified in the header is activated / deactivated
		// (0 - deactivate : 1 - activate)   (SonarMessageLongType)
		// Subsystem and channel must be valid.

//		m_sStatus.m_bDataActive = static_cast<bool>(((long) rMessage) != 0);
//		eStatus = statusUpdateDataActive;

		break;

	case SONAR_MESSAGE_PROCESSING_ENHANCE_WINDOW:

		// Window for processing optimization (SonarMessageWindowType)
		// Subsystem and channel must be valid.

		//
		//m_sEnhanceWindow = *((SonarMessageWindowType *) rMessage);
		//eStatus = statusUpdateEnhanceWindow;
		//

		break;

	case SONAR_MESSAGE_PROCESSING_DIRECT_PATH:

		// Samples to ignore due to direct path on AGC, normalization
		// algorithms (SonarMessageLongType)
		// Subsystem and channel must be valid.

//		m_sStatus.m_lDirectPathHoldoff = (long) rMessage;
//		eStatus = statusUpdateProcessingDirectPath;

		break;

	case SONAR_MESSAGE_PING:

		// Enable/disable ping: 0 => disable, 1=>enable, 2 => single ping only
		// (SonarMessageLongType)
		// Note:  This is a subsystem command, the channel number must be 0.

//		m_sStatus.m_lPingEnable = (long) rMessage;
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING received "); //: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLPingEnable((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);

		}
		//		eStatus = statusUpdatePing;

		break;

	case SONAR_MESSAGE_PING_GAIN:

		// 1000.0 * DAC gain to scale outgoing pulse by (SonarMessageLongType)
		// Subsystem and channel must be valid.

//		m_sStatus.m_lPingGain = (long) rMessage;
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_GAIN received ");		//: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLPingGain((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}
//		eStatus = statusUpdatePingGain;

		break;

	case SONAR_MESSAGE_PING_LIST:

		// A set message takes no parameters. A set message resets the list of
		// pulse records so that the next get message will return the first
		// record in the list.
		// A get message with a (SonarMessageLongType) parameter, returns up to
		// the specified number of pulse records as an array of
		// (SonarMessagePingType) values, up to a maximum of 30.
		// A get message can also be sent with no parameters, in this case it
		// returns a single (SonarMessagePingType) structure.
		// The pulse record following the last valid pulse record will have a
		// NULL pulse name field.
		// Note:  This is a subsystem command, the channel number must be 0.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_LIST received\n");		//: %li\n",m_sStatus.m_lPingEnable);

		psPingType = (SonarMessagePingType *) rMessage;
		ulNumPulses = rMessage.MessageSize() / sizeof(SonarMessagePingType);

//		ulminNumPulses;
		subidx = GetSubsysIdxFromMessage(rMessage);

		if (ulNumPulses <= m_ulMaxPulseFiles)
			ulminNumPulses = ulNumPulses;
		else
			ulminNumPulses = m_ulMaxPulseFiles;

		for (ulPulse = 0UL; ulPulse < ulminNumPulses; ulPulse++) {
			etsubsys[subidx].m_asPulseFile[ulPulse] = psPingType[ulPulse];
//			fprintf(logfds,
//					"EdgeTechHandler::HandleSonarCommand(): Got pulsefile [ %s ]\n",
//					etsubsys[subidx].m_asPulseFile[ulPulse].fileName);
		}

		etsubsys[subidx].m_ulNumPulses = ulNumPulses;

		break;

	case SONAR_MESSAGE_PING_SELECT:

		// Select an outgoing set of pulses, and matched filters.
		// (SonarMessageStringType)
		// Note:  This is a subsystem command, the channel number must be 0.

	{
		subidx = GetSubsysIdxFromMessage(rMessage);
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_SELECT received ");	//: %li\n",m_sStatus.m_lPingEnable);

		pszFileName = (char *) rMessage;
		//fprintf(logfds, "Selected pulsefile was: %s\n", pszFileName);
		strncpy(&(etsubsys[subidx].m_szSelectedPulse[0]), pszFileName, ulMaxVersionStringLength_c - 1);
		fprintf(logfds, "[CH%i:%s]\n", subidx, pszFileName);
	}

		break;

	case SONAR_MESSAGE_PING_RATE:

		// Number of pings pers second required * 1000
		// (SonarMessageLongType)
		// Actual ping rate may be slightly lower (2048 sample granuality)
		// Note:  This is a subsystem command, the channel number must be 0.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_RATE received ");		//: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLPingRate((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}
		break;

	case SONAR_MESSAGE_PING_TRIGGER:

		// Set trigger for internal(0), external(1), coupled(2), or gated(3)
		// (SonarMessageLongType).  Coupled mode causes a system to be triggered
		// by another one (eg Sidescan triggered by Subbottom)
		// See SONAR_MESSAGE_PING_COUPLING_PARAMETERS message.  In gated mode
		// the external trigger is used as a trigger inhibit, and the inhibit
		// time is based on the coupling parameter delay.
		// Note:  This is a subsystem command, the channel number must be 0.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_TRIGGER received "); //: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLTriggerMode((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}

		break;

	case SONAR_MESSAGE_PING_DELAY:

		// Set delay for external trigger in ms (SonarMessageLongType).  This
		// message applies only to the soft trigger.  A soft trigger uses the
		// ethernet to transmit trigger requests to an underwater electronics
		// bottle and is not present in an FS-SB standard system.  See the
		// SONAR_MESSAGE_PING_COUPLING_PARAMETERS message for a hardwired
		// trigger delay.
		// Note:  This is a subsystem command, the channel number must be 0.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_DELAY received "); //: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLExternalSoftTriggerDelay((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}

		break;

	case SONAR_MESSAGE_PING_MAX_SAMPLES:

		// A get message takes a parameter that is 1000 * the ping rate in Hz.
		// (SonarMessageLongType) and returns a (SonarMessageLongType) with the
		// maximum number of samples that can be received for that ping rate.
		// A get message can also be sent with no parameters, in this case it
		// returns the maximum number of samples for the current ping rate.
		// Note:  This is a subsystem command, the channel number must be 0.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_MAX_SAMPLEs received "); //: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLMaxSamplesPossible((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}

		break;

	case SONAR_MESSAGE_PING_RANGE:

		// Set the ping rate for sidescan systems.  Sets the ping rate  based
		// on the range in millimeters. (SonarMessageLongType)
		// Note:  This is a subsystem command, the channel number must be 0.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_RANGE received "); //: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLRangeInMillimeters((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}

		break;

	case SONAR_MESSAGE_PING_COUPLING_PARAMETERS:

		// Set the coupling parameters for this system when in trigger mode
		// coupled, and the hardware trigger delay in external trigger modes.
		// (SonarMessageCouplingParametersType)
		// Note:  This is a subsystem command, the channel number must be 0.

//		m_sStatus.m_sCouplingParameters =
//				*((SonarMessageCouplingParametersType *) rMessage);
//		eStatus = statusUpdatePingCouplingParameters;

		break;

	case SONAR_MESSAGE_PING_FISH_LIST:

		// Get the list of human readable strings and fish IDs.  Only a
		// SONAR_COMMAND_GET message is meaningful.  Returns an array of
		// (SonarMessageFishType) records.  The number of records can be
		// determined by the size of the return message.
		// This message should be used to get the available EdgeTech fish to
		// present to the end user.  The user should first select the fish
		// which is attached to the sonar processing system, and should then
		// select a pulse only from the subset of the selected fish.
		// Note:  This is a subsystem command, the channel number must be 0.

//	{
//		SonarMessageFishType *pFish = (SonarMessageFishType *) rMessage;
//		unsigned long ulNumFish = rMessage.MessageSize()
//				/ sizeof(SonarMessageFishType);
//
//		for (unsigned long ulFish = 0UL;
//				ulFish < min(ulNumFish, m_ulMaxFishTypes); ulFish++) {
//			m_sStatus.m_asFishType[ulFish] = pFish[ulFish];
//		}
//	}

//		eStatus = statusUpdatePingFishList;

		break;

	case SONAR_MESSAGE_ADC_GAIN:

		// Set gain factor for ADC when AGC disabled (SonarMessageLongType)
		// Value is * 1000.0 (only 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
		// and 2048 supported)
		// Subsystem and channel must be valid.  Ignored on sidescan systems.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_ADC_GAIN received "); //: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLAdcGain((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}

		break;

	case SONAR_MESSAGE_ADC_AGC:

		// 0=> disable, 1=> enable (Automatic Gain Control)
		// (SonarMessageLongType)
		// Subsystem and channel must be valid. Ignored on sidescan systems.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_ADC_AGC received ");	//: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLAdcagc((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}

		break;

	case SONAR_MESSAGE_ADC_RATE:

		// ADC rate in Hz * 1000.  This is a read only value and changes with
		// the pulse selected (SonarMessageLongType)
		// Note:  This is a subsystem command, the channel number must be 0.

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SONAR_MESSAGE_PING_RATE received ");		//: %li\n",m_sStatus.m_lPingEnable);

		subidx = GetSubsysIdxFromMessage(rMessage);
		if (subidx >= 0) {
			etsubsys[subidx].setLAdcRate((long) rMessage);
			fprintf(logfds, "[CH%i:%li]\n", subidx, (long) rMessage);
		}

		break;

	case STORAGE_MESSAGE_LOGGING_STATUS:

		/* Data logging status (DataLoggingStatusType)                          */
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), STORAGE_MESSAGE_LOGGING_STATUS received\n");		//: %li\n",m_sStatus.m_lPingEnable);

		sLogStatus = (DataLoggingStatusType *) rMessage;

		if (sLogStatus->recordingState == 1)
			c_bDiskLoggingEnabled = true;
		else if (sLogStatus->recordingState == 0)
			c_bDiskLoggingEnabled = false;

		if (strlen(sLogStatus->fileName.name) > 0)
			strcpy(c_cCurrentLogFileName, sLogStatus->fileName.name);

		c_lCurrentLogFileSize = sLogStatus->fileSize;
		c_lDiskLogFreeSpace = sLogStatus->freeSpace;

		break;

	case STORAGE_MESSAGE_MASTER_RECORD:

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), STORAGE_MESSAGE_MASTER_RECORD received ");	//: %li\n",m_sStatus.m_lPingEnable);

		if (((long) rMessage) == 1)
			c_bMasterRecordEnabled = true;
		else if (((long) rMessage) == 0)
			c_bMasterRecordEnabled = false;

		fprintf(logfds, "[%s]\n", (c_bMasterRecordEnabled) ? "true" : "false");
		break;

	case STORAGE_MESSAGE_DATA_ENABLE:
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), STORAGE_MESSAGE_DATA_ENABLE received\n");	//: %li\n",m_sStatus.m_lPingEnable);

		if (((long) rMessage) == 1)
			c_bStorageMessageDataEnabled = true;
		else if (((long) rMessage) == 0)
			c_bStorageMessageDataEnabled = false;

		break;

	case SYSTEM_MESSAGE_SONAR_CONFIGURATION:
		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), SYSTEM_MESSAGE_SONAR_CONFIGURATION received\n");	//: %li\n",m_sStatus.m_lPingEnable);
		break;

	default:

		fprintf(logfds, "EdgetechHandler::HandleSonarCommand(), Unhandled message received (%d)\n", unMessage);
		break;
	}
	return true;
}

/*
 * EdgeTechHandler::HandleSonarError() - generic handler for messages that have error
 * 	codes.
 */
void EdgeTechHandler::HandleSonarError(const EdgeTechMessage& rMessage) {

	assert(rMessage.SonarCommand() == SONAR_COMMAND_ERROR);

	if (rMessage.SonarCommand() == SONAR_COMMAND_ERROR) {

//		const long lErrorCode = static_cast<long>(rMessage);
		const long lErrorCode = (long) rMessage;

		switch (lErrorCode) {
		case SONAR_MESSAGE_ERROR_NONE:              // No error.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_NONE\n");
			break;
		case SONAR_MESSAGE_ERROR_FAILED:            // General command failure.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_FAILED - General command failure.\n");
			break;
		case SONAR_MESSAGE_ERROR_DATA_SIZE:         // Message size mismatch.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_DATA_SIZE - Message size mismatch. \n");
			break;
		case SONAR_MESSAGE_ERROR_SUBSYSTEM:  // Subsystem number is not present.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_SUBSYSTEM - Subsystem number is not present.\n");
			break;
		case SONAR_MESSAGE_ERROR_CHANNEL:      // Channel number is not present.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_CHANNEL - Channel number is not present.\n");
			break;
		case SONAR_MESSAGE_ERROR_UNKNOWN: // Sonar message field contains unknown command.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_UNKNOWN - Sonar message field contains unknown command.\n");
			break;
		case SONAR_MESSAGE_ERROR_PULSE_FILE: // Pulse file error... pulse not loaded.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_PULSE_FILE - Pulse file error... pulse not loaded.\n");
			break;
		case SONAR_MESSAGE_ERROR_SESSION_ID: // Session id error... command rejected.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_SESSION_ID - Session id error... command rejected.\n");
			break;
		case SONAR_MESSAGE_ERROR_OVERRIDE_REQUIRED: // Override required... command rejected.
			fprintf(logfds, "EdgeTechHandler::HandleSonarError(): SONAR_ERROR_OVERIDE_REQUIRED - Override required... command rejected.\n");
			break;

			// Nothing to do for now.

			break;

		default:

			// Nothing to do for now.

			break;
		}
	}
}

/*
 * EdgeTechHandler::SendCommand() - assemble a message and call transmitting function
 * 	on the command data port
 */
bool EdgeTechHandler::SendCommand(unsigned short unMessage, unsigned char ucCommand, unsigned char ucSubSystem, unsigned char ucChannel, unsigned char *pbyMessage, unsigned long ulBytesInMessage, const long &lTimeOutMS) {

	bool bSuccess = false;

	EdgeTechMessage Command;

//	fprintf(logfds,"SENDING: %i\n",unMessage);

//	This is a device message, which has a different constructor
	if ((unMessage >= MESSAGE_OFFSET_DEV && unMessage < MESSAGE_OFFSET_BATHYMETRIC_DATA)) {

//		For Device Messages, we need to nest one complete message inside of a container.
		EdgeTechMessage DevCommand;

//		First create the dev message which we will encapsulate within the container message;
		if (!DevCommand.SetDevMessage(unMessage, ucCommand, pbyMessage, ulBytesInMessage, (unsigned char) 0,              // Session ID,
				ucSubSystem, ucChannel)) {
			return bSuccess;
		}

//		Now pack that message inside of a container message
		if (!Command.SetMessage(SONAR_MESSAGE_CONTAINER, SONAR_COMMAND_SET, DevCommand.Message(), DevCommand.MessageSize(), (unsigned char) 0,              // Session ID,
				ucSubSystem, ucChannel)) {
			return bSuccess;
		}

//		Transmit
		if (!SendMessage(Command, PORT_CMD)) {
			return bSuccess;
		}
	} else
//	We are sending a more standard message.
	{

		if (!Command.SetMessage(unMessage, ucCommand, pbyMessage, ulBytesInMessage, (unsigned char) 0,              // Session ID,
				ucSubSystem, ucChannel)) {
			return bSuccess;
		}
//		Transmit
		if (!SendMessage(Command, PORT_CMD)) {
			return bSuccess;
		}
	}

	bSuccess = true;
	return bSuccess;

}

/*
 * EdgeTechHandler::Connect() - configure and connect tcp client sockets
 */
bool EdgeTechHandler::Connect() {

	if (c_netCmdConnected || c_netDataConnected) {
		perror("EdgeTechHandler: ERROR Socket is already connected");
		return false;
	}

	struct sockaddr_in cmd_serv_addr;
	struct sockaddr_in data_serv_addr;
	struct hostent *server;

	server = gethostbyname(c_sockAddress);
	if (server == NULL) {
		perror("EdgeTechHandler: ERROR, no such host\n");
		return false;
	}

//	Configure the command port
//	bzero((char *) &cmd_serv_addr, sizeof(cmd_serv_addr));
	memset((char *) &cmd_serv_addr, 0, sizeof(cmd_serv_addr));
	cmd_serv_addr.sin_family = AF_INET;
//	bcopy((char *) server->h_addr,(char *)&cmd_serv_addr.sin_addr.s_addr,server->h_length);
	memcpy((char *) &cmd_serv_addr.sin_addr.s_addr, (char *) server->h_addr, server->h_length);
	cmd_serv_addr.sin_port = htons(c_cmdSockPort);

//	Create the socket
	c_cmdSockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (c_cmdSockfd < 0) {
		perror("EdgeTechHandler: ERROR opening command socket");
		return false;
	}

//	Connect the socket
	if (connect(c_cmdSockfd, (struct sockaddr *) &cmd_serv_addr, sizeof(cmd_serv_addr)) < 0) {
		perror("EdgeTechHandler: ERROR connecting to command socket");
		return false;
	}

	fprintf(logfds, "EdgeTechHandler: Command Connection to %s::%i established...\n", c_sockAddress, c_cmdSockPort);
	this->c_netCmdConnected = true;

//	Configure the data port
	memset((char *) &data_serv_addr, 0, sizeof(data_serv_addr));
	data_serv_addr.sin_family = AF_INET;
	memcpy((char *) &data_serv_addr.sin_addr.s_addr, (char *) server->h_addr, server->h_length);
	data_serv_addr.sin_port = htons(c_dataSockPort);

//	Create the data socket
	c_dataSockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (c_dataSockfd < 0) {
		perror("EdgeTechHandler: ERROR opening data socket");
		return false;
	}

//	Connect the Data Socket
	if (connect(c_dataSockfd, (struct sockaddr *) &data_serv_addr, sizeof(data_serv_addr)) < 0) {
		perror("EdgeTechHandler: ERROR connecting to data socket");
		return false;
	}

//	Suppress broken pipes, we'll catch it on write.
#ifndef _QNX
	signal(SIGPIPE, SIG_IGN);
#endif
//	Now create polling structures
//	c_ufds[0].fd = c_cmdSockfd;
//	c_ufds[0].events = POLLIN;
//
//	c_ufds[1].fd = c_dataSockfd;
//	c_ufds[1].events = POLLIN;

//	Create the Select fds;
	FD_SET(c_cmdSockfd, &c_readfds);
	FD_SET(c_cmdSockfd, &c_readfds);

	fprintf(logfds, "EdgeTechHandler: Data Connection to %s::%i established...\n", c_sockAddress, c_dataSockPort);

	this->c_netDataConnected = true;

	return true;

}

/*
 * EdgeTechHandler::Initialize() - Function that should directly follow established connection.
 */
bool EdgeTechHandler::Initialize() {

//	This routine automatically queries common setting and populates class data.
	if (QueryCommonSettings() < 0)
		return true;
	else
		return false;
}

/*
 * Returns the logical status of an established tcp client to the edgetech command port.
 */
bool EdgeTechHandler::isConnected() {
	return c_netCmdConnected;
	return c_netDataConnected;
}

/*
 * EdgeTechHandler::EnablePingAllChannels() - enable (true) or disable (false)
 * 	all channels
 */

int EdgeTechHandler::EnablePingAllChannels(bool bEnable, const bool &bUpdate, const long &lTimeOutMS) {
	int i;
	int errcount = 0;

	for (i = 0; i < ET_SUBSYSTEMS; i++) {
		if (EnablePing(etsubsys[i].getId(), bEnable, bUpdate, lTimeOutMS) < 0) {
			errcount++;
		}
	}
	return (-errcount);
}

bool EdgeTechHandler::IsHealthy(const long &lTimeOutMS) {
	bool bRet = false;

	if (SendCommand(SONAR_MESSAGE_SYSTEM_VERSION, SONAR_COMMAND_GET))
		bRet = ReceiveMessage(lTimeOutMS);
	else
		bRet = false;

	//We are not healthy, disconnect
	if (!bRet) {
		fprintf(logfds, "ERROR::EdgeTechHandler::IsHealthy(), failed to receive version...\n");
		Disconnect();
		return false;
	}

	return true;
}
/*
 * EdgeTechHandler::EnableAGC() - enables AGC on subbottom system only
 *
 *  0=> disable, 1=> enable (Automatic Gain Control)
 *  (SonarMessageLongType)
 *  Subsystem and channel must be valid.  Ignored on sidescan systems.
 */
int EdgeTechHandler::EnableAGC(bool bEnable, const bool &bUpdate, const long &lTimeOutMS) {
	int errcount = 0;
	subsysId_t subsys = SUBSYSID_SB;
	long lAGCMode = bEnable ? 1 : 0;

	if (!SendCommand(SONAR_MESSAGE_ADC_AGC, SONAR_COMMAND_SET, subsys, 0, (unsigned char *) &lAGCMode, (unsigned long) sizeof(lAGCMode))) {
		errcount++;
	}

	//	Update if requested
	if (bUpdate) {
		if (SendCommand(SONAR_MESSAGE_ADC_AGC, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS)) {
				errcount++;
			}
		} else
			errcount++;
	}
	return (-errcount);
}

/*
 * EdgeTechHandler::SetRange() - sets the range in meters
 *
 * From SonarMessages.h
 *  Set the ping rate for sidescan systems.  Sets the ping rate  based
 *  on the range in millimeters. (SonarMessageLongType)
 *  Note:  This is a subsystem command, the channel number must be 0.
 */
int EdgeTechHandler::SetRange(subsysId_t subsys, const float &fRangeM, const bool &bUpdate, const long &lTimeOutMS) {
	int errcount = 0;
	long lRange = (long) fabs(floor(fRangeM * 1000 + 0.5));
	printf("fl: %f, lng: %li\n", fRangeM, lRange);

	if (subsys == SUBSYSID_NONE) {
		errcount++;
		return (-errcount);
	}
	if (!SendCommand(SONAR_MESSAGE_PING_RANGE, SONAR_COMMAND_SET, subsys, 0, (unsigned char *) &lRange, sizeof(lRange))) {
		errcount++;
	}
	if (bUpdate) {
		if (SendCommand(SONAR_MESSAGE_PING_RANGE, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS)) {
				errcount++;
			}
		} else
			errcount++;
	}
	return (-errcount);
}
/*
 * EdgeTechHandler::SetRxGain() - set receive gain, AGC must be disabled
 *
 * From SonarMessages.h:
 *	Set gain factor for ADC when AGC disabled (SonarMessageLongType)
 * 	Value is * 1000.0 (only 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
 * 	and 2048 supported)
 * 	Subsystem and channel must be valid.  Ignored on sidescan systems.
 */
int EdgeTechHandler::SetRxGain(const float& fRxGain, const bool &bUpdate, const long &lTimeOutMS) {
	int errcount = 0;
	subsysId_t subsys = SUBSYSID_SB;

	long lRxGain = (long) fabs(floor(fRxGain * 1000 + 0.05));
	printf("f: %f | l: %li\n", fRxGain, lRxGain);
	if (SendCommand(SONAR_MESSAGE_ADC_GAIN, SONAR_COMMAND_SET, subsys, 0, (unsigned char *) &lRxGain, sizeof(lRxGain)) == 0) {
		errcount++;
	}
	if (bUpdate) {
		if (!SendCommand(SONAR_MESSAGE_ADC_GAIN, SONAR_COMMAND_GET, subsys)) {
			errcount++;
			if (!ReceiveMessage(lTimeOutMS)) {
				errcount++;
			}
		}
	}
	return (-errcount);
}

/*
 *	EdgeTechHandler::SetTriggerMode()- set the triggering mode for an individual channel.
 *
 *	From SonarMessages.h:
 *	Set trigger for internal(0), external(1), coupled(2), or gated(3)
 *  (SonarMessageLongType).  Coupled mode causes a system to be triggered
 *  by another one (eg Sidescan triggered by Subbottom)
 *  See SONAR_MESSAGE_PING_COUPLING_PARAMETERS message.  In gated mode
 *  the external trigger is used as a trigger inhibit, and the inhibit
 *  time is based on the coupling parameter delay.
 *  Note:  This is a subsystem command, the channel number must be 0.
 *
 */
int EdgeTechHandler::SetTriggerMode(subsysId_t subsys, ETRIGGERMODE trigMode, const bool &bUpdate, const long &lTimeOutMS) {
	int errcount = 0;
	if (subsys == SUBSYSID_NONE) {
		errcount++;
		return (-errcount);
	}
	long lTrigMode = (long) trigMode;
	if (!SendCommand(SONAR_MESSAGE_PING_TRIGGER, SONAR_COMMAND_SET, subsys, 0, (unsigned char *) &lTrigMode, sizeof(lTrigMode))) {
		errcount++;
	}
	if (bUpdate) {
		if (SendCommand(SONAR_MESSAGE_PING_TRIGGER, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS)) {
				errcount++;
			}
		} else
			errcount++;
	}
	return (-errcount);
}

/*
 * EdgeTechHandler::SetTimeFromSystem()- set the time from the calling system
 *
 * From SonarMessages.h:
 * 	Get or set the time (TimestampType)
 * 	Note that because of the nagle algorithm on sockets, the actual
 * 	message can be delayed.  To set the time with greater accuracy, say,
 * 	within 10 ms, the nagle algorithm should be disabled by the sender.
 *  Or a subsequent message bigger than the maximum network packet size
 *  should be sent following this message (usually about 1600 bytes).
 *  Note:  This is a system command, the subsystem and channel numbers
 *  must be 0.
 *  NOTE: The SONAR_MESSAGE_NONE message can be any size desired.
 *
 */
int EdgeTechHandler::SetTimeFromSystem(const bool &bUpdate, const long &lTimeOutMS) {

	int errcount = 0;
	TimestampType tstime;
	tstime.time = (long) time(NULL);

	struct timeval tv;
	struct timezone tz;
	gettimeofday(&tv, &tz);

	tstime.milliseconds = (long) fabs(floor((tv.tv_usec / 1000) + 0.5));

	fprintf(logfds, "EdgetechHandler::SetTimeFromSystem(), Sent time is "
			"%li seconds, %li milliseconds\n", tstime.time, tstime.milliseconds);

	if (!SendCommand(SONAR_MESSAGE_SYSTEM_TIME, SONAR_COMMAND_SET, 0, 0, (unsigned char *) &tstime, sizeof(tstime))) {
		errcount++;
	}

	if (bUpdate) {
		if (SendCommand(SONAR_MESSAGE_SYSTEM_TIME, SONAR_COMMAND_GET)) {
			if (!ReceiveMessage(lTimeOutMS)) {
				errcount++;
			}
		} else
			errcount++;
	}
	return (-errcount);
}

/*
 * EdgeTechHandler::SetPingRate() - sets the ping rate through
 * pulses per second.
 *
 *
 * Message sent to FSDW is
 * Number of pings per second required * 1000
 *
 * (SonarMessageLongType)
 * Actual ping rate may be slightly lower (2048 sample granuality)
 * Note:  This is a subsystem command, the channel number must be 0.
 */
int EdgeTechHandler::SetPingRate(subsysId_t subsys, const float& fPingRatePPS, const bool& bUpdate, const long &lTimeOutMS) {

	//subsysId_t subsys = SUBSYSID_SB;
	int errcount = 0;
	long lPingRate = (long) fabs(floor(fPingRatePPS * 1000));

	// Enact the command.

	if (!SendCommand(SONAR_MESSAGE_PING_RATE, SONAR_COMMAND_SET, subsys, 0, (unsigned char *) &lPingRate, sizeof(lPingRate))) {
		errcount++;
	}

	// Query its setting.
	if (bUpdate) {
		if (SendCommand(SONAR_MESSAGE_PING_RATE)) {
			if (!ReceiveMessage(lTimeOutMS)) {
				errcount++;
			}
		} else
			errcount++;
	}

	return (-errcount);
}

/*
 * EdgeTechHandler::SetPulseDuration() - sets pulse duration and
 * then changes ping rate accordingly.
 *
 */
int EdgeTechHandler::SetPulseDuration(subsysId_t subsys, const float& rfPingDurationMS, const bool& bUpdate, const long & lTimeOutMS) {

	int errcount = 0;
	float fPingRatePPS;
	const float minduration = 10.0;

	if (fabs(rfPingDurationMS) < minduration) {
		fPingRatePPS = 1000 / (minduration);
	} else {
		fPingRatePPS = 1000 / (fabs(rfPingDurationMS));
	}
	fprintf(logfds, "Setting Pulse Duration to %f with corresponding Ping Rate of %f\n", rfPingDurationMS, fPingRatePPS);

	if (subsys == SUBSYSID_SB && fPingRatePPS >= 0) {
		errcount = this->SetPingRate(subsys, fPingRatePPS, bUpdate, lTimeOutMS);
	} else {
		errcount++;

	}

	return (-errcount);
}

bool EdgeTechHandler::isAGCEnabled(void) {
	bool bRet = (etsubsys[0].getLAdcagc() == 1) ? true : false;

	return bRet;
}

int EdgeTechHandler::TimetoMicroseconds(ulonglong* timestampus, timespec * ts) {

	timespec _ts;

	// If time is not supplied
	if (ts == NULL) {
		clock_gettime(CLOCK_REALTIME, &_ts );
		ts = &_ts;
	}

	unsigned long timesec = ts->tv_sec;
	unsigned long microsec =ts->tv_nsec/1000UL;
	unsigned long timesec1, timesec2, microsecrem;

//	timestampus->LowPart = timesec*1000000UL+microsec;

	mul64(timesec, 1000000UL, &(timestampus->HighPart), &(timestampus->LowPart));
	add32to64(microsec, &(timestampus->HighPart), &(timestampus->LowPart));

//	double time_uS = ((double)timesec)*1000000.0 + ((double)microsec);
//	printf("DVAL= %f || LOW: %lu || HIGH: %lu\n",time_uS,timestampus->LowPart,timestampus->HighPart);
	return 0;
}

int EdgeTechHandler::TimetoNanoseconds(ulonglong * timestampus, timespec * ts) {
	return 0;
}

/*
 * EdgeTechHandler::SendMessage() - transmit message on previously connected port.
 */
bool EdgeTechHandler::SendMessage(EdgeTechMessage& rMessage, EPORTTYPE port, const long &lTimeOutMS) {

	bool bSuccess = false;
	unsigned long msglen, n;
	unsigned char * msgbuf;

	msglen = rMessage.MessageSize();
	msgbuf = rMessage.Message();
	printf("SENDING %i BYTES.\n",msglen);
	if (isConnected()) {

//	Write data to the appropriate socket

		n = 0;
		if (port == PORT_CMD)
			n = send(c_cmdSockfd, msgbuf, msglen, 0);
		else if (port == PORT_DATA)
			n = send(c_dataSockfd, msgbuf, msglen, 0);

		if (n == msglen) {
			bSuccess = true;
		} else {
			fprintf(logfds, "ERROR, send failed: code %li\n", n);
			Disconnect();
		}
	}
//	Output a binary representation of the message
	fprintf(logfds, "EdgetechHandler:: wrote %li bytes to socket\n", n);
	fprintf(logfds, "SND: ");
	for (n = 0; n < msglen; n++) {
		fprintf(logfds, "%02X ", msgbuf[n]);
	}
	fprintf(logfds, "\n");

	return bSuccess;
}

/*
 * Disconnect(): Close file descriptors opened in the constructor
 */
void EdgeTechHandler::Disconnect() {

	if (isConnected()) {
		fprintf(logfds, "EdgeTechHandler::Disconnect(), severing connections...\n");
		close(c_cmdSockfd);
		close(c_dataSockfd);

		c_netCmdConnected = false;
		c_netDataConnected = false;

		// A little pause in case we want to reconnect right away
		sleep(1);
	}
	//delete(&c_cmdSockfd);
	//delete(&c_dataSockfd);

}

/*
 * GetSubsysIdxFromMessage() - retrieve's the subsystems id from a received
 *  or passed message object, then match it to the handlers memory structure and
 * 	return the array index.
 */
int EdgeTechHandler::GetSubsysIdxFromMessage(const EdgeTechMessage &rMessage) {
	int retval = -1;

	for (int i = 0; i < ET_SUBSYSTEMS; i++) {
		if (rMessage.Subsystem() == etsubsys[i].getId()) {
			retval = i;
		}
	}

	return retval;
}

/*
 * ReceiveMessage(EPORTTYPE):
 *	Poll the passed port type for a message in buffer.
 */
bool EdgeTechHandler::ReceiveMessage(const long &lTimeOutMS) {
	int inptsz = 0;
	int rv = 0, retryCnt = 0, highsock;
	struct timeval timeout;
	unsigned char replybuff[MAX_NETCMDLEN];

	EdgeTechMessage Reply;

	if (!this->isConnected()) {
		return false;
	}

//first poll that there is data
//	rv = poll(c_ufds, 2, lTimeOutMS);

//	first select on all ports
//	set timeout properly

	//determine highest socket
	highsock = (c_dataSockfd > c_cmdSockfd) ? c_dataSockfd : c_cmdSockfd;

	timeout.tv_sec = (time_t) floor((double) (lTimeOutMS / 1000));
	timeout.tv_usec = (suseconds_t)((lTimeOutMS % 1000) * 1000);
	FD_ZERO(&c_readfds);
	FD_SET(c_cmdSockfd, &c_readfds);
	FD_SET(c_dataSockfd, &c_readfds);

	while ((retryCnt < POLL_RETRY_MAX && rv <= 0)) {
		rv = select(highsock + 1, &c_readfds, NULL, NULL, &timeout);

		if (rv == -1) {
//			fprintf(logfds, "EdgeTechHandler::ReceiveMessage(): SELECT returned -1\n");
			perror("  select error.");
			return false;
		} else if (rv == 0) {
			//fprintf(logfds, "EdgeTechHandler::ReceiveMessage(): SELECT returned 0, no response from server.\n");
			retryCnt++;
//			return false;
		} else {
//			fprintf(logfds, "EdgeTechHandler::ReceiveMessage(): SELECT returned %i\n", rv);
		}

	}

	if (retryCnt == POLL_RETRY_MAX && rv <= 0) {
//		fprintf(logfds, "EdgeTechHandler::ReceiveMessage(): SELECT returned 0, no response from server.\n");
		return false;
	}

	if (FD_ISSET(c_cmdSockfd, &c_readfds)) {	//c_ufds[0].revents & POLLIN) {
		inptsz = recv(c_cmdSockfd, replybuff, MAX_NETCMDLEN, 0);
		if (inptsz < 0) {
			perror("ERROR::EdgetechHandler::ReceiveMessage(): error reading from command socket");
			fprintf(logfds, "ERROR::EdgetechHandler::ReceiveMessage(): error reading from command socket.\n");
			Disconnect();

			return false;
		} else if (inptsz > 0) {

//		Recast the message into a header, and thus move the working pointer
//		inside the EdgetechMessage class.
			SonarMessageHeaderType *pHeader = (SonarMessageHeaderType *) (replybuff);
			if (!Reply.SetHeader(pHeader)) {
				perror("Can't set header");
				return false;
			}

//			Identify port
			fprintf(logfds, "NetCmnd::%i::", inptsz);
//		Process the command, in most cases send it to the handler for sonar commands.
			switch (Reply.SonarCommand()) {
			case SONAR_COMMAND_ERROR:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_ERROR\n");
				HandleSonarError(Reply);
				break;
			case SONAR_COMMAND_GET:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_GET\n");
			case SONAR_COMMAND_SET:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_SET\n");
			case SONAR_COMMAND_PLAYBACK:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_PLAYBACK\n");
			case SONAR_COMMAND_REPLY:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_REPLY\n");

			default:
				HandleSonarCommand(Reply);
				break;
			}
//		Print a binary representation of the input message
//		printf("Received %i bytes: %sEOL\nRCV: ", inptsz, replybuff);
//		for ( int i = 0; i < inptsz; i++)
//			printf("%02X ", replybuff[i]);
//		printf("\n");
		} else if (inptsz == 0) {	//the peer has closed the connection
			fprintf(logfds, "ERROR::EdgetechHandler::ReceiveMessage(): Peer has closed the command connection.\n");
			Disconnect();
			return false;
		}

	}

	if (FD_ISSET(c_dataSockfd, &c_readfds)) { //c_ufds[1].revents & POLLIN) {
		inptsz = recv(c_dataSockfd, replybuff, MAX_NETCMDLEN, 0);
//	We were unable to read data
		if (inptsz < 0) {
			perror("ERROR::EdgetechHandler::ReceiveMessage(): error reading from data socket");
			fprintf(logfds, "ERROR::EdgetechHandler::ReceiveMessage(): error reading from data socket.\n");
			Disconnect();
			return false;
		} else if (inptsz > 0) {
			//		Recast the message into a header, and thus move the working pointer
			//		inside the EdgetechMessage class.
			SonarMessageHeaderType *pHeader = (SonarMessageHeaderType *) (replybuff);
			if (!Reply.SetHeader(pHeader)) {
				perror("ERROR Setting data header.");
				return false;
			}
//			Identify port
			fprintf(logfds, "NetData::%i::", inptsz);
//			Process the command, in most cases send it to the handler for sonar commands.
			switch (Reply.SonarCommand()) {
			case SONAR_COMMAND_ERROR:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_ERROR\n");
				HandleSonarError(Reply);
				break;
			case SONAR_COMMAND_GET:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_GET\n");
			case SONAR_COMMAND_SET:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_SET\n");
			case SONAR_COMMAND_PLAYBACK:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_PLAYBACK\n");
			case SONAR_COMMAND_REPLY:
//			fprintf(logfds,
//					"EdgeTechHandler::ReceiveMessage(): SONAR_COMMAND_REPLY\n");

			default:
				HandleSonarCommand(Reply);
				break;
			}

			//		Print a binary representation of the input message
			//		printf("Received %i bytes: %sEOL\n", inptsz, replybuff);
			//		for (i = 0; i < inptsz; i++)
			//			printf("%02X ", replybuff[i]);
			//		printf("\n");
		} else if (inptsz == 0) {	//the peer has closed the connection
			fprintf(logfds, "ERROR::EdgetechHandler::ReceiveMessage(): Peer has closed the data connection.\n");
			Disconnect();
			return false;
		}
	}
//	else if (c_ufds[1].revents & POLLERR) {
//		perror("ERROR Polling Socket");
//	}

	return true;

}

/*
 * EdgeTechHandler::QueryPulseFileListQuery() - send messages to query the sonar
 * 	for available pulsefiles.
 */
int EdgeTechHandler::QueryPulseFileListQuery(subsysId_t subsys, const long &lTimeOutMS) {
// Reset to the top of the list.

	int errcount = 0;
	if (SendCommand(SONAR_MESSAGE_PING_LIST, SONAR_COMMAND_SET)) {
		if (!ReceiveMessage(lTimeOutMS))
			errcount++;
	} else
		errcount++;

// Solicit the entire pulse list.

	const long lNumberToGet = (long) (m_ulMaxPulseFiles);

	if (SendCommand(SONAR_MESSAGE_PING_LIST, SONAR_COMMAND_GET, subsys, 0, (unsigned char *) &lNumberToGet, sizeof(lNumberToGet))) {
		if (!ReceiveMessage(lTimeOutMS))
			errcount++;
	} else
		errcount++;

	return (-errcount);
}

/*
 * EdgeTechHandler::StorageEnable() - Enables recording to storage if 1, disables on 0.
 * TODO: does not work, investigating with Edgetech.
 */
int EdgeTechHandler::EnableStorage(bool bEnable, const bool &bUpdate, const long &lTimeOutMS) {

	int errcount = 0;
	long lStorageMode = bEnable ? 1 : 0;
	fprintf(logfds, "sizeof: %i\n", sizeof(lStorageMode));

	if (!SendCommand(STORAGE_MESSAGE_MASTER_RECORD, SONAR_COMMAND_SET, 0, 0, (unsigned char *) &lStorageMode, sizeof(lStorageMode)))
		errcount++;

	if (bUpdate) {
		if (SendCommand(STORAGE_MESSAGE_MASTER_RECORD, SONAR_COMMAND_GET)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;
	}
	return (-errcount);
}

/*
 * EdgeTechHandler::printStatus() - print class/subclass variables to passed file descriptor
 */
void EdgeTechHandler::printStatus(FILE* fid) {

	fprintf(fid, "-----------------------------------------\n");
	fprintf(fid, "Edgetech FSDW Status:\n");
	fprintf(fid, "|\t  VERSION:          \t %s\n", this->c_cSystemVersion);
	fprintf(fid, "|\tDISK LOGGING:       \t \n");
	fprintf(fid, "|\t  MASTER RECORD     \t %s\n", (this->c_bMasterRecordEnabled) ? "true" : "false");
	fprintf(fid, "|\t  STORAGE DATA:     \t %s\n", (this->c_bStorageMessageDataEnabled) ? "true" : "false");
	fprintf(fid, "|\t  ENABLED:          \t %s\n", (this->c_bDiskLoggingEnabled) ? "true" : "false");
	fprintf(fid, "|\t  LOGGING TO:       \t %s\n", this->c_cCurrentLogFileName);
	fprintf(fid, "|\t  FILESIZE:         \t %liK\n", this->c_lCurrentLogFileSize);
	fprintf(fid, "|\t  FREE SPACE:       \t %liK\n", this->c_lDiskLogFreeSpace);
	fprintf(fid, "|\tNETWORK::           \t \n");
	fprintf(fid, "|\t  CONNECTED:        \t %s\n", (this->c_netCmdConnected) ? "true" : "false");
	fprintf(fid, "|\t  SERVER ADDR:      \t %s\n", this->c_sockAddress);
	fprintf(fid, "|\t  SERVER CMD PORT:      \t %i\n", this->c_cmdSockPort);
	fprintf(fid, "|\t  SERVER DATA PORT:      \t %i\n", this->c_dataSockPort);

	fprintf(fid, "-----------------------------------------\n");

	for (int i = 0; i < ET_SUBSYSTEMS; i++) {
		etsubsys[i].printStatus(logfds);
	}

}

/*
 * EdgeTechHandler::QueryCommonSettings() - pool sonar servers for status and values.
 */
int EdgeTechHandler::QueryCommonSettings(const long &lTimeOutMS) {
	int i;
	int errcount = 0;
	subsysId_t subsys;

//test if we have comms by obtaining version name
	if (!IsHealthy())
		return (-1);

//TEST Ping Enable
	for (i = 0; i < ET_SUBSYSTEMS; i++) {

		subsys = etsubsys[i].getId();

		//for some reason this is not a channel
		if (subsys == SUBSYSID_NONE) {
			errcount++;
			continue;
		}

		//Get Ping Status
		if (SendCommand(SONAR_MESSAGE_PING, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve Ping Gain
		if (SendCommand(SONAR_MESSAGE_PING_GAIN, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve Ping Rate
		if (SendCommand(SONAR_MESSAGE_PING_RATE, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve Trigger Mode
		if (SendCommand(SONAR_MESSAGE_PING_TRIGGER, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

//		No longer supported.
//		//Retrieve External Trigger Delay
//		SendCommand(SONAR_MESSAGE_PING_DELAY, SONAR_COMMAND_GET, id);
//		ReceiveMessage(lTimeOutMS);

		//Retrieve Max Samples
		if (SendCommand(SONAR_MESSAGE_PING_MAX_SAMPLES, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve Range in mm
		if (SendCommand(SONAR_MESSAGE_PING_RANGE, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve ADC Gain
		if (SendCommand(SONAR_MESSAGE_ADC_GAIN, SONAR_COMMAND_GET, subsys)) {
			errcount++;
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve ADC AGC
		if (SendCommand(SONAR_MESSAGE_ADC_AGC, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve ADC Rate
		if (SendCommand(SONAR_MESSAGE_ADC_RATE, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve Pulse file name
		if (SendCommand(SONAR_MESSAGE_PING_SELECT, SONAR_COMMAND_GET, subsys)) {
			if (!ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;

		//Retrieve Pulse File List
		errcount -= QueryPulseFileListQuery(subsys); // this returns a negative value of errors

		//Now modify channel amounts based on sonar type.
		if (etsubsys[i].isSBS()) {
			etsubsys[i].setIDataChannels(1L);
		} else if (etsubsys[i].isSSS()) {
			etsubsys[i].setIDataChannels(2L);
		}

	}

//	Example of querying logging status
	if (SendCommand(STORAGE_MESSAGE_LOGGING_STATUS)) {
		if (!ReceiveMessage(lTimeOutMS))
			errcount++;
	} else
		errcount++;

//Retrieve On Disk Recording Enable
	if (SendCommand(STORAGE_MESSAGE_MASTER_RECORD)) {
		if (!ReceiveMessage(lTimeOutMS))
			errcount++;
	} else
		errcount++;

//Retrieve Message Data Status
	if (SendCommand(STORAGE_MESSAGE_DATA_ENABLE)) {
		if (!ReceiveMessage(lTimeOutMS))
			errcount++;
	} else
		errcount++;

	return (-errcount);
//Get Version Name of Server

}

/*
 * EdgeTechHandler::SetPressureData() - assemble a structure and reformat common
 * 	mesasurements for onboard storage. Inputs are as follows
 * 		pressurePSI: pressure in decimal PSI
 * 		tempC: temperature in decimal degrees Celsius
 * 		salPSU: salinity in decimal PSU
 * 		condSM: conductivity in saliens per meter
 * 		sosMS:  sound of speed in meters per second
 * 		depthM: depth in decimal meters
 * 		flags:  reserved for future implementation (see devicemessages.h)
 */
int EdgeTechHandler::SendPressureData(float const &pressurePSI, float const &tempC, float const &salPSU, float const &condSM, float const &sosMS, float const &depthM, long const &flags, const long &lTimeOutMS) {

	int errcount = 0;
// Pack the input data into the structure
	DEVPressureType psPressure;

	/* Pressure in PSI * 1000                                               */
	psPressure.pressure = (long) fabs(floor(pressurePSI * 1000));

	/* Temperature in Degrees C * 1000.                                     */
	psPressure.temperature = (long) floor(tempC * 1000);

	/* Salinity in Parts Per Million.                                       */
	/* or PSU * 1000                                                        */
	psPressure.saltPPM = (long) fabs(floor(salPSU * 1000));

	/* Data valid flags:                                                    */
	/* Bit 0: pressure                                                      */
	/* Bit 1: temp                                                          */
	/* Bit 2: saltPPM                                                       */
	/* Bit 3: conductivity                                                  */
	/* Bit 4: sound velocity                                                */
	/* Bit 5: depth                                                         */
	psPressure.flags = (long) flags;

	/* Conductivity in micro-Siemens per cm                                 */
	psPressure.conductivity = (long) fabs(floor(condSM * 10000));

	/* Velocity of Sound in mm per second                                   */
	psPressure.soundVelocity = (long) fabs(floor(sosMS * 1000));

	/* depth in millimeters                                                 */
	psPressure.depth = (long) floor(depthM * 1000);

//Remember this special message gets packed inside a container before transmission.
	if (SendCommand(DEV_PRESSURE_DATA, SONAR_COMMAND_SET, 0, 0, (unsigned char *) &psPressure, sizeof(psPressure)) == 0) {
		errcount++;
	}

	printf("sizeof pspressure: %i\n",sizeof(psPressure));
	return (-errcount);
}

/*
 *  EdgeTechHandler::SetCompleteSituation() - assemble a structure pack into
 *   message for onboard storage, Inputs are as follows
 *   	flags: ValidityFlags indicated which of the following fields are
 *	  			valid.
 *	 	vDirections: Velocity1 and 2 type.  0 => North and east, 1 => Foward and
 *	        stbd, 2 => +45 degrees rotated from forward.
 *	 	timestamp: 1/10 of a ns unit timestamp, us since 12:00:00 am GST,
 *	 				January 1, 1970.  To get seconds since 1970 divide by 1e7.
 *		latitude:  in degrees, north is positive
 *		longitude: in degrees, east is positive
 *	 	depth: below water in meters.
 *	 	altitude: in meters (above sea floor).
 *	 	heave: in meters - positive is down
 *	 	velocity1: North velocity (or forward) in meters per second (see vDirections)
 *	 	velocity2: East velocity or stbd in meters per second (see vDirections)
 *	 	velocityDown: Down velocity in meters per second
 *	 	pitch: in degrees, bow up positive
 *	 	roll: in degrees, port up positive
 *	 	heading: in degrees (0-360)
 *	 	soundSpeedMS: Sound speed in meters / second
 *	 	waterTempC: Water temperature in degrees C
 */

int EdgeTechHandler::SendCompositeSituation(const long & flags,
		const unsigned char& vDirections, const ulonglong & timestampNS, const double& latitudeD, const double& longitudeD, const float& depthM, const float& altitudeM, const float& heaveM, const float& velocity1MS, const float& velocity2MS,
		const float& velocityDownMS, const float& pitchD, const float& rollD, const float& headingD, const float& soundSpeedMS, const float& waterTempC, const long &lTimeOutMS) {

	int errcount = 0;
//Pack the data into the structure
	DEVCompositeSituationType psSitRep;

	/* 00-03: ValidityFlags indicated which of the following fields are     */
	/*  valid.                                                              */
	/*  Bit 0 : Timestamp provided by the source valid.                     */
	/*  Bit 1 : Longitude valid.                                            */
	/*  Bit 2 : Latitude valid.                                             */
	/*  Bit 3 : Depth valid.                                                */
	/*  Bit 4 : Altitude valid.                                             */
	/*  Bit 5 : Heave valid.                                                */
	/*  Bit 6 : Velocity 1 & 2 valid.                                       */
	/*  Bit 7 : Velocity down valid.                                        */
	/*  Bit 8 : Pitch valid.                                                */
	/*  Bit 9 : Roll valid.                                                 */
	/*  Bit 10: Heading valid.                                              */
	/*  Bit 11: Sound speed valid.                                          */
	/*  Bit 12: Water temperature valid.                                    */
	/*  Others: Reserved, presently 0.                                      */
	psSitRep.validityFlags = flags;

	/* 04   : Velocity1 and 2 type.  0 => North and east, 1 => Foward and   */
	/*        stbd, 2 => +45 degrees rotated from forward.                  */
	psSitRep.velocity12Directions = vDirections;

	/* 08-15: 1/10 of a ns unit timestamp, us since 12:00:00 am GST,        */
	/* January 1, 1970.  To get seconds since 1970 divide by 1e7.           */
//	if (timestampNS <= 0)
//		psSitRep.timestamp = time(NULL) * (10 ^ 7);
//	else
//		psSitRep.timestamp = timestampNS;

	/* 16-23: latitude in degrees, north is positive                        */
	psSitRep.latitude = latitudeD;

	/* 24-31: longitude in degrees, east is positive                        */
	psSitRep.longitude = longitudeD;

	/* 32-35: Depth below water in meters.                                  */
	psSitRep.depth = depthM;

	/* 36-39: Altitude in meters (above sea floor).                         */
	psSitRep.altitude = altitudeM;

	/* 40-43: heave in meters - positive is down                            */
	psSitRep.heave = heaveM;

	/* 44-47: North velocity (or forward) in meters per second              */
	psSitRep.velocity1 = velocity1MS;

	/* 48-51: East velocity or stbd in meters per second                    */
	psSitRep.velocity2 = velocity2MS;

	/* 52-55: Down velocity in meters per second                            */
	psSitRep.velocityDown = velocityDownMS;

	/* 56-59: Pitch in degrees, bow up positive                             */
	psSitRep.pitch = pitchD;

	/* 60-63: Roll in degrees, port up positive                             */
	psSitRep.roll = rollD;

	/* 64-67: Heading in degrees (0-360)                                    */
	psSitRep.heading = headingD;

	/* 68-71: Sound speed in meters / second                                */
	psSitRep.soundSpeed = soundSpeedMS;

	/* 72-75: Water temperature in degrees C                                */
	psSitRep.waterTemperature = waterTempC;

//Remember this special message gets packed inside a container before transmission.
	if (!SendCommand(DEV_SITUATION_COMPREHENSIVE2, SONAR_COMMAND_SET, 0, 0, (unsigned char *) &psSitRep, sizeof(psSitRep))) {
		errcount++;
	}

	return (-errcount);
}
/*
 * EdgeTechHandler::SendSituation() - send a long of comprehensive state data
 *
 */

int EdgeTechHandler::SendSituation(const long & flags, const ulonglong & timestampUS, const double& latitudeD, const double& longitudeD, const double& depthM, const double& headingD, const double& pitchD, const double& rollD, const double& xRelativePosM, const double& yRelativePosM,
		const double& zRelativePosM, const double& xVelocityMS, const double& yVelocityMS, const double& zVelocityMS, const double& northVelocityMS, const double& eastVelocityMS, const double& downVelocityMS, const double& xAngularRateDS, const double& yAngularRateDS, const double& zAngularRateDS,
		const double& xAccelerationMS, const double& yAccelerationMS, const double& zAccelerationMS, const double& latitudeStandardDeviationM, const double& longitudeStandardDeviationM, const double& depthStandardDeviationM, const double& headingStandardDeviationD,
		const double& pitchStandardDeviationD, const double& rollStandardDeviationD, const long &lTimeOutMS) {

	int errcount = 0;

//Pack the data into the structure
	DEVSituationType psSitRep;
	printf("sizeof sitrep: %i\n", sizeof(psSitRep));

	psSitRep.validityFlags = 0xFFFF;//flags;

	/* microsecond timestamp, us since 12:00:00 am GST, January 1, 1970     */
//	if (timestampUS <= 0)
//		psSitRep.microsecondTimestamp.HighPart = 0xFFFFFFFF; //time(NULL) * (10 ^ 6);
//		psSitRep.microsecondTimestamp.LowPart  = 0xFFFFFFFF;
	TimetoMicroseconds(&psSitRep.microsecondTimestamp, NULL);
//	else
//		psSitRep.microsecondTimestamp = 32769; //timestampUS;
	/* latitude in degrees, north is positive                               */
	psSitRep.latitude = 180.0;//latitudeD;

	/* longitude in degrees, east is positive                               */
	psSitRep.longitude = 180.0;//longitudeD;

	/* depth in meters                                                      */
	psSitRep.depth = depthM;

	/* heading in degrees                                                   */
	psSitRep.heading = headingD;

	/* pitch in degrees, bow up is positive                                 */
	psSitRep.pitch = pitchD;

	/* roll in degrees, port up is positive                                 */
	psSitRep.roll = rollD;

	/* X, forward, relative position in meters, surge                       */
	psSitRep.XRelativePosition = xRelativePosM;

	/* Y, starboard, relative position in meters, sway                      */
	psSitRep.YRelativePosition = yRelativePosM;

	/* Z, downward, relative position in meters, heave                      */
	psSitRep.ZRelativePosition = zRelativePosM;

	/* X, forward, velocity in meters per second                            */
	psSitRep.XVelocity = xVelocityMS;

	/* Y, starboard, velocity in meters per second                          */
	psSitRep.YVelocity = yVelocityMS;

	/* Z, downward, velocity in meters per second                           */
	psSitRep.ZVelocity = zVelocityMS;

	/* North velocity in meters per second                                  */
	psSitRep.NorthVelocity = northVelocityMS;

	/* East velocity in meters per second                                   */
	psSitRep.EastVelocity = eastVelocityMS;

	/* down velocity in meters per second                                   */
	psSitRep.downVelocity = downVelocityMS;

	/* X angular rate in degrees per second, port up is positive            */
	psSitRep.XAngularRate = xAngularRateDS;

	/* Y angular rate in degrees per second, bow up is positive             */
	psSitRep.YAngularRate = yAngularRateDS;

	/* Z angular rate in degrees per second, starboard is positive          */
	psSitRep.ZAngularRate = zAngularRateDS;

	/* X, forward, acceleration in meters per second per second             */
	psSitRep.XAcceleration = xAccelerationMS;

	/* Y, starboard, acceleration in meters per second per second           */
	psSitRep.YAcceleration = yAccelerationMS;

	/* Z, downward, acceleration in meters per second per second            */
	psSitRep.ZAcceleration = zAccelerationMS;

	/* latitude standard deviation in meters                                */
	psSitRep.latitudeStandardDeviation = latitudeStandardDeviationM;

	/* longitude standard deviation in meters                               */
	psSitRep.longitudeStandardDeviation = longitudeStandardDeviationM;

	/* depth standard deviation in meters                                   */
	psSitRep.depthStandardDeviation = depthStandardDeviationM;

	/* heading standard deviation in degrees                                */
	psSitRep.headingStandardDeviation = headingStandardDeviationD;

	/* pitch standard deviation in degrees                                  */
	psSitRep.pitchStandardDeviation = pitchStandardDeviationD;

	/* roll standard deviation in degrees                                   */
	psSitRep.rollStandardDeviation = rollStandardDeviationD;

//Remember this special message gets packed inside a container before transmission.
	if (!SendCommand(DEV_SITUATION_COMPREHENSIVE, SONAR_COMMAND_SET, 0, 0, (unsigned char *) &psSitRep, sizeof(psSitRep))) {
		errcount++;
	}

	return (-errcount);
}

/*
 * EdgeTechHandler::SendAltitudeData - send altitude data
 * 	inputs are as follows:
 * 		flags: validity flags, see DeviceMessages.h
 * 		altitudeM: in decimal meters
 * 		forwardVelocityMS
 * 		crossTrackVelocityMS
 *
 */
int EdgeTechHandler::SendAltitudeData(const long & flags, const float & altitudeM, const float & forwardVelocityMS, const float & crossTrackVelocityMS, const long &lTimeOutMS) {

	int errcount = 0;
	DEVAltitudeType psAlt;

	/* Indicates which values are present.                                  */
	/* Bit 0 : Altitude present                                             */
	/* Bit 1 : Forward Velocity present.                                    */
	/* Bit 2 : Across Track Velocity present.                               */
	/* Rest  : Reserved, presently 0.                                       */
	psAlt.flags = flags;

	/* Altitude in mm (or -1 if no valid reading - bottom detect failed)    */
	psAlt.altitude = (long) floor(altitudeM * 1000);

	/* Forward velocity in mm per second.                                   */
	psAlt.forwardVelocity = (long) floor(forwardVelocityMS * 1000);

	/* Cross track velocity in mm per second.                               */
	psAlt.crossTrackVelocity = (long) floor(crossTrackVelocityMS * 1000);

//Remember this special message gets packed inside a container before transmission.
	if (!SendCommand(DEV_ALTITUDE_DATA, SONAR_COMMAND_SET, 0, 0, (unsigned char *) &psAlt, sizeof(psAlt))) {
		errcount++;
	}
	return (-errcount);
}

/*
 * EdgeTechHandler::SetPulseFile() - set the active pulse for a given subsystem
 * 		subsys: id of subsystem
 * 		sPulseFileName: string pointer for filename
 * 		len: length of string
 */
int EdgeTechHandler::SetPulseFile(subsysId_t subsys, const char * sPulseFileName, int len, const bool &bUpdate, const long &lTimeOutMS) {

	int errcount = 0;
	SonarMessageStringType sMessage;

	if (len > 0) {
		strncpy(&sMessage.name[0], sPulseFileName, len);

		sMessage.name[len] = '\0';

		if (!SendCommand(SONAR_MESSAGE_PING_SELECT, SONAR_COMMAND_SET, subsys, 0, (unsigned char *) &(sMessage.name[0]), sizeof(sMessage))) {
			errcount++;
		}

		fprintf(logfds, "EdgeTechHandler::SetPulseFile(): Selected pulse file is : %s\n", sMessage.name);
	}
	if (bUpdate) {
		if (SendCommand(SONAR_MESSAGE_PING_SELECT, SONAR_COMMAND_GET, subsys)) {
			if (ReceiveMessage(lTimeOutMS))
				errcount++;
		} else
			errcount++;
	}
	return (-errcount);
}

long minimum(long a, long b) {
	if (a <= b)
		return a;
	else
		return b;

}

static void add32to64(unsigned long a, unsigned long *prh, unsigned long *prl)
{
*prl+=a;
if (*prl<a)
	*prh++;
}



static void mul64(unsigned long a, unsigned long b, unsigned long *prh, unsigned long *prl)
{
unsigned long ah = a >> 16, al = a & 0xFFFFU;
unsigned long bh = b >> 16, bl = b & 0xFFFFU;
unsigned long rl = al * bl;
unsigned long rm1 = ah * bl;
unsigned long rm2 = al * bh;
unsigned long rh = ah * bh;
unsigned long rm1h = rm1 >> 16, rm1l = rm1 & 0xFFFFU;
unsigned long rm2h = rm2 >> 16, rm2l = rm2 & 0xFFFFU;
unsigned long rml = rm1l + rm2l;
unsigned long rmh = rm1h + rm2h;

rl += rml << 16;
if (rml & 0xFFFF0000U)
rmh++;
rh += rmh;

*prl = rl;
*prh = rh;
}

/* Example Function Calls
 */

//  TODO // Example of setting file name for disk write
//	SonarMessageStringType sMessage;
//	const char * sPulseFileName = "auv1";
//	int len = strlen(sPulseFileName);
//	if (len > 0) {
//		strncpy(&sMessage.name[0], sPulseFileName, len);
//
//		sMessage.name[len] = '\0';
//
//		SendCommand(STORAGE_MESSAGE_FILE_NAME, SONAR_COMMAND_SET, 0, 0,
//				(unsigned char *) &(sMessage.name[0]), sizeof(sMessage));
//
//		fprintf(logfds,
//				"EdgeTechHandler::SetPulseFile(): Selected pulse file is : %s\n",
//				sMessage.name);
//	}
// TODO	// Example of setting tx power
//	SetTxPower(etsubsys[1].getId(), etsubsys[1].getIDataChannels(), 85);
//	//Retrieve Ping Gain
//	SendCommand(SONAR_MESSAGE_PING_GAIN, SONAR_COMMAND_GET,
//			etsubsys[1].getId());
//	ReceiveMessage(lTimeOutMS);
//
// TODO	// Example of Setting Pulse file
//	char * psname = etsubsys[2].m_asPulseFile[4].fileName;
//
//	SetPulseFile(etsubsys[2].getId(), psname, strlen(psname));
//	//Retrieve Pulsefile name
//	SendCommand(SONAR_MESSAGE_PING_SELECT, SONAR_COMMAND_GET,
//			etsubsys[2].getId());
//	ReceiveMessage(lTimeOutMS);
// TODO // Example of Ping Enable
//	PingEnable(etsubsys[0].getId(), true);
// TODO // Example of Enabling Logging
//StorageEnable(true);
//	printStatus(logfds);
//	//TODO // Example of Sending a Pressure Message
//	SetPressureData(6000, 10.1, 5.01,12.1, 1485.5, 101.5, 0 );
/*
 * Old functions deemed not necessary
 *
 */

//bool EdgeTechHandler::Shutdown(void) {
//
//	//TODO I don't think we need this function double check.
//	bool bSuccess = false;
//
//	return bSuccess;
//}
/*
 void EdgeTechHandler::ShutdownSonar(const ESHUTDOWNTYPE& reShutdownType) {
 if (SoftwareVersion() >= m_fVersionSupportingOSShutdown) {
 long lShutdown = static_cast<long>(reShutdownType);

 SendCommand(SONAR_MESSAGE_SYSTEM_SHUTDOWN, SONAR_COMMAND_SET,
 (unsigned char *) &lShutdown, sizeof(lShutdown), 0, 0);
 }
 }
 */

//TODO Not required for first rev
//long EdgeTechHandler::IdOfSubsystemType(const ESUBSYSTEMTYPE& reSubsystemType) {
//	return -1;
//}
//bool EdgeTechHandler::IsSBP(void) const {
////	ESUBSYSTEMTYPE eType = m_eSubsystemType;
////	return (eType == subsystemSBP);
//}
///////////////////////////////////////////////////////////////////////////////
// CSubsystem::STATUS class internal helpers.
/*
 inline EdgeTechHandler::tagSTATUS::tagSTATUS(void) {
 Reset();
 }

 EdgeTechHandler::tagSTATUS::~tagSTATUS(void) {
 }

 inline
 void EdgeTechHandler::tagSTATUS::Reset(void) {
 ::memset(this, 0x00, Size());
 }

 inline size_t EdgeTechHandler::tagSTATUS::Size(void) {
 return sizeof(STATUS);
 }
 */

//bool EdgeTechHandler::IsHealthy(void) const {
//	//TODO Not actually implemented in 6046 code, likely to remove.
//	return false;
//}
///*
// * SetRxGain(int, bool)
// *
// */
//void EdgeTechHandler::SetRxGain(int iRxGain, const bool& rbUpdateConfig) {
//	// AGC and Rx gain are un-supported for SSS.
//
//	//if (!IsSSS()) {
//	//long lRxGain = 1000L * static_cast<long>(iRxGain);
//
//	//SendCommand(SONAR_MESSAGE_ADC_GAIN, SONAR_COMMAND_SET,
//	//		(unsigned char *) &lRxGain, sizeof(lRxGain));
//
//	// Query its setting.
//
//	//SendCommand(SONAR_MESSAGE_ADC_GAIN);
//
//	if (rbUpdateConfig) {
//		//m_Config.m_iRxGain = iRxGain;
//	}
//	//}
//}
//void EdgeTechHandler::EnablePingDataTxOnSonarChannel(const bool& rbEnable) {
//// Enables or disables the ping data transmission on the data port.
//
//// Note: data format is hard coded in the sonar's "sonar.ini" file and is not programmable.
//// The following example has been extracted from this file:
////
//// TelemetryFormat=1
//// ;   Reporting format for data.  Choose from:
//// ;   0: Segy
//// ;   1: SideScan
//// ;   2: Private - Compressed for Telemetry Bandwidth Reduction
//
////long lEnable = (long) rbEnable;
//
////	assert( m_iDataChannels > 0);
////
////	for (int iChannel = 0; iChannel < m_iDataChannels; iChannel++) {
////		SendCommand(SONAR_MESSAGE_DATA_ACTIVE, SONAR_COMMAND_SET,
////				(unsigned char *) &lEnable, sizeof(lEnable),
////				(unsigned char) iChannel);
////	}
//}
//bool EdgeTechHandler::HandleSonarData(const EdgeTechMessage& rMessage,
//		const unsigned long & rulTimeStamp) {
//// Sonar data receive handler: the sonar sends data on a per channel basis for each subsystem
//// so we accumulate an entire ping and notify our calling host when it's ready.
////
//// NB: this routine is called in the context of a thread running within the sonar channel handler
//// so care should be taken to access this subsystem's state in a thread safe manner.
//
//	bool bSuccess = false;
////TODO implement data handling at a later date.
//	/*
//	 const unsigned short unMessage = rMessage.SonarMessage();
//
//	 //  Total message size is:  size of SonarMessageHeaderType (16 bytes) +
//	 //                          size of ping header +
//	 //                          size of ping data.
//
//	 //TRACE( _T(  "HandleSonarData(), %d, %d, %lu, %lu\n" ),  m_iSubsystemId, unMessageType, rMessage.MessageSize(), rulTimeStamp );
//
//	 switch (unMessage) {
//	 case SONAR_MESSAGE_DATA:            // SBP data (SegyDataType)
//
//	 // Note: here we allow all SEG-Y data to pass since all collected using JStar and replayed will
//	 // be in SEG-Y format.
//
//	 if (IsSBP()) {
//	 bSuccess = HandleSubbottomChannel(rulTimeStamp,
//	 (SegyDataType *) rMessage);
//	 } else {
//	 bSuccess = true;
//	 }
//
//	 break;
//
//	 case SONAR_MESSAGE_DATA_SIDESCAN: // Side scan sonar data (SidescanHeaderType)
//
//	 // Ensure the subsystem is indeed a side scan for this data type.
//
//	 if (IsSSS()) {
//	 bSuccess = HandleSidescanChannel(rulTimeStamp,
//	 (SidescanHeaderType *) rMessage);
//	 } else {
//	 fprintf(logfds,"CSubsystem::HandleSonarData(), Non sidescan subsystem recieved a sidescan sonar data packet ... !!!\n"));
//	 }
//
//	 break;
//
//	 default:  // Unexpected or uninteresting message type on sonar data channel.
//
//	 bSuccess = true;
//	 fprintf(logfds,"CSubsystem::HandleSonarData(), Unhandled message recieved (%d)\n",	unMessage);
//	 break;
//	 }
//	 */
//	return bSuccess;
//}
///*
// * EdgeTechHandler::SetPingDuration() - tranmits a message to set duration of the ping
// */
//void EdgeTechHandler::SetPingDuration(subsysId_t subsys,
//		const float& rfPingDuration) {
//}
//
///*
// * EdgeTechHandler::EnableAGC() - enable (true) or disable (false) on given subsys
// */
//void EdgeTechHandler::EnableAGC(subsysId_t subsys, bool bEnable) {
//}
//
///*
// * EdgeTechHandler::SetRange() - set range of given subsys
// */
//void EdgeTechHandler::SetRange(subsysId_t subsys, float fRange) {
//}
//
///*
// *
// */
//void EdgeTechHandler::SetTriggerMode(subsysId_t subsys,
//		const long & rlTriggerMode) {
//}
//
//void EdgeTechHandler::SetTriggerModeProperties(const long & rlTriggerMode,
//		long lDelayInMicroseconds, long lCoupledSubsystemIndex,
//		long lEventsToTriggerOn) {
//}
