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

#include <vcl.h>
#pragma hdrstop

#include "MainUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "CSPIN"
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
    : TForm(Owner) {
    ccbChannels[0]  = cbbLCh1_Channel;
    ccbRanges[0]    = cbbLCh1_Range;
    ccbModes[0]     = cbbLCh1_Mode;
    edtChResults[0] = edtLCh1_Result;

    ccbChannels[1]  = cbbLCh2_Channel;
    ccbRanges[1]    = cbbLCh2_Range;
    ccbModes[1]     = cbbLCh2_Mode;
    edtChResults[1] = edtLCh2_Result;

    ccbChannels[2]  = cbbLCh3_Channel;
    ccbRanges[2]    = cbbLCh3_Range;
    ccbModes[2]     = cbbLCh3_Mode;
    edtChResults[2] = edtLCh3_Result;
}

void TMainForm::updateControls() {
    bool module_opened = (LTR11_IsOpened(&hltr11)==LTR_OK);
    bool devsel = (ltr11_list.Length > 0) && (cbbModulesList->ItemIndex >= 0);

    //обновление списка устройств и выбор можно делать только пока не открыто конкретное устройство
    btnRefreshDevList->Enabled = !module_opened;
    cbbModulesList->Enabled = !module_opened;

    //установить связь можно только если выбрано устройство
    btnOpen->Enabled = devsel;
    btnOpen->Caption =  module_opened ? "Закрыть соединение" : "Установить соединение";

    btnStart->Enabled = module_opened && !threadRunning;
    btnStop->Enabled = module_opened && threadRunning;

    //изменение настроек возможно только при открытом устройстве и не запущенном сборе
    bool cfg_en = module_opened && !threadRunning;

    cbbAdcPrescaler->Enabled = cfg_en;
    seAdcDivider->Enabled = cfg_en;
    btnFindAdcFreq->Enabled = cfg_en;
    edtAdcFreq->Enabled = cfg_en;

    seLChCnt->Enabled = cfg_en;
    for (unsigned ch = 0; ch < EXAMPLE_MAX_LCH_CNT; ch++) {
        ccbChannels[ch]->Enabled = cfg_en;
        ccbRanges[ch]->Enabled = cfg_en;
        ccbModes[ch]->Enabled = cfg_en;
    }
}



void TMainForm::refreshDeviceList() {
    INT err = LTR_OK;
    int modules_cnt = 0;
    ltr11_list.Length = 0;
    cbbModulesList->Items->Clear();


    // устанавливаем связь с управляющим каналом сервера, чтобы получить список крейтов
    TLTR srv;
    LTR_Init(&srv);
    err=LTR_OpenSvcControl(&srv, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT);
    if (err!=LTR_OK) {
        MessageDlg("Не удалось установить связь с сервером: " + String(LTR_GetErrorString(err)),
                    mtError, TMsgDlgButtons() << mbOK,NULL);
    } else {
        char serial_list[LTR_CRATES_MAX][LTR_CRATE_SERIAL_SIZE];
        //получаем список серийных номеров всех подключенных крейтов
        err=LTR_GetCrates(&srv, (BYTE*)&serial_list[0][0]);
        //серверное соединение больше не нужно - можно закрыть
        LTR_Close(&srv);
        if (err!=LTR_OK) {
             MessageDlg("Не удалось получить список крейтов: " + String(LTR_GetErrorString(err)),
                    mtError, TMsgDlgButtons() << mbOK,NULL);
        } else {
            for (int crate_ind=0 ; crate_ind < LTR_CRATES_MAX; crate_ind++) {
                //каждому крейту соответствует непустой серийный номер
                if (serial_list[crate_ind][0]!='\0') {
                    // Устанавливаем связь с каждым крейтом, чтобы получить список модулей
                    TLTR crate;
                    LTR_Init(&crate);
                    err =  LTR_OpenCrate(&crate, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT,
                                         LTR_CRATE_IFACE_UNKNOWN, serial_list[crate_ind]);
                    if (err==LTR_OK) {
                        WORD mids[LTR_MODULES_PER_CRATE_MAX];
                        err=LTR_GetCrateModules(&crate, mids);
                        if (err==LTR_OK) {
                            for (int module_ind=0; module_ind < LTR_MODULES_PER_CRATE_MAX; module_ind++) {
                                //ищем модули LTR210
                                if (mids[module_ind]==LTR_MID_LTR11) {
                                    // сохраняем информацию о найденном модуле, необходимую для
                                    // последующего установления соединения с ним, в список
                                    modules_cnt++;
                                    ltr11_list.Length=modules_cnt;
                                    ltr11_list[modules_cnt-1].csn = String(serial_list[crate_ind]);
                                    ltr11_list[modules_cnt-1].slot = module_ind+LTR_CC_CHNUM_MODULE1;
                                    // и добавляем в ComboBox для возможности выбора нужного
                                    cbbModulesList->AddItem("Крейт " + ltr11_list[modules_cnt-1].csn +
                                            ", Слот " + IntToStr(ltr11_list[modules_cnt-1].slot), NULL);
                                }
                            }
                        }        
                        //закрываем соединение с крейтом
                        LTR_Close(&crate);
                    }
                }
            }
        }
    }

    cbbModulesList->ItemIndex = 0;
    updateControls();
}


void TMainForm::closeDevice() {
    // остановка сбора и ожидание завершения потока
    if (threadRunning) {
        thread->stop=true;
        thread->WaitFor();
    }

    LTR11_Close(&hltr11);
}


//функция, вызываемая по завершению потока сбора данных
//разрешает старт нового, устанавливает threadRunning
void __fastcall TMainForm::OnThreadTerminate(TObject *obj) {
    if (thread->err != LTR_OK) {
        MessageDlg("Сбор данных завершен с ошибкой: " + String(LTR11_GetErrorString(thread->err)),
                    mtError, TMsgDlgButtons() << mbOK,NULL);
    }

    threadRunning = false;
    updateControls();
}



//---------------------------------------------------------------------------
void __fastcall TMainForm::btnRefreshDevListClick(TObject *Sender) {
    refreshDeviceList();
}
//---------------------------------------------------------------------------

void __fastcall TMainForm::FormCreate(TObject *Sender) {
    thread = 0;
    LTR11_Init(&hltr11);

    refreshDeviceList();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::btnOpenClick(TObject *Sender) {
// если соединение с модулем закрыто - то открываем новое
    if (LTR11_IsOpened(&hltr11)!=LTR_OK) {
        INT res = LTR_OK;
        // информацию о крейте и слоте берем из сохраненного списка по индексу
        // текущей выбранной записи
        TLTR_MODULE_LOCATION location = ltr11_list[ cbbModulesList->ItemIndex ];
        LTR11_Init(&hltr11);
        res=LTR11_Open(&hltr11, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT, location.csn.c_str(), location.slot);
        if (res!=LTR_OK) {
            MessageDlg("Не удалось установить связь с модулем: " +
                        String(LTR11_GetErrorString(res)), mtError, TMsgDlgButtons() << mbOK, 0);
        }

        if (res==LTR_OK) {
            // чтение информации из Flash-памяти (включая калибровочные коэффициенты)
            res=LTR11_GetConfig(&hltr11);
            if (res != LTR_OK) {
                MessageDlg("Не удалось прочитать конфигурацию из модуля: "
                           + String(LTR11_GetErrorString(res)), mtError, TMsgDlgButtons() << mbOK, 0);
            }
        }

        if (res==LTR_OK) {
            edtDevSerial->Text = String(hltr11.ModuleInfo.Serial);
            edtVerFirm->Text = IntToStr((hltr11.ModuleInfo.Ver >> 8) & 0xFF) + "." +
                                IntToStr(hltr11.ModuleInfo.Ver & 0xFF);
        } else {
            LTR11_Close(&hltr11);
        }
    } else {
        closeDevice();
    }
    updateControls();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormClose(TObject *Sender, TCloseAction &Action) {
    closeDevice();
    delete thread;
}
//---------------------------------------------------------------------------

void __fastcall TMainForm::btnStartClick(TObject *Sender) {
    int res = LTR_OK;

    /* Сохраняем значения из элементов управления в соответствующие
       поля описателя модуля. Для простоты здесь не делается доп. проверок, что
        введены верные значения... */
    hltr11.ADCRate.prescaler = StrToInt(cbbAdcPrescaler->Text);
    hltr11.ADCRate.divider   = seAdcDivider->Value;
    hltr11.LChQnt = seLChCnt->Value;

    for (int ch=0; (ch < hltr11.LChQnt) && (res == LTR_OK); ch++) {
        int phy_ch = ccbChannels[ch]->ItemIndex;
        int range  = ccbRanges[ch]->ItemIndex;
        int mode   = ccbModes[ch]->ItemIndex;

        if ((phy_ch < 0) || (phy_ch >= 32) || (range < 0) || (mode < 0)) {
            res = LTR_ERROR_PARAMETERS;
        } else {
            hltr11.LChTbl[ch] = LTR11_CreateLChannel(phy_ch, mode, range);
        }
    }


    if (res == LTR_OK)
        res = LTR11_SetADC(&hltr11);

    if (res == LTR_OK) {
        if (thread) {
            delete thread;
            thread = 0;
        }

        edtChFreq->Text = FloatToStr(hltr11.ChRate*1000);
        edtAdcFreq->Text = FloatToStr(hltr11.ChRate*hltr11.LChQnt*1000);

        thread = new TLTR11_ProcessThread(true);
        /* Так как структура должна быть одна и та же, что используемая потоком,
            что в классе основного окна, то вынуждены передать ее как pointer */
        thread->phltr11 = &hltr11;
        /* Сохраняем элементы интерфейса, которые должны изменяться обрабатывающим
           потоком в класс потока */
        for (unsigned ch=0; ch < EXAMPLE_MAX_LCH_CNT; ch++) {
            thread->edtChAvg[ch]=edtChResults[ch];
            edtChResults[ch]->Text = "";
        }
        /*  устанавливаем функцию на событие завершения потока (в частности,
            чтобы отследить, если поток завершился сам из-за ошибки при сборе
            данных) */
        thread->OnTerminate = OnThreadTerminate;
        thread->Resume();

        threadRunning = true;
    }

    if (res != LTR_OK) {
        MessageDlg("Не удалось установить настройки: " +
                   String(LTR11_GetErrorString(res)), mtError, TMsgDlgButtons() << mbOK, 0);
    }

    updateControls();
}
//---------------------------------------------------------------------------

void __fastcall TMainForm::btnStopClick(TObject *Sender) {
    // устанавливаем запрос на завершение потока
    if (threadRunning) {
        thread->stop=true;
    }
    btnStop->Enabled = false;
}
//---------------------------------------------------------------------------


void __fastcall TMainForm::btnFindAdcFreqClick(TObject *Sender)  {
    INT prescaler, divider;
    double freq;
    /* Подбор по частоте АЦП значения делителей */

    freq = StrToFloat(edtAdcFreq->Text);
    LTR11_FindAdcFreqParams(freq, &hltr11.ADCRate.prescaler, &hltr11.ADCRate.divider, &freq);
    seAdcDivider->Value = hltr11.ADCRate.divider;
    cbbAdcPrescaler->Text = IntToStr(hltr11.ADCRate.prescaler);
    edtAdcFreq->Text = FloatToStr(freq);     
}
//---------------------------------------------------------------------------


