/**********************************************************************
    all.c
    Z-World, 2001
   
    This program contains all of the DeviceMate subsystems.  It must be
    run with DEVMATE_FS.C, DEVMATE_2FLASH.C, or DEVMATE_ZCONSOLE.C.
   
    Many of the components of this sample are taken from the individual
    target samples.  See those samples for more specific information.
   
    Note that the file upload will be available on the webserver as
    "dynamic.html".
**********************************************************************/

/*
 * This macro defines the maximum number of variables that the
 * target processor will support.
 */
#define DEVMATE_VAR_MAXVARS 10

/* Request use of var services */
#define USE_TC_VAR
/* Request use of watchdog services */
#define USE_TC_WD
/* Request use of TCP services */
#define USE_TC_TCPIP
/* Request use of filesystem services */
#define USE_TC_FS
/* Request use of logging services */
#define USE_TC_LOG
/* Request use of SMTP email services */
#define USE_TC_SMTP

/*
 * Uncomment this line if this program will be loaded over ethernet. This will
 * force the DevMate communication to use serial port A, re-using the programming
 * link that was used to download the program.
 */
//#define USE_TC_LOADER

#ifdef CC_VER
	/* Dynamic C */
	#memmap xmem
	#use "tc_conf.lib"
#else
	/* Non-Rabbit */
	#include "tc_conf.h"
	#include <stdio.h>	
#endif

/* Change these items for watchdog capability if desired... */
#define RESPONSE_TIMEOUT	3000	/* Expect responses within this many ms */
#define WD_TIMEOUT			4000	/* Minimum watchdog timeout in ms */
#define NUM_WD					10		/* Number of watchdogs to set */
#define NUM_HITS				40		/* Max hits of the 1st (fastest) watchdog */

/* Watchdog arrays */
uint16 wdto[NUM_WD];		/* watchdog timeout, ms */
uint16 hitrate[NUM_WD];	/* ms between hits, per wd */
uint16 nexthit[NUM_WD];	/* next hit timestamp */

/* Change these macros for the TCP echo starting on port 1234 */
#define NSOCK 1			/* Define bigger to accept more connections.
									Port numbers will be 1234, 1235, 1236... */
#define BOOFSIZE 256

/* TCP echo variables */
char buf[NSOCK][BOOFSIZE];
char rxbuf[NSOCK][BOOFSIZE];

/* Change this item for the filesystem subsystem if desired */
#define UPDATE_RATE	30		/* Update stored file every this many seconds */

/* Filesystem variables */
int counter1, counter2, counter3, counter4;
int update_flag;
const char file_name[] = "/dynamic.html";
const char file_header[] = "<html><header><title>Test Dynamic Page!</title></header><body><h2>The current value of the counters:</h2><br><br>";
const char file_footer[] = "</body></html>";

/* SMTP configuration macros */
#define MY_SMTP_SERVER	"mail.somewhere.org"
#define MY_SMTP_DOMAIN	"somewhere.org"
#define FROM				"DevMate@somewhere.org"
#define TO					"nobody@nowhere.org,everybody@everywhere.org"
#define SUBJECT			"test mail"

/*
 * The states used in the FSM that uploads the file.
 */
#define UPDATE_SYNC			0
#define UPDATE_SYNCFINISH	1
#define UPDATE_LOOKUP		2
#define UPDATE_LOOKUPWAIT	3
#define UPDATE_CREATE		4
#define UPDATE_SEND			5
#define UPDATE_SENDHEADER	6
#define UPDATE_SENDVARS		7
#define UPDATE_SENDVAR		8
#define UPDATE_SENDVARTEXT	9
#define UPDATE_SENDFOOTER	10
#define UPDATE_CLOSE			11
#define UPDATE_ERROR			12
#define UPDATE_POLL			13
#define UPDATE_REPLACE		14

/*
 * Update the HTML file on the DevMate.
 *
 * This is a state machine that will start an
 * update whenever a specific flag is set.
 */
void update_file(void)
{
	static char not_first_time;	// false if this is the first time the file is sent (a CREATE)
	static DMFile	file;		// the file pointer used for this transaction
	static int state;			// current state...
	static int rv;
#define SENDBUFSIZE	128
	static char sendbuf[SENDBUFSIZE];
	static int varcache[4];
	static int currentvar;
	static int length, offset;
	static FNumber old_id;
	static long timeout;

#ifdef __DC__
#GLOBAL_INIT {
	state = UPDATE_SYNC;		// the initial state...
	update_flag = 0;			// no update requested yet!
	not_first_time = 0;			// do a CREATE!
}
#endif

	switch(state) {
	case UPDATE_SYNC:
		/* try and SYNC with the target */
		rv = devmate_fs_sync(&file,0);
		if(TC_PENDING == rv) {
			timeout = MS_TIMER;
			timeout += 2000;	// wait two seconds...
			
			state = UPDATE_SYNCFINISH;
			// fall through to SYNCFINISH
		} else {
			break;
		}

	case UPDATE_SYNCFINISH:
		/* is the SYNC command finished? */
		rv = devmate_fs_finish(&file);
		if(TC_PENDING == rv) {
			if(timeout < MS_TIMER) {
				// error - timeout! try this again...
				state = UPDATE_SYNC;
			}
			break; // still waiting...
		} else {
			if(TC_ERROR == rv) {
				printf("FS could not SYNC with the DevMate!\n");
				exit(-1);
			}
			// SYNC has finished - the DevMate is ready.
			// fall through to the LOOKUP state
			state = UPDATE_LOOKUP;
		}

	case UPDATE_LOOKUP:
		/* is the file we are trying to create allready there? */
		rv = devmate_fs_idlookup(&file, (char *)file_name, &old_id);
		if(rv != TC_PENDING) {
			/* error! */
			printf("idlookup error\n");
			exit(-1);
		} else {
			// wait for the idlookup to finish
			state = UPDATE_LOOKUPWAIT;
			// fall through to the LOOKUPWAIT state
		}

	case UPDATE_LOOKUPWAIT:
		rv = devmate_fs_finish(&file);
		if(TC_PENDING == rv) {
			// still waiting...
			break;
		} else if(TC_SUCCESS == rv) {
			/* the file allready exists! replace the file */
			state = UPDATE_REPLACE;
			break;
		} else {
			/* the file doesn't exist yet! create a new file... */
			state = UPDATE_CREATE;
			// fall through to the CREATE state!
		}

	case UPDATE_CREATE:
		/* try and create a new file */
		rv = devmate_fs_open(&file, 0, (char *)file_name, TC_FS_FLAGS_NEWID);
		if(TC_PENDING == rv) {
			break; // open isn't ready yet
		} else {
			if(TC_SUCCESS != rv) {
				state = UPDATE_ERROR;
				break;
			}
			// the file is opened - send it!
			state = UPDATE_SEND;
			// fall through to the SEND state
		}

	case UPDATE_SEND:
		/* send the header... */
		length = strlen(file_header);
		offset = 0;
		state = UPDATE_SENDHEADER;
		// fall through to the SENDHEADER state

	case UPDATE_SENDHEADER:
		if(offset < length) {
			// send a piece of the header
			rv = devmate_fs_append(&file, (char *)file_header+offset, length-offset);
			if(-1 == rv) {
				// error!
				state = UPDATE_ERROR;
			}
			offset += rv;
			break;
		} else {
			// all done w/ sending the header!
			state = UPDATE_SENDVARS;
			// fall through to the SENDVARS state
		}

	case UPDATE_SENDVARS:
		/* first grap a cache of the variable, as this could take
			a while, and some variables might change in the meantime */
		varcache[0] = counter1;
		varcache[1] = counter2;
		varcache[2] = counter3;
		varcache[3] = counter4;
		currentvar = 0;	// start with the first variable
		state = UPDATE_SENDVAR;
		// fall through tot he SENDVAR state

	case UPDATE_SENDVAR:
		if(currentvar > 3) {
			state = UPDATE_SENDFOOTER;
			break;
		}
		/* generate the text-line to send for one variable */
		sprintf(sendbuf,"counter%c == 0x%04x<br>", currentvar + '1', varcache[currentvar]);
		offset = 0;
		length = strlen(sendbuf);
		state = UPDATE_SENDVARTEXT;
		// fall through to the SENDVARTEXT state

	case UPDATE_SENDVARTEXT:
		if(offset < length) {
			// send a piece of the text-line
			rv = devmate_fs_append(&file, sendbuf+offset, length-offset);
			if(-1 == rv) {
				// error!
				state = UPDATE_ERROR;
			}
			offset += rv;
			break;
		} else {
			// all done w/ sending that text-line. is there more?
			currentvar += 1;
			state = UPDATE_SENDVAR;
			break;
		}

	case UPDATE_SENDFOOTER:
		if(offset < length) {
			// send a piece of the text-line
			rv = devmate_fs_append(&file, (char *)file_footer+offset, length-offset);
			if(-1 == rv) {
				// error!
				state = UPDATE_ERROR;
			}
			offset += rv;
			break;
		} else {
			// all done with the file! close it!
			state = UPDATE_CLOSE;
			// fall through to the CLOSE state
		}

	case UPDATE_CLOSE:
		/* spin on close to flush the remaining data */
		switch(devmate_fs_close(&file,NULL)) {
		case TC_PENDING:
			// still pending - do nothing
			break;

		case TC_SUCCESS:
			// upload was successfull!
			not_first_time = 1;		// no longer the first time
			update_flag = 0;		// all done w/ this update
			state = UPDATE_POLL;	// poll for the next update request
			break;

		default:
			// error!
			state = UPDATE_ERROR;
			break;
		}		
		break;

	case UPDATE_ERROR:
		/* error in update! try this again... */
		if(!not_first_time) {
			state = UPDATE_CREATE;
		} else {
			state = UPDATE_REPLACE;
		}
		break;

	case UPDATE_POLL:
		/* poll the update flag to see if the file needs to be reset */
		if(update_flag) {
			state = UPDATE_REPLACE;
			// fall through to the REPLACE state
		} else {
			break; // the flag is not set - don't update yet.
		}

	case UPDATE_REPLACE:
		rv = devmate_fs_open(&file, 0, (char *)file_name, TC_FS_FLAGS_REPLACE);
		if(TC_PENDING == rv) {
			// open is still pending
		} else {
			if(TC_SUCCESS != rv) {
				state = UPDATE_ERROR;
				break;
			}
			// open w/ replace was successfull!
			state = UPDATE_SEND;
		}
		break;

	default:
		/* internal error! */
		exit(-1);
	}
}

/* Generate some not-very-random data (for the filesystem subsystem demo) */

void worker(int *data)
{
	static foo;
	*data = (int)MS_TIMER + foo++;
}

int main(void)
{
	/* Common variable */
	int rc;
	int i;

	/* Variable subsystem variables */
	long starttime;
	long oldseconds;
	long seconds;
	char strvar[20];
	float floatvar;

	/* Watchdog variables */
	int hitcount;
	char wdname[20];

	/* TCP echo variables */
	int sock, sm1;
	int nrx, num_rx[NSOCK];
	int ntx, num_tx[NSOCK];
	int len;
	char * b, * rxb;
	int st, state[NSOCK];
#define S_LISTEN	1
#define S_DATE		2
#define S_ECHO		3
#define S_CLOSE	4
#define S_CLOSING	5
#define S_CLOSED	6
#define S_ERROR	7

	/* Filesystem subsystem variables */
	long timer;

	/* Logging subsystem variables */
	uint16 logtimer;
	char logbuf[116];
	int logstate;
#define S_LOG_INIT	1
#define S_LOG_WAIT	2
#define S_LOG_SEND	3
	long log_oldseconds;
	long log_seconds;
	int messagenum;
	int facilitynum;
#define NUM_TEST_MESSAGES 3
	static const char * test_messages[NUM_TEST_MESSAGES] =
	{
		"Z-World",
		"Rabbit Semiconductor",
		"Talk Async Serial!"
	};

	/* SMTP subsystem variables */
	char message[256];
	int mailsent;

	/*
	 * This call is necessary to initialize target communications
	 * beween the target processor and the DeviceMate.
	 */
	devmate_init();

	/*
	 * Set up the variable demonstration
	 */
	starttime = SEC_TIMER;
	oldseconds = 0;
	seconds = 0;
	strcpy(strvar, "Good bunny.");
	floatvar = 42.42;
	devmate_var_add("seconds", &seconds, VAR_INT32, "%ld", 0, VAR_SERVER_HTTP);
	devmate_var_add("string", strvar, VAR_STRING, "%s", 20, VAR_SERVER_HTTP);
	devmate_var_add("float", &floatvar, VAR_FLOAT32, "%f", 0, VAR_SERVER_HTTP);

	/*
	 * Set up the watchdog demonstration
	 */
	/* initialize hit rates etc. */
	for (i = 0; i < NUM_WD; i++) {
		wdto[i] = WD_TIMEOUT + i * 300;
		hitrate[i] = wdto[i] / 3;
	}
	if ((rc = devmate_wd_init(RESPONSE_TIMEOUT)) != DEVMATE_WD_ACKED) {
		printf("devmate_wd_init: failed %d\n", rc);
		exit(19);
	}
	for (i = 0; i < NUM_WD; i++) {
		sprintf(wdname, "test%d", i+1);
		rc = devmate_wd_add(wdname, wdto[i], RESPONSE_TIMEOUT);
		switch (rc) {
			case DEVMATE_WD_TIMEOUT:
				printf("devmate_wd_add: %s timed out\n", wdname);
				exit(21);
			case DEVMATE_WD_NAKED:
				printf("devmate_wd_add: %s NAK\n", wdname);
				exit(22);
			case DEVMATE_WD_ACKED:
				break;
			default:
				printf("devmate_wd_add: %s unexpected response %d\n", wdname, rc);
				exit(20);
		}
	}
	for (i = 0; i < NUM_WD; i++)
		nexthit[i] = (uint16)MS_TIMER + hitrate[i];
	hitcount = 0;

	/*
	 * Set up TCP echo
	 */
	rc = devmate_sock_init(NULL, NULL, NULL, NULL, NULL, NULL);
	if (rc <= 0) {
		printf("Failed devmate_sock_init.\n");
		exit(20);
	}
	/* Open sockets in passive mode */
	for (sock = 0; sock < NSOCK; sock++) {
		state[sock] = S_CLOSED;
	}
	sock = 1;

	/*
	 * Set up the filesystem subsystem demo
	 */
	timer = SEC_TIMER + UPDATE_RATE;	// update every this many seconds

	/*
	 * Set up the logging subsystem demo
	 */
	logstate = S_LOG_INIT;
	log_oldseconds = 0;
	log_seconds = 0;
	messagenum = 0;
	facilitynum = 0;

	/*
	 * Set up the SMTP subsystem demo
	 */
	devmate_smtp_setserver(MY_SMTP_SERVER);
	devmate_smtp_setdomain(MY_SMTP_DOMAIN);
	strcpy(message,
	  "Warning! Warning!\r\nTemperature in the computer room over 40 deg C!"
	  );
	mailsent = 0;
	devmate_smtp_sendmail(TO, FROM, SUBJECT, message);

	/*
	 * Main loop
	 */
	for (;;) {
		/*
		 * Drive the target communications (including the DeviceMate variable
		 * support).
		 */
		devmate_tick();

		/*
		 * Do variable processing
		 */
		seconds = SEC_TIMER - starttime;
		if (((seconds % 10) == 0) && (seconds > oldseconds)) {
			oldseconds = seconds;

			devmate_var_update("seconds");

			devmate_var_update("string");
			devmate_var_update("float");
			
			floatvar += 3.14159;
			if (strvar[0] == 'G')
				strcpy(strvar, "Bad bunny.");
			else
				strcpy(strvar, "Good bunny.");
		}

		/*
		 * Do watchdog processing
		 */
		for (i = 0; i < NUM_WD; i++) {
			if ((int16)((uint16)MS_TIMER - nexthit[i]) > 0) {
				sprintf(wdname, "test%d", i+1);
				printf("hitting %s\n", wdname);
				devmate_wd_hit(wdname);
				nexthit[i] = (uint16)MS_TIMER + hitrate[i];
				if (!i)
					hitcount++;
			}
		}

		/*
		 * Do TCP echo processing
		 */
		sm1 = sock - 1;
		st = state[sm1];
		b = &buf[sm1][0];
		rxb = &rxbuf[sm1][0];
		ntx = num_tx[sm1];
		nrx = num_rx[sm1];

		switch (st) {
			case S_LISTEN:
				if (devmate_tcp_isestablished(sock)) {
					printf("Socket %d established\n", sock);
					st = S_DATE;	// fall thru
					strcpy(b, "Hello world\n");
				}
				else
					break;
			case S_DATE:
				len = strlen(b);
				if (ntx == len) {
					ntx = 0;
					printf("Socket %d going to echo\n", sock);
					st = S_ECHO;
					break;
				}
				rc = devmate_tcp_fastwrite(sock, b+ntx, len-ntx);
				if (rc < 0) {
					printf("fastwrite error sock %d\n", sock);
					st = S_CLOSE;
					break;
				}
				ntx += rc;
				break;
			case S_ECHO:
				len = sizeof(rxbuf[0]);
				if (len > nrx) {
					rc = devmate_tcp_fastread(sock, rxb+nrx, len-nrx);
					if (rc < 0) {
						printf("fastread error sock %d\n", sock);
						st = S_CLOSE;
						break;
					}
					nrx += rc;
				}
				if (nrx > ntx) {
					rc = devmate_tcp_fastwrite(sock, rxb+ntx, nrx-ntx);
					if (rc < 0) {
						printf("fastwrite echo error sock %d\n", sock);
						st = S_CLOSE;
						break;
					}
					ntx += rc;
					if (rc) {
						len = nrx - ntx;
						memcpy(rxb, rxb+ntx, len);
						nrx -= ntx;
						ntx = 0;
					}
				}
				break;
			case S_CLOSE:
				devmate_tcp_close(sock);
				printf("socket %d closing...\n", sock);
				st = S_CLOSING;
			case S_CLOSING:
				if (devmate_tcp_isclosed(sock)) {
					printf("...socket %d closed.\n", sock);
					st = S_CLOSED;
					// fall thru;
				}
				else
					break;
			case S_CLOSED:
				if ((rc = devmate_tcp_listen(sock, 1234+sm1, NULL, 0)) > 0) {
					printf("Socket %d listening\n", sock);
					st = S_LISTEN;
					ntx = 0;
					nrx = 0;
				}
				else if (rc < 0) {
					printf("Listen failed\n");
					st = S_CLOSE;
				}
				break;
		}
		
		state[sm1] = st;
		num_tx[sm1] = ntx;
		num_rx[sm1] = nrx;
		sock++;
		if (sock == NSOCK+1)
			sock = 1;

		/*
		 * Do filesystem processing
		 */
		worker(&counter1);
		worker(&counter2);
		worker(&counter3);
		worker(&counter4);

		update_file();

		if(SEC_TIMER > timer) {
			update_flag = 1;
			timer = SEC_TIMER + UPDATE_RATE;
		}

		/*
		 * Do logging processing
		 */
		switch (logstate) {
		case S_LOG_INIT:
			devmate_log_init();
			logstate = S_LOG_WAIT;
			break;
		case S_LOG_WAIT:
			if (devmate_log_status() == 1) {
				logstate = S_LOG_SEND;
			}
			break;
		case S_LOG_SEND:
			log_seconds = SEC_TIMER - starttime;
			if (((log_seconds % 12) == 0) && (log_seconds > log_oldseconds)) {
				log_oldseconds = log_seconds;

				sprintf(logbuf, "Log message:  %s", test_messages[messagenum]);
				printf("Sending '%s'...\n", logbuf);
				rc = devmate_log_put(facilitynum << 3, logbuf, strlen(logbuf));
				printf("...rc = %d\n", rc);
				messagenum = (messagenum + 1) % NUM_TEST_MESSAGES;
				if (facilitynum == 0) {
					facilitynum = 1;
				} else {
					facilitynum = 0;
				}
				switch (rc) {
				case 0:
					break;
				case 1:
					printf("  Not buffered!\n");
					break;
				case -2:
					printf("  Message too long!\n");
					break;
				default:
					printf("  Logging unavailable - re-establishing...\n");
					logstate = S_LOG_INIT;
					break;
				}
			}
		}

		/*
		 * Do SMTP processing
		 */
		if (mailsent == 0) {
			rc = devmate_smtp_mailtick();
			if (rc == DM_SMTP_SUCCESS) {
				printf("Email message sent!\n");
				mailsent = 1;
			} else if (rc == DM_SMTP_PENDING) {
				// Do nothing
			} else {
				printf("Error sending email message!\n");
				mailsent = 1;
			}
		}
	}
}
