//Author : LabJack
//April 29, 2008
//This example demonstrates how to write and read some or all analog and digital
//I/O.  It records the time for 1000 iterations and divides by 1000, to allow
//measurment of the basic command/response communication times.  These times
//should be comparable to the Window's command/response communication times
//documented in Section 3.1 of the U3 User's Guide.
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using LabJack;

namespace u3AllIO
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class U3AllIOForm : System.Windows.Forms.Form
	{
		private System.Windows.Forms.NumericUpDown idBox;
		private System.Windows.Forms.Label idLabel;
		private System.Windows.Forms.Label errorLabel;
		private System.Windows.Forms.Button allIOButton;
		private System.Windows.Forms.MainMenu mainMenu1;
		private System.Windows.Forms.Label label2;
		private System.Windows.Forms.Label label3;
		private System.Windows.Forms.Label label4;
		private System.Windows.Forms.Label label5;
		private System.Windows.Forms.TabControl tabControl1;
		private System.Windows.Forms.TabPage tabPage1;
		private System.Windows.Forms.TabPage tabPage2;
		private System.Windows.Forms.Label label6;
		private System.Windows.Forms.TextBox statusBox;
		private System.Windows.Forms.TextBox ainBox;
		private System.Windows.Forms.TextBox dinBox;
		private System.Windows.Forms.NumericUpDown numAINBox;
		private System.Windows.Forms.NumericUpDown numIterBox;
		private System.Windows.Forms.TextBox msPerIterBox;
		private System.Windows.Forms.CheckBox longSettlingCB;
		private System.Windows.Forms.CheckBox quickSampleCB;


		private U3 u3Dev;
		private int numChannels;   //Number of AIN channels, 0-16.
		private int quickSample;   //Set to 1 (TRUE) for quick AIN sampling.
		private int longSettling;  //Set to 1 (TRUE) for extra AIN settling time.
		private long numIterations;
		private bool isDAC1Enabled;

		public U3AllIOForm()
		{
			InitializeComponent();
			numChannels = 8;
			quickSample = 1;
			longSettling = 0;
			numIterations = 1000;
			isDAC1Enabled = false;
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			base.Dispose( disposing );
		}
		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.mainMenu1 = new System.Windows.Forms.MainMenu();
			this.idBox = new System.Windows.Forms.NumericUpDown();
			this.idLabel = new System.Windows.Forms.Label();
			this.errorLabel = new System.Windows.Forms.Label();
			this.statusBox = new System.Windows.Forms.TextBox();
			this.allIOButton = new System.Windows.Forms.Button();
			this.numAINBox = new System.Windows.Forms.NumericUpDown();
			this.label2 = new System.Windows.Forms.Label();
			this.label3 = new System.Windows.Forms.Label();
			this.numIterBox = new System.Windows.Forms.NumericUpDown();
			this.label4 = new System.Windows.Forms.Label();
			this.label5 = new System.Windows.Forms.Label();
			this.ainBox = new System.Windows.Forms.TextBox();
			this.tabControl1 = new System.Windows.Forms.TabControl();
			this.tabPage1 = new System.Windows.Forms.TabPage();
			this.quickSampleCB = new System.Windows.Forms.CheckBox();
			this.longSettlingCB = new System.Windows.Forms.CheckBox();
			this.tabPage2 = new System.Windows.Forms.TabPage();
			this.dinBox = new System.Windows.Forms.TextBox();
			this.label6 = new System.Windows.Forms.Label();
			this.msPerIterBox = new System.Windows.Forms.TextBox();
			// 
			// idBox
			// 
			this.idBox.Location = new System.Drawing.Point(80, 208);
			this.idBox.Maximum = new System.Decimal(new int[] {
																  255,
																  0,
																  0,
																  0});
			this.idBox.Minimum = new System.Decimal(new int[] {
																  1,
																  0,
																  0,
																  -2147483648});
			this.idBox.Size = new System.Drawing.Size(56, 20);
			this.idBox.Value = new System.Decimal(new int[] {
																1,
																0,
																0,
																-2147483648});
			// 
			// idLabel
			// 
			this.idLabel.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
			this.idLabel.Location = new System.Drawing.Point(8, 208);
			this.idLabel.Size = new System.Drawing.Size(64, 16);
			this.idLabel.Text = "Local ID :";
			// 
			// errorLabel
			// 
			this.errorLabel.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
			this.errorLabel.Location = new System.Drawing.Point(8, 240);
			this.errorLabel.Size = new System.Drawing.Size(64, 20);
			this.errorLabel.Text = "Status:";
			// 
			// statusBox
			// 
			this.statusBox.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold);
			this.statusBox.Location = new System.Drawing.Point(72, 240);
			this.statusBox.ReadOnly = true;
			this.statusBox.Size = new System.Drawing.Size(160, 21);
			this.statusBox.Text = "";
			// 
			// allIOButton
			// 
			this.allIOButton.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
			this.allIOButton.Location = new System.Drawing.Point(160, 208);
			this.allIOButton.Size = new System.Drawing.Size(72, 24);
			this.allIOButton.Text = "Run";
			this.allIOButton.Click += new System.EventHandler(this.allIOButton_Click);
			// 
			// numAINBox
			// 
			this.numAINBox.Location = new System.Drawing.Point(96, 16);
			this.numAINBox.Maximum = new System.Decimal(new int[] {
																	  16,
																	  0,
																	  0,
																	  0});
			this.numAINBox.Size = new System.Drawing.Size(48, 20);
			this.numAINBox.Value = new System.Decimal(new int[] {
																	8,
																	0,
																	0,
																	0});
			// 
			// label2
			// 
			this.label2.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
			this.label2.Location = new System.Drawing.Point(16, 16);
			this.label2.Size = new System.Drawing.Size(56, 16);
			this.label2.Text = "# AIN :";
			// 
			// label3
			// 
			this.label3.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
			this.label3.Location = new System.Drawing.Point(8, 176);
			this.label3.Size = new System.Drawing.Size(112, 16);
			this.label3.Text = "ms per iteration :";
			// 
			// numIterBox
			// 
			this.numIterBox.Location = new System.Drawing.Point(96, 48);
			this.numIterBox.Maximum = new System.Decimal(new int[] {
																	   2147483647,
																	   0,
																	   0,
																	   0});
			this.numIterBox.Size = new System.Drawing.Size(96, 20);
			this.numIterBox.Value = new System.Decimal(new int[] {
																	 1000,
																	 0,
																	 0,
																	 0});
			// 
			// label4
			// 
			this.label4.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
			this.label4.Location = new System.Drawing.Point(16, 40);
			this.label4.Size = new System.Drawing.Size(72, 32);
			this.label4.Text = "# of iterations :";
			// 
			// label5
			// 
			this.label5.Font = new System.Drawing.Font("Tahoma", 9F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))));
			this.label5.Location = new System.Drawing.Point(40, 8);
			this.label5.Size = new System.Drawing.Size(40, 16);
			this.label5.Text = "AIN";
			// 
			// ainBox
			// 
			this.ainBox.AcceptsReturn = true;
			this.ainBox.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular);
			this.ainBox.Location = new System.Drawing.Point(16, 32);
			this.ainBox.Multiline = true;
			this.ainBox.ReadOnly = true;
			this.ainBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
			this.ainBox.Size = new System.Drawing.Size(88, 96);
			this.ainBox.Text = "";
			// 
			// tabControl1
			// 
			this.tabControl1.Controls.Add(this.tabPage1);
			this.tabControl1.Controls.Add(this.tabPage2);
			this.tabControl1.SelectedIndex = 0;
			this.tabControl1.Size = new System.Drawing.Size(240, 168);
			// 
			// tabPage1
			// 
			this.tabPage1.Controls.Add(this.quickSampleCB);
			this.tabPage1.Controls.Add(this.longSettlingCB);
			this.tabPage1.Controls.Add(this.numIterBox);
			this.tabPage1.Controls.Add(this.label4);
			this.tabPage1.Controls.Add(this.numAINBox);
			this.tabPage1.Controls.Add(this.label2);
			this.tabPage1.Location = new System.Drawing.Point(4, 4);
			this.tabPage1.Size = new System.Drawing.Size(232, 142);
			this.tabPage1.Text = "Settings";
			// 
			// quickSampleCB
			// 
			this.quickSampleCB.Checked = true;
			this.quickSampleCB.CheckState = System.Windows.Forms.CheckState.Checked;
			this.quickSampleCB.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
			this.quickSampleCB.Location = new System.Drawing.Point(56, 104);
			this.quickSampleCB.Size = new System.Drawing.Size(128, 20);
			this.quickSampleCB.Text = "Quick Sample";
			// 
			// longSettlingCB
			// 
			this.longSettlingCB.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
			this.longSettlingCB.Location = new System.Drawing.Point(56, 80);
			this.longSettlingCB.Size = new System.Drawing.Size(128, 20);
			this.longSettlingCB.Text = "Long Settling";
			// 
			// tabPage2
			// 
			this.tabPage2.Controls.Add(this.dinBox);
			this.tabPage2.Controls.Add(this.label6);
			this.tabPage2.Controls.Add(this.label5);
			this.tabPage2.Controls.Add(this.ainBox);
			this.tabPage2.Location = new System.Drawing.Point(4, 4);
			this.tabPage2.Size = new System.Drawing.Size(232, 142);
			this.tabPage2.Text = "Last Iteration Readings";
			// 
			// dinBox
			// 
			this.dinBox.AcceptsReturn = true;
			this.dinBox.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular);
			this.dinBox.Location = new System.Drawing.Point(136, 48);
			this.dinBox.Multiline = true;
			this.dinBox.ReadOnly = true;
			this.dinBox.Size = new System.Drawing.Size(72, 24);
			this.dinBox.Text = "";
			// 
			// label6
			// 
			this.label6.Font = new System.Drawing.Font("Tahoma", 9F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))));
			this.label6.Location = new System.Drawing.Point(128, 8);
			this.label6.Size = new System.Drawing.Size(88, 40);
			this.label6.Text = "Digital Inputs (CIO)";
			// 
			// msPerIterBox
			// 
			this.msPerIterBox.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold);
			this.msPerIterBox.Location = new System.Drawing.Point(128, 176);
			this.msPerIterBox.ReadOnly = true;
			this.msPerIterBox.Size = new System.Drawing.Size(104, 21);
			this.msPerIterBox.Text = "";
			// 
			// U3AllIOForm
			// 
			this.Controls.Add(this.tabControl1);
			this.Controls.Add(this.idBox);
			this.Controls.Add(this.idLabel);
			this.Controls.Add(this.errorLabel);
			this.Controls.Add(this.statusBox);
			this.Controls.Add(this.allIOButton);
			this.Controls.Add(this.msPerIterBox);
			this.Controls.Add(this.label3);
			this.Menu = this.mainMenu1;
			this.MinimizeBox = false;
			this.Text = "U3 AllIO";

		}
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>

		static void Main() 
		{
			Application.Run(new U3AllIOForm());
		}

		private void allIOButton_Click(object sender, System.EventArgs e)
		{
			msPerIterBox.Text = "";
			ainBox.Text = "";
			dinBox.Text = "";
			statusBox.Text = "";

			quickSample = Convert.ToInt32(quickSampleCB.Checked);
			longSettling = Convert.ToInt32(longSettlingCB.Checked);
			numIterations = Convert.ToInt64(numIterBox.Text);
			numChannels = Convert.ToInt32(numAINBox.Text);

			int err = 0;
			u3Dev = new U3();

			//Opening U3 over USB
			if(u3Dev.openUSBConnection(Convert.ToInt32(idBox.Value)) < 0)
			{
				statusBox.Text = u3Dev.errorText;
				goto done;
			}
			
			//Getting calibration information from U3
			if(u3Dev.getCalibrationInfo() < 0)
			{
				statusBox.Text = u3Dev.errorText;
				goto close;
			}

			if( (err = configAllIO()) < 0)
			{
				statusBox.Text = getError(err) + "(configAllIO)";
				goto close;
			}

			if( (err = allIO()) < 0)
			{
				statusBox.Text = getError(err) + "(allIO)";
				goto close;
			}
			
			if(u3Dev.closeUSBConnection() < 0)
			{
				statusBox.Text = u3Dev.errorText;
				return;
			}

			close:
				u3Dev.closeUSBConnection();
			done:
				return;
		}


		/// <summary>
		///  Sends a ConfigIO low-level command that will set desired lines as analog, and 
		///  all else as digital.
		/// </summary>
		/// <returns>Negative number on error, 0 on success.</returns>
		public int configAllIO()
		{
			byte []sendBuff = new byte[12];
			byte []recBuff = new byte[12];
			uint sendChars, recChars;
			ushort checksumTotal, FIOEIOAnalog;

			sendBuff[1] = (byte)(0xF8);  //Command byte
			sendBuff[2] = (byte)(0x03);  //Number of data words
			sendBuff[3] = (byte)(0x0B);  //Extended command number

			sendBuff[6] = 13;	//Writemask : Setting writemask for TimerCounterConfig (bit 0),
								//            FIOAnalog (bit 2) and EIOAnalog (bit 3)

			sendBuff[7] = 0;	//Reserved
			sendBuff[8] = 64;	//TimerCounterConfig: disable timer nad counter, set
								//                    TimerCounterPinOffset to 4 (bits 4-7)
			sendBuff[9] = 0;	//DAC1 enable : not enabling, though could already be enabled

			sendBuff[10] = 0;

			FIOEIOAnalog = (byte)(Math.Pow(2.0, numChannels)-1);
			sendBuff[10] = (byte)(FIOEIOAnalog & (0xFF));	//FIOAnalog
			sendBuff[11] = (byte)(FIOEIOAnalog / 256);		//EIOAnalog
			U3.extendedChecksum(ref sendBuff, 12);

			//Sending command to U3
			if( (sendChars = U3.LJUSB_BulkWrite(u3Dev.handle, U3.U3_PIPE_EP1_OUT, sendBuff, 12)) < 12)
				return -1;

			//Reading response from U3
			if( (recChars = U3.LJUSB_BulkRead(u3Dev.handle, U3.U3_PIPE_EP1_IN, recBuff, 12)) < 12)
				return -2;

			checksumTotal = U3.extendedChecksum16(recBuff, 12);
			if( (byte)((checksumTotal / 256) & 0xff) != recBuff[5] || (byte)(checksumTotal & 0xff) != recBuff[4])
				return -4;

			if( U3.extendedChecksum8(recBuff) != recBuff[0])
				return -3;

			if( recBuff[1] != (byte)(0xF8) || recBuff[2] != (byte)(0x03) || recBuff[3] != (byte)(0x0B) )
				return -6;

			if( recBuff[6] != 0)
			{
				u3Dev.errorcode = (int)recBuff[6];
				return -200;
			}

			if( recBuff[10] != (FIOEIOAnalog&(0xFF)) && recBuff[10] != ((FIOEIOAnalog&(0xFF))|(0x0F)))
				return -8;

			if( recBuff[11] != FIOEIOAnalog/256)
				return -9;

			isDAC1Enabled = Convert.ToBoolean(recBuff[9]&(0x01));

			return 0;
		}

		
		/// <summary>
		///  Calls a Feedback low-level command 1000(default) times.
		/// </summary>
		/// <returns>Negative number on error, 0 on success.</returns>
		public int allIO()
		{
			byte []sendBuff;
			byte []recBuff;
			ushort checksumTotal, ainBytes;
			uint sendChars, recChars, sendSize, recSize;
			int valueDIPort;
			double []valueAIN = new double[16];
			long time;  

			for(int i = 0; i < 16; i++)
				valueAIN[i] = 9999;
			valueDIPort = 0;


			//Setting up a Feedback command that will set CIO0-3 as input
			sendBuff = new byte[14];
			recBuff = new byte[10];

			sendBuff[1] = (byte)(0xF8);	//Command byte
			sendBuff[2] = 4;			//Number of data words (.5 word for echo, 3.5 
										//words for IOTypes and data)   
			sendBuff[3] = (byte)(0x00);	//Extended command number

			sendBuff[6] = 0;    //Echo  
			sendBuff[7] = 29;   //IOType is PortDirWrite  
			sendBuff[8] = 0;    //Writemask (for FIO)
			sendBuff[9] = 0;    //Writemask (for EIO)
			sendBuff[10] = 15;  //Writemask (for CIO)
			sendBuff[11] = 0;   //Direction (for FIO)
			sendBuff[12] = 0;   //Direction (for EIO)
			sendBuff[13] = 0;   //Direction (for CIO)

			U3.extendedChecksum(ref sendBuff, 14);

			//Sending command to U3
			if( (sendChars = U3.LJUSB_BulkWrite(u3Dev.handle, U3.U3_PIPE_EP1_OUT, sendBuff, 14)) < 14)
				return -1;

			//Reading response from U3
			if( (recChars = U3.LJUSB_BulkRead(u3Dev.handle, U3.U3_PIPE_EP1_IN, recBuff, 10)) < 10)
				return -2;

			checksumTotal = U3.extendedChecksum16(recBuff, 10);
			if( (byte)((checksumTotal / 256) & 0xff) != recBuff[5] || (byte)(checksumTotal & 0xff) != recBuff[4])
				return -4;
			
			if( U3.extendedChecksum8(recBuff) != recBuff[0])
				return -3;

			if( recBuff[1] != (byte)(0xF8) || recBuff[3] != (byte)(0x00) )
				return -6;

			if( recBuff[6] != 0)
			{
				u3Dev.errorcode = (int)recBuff[6];
				return -200;
			}

			//Setting up Feedback command that will run numIterations times
			if( ((sendSize = (uint)(7+2+1+numChannels*3)) % 2) != 0)
				sendSize++;
			sendBuff = new byte[sendSize]; //Creating an array of size sendSize

			if( ((recSize = (uint)(9+3+numChannels*2)) % 2) != 0)
				recSize++;
			recBuff = new byte[recSize];   //Creating an array of size recSize

			sendBuff[1] = (byte)(0xF8);		//Command byte
			sendBuff[2] = (byte)((sendSize - 6)/2);	//Number of data words 
			sendBuff[3] = (byte)(0x00);		//Extended command number

			sendBuff[6] = 0;     //Echo  

			//Setting DAC0 with 2.5 volt output
			sendBuff[7] = 34;    //IOType is DAC0

			//Value is 2.5 volts (in binary)
			u3Dev.analogToCalibratedBinaryVoltage(0, 2.5, ref sendBuff[8]);

			sendBuff[9] = 26;    //IOType is PortStateRead

			//Setting AIN read commands
			for(int j = 0; j < numChannels; j++)
			{
				sendBuff[10 + j*3] = 1;     //IOType is AIN

				//Positive Channel (bits 0 - 4), LongSettling (bit 6) and QuickSample (bit 7)
				sendBuff[11 + j*3] = (byte)(j + (longSettling&(0x01))*64 + (quickSample&(0x01))*128);      
				sendBuff[12 + j*3] = 31;   //Negative Channel is single-ended
			}

			if( (sendSize % 2) != 0)
				sendBuff[sendSize - 1] = 0;

			U3.extendedChecksum(ref sendBuff, (int)sendSize);

			statusBox.Text = "Reading AIN and DIO ";

			time = U3.getTickCount();

			for(int i = 0; i < numIterations; i++)
			{
				//Sending command to U3
				if( (sendChars = U3.LJUSB_BulkWrite(u3Dev.handle, U3.U3_PIPE_EP1_OUT, sendBuff, sendSize)) < sendSize)
					return -1;

				//Reading response from U3
				if( (recChars = U3.LJUSB_BulkRead(u3Dev.handle, U3.U3_PIPE_EP1_IN, recBuff, recSize)) < recSize)
					return -2;

				checksumTotal = U3.extendedChecksum16(recBuff, (int)recChars);
				if( (byte)((checksumTotal / 256) & 0xff) != recBuff[5] || (byte)(checksumTotal & 0xff) != recBuff[4])
					return -4;

				if( U3.extendedChecksum8(recBuff) != recBuff[0])
					return -3;

				if( recBuff[1] != (byte)(0xF8) || recBuff[3] != (byte)(0x00) )
					return -6;

				if( recBuff[6] != 0)
				{
					u3Dev.errorcode = (int)recBuff[6];
					return -200;
				}

				if(recChars != recSize)
					return -2;

				//Getting CIO digital states
				valueDIPort = recBuff[11];

				//Getting AIN voltages
				for(int j = 0; j < numChannels; j++)
				{
					ainBytes = (ushort)(recBuff[12+j*2] + recBuff[13+j*2]*256);
					if(u3Dev.calibrationInfo.hardwareVersion >= 1.30)
						u3Dev.binaryToCalibratedAnalogVoltage_hw130((byte)j, 31, ainBytes, ref valueAIN[j]);
					else
						u3Dev.binaryToCalibratedAnalogVoltage(isDAC1Enabled, 31, ainBytes, ref valueAIN[j]);
				}
			}

			time = U3.getTickCount() - time;
			msPerIterBox.Text = string.Format("{0:f3}", ((double)time / (double)numIterations));
			dinBox.Text = valueDIPort.ToString();
			ainBox.Text = "";

			for(int j = 0; j < numChannels; j++)
				ainBox.Text += j + " : " + string.Format("{0:f3}", valueAIN[j]) + "\r\n";

			statusBox.Text = "Done";
			return 0;
		}

		private string getError(int error)
		{
			switch(error)
			{
				case 0: return("");  //no errors
				case -1: return("Error : write failed");
				case -2: return("Error : read failed");
				case -3: return("Error : read bad checksum8");
				case -4: return("Error : read bad checksum16");
				case -5: return("Error : read bad checksum");
				case -6: return("Error : read wrong command bytes");
				case -7: return("Error : read buffer has wrong IOType");
				case -8: return("Error : FIOAnalog did not set correctly");
				case -9: return("Error : EIOAnalog did not set correctly");
				case -200: return("Error : received error code # " + u3Dev.errorcode + " from the response");
				default: statusBox.Text = "Error"; break;
			}
			return("");
		}
	}
}
