//>>***************************************************************************
//                              UNCLASSIFIED
//
//                COPYRIGHT 2017 RAYTHEON MISSILE SYSTEMS 
//                         ALL RIGHTS RESERVED
//   This data was developed pursuant to Contract Number HQ0147-12-C-0004/1088370
//     with the US Government. The US Government's rights in and to this 
//        copyrighted data are as specified in DFAR 252.227-7013 
//               which was made part of the above contract.
//
//    Distribution Statement: D -Distribution authorized to the DoD and DoD 
//       contractors only based on Critical Technology Requirements, May 2001.
//       Other requests shall be referred to PEO THEATER AIR DEFENSE (PMS 422).
//    Warning: - This document contains technical data whose export is restricted
//       by the Arms Export Control Act (Title 22, U.S.C.) or Export 
//       Administration Act of 1979, as amended (Title 50, U.S.C.). Violations 
//       of these laws are subject to severe criminal penalties. Disseminate in 
//       accordance with provisions of DoD 5230.25.
//    Destruction Notice: - For unclassified, limited distribution information, 
//       destroy by any method that will prevent disclosure of contents or 
//       reconstruction of the document. Classified information, destroy in 
//       accordance with DoD-5220.22-M or OPNAVINST 5510.1h.
//>>***************************************************************************

using NLog;
using Raytheon.Common;
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Raytheon.Instruments
{
   /// <summary>
   /// A class for controlling a scope via scpi commands
   /// </summary>
   public class ScopeKeysightScpi : IOscilloScope
	{
		#region PublicMembers
		#endregion

		#region PrivateMembers
		/*private enum SourceType
		{
		   CHANNEL,
		   FUNCTION
		}*/

		// if other commands are needed http://www.keysight.com/upload/cmc_upload/All/Infiniium_prog_guide.pdf pg 770
		private const string m_SUBTRACTFUNC = ":SUBT ";
		private const string m_MEASFALLCMD = ":MEAS:FALL? ";
		private const string m_MEASRISCMD = ":MEAS:RISetime? ";
		private const string m_MEASVTIMCMD = ":MEAS:VTIM? ";

		private const string _READ_ERROR_CODE_CMD = "SYST:ERR?";
		private const string m_MEASFREQCMD = ":MEAS:FREQ? ";
		private const string m_SINGLECMD = ":SINGle";
		private const string m_STOPCMD = ":STOP";
		private const string m_RUNCMD = ":RUN";
		private const string m_RESETCMD = "*RST";
		//private const string m_ISOPCCMD = "*OPC? ";
		private const string m_ISITCOMPLETEDCMD = ":TER? ";
		private const string m_SELFTESTCMD = "*TST? ";
		private const string m_COLON = ":";
		private const string m_COMMA = ",";
		private const string m_QUESTMARK = "?";
		private const string m_SPACE = " ";
		private const string m_SLASH = "\\";
		private const string m_DOUBLESLASH = "\\\\";
		private const string m_DOUBLEQUOTES = "\"";

		// Turn Headers Off when Returning Values to Numeric Variables
		private const string m_SYSHEADEROFFCMD = ":SYST:HEAD OFF";
		private const string m_ANALAEDGCMD = ":ANALyze:AEDGes ";
		private const string m_ANALASIGTYPECMD = ":ANALyze:SIGNal:TYPE";
		private const string m_ANALASIGTYPE_OPTION = "PAM4";
		private const string m_CHANCMD = "CHAN";
		private const string m_FUNCCMD = "FUNC";
		private const string m_CHANDISPCMD = ":DISP ";
		private const string m_CHANINPUTCMD = ":INP ";
		private const string m_INPUTIMPEDANCECMD = "DC";
		private const string m_TIMESCALECMD = ":TIM:SCAL ";
		private const string m_TIMEOFFSETCMD = ":TIM:POS ";
		private const string m_TRIGSRCCMD = ":TRIG:EDGE:SOUR ";
		private const string m_TRIGLEVCMD = ":TRIG:LEV ";
		private const string m_RISETRIGEDGESELCMD = ":TRIG:EDGE:SLOP POS";
		private const string m_FALLTRIGEDGESELCMD = ":TRIG:EDGE:SLOP NEG";
		private const string m_VOLTPERDIVCMD = ":SCAL ";
		private const string m_VOLTOFFSETCMD = ":OFFS ";

		// The :MEASure:CLEar command clears the measurement results from the screen
		// and disables all previously enabled measurements.
		private const string m_MEASCLEARCMD = ":MEAS:CLE";
		private const string m_MEASDELTCMD = ":MEAS:DELT";
		private const string m_MEASDELTDEFCMD = ":MEAS:DELT:DEF";
		private const string m_MEASSOURCMD = ":MEAS:SOUR";

		// These threshold settings in voltages are used for rise/fall measurements.
		private const string m_MEASTHRRFALABSCMD = ":MEAS:THR:RFAL:ABS";
		private const string m_MEASTHRGENABSCMD = ":MEAS:THR:GEN:ABS";
		private const string m_MEASTHRGENMETHCMD = ":MEAS:THR:GEN:METH";
		private const string m_MEASTHRGENTOPBABSCMD = ":MEAS:THR:GEN:TOPB:ABS";
		private const string m_MEASTHRGENTOPBABS_OPTION = "5.0,1.0";
		private const string m_MEASTHRGENTOPBMETHCMD = ":MEAS:THR:GEN:TOPB:METH";

		// The :MEASure:PWIDth command measures the width of the first positive pulse on the screen 
		// using the mid-threshold levels of the waveform(50% levels with standard measurements selected).
		private const string m_MEASPWIDCMD = ":MEAS:PWID? ";

		private const string m_MEASTHRMETH_OPTION = "ABS";

		// Trigger sweep
		private const string m_ISTRIGGEREDCMD = ":TRIG:SWE? ";
		private const string m_TRIGGERCMD = ":TRIG:SWE ";
		private const string m_TRIGGER_OPTION = "TRIG";

		// ON or OFF
		private const string m_OPTION_ON = "ON";
		private const string m_OPTION_OFF = "OFF";

		private const string m_IMAGE_DEST = @"\Users\Public\Documents";
		private const string m_DESIRED_FOLDER = @"\Scope_Measurements";
		private const string m_IMAGE_FORMAT = "GIF";
		private const string m_IMAGE_SOURCE = "SCR";
		private const string m_SAVEIMAGECMD = ":DISK:SAVE:IMAGe ";

		// Maximum number of channels provided by scope.
		private const int m_MAXNUMOFCHANN = 4;
		private const int m_READ_TIMEOUT = 5000;

		private readonly string _address;
		private byte[] _readBuffer;
		private NetworkStream _tcpStream;

		private State _state;
		private SelfTestResult _selfTestResult;
		private string _name;

		/// <summary>
		/// NLog logger
		/// </summary>
		private readonly ILogger _logger;
		/// <summary>
		/// Raytheon configuration
		/// </summary>
		private readonly IConfigurationManager _configurationManager;
		private readonly IConfiguration _configuration;

		#endregion

		#region PrivateFunctions

		/// <summary>
		/// The Finalizer which will release resources if required
		/// </summary>
		~ScopeKeysightScpi()
		{
			Dispose(false);
		}

		/// <summary>
		/// Convert scpi data to string
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		private string ConvertToString(ref byte[] data)
		{
			string rsp = System.Text.Encoding.ASCII.GetString(data);
			return rsp;
		}

		/// <summary>
		/// Dispose of this object
		/// </summary>
		/// <param name="disposing"></param>
		protected virtual void Dispose(bool disposing)
		{
			try
			{
				if (disposing)
				{
					if (_state == State.Ready)
					{
						Reset();

						_tcpStream.Close();

						_tcpStream.Dispose();

						_state = State.Uninitialized;
					}
				}
			}
			catch (Exception)
			{
				try
				{
					//ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace);
				}
				catch (Exception)
				{
					//Do not rethrow. Exception from error logger that has already been garbage collected
				}
			}
		}

		/// <summary>
		/// Get the error code.
		/// </summary>
		/// <param name="errorCode">The error code.</param>
		/// <returns>The error description.</returns>
		private string GetErrorCode(out int errorCode)
		{
			// not calling IOQuery() here so IOQuery() can call GetErrorCode() after each query

			string command = _READ_ERROR_CODE_CMD + "\n";

			byte[] commandBuffer = Encoding.ASCII.GetBytes(command);

			// send the data out
			_tcpStream.Write(commandBuffer, 0, commandBuffer.Length);

			// clear our buffer
			Array.Clear(_readBuffer, 0, _readBuffer.Length);

			// read the response
			int numBytesRead = _tcpStream.Read(_readBuffer, 0, _readBuffer.Length);

			// convert to a string
			string rspStr = ConvertToString(ref _readBuffer);

			// parse the response
			string[] tokens = rspStr.Split(',');

			errorCode = Util.ConvertStringToInt32(tokens[0]);

			// it should always be 2
			if (tokens.Length >= 2)
			{
				return tokens[1];
			}
			else
			{
				return "";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="statusString"></param>
		/// <param name="expectedResult"></param>
		/// <returns></returns>
		private bool IsOperationCompleted(string statusString, string expectedResult = "1")
		{
			bool completed = false;

			// If Scope returns '+x' instead of 'x', where 'x' is any character, trim it.
			if (statusString.IndexOf('+') == 0)
			{
				statusString = statusString.TrimStart('+');
			}

			if (statusString.Equals(expectedResult) == true)
			{
				completed = true;
			}

			return completed;
		}

		#endregion

		#region PublicFunctions
		/// <summary>
		/// ScopeKeysightScpi factory constructor
		/// </summary>
		/// <param name="deviceName"></param>
		/// <param name="configurationManager"></param>
		public ScopeKeysightScpi(string deviceName, IConfigurationManager configurationManager, ILogger logger)
		{
			Name = deviceName;

			_logger = logger;

			_configurationManager = configurationManager;
			_configuration = _configurationManager.GetConfiguration(Name);

			_address = _configuration.GetConfigurationValue("ScopeKeysightScpi", "Address", "");
			int port = _configuration.GetConfigurationValue("ScopeKeysightScpi", "Port", 0);

			const int READ_BUFFER_SIZE = 512;
			_readBuffer = new byte[READ_BUFFER_SIZE];

			TcpClient scopeSocketConn = new TcpClient(_address, port);
			_tcpStream = scopeSocketConn.GetStream();
			_tcpStream.ReadTimeout = m_READ_TIMEOUT;
			_state = State.Uninitialized;
			_selfTestResult = SelfTestResult.Unknown;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="name"></param>
		/// <param name="address"></param>
		public ScopeKeysightScpi(string name, string address, int port)
		{
			const int READ_BUFFER_SIZE = 512;
			_name = name;
			_logger = LogManager.GetCurrentClassLogger();
			_address = address;
			_readBuffer = new byte[READ_BUFFER_SIZE];
			TcpClient scopeSocketConn = new TcpClient(_address, port);
			_tcpStream = scopeSocketConn.GetStream();
			_tcpStream.ReadTimeout = m_READ_TIMEOUT;
			_state = State.Uninitialized;
			_selfTestResult = SelfTestResult.Unknown;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public bool ClearErrors()
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="channelNumber"></param>
		/// <param name="timePerDivison"></param>
		/// <param name="timeOffset"></param>
		/// <param name="voltageOffset"></param>
		/// <param name="voltageScale"></param>
		/// <param name="inputImpedance"></param>
		public void ConfigureChannel(int channelNumber, double timePerDivison, double timeOffset, double voltageOffset, double voltageScale, string inputImpedance)
		{
			// make sure it is stopped first
			string command = m_STOPCMD + "\n";
			IOWrite(command);

			/*command = m_ISOPCCMD + "\n";
			string rspStr = IOQuery(command);

			string[] tokens = rspStr.Split('\n');

			// check to see if STOP was completed.
			if (IsOperationCompleted(tokens[0]) == false)
			{
			   throw new Exception("STOP was not completed.");
			}*/

			// clear the measurement
			command = m_MEASCLEARCMD + "\n";
			IOWrite(command);

			//1. time per division
			command = m_TIMESCALECMD + timePerDivison.ToString() + "\n";
			IOWrite(command);

			//2. time offset
			command = m_TIMEOFFSETCMD + timeOffset.ToString() + "\n";
			IOWrite(command);

			//3. Volt per division
			command = m_COLON + m_CHANCMD + channelNumber.ToString() + m_VOLTPERDIVCMD + voltageScale.ToString() + "\n";
			IOWrite(command);

			//4. Volt offset
			command = m_COLON + m_CHANCMD + channelNumber.ToString() + m_VOLTOFFSETCMD + voltageOffset.ToString() + "\n";
			IOWrite(command);

			// It sets input impedance to 1M ohmns.
			// The value of inputImpedance is expected to be "DC", equivalent to 1M ohmn; otherwise, it outputs error.
			if (inputImpedance.ToUpper().Trim() != m_INPUTIMPEDANCECMD.ToUpper())
			{
				throw new Exception("ScopeSetupChannel() expected inputImpedance to be DC.");
			}

			command = m_COLON + m_CHANCMD + channelNumber.ToString() + m_CHANINPUTCMD + inputImpedance + "\n";
			IOWrite(command);

			// It turns on the specified channel.
			command = m_COLON + m_CHANCMD + channelNumber.ToString() + m_CHANDISPCMD + m_OPTION_ON + "\n";
			IOWrite(command);

			// start it back up
			command = m_RUNCMD + "\n";
			IOWrite(command);
		}

		/// <summary>
		/// 
		/// </summary>
		public string DetailedStatus
		{
			get
			{
				return "This is a Scpi Scope";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public bool DisplayEnabled
		{
			get
			{
				throw new NotImplementedException();
			}

			set
			{
				throw new NotImplementedException();
			}
		}


		/// <summary>
		/// Dispose of this objects resources
		/// </summary>
		public void Dispose()
		{
			try
			{
				Dispose(true);

				GC.SuppressFinalize(this);
			}
			catch (Exception)
			{
				try
				{
					//ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace);
				}
				catch (Exception)
				{
					//Do not rethrow. Exception from error logger that has already been garbage collected
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public bool FrontPanelEnabled
		{
			get
			{
				throw new NotImplementedException();
			}

			set
			{
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public bool HasItTriggered()
		{
			// Check the Trigger Event register to see if the event has properly triggered

			string command = m_ISITCOMPLETEDCMD + "\n";
			//string command = m_ISTRIGGEREDCMD + "\n";
			string rspStr = IOQuery(command);
			bool isTriggered = false;

			string[] tokens = rspStr.Split('\n');

			// check to see if it is triggered.
			if (IsOperationCompleted(tokens[0]) == true)
			{
				isTriggered = true;
			}

			return isTriggered;
		}

		/// <summary>
		/// 
		/// </summary>
		public InstrumentMetadata Info
		{
			get
			{
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public void Initialize()
		{
			// if we have not yet been initialized, go ahead and create the socket
			if (_state == State.Uninitialized)
			{
				Reset();

				// we already connected to the instrument in the constructor
				_state = State.Ready;
			}
			else
			{
				throw new Exception("expected the state of System " + _name + " to be Uninitialized, state was: " + _state.ToString());
			}
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="command"></param>
		/// <returns></returns>
		public string IOQuery(string command)
		{
			// send the command
			IOWrite(command, false);

			// clear our buffer
			Array.Clear(_readBuffer, 0, _readBuffer.Length);

			// read from the response
			int numBytesRead = _tcpStream.Read(_readBuffer, 0, _readBuffer.Length);

			// convert response to a string
			string rspStr = ConvertToString(ref _readBuffer);

			// check for errors
			int err = 0;
			string errorMessage = GetErrorCode(out err);
			if (err != 0)
			{
				throw new Exception("ScopeKeysightScpi:IOQuery() - Scope " + _name + " returned error code: " + err.ToString() + "," + errorMessage);
			}

			return rspStr;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="command"></param>
		public void IOWrite(string command, bool shallWeCheckForError = true)
		{
			// convert to a byte array
			byte[] commandBuffer = Encoding.ASCII.GetBytes(command);

			// send the data out
			_tcpStream.Write(commandBuffer, 0, commandBuffer.Length);

			// check for errors
			if (shallWeCheckForError == true)
			{
				int err = 0;
				string errorMessage = GetErrorCode(out err);
				if (err != 0)
				{
					throw new Exception("ScopeKeysightScpi:IOWrite() - Scope " + _name + " returned error code: " + err.ToString() + "," + errorMessage);
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public double MeasureFallTime(int channelNumber)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="channelNumber"></param>
		/// <returns></returns>
		public double MeasureFrequency(int channelNumber)
		{
			// turn off system header
			string command = m_SYSHEADEROFFCMD + "\n";
			IOWrite(command);

			// send the command to read the data
			command = m_MEASFREQCMD + m_CHANCMD + channelNumber.ToString() + "\n";

			string rspStr = IOQuery(command);

			string[] tokens = rspStr.Split('\n');

			double freq = Util.ConvertStringToDouble(tokens[0]);

			return freq;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public double MeasureMaxVoltage(int channelNumber)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public double MeasureMinVoltage(int channelNumber)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public double MeasurePulseWidth(int channelNumber)
		{
			// turn off system header
			string command = m_SYSHEADEROFFCMD + "\n";
			IOWrite(command);

			// send the command to read the data
			command = m_MEASPWIDCMD + m_CHANCMD + channelNumber.ToString() + "\n";

			string rspStr = IOQuery(command);

			string[] tokens = rspStr.Split('\n');

			double pulseWidth = Util.ConvertStringToDouble(tokens[0]);

			return pulseWidth;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public double MeasureRiseTime(int channelNumber)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public double MeasureVoltageLevel(int channelNumber)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		public string Name
		{
			get { return _name; }
			set { _name = value; }
		}

		/// <summary>
		/// 
		/// </summary>
		public SelfTestResult SelfTestResult
		{
			get
			{
				return _selfTestResult;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public State Status
		{
			get
			{
				return _state;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public SelfTestResult PerformSelfTest()
		{
			try
			{
				// change the timeout to account for the long self test
				_tcpStream.ReadTimeout = 90000;

				// send the command and get the response 
				string command = m_SELFTESTCMD + "\n";
				string rspStr = IOQuery(command);

				// parse the response
				string[] tokens = rspStr.Split('\n');

				//Check if self test is completed; returns status of "0".
				if (IsOperationCompleted(tokens[0], "0") == false)
				{
					_selfTestResult = SelfTestResult.Fail;
					string errorMsg = "System " + _name + " returned an error: " + tokens[0];
					throw new Exception(errorMsg);
				}
				else
				{
					_selfTestResult = SelfTestResult.Pass;
				}

				return _selfTestResult;
			}
			catch (Exception)
			{
				throw;
			}
			finally
			{
				// restore the timeout
				_tcpStream.ReadTimeout = m_READ_TIMEOUT;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public void Reset()
		{
			// Resets the oscilloscope
			string command = m_RESETCMD + "\n";
			IOWrite(command);

			// just a swag
			Thread.Sleep(3000);

			/*command = m_ISOPCCMD + "\n";
			string rspStr = IOQuery(command);

			// parse the response
			string[] tokens = rspStr.Split('\n');

			if (IsOperationCompleted(tokens[0]) == false)
			{
			   throw new Exception("Reset was not completed.");
			}*/
		}

		/// <summary>
		/// 
		/// </summary>
		public void SaveImage(string fileName)
		{
			try
			{
				string timeStamp_date = DateTime.Now.ToString("yyyyMMdd");
				string timeStamp_sec = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
				string folderTimeStamp = DateTime.Now.ToString("yyyy-MM-dd-HH-mm");

				string srcPath = $"{m_DOUBLESLASH}{_address}{m_IMAGE_DEST}{m_DESIRED_FOLDER}{m_SLASH}{timeStamp_date}{m_SLASH}{folderTimeStamp}{m_SLASH}";

				bool exists = System.IO.Directory.Exists(srcPath);

				if (!exists)
				{
					try
					{
						System.IO.Directory.CreateDirectory(srcPath);
						//m_SrcDirectory = $"{m_DOUBLESLASH}{_address}{m_IMAGE_DEST}{m_DESIRED_FOLDER}{m_SLASH}{testUnit}";
						// m_TimeStampeDate = timeStamp_date;
					}
					catch (Exception)
					{
						Reset();
						throw;
					}
				}

				string m_scopeFileName = $"{timeStamp_date}{m_SLASH}{folderTimeStamp}{m_SLASH}{fileName}_{timeStamp_sec}.{m_IMAGE_FORMAT}";

				string filename = $"{srcPath}{fileName}_{timeStamp_sec}";

				string command = m_SAVEIMAGECMD + m_DOUBLEQUOTES + filename + m_DOUBLEQUOTES + m_COMMA + m_IMAGE_FORMAT + m_COMMA + m_IMAGE_SOURCE + m_COMMA + m_OPTION_ON + "\n";
				IOWrite(command);
			}
			catch (Exception)
			{
				Reset();
				throw;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public void SetupTrigger(bool useRisingEdge, int channelNumber, double triggerLevel)
		{
			try
			{
				string command = m_STOPCMD + "\n";
				IOWrite(command);

				/*command = m_ISOPCCMD + "\n";
				string rspStr = IOQuery(command);
				string[] tokens = rspStr.Split('\n');

				// check to see if STOP was completed
				if (IsOperationCompleted(tokens[0]) == false)
				{
				   throw new Exception("STOP was not completed.");
				}*/

				command = m_MEASCLEARCMD + "\n";
				IOWrite(command);

				//5. Trigger level
				//*** Trigger edge source
				command = m_TRIGSRCCMD + m_CHANCMD + channelNumber.ToString() + "\n";
				IOWrite(command);

				//*** select edge type
				if (useRisingEdge)
				{
					command = m_RISETRIGEDGESELCMD + "\n";
				}
				else
				{
					command = m_FALLTRIGEDGESELCMD + "\n";
				}
				IOWrite(command);

				//*** select trigger level
				command = m_TRIGLEVCMD + m_CHANCMD + channelNumber.ToString() + ", " + triggerLevel.ToString() + "\n";
				IOWrite(command);

				/*command = m_ISOPCCMD + "\n";
				rspStr = IOQuery(command);

				tokens = rspStr.Split('\n');

				// check to see if trigger level command was completed
				if (IsOperationCompleted(tokens[0]) == false)
				{
				   throw new Exception("Trigger level was not completed.");
				}*/

				// start it back up
				command = m_RUNCMD + "\n";
				IOWrite(command);
			}
			catch (Exception)
			{
				Reset();
				throw;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public void Shutdown()
		{
			string errorMsg = "";

			if (_state == State.Ready)
			{
				try
				{
					//Reset System
					Reset();
				}
				catch (Exception err)
				{
					errorMsg += err.Message + " ";
				}

				_state = State.Uninitialized;
			}

			// the stream was created in the constructor, dispose of it here
			try
			{
				if (_tcpStream != null)
				{
					_tcpStream.Dispose();
					_tcpStream = null;
				}
			}
			catch (Exception err)
			{
				errorMsg += err.Message + " ";
			}

			if (errorMsg != "")
			{
				throw new Exception("System " + _name + " Had an error: " + errorMsg);
			}
		}

		#endregion
	}
}
