/*******************************************************************************
        ucos2.c
        Z-World, 2000

        A sample data-gathering app, that combines a task reading from the
        serial port with another task to report this data to the user over the
        network, with associated local messaging and processing.
*******************************************************************************/
 
/***********************************
 * Configuration                   *
 * -------------                   *
 * All fields in this section must *
 * be altered to match your local  *
 * network settings.               *
 ***********************************/

 /*
 * You must change the following values to whatever
 * your local IP address and netmask are. Contact your
 * network administrator for these numbers.
 */
#define MY_IP_ADDRESS   "10.10.6.105"
#define MY_NETMASK      "255.255.255.0"

/* Serial port configuration */

/*
 * The Speed at which to run the serial port
 */
#define SERIAL_PORT_SPEED 9600

/*
 * The size of the read/write buffers in the serial port driver.
 * You probably don't have to change this
 */
#define CINBUFSIZE	1023
#define COUTBUFSIZE	31

/********************************
 * End of configuration section *
 ********************************/
  
/* MCOS configuration */
#define OS_MAX_TASKS           2  		// Maximum number of tasks system can create (less stat and idle tasks)
#define OS_SEM_EN					 1			// Enable semephores
#define OS_SEM_POST_EN			 1       // Enable old style post to semaphore
#define OS_MAX_EVENTS			 7			// MAX_TCP_SOCKET_BUFFERS + 2 + 1 (1 semaphore is used in this app)
#define STACK_CNT_256			 2			// LED update task + main()
#define STACK_CNT_2K         	 1			// TCP/IP needs a 2K stack

/*
 * IMPORTANT NOTE!
 *
 * It is important to note that the TCP/IP task REQUIRES a 2K stack!
 * This is because the function resolve() (a DNS lookup) creates a
 * udp socket on the stack. If DNS lookups are NOT needed, and
 * resolve() is never called, it is possible to run the TCP/IP stack
 * in only a 1K stack.
 *
 * Also note, all TCP/IP stack functions should be call from ONE (1)
 * thread only, or data corruption and/or system instability may result!
 */

#memmap xmem
#use "ucos2.lib"
#use "dcrtcp.lib"

/* buffer for passing messages between the two tasks */
#define MSG_BUF_SIZE		256
char	message_buffer[MSG_BUF_SIZE];
int	base, boundary;

/* a semaphore for locking the above buffer */
OS_EVENT	*Semaphore;

/* buffer and count used by the transmitter */
char	send_buf[CINBUFSIZE + 1], temp_buf[CINBUFSIZE + 1];
int	offset;

/* our outgoing socket */
tcp_Socket s;

/*
 * The task that reads from the serial port, and stores the data in the
 * message_buffer. Idealy, most of this (reading from the serial port) should
 * be in the serial ISR, as we delay an OS level tick each time we poll for
 * new data. This is a rather serious ammount of latency between polls, and
 * could easily lead to data loss.
 */
void reader_task(void *ptr)
{
	INT8U	error;
	char *p;
	int count, temp;

	/* initilization part */
	serCopen(SERIAL_PORT_SPEED);
		
	for(;;) {
		count = serCread(temp_buf, CINBUFSIZE, 0);
		if(count) {
			OSSemPend(Semaphore, 0, &error);

			p = temp_buf;
			while(count) {
				count--;

				temp = base + 1;
				if(temp == MSG_BUF_SIZE)
					temp = 0;
				if(temp == boundary) {
					/* no room; drop the character on the floor */
					continue;
				}

				message_buffer[base] = *p;
				p++;
				base = temp;
			}
			
			OSSemPost(Semaphore);

		} else {
			/* no data read - yield */
			OSTimeDly(1);
		}
	}
}

void process_data(char c)
{
	/* do extra processing on c here - nothing is done for now */

	/* queue the data for transmit */
	send_buf[offset] = c;
	offset++;
}

void send_data(void)
{
	int cnt;
	
#GLOBAL_INIT
	{
		offset = 0;
	}

	/* send the buffer */
	cnt = 0;
	while(cnt < offset) {
		cnt += sock_fastwrite(&s, send_buf + cnt, offset - cnt);
		tcp_tick((sock_type *)&s);
	}	

	/* reset */
	offset = 0;	
}

void writer_task(void *ptr)
{
	INT8U	error;
	int temp;

	/*
	 * Listen on a socket for a connection to be made.
	 *
	 * NOTE - this should really be making a connection to a
	 * custom client program in a more active manner, but for
	 * purposes of this sample program, listening should be
	 * sufficient, and does not require a sample program to
	 * be compiled on another machine, as it only requires
	 * 'telnet'.
	 */
	tcp_listen(&s, 23, 0, 0, NULL, 0);

	/* wait for a connection to be made */
	while(!sock_established((sock_type *)&s))
		tcp_tick((sock_type *)&s);
		
	for(;;) {
		tcp_tick((sock_type *)&s);
		OSSemPend(Semaphore, 0, &error);
		while(base != boundary) {
			/* while unprocessed data exists... */

			temp = boundary + 1;
			if(temp == MSG_BUF_SIZE)
				temp = 0;

			/* as we are holding the semaphore at the moment, this should be fast */
			process_data(message_buffer[boundary]);

			boundary = temp;
		}
		OSSemPost(Semaphore);

		send_data();
	}
}

main()
{
	INT8U error;

	OSInit();

	sock_init();		// init the TCP/IP stack

	/* init our buffer */
	memset(message_buffer, 0, MSG_BUF_SIZE);
	base = boundary = 0;

	/* create our semaphore */
	Semaphore = OSSemCreate(1);
	
	/* Build the two tasks */
	error = OSTaskCreate(reader_task, 	NULL, 256, 0);
	error = OSTaskCreate(writer_task,	NULL, 2048, 1); // does TCP/IP, and therefor need the 2k stack

	/* start it all running */
	OSStart();
}