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

    Данный пример использует факт, что в бибилотеках 1.30.8 и выше можно передать
    пустую строку либо NULL в качестве имени bio-файла для загурузки и будет взят
    файл из ресурса библиотеки (Windows) или по стандартному пути (Linux).


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

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

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

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

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

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

static const char* f_module_type_str[] = {
    "LTR212/LTR212-M3",
    "LTR212-M1",
    "LTR212-M2"
};

/* Разбор параметров командной строки. Если указано меньше, то используются
 * значения по умолчанию:
 * 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;
}



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


int main(int argc, char** argv) {
    INT err = LTR_OK;
    TLTR212 hltr212;
    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) {
        LTR212_Init(&hltr212);
        /* Устанавливаем соединение с модулем. Используем встроенную в библиотеку
            прошивку DSP*/
        err = LTR212_Open(&hltr212, par.addr, LTRD_PORT_DEFAULT, par.serial, par.slot, NULL);
        if (err != LTR_OK) {
            fprintf(stderr, "Не удалось установить связь с модулем. Ошибка %d (%s)\n",
                    err, LTR212_GetErrorString(err));
        } else {
            /* после открытия модуля доступна информация, кроме версии прошивки ПЛИС */
            printf("Модуль открыт успешно!\n");
            printf("  Название модуля    = %s\n", hltr212.ModuleInfo.Name);
            printf("  Серийный номер     = %s\n", hltr212.ModuleInfo.Serial);
            printf("  Тип модуля         = %s\n", hltr212.ModuleInfo.Type <
                   sizeof(f_module_type_str)/sizeof(f_module_type_str[0]) ?
                        f_module_type_str[hltr212.ModuleInfo.Type] : "Неизвествный тип");
            printf("  Версия прошивки    = %s\n", hltr212.ModuleInfo.BiosVersion);
            printf("  Дата прошивки    = %s\n",   hltr212.ModuleInfo.BiosDate);
            fflush(stdout);

            /* Проверка контрольной суммы EEPROM */
            err = LTR212_TestEEPROM(&hltr212);
            if (err!=LTR_OK) {
                fprintf(stderr, "Ошибка проверки контрольной суммы EEPROM: %d (%s)\n",
                        err, LTR212_GetErrorString(err));
            }

            if (err==LTR_OK) {
                INT ch;
                /* ------------- Установка настроек модуля --------------- */

                /* один из 3-х режимов измерения */
                hltr212.AcqMode = LTR212_FOUR_CHANNELS_WITH_HIGH_RESOLUTION;
                /* пользовательская калибровка не используется */
                hltr212.UseClb = FALSE;
                 /* используется заводская калибровка */
                hltr212.UseFabricClb = TRUE;
                /* сбор по 4-м каналам */
                hltr212.LChQnt = 4;
                /* используем опорное напряжение 5 В */
                hltr212.REF = LTR212_REF_5V;
                /* без знакопеременного измерения */
                hltr212.AC = FALSE;

                /* задание настроек каналов (тут используются подряж с диапазоном +/-80 мВ) */
                for (ch=0; ch < hltr212.LChQnt; ch++) {
                    hltr212.LChTbl[ch]=LTR212_CreateLChannel(ch+1, LTR212_SCALE_B_80);
                }

                err = LTR212_SetADC(&hltr212);
                if (err!=LTR_OK) {
                    fprintf(stderr, "Не удалось установить настройки АЦП: Ошибка %d (%s)\n",
                            err, LTR212_GetErrorString(err));
                }
            }

            if (err==LTR_OK) {
                DWORD *wrds=NULL;
                double *data=NULL;
                INT recv_data_cnt = hltr212.LChQnt * RECV_CH_SIZE;
                //каждый отсчет состоит из 2-х слов
                INT recv_wrds_cnt = 2*recv_data_cnt;
                DWORD tout;
                INT Run = 0;
                INT frame_num = 0;

                tout = LTR212_CalcTimeOut(&hltr212, RECV_CH_SIZE);

                /* выделяем память под принимаемые и обработанные слова */
                wrds = (DWORD*)malloc(recv_wrds_cnt*sizeof(wrds[0]));
                data = (double*)malloc(recv_data_cnt*sizeof(data[0]));

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

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

                while (!f_out && (err==LTR_OK)) {
                    INT recv_cnt=0;
                    /* Прием данных за рассчитанный таймаут */
                    recv_cnt = LTR212_Recv(&hltr212, wrds, NULL, recv_wrds_cnt, tout);
                    /* Значение меньше нуля соответствуют коду ошибки */
                    if (recv_cnt<0) {
                        err = recv_cnt;
                        fprintf(stderr, "Ошибка при приеме кадра: Ошибка %d (%s)\n",
                                err, LTR212_GetErrorString(err));
                    }
                    else if (recv_cnt < recv_wrds_cnt) {
                        fprintf(stderr, "Принято меньше слов, чем было в кадре! запрашивали %d, приняли %d\n",
                                recv_wrds_cnt, recv_cnt);
                        err = LTR_ERROR_RECV_INSUFFICIENT_DATA;
                    } else {

                        /* переводим данные в Вольты */
                        err = LTR212_ProcessData(&hltr212, wrds, data, (DWORD*)&recv_cnt, TRUE);
                        if (err!=LTR_OK) {
                            printf("Ошибка обработки данных! Ошибка %d (%s)\n",
                                   err, LTR212_GetErrorString(err));
                        } else {
                            int i;
                            frame_num++;
                            /* на экран выводим первые два отсчета,а
                             * полностью кадр сохраняем в файл */
                            printf("Успешно приняли кадр %d: первые отсчеты: ", frame_num);
                            for (i=0; (i < hltr212.LChQnt) && (i < recv_cnt); i++)
                                printf(" %.2f мВ", data[i]*1000);
                            printf("\n");
                            fflush(stdout);
                        }

                    }

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

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

                free(wrds);
                free(data);
            }
        }
        LTR212_Close(&hltr212);
    }


    return err;
}
