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

    Пример принимает данные от двух каналов в режиме периодического покадрового сбора,
    отображает на экране первые 2 слова кадра, а полный кадр сохраняется в
    файл Frame<номер кадра>.txt (по файлу на каждый кадр) в текстовом виде.

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

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

    Также пример на ОС Windows предполает наличие файла прошивки в директории запуска
    (можно переопределить через LTR210_FPGA_FIRM_FILE).

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

#include "getopt.h"

#ifdef _WIN32
    #include <locale.h>
#endif



#define OPT_HELP       'h'
#define OPT_SWITCH_1   0x1000
#define OPT_SWITCH_2   0x1001
#define OPT_SWITCH_3   0x1002
#define OPT_SWITCH_4   0x1003
#define OPT_GAIN_1     0x1004
#define OPT_GAIN_2     0x1005
#define OPT_DDS_DIV    0x1006
#define OPT_DDS_FREQ   0x1007
#define OPT_DDS_AMP    0x1008
#define OPT_DDS_RST    0x1009


#define ERR_INVALID_USAGE           -1
#define ERR_MODULE_NOT_FOUND        -2


static const struct option f_long_opt[] = {
    {"help",         no_argument,       0, OPT_HELP},
    {"sw1",          required_argument, 0, OPT_SWITCH_1},
    {"sw2",          required_argument, 0, OPT_SWITCH_2},
    {"sw3",          required_argument, 0, OPT_SWITCH_3},
    {"sw4",          required_argument, 0, OPT_SWITCH_4},
    {"gain1",        required_argument, 0, OPT_GAIN_1},
    {"gain2",        required_argument, 0, OPT_GAIN_2},
    {"div",          required_argument, 0, OPT_DDS_DIV},
    {"freq",         required_argument, 0, OPT_DDS_FREQ},
    {"amp",          required_argument, 0, OPT_DDS_AMP},
    {"dds-reset",    no_argument,       0, OPT_DDS_RST},
    {0,0,0,0}
};

static const char* f_opt_str = "h";

static const double f_gain_tbl[] = {4., 2., 1.};
static const double f_div_tbl[] = {1., 0.75, 0.5, 0.25};
static const char*  f_sw_names[] = {"Zero", "DDS", "DAC", "Z"};


static const char* f_usage_descr = \
"\nUsage: ltrt10 [OPTIONS]\n\n" \
" ltrt10 finds first LTRT10 module and sets specified settings.\n"\
"   All settings is optional and can be specified with options (see below).\n";



static const char* f_options_descr =
"Options:\n" \
"     --amp=val         - Specify amplitude of DDS signal in Volts.\n"
"                         You can use this option instead of --gain1, --gain2\n"
"                           and --div options\n"
"     --dds-reset       - Leave DDS in reset state\n"
"     --div=val         - Specify DDS signal devider:\n"
"                          - 0, 1.0 \n"\
"                          - 1, 0.75 \n"\
"                          - 2, 0.5 \n"\
"                          - 3, 0.25 \n"\
"     --freq=val        - Specify DDS sinus frequency value in Hz\n"
"     --gain1=val       - Specify gain coef. for 1st stage DDS signal amplifier:\n"\
"                          - 0, 7.0 \n"\
"                          - 1, 3.5 \n"\
"                          - 2, 2.3 \n"\
"     --gain2=val       - Specify gain coef. for second stage DDS signal\n"\
"                           amplifier.\n"\
" -h, --help            - Print this help and exit\n"\
"     --sw1=val         - Specify switch position for channel 1 (A):\n"\
"                         Valid values:\n"\
"                          - 0, zero\n"\
"                          - 1, DDS - signal from DDS\n"\
"                          - 2, DAC - signal from LTR35\n"\
"                          - 3, Z   - high-impedance state\n"\
"     --sw2=val         - Specify switch position for channel 2 (B):\n"\
"     --sw3=val         - Specify switch position for channel 3 (C):\n"\
"     --sw4=val         - Specify switch position for channel 4 (D):\n"\
;


static void  f_print_usage(void) {
    fprintf(stdout, "%s", f_usage_descr);
    fprintf(stdout,"\n");
    fprintf(stdout, "%s", f_options_descr);
}


static int f_stricmp (const char *p1, const char *p2) {
    unsigned char *s1 = (unsigned char *) p1;
    unsigned char *s2 = (unsigned char *) p2;
    unsigned char c1, c2;

    do {
        c1 = (unsigned char) toupper((int)*s1++);
        c2 = (unsigned char) toupper((int)*s2++);
    }
    while ((c1 == c2) && (c1 != '\0'));

    return c1 - c2;
}

static int f_get_slot(int *slot, char *serial) {
    TLTR srv;
    int fnd=0;
    INT err = LTR_OK;
    LTR_Init(&srv);
    err=LTR_OpenSvcControl(&srv, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT);
    if (err != LTR_OK) {
        fprintf(stderr, "Не удалось установить связь с LTR-сервером. Ошибка %d: %s!\n",
                err, LTR_GetErrorString(err));
    } else {
        char serial_list[LTR_CRATES_MAX][LTR_CRATE_SERIAL_SIZE];
        err=LTR_GetCrates(&srv, (BYTE*)&serial_list[0][0]);
        LTR_Close(&srv);
        if (err != LTR_OK) {
            fprintf(stderr, "Не удалось получить список крейтов. Ошибка %d: %s!\n",
                    err, LTR_GetErrorString(err));
        } else {
            int crate_ind;
            for (crate_ind = 0 ; (crate_ind < LTR_CRATES_MAX) && !fnd; crate_ind++) {
                if (serial_list[crate_ind][0] != '\0') {
                    TLTR crate;
                    INT crate_err = LTR_OK;
                    LTR_Init(&crate);
                    crate_err = LTR_OpenCrate(&crate, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT,
                                              LTR_CRATE_IFACE_UNKNOWN, serial_list[crate_ind]);
                    if (crate_err == LTR_OK) {
                        WORD mids[LTR_MODULES_PER_CRATE_MAX];
                        crate_err = LTR_GetCrateModules(&crate, mids);
                        if (crate_err == LTR_OK) {
                            int module_ind;
                            for (module_ind = 0; (module_ind < LTR_MODULES_PER_CRATE_MAX) && !fnd; module_ind++) {
                                if (mids[module_ind] == LTR_MID_LTR01) {
                                    WORD subid;
                                    crate_err = LTR01_GetSubID(LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT,
                                                               crate.csn, LTR_CC_CHNUM_MODULE1 + module_ind,
                                                               &subid, NULL);
                                    if ((crate_err == LTR_OK) && (subid == LTR01_SUBID_LTRT10)) {
                                        fnd = 1;
                                        strcpy(serial, crate.csn);
                                        *slot = module_ind + LTR_CC_CHNUM_MODULE1;
                                    }
                                }
                            }
                        }
                        LTR_Close(&crate);
                    }
                }
            }
        }
    }

    if (err == LTR_OK) {
        if (!fnd) {
            fprintf(stderr, "Ни одного модуля LTRT10 не было найдено!\n");
            err = ERR_MODULE_NOT_FOUND;
        } else {
            printf("Найден модуль LTRT10: крейт %s, слот %d\n", serial, *slot);
        }
    }
    return err;
}




static int f_parse_params(int argc, char** argv, TLTRT10_CONFIG *cfg, int *out, int *rst) {
    int err=0;
    int opt = 0;

    memset(cfg, 0, sizeof(TLTRT10_CONFIG));

    /************************** разбор опций *****************************/
    while ((opt!=-1) && !err && !*out) {
        opt = getopt_long(argc, argv, f_opt_str,
                          f_long_opt, 0);
        switch (opt) {
            case -1:
                break;
            case OPT_HELP:
                f_print_usage();
                *out = 1;
                break;
            case OPT_SWITCH_1:
            case OPT_SWITCH_2:
            case OPT_SWITCH_3:
            case OPT_SWITCH_4: {
                    long val;
                    char* endptr=0;
                    if (!f_stricmp(optarg, "ZERO")) {
                        val = LTRT10_SWITCH_POS_ZERO;
                    } else if (!f_stricmp(optarg, "DDS")) {
                        val = LTRT10_SWITCH_POS_DDS;
                    } else if (!f_stricmp(optarg, "DAC") || !f_stricmp(optarg, "LTR35")) {
                        val = LTRT10_SWITCH_POS_DAC;
                    } else if (!f_stricmp(optarg, "Z")) {
                        val = LTRT10_SWITCH_POS_ZSTATE;
                    } else {
                        val = strtol(optarg, &endptr, 10);
                        if (!endptr || (endptr==optarg) || *endptr || (val > LTRT10_SWITCH_POS_ZSTATE)) {
                            fprintf(stderr, "Error: Invalid switch position specified!\n");
                            err = ERR_INVALID_USAGE;
                        }
                    }

                    if (!err) {
                        cfg->SwitchPos[opt-OPT_SWITCH_1] = (BYTE)val;
                    }
                }
                break;
            case OPT_GAIN_1:
            case OPT_GAIN_2: {
                    long val;
                    char* endptr=0;
                    if (!f_stricmp(optarg, "4.0") || !f_stricmp(optarg, "4")) {
                        val = LTRT10_DDS_GAIN_4;
                    } else if (!f_stricmp(optarg, "2.0")) {
                        val = LTRT10_DDS_GAIN_2;
                    } else if (!f_stricmp(optarg, "1.0")) {
                        val = LTRT10_DDS_GAIN_1;
                    } else {
                        val = strtol(optarg, &endptr, 10);
                        if (!endptr || (endptr==optarg) || *endptr || (val > LTRT10_DDS_GAIN_1)) {
                            fprintf(stderr, "Error: Invalid gain value specified!\n");
                            err = ERR_INVALID_USAGE;
                        }
                    }

                    if (!err) {
                        cfg->DDS.Gain[opt-OPT_GAIN_1] = (BYTE)val;
                    }
                }
                break;
            case OPT_DDS_DIV: {
                    long val;
                    char* endptr=0;
                    if (!f_stricmp(optarg, "1.0")) {
                        val = LTRT10_DDS_DIV_1;
                    } else if (!f_stricmp(optarg, "0.75")) {
                        val = LTRT10_DDS_DIV_0_75;
                    } else if (!f_stricmp(optarg, "0.5")) {
                        val = LTRT10_DDS_DIV_0_5;
                    } else if (!f_stricmp(optarg, "0.25")) {
                        val = LTRT10_DDS_DIV_0_25;
                    } else {
                        val = strtol(optarg, &endptr, 10);
                        if (!endptr || (endptr==optarg) || *endptr || (val > LTRT10_DDS_DIV_0_25)) {
                            fprintf(stderr, "Error: Invalid divider value specified!\n");
                            err = ERR_INVALID_USAGE;
                        }
                    }

                    if (!err) {
                        cfg->DDS.AmpDiv = (BYTE)val;
                    }
                }
                break;
            case OPT_DDS_FREQ: {
                    double val;
                    char* endptr=0;

                    val = strtod(optarg, &endptr);
                    if (!endptr || (endptr==optarg) || *endptr) {
                        fprintf(stderr, "Error: Invalid freq value specified!\n");
                        err = ERR_INVALID_USAGE;
                    } else {
                        LTRT10_FillDDSFreq(cfg, val, NULL);
                    }
                }
                break;
            case OPT_DDS_AMP: {
                    double val;
                    char *endptr=0;

                    val = strtod(optarg, &endptr);
                    if (!endptr || (endptr==optarg) || *endptr) {
                        fprintf(stderr, "Error: Invalid amplidude value specified!\n");
                        err = ERR_INVALID_USAGE;
                    } else {
                        LTRT10_FillDDSOutAmpVolt(cfg, val, NULL);
                    }
                }
                break;
            case OPT_DDS_RST:
                *rst = 1;
                break;
            default:
                break;
        }
    }

    return err;
}





int main(int argc, char** argv) {
    INT err = LTR_OK, out=0, rst=0;
    TLTRT10 hltrt10;
    TLTRT10_CONFIG cfg;
    char csn[LTR_CRATE_SERIAL_SIZE];
    int slot=LTR_CC_CHNUM_MODULE1;

#ifdef _WIN32
    /* для вывода русских букв в консоле... */
    setlocale(LC_ALL, "");
#endif

    /* разбор параметров входной строки */
    err = f_parse_params(argc, argv, &cfg, &out, &rst);
    /* находим нужный модуль */
    if (!err && !out) {
        err = f_get_slot(&slot, csn);
    }

    if (!err && !out) {
        LTRT10_Init(&hltrt10);
        /* устанавливаем соединение с модулем */
        err = LTRT10_Open(&hltrt10, LTRD_ADDR_DEFAULT, LTRD_PORT_DEFAULT, csn, slot);
        if (err != LTR_OK) {
            fprintf(stderr, "Не удалось установить связь с модулем. Ошибка %d (%s)\n",
                    err, LTRT10_GetErrorString(err));
        } else {
            /* после открытия модуля доступна информация, кроме версии прошивки ПЛИС */
            printf("Модуль открыт успешно!\n");
            printf("  Название модуля    = %s\n", hltrt10.ModuleInfo.Name);
            printf("  Серийный номер     = %s\n", hltrt10.ModuleInfo.Serial);
            printf("  Версия PLD         = %d\n", hltrt10.ModuleInfo.VerPLD);

            hltrt10.Cfg = cfg;
            err = LTRT10_SetConfig(&hltrt10);
            if (err != LTR_OK) {
                fprintf(stderr, "Не удалось записать конфигурацию модуля! Ошибка %d (%s)\n",
                        err, LTRT10_GetErrorString(err));
            } else {
                printf("Конфигурация записана успешно:\n"
                       "  Амплитуда            = %.2f Вольт\n"
                       "  Частота              = %.2f Гц\n"
                       "  Регистр частоты      = %d\n"
                       "  Коэф. усиления       = %.2f, %.2f, %.2f\n"
                       "  Позиции коммутатора  = %3s, %3s, %3s, %3s\n",
                        hltrt10.DDSAmp, hltrt10.DDSFreq, hltrt10.Cfg.DDS.FreqVal,
                        f_div_tbl[hltrt10.Cfg.DDS.AmpDiv], f_gain_tbl[hltrt10.Cfg.DDS.Gain[0]],
                        f_gain_tbl[hltrt10.Cfg.DDS.Gain[1]],
                        f_sw_names[hltrt10.Cfg.SwitchPos[0]], f_sw_names[hltrt10.Cfg.SwitchPos[1]],
                        f_sw_names[hltrt10.Cfg.SwitchPos[2]], f_sw_names[hltrt10.Cfg.SwitchPos[3]]
                       );

                if (!rst) {
                    err = LTRT10_SetDDSReset(&hltrt10, FALSE);
                    if (err != LTR_OK) {
                        fprintf(stderr, "Ошибка снятия сигнала сброса с DDS! Ошибка %d (%s)\n",
                                err, LTRT10_GetErrorString(err));
                    }
                }
            }
        }
        LTRT10_Close(&hltrt10);
    }

    return err;
}
