/*
    Данный пример демонстрирует работу с модулем LTR24.
    По умолчанию идет работа с первым слотом первого крейта, но это можно изменить
        через параметры командной строки:
    ltr24_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))
    на нужный в зависимаости от пути установки библиотек (ltr24api.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/ltr24api.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*3
/* таймаут на ожидание данных при приеме */
#define RECV_TOUT  4000


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


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

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


/*------------------------------------------------------------------------------------------------*/
static int f_get_params(int argc, char **argv, t_open_param *par) {
    /* Разбор параметров командной строки. Если указано меньше, то используются
     * значения по умолчанию:
     * 1 параметр - номер слота (от 1 до 16)
     * 2 параметр - номер частоты дискретизации
     * 3 параметр - серийный номер крейта
     * 4 параметр - ip-адрес сервера
     */
    int err = 0;

    par->slot = LTR_CC_CHNUM_MODULE1;
    par->freq_rate = LTR24_FREQ_117K;
    par->serial = "";
    par->addr = LTRD_ADDR_DEFAULT;


    if (argc > 1)
        par->slot = atoi(argv[1]);
    if (argc > 2)
        par->freq_rate = atoi(argv[2]);
    if (argc > 3)
        par->serial = argv[3];
    if (argc > 4) {
        int a[4];
        int i;
        if (sscanf(argv[4], "%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) || (255 < a[i])) {
                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;
    TLTR24 hltr24;
    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) {
        LTR24_Init(&hltr24);
        /* Устанавливаем соединение с модулем */
        err = LTR24_Open(&hltr24, par.addr, LTRD_PORT_DEFAULT, par.serial, par.slot);
        if (err != LTR_OK) {
            fprintf(stderr, "Не удалось установить связь с модулем. Ошибка %d (%s)\n",
                    err, LTR24_GetErrorString(err));
        } else {
            INT ch_cnt=0;
            INT close_err;

            /* Читаем информацию о модуле, включая калибровки, из Flash-памяти
             * модуля. Без вызова этой функции нельзя будет получить правильные
             * откалиброванные значения */
            err = LTR24_GetConfig(&hltr24);
            if (err != LTR_OK) {
                fprintf(stderr, "Не удалось прочитать информацию о модуле из Flash-памяти. Ошибка %d (%s)\n",
                        err, LTR24_GetErrorString(err));
            } else {
                /* Выводим прочитанную информацию о модуле */
                printf("Модуль открыт успешно! Информация о модуле: \n");
                printf("  Название модуля    = %s\n", hltr24.ModuleInfo.Name);
                printf("  Серийный номер     = %s\n", hltr24.ModuleInfo.Serial);
                printf("  Версия PLD         = %d\n", hltr24.ModuleInfo.VerPLD);
                printf("  Поддержка ICP      = %s\n", hltr24.ModuleInfo.SupportICP ? "Есть" : "Нет");
                fflush(stdout);
            }

            /* Настройка модуля */
            if (err == LTR_OK) {
                /* Формат - 24 или 20 битный */
                hltr24.DataFmt = LTR24_FORMAT_20;
                /* Устанавливаем частоту с помощью одной из констант (Для 24-битного режима
                   макс. частота только при 2-х каналах, все 4 - только пр 58)  */
                hltr24.ADCFreqCode = par.freq_rate;
                /* Вкл./откл. тестовых режимов (измерение нуля/ICP-тест) */
                hltr24.TestMode = FALSE;

                /* Настройка режимов каналов */
                hltr24.ChannelMode[0].Enable = TRUE;
                hltr24.ChannelMode[0].Range = LTR24_RANGE_10;
                hltr24.ChannelMode[0].AC = FALSE;
                hltr24.ChannelMode[0].ICPMode = FALSE;

                hltr24.ChannelMode[1].Enable = TRUE;
                hltr24.ChannelMode[1].Range = LTR24_RANGE_2;
                hltr24.ChannelMode[1].AC = TRUE;
                hltr24.ChannelMode[1].ICPMode = FALSE;

                hltr24.ChannelMode[2].Enable = TRUE;
                hltr24.ChannelMode[2].Range = LTR24_RANGE_2;
                hltr24.ChannelMode[2].AC = TRUE;
                hltr24.ChannelMode[2].ICPMode = FALSE;

                hltr24.ChannelMode[3].Enable = FALSE;

                err = LTR24_SetADC(&hltr24);
                if (err != LTR_OK) {
                    fprintf(stderr, "Не удалось установить настройки АЦП: Ошибка %d (%s)\n",
                            err, LTR24_GetErrorString(err));
                } else  {
                    INT i;
                    /* подсчитываем кол-во разрешенных каналов */
                    for (i = 0, ch_cnt=0; i < LTR24_CHANNEL_NUM; i++) {
                        if (hltr24.ChannelMode[i].Enable)
                            ch_cnt++;
                    }

                    /* после SetADC() обновляется поле AdcFreq. Становится равной действительной
                     * установленной частоте */
                    printf("Настройки АЦП установленны успешно. Частота = %.2f Гц, Кол-во каналов = %d\n",
                            hltr24.ADCFreq, ch_cnt);
                }
            }

            if (err == LTR_OK) {
                DWORD recvd_blocks=0;
                INT recv_data_cnt = RECV_BLOCK_CH_SIZE*ch_cnt;
                /* В 24-битном формате каждому отсчету соответствует два слова от модуля,
                   а в 20-битном - одно */
                INT   recv_wrd_cnt = recv_data_cnt*(hltr24.DataFmt==LTR24_FORMAT_24 ? 2 : 1);
                DWORD  *rbuf = (DWORD*)malloc(recv_wrd_cnt*sizeof(rbuf[0]));
                double *data = (double *)malloc(recv_data_cnt*sizeof(data[0]));
                BOOL   *ovlds = (BOOL *)malloc(recv_data_cnt*sizeof(ovlds[0]));

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

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

                if (err == LTR_OK) {
                    printf("Сбор данных запущен. Для останова нажмите %s\n",
#ifdef _WIN32
                           "любую клавишу"
#else
                           "CTRL+C"
#endif
                           );
                    fflush(stdout);
                }


                /* ведем сбор данных до возникновения ошибки или до
                 * запроса на завершение */
                while (!f_out && (err == LTR_OK)) {
                    INT recvd;
                    /* В таймауте учитываем время сбора запрашиваемого числа отсчетов */
                    DWORD tout = RECV_TOUT + (DWORD)(1000.*RECV_BLOCK_CH_SIZE/hltr24.ADCFreq + 1);

                    /* Прием данных от модуля.  */
                    recvd = LTR24_Recv(&hltr24, rbuf, NULL, recv_wrd_cnt, tout);

                    /* Значение меньше нуля соответствуют коду ошибки */
                    if (recvd < 0) {
                        err = recvd;
                        fprintf(stderr, "Ошибка приема данных. Ошибка %d:%s\n",
                                err, LTR24_GetErrorString(err));
                    } else if (recvd != recv_wrd_cnt) {
                        fprintf(stderr, "Принято недостаточно данных. Запрашивали %d, приняли %d\n",
                                recv_wrd_cnt*ch_cnt, recvd);
                        err = LTR_ERROR_RECV_INSUFFICIENT_DATA;
                    } else {
                        err = LTR24_ProcessData(&hltr24, rbuf, data, &recvd,
                                                LTR24_PROC_FLAG_VOLT |
                                                LTR24_PROC_FLAG_CALIBR |
                                                LTR24_PROC_FLAG_AFC_COR_EX |
                                                LTR24_PROC_FLAG_ICP_PHASE_COR,
                                                ovlds);
                        if (err != LTR_OK) {
                            fprintf(stderr, "Ошибка обработки данных. Ошибка %d:%s\n",
                                    err, LTR24_GetErrorString(err));
                        } else {
                            unsigned cur_pos,ch;
                            recvd_blocks++;
                            /* выводим по первому слову на канал */
                            printf("Блок %4d:", recvd_blocks);
                            for (ch = 0, cur_pos = 0; ch < LTR24_CHANNEL_NUM; ch++) {
                                if (hltr24.ChannelMode[ch].Enable) {
                                    printf(" Канал %d = %9.6f", ch+1, data[cur_pos]);
                                    cur_pos++;
                                }
                            }
                            printf("\n");

                            fflush(stdout);
                        }
                    }

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

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

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

    return err;
}
