/*
    Данный пример демонстрирует работу с модулем LTR41 в режиме потокового ввода.
    По умолчанию идет работа с первым слотом первого крейта, но это можно изменить
        через параметры командной строки:
    ltr41_stream_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))
    на нужный в зависимаости от пути установки библиотек (ltr41api.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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <locale.h>
#include <conio.h>
#else
#include <signal.h>
#include <unistd.h>
#endif

#include "ltr/include/ltr41api.h"

/* частот сбора (в Гц) */
#define STREAM_FREQ     100000
/* размер блока для приема */
#define RECV_BLOCK_SIZE 10000


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

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

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

/* Разбор параметров командной строки. Если указано меньше, то используются
 * значения по умолчанию:
 * 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[]) {
    INT err = LTR_OK;
    TLTR41 hltr41;
    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) {
        LTR41_Init(&hltr41);
        /* установление соединения с модулем */
        err = LTR41_Open(&hltr41, par.addr, LTRD_PORT_DEFAULT, par.serial, par.slot);
        if (err != LTR_OK) {
            fprintf(stderr, "Не удалось установить связь с модулем. Ошибка %d (%s)\n",
                    err, LTR41_GetErrorString(err));
        } else {
            /* вывод информации о модуле */
            printf("Модуль открыт успешно:\n");
            printf("  Название:        %s\n", hltr41.ModuleInfo.Name);
            printf("  Серийный номер:  %s\n", hltr41.ModuleInfo.Serial);
            printf("  Версия прошивки: %s\n", hltr41.ModuleInfo.FirmwareVersion);
            printf("  Дата прошивки:   %s\n", hltr41.ModuleInfo.FirmwareDate);

            /* настройка частоты потокового ввода */
            hltr41.StreamReadRate = STREAM_FREQ;
            err = LTR41_Config(&hltr41);
            if (err != LTR_OK) {
                fprintf(stderr, "Не удалось сконфигурировать модуль. Ошибка %d (%s)\n",
                        err, LTR41_GetErrorString(err));
            }
        }

        if (err == LTR_OK) {
            /* таймаут время сбора блока + 4c запас*/
            DWORD tout = 4000 + (DWORD)(RECV_BLOCK_SIZE*1000/hltr41.StreamReadRate);
            /* массив необработанных слов в спец. формате LTR */
            DWORD *rbuf = (DWORD*)malloc(RECV_BLOCK_SIZE*sizeof(rbuf[0]));
            /* массив обработанных слов (значения входов) */
            WORD *wrds = (WORD*)malloc(RECV_BLOCK_SIZE*sizeof(wrds[0]));
            /* массив принятых меток */
            DWORD *tmark = (DWORD*)malloc(RECV_BLOCK_SIZE*sizeof(tmark[0]));
            DWORD block = 0;
            WORD prev_word = 0;
            DWORD prev_tmark = 0;

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

            if (err == LTR_OK) {
                /* запуск потокового ввода */
                err = LTR41_StartStreamRead(&hltr41);
                if (err != LTR_OK) {
                    fprintf(stderr, "Ошибка запуска потокового ввода. Ошибка %d (%s)\n",
                            err, LTR41_GetErrorString(err));
                } else {
                    INT stop_err;
                    printf("Потоковый ввод запущен. Для останова нажмите %s\n",
#ifdef _WIN32
                           "любую клавишу"
#else
                           "CTRL+C"
#endif
                           );
                    fflush(stdout);

                    printf("    Блок, позиция: Биты 16 - 1                     Старт  Секунда\n");

                    while ((err == LTR_OK) && !f_out) {
                        INT read_size = LTR41_Recv(&hltr41, rbuf, tmark, RECV_BLOCK_SIZE, tout);
                        if (read_size < 0) {
                            /* значение меньше 0 - ошибка */
                            err = read_size;
                            fprintf(stderr, "Ошибка приема данных. Ошибка %d (%s)\n",
                                    err, LTR41_GetErrorString(err));
                        } else if (read_size != RECV_BLOCK_SIZE) {
                            /* т.к. мы задали достаточный таймаут с запасом, то
                             * приход меньшего числа слов (чем запрашивали)
                             * считаем ошибкой */
                            err = LTR_ERROR_RECV_INSUFFICIENT_DATA;
                            fprintf(stderr, "Принято недостаточно данных. Запрашивали %d, приняли %d\n",
                                    RECV_BLOCK_SIZE, read_size);
                        } else {
                            DWORD proc_size = read_size;
                            err = LTR41_ProcessData(&hltr41, rbuf, wrds, &proc_size);
                            if (err != LTR_OK) {
                                fprintf(stderr, "Ошибка обработки данных. Ошибка %d (%s)\n",
                                        err, LTR41_GetErrorString(err));
                            } else {
                                DWORD i;
                                for (i=0; i < proc_size; i++) {
                                    int out = 0;
                                    /* выводим на экран значения либо при
                                     * изменении самого значения, либо при изменении
                                     * значения меток */
                                    if (prev_tmark != tmark[i]) {
                                        prev_tmark = tmark[i];
                                        out = 1;
                                    }
                                    if (prev_word != wrds[i]) {
                                        prev_word = wrds[i];
                                        out = 1;
                                    }

                                    if (out) {
                                        WORD sec = prev_tmark & 0xFFFF;
                                        WORD start = (prev_tmark >> 16) & 0xFFFF;
                                        WORD msk;

                                        printf("%8d, %7d: ", block, i);
                                        for (msk = 0x8000; msk != 0; msk>>=1)
                                            printf("%d ", prev_word & msk ? 1 : 0);
                                        printf("%5d    %5d\n", start, sec);
                                    }
                                }

                                block++;
                            }
                        }

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

                    /* останов потокового ввода */
                    stop_err = LTR41_StopStreamRead(&hltr41);
                    if (stop_err != LTR_OK) {
                        fprintf(stderr, "Ошибка останова потокового ввода. Ошибка %d (%s)\n",
                                stop_err, LTR41_GetErrorString(stop_err));
                        if (err == LTR_OK)
                            err = stop_err;
                    } else {
                        printf("Потоковый ввод остановлен успешно!\n");
                    }
                }
            }


            free(rbuf);
            free(wrds);
            free(tmark);
        }

        if (LTR41_IsOpened(&hltr41) == LTR_OK)
            LTR41_Close(&hltr41);
    }


    return err;
}
