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

#include <vcl.h>
#pragma hdrstop

#include "MainUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
    : TForm(Owner) {
}
//---------------------------------------------------------------------------

// назначение элементов ComboBox'у с сохранением выбранного индекса
void TMainForm::assignComboList(TComboBox *comboBox, TStringList *list) {
    int index;
    index = comboBox->ItemIndex;
    comboBox->Items->Assign(list);
    comboBox->ItemIndex = index;
}

// установка нужного текста в поле выбора диапазона в зависимости от того, включен ли ICP-режим
void TMainForm::setRangeComboList(TComboBox *rangeBox, TComboBox *icpBox) {
    TStringList *rangeStrings = new TStringList();
    if (icpBox->ItemIndex==0) {
        rangeStrings->Add(String("+/- 2В"));
        rangeStrings->Add(String("+/- 10В"));
    } else {
        rangeStrings->Add(String("~ 1В"));
        rangeStrings->Add(String("~ 5В"));
    }
    assignComboList(rangeBox, rangeStrings);

    delete rangeStrings;
}

void TMainForm::updateControls() {
    bool module_opened = (LTR24_IsOpened(&hltr24)==LTR_OK);
    bool devsel = (ltr24_list.Length > 0) && (cbbModulesList->ItemIndex >= 0);
    bool icp_support = hltr24.ModuleInfo.SupportICP;

    //обновление списка устройств и выбор можно делать только пока не открыто конкретное устройство
    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;

    cbbAdcFreq->Enabled = cfg_en;
    cbbDataFmt->Enabled = cfg_en;
    cbbISrcValue->Enabled = cfg_en && icp_support;
    chkTestModes->Enabled = cfg_en;

    //если нет поддержки ICP, то если был выставлен ICP-режим, сбрасываем на диф. вход
    if (cfg_en && !icp_support) {
        cbbICPMode1->ItemIndex = 0;
        cbbICPMode2->ItemIndex = 0;
        cbbICPMode3->ItemIndex = 0;
        cbbICPMode4->ItemIndex = 0;
    }

    chkChEn1->Enabled = cfg_en;
    chkChEn2->Enabled = cfg_en;
    chkChEn3->Enabled = cfg_en;
    chkChEn4->Enabled = cfg_en;

    cbbRange1->Enabled = cfg_en;
    cbbRange2->Enabled = cfg_en;
    cbbRange3->Enabled = cfg_en;
    cbbRange4->Enabled = cfg_en;

    // AC не имеет значения при ICP-режиме => запрет выбора
    cbbAC1->Enabled = cfg_en && (cbbICPMode1->ItemIndex==0);
    cbbAC2->Enabled = cfg_en && (cbbICPMode2->ItemIndex==0);
    cbbAC3->Enabled = cfg_en && (cbbICPMode3->ItemIndex==0);
    cbbAC4->Enabled = cfg_en && (cbbICPMode4->ItemIndex==0);

    cbbICPMode1->Enabled = cfg_en && icp_support;
    cbbICPMode2->Enabled = cfg_en && icp_support;
    cbbICPMode3->Enabled = cfg_en && icp_support;
    cbbICPMode4->Enabled = cfg_en && icp_support;

    /* В зависимости от того, выбран ли тестовый режим или
        обычный, устанавливаем соответствующие названия элементов ComboBox'а.
        При этом сохраняем индекс выбранного элемента неизменным */
    TStringList *modeStrings = new TStringList();
    if (chkTestModes->Checked) {
        modeStrings->Add(String("Собств. ноль"));
        modeStrings->Add(String("ICP тест"));
    } else {
        modeStrings->Add(String("Диф. вход"));
        modeStrings->Add(String("ICP вход"));
    }

    assignComboList(cbbICPMode1, modeStrings);
    assignComboList(cbbICPMode2, modeStrings);
    assignComboList(cbbICPMode3, modeStrings);
    assignComboList(cbbICPMode4, modeStrings);
    
    delete modeStrings;


    setRangeComboList(cbbRange1, cbbICPMode1);
    setRangeComboList(cbbRange2, cbbICPMode2);
    setRangeComboList(cbbRange3, cbbICPMode3);
    setRangeComboList(cbbRange4, cbbICPMode4);
}

void TMainForm::refreshDeviceList() {
    INT err = LTR_OK;
    int modules_cnt = 0;
    ltr24_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, &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++) {
                                //ищем модули LTR24
                                if (mids[module_ind]==LTR_MID_LTR24) {
                                    // сохраняем информацию о найденном модуле, необходимую для
                                    // последующего установления соединения с ним, в список
                                    modules_cnt++;
                                    ltr24_list.Length=modules_cnt;
                                    ltr24_list[modules_cnt-1].csn = String(serial_list[crate_ind]);
                                    ltr24_list[modules_cnt-1].slot = module_ind+LTR_CC_CHNUM_MODULE1;
                                    // и добавляем в ComboBox для возможности выбора нужного
                                    cbbModulesList->AddItem("Крейт " + ltr24_list[modules_cnt-1].csn +
                                            ", Слот " + IntToStr(ltr24_list[modules_cnt-1].slot), NULL);
                                }
                            }
                        }        
                        //закрываем соединение с крейтом
                        LTR_Close(&crate);
                    }
                }
            }
        }
    }

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

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

    LTR24_Close(&hltr24);
}

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

    threadRunning = false;
    updateControls();
}

void __fastcall TMainForm::cfgChanged(TObject *Sender) {
    updateControls();
}
//---------------------------------------------------------------------------


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

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

    refreshDeviceList();
}
//---------------------------------------------------------------------------


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

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

        if (res==LTR_OK) {
            edtDevSerial->Text = String(hltr24.ModuleInfo.Serial);
            edtVerPld->Text = IntToStr(hltr24.ModuleInfo.VerPLD);
            edtICPSupport->Text = hltr24.ModuleInfo.SupportICP ? "Есть" : "Нет";
        } else {
            LTR24_Close(&hltr24);
        }
    } else {
        closeDevice();
    }
    updateControls();     
}
//---------------------------------------------------------------------------

void __fastcall TMainForm::btnStartClick(TObject *Sender) {
    /* Сохраняем значения из элементов управления в соответствующие
       поля описателя модуля. Для простоты здесь не делается доп. проверок, что
        введены верные значения... */
    hltr24.ADCFreqCode = cbbAdcFreq->ItemIndex;
    hltr24.DataFmt     = cbbDataFmt->ItemIndex;
    hltr24.ISrcValue   = cbbISrcValue->ItemIndex;
    hltr24.TestMode    = chkTestModes->Checked;

    hltr24.ChannelMode[0].Enable   = chkChEn1->Checked;
    hltr24.ChannelMode[0].AC       = cbbAC1->ItemIndex != 0;
    hltr24.ChannelMode[0].Range    = cbbRange1->ItemIndex;
    hltr24.ChannelMode[0].ICPMode  = cbbICPMode1->ItemIndex != 0;

    hltr24.ChannelMode[1].Enable   = chkChEn2->Checked;
    hltr24.ChannelMode[1].AC       = cbbAC2->ItemIndex != 0;
    hltr24.ChannelMode[1].Range    = cbbRange2->ItemIndex;
    hltr24.ChannelMode[1].ICPMode  = cbbICPMode2->ItemIndex != 0;

    hltr24.ChannelMode[2].Enable   = chkChEn3->Checked;
    hltr24.ChannelMode[2].AC       = cbbAC3->ItemIndex != 0;
    hltr24.ChannelMode[2].Range    = cbbRange3->ItemIndex;
    hltr24.ChannelMode[2].ICPMode  = cbbICPMode3->ItemIndex != 0;

    hltr24.ChannelMode[3].Enable   = chkChEn4->Checked;
    hltr24.ChannelMode[3].AC       = cbbAC4->ItemIndex != 0;
    hltr24.ChannelMode[3].Range    = cbbRange4->ItemIndex;
    hltr24.ChannelMode[3].ICPMode  = cbbICPMode4->ItemIndex != 0;

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

    if (res == LTR_OK) {
        if (thread) {
            delete thread;
            thread = 0;
        }
        thread = new TLTR24_ProcessThread(true);
        /* Так как структура должна быть одна и та же, что используемая потоком,
            что в классе основного окна, то вынуждены передать ее как pointer */
        thread->phltr24 = &hltr24;
        /* Сохраняем элементы интерфейса, которые должны изменяться обрабатывающим
           потоком в класс потока */
        thread->edtChAvg[0]=edtCh1Avg;
        thread->edtChAvg[1]=edtCh2Avg;
        thread->edtChAvg[2]=edtCh3Avg;
        thread->edtChAvg[3]=edtCh4Avg;

        /*  устанавливаем функцию на событие завершения потока (в частности,
            чтобы отследить, если поток завершился сам из-за ошибки при сборе
            данных) */
        thread->OnTerminate = OnThreadTerminate;
        thread->Resume();

        threadRunning = true;
    }

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

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

void __fastcall TMainForm::FormClose(TObject *Sender, TCloseAction &Action) {
    closeDevice();
    delete thread;
}
//---------------------------------------------------------------------------

