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


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;
}

/* вывод строки, соответствующий коду статуса фонового измерения */
static void f_print_meas_status(DWORD status) {
    switch (status) {
        case LTR216_MEASSTATUS_NOT_USED: printf("Не исп."); break;
        case LTR216_MEASSTATUS_OK: printf("Ок"); break;
        case LTR216_MEASSTATUS_NOT_INIT: printf("Иниц."); break;
        case LTR216_MEASSTATUS_ADC_OVERRANGE: printf("Зашкал"); break;
        case LTR216_MEASSTATUS_SHORT: printf("КЗ"); break;
        case LTR216_MEASSTATUS_OPEN: printf("Обр."); break;
        case LTR216_MEASSTATUS_BAD_VALUE_RANGE: printf("Вне диап."); break;
        case LTR216_MEASSTATUS_CANT_CALC: printf("Не выч."); break;
        default: printf("%d", status); break;
   }
}

/* вывод значения измерения, если оно действительно, или статуса в противном случае */
static void f_print_meas_valstate(const TLTR216_MEASSTATE *meas) {
    if (meas->ValueValid) {
        printf("%.3f", meas->Value);
    } else {
        f_print_meas_status(meas->Status);
    }
}

/* вывод значения и состояния Uref (при четырехпроводной схеме вместе со значением
 * UrefR) со статусами */
static void f_print_uref(const TLTR216_DATA_STATUS *mstatus) {
    printf("Uref = ");
    f_print_meas_valstate(&mstatus->Uref);
    if (mstatus->UrefR.Status != LTR216_MEASSTATUS_NOT_USED) {
        printf(", UrefR = ");
        f_print_meas_valstate(&mstatus->UrefR);
    }
}


int main(int argc, char** argv) {
    INT err = LTR_OK;
    TLTR216 hltr216;
    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) {
        LTR216_Init(&hltr216);

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

            /* Вывод информации о модуле */
            printf("Модуль открыт успешно! Информация о модуле: \n");
            printf("  Название модуля    = %s\n", hltr216.ModuleInfo.Name);
            printf("  Серийный номер     = %s\n", hltr216.ModuleInfo.Serial);
            printf("  Версия ПЛИС        = %d\n", hltr216.ModuleInfo.VerFPGA);
            fflush(stdout);

            /* Настройка модуля */
            /* многоканальный режим */
            hltr216.Cfg.AdcSwMode = LTR216_ADC_SWMODE_MULTICH_SYNC;
            /* частота АЦП - 10 КГц */
            LTR216_FillSyncFreqDiv(&hltr216, 10000, NULL);
            /* Время на коммутация - 20 мкс (типичная длина 7-10 м) */
            hltr216.Cfg.AdcMinSwTimeUs = 20;
            /* Заполняем параметры фильтра, исходя из частоты АЦП и времени на коммутацию */
            LTR216_FillFilterParams(&hltr216, 0, NULL, NULL);

            /* Устанавливаем количество опрашиваемых каналов */
            hltr216.Cfg.LChCnt = LCH_CNT;
            for (ch_idx = 0; ch_idx < hltr216.Cfg.LChCnt; ch_idx++) {
                hltr216.Cfg.LChTbl[ch_idx].PhyChannel = ch_idx;
                hltr216.Cfg.LChTbl[ch_idx].Range = LTR216_RANGE_35;
            }

            /* выбор фоновых измерений - включаем все */
            hltr216.Cfg.BgMeas = LTR216_BG_MEASGROUP_ALL;

            /* Ток питания датчиков - 25 мА */
            LTR216_FillISrcCode(&hltr216, 25, NULL);
            /* 4-х проводная схема подключения опорного датчика */
            hltr216.Cfg.Ch16ForUref = FALSE;
            /* без тарировки */
            hltr216.Cfg.TareEnabled = FALSE;

            
            

            /* Записываем настройки в модуль */
            err = LTR216_SetADC(&hltr216);
            if (err != LTR_OK) {
                fprintf(stderr, "Не удалось установить код потенциометра. Ошибка %d (%s)\n",
                        err, LTR216_GetErrorString(err));
            } else {
                printf("Настройки АЦП установлены успешно.\n Частота АЦП = %.2f Гц\n Частота на канал %.2f\n Размер кадра: %d\n",
                       hltr216.State.AdcFreq, hltr216.State.FrameFreq, hltr216.State.FrameWordsCount);
                fflush(stdout);
            }

            /* Выполняем измерение начальных параметров */
            if (err == LTR_OK) {
                TLTR216_DATA_STATUS status;
                LTR216_DataStatusInit(&status);

                err = LTR216_InitMeasParams(&hltr216, LTR216_INIT_MEAS_ALL,
                                            LTR216_BG_MEASGROUP_UREF_CHECK, &status);
                if (err != LTR_OK) {
                    fprintf(stderr, "Не удалось выполнить измерение начальных параметров. Ошибка %d (%s)\n",
                            err, LTR216_GetErrorString(err));
                } else {
                    printf("Измерение начальных параметров выполнено успешно\n");
                }

                /* Вне зависимости от успеха начальных измерений, выводим
                 * полученное значение Uref */
                f_print_uref(&status);
                printf("\n");
                fflush(stdout);

                if (err==LTR_OK) {
                    DWORD recvd_blocks=0;
                    /* количество отсчетов данных, которые хотим принимать за блок */
                    INT recv_data_cnt = RECV_BLOCK_FRAMES_CNT*hltr216.Cfg.LChCnt;
                    /* количество слов, соответствующее этому количеству данных,
                     * исходя из размера кадра */
                    INT   recv_wrd_cnt = RECV_BLOCK_FRAMES_CNT*hltr216.State.FrameWordsCount;
                    /* выделяем соответствующие массивы */
                    DWORD  *rbuf = (DWORD*)malloc(recv_wrd_cnt*sizeof(rbuf[0]));
                    double *data = (double *)malloc(recv_data_cnt*sizeof(data[0]));
                    TLTR216_DATA_CH_STATUS *chStatusList = (TLTR216_DATA_CH_STATUS*)
                            malloc(hltr216.Cfg.LChCnt * sizeof(chStatusList[0]));


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

                    if (err==LTR_OK) {
                        LTR216_DataChannelsStatusInit(chStatusList, hltr216.Cfg.LChCnt);

                        /* Запуск сбора данных */
                        err=LTR216_Start(&hltr216);
                        if (err!=LTR_OK) {
                            fprintf(stderr, "Не удалось запустить сбор данных! Ошибка %d (%s)\n",
                                    err, LTR216_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_FRAMES_CNT/
                                                         hltr216.State.FrameFreq + 1);
                        /* Прием данных от модуля.  */
                        recvd = LTR216_Recv(&hltr216, rbuf, NULL, recv_wrd_cnt, tout);

                        /* Значение меньше нуля соответствуют коду ошибки */
                        if (recvd < 0) {
                            err = recvd;
                            fprintf(stderr, "Ошибка приема данных. Ошибка %d:%s\n",
                                    err, LTR216_GetErrorString(err));
                        } else if (recvd != recv_wrd_cnt) {
                            fprintf(stderr, "Принято недостаточно данных. Запрашивали %d, приняли %d\n",
                                    recv_wrd_cnt, recvd);
                            err = LTR_ERROR_RECV_INSUFFICIENT_DATA;
                        } else {
                            err = LTR216_ProcessData(&hltr216, rbuf, data, &recvd,
                                                     LTR216_PROC_FLAG_MEAS_UNBALANCE,
                                                     &status, chStatusList);
                            if (err != LTR_OK) {
                                fprintf(stderr, "Ошибка обработки данных. Ошибка %d:%s\n",
                                        err, LTR216_GetErrorString(err));
                            } else {
                                DWORD ch;

                                recvd_blocks++;
                                /* выводим параметры блока */
                                printf("Блок %d (размер = %d, ", recvd_blocks, recvd);
                                /* выводим состояние uref для справки */
                                f_print_uref(&status);
                                printf("):");

                                /* выводим для примера по одному значению на
                                 * разрешенный канал */
                                for (ch=0; (ch < hltr216.Cfg.LChCnt) ; ch++) {
                                    /* Проверяем статус результирующего
                                     * измерения, чтобы не выводить явно не действительное
                                     * значение. При этом более подробный
                                     * анализ состояния всех флагов контроля по
                                     * всем фоновым измерениям в данном примере не
                                     * приводятся для краткости */
                                    if (chStatusList[ch].Ures.ValueValid) {
                                        printf("%8.2f", 1000.*data[ch]);
                                    } else {
                                        f_print_meas_status(chStatusList[ch].Ures.Status);
                                    }

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

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

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

                    free(rbuf);
                    free(data);
                    free(chStatusList);

                }
            }


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

    return err;
}
