//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

Connection *Aut;
TCP *Tcp;
Cmd *Cmds;
queue<AnsiString> *Fila;
_tPROT Prot;
Terminal* Term;


//---------------------------------------------------------------------------
Terminal::Terminal (void)
{
	idx = 1;
	Sm_Task = STATE;
	is_closing = false;
	VersaoMenu = VERSAO;
}
//---------------------------------------------------------------------------
void Terminal::ChangeConfig (void)
{
	Term->VersaoMenu = Str(Form1->E_C_Versao->Text).Ajuste_Len(' ','R',20);
	Tcp->ToutTx = StrToInt(Form1->E_TCPOUTTX->Text);
    Tcp->ToutRx = StrToInt(Form1->E_TCPOUTRX->Text);
	for (int count = 0; count < 100; count++)
	{
		Cmds[count].sm_task = READVERSION;
	}
}
//---------------------------------------------------------------------------
void Terminal::Task(void)
{
	switch (Sm_Task)
	{
		case STATE:
			Num = Cmds[idx].Send_Status();
			if (Num)
			{
				Sm_Task = LOOP;
			}
		break;
		case LOOP:
			if (Cont < Num)
			{
				idx = List[Cont];
				if (!idx) idx = 1;
				Cmds[idx].Task();
			}
			else
			{
				Sm_Task = STATE; // ja varreu todos os terminais
				Cont = 0;
			}
		break;
	}

}
//---------------------------------------------------------------------------
String Str::Ajuste_Len (char ch ,char side, int new_len)
{
	int count = 0,dif,len = Txt.Length();
	String a;
	char* acc = new char[new_len+1];

	dif = new_len - len;
	if (dif > 0)
	{
		for (int count_aux = 1; count < new_len; count++)
		{
			if (side == 'L')
			{
				if (count < dif)
				{
					acc[count] = ch;
				}
				else
				{
					acc[count] = Txt[count_aux++];
				}
			}
			else if ('R')
			{
				if (count < len)
				{
					acc[count] = Txt[count_aux++];
				}
				else
				{
					acc[count] = ch;
				}
			}
			else break;
		}
		acc[count] = '\0';
		Txt = String(acc);
	}
	free(acc);
	return Txt;
}
//---------------------------------------------------------------------------
Str::Str (String val)
{
	Txt = val;
}
//---------------------------------------------------------------------------
void Cmd::AutPutBegin (void)
{
	Tx_Checksum = 0;
	Buff_Tx[Tx_Push++]  = '>';   // Não entra no cálculo do check
	Buff_Tx[Tx_Push]  = '?';
	Tx_Checksum = Buff_Tx[Tx_Push++];
}
//---------------------------------------------------------------------------
void Cmd::AutPut (String cmd)
{
   for (int count = 0; count < cmd.Length(); count++)
   {
		Buff_Tx[Tx_Push + 4 + count] = cmd[count+1]; // ansi indexa do 1
		Tx_Checksum += cmd[count+1];
   }
   Tx_Push += cmd.Length();
}
//---------------------------------------------------------------------------
void Cmd::AutPutFlush (void)
{
   BYTE len = ((Tx_Push - Tx_Pop) - 2);// não conta os marcadores nem o próprio campo
   String tamanho = IntToHex(len,4);
   for (int count = 0; count < 4; count++)
   {
		Buff_Tx[Tx_Push - len + count]  = tamanho[count+1];  //string indexa do 1, pula os marcadores
		Tx_Checksum += tamanho[count+1];
   }
   Tx_Push += 4;
   String ck = IntToHex(Tx_Checksum,2);

   for (int count = 1; count < 3; count++)
   {
		Buff_Tx[Tx_Push++] = ck[count];
   }
   Buff_Tx[Tx_Push] = '\0';
   String cmd = Buff_Tx;


   Fila->push(cmd);
   Tx_Push = 0;
   Tx_Pop = 0;
   Tx_Checksum = 0;

   Pckg_Ok = false;

}
//---------------------------------------------------------------------------
Cmd::Cmd(void)
{
	Tx_Push = 0;
	sm_task = READVERSION;
	sm_back = PENDENCY;
	sm_state = TX;
	Tx_Pop = 0;
	Tx_Pop = 0;
	Pckg_Ok = false;
}
//---------------------------------------------------------------------------
String Cmd::Parser(char *p, int len)
{
	int count = 0,count_aux = 0;;
	char *Data = new char[len];


	while (count < len)
	{
		BYTE data = p[count++];
		switch (Prot.Sm)
		{
			case STX:
				if (data == '>')
				{
					Prot.Sm = INIT;
					Prot.Checksum = 0;
				}
			break;
			case INIT:
				if (data == '!')
				{
					Prot.Sm = GET_LEN;
					Prot.Checksum += data;
				}
			break;
			case GET_LEN:
					Prot.Len_Acc[Prot.Len_Count++] = data;
					Prot.Checksum += data;
				if (Prot.Len_Count == 4)
				{
					HexToBin(Prot.Len_Acc,Prot.Len_Aux,2);
					Prot.Len = (Prot.Len_Aux[0] << 8 | Prot.Len_Aux[1]);
					if (Prot.Len > 4)
					{
						//a = 10;
					}
					Prot.Len_Count = 0;
					Prot.Sm = GET_DATA;
				}
			break;
			case GET_DATA:
			   Prot.Checksum += data;
			   Data[count_aux++] = data;
			   if (Prot.Data_Count++ >= Prot.Len - 1)
			   {
					Data[count_aux] = '\0';
					Prot.Data_Count = 0;
					Prot.Sm = GET_CHECK;
			   }
			break;
			case GET_CHECK:
				Prot.Ck_Acc[Prot.Ck_Count++] = data;
				if (Prot.Ck_Count >= 2)
				{
					Prot.Ck_Acc[Prot.Ck_Count] = '\0';
					Prot.Ck = IntToHex(Prot.Checksum,2);
					Prot.Ck_Count = 0;
					if (AnsiString(Prot.Ck_Acc) == Prot.Ck)
					{
						Dados = String(Data);
						Aut->Write_term("Rx",String(Dados).SubString(1,2),AnsiString(Tcp->Buff_Rx),Data,NULL);
						Pckg_Ok = true;
					}
				}
			break;

			default:
			;
		}
	}

}
//---------------------------------------------------------------------------
int Cmd::Send_Status (void)
{
	switch (sm_state)
	{
		case TX:
			AutPutStr("2F");
			sm_state = RX;
			return 0;
		break;
		case RX:
			if (Pckg_Ok)
			{
				String cmd = Dados.SubString(1,2);
				if (cmd == "2F")
				{
					int len = Dados.Length();
					if (len > 2)
					{
						int Qtd_term = Extract_Term(Dados);
						return Qtd_term;
					}
				sm_state = TX;
				return 0;
				}
				else
				{
					sm_state = TX;
					return 0;
				}
			}
			else return 0;
		break;
	}
}
//---------------------------------------------------------------------------
int Cmd::Extract_Term(String str)
{
	int N_terminais = (str.Length() - 2)/2;

	for (int i = 0; i < N_terminais; i++)
	{
		 Term->List[i] = StrToInt(str.SubString((i*2)+3,2));
	}

	return N_terminais;
}
//---------------------------------------------------------------------------
void Cmd::Parser_Term(String s)
{
	String cmd = Dados.SubString(1,2);
	String escape = Dados.SubString(10,1);
	String index = Dados.SubString(7,2);
	int len, Qtd_term;
	len = Dados.Length();

	if (cmd == "2C")
	{
		if (Error < MAXERROR)
		{
			if (escape != "G")
			{
				//***************** inicia abastecimento frota/coleta placa *****************
				if (index == "20" || index == "2A")
				{
					Form1->L_F_Placa->Text = s.SubString(12,8);
					Menu = FROTA;
					sm_task = WRITEODO;
				}
				//***************** inicia abastecimento cliente/coleta card frentista *****************
				else if (index == "21" || index == "2B")
				{
					Form1->L_C_CardC->Text = s.SubString(15,16);
					Menu = CLIENTE;
					sm_task = WRITECARDF;
				}
				//***************** lendo versao de menu *****************
				if (index == "80")
				{
					sm_back = sm_task;
					String versao = Dados.SubString(10, len - 9);
					if (versao != Term->VersaoMenu)
					{
						sm_task = WRITEVERSION;
					}
					else sm_task = CHECKED;
				}
				//***************** Abastecimento frota /coleta de Odometro *****************
				else if (index == "82")
				{
					Form1->L_F_Odo->Text = s.SubString(17,8);
					sm_task = WRITECPF;
				}
				//***************** Abastecimento frota /coleta de CPF *****************
				else if (index == "83")
				{
					Form1->L_F_CPF->Text = s.SubString(18,16);
					sm_task = WRITECARDF;
				}
				//***************** Abastecimento/coleta de card Frentista *****************
				else if (index == "84")
				{
					String aux =  s.SubString(15,16);
					if (Menu == FROTA)
					{
						Form1->L_F_CardF->Text = aux;
						sm_task = WRITEOLEO;
					}
					else if (Menu == CLIENTE)
					{
						Form1->L_C_CardF->Text = aux;
						sm_task = WRITESENHA;
					}
				}
				//***************** Abastecimento frota /coleta de troca de óleo *****************
				else if (index == "85")
				{
					sm_task = WRITEBICO;
					char digit = s[15];
					if (digit == '1') Form1->L_F_Óleo->Text = "Sim";
					else if (digit == '2') Form1->L_F_Óleo->Text = "Não";
					else sm_task = WRITEOLEO;
				}
				//***************** Abastecimento/coleta de bico *****************
				else if (index == "86")
				{
					String aux = s.SubString(13,2);
					if (Menu == FROTA) Form1->L_F_Bico->Text = aux;
					else if (Menu == CLIENTE) Form1->L_C_Bico->Text = aux;
					sm_task = WRITEABAST;
				}
				//***************** Abastecimento cliente /coleta de senha *****************
				else if (index == "87")
				{
					 Form1->L_C_Senha->Text = s.SubString(15,5);
					 sm_task = WRITETAG;
				}
				//***************** Abastecimento cliente /coleta de tag do veículo *****************
				else if (index == "88")
				{
					 Form1->L_C_TagV->Text = s.SubString(15,16);
					 sm_task = WRITEVALUE;
				}
				//***************** menu p/ consulta de cartao *****************
				else if (index == "89")
				{
					Form1->L_C_Value->Text = s.SubString(15,6);
					sm_task = WRITEBICO;
				}
				else if (index == "93" || index == "94")
				{
					sm_task = WRITEBICO;
				}
				else Error++;
			}
			else sm_task = CANCELED;
		}
		else
		{
			Error = 0;
			sm_task = sm_back;
		}
	}
	else if (cmd == "26" )
	{
		String valid = s.SubString(3,2);
		char erro = valid[1];
		if (valid == "00") sm_task = WRITEABASTINIT;
		else if (erro == '7') sm_task = WRITEBICOOFF;
		else if (erro == '8') sm_task = WRITEBICOBUSY;
	}
	else if (cmd == "2B" || cmd == "2E")
	{
		String index2 = Dados.SubString(3,2);
		if (index2 == "E1" || index2 == "E5") sm_task = CLEAN;
		Error = 0;
	}
}
//---------------------------------------------------------------------------
void Cmd::Task(void)
{
	if (!Pendency)
	{
		Pendency = true;
		String NumLogic = Str(IntToStr(Term->idx)).Ajuste_Len('0','L',2);
		switch (sm_task)
		{
			case PENDENCY:
				AutPutStr("2C" + NumLogic);
			break;
			case READVERSION:
				AutPutStr("2B" + NumLogic + "0E80|ML");
				sm_back = sm_task;
				sm_task = PENDENCY;
			break;
			case WRITEVERSION:
				AutPutStr("2E" + NumLogic + "0E81|MG" + Term->VersaoMenu);
				sm_task = WRITEMEN1;
			break;
			case WRITEMEN1:
				AutPutStr("2E" + NumLogic + "0E20Abastecer Frota|D0030|T1Insira a Placa: |EM08SSSS-NNN");
				sm_task = WRITEMEN2;
			break;
			case WRITEMEN2:
				AutPutStr("2E" + NumLogic + "0E21Abastecer Cliente|D1730|T1Aproxime o Cartão|D2450|T1 do cliente.|D2070|C|KN");
				sm_task = WRITEMENA;
			break;
			case WRITEMENA:
				AutPutStr("2E" + NumLogic + "0E2BAbastecer Cliente|D1730|T1Aproxime o Cartão|D2450|T1 do cliente.|D2070|C|KN");
				sm_task = WRITEMENB;
			break;
			case WRITEMENB:
				AutPutStr("2E" + NumLogic + "0E2BAbastecer Frota|D0030|T1Insira a Placa: |EM08SSSS-NNN");
				sm_task = WRITETELA;
			break;
			case WRITETELA:
				AutPutStr("2E" + NumLogic + "0E2CCompanytec System|D1820|T2Companytec|D2250|T2Soluções");
				sm_task = READVERSION;
			break;
			case CHECKED:
				AutPutStr("2E" + NumLogic + "0E91Terminal|T1Sucesso!|R|R|T1Terminal Atualizado ...|KN");
				sm_task = PENDENCY;
			break;
			case CANCELED:
				AutPutStr("2E" + NumLogic + "0E90Terminal|D1535|T1Operação Cancelada|KN");
				sm_task = PENDENCY;
			break;
			case CLEAN:
				AutPutStr("2D" + NumLogic);
				sm_task = PENDENCY;
			break;
			case WRITEODO:
				AutPutStr("2E" + NumLogic + "0E82Lê Odômetro|D1730|T1Insira o Odômetro|R|R|D2450|T1 do Veículo.|D3570|EN080");
				sm_task = PENDENCY;
			break;
			case WRITECPF:
				AutPutStr("2E" + NumLogic + "0E83Le CPF|D2530|T1Insira o CPF|D3045|T1do Cliente|D2065|EC");
				sm_task = PENDENCY;
			break;
			case WRITECARDF:
				AutPutStr("2E" + NumLogic + "0E84Le Cartão|D3030|T1Aproxime o|D3050|T1seu Cartão.|D2070|C|KN");
				sm_task = PENDENCY;
			break;
			case WRITEOLEO:
				AutPutStr("2E" + NumLogic + "0E85Le Óleo|D2430|T1Troca de óleo?|D2250|T1<1:Sim   2:Não>|D5070|EN010");
				sm_task = PENDENCY;
			break;
			case WRITEBICO:
				AutPutStr("2E" + NumLogic + "0E86Le Bico|D2530|T1Insira o Bico.|D4550|EN020");
				sm_task = PENDENCY;
			break;
			case WRITESENHA:
				AutPutStr("2E" + NumLogic + "0E87Le Senha|D2530|T1Insira a Senha|D2850|T1de 5 dígitos.|D4070|EM05NNNKK");
				sm_task = PENDENCY;
			break;
			case WRITETAG:
				AutPutStr("2E" + NumLogic + "0E88Le Tag|D2530|T1Leia a Tag do|D3550|T1Veiculo.|D2170|C|KC");
				sm_task = PENDENCY;
			break;
			case WRITEVALUE:
				AutPutStr("2E" + NumLogic + "0E89Le Valor|D1830|T1Insira o Valor ($)|D2150|T1do abastecimento|D3870|EN062");
				sm_task = PENDENCY;
			break;
			case WRITEABAST:
				switch (Menu)
				{
					case FROTA:
						AutPutStr("26" + Form1->L_F_Bico->Text + Form1->L_F_CardF->Text + "0S00000020V00000");
					break;
					case CLIENTE:
						AutPutStr("26" + Form1->L_C_Bico->Text + Form1->L_C_CardF->Text + "0S" + Form1->L_C_Value->Text + "20$00000");
					break;

				}
				sm_task = PENDENCY;
			break;
			case WRITEABASTINIT:
				AutPutStr("2E" + NumLogic + "0E92Terminal|T1Sucesso!|R|R|T1Bico Liberado ...|KN");
				sm_task = PENDENCY;
			break;
			case WRITEBICOBUSY:
				AutPutStr("2E" + NumLogic + "0E93Terminal|T1Erro!|R|R|T1Bico Ocupado ...|KC");
				sm_task = PENDENCY;
			break;
			case WRITEBICOOFF:
				AutPutStr("2E" + NumLogic + "0E94Terminal|T1Erro|R|R|T1Bico Inexistente ...|KC");
				sm_task = PENDENCY;
			break;
		}
	}
	else
	{
		if (!Aut->Connected()) Pendency = false;
		else
		{
			if (Pckg_Ok)
			{
				Parser_Term(Dados);
				Pendency = false;
				Term->Cont++; // incrementa contador de terminais.
			}
		}
	}
}
//---------------------------------------------------------------------------
void Cmd::AutPutStr(String str)
{
	if (Aut->Connected())
	{
		AutPutBegin();
		AutPut(str);
		AutPutFlush();
	}
}
//---------------------------------------------------------------------------
Tick::Tick(void)
{
	Count = 0;
}
//---------------------------------------------------------------------------
int Tick::GetTick(void)
{
	return Count;
}
//---------------------------------------------------------------------------
bool Tick::TickCompare(int val)
{
	if (val - GetTick() <= 0) return true;
	else return false;
}
//---------------------------------------------------------------------------
Grid::Grid(void)
{
	Line = 1;
}
//---------------------------------------------------------------------------
void Connection::Write_term(String idx = NULL,String Cmd = NULL, String Cmd1 = NULL, String Cmd2 = NULL,String Cmd3 = NULL)
{
	Term.Grade->Cells[0][Term.Line] = idx;
	Term.Grade->Cells[1][Term.Line] = Cmd;
	Term.Grade->Cells[2][Term.Line] = Cmd1;
	Term.Grade->Cells[3][Term.Line] = Cmd2;
	Term.Grade->Cells[4][Term.Line] = Cmd3;
	Term.Grade->RowCount++;
	if (!Term.Paused) Term.Grade->Row = Term.Line;
	Term.Line++;
}
//---------------------------------------------------------------------------
void Connection::Inc_Port(void)
{
	if (Port->ItemIndex > 2) Port->ItemIndex = 0;
	else Port->ItemIndex++;
}
//---------------------------------------------------------------------------
bool Connection::Connected(void)
{
	return is_Connected;
}
//---------------------------------------------------------------------------
void Connection::Task(void)
{
	switch (Sm)
	{
		case CONNECT:
			if (Tcp->Connect(AnsiString(Ip->Text).c_str(),StrToInt(Port->Text),Time))
			{
				Cmds = new Cmd[99];
				Write_term("Info","Conetado",Ip->Text,Port->Text);
				Sm = WAIT;
				is_Connected = true;
				Reconnecting = false;
				Connect_b->Caption = "Desconectar";
			}
			else
			{
				if (Reconnecting) Sm = DISCONNECT;
				Write_term("Info","Não Conetado",Ip->Text,Port->Text);
				Inc_Port();
				Connect_b->Caption = "Parar";
			}
		break;

		case RECONNECT:
			Reconnecting = true;
			Sm = DISCONNECT;
		break;
		case DISCONNECT:
			if (Tcp->Disconnect())
			{
				Write_term("Info","Desconectado",Ip->Text,Port->Text);
				is_Connected = false;
				Connect_b->Caption = "Conectar";
				if (!Reconnecting) Sm = WAIT;
				else Sm = CONNECT;
			}
		break;

		case WAIT:

		break;
	}
}
//---------------------------------------------------------------------------
void Connection::ConnectClick (TObject *Sender)
{
	if (!is_Connected) Sm = CONNECT;
	else  Sm = DISCONNECT;
}
//---------------------------------------------------------------------------
Connection::Connection(void)
{
	Ip = Form1->Edit1;
	Port = Form1->ComboBox2;
	Connect_b = Form1->Button1;
	Term.Grade = Form1->StringGrid1;
   	is_Connected = false;
	Connect_b->Caption = "Conectar";

	Ip->Text = IP;

	Port->Items->Add("1771");
	Port->Items->Add("2001");
	Port->Items->Add("857");
	Port->Items->Add("771");

	Port->ItemIndex = 0;

	Sm = WAIT;

	Time.tv_sec = 1;
	Time.tv_usec = 0;

	Line_term = 1;
	Term.Paused = false;
}
//---------------------------------------------------------------------------
TCP::TCP (void)
{
	Sm = TX;
	Tx = 0;
	Rx = 0;
	ToutTx = TOUTTX;
	ToutRx = TOUTRX;
}
//---------------------------------------------------------------------------
void TCP::Verify_Error(void)
{
	int a = GetError();
	switch (GetError())
	{
		case 1054:
			Aut->Sm = RECONNECT;
		break;
	}
}
//---------------------------------------------------------------------------
void TCP::Task(void)
{
	switch (Sm)
	{
		case TX:
			if (Tout.TickCompare(Tx))
			{
				Tx = Tout.GetTick() + ToutTx;
				Rx = Tout.GetTick() + ToutTx;
				if (!Fila->empty() && Aut->Connected())
				{
					AnsiString cmd = Fila->front();
					if (Send(cmd.c_str(),cmd.Length()))
					{
						Fila->pop();
						Aut->Write_term("Tx",cmd.SubString(7,2),cmd);
						Recept = false;
						Sm = RX;
					}
					else
					{
						Aut->Write_term("Tx","Não Enviado",cmd.SubString(7,2),cmd);
						Verify_Error();
					}
				}
			}
		break;
		case RX:
			if (!Tout.TickCompare(Rx))
			{
				int size = Receive(Buff_Rx,200);
				if (size > 0)
				{
					Buff_Rx[size] = '\0';
					Sm = TX;
					Recept = true;
					Rx_Push = size;
					Cmds[Term->idx].Parser(Buff_Rx,Rx_Push);
				}
			}
			else Sm = TX;
		break;

	}
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{

}
//---------------------------------------------------------------------------


void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Aut->ConnectClick(NULL);
}
//---------------------------------------------------------------------------


void __fastcall TForm1::FormCreate(TObject *Sender)
{

	Aut = new Connection;
	Tcp = new TCP;
	Term = new Terminal;
	Fila =  new queue<AnsiString>();

	Aut->Term.Grade->Cells[0][0] = "Idx";
	Aut->Term.Grade->Cells[1][0] = "Cmd";
	Aut->Term.Grade->Cells[2][0] = "Cmd1";
	Aut->Term.Grade->Cells[3][0] = "Cmd2";

}
//---------------------------------------------------------------------------

void __fastcall TForm1::T_TaskTimer(TObject *Sender)
{
	Aut->Task();
	if (Aut->Connected())
	{
		Term->Task();
		Tcp->Task();
		Tcp->Tout.Count++;
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::B_T_PausedClick(TObject *Sender)
{
	if (B_T_Paused->Caption == "Parar")
	{
		Aut->Term.Paused = true;
		B_T_Paused->Caption = "Continuar";
	}
	else if (B_T_Paused->Caption == "Continuar")
	{
		B_T_Paused->Caption = "Parar";
		Aut->Term.Paused = false;
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::B_T_CleanClick(TObject *Sender)
{
	Aut->Term.Line = 1;
	Aut->Term.Grade->RowCount = 1;
}
//---------------------------------------------------------------------------


void __fastcall TForm1::Button2Click(TObject *Sender)
{
	Term->ChangeConfig();
}
//---------------------------------------------------------------------------

