/*----------------------------------------------------------------------------
(c) TCTEC Pty Ltd www.tctec.net

lrelayset   (Linux Relay Set)
Super4 Relay module control application.

dll linking code copied from example at 
http:/www.ibm.com/developerworks/linux/library/l-dll.html

Code created with "Geany" IDE on Puppy Linux

Compiled with: gcc -Wall -c "%f"
Built with: gcc -Wall -ldl "%f"


10-12-2007	Version 1.0 Created

 
----------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <inttypes.h>
#include <string.h>

#define VERSION_STRING ("lrelayset Version 1.0") 
//#define DEBUG	(1) 

void *FunctionLib;		/* handle to shared lib file */
uint32_t (*Function)();		/* Pointer to loaded routine */
const char *dlError;	/* Pointer to error string */

// FTDI constants
//
const uint32_t FT_LIST_NUMBER_ONLY =	0x80000000 ;
const uint32_t FT_LIST_BY_INDEX =		0x40000000 ;
const uint32_t FT_LIST_ALL = 			0x20000000 ;

const uint32_t FT_OPEN_BY_SERIAL_NUMBER =	1 ;
const uint32_t FT_OPEN_BY_DESCRIPTION =		2 ;
const uint32_t FT_OPEN_BY_LOCATION = 		4 ;


const uint32_t FT_OK = 0 ;

//------------------- DL (Dynamic Library) Functions -----------------------

void openLibrary()
{	
	// Open Dynamic Loadable Library with absolute path
	//
	//FunctionLib = dlopen("/mnt/home/Super4/libftd2xx.so.0.4.13", RTLD_LAZY) ;
	//dlError = dlerror() ;
	
	FunctionLib = dlopen ("libftd2xx.so", RTLD_LAZY) ;
	dlError = dlerror() ;
	
	#ifdef DEBUG
	printf("Open Library with absolute path return = %s\n", dlError) ;
	#endif
	
	if (dlError) 
	{
		printf("Could not load library libftd2xx.so\n") ;
		exit(1) ;	
	}	
	
}

void closeLibrary()
{
	int rc ;
	// Close the shared library
	//
	rc = dlclose(FunctionLib) ;
	dlError = dlerror() ;
	
	#ifdef DEBUG
	printf("Close function lib returned %s\n", dlError) ;	
	#endif	
}


void loadDLFunction(char* funcName)
{
	Function = dlsym(FunctionLib, funcName) ;
	dlError = dlerror() ;
	#ifdef	 DEBUG	
	printf("loaded %s, return = %s\n", funcName, dlError) ;
	#endif
	if (dlError) exit(1) ;	
	
}

//-------------------------------- Relay functions ----------------------

/*
List connected TCTEC Relays
*/
void listTCTECRelays()
{
	uint32_t numDevices ;
	uint32_t temp ;
	uint32_t Flags ;
	uint32_t id ;
	uint32_t locID ;
	volatile char pcSerialNumber[255] ;
	uint32_t Handle ;
	volatile char pcDescription[255] ;
	uint32_t deviceType ;	
		
	
	loadDLFunction("FT_ListDevices") ;
	
	// call the function
	//
	(*Function) ((void*) &numDevices, (void*)&temp, FT_LIST_NUMBER_ONLY) ;
	
	printf("\n\n\nFTDI Devices = %ld\n", numDevices) ;
	printf("--------------------------------------\n") ;
	
	// get details of connected FTDI devices
	//	
	loadDLFunction("FT_CreateDeviceInfoList") ;	
	(*Function) (&numDevices) ;
	
	printf("\n\nDevice\t\t\tSerialNumber\n") ;
	printf("---------------------------------------------------------\n") ;
	
	
	// list all the devices with the description : "TCTEC USB RELAY"
	//
	loadDLFunction("FT_GetDeviceInfoDetail") ;	
	for(temp = 0 ; temp < numDevices; temp++)
	{
		(*Function) ((unsigned int) temp, &Flags, &deviceType, &id, &locID, pcSerialNumber, pcDescription, &Handle) ;
		if (strcmp(pcDescription, "TCTEC USB RELAY") == 0) 
		{
			// found a relay board
			//
			printf("%s\t\t\t%s\n", pcDescription, pcSerialNumber) ;
		}		
		
	}
	
	
	return;		
	
}

/*
Set the relay state by writing a nibble to the relay board.
This also sets the drive state of the pins to "output"

serialNumber:	The serial number of the relay board
mask:			The bits to set / un-set  (e.g 4 = 0100 = relay 3)
on:				1 = set (on) 0 = unset (off)
*/
int setBits(char *serialNumber, uint8_t mask, int on) 
{
	uint32_t tempInt ;
	uint32_t Handle ;	
	uint8_t bRelayStates = 0 ;
	uint8_t bitMode ;	
	
	// open a device and set to outputs, may already be done.
	//
	loadDLFunction("FT_OpenEx") ;	
	tempInt = (*Function) ((void*)serialNumber, FT_OPEN_BY_SERIAL_NUMBER, &Handle) ;
	if (tempInt != FT_OK)
	{
		printf("Device did not open, returned = %ld\n", tempInt) ;
		printf("serialNumber = %s\n", serialNumber) ;
		return(1) ;
	}
	loadDLFunction("FT_GetBitMode") ;	
	tempInt = (*Function) (Handle, &bitMode) ;
	if (tempInt != FT_OK)
	{
		return(1) ;
	}
		
	// The bit mode only needs to be set once after first plugged in
	//		
	if ((bitMode >> 4) > 0)
	{
		// bit mode has not yet been set, so set it
		//		
		// set the bit mode (all inputs)
		//
		loadDLFunction("FT_SetBitMode") ;		
		tempInt = (*Function) (Handle, 0xF0, 0x01) ;
		if (tempInt != FT_OK)
		{
			printf("Error setting bit mode, returned = %ld\n", tempInt) ;
			return(2) ;			
		}
		
		// set all to high (off)
		//		
		bRelayStates = 15 ;
		loadDLFunction("FT_Write") ;
		
		// write twice, as there is buffering
		//
		(*Function) (Handle, (void*) &bRelayStates, 1, &tempInt) ; 		
		(*Function) (Handle, (void*) &bRelayStates, 1, &tempInt) ; 		
							
		// Set up asyncronous bit bang mode all outputs		
		//		
		loadDLFunction("FT_SetBitMode") ;
		(*Function) (Handle, 0xFF, 0x01) ; 	
	}
	
	// read the current state of the bits (relays)
	//
	loadDLFunction("FT_Read") ;
	(*Function) (Handle, (void*)&bRelayStates, 1, (void*)&tempInt) ;
		
	// invert states, because 0 = on, 1 = off
	//	
	bRelayStates = ~bRelayStates ;
	
	// if turning on, then OR with current state
	//
	if (on)
	{
		bRelayStates |= mask ;
	}
	else
	{
		bRelayStates &= ~(mask) ; // turn off
	}
	// set the relays, invert before writing out. (0 = on, 1 = off)
	//
	bRelayStates = ~bRelayStates ;
		
	#ifdef DEBUG
	printf("Relay state = %d\n", (int) bRelayStates) ;
	#endif
	
	loadDLFunction("FT_Write") ;
	
	// write twice, as there is buffering
	//
	(*Function) (Handle, (void*)&bRelayStates, 1, &tempInt) ;
	(*Function) (Handle, (void*)&bRelayStates, 1, &tempInt) ;
	
	
	loadDLFunction("FT_Close") ;
	tempInt = (*Function) (Handle) ;
	if (tempInt != FT_OK)
	{
		printf("Error. FTDI library returned %ld on FT_Close\n", tempInt) ;
		return(1) ;
	}	
	
	return(0) ;		
}



void printHelpMessage()
{
	printf("%s\n", VERSION_STRING) ;
	printf("\n-------------------------------\n") ;
	printf("TCTEC relay command for Linux\n") ;
	printf("%s\n",VERSION_STRING) ;
	printf("www.tctec.net\n") ;
	printf("\n-------------------------------\n") ;
	printf("Commands:\n") ;
	printf("\n-l\t\tList all connected TCTEC relay modules\n") ;
	printf("\nUSAGE:\n") ;
	printf("lrelayset [-u,-s][SERIAL NUMBER],[MASK][ -tTIME IN MILLISECONDS]\n") ;
	printf("\t\t\t\t-t :optional for momentary action (1 to 10000)\n") ;
	printf("\t\t\t\t-u :{u}nset the relay/s\n") ;
	printf("\t\t\t\t-s :{s}et the relay/s\n") ;
	printf("\t\t\t\tMASK is a number representing bits (01 to 15)\n\n") ;
	printf("-s[SERIAL NUMBER],[MASK]\tSet the relays of a module (Turn ON)\n") ;
	printf("\t\t\t\te.g: -sFTQ57KP1,15\n") ;
	printf("\t\t\t\t( 15 = binary 1111. Therefore all relays on. ) ") ;
	printf("\n\t\t\t\te.g: -uFTQ57KP1,15\n") ;
	printf("\t\t\t\t( 15 = binary 1111. Therefore all relays off. ) ") ;
	printf("\n\n\n") ;		
	
}

int main(int argc, char** argv) 
{		
	char serialNum[255] ="";
	char* serNumEnd ;
	int state ;
	uint16_t momentaryPause = 0 ;
	int on ;  // (1 == on, 0 == off) 
	
		
	if (argc > 1)
	{
		openLibrary() ;  // open the FTDI library
		
		// parse arguments
		//
		if (strcmp(argv[1], "-l") == 0)
		{
			listTCTECRelays() ;			
		}
		else if ((strncmp(argv[1], "-s", 2) == 0) || (strncmp(argv[1], "-u", 2) == 0))
		{
			
			#ifdef DEBUG
			printf("Setting relays\n") ;
			#endif
			
			state = -1 ;
			
			if (strncmp(argv[1], "-s", 2) == 0)
			{
				on = 1;
			}
			else
			{
				on = 0 ;
			}
			
			
			// find the serial number in the argument
			//
		
			strncpy(serialNum, (char*)&argv[1][2], 250) ;  // ignore the "-s" or "-u"			
			
			serNumEnd = memchr(serialNum, ',', strlen(serialNum)) ; // find the comma
			if (serNumEnd)
			{				
				// get the state value from the string
				//
				state = atoi(serNumEnd+1) ;
			
				(*serNumEnd) = NULL ;  // null terminate the serial number string
			}
			
			#ifdef DEBUG
			printf("SerialNumber = %s\n", serialNum) ;
			printf("State = %d\n", state) ;
			#endif
			
			// validate the state value
			//
			if ((state < 0) || (state > 15))
			{
				printf("Invalid state value.\n") ;
				return(1) ;
			}
			
			// have we specified a momentary action ?
			//			
			if (argc >= 3)
			{
				if (strncmp(argv[2], "-t", 2) == 0)
				{
					momentaryPause = atoi((char*)&argv[2][2]) ;
					
					// must be less than or equal to 10 seconds
					//
					if (momentaryPause > 10000)
					{
						momentaryPause = 0 ;
					}
					
					#ifdef DEBUG
					printf("Momentary Pause selected: %d milli seconds.\n", momentaryPause) ;
					#endif
				}				
			}
			setBits((char*) serialNum, state, on) ;
			
			if (momentaryPause > 0)
			{
				usleep(momentaryPause*1000);
				
				// do the opposite to above, ie if relays on, then turn off
				//
				setBits(serialNum, state, ((~on) & 0x01)) ;
			}						
		}
		else 
		{
			printHelpMessage() ;
		}		
		
		closeLibrary() ;
	}
	else   // no arguments specified, so print help message
	{
		printHelpMessage() ;
	}	
	exit(0) ;	
	
	return(0) ;
	
}
