/**********************************************************************
 *****  COMMON FILE FOR EVERY GILD-SYSTEM PROJECTS                *****
 *****  (WARNING: do not change main routines except user areas)  *****
 *****                                                            *****
 *****  Name: _core_data.c                                        *****
 *****  Desc: API functions for GILD protocol				      *****

 *****  Ver.: 1.0                                                 *****
 *****  Date: 01/2011		                                      *****
 *****  Auth: Martin Mechacek		                              *****
 *****  Comp: ESTELAR s.r.o.									  *****
 *****                                                            *****
 *****  Copyright ESTELAR s.r.o. (c) 2010, all rights reserved    *****
 **********************************************************************/ 



/**********************************************************************
 *****  USER AREA - define dependeces here						  *****
 **********************************************************************/ 




/////// end of USER AREA //////////////////////////////////////////////





/**********************************************************************
 *****  MAIN AREA - do not change !	   						      *****
 **********************************************************************/ 

#include "_core_def.h" 

#include "_core_bus.h" 

#include "_core_data.h" 

#include "_core_err.h" 

#include "_core_pac.h" 





//========================================================================

//>> Global variables



PACKET_HEADER	*gPh;

DATA_PARSING	gData;

// R8C nema rad promene typu volatile
#if defined CORE_EDK || defined CORE_POW || defined CORE_LOG
 unsigned int    _gUnitAddr;		// store the bus ID address

 unsigned int    _resendReq;		// the repeat counter for packet resending, for each packet type

 unsigned int    _resendCnt;		// total repeat counter packet resending from init

#else

volatile unsigned int    _gUnitAddr;		// store the bus ID address

volatile unsigned int    _resendReq;		// the repeat counter for packet resending, for each packet type

volatile unsigned int    _resendCnt;		// total repeat counter packet resending from init

#endif

//<< end of Global variables





//========================================================================

//>> Internal non-document functions



const char nibble_parity[16] =

                   { 0, 1, 1, 0,

                     1, 0, 0, 1,

                     1, 0, 0, 1,

                     0, 1, 1, 0 };



unsigned char calc_even_parity8(unsigned char _byte)

{

    return( nibble_parity[_byte&0xf] ^ nibble_parity[(_byte>>4)&0xf]);

}



unsigned int calc_even_parity16(unsigned int _byte)

{
#if defined CORE_EDK || defined CORE_POW || defined CORE_LOG
  unsigned int retvalue;
  
  retvalue = calc_even_parity8(_byte) ^ calc_even_parity8(_byte >>8);
  return (retvalue);
#else
    return( nibble_parity[_byte&0xf] ^ nibble_parity[(_byte>>4)&0xf] ^ nibble_parity[(_byte>>8)&0xf] ^ nibble_parity[(_byte>>12)&0xf]);

#endif
}


//<< end of Internal non-document functions







//+-------------------------------------------------------------------+

//| DataInitParse()

//| initialise packet data parsing

//+-------------------------------------------------------------------+

void DataInitParse(void)

{

	gData.PacCount = 0;

//	BusControl.BusState &= ~(SCI_BUSY);

 	gData.SciState = 0;

	gPh = (PACKET_HEADER *)gData.bufferRxD;



} // end of DataInitParse()



//+-------------------------------------------------------------------+

//| DataInitVar()

//| re-initialise packet parsing variables, if not data buffered

//+-------------------------------------------------------------------+

void DataInitVar(void)

{


  BusInitVar();

  gData.RID = 0;

  DataInitParse();

  _resendCnt = 0;

  _resendReq = 0;


}  // end of DataInitVar()





//+-------------------------------------------------------------------+

//| DataAddrSet()

//| store the bus address of main unit. Use for all packet creation 

//| je treba aktualizovat pri kazde zmene idAdr v aplikaci

//+-------------------------------------------------------------------+

void DataAddrSet(unsigned int idAdr)

{

  _gUnitAddr = idAdr;


}  // end of DataAddrSet()




//+-------------------------------------------------------------------+

//| DataAddrRead()

//| read the bus address of main unit. Store by DataAddrSet()

//+-------------------------------------------------------------------+

unsigned int DataAddrRead()

{

  return(_gUnitAddr);



}  // end of DataAddrRead()





//+-------------------------------------------------------------------+

//| DataInitPacket()

//| re-initialise packet parsing variables, if not data buffered

//+-------------------------------------------------------------------+

unsigned int DataInitPacket(void)

{

  if(gRxPos)	

   { 

    return (0);

   } 

  

	gData.PacCount = 0;

	BusControl.BusState &= ~(SCI_BUSY);

 	gData.SciState = 0;



return(1);

}  // end of DataInitPacket()





//+-------------------------------------------------------------------+

//| SCI_PutPacket()

//| send packet on bus with collision check

//+-------------------------------------------------------------------+

// hlavni a jedina rutina pro odesilani dat s rizenym pristupem na linku

// parametr px urcuje delku dat k odeslani predanych v parametru pbStrPtr

// vraci result dle BusPutChar()

// 0 = OK, nenulova hodnota znamena, ze se nepovedlo odeslat a prenos se musi opakovat s volanim SCI_ResendReq()

// return -1 = timeout - nepodarilo se ziskat pristup na linku 

// pozadovana uloha ze ktere je volano se musi znovu zaradit do fronty udalosti reqBuffer

// do fronty se zaradi s puvodni prioritou takze nasledne znovu odeslani mohou predbehnout pakety s vyssi prioritou

// opakovane odeslani paketu v tomto pripade bude mit jine RID, ale to nevadi protoze puvodni paket neodesel z jednotky

// ---------------------------------------------------------------------------------------------------
#if !defined CORE_EDK && !defined CORE_POW && !defined CORE_LOG

unsigned int SCI_PutPacket( unsigned char *pbStrPtr, unsigned char px )  

{  

 unsigned int  _rtv;


#ifdef CORE_GR_U01

 if ((BusControl.BusState&BUS_SRCUSB) )			// data jdou do USB kanalu
 {
 	while( px )  
     {  
	   _rtv = SCI_PutChar( *pbStrPtr );  
	 	pbStrPtr++;  
		px--;
	 }

	 return (0);				// data odeslany
 }
 
 else 		// data jsou standartnim SCI rozhranim
#endif

 {
	// je vyhlasen klid na lince nikdo nesmi samovolne nic vysilat
//	if((BusControl.BusState&BUS_QUIET)) return 0;
    // avsak na primo adresovane pakety budeme odpovidat, aby bylo mozne jednotky servisne obsluhovat
	if((BusControl.BusState&BUS_QUIET) && *(int *)(pbStrPtr+2)==0x0000) return 0;


    _rtv = 0;

	BusControl.BusState |= BUS_TIMEOUT;

	

	// vnitrek smycky NEMENIT ma zasadni vliv na casovani na sbernici 

	// tato prodleva musi byt konstatntni aby byla platna konstatna BUS_FREE_TIMEOUT

	// pro korekci rychlosti procesoru je pouzita konstanta BYTEMARKER

    while( px )  

     {  


		if( (BusControl.BusState&BUS_FREE) ) 

		 {

		    RX_InterruptDisable;



		    // Send byte with collision check

	        	_rtv = BusPutChar( *pbStrPtr );  


    	    if(_rtv)			// byte se nepodarilo odeslat - cely prenos se bude opakovat - osetreno v miste volani teto funkce

			 {

			  BusControl.BusState &= ~BUS_FREE; 		// maze se priznak volne sbernice

	  		  BusControl.Timer_counter = 0;				// a nuluje se timeout pro znovu nastaveni flagu

			  break;				

			 }

			

			pbStrPtr++;  

			px--;

			continue;			// pokud uz vysilame ignoruje se BUS_TIMEOUT, protoze by to vypadlo v prubehu paketu

		 }


	 // timeout	cekani na uvolneni linky - pokud to jde sem neni kriticky problem, bude se asi stavat casto pokud behaji data po sbernici

	 // probihajici prenos jednou skonci a pak maji vsechny jednotky stejnou vychozi posici. Po uplynuti casu BUS_FREE se bude rozhodovat podle priority paketu

	 if( !(BusControl.BusState&BUS_TIMEOUT) ) 

	  { 

	   _rtv = -1; 

	   break; 

	  }		 

	  
#ifdef CORE_GL_C700
// pouze pro CL700
// smycka cekani na uvolneni linky je delsi nez rychlost na I2C coz zpusobi chyby ERR_I2C_WRITE, ERR_I2C_WRITE1
   if((_IIC2.ICCR1.BIT.TRS)) break;
#endif

    }  

  // odeslani paketu probehlo OK
  // vynulovat pocitadlo pro opakovani paketu
  // a take nastavit priznak linka obsazena, potrebujeme za kazdym paketem udelat nutnou mezeru
  if(_rtv == 0)
   {
	 _resendReq = 0;
	 
#ifndef CORE_DH485
     // v GR-DH způsobuje totoální zatuhnutí zařízení, že nejde ani ping
	 // pokud byl zapnutý časový server a byla připojena konzola na portu 13000 tak při přístupu na web a procházení stránek to vytuhlo 
   	 BusControl.BusState &= ~BUS_FREE; 		// maze se priznak volne sbernice
	 BusControl.Timer_counter = 0;			// a nuluje se timeout pro znovu nastaveni flagu
#endif	 

   }
   
  RX_InterruptEnable;

  

return(_rtv);  

 } // else   - data jsou standartnim SCI rozhranim

}  // end of SCI_PutPacket()
#endif



//+-------------------------------------------------------------------+

//| SCI_ResendReq()

//| hlida pocet opakovani odeslani paketu, aplikace musi volat v pripade chyby funkce SCI_PutPacket

//| odeslani paketu pomoci SCI_PutPacket se bude opakovat dokud toto vraci > 0

//| pokud vrati 0 je treba vyhlasit chybu = ERR_SCI_SEND

//+-------------------------------------------------------------------+

unsigned int SCI_ResendReq()
{
// nutno rozpracovat podle typu paketu,
// kazdy typ paketu potrebuje vlastni pocitadlo opakovani
  if(++_resendReq >= SCI_MAX_RESEND) _resendReq = 0;
  else _resendCnt++;  	// zde se uklada celkovy pocet opakovani pro servisni statistiky
	
return(_resendReq);
}

unsigned int SCI_ResendCnt()
{
unsigned int _rCnt;
  _rCnt = _resendCnt;
  _resendCnt = 0;
return(_rCnt);
}


//+-------------------------------------------------------------------+

//| DataCreateSystemMessage()

//| funkce vytvori hlavicku paketu a ulozi gData.bufferTxD pro nasledne odeslani

//+-------------------------------------------------------------------+

void DataCreateMessageHeader(unsigned int _Sys, unsigned int _UnitAddr, unsigned int _Rid, unsigned int _Datalen)  

{

unsigned int *txm;

unsigned int _ep;



  txm = (unsigned int *)gData.bufferTxD;

  _ep = 0;

  

  // hlavicka - ID paketu
#if !defined CORE_EDK && !defined CORE_POW && !defined CORE_LOG
	*txm = SWAP(_Sys & 0xFFFE);		// maskovat paritni bit z PID dotazu, bude nastaven nakonec
#else
	*txm = SWAP(_Sys);	
#endif

	

	_ep ^= calc_even_parity16(*txm);

	txm++;

	

  // hlavicka - ADDR

	*txm = SWAP(_UnitAddr);

	_ep ^= calc_even_parity16(*txm);

	txm++;



  // hlavicka - datalen nebo status pro ACK paket

	*txm = SWAP(_Datalen);

	_ep ^= calc_even_parity16(*txm);

	txm++;

  

  // hlavicka - RID

    if(_Rid) *txm = SWAP(_Rid);

	else *txm = SWAP(gData.RID++);

	_ep ^= calc_even_parity16(*txm);

	txm++;



  // doplneni parity	
#if !defined CORE_EDK && !defined CORE_POW && !defined CORE_LOG
   if(_ep) gData.bufferTxD[1] |= PAC_PARITY_EVEN; 
#else
   if(_ep) *(unsigned int *)&gData.bufferTxD[1] |= PAC_PARITY_EVEN; 
#endif



return;

}  // end of DataCreateMessageHeader





//+-------------------------------------------------------------------+

//| DataCreateSystemMessage()

//| funkce sestavi paket SYS_STATE_CHANGE, vcetne hlavicky a crc

//| a ulozi do gData.bufferTxD pro nasledne odeslani

//+-------------------------------------------------------------------+

void DataCreateSystemMessage(unsigned int _Sys, unsigned int _ioAdr, unsigned int _Code)  

{

unsigned int *txm;



    DataCreateMessageHeader(_Sys, 0, 0, 8);  



    txm = (unsigned int *)(gData.bufferTxD+SYS_HEADER_LEN);



	*txm = SWAP(_gUnitAddr);

	txm++;

	*txm = SWAP(_ioAdr);

	txm++;

	*txm = SWAP(_Code);

	txm++;

  

   DataAddCrcMessage( 6 );  	

// pouzit zde funkci AddCrcMessage()

  

return;  

}	// end of DataCreateSystemMessage





//+-------------------------------------------------------------------+

//| DataAddCrcMessage()

//| vypocet CRC a doplneni na konec buferu

//+-------------------------------------------------------------------+

// data musi byt naplnena v gData.bufferTxD

// parametr _Datalen se zadava v bytech

void DataAddCrcMessage( unsigned int _Datalen )  

{

unsigned int  _crc;

unsigned char *txm, _ci;



   txm = (unsigned char *)(gData.bufferTxD+SYS_HEADER_LEN);

   _crc = 0;

	for(_ci = 0; _ci < _Datalen; _ci++)

	 {

	   _crc += *txm;

	   txm++;

	 }



	*(unsigned int *)(txm) = SWAP(_crc);

	

return;

}	// end of DataAddCrcMessage







//+-------------------------------------------------------------------+

//| SCI_RecvPacket()

//| receive packet from bufer and prepare for parsing

//+-------------------------------------------------------------------+

// jako parametr jsou postupne vkladany prijate znakzy ze sbernice

// vysledny paket se uklada do gData.bufferRxD

// funkce zajistuje vypocet CRC a parity a osetruje hlidani delky paketu

// vraci 0 = OK data zpracovana

// vraci > 0 podle funkce SCI_CheckPacketAddr, v okamziku kdy byl prijat cely paket bez chyb

// vraci -1 = chyba, kod chyby je ulozen ve spodnich 4 bitech gData.SciState

// v druhem parametru je vracen pocet platnych znaku v _Data, pokud return > 0

int SCI_RecvPacket( unsigned char _Data, unsigned char *_DataLen)

{

int  _rState;


	*_DataLen = 0;
    _rState = 0;

	


	// zpracovani kazdeho prijateho byte - do buferu se uklada pouze pokud se vejde

	if(	!(gData.SciState&SCI_OVERFLOW_FLAG) ) 

      gData.bufferRxD[gData.PacCount] = _Data;


	// pocitani CRC

	if(	(gData.SciState&SCI_CRCCOUNT_FLAG) ) 

	 {

		gData.Crc += gData.bufferRxD[gData.PacCount];

		if( (gData.PacCount-SYS_HEADER_LEN) == _DATALEN-3 ) 
			gData.SciState &= ~SCI_CRCCOUNT_FLAG;		// konec pocitani CRC

	 }



    // sledovani parity	v hlavicce

   	if( calc_even_parity8(gData.bufferRxD[gData.PacCount]) ) gData.SciState ^= SCI_PARITY_FLAG;



    // zde osetrena ochrana proti preteceni buferu

	gData.PacCount++;

	if(gData.PacCount > MAX_MESSAGE_LEN) 		

	 {

	   // do ruznych jednotek mohou chodit ruzne delky pakety

	   // pri zaplneni buferu se ztrati prenasena data, ale paket se cely prijme a nastavi se chyba

	   // chybovy paket se generuje vzdy, protoze pripadna delsi data budou chodit adresne do jinych jednotek

	   // tedy k preteceni by nemelo nikdy dojit za normalnich okolnosti

		

	  gData.SciState |= SCI_OVERFLOW_FLAG | ERR_SCI_LONG;

	 }	



    ////////// dekodovani hlavicky paketu pro urceni delky prijimanych dat	

	////////// 

	if(gData.PacCount == SYS_HEADER_LEN)			// nactena cela hlavicka nebo ACK paket 

	 {


		// spatna parita v hlavicce vygenerovat NACK

		if((gData.SciState&SCI_PARITY_FLAG))

		 {
#ifndef NO_SCI_CONTROL
 		   gData.SciState &= 0xf0;
		   gData.SciState |= ERR_SCI_PARITY;	 
#endif				
		 }			 


		// je to ACK nebo ERR paket ?

		if( SCI_PACKETTYPE || gPh->ph_datalen == 0)
		// pridana podminka  gPh->ph_datalen == 0
		// paket ktery neobsahuje data - napr HWX pokud v jednotce nejsou zadne programovaci zaznamy
		 {

#ifdef CORE_DH485
// DH485 funguje jako prevodnik proto se adresa nekontroluje
// pokud by mel DH485 take prijimat pakety ze sbernice nebo TCP adresovane jemu tak by se musela predelat cela logika aby nekolidovalo zpracovani z TCP a sbernice
// bude se to muset nejak obejit ze se prime vse a az potom se kontroluje dodatecne adresa a pokud to neni pro nas tak se zahodi
 		  _rState = PACKET_DH485;
#else
		  _rState = SCI_CheckPacketAddr(SWAP(gPh->ph_ADDR));			 
#endif

		 }

		else 		

		 // pro ostatni pakety nastavit flagy a pokracovat v prijmu

		 {

		  gData.SciState |= SCI_HEADERREC_FLAG | SCI_CRCCOUNT_FLAG;
		  gData.Crc = 0;
			
		 }



	  goto done;

	 }	// konec dekodovani hlavicky 





    ////////// nactena vsechna data dle delky v havicce, bude provedena kontrola	

	////////// 

	if( (gData.SciState&SCI_HEADERREC_FLAG) && (gData.PacCount-SYS_HEADER_LEN) >= _DATALEN)			

	 {


#ifdef CORE_DH485
// DH485 funguje jako prevodnik proto se adresa nekontroluje
// pokud by mel DH485 tak prijimat pakety ze sbernice nebo TCP adresovane jemu tak by se musela predelat cela logika aby nekolidovalo zpracovani z TCP a sbernice
// bude se to muset nejak obejit ze se prime vse a az potom se kontroluje dodatecne adresa a pokud to neni pro nas tak se zahodi
 	   _rState = PACKET_DH485;
#else
       _rState = SCI_CheckPacketAddr(SWAP(gPh->ph_ADDR));
#endif

	   if( _rState )		// je to pro nas ? kontrola cilove adresy v hlavicce

		 {

#ifndef NO_SCI_CONTROL

			// jakakoliv chyba pri zpracovani hlavicky a dat - napr parita
			if( (gData.SciState&0x0f) ) 

			 {

			   // gData.SciState uz je nastaven

		       _rState = -1;

			 }
			 

			else

		    // zkontrolovat jestli delka dat je suda

		    if( (_DATALEN&0x01))

		     {

			   // nastavit flagy pro generovani ACK paketu prekroceni delky paketu

 			   gData.SciState &= 0xf0;

			   gData.SciState |= ERR_SCI_EVEN;

		       _rState = -1;

		     }	 

			else

/*

			// zprava byla pro nas moc dlouha ?

			if( (gData.SciState&SCI_OVERFLOW_FLAG) ) 

       		 {

			   // nastavit flagy pro generovani ACK paketu prekroceni delky paketu

 			   gData.SciState &= 0xf0;

			   gData.SciState |= ERR_SCI_LONG;

			   // a priznak vymazat

	  		   gData.SciState &= ~SCI_OVERFLOW_FLAG;

		       _rState = -1;

			 }

			else

*/

			// jestli souhlasi CRC ?

   		    if(gData.Crc != SWAP(*((unsigned int *)(gData.bufferRxD+SYS_HEADER_LEN + _DATALEN-2))) )
 
			 {

			   // nastavit flagy pro generovani ACK paketu s chybou CRC

 			   gData.SciState &= 0xf0;

			   gData.SciState |= ERR_SCI_CRC;

		       _rState = -1;

			 }

#endif		

		

		 }	// konec podminky SCI_CheckPacketAddr

	    else 

   		  DataInitParse();		// pokud to nebylo adresovano pro nas tak se pripravit na dalsi paket

		 

	 }	// konec zpracovani paketu a kontroly

		    

done:			  

if(_rState) *_DataLen = gData.PacCount;

return(_rState);

}	// end of SCI_RecvPacket





//+-------------------------------------------------------------------+

//| SCI_CheckPacketAddr()

//| check target address in received packet 

//+-------------------------------------------------------------------+

int SCI_CheckPacketAddr(unsigned int _which)
{
// DH485 funguje jako prevodnik proto se adresa nekontroluje
// pokud by mel DH485 tak prijimat pakety ze sbernice nebo TCP adresovane jemu tak by se musela predelat cela logika aby nekolidovalo zpracovani z TCP a sbernice
// bude se to muset nejak obejit ze se prime vse a az potom se kontroluje dodatecne adresa a pokud to neni pro nas tak se zahodi
// pro podmineny preklad DH485 je volani teto funkce nahrazeno konstantou 9

  if(_which == _gUnitAddr)			return(PACKET_SINGLECAST);

  if(_which == PAC_ADDR_MULTICAST)	return(PACKET_MULTICAST);			

  if(_which == PAC_ADDR_BROADCAST)	return(PACKET_BROADCAST);

	

return(0);	

}	// SCI_CheckPacketAddr

// ************************************************************************* 
// SCI_PutPacket
// ************************************************************************* 
#if defined CORE_EDK || defined CORE_POW || defined CORE_LOG

unsigned int SCI_PutPacket( unsigned char *pbStrPtr, unsigned char px )  
{  
  unsigned int  _rtv;
  _rtv = 0;

	// je vyhlasen klid na lince nikdo nesmi nic vysilat
	if((BusControl.BusState&BUS_QUIET)) return 0;

  
	do {
			ClearWDT();
	   } 
		while (!(BusControl.BusState&BUS_FREE) );

    RX_InterruptDisable;
			
    while( px )  
     {  
		ClearWDT();

	    // Send byte with collision check
			#if defined CORE_EDK || defined CORE_POW
			  int1ic.byte = INT_DISABLED;
		       _rtv = BusPutCharAsm( *pbStrPtr );  
  		      int1ic.byte = INT_LEVEL_INT1;	// INT1 priority 
			#else				
	          _rtv = BusPutChar( *pbStrPtr );  
			#endif			   
			
   	    if(_rtv)			// byte se nepodarilo odeslat - cely prenos se bude opakovat - osetreno v miste volani teto funkce
		 {
		  break;				
		 }

		pbStrPtr++;  
		px--;
		continue;			// pokud uz vysilame ignoruje se BUS_TIMEOUT, protoze by to vypadlo v prubehu paketu

	 // timeout	cekani na uvolneni linky - pokud to jde sem neni kriticky problem, bude se asi stavat casto pokud behaji data po sbernici
	 // probihajici prenos jednou skonci a pak maji vsechny jednotky stejnou vychozi pozici.
	 // Po uplynuti casu BUS_FREE se bude rozhodovat podle priority paketu

	 if(BusControl.BusState&BUS_TIMEOUT) 
	  { 
//		  BusControl.BusState &= ~BUS_TIMEOUT;
	   _rtv = -1; 
	   break; 
	  }		 
   

 
 }    // while( px ) 


  // odeslani paketu probehlo OK
  // vynulovat pocitadlo pro opakovani paketu
  // a take nastavit priznak linka obsazena, potrebujeme za kazdym paketem udelat nutnou mezeru  
  if(_rtv == 0)
   {
	BusControl.BusState &= ~BUS_FREE; 					// maze se priznak volne sbernice
	BusControl.Timer_counter = BUS_FREE_TIMEOUT;		// a nuluje se timeout pro znovu nastaveni flagu
   }
   
     
return(_rtv);  
}  // end of SCI_PutPacket()

#endif






/////// end of MAIN AREA //////////////////////////////////////////////

