//******************************************************************************
// Консольная программа по организации процедуры ввода-вывода данных
// для модуля USB3000
//******************************************************************************
#include <stdio.h>
#include <conio.h>
#include <math.h>
#include "Rtusbapi.h"

// аварийное завершение программы
void TerminateApplication(char *ErrorString, bool TerminationFlag = true);
// ожидание завершения выполнения очередного запроса
BOOL WaitingForWriteRequestCompleted(OVERLAPPED * const WriteOv);
// отображение ошибок
void ShowThreadErrorMessage(void);

// функция потока ввода данных
DWORD WINAPI ServiceReadThread(PVOID /*Context*/);
// идентификатор потока ввода
HANDLE hReadThread;
DWORD ReadTid;

// функция потока вывода данных
DWORD WINAPI ServiceWriteThread(PVOID /*Context*/);
// идентификатор потока вывода
HANDLE hWriteThread;
DWORD WriteTid;

// идентификатор файла для записи полученных данных
HANDLE hFile;


// максимально возможное кол-во опрашиваемых виртуальных слотов
const WORD MaxVirtualSoltsQuantity = 127;

// текущая версия библиотеки Rtusbapi.dll
DWORD DllVersion;
// указатель на интерфейс модуля
IRTUSB3000 *pModule;
// идентификатор устройства
HANDLE ModuleHandle;
// название модуля
char ModuleName[10];
// скорость работы шины USB
BYTE UsbSpeed;
// серийный номер модуля
char ModuleSerialNumber[9];
// версия драйвера AVR
char AvrVersion[5];
// структура информации в ППЗУ модуля
RTUSB3000::FLASH fi;
// структура, содержащая информацию о версии драйвера DSP
RTUSB3000::DSP_INFO di;
// структура, содержащая параметры ввода данных
RTUSB3000::INPUT_PARS ip;
// структура, содержащая параметры вывода данных
RTUSB3000::OUTPUT_PARS dp;

//max возможное кол-во передаваемых отсчетов (кратное 32) для ф. ReadData и WriteData()
DWORD DataStep = 1024*1024;
// кол-во вводимых блоков данных по DataStep отсчётов
const WORD NBlockRead = 20;
// длина буфера для ввода данных
DWORD ReadPoints = NBlockRead*DataStep;
// длина буфера для выводимых данных
DWORD WritePoints = 2*DataStep;
// указатель на буфер для вводимых данных
SHORT *ReadBuffer;
// указатель на буфер для выводимых данных
SHORT *WriteBuffer;

// кол-во опрашиваемых каналов модуля
const WORD CHANNELS_QUANTITY = 0x1;
// частота  ввода данных
const double ReadRate = 3000.0;
// номер канала ЦАП
const WORD DacNumber = 0x0;
// частота  вывода данных
const double WriteRate = 100.0;

// экранный счетчик-индикатор
DWORD Counter[2] = { 0x0, 0x0};
DWORD OldCounter[2] = { 0xFFFFFFFF, 0xFFFFFFFF };

// номер ошибки при выполнении потока сбора данных
WORD ReadThreadErrorNumber;
// номер ошибки при выполнении потока вывода данных
WORD WriteThreadErrorNumber;
// флажки завершения потоков сбора и выдачи
bool IsThreadComplete = false;

//------------------------------------------------------------------------
// основная программа
//------------------------------------------------------------------------
void main(void)
{
	WORD i;

	// пока ничего не выделено под буфера ввода-вывода данных
	ReadBuffer  = WriteBuffer = NULL;
	// пока не созданы потоки ввода-вывода данных
	hReadThread = hWriteThread = NULL;
	// пока откытого файла нет :(
	hFile = INVALID_HANDLE_VALUE;
 	// сбросим флаги ошибок потоков ввода и вывода данных
	ReadThreadErrorNumber = WriteThreadErrorNumber = 0x0;

	// зачистим экран монитора
	clrscr();

	printf(" *********************************************\n");
	printf(" Console example for USB3000 Read-Write stream\n");
	printf(" *********************************************\n\n");

	// проверим версию используемой библиотеки Rtusbapi.dll
	if((DllVersion = RtGetDllVersion()) != CURRENT_VERSION_RTUSBAPI)
	{
		char String[128];
		sprintf(String, " Rtusbapi.dll Version Error!!!\n   Current: %1u.%1u. Required: %1u.%1u",
											DllVersion >> 0x10, DllVersion & 0xFFFF,
											CURRENT_VERSION_RTUSBAPI >> 0x10, CURRENT_VERSION_RTUSBAPI & 0xFFFF);

		TerminateApplication(String);
	}
	else printf(" Rtusbapi.dll Version --> OK\n");

	// получим указатель на интерфейс модуля USB3000
	pModule = static_cast<IRTUSB3000 *>(RtCreateInstance("usb3000"));
	if(pModule == NULL)  TerminateApplication(" Module Interface --> Bad\n");
	else printf(" Module Interface --> OK\n");

	// попробуем обнаружить модуль USB3000 в первых 127 виртуальных слотах
	for(i = 0x0; i < MaxVirtualSoltsQuantity; i++) if(pModule->OpenDevice(i)) break;
	// что-нибудь обнаружили?
	if(i == MaxVirtualSoltsQuantity) TerminateApplication(" Can't find module USB3000 in first 127 virtual slots!\n");
	else printf(" OpenDevice(%u) --> OK\n", i);

	// попробуем получить дескриптор (handle) устройства
	ModuleHandle = pModule->GetModuleHandle();
	if(ModuleHandle == INVALID_HANDLE_VALUE) TerminateApplication(" GetModuleHandle() --> Bad\n");
	else printf(" GetModuleHandle() --> OK\n");

	// прочитаем название обнаруженного модуля
	if(!pModule->GetModuleName(ModuleName)) TerminateApplication(" GetModuleName() --> Bad\n");
	else printf(" GetModuleName() --> OK\n");
	// проверим, что это именно 'USB3000'
	if(strcmp(ModuleName, "USB3000")) TerminateApplication(" The module is not 'USB3000'\n");
	else printf(" The module is 'USB3000'\n");

	// узнаем текущую скорость работы шины USB20
	if(!pModule->GetUsbSpeed(&UsbSpeed)) { printf(" GetUsbSpeed() --> Bad\n"); exit(1); }
	else printf(" GetUsbSpeed() --> OK\n");
	// теперь отобразим версию драйвера AVR
	printf(" USB Speed is %s\n", UsbSpeed ? "HIGH (480 Mbit/s)" : "FULL (12 Mbit/s)");

	// прочитаем серийный номер модуля
	if(!pModule->GetModuleSerialNumber(ModuleSerialNumber)) TerminateApplication(" GetModuleSerialNumber() --> Bad\n");
	else printf(" GetModuleSerialNumber() --> OK\n");
	// теперь отобразим серийный номер модуля
	printf(" Module Serial Number is %s\n", ModuleSerialNumber);

	// прочитаем версию драйвера AVR
	if(!pModule->GetAvrVersion(AvrVersion)) TerminateApplication(" GetAvrVersion() --> Bad\n");
	else printf(" GetAvrVersion() --> OK\n");
	// теперь отобразим версию драйвера AVR
	printf(" Avr Driver Version is %s\n", AvrVersion);

	// код драйвера DSP возьмём из соответствующего ресурса штатной DLL библиотеки
	if(!pModule->LOAD_DSP()) TerminateApplication(" LOAD_DSP() --> Bad\n");
	else printf(" LOAD_DSP() --> OK\n");

	// проверим загрузку модуля
 	if(!pModule->MODULE_TEST()) TerminateApplication(" MODULE_TEST() --> Bad\n");
	else printf(" MODULE_TEST() --> OK\n");

	// получим информацию об загруженном драйвере DSP
	if(!pModule->GET_DSP_INFO(&di)) TerminateApplication(" GET_DSP_INFO() --> Bad\n");
	else printf(" GET_DSP_INFO() --> OK\n");
	// теперь отобразим версию загруженного драйвера DSP
	printf(" DSP Driver version is %1u.%1u\n", di.DspMajor, di.DspMinor);

	// прочитаем ППЗУ
	fi.size = sizeof(RTUSB3000::FLASH);
	if(!pModule->GET_FLASH(&fi)) TerminateApplication(" GET_FLASH() --> Bad\n");
	else printf(" GET_FLASH() --> OK\n");

	// проверим присутствует ли на модуле ЦАП
	if(!fi.IsDacPresented) TerminateApplication("\n DAC is absent on this module USB3000!\n");

	// обязательно проинициализируем поле size структуры RTUSB3000::INPUT_PARS
	ip.size = sizeof(RTUSB3000::INPUT_PARS);
	// получим текущие параметры ввода данных
	if(!pModule->GET_INPUT_PARS(&ip)) TerminateApplication(" GET_INPUT_PARS() --> Bad\n");
	else printf(" GET_INPUT_PARS() --> OK\n");

	// зададим желаемые параметры ввода данных
	ip.CorrectionEnabled = true;							// разрешим корректировку данных
	ip.InputClockSource = RTUSB3000::INTERNAL_INPUT_CLOCK;	// будем использовать внутренние тактовые испульсы для ввода данных
//	ip.InputClockSource = RTUSB3000::EXTERNAL_INPUT_CLOCK;	// будем использовать внешние тактовые испульсы для ввода данных
	ip.SynchroType = RTUSB3000::NO_SYNCHRO;			// не будем использовать никакую синхронизацию при вводе данных
//	ip.SynchroType = RTUSB3000::TTL_START_SYNCHRO;	// будем использовать цифровую синхронизацию старта при вводе данных
	ip.ChannelsQuantity = CHANNELS_QUANTITY; 			// один активный канал
	for(i = 0x0; i < ip.ChannelsQuantity; i++) ip.ControlTable[i] = (WORD)(i);
	ip.InputRate = ReadRate;								// частота ввода данных в кГц
	ip.InterKadrDelay = 0.0;
	ip.InputFifoBaseAddress = 0x0;  						// базовый адрес FIFO буфера ввода в драйвере DSP
	ip.InputFifoLength = 0x3000;	 						// длина FIFO буфера ввода в драйвере DSP
	// будем использовать фирменные калибровочные коэффициенты, которые храняться в ППЗУ модуля
	for(i = 0x0; i < 0x8; i++) { ip.AdcOffsetCoef[i] = fi.AdcOffsetCoef[i]; ip.AdcScaleCoef[i] = fi.AdcScaleCoef[i]; }
	// передадим требуемые параметры ввода данных в модуль
	if(!pModule->SET_INPUT_PARS(&ip)) TerminateApplication(" SET_INPUT_PARS() --> Bad\n");
	else printf(" SET_INPUT_PARS() --> OK\n");

	// прочитаем текущие параметры вывода данных
	dp.size = sizeof(RTUSB3000::OUTPUT_PARS);
	if(!pModule->GET_OUTPUT_PARS(&dp)) TerminateApplication(" GET_OUTPUT_PARS() --> Bad\n");
	else printf(" GET_OUTPUT_PARS() --> OK\n");

	// зададим желаемые параметры вывода данных
	dp.OutputRate = WriteRate;		  			// частота вывода данных в кГц
	dp.OutputFifoBaseAddress = 0x3000;   	// базовый адрес FIFO буфера вывода в драйвере DSP
	dp.OutputFifoLength = 0xF80;				// длина FIFO буфера вывода в драйвере DSP

	// установим требуемые параметры вывода данных
	if(!pModule->SET_OUTPUT_PARS(&dp)) TerminateApplication(" SET_OUTPUT_PARS() --> Bad\n");
	else printf(" SET_OUTPUT_PARS() --> OK\n");

	// попробуем выделить память под буфер вводимых данных
	ReadBuffer = new SHORT[ReadPoints];
	if(!ReadBuffer) TerminateApplication(" Cannot allocate ReadBuffer memory\n");
	// попробуем выделить память под буфер выводимых данных
	WriteBuffer = new SHORT[WritePoints];
	if(!WriteBuffer) TerminateApplication(" Cannot allocate WriteBuffer memory\n");

	printf(" \n");
	printf(" Module USB3000 (S/N %s) is ready ... \n", fi.SerialNumber);
	printf(" Read parameters:\n");
	printf("   InputClockSource is %s\n", ip.InputClockSource ? "EXTERNAL" : "INTERNAL");
	printf("   SynchroType is %s\n", ip.SynchroType ? "TTL_START_SYNCHRO" : "NO_SYNCHRO");
	printf("   ChannelsQuantity = %2d\n", ip.ChannelsQuantity);
	printf("   AdcRate = %8.3f kHz\n", ip.InputRate);
	printf("   InterKadrDelay = %2.4f ms\n", ip.InterKadrDelay);
	printf("   ChannelRate = %8.3f kHz\n", ip.ChannelRate);
	printf(" Write parameters:\n");
	printf("   DacRate = %6.1f kHz\n\n", dp.OutputRate);

	// Создаем и запускаем поток сбора данных
	hReadThread = CreateThread(0, 0x2000, ServiceReadThread, 0, 0, &ReadTid);
	if(!hReadThread) TerminateApplication("Cann't start input data thread!");
	// Создаем и запускаем поток выдачи данных
	hWriteThread = CreateThread(0, 0x2000, ServiceWriteThread, 0, 0, &WriteTid);
	if(!hWriteThread) TerminateApplication("Cann't start output data thread!");

	// ждем завершения работы обоих потоков
	while(!IsThreadComplete)
	{
		if(Counter[0] != OldCounter[0]) { printf(" Read Counter %3lu from %3lu\r", Counter[0], NBlockRead); OldCounter[0] = Counter[0]; }
		else Sleep(20);
	}
	// ждём окончания работы потока ввода данных
	WaitForSingleObject(hReadThread, INFINITE);
	// ждём окончания работы потока вывода данных
	WaitForSingleObject(hWriteThread, INFINITE);
	// две пустые строчки
	printf("\n\n");

	// если не было ошибок ввода или вывода данных - запишем полученные данные в файл
 	if(!ReadThreadErrorNumber | WriteThreadErrorNumber)
	{
		// откроем файл для записи полученных данных
		hFile = CreateFile("Test.dat", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
		if(hFile == INVALID_HANDLE_VALUE) TerminateApplication(" Open file 'Test.dat' --> Failed!!!\n");
		else printf(" CreateFile(Test.dat) --> Ok\n");

		// теперь запишем в файл полученные данные
		DWORD FileBytesWritten=0x0;
		if(!WriteFile(	hFile,					// handle to file to write to
	    					ReadBuffer,		 		// pointer to data to write to file
							2*ReadPoints,	 		// number of bytes to write
    						&FileBytesWritten,	// pointer to number of bytes written
					   	NULL			  			// pointer to structure needed for overlapped I/O
					   )) TerminateApplication(" WriteFile(Test.dat) --> Failed!!!");
		else printf(" WriteFile(Test.dat) --> Ok\n");
	}

	// если были ошибки - сообщим об этом
	if(ReadThreadErrorNumber | WriteThreadErrorNumber) { TerminateApplication(NULL, false); ShowThreadErrorMessage(); }
	else { printf("\n"); TerminateApplication("\n The program was completed successfully!!!\n", false); }
}

//------------------------------------------------------------------------
// Поток, в котором осуществляется ввод данных с модуля
//------------------------------------------------------------------------
DWORD WINAPI ServiceReadThread(PVOID /*Context*/)
{
	WORD i;
	WORD RequestNumber;
	// идентификатор массива их двух событий
	HANDLE ReadEvent[2];
	// массив OVERLAPPED структур из двух элементов
	OVERLAPPED ReadOv[2];
	DWORD BytesTransferred[2];
	DWORD TimeOut;

	// остановим ввод данных и одновременно прочистим соответствующий канал bulk USB
	if(!pModule->STOP_READ()) { ReadThreadErrorNumber = 0x6; IsThreadComplete = true; return 0; }

	// создадим два события
	ReadEvent[0] = CreateEvent(NULL, FALSE , FALSE, NULL);
	memset(&ReadOv[0], 0, sizeof(OVERLAPPED)); ReadOv[0].hEvent = ReadEvent[0];
	ReadEvent[1] = CreateEvent(NULL, FALSE , FALSE, NULL);
	memset(&ReadOv[1], 0, sizeof(OVERLAPPED)); ReadOv[1].hEvent = ReadEvent[1];

	// таймаут ввода данных
	TimeOut = DataStep/ReadRate + 1000;

	// делаем предварительный запрос на ввод данных
	RequestNumber = 0;
	if(!pModule->ReadData(ReadBuffer, &DataStep, &BytesTransferred[RequestNumber], &ReadOv[RequestNumber]))
				if(GetLastError() != ERROR_IO_PENDING) { CloseHandle(ReadEvent[0]); CloseHandle(ReadEvent[1]); ReadThreadErrorNumber = 0x2; IsThreadComplete = true; return 0; }

	// теперь запускаем ввод данных
	if(pModule->START_READ())
	{
		// цикл сбора данных
		for(i = 0x1; i < NBlockRead; i++)
		{
			RequestNumber ^= 0x1;
			// сделаем запрос на очередную порции данных
			if(!pModule->ReadData(ReadBuffer + i*DataStep, &DataStep, &BytesTransferred[RequestNumber], &ReadOv[RequestNumber]))
					if(GetLastError() != ERROR_IO_PENDING) { ReadThreadErrorNumber = 0x2; break; }

			// ждём окончания операции сбора очередной порции данных
			if(WaitForSingleObject(ReadEvent[RequestNumber^0x1], TimeOut) == WAIT_TIMEOUT)
				            		{ ReadThreadErrorNumber = 0x3; break; }

			if(ReadThreadErrorNumber) break;
			else if(kbhit()) { ReadThreadErrorNumber = 0x1; break; }
			else Sleep(20);
			Counter[0]++;
		}
		// ждём окончания операции сбора последней порции данных
		if(!ReadThreadErrorNumber)
		{
			RequestNumber ^= 0x1;
			if(WaitForSingleObject(ReadEvent[RequestNumber^0x1], TimeOut) == WAIT_TIMEOUT) ReadThreadErrorNumber = 0x3;
			Counter[0]++;
		}
	}
	else { ReadThreadErrorNumber = 0x5; }

	// остановим ввод данных
	if(!pModule->STOP_READ()) ReadThreadErrorNumber = 0x6;
	// прервём возможно незавершённый асинхронный запрос на ввод данных
	if(!CancelIo(ModuleHandle)) { ReadThreadErrorNumber = 0x7; }
	// освободим все идентификаторы событий
	for(i = 0x0; i < 0x2; i++) CloseHandle(ReadEvent[i]);
	// небольшая задержка
	Sleep(100);
	// установим флажок окончания потока сбора данных
	IsThreadComplete = true;
	// теперь можно спокойно выйти из потока сбора данных
	return 0;
}

//------------------------------------------------------------------------
// Поток, в котором осуществляется вывод данных на модуль
//------------------------------------------------------------------------
DWORD WINAPI ServiceWriteThread(PVOID /*Context*/)
{
	WORD RequestNumber;
	DWORD i;
	DWORD BaseIndex;
	// идентификатор события
	HANDLE WriteEvent[2];
	// OVERLAPPED структура для асинхронной записи данных в модуль
	OVERLAPPED WriteOv[2];
	DWORD BytesTransferred[2];

	// параметры выводимого на ЦАП сигнала
	double CurrentTime = 0.0; 			  		// в млс
	double SignalFrequency = 1.0; 	  		// в кГц
	double SignalAmplitude = 2000.0;			// в кодах ЦАП

	// остановим вывод данных и одновременно прочистим соответствующий канал bulk USB  (PIPE_RESET)
	if(!pModule->STOP_WRITE()) { WriteThreadErrorNumber = 0x6; IsThreadComplete = true; return 0; }

	// формируем данные для целого FIFO буфера ЦАП (без учёта корректировки)
	for(i = 0x0; i < (DWORD)dp.OutputFifoLength; i++)
	{
		WriteBuffer[i] = (WORD)(2047.0 + SignalAmplitude*sin(2.*M_PI*SignalFrequency*CurrentTime));
		WriteBuffer[i] &= (WORD)(0xFFF);
		WriteBuffer[i] |= (WORD)(DacNumber << 15) | (WORD)(0x1 << 14);
		CurrentTime += 1.0/dp.OutputRate;
	}
	// заполняем целиком FIFO буфер ЦАП в модуле
	if(!pModule->PUT_DM_ARRAY(dp.OutputFifoBaseAddress, dp.OutputFifoLength, (SHORT *)WriteBuffer))
   										{ WriteThreadErrorNumber = 0x1; IsThreadComplete = true; return 1; }

	// теперь формируем данные для целого буфера WriteBuffer (без учёта корректировки)
	for(i = 0x0; i < 2*DataStep; i++)
	{
		WriteBuffer[i] = (WORD)(2047.0 + SignalAmplitude*sin(2.*M_PI*SignalFrequency*CurrentTime));
		WriteBuffer[i] &= (WORD)(0xFFF);
		WriteBuffer[i] |= (WORD)(DacNumber << 15) | (WORD)(0x1 << 14);
		CurrentTime += 1.0/dp.OutputRate;
	}

	WriteEvent[0] = CreateEvent(NULL, FALSE , FALSE, NULL);
	memset(&WriteOv[0],0,sizeof(OVERLAPPED)); WriteOv[0].hEvent=WriteEvent[0];
	WriteEvent[1] = CreateEvent(NULL, FALSE , FALSE, NULL);
	memset(&WriteOv[1],0,sizeof(OVERLAPPED)); WriteOv[1].hEvent=WriteEvent[1];

	RequestNumber = 0x0;
	// сделаем первый запрос данных
	if(!pModule->WriteData(WriteBuffer, &DataStep, &BytesTransferred[RequestNumber], &WriteOv[RequestNumber]))
				if(GetLastError() != ERROR_IO_PENDING) { CloseHandle(WriteEvent[0]); CloseHandle(WriteEvent[1]); WriteThreadErrorNumber = 0x2; IsThreadComplete = true; return 0; }

	// разрешим вывод данных
	if(pModule->START_WRITE())
	{
		// вечный цикл передачи данных в модуль
		while(!IsThreadComplete)
		{
			RequestNumber ^= 0x1;
			// запустим асинхронную перадачу новых данных в модуль из второй половинки WriteBuffer
			if(!pModule->WriteData(WriteBuffer + RequestNumber*DataStep, &DataStep, &BytesTransferred[RequestNumber], &WriteOv[RequestNumber]))
					if(GetLastError() != ERROR_IO_PENDING) { WriteThreadErrorNumber = 0x2; break; }

			// ожидание завершения запроса на вывод очередной порции данных
			if(!WaitingForWriteRequestCompleted(&WriteOv[RequestNumber^0x1])) break;

			// сформируем следующую порцию данных (без учёта корректировки)
			BaseIndex = (RequestNumber^0x1)*DataStep;
			for(i = 0x0; i < DataStep; i++)
			{
				WriteBuffer[i + BaseIndex] = (WORD)(2047.0 + SignalAmplitude*sin(2.*M_PI*SignalFrequency*CurrentTime));
				WriteBuffer[i + BaseIndex] &= (WORD)(0xFFF);
				WriteBuffer[i + BaseIndex] |= (WORD)(DacNumber << 15) | (WORD)(0x1 << 14);
				CurrentTime += 1.0/dp.OutputRate;
	  		}

			if(IsThreadComplete) break;
			else if(kbhit()) { WriteThreadErrorNumber = 0x3; break; }
			else Sleep(20);
			Counter[1]++;
		}
	}
	else { WriteThreadErrorNumber = 0x5; }

	// остановим вывод данных и одновременно прочистим соответствующий канал bulk USB  (PIPE_RESET)
	if(!pModule->STOP_WRITE()) WriteThreadErrorNumber = 0x6;
	// прервём возможно незавершённый асинхронный запрос на вывод данных
	if(!CancelIo(ModuleHandle)) { WriteThreadErrorNumber = 0x7; }
	// освободим идентификатор события
	for(i = 0x0; i < 0x2; i++) CloseHandle(WriteEvent[i]);
	// небольшая задержка
	Sleep(100);
	// установим флажок окончания потока выдачи данных
	IsThreadComplete = true;
	// теперь можно спокойно выйти из потока вывода данных
	return 0;
}

//------------------------------------------------------------------------------
// ожидание завершения выполнения очередного запроса на вывод данных
//------------------------------------------------------------------------------
BOOL WaitingForWriteRequestCompleted(OVERLAPPED * const WriteOv)
{
	DWORD BytesTransferred;

	while(true)
	{
			if(GetOverlappedResult(ModuleHandle, WriteOv, &BytesTransferred, FALSE)) break;
			else if(GetLastError() !=  ERROR_IO_INCOMPLETE) { WriteThreadErrorNumber = 0x2; return FALSE; }
			else if(IsThreadComplete) return FALSE;
			else Sleep(20);
	}
	return TRUE;
}

//------------------------------------------------------------------------
// Отобразим сообщение с ошибкой
//------------------------------------------------------------------------
void ShowThreadErrorMessage(void)
{
	switch(ReadThreadErrorNumber)
	{
		case 0x0:
			break;

		case 0x1:
			// если программа была злобно прервана, предъявим ноту протеста
			printf("\n READ Thread: The program was terminated! :(((\n");
			break;

		case 0x2:
			printf("\n READ Thread: ReadData() --> Bad :(((\n");
			break;

		case 0x3:
			printf("\n READ Thread: Timeout is occured :(((\n");
			break;

		case 0x4:
			printf("\n READ Thread: Buffer Data Error! :(((\n");
			break;

		case 0x5:
			printf("\n READ Thread: START_READ() --> Bad :(((\n");
			break;

		case 0x6:
			printf("\n READ Thread: STOP_READ() --> Bad! :(((\n");
			break;

		case 0x7:
			printf("\n READ Thread: Can't complete input and output (I/O) operations! :(((");
			break;

		default:
			printf("\n READ Thread: Unknown error! :(((\n");
			break;
   }


	switch(WriteThreadErrorNumber)
	{
		case 0x0:
			break;

		case 0x1:
			printf("\n WRITE Thread: PUT_DM_ARRAY() --> Bad! :(((\n");
			break;

		case 0x2:
			printf("\n WRITE Thread: Write Request --> Bad :(((\n");
			break;

		case 0x3:
			// если программа была злобно прервана, предъявим ноту протеста
			printf("\n WRITE Thread: The program was terminated! :(((\n");
			break;

		case 0x4:
			printf("\n WRITE Thread: Timeout is occured :(((\n");
			break;

		case 0x5:
			printf("\n WRITE Thread: START_WRITE() --> Bad :(((\n");
			break;

		case 0x6:
			printf("\n WRITE Thread: STOP_WRITE() --> Bad! :(((\n");
			break;

		case 0x7:
			printf("\n WRITE Thread: Can't complete input and output (I/O) operations! :(((");
			break;

		default:
			printf("\n WRITE Thread: Unknown error! :(((\n");
			break;
	}

	return;
}

//------------------------------------------------------------------------
// вывод сообщения и, если нужно, аварийный выход из программы
//------------------------------------------------------------------------
void TerminateApplication(char *ErrorString, bool TerminationFlag)
{
	// подчищаем интерфейс модуля
	if(pModule)
	{
		// освободим интерфейс модуля
		if(!pModule->ReleaseInstance()) printf(" ReleaseInstance() --> Bad\n");
		else printf(" ReleaseInstance() --> OK\n");
		// обнулим указатель на интерфейс модуля
		pModule = NULL;
	}

	// освобождаем занятые ресурсы
	if(ReadBuffer) { delete[] ReadBuffer; ReadBuffer = NULL; }
	if(WriteBuffer) { delete[] WriteBuffer; WriteBuffer = NULL; }
	// освободим идентификатор потока сбора данных
	if(hReadThread) { CloseHandle(hReadThread); hReadThread = NULL; }
	// освободим идентификатор потока вывода данных
	if(hWriteThread) { CloseHandle(hWriteThread); hWriteThread = NULL; }
	// освободим идентификатор файла данных
	if(hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; }

	// выводим текст сообщения
	if(ErrorString) printf(ErrorString);

	// если нужно - аварийно завершаем программу
	if(TerminationFlag) exit(1);
	else return;
}

