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

    Пример принимает данные от 4-х каналов с частотой АЦП 400 КГц.
    На экране отображается только по первому отсчету каждого принятого блока.

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

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

    Сборка в VisualStudio:
    Для того чтобы собрать проект в Visual Studio, измените путь к заголовочным файлам
    (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties)
    -> С/С++ -> Общие (General) -> Дополнительные каталоги включения (Additional Include Directories))
    на нужный в зависимаости от пути установки библиотек (LTR12api.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)
    и сохраните изменения в файле. А также следует убедится, что в настройках
    консоли стоит шрифт с поддержкой русского языка (например Consolas).
*/


#include "ltr/include/ltr12api.h"
/* остальные заголовочные файлы */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifdef _WIN32
#include <locale.h>
#include <conio.h>
#else
#include <signal.h>
#include <unistd.h>
#endif


/* Количество отсчетов на канал, принмаемых за раз */

/* таймаут на ожидание данных при приеме (без учета времени преобразования) */
#define RECV_TOUT  4000

#define ADC_FREQ 20000

#define RECV_BLOCK_CH_SIZE  (500 * ADC_FREQ / 1000)
#define SKIP_POINTS         (100 * ADC_FREQ / 1000)

#define CH_NUM(i) (30 - i)

#define CH_CNT 1

//#define REOPEN_MODULE
#define RESTART_MODULE

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[]) {
    TLTR12 hltr12;
    INT  err;
    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) {
        /* инициализация дескриптора модуля */
        LTR12_Init(&hltr12);
        /* открытие канала связи с модулем, установленным в заданный слот */
        err = LTR12_Open(&hltr12, par.addr, LTRD_PORT_DEFAULT, par.serial, par.slot);
    }
    if (err != LTR_OK) {
        fprintf(stderr, "Не удалось установить связь с модулем. Ошибка %d (%s)\n",
                err, LTR12_GetErrorString(err));
    } else {

        DWORD ch_num;
        /* вывод информации о модуле */
        printf( "Информация о модуле:\n"
                "  Название модуля  : %s\n"
                "  Серийный номер   : %s\n"
                "  Версия прошивки  : %u.%u\n",
                   hltr12.ModuleInfo.Name, hltr12.ModuleInfo.Serial,
                  (hltr12.ModuleInfo.FirmwareVersion >> 8) & 0xFF, hltr12.ModuleInfo.FirmwareVersion & 0xFF);
        fflush(stdout);



        /* задание параметров работы модуля */
        /* режим старта сбора данных - внутренний */
        hltr12.Cfg.ConvStartSrc = LTR12_CONV_START_SRC_INT;
        /* режим синхронизации АПЦ - внутренний */
        hltr12.Cfg.AcqStartSrc      = LTR12_ACQ_START_SRC_INT;
        /* количество логических каналов - 4 */
        hltr12.Cfg.LChCnt       = CH_CNT;
        /* таблица управления логическими каналами */
        for (ch_num = 0; ch_num < hltr12.Cfg.LChCnt; ch_num++) {
            LTR12_FillLChannel(&hltr12, ch_num,  (CH_NUM(ch_num) - 1) & 0xFF, LTR12_CH_MODE_COMM);
        }
        LTR12_FillAdcFreqParams(&hltr12, ADC_FREQ, NULL);


#ifndef RESTART_MODULE
        if (err == LTR_OK) {
            /* передаем настройки в модуль */
            err = LTR12_SetADC(&hltr12);
            if (err != LTR_OK) {
                fprintf(stderr, "Не удалось установить настройки модуля. Ошибка %d: %s\n", err,
                        LTR12_GetErrorString(err));
            }
            if (err == LTR_OK) {
                /* запуск сбора данных */
                err = LTR12_Start(&hltr12);
                if (err != LTR_OK) {
                    fprintf(stderr, "Не удалось запустить сбор данных. Ошибка %d: %s\n", err,
                            LTR12_GetErrorString(err));
                }
            }
        }
#endif

        if (err == LTR_OK) {
            DWORD recvd_blocks=0;
            INT recv_data_cnt = RECV_BLOCK_CH_SIZE * hltr12.Cfg.LChCnt;
            DWORD  *rbuf = (DWORD*)malloc(recv_data_cnt*sizeof(rbuf[0]));
            double *data = (double *)malloc(recv_data_cnt*sizeof(data[0]));
            if ((rbuf==NULL) || (data==NULL)) {
                fprintf(stderr, "Ошибка выделения памяти!\n");
                err = LTR_ERROR_MEMORY_ALLOC;
            } else {
                INT stop_err = 0;
                double prev_vals[CH_CNT];
                double max_delta[CH_CNT];
                double sum_delta[CH_CNT];
                int deltas_cnt = 0;
                INT prev_valid = FALSE;
                printf("Сбор данных запущен. Для останова нажмите %s\n",
       #ifdef _WIN32
                       "любую клавишу"
       #else
                       "CTRL+C"
       #endif
                       );
                fflush(stdout);

                while (!f_out && (err==LTR_OK)) {
#ifdef RESTART_MODULE
                    /* передаем настройки в модуль */
                    err = LTR12_SetADC(&hltr12);
                    if (err != LTR_OK) {
                        fprintf(stderr, "Не удалось установить настройки модуля. Ошибка %d: %s\n", err,
                                LTR12_GetErrorString(err));
                    }
                    if (err == LTR_OK) {
                        /* запуск сбора данных */
                        err = LTR12_Start(&hltr12);
                        if (err != LTR_OK) {
                            fprintf(stderr, "Не удалось запустить сбор данных. Ошибка %d: %s\n", err,
                                    LTR12_GetErrorString(err));
                        }
                    }
#endif

                    if (err == LTR_OK) {
                        INT recvd;
                        /* в таймауте учитываем время выполнения самого преобразования*/
                        DWORD tout = RECV_TOUT + (DWORD)(1000. * RECV_BLOCK_CH_SIZE/hltr12.State.FrameFreq + 1);
                        /* получение данных от LTR12 */
                        recvd = LTR12_Recv(&hltr12, rbuf, NULL, recv_data_cnt, tout);
                        /* Значение меньше нуля соответствуют коду ошибки */
                        if (recvd<0) {
                            err = recvd;
                            fprintf(stderr, "Ошибка приема данных. Ошибка %d:%s\n",
                                    err, LTR12_GetErrorString(err));
                        } else if (recvd!=recv_data_cnt) {
                            fprintf(stderr, "Принято недостаточно данных. Запрашивали %d, приняли %d\n",
                                    recv_data_cnt, recvd);
                            err = LTR_ERROR_RECV_INSUFFICIENT_DATA;
                        } else {
                            /* сохранение принятых и обработанных данных в буфере */
                            err = LTR12_ProcessData(&hltr12, rbuf, data, &recvd, LTR12_PROC_FLAG_CALIBR | LTR12_PROC_FLAG_CONV_UNIT);
                            if (err!=LTR_OK) {
                                fprintf(stderr, "Ошибка обработки данных. Ошибка %d:%s\n",
                                        err, LTR12_GetErrorString(err));
                            } else {
                                DWORD ch, pt;
                                recvd_blocks++;
                                /* выводим по первому слову на канал */
                                printf("Блок %4d: ", recvd_blocks);

                                for (ch=0; ch < hltr12.Cfg.LChCnt; ch++) {
                                    double avg = 0;
                                    for (pt = SKIP_POINTS; pt < RECV_BLOCK_CH_SIZE; pt++) {
                                        avg += data[pt * hltr12.Cfg.LChCnt + ch];
                                    }
                                    avg /= RECV_BLOCK_CH_SIZE;

                                    printf("%8.4f", avg);
                                    if (prev_valid) {
                                        double delta = avg - prev_vals[ch];
                                        double abs_delta = fabs(delta);
                                        sum_delta[ch] += abs_delta;
                                        printf(" d %9.6f", delta);
                                        if (max_delta[ch] < abs_delta) {
                                            max_delta[ch] = abs_delta;
                                            printf(" !");
                                        } else {
                                            printf("  ");
                                        }
                                        printf(" dm %8.6f", max_delta[ch]);
                                        printf("   avg %8.6f", sum_delta[ch]/(deltas_cnt));
                                    } else {
                                        printf("%11s", "");
                                        max_delta[ch]  = 0;
                                        sum_delta[ch] = 0;
                                    }

                                    if (ch==(hltr12.Cfg.LChCnt-1)) {
                                        printf("\n");
                                    } else {
                                        printf(",  ");
                                    }

                                    prev_vals[ch] = avg;
                                }
                                fflush(stdout);
                                prev_valid = TRUE;
                                deltas_cnt++;
                            }
                        }
#ifdef RESTART_MODULE
                        /* останавливаем сбор данных */
                        stop_err = LTR12_Stop(&hltr12);
                        if (stop_err!=LTR_OK) {
                            fprintf(stderr, "Не удалось остановить сбор данных. Ошибка %d: %s\n", err,
                                LTR12_GetErrorString(stop_err));
                            if (err == LTR_OK)
                                err = stop_err;
                        }
#endif
#ifdef REOPEN_MODULE

                        LTR12_Close(&hltr12);
                        if (err == LTR_OK) {
                            err = LTR12_Open(&hltr12,
                                       hltr12.Channel.saddr,
                                       hltr12.Channel.sport,
                                       hltr12.Channel.csn,
                                       hltr12.Channel.cc);
                        }
#endif
#ifdef _WIN32
                        /* проверка нажатия клавиши для выхода */
                        if (err==LTR_OK) {
                            if (_kbhit())
                                f_out = 1;
                        }
#endif
                    } //while (!f_out && (err==LTR_OK))



                }

                free(rbuf);
                free(data);
            }
        }
    }



    if (LTR12_IsOpened(&hltr12) == LTR_OK) {

        /* закрытие канала связи с модулем */
        LTR12_Close(&hltr12);
    }
        
    return err;
}



