/*
 File      : Demo2410_EventDriven.c
 Function  : Event-driven application for Sensoray Model 2410 (48-channel digital I/O over ethernet)
 Copyright : (C) 2010 Sensoray

 This program shows how to create an entirely event-driven application using the 2410's
 asynchronous notification system.

 The main program begins by opening a remote shell on the 2410.
 The program issues shell commands to configure the 2410, and then it registers a
 callback function that is to be executed whenever the 2410 detects an asynchronous event.

 The callback function responds to DIO capture events by copying the capture flags, shifting
 them left by one bit, and then writing them back out to the DIOs, thus implementing a
 self-clocking shift register.
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/timeb.h>
#include <time.h>

#include "s24xx.h"

// Configuration constants.
#define DEFAULT_TIMEOUT 		1000		// Transaction timeout in milliseconds. Increase this if connected via WAN.
#define TELNET_PORTNUM			23			// Use standard telnet port number.

// Forward declaration.
static void EventCallback( char msgtype, const u16 *val );

// Set 48 DIO channel flags.
#define SETFLAGS(a,b,c)		{ flags[2] = (a); flags[1] = (b); flags[0] = (c); }

// Storage.
static HEVCAP	hEvcap = NULL;		// Handle to async capture system.
static HSESSION	sess;				// Handle to remote shell session, through which 2410 commands can be executed.


//////////////////////////////////////////////////////////////////////////
/////////////////////////  OS DEPENDENT CODE  ////////////////////////////
//////////////////////////////////////////////////////////////////////////


#ifdef OSTYPE_WINDOWS
#include <conio.h>
#endif


#ifdef OSTYPE_LINUX
#include <termios.h>
#include <stdio.h>
char getch(void)
{
	char ch;
	struct termios old;
	struct termios new;

	// Initialize new terminal i/o settings
	tcgetattr( 0, &old );				// grab old terminal i/o settings
	new = old;							// make new settings same as old settings
	new.c_lflag &= ~( ICANON | ECHO );	// disable buffered i/o, echo mode
	tcsetattr( 0, TCSANOW, &new );		// use these new terminal i/o settings now

	ch = getchar();

	tcsetattr( 0, TCSANOW, &old );		// Restore old terminal i/o settings
	return ch;
}
#endif


//////////////////////////////////////////////////////////////////////////
////////////////////////  OS INDEPENDENT CODE  ///////////////////////////
//////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////
// Application main function.
// Invocation: APPNAME IPADDR
// Example: demo 192.168.24.10

int main( int argc, char *argv[] )
{
	char	*ipaddr = argv[1];			// IP address of Sensoray 2410
	u16		port = TELNET_PORTNUM;		// Use default telnet port number

	u16		flags[3];
	S24XXERR err = ERR_NONE;
	// Validate command line.

	if ( argc != 2 ) {
		printf( "Error: Invocation error\nUsage: %s ipaddr\n", argv[0] );
		return -1;
	}

	// Open the 2410 api.
	if ( !s24xx_ApiOpen() ) {
		printf( "Error: Failed to open session interface\n" );
		return -2;
	}

	// Open remote shell on the 2410 via telnet session and get session handle
	err = s24xx_SessionOpen( &sess, &err, 2410, argv[1], TELNET_PORTNUM, DEFAULT_TIMEOUT );
	if (!err) {
		printf("session error\n");
		s24xx_ErrorText( err ) ;
	// If we successfully connected to remote shell ...
	} else {
		// Do some initialization.
		printf("start\n");
		err = 0;
		s24xx_ResetIo( sess, &err );						// Reset all DIOs on the 2410.
		s2410_SetLedBrightness(sess, &err, 50 );			// Set led brightness to aesthetically pleasing 50% of full intensity.
		SETFLAGS( 0xffff, 0xffff, 0xffff );
		s2410_WriteCapDisable( sess, &err, flags );		// Disable event capture on all DIO channels.		
		// Abort if any session or shell errors detected.
		if ( err != ERR_NONE )
		{
			printf( "Error: %s\n", s24xx_ErrorText( err ) );
			getch();
		}

		else	// If everything is okay so far ...
		{
			hEvcap = s2410_AsyncCapBegin( ipaddr, &err, port, EventCallback, DEFAULT_TIMEOUT );		// Set up async capture notification.

			if ( hEvcap == NULL )
			{
				printf( "Error: can't start async capture notification\n" );	// if capture setup failed, display error
				getch();														//   and wait for keypress.
			}
			else
			{
				getch();						// Wait for any keypress.
				s2410_AsyncCapEnd( hEvcap, &err );	// Exit cap command and return to remote shell.
			}
		}
		printf("exiting\n");
		s24xx_SessionClose( sess );		// Terminate connection.
		s24xx_ApiClose();				// Free API resources.
	}

	return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Event callback function. This callback is registered by calling s2410_AsyncCapBegin() and unregistered by s2410_AsyncCapEnd().
// Once registered, it will be called whenever a message is generated by the 2410's asynchronous event notification system.
// Various message types are generated to indicate various types of events. See the code below for details.
// Imports:
//	msgtype	- message type code.
//	val		- pointer to array of u16 values. In most cases these contain bit flags for the 48 DIO channels.
//	nvals	- number of arguments in val[].

static void EventCallback( char msgtype, const u16 *val )
{
	u16	flags[3];		// Bit flags for the 48 DIO channels.
    int err;
	// Decode the message type and process it accordingly. Some message types will never be encountered in this simple demo
	// program, but for completeness, all message types are handled here anyway. Messages are received in the order they were
	// generated so that the callback can accurately keep track of the order of events, if required by the application.
	switch ( msgtype )
	{
	case CAPMSG_ATTACH:		// val[] not used.
		
		// This message only occurs once, when the callback is called for the first time. To process this,
		// the callback can do any initialization required for the application's event-driven functionality.

		// In this demo program, we will service this message by initializing the 48-bit "shift-register" we are implementing.
		// Your application can perform whatever initialization it requires here, in place of the following demo code:

		printf( "begin\n" );						// Notify user that callback is being called for the first time.

		SETFLAGS( 0xffff, 0xffff, 0xffff );			// Enable continuous capture on all DIN channels.
		s2410_WriteCapContinuous( sess, &err, flags );

		SETFLAGS( 0, 0, 1 );						// prime the dio "shift register"
		s2410_WriteDout( sess, &err, flags );
		
		break;

	case CAPMSG_DETACH:		// val[] not used.

		// This message only occurs once, when the callback is being shutdown.
		// This is the final time the callback will be executed.
		// Your application can perform cleanup tasks here as required.

		printf( "quit\n" );							// Notify user that callback is being called for the last time.

		s24xx_ResetIo( sess, &err );						// reset all DOUTs

		break;

	case CAPMSG_NACK:
		// Capture command not understood. val[] not used.
		printf( "nack\n" );
		break;

	case CAPMSG_ERROR:
		// Capture command error. val[] not used.
		printf( "error\n" );
		break;

	case CAPMSG_TIMER:
		// Capture inactivity timer programmed to timeout in NNNN milliseconds. val[0] contains milliseconds value.
		// This results from an earlier call to s2410_WriteCapTimer().
		printf( "start timer: %d msec\n", val[0] );
		break;

	case CAPMSG_POLARITY:
		// Capture polarities programmed on all DIO channels. val[] contains all DIO capture polarities.
		// This results from an earlier call to s2410_WriteCapPolarity().
		printf( "pol:  %04x %04x %04x\n", val[2], val[1], val[0] );
		break;

	case CAPMSG_CONTINUOUS:
		// Continuous capture enabled on specified DIO channels. val[] specifies DIO channels with logic '1'.
		// This results from an earlier call to s2410_WriteCapContinuous().
		printf( "cont:  %04x %04x %04x\n", val[2], val[1], val[0] );
		break;

	case CAPMSG_ONESHOT:
		// One-shot capture enabled on specified DIO channels. val[] specifies DIO channels with logic '1'.
		// This results from an earlier call to s2410_WriteCapOneshot().
		printf( "1shot: %04x %04x %04x\n", val[2], val[1], val[0] );
		break;

	case CAPMSG_DISABLE:
		// Disabled capture enabled on specified DIO channels. val[] specifies DIO channels with logic '1'.
		// This results from an earlier call to s2410_WriteCapDisable().
		printf( "disab: %04x %04x %04x\n", val[2], val[1], val[0] );
		break;

	case CAPMSG_EVENT:
		
		// One or more events have been captured, or the inactivity timer timed out.
		// The bit flags in val[] indicate which of the 48 DIO channels have captured edge events.
		// All capture flags are internally reset on the 2410 when this message is issued.
		// If this message was issued due to an inactivity timer timeout, all capture flags will contain zero.

		// In this demo, we simulate a 48-bit shift register by shifting the capture flags (only one flag
		// should be set) and writing them to the DOUTs, which in turn will cause the next event capture.
		// You can replace this code with your own event handler as required by your application.

		flags[2] = ( val[2] << 1 ) + ( val[1] >> 15 );		// fetch and shift capture flags
		flags[1] = ( val[1] << 1 ) + ( val[0] >> 15 );
		flags[0] = ( val[0] << 1 ) + ( val[2] >> 15 );
		s2410_WriteDout( sess, &err, flags );						// write to DOUTs
		break;
	}
}
