/*
    Robot Interface
    (C) 2006 Jason Hunt
    nulluser@gmail.com
    
    File: serial.cpp
*/


#include <windows.h>
#include <stdio.h>

#include "serial.h"
#include "robot.h"
#include "display.h"


HANDLE h_serial = NULL;			      // Handle for serial port

bool serial_ok = false;               // True if serial is working 

HANDLE h_serial_thread = NULL;               // Handel for serial control thread
DWORD serial_thread_id;               // Serial control 
bool serial_running = false;          // True if serial thread is running


CRITICAL_SECTION serial_thread_lock;  // Lock for serial thread

bool timeout_flag = false;            // Set once for a timeout condition

char serial_port[10];                 // Filename of serial port



/* Make sure there is no pending data for the next retry */
void flush_serial( void )
{
    DWORD read = 1;
                    
    // Wait for byte
    while (read)
    {
        char ch;
        ReadFile (h_serial, &ch, 1, &read, NULL);  
    }  
} 
/* End of flush serial */


/* Preform transfer with robot */
int transfer_serial( void )
{
    // Buffer for send/recv
    unsigned char buffer[SERIAL_RECV_SIZE + SERIAL_SEND_SIZE];

    // Length of trnaferd data for send or recv
  	unsigned long length; 

    // Copy control data into the send buffer
    robot.get_control(buffer);
  
   	// Send data to controller
   	if (!WriteFile(h_serial, buffer, SERIAL_SEND_SIZE, &length, NULL))
  	{
        add_line("Unable to send data");
        serial_ok = false;
        return(1);
    }
        
    // Check data sent length 
    if (length != SERIAL_SEND_SIZE)
    {
        add_line("Send data error");
        serial_ok = false;        
        return(1);
    }
                                                              
    // Read telemetry data from the controller 
    if (!ReadFile(h_serial, buffer, SERIAL_RECV_SIZE, &length, NULL))
    {
        add_line("Unable to read data");
        serial_ok = false;        
        return(1);
    }
            
    // Check length of packet
    if (length != SERIAL_RECV_SIZE)
    {
        if (!timeout_flag)
        {
            add_line("Read data timeout");            
            timeout_flag = true;
        }

        flush_serial();
        
        // Clear recv buffer 
        memset(buffer, 0, SERIAL_RECV_SIZE);

        serial_ok = false;        
        return(1);
    }


    timeout_flag = false;

    robot.set_telemetry(buffer);
        
    return(0);
}
/* End of transfer serial */


/* Core thread for serial transfer */
DWORD WINAPI serial_thread(LPVOID p)
{
    DWORD length;

    bool done = false;

    while (!done)
    {
        // Preform serial transfer, and send result to update    
        robot.update_complete(transfer_serial());
        
        EnterCriticalSection(&serial_thread_lock);
        done = !serial_running;
        LeaveCriticalSection(&serial_thread_lock);
    }
        
    return(0);
}
/* End of serial thread */



/* Starts the serial system */
void start_serial( void )
{
//	serial_ok = false;
  
	h_serial = CreateFile(serial_port,							// Port name 
			 			  GENERIC_READ | GENERIC_WRITE,		// File access
		                  0,								// share mode
			 			  NULL,								// Security
			 			  OPEN_ALWAYS,						// Creation					
			 			  0,								// Flags
						  NULL );							// Template

	if (h_serial == INVALID_HANDLE_VALUE)
	{
        char b[400];
        sprintf(b, "Unable to open serial port (%s)", serial_port);
        add_line(b);

		return;
	}

	DCB serial_param;

	if (!GetCommState(h_serial, &serial_param))
	{
		add_line("Unable to get commstate");
		return;
	}

    serial_param.BaudRate = CBR_19200;
	serial_param.fParity = false;	
    serial_param.fBinary  = true;
	serial_param.fOutxCtsFlow = false;
	serial_param.fOutxDsrFlow = false;
	serial_param.fDtrControl = DTR_CONTROL_DISABLE;
    serial_param.fDsrSensitivity = false;
    serial_param.fErrorChar = false;
    serial_param.fNull = false;
	serial_param.fOutX = false;
	serial_param.fInX = false;
    serial_param.fRtsControl = RTS_CONTROL_DISABLE;		// Disable request to send control
    serial_param.Parity = NOPARITY;
    serial_param.ByteSize = 8;
	serial_param.StopBits = TWOSTOPBITS;

	if (!SetCommState(h_serial, &serial_param))
	{
		add_line("Unable to set commstate");
		return;
	}

	COMMTIMEOUTS timeouts;

	timeouts.ReadIntervalTimeout = 200;
	
	timeouts.ReadTotalTimeoutMultiplier = 1;
	timeouts.ReadTotalTimeoutConstant = 200; 
	timeouts.WriteTotalTimeoutMultiplier = 1;
	timeouts.WriteTotalTimeoutConstant = 200;

	if (!SetCommTimeouts(h_serial, &timeouts))
	{
		add_line("Unable to get comm timeouts");
		return;
	}


    InitializeCriticalSection(&serial_thread_lock);

    // Set serial thread active
    serial_running = true;

    // Start the serial therad
    h_serial_thread = CreateThread(0, 0, serial_thread, NULL, 0, &serial_thread_id);

	serial_ok = true;
	
	add_line("Serial Online");
}
/* End of start serial */



/* Close the serial port */
void stop_serial( void )
{    
    if (h_serial_thread != NULL)
    {
        EnterCriticalSection(&serial_thread_lock);
        serial_running = false;                         // Tell thread to exit
        LeaveCriticalSection(&serial_thread_lock);

        WaitForSingleObject(h_serial_thread, INFINITE); // Wait for thread to terminate
    }

    if (h_serial != NULL)
    	CloseHandle(h_serial);
}
/* End of stop serial */



