// Module Name: DeltaT_External_Control_TCP.cpp
//
// Date: 08APR11
//
// Description:
//
//    This sample program illustrates how to develop a simple TCP client
//		application that controls the Imagenex DeltaT.exe Multibeam beamforming
//		sonar program running on a seperate computer. This external control
//		scheme allows the user to control sonar range, gain, mode and many
//		other features. When the sonar program receives the command, it will
//		interrogate the addressed DeltaT sonar head and return data via the
//		83P or 83B formats. The DeltaT.exe program will only return data when
//		requested. This sample is implemented as a console-style application
//		and simply prints status messages when data is received.
//
// Command Line:
//
//    DeltaT_External_Control_TCP.exe
//
//		08APR11 - add Profile Point Filter (Byte 34)
//
//		NOTE: You must have a specially compiled version of DeltaT.exe in order
//				to use the TCP external control function. If you would like this
//				special version, please contact us:
//
//				Imagenex Technology Corp.
//				#209-1875 Broadway Street
//				Port Coquitlam, BC, V3C 4Z1
//				CANADA
//				Tel: 604-944-8248
//				Fax: 604-944-8249
//				email: imagenex@shaw.com
//
//				To enable external control, the following entry must be present in
//				the [Settings] section of the DeltaT.INI configuration file:
//
//				"ExternalControlEnableTCP=1"     to enable external control
//				"ExternalControlEnableTCP=0"     to disable external control
//

#include <winsock2.h>
#include <stdio.h>
#include <conio.h>
#include <winbase.h>

#define IPADDR "127.0.0.1" /*"192.168.0.100"*/	//IP Address of computer running DeltaT.exe

void SetExternalControlSwitches(void);

int Port = 4040;

unsigned char SendBuf[256];
unsigned char RecvBuf[65536];

int main(void)
{
	register int			i;
   WSADATA              wsaData;
   SOCKET               s;
   SOCKADDR_IN          ServerAddr;
   int                  Ret;
   int						nBytes;
   int						nLeft,index,ping;
   BOOL						connected = FALSE;

	while(!connected && !kbhit()) {
   	//Initialize Winsock version 2.2
	   if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
   	{
      	//NOTE: Since Winsock failed to load we cannot use WSAGetLastError
	      //to determine the error code as is normally done when a Winsock
   	   //API fails. We have to report the return status of the function.
      	printf("WSAStartup failed with error %d\n", Ret);
	   }

   	//Create a new socket to make a client connection.
	   if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))
   	    == INVALID_SOCKET)
	   {
   	   printf("Socket failed with error %d\n", WSAGetLastError());
      	WSACleanup();
	   }

   	//Setup a SOCKADDR_IN structure that will be used to connect
	   //to a listening server on the specified port.
   	ServerAddr.sin_family = AF_INET;
	   ServerAddr.sin_port = htons(Port);
   	ServerAddr.sin_addr.s_addr = inet_addr(IPADDR);

	   //Make a connection to the server with socket s.
   	printf("Connecting to %s (Port %d)... ",
      	    inet_ntoa(ServerAddr.sin_addr), htons(ServerAddr.sin_port));


	   if (connect(s, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr))
   	    == SOCKET_ERROR)
	   {
   	   printf("No Connection (%d)\n", WSAGetLastError());
      	closesocket(s);
	      WSACleanup();
   	}
   	else connected = TRUE;
	}

   printf("Connection successful!\n");

   //At this point we can start sending and receiving data on the socket s.
	while(!kbhit()) {
		//Sleep(1000);

		nBytes = 256;
   	nLeft = nBytes;
	   index = 0;

   	SetExternalControlSwitches();

	   while(nLeft>0) {
		   if ((Ret = send(s, &SendBuf[index], nLeft, 0)) == SOCKET_ERROR)
   		{
      		printf("send failed with error %d\n", WSAGetLastError());
		      closesocket(s);
   		   WSACleanup();
      		getch();
	      	return 1;
	   	}
			nLeft-=Ret;
      	index+=Ret;
	   }



   	nBytes = 256;	//read 256 byte header
	   nLeft = nBytes;
   	index = 0;
	   while(nLeft>0) {
		   if ((Ret = recv(s, &RecvBuf[index], nLeft, 0)) == SOCKET_ERROR)
	   	{
   	   	printf("recv failed with error %d\n", WSAGetLastError());
	      	closesocket(s);
		      WSACleanup();
   		   getch();
      		return 1;
		   }
			index+=Ret;
      	nLeft-=Ret;
	   }
		switch(RecvBuf[2]) {
   		case 'B': nBytes = (int)((RecvBuf[4]<<16) | (RecvBuf[5]<<8) | RecvBuf[6]);
      				 break;
	   	case 'P': nBytes = (int)((RecvBuf[4]<<8) | RecvBuf[5]);
   	   			 break;
	   }
   	nLeft = nBytes-256;
	   while(nLeft>0) {
		   if ((Ret = recv(s, &RecvBuf[index], nLeft, 0)) == SOCKET_ERROR)
	   	{
   	   	printf("recv failed with error %d\n", WSAGetLastError());
	      	closesocket(s);
		      WSACleanup();
   		   getch();
      		return 1;
		   }
			index+=Ret;
      	nLeft-=Ret;
	   }
   	printf("%c%c%c: %d bytes, ",RecvBuf[0],RecvBuf[1],RecvBuf[2],index);
	   ping = ((RecvBuf[93]<<24) | (RecvBuf[94]<<16) | (RecvBuf[95]<<8) | RecvBuf[96]);
   	printf("Ping %d\n",ping);
	}


   //When you are finished sending and receiving data on socket s,
   //you should close the socket.
   printf("\nWe are closing the connection.\n");

   //When your application is finished handling the connection, call WSACleanup.
	closesocket(s);
	WSACleanup();

   getch();

   return 0;
}

void SetExternalControlSwitches(void)
{
	register int i;

   for(i=0;i<256;i++) SendBuf[i] = 0;

   SendBuf[0] = 'E';
   SendBuf[1] = 'C';
	SendBuf[2] = 0;		//ID

	SendBuf[3] = 1;		//Control Byte 1
                        //Bit0: 0 = LocalControl, 1 = ExternalControl
                        //Bit0 must be set to ExternalControl for Switch
                        //settings to take effect

   SendBuf[4] = 0;		//Control Byte 2
   							//Bit0: 0 = Transmit & Receive, 1 = Receive Only (disable transmitter)
                        //Bit1: 0 = Enable Plotting, 1 = Disable Plotting and run minimized

   SendBuf[5] = 0;		//Control Byte 3
   SendBuf[6] = 0;		//Control Byte 4

   SendBuf[7] = 7;		//Range
   							//must be in units of Meters
   							//0 = n/a
                        //1 = n/a
                        //2 = 5M
                        //3 = 10M
                        //4 = 20M
                        //5 = 30M
                        //6 = 40M
                        //7 = 50M
                        //8 = 60M
                        //9 = 80M
   							//10 = 100M

   SendBuf[8] = 3;		//Gain, 0 to 20dB
   SendBuf[9] = 50;		//Display Gain, 1 to 100 percent
   SendBuf[10] = 1;		//Gain Equalization, 0=Off, 1=On
   SendBuf[11] = 3;		//Sector Size, 0=30, 1=60, 2=90, 3=120deg
   SendBuf[12] = 2;		//Beamwidth, 0=Wide, 1=Normal, 2=Narrow, 3=Narrow Mixed
   SendBuf[13] = 1;		//Number of Beams, 0=480, 1=240, 2=120
   SendBuf[14] = 3;		//Averaging, 0,1=Off, 2,3,4,...10 = number of shots to average

   SendBuf[15] = (0&0xFF00)>>8;			//Persistence (Hi Byte), 0 to 600sec
   SendBuf[16] = (0&0x00FF);				//Persistence (Lo Byte)

   SendBuf[17] = (15000&0xFF00)>>8;;	//Sound Velocity*10 (Hi Byte), 1400.0 to 1600.0m/s
   SendBuf[18] = (15000&0x00FF);			//Sound Velocity*10 (Lo Byte)
   												//must be in units of Meters

   SendBuf[19] = 3;		//Mode, 0=Sector, 1=Linear, 2=Perspective, 3=Profile, 4=Beamtest

   SendBuf[20] = 0;		//83P/83B Output Enable, 0=83P, 1=83B

                        //For 83P Output:
                        //Enable Profile Point Detection (set SendBuf[21]=1)

                        //For 83B Output:
                        //Sector Size must be 120 Deg (set SendBuf[11]=3)
   							//Number of Beams must be 120 (set SendBuf[13]=2)

   SendBuf[21] = 1;		//Profile Point Detection, 0=Disable, 1=Enable
   SendBuf[22] = 0;		//Profile Minimum Range, 0 to 100M
   							//must be in units of Meters
   SendBuf[23] = 25;		//Profile Minimum Level, 10 to 90 percent
   SendBuf[24] = 0;		//Transducer Up/Down, 0=Down, 1=Up
   SendBuf[25] = 0+180;	//Profile Tilt Angle + 180, -30 to +30deg
   SendBuf[26] = 0;		//Roll Correction, 0=Off, 1=On
   SendBuf[27] = 0;		//Measurement Units, 0=Meters, 1=Feet, 2=Yards
   SendBuf[28] = 0;		//Record Start/Start (.837)

   SendBuf[29] = 0;		//Record Start/Start (.83P)
   //Not implemented

   SendBuf[30] = 0;		//Record Start/Start (.83B)
   //Not implemented

   //The following External Trigger Control Bytes are valid only for DeltaT
   //Sonar Heads supplied with the External Trigger Hardware Option
   SendBuf[31] = 0x00;					//External Trigger Control
   											//Bit0, Edge: 	 0=NEG,     1=POS
                        				//Bit1, Enable: 0=Disable, 1=Enable

   SendBuf[32] = (0&0xFF00)>>8;		//External Trigger Transmit Delay (Hi Byte)
   SendBuf[33] = (0&0x00FF);			//External Trigger Transmit Delay (Lo Byte)
   											//0 to 10000 in 100 microsecond increments

   SendBuf[34] = 2;		//Profile Point Filter,
   							//0 = First Return, 1 = Maximum Return, 2 = Bottom Following
}
