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

#include <vcl.h>
#pragma hdrstop

#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "wclBluetooth"
#pragma resource "*.dfm"

//---------------------------------------------------------------------------
const Byte SHORT_DATA_LEN = 5;
const Word LONG_DATA_LEN = 1024;

Byte LongData[LONG_DATA_LEN];
Byte ShortData[SHORT_DATA_LEN];
//---------------------------------------------------------------------------
TfmMain *fmMain;
//---------------------------------------------------------------------------
__fastcall TfmMain::TfmMain(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btClearClick(TObject *Sender)
{
        lbLog->Items->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::Trace(const String& Msg)
{
        lbLog->Items->Add(Msg);
        lbLog->TopIndex = lbLog->Items->Count - 1;
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::Trace(const Byte Spaces, const String& Msg)
{
        String Str = "";
        if (Spaces > 0)
                Str = String::StringOfChar(' ', Spaces);
        Str = Str + Msg;
        Trace(Str);
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::Trace(const String& Msg, const int Error)
{
        Trace(Msg + " failed: 0x" + IntToHex(Error, 8));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::Trace(const Byte Spaces, const String& Msg,
        const int Error)
{
        Trace(Spaces, Msg + " failed: 0x" + IntToHex(Error, 8));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::FormCreate(TObject *Sender)
{
        btStop->Enabled = false;
        btDisconnect->Enabled = false;
        btNotify->Enabled = false;
        btNotifyAll->Enabled = false;
        btSetParameters->Enabled = false;

        for (int i = 0; i < SHORT_DATA_LEN; i++)
                ShortData[i] = i + 1;
        for (int i = 0; i < LONG_DATA_LEN; i++)
                LongData[i] = LOBYTE(i);
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerAfterOpen(TObject *Sender)
{
        Trace("Bluetooth Manager opened");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerBeforeClose(TObject *Sender)
{
        Trace("Bluetooth Manager is closing");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclBluetoothManagerClosed(TObject *Sender)
{
        Trace("Bluetooth Manager closed");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerStarted(TObject *Sender)
{
        btStop->Enabled = true;
        btStart->Enabled = false;

        Trace("Server started");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerStopped(TObject *Sender)
{
        btStop->Enabled = false;
        btStart->Enabled = true;

        Trace("Server stopped");
}
//---------------------------------------------------------------------------
bool __fastcall TfmMain::InitializeBluetooth(TwclBluetoothRadio*& Radio)
{
        Radio = NULL;

        bool Result = false;

        Trace("Opening Bluetooth Manager");
        int Res = wclBluetoothManager->Open();
        if (Res != WCL_E_SUCCESS)
                Trace("Open Bluetooth Manager", Res);
        else
        {
                Trace("Get working LE Radio");
                Res = wclBluetoothManager->GetLeRadio(Radio);
                if (Res != WCL_E_SUCCESS)
                        Trace("Get working LE Radio", Res);
                else
                {
                        Trace("Working radio found. Use " + Radio->ApiName +
                                " radio");
                        Result = true;
                }

                if (!Result)
                {
                        Trace("Closing Bluetooth Manager");
                        wclBluetoothManager->Close();
                }
        }
        return Result;
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::UninitializeBluetooth()
{
        if (wclBluetoothManager->Active)
        {
                Trace("Closing Bluetooth Manager");
                wclBluetoothManager->Close();
        }
}
//---------------------------------------------------------------------------
bool __fastcall TfmMain::InitializeServer(TwclBluetoothRadio* const Radio)
{
        Trace("Initializing GATT server");
        int Res = wclGattServer->Initialize(Radio);
        if (Res != WCL_E_SUCCESS)
        {
                Trace("GATT server initialization", Res);
                return false;
        }

        Trace("GATT server initialized");
        return true;
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::UninitializeServer()
{
        if (wclGattServer->Initialized)
        {
                Trace("Uninitializing GATT server");
                int Res = wclGattServer->Uninitialize();
                if (Res != WCL_E_SUCCESS)
                        Trace("GATT server uninitialization", Res);
        }
}
//---------------------------------------------------------------------------
bool __fastcall TfmMain::AddCharacteristics(TwclGattLocalService* const Service)
{
        Trace(4, "Adding GATT characteristics");

        TwclGattLocalCharacteristic* Char;

        TwclGattUuid Uuid;
        Uuid.IsShortUuid = true;

        TwclGattLocalCharacteristicParameters Params;
        Params.Descriptors.Length = 0;
        Params.PresentationFormats.Length = 0;

        Trace(8, "Add empty characteristic");
        Uuid.ShortUuid = 0xFFF1;
        Params.Props.Clear();
        Params.UserDescription = "Empty characteristic";
        int Res = Service->AddCharacteristic(Uuid, Params, Char);
        if (Res != WCL_E_SUCCESS)
        {
                Trace(12, "Add empty characteristic", Res);
                return false;
        }
        Trace(12, "Empty characteristic added");

        Trace(8, "Add short readable characteristic");
        Uuid.ShortUuid = 0xFFF2;
        Params.Props << cpReadable;
        Params.UserDescription = "Short readable characteristic";
        Res = Service->AddCharacteristic(Uuid, Params, Char);
        if (Res != WCL_E_SUCCESS)
        {
                Trace(12, "Add short readable characteristic", Res);
                return false;
        }
        Trace(12, "Short readable characteristic added");

        Trace(8, "Add long readable characteristic");
        Uuid.ShortUuid = 0xFFF3;
        Params.Props.Clear();
        Params.Props << cpReadable;
        Params.UserDescription = "Long readable characteristic";
        Res = Service->AddCharacteristic(Uuid, Params, Char);
        if (Res != WCL_E_SUCCESS)
        {
                Trace(12, "Add long readable characteristic: 0x", Res);
                return false;
        }
        Trace(12, "Long readable characteristic added");

        Trace(8, "Add writable characteristic");
        Uuid.ShortUuid = 0xFFF4;
        Params.Props.Clear();
        Params.Props << cpWritable << cpWritableWithoutResponse;
        Params.UserDescription = "Writable characteristic";
        Res = Service->AddCharacteristic(Uuid, Params, Char);
        if (Res != WCL_E_SUCCESS)
        {
                Trace(12, "Add writable characteristic", Res);
                return false;
        }
        Trace(12, "Writable characteristic added");

        Trace(8, "Add notifiable characteristic");
        Uuid.ShortUuid = 0xFFF5;
        Params.Props.Clear();
        Params.Props << cpNotifiable;
        Params.UserDescription = "Notifiable characteristic";
        Res = Service->AddCharacteristic(Uuid, Params, Char);
        if (Res != WCL_E_SUCCESS)
        {
                Trace(12, "Add notifiable characteristic", Res);
                return false;
        }
        Trace(12, "Notifiable characteristic added");

        Trace(8, "Add indicatable characteristic");
        Uuid.ShortUuid = 0xFFF6;
        Params.Props.Clear();
        Params.Props << cpIndicatable;
        Params.UserDescription = "Indicatable characteristic";
        Res = Service->AddCharacteristic(Uuid, Params, Char);
        if (Res != WCL_E_SUCCESS)
        {
                Trace(12, "Add indicatable characteristic", Res);
                return false;
        }
        Trace(12, "Indicatable characteristic added");

        Trace(4, "All GATT charactertistics added");
        return true;
}
//---------------------------------------------------------------------------
TwclGattLocalService* __fastcall TfmMain::AddService()
{
        Trace("Adding GATT service");

        TwclGattUuid Uuid;
        Uuid.IsShortUuid = true;
        Uuid.ShortUuid = 0xFFF0;
        TwclGattLocalService* Result = NULL;
        int Res = wclGattServer->AddService(Uuid, Result);
        if (Res != WCL_E_SUCCESS)
                Trace(4, "Add GATT service", Res);
        else
                Trace("GATT service added");
        return Result;
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::Start()
{
        TwclBluetoothRadio* Radio = NULL;
        if (InitializeBluetooth(Radio))
        {
                bool BoolRes = InitializeServer(Radio);
                if (BoolRes)
                {
                        TwclGattLocalService* Service = AddService();
                        if (Service == NULL)
                                BoolRes = false;
                        else
                        {
                                BoolRes = AddCharacteristics(Service);
                                if (BoolRes)
                                {
                                        Trace("Starting GATT server");
                                        int Res = wclGattServer->Start();
                                        if (Res != WCL_E_SUCCESS)
                                        {
                                                Trace("GATT server start", Res);
                                                BoolRes = false;
                                        }
                                }
                        }
                        if (!BoolRes)
                                UninitializeServer();
                }
                if (!BoolRes)
                        UninitializeBluetooth();
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::Stop()
{
        Trace("Stopping GATT server");

        int Res = wclGattServer->Stop();
        if (Res != WCL_E_SUCCESS)
                Trace("Stop GATT server", Res);

        UninitializeServer();
        UninitializeBluetooth();
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::FormDestroy(TObject *Sender)
{
        Stop();
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btStartClick(TObject *Sender)
{
        Start();
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btStopClick(TObject *Sender)
{
        Stop();
}
//---------------------------------------------------------------------------
TListItem* __fastcall TfmMain::FindItem(
        TwclGattServerClient* const Client)
{
        if (Client != NULL)
        {
                if (lvDevices->Items->Count > 0)
                {
                        for (int i = 0; i < lvDevices->Items->Count; i++)
                        {
                                if (lvDevices->Items->Item[i]->Data == (void*)Client)
                                        return lvDevices->Items->Item[i];
                        }
                }
        }
        return NULL;
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::UpdateMaxPduSize(TwclGattServerClient* const Client)
{
        TListItem* Item = FindItem(Client);
        if (Item != NULL)
        {
                Word Size;
                if (Client->GetMaxPduSize(Size) == WCL_E_SUCCESS)
                        Item->SubItems->Strings[1] = IntToStr(Size);
                else
                        Item->SubItems->Strings[1] = "-----";
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::UpdateConnectionParams(
        TwclGattServerClient* const Client)
{
        TListItem* Item = FindItem(Client);
        if (Item != NULL)
        {
                TwclBluetoothLeConnectionParameters Params;
                if (Client->GetConnectionParams(Params) == WCL_E_SUCCESS)
                {
                        Item->SubItems->Strings[2] = IntToStr(Params.Interval);
                        Item->SubItems->Strings[3] = IntToStr(Params.Latency);
                        Item->SubItems->Strings[4] = IntToStr(Params.LinkTimeout);
                }
                else
                {
                        Item->SubItems->Strings[2] = "-----";
                        Item->SubItems->Strings[3] = "-----";
                        Item->SubItems->Strings[4] = "-----";
                }
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::UpdatePhy(TwclGattServerClient* const Client)
{
        TListItem* Item = FindItem(Client);
        if (Item != NULL)
        {
                TwclBluetoothLeConnectionPhy Phy;
                if (Client->GetConnectionPhyInfo(Phy) == WCL_E_SUCCESS)
                {
                        Item->SubItems->Strings[5] = BoolToStr(Phy.Receive.IsCoded);
                        Item->SubItems->Strings[6] = BoolToStr(Phy.Receive.IsUncoded1MPhy);
                        Item->SubItems->Strings[7] = BoolToStr(Phy.Receive.IsUncoded2MPhy);

                        Item->SubItems->Strings[8] = BoolToStr(Phy.Transmit.IsCoded);
                        Item->SubItems->Strings[9] = BoolToStr(Phy.Transmit.IsUncoded1MPhy);
                        Item->SubItems->Strings[10] = BoolToStr(Phy.Transmit.IsUncoded2MPhy);
                }
                else
                {
                        Item->SubItems->Strings[5] = "-----";
                        Item->SubItems->Strings[6] = "-----";
                        Item->SubItems->Strings[7] = "-----";
                        Item->SubItems->Strings[8] = "-----";
                        Item->SubItems->Strings[9] = "-----";
                        Item->SubItems->Strings[10] = "-----";
                }
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::UpdateNotificationSize(
        TwclGattServerClient* const Client)
{
        TListItem* Item = FindItem(Client);
        if (Item != NULL)
        {
                Word Size;
                if (Client->GetMaxNotificationSize(Size) == WCL_E_SUCCESS)
                        Item->SubItems->Strings[11] = IntToStr(Size);
                else
                        Item->SubItems->Strings[11] = "-----";
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerClientConnected(TObject *Sender,
      const TwclGattServerClient *Client)
{
        Trace("Client connected: " + IntToHex(Client->Address, 12));

        TListItem* Item = lvDevices->Items->Add();
        Item->Caption = IntToHex(Client->Address, 12);
        Item->Data = (void*)Client;
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");
        Item->SubItems->Add("");

        UpdateMaxPduSize((TwclGattServerClient*)Client);
        UpdateConnectionParams((TwclGattServerClient*)Client);
        UpdatePhy((TwclGattServerClient*)Client);
        UpdateNotificationSize((TwclGattServerClient*)Client);
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerMaxPduSizeChanged(TObject *Sender,
      const TwclGattServerClient *Client)
{
        UpdateMaxPduSize((TwclGattServerClient*)Client);

        Trace("Max PDU size changed: " + IntToHex(Client->Address, 12));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerConnectionParamsChanged(
      TObject *Sender, const TwclGattServerClient *Client)
{
        UpdateConnectionParams((TwclGattServerClient*)Client);

        Trace("Connection parameters changed: " + IntToHex(Client->Address, 12));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerConnectionPhyChanged(TObject *Sender,
      const TwclGattServerClient *Client)
{
        UpdatePhy((TwclGattServerClient*)Client);

        Trace("PHY changed: " + IntToHex(Client->Address, 12));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerNotificationSizeChanged(
      TObject *Sender, const TwclGattServerClient *Client)
{
        UpdateNotificationSize((TwclGattServerClient*)Client);

        Trace("Notification size changed: " + IntToHex(Client->Address, 12));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerClientDisconnected(TObject *Sender,
      const TwclGattServerClient *Client)
{
        Trace("Client disconnected: " + IntToHex(Client->Address, 12));

        TListItem* Item = FindItem((TwclGattServerClient*)Client);
        if (Item != NULL)
        {
                // Characteristics should already be removed because unsubscribe
                // always called before disconnect.
                lvDevices->Items->Delete(Item->Index);

                UpdateNotifyAllStatus();
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::lvDevicesSelectItem(TObject *Sender,
      TListItem *Item, bool Selected)
{
        if (lvDevices->Selected != NULL)
        {
                btDisconnect->Enabled = (lvDevices->Selected->Caption != "");
                btNotify->Enabled = (lvDevices->Selected->Caption == "");
                btSetParameters->Enabled = (lvDevices->Selected->Caption != "");
        }
        else
        {
                btDisconnect->Enabled = false;
                btNotify->Enabled = false;
                btSetParameters->Enabled = false;
        }

        UpdateNotifyAllStatus();
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::UpdateNotifyAllStatus()
{
        bool Found = false;

        if (lvDevices->Items->Count > 0)
        {
                for (int i = 0; i < lvDevices->Items->Count; i++)
                {
                        if (lvDevices->Items->Item[i]->Caption == "")
                        {
                                Found = true;
                                break;
                        }
                }
        }

        btNotifyAll->Enabled = (Found && lvDevices->Selected != NULL &&
                lvDevices->Selected->Caption == "");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btDisconnectClick(TObject *Sender)
{
        // Button is disabled if nothing or incorrect line selected.
        int Res = ((TwclGattServerClient*)lvDevices->Selected->Data)->Disconnect();
        if (Res != WCL_E_SUCCESS)
                ShowMessage("Disconnect error: 0x" + IntToHex(Res, 8));
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerWrite(TObject *Sender,
      const TwclGattServerClient *Client,
      const TwclGattLocalCharacteristic *Characteristic,
      const TwclGattLocalCharacteristicWriteRequest *Request)
{
        Trace("Write request received from: " + IntToHex(Client->Address, 12));
        Trace(4, "Characteristic: " + IntToHex(Characteristic->Uuid.ShortUuid, 4));
		Trace(4, "Size: " + IntToStr((int)Request->Size));
		Trace(4, "Offset: " + IntToStr((int)Request->Offset));

        String s = "";
        for (unsigned i = 0; i < Request->Size; i++)
                s = s + IntToHex(((Byte*)Request->Data)[i], 2);
        Trace(8, s);

        if (Request->WithResponse)
        {
                Trace(4, "Sending response");
                int Res = ((TwclGattLocalCharacteristicWriteRequest*)Request)->Respond();
                // If data is incorrect you can answer with error using:
                //         Res = Request->RespondWithError(Error);
                // For possible error code refer to RespondWithError() method
                // description.
                if (Res != WCL_E_SUCCESS)
                        Trace(8, "Send response", Res);
                else
                        Trace(8, "Response sent");
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::edDataKeyPress(TObject *Sender, char &Key)
{
        if ((Key >= '0' && Key <= '9') || Key == 8 ||
            (Key >= 'a' && Key <= 'f') || (Key >= 'A' && Key <= 'F'))
        {
                return;
        }
        Key = 0;
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerSubscribed(TObject *Sender,
      const TwclGattServerClient *Client,
      const TwclGattLocalCharacteristic *Characteristic)
{
        Trace("Client " + IntToHex(Client->Address, 12) + " subscribed to");
        Trace(4, "Characteristic: " + IntToHex(Characteristic->Uuid.ShortUuid, 4));

        // Client already connected on this stage.
        TListItem* Item = FindItem((TwclGattServerClient*)Client);
        if (Item != NULL)
        {
                Item = lvDevices->Items->Insert(Item->Index + 1);
                Item->Caption = "";
                Item->Data = (void*)Characteristic;
                Item->SubItems->Add(IntToHex(Characteristic->Uuid.ShortUuid, 4));
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");
                Item->SubItems->Add("");

                UpdateNotificationSize((TwclGattServerClient*)Client);
                UpdateNotifyAllStatus();
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerUnsubscribed(TObject *Sender,
      const TwclGattServerClient *Client,
      const TwclGattLocalCharacteristic *Characteristic)
{
        Trace("Client " + IntToHex(Client->Address, 12) + " unsubscribed from");
        Trace(4, "Characteristic: " + IntToHex(Characteristic->Uuid.ShortUuid, 4));

        TListItem* Item = FindItem((TwclGattServerClient*)Client);
        if (Item != NULL)
        {
                if (Item->Index < lvDevices->Items->Count - 1)
                {
                        for (int i = Item->Index + 1; i < lvDevices->Items->Count; i++)
                        {
                                Item = lvDevices->Items->Item[i];
                                if (Item->Caption != "")
                                        break;

                                if (Item->Data == (void*)Characteristic)
                                {
                                        lvDevices->Items->Delete(Item->Index);

                                        UpdateNotificationSize((TwclGattServerClient*)Client);
                                        UpdateNotifyAllStatus();

                                        break;
                                }
                        }
                }
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::wclGattServerRead(TObject *Sender,
      const TwclGattServerClient *Client,
      const TwclGattLocalCharacteristic *Characteristic,
      const TwclGattLocalCharacteristicReadRequest *Request)
{
        Trace("Read request received from: " + IntToHex(Client->Address, 12));
        Trace(4, "Characteristic: " + IntToHex(Characteristic->Uuid.ShortUuid, 4));
		Trace(4, "Offset: " + IntToStr((int)Request->Offset));
		Trace(4, "Buffer size: " + IntToStr((int)Request->Size));

        int Res;
        if (Characteristic->Uuid.ShortUuid == 0xFFF2)
                Res = ((TwclGattLocalCharacteristicReadRequest*)Request)->Respond(&ShortData, SHORT_DATA_LEN);
        else
        {
                Res = ((TwclGattLocalCharacteristicReadRequest*)Request)->Respond(&LongData[Request->Offset],
                        LONG_DATA_LEN - Request->Offset);
        }

        if (Res != WCL_E_SUCCESS)
        {
                Trace(8, "Set data", Res);
                Trace(8, "Respond with error");
                // For possible error code refer to RespondWithError() method description.
                Res = ((TwclGattLocalCharacteristicReadRequest*)Request)->RespondWithError(
                        WCL_E_BLUETOOTH_LE_UNLIKELY);
                if (Res != WCL_E_SUCCESS)
                        Trace(12, "Respond", Res);
        }
        else
                Trace(8, "Data sent");
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::Notify(const __int64 Address)
{
  //Characteristic: TwclGattLocalCharacteristic;
//  Res: Integer;
//  Len: Integer;
//  Str: string;
//  DataLen: Integer;
//  Data: array of Byte;
//  i: Integer;
//  Repeats: Integer;
//  Start: Cardinal;
//  _End: Cardinal;
        int Len = edData->Text.Length();
        if (Len == 0)
                ShowMessage("Data is empty");
        else
        {
                Byte Repeats;
                if (edRepeats->Text == "")
                        Repeats = 1;
                else
                        Repeats = StrToInt(edRepeats->Text);

                if (Repeats > 20)
                        ShowMessage("Too many notification repeats");
                else
                {
                        TwclGattLocalCharacteristic* Characteristic = (TwclGattLocalCharacteristic*)lvDevices->Selected->Data;

                        if (Address == 0)
                                Trace("Sending notification to all subscribed clients");
                        else
                                Trace("Sending notification to " + IntToHex(Address, 12));
                        Trace(4, "Characteristic: " + IntToHex(Characteristic->Uuid.ShortUuid, 4));

                        String Str = edData->Text;
                        if (Len % 2 != 0)
                        {
                                Str = "0" + Str;
                                Len++;
                        }

                        int DataLen = Len / 2;
                        TByteDynArray Data;
                        Data.Length = DataLen;
                        for (int i = 0; i < DataLen; i++)
                                Data[i] = StrToInt("$" + Str.SubString(i * 2, 2));

                        for (int i = 0; i < Repeats; i++)
                        {
                                DWORD Start;
                                DWORD End;
                                int Res;
                                if (Address == 0)
                                {
                                        Start = GetTickCount();
                                        Res = Characteristic->Notify(&Data[0], DataLen);
                                        End = GetTickCount();
                                }
                                else
                                {
                                        Start = GetTickCount();
                                        Res = Characteristic->Notify(Address, &Data[0], DataLen);
                                        End = GetTickCount();
                                }

                                if (Res != WCL_E_SUCCESS)
                                        Trace(8, "Attempt [" + IntToStr(i + 1) + "]", Res);
                                else
                                {
                                        Trace(8, "Attempt [" + IntToStr(i + 1) + "] took " +
                                                IntToStr((int)(End - Start)) + " ms");
                                }
                        }

                        Trace("Notification procedure completed");
                }
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btNotifyAllClick(TObject *Sender)
{
        Notify(0);        
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btNotifyClick(TObject *Sender)
{
        for (int i = lvDevices->Selected->Index; i >= 0; i--)
        {
                if (lvDevices->Items->Item[i]->Caption != "")
                {
                        Notify(((TwclGattServerClient*)lvDevices->Items->Item[i]->Data)->Address);
                        break;
                }
        }
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::edRepeatsKeyPress(TObject *Sender, char &Key)
{
        if ((Key >= '0' && Key <= '9') || Key == 8)
                return;
        Key = 0;
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::btSetParametersClick(TObject *Sender)
{
        TwclGattServerClient* Client = (TwclGattServerClient*)lvDevices->Selected->Data;

        Trace("Set connection parameters for " + IntToHex(Client->Address, 12));
        int Res;
        if (cbParams->ItemIndex == 3)
        {
                TwclBluetoothLeConnectionParametersValue ParamsValue;
                ParamsValue.MinInterval = 84;
                ParamsValue.MaxInterval = 84;
                ParamsValue.Latency = 0;
                ParamsValue.LinkTimeout = 800;
                Res = Client->SetConnectionParams(ParamsValue);
        }
        else
        {
                TwclBluetoothLeConnectionParametersType Params;
                switch (cbParams->ItemIndex)
                {
                        case 0:
                                Params = ppBalanced;
                                break;
                        case 1:
                                Params = ppPowerOptimized;
                                break;
                        case 2:
                                Params = ppThroughputOptimized;
                                break;
                        default:
                                throw new wclEInvalidArgument("Invalid connection parameters.");
                }
                Res = Client->SetConnectionParams(Params);
        }

        if (Res != WCL_E_SUCCESS)
                Trace(4, "Set connection parameters", Res);
        else
                Trace(4, "Connection parameters set");
}
//---------------------------------------------------------------------------

