/*
 * This program serves only to demonstrate the functionality of VersaPic electronics 
 * The program can be used for Ibsen Freedom, ROCK and Eagle spectrometer equiped with Versapic electronics.
 * Examples of how to address the Thermo Electric Cooling PCB board are also included.
 * 
 * 
 * Ibsen Phtononics - RBS
 */

using System;
using System.Threading;
using System.IO.Ports;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;



namespace JetiVersaPIC
{
	class Program
	{

		static SerialPort comPort;
		static void Main(string[] args)
		{
			// Create a new SerialPort object with default settings.
			comPort = new SerialPort();

			// Allow the user to set the appropriate properties.
			// The 921600 is most often used for Freedom Spectrometer, while 115200 is for ROCK and TEC PCB's
			comPort.PortName = "COM16";
			comPort.BaudRate = 921600;
			//comPort.BaudRate = 115200;
			comPort.Parity = 0;
			comPort.DataBits = 8;
			comPort.StopBits = StopBits.One;
			comPort.Handshake = Handshake.None;

			// Set the read/write timeouts
			comPort.ReadTimeout = 100;
			comPort.WriteTimeout = 100;
			comPort.Open();

			// This section of the code, read different parameter from a TEC board.
			/*
			//----------------------------------------------------------------------------------------------------
			int TecBaud = TECGetBaud();
			Console.WriteLine(TecBaud);
			//----------------------------------------------------------------------------------------------------

			//----------------------------------------------------------------------------------------------------
			double TecTemp = TECGetTemp();
			Console.WriteLine(TecTemp);
			//----------------------------------------------------------------------------------------------------

			//----------------------------------------------------------------------------------------------------
			double TecKP = TECGetKP();
			Console.WriteLine(TecKP);
			//----------------------------------------------------------------------------------------------------

			//----------------------------------------------------------------------------------------------------
			double TecKI = TECGetKI();
			Console.WriteLine(TecKI);
			//----------------------------------------------------------------------------------------------------

			//----------------------------------------------------------------------------------------------------
			double SetTemp = TECGetSetTemp();
			Console.WriteLine(SetTemp);
			//----------------------------------------------------------------------------------------------------

			//----------------------------------------------------------------------------------------------------
			double TecThres = TECGetAdapThreshold();
			Console.WriteLine(TecThres);
			//----------------------------------------------------------------------------------------------------

			
			// This section of the code, sets a new target temperature for the TEC board.
			// Then reads the current temperature of the TEC in intervals of 2 seconds.
			//----------------------------------------------------------------------------------------------------
			double Temp = 0;
			TECSetAdapThreshold(Temp);
			//----------------------------------------------------------------------------------------------------

			TecTemp = TECGetTemp();
			Console.WriteLine(TecTemp);

			Thread.Sleep(2000);

			TecTemp = TECGetTemp();
			Console.WriteLine(TecTemp);

			Thread.Sleep(2000);

			TecTemp = TECGetTemp();
			Console.WriteLine(TecTemp);

			Thread.Sleep(2000);

			TecTemp = TECGetTemp();
			Console.WriteLine(TecTemp); */
			//----------------------------------------------------------------------------------------------------


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

			// Here the examples for how to address the VersaPic eqiupped spectrometer, starts.  

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

			//Read Calibration values
			//----------------------------------------------------------------------------------------------------
			/*
			double[] ReturnedVal = Get_calibrationcoefficients();
			Console.WriteLine(ReturnedVal[0]);
			Console.WriteLine(ReturnedVal[1]);
			Console.WriteLine(ReturnedVal[2]);
			Console.WriteLine(ReturnedVal[3]);
			Console.WriteLine(ReturnedVal[4]);
			//----------------------------------------------------------------------------------------------------

			//Read number of detector pixels
			//----------------------------------------------------------------------------------------------------
			double pixval = GetPixnNum();
			Console.WriteLine(pixval);
			//----------------------------------------------------------------------------------------------------

			//Read specrotmeter serial number
			//----------------------------------------------------------------------------------------------------
			double Serial = GetSerial();
			Console.WriteLine(Serial);
			//----------------------------------------------------------------------------------------------------

			//Read Spectrometer number
			//----------------------------------------------------------------------------------------------------
			double Specmum = GetSpecNum();
			Console.WriteLine(Specmum);
			//----------------------------------------------------------------------------------------------------

			//Read Spectrometer number
			//----------------------------------------------------------------------------------------------------
			double Sensor = GetSensor();
			Console.WriteLine(Sensor);
			//----------------------------------------------------------------------------------------------------

			//Read ADC res number
			//----------------------------------------------------------------------------------------------------
			double ADC = GetADCresolution();
			Console.WriteLine(ADC);
			//----------------------------------------------------------------------------------------------------

			//Read BAUD rate
			//----------------------------------------------------------------------------------------------------
			double BAUD = GetBaud();
			Console.WriteLine(BAUD);
			//----------------------------------------------------------------------------------------------------

			//Read TINT rate
			//----------------------------------------------------------------------------------------------------
			double TINT = GetIntTime();
			Console.WriteLine(TINT);
			//----------------------------------------------------------------------------------------------------
			
			//Read Scan delay rate
			//----------------------------------------------------------------------------------------------------
			double GetScanD = GetScanDelay();
			Console.WriteLine(GetScanD);
			//----------------------------------------------------------------------------------------------------

		
			//Capture a spectrum with 30 ms integration time and 1 spectrum average
			//----------------------------------------------------------------------------------------------------
			List<UInt16> Spectrum = RawSpectrum(30, 1);
			Console.WriteLine("AAaaaaanD Done");
			//----------------------------------------------------------------------------------------------------



			*/			

		}

		//----------------------------------------------------------------------------------------------------
		// TEC board control
		//----------------------------------------------------------------------------------------------------

		// GET COMMANDS
		private static int TECGetBaud()
		{
			int TecBaud = 0;
			int TotalChar = 0;
			string String = "*para:baud?\r\n";
			comPort.Write(String);

			string completeString = "";

			try
			{
				Byte b = (Byte)comPort.ReadByte();

				if (b == 21)
				{
					Console.WriteLine("Command Not Acknowledged");
				}
				if (b == 6)
				{
					Console.WriteLine("Acknowledged");
				}

			}
			catch (TimeoutException) { }

			while (TotalChar < 25)
			{
				try
				{
					char c = (char)comPort.ReadChar();
					completeString += Char.ToString(c);
				}
				catch (TimeoutException) { }
				TotalChar++;

			}

			TecBaud = Convert.ToInt32(Regex.Match(completeString, @"\d+").Value);

			return TecBaud;
		}

		private static double TECGetTemp()
		{
			//int TecTemp = 0;
			int TotalChar = 0;
			string String = "*para:tectemp?\r\n";
			comPort.Write(String);

			string completeString = "";

			try
			{
				Byte b = (Byte)comPort.ReadByte();

				if (b == 21)
				{
					//Console.WriteLine("Command Not Acknowledged");
				}
				if (b == 6)
				{
					//Console.WriteLine("Acknowledged");
					while (TotalChar < 25)
					{
						try
						{
							char c = (char)comPort.ReadChar();
							completeString += Char.ToString(c);
						}
						catch (TimeoutException) { }
						TotalChar++;

					}
				}

			}
			catch (TimeoutException) { }

			bool check = double.TryParse(completeString.Substring(15, completeString.Length - 15), out double TecTemp);
			if (check == true)
			{
				return TecTemp;
			}
			else
			{
				return 0;
			}
		}

		private static double TECGetKP()
		{
			//int TecTemp = 0;
			int TotalChar = 0;
			string String = "*para:teckp?\r\n";
			comPort.Write(String);

			string completeString = "";

			try
			{
				Byte b = (Byte)comPort.ReadByte();

				if (b == 21)
				{
					//Console.WriteLine("Command Not Acknowledged");
				}
				if (b == 6)
				{
					//Console.WriteLine("Acknowledged");
					while (TotalChar < 25)
					{
						try
						{
							char c = (char)comPort.ReadChar();
							completeString += Char.ToString(c);
						}
						catch (TimeoutException) { }
						TotalChar++;

					}
				}

			}
			catch (TimeoutException) { }

			bool check = double.TryParse(completeString.Substring(17, completeString.Length - 17), out double TecKP);
			if (check == true)
			{
				return TecKP;
			}
			else
			{
				return 0;
			}
		}

		private static double TECGetKI()
		{
			//int TecTemp = 0;
			int TotalChar = 0;
			string String = "*para:tecki?\r\n";
			comPort.Write(String);

			string completeString = "";

			try
			{
				Byte b = (Byte)comPort.ReadByte();
				if (b == 21)
				{
					//Console.WriteLine("Command Not Acknowledged");
				}
				if (b == 6)
				{
					//Console.WriteLine("Acknowledged");
					while (TotalChar < 25)
					{
						try
						{
							char c = (char)comPort.ReadChar();
							completeString += Char.ToString(c);
						}
						catch (TimeoutException) { }
						TotalChar++;

					}
				}

			}
			catch (TimeoutException) { }

			bool check = double.TryParse(completeString.Substring(16, completeString.Length - 16), out double TecKI);
			if (check == true)
			{
				return TecKI;
			}
			else
			{
				return 0;
			}
		}

		private static double TECGetSetTemp()
		{
			int TotalChar = 0;
			string command = "*para:tecref?\r\n";
			comPort.Write(command);

			string completeString = "";

			try
			{
				Byte b = (Byte)comPort.ReadByte();

				if (b == 21)
				{
					//	Console.WriteLine("Command Not Acknowledged");
				}
				if (b == 6)
				{
					//Console.WriteLine("Acknowledged");
					while (TotalChar < 25)
					{
						try
						{
							char c = (char)comPort.ReadChar();
							completeString += Char.ToString(c);
						}
						catch (TimeoutException) { }
						TotalChar++;

					}
				}

			}
			catch (TimeoutException) { }

			bool check = double.TryParse(completeString.Substring(16, completeString.Length - 16), out double SetTemp);
			if (check == true)
			{
				return SetTemp;
			}
			else
			{
				return 0;
			}
		}

		private static double TECGetAdapThreshold()
		{
			/*
			used to set/read the threshold for control adaption. Typical values range from 0…2.
			It defines a small area around the setpoint where control algorithm is changed to
			reach high accuracy. */

			int TotalChar = 0;
			string command = "*para:tecthres?\r\n";
			comPort.Write(command);

			string completeString = "";

			try
			{
				Byte b = (Byte)comPort.ReadByte();

				if (b == 21)
				{
					//	Console.WriteLine("Command Not Acknowledged");
				}
				if (b == 6)
				{
					//Console.WriteLine("Acknowledged");
					while (TotalChar < 25)
					{
						try
						{
							char c = (char)comPort.ReadChar();
							completeString += Char.ToString(c);
						}
						catch (TimeoutException) { }
						TotalChar++;

					}
				}

			}
			catch (TimeoutException) { }

			bool check = double.TryParse(completeString.Substring(16, completeString.Length - 16), out double TecThres);
			if (check == true)
			{
				return TecThres;
			}
			else
			{
				return 0;
			}
		}

		// SET COMMANDS
		private static void TECSetKP(double NewKP)
		{
			string String = "*para:teckp " + NewKP + "\r\n";
			comPort.Write(String);
			Byte b = (Byte)comPort.ReadByte();
		}

		private static void TECSetKI(double NewKI)
		{
			string String = "*para:tecki " + NewKI + "\r\n";
			comPort.Write(String);
			Byte b = (Byte)comPort.ReadByte();
		}

		private static void TECSetSetTemp(double NewTempTarget)
		{
			string command = "*para:tecref " + NewTempTarget + "\r\n";
			comPort.Write(command);
			Byte b = (Byte)comPort.ReadByte();
		}

		private static void TECSetAdapThreshold(double NewThreshold)
		{
			string command = "*para:tecthres " + NewThreshold + "\r\n";
			comPort.Write(command);
			Byte b = (Byte)comPort.ReadByte();
		}




		//----------------------------------------------------------------------------------------------------
		// VersaPic Spectrometer
		//----------------------------------------------------------------------------------------------------

		// GET COMMANDS
		private static double GetPixnNum()
		{
			double numberofpix = 0;
			int TotalChar = 0;
			string String = "*PARA:PIX?\r\n";
			comPort.Write(String);

			numberofpix = ReadLine();

			return numberofpix;
		}

		private static double GetSerial()
		{
			double serial = 0;
			int TotalChar = 0;
			string String = "*PARA:SERN?\r\n";
			comPort.Write(String);

			serial = ReadLine();

			return serial;
		}

		private static double GetSpecNum()
		{
			double specnum = 0;
			int TotalChar = 0;
			string command = "*PARA:SPNUM?\r\n";
			comPort.Write(command);

			specnum = ReadLine();

			return specnum;
		}

		private static double GetSensor()
		{
			double sensor = 0;
			int TotalChar = 0;
			string command = "*PARA:SENS?\r\n";
			comPort.Write(command);

			sensor = ReadLine();
			return sensor;
		}

		private static double GetADCresolution()
		{
			double adc = 0;
			int TotalChar = 0;
			string command = "*PARA:ADCR?\r\n";
			comPort.Write(command);

			adc = ReadLine();

			return adc;
		}

		private static double GetBaud()
		{
			double baud = 0;
			int TotalChar = 0;
			string command = "*PARA:BAUD?\r\n";
			comPort.Write(command);

			baud = ReadLine();
			return baud;
		}

		private static double GetIntTime()
		{
			double tint = 0;
			int TotalChar = 0;
			string command = "*PARA:TINT?\r\n";
			comPort.Write(command);

			tint = ReadLine();
			return tint;
		}

		private static double GetScanDelay()
		{
			double scandelay = 0;
			int TotalChar = 0;
			string command = "*PARA:SDEL?\r\n";
			comPort.Write(command);
			scandelay = ReadLine();
			return scandelay;
		}

		private static double ReadLineCalibrationCoefficients()
		{
			Byte FirstByte = (Byte)comPort.ReadByte();
			Byte newValue = FirstByte;
			Byte[] ReadBuffer = new Byte[1] { FirstByte };
			while (newValue != 13)
			{
				try
				{
					newValue = (Byte)comPort.ReadByte();
					Array.Resize(ref ReadBuffer, ReadBuffer.Length + 1);
					ReadBuffer[ReadBuffer.GetUpperBound(0)] = newValue;
				}
				catch (TimeoutException) { }
			}
			var text = System.Text.Encoding.ASCII.GetString(ReadBuffer);
			double doubleValue = Convert.ToDouble(text.Substring(15, text.Length - 15));
			return doubleValue;
		}

		private static double[] Get_calibrationcoefficients()
		{
			string[] completeStringArray = new string[5];
			double[] FitVal = new double[5];
			for (int i = 0; i < 5; i++)
			{
				int TotalChar = 0;
				string String = "*PARA:FIT" + i + "?\r\n";
				comPort.Write(String);

				double calibrationCoefficeint = ReadLineCalibrationCoefficients();
				FitVal[i] = calibrationCoefficeint;
			}
			return FitVal;
		}

		private static List<UInt16> RawSpectrum(int integration_time, int average)
		{
			//Set up for reading data format 3. 


			string command = "*MEAS:DARK " + integration_time.ToString() + " " + average.ToString() + " 3\r\n";
			comPort.Write(command);
			Thread.Sleep(integration_time);

			Byte ack = (Byte)comPort.ReadByte();
			Byte bell = (Byte)comPort.ReadByte();
			

			Byte[] lengthBuffer = new Byte[2];
			for(int i = 0; i < 2; i++)
			{

				try
				{
					lengthBuffer[i] = (Byte)comPort.ReadByte();
				}
				catch (TimeoutException) { }
			}

			int pixnumber = Convert.ToUInt16(lengthBuffer[1] << 8 | lengthBuffer[0] & 0xff);
	

			UInt16[] spectrum = new UInt16[pixnumber/2];
			var arrList = spectrum.ToList();
			Byte[] spectrumBuffer = new Byte[pixnumber];

			int ByteIndex = 0;
			while (ByteIndex < pixnumber )
			{
				try
				{
					spectrumBuffer[ByteIndex] = (Byte)comPort.ReadByte();
				}
				catch (TimeoutException) { }
				ByteIndex++;
			}

			int index = 0;
			while (index < pixnumber/2)
			{
				arrList[index] = Convert.ToUInt16(spectrumBuffer[index * 2 + 1] << 8 | spectrumBuffer[index * 2] & 0xff);
				index++;
			}

			return arrList;
		}

		private static double ReadLine()
		{
			Byte FirstByte = (Byte)comPort.ReadByte();
			Byte newValue = FirstByte;
			Byte[] ReadBuffer = new Byte[1] { FirstByte };
			while (newValue != 13)
			{
				try
				{
					newValue = (Byte)comPort.ReadByte();
					Array.Resize(ref ReadBuffer, ReadBuffer.Length + 1);
					ReadBuffer[ReadBuffer.GetUpperBound(0)] = newValue;
				}
				catch (TimeoutException) { }
			}
			var text = System.Text.Encoding.ASCII.GetString(ReadBuffer);
			double doubleValue = Convert.ToDouble(Regex.Match(text, @"\d+").Value);
			return doubleValue;
		}


		// SET COMMANDS
		private static void SetScanDelay(double Newscandelay)
		{
			string command = "*PARA:SDEL " + Newscandelay.ToString() + "\r\n";
			comPort.Write(command);
			Checkifack();
		}

		private static void SetIntTime(double Newtint)
		{
			string command = "*PARA:TINT " + Newtint.ToString() + "\r\n";
			comPort.Write(command);
			Checkifack();
		}

		private static void SetBaudRate(double NewBaud)
		{
			string command = "*PARA:BAUD " + NewBaud.ToString() + "\r\n";
			comPort.Write(command);
			Checkifack();
		}

		private static void SetGain(double NewGain)
		{
			string command = "*PARA:GAIN " + NewGain.ToString() + "\r\n";
			comPort.Write(command);
			Checkifack();
		}

		private static void Checkifack()
		{
			Byte FirstByte = (Byte)comPort.ReadByte();
			if (FirstByte != 06)
			{
				Console.WriteLine("Command was not acknowledged, check if the set value is within allowed range");
			}
		}

	}



}
	


 
