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


#pragma hdrstop

#include "LTR210_ProcessThread.h"

// Таймаут на прием блока данных от модуля
#define RECV_TOUT        1000
// Если за данное время не придет ни одного слова от модуля, то считаем его неисаравным
#define KEEPALIVE_TOUT    10000


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

#pragma package(smart_init)

 __fastcall LTR210_ProcessThread::LTR210_ProcessThread(bool CreateSuspended)
    : TThread(CreateSuspended), stop(false), err (LTR_OK) {

}

/* обновление индикаторов формы результатами последнего измерения.
   Метод должен выполняться только через Syncronize, который нужен
   для доступа к элементам VCL не из основного потока */
void __fastcall LTR210_ProcessThread::updateData() {
    edtValidFrameCntr->Text   = IntToStr(ValidFrameCntr);
    edtInvalidFrameCntr->Text = IntToStr(InvalidFrameCntr);
    edtSyncSkipCntr->Text     = IntToStr(SyncSkipCntr);
    edtOverlapCntr->Text      = IntToStr(OverlapCntr);
    edtInvalidHistCntr->Text  = IntToStr(InvalidHistCntr);
    for (int ch=0; ch < LTR210_CHANNEL_CNT; ch++) {
        if (ChValidData[ch]) {
            edtChAvg[ch]->Text = FloatToStrF(ChAvg[ch], ffFixed, 4, 8);
        } else {
            edtChAvg[ch]->Text = "";
        }
    }
}

void __fastcall LTR210_ProcessThread::checkFrameStatusFlags(WORD status_flags) {
    //анализируем флаги статуса принятого кадра и обновляем соответствующие счетчики
    if (status_flags & LTR210_STATUS_FLAG_SYNC_SKIP)
        SyncSkipCntr++;
    if (status_flags & LTR210_STATUS_FLAG_OVERLAP)
      OverlapCntr++;
    if (status_flags & LTR210_STATUS_FLAG_INVALID_HIST)
      InvalidHistCntr++;
}

void __fastcall LTR210_ProcessThread::Execute() {
    DWORD *rcv_buf;
    double *data;
    TLTR210_DATA_INFO *info;
    INT err = LTR_OK;

    //обнуляем переменные
    ValidFrameCntr=0;
    InvalidFrameCntr=0;
    SyncSkipCntr=0;
    OverlapCntr=0;
    InvalidHistCntr=0;
    for (int ch=0; ch < LTR210_CHANNEL_CNT; ch++)
        ChValidData[ch]=false;
    Synchronize(updateData);


    /* выделяем массивы для приема данных. Используем размер принимаемых
      данных за кадр, рассчитанный библиотекой */
    rcv_buf = new DWORD[phltr210->State.RecvFrameSize];
    data = new double[phltr210->State.RecvFrameSize];
    info = new TLTR210_DATA_INFO[phltr210->State.RecvFrameSize];

    err = LTR210_Start(phltr210);
    if (err==LTR_OK) {

        while (!stop && (err==LTR_OK)) {
            DWORD evt;
            // Ожидаем прихода данных от модуля
            err = LTR210_WaitEvent(phltr210, &evt, NULL, 100);
            //Анализ, что за событие произошло
            switch (evt) {
                case  LTR210_RECV_EVENT_SOF: {
                        TLTR210_FRAME_STATUS frame_st;
                        INT recv_size;
                        DWORD rd_pos=0;

                        /* Чтобы не использовать большой таймаут на прием (в течение
                           которого мы не можем отменить сбор), допускаем возможность
                           приема кадра несколькими блоками. При этом выбираем
                           RECV_TOUT таким, чтобы хотя бы слово пришло за этот таймаут. */
                        while ((rd_pos!=phltr210->State.RecvFrameSize) && (err==LTR_OK) && !stop) {
                            recv_size = LTR210_Recv(phltr210, &rcv_buf[rd_pos], NULL,
                                                    phltr210->State.RecvFrameSize-rd_pos, RECV_TOUT);
                            if (recv_size<0) {
                                err = recv_size;
                            } else if (recv_size==0) {
                                err = LTR_ERROR_RECV_INSUFFICIENT_DATA;
                            } else {
                                rd_pos+=recv_size;
                            }
                        }

                        if ((err==LTR_OK) && !stop) {
                            recv_size = rd_pos;
                            err = LTR210_ProcessData(phltr210, rcv_buf, data, &recv_size,
                                                     LTR210_PROC_FLAG_VOLT |
                                                     LTR210_PROC_FLAG_AFC_COR |
                                                     LTR210_PROC_FLAG_ZERO_OFFS_COR,
                                                     &frame_st, info);
                        }

                        if ((err==LTR_OK) && !stop) {
                            // По полю Result делаем общий вывод, действительны ли данные
                            if (frame_st.Result == LTR210_FRAME_RESULT_OK)
                            {
                                double ch_avg[LTR210_CHANNEL_CNT];
                                DWORD  ch_size[LTR210_CHANNEL_CNT];
                                bool   ch_valid[LTR210_CHANNEL_CNT];
                                
                                ValidFrameCntr++;
                                checkFrameStatusFlags(frame_st.Flags);

                                // для примера рассчитываем просто среднее по каждому каналу
                                for (int ch=0; ch < LTR210_CHANNEL_CNT; ch++) {
                                    ch_size[ch] = 0;
                                    ch_avg[ch] = 0;
                                    ch_valid[ch] = 0;
                                }

                                /* В данном примере определяем принадлежность каналам по
                                    доп. информации. Хотя мы могли бы и воспользоваться фактом,
                                    что данные идут чередуясь (при двух разрешенных каналах:
                                    1-ый отсчет 1-го канала, 1-ый 2-го канала, 2-ой 1-го канала и т.д.) */
                                for (int i=0; i < recv_size; i++) {
                                    //для примера просто считаем среднее и выводим
                                    ch_size[info[i].Ch]++;
                                    ch_avg[info[i].Ch]+= data[i];
                                    ch_valid[info[i].Ch]=true;
                                }

                                for (int ch=0; ch < LTR210_CHANNEL_CNT; ch++) {
                                    if (ch_valid[ch])
                                        ChAvg[ch]=ch_avg[ch]/ch_size[ch];
                                    ChValidData[ch]=ch_valid[ch];
                                }
                            } else if (frame_st.Result == LTR210_FRAME_RESULT_ERROR) {
                                InvalidFrameCntr++;
                                checkFrameStatusFlags(frame_st.Flags);
                            }

                            // обновляем значения элементов управления
                            Synchronize(updateData);
                        }

                    }
                    break;
                case LTR210_RECV_EVENT_TIMEOUT:
                    if (phltr210->Cfg.Flags & LTR210_CFG_FLAGS_KEEPALIVE_EN) {
                        DWORD interval;
                        /* При включенной посылке периодических статусов,
                           если данных не пришло, то проверяем, что не превысили
                           предельный интервал ожидания */
                        err = LTR210_GetLastWordInterval(phltr210, &interval);
                        if ((err==LTR_OK) && (interval > KEEPALIVE_TOUT)) {
                            err = LTR210_ERR_KEEPALIVE_TOUT_EXCEEDED;
                        }
                    }
                    break;
            }
        }

        /* По выходу из цикла отсанавливаем сбор данных.
        Чтобы не сбросить код ошибки (если вышли по ошибке)
        результат останова сохраняем в отдельную переменную */
        INT stoperr= LTR210_Stop(phltr210);
        if (err == LTR_OK)
            err= stoperr;
    }
    delete []rcv_buf;
    delete []data;
    delete []info;
}
