/* Данный пример представляет из себя консольную программу на языке C,
   демонстрирующую работу с модулями E16, L502 и E502 на примере синхронного
   ввода данных с АЦП и цифровых линий.

   Перед сбором в примере идет поиск модулей, подключенных по интерфейсам PCI-Express и USB,
   и предоставляется список для выбора модуля, с которым нужно работать.
   Для подключения по Ethernet нужно указать IP-адреса интересующих модулей
   в качестве аргументов командной строки при вызове примера, тогда эти адреса
   будут добавлены в конец списка выбора модуля.
   Например, если интересуют модули с адресами 192.168.1.5 и 192.168.1.6,
   то пример можно вызвать:
   x502_stream_read 192.168.1.5 E16:192.168.1.6
   и две дополнительные строки с этими адресами появятся в списке выбора.
   Для указания что ip адрес принадлежит модулю E16, адрес надо указать с префиксом, пример: E16:192.168.1.6

   Настройки частот, количества принимаемых данных и т.д. задаются с помощью макросов в
   начале программы.
   Настройки логических каналов - с помощью таблиц f_channels/f_ch_modes/f_ch_ranges.

   Пример выполняет прием блоков данных заданного размера.
   Сбор идет до нажатия любой клавиши на Windows или  CTRL+C на Linux.

   Пример также показывает как выполнять обработку данных и определять начало кадра,
   в случае если в X502_ProcessData() передается не целое число кадров.

   Данный пример содержит проект для Visual Studio 2008, а также может быть собран
   gcc в Linux или mingw в Windows через makefile или с помощью cmake (подробнее
   в комментариях в соответствующих файлах).

   Для того чтобы собрать проект в Visual Studio, измените путь к заголовочным файлам
   (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties)
   -> С/С++ -> Общие (General) -> Дополнительные каталоги включения (Additional Include Directories))
   на тот, где у вас лежат заголовочный файлы x502api.h, l502api.h и e502api.h и измените путь к библиотекам
   (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties) ->
   Компановщик (Linker) -> Общие (General) -> Дополнительные катологи библиотек (Additional Library Directories)).

   Внимание!!: Если Вы собираете проект под Visual Studio и взяли проект с сайта (а не из SDK),
   то для корректного отображения русских букв в программе нужно изменить кодировку
   или указать сохранение с сигнатурой кодировки для UTF-8:
   выберите Файл (File) -> Дополнительные параметры сохранения (Advanced Save Options)...
   и в поле Кодировка (Encoding) выберите Юникод (UTF8, с сигнатурой)/Unicode (UTF-8 with signature)
   и сохраните изменения в файле.

   */

//#include "dislin.h" //graphics library


#include <time.h> //for namigs of saved files

#include <l502api.h>
#include "e502api.h"
//#include "dev_funcs.h"

#ifdef _WIN32
#include <locale.h>
#include <conio.h>
#else
#include <signal.h>
#include <unistd.h>
#include <string.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#include "l502_BF_enums.h"
#include "l502_fpga_regs.h"


/* Remove unused enums/structs and constants that are not referenced anywhere */

// количество используемых логических каналов //
#define ADC_LCH_CNT  1

/* Unused frequency/timing defines removed */


// сколько отсчетов считываем за блок //
#define READ_BLOCK_SIZE   4096*200
// таймаут на прием блока (мс) //
#define READ_TIMEOUT     2000


/* Unused BF user command define removed */


// номера используемых физических каналов //
static uint32_t f_channels[ADC_LCH_CNT] = {0, 0, 1, 0};
// режимы измерения для каналов //
static uint32_t f_ch_modes[ADC_LCH_CNT] = {X502_LCH_MODE_COMM, X502_LCH_MODE_COMM,X502_LCH_MODE_COMM,X502_LCH_MODE_COMM};
//static uint32_t f_ch_modes[ADC_LCH_CNT] = {X502_LCH_MODE_COMM};
//static uint32_t f_ch_modes[ADC_LCH_CNT] = {X502_LCH_MODE_COMM, X502_LCH_MODE_COMM,X502_LCH_MODE_COMM,X502_LCH_MODE_COMM,X502_LCH_MODE_COMM,X502_LCH_MODE_COMM,X502_LCH_MODE_COMM,X502_LCH_MODE_COMM,X502_LCH_MODE_COMM,X502_LCH_MODE_COMM};
//static uint32_t f_ch_modes[ADC_LCH_CNT] = {X502_LCH_MODE_COMM, X502_LCH_MODE_ZERO,X502_LCH_MODE_ZERO,X502_LCH_MODE_ZERO,X502_LCH_MODE_ZERO,X502_LCH_MODE_ZERO,X502_LCH_MODE_ZERO,X502_LCH_MODE_ZERO,X502_LCH_MODE_ZERO,X502_LCH_MODE_ZERO};
//X502_LCH_MODE_COMM0
//X502_LCH_MODE_DIFF
//X502_LCH_MODE_ZERO

// диапазоны измерения для каналов //
static uint32_t f_ch_ranges[ADC_LCH_CNT] = {X502_ADC_RANGE_02, X502_ADC_RANGE_5,X502_ADC_RANGE_5,X502_ADC_RANGE_5};
//static uint32_t f_ch_ranges[ADC_LCH_CNT] = {X502_ADC_RANGE_1};
//static uint32_t f_ch_ranges[ADC_LCH_CNT] = {X502_ADC_RANGE_1, X502_ADC_RANGE_1,X502_ADC_RANGE_1,X502_ADC_RANGE_1,X502_ADC_RANGE_1,X502_ADC_RANGE_1, X502_ADC_RANGE_1,X502_ADC_RANGE_1,X502_ADC_RANGE_1,X502_ADC_RANGE_1};
//static uint32_t f_ch_ranges[ADC_LCH_CNT] = {X502_ADC_RANGE_5, X502_ADC_RANGE_5,X502_ADC_RANGE_5,X502_ADC_RANGE_5,X502_ADC_RANGE_5,X502_ADC_RANGE_5, X502_ADC_RANGE_5,X502_ADC_RANGE_5,X502_ADC_RANGE_5,X502_ADC_RANGE_5};
//X502_ADC_RANGE_10  Диапазон +/-10V
//X502_ADC_RANGE_5  Диапазон +/-5V
//X502_ADC_RANGE_2  Диапазон +/-2V
//X502_ADC_RANGE_1  Диапазон +/-1V
//X502_ADC_RANGE_05  Диапазон +/-0.5V
//X502_ADC_RANGE_02  Диапазон +/-0.2V



#define SYNC_FREC_SOURCE X502_SYNC_DI_SYN1_RISE
#define SYNC_START_SOURCE X502_SYNC_DI_SYN2_RISE



//X502_SYNC_INTERNAL
//X502_SYNC_EXTERNAL_MASTER1
//X502_SYNC_DI_SYN1_RISE
//X502_SYNC_DI_SYN1_FALL
//X502_SYNC_DI_SYN2_RISE
//X502_SYNC_DI_SYN2_FALL



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

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

char const *DEV_E502 = "E502";
char const *DEV_E16 = "E16";

typedef struct {
    uint32_t ip_addr;
    char const *devname;
} ip_dev_list_t;


typedef enum {
  RUN_MODE_TEST = 0,
  RUN_MODE_FINITE = 1,
  RUN_MODE_INF = 2
} run_mode_t;

typedef enum {
  BF_MODE_TRANSPARENT = 0,
  BF_MODE_AVG = 1
} bf_mode_t;

typedef struct main_state_typedef{
  run_mode_t run_mode; // TEST, FINITE_RUN, INF_RUN
  bf_mode_t BF_mode;   // TRANSPARENT, AVG
  uint32_t run_length; // in ms. Used as timeout for receive_to_file
  uint32_t run_I;      // № current run
  uint32_t runs_N;     // total number of runs
} main_state;

static void main_state_set_defaults(main_state* st) {
  st->run_mode = RUN_MODE_TEST;
  st->BF_mode = BF_MODE_TRANSPARENT;
  st->run_length = 1000;
  st->run_I = 0;
  st->runs_N = 1;
}

static char* f_trim(char* s) {
  char* end;
  while (*s==' ' || *s=='\t' || *s=='\r' || *s=='\n') s++;
  if (*s == 0) return s;
  end = s + strlen(s) - 1;
  while (end > s && (*end==' ' || *end=='\t' || *end=='\r' || *end=='\n')) end--;
  end[1] = '\0';
  return s;
}

static void parse_cmd_file(const char* filename, main_state* st) {
  FILE* f = fopen(filename, "r");
  if (f == NULL) {
    printf("Command file '%s' not found. Using defaults.\n", filename);
    return;
  }
  char line[512];
  while (fgets(line, sizeof(line), f)) {
    char* p = line;
    char* comment = strstr(p, "//");
    if (comment) *comment = '\0';
    p = f_trim(p);
    if (*p == '\0') continue;
    char* field = strtok(p, "\t \r\n");
    char* value = strtok(NULL, "\t \r\n");
    if (!field || !value) continue;

    if (strcmp(field, "run_mode") == 0) {
      if (strcmp(value, "FINITE_RUN") == 0) {
        st->run_mode = RUN_MODE_FINITE;
      } else if (strcmp(value, "INF_RUN") == 0) {
        st->run_mode = RUN_MODE_INF;
      } else if (strcmp(value, "TEST") == 0) {
        st->run_mode = RUN_MODE_TEST;
      } else {
        st->run_mode = RUN_MODE_TEST; // default on mismatch
      }
    } else if (strcmp(field, "BF_mode") == 0) {
      if (strcmp(value, "TRANSPARENT") == 0) {
        st->BF_mode = BF_MODE_TRANSPARENT;
      } else if (strcmp(value, "AVG") == 0) {
        st->BF_mode = BF_MODE_AVG;
      } else {
        st->BF_mode = BF_MODE_TRANSPARENT; // default on mismatch
      }
    } else if (strcmp(field, "run_length") == 0) {
      char* endp = NULL;
      unsigned long v = strtoul(value, &endp, 0);
      if (endp != value) st->run_length = (uint32_t)v;
    } else if (strcmp(field, "runs_N") == 0) {
      char* endp = NULL;
      unsigned long v = strtoul(value, &endp, 0);
      if (endp != value) st->runs_N = (uint32_t)v;
    } else if (strcmp(field, "run_I") == 0) {
      char* endp = NULL;
      unsigned long v = strtoul(value, &endp, 0);
      if (endp != value) st->run_I = (uint32_t)v;
    }
  }
  fclose(f);
}

/* Unused SIGINT handler and flag removed */


/* Функция находит все подключенные модули по интерфейсам PCI-Express и USB и
 * сохраняет записи о этих устройствах в выделенный массив.
 * Также создаются записи по переданным IP-адресам модулей и добавляются в конец
 * массива.
 * Указатель на выделенный массив, который должен быть потом очищен, сохраняется
 * в pdevrec_list, а количество действительных элементов (память которых должна
 * быть в дальнейшем освобождена с помощью X502_FreeDevRecordList()) возвращается
 * как результат функции */
static uint32_t f_get_all_devrec(t_x502_devrec **pdevrec_list, ip_dev_list_t *ip_dev_list, unsigned ip_cnt) {
    int32_t fnd_devcnt = 0;
    uint32_t pci_devcnt = 0;
    uint32_t usb_devcnt = 0;
    uint32_t e16_usb_devcnt = 0;

    t_x502_devrec *rec_list = NULL;

    // получаем количество подключенных устройств по интерфейсам PCI и USB //
    L502_GetDevRecordsList(NULL, 0, 0, &pci_devcnt);
    E502_UsbGetDevRecordsList(NULL, 0, 0, &usb_devcnt);
   // E16_UsbGetDevRecordsList(NULL, 0, 0, &e16_usb_devcnt);

    if ((pci_devcnt+usb_devcnt + e16_usb_devcnt + ip_cnt) != 0) {
        // выделяем память для массива для сохранения найденного количества записей //
        rec_list = malloc((pci_devcnt + usb_devcnt +e16_usb_devcnt + ip_cnt) * sizeof(t_x502_devrec));

        if (rec_list != NULL) {
            unsigned i;
            // получаем записи о модулях L502, но не больше pci_devcnt //
            if (pci_devcnt!=0) {
                int32_t res = L502_GetDevRecordsList(&rec_list[fnd_devcnt], pci_devcnt, 0, NULL);
                if (res >= 0) {
                    fnd_devcnt += res;
                }
            }
            // добавляем записи о модулях E502, подключенных по USB, в конец массива //
            if (usb_devcnt!=0) {
                int32_t res = E502_UsbGetDevRecordsList(&rec_list[fnd_devcnt], usb_devcnt, 0, NULL);
                if (res >= 0) {
                    fnd_devcnt += res;
                }
            }

            // добавляем записи о модулях E16, подключенных по USB, в конец массива //
      //      if (e16_usb_devcnt!=0) {
       //         int32_t res = E16_UsbGetDevRecordsList(&rec_list[fnd_devcnt], e16_usb_devcnt, 0, NULL);
        //        if (res >= 0) {
         //           fnd_devcnt += res;
          //      }
           // }

            // создаем записи для переданного массива ip-адресов //
            
        }
    }

    if (fnd_devcnt != 0) {
        // если создана хотя бы одна запись, то сохраняем указатель на выделенный массив //
        *pdevrec_list = rec_list;
    } else {
        *pdevrec_list = NULL;
        free(rec_list);
    }

    return fnd_devcnt;
}


static t_x502_hnd f_dev_select_open(int argc, char** argv) {
    t_x502_hnd hnd = NULL;
    uint32_t fnd_devcnt,i, dev_ind;
    t_x502_devrec *devrec_list = NULL;
    ip_dev_list_t *ip_dev_list = NULL;
    uint32_t ip_cnt = 0;

    // если есть аргументы командной строки, то предполагаем, что это могут быть
    //   ip-адреса интересующих устройств. //
    if (argc > 1) {
        ip_dev_list = malloc((argc - 1) * sizeof(ip_dev_list[0]));
        if (ip_dev_list == NULL) {
            fprintf(stderr, "Ошибка выделения памяти!\n");
        } else {
            for (i=1; (int)i < argc; i++) {
                int a[4];
                if (sscanf(argv[i], "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3])==4) {
                    ip_dev_list[ip_cnt].devname = DEV_E502;
                    ip_dev_list[ip_cnt++].ip_addr = ((a[0] & 0xFF) << 24) |
                                             ((a[1] & 0xFF) << 16) |
                                             ((a[2] & 0xFF) <<  8) |
                                             (a[3] & 0xFF);
                } else
                if (sscanf(argv[i], "E16:%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3])==4) {
                    ip_dev_list[ip_cnt].devname = DEV_E16;
                    ip_dev_list[ip_cnt++].ip_addr = ((a[0] & 0xFF) << 24) |
                                             ((a[1] & 0xFF) << 16) |
                                             ((a[2] & 0xFF) <<  8) |
                                             (a[3] & 0xFF);
                }
            }
        }
    }

    // получаем список модулей для выбора //
    fnd_devcnt = f_get_all_devrec(&devrec_list, ip_dev_list, ip_cnt);

    if (fnd_devcnt == 0) {
        printf("Не найдено ни одного модуля\n");
    } else {
        // выводим информацию по списку модулей //
        printf("Доступны следующие модули:\n");
        for (i=0; i < fnd_devcnt; i++) {
            printf("Модуль № %d: %s, %-9s", i, devrec_list[i].devname,
                   devrec_list[i].iface == X502_IFACE_PCI ? "PCI/PCIe" :
                   devrec_list[i].iface == X502_IFACE_USB ? "USB" :
                   devrec_list[i].iface == X502_IFACE_ETH ? "Ethernet" : "?");

            // при подключении по сети по IP-адресу серийный номер можно узнать
            //   только после открытия соединения. При этом поле location
            //   содержит строку с описанием адреса устройства //
            if (devrec_list[i].iface != X502_IFACE_ETH) {
                printf("Сер. номер: %s\n", devrec_list[i].serial);
            } else {
                printf("Адрес: %s\n", devrec_list[i].location);
            }
        }

        // выбираем нужный по введенному номеру модуля по порядку с клавиатуры //
        //printf("Введите номер модуля, с которым хотите работать (от 0 до %d)\n", fnd_devcnt-1);
        //fflush(stdout);
        //scanf("%d", &dev_ind);
        dev_ind = 0;

        if (dev_ind >= fnd_devcnt) {
            printf("Неверно указан номер модуля...\n");
        } else {
            // если ввели номер правильно - создаем описатель //
            hnd = X502_Create();
            if (hnd==NULL) {
                fprintf(stderr, "Ошибка создания описателя модуля!");
            } else {
                // устанавливаем связь с модулем по записи //
                int32_t err = X502_OpenByDevRecord(hnd, &devrec_list[dev_ind]);
                if (err != X502_ERR_OK) {
                    fprintf(stderr, "Ошибка установления связи с модулем: %s!", X502_GetErrorString(err));
                    X502_Free(hnd);
                    hnd = NULL;
                }
            }
        }

        // освобождение ресурсов действительных записей из списка //
        X502_FreeDevRecordList(devrec_list, fnd_devcnt);
        // очистка памяти самого массива //
        free(devrec_list);
    }

    // освобождаем выделенный массив под IP-адреса (если был выделен) //
    free(ip_dev_list);

    return hnd;
}


// настройка параметров модуля //
int32_t f_setup_params(t_x502_hnd hnd) {
    int32_t err = X502_ERR_OK, i;

    // устанавливаем параметры логической таблицы АЦП //
    err = X502_SetLChannelCount(hnd, ADC_LCH_CNT);
    for (i=0; (i < ADC_LCH_CNT) && (err == X502_ERR_OK); i++)
        err = X502_SetLChannel(hnd, i, f_channels[i], f_ch_modes[i], f_ch_ranges[i], 0);

//    // устанавливаем частоты ввода для АЦП и цифровых входов //
//    if (err == X502_ERR_OK) {
//        double f_adc = ADC_FREQ, f_frame = ADC_FRAME_FREQ, f_din = DIN_FREQ;
//        err = X502_SetAdcFreq(hnd, &f_adc, &f_frame);
//        if (err == X502_ERR_OK)
//            err = X502_SetDinFreq(hnd, &f_din);
//        if (err == X502_ERR_OK) {
//            // выводим реально установленные значения - те что вернули функции //
//            printf("Установлены частоты:\n    Частота сбора АЦП = %0.0f\n"
//                "    Частота на лог. канал = %0.0f\n    Частота цифрового ввода = %0.0f\n",
//                f_adc, f_frame, f_din);
//        }
//    }
    
    
    
    //double f_din = DIN_FREQ;
    //err = X502_SetDinFreq(hnd, &f_din);
    //printf();

      //set ADC sample frequency from internal generator with frequency 2 MHz
    X502_SetRefFreq(hnd, X502_REF_FREQ_2000KHZ);

    //=======================================================//
    //for real work with Radar
    //X502_SetSyncMode(hnd, X502_SYNC_DI_SYN1_FALL);
    //X502_SetSyncStartMode(hnd, X502_SYNC_DI_SYN2_RISE); //start ADC sampling by external trigger

    
    //=======================================================//
    //for debug purposes:
//    X502_SetRefFreq(hnd, X502_REF_FREQ_2000KHZ);
//    X502_SetSyncMode(hnd, X502_SYNC_DI_SYN1_FALL);
    //X502_SetSyncMode(hnd, X502_SYNC_INTERNAL);
    //X502_SetSyncStartMode(hnd, X502_SYNC_INTERNAL); //start ADC samping after executing X502_StreamsStart()
//    X502_SetSyncStartMode(hnd, X502_SYNC_DI_SYN2_RISE); //start ADC sampling by external trigger

    X502_SetSyncMode(hnd, SYNC_FREC_SOURCE);
    //X502_SetSyncMode(hnd, X502_SYNC_INTERNAL);
    X502_SetSyncStartMode(hnd, SYNC_START_SOURCE);

    //X502_SYNC_INTERNAL
    //X502_SYNC_EXTERNAL_MASTER1
    //X502_SYNC_DI_SYN1_RISE
    //X502_SYNC_DI_SYN1_FALL
    //X502_SYNC_DI_SYN2_RISE
    //X502_SYNC_DI_SYN2_FALL

      //set ADC sample frequency from internal generator with frequency 2 MHz
    //X502_SetRefFreq(hnd, X502_REF_FREQ_1500KHZ);
    //X502_REF_FREQ_1500KHZ
    //X502_REF_FREQ_2000KHZ









    
    

    // записываем настройки в модуль //
    if (err == X502_ERR_OK)
        err = X502_Configure(hnd, 0);

    // разрешаем синхронные потоки //
    if (err == X502_ERR_OK) {
        err = X502_StreamsEnable(hnd, X502_STREAM_ADC);
    }

    return err;
}


void uin32_t_to_bin(uint32_t val, char* string){
	for (int i = 0; i < 8; ++i){
		if (val & (0b1 << i)){
			string[i] = '1';
		}else{
			string[i] = '0';
		}
	}
}


uint32_t BF_exec_cmd_simple(t_x502_hnd hnd, uint16_t cmd_code, uint32_t cmd_par, uint8_t verbosity_lvl){

	uint32_t BF_cmd_receive_code = 0;
    //uint16_t cmd_code = 0x8001;  //read ADC data
    //uint32_t cmd_par = 87;
    const uint32_t snd_data[1] = {0,};
    uint32_t snd_size = 0;
    uint32_t rcv_data[100] = {0,};
    uint32_t rcv_size = 100;
    uint32_t tout = 1;
    uint32_t recvd_size = 0;
//*/
    if (verbosity_lvl == 2){
    printf("\n\n");

    printf("TX cmd_code: 0x%X\n", cmd_code);
    printf("TX tout: %u\n", tout);
    printf("TX par: %u\n", cmd_par);
    printf("TX snd_data size: %u\n", snd_size);
    for (int i = 0; i < snd_size; ++i){
    	printf(" 0x%0X,", snd_data[i]);
    }
    printf("\n");
    printf("TX rcv_size: %u\n", rcv_size);
    }

    BF_cmd_receive_code =  X502_BfExecCmd (hnd,
    										cmd_code,
											cmd_par,
											snd_data,
											snd_size,
											rcv_data,
											rcv_size,
											tout,
											&recvd_size);

    if (verbosity_lvl == 2){

    printf("\n======================\n\n");
    printf("RX received code dec: %u  hex: 0x%3X, %d\n", BF_cmd_receive_code, BF_cmd_receive_code,BF_cmd_receive_code);
    printf("RX recvd_size: %u\n", recvd_size);
    printf("RX received_data:\n" );
    for (int i = 0; i < recvd_size; ++i){
       	printf(" 0x%0X,", rcv_data[i]);
    }
    printf("\n\n\n\n");
    }



    if (verbosity_lvl == 1){
    	printf("\ncmd: 0x%0X, par: %d, rcv_code: %d,  result[0]: %d (0x%0X) , result_L: %d\n", cmd_code, cmd_par, BF_cmd_receive_code, rcv_data[0],rcv_data[0], recvd_size);
    }
    return BF_cmd_receive_code;
}

uint32_t BF_exec_cmd_with_arr(t_x502_hnd hnd, uint16_t cmd_code, uint32_t cmd_par, uint32_t* snd_data, uint32_t snd_data_size,  uint8_t verbosity_lvl){

	uint32_t BF_cmd_receive_code = 0;
    //uint16_t cmd_code = 0x8001;  //read ADC data
    //uint32_t cmd_par = 87;
    //const uint32_t snd_data[1] = {0,};
    //uint32_t snd_data_size = 0;
    uint32_t rcv_data[100] = {0,};
    uint32_t rcv_size = 100;
    uint32_t tout = 1;
    uint32_t recvd_size = 0;
//*/
    if (verbosity_lvl == 2){
    printf("\n\n");
    printf("TX cmd_code: 0x%X\n", cmd_code);
    printf("TX tout: %u\n", tout);
    printf("TX par: %u\n", cmd_par);
    printf("TX snd_data size: %u\n", snd_data_size);
    for (int i = 0; i < snd_data_size; ++i){
    	printf(" 0x%0X,", snd_data[i]);
    }
    printf("\n");
    printf("TX rcv_size: %u\n", rcv_size);
    }

    BF_cmd_receive_code =  X502_BfExecCmd (hnd,
    										cmd_code,
											cmd_par,
											snd_data,
											snd_data_size,
											rcv_data,
											rcv_size,
											tout,
											&recvd_size);

    if (verbosity_lvl == 2){

    printf("\n======================\n\n");
    printf("RX received code dec: %u  hex: 0x%3X, %d\n", BF_cmd_receive_code, BF_cmd_receive_code,BF_cmd_receive_code);
    printf("RX recvd_size: %u\n", recvd_size);
    printf("RX received_data:\n" );
    for (int i = 0; i < recvd_size; ++i){
       	printf(" 0x%0X,", rcv_data[i]);
    }
    printf("\n\n\n\n");
    }



    if (verbosity_lvl == 1){
    	printf("\n");
    	printf("cmd: 0x%0X, par: %d, rcv_code: %d,  result[0]: %d (0x%0X) , result_L: %d\n", cmd_code, cmd_par, BF_cmd_receive_code, rcv_data[0],rcv_data[0], recvd_size);
    	printf("      TX arr: ");
        for (int i = 0; i < snd_data_size; ++i){
           	printf(" 0x%0X,", snd_data[i]);
        }
    	printf("\n");
    	printf("      RX arr: ");
    	int values_in_line = 0;

        for (int i = 0; i < recvd_size; ++i){
           	printf(" 0x%08X,", rcv_data[i]);
           	++values_in_line;
           	if (values_in_line == 10){
           		printf("\n");
           		values_in_line = 0;
           	}
        }
    	printf("\n");
    }

    if (verbosity_lvl == 3){
    	printf("\n");
    	printf("cmd: 0x%0X, par: %d, rcv_code: %d,  result[0]: %d (0x%0X) , result_L: %d\n", cmd_code, cmd_par, BF_cmd_receive_code, rcv_data[0],rcv_data[0], recvd_size);
    	printf("      TX arr: ");
        for (int i = 0; i < snd_data_size; ++i){
           	printf(" 0x%0X,", snd_data[i]);
        }
    	printf("\n");
    	printf("      RX arr: ");
    	int values_in_line = 0;
        for (int i = 0; i < recvd_size; ++i){
        	char bin_str_val[9] = {0,};
        	uin32_t_to_bin(rcv_data[i], bin_str_val);
           	//printf(" 0x%0X,", rcv_data[i]);
        	printf(" 0b%s,", bin_str_val);
        	++values_in_line;
        	if (values_in_line == 10){
        		printf("\n");
        		values_in_line = 0;
        	}
        }
    	printf("\n");
    }

    return BF_cmd_receive_code;
}


uint32_t X502_FlushRcv_buff(t_x502_hnd hnd){
	uint32_t ready;
	X502_GetRecvReadyCount(hnd, &ready);
	if (ready > 0) {
	    uint32_t *tmp = malloc(ready * sizeof(uint32_t));
	    X502_Recv(hnd, tmp, ready, 0); // вычитываем и отбрасываем
	    free(tmp);
	}
	return ready;
}

void insert_marker_to_file(char* logfilename, char* marker_text){
	printf("marker %s <======================================\n", marker_text);
	FILE* logfile_ptr = fopen(logfilename, "a");
	fprintf(logfile_ptr, "marker %s \n", marker_text);
	fclose(logfile_ptr);
}


void receive_to_file(t_x502_hnd hnd, char* logfilename, uint32_t* inp_buff, uint32_t max_total_words, uint32_t timeout){
  FILE* logfile_ptr = fopen(logfilename, "a");

//  fprintf(logfile_ptr, "0xFF00000 \n");
//  fprintf(logfile_ptr, "0xFFFFFFF \n");
//  fprintf(logfile_ptr, "0xFF00000 \n");

//  uint32_t timeout = 100;
  uint32_t recv_Err_code = X502_Recv(hnd, inp_buff, max_total_words, timeout);
//uint32_t recv_Err_code = X502_Recv(hnd, inp_buff, max_total_words, 20000);
  if (recv_Err_code > 0){
        uint32_t received_words = recv_Err_code;
        for (uint32_t word_I = 0; word_I < received_words; word_I ++){
                //printf("%06d 0x%08X\n", word_I, inp_buff[word_I]);
                fprintf(logfile_ptr, "0x%08X \n", inp_buff[word_I]);
        }
    printf("received %ld words\n", received_words);
  }else{
        printf("receive error: %d\n======================\n", recv_Err_code);
  }
  fclose(logfile_ptr);
}








int main(int argc, char** argv) {
    int32_t err = X502_ERR_OK;
    uint32_t ver;
    t_x502_hnd hnd = NULL;


    // читаем имя командного файла из аргументов CLI
    const char* cmd_filename = "default.cmd";
    if (argc > 1 && argv[1] != NULL && argv[1][0] != '\0') {
        cmd_filename = argv[1];
    }

    // получаем версию библиотеки //
    ver = X502_GetLibraryVersion();
    printf("Версия библиотеки: %d.%d.%d\n", (ver >> 24)&0xFF, (ver>>16)&0xFF, (ver>>8)&0xFF);
    printf("Command file: %s\n", cmd_filename);

    // загружаем состояние из командного файла
    main_state state;
    main_state_set_defaults(&state);
    parse_cmd_file(cmd_filename, &state);
    // отладочный вывод принятых значений
    printf("Parsed state: run_mode=%d, BF_mode=%d, run_length=%u, runs_N=%u, run_I=%u\n",
           (int)state.run_mode, (int)state.BF_mode, state.run_length, state.runs_N, state.run_I);

/* Removed unused temporary buffers and FFT/LFSM placeholders */





    ////******* Получение списка устройств и выбор, с каким будем работать *****************//
    hnd = f_dev_select_open(argc, argv);

    ////******************************* Работа с модулем *************************//
    // если успешно выбрали модуль и установили с ним связь - продолжаем работу //
    if (hnd != NULL) {
        // получаем информацию //
        t_x502_info info;
//        err = X502_GetDevInfo(hnd, &info);
        if (err != X502_ERR_OK) {
            fprintf(stderr, "Ошибка получения серийного информации о модуле: %s!", X502_GetErrorString(err));
        } else {
            // выводим полученную информацию //
            printf("Установлена связь со следующим модулем:\n");
            printf(" Серийный номер          : %s\n", info.serial);
            printf(" Наличие ЦАП             : %s\n", info.devflags & X502_DEVFLAGS_DAC_PRESENT ? "Да" : "Нет");
            printf(" Наличие BlackFin        : %s\n", info.devflags & X502_DEVFLAGS_BF_PRESENT ? "Да" : "Нет");
            printf(" Наличие гальваноразвязки: %s\n", info.devflags & X502_DEVFLAGS_GAL_PRESENT ? "Да" : "Нет");
            printf(" Индустриальное исп.     : %s\n", info.devflags & X502_DEVFLAGS_INDUSTRIAL ? "Да" : "Нет");
            printf(" Наличие интерф. PCI/PCIe: %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_PCI ? "Да" : "Нет");
            printf(" Наличие интерф. USB     : %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_USB ? "Да" : "Нет");
            printf(" Наличие интерф. Ethernet: %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_ETH ? "Да" : "Нет");
            printf(" Версия ПЛИС             : %d.%d\n", (info.fpga_ver >> 8) & 0xFF, info.fpga_ver & 0xFF);
            printf(" Версия PLDA             : %d\n", info.plda_ver);
            if (info.mcu_firmware_ver != 0) {
                printf(" Версия прошивки ARM     : %d.%d.%d.%d\n",
                       (info.mcu_firmware_ver >> 24) & 0xFF,
                       (info.mcu_firmware_ver >> 16) & 0xFF,
                       (info.mcu_firmware_ver >>  8) & 0xFF,
                       info.mcu_firmware_ver & 0xFF);
            }
        }


        X502_StreamsStop(hnd); //stop all streams. We need it because we can connect to module that is already sampling. And sampling blocks any setup trials.

        if (err == X502_ERR_OK) {
            // настраиваем параметры модуля //
            err = f_setup_params(hnd);
            if (err != X502_ERR_OK)
                fprintf(stderr, "Ошибка настройки модуля: %s!", X502_GetErrorString(err));
        }
        
        char* BF_firmware_filename = "l502-BFfirmware0.ldr";
        
        if (1){

            printf("loading Blackfin firmware file: %s\n", BF_firmware_filename);

            uint32_t bf_firmware_load_state = X502_BfLoadFirmware(hnd, BF_firmware_filename); //load firmware from l502-BFfirmware0.ldr file to BlackFin
            printf("load state: %d \n", bf_firmware_load_state);

        }else{
        	printf("No BF firmware loaded\n");
        }
        printf("setup module again");
        if (err == X502_ERR_OK) {
                    // настраиваем параметры модуля //
                    err = f_setup_params(hnd);
                    if (err != X502_ERR_OK)
                        fprintf(stderr, "Ошибка настройки модуля: %s!", X502_GetErrorString(err));
        }


        printf("\n\n\n");

        //setup ADC:



		uint32_t streams_enable_Err = 0;
		streams_enable_Err = X502_StreamsEnable(hnd, X502_STREAM_ADC | X502_STREAM_DIN );
		printf("Streams enable err: %d \n", streams_enable_Err);
		uint32_t streams_start_Err = 0;
		streams_start_Err = X502_StreamsStart(hnd);
		printf("Streams start err: %d \n", streams_start_Err);


			if (state.run_mode == RUN_MODE_FINITE){
				state.run_I = 0;
				char tmp_data_filename[] = "                                                                           ";
				struct timespec ts;
				uint32_t max_total_words = 0;
				uint32_t *inp_buff = malloc(1024*100*1024*2*4);
				while(state.run_I < state.runs_N){
					max_total_words = 10000000;
					timespec_get(&ts, TIME_UTC);
	//		    sprintf(&tmp_data_filename, "tmp/received_data_%ld.%ld.csv", ts.tv_sec, ts.tv_nsec);
	//		    sprintf(&logfilename, "tmp/received_data_%ld.%ld.csv", ts.tv_sec, ts.tv_nsec);
				//sprintf(&logfilename, "data/received_data_%ld.csv", seconds);
					snprintf(tmp_data_filename, sizeof(tmp_data_filename), "tmp/received_data_%ld.%ld.csv", ts.tv_sec, ts.tv_nsec);
					printf("%u/%u dumping to file: %s\n", (unsigned)state.run_I, (unsigned)state.runs_N, tmp_data_filename);
					receive_to_file(hnd, tmp_data_filename, inp_buff,  max_total_words, state.run_length);

					state.run_I++;
				  }
				  free(inp_buff);
					  X502_Close(hnd);
					  // освобождаем описатель
				  X502_Free(hnd);
				  return 0;

			}else if (state.run_mode == RUN_MODE_INF){
				state.run_I = 0;
				char tmp_data_filename[] = "                                                                           ";
				struct timespec ts;
				uint32_t max_total_words = 0;
				uint32_t *inp_buff = malloc(1024*100*1024*2*4);
				while (1){ //should be infinite. because it is RUN_MODE_INF
				//while(state.run_I < state.runs_N){
					max_total_words = 10000000;
					timespec_get(&ts, TIME_UTC);
	//		    sprintf(&tmp_data_filename, "tmp/received_data_%ld.%ld.csv", ts.tv_sec, ts.tv_nsec);
	//		    sprintf(&logfilename, "tmp/received_data_%ld.%ld.csv", ts.tv_sec, ts.tv_nsec);
				//sprintf(&logfilename, "data/received_data_%ld.csv", seconds);
					snprintf(tmp_data_filename, sizeof(tmp_data_filename), "tmp/received_data_%ld.%ld.csv", ts.tv_sec, ts.tv_nsec);
						printf("%u dumping to file: %s\n", (unsigned)state.run_I, tmp_data_filename);
					receive_to_file(hnd, tmp_data_filename, inp_buff,  max_total_words, state.run_length);
					state.run_I++;
				  }
				  free(inp_buff);
					  X502_Close(hnd);
					  // освобождаем описатель
				  X502_Free(hnd);
				  return 0;

			}else{ //TEST mode as default




				time_t seconds;
				time(&seconds);
				char logfilename[] = "                                                 ";
				sprintf(&logfilename, "data/received_data_%ld.csv", seconds);
				//logfile_ptr = fopen(logfilename, "w");
				FILE *logfile_ptr;
				logfile_ptr = fopen(logfilename, "a");

				fprintf(logfile_ptr, "sync_frec_source: %d \nsync_start_source: %d \n", SYNC_FREC_SOURCE, SYNC_START_SOURCE);

				for (int chan_I = 0; chan_I < ADC_LCH_CNT; chan_I++){
					fprintf(logfile_ptr, "chan: %d, mode: %d, range: %d \n", chan_I, f_ch_modes[chan_I], f_ch_ranges[chan_I]);
				}
				fclose(logfile_ptr);


		//		printf("dumping to file: %s\n", logfilename);

				printf("\n");
				//printf("start streams sampling\n");
				//BF_exec_cmd_with_arr(hnd, 0x8004U, 0, NULL, 0, 1); //setup and start streams



				uint32_t waiting_cnt = 1;
				waiting_cnt = 100000;
				printf("\nwaiting %d ...", waiting_cnt);
				while(--waiting_cnt){;}
				//for(uint32_t timeout = 100000000; timeout; --timeout){;}
				printf("done\n");

				printf("\n\nget counters of calls of SPORT_RX, SPORT_TX, HDMA_RX, HDMA_TX\n");
				BF_exec_cmd_with_arr(hnd, 0x8005U, 100, NULL, 0, 1);


		//		printf("\n\nget a some data made by sport isr handler\n");
		//		BF_exec_cmd_with_arr(hnd, 0x8006U, 100, NULL, 0, 1);
				//BF_exec_cmd_with_arr(hnd, 0x8006U, 100, NULL, 0, 3);


				uint32_t *inp_buff = malloc(1024*100*1024*2*4);
				double *adc_data = malloc(1024*1024*sizeof(double));
				//uint32_t inp_buff[1024*2048] = {0,};
				/* Removed unused counters */


				insert_marker_to_file(logfilename, "start transparent");

				printf("\nStart transparent mode\n");
				BF_exec_cmd_simple(hnd, 0x8007, 10, 1); //start transparent

				printf("\nFlushed from receiving buff: %d\n", X502_FlushRcv_buff(hnd));

				printf("receiving data...\n");


				/* Removed unused readiness checks */

				uint32_t max_total_words = 100000;
				/* Removed unused data_receive_trys_counter */
		//		printf("\nflush TX buff. \n Number of free TX descriptors before flushing:");
		//		BF_exec_cmd_simple(hnd, 0x8010, 10, 1);


		//		receive_to_file(hnd, logfilename, inp_buff,  max_total_words, 10000);


				printf("\n\nget counters of calls of SPORT_RX, SPORT_TX, HDMA_RX, HDMA_TX\n");
				BF_exec_cmd_with_arr(hnd, 0x8005U, 100, NULL, 0, 1);




		//*













				insert_marker_to_file(logfilename, "start AVG");
				printf("\nStart AVG mode\n");
				BF_exec_cmd_simple(hnd, 0x8008, 2, 1); //start averaging


		//		printf("\nflush TX buff. \n Number of free TX descriptors before flushing:");
		//		BF_exec_cmd_simple(hnd, 0x8010, 10, 1);

				X502_StreamsStop(hnd);
				X502_StreamsStart(hnd);


		//		for(uint32_t wait_i = 1e6; wait_i; --wait_i){;}

				printf("\nFlushed from receiving buff: %d\n", X502_FlushRcv_buff(hnd));




		//		printf("\n\nget counters of calls of SPORT_RX, SPORT_TX, HDMA_RX, HDMA_TX\n");
		//		BF_exec_cmd_with_arr(hnd, 0x8005U, 100, NULL, 0, 1);


				//fprintf(logfile_ptr, "value number; time, sec; adc_value, V\n");


				max_total_words = 100000;

				//printf("\n dbg value:  ");
				//BF_exec_cmd_simple(hnd, 0x800A, 10, 1);

				struct timespec time_receive_started, time_receive_ended;

				clock_gettime(CLOCK_MONOTONIC, &time_receive_started);
				receive_to_file(hnd, logfilename, inp_buff,  max_total_words, 10000);
				clock_gettime(CLOCK_MONOTONIC, &time_receive_ended);


				double receive_time = (time_receive_ended.tv_sec - time_receive_started.tv_sec);
				receive_time += time_receive_ended.tv_nsec*1e-9;
				receive_time += time_receive_started.tv_nsec*1e-9;


				printf("\n   Receive time: %g, %g sec/value \n", receive_time, receive_time/max_total_words);











				insert_marker_to_file(logfilename, "start transparent");

				printf("\nStart transparent mode\n");
				BF_exec_cmd_simple(hnd, 0x8007, 2, 1); //start transparent
				X502_StreamsStop(hnd);
				X502_StreamsStart(hnd);

				printf("\nflush TX buff. \n Number of free TX descriptors before flushing:");
				BF_exec_cmd_simple(hnd, 0x8010, 10, 1);

				printf("\nFlushed from receiving buff: %d\n", X502_FlushRcv_buff(hnd));


		//		printf("\n dbg value:  ");
		//		BF_exec_cmd_simple(hnd, 0x800A, 10, 1);
				receive_to_file(hnd, logfilename, inp_buff,  max_total_words, 10000);
				printf("\n dbg value:  ");
				BF_exec_cmd_simple(hnd, 0x800A, 10, 1);

		//*/




				printf("\n\nreceive done. Exiting...\n\n\n");
				//logfile_ptr = fopen(logfilename, "w");
				printf("dumped to file: %s\n", logfilename);
				fprintf(stderr, "%s\n", logfilename);

				X502_Close(hnd);
				// освобождаем описатель
				X502_Free(hnd);
				return 0;
			}
        


    }
    return -1;

}
