#include "ltr/include/ltr27api.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <locale.h>
#endif


#define ADC_FREQ        (100)
/* количество отсчетов на каждый мезанин, принимаемых за раз (блок) */
#define RECV_FRAMES     (100)
/* количество принятых блоков по RECV_FRAMES отсчетов, после которых пример завершится. */
#define RECV_BLOCK_CNT  (0)
/* таймаут в мс на прием блока */
#define RECV_BLOCK_TOUT  (10000)

typedef struct {
    int slot;
    const char *serial;
    DWORD addr;
} t_open_param;

static int f_get_params(int argc, char** argv, t_open_param* par) {
    INT err = 0;
    par->slot = LTR_CC_CHNUM_MODULE1;
    par->serial = "";
    par->addr = LTRD_ADDR_DEFAULT;


    if (argc > 1)
        par->slot = atoi(argv[1]);
    if (argc > 2)
        par->serial = argv[2];
    if (argc > 3) {
        int a[4],i;
        if (sscanf(argv[3], "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3])!=4) {
            fprintf(stderr, "Неверный формат адреса сервера!!\n");
            err = -1;
        }

        for (i=0; (i < 4) && !err; i++) {
            if ((a[i]<0) || (a[i] > 255)) {
                fprintf(stderr, "Неверный формат адреса сервера!!\n");
                err = -1;
            }
        }

        if (!err) {
            par->addr = (a[0] << 24) | (a[1]<<16) | (a[2]<<8) | a[3];
        }
    }
    return err;
}


int main(int argc, char** argv) {
    INT err=0, stop_err=0;
    TLTR27 ltr27;
    t_open_param par;


#ifdef _WIN32
    setlocale(LC_ALL, "");
#endif

    err = f_get_params(argc, argv, &par);

    if (err==LTR_OK) {
        LTR27_Init(&ltr27);
        /* устанавливаем соединение с модулем находящемся в первом слоте крейта.
           для сетевого адреса, сетевого порта ltr-сервера и серийного номера
           крейта используем значения по умолчанию */
        err=LTR27_Open(&ltr27, par.addr, LTRD_PORT_DEFAULT, par.serial, par.slot);
        if (err==LTR_OK) {
            printf("Модуль открыт успешно!\n");
        } else {
             fprintf(stderr, "Не удалось открыть модуль. Ошибка %d (%s)\n",
                                    err, LTR27_GetErrorString(err));
        }
    }

    if(err==LTR_OK) {
        DWORD mez_idx;
        /* получаем конфигурацию модуля:
           тип установленных мезонинов и параметры сбора данных АЦП */
        err=LTR27_GetConfig(&ltr27);
        if (err!=LTR_OK) {
            fprintf(stderr, "Не удалось прочитать конфигурацию. Ошибка %d (%s)\n",
                            err, LTR27_GetErrorString(err));
        }

        if(err==LTR_OK) {
            /* считываем описание модуля */
            err=LTR27_GetDescription(&ltr27,  LTR27_MODULE_DESCRIPTION);

            /* считываем информацию о мезонинах */
            for(mez_idx=0; mez_idx<LTR27_MEZZANINE_CNT && err==LTR_OK; mez_idx++) {
                if(strcmp(ltr27.Mezzanine[mez_idx].Name, "EMPTY")) {
                    err=LTR27_GetDescription(&ltr27, LTR27_MEZZANINE1_DESCRIPTION<<mez_idx);
                }
            }

            if (err!=LTR_OK) {
                fprintf(stderr, "Не удалось прочитать описание модуля или мезанинов. Ошибка %d (%s)\n",
                                err, LTR27_GetErrorString(err));
            }
        }


        if(err==LTR_OK) {
            printf("Прочитана информация о модуле :\n");
            printf("    Название       : %s\n", ltr27.ModuleInfo.Module.DeviceName);
            printf("    Серийный номер : %s\n", ltr27.ModuleInfo.Module.SerialNumber);
            if(ltr27.ModuleInfo.Cpu.Active) {
                printf("   Информация о процессоре :\n");
                printf("     Название        : %s\n",      ltr27.ModuleInfo.Cpu.Name);
                printf("     Частота клока   : %.1f Hz\n", ltr27.ModuleInfo.Cpu.ClockRate);
                printf("     Версия прошивки : %d.%d.%d.%d\n",
                       (ltr27.ModuleInfo.Cpu.FirmwareVersion>>24) & 0xFF,
                       (ltr27.ModuleInfo.Cpu.FirmwareVersion>>16) & 0xFF,
                       (ltr27.ModuleInfo.Cpu.FirmwareVersion>>8) & 0xFF,
                       ltr27.ModuleInfo.Cpu.FirmwareVersion & 0xFF);
                printf("     Комментарий     : %s\n",      ltr27.ModuleInfo.Cpu.Comment);
            } else {
                printf("  Не найдено действительное описание процессора!!!\n");
            }

            for(mez_idx=0; mez_idx<LTR27_MEZZANINE_CNT; mez_idx++) {
                if(ltr27.ModuleInfo.Mezzanine[mez_idx].Active) {
                    INT ceof_idx;
                    printf("    Информация о мезонине в слоте %d :\n", mez_idx+1);

                    printf("      Название        : %s\n", ltr27.ModuleInfo.Mezzanine[mez_idx].Name);
                    printf("      Серийный номер  : %s\n", ltr27.ModuleInfo.Mezzanine[mez_idx].SerialNumber);
                    printf("      Ревизия         : %d\n", (int)ltr27.ModuleInfo.Mezzanine[mez_idx].Revision);
                    for(ceof_idx=0; ceof_idx < LTR27_MEZZANINE_CBR_COEF_CNT; ceof_idx++)
                        printf("      Калибр. коэф. %d : %.3f\n", ceof_idx, ltr27.ModuleInfo.Mezzanine[mez_idx].Calibration[ceof_idx]);
                }
            }

            fflush(stdout);
        }


        if (err==LTR_OK) {
            /* Задаем частоту АЦП */
            double adc_freq;
            LTR27_FindAdcFreqParams(ADC_FREQ, &ltr27.FrequencyDivisor, &adc_freq);
            /* копируем калибровочные коэффициенты (в противном случае не будут применяться) */
            for(mez_idx=0; mez_idx<LTR27_MEZZANINE_CNT; mez_idx++) {
                INT ceof_idx;
                for(ceof_idx=0; ceof_idx < LTR27_MEZZANINE_CBR_COEF_CNT; ceof_idx++)
                    ltr27.Mezzanine[mez_idx].CalibrCoeff[ceof_idx]=ltr27.ModuleInfo.Mezzanine[mez_idx].Calibration[ceof_idx];
            }
            // передаем параметры сбора данных в модуль
            err=LTR27_SetConfig(&ltr27);
            if (err) {
                fprintf(stderr, "Не удалось записать настройки модуля. Ошибка %d (%s)\n",
                                err, LTR27_GetErrorString(err));
            }
        }

        if(err==LTR_OK) {
            // запускаем сбор данных АЦП
            err=LTR27_ADCStart(&ltr27);
            if (err) {
                fprintf(stderr, "Не удалось запустить сбор данных. Ошибка %d (%s)\n",
                                err, LTR27_GetErrorString(err));
            }
        }

        if(err==LTR_OK) {
            DWORD buf[RECV_FRAMES][2*LTR27_MEZZANINE_CNT];
            double data[RECV_FRAMES][2*LTR27_MEZZANINE_CNT];
            DWORD b;

            printf("Сбор данных запущен успешно\n");


            for (b=0 ; (err==LTR_OK)
     #if RECV_BLOCK_CNT
                 && (b < RECV_BLOCK_CNT)
     #endif
                 ; b++) {
                // забираем данные АЦП
                INT size = sizeof(buf)/sizeof(buf[0][0]);
                INT recvd=LTR27_Recv(&ltr27, &buf[0][0], NULL, (DWORD)size, RECV_BLOCK_TOUT);
                if (recvd<0) {
                    err = recvd;
                    fprintf(stderr, "Ошибка приема данных. Ошибка %d:%s\n",
                            err, LTR27_GetErrorString(err));
                } else if (recvd!=size) {
                    fprintf(stderr, "Принято недостаточно данных. Запрашивали %d, приняли %d\n",
                            size, recvd);
                    err = LTR_ERROR_RECV;
                } else {
                    // применяем калибровку, переводим в вольты
                    err=LTR27_ProcessData(&ltr27, &buf[0][0], &data[0][0], (DWORD*)&recvd, 1, 1);
                    if (err!=LTR_OK) {
                        fprintf(stderr, "Ошибка обработки данных! Ошибка %d (%s)\n",
                                        err, LTR27_GetErrorString(err));
                    } else {
                        /* отображаем данные */
                        printf("Успешно приняли блок данных %d:\n", b+1);
                        INT frame_idx = 0;
                        INT ch_idx;
                        /* для примера выводим по одному отсчету каждого канала
                         * с первого кадра */
                        for(ch_idx=0; ch_idx < LTR27_CHANNEL_CNT; ch_idx++) {
                            if(strcmp(ltr27.Mezzanine[ch_idx>>1].Name, "EMPTY")) {
                                printf("  %.4f %s", data[frame_idx][ch_idx],
                                       ltr27.Mezzanine[ch_idx/LTR27_MEZZANINE_CHANNEL_CNT].Unit);
                            }
                        }
                        printf("\n");
                        fflush(stdout);
                    }
                }
            }


            // останавливаем АЦП
            stop_err = LTR27_ADCStop(&ltr27);
            if (stop_err!=LTR_OK) {
                fprintf(stderr, "Не удалось остановить сбор данных. Ошибка %d (%s)\n",
                                stop_err, LTR27_GetErrorString(stop_err));
                if (err==LTR_OK)
                    err = stop_err;
            } else {
                printf("Сбор данных остановлен успешно\n");
            }
        }

        // разрываем соединение
        stop_err = LTR27_Close(&ltr27);
        if (stop_err!=LTR_OK) {
            fprintf(stderr, "Не удалось закрыть модуль. Ошибка %d (%s)\n",
                            stop_err, LTR27_GetErrorString(stop_err));
            if (err==LTR_OK)
                err = stop_err;
        } else {
            printf("Соединение с модулем закрыто успешно\n");
        }
    }
    return err;
}

