/* Пример демонстрирует вывод в потоковом режиме без явного контроля заполненности
   буфера модуля  */
#include "ltr/include/ltr35api.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




#define WRITE_BLOCK_SIZE   4*4096

#define CH1_SIN_POINTS     128
#define CH2_SIN_POINGS     512

#define FORMAT_24       TRUE
#define EN_ARITH_CHS    TRUE
#define EN_CH_CNT       1


//#define DIGOUT_ENABLE






#ifndef M_PI
    #define M_PI 3.1415926535897932384626433832795
#endif


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




static int f_send_data(TLTR35 *phltr35) {
    INT err = 0;
    static DWORD   wrds[2*(LTR35_DAC_CHANNEL_CNT+1)*WRITE_BLOCK_SIZE];
    static double  sig[LTR35_DAC_CHANNEL_CNT*WRITE_BLOCK_SIZE];
#ifdef DIGOUT_ENABLE
    static DWORD   douts[WRITE_BLOCK_SIZE];
#endif

    INT ch;
    DWORD i;
    static  unsigned ch_cntr = 0;
    DWORD snd_size;
    DWORD dac_size = 0;
#ifdef DIGOUT_ENABLE
    DWORD dout_size = 0;
#endif


    for (i=0; i < WRITE_BLOCK_SIZE; i++) {
        for (ch=0; ch < phltr35->State.SDRAMChCnt; ch++) {
            sig[phltr35->State.SDRAMChCnt*i+ch] = 5. * cos(2.*M_PI*ch_cntr*(ch+1)/(CH1_SIN_POINTS));
            dac_size++;
        }
#ifdef DIGOUT_ENABLE
        douts[dout_size++] = ch_cntr & 0xFFFF;
#endif
        ch_cntr++;
    }

    snd_size = sizeof(wrds)/sizeof(wrds[0]);
    err = LTR35_PrepareData(phltr35, sig, &dac_size,
#ifdef DIGOUT_ENABLE
                                   douts, &dout_size,
#else
                                   NULL, NULL,
#endif
                                   LTR35_PREP_FLAGS_VOLT, wrds, &snd_size);
    if (err!=LTR_OK) {
        fprintf(stderr, "Ошибка подготовки данных (%d) : %s!\n", err, LTR35_GetErrorString(err));
    }

    if (!err) {
        INT sent_size = 0;

        /* передаем массив, либо пока не передадим все данные, либо пока не
         * произойдет ошибка, либо не будет признака выхода */
        while (!f_out && !err && (sent_size < (INT)snd_size)) {
            INT cur_sent = LTR35_Send(phltr35, &wrds[sent_size], snd_size-sent_size, 500);
            if (cur_sent < 0) {
                err = cur_sent;
                fprintf(stderr, "Ошибка передачи данных (%d) : %s!\n", err, LTR35_GetErrorString(err));
            } else {
                sent_size += cur_sent;
            }
#ifdef _WIN32
            /* проверка нажатия клавиши для выхода */
            if (err==LTR_OK) {
                if (_kbhit())
                    f_out = 1;
            }
#endif
        }
    }
    return err;
}


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

#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
    /* для вывода русских букв в консоле... */
    setlocale(LC_ALL, "");
#endif


    err = f_get_params(argc, argv, &par);

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

            printf("Модуль открыт успешно!\n");
            printf("  Название модуля    = %s\n", hltr35.ModuleInfo.Name);
            printf("  Серийный номер     = %s\n", hltr35.ModuleInfo.Serial);
            printf("  Модификация        = LTR35-%d\n", hltr35.ModuleInfo.Modification);
            printf("  Версия PLD         = %d\n", hltr35.ModuleInfo.VerPLD);
            printf("  Версия ПЛИС        = %d\n", hltr35.ModuleInfo.VerFPGA);
            printf("  Выходов ЦАП        = %d\n", hltr35.ModuleInfo.DacChCnt);
            printf("  Цифровых выходов   = %d\n", hltr35.ModuleInfo.DoutLineCnt);

            fflush(stdout);

            /* выбираем частоту ЦАП - 192 КГц */
            err = LTR35_FillOutFreq(&hltr35.Cfg, 192*1000, &fnd_freq);
            if (err==LTR_OK) {
                BYTE ch_num;
                hltr35.Cfg.OutDataFmt = LTR35_OUTDATA_FORMAT_20;
                hltr35.Cfg.OutMode = LTR35_OUT_MODE_STREAM;
                /* первый канал разрешаем потоковым */
                for (ch_num = 0; ch_num < hltr35.ModuleInfo.DacChCnt; ch_num++) {
                    hltr35.Cfg.Ch[ch_num].Enabled = TRUE;
                    if (ch_num < EN_CH_CNT) {
                        hltr35.Cfg.Ch[ch_num].Source = LTR35_CH_SRC_SDRAM;
                    } else {
                        hltr35.Cfg.Ch[ch_num].Source = LTR35_CH_SRC_COS1;
                        hltr35.Cfg.Ch[ch_num].Enabled = TRUE;
                        hltr35.Cfg.Ch[ch_num].ArithAmp = 5.;
                    }
                }
                LTR35_FillArithPhaseDegree(&hltr35.Cfg.ArithSrc[0].Phase, 0, NULL);
                LTR35_FillArithPhaseDegree(&hltr35.Cfg.ArithSrc[0].Delta, 2*M_PI/(CH2_SIN_POINGS), NULL);

                /* настраиваем параметры модуля */
                err = LTR35_Configure(&hltr35);
                if (err != LTR_OK) {
                    fprintf(stderr, "Не удалось настроить модуль. Ошибка %d (%s)\n",
                            err, LTR35_GetErrorString(err));
                }
            }

            /* Чтобы данные уже были при старте, подкачиваем один блок данных */
            if (err==LTR_OK) {                
                err = f_send_data(&hltr35);
            }

            if (err==LTR_OK) {
                err = LTR35_StreamStart(&hltr35, 0);
                if (err!=LTR_OK) {
                    fprintf(stderr, "Ошибка запуска потокового режима (%d) : %s!\n",
                            err, LTR35_GetErrorString(err));
                } else {
                    printf("Запущен потоковый вывод!\n"); fflush(stdout);
                }
            }

            while ((err==LTR_OK) && !f_out) {
                err = f_send_data(&hltr35);
            }

            /* останавливаем выдачу данных, если она была запущена */
            if (hltr35.State.Run) {
                INT stop_err = LTR35_StopWithTout(&hltr35, 0, 30000);
                if (stop_err!=LTR_OK) {
                    fprintf(stderr, "Не удалось остановить потоковый вывод. Ошибка %d:%s\n",
                            stop_err, LTR35_GetErrorString(stop_err));
                    if (err==LTR_OK)
                        err = stop_err;
                } else {
                    printf("Потоковый вывод остановлен успешно.\n");
                }
            }


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

    return err;
}
