unit LTR25_ProcessThread;



interface
uses Classes, Math, SyncObjs,StdCtrls,SysUtils, ltr25api, ltrapi;
// Время, за которое будет отображаться вычисленное значение (в мс)
const RECV_BLOCK_TIME          = 1000;
// Дополнительный  постоянный таймаут на прием данных (в мс)
const RECV_TOUT                = 4000;




type TLTR25_ProcessThread = class(TThread)
  public
    //элементы управления для отображения результатов обработки
    edtChVal : array [0..LTR25_CHANNEL_CNT-1] of TEdit;

    phltr25: pTLTR25; //указатель на описатель модуля

    err : Integer; //код ошибки при выполнении потока сбора
    stop : Boolean; //запрос на останов (устанавливается из основного потока)

    constructor Create(SuspendCreate : Boolean);
    destructor Free();

  private
    { Private declarations }
    // значения пик-пик по последнему принятому блоку по каждому каналу
    ChPP : array [0..LTR25_CHANNEL_CNT-1] of Double;
    // состояние каналов
    ChStatus : array [0..LTR25_CHANNEL_CNT-1] of LongWord;
    // признак, что есть вычесленные данные по каналам в ChPP и статус в ChStatus
    ChValidData : array [0..LTR25_CHANNEL_CNT-1] of Boolean;


    procedure updateData;
  protected
    procedure Execute; override;
  end;
implementation


  constructor TLTR25_ProcessThread.Create(SuspendCreate : Boolean);
  begin
     Inherited Create(SuspendCreate);
     stop:=False;
     err:=LTR_OK;
  end;

  destructor TLTR25_ProcessThread.Free();
  begin
      Inherited Free();
  end;

  { обновление индикаторов формы результатами последнего измерения.
   Метод должен выполняться только через Synchronize, который нужен
   для доступа к элементам VCL не из основного потока }
  procedure TLTR25_ProcessThread.updateData;
  var
    ch: Integer;
  begin
      for ch:=0 to LTR25_CHANNEL_CNT-1 do
      begin
        if ChValidData[ch] then
        begin
          if ChStatus[ch] = LTR25_CH_STATUS_OK then
            edtChVal[ch].Text := FloatToStrF(ChPP[ch], ffFixed, 8, 6);
          if ChStatus[ch] = LTR25_CH_STATUS_SHORT then
            edtChVal[ch].Text := 'КЗ';
          if ChStatus[ch] = LTR25_CH_STATUS_OPEN then
            edtChVal[ch].Text := 'Обрыв';
        end
        else
          edtChVal[ch].Text := '';
      end;
  end;


  procedure TLTR25_ProcessThread.Execute;
  var
    stoperr, recv_size : Integer;
    rcv_buf  : array of LongWord;  //сырые принятые слова от модуля
    data     : array of Double;    //обработанные данные
    i        : Integer;
    ch       : Integer;
    ch_cnt   : Integer;  //количество разрешенных каналов
    recv_wrd_cnt : Integer;  //количество принимаемых сырых слов за раз
    recv_data_cnt : Integer; //количество обработанных слов, которые должны принять за раз
    // номера разрешенных каналов
    ch_nums  : array [0..LTR25_CHANNEL_CNT-1] of Byte;
    // временные переменные для вычисления значений
    // результат же сохраняется в конце вычислений в поля класса
    ch_min      : array [0..LTR25_CHANNEL_CNT-1] of Double;
    ch_max      : array [0..LTR25_CHANNEL_CNT-1] of Double;
    ch_cur_status: array [0..LTR25_CHANNEL_CNT-1] of LongWord;
    ch_valid : array [0..LTR25_CHANNEL_CNT-1] of Boolean;
  begin
    //обнуляем переменные
    for ch:=0 to LTR25_CHANNEL_CNT-1 do
      ChValidData[ch]:=False;
    Synchronize(@updateData);

    //Проверяем, сколько и какие каналы разрешены
    ch_cnt := 0;
    for ch:=0 to LTR25_CHANNEL_CNT-1 do
    begin
      if phltr25^.Cfg.Ch[ch].Enabled then
      begin
        ch_nums[ch_cnt] := ch;
        ch_cnt := ch_cnt+1;
      end;
    end;

    { Определяем, сколко преобразований будет выполненно за заданное время
      => будем принимать данные блоками такого размера }
    recv_data_cnt:=  Round(phltr25^.State.AdcFreq*RECV_BLOCK_TIME/1000) * ch_cnt;
    { В 24-битном формате каждому отсчету соответствует два слова от модуля,
                   а в 20-битном - одно }
    if phltr25^.Cfg.DataFmt = LTR25_FORMAT_32 then
      recv_wrd_cnt :=  2*recv_data_cnt
    else
      recv_wrd_cnt :=  recv_data_cnt;




    { выделяем массивы для приема данных }
    SetLength(rcv_buf, recv_wrd_cnt);
    SetLength(data, recv_data_cnt);
    err:= LTR25_Start(phltr25^);
    if err = LTR_OK then
    begin
      while not stop and (err = LTR_OK) do
      begin
        { Принимаем данные (здесь используется вариант без синхрометок, но есть
          и перегруженная функция с ними) }
        recv_size := LTR25_Recv(phltr25^, rcv_buf, recv_wrd_cnt, RECV_TOUT + RECV_BLOCK_TIME);
        //Значение меньше нуля соответствуют коду ошибки
        if recv_size < 0 then
          err:=recv_size
        else  if recv_size < recv_wrd_cnt then
          err:=LTR_ERROR_RECV_INSUFFICIENT_DATA
        else
        begin
          err:=LTR25_ProcessData(phltr25^, rcv_buf, data, recv_size,
                                  LTR25_PROC_FLAG_VOLT, ch_cur_status);
          if err=LTR_OK then
          begin
            for ch:=0 to LTR25_CHANNEL_CNT-1 do
            begin
              ch_valid[ch] := False;
            end;

            // получаем кол-во отсчетов на канал
            recv_size := Trunc(recv_size/ch_cnt) ;

            for ch:=0 to ch_cnt-1 do
            begin
	      ChStatus[ch_nums[ch]]:= ch_cur_status[ch];
              ch_min[ch_nums[ch]] := data[ch];
              ch_max[ch_nums[ch]] := data[ch];
              ch_valid[ch_nums[ch]] := True;
            end;

            // рассчет минимального и максимального значения для всех каналов,
            // которые разрешены и для которых статус = OK
            for i:=0 to recv_size-1 do
            begin
              for ch:=0 to ch_cnt-1 do
              begin
                if ch_cur_status[ch] = LTR25_CH_STATUS_OK then
                begin
                  if ch_min[ch_nums[ch]] > data[ch_cnt*i + ch] then
                    ch_min[ch_nums[ch]] := data[ch_cnt*i + ch];
                  if ch_max[ch_nums[ch]] < data[ch_cnt*i + ch] then
                    ch_max[ch_nums[ch]] := data[ch_cnt*i + ch];
                end;
              end;
            end;



            // вычисляем пик-пик просто как разницу максимального и минимального
            for ch:=0 to LTR25_CHANNEL_CNT-1 do
            begin
              if ch_valid[ch] then
              begin
                if ChStatus[ch] = LTR25_CH_STATUS_OK then
                  ChPP[ch]:=ch_max[ch] - ch_min[ch];

              end;
              ChValidData[ch]:= ch_valid[ch];
            end;
            // обновляем значения элементов управления
            Synchronize(@updateData);
          end;
        end;

      end; //while not stop and (err = LTR_OK) do

      { По выходу из цикла отсанавливаем сбор данных.
        Чтобы не сбросить код ошибки (если вышли по ошибке)
        результат останова сохраняем в отдельную переменную }
      stoperr:= LTR25_Stop(phltr25^);
      if err = LTR_OK then
        err:= stoperr;
    end;

  end;
end.

