/*
    Данный пример демонстрирует работу с модулем 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 "ltr/include/lpw25api.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


typedef struct {
    int slot;
    int channel;
    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

extern LTR25API_DllExport(INT) LTR25_TEDSWriteData(TLTR25 *hnd, INT ch, BYTE *data, DWORD size);


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


    if (argc > 1)
        par->slot = atoi(argv[1]);
    if (argc > 2)
        par->channel = atoi(argv[2]) - 1;
    if (argc > 3)
        par->serial = argv[3];
    if (argc > 4) {
        int a[4],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) || (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;
    INT stop_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 0
    if (err == LTEDS_OK) {
        TLPW25_INFO info;
        DWORD encode_bits;
        BYTE teds_data[LPW25_TEDS_MAX_SIZE];

        LPW25_InfoInit(&info);
        LPW25_InfoStdFill(&info, LTEDS_LCARD_MODEL_ID_LPW25_U_2_230_CH1);
        info.SerialNumber = 1234567;
        info.VersionNumber = 1;

        info.CalInfo.CalDate.Day = 21;
        info.CalInfo.CalDate.Month = 11;
        info.CalInfo.CalDate.Year = 2018;
        info.CalInfo.CalPeriod = 365;
        info.CalInfo.CalCoef.InputValue = 220 * sqrt(2);
        info.CalInfo.CalCoef.Sens = info.ElectricalRange / info.PhysicalRange * 1.01;

        err = LPW25_TEDSEncode(teds_data, sizeof(teds_data), &info,  &encode_bits);

        if (err == LTEDS_OK) {
            printf("TEDS encode. used %d of %d bits\n", encode_bits, (int)sizeof(teds_data)*8);
        } else {
            printf("TEDS encode error %d: %s\n", err, LPW25_GetErrorString(err));
        }

        if (err == LTEDS_OK) {
            TLPW25_PROC_CTX procctx;
            TLPW25_INFO info_rd;
            BOOLEAN all_parsed;
            err = LPW25_TEDSDecode(teds_data, sizeof(teds_data), &procctx, &info_rd, &all_parsed);
            if (err == LTEDS_OK) {
                if (all_parsed) {
                    printf("TEDS parsed successfully\n");
                } else {
                    printf("TEDS partially parsed successfully\n");
                }

                printf("Module info:\n");
                printf("   Model:         %d\n", info_rd.ModelID);
                printf("   Version:       %d%c\n", info_rd.VersionNumber, info_rd.VersionLetter);
                printf("   SerialNumber:  %d\n", info_rd.SerialNumber);
                printf("   PhysMesurand:  %d\n", info_rd.PhysicalMeasurand);
                printf("   PhysicalMax:   %.2f\n", info_rd.PhysicalRange);
                printf("   ElectricalMax: %.3f\n", info_rd.ElectricalRange);
                if (LTEDS_DateIsValid(&info_rd.CalInfo.CalDate)) {
                    printf("Calibration info:\n");
                    printf("   Calibration date:   %d.%d.%d\n", info_rd.CalInfo.CalDate.Day,
                           info_rd.CalInfo.CalDate.Month, info_rd.CalInfo.CalDate.Year);
                    printf("   Calibration period: %d days\n", info_rd.CalInfo.CalPeriod);
                    printf("   Sens:               %f at %.2f V\n",
                           info_rd.CalInfo.CalCoef.Sens,
                           info_rd.CalInfo.CalCoef.InputValue);
                    printf("   Rout at 2.86 mA:    %.2f\n", info_rd.CalInfo.ROutCoefs.R_I_2_86);
                    printf("   Rout at 10 mA:      %.2f\n", info_rd.CalInfo.ROutCoefs.R_I_10);
                    printf("   Phase delay:        %f.3 at %.1f Hz\n",
                           info_rd.CalInfo.PhaseCoefs.PhaseShift,
                           info_rd.CalInfo.PhaseCoefs.PhaseShiftRefFreq);
                }
                printf("Proc info:\n");
                printf("   Sens:    %f\n", procctx.Sens);
                fflush(stdout);
            }
        }
    }
#endif

    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 {
            /* Выводим прочитанную информацию о модуле */
            printf("Модуль открыт успешно! Информация о модуле: \n");
            printf("  Название модуля    = %s\n", hltr25.ModuleInfo.Name);
            printf("  Серийный номер     = %s\n", hltr25.ModuleInfo.Serial);
            printf("  Версия PLD         = %d\n", hltr25.ModuleInfo.VerPLD);
            printf("  Версия ПЛИС        = %d\n", hltr25.ModuleInfo.VerFPGA);
            printf("  Ревизия платы      = %d\n", hltr25.ModuleInfo.BoardRev);
            printf("  Темп. диапазон     = %s\n", hltr25.ModuleInfo.Industrial ? "Индустриальный" : "Коммерческий");
            fflush(stdout);

            err = LTR25_CheckSupportTEDS(&hltr25);
            if (err != LTR_OK) {
                fprintf(stderr, "Ошибка проверки поддержки TEDS модулем: %s\n", LTR25_GetErrorString(err));
            } else {
                err = LTR25_SetSensorsPowerMode(&hltr25, LTR25_SENSORS_POWER_MODE_TEDS);
                if (err != LTR_OK) {
                    fprintf(stderr, "Не удалось перевести модуль в цифровой режим. Ошибка %d (%s)\n",
                            err, LTR25_GetErrorString(err));
                } else {
                    TLTR25_TEDS_NODE_INFO devinfo;
                    err = LTR25_TEDSNodeDetect(&hltr25, par.channel, &devinfo);
                    if (err != LTR_OK) {
                        fprintf(stderr, "Не удалось обнаружить узел TEDS. Ошибка %d (%s)\n",
                                err, LTR25_GetErrorString(err));
                    } else {
                        printf("Обнаружен узел TEDS. Тип 0x%02X, размер TEDS-данных: %d байт\n",
                                devinfo.DevFamilyCode, devinfo.TEDSDataSize);
                    }

                    if (err == LTEDS_OK) {
                        TLPW25_INFO info;
                        DWORD encode_bits;
                        BYTE teds_data[LPW25_TEDS_MAX_SIZE];

                        LPW25_InfoInit(&info);
                        LPW25_InfoStdFill(&info, LTEDS_LCARD_MODEL_ID_LPW25_U_2_230_CH1);
                        info.SerialNumber = 1234567;
                        info.VersionNumber = 1;

                        info.CalInfo.CalDate.Day = 21;
                        info.CalInfo.CalDate.Month = 11;
                        info.CalInfo.CalDate.Year = 2018;
                        info.CalInfo.CalPeriod = 365;
                        info.CalInfo.CalCoef.InputValue = 220 * sqrt(2);
                        info.CalInfo.CalCoef.Sens = info.ElectricalRange / info.PhysicalRange * 1.01;

                        err = LPW25_TEDSEncode(teds_data, sizeof(teds_data), 0, &info,  &encode_bits);

                        if (err == LTR_OK) {
                            printf("TEDS данные успешно закодированы. Использовано %d из %d бит\n", encode_bits, (int)sizeof(teds_data)*8);
                        } else {
                            printf("Не удалось закодировать TEDS. Ошибка %d: %s\n", err, LPW25_GetErrorString(err));
                        }

                        if (err == LTR_OK) {
                            err = LTR25_TEDSWriteData(&hltr25, par.channel, teds_data, sizeof(teds_data));
                            if (err != LTR_OK) {
                                fprintf(stderr, "Не удалось записать данные в TEDS.  Ошибка %d (%s)\n",
                                        err, LTR25_GetErrorString(err));
                            } else {
                                printf("Данные TEDS записаны успешно\n");
                            }
                        }

                        if (err == LTR_OK) {
                            BYTE teds_rd_data[LPW25_TEDS_MAX_SIZE];
                            DWORD ret_size;

                            err = LTR25_TEDSReadData(&hltr25, par.channel, teds_rd_data, sizeof(teds_rd_data), &ret_size);
                            if (err != LTR_OK) {
                                fprintf(stderr, "Не удалось записать данные в TEDS.  Ошибка %d (%s)\n",
                                        err, LTR25_GetErrorString(err));
                            } else {
                                printf("Данные TEDS считаны успешно");
                                if (memcmp(teds_data, teds_rd_data, ret_size) != 0) {
                                    fprintf(stderr, "Считанные данные не совпадают с записанными!\n");
                                } else {
                                    TLPW25_INFO info_rd;
                                    BOOLEAN all_parsed;
                                    err = LPW25_TEDSDecode(teds_rd_data, ret_size, 0, NULL, &info_rd, &all_parsed);
                                    if (err == LTEDS_OK) {
                                        if (all_parsed) {
                                            printf("TEDS parsed successfully\n");
                                        } else {
                                            printf("TEDS partially parsed successfully\n");
                                        }

                                        printf("Module info:\n");
                                        printf("   Model:         %d\n", info_rd.ModelID);
                                        printf("   Version:       %d%c\n", info_rd.VersionNumber, info_rd.VersionLetter);
                                        printf("   SerialNumber:  %d\n", info_rd.SerialNumber);
                                        printf("   PhysMesurand:  %d\n", info_rd.PhysicalMeasurand);
                                        printf("   PhysicalMax:   %.2f\n", info_rd.PhysicalRange);
                                        printf("   ElectricalMax: %.3f\n", info_rd.ElectricalRange);
                                        if (LTEDS_DateIsValid(&info_rd.CalInfo.CalDate)) {
                                            printf("Calibration info:\n");
                                            printf("   Calibration date:   %d.%d.%d\n", info_rd.CalInfo.CalDate.Day,
                                                   info_rd.CalInfo.CalDate.Month, info_rd.CalInfo.CalDate.Year);
                                            printf("   Calibration period: %d days\n", info_rd.CalInfo.CalPeriod);
                                            printf("   Sens:               %f at %.2f V\n",
                                                   info_rd.CalInfo.CalCoef.Sens,
                                                   info_rd.CalInfo.CalCoef.InputValue);
                                            printf("   Rout at 2.86 mA:    %.2f\n", info_rd.CalInfo.ROutCoefs.R_I_2_86);
                                            printf("   Rout at 10 mA:      %.2f\n", info_rd.CalInfo.ROutCoefs.R_I_10);
                                            printf("   Phase delay:        %f.3 at %.1f Hz\n",
                                                   info_rd.CalInfo.PhaseCoefs.PhaseShift,
                                                   info_rd.CalInfo.PhaseCoefs.PhaseShiftRefFreq);
                                        }
                                        fflush(stdout);
                                    }
                                }

                            }
                        }
                        //err = LTR25_TEDSReadData(hnd, par.channel, )
                    }

                    stop_err = LTR25_SetSensorsPowerMode(&hltr25, LTR25_SENSORS_POWER_MODE_ICP);
                    if (err == LTR_OK)
                        err = stop_err;
                }


            }


        }
    }
    return err;
}

