// #define PRUEBAS_SIN_VCL


//---------------------------------------------------------------------------
#include "stdlib.h"
#include "stdio.h"
#include "conio.h"
#include "windows.h"
#define _verbose ACTIVA_EXPLICACION_PROCESOS
#include "LM2000.h"

#ifndef PRUEBAS_SIN_VCL

#include <vcl.h>
#pragma hdrstop

//#include "stdafx.h"

#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck(TLm2000 *)
{
        new TLm2000(NULL);
}
//---------------------------------------------------------------------------
__fastcall TLm2000::TLm2000(TComponent* Owner)
        : TComponent(Owner)
#else
void __fastcall TLm2000::printfv( AnsiString msg )
{
 printf( "%s\n", msg.c_str() );
}

__fastcall TLm2000::TLm2000(void)
#endif
{
    LM_opened = false;        
}
//-----------------------------------------------------------------------------
/**
  Construtor de clase, que inicializa los parámetros de comunicación
*/
__fastcall TLm2000::~TLm2000(void)
{
  // Si el puerto aun sigue abierto, lo cerramos...
  if ( LM_opened )
        CierraPuerto();
}
//---------------------------------------------------------------------------
#ifndef PRUEBAS_SIN_VCL
namespace Lm2000
{
        void __fastcall PACKAGE Register()
        {
                 TComponentClass classes[1] = {__classid(TLm2000)};
                 RegisterComponents("JD soft.", classes, 0);
        }
}
#endif
//---------------------------------------------------------------------------
#define ENTER_CARD_MEMORY		"A"
// B: solo se usa por compatibilidad		-[ NO IMPLEMENTADO ]-
#define CARD_NOT_VALID			"C"
	#define RTF_MOTIVE_NOT_STANDARD		0
	#define RTF_CARD_CODE_UNKNOWN		1
	#define RTF_MAGNETIC_CARD_EXPIRED	2
	#define RTF_PERIOD_NOT_VALID		3
	#define RTF_ANTIPASSBACK_ERROR		4
	#define RTF_CARD_VERSION_NOT_VALID	5
	#define RTF_CREDIT_INSUFFICIENT		6
#define DELETE_CARD_MEMORY		"D"
#define CONFIGURE_MEMORY		"E"
	#define FMC_LONG_CODES				1
	#define FMC_PIN						2
	#define FMC_EXPIRATION				4
	#define FMC_PREFIX_FREE				8
	#define FMC_MINI_MEMORY			   16
	#define FMC_CARD_REVISION		   32
#define WRITE_CHARS_ON_BAND               "F"
#define READ_CHARS_ON_BAND                "F?"
// G:	CONFIGURA PREFIJOS					-[ NO IMPLEMENTADO ]-
#define PROGRAMMING_PERIODS		"H"
#define SIMPLE_INTERROGATION	"I"
#define PROGRAM_OPERATION_PARAMETERS "J"
#define RESET_CLOCK				"K"
// L:	Configura los tipos de usuarios		-[ NO IMPLEMENTADO ]-
#define CARD_VALID				"R"
// S:	Sustituido por 'C'					-[ NO IMPLEMENTADO ]-
#define DELETE_ALL_CARD_MEMORY	"T"
#define PASSAGE_OPENING         "P"
#define SHOW_NEXT_TRANSIT		"U"
#define SET_TIME				"V"

// Secuencia de lectura correcta de tarjetas
#define INITIALIZE_READING      "O?"
#define READ_CARD_MEMORY		"A?"
#define SHOW_NEXT_MESSAGE		"N?"

// B?:	Inicializa la lectura de mensajes personales
//											-[ NO IMPLEMENTADO ]-

// Secuencia de lectura de mensajes personales
//											-[ NO IMPLEMENTADO ]-
#define INITIALIZE_READING_CM	"B?"
#define READ_PERSONAL_MESSAGE	"Y?"
#define SHOW_NEXT_PMESSAGE		"D?"


#define REINITILIZE_TRANSIS_M	"C?"

#define READ_MEMORY_CFG			"E?"
// F?:	Lee cfg caracteres de la banda		-[ NO IMPLEMENTADO ]-
#define READ_PREFIXES			"G?"
// H?:	Lee periodos
#define READ_OPERATION_CFG		"J?"


#define READ_FIRMWARE			"I?"
#define READ_TIMER(X)			"b?,X"


#define READ_SYSTEM_MESSAGE		"X?"

//-----------------------------------------------------------------------------
/**
  Abre el puerto de comunicación "lpszrPortName"
  Esta función debe ser llamada antes de cualquier otra comunicación;
*/
int __fastcall TLm2000::AbrePuerto(LPTSTR lpszPortName)
{
  DWORD dwError;
  DCB PortDCB;
  COMMTIMEOUTS CommTimeouts;

  // Antes de abrir, cerramos si asi procede
  if ( LM_opened ) return true;

  LM_opened = false;

  // Open the serial port.
  LM_handle = CreateFile (lpszPortName, // Pointer to the name of the port
                      GENERIC_READ | GENERIC_WRITE,
                                    // Access (read/write) mode
                      0,            // Share mode
                      NULL,         // Pointer to the security attribute
                      OPEN_EXISTING,// How to open the serial port
                      0,            // Port attributes
                      NULL);        // Handle to port with attribute
                                    // to copy

  // If it fails to open the port, return FALSE.
  if ( LM_handle == INVALID_HANDLE_VALUE ) 
  {
	dwError = GetLastError ();
	return false;
  } else {
	LM_opened = true;
  }
  
  PortDCB.DCBlength = sizeof (DCB);     

  // Get the default port setting information.
  GetCommState (LM_handle, &PortDCB);

  // Change the DCB structure settings.
  PortDCB.BaudRate = 9600;              // Current baud
  PortDCB.fBinary = TRUE;               // Binary mode; no EOF check
  PortDCB.fParity = TRUE;               // Enable parity checking.
  PortDCB.fOutxCtsFlow = FALSE;         // No CTS output flow control
  PortDCB.fOutxDsrFlow = FALSE;         // No DSR output flow control 
  PortDCB.fDtrControl = DTR_CONTROL_ENABLE; 
                                        // DTR flow control type 
  PortDCB.fDsrSensitivity = FALSE;      // DSR sensitivity 
  PortDCB.fTXContinueOnXoff = TRUE;     // XOFF continues Tx 
  PortDCB.fOutX = FALSE;                // No XON/XOFF out flow control 
  PortDCB.fInX = FALSE;                 // No XON/XOFF in flow control 
  PortDCB.fErrorChar = FALSE;           // Disable error replacement. 
  PortDCB.fNull = FALSE;                // Disable null stripping. 
  PortDCB.fRtsControl = RTS_CONTROL_ENABLE;
                                        // RTS flow control 
  PortDCB.fAbortOnError = FALSE;        // Do not abort reads/writes on 
                                        // error.
  PortDCB.ByteSize = 8;                 // Number of bits/bytes, 4-8 
  PortDCB.Parity = NOPARITY;            // 0-4=no,odd,even,mark,space 
  PortDCB.StopBits = ONESTOPBIT;        // 0,1,2 = 1, 1.5, 2 

  // Configure the port according to the specifications of the DCB 
  // structure.
  if (!SetCommState (LM_handle, &PortDCB))
  {
    // Could not configure the serial port.
    dwError = GetLastError ();
    (printfv)( Format("Unable to configure the serial port\n%s\n", ARRAYOFCONST(("dwError")) ) );
    return FALSE;
  }


  // Retrieve the time-out parameters for all read and write operations
  // on the port.
  GetCommTimeouts (LM_handle, &CommTimeouts);

  // Change the COMMTIMEOUTS structure settings.
  CommTimeouts.ReadIntervalTimeout = MAXDWORD;

///  CommTimeouts.ReadTotalTimeoutMultiplier = 0;
  CommTimeouts.ReadTotalTimeoutMultiplier = 5000;
  CommTimeouts.ReadTotalTimeoutConstant = 1000;
  CommTimeouts.WriteTotalTimeoutMultiplier = 10;
  CommTimeouts.WriteTotalTimeoutConstant = 1000;

  // Set the time-out parameters for all read and write operations
  // on the port.
  if (!SetCommTimeouts (LM_handle, &CommTimeouts))
  {
    // Could not set the time-out parameters.
    dwError = GetLastError ();
    (printfv)( Format( "Unable to set the time-out parameters: %s", ARRAYOFCONST(("dwError")) )  );
    return FALSE;
  }

  // Direct the port to perform extended functions SETDTR and SETRTS.
  // SETDTR: Sends the DTR (data-terminal-ready) signal.
  // SETRTS: Sends the RTS (request-to-send) signal.
  EscapeCommFunction (LM_handle, SETDTR);
  EscapeCommFunction (LM_handle, SETRTS);

  return LM_opened;
};
//-----------------------------------------------------------------------------
/**
    Cierra el puerto, (siempre que este abierto) y libera los recursos pedidos
*/
void __fastcall TLm2000::CierraPuerto(void)
{
 if ( LM_opened )
 {
   CloseHandle( LM_handle );
 }
};
//-----------------------------------------------------------------------------
/**
  Dada una cadena de entrada, genera el CheckSum de comprobación necesario,
  devolviendo este al final.
*/
int __fastcall TLm2000::GeneraChecksum( char *cadena )
{
  int CHK, i;
  CHK = 0;
  for ( i=0; cadena[i] != NULL; i++ )
  {
    CHK += cadena[i];
  }

  return ( CHK - ( (CHK / 10000)*10000 ) );
}
//-----------------------------------------------------------------------------
/**
  NO SE PARA QUE ERA ESTO
*/
/*
int __fastcall:tieneRespuesta( char *CMD )
{
  int dev = -1;

  if ( strcmp( CMD, READ_FIRMWARE ) == 0 ) dev = 1;

  return dev;
}
*/
//-----------------------------------------------------------------------------
/**
 ObtenerRespuesta
 Lee del puerto los datos pendientes del buffer
 (solo lee lo que expulse el LM)
*/
char * __fastcall TLm2000::ObtenerRespuesta( char *rcv )
{
 char buf[80], enviar[80];
 DWORD CHK, chkF = 0;
 int i = 0;
 DWORD dwCommModemStatus;

   if ( LM_opened )
   {

/*
03-02-2002 ELIMINADO PARA EVITAR QUEDAR COLGADO A LA ESPERA DEL LEGO
           Esperemos que los TIME-OUTS funcienen BIEN sin esto.
		// Specify a set of events to be monitored for the port.
		SetCommMask (LM_handle, EV_RXCHAR | EV_CTS | EV_DSR | EV_RLSD | EV_RING);
		// Wait for an event to occur for the port.
		WaitCommEvent (LM_handle, &dwCommModemStatus, 0);
	    // Re-specify the set of events to be monitored for the port.
		SetCommMask (LM_handle, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING);
*/
		// Recogemos la respuesta del lector
		while( i < 75 )
		{
		   ReadFile( LM_handle, &(rcv[i]), 1, &CHK, NULL );
		   if ( CHK == 1 )
		   {
		     if ( rcv[i] == 13 ) break; else i++;
		   } else {
//		     if ( CHK == 0 )
		      if ( (++chkF) >= 10 )
                      {
		      	sprintf( rcv, "ERROR: Respuesta fuera de tiempo" );
		      	return rcv;
		      }
                   }
		} 
		rcv[i+1] = '\0';

		// Verificamos la respuesta del lector
		if ( i > 10 && i < 75)
		{
			char *ptr;
			// Generamos nuestro CHECKSUM de la respuesta
			strcpy( buf, rcv );		buf[ i - 4 ] = '\0';
			CHK = GeneraChecksum( buf );
			// Aislamos el CHECKSUM de la respuesta
			strcpy( buf, rcv );		ptr = buf + i - 4;		buf[ i ] = '\0';
			// Comparamos los CHECKSUM
			sprintf( enviar, "%04d", CHK );
			if ( strcmp( enviar, ptr ) != 0 )
			{
			  strcpy( enviar, rcv );
			   sprintf( rcv, "ERROR CHECKSUM: (%04d) - %s", CHK, enviar );
			} 
		} else 
			sprintf( rcv, "ERROR: Respuesta de longitud incorrecta" );
	} else {
		sprintf( rcv, "ERROR: LM no abierto" );
	}

  #ifdef _verbose   
	(printfv)( Format("Recibido: %s\n", ARRAYOFCONST((rcv)) )	);
  #endif

  return rcv;
};
//-----------------------------------------------------------------------------
/**
  Envia el comando CMD al lector ID, devolviendo la respuesta 'rcv'
*/
char * __fastcall TLm2000::EnviarComando( char *CMD, int ID, char *rcv )
{
 char buf[80], enviar[80];
 DWORD CHK;
 if ( LM_opened )
 {
   sprintf( buf, "L%03d:%s,", ID, CMD );
   CHK = GeneraChecksum( buf );
   sprintf( enviar, "%s%04d\r", buf, CHK );

   #ifdef _verbose
		(printfv)( Format("\nEnviado: %s\n", ARRAYOFCONST((enviar)) ) );
   #endif

   WriteFile( LM_handle, enviar, strlen(enviar), &CHK, NULL );

   ObtenerRespuesta( rcv );

 } else
  sprintf( rcv, "ERROR: LM no abierto" );

 return rcv;
}
//-----------------------------------------------------------------------------
/**
   Procesa el transito devuelto tras una llamada a CMD=I
*/
void __fastcall TLm2000::ProcesaTransito( char *FGT, char *TPT, char *CDT, char *O, char *DTT, char *CTT, char *ETT )
{
  #ifdef _verbose
	(printfv)( Format("\nFGT %s\nTPT: %s\nCDT: %s\nO:  %s\nDTT: %s\nCTT: %s\nETT: %s\n", ARRAYOFCONST((FGT, TPT, CDT, O, DTT, CTT, ETT)) ) );
  #endif
#ifndef PRUEBAS_SIN_VCL
  if ( FOnProcesaTransito ) (FOnProcesaTransito)( FGT, TPT, CDT, O, DTT, CTT, ETT );
#endif
}
//-----------------------------------------------------------------------------
/**
  Dada una cadena de parámetros delimitados por coma ',', obtiene y de vuelve
  en 'prm' el parametro 'narg'-esimo.
*/
char * __fastcall TLm2000::ObtenerParametro( char *cadena, int narg, char *prm )
{
  int i;
  char *inicio, *fin;

  inicio = cadena;
  fin = cadena;

  // Delimitación del parámetro
  for ( i=0; *fin != '\0' && i < narg; fin++ )
  {
    // Si es el comienzo de un nuevo parametro...
	if ( *fin == ',' )
	{
	  i++;
	  if ( i < narg )
		inicio = (fin + 1);
	}
  }

  // Longitud del parametro
  i = fin - inicio - 1;
  // Copia aislada del parámetro
  strncpy( prm, inicio, i );
  prm[ i ] = '\0';

  return prm;
}
//-----------------------------------------------------------------------------
/**
  Esta es la función que debe ser redefinida...
*/
// bool BuscaTarjeta( int FGA, int TPA, int CDA, int *TPR, int *PNR, char *message )
// {
//   bool enc = false;
//
//   #ifdef _verbose
// 	(printfv)( "Localizando tarjeta: (%d)-%d-%d", FGA, TPA, CDA );
//   #endif
//   // Localizamos la tarjeta
//   if ( /* LOCALIZADA */ 1 )
//   {
// 	  *TPR = 0;
// 	  *PNR = 0;
// 	  sprintf( message, "ACCESO PERMITIDOwww.infdj.com" );
//  	  enc = true;
//   } else {
// 	  sprintf( message, "ACCESO DENEGADOwww.infdj.com" );
// 	  enc = false;
//   }
//
//   return enc;
// }
//
//-----------------------------------------------------------------------------
/**
   Procesa una tarjeta despues de un SIMPLE_INTERROGATION
*/
bool __fastcall TLm2000::ProcesaTarjeta( int ID, char *msg )
{
 char buff[80], rcv[80];
 char message[80];
 int FGA, TPA, CDA, TPR, PNR;
 #ifdef _verbose
	(printfv)( Format("ProcesaTarjeta: %s", ARRAYOFCONST((msg)) ) );
 #endif

 // Obtenemos los datos de la tarjeta
 FGA = atoi( ObtenerParametro( msg, 2, buff ) );
 TPA = atoi( ObtenerParametro( msg, 3, buff ) );
 CDA = atoi( ObtenerParametro( msg, 4, buff ) );


 // La cotejamos con nuestra base de datos ( CDA )
#ifdef PRUEBAS_SIN_VCL
 if ( 1 )
#else
 if ( (FOnBuscaTarjeta) && (FOnBuscaTarjeta)( ID, FGA, TPA, CDA, &TPR, &PNR, message ) )
#endif
 {
	 // ¿Tiene permiso?
	 sprintf( buff, "%s, %d, %d,%s", CARD_VALID, TPR, PNR, message );
	 EnviarComando( buff, ID, rcv );

         if ( rcv[5] == 'X' )
         {
           // Necesitamos esperar a que se libere la linea de comunicación
           Sleep( 1000 );
           PassageOpening( ID, (FGA & 0x0008) ? 1 : 2 );
         }

 } else {
	// Si no esta...
	sprintf( buff, "%s, %d,%s", CARD_NOT_VALID, RTF_MOTIVE_NOT_STANDARD, message );
	EnviarComando( buff, ID, rcv );
 }

 return ( rcv[5] == 'X' );

}
//-----------------------------------------------------------------------------
/**
 Realiza una InterrogaciónSimple
*/
char * __fastcall TLm2000::SimpleInterrogation( int ID, char *rcv )
{
	char prm[7][20];


	EnviarComando( SIMPLE_INTERROGATION, ID, rcv );

	// Tratamos las posibles respuestas a una Interrogación Simple
    switch( rcv[5] )
	{
		// Transit to download
	    case 'T':
			#ifdef _verbose
				(printfv)( "Transit to download\n" );
			#endif
			ProcesaTransito( ObtenerParametro( rcv, 2, prm[0] ),
							 ObtenerParametro( rcv, 3, prm[1] ),
							 ObtenerParametro( rcv, 4, prm[2] ),
							 ObtenerParametro( rcv, 5, prm[3] ),
							 ObtenerParametro( rcv, 6, prm[4] ),
							 ObtenerParametro( rcv, 7, prm[5] ),
							 ObtenerParametro( rcv, 8, prm[6] ) );
			EnviarComando( SHOW_NEXT_TRANSIT, ID, rcv );
   		  break;
		// Information to Request
	    case 'H':
			#ifdef _verbose
				(printfv)( "Information to Request\n" );
			#endif
			ProcesaTarjeta( ID, rcv );
		  break;
		// Alarm
	    case 'A':
		  	#ifdef _verbose
				(printfv)( "Alarm\n" );
			#endif
		  break;
		// No information to communicate
	    case 'R':
			#ifdef _verbose
				(printfv)( "No information to communicate\n" );
			#endif
		  break;
		// -- error --
		default:
			#ifdef _verbose
				(printfv)( "ERROR: SimpleInterrogration ERROR de RESPUESTA\n" );
			#endif
		  break;
	}

	return rcv;

}
//-----------------------------------------------------------------------------
/**
    Lee una tarjeta de la memoria del lector
*/
bool __fastcall TLm2000::ReadCardMemory( int ID, TCard *CRD )
{
  bool hay_mas_tarjetas = false;	
  char buff[80], rcv[80];

  EnviarComando( READ_CARD_MEMORY, ID, rcv );
  if ( rcv[0] != 'E' )
  {
	hay_mas_tarjetas = !( rcv[5] == 'X' && rcv[7] == '1' );
	if ( hay_mas_tarjetas )
	{
		// Procesamos los parámetros
		CRD->FLG = atol( ObtenerParametro( rcv,  2, buff ) ); // FLG
		CRD->TYP = atol( ObtenerParametro( rcv,  3, buff ) ); // TYP
		CRD->CDA = atol( ObtenerParametro( rcv,  4, buff ) ); // CDA 
		CRD->PIN = atol( ObtenerParametro( rcv,  5, buff ) ); // PIN 
		CRD->GIS = atol( ObtenerParametro( rcv,  6, buff ) ); // GIS
		CRD->MES = atol( ObtenerParametro( rcv,  7, buff ) ); // MES
		CRD->ANS = atol( ObtenerParametro( rcv,  8, buff ) ); // ANS 
		CRD->ORS = atol( ObtenerParametro( rcv,  9, buff ) ); // ORS 
		CRD->MIS = atol( ObtenerParametro( rcv, 10, buff ) ); // MIS
		CRD->SES = atol( ObtenerParametro( rcv, 11, buff ) ); // SES
		CRD->REV = atol( ObtenerParametro( rcv, 12, buff ) ); // REV
		CRD->CRD = atol( ObtenerParametro( rcv, 13, buff ) ); // CRD
		#ifdef _verbose
			(printfv)( Format("FLG: %ld\nTYP: %ld\nCDA: %ld\nPIN: %ld\nGIS: %ld\nMES: %ld\nANS: %ld\nORS: %ld\nMIS: %ld\nSES: %ld\nREV: %ld\nCRD: %ld\n", ARRAYOFCONST((CRD->FLG, CRD->TYP, CRD->CDA, CRD->PIN, CRD->GIS, CRD->MES, CRD->ANS, CRD->ORS, CRD->MIS, CRD->SES, CRD->REV, CRD->CRD)) ) );
			(printfv)( "CARD_FLAG\n--------\n" );
	   	    int nFLAG = CRD->FLG;
			(printfv)( Format("EXTERNAL READER:   %s\nINTERNAL READER:   %s\nFROM LEFT TO RIGHT:   %s\nFROM RIGHT TO LEFT   %s\n",ARRAYOFCONST(( (nFLAG & 0x08) ? "SI":"NO",
																			   (nFLAG & 0x04) ? "SI":"NO",
                                                                                                                                                           (nFLAG & 0x02) ? "SI":"NO",
																			   (nFLAG & 0x01) ? "SI":"NO")) ) );
		#endif
	}
  } else {
	#ifdef _verbose
	  (printfv)( Format("ERROR READ_CARD_MEMORY: %s\n", ARRAYOFCONST((rcv)) ) );
	#endif
  }

  return hay_mas_tarjetas;
}
//-----------------------------------------------------------------------------
/**
   Debe devolver la lista de tarjetas de la memoria completa
   (No implento el parametro de salida de momento)
*/
void __fastcall TLm2000::ReadEntireCardMemory( int ID )
{
  char rcv[80];
  TCard CRD;

  	EnviarComando( INITIALIZE_READING, ID, rcv );
	if ( rcv[0] != 'E' )
	{
		while( ReadCardMemory( ID, &CRD ) )
		{
  			// Procesar Tarjeta
			///***///
			EnviarComando( SHOW_NEXT_MESSAGE, ID, rcv );
		};
	} else {
		#ifdef _verbose
		  (printfv)( Format("ERROR FIRST_CARD: %s\n", ARRAYOFCONST((rcv)) ) );
		#endif
	}
}
// #############################################################################
// #############################################################################
// #############################################################################
// #############################################################################
// #############################################################################
// #############################################################################
// #############################################################################
//-----------------------------------------------------------------------------
/**
   Introducimos tarjetas en la Memoria
*/
int __fastcall TLm2000::EnterCardMemory( int ID, TCard *CRD )
{
  int dev;
  char enviar[80], rcv[80];

  sprintf( enviar, "%s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", ENTER_CARD_MEMORY,
	  CRD->FLG,   CRD->TYP,	  CRD->CDA,	  CRD->PIN,	  CRD->GIS,   CRD->MES,
	  CRD->ANS,	  CRD->ORS,	  CRD->MIS,	  CRD->SES,	  CRD->REV,	  CRD->CRD );

  EnviarComando( enviar, ID, rcv );

  if ( rcv[5] == 'X' )
  {
	  dev = atol( ObtenerParametro( rcv,  2, enviar ) );
  } else
  if ( rcv[5] == 'E' )
  { 
	switch( rcv[7] )
	{
	case '6':
			dev = -6;
			#ifdef _verbose
				(printfv)( Format("ERROR EnterCardMemory Memory full: %s", ARRAYOFCONST((rcv)) ) );
			#endif
			break;
	case '8':
			dev = -8;
			#ifdef _verbose
				(printfv)( Format("ERROR EnterCardMemory Card code below acceptable minimum: %s", ARRAYOFCONST((rcv)) ) );
			#endif
			break;
	default:
			#ifdef _verbose
				(printfv)( Format("ERROR EnterCardMemory (SIN IDENTIFICAR): %s", ARRAYOFCONST((rcv)) ) );
			#endif
	}

  }
  return dev;
}
//-----------------------------------------------------------------------------
/**
 Elimina del LM'ID' la tarjeta TIP/COD
*/                    
int __fastcall TLm2000::DeleteCardMemory( int ID, int TIP, int COD )
{
 int dev;
 char enviar[80], rcv[80];
 sprintf( enviar, "%s,0,%d,%d", DELETE_CARD_MEMORY, TIP, COD );
 EnviarComando( enviar, ID, rcv );

 if ( rcv[5] == 'X' )
	dev = atoi( ObtenerParametro( rcv, 2, enviar ) );
 else
	dev = 0;

 return dev;
}
//-----------------------------------------------------------------------------
/**
 Elimina TODAS las tarjetas de la memoria
*/
int __fastcall TLm2000::DeleteAllCardMemory( int ID, int TIP )
{
  return DeleteCardMemory( ID, TIP, TIP );
}
//-----------------------------------------------------------------------------
/**
 Lee la configuración del Lector ID
*/
RMem * __fastcall TLm2000::ReadMemoryCFG( int ID, RMem *mem )
{
  char rcv[80], prm[80];

  EnviarComando( READ_MEMORY_CFG, ID, rcv );

  mem->FMC = atoi( ObtenerParametro( rcv, 2, prm ) );
  mem->MAC = atoi( ObtenerParametro( rcv, 3, prm ) );
  mem->LFN = atoi( ObtenerParametro( rcv, 4, prm ) );
  mem->NCR = atoi( ObtenerParametro( rcv, 5, prm ) );
  mem->CMN = atoi( ObtenerParametro( rcv, 6, prm ) );
  mem->CMX = atoi( ObtenerParametro( rcv, 7, prm ) );
  mem->NBN = atoi( ObtenerParametro( rcv, 8, prm ) );
  mem->NMP = atoi( ObtenerParametro( rcv, 9, prm ) );
  mem->NFA = atoi( ObtenerParametro( rcv, 10, prm ) );
  mem->RMS = atoi( ObtenerParametro( rcv, 11, prm ) );
  mem->MCA = atoi( ObtenerParametro( rcv, 12, prm ) );
  mem->MCR = atoi( ObtenerParametro( rcv, 13, prm ) );
  
  #ifdef _verbose
	(printfv)( Format("%s\n", ARRAYOFCONST((rcv)) ) );
	(printfv)( Format("FMC: %d\nMAC: %d\nLFN: %d\nNCR: %d\nCMN: %d\nCMX: %d\nNBN: %d\nNMP: %d\nNFA: %d\nRMS: %d\nMCA: %d\nMCR: %d\n", ARRAYOFCONST((
		mem->FMC, mem->MAC, mem->LFN, mem->NCR, mem->CMN, mem->CMX, 
		mem->NBN, mem->NMP, mem->NFA, mem->RMS, mem->MCA, mem->MCR)) ) );
  #endif

  return mem;
}
//-----------------------------------------------------------------------------
/**
 Configura el Lector ID
*/
bool __fastcall TLm2000::ConfigureMemory( int ID, RMem *mem )
{
  bool dev;
  char enviar[80], rcv[80];
  // Verificamos que todos los parámetros sean correctos
  if ( ((mem->FMC) & ( FMC_LONG_CODES + FMC_MINI_MEMORY )) )
  {
	#ifdef _verbose
	  (printfv)( "Error ConfigureMemory: FMC_LONG_CODES y FMC_MINI_MEMORY son incompatibles" );
	#endif
    dev = false;
  } else {
    // Configuramos la memoria
	sprintf( enviar, "%s,%s,,%s,,%s,%s,%s,%s,%s,%s,%s,%s", CONFIGURE_MEMORY, 
		mem->FMC, mem->LFN, /*mem->NCR, */ mem->CMN, mem->CMX, mem->NBN,
		mem->NMP, mem->NFA, mem->RMS, mem->MCA, mem->MCR );
	EnviarComando( enviar, ID, rcv );
	if ( rcv[5] == 'E' ) 
	{
		#ifdef _verbose
			(printfv)( "ERROR ConfigureMemory: MEMORY IS FULL ó PAMARETERS ARE INCORRECT" );
		#endif
		dev = false;
	}
	if ( rcv[5] == 'X' )
	{
		int resp = atoi( ObtenerParametro( rcv, 2, enviar ) );
		#ifdef _verbose
			(printfv)( "ConfigureMemory: " );
			switch( resp )
			{
			case 0: (printfv)( "Memory not affected" ); break;
			case 1: (printfv)( "Transit memory" ); break;
			case 2: (printfv)( "Transit memory + personal messages memory" ); break;
			case 3: (printfv)( "Transit memory + personal messages memory + periods" ); break;
			default:
				(printfv)( Format("Card Memory [ %d ]", ARRAYOFCONST((resp)) ) ); break;
			}
			dev = true;
		#endif
	}
  }
  return dev;
}
//-----------------------------------------------------------------------------
/**
  Programación de los Periodos
*/
bool __fastcall TLm2000::ProgrammingPeriods( int ID, int NFS, int PBN, char *StarTime, char *EndTime, int GIF )
{
  char enviar[80], rcv[80];

  sprintf( enviar, "%s,%d,%d,%s,%s,%d", PROGRAMMING_PERIODS, NFS, PBN, StarTime, EndTime, GIF );
  EnviarComando( enviar, ID, rcv );

  #ifdef _verbose
   if ( rcv[5] == 'E' )
	(printfv)( Format("ERROR ProgrammingPeriods Arguments incorrect: %s", ARRAYOFCONST((rcv)) ) );
  #endif

  return ( rcv[5] == 'X' );
}
//-----------------------------------------------------------------------------
/**
 Configura el número de caracteres en la banda
*/
bool   __fastcall TLm2000::SetCharsOnBand( int ID, CAB *cab )
{
  char enviar[80], rcv[80];

  sprintf( enviar, "%s,%d,%d,%d,%d,%d,%d,%d,%d", WRITE_CHARS_ON_BAND, cab->GAP, cab->LEP, cab->GAC, cab->LEC, cab->GAR, cab->LER, cab->GDI, cab->GDF );
  EnviarComando( enviar, ID, rcv );

  #ifdef _verbose
   if ( rcv[5] == 'E' )
	(printfv)( Format("ERROR SetCharsOnBand: %s", ARRAYOFCONST((rcv)) ) );
  #endif

  return ( rcv[5] == 'X' );
}
//-----------------------------------------------------------------------------
/**
  Parametros de operacion del programa
*/
bool __fastcall TLm2000::ProgramOperationParameters( int ID, POP *pop )
{
  char enviar[80], rcv[80];

  sprintf( enviar, "%s,%d,%d,%d,%d,%d,%d,%d,%d", PROGRAM_OPERATION_PARAMETERS, pop->BKN, pop->BKP, pop->PRN, pop->MCL, pop->MCK, pop->ENF, pop->SNV, pop->ENI );
  EnviarComando( enviar, ID, rcv );

  return ( rcv[5] == 'X' );
}
//-----------------------------------------------------------------------------
/**
  Activar los reles de salida
  1 Internal Reader
  2 External Reader
  3 Output 1
  4 Output 2
*/
bool __fastcall TLm2000::PassageOpening( int ID, int Passage )
{
  char enviar[80], rcv[80];

  sprintf( enviar, "%s,%d", PASSAGE_OPENING, Passage );
  EnviarComando( enviar, ID, rcv );

  return ( rcv[5] == 'X' );
}
//-----------------------------------------------------------------------------
/**
  Reconfigura el reloj
*/
bool __fastcall TLm2000::ResetClock( int ID, RClock *clock )
{
  int err;
  char enviar[80], rcv[80];

  sprintf( enviar, "%s,%d,%d,%d,%d,%d,%d", RESET_CLOCK, clock->GIA, clock->MEA, clock->ANA, clock->ORA, clock->MIA, clock->SEA );
  EnviarComando( enviar, ID, rcv );

  #ifdef _verbose
	if ( rcv[5] == 'E' )
	{
		err = atoi( ObtenerParametro( rcv, 2, enviar ) );
		(printfv)( Format( "ERROR ResetClock: %s", ARRAYOFCONST(( (err == 0) ? "Date incorrect" : "Time incorrect")) ) );
	}
  #endif


  return ( rcv[5] == 'X' );
}
//-----------------------------------------------------------------------------
/**
 Lee los caracteres en la banda
*/
bool __fastcall TLm2000::GetCharsOnBand( int ID, CAB *cab )
{
  int dev;
  char enviar[80], rcv[80];

  sprintf( enviar, "%s", READ_CHARS_ON_BAND );
  EnviarComando( enviar, ID, rcv );

  if ( rcv[5] != 'E' )
  {
	  dev = true;
          cab->GAP = atoi( ObtenerParametro( rcv, 2, enviar ) );
          cab->LEP = atoi( ObtenerParametro( rcv, 3, enviar ) );
          cab->GAC = atoi( ObtenerParametro( rcv, 4, enviar ) );
          cab->LEC = atoi( ObtenerParametro( rcv, 5, enviar ) );
          cab->GAR = atoi( ObtenerParametro( rcv, 6, enviar ) );
          cab->LER = atoi( ObtenerParametro( rcv, 7, enviar ) );
          cab->GDI = atoi( ObtenerParametro( rcv, 8, enviar ) );
          cab->GDF = atoi( ObtenerParametro( rcv, 9, enviar ) );
	  #ifdef _verbose
		(printfv)( Format("ReadCharsOnBand: %d", ARRAYOFCONST((dev)) ) );
	  #endif
  } else {
	  dev = false;
	  #ifdef _verbose
		(printfv)( Format("ERROR ReadCharsOnBand: %s", ARRAYOFCONST((rcv)) ) );
	  #endif
  }

  return dev;

}
//-----------------------------------------------------------------------------
/**
  Lee los prefijos activos
*/
int __fastcall TLm2000::ReadingPrefixes( int ID, int NPR )
{
  int dev;
  char enviar[80], rcv[80];

  sprintf( enviar, "%s,%d", READ_PREFIXES, NPR );
  EnviarComando( enviar, ID, rcv );

  if ( rcv[5] != 'E' )
  {
	  dev = atoi( ObtenerParametro( rcv, 2, enviar ) );
	  #ifdef _verbose
		(printfv)( Format("ReadingPrefixes: %d", ARRAYOFCONST((dev)) ) );
	  #endif
  } else {
	  dev = -1;
	  #ifdef _verbose
		(printfv)( Format("ERROR ReadingPrefixes: NPR argument", ARRAYOFCONST((dev)) ) );
	  #endif
  }

  return dev;
}
//-----------------------------------------------------------------------------
/**
 Lee los mensajes personales
*/
bool __fastcall TLm2000::ReadPersonalMessage( int ID )
{
  char rcv[80], prm[80];
  int TPM, CDM, MSU;

  EnviarComando( READ_PERSONAL_MESSAGE, ID, rcv );

  TPM = atoi( ObtenerParametro( rcv, 2, prm ) );
  CDM = atoi( ObtenerParametro( rcv, 3, prm ) );
  MSU = atoi( ObtenerParametro( rcv, 4, prm ) );

  #ifdef _verbose
	(printfv)( Format("TPM: %d\nCDM: %d\nMSU: %d\n-------\n", ARRAYOFCONST((TPM, CDM, MSU)) ) );
  #endif

  return ( rcv[5] != 'X' );
}
//-----------------------------------------------------------------------------
/**
  Lee todos los mensajes personales
*/
void __fastcall TLm2000::ReadAllPersonalMessage( int ID )
{
  char rcv[80];

  EnviarComando( INITIALIZE_READING_CM, ID, rcv );

  if ( rcv[5] == 'X' )
  {
	  while( ReadPersonalMessage( ID ) )
	  {
		  EnviarComando( SHOW_NEXT_PMESSAGE, ID, rcv );
	  };
  }
}

//-----------------------------------------------------------------------------
/**
 N.P.I de lo que hace esto
*/
void __fastcall TLm2000::SetTime( int ID, TSD *tsd )
{
  char enviar[80], rcv[80];

  sprintf( enviar, "%s,%d,%d,%d,%d,%d,%d,%d,", SET_TIME, tsd->CHN, tsd->GSL, tsd->MMS, tsd->ASL, tsd->HSL, tsd->MSL, tsd->SSL );
  EnviarComando( enviar, ID, rcv );

  if ( rcv[5] == 'X' )
  {
/*
	  while( ReadPersonalMessage( ID ) )
	  {
		  EnviarComando( SHOW_NEXT_PMESSAGE, ID, rcv );
	  };
*/
  }
}
//-----------------------------------------------------------------------------
/**
  Lee los mensajes del sistema
*/
char * __fastcall TLm2000::ReadSystemMessages( int ID, int IDmsg, char *rcv )
{
  char enviar[80];

  sprintf( enviar, "%s,%d,", READ_SYSTEM_MESSAGE, IDmsg );
  EnviarComando( enviar, ID, rcv );

  if ( rcv[5] == 'E' )
  {
	#ifdef _verbose
	  sprintf( rcv, "ERROR ReadSystemMessage: IDmsg fuera de RANGO" );
	#endif
	  
  }
  return rcv;
}
