using System;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using Microsoft.Win32;
using System.Text.RegularExpressions;
using System.IO.Ports;
using System.Net.NetworkInformation;

namespace ClimaChamber
{
    /// <summary>
    /// Climatic Chamber Class. Provides communication between PC and climatic chamber by Ethernet or RS232.
    /// Periodically reads actual temperature and humidity from climatic chamber
    /// </summary>
    class Weiss : IClimaticChamber
    {
        #region Private global values
        /// <summary>
        /// Delay to wait for response
        /// </summary>
        private const int WaitDelay = 300; //[ms]
        /// <summary>
        /// Chambers timeout
        /// </summary>
        private readonly Int32 ChamberTimeout = 0; // [ms]
        /// <summary>
        /// Maximum buffers size
        /// </summary>
        private Int32 ChamberBufferSize = 2048;
        /// <summary>
        /// Ending messages character
        /// </summary>
        private readonly String ChamberEndMSG = "\r";
        /// <summary>
        /// Minimum interval between messages
        /// </summary>
        private readonly Double ChamberInterval = 300;
        /// <summary>
        /// PID of port
        /// </summary>
        private readonly String PortPID = "7715";
        /// <summary>
        /// VID of port
        /// </summary>
        private readonly String PortVID = "9710";
        /// <summary>
        /// Communication speed
        /// </summary>
        private readonly Int32 Baudrate = 9600;
        /// <summary>
        /// Maximum temperature limit
        /// </summary>
        private readonly int MAX_TEMP = 180;
        /// <summary>
        /// Minimum temperature limit
        /// </summary>
        private readonly int MIN_TEMP = -70;
        /// <summary>
        /// Maximum humidity limit
        /// </summary>
        private readonly int MAX_HUMI = 98;
        /// <summary>
        /// Minimal humidity limit
        /// </summary>
        private readonly int MIN_HUMI = 10;
        /// <summary>
        /// Maximum heating gradient
        /// </summary>
        private readonly float RATE_HEAT = 5f;
        /// <summary>
        /// Maximum cooling gradient
        /// </summary>
        private readonly float RATE_COOL = 25f;
        /// <summary>
        /// Maximum humidity gradient
        /// </summary>
        private readonly float RATE_HUMI = 25f;
        /// <summary>
        /// Maximum dehumidity gradient
        /// </summary>
        private readonly float RATE_DEHUMI = 25f;
        #endregion
        /// <summary>
        /// Device IP address
        /// </summary>
        private IPAddress ChamberIPaddress;
        /// <summary>
        /// Device port
        /// </summary>
        private Int32 ChamberPort;
        /// <summary>
        /// Device address 
        /// </summary>
        private Int32 ChamberBussAddress = 1;
        /// <summary>
        /// Device state
        /// </summary>
        private  ChamberStatus LastStatus;
        /// <summary>
        /// Holds all information about device
        /// </summary>
        private  Chamber chamber;
        /// <summary>
        /// Internal updater
        /// </summary>
        private  Thread UpdateTh = null;
        /// <summary>
        /// State of idle
        /// </summary>
        private  bool Idle = true;
        /// <summary>
        /// Locker
        /// </summary>
        private object readlocker = new object();
        /// <summary>
        /// Delay for updater
        /// </summary>
        private TimeSpan UpdaterDelay = TimeSpan.FromSeconds(2);
        /// <summary>
        /// Locker
        /// </summary>
        private object Locker4Var = new object();

        #region Public Global values
        /// <summary>
        /// Returns chamber status
        /// </summary>
        public  ChamberStatus GetStatus { get { return chamber.Status; } }
        /// <summary>
        /// Returns chambers client status
        /// </summary>
        public  ChamberStatus GetClientStatus { get { return chamber.Comm.Status; } }
        /// <summary>
        /// Returns string filled with error. No errors => null string
        /// </summary>
        public  String GetError { get { return chamber.Error; } }
        /// <summary>
        /// Last know temperature
        /// </summary>
        public  Double Chamber_Actual_Temperature { get { return chamber.Actual.Temperature; } }
        /// <summary>
        /// Last know humidity
        /// </summary>
        public  Double Chamber_Actual_Humidity { get { return chamber.Actual.Humidity; } }
        /// <summary>
        /// Gets nominal temperature
        /// </summary>
        public  Double Chamber_Nominal_Temperature { get { return chamber.Nominal.Temperature; } }
        /// <summary>
        /// Gets nominal humidity
        /// </summary>
        public  Double Chamber_Nominal_Humidity { get { return chamber.Nominal.Humidity; } }
         /// <summary>
         /// Gets nominal fan speed
         /// </summary>
        public  Single Chamber_Nominal_FanSpeed { get { return chamber.Nominal.FanSpeed; } }
         /// <summary>
         /// Gets heats gradient
         /// </summary>
        public  Single Chamber_HeatGradient { get { return chamber.Rates.HeatGradient; } }
         /// <summary>
         /// Gets cooling gradient
         /// </summary>
        public  Single Chamber_CoolGradient { get { return chamber.Rates.CoolGradient; } }
         /// <summary>
         /// Gets humidity gradient
         /// </summary>
        public  Single Chamber_HumidityGradient { get { return chamber.Rates.HumidityGradient; } }
        /// <summary>
        /// Gets dehumidity gradient
        /// </summary>
        public  Single Chamber_DehumidityGradient { get { return chamber.Rates.DeHumidityGradient; } }
        /// <summary>
        /// Gets last status time
        /// </summary>
        public  DateTime Chamber_LastStatusTime { get { return chamber.Comm.LastStatusTime; } }
        /// <summary>
        /// Gets chamber connecting status
        /// </summary>
        public  Boolean IsChamberConnected { get { return chamber.Comm.IsConnected; } }
        /// <summary>
        /// Sets or Gets correct COM (only for RS232)
        /// </summary>
        public  String COM = "";

        public IPAddress GetChamberIP { get { return ChamberIPaddress; } }
        #endregion

        #region enums & struct
        /// <summary>
        /// Programs state
        /// </summary>
        public enum ChamberProgram
        {
            Start,
            Stop
        }
        /// <summary>
        /// Chambers state
        /// </summary>
        public enum ChamberStatus
        {
            Error_Communication,
            Error_Chamber,
            NOTready_ON, 
            NOTready_OFF,
            Ready_ON,
            Ready_OFF,
            Connected,
            Disconnected,
            Invalid_Response,
            Values_could_not_be_set,
        }
        /// <summary>
        /// Structure for actual parameters
        /// </summary>
        private struct ChamberActual
        {
            public Double Temperature;
            public Double Humidity;
        }
        /// <summary>
        /// Structure for nominal parameters
        /// </summary>
        private struct ChamberNominal
        {
            public Double Temperature;
            public Double Humidity;
            /// <summary>
            /// in %
            /// </summary>
            public Single FanSpeed;
        }
        /// <summary>
        /// Structure for gradients for all parameters
        /// </summary>
        private struct ChamberRates
        {
            public Single HeatGradient;
            public Single CoolGradient;
            public Single HumidityGradient;
            public Single DeHumidityGradient;
        }
        /// <summary>
        /// Structure for communication _data
        /// </summary>
        private struct ChamberCommunication
        {
            public Boolean UsingNetwork;
            public Boolean UsingSerial;
            public SerialPort SP;
            public TcpClient Client;
            public Boolean IsConnected;
            public NetworkStream Stream;
            public IPEndPoint Address;
            public Int32 Timeout;
            public Byte[] RxBuffer;
            public Byte[] TxBuffer;
            public DateTime LastStatusTime;
            public ChamberStatus Status;
            public String ReadLine;
            public String RxMsg;
            public EventWaitHandle W8read;
        }
        /// <summary>
        /// Main structure. All above structure included
        /// </summary>
        private struct Chamber
        {
            public ChamberNominal Nominal;
            public ChamberRates Rates;
            public ChamberCommunication Comm;
            public ChamberStatus Status;
            public ChamberActual Actual;
            public String Error;
        }
        #endregion

        #region Constructors&Destructor
        /// <summary>
        /// Initialization this class. Starts thread 'Updater' - UpdaterThread()
        /// </summary>
        public Weiss()
        {
            Initialize();
        }
        #endregion

        #region Private sector
        /// <summary>
        /// If climatic chamber connected, this function will update its status every 3 seconds
        /// </summary>
        private void UpdaterThread()
        {
            try
            {
                while (!chamber.Comm.IsConnected)
                    Thread.Sleep(UpdaterDelay);
                while (true)
                {
                    UpdateStatus();
                    Thread.Sleep(UpdaterDelay);
                }
            }
            catch (ThreadAbortException ex)
            {
                ClimError.Add("WEISS.UpdaterThread", "Error in updater", ex.ToString());
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.UpdaterThread", "Error in updater", ex.ToString());
            }
        }
        /// <summary>
        /// Connect to Climatic chamber trough TCP
        /// </summary>
        private  bool NetworkConnect()
        {
            bool retVal = false;
            try
            {
                    chamber.Comm.Client = new TcpClient();
                    chamber.Comm.Client.SendTimeout = chamber.Comm.Timeout;
                    chamber.Comm.Client.ReceiveTimeout = chamber.Comm.Timeout;
                    if (!checkIfPortExist(ChamberPort))
                    {
                        ClimError.Add("WEISS.NetworkConnect", string.Format("Port {0} on IP {1} does not exist", ChamberPort, ChamberIPaddress)
                            , string.Format("Port {0} on IP {1} does not exist", ChamberPort, ChamberIPaddress));
                        retVal = false;
                    }
                    chamber.Comm.Client.Connect(chamber.Comm.Address);
                    chamber.Comm.Stream = chamber.Comm.Client.GetStream();
                    chamber.Comm.Status = ChamberStatus.Connected;
                    chamber.Status = ChamberStatus.Ready_ON;
                    chamber.Comm.IsConnected = true;
                    retVal = true;
            }
            catch (Exception ex)
            {
                ClimError.Add("Climatic chamber", "Can't connect to climatic chamber thought TCP", ex.ToString());
				retVal = false;
            }
            return retVal;
        }

        /// <summary>
        /// Checking iPort existing
        /// </summary>
        /// <param name="iPort">Ports number</param>
        /// <returns>True - exist, or False</returns>
        private  bool checkIfPortExist(int iPort)
        {
            bool _result = true;

            try
            {
                IPGlobalProperties _ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
                TcpConnectionInformation[] _tcpConnInfoArray = _ipGlobalProperties.GetActiveTcpConnections();

                foreach (TcpConnectionInformation tcpi in _tcpConnInfoArray)
                {
                    if (tcpi.LocalEndPoint.Port == iPort)
                    {
                        _result = false;
                        break;
                    }
                }

            }
            catch (NetworkInformationException ex)
            { 
                ClimError.Add("Climatic chamber", "Cant't find port", ex.ToString());
            }
            return _result;
        }

        /// <summary>
        /// Initialization Chamber structure
        /// </summary>
        private  void Initialize()
        {
            chamber = new Chamber() 
            { 
                Comm = new ChamberCommunication(), 
                Nominal = new ChamberNominal(), 
                Rates = new ChamberRates(), 
                Actual = new ChamberActual(), 
                Status = ChamberStatus.NOTready_OFF 
            };
            chamber.Comm.Status = ChamberStatus.Disconnected;
            chamber.Comm.IsConnected = false;
            chamber.Comm.RxBuffer = new Byte[ChamberBufferSize];
            chamber.Comm.TxBuffer = new Byte[ChamberBufferSize];
            chamber.Actual.Humidity = 0;
            chamber.Actual.Temperature = 0;
            chamber.Error = null;
            chamber.Comm.Timeout = ChamberTimeout;
            chamber.Comm.LastStatusTime = DateTime.UtcNow;
            chamber.Comm.W8read = new EventWaitHandle(false, EventResetMode.AutoReset);
        }

        /// <summary>
        /// Sends string to climatic chamber
        /// </summary>
        /// <param name="_data">Message for climatic chamber (! must be in correct form)</param>
        private  void Send(String iData)
        {
            try
            {
                while (DateTime.UtcNow - chamber.Comm.LastStatusTime < TimeSpan.FromMilliseconds(ChamberInterval))
                {
                    Thread.Sleep(300);
                }
                lock (readlocker)
                {
                    if (chamber.Comm.Status == ChamberStatus.Connected)
                    {
                        chamber.Comm.TxBuffer = Encoding.ASCII.GetBytes(iData);
                        SendProvider(0, iData.Length);
                        chamber.Comm.LastStatusTime = DateTime.UtcNow;
                    }
                }
            }
            catch (Exception ex)
            {
                ClimError.Add("Climatic chamber", "Error in send provider", ex.ToString());
            }
        }

        /// <summary>
        /// Sends _data through TCP or RS232
        /// </summary>
        /// <param name="iOffset"><_data iOffset/param>
        /// <param name="iLength">iLength of _data array</param>
        /// <param name="iOutBuffer">UNnesseccery parameter, If it is null, iProgram will use chamber.Comm.TxBuffer</param>
        private  void SendProvider(int iOffset, int iLength, byte[] iOutBuffer = null)
        {
            try
            {
                if (chamber.Comm.UsingNetwork)
                {
                    if (iOutBuffer != null)
                        chamber.Comm.Stream.Write(iOutBuffer, iOffset, iLength);
                    else
                        chamber.Comm.Stream.Write(chamber.Comm.TxBuffer, iOffset, iLength);
                    chamber.Comm.Stream.Flush();
                }
                else if (chamber.Comm.UsingSerial)
                {
                    if (iOutBuffer != null)
                        chamber.Comm.SP.BaseStream.Write(iOutBuffer, iOffset, iLength);
                    else
                        chamber.Comm.SP.Write(chamber.Comm.TxBuffer, iOffset, iLength);
                    chamber.Comm.SP.BaseStream.Flush();
                }
            }
            catch (Exception ex)
            {
                ClimError.Add("Climatic chamber", "Error in send provider", ex.ToString());
            }
        }

        /// <summary>
        /// Reads incoming message from climatic chamber
        /// </summary>
        /// <returns>String message</returns>
        private String Read()
        {
            lock (readlocker)
            {
                String _result = null;
                try
                {
                    if (chamber.Comm.Status == ChamberStatus.Connected)
                    {
                        if (chamber.Comm.UsingSerial)
                        {
                            Thread.Sleep(WaitDelay);
                            int _buff = chamber.Comm.SP.Read(chamber.Comm.RxBuffer, 0, chamber.Comm.SP.BytesToRead);
                            _result = Encoding.ASCII.GetString(chamber.Comm.RxBuffer, 0, _buff);
                        }
                        else
                        {
                            Thread.Sleep(WaitDelay);
                            if (chamber.Comm.Stream.CanRead && chamber.Comm.Stream.DataAvailable)
                            {
                                int _len = chamber.Comm.Client.Available;
                                chamber.Comm.Stream.BeginRead(chamber.Comm.RxBuffer, 0, _len, new AsyncCallback(EndReding), (object)_len);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    ClimError.Add("Climatic chamber", "Error in read provider", ex.ToString());  
                }
                return _result;
            }
        }

        private void EndReding(IAsyncResult ar)
        {
            int _len = (int)ar.AsyncState;
            chamber.Comm.RxMsg = Encoding.ASCII.GetString(chamber.Comm.RxBuffer, 0, _len);
            chamber.Comm.W8read.Set();
        }

        /// <summary>
        /// Reads saved values from climatic chamber
        /// </summary>
        private  void ReadStatus()
        {
                String _data = null;
                Send(string.Format("${0:00}I{1}", ChamberBussAddress, ChamberEndMSG));
                if (chamber.Comm.UsingNetwork)
                {
                    _data = Read();
                    if (chamber.Comm.W8read.WaitOne(TimeSpan.FromMilliseconds(WaitDelay)))
                        _data = chamber.Comm.RxMsg;
                    else
                        _data = string.Empty;
                }
                else
                {
                    ///Trigger by ErrorReceivedHandler() or SerialPortmsgReceiver()
                    chamber.Comm.W8read.WaitOne(WaitDelay);
                    _data = chamber.Comm.ReadLine;
                }
                if (_data == null)
                    chamber.Status = ChamberStatus.Error_Communication;
                else if (_data.Length != 14 * 7 + 33)
                    chamber.Status = ChamberStatus.Error_Chamber;
                else
                {
                    lock (Locker4Var)
                    {
                    chamber.Actual.Temperature = Convert.ToSingle(_data.Substring(7, 6));
                    chamber.Nominal.Temperature = Convert.ToSingle(_data.Substring(0, 6));
                    chamber.Actual.Humidity = Convert.ToSingle(_data.Substring(21, 6));
                    chamber.Nominal.Humidity = Convert.ToSingle(_data.Substring(14, 6));
                    chamber.Nominal.FanSpeed = Convert.ToSingle(_data.Substring(28, 6));
                    float _set2 = Convert.ToSingle(_data.Substring(35, 6));
                    chamber.Comm.LastStatusTime = DateTime.UtcNow;
                    }
                }
        }

        /// <summary>
        /// Creates basic message $xxE NominalTemp NominalHumidity NominalFanSpeed ...
        /// </summary>
        /// <returns>Message</returns>
        private  String ConvertToString()
        {
            String _result = string.Format("${0:00}E ", ChamberBussAddress);
            _result += Chamber_Nominal_Temperature.ToString("0000.0");
            _result += Chamber_Nominal_Humidity.ToString(" 0000.0");
            _result += Chamber_Nominal_FanSpeed.ToString(" 0000.0");
            _result += " 0000.0 0000.0 0000.0 0000.0 ";
            _result += "0";
            _result += "0"; //Start
            _result += "1"; //iHumidity
            _result += "1"; //condensation protection
            _result += "0"; //capacitive sensor
            _result += "0"; //compressed air (dry)
            _result += "0"; //regeneration dryer
            _result += "0"; //noxious gas
            _result += "0"; //Clarification
            _result += "0"; //Custom01
            _result += "0"; //Custom02
            _result += "0"; //Custom03
            _result += "0"; //Custom04
            _result += "0"; //Adjustm. iTemp. low
            _result += "0"; //Adjustm. iTemp. high
            _result += "0"; //Adjustm. iTemp. calc.
            _result += "0"; //quit all errors
            _result += "0"; //quit all errors
            _result += "00000000000000" + ChamberEndMSG; //unused
            //String _result = "$" + ChamberBussAddress.ToString("00") + "P";
            //_result += "0000" + ChamberEndMSG;
            return _result;
        }

        /// <summary>
        /// Creates basic message $xxE NominalTemp NominalHumidity NominalFanSpeed ...
        /// </summary>
        /// <param name="iTemp">Required nominal temperature</param>
        /// <param name="iHumid">Required nominal humidity</param>
        /// <param name="iFanSpeed">Required nominal fan speed</param>
        /// <returns>Message</returns>
        private  String ConvertToString(Double iTemp, Double iHumid, Single iFanSpeed)
        {
            String _result = string.Format("${0:00}E ", ChamberBussAddress);
            _result += iTemp.ToString("0000.0");
            _result += iHumid.ToString(" 0000.0");
            _result += iFanSpeed.ToString(" 0000.0");
            Idle = false;
            if(!Idle)
            {
                Idle = true;
                _result += " 0000.0 0000.0 0000.0 0000.0 ";
                _result += "0";
                _result += "1"; //Start
                _result += "1"; //iHumidity
                _result += "1"; //condensation protection
                _result += "0"; //capacitive sensor
                _result += "1"; //compressed air (dry)
                _result += "0"; //regeneration dryer
                _result += "0"; //noxious gas
                _result += "0"; //Clarification
                _result += "0"; //Custom01
                _result += "0"; //Custom02
                _result += "0"; //Custom03
                _result += "0"; //Custom04
                _result += "0"; //Adjustm. iTemp. low
                _result += "0"; //Adjustm. iTemp. high
                _result += "0"; //Adjustm. iTemp. calc.
                _result += "0"; //quit all errors
                _result += "0"; //quit all errors
                _result += "00000000000000" + ChamberEndMSG; //unused
                //_result += "01111111101010111010101010101010" + ChamberEndMSG;
                //_result += "01110000000000000000000000000000" + ChamberEndMSG;
            }
            else
            {
                _result += " 0000.0 0000.0 0000.0 0000.0 01110000000000000000000000000000" + ChamberEndMSG;
            }
            return _result;
        }

        /// <summary>
        /// Creates message for change rates in climatic chamber
        /// </summary>
        /// <param name="iHeatGrad">Heat rate</param>
        /// <param name="iCoolGrad">Cooling rate</param>
        /// <param name="iHumGrad">iHumidity rate</param>
        /// <param name="iDehumGrad">Dehumidity rate</param>
        /// <returns>Message</returns>
        private  String ConvertToString(Double iHeatGrad, Double iCoolGrad, Double iHumGrad, Double iDehumGrad)
        {
            String _result = string.Format("${0:00}U", ChamberBussAddress);

            if(iHeatGrad >= 0 && iHeatGrad <= RATE_HEAT)
                _result += iHeatGrad.ToString(" 0000.0");
            else
                _result += RATE_HEAT.ToString(" 0000.0");
            if(iCoolGrad >= 0 && iCoolGrad <= RATE_COOL)
                _result += iCoolGrad.ToString(" 0000.0");
            else
                _result += RATE_COOL.ToString(" 0000.0");
            if(iHumGrad >= 0 && iHumGrad <= RATE_HUMI)
                _result += iHumGrad.ToString(" 0000.0");
            else
                _result += RATE_HUMI.ToString(" 0000.0");
            if(iDehumGrad >= 0 && iDehumGrad <= RATE_DEHUMI)
                _result += iDehumGrad.ToString(" 0000.0");
            else
                _result += RATE_DEHUMI.ToString(" 0000.0");
            _result += ChamberEndMSG;
            return _result;
        }

        /// <summary>
        /// Creates message for start specific iProgram
        /// </summary>
        /// <param name="iProgram">Number of iProgram 0 - 32</param>
        /// <returns>Message</returns>
        private  String ConvertToString(UInt32 iProgram)
        {
            String _result = string.Format("${0:00}P", ChamberBussAddress);
            if (iProgram <= 32)
                _result += iProgram.ToString("0000");
            else
                _result += "0000";
            _result += ChamberEndMSG;
            return _result;
        }

        /// <summary>
        /// Creates message for checked or clear errors
        /// </summary>
        /// <param name="iClearError">True - clear errors, False - check errors</param>
        /// <returns>Message</returns>
        private  String ConvertToString(Boolean iClearError)
        {
            if (iClearError == false)
            {
                String _result = string.Format("${0:00}F", ChamberBussAddress);
                _result += ChamberEndMSG;
                return _result;
            }
            else
            {
                String _result = string.Format("${0:00}Q", ChamberBussAddress);
                _result += ChamberEndMSG;
                return _result;
            }
        }

        /// <summary>
        /// Get chamber iPort name (RS232)
        /// </summary>
        /// <returns>Port name</returns>
        private  String GetChamberComPort()
        {
            String _VID = PortVID;
            String _PID = PortPID;

            String _pattern = String.Format("^VID_{0}.PID_{1}", _VID, _PID);
            Regex _rx = new Regex(_pattern, RegexOptions.IgnoreCase);
            String _comport = null;

            RegistryKey _rk1 = Registry.LocalMachine;
            RegistryKey _rk2 = _rk1.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum");

            foreach (String s3 in _rk2.GetSubKeyNames())
            {
                RegistryKey rk3 = _rk2.OpenSubKey(s3);
                foreach (String s in rk3.GetSubKeyNames())
                {
                    if (_rx.Match(s).Success)
                    {
                        RegistryKey rk4 = rk3.OpenSubKey(s);
                        foreach (String s2 in rk4.GetSubKeyNames())
                        {
                            RegistryKey rk5 = rk4.OpenSubKey(s2);
                            RegistryKey rk6 = rk5.OpenSubKey("Device Parameters");
                            _comport = ((string)rk6.GetValue("PortName"));
                        }
                    }
                }
            }
            return _comport;
        }

        /// <summary>
        /// Creates serial iPort connection
        /// </summary>
        /// <param name="iPortName">Port name</param>
        private  void SetChamberComPort()
        {
            chamber.Comm.SP = new SerialPort(COM);
            chamber.Comm.SP.PortName = COM;
            chamber.Comm.SP.BaudRate = Baudrate;
            chamber.Comm.SP.DataBits = 8;
            chamber.Comm.SP.StopBits = StopBits.One;
            chamber.Comm.SP.Parity = Parity.None;
            chamber.Comm.SP.Handshake = Handshake.None;
            chamber.Comm.SP.ReadBufferSize = ChamberBufferSize;
            chamber.Comm.SP.WriteBufferSize = ChamberBufferSize;

            chamber.Comm.SP.DataReceived += new SerialDataReceivedEventHandler(SerialPortmsgReceiver);
        }

        /// <summary>
        /// Provides serial iPort connection
        /// </summary>
        /// <returns>True - connection is open, False - connection cannot be open</returns>
        private  Boolean SerialConnect()
        {
            //iPort = GetChamberComPort();
            SetChamberComPort();
            if (chamber.Comm.SP == null)
                return false;

            return TryOpenChamberSP();
        }

        /// <summary>
        /// Open serial iPort
        /// </summary>
        /// <returns>True - serial iPort is open, False - serial iPort cannot be open</returns>
        private  Boolean TryOpenChamberSP()
        {
            bool retVal = false;
            try
            {
                chamber.Comm.SP.Open();

                if (chamber.Comm.SP.IsOpen)
                {
                    chamber.Comm.Status = ChamberStatus.Connected;
                    chamber.Comm.IsConnected = true;
                    retVal = true;
                }
                else
                    retVal = false;
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.TryOpenChamberSP", "Cannot open serial port", ex.ToString());
                retVal = false;
            }
            return retVal;
        }

        /// <summary>
        /// Handler for error from serial iPort
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Serial iPort error handler</param>
        private  void ErrorReceivedHandler(object sender, SerialErrorReceivedEventHandler e)
        {
            chamber.Comm.ReadLine = Read();
            ClimError.Add("WEISS.ErrorReceivedHandler", chamber.Comm.ReadLine, chamber.Comm.ReadLine);
            chamber.Comm.W8read.Set();
        }

        /// <summary>
        /// Handler for _data message from serial iPort
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Serial iPort _data handler</param>
        private  void SerialPortmsgReceiver(object sender, SerialDataReceivedEventArgs e)
        {
            chamber.Comm.ReadLine = Read();
            chamber.Comm.W8read.Set();
        }

        /// <summary>
        /// Set nominal value and send it to chamber.
        /// </summary>
        /// <param name="iTemperature">Nominal temperature</param>
        /// <param name="iHumidity">Nominal iHumidity</param>
        /// <param name="iFanSpeed">Nominal fan speed in %</param>
        /// <param name="idle">TRUE - chamber is in idle mod, FALSE - chamber is not in idle mod</param>
        /// <returns></returns>
        private Boolean SetNominalValues(Double iTemperature, Double iHumidity, Single iFanSpeed)
        {
            try
            {
                chamber.Nominal.Temperature = iTemperature;
                chamber.Nominal.Humidity = iHumidity;
                chamber.Nominal.FanSpeed = iFanSpeed;
                string _data;
                Send(ConvertToString(iTemperature, iHumidity, iFanSpeed));
                if (chamber.Comm.UsingNetwork)
				{
                    _data = Read();
                    if (chamber.Comm.W8read.WaitOne(TimeSpan.FromMilliseconds(WaitDelay)))
                        _data = chamber.Comm.RxMsg;
                    else
                        _data = string.Empty;
                }
                else
                {
                    ///Trigger by ErrorReceivedHandler() or SerialPortmsgReceiver()
                    chamber.Comm.W8read.WaitOne(WaitDelay);
                    _data = chamber.Comm.ReadLine;
                }
                if (_data == null)
                {
                    chamber.Status = ChamberStatus.Error_Chamber;
                    return false;
                }
                else if (_data.Length != 3)
                {
                    //invalid response
                    chamber.Status = ChamberStatus.Invalid_Response;
                    return false;
                }
                else if (_data != "0 " + ChamberEndMSG)
                {
                    //Values could not be set
                    chamber.Status = ChamberStatus.Values_could_not_be_set;
                    return false;
                }

                if (chamber.Status == ChamberStatus.NOTready_OFF)
                    chamber.Status = ChamberStatus.Ready_OFF;
                else
                    chamber.Status = ChamberStatus.Ready_ON;
                return true;
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.SetNominalValues", "Cannot set nominal values", ex.ToString());
                return false;
            }
        }

        /// <summary>
        /// Read nominal and actual parameters.
        /// </summary>
        private void UpdateStatus()
        {
            try
            {
                ReadStatus();
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.UpdateStatus", "Update status failed", ex.ToString());
            }
        }
        #endregion

        #region Public sector
        /// <summary>
        /// Connect to chamber though rs232
        /// </summary>
        /// <param name="portName">port name with number of port</param>
        /// <param name="baudRate">Ignored, method uses default value.</param>
        /// <param name="dataBits">Ignored, method  uses default value.</param>
        /// <param name="stopBits">Ignored, method  uses default value.</param>
        /// <param name="parity">Ignored, method  uses default value.</param>
        /// <returns>If port is open, the methods will return TRUE</returns>
        public bool Connect(string portName,
                            string baudRate,
                            string dataBits,
                            string stopBits,
                            string parity)
        {
            COM = portName; // the remaing com port configuration is left at their default values per SetChamberComPort()
            chamber.Comm.UsingNetwork = false;
            chamber.Comm.UsingSerial = true;
            return SerialConnect();
        }

        public bool Connect(IPAddress iIP, int port)
        {
            ChamberIPaddress = iIP;
            ChamberPort = port;
            chamber.Comm.Address = new IPEndPoint(ChamberIPaddress, ChamberPort);

            chamber.Comm.UsingNetwork = true;
            chamber.Comm.UsingSerial = false;
            return NetworkConnect();
        }

        /// <summary>
        /// Disconnect from chamber and set all setting to default state
        /// </summary>
        public void Disconnect()
        {
            if (IsChamberConnected)
            {
                //StopProgram();
                if (chamber.Comm.UsingSerial)
                {
                    if (chamber.Comm.SP.IsOpen)
                        chamber.Comm.SP.Close();
                }
                else if (chamber.Comm.UsingNetwork)
                {
                    if (chamber.Comm.Stream != null)
                    {
                        chamber.Comm.Stream.Close();
                        chamber.Comm.Stream = null;
                    }

                    if (chamber.Comm.Client != null)
                    {
                        chamber.Comm.Client.Close();
                        chamber.Comm.Client = null;
                    }
                }
            }
            Initialize();
        }

        /// <summary>
        /// Set change rates and send it to chamber
        /// </summary>
        /// <param name="iHeatGradient">Gradient for heat</param>
        /// <param name="iCoolGradient">Gradient for cooling system</param>
        /// <param name="iHumidityGradient">Gradient for humidity</param>
        /// <param name="iDeHumidityGradient">Gradient for dehumidity</param>
        /// <returns>TRUE - success</returns>
        public bool SetChangeRates(float iHeatGradient, float iHumidityGradient)
        {
            try
            {
                chamber.Rates.HeatGradient = iHeatGradient;
                chamber.Rates.HumidityGradient = iHumidityGradient;
                string _data;
                Send(ConvertToString(iHeatGradient, chamber.Rates.CoolGradient, iHumidityGradient, chamber.Rates.DeHumidityGradient));
                if (chamber.Comm.UsingNetwork)
				{
                    _data = Read();
                    if (chamber.Comm.W8read.WaitOne(TimeSpan.FromMilliseconds(WaitDelay)))
                        _data = chamber.Comm.RxMsg;
                    else
                        _data = string.Empty;
                }
                else
                {
                    ///Trigger by ErrorReceivedHandler() or SerialPortmsgReceiver()
                    chamber.Comm.W8read.WaitOne(WaitDelay);
                    _data = chamber.Comm.ReadLine;
                }
                if (_data == null)
                {
                    chamber.Status = ChamberStatus.Error_Chamber;
                    return false;
                }
                else if (_data.Length != 3)
                {
                    //invalid response
                    chamber.Status = ChamberStatus.Invalid_Response;
                    return false;
                }
                else if (_data != "0 " + ChamberEndMSG)
                {
                    //Values could not be set
                    chamber.Status = ChamberStatus.Values_could_not_be_set;
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.SetChangeRates", "Cannot set rates", ex.ToString());
                return false;
            }
        }

        /// <summary>
        /// Set change rates and send it to chamber
        /// </summary>
        /// <param name="iHeatGradient">Gradient for heat</param>
        /// <param name="iCoolGradient">Gradient for cooling system</param>
        /// <param name="iHumidityGradient">Gradient for humidity</param>
        /// <param name="iDeHumidityGradient">Gradient for dehumidity</param>
        /// <returns>TRUE - success</returns>
        public bool SetChangeRates(float iHeatGradient, float iCoolGradient, float iHumidityGradient, float iDeHumidityGradient)
        {
            try
            {
                chamber.Rates.HeatGradient = iHeatGradient;
                chamber.Rates.CoolGradient = iCoolGradient;
                chamber.Rates.HumidityGradient = iHumidityGradient;
                chamber.Rates.DeHumidityGradient = iDeHumidityGradient;
                string _data;
                Send(ConvertToString(iHeatGradient, iCoolGradient, iHumidityGradient, iDeHumidityGradient));
                if (chamber.Comm.UsingNetwork)
				{
                    _data = Read();
                    if (chamber.Comm.W8read.WaitOne(TimeSpan.FromMilliseconds(WaitDelay)))
                        _data = chamber.Comm.RxMsg;
                    else
                        _data = string.Empty;
                }
                else
                {
                    ///Trigger by ErrorReceivedHandler() or SerialPortmsgReceiver()
                    chamber.Comm.W8read.WaitOne(WaitDelay);
                    _data = chamber.Comm.ReadLine;
                }
                if (_data == null)
                {
                    chamber.Status = ChamberStatus.Error_Chamber;
                    return false;
                }
                else if (_data.Length != 3)
                {
                    //invalid response
                    chamber.Status = ChamberStatus.Invalid_Response;
                    return false;
                }
                else if (_data != "0 " + ChamberEndMSG)
                {
                    //Values could not be set
                    chamber.Status = ChamberStatus.Values_could_not_be_set;
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.SetChangeRates", "Cannot set rates", ex.ToString());
                return false;
            }
        }

        /// <summary>
        /// Stop the chamber
        /// </summary>
        /// <returns>TRUE - iProgram has been stopped, FALSE - can!t stop iProgram</returns>
        public Boolean Stop()
        {
            try
            {
                String _result = string.Format("${0:00}P0000{1}", ChamberBussAddress, ChamberEndMSG);
                Send(_result);
                if (chamber.Comm.UsingNetwork)
                {
                    _result = Read();
                    if (chamber.Comm.W8read.WaitOne(TimeSpan.FromMilliseconds(WaitDelay)))
                        _result = chamber.Comm.RxMsg;
                    else
                        _result = string.Empty;
                }
                else
                {
                    ///Trigger by ErrorReceivedHandler() or SerialPortmsgReceiver()
                    chamber.Comm.W8read.WaitOne(WaitDelay);
                    _result = chamber.Comm.ReadLine;
                }
                if (_result == null)
                {
                    chamber.Status = ChamberStatus.Error_Chamber;
                    return false;
                }
                else if (_result.Length != 3)
                {
                    //invalid response
                    chamber.Status = ChamberStatus.Invalid_Response;
                    ClimError.Add("WEISS.StopProgram", "Invalid response from Climatic chamber", "Invalid response from Climatic chamber");
                    return false;
                }
                else if (_result != "0 " + ChamberEndMSG)
                {
                    //Values could not be set
                    chamber.Status = ChamberStatus.Values_could_not_be_set;
                    ClimError.Add("WEISS.StopProgram", "Climatic Chamber: Program could not be stopped", "Climatic Chamber: Program could not be stopped");
                    return false;
                }
                if (chamber.Status == ChamberStatus.NOTready_ON)
                    chamber.Status = ChamberStatus.NOTready_OFF;
                else
                    chamber.Status = ChamberStatus.Ready_OFF;
                return true;
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.StopProgram", "Program cannot be stooped", ex.ToString());
                return false;
            }
        }

        /// <summary>
        /// Checking errors present in Chamber
        /// </summary>
        /// <returns>TRUE - error present, FALSE - No error present or invalid response</returns>
        public  Boolean CheckError()
        {
            try
            {
                Send(ConvertToString(false));
                string _data;
                if (chamber.Comm.UsingNetwork)
                {
                    _data = Read();
                    if (chamber.Comm.W8read.WaitOne(TimeSpan.FromMilliseconds(WaitDelay)))
                        _data = chamber.Comm.RxMsg;
                    else
                        _data = string.Empty;
                }
                else
                {
                    ///Trigger by ErrorReceivedHandler() or SerialPortmsgReceiver()
                    chamber.Comm.W8read.WaitOne(WaitDelay);
                    _data = chamber.Comm.ReadLine;
                }
                if (_data == null)
                {
                    chamber.Status = ChamberStatus.Error_Chamber;
                    return true;
                }
                else if (_data.Length == 3)
                {
                    //invalid response
                    chamber.Status = ChamberStatus.Invalid_Response;
                    return true;
                }
                else if (_data != "0 " + ChamberEndMSG)
                {
                    //No error present
                    chamber.Error = null;
                    return false;
                }

                chamber.Error = _data.Substring(0, _data.Length - 1);
                ClimError.Add("WEISS.CheckError", "Chamber Error: " + chamber.Error, "Chamber Error: " + chamber.Error);
                LastStatus = chamber.Status;
                chamber.Status = ChamberStatus.Error_Chamber;
                return true;
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.CheckError", "Checked error failed", ex.ToString());
                return true;
            }
        }

        /// <summary>
        /// Clean all errors in chamber
        /// </summary>
        /// <returns>TRUE - All error cleaned, FALSE - invalid response or can't clean some error</returns>
        public  Boolean ClearError()
        {
            try
            {
                Send(ConvertToString(true));
                string _dataLine;
                if (chamber.Comm.UsingNetwork)
                {
                    _dataLine = Read();
                    if (chamber.Comm.W8read.WaitOne(TimeSpan.FromMilliseconds(WaitDelay)))
                        _dataLine = chamber.Comm.RxMsg;
                    else
                        _dataLine = string.Empty;
                }
                else
                {
                    ///Trigger by ErrorReceivedHandler() or SerialPortmsgReceiver()
                    chamber.Comm.W8read.WaitOne(WaitDelay);
                    _dataLine = chamber.Comm.ReadLine;
                }
                if (_dataLine == null)
                {
                    chamber.Status = ChamberStatus.Error_Chamber;
                    return false;
                }
                else if (_dataLine.Length == 3)
                {
                    //invalid response
                    chamber.Status = ChamberStatus.Invalid_Response;
                    return false;
                }
                else if (_dataLine != "00" + ChamberEndMSG)
                {
                    //Values could not be set
                    chamber.Status = ChamberStatus.Values_could_not_be_set;
                    return false;
                }

                chamber.Error = null;
                chamber.Status = LastStatus;
                return true;
            }
            catch (Exception ex)
            {
                ClimError.Add("WEISS.ClearError", "Can't clear errors", ex.ToString());
                return false;
            }
        }
        /// <summary>
        /// Set new temperature to chamber without starting program.
        /// </summary>
        /// <param name="iTemp">Temperature in Celsius</param>
        /// <returns>Chamber accepts new temperature = TRUE</returns>
        public bool SetTemperature(float iTemp)
        {
            if (iTemp < MIN_TEMP)
                iTemp = MIN_TEMP;
            else if (iTemp > MAX_TEMP)
                iTemp = MAX_TEMP;

            return this.SetNominalValues((double)iTemp, Chamber_Nominal_Humidity, Chamber_Nominal_FanSpeed);
        }
        /// <summary>
        /// Set new humidity to chamber without starting program.
        /// </summary>
        /// <param name="iHumi">Humidity in %</param>
        /// <returns>Chamber accepts new humidity = TRUE</returns>
        public bool SetHumidity(float iHumi)
        {
            if (iHumi < MIN_HUMI)
                iHumi = MIN_HUMI;
            else if (iHumi > MAX_HUMI)
                iHumi = MAX_HUMI;

            return this.SetNominalValues(Chamber_Nominal_Temperature, (double)iHumi, Chamber_Nominal_FanSpeed);
        }
        /// <summary>
        /// Set new humidity and temperature to chamber without starting program.
        /// </summary>
        /// <param name="iTemp">Temperature in Celsius</param>
        /// <param name="iHumi">Humidity in %</param>
        /// <returns>Chamber accepts new nominal values = TRUE</returns>
        public bool SetTempHumi(float iTemp, float iHumi)
        {
            if (iTemp < MIN_TEMP)
                iTemp = MIN_TEMP;
            else if (iTemp > MAX_TEMP)
                iTemp = MAX_TEMP;

            if (iHumi < MIN_HUMI)
                iHumi = MIN_HUMI;
            else if (iHumi > MAX_HUMI)
                iHumi = MAX_HUMI;

            return SetNominalValues((double)iTemp, (double)iHumi, Chamber_Nominal_FanSpeed);
        }
        /// <summary>
        /// Return last actual temperature
        /// </summary>
        /// <returns>Last actual temperature</returns>
        public float GetActTemp()
        {
            lock (Locker4Var)
            {
                return (float)Chamber_Actual_Temperature;
            }
        }
        /// <summary>
        /// Return last actual humidity
        /// </summary>
        /// <returns>Last actual humidity</returns>
        public float GetActHumi()
        {
            lock (Locker4Var)
            {
                return (float)Chamber_Actual_Humidity;
            }
        }
        /// <summary>
        /// Return last nominal temperature
        /// </summary>
        /// <returns>Last nominal temperature</returns>
        public float GetNominalTemp()
        {
            lock (Locker4Var)
            {
                return (float)Chamber_Nominal_Temperature;
            }
        }
        /// <summary>
        /// Return last nominal humidity
        /// </summary>
        /// <returns>Last nominal humidity</returns>
        public float GetNominalHumi()
        {
            lock (Locker4Var)
            {
                return (float)Chamber_Nominal_Humidity;
            }
        }
        /// <summary>
        /// This function is not supported
        /// </summary>
        /// <returns></returns>
        public bool CheckAndClearErr()
        {
            bool _res = CheckError();
            if (!_res)
                ClearError();
            return _res;
        }
        /// <summary>
        /// Sets chamber to run mode.
        /// </summary>
        /// <returns>Chamber accepts changes = TRUE</returns>
        public bool Run()
        {
            bool retVal = SetChangeRates(RATE_HEAT,
                        RATE_COOL,
                        RATE_HUMI,
                        RATE_DEHUMI);

            if(retVal)
                chamber.Status = ChamberStatus.Ready_ON;
            return retVal;            
        }
        /// <summary>
        /// Gets time and date of last update
        /// </summary>
        /// <returns>Time and date of last update</returns>
        public DateTime LastUpdate()
        {
            return Chamber_LastStatusTime;
        }
        /// <summary>
        /// Start internal thread. The thread updates actual values untill app calls UpdaterStop()
        /// </summary>
        /// <param name="iSleepInt">Updater interval</param>
        /// <returns>Updater is alive = TRUE</returns>
        public bool UpdaterStart(TimeSpan iSleepInt)
        {
            if (UpdateTh == null || !UpdateTh.IsAlive)
            {
                UpdaterDelay = iSleepInt;
                UpdateTh = new Thread(UpdaterThread) { Name = "Clima Chamber (Weiss) Updater", IsBackground = true };
                UpdateTh.Start();
            }
            return UpdateTh.IsAlive;
        }
        /// <summary>
        /// Stop internal thread.
        /// </summary>
        public void UpdaterStop()
        {
            if(UpdateTh != null)
            {
                if (UpdateTh.IsAlive)
                    UpdateTh.Abort();
                UpdateTh = null;
            }
        }
        /// <summary>
        /// Update actual values.
        /// </summary>
        public void ManualUpdate()
        {
            UpdateStatus();
        }
        /// <summary>
        /// Gets devices name
        /// </summary>
        public string ChamberName { get { return ("Weiss"); } }
        /// <summary>
        /// Sets all possible  parameters
        /// </summary>
        /// <param name="iHeatGradient">Heating gradient</param>
        /// <param name="iCoolGradient">Cooling gradient</param>
        /// <param name="iHumidityGradient">Humidity gradient</param>
        /// <param name="iDeHumidityGradient">Dehumidity gradient</param>
        /// <param name="iTemp">Nominal temperature</param>
        /// <param name="iHumi">Nominal humidity</param>
        /// <returns>TRUE - success</returns>
        public bool SetAll(float iHeatGradient, float iCoolGradient, float iHumidityGradient, float iDeHumidityGradient, float iTemp, float iHumi)
        {
            bool _res1 = this.SetChangeRates(iHeatGradient, iCoolGradient, iHumidityGradient, iDeHumidityGradient);
            bool _res2 = this.SetNominalValues((double)iTemp, (double)iHumi, this.Chamber_Nominal_FanSpeed);
            if (_res1 && _res2)
                return true;
            else
                return false;
        }
        #endregion
    }
}
