//---------------------------------------------------------------------------

// Пример консольного приложения, осуществляющего непрерывный сбор 10-ти порций
// данных с записью их в двоичный файл


#pragma hdrstop
 // подключаем заголовочные файлы

#include <iostream>

#include "ltr212api.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>

#define ACQ_BLOCKS_QNT (2) // Количество порций данных, и пользуемое в цикле сбора
#define TOTAL_BLOCKS_QNT (10) // Общее количество порций данных
#define POINTS_PER_CHANNEL (500) // Количество точек на канал в одной порции д.
#define CHANNELS_QNT (4) // Количество задействованных каналов

/* Описания типов функций ltr212api.DLL */
typedef void (* APIENTRY p_LTR212_GetDllVersion)(LTR212_DLL_VERSION * const DllVersion);
typedef INT (* APIENTRY p_LTR212_Init)(PTLTR212 hnd);
typedef INT (* APIENTRY p_LTR212_IsOpened)(PTLTR212 hnd);
typedef INT (* APIENTRY p_LTR212_Open)(PTLTR212 hnd, DWORD net_addr, WORD net_port, const CHAR *crate_sn, INT slot_num, const CHAR *biosname);
typedef INT (* APIENTRY p_LTR212_Close)(PTLTR212 hnd);
typedef INT (* APIENTRY p_LTR212_CreateLChannel)(INT PhysChannel, INT Scale);
typedef INT (* APIENTRY p_LTR212_CreateLChannel2)(DWORD PhysChannel, DWORD Scale, DWORD BridgeType);
typedef INT (* APIENTRY p_LTR212_SetADC)(PTLTR212 hnd);
typedef INT (* APIENTRY p_LTR212_SetADCExpFlt)(PTLTR212 hnd, const TLTR212_FILTER *firFlt, const TLTR212_FILTER *iirFlt);
typedef INT (* APIENTRY p_LTR212_Start)(PTLTR212 hnd);
typedef INT (* APIENTRY p_LTR212_Stop)(PTLTR212 hnd);
typedef INT (* APIENTRY p_LTR212_Recv)(PTLTR212 hnd, DWORD *data, DWORD *tmark, DWORD size, DWORD timeout);
typedef INT (* APIENTRY p_LTR212_ProcessData)(PTLTR212 hnd, const DWORD *src, double *dest, DWORD *size, BOOL volt);
typedef LPCSTR (* APIENTRY p_LTR212_GetErrorString)(INT Error_Code);
typedef INT (* APIENTRY p_LTR212_Calibrate)(PTLTR212 hnd, BYTE *LChannel_Mask, INT mode, INT reset);
typedef INT (* APIENTRY p_LTR212_CalcFS)(PTLTR212 hnd, double *fsBase, double *fs);
typedef INT (* APIENTRY p_LTR212_TestEEPROM)(PTLTR212 hnd);
typedef DWORD (* APIENTRY p_LTR212_CalcTimeOut)(PTLTR212 hnd, DWORD n);
typedef INT (* APIENTRY p_LTR212_ProcessDataTest)(PTLTR212 hnd, const DWORD *src, double *dest, DWORD *size, BOOL volt,DWORD *bad_num);
typedef INT (* APIENTRY p_LTR212_ReadFilter)(CHAR *fname, TLTR212_FILTER *filter);
typedef INT (* APIENTRY p_LTR212_ReadUSR_Clb)(PTLTR212 hnd, TLTR212_USER_TARE_COEFS *coefs);
typedef INT (* APIENTRY p_LTR212_WriteUSR_Clb)(PTLTR212 hnd, const TLTR212_USER_TARE_COEFS *coefs);
typedef INT (* APIENTRY p_LTR212_ReadCodec_Clb)(PTLTR212 hnd, DWORD ch_mask, TLTR212_USER_TARE_COEFS *coefs);
typedef INT (* APIENTRY p_LTR212_Load_Clb)(PTLTR212 hnd);

/*
typedef void (*p_LTR212_GetDllVersion)(LTR212_DLL_VERSION * const DllVersion);
typedef INT (*p_LTR212_Init)(PTLTR212 hnd);
typedef INT (*p_LTR212_IsOpened)(PTLTR212 hnd);
typedef INT (*p_LTR212_Open)(PTLTR212 hnd, DWORD net_addr, WORD net_port, const CHAR *crate_sn, INT slot_num, const CHAR *biosname);
typedef INT (*p_LTR212_Close)(PTLTR212 hnd);
typedef INT (*p_LTR212_CreateLChannel)(INT PhysChannel, INT Scale);
typedef INT (*p_LTR212_CreateLChannel2)(DWORD PhysChannel, DWORD Scale, DWORD BridgeType);
typedef INT (*p_LTR212_SetADC)(PTLTR212 hnd);
typedef INT (*p_LTR212_SetADCExpFlt)(PTLTR212 hnd, const TLTR212_FILTER *firFlt, const TLTR212_FILTER *iirFlt);
typedef INT (*p_LTR212_Start)(PTLTR212 hnd);
typedef INT (*p_LTR212_Stop)(PTLTR212 hnd);
typedef INT (*p_LTR212_Recv)(PTLTR212 hnd, DWORD *data, DWORD *tmark, DWORD size, DWORD timeout);
typedef INT (*p_LTR212_ProcessData)(PTLTR212 hnd, const DWORD *src, double *dest, DWORD *size, BOOL volt);
typedef LPCSTR (*p_LTR212_GetErrorString)(INT Error_Code);
typedef INT (*p_LTR212_Calibrate)(PTLTR212 hnd, BYTE *LChannel_Mask, INT mode, INT reset);
typedef INT (*p_LTR212_CalcFS)(PTLTR212 hnd, double *fsBase, double *fs);
typedef INT (*p_LTR212_TestEEPROM)(PTLTR212 hnd);
typedef DWORD (*p_LTR212_CalcTimeOut)(PTLTR212 hnd, DWORD n);
typedef INT (*p_LTR212_ProcessDataTest)(PTLTR212 hnd, const DWORD *src, double *dest, DWORD *size, BOOL volt,DWORD *bad_num);
typedef INT (*p_LTR212_ReadFilter)(CHAR *fname, TLTR212_FILTER *filter);
typedef INT (*p_LTR212_ReadUSR_Clb)(PTLTR212 hnd, TLTR212_USER_TARE_COEFS *coefs);
typedef INT (*p_LTR212_WriteUSR_Clb)(PTLTR212 hnd, const TLTR212_USER_TARE_COEFS *coefs);
typedef INT (*p_LTR212_ReadCodec_Clb)(PTLTR212 hnd, DWORD ch_mask, TLTR212_USER_TARE_COEFS *coefs);
typedef INT (*p_LTR212_Load_Clb)(PTLTR212 hnd);
*/
//using namespace std;


/* Описания функций ltr212api.DLL */
p_LTR212_GetDllVersion pLTR212_GetDllVersion;
p_LTR212_Init pLTR212_Init;
p_LTR212_IsOpened pLTR212_IsOpened;
p_LTR212_Open pLTR212_Open;
p_LTR212_Close pLTR212_Close;
p_LTR212_CreateLChannel pLTR212_CreateLChannel;
p_LTR212_CreateLChannel2 pLTR212_CreateLChannel2;
p_LTR212_SetADC pLTR212_SetADC;
p_LTR212_SetADCExpFlt pLTR212_SetADCExpFlt;
p_LTR212_Start pLTR212_Start;
p_LTR212_Stop pLTR212_Stop;
p_LTR212_Recv pLTR212_Recv;
p_LTR212_ProcessData pLTR212_ProcessData;
p_LTR212_GetErrorString pLTR212_GetErrorString;
p_LTR212_Calibrate pLTR212_Calibrate;
p_LTR212_CalcFS pLTR212_CalcFS;
p_LTR212_TestEEPROM pLTR212_TestEEPROM;
p_LTR212_CalcTimeOut pLTR212_CalcTimeOut;
p_LTR212_ProcessDataTest pLTR212_ProcessDataTest;
p_LTR212_ReadFilter pLTR212_ReadFilter;
p_LTR212_ReadUSR_Clb pLTR212_ReadUSR_Clb;
p_LTR212_WriteUSR_Clb pLTR212_WriteUSR_Clb;
p_LTR212_ReadCodec_Clb pLTR212_ReadCodec_Clb;
p_LTR212_Load_Clb pLTR212_Load_Clb;

HINSTANCE hDLL212;


TLTR212 hltr212;  // Экземпляр структуры описания модуля
HANDLE AcqThreadHnd;   // Дескриптор потока сбора данных

LPCSTR ErrorString;    // Строка для записи сообщений об ошибках

// Выходной массив, куда записываются значения напряжений
double voltage[ACQ_BLOCKS_QNT][CHANNELS_QNT*POINTS_PER_CHANNEL];
DWORD ttMark[2*POINTS_PER_CHANNEL*CHANNELS_QNT];

static volatile int RunFlag = 0; // Флаг, указывающий, что идет сбор данных
 // массив из двух флагов.
static volatile int BlockReady[ACQ_BLOCKS_QNT];
// Объявления функции потока сбора данных
static DWORD WINAPI AcquireThread (LPVOID param);

// Вспомогательная функция для перевода строк в формат ДОС
int oem_printf(const char *s, ...);

//---------------------------------------------------------------------------
int oem_printf(const char *s, ...)
{
 char str[1024];
 va_list list;
 va_start(list, s);
 vsprintf(str, s, list);
 va_end(list);
// CharToOem(str, str);
 return printf(str);
}

// Функция потока сбора данных
static DWORD WINAPI AcquireThread(LPVOID param)
{
 int i;
 int err=0;
 INT DataCntr=0;
 DWORD data[2*POINTS_PER_CHANNEL*CHANNELS_QNT];  // Входной массив из модуля
 DWORD to;          // таймаут в миллисекундах
 INT BlockCntr=0;   // Счетчик блоков

 DWORD size; // Размер порции данных
 DWORD ExitCode;

printf("AcquireThread\n");
fflush(stdout);


/********* Функция, запускаемая в качестве потока сбора данных. *******/

 // Обнуляем массив из двух флагов
 for(i=0;i<ACQ_BLOCKS_QNT;i++)
    BlockReady[i]=0;

 // Стартуем сбор данных
 //
     printf("start\n\n");
     fflush(stdout);
 err=(*pLTR212_Start)(&hltr212);
 if(err)
  { // В случае ошибки останавливаем сбор и выходим
    ErrorString=(*pLTR212_GetErrorString)(err);
    oem_printf("%s", (CHAR *) ErrorString);
    (*pLTR212_Stop)(&hltr212);
    RunFlag=0;
//
    printf("err start\n\n");
    fflush(stdout);
  }

  /* Размер получаемой порции данных равен количеству точек на канал,
     Умноженному на количество каналов. Полученное значение также следует
     умножить на 2, т.к. все сэмплы - 32 битные и передаются двумя словами
     данных    */

  size=2*POINTS_PER_CHANNEL*CHANNELS_QNT;
  // Расчет таймаута при получении данных
  to=(*pLTR212_CalcTimeOut)(&hltr212, POINTS_PER_CHANNEL);

  // Когда активен флаг сбора данных
  while(RunFlag)
  {
   // Это значение (size) надо обновлять в каждой итерации,
   // т.к. при выходе из ф-ции LTR212_ProcessData()
   // оно уменьшается вдвое
   size=2*POINTS_PER_CHANNEL*CHANNELS_QNT;

   // Получаем данные
   DataCntr=(*pLTR212_Recv)(&hltr212, data, ttMark, size, to);
   if(DataCntr!=size)  // Если получено
    {
     RunFlag=0;
     if(DataCntr>=0)
       oem_printf("Ошибка! Получено меньше сэмплов, чем было заказано!\n");
     else
       {
        err=DataCntr;
        ErrorString=(*pLTR212_GetErrorString)(err);
        oem_printf("%s\n", ErrorString);
       }
       break;
     } // к if(DataCntr!=size)

     // Обрабатываем полученные данные и пересчитываем их в значения напряжения
     err=(*pLTR212_ProcessData)(&hltr212, data, voltage[BlockCntr], &size, true);
     if(err)
      {
       ErrorString=(*pLTR212_GetErrorString)(err);
       oem_printf("%s\n", ErrorString);
       RunFlag=0;
       break;
      }

      BlockReady[BlockCntr]=1;

      BlockCntr++;
      if(BlockCntr>=ACQ_BLOCKS_QNT)
        BlockCntr=0;
  } // к while(RunFlag)

  RunFlag=0; // Сбрасываем флаг сбора данных
  err=(*pLTR212_Stop)(&hltr212); // Останавливаем сбор данных
  if(err)
  {
    ErrorString=(*pLTR212_GetErrorString)(err);
    oem_printf("%s\n", (CHAR *) ErrorString);
  }

  // Выходим из потока
  ExitThread(ExitCode);
  return 0;
}

#pragma argsused
int main(int argc, char* argv[])
{
DWORD AcqThreadId;
DWORD ThreadSatus;

INT TotalBlockCntr;

INT err;                 // Переменная для хранения кода ошибки
INT i;                   // Используется для счетчиков итераций в циклах
CHAR *BiosVersion;       // В эту переменную записывается версия БИОСа
CHAR *BiosDate;          // В эту переменную записывается дата создания БИОСа
CHAR *SerialNum;         // В эту переменную запишем серийный номер модуля
CHAR *ModuleName;        // В эту переменную запишем имя модуля

int         Channel;
FILE       *DataFile;
FILE       *tFile;
const char FileName[] = "ltr212_data.bin";
const char FileT[] = "ltr212_t.bin";

int BlockCntr=0;

if((hDLL212=::LoadLibrary(L"ltr212api.dll"))==NULL)
    return -1;

/* Адреса функций ltr212api.DLL */
pLTR212_GetDllVersion=(p_LTR212_GetDllVersion)::GetProcAddress(hDLL212,"LTR212_GetDllVersion");
pLTR212_Init=(p_LTR212_Init)::GetProcAddress(hDLL212,"LTR212_Init");
pLTR212_IsOpened=(p_LTR212_IsOpened)::GetProcAddress(hDLL212,"LTR212_IsOpened");
pLTR212_Open=(p_LTR212_Open)::GetProcAddress(hDLL212,"LTR212_Open");
pLTR212_Close=(p_LTR212_Close)::GetProcAddress(hDLL212,"LTR212_Close");
pLTR212_CreateLChannel=(p_LTR212_CreateLChannel)::GetProcAddress(hDLL212,"LTR212_CreateLChannel");
pLTR212_CreateLChannel2=(p_LTR212_CreateLChannel2)::GetProcAddress(hDLL212,"LTR212_CreateLChannel2");
pLTR212_SetADC=(p_LTR212_SetADC)::GetProcAddress(hDLL212,"LTR212_SetADC");
pLTR212_SetADCExpFlt=(p_LTR212_SetADCExpFlt)::GetProcAddress(hDLL212,"LTR212_SetADCExpFlt");
pLTR212_Start=(p_LTR212_Start)::GetProcAddress(hDLL212,"LTR212_Start");
pLTR212_Stop=(p_LTR212_Stop)::GetProcAddress(hDLL212,"LTR212_Stop");
pLTR212_Recv=(p_LTR212_Recv)::GetProcAddress(hDLL212,"LTR212_Recv");
pLTR212_ProcessData=(p_LTR212_ProcessData)::GetProcAddress(hDLL212,"LTR212_ProcessData");
pLTR212_GetErrorString=(p_LTR212_GetErrorString)::GetProcAddress(hDLL212,"LTR212_GetErrorString");
pLTR212_Calibrate=(p_LTR212_Calibrate)::GetProcAddress(hDLL212,"LTR212_Calibrate");
pLTR212_CalcFS=(p_LTR212_CalcFS)::GetProcAddress(hDLL212,"LTR212_CalcFS");
pLTR212_TestEEPROM=(p_LTR212_TestEEPROM)::GetProcAddress(hDLL212,"LTR212_TestEEPROM");
pLTR212_CalcTimeOut=(p_LTR212_CalcTimeOut)::GetProcAddress(hDLL212,"LTR212_CalcTimeOut");
pLTR212_ProcessDataTest=(p_LTR212_ProcessDataTest)::GetProcAddress(hDLL212,"LTR212_ProcessDataTest");
pLTR212_ReadFilter=(p_LTR212_ReadFilter)::GetProcAddress(hDLL212,"LTR212_ReadFilter");
pLTR212_ReadUSR_Clb=(p_LTR212_ReadUSR_Clb)::GetProcAddress(hDLL212,"LTR212_ReadUSR_Clb");
pLTR212_WriteUSR_Clb=(p_LTR212_WriteUSR_Clb)::GetProcAddress(hDLL212,"LTR212_WriteUSR_Clb");
pLTR212_ReadCodec_Clb=(p_LTR212_ReadCodec_Clb)::GetProcAddress(hDLL212,"LTR212_ReadCodec_Clb");
pLTR212_Load_Clb=(p_LTR212_Load_Clb)::GetProcAddress(hDLL212,"LTR212_Load_Clb");

/* Инициализируем канал связи с модулем.
  Поля структуры описания модуля заполняются значениями по умолчанию. */

 oem_printf("%s", "Инициализация структуры описания модуля\n");
 err=(*pLTR212_Init)(&hltr212);

 if(err)
 {
 ErrorString=(*pLTR212_GetErrorString)(err);
 oem_printf("%s\n", (CHAR *) ErrorString);
 goto End;
 //return(err);
  }
 else
  oem_printf("LTR212_Init()->OK\n");



// Открываем канал связи с модулем. Сетевой адрес и номер порта - по умолчанию
// Серийный номер  первого найденного крейта;
// Номер слота - 1;
// Полный путь файла с БИОСом - "C:\\prj\\ltr\\LTR-212\\LTR212-ADSP\\ltr212.bio"

err=(*pLTR212_Open)(&hltr212, SADDR_DEFAULT, SPORT_DEFAULT, "3T778752", CC_MODULE4,
                                  "ltr212.bio");
 if(err)
 {
  ErrorString=(*pLTR212_GetErrorString)(err);
  oem_printf("%s\n", (CHAR *) ErrorString);
  goto End;
  //return(err);
  }
 else
  oem_printf("LTR212_Open()->OK\n\n");

oem_printf("Имя модуля: %s\n", hltr212.ModuleInfo.Name);
oem_printf("Версия БИОСа: %s\n", hltr212.ModuleInfo.BiosVersion);
oem_printf("Дата создания БИОСа: %s\n", hltr212.ModuleInfo.BiosDate);
oem_printf("Серийный номер модуля: %s\n\n", hltr212.ModuleInfo.Serial);

 // Призводим заполнение полей структуры описания модуля требуемыми значениями

hltr212.size=sizeof(TLTR212);  // Размер структуры
hltr212.AcqMode=1;                  // 4-канальный режим высокой точности
hltr212.UseClb=0;                   // Не используем пользовательские калибровочные коэффициенты
hltr212.UseFabricClb=0/*1*/;        // Используем автоматическую загрузку заводских калибр. коэфф.
hltr212.LChQnt=4;                   // Количество логических каналов - 4
hltr212.REF=1;                      // Опорное напряжение  5 В.
hltr212.AC=0;                       //  Постоянное опорное напряжение

// Создаем 4 логических канала
// Диапазон входного напряжения для всех каналов - +/- 80 мВ
hltr212.LChTbl[0]=(*pLTR212_CreateLChannel)(1,3);
hltr212.LChTbl[1]=(*pLTR212_CreateLChannel)(2,3);
hltr212.LChTbl[2]=(*pLTR212_CreateLChannel)(3,3);
hltr212.LChTbl[3]=(*pLTR212_CreateLChannel)(4,3);



// Передаем параметры АЦП управляющей программе модуля
err=(*pLTR212_SetADC)(&hltr212);
if(err)
 {
 ErrorString=(*pLTR212_GetErrorString)(err);
 (*pLTR212_Close)(&hltr212);
 oem_printf("%s\n", (CHAR *) ErrorString);
 goto End;
  }
 else
  oem_printf("LTR212_SetADC()->OK\n\n");
/*
oem_printf("Установлен 4-канальный режим высокой точности.\n");
oem_printf("Количество логических каналов - 4.\n");
oem_printf("Диапазон входного сигнала всех каналов - +/-80 мВ.\n");
oem_printf("Калибровочные коэффициенты не используются.\n");
oem_printf("Установлено однополярное опорное напряжение 5 В.\n\n");
*/
printf("Установлен 4-канальный режим высокой точности.\n");
printf("Количество логических каналов - 4.\n");
printf("Диапазон входного сигнала всех каналов - +/-80 мВ.\n");
printf("Калибровочные коэффициенты не используются.\n");
printf("Установлено однополярное опорное напряжение 5 В.\n\n");
fflush(stdout);

 DataFile = fopen(FileName, "wb");
 if(DataFile==NULL)
  {
   oem_printf("%s\n","Невозможно создать файл!");
   fflush(stdout);
   (*pLTR212_Close)(&hltr212);
   goto End;
  }
tFile=fopen(FileT, "wb");
if(tFile==NULL)
 {
  oem_printf("%s\n","t: Невозможно создать файл!");
  fflush(stdout);
  (*pLTR212_Close)(&hltr212);
  goto End;
 }

RunFlag = 1;

/******* Формируем поток сбора данных  ***************/

// Создаем поток
printf("AcqThreadHnd\n");
fflush(stdout);

AcqThreadHnd=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AcquireThread,
                        NULL, CREATE_SUSPENDED, (LPDWORD) &AcqThreadId);

printf("err= %d \n", GetLastError());
fflush(stdout);


if(AcqThreadHnd==NULL)
{
 printf("%s\n","Невозможно создать поток сбора данных!\n");
 fflush(stdout);
 (*pLTR212_Close)(&hltr212);
 goto End;
}

// Стартуем поток
ResumeThread(AcqThreadHnd);

oem_printf("Поток сбора данных запущен\n");

TotalBlockCntr  = 0; // Счетчик принятых и записанных в файл порций данных

while(RunFlag) // Пока активен флаг сбора данных (т.е. идет сбор)
{
 if(BlockReady[BlockCntr]) // Если  получили очередную порцию, то записываем
                           // в файл
 {
   TotalBlockCntr++;  // инкрементируем счетчик общего кол-ва полученных порций д.
   fwrite(voltage[BlockCntr], sizeof voltage[0][0],
   CHANNELS_QNT*POINTS_PER_CHANNEL, DataFile);
   fwrite(ttMark, sizeof ttMark[0],
   POINTS_PER_CHANNEL, tFile);
   oem_printf("%d-я %s\n",TotalBlockCntr, "порция данных получена и записана в файл");
   BlockReady[BlockCntr]=0;

   BlockCntr++;

   if(BlockCntr>=2)
    BlockCntr=0;

    if (TotalBlockCntr >= TOTAL_BLOCKS_QNT)
    {
     oem_printf(">> Сбор данных успешно завершен.\n");
     RunFlag = 0;
     }
    } // к if(BlockReady[BlockCntr])
  }    // к while(RunFlag)

//Дожидаемся завершение работы потока сбора данных
  WaitForSingleObject(AcqThreadHnd, INFINITE);
  GetExitCodeThread(AcqThreadHnd, &ThreadSatus);

  CloseHandle(AcqThreadHnd);
  oem_printf("Поток сбора данных удален.\n");

  // Закрываем файл
  fclose(DataFile);
  oem_printf("Файл для записи данных закрыт.\n");


End:
oem_printf(">> Для выхода нажмите любую клавишу\n");
    while(!kbhit())
        continue;


        return 0;
}
//---------------------------------------------------------------------------
