/*==============================================================================*/
/* CAN data visualizer for the '04 Toyota Prius on a Sharp SL-C700		*/
/*  Picture data is written in the Linux Frame Buffer...			*/
/*										*/
/* DATE:	2004-Jun-06 23:15:08						*/
/*										*/
/* Copyright (c) 2004 Attila Vass						*/
/*										*/
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"),to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell	*/
/* copies of the Software, and to permit persons to whom the Software is 	*/
/* furnished to do so, subject to the following conditions:			*/
/*										*/
/* The above copyright notice and this permission notice shall be included in	*/
/* all copies or substantial portions of the Software.				*/
/*										*/
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR	*/
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,	*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE	*/
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER	*/
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING	*/
/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS	*/
/* IN THE SOFTWARE.								*/
/*==============================================================================*/
/*

gcc ggana.c -o ggana

*/


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <time.h>
#include <signal.h>
#include <termios.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>


//#define	SIMULATION_ONLY	1
//#define	DEBUG	1

#define	BAUD		B115200
#define	ALARM_INTERVAL	2

#define	VERSION_STRING	"v1.1 by Attila Vass"

#define DEVICE "/dev/ttyS0"

#ifndef	ABS
#define	ABS(a)	(((a)<0)?(-(a)):((a)))
#endif

#define	WIDTH	640
#define	HEIGHT	480

#define	BKG_R	((unsigned char)0x00)
#define	BKG_G	((unsigned char)0x00)
#define	BKG_B	((unsigned char)0x00)

char		*WorkData=NULL;
struct ImageBufferStructure
{
	unsigned char	R,G,B;
}		*ImageBuffer=NULL;
char		ProcessGo=1;

int		Port=-1,R_ACR=0,R_AMR=0xFFFF,MessageAnalyzed,SampleInterval=1,CurrSamIntv=0,DP=0;
char		*IB=NULL;
int		IBS=0;
int		IBCP=0;
int		CRSignal=0;
unsigned int	StatusFlag=0,NMPF;
unsigned char	NeedSynced,SyncCounter=29;
unsigned char	ShowValues=1;
char		Version[4];
char		Serial[16];
char		Message[32];
unsigned long	IC_MessageID;
unsigned char	IC_MessageLength;
unsigned char	IC_Message[8];
unsigned char	IC_P_Message[8]={0,0,0,0,0,0,0,0};
unsigned int	IC_A_Message[8]={0,0,0,0,0,0,0,0};

struct	termios	oldT,newT;

int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
char *fbp = 0;

#define	FONTMAP_WIDTH	175
#define	FONTMAP_HEIGHT	31
#define	FONTMAP_COLORS	10

struct {
char		letter;
unsigned char	Color;
} Font_Color[FONTMAP_COLORS] = {
{'.',0x00},
{'@',0xFF},
{'#',0xF0},
{'&',0xE8},
{'%',0xE0},
{'$',0xA0},
{'-',0x80},
{'+',0x60},
{'=',0x40},
{'*',0x20},
};

char *Font_Map[FONTMAP_HEIGHT] = {
"..+@@#+....$%%%.....+#@#$....+#@&*.......+%...=%%%%%=.....$%+...%%%%%%%...-#@#-....+@@#$........................................................................................",
".%@#%#@%...%@@@....%@#%@@+..$@#&@#......$@@...-@@@@@-....=@@=...@@@@@@@..=@@%@@=..&@#%@@+.........$#@&=..%@@-..*&@#-..-#@+.....&@..-@@@%...#@*.%@@@@%..%@#*..-#@&*..............",
"*@@*.*@@*....@@...=@@*.*@@*.&@$.&@-....*@@@...%@-........#@+........$@&..+@$.+@%.-@#..*@@*.......=@%-##..=+@-..##-%@=.@&$@$...-@@..+#--=..$@$..=--$@+.$@$##.=@%-##=.............",
"=@%...%@-....@@...-@%...%@-.....&@-....&@@@...%@-.......+@#.........#@-..%@-.-@%.%@-...%@-.......%@..-@-..-@-.=@$..@%.-**@%...#@@..%%....*@&......+@*.%@.%@.%@..$@%.............",
"=@%...%@-....@@...*-=...&@-...$#@@*...$@$@@...@@+-+=...=@@&%$......$@#...=@#-#@$.%@-...%@-..$%=..%@..-@-..-@-.*-*.=@%..=@@-..+#&@..@#@#-.+@&%*....#&..=@&@&.%@..-@-.............",
"-@%...%@-....@@........%@#....%@@#*..*##.@@..*@@@@@@-..#@@@@@+.....&@$....&@@@#..$@#..*@@*..%@-..%@..-@-..-@-....*#@=..=&@%.*@$%@..@#-&@=@#%@#...$@-..%@%##*$@%-#@..............",
"-@%...%@-....@@.......%@@-....=-&@#..%@=.@@..*+&*.$@@.-@#=.-@@*...=@@*...%@%-&@%.*#@#%@@&........%@..-@-..-@-...*#@-.....+@=&&.%@.....*@%@*.$@-..##..=@+.=@+.+@@@$........$%=...",
"=@%...%@-....@@......-@@-........#@=-@%..@@........&@=%@$...&@=...&@+...=@#...#@=.*&@#@@*........%@..-@-..-@-..*#@-..$+..-@+@&%#@%-%*.*@%@..-@-.=@$..-@-..@%..*#&.........%@-...",
"=@%...%@-....@@.....-@@-...$%=...%@=#@&%%@@%=$%=...%@=%@-...%@=..*@@=...-@%...%@-....#@$....$%=..=@%-##*..-@-.*#@%--=+@+-##=%%%#@%*@&-%@=@%-##..&@*..*##-%@-..%@*...............",
"*@#..*@@=....@@....-@@-....+@&..*@@*@@@@@@@@-+@&..*@@*$@&..*@@*..+@&....=@#..*#@*...%@&.....%@-...$#@&=...-@-.-@@@@@%.+@@&*....%@..=&@#-.$#@&=.*@+....=&@#$..-@+................",
".%@&$#@&.....@@...=@@#%%%%=*#@&$#@%......@@..*#@&$#@+..#@&$#@%..*@@-.....%@&$#@%...-@@=.........................................................................................",
".*%@@@&*.....@@...-@@@@@@@-.=&@@@-.......@@...*&@@@+...*&@@@%...+@#......*%@@@+...*#@+..........................................................................................",
"....*.........................**................**........*.................*........................................................................................@@@...@@*..",
"...-*...---*....--*..=--=...=---**---*..*--...*-..*-.==...-**-..=-.-*..=-*...--.=-...-*...--*...---*....--*...---=..*-*.=---===..-*-=...=--=..-..*-==...---*..=-=---=@$$=..*@$..",
"..=@+...@@@#*.-@&%@+.%#%@#*.%#%%=-@%%=.+@%&@-.-@..-@.%%...@--@.-@-.@-..%@+..*@@.%@$..@-.-@&%@+..@&#@=.-#&%@%..@&&@-=@%@-$&@%$%%..@-+&...&%%%.=@=.%&-@=.$@=#+.*#+$%%@+@$.....@$..",
"..+@@...@-*@-=@-..-%=%%.*##.%%...-@...$#*..-%*-@..-@.%%...@--@*#%..@-..%@#..$@@.%@#..@-=@-..*#+.@-.&%=@-..*#+.@-.%@%%.$$.-@..%%..@-=@=.=@=-@.$@$.#$.%#*@+.$@.+#...$@*@$.....@$..",
".*@-@$..@+$@*+&......%%..*@=%&--*-@--*#$......-@--+@.%%...@--@%&...@-..%%@=.#%@.%%#+.@-+&....$#.@-*#++&....$@.@-.$@$@%=..-@..%%..@-.&+.+#.*@=%&%=@*.*#@&...#+@-...#+.@$.....@$..",
".$@*&&..@&%&*%%......%%...@-%#%%=-@%%=@-.%%%%$-@%%&@.%%...@--@@$...@-..%%+#=#-@.%%=@*@-%%....-@.@@@&*%&--..-@.@@@@-.$&@+.-@..%%..@-.$#.#$..&$@-@$#...%@$...$@&...$@*.@$.....@$..",
".#@@@@=.@-.&+$#......%%..-@*%%...-@...#+.--$@%-@..-@.%%...@--@$@*..@-..%%=#+#-@.%%.%&@-$#....+&.@-...$@&&@++#.@&@=....$@.-@..%%..@-..@$@*..+#@.##+..=@&#*...@-...#+..@$.....@$..",
"=@=..&%.@-*#+.#%*.%@=%%.$@%.%%...-@...-@$.*##*-@..-@.%%-%*@--@.&#..@-..%%.#@=-@.%%.*@@-*#%*.$@-.@-....#&*=@@$.@-&#.%&.%#.-@..$#*-@*..%#%...=@%.%@=..#%.#%...@-..$@*..@@@...@@$..",
"&&...$@*@@@%*.*+@@&-.%@@#+..%@@@--@....=&@@%*.-@..-@.%%*&@%.-@.*#%.@@@%%%.+#.-@.%%..+@-.*+@@&=..@-....*+@@&%@.@-*#%*&@#*.-@...+@#-...=@=....#$.-@..%#*.=@-..@-..%@@@%*$$=..*==..",
".......==...............==........=*......*-....*-.-**-....=*...........................................==......................................................................",
".......%%...............%%.......-@=......-@....-@.%=-@....@=...........................................%%....................................-#@-.=@*.............-=....*+++#..",
".=%%=%=%%+%$...$%+*..$%%&%..$%+**&&**+%$%=-@+%=.=*.%=-@.-%*@==%+%=$%$..%$%+...$%+*.$++%$...+%+%==%+.-%$=##$$$.=%*%=.=%-%.=%.=%*%=*%-+$..%$%%%%@%%@=++...-@-........@%....++&+#..",
"*@%-#@-%@$$@+.%#-##.$@$$@%.#&-%#=##=#&-&@--@%+@*-@.@--@*#$.@=-@$%@#$#$.@#$@$.%#-%#*%@$$@$.&#-%@--@&=@+@+&&=%%.-@.#+.%&*@=+@*+&.%&+#.$@.=@=--#&@$$@-@*...*-*........@%....*+&+#..",
"+&..-@-%%..$@=@*.*-*#$..%%-@$*-@+%%-@*.*@--@..@--@.@--@%%..@=-@..@-.%%.@-.%%=@*..#+%&..$#=@*.*@--@.=@+=.%%.%%.-@.$#.@-.&$&@-#$.*@@=.*@-%&..$@*%@@%$%........%@@%.@@@@@@..*+&+#..",
"%%..*@-%%..-@-@.....@-..%%-@%%%%=%%-@...@--@..@--@.@--@#+..@=-@..@-.%%.@-.%%-@...%+%%..-@-@...@--@..-&@$%%.%%.-@.*@+#..$&@-&@*.*#@*..%%@$.*@$..=-.#.....*-*.=--=.--@&--..*+&+#..",
"$@=.%@-%#**&%*#+.-%*&#**#%.#+.*=*%%*@$.-@--@..@--@.@--@=@-.@=-@..@-.%%.@-.%%*#+.-@*%#**&&*@+.-@--@.=%.&%%%.+&.+@..%@+..*@%*@%..%#%&..-@#..&&.....=&-#@-.-@-........@%....*+&+#..",
".+@@+@-%##@&*.=&@#-.*&@#&%.*&@#$.%%.-#@%@--@..@--@.@--@.+@*@=-@..@-.%%.@-.%%.=&@#-.%#@@&*.-#@%@--@.*&@&*%%.*&@&@..-@*...&-.&-.-@=*@+..@+..@@@@...&=@%%@............%$..**++&++##",
"....................................*%--@*........=@=..............................%%.........@-.....................................-@*........*#.@$$@..................*+&+#..",
".....................................-##-.........-+...............................%%.........@-.....................................&%.........-$.%@@%...................*+#..."
};

struct
{
char	letter;
int	x;
int	y;
int	width;
int	height;
} bignumbers[]={
{'0',0,0,10,13},
{'1',10,0,7,13},
{'2',17,0,10,13},
{'3',27,0,9,13},
{'4',36,0,9,13},
{'5',45,0,9,13},
{'6',54,0,9,13},
{'7',63,0,9,13},
{'8',72,0,9,13},
{'9',81,0,9,13},
{':',91,0,5,13},
{'`',-1,-1,-1,-1}
};


struct
{
char	letter;
int	x;
int	y;
int	width;
int	height;
} fonts[]={
{'A',0,13,8,8},
{'B',8,13,5,8},
{'C',13,13,8,8},
{'D',21,13,7,8},
{'E',28,13,5,8},
{'F',33,13,5,8},
{'G',38,13,8,8},
{'H',46,13,6,8},
{'I',52,13,3,8},
{'J',55,13,5,8},
{'K',60,13,6,8},
{'L',66,13,5,8},
{'M',71,13,8,8},
{'N',79,13,8,8},
{'O',87,13,8,8},
{'P',95,13,6,8},
{'Q',101,13,8,8},
{'R',109,13,6,8},
{'S',115,13,5,8},
{'T',120,13,5,8},
{'U',125,13,6,8},
{'V',131,13,7,8},
{'W',138,13,9,8},
{'X',147,13,7,8},
{'Y',154,13,6,8},
{'Z',160,13,5,8},
{'a',0,21,7,10},
{'b',7,21,6,10},
{'c',13,21,7,10},
{'d',20,21,6,10},
{'e',26,21,6,10},
{'f',32,21,4,10},
{'g',36,21,6,10},
{'h',42,21,6,10},
{'i',48,21,2,10},
{'j',50,21,3,10},
{'k',53,21,6,10},
{'l',59,21,2,10},
{'m',61,21,9,10},
{'n',70,21,6,10},
{'o',76,21,7,10},
{'p',83,21,6,10},
{'q',89,21,7,10},
{'r',96,21,3,10},
{'s',99,21,5,10},
{'t',104,21,3,10},
{'u',107,21,5,10},
{'v',112,21,6,10},
{'w',118,21,8,10},
{'x',126,21,6,10},
{'y',132,21,6,10},
{'z',138,21,4,10},
{'%',142,21,9,10},
{':',151,21,5,10},
{'.',169,0,4,10},
{'-',156,21,5,10},
{'+',160,21,6,10},
{'0',96,1,8,10},
{'1',104,1,5,10},
{'2',109,1,8,10},
{'3',117,1,6,10},
{'4',123,1,7,10},
{'5',130,1,7,10},
{'6',136,1,7,10},
{'7',143,1,6,10},
{'8',149,1,7,10},
{'9',156,1,7,10},
{' ',163,0,5,10},
{'^',168,21,9,10},
{']',169,12,5,10},
{'[',165,12,5,10},
{'`',-1,-1,-1,-1}
};




#define	XOFFS	0
#define	YOFFS	0
#define	LLENGTH	960
#define	BBPX	16
#define	BBPXSH	2

void CopyDisplayBufferToScreen(int x, int y, int w, int h);
void IntReXSigCatch(int sig);

void PrintHexa(int NumOfDigits,int Number)
{
register int a,b,c=0;

	for(a=(NumOfDigits-1);a>=0;a--)
	{
		b=((Number>>(a<<2))&0xF);
		if(b<10)
			Serial[c]=b+'0';
		else
			Serial[c]=('A'+b-10);
		++c;
	}
Serial[c]='\0';
}

int Poll(void)
{
register int a,b,c,d,ret,go=1;

	if(IBCP>=IBS) return(0);
	if((ret=read(Port,IB+IBCP,(IBS-IBCP)))<1) return(0);
	IBCP+=ret;

	while(go)
	{
		a=IBCP;
		for(b=0;b<a;b++)
		{
			if((IB[b]==13)||(IB[b]==7))	// Terminator
			{
				switch(IB[0])	// 0 is OK, because we tidy up the buffer as we go...
				{
					case 'V' :	// version
						for(c=0;c<4;c++) Version[c]=IB[c+1];
					break;
					case 'N' :
						for(c=0;c<4;c++) Serial[c]=IB[c+1];
					break;
					case 'F' :	// Status
						if(IB[1]>='A')
							StatusFlag=(IB[1]-'A')+10;
						else
							StatusFlag=(IB[1]-'0');
						StatusFlag<<=4;
						if(IB[2]>='A')
							StatusFlag+=(IB[2]-'A')+10;
						else
							StatusFlag+=(IB[2]-'0');
						CRSignal=1;
					break;
					case 'z' :	// transmission ack
					case 'Z' :
						CRSignal=1;
					break;
					case 13 :
						CRSignal=1;
					break;
					case 7 :
						CRSignal=1;
					break;
				}
		// Tidy up...
				if(IBCP==(b+1))	// This is the only message, so no copy req.
					IBCP=0;
				else
				{
					a=b+1;		// from here
					c=IBCP-b;	// this many
					for(d=0;d<c;d++)
					{
						IB[d]=IB[d+a];
					}
					IBCP-=(b+1);
				}
				b=a+2;	// quit from loop in 'C' fashion...
			}
		}
		if(b==a) go=0;
	}
return(ret);
}


int WriteToPort(char* ZTV)
{
register a=0,b=0,str=0;

	while(*(ZTV+a)!='\0') ++a;
	b=write(Port,ZTV,a);
//	tcflush(Port,TCOFLUSH);

return(b);
}

int SetUpCAN(void)
{
int	to,a;

	IBS=16*1024;
	if((IB=(char*)malloc((size_t)IBS))==NULL)
	{
		printf("\n\nCan not allocate buffer with %d size...",IBS);fflush(stdout);
		return(0);
	}
	Version[0]='\0';
	Serial[0]='\0';Serial[4]='\0';
	IBCP=0;

	if(OpenAndConfigurePort())
	{
		return(0);
	}

printf("\nPort opened.");fflush(stdout);

	sprintf(Message,"\015\015\015");
	WriteToPort(Message);

	sprintf(Message,"V\015");	// get version
	WriteToPort(Message);

	to=1000000;
	while(to)
	{
		if(Poll())
		{
			if(Version[0]!='\0') to=1;
		}
		--to;
	}

printf("\nVersion = HW : %c.%c  SW : %c.%c",Version[0],Version[1],Version[2],Version[3]);fflush(stdout);

	sprintf(Message,"N\015");	// get Serial
	WriteToPort(Message);

	to=1000000;
	while(to)
	{
		if(Poll())
		{
			if(Serial[0]!='\0') to=1;
		}
		--to;
	}

printf("\nSerial ID = %s  ( %02x:%02x:%02x:%02x )",Serial,Serial[0],Serial[1],Serial[2],Serial[3]);fflush(stdout);

	CRSignal=0;
	sprintf(Message,"S6\015");	// CAN with 500Kbps S0-10 S1-20 S2-50 S3-100 S4-125 S5-250 S7-800  S8-1M
	WriteToPort(Message);

	to=1000000;
	while(to)
	{
		if(Poll())
		{
			if(CRSignal) to=1;
		}
		--to;
	}

	CRSignal=0;

	PrintHexa(4,R_ACR);
	sprintf(Message,"M%s%s\015",Serial,Serial);
	WriteToPort(Message);
	to=1000000;
	while(to)
	{
		if(Poll())
		{
			if(CRSignal) to=1;
		}
		--to;
	}
	CRSignal=0;

	PrintHexa(4,R_AMR);
	sprintf(Message,"m%s%s\015",Serial,Serial);
	WriteToPort(Message);
	to=1000000;
	while(to)
	{
		if(Poll())
		{
			if(CRSignal) to=1;
		}
		--to;
	}

#if 0
	CRSignal=0;
	sprintf(Message,"X1\015");	// Turn on out poll
	WriteToPort(Message);
	to=1000000;
	while(to)
	{
		if(Poll())
		{
			if(CRSignal) to=1;
		}
		--to;
	}

printf("\nAuto Poll setup.");fflush(stdout);

	CRSignal=0;
	sprintf(Message,"Z0\015");	// Turn off timestamp
	WriteToPort(Message);
	to=1000000;
	while(to)
	{
		if(Poll())
		{
			if(CRSignal) to=1;
		}
		--to;
	}

printf("\nNo timestamp setup.");fflush(stdout);

#endif


	CRSignal=0;
	sprintf(Message,"O\015");	// Open the CAN channel
	WriteToPort(Message);
	to=1000000;
	while(to)
	{
		if(Poll())
		{
			if(CRSignal) to=1;
		}
		--to;
	}

printf("\nCAN opened.");fflush(stdout);

return(1);
}


void CleanUp(void)
{
int		a;
float		MPG;

	if(Port>=0)
	{
		sprintf(Message,"C\015");	// Close the CAN channel
		WriteToPort(Message);
		close(Port);
		Port=-1;
	}
	if(IB!=NULL) {free((void*)IB);IB=NULL;}
	if(ImageBuffer!=NULL) {free((void*)ImageBuffer);ImageBuffer=NULL;}
	if(WorkData!=NULL) {free((void*)WorkData);WorkData=NULL;}
	munmap(fbp, screensize);
	close(fbfd);
	ioctl(0,TCSETS,&oldT);
	
printf("\n\n");fflush(stdout);
}

void TransferFont(int fsx, int fsy, int tx, int ty, int fw, int fh,int zoom)
{
register char	*FRPtr,ch;		// current row's string ptr
register int	cr=fsy,tr=fsy+fh;	// current / target row
register int	cc,sc=fsx,tc=fsx+fw;	// current / start / target column
register int	cp,sz1,sz2;			// colour pointer
register int	WBPtr,WBpy=ty,cv;	// Pointer to workbuffer
register unsigned char	Rv,Gv,Bv;

	if(WBpy<0) {cr-=WBpy;WBpy=0;}				// don't copy if out of screen (y<0)
	if(tx<0) {sc=fsx-tx;tx=0;}

	if((WBpy+(fh*zoom))>=HEIGHT) tr=fsy+(HEIGHT-WBpy);	// don't copy if out of screen (y>width)
	if((tx+(fw*zoom))>=WIDTH) tc-=(tx+(fw*zoom)-WIDTH);

	while(cr<tr)	// raw...
	{
		FRPtr=Font_Map[cr];		// ptr to string within font map...
		for(sz2=0;sz2<zoom;sz2++)
		{
			for(cc=sc;cc<tc;cc++)	// column
			{
				ch=FRPtr[cc];
				for(sz1=0;sz1<zoom;sz1++)
				{
					WBPtr=(tx+sz1+((cc-sc)*zoom)+(WBpy+sz2)*WIDTH);
					if(WBPtr>=0)
					{
						for(cp=0;cp<FONTMAP_COLORS;cp++)
						{
							if(Font_Color[cp].letter==ch)
							{
								Rv=(unsigned char)ABS((int)ImageBuffer[WBPtr].R-(int)Font_Color[cp].Color);
								Gv=(unsigned char)ABS((int)ImageBuffer[WBPtr].G-(int)Font_Color[cp].Color);
								Bv=(unsigned char)ABS((int)ImageBuffer[WBPtr].B-(int)Font_Color[cp].Color);
								ImageBuffer[WBPtr].R=Rv;
								ImageBuffer[WBPtr].G=Gv;
								ImageBuffer[WBPtr].B=Bv;
							}
						}
					}
				}
			}
		}
		WBpy+=zoom;
		cr++;
	}
}

void PutMyString(char *Text,int x, int y, int usebignums, int zoom)
{
int	a=0,b,px=x,notfound;

	while(Text[a]!='\0')
	{
		notfound=1;
		if(usebignums)
		{
			b=0;
			while((bignumbers[b].letter!=Text[a])&&(bignumbers[b].x>=0)) ++b;
			if(bignumbers[b].x>=0)
			{
				TransferFont(bignumbers[b].x,bignumbers[b].y,px,y-2,bignumbers[b].width,bignumbers[b].height,zoom);
				px+=bignumbers[b].width*zoom;
				notfound=0;
			}
		}
		if(notfound)
		{
			b=0;
			while((fonts[b].letter!=Text[a])&&(fonts[b].x>=0)) ++b;
			if(fonts[b].x>=0)
			{
				if((fonts[b].letter>='0')&&(fonts[b].letter<='9'))
					TransferFont(fonts[b].x,fonts[b].y,px,y-1,fonts[b].width,fonts[b].height,zoom);
				else
				{
					if(fonts[b].letter=='%')
					{
						if(!usebignums)
							TransferFont(fonts[b].x,fonts[b].y,px,y-2,fonts[b].width,fonts[b].height,zoom);
						else
							TransferFont(fonts[b].x,fonts[b].y,px,y+4,fonts[b].width,fonts[b].height,zoom);
					}
					else
						TransferFont(fonts[b].x,fonts[b].y,px,y+(usebignums*9),fonts[b].width,fonts[b].height,zoom);
				}
				px+=fonts[b].width*zoom;
			}
		}
		++a;
	}
}

int GetMyStringLength(char *Text, int usebignums, int zoom)
{
int	a=0,b,px=0,notfound;

	while(Text[a]!='\0')
	{
		notfound=1;
		if(usebignums)
		{
			b=0;
			while((bignumbers[b].letter!=Text[a])&&(bignumbers[b].x>=0)) ++b;
			if(bignumbers[b].x>=0)
			{
				px+=bignumbers[b].width*zoom;
				notfound=0;
			}
		}
		if(notfound)
		{
			b=0;
			while((fonts[b].letter!=Text[a])&&(fonts[b].x>=0)) ++b;
			if(fonts[b].x>=0)
				px+=fonts[b].width*zoom;
		}
		++a;
	}
return(px);
}

int CreateMainWindow(void)
{
	fbfd = open("/dev/fb0",O_RDWR);			// Open the file for reading and writing
	if(!fbfd)
	{
		printf("Error: cannot open framebuffer device.\n");
		close(fbfd);
		return(-1);
	}


	if(ioctl(fbfd, FBIOGET_FSCREENINFO,&finfo))	// Get fixed screen information
	{
		printf("Error reading framebuffer fixed information.\n");
		close(fbfd);
		return(-2);
	}


	if(ioctl(fbfd, FBIOGET_VSCREENINFO,&vinfo))	// Get variable screen information
	{
		printf("Error reading framebuffer variable information.\n");
		close(fbfd);
		return(-3);
	}

//	if((vinfo.xres!=WIDTH)||(vinfo.yres!=HEIGHT)||(vinfo.bits_per_pixel!=BBPX)||(vinfo.xoffset!=XOFFS)||(vinfo.yoffset!=YOFFS)||(finfo.line_length!=LLENGTH))
	if((vinfo.xres!=HEIGHT)||(vinfo.yres!=WIDTH)||(vinfo.bits_per_pixel!=BBPX)||(vinfo.xoffset!=XOFFS)||(vinfo.yoffset!=YOFFS)||(finfo.line_length!=LLENGTH))
	{
		printf("\nError, screen does not match optimized parameters :");
		printf("%dx%d, %dbpp offs:%d:%d  llng:%d\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel, vinfo.xoffset, vinfo.yoffset, finfo.line_length);
		close(fbfd);
		return(-4);
	}

	
	screensize=(WIDTH * HEIGHT * BBPXSH);		// Figure out the size of the screen in bytes

    // Map the device to memory
	fbp=(char*)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED,fbfd,0);
	if((int)fbp == -1)
	{
		printf("Error: failed to map framebuffer device to memory.\n");
		close(fbfd);
		return(-5);
	}

	if((WorkData=(char*)malloc(BBPXSH*WIDTH*HEIGHT))==NULL)
	{
		munmap(fbp,screensize);
		close(fbfd);
		return(-6);
	}

	if((ImageBuffer=(struct ImageBufferStructure *)malloc(sizeof(struct ImageBufferStructure)*WIDTH*HEIGHT))==NULL)
	{
		free((void*)WorkData);
		munmap(fbp,screensize);
		close(fbfd);
		return(-7);
	}
return(0);
}

void ClearDisplayBuffer(void)
{
register int	a,b,c;

	for(b=0;b<HEIGHT;b++)
	{
		for(a=0;a<WIDTH;a++)
		{
			c=a+b*WIDTH;
			ImageBuffer[c].R=BKG_R;
			ImageBuffer[c].G=BKG_G;
			ImageBuffer[c].B=BKG_B;
		}
	}

	c=GetMyStringLength(VERSION_STRING,0,1);
	PutMyString(VERSION_STRING,(WIDTH-(c+10)),(HEIGHT-8),0,1);
}


#define	BAR_WIDTH		68
#define	BAR_SPACING		10
#define	BAR_SIDE_SPACING	5
#define	BAR_SY			112
#define	BAR_EY			367
#define	BAR_STRING_Y		380


void PrepByteBackgrounds(void)
{
register int	x,y,x1,x2,a,c;

	for(a=0;a<8;a++)
	{
		x1=BAR_SIDE_SPACING+(a*(BAR_SPACING+BAR_WIDTH));
		x2=x1+BAR_WIDTH;
		for(y=BAR_SY;y<BAR_EY;y++)
		{
			for(x=x1;x<x2;x++)
			{
				c=x+y*WIDTH;
				ImageBuffer[c].R=80;
				ImageBuffer[c].G=0;
				ImageBuffer[c].B=10;			
			}
		}
	}
}

void SetUpMySignals()
{
	signal(SIGPIPE,IntReXSigCatch);
	signal(SIGSEGV,IntReXSigCatch);
	signal(SIGBUS,IntReXSigCatch);
	signal(SIGFPE,IntReXSigCatch);
	signal(SIGILL,IntReXSigCatch);
	signal(SIGPWR,IntReXSigCatch);
	signal(SIGHUP,IntReXSigCatch);
	signal(SIGINT,IntReXSigCatch);
	signal(SIGQUIT,IntReXSigCatch);
	signal(SIGTERM,IntReXSigCatch);
	signal(SIGIO,IntReXSigCatch);
	signal(SIGALRM,IntReXSigCatch);
}

int OpenAndConfigurePort(void)
{
struct termios newio;

	if((Port=open(DEVICE,O_RDWR|O_NOCTTY)) < 0)
	{
		printf("\nError Opening Serialport ( %s ) : '%s'",DEVICE,strerror(errno));fflush(stdout);
		return(1);
	}
	memset(&newio,0, sizeof(newio)); /* Clears termios struct  */   
	newio.c_cflag = CS8 | CLOCAL | CREAD; 
 	newio.c_iflag = IGNPAR; 
	newio.c_oflag = 0; 
	newio.c_lflag = 0; 
	newio.c_cc[VTIME] = 0;
	newio.c_cc[VMIN]  = 0;   /* read min. one char at a time  */
	if (cfsetispeed(&newio, BAUD) == -1)
	{
		printf("Error setting serial input baud rate\n");
		close(Port);
		return(1);
	}
	if(cfsetospeed(&newio, BAUD) == -1)
	{
		printf("Error setting serial output baud rate\n");
		close(Port);
		return(1);
	}
	tcflush(Port, TCIFLUSH);
	if (tcsetattr( Port, TCSANOW, &newio) == -1)
	{
		printf("Error setting terminal attributes\n");
		close(Port);
		return(1);
	}

return(0);
}

void SetUpPicture(void)
{
int	a,b,c;

	ClearDisplayBuffer();
	PrepByteBackgrounds();

	PrintHexa(3,MessageAnalyzed);
	sprintf(Message,"Analyzing ID 0x%s",Serial);
	c=GetMyStringLength(Message,0,3);
	PutMyString(Message,((WIDTH-c)>>1),20,0,3);

	if(SampleInterval==1)
		sprintf(Message,"Update on all samples.",SampleInterval);
	else
		sprintf(Message,"Update on every %d samples.",SampleInterval);
	c=GetMyStringLength(Message,0,2);
	PutMyString(Message,((WIDTH-c)>>1),62,0,2);

	CopyDisplayBufferToScreen(0,0,WIDTH,HEIGHT);
}

void ProcessSigIo()
{
struct timeval	to;
fd_set		fdset;
int		c;

	FD_ZERO(&fdset);
	FD_SET(0,&fdset);
	to.tv_sec=0;
	to.tv_usec=0;
	if(select(FD_SETSIZE,&fdset,0,0,&to)!=-1)
	{
		if(FD_ISSET(0,&fdset))
		{
			c=getchar();
			switch(c)
			{
				case 27 :
					ProcessGo=0;
				break;
				case 'v' :
					if(++ShowValues>3) ShowValues=0;
				break;
				case 'p' :
					++SampleInterval;
					SetUpPicture();
				break;
				case 'o' :
					if(--SampleInterval<1) SampleInterval=1;
					SetUpPicture();
				break;
				case ' ' :
					CopyDisplayBufferToScreen(0,0,WIDTH,HEIGHT);
				break;
			}
		}
	}
}

void IntReXSigCatch(int sig)
{
	switch(sig)
	{
		case SIGIO :
			ProcessSigIo();
		break;
		case SIGALRM	:
			NeedSynced=1;
			signal(SIGALRM,IntReXSigCatch);
			alarm(ALARM_INTERVAL);
		break;

		case SIGQUIT	: 
			ProcessGo=0;
		break;
		case SIGPWR	: 
		case SIGILL	:
		case SIGFPE	:
		case SIGBUS	:
		case SIGSEGV	:
		case SIGPIPE	:
		case SIGINT	: 
		case SIGHUP	: 
		case SIGTERM	:
			CleanUp();
			exit(-1);
	}
}

void CopyDisplayBufferToScreen(int x, int y, int w, int h)
{
register unsigned short	int*TmpWrk=(unsigned short*)WorkData;
register unsigned char	*TmpChrWrk=(unsigned char*)WorkData;
register int		a,b,c,d,wx=x,wy=y,wh=h,ww=w;

	for(b=wy;b<wh;b++)
	{
		for(a=wx;a<ww;a++)
		{
			c=a+b*WIDTH;
			TmpWrk[c]=((unsigned short)(ImageBuffer[c].R&0xF8)<<8)|((unsigned short)(ImageBuffer[c].G&0xFC)<<3)|((unsigned short)(ImageBuffer[c].B&0x1F));
		}
	}

	for(b=wy;b<wh;b++)		// Actual Copy to screen
	{

		c=((HEIGHT-1)-b)*BBPXSH+(wx*LLENGTH);
		d=wx+b*WIDTH;
		for(a=wx;a<ww;a++)
		{
			*((unsigned short int*)(fbp + c)) = TmpWrk[d];
			c+=LLENGTH;
			++d;
		}
	}
}

void UpdateValues(void)
{
register int	a,x,y,ty,c,x1,x2;
unsigned char	Value;

	if(SampleInterval>1)
	{
		for(a=0;a<IC_MessageLength;a++) IC_Message[a]=(unsigned char)(IC_A_Message[a]/SampleInterval);
	}

	for(a=0;a<IC_MessageLength;a++)
	{
		if(IC_P_Message[a]!=IC_Message[a])
		{
			IC_P_Message[a]=IC_Message[a];
			ty=BAR_EY-IC_Message[a];	// How high the bar is
			x1=BAR_SIDE_SPACING+(a*(BAR_SPACING+BAR_WIDTH));
			x2=x1+BAR_WIDTH;
			for(y=BAR_SY;y<ty;y++)
			{
				c=x1+y*WIDTH;
				for(x=x1;x<x2;x++)
				{
					ImageBuffer[c].G=0;
					++c;
				}
			}
			for(y=ty;y<BAR_EY;y++)
			{
				c=x1+y*WIDTH;
				for(x=x1;x<x2;x++)
				{
					ImageBuffer[c].G=255;
					++c;
				}
			}
	
			if(ShowValues>0)
			{
				for(y=BAR_STRING_Y;y<(BAR_STRING_Y+30);y++)
				{
					c=x1+y*WIDTH;
					for(x=x1;x<x2;x++)
					{
						ImageBuffer[c].R=0;
						ImageBuffer[c].G=0;
						ImageBuffer[c].B=0;
						++c;
					}
				}
				Value=IC_Message[a];
				c=(Value>>4&0xF);
				if(c<10)
					Message[0]=c+'0';
				else
					Message[0]=('A'+c-10);
				c=(Value&0xF);
				if(c<10)
					Message[1]=c+'0';
				else
					Message[1]=('A'+c-10);
				Message[2]='\0';
				PutMyString(Message,x1+10,BAR_STRING_Y+2,0,3);
			}
			CopyDisplayBufferToScreen(x1,BAR_SY,x2,BAR_STRING_Y+30);
		}
	}
}

void MineData(int Position)
{
register int	a=Position,c;
	
	for(c=0;c<IC_MessageLength;c++)
	{
		IC_Message[c]=0;
		if(IB[a]>='A')
			IC_Message[c]=(IB[a]-'A')+10;
		else
			IC_Message[c]=(IB[a]-'0');
		IC_Message[c]<<=4;
		++a;
		if(IB[a]>='A')
			IC_Message[c]+=(IB[a]-'A')+10;
		else
			IC_Message[c]+=(IB[a]-'0');
		++a;
	}
}


void FastPoll(void)
{
register int	cp,pp,a,b,go,ml,c,d,mid;
register unsigned char	kr;

#ifdef SIMULATION_ONLY
	DP=(int)(rand()&0x1);
	IC_MessageLength=8;
	ml=IC_MessageLength;
	IC_MessageID=MessageAnalyzed;
	for(a=0;a<8;a++) IC_Message[a]=rand()&0xFF;
	if(SampleInterval==1)
	{
		UpdateValues();
	}
	else
	{
		if(--CurrSamIntv==0)
		{
			for(c=0;c<ml;c++) IC_A_Message[c]+=(unsigned int)IC_Message[c];
			UpdateValues();
			CurrSamIntv=SampleInterval;
			for(c=0;c<ml;c++) IC_A_Message[c]=0;
		}
		else
		{
			for(c=0;c<ml;c++) IC_A_Message[c]+=(unsigned int)IC_Message[c];
		}
	}
return;
#endif

#ifdef DEBUG
printf(".");fflush(stdout);
#endif

	if(IBCP>=IBS) IBCP=0;					// Panic
	if((cp=read(Port,IB+IBCP,(IBS-IBCP)))<=0) return;	// max read to the end

#ifdef DEBUG
printf(" read %d ( CP = %d )",cp,IBCP);fflush(stdout);
#endif

	if(ShowValues>1)
	{
		if(ShowValues>2)			// Show traffic
		{
			for(ml=430;ml<463;ml++)
			{
				mid=0+ml*WIDTH;
				for(a=0;a<300;a++)
				{
					ImageBuffer[mid].R=0;
					ImageBuffer[mid].G=0;
					ImageBuffer[mid].B=0;
					++mid;
				}
			}
			sprintf(Message,"%d bytes",cp);
			PutMyString(Message,2,433,0,2);
			CopyDisplayBufferToScreen(0,430,300,463);
		}
		else
		{
			for(d=430;d<463;d++)
			{
				c=0+d*WIDTH;
				for(b=0;b<300;b++)
				{
					ImageBuffer[c].R=0;
					ImageBuffer[c].G=0;
					ImageBuffer[c].B=0;
					++c;
				}
			}
			sprintf(Message,"%1.2f m/s",((float)NMPF/(float)ALARM_INTERVAL));
			PutMyString(Message,2,433,0,2);
			CopyDisplayBufferToScreen(0,430,600,463);
		}
	}
	DP=1;
	IBCP+=cp;
	go=1;cp=0;pp=0;
	while(go)
	{
		while((cp<IBCP)&&(IB[cp]!='t')) ++cp;
		if(cp>=IBCP)
			go=0;
		else
		{			// 't' found, a points to MessageID;
			pp=cp;
			a=cp+1;
			if((a+4)<IBCP)	// The header can be mined
			{
				ml=(int)(IB[a+3]-'0');		// t3CDl0011223344556677
				if((ml>0)&&(ml<9))		// valid ?
				{
					if((a+4+(ml<<1))<IBCP)		// enough bytes ?
					{
						mid=0;
						for(c=0;c<3;c++,a++)
						{
							mid<<=4;
							if(IB[a]>='A')
								mid+=(IB[a]-'A')+10;
							else
								mid+=(IB[a]-'0');
						}
#ifdef DEBUG
printf("\nID = %03x / %03x  [%d]",mid,MessageAnalyzed,ml);fflush(stdout);
#endif
						if(mid==MessageAnalyzed)
						{
							++NMPF;
							IC_MessageLength=(ml-1);	// No need for CRC
							MineData((a+1));
							if(SampleInterval==1)
							{
								UpdateValues();
							}
							else
							{
								if(--CurrSamIntv==0)
								{
									for(c=0;c<ml;c++) IC_A_Message[c]+=(unsigned int)IC_Message[c];
									UpdateValues();
									CurrSamIntv=SampleInterval;
									for(c=0;c<ml;c++) IC_A_Message[c]=0;
								}
								else
								{
									for(c=0;c<ml;c++) IC_A_Message[c]+=(unsigned int)IC_Message[c];
								}
							}
						}
						else
						{
							if(ShowValues>1)
							{
								for(d=430;d<463;d++)
								{
									c=0+d*WIDTH;
									for(b=330;b<600;b++)
									{
										ImageBuffer[c].R=0;
										ImageBuffer[c].G=0;
										ImageBuffer[c].B=0;
										++c;
									}
								}
								sprintf(Message,"%03x",mid);
								PutMyString(Message,332,433,0,2);
								CopyDisplayBufferToScreen(330,430,600,463);
							}
						}
						cp=a+3+(ml<<1);
					}
					else
						go=0;
				}
				else
					cp=a+3;
			}
			else
				go=0;
		}
	}

	if(pp==0)
	{
		IBCP=0;
		return;
	}
	memcpy((void*)(&IB[0]),(void*)(&IB[pp]),(size_t)(IBCP-pp));
	if((IBCP-=pp)>20)	// failsafe
	{
		IBCP=0;
	}
#ifdef DEBUG
printf("!");fflush(stdout);
#endif
return;
}

int main(int argc,char **argv)
{
int	a,c,d,r,x,y;
char	RV,GV;

	if(argc>1)
	{
		if((sscanf(argv[1],"%0xd",&r))!=1)
		{
			if((sscanf(argv[1],"%d",&r))!=1)
			{
				printf("Error reading '%s'",argv[1]);fflush(stdout);
				return(1);
			}
			else
				MessageAnalyzed=r;
		}
		else
			MessageAnalyzed=r;
		if(argc>2)
		{
			if((sscanf(argv[2],"%d",&a))!=1)
			{
				printf("Error reading '%s' for interval, using 1...",argv[2]);fflush(stdout);
				SampleInterval=1;
			}
			else
				SampleInterval=a;
		}
	}
	else
	{
		printf("Usage : %s id ( 0xXXXX for hexa or DDDDD for decimal...\n\n",argv[0]);fflush(stdout);
		return(1);
	}

	R_ACR=0;R_AMR=0;
	for(a=0;a<11;a++)
	{
		c=-1;
		d=((r>>a)&1);
		switch(c)
		{
			case -1 :			// no data yet.
				c=d;
			break;
			case 1 :			// so far 1s
				if(d==0) c=2;		// this is going to be a don't care bit
			break;
			case 0 :
				if(d==1) c=2;
			break;
		}
		if(c==2)
			R_AMR|=(1<<a);
		else
			if(c==1)
				R_ACR|=(1<<a);
	}

	R_ACR<<=5;R_AMR<<=5;
	R_AMR|=0xF;	// don't care about data bytes
	PrintHexa(4,R_ACR);
	printf("\nWorking with ACR=0x%s",Serial);
	PrintHexa(4,R_AMR);
	printf(" and AMR=0x%s.\n",Serial);fflush(stdout);

	printf("Data Eval on every %d sample.",SampleInterval);fflush(stdout);

	SetUpMySignals();

	if(CreateMainWindow()) return(0);

	SetUpPicture();

	ioctl(0,TCGETS,&oldT);
	ioctl(0,TCGETS,&newT);
	newT.c_lflag&=~ECHO;
	newT.c_lflag&=~ICANON;
	ioctl(0,TCSETS,&newT);

	c=0;
	if(fcntl(0,F_SETOWN,getpid())>=0)
	{
		c|=FASYNC;
		if(fcntl(0,F_SETFL,c)<0)
		{
			printf("\nError with SIGIO for terminal...");fflush(stdout);
		}
	}


	alarm(ALARM_INTERVAL);
	CopyDisplayBufferToScreen(0,0,WIDTH,HEIGHT);

#ifdef SIMULATION_ONLY
	PrintHexa(4,R_ACR);
	sprintf(Message,"M%s%s\015",Serial,Serial);
	printf("\nSending to CAN232 : \n%s",Message);
	PrintHexa(4,R_AMR);
	sprintf(Message,"m%s%s\015",Serial,Serial);
	printf("\nSending to CAN232 : \n%s",Message);
	fflush(stdout);
#else
	if(SetUpCAN()==0)
	{
		CleanUp();
		return(1);
	}
#endif

	CurrSamIntv=SampleInterval;
	NeedSynced=1;a=0;NMPF=0;
	while(ProcessGo)
	{
		FastPoll();
		if(NeedSynced)
		{
			if(DP) {RV=0;GV=255;} else {RV=255;GV=0;}
			for(y=0;y<20;y++)
			{
				c=a+y*WIDTH;
				for(x=a;x<(a+20);x++)
				{
					ImageBuffer[c].R=0;
					ImageBuffer[c].G=0;
					ImageBuffer[c].B=0;
					++c;
				}
			}
			a+=619;
			if(a>620) a=0;
			for(y=0;y<20;y++)
			{
				c=a+y*WIDTH;
				for(x=a;x<(a+20);x++)
				{
					ImageBuffer[c].R=RV;
					ImageBuffer[c].G=GV;
					ImageBuffer[c].B=0;
					++c;
				}
			}
			if(++SyncCounter>30)	// Refresh whole screen every minute
			{
					CopyDisplayBufferToScreen(0,0,WIDTH,HEIGHT);
					SyncCounter=0;
			}
			else
			{
				CopyDisplayBufferToScreen(0,0,20,20);
				CopyDisplayBufferToScreen(619,0,639,20);
			}
			DP=0;NMPF=0;
			NeedSynced=0;
		}

	}
	CleanUp();
}

