//----------------------------------------------------
/**
	\file		ctk/inc/ulecom.h
	\ingroup	serial
	\ingroup	all

	\brief		Funkce pro seriovou komunikaci protokol Lecom A/B
				<<< ALL >>>

	\author		ConTeK s.r.o., Ing. Vladimir Kastner
	\version	01.01
	\date		26.02.2011
*/
//----------------------------------------------------

#ifndef _ULECOM_H_
#define _ULECOM_H_			//!< Definice pritomnosti knihovny

#include <ctktypes.h>
#include <comm.h>
#include <ucrc16.h>
#include <ucnp1c.h>
#include <hwmodel.h>

//----------------------------------------------------
/**
	\brief	Definice Lecom A/B protokolu
			DIN 66019, ISO 1745 and ANSI X3.28 (category 2.5 and A2, A4).
			These standards are similar to each other and describe the control mode of a transmission section of a transmission system.
			The host, which is the master, can communicate with a slave (Lenze controller) in three modes:
			 RECEIVE (see 8.H)
			 SEND (see 8.I)
			 BROADCAST/MULTICAST (see 8.J)
	
			definice fizycke vrstvy:
				1 start bit
				7 data bits, nejnizsi bit zasilany prvni
				1 paritni bit, sudy - event
				1 stop bit

			definice protokulu:
				ramec pro zapis:
				                       +--------------------+
				                       |         BCC        |
				+-------+--------+-----+------+-------+-----+-----+
				|  EOT  |  ADDR  | STX | CODE | VALUE | ETX | BCC |
				+-------+--------+-----+------+-------+-----+-----+
				odpoved:
				+-----+
				| ACK |
				+-----+
				   nebo
				+-----+
				| NAK |
				+-----+

				ramec pro cteni:
				+-------+--------+------+-----+
				|  EOT  |  ADDR  | CODE | ENQ |
				+-------+--------+------+-----+
				odpoved:
				        +--------------------+
				        |         BCC        |
				+-------+------+-------+-----+-----+
				|  STX  | CODE | VALUE | ETX | BCC |	ok
				+-------+------+-------+-----+-----+
				   nebo
				+-------+------+---+-----+
				|  STX  | CODE | ? | ETX |				chybne CRC, parity, ...
				+-------+------+---+-----+
				   nebo
				+-------+------+-----+
				|  STX  | CODE | EOT |					parametr code neexistuje
				+-------+------+-----+


				ADDR:	A1,A2
						A1 = '0' + (ADDR / 10)
						A2 = '0' + (ADDR % 10)
				CODE:	C1,C2,...
						zkracene adresovani
						C1 = '0' + ((CODE % 790) / 10 )
						C2 = '0' + ((CODE % 790) % 10 + (CODE / 790) * 10 )
						rozsirene adresovani
						C1 = '!'
						C2..C5 = HEX(CODE)
						C6..C7 = HEX(SUBCODE)

				broadcast adresy
							 0	pro vsechny adresy
							10	pro adresy 10..19
							...
				VALUE:	
						VD	ASCII decimal		"-123456.1234"
						VH	ASCII hexadecimal	"H12" nebo "H1234" nebo "H12345678"
						VS	string, max.12(20)	"S12345678901234567890", bez ridicich znaku
						VO	ASCII octal			"O12345678"
				BCC:	XOR pocinaje CODE az po ETX
*/
//----------------------------------------------------

//----------------------------------------------------
/**
	\name	Definice typu
*/
//----------------------------------------------------
//@{

#define LCM_FIXP	10000			//!< Nasobitel FIXP

typedef uint8_t		lcaddr_t;		//!< Adresa, <0..99>, 0 - broadcast for all, 10 - broadcast for 11..19, ... 	
typedef uint16_t	lccode_t;		//!< Code, <0.. >
typedef uint8_t		lcsubcode_t;	//!< Subcode, <0..255> 

typedef enum _lccmd_t				//!< Prikazy
{						
	LCC_READ,						//!< Cteni
	LCC_WRITE						//!< Zapis
} lccmd_t;

typedef enum _type_lct				//!< Typ dat / komunikace
{
	LCT_FIXED,						//!< Fixed point * LCM_FIXP = Integer / VD, <-214748.3648 .. 214748.3647>
	LCT_HEX							//!< Unsigned integer / VH, <0..4294967295>
#ifdef ULCM_OCTET
	,
	LCT_OCTET						//!< Unsigned integer / VO, <0..??? 4294967295 ???>
#endif //ULCM_OCTET
#ifdef ULCM_STRING
	,
	LCT_STRING						//!< String  / VS
#endif // ULCM_STRING
} lctype_t;

typedef enum _state_lct				//!< Stav
{
	LCS_OK,							//!< OK
	LCS_CODE_ERROR,					//!< Code chyba
	LCS_CRC_ERROR,					//!< Crc chyba
	LCS_ERROR,						//!< Chyba
	LCS_NO_RESPONSE					//!< Slave neodpovida
} lcstate_t;

#define LCM_MAX_FIXED	11			//!< Maximalni delka strigu Fixed point
#define LCM_MAX_HEX		8			//!< Maximalni delka strigu Hex
#define LCM_MAX_OCTET	240			//!< Maximalni delka strigu Octet
#define LCM_MAX_STRING	240			//!< Maximalni delka strigu

typedef union						//!< Hodnota
{
	int32_t		FP;					//!< Fixed point * 10000 = integer, VD
	uint32_t	U;					//!< Unsigned integer
#ifdef ULCM_STRING
	char_t		str[ LCM_MAX_STING ];	//!< String
#endif // ULCM_STRING
} lcdata_t;

//! Master
//----------------------------------------------------
//! Struktura definujici zadosti a odpovedi
typedef struct
{
	lcaddr_t	Addr;				//!< Adresa zarizeni
	bool_t		ExtCode;			//!< Priznak subcode
	lccode_t	Code;				//!< Code parametru
	lcsubcode_t	SubCode;			//!< Subcode parametru
	lccmd_t		Cmd;				//!< Prikaz
	lctype_t	LcType;				//!< Typ komunikace
	lcdata_t	Data;				//!< Data
} MRQLCM;

typedef struct
{
	lcstate_t	State;				//!< Stav
	lcaddr_t	Addr;				//!< Adresa zarizeni
	bool_t		ExtCode;			//!< Priznak subcode
	lccode_t	Code;				//!< Code parametru
	lcsubcode_t	SubCode;			//!< Subcode parametru
	lccmd_t		Cmd;				//!< Prikaz
	lctype_t	LcType;				//!< Typ komunikace
	lcdata_t	Data;				//!< Data
} MRSLCM;

//----------------------------------------------------
//! Struktura definujici obecny vysilaci buffer zadosti

typedef struct
{
	BUFDESC		Desc;				//!< popisovac bufferu
	MRQLCM		Msg;				//!< Lecom zpravy
} MRQLCMBUFDESC;

//----------------------------------------------------
//! Struktura definujici obecny vysilaci buffer odpovedi

typedef struct
{
	BUFDESC		Desc;				//!< popisovac bufferu
	MRSLCM		Msg;				//!< Lecom zpravy
} MRSLCMBUFDESC;

//! Slave
//----------------------------------------------------
//! Struktura definujici zadosti a odpovedi
typedef struct
{
	lcstate_t	State;				//!< Stav
	lcaddr_t	Addr;				//!< Adresa zarizeni
	bool_t		ExtCode;			//!< Priznak subcode
	lccode_t	Code;				//!< Code parametru
	lcsubcode_t	SubCode;			//!< Subcode parametru
	lccmd_t		Cmd;				//!< Prikaz
	lctype_t	LcType;				//!< Typ komunikace
	lcdata_t	Data;				//!< Data
} SRQLCM;

typedef struct
{
	lcstate_t	State;				//!< Stav
	bool_t		ExtCode;			//!< Priznak subcode
	lccode_t	Code;				//!< Code parametru
	lcsubcode_t	SubCode;			//!< Subcode parametru
	lccmd_t		Cmd;				//!< Prikaz
	lctype_t	LcType;				//!< Typ komunikace
	lcdata_t	Data;				//!< Data
} SRSLCM;

//----------------------------------------------------
//! Struktura definujici obecny vysilaci buffer zadosti

typedef struct
{
	BUFDESC		Desc;		//!< popisovac bufferu
	SRQLCM		Msg;		//!< Lecom zpravy
} SRQLCMBUFDESC;

//----------------------------------------------------
//! Struktura definujici obecny prijimaci buffer odpovedi

typedef struct
{
	BUFDESC		Desc;		//!< popisovac bufferu
	SRSLCM		Msg;		//!< Lecom zpravy
} SRSLCMBUFDESC;
//@}

//----------------------------------------------------
/**
	\brief	Forward deklarace kontextu
*/
//----------------------------------------------------

//! Master
struct _MLCMDESC;

//! Slave
struct _SLCMDESC;


//----------------------------------------------------
/**
	\name	Metody objektu
*/
//----------------------------------------------------
//@{

// Master
typedef void (* clcMTick) ( struct _MLCMDESC * prt );

// Slave
typedef void (* clcSTick) ( struct _SLCMDESC * prt );

//! definice CallBacku a zprav
// master

//----------------------------------------------------
//! \brief	Funkce zjistujici, zda je mozne odvysilat dalsi pozadavek
bool_t LcmMReadyToSend(struct _MLCMDESC * _lcm);

typedef bool_t (* clcMReadyToSend)(struct _MLCMDESC * _lcm);

//----------------------------------------------------
//! \brief	Funkce a callback pro Read Code
void LcmMRdCodeReq(struct _MLCMDESC * _lcm, lcaddr_t _addr, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);
typedef void (* clcMRdCodeRs)(struct _MLCMDESC * _lcm, lcaddr_t _addr, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode, lctype_t _lctype, lcdata_t * _data);
typedef void (* clcMRdCodeRsCodeError)(struct _MLCMDESC * _lcm, lcaddr_t _addr, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);
typedef void (* clcMRdCodeRsCrcError)(struct _MLCMDESC * _lcm, lcaddr_t _addr, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);

//----------------------------------------------------
//! \brief	Funkce a callback pro Write Code
void LcmMWrCodeReq(struct _MLCMDESC * _lcm, lcaddr_t _addr, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode, lctype_t _lctype, lcdata_t * _data);
typedef void (* clcMWrCodeRs)(struct _MLCMDESC * _lcm, lcaddr_t _addr, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);
typedef void (* clcMWrCodeRsError)(struct _MLCMDESC * _lcm, lcaddr_t _addr, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);

//----------------------------------------------------
//! \brief	callback pri neprijeti odpovedi
typedef void (* clcMNoResponse)(struct _MLCMDESC * _lcm, MRQLCMBUFDESC * _sndDesc, lcaddr_t _addr, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);

typedef void (* clcMErrorMsg)( struct _MLCMDESC * _lcm, MRSLCMBUFDESC * _recdesc );

// Slave

//----------------------------------------------------
//! \brief	Funkce zjistujici, zda je mozne odvysilat dalsi odpoved
bool_t LcmSReadyToSend(struct _SLCMDESC * _lcm);
typedef bool_t (* clcSReadyToSend)(struct _SLCMDESC * _lcm);

//----------------------------------------------------
//! \brief	Funkce a callback pro Read Code
typedef void (* clcSRdCodeRq)(struct _SLCMDESC * _lcm, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);
void LcmSRdCodeRsp(struct _SLCMDESC * _lcm, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode, lctype_t _lctype, lcdata_t * _data);
void LcmSRdCodeRspCodeError(struct _SLCMDESC * _lcm, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);

typedef void (* clcSRdCodeRqCrcError)(struct _SLCMDESC * _lcm, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);
void LcmSRdCodeRspCrcError(struct _SLCMDESC * _lcm, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode);

//----------------------------------------------------
//! \brief	Funkce a callback pro Write Code
typedef void (* clcSWrCodeRq)(struct _SLCMDESC * _lcm, bool_t _extcode, lccode_t _code, lcsubcode_t _subcode, lctype_t _lctype, lcdata_t * _data);
void LcmSWrCodeRsp(struct _SLCMDESC * _lcm);
void LcmSWrCodeRspError(struct _SLCMDESC * _lcm);

typedef void (* clcSWrCodeRqCrcError)(struct _SLCMDESC * _lcm);

typedef void (* clcSErrorMsg)( struct _SLCMDESC * _lcm, SRQLCMBUFDESC * _recdesc );
//@}

//----------------------------------------------------
//! \brief	Struktura popisujici stav Lecom automatu

// Master
typedef struct _MLCMDESC
{
	volatile bool_t			flRunning;			//!< je inicializovano, po ininializaci na TRUE, pred ukoncenim FALSE

	COMMDESC *				CmmL1;				//!< Ovladac seriove linky

	volatile bool_t			flTick;				//!< Tcik je vykonavan
	volatile uint8_t		State;				//!< Stav automatu
	volatile bool_t			flState;			//!< Automat je vykonavan
	lccmd_t					Cmd;				//!< Posledni Cmd
	lcaddr_t				Addr;				//!< Posledni Addr zpravy
	bool_t					ExtCode;			//!< Posledni ExtCode
	lccode_t				Code;				//!< Posledni Code
	lcsubcode_t				SubCode;			//!< Posledni Subcode
	bool_t					ReqPending;			//!< Priznak probihajiciho pozadavku
	ticks_t					RetryCount;			//!< Pocet opakovani
	ticks_t					RetryCountCnt;		//!< Pocitadlo opakovani vysilani
	ticks_t					RetryTimer;			//!< Casovac pro opakovani zpravy
	ticks_t					RetryTimeout;		//!< Timeout pro opakovani zpravy

	// buffery
	MRQLCMBUFDESC			SendBuf;			//!< Buffer odesilaneho paketu
	pktsize_t				SendSize;			//!< Velikost zpravy pro opakovani
	MRSLCMBUFDESC			RecvBuf1;			//!< Buffer1 prijimaneho paketu
	MRSLCMBUFDESC			RecvBuf2;			//!< Buffer2 prijimaneho paketu

	// metody
	clcMReadyToSend			ReadyToSend;		//!< ready to master send

	// callbacky
	clcMRdCodeRs			cbRdCodeRs;
	clcMRdCodeRsCodeError	cbRdCodeRsCodeError;
	clcMRdCodeRsCrcError	cbRdCodeRsCrcError;

	clcMWrCodeRs			cbWrCodeRs;
	clcMWrCodeRsError		cbWrCodeRsError;

	clcMNoResponse			cbNoResponse;

	clcMErrorMsg			cbErrorMsg;

	clcMTick				Tick;				//!< tick knihovny

} MLCMDESC;

// Slave
typedef struct _SLCMDESC
{
	volatile bool_t			flRunning;			//!< je inicializovano, po ininializaci na TRUE, pred ukoncenim FALSE

	COMMDESC *				CmmL1;				//!< Ovladac seriove linky

	volatile bool_t			flTick;				//!< Tick je vykonavan
	volatile uint8_t		State;				//!< Stav automatu
	volatile bool_t			flState;			//!< Automat je vykonavan

	// buffery
	SRQLCMBUFDESC			RecvBuf1;			//!< Slave buffer prijimaneho paketu
	SRQLCMBUFDESC			RecvBuf2;			//!< Slave buffer prijimaneho paketu
	SRSLCMBUFDESC			SendBuf;			//!< Slave buffer odesilaneho paketu

	// metody
	clcSReadyToSend			ReadyToSend;		//!< ready to slave send

	// callbacky
	clcSRdCodeRq			cbRdCodeRq;
	clcSRdCodeRqCrcError	cbRdCodeRqCrcError;

	clcSWrCodeRq			cbWrCodeRq;
	clcSWrCodeRqCrcError	cbWrCodeRqCrcError;

	clcSErrorMsg			cbErrorMsg;

	clcSTick				Tick;				//!< tick knihovny

} SLCMDESC;

// Master
//----------------------------------------------------
/**
	\brief	Funkce pro inicializaci komunikačního automatu Master

	\param	_lcm			Struktura kontextu automatu
	\param	_cmmL1			Komunikacni kanal
	\param	_retryTimeout	Timeout cekani na zpravu
	\param	_retryCount		Pocet opakovani zpravy

	\return	TRUE			inicializace probehla v poradku
			FALSE			chyba inicializace
*/
//----------------------------------------------------

bool_t LcmMInitialize(MLCMDESC * _lcm, COMMDESC * _cmmL1, ticks_t _retryTimeout, ticks_t _retryCount);

ticks_t LcmMGetRetryCount(const MLCMDESC * const _lcm);

ticks_t LcmMGetRetryTimeout(const MLCMDESC * const _lcm);

void LcmMSetRetry(MLCMDESC * const _lcm, const ticks_t _retryCount, const ticks_t _retryTimeout);

// Slave
//----------------------------------------------------
/**
	\brief	Funkce pro inicializaci komunikačního automatu Slave

	\param	_lcm			Struktura kontextu automatu
	\param	_cmmL1			Komunikacni kanal

	\return	TRUE			inicializace probehla v poradku
			FALSE			chyba inicializace
*/
//----------------------------------------------------

bool_t LcmSInitialize(SLCMDESC * _lcm, COMMDESC * _cmmL1);

//----------------------------------------------------
/**
	\brief	Funkce pro ukonceni komunikačního automatu
*/
//----------------------------------------------------

#ifdef ALL_FINALIZE

// Master
void LcmMFinalize(MLCMDESC * _lcm);

// Slave
void LcmSFinalize(SLCMDESC * _lcm);

#else

 // Master
 #define LcmFinalize(_lcm)

 // Slave
 #define LcmFinalize(_lcm)

#endif // ALL_FINALIZE

#endif // _LECOM_H_
