/*
    Данный пример демонстрирует работу с модулем LTR25.
    По умолчанию идет работа с первым слотом первого крейта, но это можно изменить
        через параметры командной строки:
    ltr25_recv  slot crate_serial srvip
    где:
        slot         - номер слота (от 1 до 16)
        crate_serial - серийный номер крейта (если крейтов больше одного)
        srvip        - ip-адрес программы ltrd или LtrServer (если он запущен на другой машине)
    Параметры опциональны: можно указать только слот, слот и серийный номер крейта или все

    Пример принимает данные от первых трех каналов на максимальной частоте сбора.
    На экране отображается значение пик-пик (максимум - минимум) по принятому блоку
    по каждому каналу.

    Пользователь может изменить настройки на свои, поменяв заполнение полей
    структуры перед запуском сбора.

    Сбор идет до нажатия любой клавиши на Windows или  CTRL+C на Linux

    Сборка в VisualStudio:
    Для того чтобы собрать проект в Visual Studio, измените путь к заголовочным файлам
    (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties)
    -> С/С++ -> Общие (General) -> Дополнительные каталоги включения (Additional Include Directories))
    на нужный в зависимаости от пути установки библиотек (ltr25api.h  и остальные заголовочные
    файлы должны находится в поддиректории ltr/include относительно указанного пути)
    и измените путь к .lib файлам на <путь установки библиотек>/lib/msvc
    (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties) ->
    Компоновщик (Linker) -> Общие (General) -> Дополнительные каталоги библиотек (Additional Library Directories)).

    Внимание!: Если Вы собираете проект под Visual Studio и у Вас некорректно
    отображается вывод русских букв, то нужно изменить кодировку:
    выберите Файл (File) -> Дополнительные параметры сохранения (Advanced Save Options)...
    и в поле Кодировка (Encoding) выберите Юникод (UTF8, с сигнатурой)/Unicode (UTF-8 with signature)
    и сохраните изменения в файле.
*/

#include "ltr/include/ltr25api.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <locale.h>
#include <conio.h>
#else
#include <signal.h>
#include <unistd.h>
#endif


/* количество отсчетов на канал, принмаемых за раз */
#define RECV_BLOCK_CH_SIZE  4096*128


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


/* признак необходимости завершить сбор данных */
static int f_out = 0;

#ifndef _WIN32
/* Обработчик сигнала завершения для Linux */
static void f_abort_handler(int sig) {
    f_out = 1;
}
#endif


/* Разбор параметров командной строки. Если указано меньше, то используются
 * значения по умолчанию:
 * 1 параметр - номер слота (от 1 до 16)
 * 2 параметр - серийный номер крейта
 * 3 параметр - ip-адрес сервера */
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, "Неверный формат IP-адреса!!\n");
            err = -1;
        }

        for (i=0; (i < 4) && !err; i++) {
            if ((a[i]<0) || (a[i] > 255)) {
                fprintf(stderr, "Недействительный IP-адрес!!\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 = LTR_OK;
    TLTR25 hltr25;
    t_open_param par;
#ifndef _WIN32
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    /* В ОС Linux устанавливаем свой обработчик на сигнал закрытия,
       чтобы завершить сбор корректно */
    sa.sa_handler = f_abort_handler;
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGABRT, &sa, NULL);
#endif

#ifdef _WIN32
    /* для вывода русских букв в консоль для ОС Windows*/
    setlocale(LC_ALL, "");
#endif

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

    if (err==LTR_OK) {
        LTR25_Init(&hltr25);
        /* Устанавливаем соединение с модулем */
        err = LTR25_Open(&hltr25, par.addr, LTRD_PORT_DEFAULT, par.serial, par.slot);
        if (err!=LTR_OK) {
            fprintf(stderr, "Не удалось установить связь с модулем. Ошибка %d (%s)\n",
                    err, LTR25_GetErrorString(err));
        } else {
            INT close_err;

            /* Выводим прочитанную информацию о модуле */
            printf("LTR25 Info: \n");
            printf("  Serial      = %s\n", hltr25.ModuleInfo.Serial);
            printf("  PLD Ver     = %d\n", hltr25.ModuleInfo.VerPLD);
            printf("  FPGA Ver    = %d\n", hltr25.ModuleInfo.VerFPGA);
            printf("  Board Rev   = %d\n", hltr25.ModuleInfo.BoardRev);
            fflush(stdout);

            /* Настройка модуля */
            /* Формат - 24 или 20 битный */
            hltr25.Cfg.DataFmt = LTR25_FORMAT_32;
            /* Устанавливаем частоту с помощью одной из констант */
            hltr25.Cfg.FreqCode = LTR25_FREQ_19K;
            /* Источник тока 2.86 или 10 мА */
            hltr25.Cfg.ISrcValue = LTR25_I_SRC_VALUE_2_86;

            /* Настройка режимов каналов */
            hltr25.Cfg.Ch[0].Enabled = TRUE;
            hltr25.Cfg.Ch[1].Enabled = TRUE;
            hltr25.Cfg.Ch[2].Enabled = TRUE;

            err = LTR25_SetADC(&hltr25);
            if (err!=LTR_OK) {
                fprintf(stderr, "Не удалось установить настройки АЦП: Ошибка %d (%s)\n",
                        err, LTR25_GetErrorString(err));
            } else {
                /* после SetADC() обновляется поле AdcFreq. Становится равной действительной
                 * установленной частоте */
                printf("Adc configured. Freq = %.2f Гц, ch cnt = %d\n",
                        hltr25.State.AdcFreq, hltr25.State.EnabledChCnt);
            }

            if (err==LTR_OK) {
                DWORD recvd_blocks=0;
                INT recv_data_cnt = RECV_BLOCK_CH_SIZE*hltr25.State.EnabledChCnt;
                /* В 24-битном формате каждому отсчету соответствует два слова от модуля,
                   а в 20-битном - одно */
                INT   recv_wrd_cnt = recv_data_cnt*(hltr25.Cfg.DataFmt==LTR25_FORMAT_20 ? 1 : 2);
                DWORD  *rbuf = (DWORD*)malloc(recv_wrd_cnt*sizeof(rbuf[0]));
                DWORD  *rbuf_prev = (DWORD*)malloc(recv_wrd_cnt*sizeof(rbuf[0]));
                WORD wrd_cntr = 0;
                WORD ch_cntr = 0;
                BOOL wrd_cntr_valid = FALSE;

                INT prev_recvd = 0;
                LARGE_INTEGER freq;
                QueryPerformanceFrequency(&freq);

                if ((rbuf==NULL) || (rbuf_prev==NULL))  {
                    fprintf(stderr, "Ошибка выделения памяти!\n");
                    err = LTR_ERROR_MEMORY_ALLOC;
                }

                if (err==LTR_OK) {
                    /* Запуск сбора данных */
                    err=LTR25_Start(&hltr25);
                    if (err!=LTR_OK) {
                        fprintf(stderr, "Не удалось запустить сбор данных! Ошибка %d (%s)\n",
                                err, LTR25_GetErrorString(err));
                    }
                }

                if (err==LTR_OK) {
                    printf("Started!\n");
                    fflush(stdout);
                }


                /* ведем сбор данных до возникновения ошибки или до
                 * запроса на завершение */
                while (!f_out && (err==LTR_OK)) {
                    LARGE_INTEGER tstart, tend;
                    INT recvd, i;
                    /* В таймауте учитываем время сбора запрашиваемого числа отсчетов */
                    DWORD tout = 20;
                    /* Прием данных от модуля.  */
                    QueryPerformanceCounter(&tstart);
                    recvd = LTR25_Recv(&hltr25, rbuf, NULL, recv_wrd_cnt, tout);
                    QueryPerformanceCounter(&tend);

                    /* Значение меньше нуля соответствуют коду ошибки */
                    if (recvd<0) {
                        err = recvd;
                        fprintf(stderr, "Ошибка приема данных. Ошибка %d:%s\n",
                                err, LTR25_GetErrorString(err));
                    } else {
                        int err_idx = -1;
                        printf("recvd %d in %lld ms\n",
                               recvd, (tend.QuadPart - tstart.QuadPart)*1000/freq.QuadPart);
                        for (i = 0; i < recvd; i++) {
#if 0
                            BYTE rcv_cntr = rbuf[i] & 0x0F;
                            if (rcv_cntr != wrd_cntr) {
                                f_out = 1;
                                printf(" --cntr err %3d: %d\n", i, (INT)(rcv_cntr - wrd_cntr));
                                wrd_cntr = rcv_cntr;
                                if (err_idx < 0)
                                    err_idx = i;
                            }
                            if (++wrd_cntr == 13)
                                wrd_cntr = 0;
#else
                            WORD rcv_cntr = rbuf[i] >> 16;
                            if (!wrd_cntr_valid) {
                                wrd_cntr_valid = TRUE;
                                wrd_cntr = rcv_cntr;
                            } else {
                                if (wrd_cntr != rcv_cntr) {
                                    f_out = 1;
                                    printf(" --cntr err %3d: %d\n", i, (rcv_cntr - wrd_cntr));
                                    wrd_cntr = rcv_cntr;
                                    if (err_idx < 0)
                                        err_idx = i;
                                }
                            }
                            wrd_cntr++;
#endif
                        }

                        if (err_idx >= 0) {
                            printf("error at word %d of %d\n", err_idx, recvd);
#if 0
                            printf("prev block (size %d):\n", prev_recvd);
                            for (i = 0; i < prev_recvd; i++) {
                                printf("    %3d: 0x%08lX\n", i, rbuf_prev[i]);
                            }
                            printf("\n");
#endif
                            for (i = err_idx - 20; (i < recvd) && (i < err_idx + 20); i++) {

                                if (i < 0) {
                                    if (prev_recvd + i >= 0) {
                                        printf("    %3d: 0x%08lX\n", i, rbuf_prev[prev_recvd + i]);
                                    }
                                } else {
                                    printf("    %3d: 0x%08lX %c\n", i, rbuf[i], i == err_idx ? '!' : ' ');
                                }
                            }
                            while (!_kbhit()) {}
                        } else {
                            prev_recvd = recvd;
                            memcpy(rbuf_prev, rbuf, recvd * 4);
                        }

                    }

#ifdef _WIN32
                    /* проверка нажатия клавиши для выхода */
                    if (err==LTR_OK) {
                        if (_kbhit())
                            f_out = 1;
                    }
#endif
                } //while (!f_out && (err==LTR_OK))




                /* по завершению останавливаем сбор, если был запущен */
                if (hltr25.State.Run) {
                    INT stop_err = LTR25_Stop(&hltr25);
                    if (stop_err!=LTR_OK) {
                        fprintf(stderr, "Не удалось остановить сбор данных. Ошибка %d:%s\n",
                                stop_err, LTR25_GetErrorString(stop_err));
                        if (err==LTR_OK)
                            err = stop_err;
                    } else {
                        printf("Stopped!\n");
                    }
                }


                free(rbuf);

            }

            /* закрываем связь с модулем */
            close_err = LTR25_Close(&hltr25);
            if (close_err!=LTR_OK) {
                fprintf(stderr, "Не удалось закрыть связь с модулем. Ошибка %d:%s\n",
                        close_err, LTR25_GetErrorString(close_err));
                if (err==LTR_OK)
                    err = close_err;
            } else {
                printf("Closed!\n");
            }
        }
    }

    return err;
}
