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

#include <vcl.h>
#pragma hdrstop

#include "powerdaq_ain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "iScope"
#pragma link "iComponent"
#pragma link "iCustomComponent"
#pragma link "iPlot"
#pragma link "iPlotComponent"
#pragma link "iVCLComponent"
#pragma link "LPControlDrawLayers"
#pragma link "SLControlCollection"
#pragma link "SLScope"
#pragma link "VCL.LPControl"
#pragma link "iLabel"
#pragma link "iModeComboBox"
#pragma link "iSwitchLed"
#pragma resource "*.dfm"
TForm1 *Form1;

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{

}

__fastcall TForm1::InitBufferedAI(tBufferedAiData *pAiData)
{
  int retVal = 0;
  Adapter_Info adaptInfo;
  
  // initializes acquisition session parameters
  pAiData->board = 0;
  pAiData->handle = 0;
  pAiData->driver = 0;
  pAiData->abort = FALSE;
  pAiData->nbOfChannels = 8;
  pAiData->nbOfFrames = 4;
  for (int i = 0; i < pAiData->nbOfChannels; i++) {
    pAiData->channelList[i] = i;
  }
  pAiData->rawBuffer = NULL;
  /*
   * Dictates how often we "wake up":
   *   at scanRate of 34375 x 8 channels, a 1024 sample buffer -> every 30ms
   */
  pAiData->nbOfSamplesPerChannel = 1024;
  pAiData->scanRate = 34375;
  pAiData->polarity = AIN_BIPOLAR;
  pAiData->range = AIN_RANGE_10V;
  pAiData->inputMode = AIN_SINGLE_ENDED;
  pAiData->state = closed;
  //   if(params.trigger == 1)
  //	  pAiData->trigger = AIB_STARTTRIG0;
  //   else if(params.trigger == 2)
  //	  pAiData->trigger = AIB_STARTTRIG0 + AIB_STARTTRIG1;
  //   else
  pAiData->trigger = 0;

  retVal = PdDriverOpen(&pAiData->driver, &pAiData->Error, &pAiData->nbOfBoards);
  if (!retVal) {
    Memo1->Lines->Add("No adapters found");
    return 0;
  }
  
  //   printf("Number of adapters installed: %d.\n", pAiData->nbOfBoards);
  
  retVal = _PdAdapterOpen(pAiData->board, &pAiData->Error, &pAiData->handle);
  if(!retVal)
    {
      Memo1->Lines->Add("PdAdapterOpen failed with " + 
			IntToStr((int) pAiData->Error));
      return (0);
    }
  
   // get adapter type
   retVal = _PdGetAdapterInfo(pAiData->board, &pAiData->Error, &adaptInfo);
   if (!retVal)
   {
     //	  printf("BufferedAI: __PdGetAdapterInfo error %d\n", retVal);
     return 0;
   }
   
   pAiData->maxRate = adaptInfo.SSI[AnalogIn].dwRate;
   
   retVal = PdAdapterAcquireSubsystem(pAiData->handle, 
				      &pAiData->Error, AnalogIn, 1);
   if(!retVal)
     {
       //	  printf("BufferedAI: PdAdapterAcquireSubsystem failed\n");
       return 0;
     }
   
   pAiData->state = unconfigured;
   
   retVal = _PdAInReset(pAiData->handle, &pAiData->Error);
   if (!retVal)
     {
       //	  printf("BufferedAI: PdAInReset error %d\n", retVal);
       return 0;
     }
   
   
   buffer = (double *) malloc(pAiData->nbOfChannels * pAiData->nbOfSamplesPerChannel *
			      sizeof(double));
   if(buffer == NULL)
     {
       //	  printf("SingleAI: could not allocate enough memory for the acquisition buffer\n");
       //	  exit(EXIT_FAILURE);
     }
   
   Memo1->Lines->Add("Initialized " + IntToStr((int) pAiData->nbOfBoards) + " board(s)");
   return 1;
}

__fastcall TForm1::ReleaseBufferedAI(tBufferedAiData *pAiData)
{
  int retVal;
  if(pAiData->state == running)
    {
      retVal = _PdAInAsyncStop(pAiData->handle, &pAiData->Error);
      pAiData->state = configured;
   }

   if(pAiData->state == configured)
   {
	  retVal = _PdAInClearPrivateEvent(pAiData->handle, &pAiData->hNotifyEvent);

	  retVal = _PdClearUserEvents(pAiData->handle, &pAiData->Error, AnalogIn, eAllEvents);

	  retVal = _PdAInAsyncTerm(pAiData->handle, &pAiData->Error);

	  retVal = _PdReleaseBuffer(pAiData->handle, &pAiData->Error, AnalogIn, pAiData->rawBuffer); //r3

	  pAiData->state = unconfigured;
   }

   if(pAiData->handle > 0 && pAiData->state == unconfigured)
   {
	  retVal = PdAdapterAcquireSubsystem(pAiData->handle,&pAiData->Error, AnalogIn, 0);
	  
	  _PdAdapterClose(pAiData->handle, &pAiData->Error);

	  PdDriverClose(pAiData->driver, &pAiData->Error);
   }

   pAiData->state = closed;

   if (buffer) {
     free(buffer);
     buffer = NULL;
   }
   return retVal;
}

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

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  InitBufferedAI(&(Form1->pdThread->AiData));
  SetEvent(AcquireEvent);
  Running = 1;
  Button1->Enabled = 0;
  Button2->Enabled = 1;
}

void __fastcall TForm1::Button2Click(TObject *Sender)
{
  ReleaseBufferedAI(&(Form1->pdThread->AiData));
  Button2->Enabled = 0;
  Button1->Enabled = 1;
  Running = 0;
}


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

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  TiPlot *plots[3] = { iPlot1, iPlot2, iPlot3 };
  TColor colors[3] = { TColor(255), TColor(255<<24|255<8), TColor(255<<8) };
  for (int i = 0; i < 3; i++) {
    plots[i]->XAxis[0]->Span = 20;
    plots[i]->Channel[0]->RingBufferSize = 2048;
    plots[i]->Channel[0]->Color = colors[i];
    plots[i]->XAxis[0]->TrackingEnabled = False;
    plots[i]->YAxis[0]->TrackingEnabled = False;
    plots[i]->YAxis[0]->Visible = False;
    
    plots[i]->YAxis[0]->Min = -10;
    plots[i]->YAxis[0]->Span = 20;
    
    
    plots[i]->AddAnnotation();
    plots[i]->Annotation[0]->Style = ipasText;
    plots[i]->Annotation[0]->PenColor = clYellow;
    plots[i]->Annotation[0]->TextHorzAlignment = iahRight;
    plots[i]->Annotation[0]->X = 100;
    plots[i]->Annotation[0]->Y = 88;
    plots[i]->Annotation[0]->Text = "2.50";
    
    plots[i]->AddLimit();
    plots[i]->Limit[0]->Line1Position = 2.5;
    plots[i]->Limit[0]->Style = iplsLineY;
    plots[i]->Limit[0]->Color = clYellow;
    plots[i]->Limit[0]->UserCanMove = True;
    plots[i]->ToolBar[0]->DoButtonClickSelect();
  }
  
  // Use this to initiate acquisition thread
  AcquireEvent = CreateEvent(
			     NULL,               // default security attributes
			     FALSE,               // manual-reset event
			     FALSE,              // initial state is nonsignaled
			     TEXT("AcquireEvent")  // object name
			     );
  
  Running = 0;
  
  String msg = "TCP Server started on port: " + IntToStr(IdTCPServer1->DefaultPort);
  Form1->Memo1->Lines->Add(msg);
  
  pdThread = new TPowerDaqThread();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
  pdThread->Terminate();
  CloseHandle(AcquireEvent);
  
}
//---------------------------------------------------------------------------

__fastcall TPowerDaqThread::TPowerDaqThread(void) : TThread(False)
{
  FreeOnTerminate = True;
}

void __fastcall TPowerDaqThread::Execute(void)
{
  while (!Terminated) {
    WaitForSingleObject(Form1->AcquireEvent, INFINITE);
    
    if ((Form1->pdThread->AiData).nbOfBoards) {
      BufferedAI(&(Form1->pdThread->AiData), Form1->buffer);
    }
    else {
      while (Form1->Running && !Terminated) {
	Sleep(30);
	
	/* Should fill data buffers here */
	
	Synchronize(UpdateDisplay);
      }
    }
    Message = "Acquisition stopped";
    Synchronize(MemoMessage);
  }
}

void __fastcall TPowerDaqThread::MemoMessage(void)
{
  if (Message.Length()) {
    Form1->Memo1->Lines->Add(Message);
  }
  Message.SetLength(0);
}

double __fastcall TPowerDaqThread::GetNoise(double Magnitude)
{
  //Noise is added to give data a "real-world" look
  return ((double)random(1000)/1000.0) * Magnitude/1000;
}

void __fastcall TPowerDaqThread::UpdateDisplay(void)
{
  double timeIncr = .029090909;
  double x = 0.0;
  int i, j;
  for (i = 0; i < 2048; i++) {
    Form1->iPlot1->Channel[0]->AddXY(x, GetNoise(1000)+6.*sin(x*3.));
    Form1->iPlot2->Channel[0]->AddXY(x, GetNoise(1000)+6.*sin(x*3.));
    Form1->iPlot3->Channel[0]->AddXY(x, GetNoise(1000)+6.*sin(x*4.));
    x+=timeIncr;
  }
}

void __fastcall TPowerDaqThread::UpdateDisplayReal(void)
{
  tBufferedAiData *pAiData = &(Form1->pdThread->AiData);
  double timeIncr = .029090909;
  double x = 0.0;
  int i, j;
  for (i = 0, j = 0; i < pAiData->nbOfSamplesPerChannel; i++, j+=pAiData->nbOfChannels) {
    Form1->iPlot1->Channel[0]->AddXY(x, Form1->buffer[j]);
    Form1->iPlot2->Channel[0]->AddXY(x, Form1->buffer[j]+GetNoise(1000));
    Form1->iPlot3->Channel[0]->AddXY(x, Form1->buffer[j]);
    x+=timeIncr;
  }
  
}



void __fastcall TPowerDaqThread::BufferedAI(tBufferedAiData *pAiData, double *buffer)
{
   int retVal;
   DWORD cldivider, cvdivider;
   DWORD eventsToNotify = eFrameDone | eBufferDone | eTimeout | eBufferError | eStopped;
   DWORD event;
   DWORD dwWaitEventResult;
   DWORD scanIndex, numScans;
   DWORD T1;
   int count;
   int i = 0;
   DWORD aiCfg;
   pAiData->hNotifyEvent = NULL;


   // setup the board to use hardware internal clock
   aiCfg = AIB_INTCLSBASE | AIB_INTCVSBASE | 
     AIB_CVSTART0 | AIB_CVSTART1 | AIB_CLSTART0 | 
     pAiData->range | pAiData->inputMode |
     pAiData->polarity | pAiData->trigger;
   retVal = _PdAcquireBuffer(pAiData->handle,&pAiData->Error,
			     (void **) &pAiData->rawBuffer,
			     pAiData->nbOfFrames, pAiData->nbOfSamplesPerChannel,
			     pAiData->nbOfChannels, AnalogIn, /*BUF_BUFFERRECYCLED |*/ BUF_BUFFERWRAPPED);
   if (!retVal)
     {
       //	  printf("BufferedAI: PdRegisterBuffer error %d\n", retVal);
       return;
     }
   
   // set clock divider, assuming that we use the 33MHz timebase
   cldivider = (unsigned long)(33000000.0 / pAiData->scanRate)-1;
   
   // Program interchannel delay to the maximum speed of the board
   cvdivider = (unsigned long)(33000000.0 / pAiData->maxRate)-1;
   
   retVal = _PdAInAsyncInit(pAiData->handle, &pAiData->Error, aiCfg, 0, 0,
			    cvdivider, cldivider, eventsToNotify,
			    pAiData->nbOfChannels, pAiData->channelList);
   if (!retVal)
     {
       //	  printf("TestAsyncAI: PdAInAsyncInit error %d\n", retVal);
       return;
     }
   
   pAiData->state = configured;
   
   retVal = _PdAInSetPrivateEvent(pAiData->handle, &pAiData->hNotifyEvent);
   if (!retVal)
     {
       //	  printf("BufferedAI: _PdAInSetPrivateEvent error %d\n", retVal);
       return;
     }
   
   retVal = _PdSetUserEvents(pAiData->handle, &pAiData->Error, AnalogIn, eventsToNotify);
   if (!retVal)
     {
       //	  printf("BufferedAI: _PdSetUserEvents error %d\n", retVal);
       return;
     }
   
   retVal = _PdAInAsyncStart(pAiData->handle,&pAiData->Error);
   if (!retVal)
     {
       //	  printf("BufferedAI: PdAInAsyncStart error %d\n", retVal);
       return;
     }
   
   pAiData->state = running;
   
   T1 = GetTickCount();
   count = 0;
   
   while(!pAiData->abort)
     {
       // if ((dwWaitEventResult = WaitForSingleObject(pAiData->hNotifyEvent, 1000)) == WAIT_OBJECT_0)
       dwWaitEventResult = WaitForSingleObject(pAiData->hNotifyEvent, 1000);
       if(dwWaitEventResult == WAIT_OBJECT_0)
	 {
	   
	   retVal = _PdGetUserEvents(pAiData->handle, &pAiData->Error, AnalogIn, &event);
	   if (!retVal)
	     {
	       //	printf("BufferedAI: _PdGetUserEvents error %d\n", retVal);
	       return;
	     }
	   
	   if (event & eTimeout)
	     {
	       //			printf("BufferedAI: wait timed out\n");
	       return;
	     }
	   
	   if ((event & eBufferError) || (event & eStopped))
	     {
	       //			printf("BufferedAI: buffer error\n");
	       return;
	     }
	   
	   if ((event & eFrameDone))
	     {
	       retVal = _PdAInGetScans(pAiData->handle, &pAiData->Error, pAiData->nbOfSamplesPerChannel, &scanIndex, &numScans);
	       if(!retVal)
		 {
		   //			   printf("BufferedAI: buffer error\n");
		   return;
		 }
	       
	       //			printf("BufferedAI: got %d scans at %d\n", numScans, scanIndex);
	       
	       retVal = PdAInRawToVolts(pAiData->handle, aiCfg, pAiData->rawBuffer,
					buffer, numScans * pAiData->nbOfChannels);
	       if(!retVal)
		 {
		   //			   printf("BufferedAI: Error %d in PdAInRawToVolts\n", retVal);
		   return;
		 }
	       
	       /*printf("BufferedAI:");
		 for(i=0; i<pAiData->nbOfChannels; i++)
		 printf(" ch%d = %f", i, *(buffer + i));
		 printf("\n");*/
	       
	       retVal = _PdSetUserEvents(pAiData->handle, &pAiData->Error, AnalogIn, event/*sToNotify*/);
	       if (!retVal)
		 {
		   //			   printf("BufferedAI: _PdSetUserEvents error %d\n", retVal);
		   return;
		 }
	       
	       count++;
	       if(!(count%30))
		 {
		   DWORD T2 = GetTickCount();
		   // 			   Message = "Rate -> " + FloatToStr(pAiData->nbOfSamplesPerChannel * 30000.0/(T2-T1));
		   //	   Message = "Update Interval: " + FloatToStr((T2-T1)/30.) + "ms";
		   //		   Synchronize(MemoMessage);
		   
		   T1 = T2;
		 }
	       
	       // Update the display
	       Synchronize(UpdateDisplayReal);
	     }
	 }
       else
	 {
	   //		 printf(".");//Timeout
	 }
     }//end while loop
   
   return;
}

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

void __fastcall TForm1::iPlot1LimitLine1PositionChange(int Index, double OldValue,
		  double NewValue)
{
	String valstr = FloatToStrF(NewValue, ffNumber, 5, 2);
	iPlot1->Annotation[0]->Text = valstr;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::iPlot2LimitLine1PositionChange(int Index, double OldValue,
		  double NewValue)
{
	String valstr = FloatToStrF(NewValue, ffNumber, 5, 2);
	iPlot2->Annotation[0]->Text = valstr;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::iPlot3LimitLine1PositionChange(int Index, double OldValue,
          double NewValue)
{
	String valstr = FloatToStrF(NewValue, ffNumber, 5, 2);
	iPlot3->Annotation[0]->Text = valstr;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
	String lCmd;
	lCmd = AContext->Connection->IOHandler->ReadLn();

	if (lCmd.SubString(0,5)  == "SCALE") {
		String newScaleStr = lCmd.SubString(7,lCmd.Length()-6);
//		iScope1->Channel[2]->VoltsPerDivision = newScaleStr.ToDouble();
		AContext->Connection->IOHandler->WriteLn("Label -> " + newScaleStr);
	}
	else if (lCmd.SubString(0,4)  == "QUIT") {
		AContext->Connection->IOHandler->WriteLn("Goodbye...");
		AContext->Connection->Disconnect();
	}
}
//---------------------------------------------------------------------------

