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

#include <vcl.h>
#pragma hdrstop

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

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
	// We set the variable to know which hardware is
	// currently selected (none!)
	//
    ActiveHardware = (HardwareType)-1;

	// Create a list to store the displayed mesasges
	//
    LastMsgsList = new TList();

    // Set the standard values in the interface
    //
    cbbHws->ItemIndex = 0;
    cbbBaudrates->ItemIndex = 0;
    cbbIO->ItemIndex = 18;
    cbbInterrupt->ItemIndex = 3;
    cbbMsgType->ItemIndex = 0;

    cbbBaudratesChange(this);
}
//---------------------------------------------------------------------------


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    tmrRead->Enabled = false;

    if(btnRelease->Enabled)
        btnRelease->Click();

    while(LastMsgsList->Count)
    {
        delete LastMsgsList->First();
        LastMsgsList->Delete(0);
    }
    delete LastMsgsList;
}
//---------------------------------------------------------------------------

void TForm1::ModifyMsgEntry(MessageStatus LastMsg,TPCANMsg NewMsg)
{
    AnsiString strLastData, strNewData;
    MessageStatus *NewStatus,*ToDelete;
    TListItem *CurrItem;
    int iLen;
    int iCount;

    strNewData = strLastData = "";

    // Values from the last messages
    //
    CurrItem = lstMessages->Items->Item[LastMsg.Position()];
    iCount = StrToInt(CurrItem->SubItems->Strings[3]);
    strLastData = CurrItem->SubItems->Strings[2];;
    iLen = (LastMsg.CANMessage()).LEN;

    // New values
    //
    if((NewMsg.MSGTYPE & MSGTYPE_RTR) != 0)
        strNewData = "Remote Request";
    else
        for(int i=0; i< NewMsg.LEN; i++)
            strNewData = (strNewData + IntToHex(NewMsg.DATA[i],2) + " ");

    // Count is updated
    //
    iCount += 1;


    // Set the changes
    //
    if(iLen != NewMsg.LEN)
    {
        iLen = NewMsg.LEN;
        CurrItem->SubItems->Strings[1] = IntToStr(iLen);
    }

    if(strLastData != strNewData)
        CurrItem->SubItems->Strings[2] = strNewData;

    CurrItem->SubItems->Strings[3] = IntToStr(iCount);

    // Save the new Status of the message
    // NOTE: The objects are saved in the same index position which
    // they use in the Listview
    //
    NewStatus = new MessageStatus(&NewMsg,LastMsg.Position());
    ToDelete = (MessageStatus*)LastMsgsList->Items[LastMsg.Position()];
    LastMsgsList->Delete(LastMsg.Position());
    delete ToDelete;
    LastMsgsList->Insert(LastMsg.Position(),NewStatus);
}

void TForm1::InsertMsgEntry(TPCANMsg NewMsg)
{
    AnsiString strNewData,strTemp;
    TListItem *CurrItem;
    MessageStatus *ToInsert;

    strTemp = strNewData = "";

    // Add the new ListView Item with the Type of the message
    //
    if((NewMsg.MSGTYPE & MSGTYPE_EXTENDED) != 0)
        strTemp = "EXTENDED";
    else
        strTemp = "STANDARD";

    if((NewMsg.MSGTYPE & MSGTYPE_RTR) == MSGTYPE_RTR)
        strTemp = (strTemp + "/RTR");

    CurrItem = lstMessages->Items->Add();
    CurrItem->Caption = strTemp;

    // We format the ID of the message and show it
    //
    if((NewMsg.MSGTYPE & MSGTYPE_EXTENDED) != 0)
        CurrItem->SubItems->Add(IntToHex((int)NewMsg.ID,8) + "h");
    else
        CurrItem->SubItems->Add(IntToHex((int)NewMsg.ID,3) + "h");

    // We set the length of the Message
    //
    CurrItem->SubItems->Add(IntToStr(NewMsg.LEN));

    // We format the data of the message. Each data is one
    // byte of Hexadecimal data
    //
    if((NewMsg.MSGTYPE & MSGTYPE_RTR) == MSGTYPE_RTR)
        strNewData = "Remote Request";
    else
        for(int i=0; i< NewMsg.LEN; i++)
            strNewData = (strNewData + IntToHex(NewMsg.DATA[i],2) + " ");

    CurrItem->SubItems->Add(strNewData);

    // The message is the First, so count is 1
    //
    CurrItem->SubItems->Add("1");

    // We add this status in the last message list
    //
    ToInsert = new MessageStatus(&NewMsg,CurrItem->Index);
    LastMsgsList->Add(ToInsert);
}

void TForm1::ProcessMessage(TPCANMsg MyMsg)
{
    bool bFound = false;
    MessageStatus *CurrMessage;

    // We search if a message (Smae ID, Type and same Net) is
    // already received or if this is a new message
    //
    for(int i=0; i< LastMsgsList->Count; i++)
    {
        CurrMessage = (MessageStatus*)LastMsgsList->Items[i];
        if(CurrMessage->CANMessage().ID == MyMsg.ID)
            if(CurrMessage->CANMessage().MSGTYPE == MyMsg.MSGTYPE)
            {
                bFound = true;
                break;
            }
    }

    if(bFound)
        // Messages of this kind are already received; we make an update
        //
        ModifyMsgEntry(*CurrMessage,MyMsg);
    else
        // Messages of this kind are not received; we make a new entry
        //
        InsertMsgEntry(MyMsg);
}
void __fastcall TForm1::cbbHwsChange(TObject *Sender)
{
    bool bShowIO;
    int current;

    current = cbbHws->ItemIndex;

    // According with the selection in the Hardware list,
    // we show/hide the input controls for I/O Address and
    // Interrupt. (This parameters are NOT necessary for all
    // hardware types) .
    //
    switch (cbbHws->ItemIndex)
    {
        case DNG:
            bShowIO = true;
            break;
        case DNP:
            bShowIO = true;
            break;
        case ISA_1CH:
            bShowIO = true;
            break;
        case ISA_2CH:
            bShowIO = true;
            break;
        default:
            bShowIO = false;
            break;
    }

    cbbIO->Enabled = bShowIO;
    cbbInterrupt->Enabled = bShowIO;

	// According with the selection in the Hardware list, we 
	// Enable/Disable the controls for Get/Set the USB device Number.
	//
	btnGetDeviceNumber->Enabled = (current == USB_1CH) || (current == USB_2CH);
	btnSetDeviceNumber->Enabled = (btnGetDeviceNumber->Enabled);
	nudDeviceNumber->Enabled = (btnGetDeviceNumber->Enabled);

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

void __fastcall TForm1::cbbBaudratesChange(TObject *Sender)
{
    // We save the corresponding Baudrate enumeration
    // type value for every selected Baudrate from the
    // list.
    //
    switch(cbbBaudrates->ItemIndex)
    {
        case 0:
            cbbBaudrates->Tag = BAUD_1M;
            break;
        case 1:
            cbbBaudrates->Tag = BAUD_500K;
            break;
        case 2:
            cbbBaudrates->Tag = BAUD_250K;
            break;
        case 3:
            cbbBaudrates->Tag = BAUD_125K;
            break;
        case 4:
            cbbBaudrates->Tag = BAUD_100K;
            break;
        case 5:
            cbbBaudrates->Tag = BAUD_50K;
            break;
        case 6:
            cbbBaudrates->Tag = BAUD_20K;
            break;
        case 7:
            cbbBaudrates->Tag = BAUD_10K;
            break;
        case 8:
            cbbBaudrates->Tag = BAUD_5K;
            break;
        default:
            cbbBaudrates->Tag = 0;
            break;
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtIDKeyPress(TObject *Sender, char &Key)
{
    // We convert the Character to its Upper case equivalent
    //
    Key = *(UpperCase((AnsiString)Key)).c_str();

    // The Key is the Delete (Backspace) Key
    //
    if(Key == 8)
        return;
    // The Key is a number between 0-9
    //
    if((Key > 47)&&(Key < 58))
        return;
    // The Key is a character between A-F
    //
    if((Key > 64)&&(Key < 71))
        return;

    // Is neither a number nor a character between A(a) and F(f)
    //
    Key = NULL;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtIDExit(TObject *Sender)
{
    int TextLength, MaxValue;

    // calculate the text length and Maximum ID value according
    // with the Message Type
    //
    TextLength = (chbExtended->Checked) ? 8 : 3;
    MaxValue = (chbExtended->Checked) ? 0x1FFFFFFF : 0x7FF;

    // The Textbox for the ID is represented with 3 characters for
    // Standard and 8 characters for extended messages.
    // Therefore if the Length of the text is smaller than TextLength,
    // we add "0"
    //
    while(txtID->Text.Length() != TextLength)
        txtID->Text = ("0" + txtID->Text);

    // Because in this example will be sent only Standard messages
    // we check that the ID is not bigger than 0x7FF
    //
    if(StrToInt("0x" + txtID->Text) > MaxValue)
        txtID->Text = IntToHex(MaxValue,TextLength);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::nudLengthClick(TObject *Sender, TUDBtnType Button)
{
    TEdit *CurrentTextBox;

    // We enable so much TextBox Data fields as the length of the
    // message will be, that is the value of the UpDown control
    //
    for(int i=0; i< 8; i++)
    {
        CurrentTextBox = (TEdit*)FindComponent("txtData"+IntToStr(i));
        CurrentTextBox->Enabled = (i < nudLength->Position)? true : false;
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtData0Exit(TObject *Sender)
{
    TEdit *CurrentEdit;

    if(String(Sender->ClassName()) == "TEdit"){
        CurrentEdit = (TEdit*)Sender;
        while(CurrentEdit->Text.Length() != 2)
            CurrentEdit->Text = ("0" + CurrentEdit->Text);
    }    
}
//---------------------------------------------------------------------------

void __fastcall TForm1::chbExtendedClick(TObject *Sender)
{
    int iTemp;

    txtID->MaxLength = (chbExtended->Checked)? 8: 3;

    // the only way that the text length can be bigger als MaxLength
    // is when the change is from Extended to Standard message Type.
    // We have to handle this and set an ID not bigger than the Maximum
    // ID value for a Standard Message (0x7FF)
    //
    if(txtID->Text.Length() > txtID->MaxLength)
    {
        iTemp = StrToInt("0x" + txtID->Text);
        txtID->Text = (iTemp < 0x7FF) ?  IntToHex(iTemp,3) : AnsiString("7FF");
    }

    txtIDExit(txtID);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::chbRemoteClick(TObject *Sender)
{
    TEdit *CurrentTextBox;

    // We enable so much TextBox Data fields as the length of the
    // message will be, that is the value of the UpDown control.
    //
    for(int i=0; i< 8; i++)
    {
        CurrentTextBox = (TEdit*)FindComponent("txtData" + IntToStr(i));
        CurrentTextBox->Visible = !chbRemote->Checked;
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtInfoDblClick(TObject *Sender)
{
    // We clear the Information edit box
    //
    txtInfo->Text = "";
}
//---------------------------------------------------------------------------

void __fastcall TForm1::lstMessagesDblClick(TObject *Sender)
{
    lstMessages->Items->Clear();
    while(LastMsgsList->Count)
    {
        delete LastMsgsList->First();
        LastMsgsList->Delete(0);
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::tmrReadTimer(TObject *Sender)
{
    TPCANMsg MyMsg;
    CANResult Res;

    // We read at least one time the queue looking for messages.
    // If a message is found, we look again trying to find more.
    // If the queue is empty or an error occurr, we get out from
    // the dowhile statement.
    //
    do
    {
        // We read the queue looking for messages.
        //
        Res = CANLight::Read(ActiveHardware,&MyMsg);

        // A message was received
        // We process the message(s)
        //
        if(Res == ERR_OK)
            ProcessMessage(MyMsg);
    }while(!(Res & ERR_QRCVEMPTY));
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnInfoClick(TObject *Sender)
{
    AnsiString strInfo;
    CANResult Res;
    int i=1;

    // We execute the "VersionInfo" function of the PCANLight
    // using as parameter the Hardware type and a string
    // variable to get the info.
    //
    Res = CANLight::VersionInfo((HardwareType)cbbHws->ItemIndex,strInfo);
    while(i < strInfo.Length())
    {
        if(IsDelimiter('\n',strInfo,i)){
            strInfo.Delete(i,1);
            strInfo.Insert("\r\n",i);
            i+=2;
        }
        else
            i+=1;
    }

    // The function was successfully executed
    //
    if(Res == ERR_OK)
        // We show the Version Information
        //
        txtInfo->Text = strInfo;
        // An error occurred.  We show the error.
        //
    else
        txtInfo->Text  = "Error Number: 0x" + IntToHex(int(Res),4);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnInitClick(TObject *Sender)
{
    CANResult Res;

    // According with the active parameters/hardware, we
    // use one of the two possible "Init" PCANLight functions.
    // One is for Plug And Play hardware, and the other for
    // Not P&P.
    //
    if(cbbIO->Enabled)
        // Not P&P Hardware
        //
        Res = CANLight::Init((HardwareType)cbbHws->ItemIndex,
            (Baudrates)cbbBaudrates->Tag,
            (FramesType)cbbMsgType->ItemIndex,
            StrToInt("0x" + cbbIO->Text),
            StrToInt(cbbInterrupt->Text));
    else
        // P&P Hardware
        //
        Res = CANLight::Init((HardwareType)cbbHws->ItemIndex,
            (Baudrates)cbbBaudrates->Tag,
            (FramesType)cbbMsgType->ItemIndex);

    // The Hardware was successfully initiated
    //
    if(Res == ERR_OK)
    {
        // We save the hardware type which is currently
        // initiated
        //
        ActiveHardware = (HardwareType)cbbHws->ItemIndex;

        // We start to read from the CAN Queue
        //
        tmrRead->Enabled = true;

        // We show the information of the configured
        // and initiated hardware
        //
        btnWrite->Enabled = true;
        btnRelease->Enabled = true;
        btnSetFilter->Enabled = true;
        btnResetFilter->Enabled = true;
        btnInit->Enabled = false;
        txtInfo->Text = "Active Hardware: " + cbbHws->Text;
        txtInfo->Text = txtInfo->Text + "\r\nBaud Rate: " + cbbBaudrates->Text;
        txtInfo->Text = txtInfo->Text + "\r\nFrame Type: " + cbbMsgType->Text;
        // If was a no P&P Hardware, we show additional information
        //
        if(cbbIO->Enabled)
        {
            txtInfo->Text = txtInfo->Text + "\r\nI/O Addr.: " + cbbIO->Text + "h";
            txtInfo->Text = txtInfo->Text + "\r\nInterrupt: " + cbbInterrupt->Text;
        }
    }
        // An error occurred.  We show the error.
        //
    else
        txtInfo->Text  = "Error Number: 0x" + IntToHex(int(Res),4);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnReleaseClick(TObject *Sender)
{
    CANResult Res;

    // We stopt to read from tehe CAN Queue
    //
    tmrRead->Enabled = false;

    // We close the active hardware using the
    // "Close" function of the PCANLight using
    // as parameter the Hardware type.
    //
    Res = CANLight::Close(ActiveHardware);

    // The Hardware was successfully closed
    //
    if(Res == ERR_OK)
        txtInfo->Text = "Hardware was successfully Released.\r\n";
        // An error occurred.  We show the error.
        //
    else
        txtInfo->Text = "Error Number: 0x" + IntToHex(int(Res),4);

    // We set the varibale of active hardware to None
    // and activate/deactivate the corresponding buttons
    //
    ActiveHardware = (HardwareType)-1;
    btnInit->Enabled = true;
    btnWrite->Enabled = false;
    btnRelease->Enabled = false;
    btnSetFilter->Enabled = false;
    btnResetFilter->Enabled = false;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnWriteClick(TObject *Sender)
{
    TPCANMsg MsgToSend;
    TEdit *CurrentTextBox;
    CANResult Res;

    // We configurate the Message.  The ID (max 0x1FF),
    // Length of the Data, Message Type (Standard in
    // this example) and die data
    //
    MsgToSend.ID = StrToInt("0x" + txtID->Text);
    MsgToSend.LEN = nudLength->Position;
    MsgToSend.MSGTYPE = (chbExtended->Checked) ? MSGTYPE_EXTENDED : MSGTYPE_STANDARD;

    // If a remote frame will be sent, the data bytes are not important.
    //
    if(chbRemote->Checked)
        MsgToSend.MSGTYPE |= MSGTYPE_RTR;
    else
        // We get so much data as the Len of the message
        //
        for(int i=0; i < MsgToSend.LEN; i++)
        {
            CurrentTextBox = (TEdit*)FindComponent("txtData"+IntToStr(i));
            MsgToSend.DATA[i] = StrToInt("0x" + CurrentTextBox->Text);
        }

    // The message is sent to the configured hardware
    //
    Res = CANLight::Write(ActiveHardware,&MsgToSend);

    // The Hardware was successfully sent
    //
    if(Res == ERR_OK)
        txtInfo->Text = "Message was successfully SENT.\r\n";
        // An error occurred.  We show the error.
        //
    else
        txtInfo->Text = "Error Number: 0x" + IntToHex(int(Res),4);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnCloseClick(TObject *Sender)
{
    // We terminate the application
    //
    Close();
}
//---------------------------------------------------------------------------


void __fastcall TForm1::rdbStandardClick(TObject *Sender)
{
    int uiTemp;

    txtIdFrom->MaxLength = (rdbExtended->Checked) ? 8 : 3;
    txtIdTo->MaxLength = txtIdFrom->MaxLength;

    // the only way that the text length can be bigger als MaxLength
    // is when the change is from Extended to Standard message Type.
    // We have to handle this and set an ID not bigger than the Maximum
    // ID value for a Standard Message (0x7FF)
    //
    if (txtIdFrom->Text.Length() > txtIdFrom->MaxLength)
    {
        uiTemp =  StrToInt("0x" + txtIdFrom->Text);
        txtIdFrom->Text = (uiTemp < 0x7FF) ? IntToHex(uiTemp,3) : (AnsiString)"7FF";
    }
    if (txtIdTo->Text.Length() > txtIdTo->MaxLength)
    {
        uiTemp = StrToInt("0x" + txtIdTo->Text);
        txtIdTo->Text = (uiTemp < 0x7FF) ? IntToHex(uiTemp,3) : (AnsiString)"7FF";
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::txtIdFromExit(TObject *Sender)
{
    int TextLength, MaxValue;
    TEdit *IdBox;

    IdBox = (TEdit*)Sender;

    // calculate the text length and Maximum ID value according
    // with the Message Type
    //
    TextLength = (rdbExtended->Checked) ? 8 : 3;
    MaxValue = (rdbExtended->Checked) ? 0x1FFFFFFF : 0x7FF;

    // The Textbox for the ID is represented with 3 characters for
    // Standard and 8 characters for extended messages.
    // Therefore if the Length of the text is smaller than TextLength,
    // we add "0"
    //
    while(IdBox->Text.Length() != TextLength)
        IdBox->Text = ("0" + IdBox->Text);

    // Because in this example will be sent only Standard messages
    // we check that the ID is not bigger than 0x7FF
    //
    if(StrToInt("0x" + IdBox->Text) > MaxValue)
        IdBox->Text = IntToHex(MaxValue,TextLength);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnSetFilterClick(TObject *Sender)
{
    unsigned long FromID, ToID;
    CANResult Res;

    // The range IDs is read
    //
    FromID = StrToInt("0x" + txtIdFrom->Text);
    ToID = StrToInt("0x" + txtIdTo->Text);

    // The desired Filter is set on the configured Hardware
    //
    Res = CANLight::MsgFilter(ActiveHardware,FromID,ToID,(rdbStandard->Checked) ? MSGTYPE_STANDARD : MSGTYPE_EXTENDED);

    // The Filter was successfully set
    //
    if(Res == ERR_OK)
        txtInfo->Text = "Filter was successfully SET.\r\n";
        // An error occurred.  We show the error.
        //
    else
        txtInfo->Text = "Error Number: 0x" + IntToHex(int(Res),4);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnResetFilterClick(TObject *Sender)
{
    CANResult Res;

    // The current Filter on the configured Hardware is reset
    //
    Res = CANLight::ResetFilter(ActiveHardware);

    // The Filter was successfully reset
    //
    if (Res == ERR_OK)
        txtInfo->Text = "Filter was successfully RESET.\r\n";
    // An error occurred.  We show the error.
    //
    else
        txtInfo->Text = "Error Number: 0x" + IntToHex(int(Res),4);
}
//---------------------------------------------------------------------------


void __fastcall TForm1::btnGetDeviceNumberClick(TObject *Sender)
{
	long i;
	CANResult Res;

	// The USB Device Number will asked 
	//
	Res = CANLight::GetUSBDeviceNr((HardwareType)cbbHws->ItemIndex, &i);

	// The Device number was got successfully
	//
	if(Res == ERR_OK)
		txtInfo->Text = "USB Device Number is: " +  IntToStr(i) + ".\r\n";
		// An error occurred.  We show the error.
		//
	else
		txtInfo->Text = "Error Number: 0x" + IntToHex((int)Res,4);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnSetDeviceNumberClick(TObject *Sender)
{
	CANResult Res;

	// The USB Device Number will set
	//
	Res = CANLight::SetUSBDeviceNr((HardwareType)cbbHws->ItemIndex, nudDeviceNumber->Position);

	// The Device number was set successfully
	//
	if(Res == ERR_OK)
		txtInfo->Text  = "USB Device Number was set";
		// An error occurred.  We show the error.
		//
	else
		txtInfo->Text  = "Set USB Device Number failed: 0x" + IntToHex((int)Res,4);
}
//---------------------------------------------------------------------------

