unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ltrapi, ltrapitypes, ltrapidefine, ltr25api, LTR25_ProcessThread;

{ Информация, необходимая для установления соединения с модулем }
type TLTR_MODULE_LOCATION = record
  csn : string; //серийный номер крейта
  slot : Word; //номер слота
end;

type
  TMainForm = class(TForm)
    cbbModulesList: TComboBox;
    btnRefreshDevList: TButton;
    btnOpen: TButton;
    btnStart: TButton;
    btnStop: TButton;
    grpDevInfo: TGroupBox;
    lblDevSerial: TLabel;
    lblVerPld: TLabel;
    lblVerFPGA: TLabel;
    edtDevSerial: TEdit;
    edtVerFPGA: TEdit;
    edtVerPld: TEdit;
    grpConfig: TGroupBox;
    lblAdcFreq: TLabel;
    lblDataFmt: TLabel;
    lblISrcVal: TLabel;
    cbbAdcFreq: TComboBox;
    cbbDataFmt: TComboBox;
    cbbISrcValue: TComboBox;
    chkCh1En: TCheckBox;
    edtCh1Val: TEdit;
    chkCh2En: TCheckBox;
    edtCh2Val: TEdit;
    chkCh3En: TCheckBox;
    edtCh3Val: TEdit;
    chkCh4En: TCheckBox;
    edtCh4Val: TEdit;
    chkCh5En: TCheckBox;
    edtCh5Val: TEdit;
    chkCh6En: TCheckBox;
    edtCh6Val: TEdit;
    chkCh7En: TCheckBox;
    edtCh7Val: TEdit;
    chkCh8En: TCheckBox;
    edtCh8Val: TEdit;
    lbl1: TLabel;
    lbl2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure btnRefreshDevListClick(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure btnStartClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    ltr25_list: array of TLTR_MODULE_LOCATION; //список найденных модулей
    hltr25 : TLTR25; // Описатель модуля, с которым идет работа
    threadRunning : Boolean; // Признак, запущен ли поток сбора данных
    thread : TLTR25_ProcessThread; //Объект потока для выполнения сбора данных

    procedure refreshDeviceList();
    procedure updateControls();
    procedure closeDevice();
    procedure OnThreadTerminate(par : TObject);
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.refreshDeviceList();
var
  srv : TLTR; //Описатель для управляющего соединения с LTR-сервером
  crate: TLTR; //Описатель для соединения с крейтом
  res, crates_cnt, crate_ind, module_ind, modules_cnt : integer;
  serial_list : array [0..LTR_CRATES_MAX-1] of string; //список номеров керйтов
  mids : array [0..LTR_MODULES_PER_CRATE_MAX-1] of Word; //список идентификаторов модулей для текущего крейта
begin
  //обнуляем список ранее найденных модулей
  modules_cnt:=0;
  cbbModulesList.Items.Clear;
  SetLength(ltr25_list, 0);

  // устанавливаем связь с управляющим каналом сервера, чтобы получить список крейтов
  LTR_Init(srv);
  res:=LTR_OpenSvcControl(srv, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT);
  if res <> LTR_OK then
    MessageDlg('Не удалось установить связь с сервером: ' + LTR_GetErrorString(res), mtError, [mbOK], 0)
  else
  begin
    //получаем список серийных номеров всех подключенных крейтов
    res:=LTR_GetCrates(srv, serial_list, crates_cnt);
    //серверное соединение больше не нужно - можно закрыть
    LTR_Close(srv);

    if (res <> LTR_OK) then
      MessageDlg('Не удалось получить список крейтов: ' + LTR_GetErrorString(res), mtError, [mbOK], 0)
    else
    begin
      for crate_ind:=0 to crates_cnt-1 do
      begin
        //устанавливаем связь с каждым крейтом, чтобы получить список модулей
        LTR_Init(crate);
        res:=LTR_OpenCrate(crate, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT,
                           LTR_CRATE_IFACE_UNKNOWN, serial_list[crate_ind]);
        if res=LTR_OK then
        begin
          //получаем список модулей
          res:=LTR_GetCrateModules(crate, mids);
          if res = LTR_OK then
          begin
              for module_ind:=0 to LTR_MODULES_PER_CRATE_MAX-1 do
              begin
                //ищем модули LTR25
                if mids[module_ind]=LTR_MID_LTR25 then
                begin
                    // сохраняем информацию о найденном модуле, необходимую для
                    // последующего установления соединения с ним, в список
                    modules_cnt:=modules_cnt+1;
                    SetLength(ltr25_list, modules_cnt);
                    ltr25_list[modules_cnt-1].csn := serial_list[crate_ind];
                    ltr25_list[modules_cnt-1].slot := module_ind+LTR_CC_CHNUM_MODULE1;
                    // и добавляем в ComboBox для возможности выбора нужного
                    cbbModulesList.Items.Add('Крейт ' + ltr25_list[modules_cnt-1].csn +
                                            ', Слот ' + IntToStr(ltr25_list[modules_cnt-1].slot));
                end;
              end;
          end;
          //закрываем соединение с крейтом
          LTR_Close(crate);
        end;
      end;
    end;

    cbbModulesList.ItemIndex := 0;
    updateControls;

  end;
end;

procedure TMainForm.updateControls();
var
  module_opened, devsel, cfg_en: Boolean;
begin
  module_opened:=LTR25_IsOpened(hltr25)=LTR_OK;
  devsel := (Length(ltr25_list) > 0) and (cbbModulesList.ItemIndex >= 0);

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

  //установить связь можно только если выбрано устройство
  btnOpen.Enabled := devsel;
  if module_opened then
    btnOpen.Caption := 'Закрыть соединение'
  else
    btnOpen.Caption := 'Установить соединение';


  btnStart.Enabled := module_opened and not threadRunning;
  btnStop.Enabled := module_opened and threadRunning;

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


  cbbAdcFreq.Enabled := cfg_en;
  cbbDataFmt.Enabled := cfg_en;
  cbbISrcValue.Enabled := cfg_en;


  chkCh1En.Enabled := cfg_en;
  chkCh2En.Enabled := cfg_en;
  chkCh3En.Enabled := cfg_en;
  chkCh4En.Enabled := cfg_en;
  chkCh5En.Enabled := cfg_en;
  chkCh6En.Enabled := cfg_en;
  chkCh7En.Enabled := cfg_en;
  chkCh8En.Enabled := cfg_en;
end;

procedure TMainForm.closeDevice();
begin
  // остановка сбора и ожидание завершения потока
  if threadRunning then
  begin
    thread.stop:=True;
    thread.WaitFor;
  end;

  LTR25_Close(hltr25);
end;

//функция, вызываемая по завершению потока сбора данных
//разрешает старт нового, устанавливает threadRunning
procedure TMainForm.OnThreadTerminate(par : TObject);
begin
    if thread.err <> LTR_OK then
    begin
        MessageDlg('Сбор данных завершен с ошибкой: ' + LTR25_GetErrorString(thread.err),
                  mtError, [mbOK], 0);
    end;

    threadRunning := false;
    updateControls;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  LTR25_Init(hltr25);
  refreshDeviceList
end;

procedure TMainForm.btnRefreshDevListClick(Sender: TObject);
begin
  refreshDeviceList
end;



procedure TMainForm.btnOpenClick(Sender: TObject);
var
  location :  TLTR_MODULE_LOCATION;
  res : Integer;
begin
  // если соединение с модулем закрыто - то открываем новое
  if LTR25_IsOpened(hltr25)<>LTR_OK then
  begin
    // информацию о крейте и слоте берем из сохраненного списка по индексу
    // текущей выбранной записи
    location := ltr25_list[ cbbModulesList.ItemIndex ];
    LTR25_Init(hltr25);
    res:=LTR25_Open(hltr25, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT, location.csn, location.slot);
    if res<>LTR_OK then
      MessageDlg('Не удалось установить связь с модулем: ' + LTR25_GetErrorString(res), mtError, [mbOK], 0);

    if res=LTR_OK then
    begin
      edtDevSerial.Text := String(hltr25.ModuleInfo.Serial);
      edtVerPld.Text := IntToStr(hltr25.ModuleInfo.VerPLD);
      edtVerFPGA.Text := IntToStr(hltr25.ModuleInfo.VerFPGA);
    end
    else
    begin
      LTR25_Close(hltr25);
    end;
  end
  else
  begin
    closeDevice;
  end;

  updateControls;
end;

procedure TMainForm.btnStartClick(Sender: TObject);
var
  res : Integer;
begin
   { Сохраняем значения из элементов управления в соответствующие
    поля описателя модуля. Для простоты здесь не делается доп. проверок, что
    введены верные значения... }
   hltr25.Cfg.FreqCode := cbbAdcFreq.ItemIndex;
   hltr25.Cfg.DataFmt  := cbbDataFmt.ItemIndex;
   hltr25.Cfg.ISrcValue:= cbbISrcValue.ItemIndex;

   hltr25.Cfg.Ch[0].Enabled   := chkCh1En.Checked;
   hltr25.Cfg.Ch[1].Enabled   := chkCh2En.Checked;
   hltr25.Cfg.Ch[2].Enabled   := chkCh3En.Checked;
   hltr25.Cfg.Ch[3].Enabled   := chkCh4En.Checked;
   hltr25.Cfg.Ch[4].Enabled   := chkCh5En.Checked;
   hltr25.Cfg.Ch[5].Enabled   := chkCh6En.Checked;
   hltr25.Cfg.Ch[6].Enabled   := chkCh7En.Checked;
   hltr25.Cfg.Ch[7].Enabled   := chkCh8En.Checked;

   res:= LTR25_SetADC(hltr25);
   if res <> LTR_OK then
      MessageDlg('Не удалось установить настройки: ' + LTR25_GetErrorString(res), mtError, [mbOK], 0);

  if res = LTR_OK then
  begin
    if thread <> nil then
    begin
      FreeAndNil(thread);
    end;

    thread := TLTR25_ProcessThread.Create(True);
    { Так как структура должна быть одна и та же, что используемая потоком,
     что в классе основного окна, то вынуждены передать ее как pointer }
    thread.phltr25 := @hltr25;
    { Сохраняем элементы интерфейса, которые должны изменяться обрабатывающим
      потоком в класс потока }
    thread.edtChVal[0]:=edtCh1Val;
    thread.edtChVal[1]:=edtCh2Val;
    thread.edtChVal[2]:=edtCh3Val;
    thread.edtChVal[3]:=edtCh4Val;
    thread.edtChVal[4]:=edtCh5Val;
    thread.edtChVal[5]:=edtCh6Val;
    thread.edtChVal[6]:=edtCh7Val;
    thread.edtChVal[7]:=edtCh8Val;


    { устанавливаем функцию на событие завершения потока (в частности,
    чтобы отследить, если поток завершился сам из-за ошибки при сборе
    данных) }
    thread.OnTerminate := OnThreadTerminate;
    thread.Resume; //для Delphi 2010 и выше рекомендуется использовать Start
    threadRunning := True;

    updateControls;
  end;

end;

procedure TMainForm.btnStopClick(Sender: TObject);
begin
   // устанавливаем запрос на завершение потока
    if threadRunning then
        thread.stop:=True;
    btnStop.Enabled:= False;
end;

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  closeDevice;
  if thread <> nil then
    FreeAndNil(thread);
end;

end.
