using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

using System.Windows.Forms;
using System.Xml;
using System.Windows.Threading;

using System.IO;
using TwinCAT.Ads;

using SpectAnalysis;


namespace TwinCatIdent
{
    /// <summary>
    /// Delegate for event for 2-state events
    /// </summary>
    /// <param name="source"></param>
    /// <param name="e"></param>
    public delegate void StateEventHandler(object source, StateEvent e);

    /// <summary>
    /// Delegate for event without arguements
    /// </summary>
    /// <param name="source"></param>
    public delegate void ActionEventHandler(object source);

    /// <summary>
    /// Event class for state of external generator
    /// </summary>
    public class StateEvent : EventArgs
    {
        private eStateExtGen EventInfo;
        /// <summary>
        /// State event
        /// </summary>
        /// <param name="Value"></param>
        public StateEvent(eStateExtGen Value)
        {
            EventInfo = Value;
        }
        /// <summary>
        /// Get info
        /// </summary>
        /// <returns></returns>
        public eStateExtGen GetInfo()
        {
            return EventInfo;
        }
    }

    /// <summary>
    /// State of external generator
    /// </summary>
    public enum eStateExtGen
    {
        /// <summary>
        /// Offline state of external generator
        /// </summary>
        Offline,
        /// <summary>
        /// External generator is connected and ready
        /// </summary>
        Online,
        /// <summary>
        /// External generator is ready
        /// </summary>
        Running,
        /// <summary>
        /// Error of external generator
        /// </summary>
        Error
    };

    /// <summary>
    /// Mode of drive control
    /// </summary>
    public enum eControlMode
    {
        /// <summary>
        /// Position control
        /// </summary>
        PositionControl,
        /// <summary>
        /// Velocity control
        /// </summary>
        VelocityControl,
        /// <summary>
        /// Torque control
        /// </summary>
        TorqueControl,
        /// <summary>
        /// Unknow control
        /// </summary>
        UnknownControl
    };

    /// <summary>
    /// Units
    /// </summary>
    public enum eUnits
    {
        /// <summary>
        /// Degrees
        /// </summary>
        degree,
        /// <summary>
        /// Milimeters
        /// </summary>
        mm,
        /// <summary>
        /// None
        /// </summary>
        none
    };

    /// <summary>
    /// Error ID of external generator in PLC
    /// </summary>
    public enum eErrorIdExtGenerator
    {
        /// <summary>
        /// No error
        /// </summary>
        eNoError = 0,
        /// <summary>
        /// overflow main buffer with identification/step response data - too low sending speed through ADS
        /// </summary>
        eMainBufferOverflow = 1,
        /// <summary>
        /// overflow current buffer with current's values - too low sending speed through ADS
        /// </summary>
        eCurrentBufferOverflow = 2,
        /// <summary>
        /// nDriveType is out of range
        /// </summary>
        eUnexpectedDriveType = 3,
        /// <summary>
        /// cannot determine task time
        /// </summary>
        eErrorDetermineTaskTime = 4,
        /// <summary>
        /// out of position range
        /// </summary>
        eOutOfPositionRange = 5,
        /// <summary>
        /// out of torque range during identification
        /// </summary>        
        eOutOfTorqueRangeIdent = 6,
        /// <summary>
        /// out of torque range during step response
        /// </summary>
        eOutOfTorqueRangeStRes = 7,
        /// <summary>
        /// timeout between App and PLC
        /// </summary> 
        eTimeout = 8
    };

    /// <summary>
    /// Class for storage target, generator and analysis properties
    /// </summary>
    public class DataProperties
    {
        /// <summary>
        /// AMS Net ID of target
        /// </summary>
        private string i_AmsNetId;

        /// <summary>
        /// ADS port of target
        /// </summary>
        private int i_AdsPort;

        /// <summary>
        /// Path to external generator in PLC
        /// </summary>
        private string i_PathExtGen;

        /// <summary>
        /// Units 
        /// </summary>
        private eUnits i_Units;

        /// <summary>
        /// Name of input signal
        /// </summary>
        private string i_NameInputSignal;

        /// <summary>
        /// ID of input signal
        /// </summary>
        private int i_IdInputSignal;

        /// <summary>
        /// Period of input signal
        /// </summary>
        private Double i_PeriodInputSignal;

        /// <summary>
        /// Lenght of input signal = number of samples of one period input signal
        /// </summary>
        private uint i_LengthInputSignal;

        /// <summary>
        /// Count of repeat one period of input signal
        /// </summary>
        private UInt16 i_CountRepeat;

        /// <summary>
        /// Control mode
        /// </summary>
        private eControlMode i_ControlMode;

        /// <summary>
        /// Amplitude of Input signal
        /// </summary>
        double i_AmplitudeInputSignal;

        /// <summary>
        /// Amplitude of auxiliary movement [in the appropriate units according to mode control] (suppression of friction)
        /// </summary>
        double i_AmplAuxMove;

        /// <summary>
        /// Frequency of auxiliary movement in Hz (suppression of friction)
        /// </summary>
        double i_FreqAuxMove;

        /// <summary>
        /// Date and time created new class
        /// </summary>
        DateTime i_DateTimeMd;

        /// <summary>
        /// Is linear motor
        /// </summary>
        private bool i_IsLinear;

        /// <summary>
        /// Scale for position 
        /// </summary>
        private double i_xScale;

        /// <summary>
        /// Scale for velocity 
        /// </summary>
        private double i_vScale;

        /// <summary>
        /// Feed constant
        /// </summary>
        private double i_FeedConstant;

        /// <summary>
        /// Cycle time of PLC task
        /// </summary>
        private double i_TaskTime;

        /// <summary>
        /// Name of axis
        /// </summary>
        private string i_NameAxis;

        /// <summary>
        /// Pre-trigger time in step response [s]
        /// </summary>
        private double i_StepPreTrigger;

        /// <summary>
        /// Duration of step response [s]
        /// </summary>
        private double i_StepTime;

        /// <summary>
        /// Initial level of step response [X]
        /// </summary>
        private double i_StepInitLevel;

        /// <summary>
        /// Final level of step response [X]
        /// </summary>
        private double i_StepFinalLevel;

        /// <summary>
        /// Notes
        /// </summary>
        private string i_Notes;

        // Parameters for main app //

        /// <summary>
        /// Length of window for computing BodePlot
        /// </summary>
        private int i_LengthWindow;

        /// <summary>
        /// Mode of spectral analysis for computing BodePlot
        /// </summary>
        private eModeAnalysis i_ModeSpectAnalys;

        /// <summary>
        /// Check phase overflow
        /// </summary>
        private bool i_CheckPhaseOverflow;

        /// <summary>
        /// Offset of input/output data. If offset is positive input data are shifted forward. If offset is negative input data are shifted backward.
        /// </summary>
        private int i_OffsetIOdata;

        /// <summary>
        /// Allowed position range
        /// </summary>
        private double i_AllowedPositionRange;

        /// <summary>
        /// Enable position range check
        /// </summary>
        private bool i_EnablePositionRangeCheck;

        /// <summary>
        /// Allowed torque range during identification [°%]
        /// </summary>
        private Int16 i_AllowedTorqueRangeIdent;

        /// <summary>
        /// Enable torque range check during identification
        /// </summary>
        private bool i_EnableTorqueRangeCheckIdent;

        /// <summary>
        /// Allowed torque range during step response [°%]
        /// </summary>
        private Int16 i_AllowedTorqueRangeStRes;

        /// <summary>
        /// Enable torque range check during step response
        /// </summary>
        private bool i_EnableTorqueRangeCheckStRes;

        /// <summary>
        /// Constructor
        /// </summary>
        public DataProperties()
        {
            i_AmsNetId = "";
            i_AdsPort = 0;
            i_PathExtGen = "";
            i_Units = eUnits.none;
            i_NameInputSignal = "";
            i_IdInputSignal = 0;
            i_PeriodInputSignal = 0;
            i_LengthInputSignal = 0;
            i_ControlMode = eControlMode.UnknownControl;
            i_AmplitudeInputSignal = 0;
            i_AmplAuxMove = 0;
            i_FreqAuxMove = 0;
            i_IsLinear = false;
            i_xScale = 0;
            i_vScale = 0;
            i_FeedConstant = 1;
            i_TaskTime = 0;
            i_NameAxis = "";
            i_Notes = "";
            i_DateTimeMd = new DateTime();
            i_StepPreTrigger = 0;
            i_StepTime = 0;
            i_StepInitLevel = 0;
            i_StepFinalLevel = 0;
            i_LengthWindow = 512;
            i_ModeSpectAnalys = eModeAnalysis.Bartlett;
            i_CheckPhaseOverflow = false;
            i_OffsetIOdata = 0;
            i_CountRepeat = 1;
            i_AllowedPositionRange = 1000;
            i_EnablePositionRangeCheck = true;
            i_AllowedTorqueRangeIdent = 500;
            i_EnableTorqueRangeCheckIdent = true;
            i_AllowedTorqueRangeStRes = 800;
            i_EnableTorqueRangeCheckStRes = true;
        }

        /// <summary>
        /// Constructor from DataProperties
        /// </summary>
        /// <param name="PropData"> Source class </param>
        public DataProperties(DataProperties PropData)
        {
            i_AmsNetId = PropData.i_AmsNetId;
            i_AdsPort = PropData.i_AdsPort;
            i_PathExtGen = PropData.i_PathExtGen;
            i_Units = PropData.i_Units;
            i_NameInputSignal = PropData.i_NameInputSignal;
            i_IdInputSignal = PropData.i_IdInputSignal;
            i_PeriodInputSignal = PropData.i_PeriodInputSignal;
            i_LengthInputSignal = PropData.i_LengthInputSignal;
            i_ControlMode = PropData.i_ControlMode;
            i_AmplitudeInputSignal = PropData.i_AmplitudeInputSignal;
            i_AmplAuxMove = PropData.i_AmplAuxMove;
            i_FreqAuxMove = PropData.i_FreqAuxMove;
            i_IsLinear = PropData.i_IsLinear;
            i_xScale = PropData.i_xScale;
            i_vScale = PropData.i_vScale;
            i_FeedConstant = PropData.i_FeedConstant;
            i_TaskTime = PropData.i_TaskTime;
            i_NameAxis = PropData.i_NameAxis;
            i_Notes = PropData.i_Notes;
            i_DateTimeMd = PropData.i_DateTimeMd;
            i_StepPreTrigger = PropData.i_StepPreTrigger;
            i_StepTime = PropData.i_StepTime;
            i_StepInitLevel = PropData.i_StepInitLevel;
            i_StepFinalLevel = PropData.i_StepFinalLevel;
            i_LengthWindow = PropData.i_LengthWindow;
            i_ModeSpectAnalys = PropData.i_ModeSpectAnalys;
            i_CheckPhaseOverflow = PropData.i_CheckPhaseOverflow;
            i_OffsetIOdata = PropData.i_OffsetIOdata;
            i_CountRepeat = PropData.i_CountRepeat;
            i_AllowedPositionRange = PropData.i_AllowedPositionRange;
            i_EnablePositionRangeCheck = PropData.i_EnablePositionRangeCheck;
            i_AllowedTorqueRangeIdent = PropData.i_AllowedTorqueRangeIdent;
            i_EnableTorqueRangeCheckIdent = PropData.i_EnableTorqueRangeCheckIdent;
            i_AllowedTorqueRangeStRes = PropData.i_AllowedTorqueRangeStRes;
            i_EnableTorqueRangeCheckStRes = PropData.i_EnableTorqueRangeCheckStRes;
        }

        /// <summary>
        /// Get/Set AMS NET ID of target
        /// </summary>
        public string AmsNetId
        {
            set { i_AmsNetId = value; }
            get { return i_AmsNetId; }
        }


        /// <summary>
        /// Get/Set ADS Port of target
        /// </summary>
        public int AdsPort
        {
            set
            {
                if (value < 0)
                    throw new ApplicationException(" - have to be positive value");
                i_AdsPort = value;
            }
            get { return i_AdsPort; }
        }

        /// <summary>
        /// Get/Set ADS Port of target (conversion to/from string)
        /// </summary>
        public string sAdsPort
        {
            set
            {
                try
                {
                    AdsPort = Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong ADS port" + ErrMes));
                }

            }
            get { return i_AdsPort.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get path into external generator in PLC
        /// </summary>
        public string PathExtGen
        {
            set { i_PathExtGen = value; }
            get { return i_PathExtGen; }
        }

        /// <summary>
        /// Get/Set path into external generator in PLC
        /// </summary>
        public eUnits Units
        {
            set { i_Units = value; }
            get { return i_Units; }
        }

        /// <summary>
        /// Get/Set path into external generator in PLC (conversion to/from int)
        /// </summary>
        public int intUnits
        {
            set
            {
                try
                {
                    Units = (eUnits)value;
                }
                catch
                {
                    throw (new Exception("Wrong units"));
                }
            }
            get { return Convert.ToInt32(Units, System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get name of input signal for identification
        /// </summary>
        public string NameInputSignal
        {
            set { i_NameInputSignal = value; }
            get { return i_NameInputSignal; }
        }

        /// <summary>
        /// Get/Set ID of input signal for identification
        /// </summary>
        public int IdInputSignal
        {
            set { i_IdInputSignal = value; }
            get { return i_IdInputSignal; }
        }

        /// <summary>
        /// Get/Set period of input signal in seconds[s]
        /// </summary>
        public Double PeriodInputSignal
        {
            set { i_PeriodInputSignal = value; }
            get { return i_PeriodInputSignal; }
        }

        /// <summary>
        /// Get/Set period of input signal in seconds[ms] (conversion to/from string)
        /// </summary>
        public string sPeriodInputSignal
        {
            set
            {
                try
                {
                    PeriodInputSignal = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture) / 1000;
                    if (i_PeriodInputSignal < i_TaskTime)
                    {
                        throw (new Exception("Period of input signal is shorter than task time of PLC."));
                    }
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong period of input signal for identification.\n" + ErrMes));
                }

            }
            get { return (PeriodInputSignal * 1000).ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get length of input signal (number samples of input signal)
        /// </summary>
        public uint LengthInputSignal
        {
            set { i_LengthInputSignal = value; }
            get { return i_LengthInputSignal; }
        }

        /// <summary>
        /// Get/Set count repeat of sequence for indentification
        /// </summary>
        public UInt16 CountRepeat
        {
            set { i_CountRepeat = value; }
            get { return i_CountRepeat; }
        }

        /// <summary>
        /// Get/Set count repeat of sequence for indentification (conversion to/from string)
        /// </summary>
        public string sCountRepeat
        {
            set
            {
                try
                {
                    CountRepeat = Convert.ToUInt16(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong count repeat of sequence for indentification" + ErrMes));
                }
            }
            get { return CountRepeat.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set amplitude of input signal [X]
        /// </summary>
        public double AmplitudeInputSignal
        {
            set { i_AmplitudeInputSignal = value; }
            get { return i_AmplitudeInputSignal; }
        }

        /// <summary>
        /// Get/Set amplitude of input signal[X] (conversion to/from string)
        /// </summary>
        public string sAmplitudeInputSignal
        {
            set
            {
                try
                {
                    AmplitudeInputSignal = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong amplitude of input signal for identification" + ErrMes));
                }

            }
            get { return AmplitudeInputSignal.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Amplitude of auxiliary movement [X] (suppression of friction) 
        /// </summary>
        public double AmplAuxMove
        {
            set { i_AmplAuxMove = value; }
            get { return i_AmplAuxMove; }
        }

        /// <summary>
        /// Amplitude of auxiliary movement [X] (conversion to/from string)
        /// </summary>
        public string sAmplAuxMove
        {
            set
            {
                try
                {
                    AmplAuxMove = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong amplitude of auxiliary movement for identification" + ErrMes));
                }

            }
            get { return AmplAuxMove.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Frequency of auxiliary movement in Hz (suppression of friction)
        /// </summary>
        public double FreqAuxMove
        {
            set { i_FreqAuxMove = value; }
            get { return i_FreqAuxMove; }
        }

        /// <summary>
        /// Amplitude of auxiliary movement [X] (conversion to/from string)
        /// </summary>
        public string sFreqAuxMove
        {
            set
            {
                try
                {
                    FreqAuxMove = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong frequency of auxiliary movement for identification" + ErrMes));
                }

            }
            get { return FreqAuxMove.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set Control mode:
        ///     - NoneControl
        ///     - PositionControl
        ///     - TorqueControl
        ///     - VelocityControl   
        /// </summary>
        public eControlMode ControlMode
        {
            set { i_ControlMode = value; }
            get { return i_ControlMode; }
        }

        /// <summary>
        /// Get/Set Control mode: (conversion to/from int)
        ///     - NoneControl
        ///     - PositionControl
        ///     - TorqueControl
        ///     - VelocityControl   
        /// </summary>
        public int intControlMode
        {
            set
            {
                try
                {
                    ControlMode = (eControlMode)value;
                }
                catch
                {
                    throw (new Exception("Wrong control mode"));
                }
            }
            get { return Convert.ToInt32(ControlMode, System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set date ant time of measuring
        /// </summary>
        public DateTime DateTimeMd
        {
            set { i_DateTimeMd = value; }
            get { return i_DateTimeMd; }
        }

        /// <summary>
        /// Get/Set date ant time of measuring (conversion to/from string)
        /// </summary>
        public string sDateTime
        {
            set
            {
                try
                {
                    DateTimeMd = Convert.ToDateTime(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch
                {
                    DateTimeMd = Convert.ToDateTime(0, System.Globalization.CultureInfo.InvariantCulture);
                }

            }
            get { return DateTimeMd.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set liner motor type
        /// </summary>
        public bool IsLinear
        {
            set { i_IsLinear = value; }
            get { return i_IsLinear; }
        }

        /// <summary>
        /// Get/Set scale for position mode
        /// </summary>
        public double xScale
        {
            set { i_xScale = value; }
            get { return i_xScale; }
        }

        /// <summary>
        /// Get/Set scale for position mode (conversion to/from string)
        /// </summary>
        public string sxScale
        {
            set
            {
                try
                {
                    xScale = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong scale for position mode" + ErrMes));
                }

            }
            get { return xScale.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set scale for velocity mode
        /// </summary>
        public double vScale
        {
            set { i_vScale = value; }
            get { return i_vScale; }
        }

        /// <summary>
        /// Get/Set scale for velocity mode (conversion to/from string)
        /// </summary>
        public string svScale
        {
            set
            {
                try
                {
                    vScale = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong scale for velocity mode" + ErrMes));
                }

            }
            get { return vScale.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set feed constant
        /// </summary>
        public double FeedConstant
        {
            set { i_FeedConstant = value; }
            get { return i_FeedConstant; }
        }

        /// <summary>
        /// Get/Set feed constant (conversion to/from string)
        /// </summary>
        public string sFeedConstant
        {
            set
            {
                try
                {
                    FeedConstant = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong feed constant" + ErrMes));
                }

            }
            get { return FeedConstant.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set cyclus time of PLC task [s]
        /// </summary>
        public double TaskTime
        {
            set { i_TaskTime = value; }
            get { return i_TaskTime; }
        }

        /// <summary>
        /// Get/Set cyclus time of PLC task [ms] (conversion to/from string)
        /// </summary>
        public string sTaskTime
        {
            set
            {
                try
                {
                    TaskTime = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture) / 1000;
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong cyclus time of PLC task" + ErrMes));
                }

            }
            get { return (TaskTime * 1000).ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set name of axis
        /// </summary>
        public string NameAxis
        {
            set { i_NameAxis = value; }
            get { return i_NameAxis; }
        }

        /// <summary>
        /// Get/Set pre-trigger time of step response [s]
        /// </summary>
        public double StepPreTrigger
        {
            set { i_StepPreTrigger = value; }
            get { return i_StepPreTrigger; }
        }

        /// <summary>
        /// Get/Set pre-trigger time of step response [s] (conversion to/from string)
        /// </summary>
        public string sStepPreTrigger
        {
            set
            {
                try
                {
                    StepPreTrigger = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture) / 1000;
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong pre-trigger time of step response" + ErrMes));
                }

            }
            get { return (StepPreTrigger * 1000).ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set duration of step response [s]
        /// </summary>
        public double StepTime
        {
            set { i_StepTime = value; }
            get { return i_StepTime; }
        }

        /// <summary>
        /// Get/Set duration of step response [s] (conversion to/from string)
        /// </summary>
        public string sStepTime
        {
            set
            {
                try
                {
                    StepTime = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture) / 1000;
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong duration of step response" + ErrMes));
                }

            }
            get { return (StepTime * 1000).ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set initial level of step response [X]
        /// </summary>
        public double StepInitLevel
        {
            set { i_StepInitLevel = value; }
            get { return i_StepInitLevel; }
        }

        /// <summary>
        /// Get/Set initial level of step response [X] (conversion to/from string)
        /// </summary>
        public string sStepInitLevel
        {
            set
            {
                try
                {
                    StepInitLevel = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong initial level of step response" + ErrMes));
                }

            }
            get { return StepInitLevel.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set final level of step response [X]
        /// </summary>
        public double StepFinalLevel
        {
            set { i_StepFinalLevel = value; }
            get { return i_StepFinalLevel; }
        }

        /// <summary>
        /// Get/Set final level of step response [X] (conversion to/from string)
        /// </summary>
        public string sStepFinalLevel
        {
            set
            {
                try
                {
                    StepFinalLevel = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong final level of step response" + ErrMes));
                }

            }
            get { return StepFinalLevel.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set Notes
        /// </summary>
        public string Notes
        {
            set { i_Notes = value; }
            get { return i_Notes; }
        }

        /// <summary>
        /// Get/Set Length of window for computing BodePlot
        /// </summary>
        public int LengthWindow
        {
            set { i_LengthWindow = value; }
            get { return i_LengthWindow; }
        }

        /// <summary>
        /// Get/Set length of window for computing BodePlot (conversion to/from string)
        /// </summary>
        public string sLengthWindow
        {
            set
            {
                try
                {
                    LengthWindow = Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong length of window for computing BodePlot" + ErrMes));
                }

            }
            get { return LengthWindow.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set Mode of spectral analysis for computing BodePlot
        /// </summary>
        public eModeAnalysis ModeSpectAnalys
        {
            set { i_ModeSpectAnalys = value; }
            get { return i_ModeSpectAnalys; }
        }

        /// <summary>
        /// Get/Set Mode of spectral analysis for computing BodePlot (conversion to/from int)   
        /// </summary>
        public int intModeSpectAnalys
        {
            set
            {
                try
                {
                    ModeSpectAnalys = (eModeAnalysis)value;
                }
                catch
                {
                    throw (new Exception("Wrong mode of spectral analysis for computing BodePlot"));
                }
            }
            get { return Convert.ToInt32(ModeSpectAnalys, System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Get/Set check phase overflow over +/- 180 degrees
        /// </summary>
        public bool CheckPhaseOverflow
        {
            set { i_CheckPhaseOverflow = value; }
            get { return i_CheckPhaseOverflow; }
        }

        /// <summary>
        /// Offset of input/output data. If offset is positive input data are shifted forward. If offset is negative input data are shifted backward.
        /// </summary>
        public int OffsetIOdata
        {
            set { i_OffsetIOdata = value; }
            get { return i_OffsetIOdata; }
        }

        /// <summary>
        /// Offset of input/output data. If offset is positive input data are shifted forward. If offset is negative input data are shifted backward. (conversion to/from string)
        /// </summary>
        public string sOffsetIOdata
        {
            set
            {
                try
                {
                    OffsetIOdata = Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong offset of I/O data for computing BodePlot" + ErrMes));
                }

            }
            get { return OffsetIOdata.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Allowed position range
        /// </summary>
        public double AllowedPositionRange
        {
            set { i_AllowedPositionRange = value; }
            get { return i_AllowedPositionRange; }
        }

        /// <summary>
        /// Allowed position range [X] (conversion to/from string)
        /// </summary>
        public string sAllowedPositionRange
        {
            set
            {
                try
                {
                    AllowedPositionRange = Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong value of allowed position range" + ErrMes));
                }

            }
            get { return AllowedPositionRange.ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Enable position range check
        /// </summary>
        public bool EnablePositionRangeCheck
        {
            set { i_EnablePositionRangeCheck = value; }
            get { return i_EnablePositionRangeCheck; }
        }

        /// <summary>
        /// Allowed torque range during identification [°%]
        /// </summary>
        public Int16 AllowedTorqueRangeIdent
        {
            set { i_AllowedTorqueRangeIdent = value; }
            get { return i_AllowedTorqueRangeIdent; }
        }

        /// <summary>
        /// Allowed torque range during identification [%] (conversion to/from string)
        /// </summary>
        public string sAllowedTorqueRangeIdent
        {
            set
            {
                try
                {
                    AllowedTorqueRangeIdent = Convert.ToInt16(Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture) * 10);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong value of allowed torque range during identification" + ErrMes));
                }

            }
            get { return (AllowedTorqueRangeIdent / 10).ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Enable torque range check during identification 
        /// </summary>
        public bool EnableTorqueRangeCheckIdent
        {
            set { i_EnableTorqueRangeCheckIdent = value; }
            get { return i_EnableTorqueRangeCheckIdent; }
        }

        /// <summary>
        /// Allowed torque range during step response [°%]
        /// </summary>
        public Int16 AllowedTorqueRangeStRes
        {
            set { i_AllowedTorqueRangeStRes = value; }
            get { return i_AllowedTorqueRangeStRes; }
        }

        /// <summary>
        /// Allowed torque range during identification [%] (conversion to/from string)
        /// </summary>
        public string sAllowedTorqueRangeStRes
        {
            set
            {
                try
                {
                    AllowedTorqueRangeStRes = Convert.ToInt16(Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture) * 10);
                }
                catch (Exception e)
                {
                    string ErrMes = "";
                    if (e.Source == "SysIdentifi")
                        ErrMes = e.Message;
                    throw (new Exception("Wrong value of allowed torque range during identification" + ErrMes));
                }

            }
            get { return (AllowedTorqueRangeStRes / 10).ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Enable torque range check during step response
        /// </summary>
        public bool EnableTorqueRangeCheckStRes
        {
            set { i_EnableTorqueRangeCheckStRes = value; }
            get { return i_EnableTorqueRangeCheckStRes; }
        }

        /// <summary>
        /// Get relevant scale according to ControlMode
        /// </summary>
        /// <returns></returns>
        public double GetScale()
        {
            //Scale
            double Scale = 1;
            switch (ControlMode)
            {
                case eControlMode.PositionControl:
                    Scale = xScale * FeedConstant;
                    break;
                case eControlMode.VelocityControl:
                    if (IsLinear)
                    {
                        Scale = (vScale / 1000) * FeedConstant;
                    }
                    else
                    {
                        Scale = vScale * FeedConstant;
                    }

                    break;
                case eControlMode.TorqueControl:
                    Scale = 0.1;
                    break;
                case eControlMode.UnknownControl:
                    Scale = 1;
                    break;
            }
            return Scale;
        }

        /// <summary>
        /// Save settings
        /// </summary>
        /// <param name="Path"> Path for new settings file </param>
        public void SaveSettingsData(string Path)
        {
            System.Globalization.CultureInfo invC = System.Globalization.CultureInfo.InvariantCulture;
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.NewLineOnAttributes = true;
            XmlWriter xmlWriter = XmlWriter.Create(Path, settings);
            xmlWriter.WriteStartDocument();
            xmlWriter.WriteStartElement("IdentifiData");
            xmlWriter.WriteStartElement("Properties");
            xmlWriter.WriteAttributeString("Version", "1");
            xmlWriter.WriteAttributeString("AmsNetId", AmsNetId);
            xmlWriter.WriteAttributeString("AdsPort", AdsPort.ToString(invC));
            xmlWriter.WriteAttributeString("PathExtGen", PathExtGen);
            xmlWriter.WriteAttributeString("NameAxis", NameAxis);
            xmlWriter.WriteAttributeString("NameInputSignal", NameInputSignal);
            xmlWriter.WriteAttributeString("IdInputSignal", Convert.ToString(IdInputSignal, invC));
            xmlWriter.WriteAttributeString("PeriodInputSignal", PeriodInputSignal.ToString(invC));
            xmlWriter.WriteAttributeString("CountRepeat", CountRepeat.ToString());
            xmlWriter.WriteAttributeString("ControlMode", Convert.ToString((int)ControlMode, invC));
            xmlWriter.WriteAttributeString("Units", Convert.ToString((int)Units, invC));
            xmlWriter.WriteAttributeString("DateTime", DateTimeMd.ToString(invC));
            xmlWriter.WriteAttributeString("IsLinear", IsLinear.ToString(invC));
            xmlWriter.WriteAttributeString("xScale", xScale.ToString(invC));
            xmlWriter.WriteAttributeString("vScale", vScale.ToString(invC));
            xmlWriter.WriteAttributeString("FeedConstant", FeedConstant.ToString(invC));
            xmlWriter.WriteAttributeString("TaskTime", TaskTime.ToString(invC));
            xmlWriter.WriteAttributeString("AmplitudeInputSignal", AmplitudeInputSignal.ToString(invC));
            xmlWriter.WriteAttributeString("AmplitudeAuxiliaryMoveSignal", AmplAuxMove.ToString(invC));
            xmlWriter.WriteAttributeString("FrequencyAuxiliaryMoveSignal", FreqAuxMove.ToString(invC));
            xmlWriter.WriteAttributeString("StepPreTrigger", StepPreTrigger.ToString(invC));
            xmlWriter.WriteAttributeString("StepTime", StepTime.ToString(invC));
            xmlWriter.WriteAttributeString("StepInitLevel", StepInitLevel.ToString(invC));
            xmlWriter.WriteAttributeString("StepFinalLevel", StepFinalLevel.ToString(invC));
            xmlWriter.WriteAttributeString("LengthWindow", LengthWindow.ToString(invC));
            xmlWriter.WriteAttributeString("ModeSpectAnalys", Convert.ToString((int)ModeSpectAnalys, invC));
            xmlWriter.WriteAttributeString("CheckPhaseOverflow", CheckPhaseOverflow.ToString(invC));
            xmlWriter.WriteAttributeString("OffsetIOdata", OffsetIOdata.ToString(invC));
            xmlWriter.WriteAttributeString("AllowedPositionRange", AllowedPositionRange.ToString(invC));
            xmlWriter.WriteAttributeString("EnablePositionRangeCheck", EnablePositionRangeCheck.ToString(invC));
            xmlWriter.WriteAttributeString("AllowedTorqueRangeIdent", AllowedTorqueRangeIdent.ToString(invC));
            xmlWriter.WriteAttributeString("EnableTorqueRangeCheckIdent", EnableTorqueRangeCheckIdent.ToString(invC));
            xmlWriter.WriteAttributeString("AllowedTorqueRangeStRes", AllowedTorqueRangeStRes.ToString(invC));
            xmlWriter.WriteAttributeString("EnableTorqueRangeCheckStRes", EnableTorqueRangeCheckStRes.ToString(invC));
            xmlWriter.WriteEndElement();
            xmlWriter.WriteStartElement("Notes");
            xmlWriter.WriteAttributeString("Count", Convert.ToString(Notes.Length));
            xmlWriter.WriteAttributeString("ValueType", "string");
            xmlWriter.WriteStartElement("Data");
            byte[] BufferNotes = Encoding.Unicode.GetBytes(Notes);
            xmlWriter.WriteBase64(BufferNotes, 0, Notes.Length * 2);
            xmlWriter.WriteEndDocument();
            xmlWriter.Close();
        }

        /// <summary>
        /// Load settings data from file to class
        /// </summary>
        /// <param name="Path"></param>
        public static DataProperties LoadSettingsData(string Path)
        {
            DataProperties iLoadedData = new DataProperties();
            int iVersion;

            System.Globalization.CultureInfo invC = System.Globalization.CultureInfo.InvariantCulture;

            try
            {
                XmlReader xmlReader = XmlReader.Create(Path);

                // Read Properties
                xmlReader.ReadToFollowing("Properties");
                iVersion = Convert.ToInt32(xmlReader.GetAttribute("Version"), invC);
                iLoadedData.AmsNetId = xmlReader.GetAttribute("AmsNetId");
                iLoadedData.AdsPort = Convert.ToInt32(xmlReader.GetAttribute("AdsPort"), invC);
                iLoadedData.PathExtGen = xmlReader.GetAttribute("PathExtGen");
                iLoadedData.NameAxis = xmlReader.GetAttribute("NameAxis");
                iLoadedData.NameInputSignal = xmlReader.GetAttribute("NameInputSignal");
                iLoadedData.IdInputSignal = Convert.ToInt32(xmlReader.GetAttribute("IdInputSignal"), invC);
                iLoadedData.PeriodInputSignal = Convert.ToDouble(xmlReader.GetAttribute("PeriodInputSignal"), invC);
                iLoadedData.CountRepeat = Convert.ToUInt16(xmlReader.GetAttribute("CountRepeat"));
                iLoadedData.ControlMode = (eControlMode)Convert.ToInt32(xmlReader.GetAttribute("ControlMode"), invC);
                iLoadedData.Units = (eUnits)Convert.ToInt32(xmlReader.GetAttribute("Units"), invC);
                iLoadedData.DateTimeMd = Convert.ToDateTime(xmlReader.GetAttribute("DateTime"), invC);
                string tempString = xmlReader.GetAttribute("IsLinear");
                if (tempString == "True")
                {
                    iLoadedData.IsLinear = true;
                }
                else
                {
                    iLoadedData.IsLinear = false;
                }
                iLoadedData.xScale = Convert.ToDouble(xmlReader.GetAttribute("xScale"), invC);
                iLoadedData.vScale = Convert.ToDouble(xmlReader.GetAttribute("vScale"), invC);
                iLoadedData.FeedConstant = Convert.ToDouble(xmlReader.GetAttribute("FeedConstant"), invC);
                iLoadedData.TaskTime = Convert.ToDouble(xmlReader.GetAttribute("TaskTime"), invC);
                iLoadedData.AmplitudeInputSignal = Convert.ToDouble(xmlReader.GetAttribute("AmplitudeInputSignal"), invC);
                iLoadedData.AmplAuxMove = Convert.ToDouble(xmlReader.GetAttribute("AmplitudeAuxiliaryMoveSignal"), invC);
                iLoadedData.FreqAuxMove = Convert.ToDouble(xmlReader.GetAttribute("FrequencyAuxiliaryMoveSignal"), invC);
                iLoadedData.StepPreTrigger = Convert.ToDouble(xmlReader.GetAttribute("StepPreTrigger"), invC);
                iLoadedData.StepTime = Convert.ToDouble(xmlReader.GetAttribute("StepTime"), invC);
                iLoadedData.StepInitLevel = Convert.ToDouble(xmlReader.GetAttribute("StepInitLevel"), invC);
                iLoadedData.StepFinalLevel = Convert.ToDouble(xmlReader.GetAttribute("StepFinalLevel"), invC);
                iLoadedData.LengthWindow = Convert.ToInt32(xmlReader.GetAttribute("LengthWindow"), invC);
                iLoadedData.ModeSpectAnalys = (eModeAnalysis)Convert.ToInt32(xmlReader.GetAttribute("ModeSpectAnalys"), invC);
                tempString = xmlReader.GetAttribute("CheckPhaseOverflow");
                if (tempString == "True")
                {
                    iLoadedData.CheckPhaseOverflow = true;
                }
                else
                {
                    iLoadedData.CheckPhaseOverflow = false;
                }
                iLoadedData.OffsetIOdata = Convert.ToInt32(xmlReader.GetAttribute("OffsetIOdata"), invC);
                iLoadedData.AllowedPositionRange = Convert.ToDouble(xmlReader.GetAttribute("AllowedPositionRange"), invC);
                tempString = xmlReader.GetAttribute("EnablePositionRangeCheck");
                if (tempString == "True")
                {
                    iLoadedData.EnablePositionRangeCheck = true;
                }
                else
                {
                    iLoadedData.EnablePositionRangeCheck = false;
                }
                iLoadedData.AllowedTorqueRangeIdent = Convert.ToInt16(xmlReader.GetAttribute("AllowedTorqueRangeIdent"), invC);
                tempString = xmlReader.GetAttribute("EnableTorqueRangeCheckIdent");
                if (tempString == "True")
                {
                    iLoadedData.EnableTorqueRangeCheckIdent = true;
                }
                else
                {
                    iLoadedData.EnableTorqueRangeCheckIdent = false;
                }
                iLoadedData.AllowedTorqueRangeStRes = Convert.ToInt16(xmlReader.GetAttribute("AllowedTorqueRangeStRes"), invC);
                tempString = xmlReader.GetAttribute("EnableTorqueRangeCheckStRes");
                if (tempString == "True")
                {
                    iLoadedData.EnableTorqueRangeCheckStRes = true;
                }
                else
                {
                    iLoadedData.EnableTorqueRangeCheckStRes = false;
                }
                // Read Notes 
                xmlReader.ReadToFollowing("Notes");
                int CountSamples = Convert.ToInt32(xmlReader.GetAttribute("Count"));
                xmlReader.ReadToFollowing("Data");
                byte[] DataNotes = new byte[CountSamples * 2];
                xmlReader.ReadElementContentAsBase64(DataNotes, 0, CountSamples * 2);
                iLoadedData.Notes = Encoding.Unicode.GetString(DataNotes);

                return (iLoadedData);

            }
            catch (Exception e)
            {
                throw new Exception("Wrong file" + e.Message);
            }
        }

        /// <summary>
        /// Load motion profile data from file to class
        /// </summary>
        /// <param name="Path"></param>
        public static DataProperties LoadProfileData(string Path)
        {
            DataProperties iLoadedData = new DataProperties();
            int iVersion;

            System.Globalization.CultureInfo invC = System.Globalization.CultureInfo.InvariantCulture;

            try
            {
                XmlReader xmlReader = XmlReader.Create(Path);

                // Read Properties
                xmlReader.ReadToFollowing("Properties");
                iVersion = Convert.ToInt32(xmlReader.GetAttribute("Version"), invC);
                iLoadedData.xScale = Convert.ToDouble(xmlReader.GetAttribute("xScale"), invC);
                iLoadedData.vScale = Convert.ToDouble(xmlReader.GetAttribute("vScale"), invC);
                iLoadedData.OffsetIOdata = Convert.ToInt32(xmlReader.GetAttribute("OffsetIOdata"), invC);
                // Read Notes 
                xmlReader.ReadToFollowing("Notes");
                int CountSamples = Convert.ToInt32(xmlReader.GetAttribute("Count"));
                xmlReader.ReadToFollowing("Data");
                byte[] DataNotes = new byte[CountSamples * 2];
                xmlReader.ReadElementContentAsBase64(DataNotes, 0, CountSamples * 2);
                iLoadedData.Notes = Encoding.Unicode.GetString(DataNotes);

                return (iLoadedData);

            }
            catch (Exception e)
            {
                throw new Exception("Wrong file" + e.Message);
            }
        }

    }

    /// <summary>
    /// Class for data storage from PLC
    /// </summary>
    public class MeasureData : DataProperties
    {
        /// <summary>
        /// List of measured data
        /// </summary>
        private List<MData> iData;

        /// <summary>
        /// List of measured data in matrix
        /// </summary>
        private List<MData>[] iOrganizedData;

        /// <summary>
        /// List of Current data
        /// </summary>
        public List<Int16> iCurrentValue;

        /// <summary>
        /// List of Step response data
        /// </summary>
        public List<MData> iStepResValue;

        /// <summary>
        /// Unique ID of curve set. If i_ID = -1, this value is uninitialized.
        /// </summary>
        private int i_ID = -1;

        /// <summary>
        /// Length of data in matrix [m*n], m = number of repeat, n = number of samples in one period input signal catched in time of sampling PLC task.
        /// LengthInterestData = n
        /// </summary>
        private uint i_LengthData;

        /// <summary>
        /// Length of data in matrix [m*n], m = count of repeat, n = number of samples in one period input signal catched in time of sampling input signal.
        /// LengthInterestData = n
        /// </summary>
        private uint i_LengthInterestData;

        /// <summary>
        /// position for iData
        /// </summary>
        public int Position;
        ///<summary>
        /// index for storing data to matrix(interesting values)
        ///</summary>  
        private int IndexRepeatSet;
        /// <summary>
        /// position for iOrganizedData - individual samples
        /// </summary>
        public int PositionOrganized;
        /// <summary>
        /// position for iOrganizedData - count of repeat
        /// </summary>
        public int IndexRepeatGet;
        /// <summary>
        /// Position for current values list
        /// </summary>
        public int PostionCurrent;

        /// <summary>
        /// Position for Step response values list
        /// </summary>
        public int PositionStepRes;

        /// <summary>
        /// Empty consructor
        /// </summary>
        public MeasureData() : base()
        {
            Position = 0;
            PositionOrganized = 0;
            PostionCurrent = 0;
            PositionStepRes = 0;
            IndexRepeatGet = 0;
            IndexRepeatSet = 0;
            i_LengthData = 0;
            i_LengthInterestData = 0;
        }

        /// <summary>
        /// Constructor from base class
        /// </summary>
        /// <param name="PropData"></param>
        public MeasureData(DataProperties PropData) : base(PropData)
        {
            Position = 0;
            PositionOrganized = 0;
            IndexRepeatGet = 0;
            IndexRepeatSet = 0;
            iData = new List<MData>();
            iOrganizedData = new List<MData>[CountRepeat];
            for (int i = 0; i < CountRepeat; i++)
            {
                iOrganizedData[i] = new List<MData>();
            }
            iCurrentValue = new List<Int16>();
            iStepResValue = new List<MData>();

            if (CountRepeat == 0)
            {
                i_LengthData = 0;
                i_LengthInterestData = 0;
            }
            else
            {
                i_LengthData = LengthInputSignal * Convert.ToUInt32(PeriodInputSignal / TaskTime);
                i_LengthInterestData = LengthInputSignal;
            }


        }

        /// <summary>
        /// Add measured data
        /// </summary>
        public void Add(Int32 CommandValue, Int32 FeedbackValue)
        {
            if (IndexRepeatSet < iOrganizedData.Count())
            {
                MData tempData = new MData(CommandValue, FeedbackValue);
                iData.Add(tempData);

                iOrganizedData[IndexRepeatSet].Add(tempData);
                if (i_LengthData <= iOrganizedData[IndexRepeatSet].Count)
                {
                    ++IndexRepeatSet;
                }
            }
        }

        /// <summary>
        /// Add measured current value
        /// </summary>
        /// <param name="CurrentValue"></param>
        public void AddCurrentValue(Int16 CurrentValue)
        {
            iCurrentValue.Add(CurrentValue);
        }

        /// <summary>
        /// Add measured Step response value
        /// </summary>
        /// <param name="CommandValue"></param>
        /// <param name="FeedbackValue"></param>
        public void AddStepResValue(Int32 CommandValue, Int32 FeedbackValue)
        {
            MData tempData = new MData(CommandValue, FeedbackValue);
            iStepResValue.Add(tempData);
        }

        /// <summary>
        /// Return actual item in buffer and shift to next item
        /// </summary>
        /// <returns></returns>
        public MData GetData()
        {
            MData ret_data = new MData();
            ret_data = iData[Position];
            Position++;
            return ret_data;
        }

        /// <summary>
        /// Get current value according to PostionCurrent
        /// </summary>
        /// <returns></returns>
        public Int16 GetCurrentValue()
        {
            return iCurrentValue[PostionCurrent++];
        }

        /// <summary>
        /// Get step response value according to PositionStepRes
        /// </summary>
        /// <returns></returns>
        public MData GetStepResValue()
        {
            MData ret_data = new MData();
            ret_data = iStepResValue[PositionStepRes++];
            return ret_data;
        }

        /// <summary>
        /// Return actual item in buffer and shift to next item
        /// </summary>
        /// <returns></returns>
        public MData GetOrganizedData()
        {
            MData ret_data = new MData();
            ret_data = iOrganizedData[IndexRepeatGet][PositionOrganized];
            PositionOrganized++;
            if (i_LengthData <= PositionOrganized)
            {
                ++IndexRepeatGet;
                PositionOrganized = 0;
            }
            return ret_data;
        }

        /// <summary>
        /// Returns all command values in one row vector
        /// </summary>
        /// <returns></returns>
        public Int32[] GetAllCommandData()
        {
            Int32[] ret_data = new Int32[Count()];
            for (int i = 0; i < Count(); i++)
            {
                ret_data[i] = iData[i].CommandValue;
            }
            return (ret_data);
        }

        /// <summary>
        /// Returns all feedback values in one row vector from Identification
        /// </summary>
        /// <returns></returns>
        public Int32[] GetAllFeedbackData()
        {
            Int32[] ret_data = new Int32[Count()];
            for (int i = 0; i < Count(); i++)
            {
                ret_data[i] = iData[i].FeedbackValue;
            }
            return (ret_data);
        }

        /// <summary>
        /// Returns all current values in one row vector from Identification
        /// </summary>
        /// <returns></returns>
        public short[] GetAllCurrentData()
        {
            short[] ret_data = new short[iCurrentValue.Count];
            for (int i = 0; i < iCurrentValue.Count; i++)
            {
                ret_data[i] = iCurrentValue[i];
            }
            return (ret_data);
        }

        /// <summary>
        /// Returns all command values in one row vector from Step response
        /// </summary>
        /// <returns></returns>
        public Int32[] GetAllStepResCommandData()
        {
            Int32[] ret_data = new Int32[iStepResValue.Count];
            for (int i = 0; i < iStepResValue.Count; i++)
            {
                ret_data[i] = iStepResValue[i].CommandValue;
            }
            return (ret_data);
        }

        /// <summary>
        /// Returns all feedback values in one row vector from Step response (See also GetAllStepResFeedbackData())
        /// </summary>
        /// <returns></returns>
        public Int32[] GetAllStepResFeedbackData()
        {
            Int32[] ret_data = new Int32[iStepResValue.Count];
            for (int i = 0; i < iStepResValue.Count; i++)
            {
                ret_data[i] = iStepResValue[i].FeedbackValue;
            }
            return (ret_data);
        }

        /// <summary>
        /// Get time axis for step response (See also GetAllStepResFeedbackData())
        /// </summary>
        /// <returns></returns>
        public double[] GetStepResTimeAxis()
        {
            double[] ret_data = new double[iStepResValue.Count];
            ret_data[0] = 0;
            for (int i = 1; i < iStepResValue.Count; i++)
            {
                ret_data[i] = ret_data[i - 1] + TaskTime;
            }
            return (ret_data);
        }

        /// <summary>
        /// Return all command data in matrix[m*n], m = number of repeat, n = number of values in time of sampling input signal
        /// </summary>
        /// <returns></returns>
        public Int32[][] GetOrganizedInterestCommandData()
        {
            // init variable for return
            Int32[][] ret_data = new Int32[CountRepeat][];
            for (int i = 0; i < CountRepeat; i++)
            {
                ret_data[i] = new Int32[i_LengthInterestData];
            }

            // transfer data to matrix

            for (int i = 0; i < CountRepeat; i++)
            {
                int index_sampling = 0;
                for (int j = 0; j < i_LengthInterestData; j++)
                {
                    ret_data[i][j] = iOrganizedData[i][index_sampling].CommandValue;
                    index_sampling += Convert.ToInt32(PeriodInputSignal / TaskTime);
                }
            }
            return ret_data;
        }

        /// <summary>
        /// Return all command data in matrix[m*n], m = number of repeat, n = number of values in time of sampling input signal
        /// </summary>
        /// <returns></returns>
        public Int32[][] GetOrganizedInterestFeedbackData()
        {
            // init variable for return
            Int32[][] ret_data = new Int32[CountRepeat][];
            for (int i = 0; i < CountRepeat; i++)
            {
                ret_data[i] = new Int32[i_LengthInterestData];
            }

            // transfer data to matrix
            for (int i = 0; i < CountRepeat; i++)
            {
                int index_sampling = 0;
                for (int j = 0; j < i_LengthInterestData; j++)
                {
                    ret_data[i][j] = iOrganizedData[i][index_sampling].FeedbackValue;
                    index_sampling += Convert.ToInt32(PeriodInputSignal / TaskTime);
                }
            }
            return ret_data;
        }

        /// <summary>
        /// Return one row of command data from matrix[m*n], m = number of repeat, n = number of values in time of sampling with base time.
        /// Return NumRow-th period command data of input signal
        /// </summary>
        /// <param name="NumRow"> Number of period command data of input signal  </param>
        /// <returns></returns>
        public Int32[] GetRowOrganizedCommandData(int NumRow)
        {
            // init variable for return
            Int32[] ret_data = new Int32[i_LengthData];

            // transfer data to vector
            for (int j = 0; j < i_LengthData; j++)
            {
                ret_data[j] = iOrganizedData[NumRow][j].CommandValue;
            }

            return ret_data;
        }

        /// <summary>
        /// Return one row of feedback data from matrix[m*n], m = number of repeat, n = number of values in time of sampling with base time.
        /// Return NumRow-th period feedback data of input signal
        /// </summary>
        /// <param name="NumRow"> Number of period feedback data of input signal  </param>
        /// <returns></returns>
        public Int32[] GetRowOrganizedFeedbackData(int NumRow)
        {
            // init variable for return
            Int32[] ret_data = new Int32[i_LengthData];

            // transfer data to vector
            for (int j = 0; j < i_LengthData; j++)
            {
                ret_data[j] = iOrganizedData[NumRow][j].FeedbackValue;
            }

            return ret_data;
        }

        /// <summary>
        /// Return one row of command data from matrix[m*n], m = number of repeat, n = number of values in time of sampling input signal.
        /// Return NumRow-th period command data of input signal
        /// </summary>
        /// <param name="NumRow"> Number of period command data of input signal  </param>
        /// <returns></returns>
        public Int32[] GetRowOrganizedInterestCommandData(int NumRow)
        {
            // init variable for return
            Int32[] ret_data = new Int32[i_LengthInterestData];

            // transfer data to vector
            int index_sampling = 0;
            for (int j = 0; j < i_LengthInterestData; j++)
            {
                ret_data[j] = iOrganizedData[NumRow][index_sampling].CommandValue;
                index_sampling += Convert.ToInt32(PeriodInputSignal / TaskTime);
            }

            return ret_data;
        }

        /// <summary>
        /// Return one row of feedback data from matrix[m*n], m = number of repeat, n = number of values in time of sampling input signal.
        /// Return NumRow-th period feedback data of input signal
        /// </summary>
        /// <param name="NumRow"> Number of period feedback data of input signal  </param>
        /// <returns></returns>
        public Int32[] GetRowOrganizedInterestFeedbackData(int NumRow)
        {
            // init variable for return
            Int32[] ret_data = new Int32[i_LengthInterestData];

            // transfer data to vector
            int index_sampling = 0;
            for (int j = 0; j < i_LengthInterestData; j++)
            {
                ret_data[j] = iOrganizedData[NumRow][index_sampling].FeedbackValue;
                index_sampling += Convert.ToInt32(PeriodInputSignal / TaskTime);
            }

            return ret_data;
        }

        /// <summary>
        /// Count of values from Identification
        /// </summary>
        /// <returns></returns>
        public int Count()
        {
            return iData.Count();
        }

        /// <summary>
        /// Count of values in one set of input signal (one set of repeat)
        /// </summary>
        /// <returns></returns>
        public int CountOneRaw()
        {
            return iOrganizedData[0].Count();
        }

        /// <summary>
        /// Count of values from Step response
        /// </summary>
        /// <returns></returns>
        public int CountStepRes()
        {
            return iStepResValue.Count();
        }

        /// <summary>
        /// Get AMS NET ID of target
        /// </summary>
        new public string AmsNetId
        {
            get { return base.AmsNetId; }
        }

        /// <summary>
        /// Get ADS Port of target
        /// </summary>
        new public int AdsPort
        {
            get { return base.AdsPort; }
        }

        /// <summary>
        /// Get path into external generator in PLC
        /// </summary>
        new public string PathExtGen
        {
            get { return base.PathExtGen; }
        }

        /// <summary>
        /// Get path into external generator in PLC
        /// </summary>
        new public eUnits Units
        {
            get { return base.Units; }
        }

        /// <summary>
        /// Get name of input signal for identification
        /// </summary>
        new public string NameInputSignal
        {
            get { return base.NameInputSignal; }
        }

        /// <summary>
        /// Get ID of input signal for identification
        /// </summary>
        new public int IdInputSignal
        {
            get { return base.IdInputSignal; }
        }

        /// <summary>
        /// Get period of input signal in seconds[s]
        /// </summary>
        new public Double PeriodInputSignal
        {
            get { return base.PeriodInputSignal; }
        }

        /// <summary>
        /// Get length of input signal (number samples of input signal)
        /// </summary>
        new public uint LengthInputSignal
        {
            get { return base.LengthInputSignal; }
        }

        /// <summary>
        /// Get number of repeat input signal
        /// </summary>
        new public uint CountRepeat
        {
            get { return base.CountRepeat; }
        }

        /// <summary>
        /// Get amplitude of input signal
        /// </summary>
        new public double AmplitudeInputSignal
        {
            get { return base.AmplitudeInputSignal; }
        }

        /// <summary>
        /// Amplitude of auxiliary movement [in the appropriate units according to mode control] (suppression of friction)
        /// </summary>
        new public double AmplAuxMove
        {
            get { return base.AmplAuxMove; }
        }

        /// <summary>
        /// Frequency of auxiliary movement in Hz (suppression of friction)
        /// </summary>
        new public double FreqAuxMove
        {
            get { return base.FreqAuxMove; }
        }

        /// <summary>
        /// Get Control mode:
        ///     - NoneControl
        ///     - PositionControl
        ///     - TorqueControl
        ///     - VelocityControl   
        /// </summary>
        new public eControlMode ControlMode
        {
            get { return base.ControlMode; }
        }

        /// <summary>
        /// Get date ant time of measuring
        /// </summary>
        new public DateTime DateTimeMd
        {
            get { return base.DateTimeMd; }
        }

        /// <summary>
        /// Get linear type of motor
        /// </summary>
        new public bool IsLinear
        {
            get { return base.IsLinear; }
        }

        /// <summary>
        /// Get scale for position mode
        /// </summary>
        new public double xScale
        {
            get { return base.xScale; }
        }

        /// <summary>
        /// Get scale for velocity mode
        /// </summary>
        new public double vScale
        {
            get { return base.vScale; }
        }

        /// <summary>
        /// Get scale for velocity mode
        /// </summary>
        new public double FeedConstant
        {
            get { return base.FeedConstant; }
        }

        /// <summary>
        /// Get base time of PLC task
        /// </summary>
        new public double TaskTime
        {
            get { return base.TaskTime; }
        }

        /// <summary>
        /// Get name of axis
        /// </summary>
        new public string NameAxis
        {
            get { return base.NameAxis; }
        }

        /// <summary>
        /// Get pre-trigger time of step response [s]
        /// </summary>
        new public double StepPreTrigger
        {
            get { return base.StepPreTrigger; }
        }

        /// <summary>
        /// Get duration of step response [s]
        /// </summary>
        new public double StepTime
        {
            get { return base.StepTime; }
        }

        /// <summary>
        /// Get initial level of step response [X]
        /// </summary>
        new public double StepInitLevel
        {
            get { return base.StepInitLevel; }
        }

        /// <summary>
        /// Get final level of step response [X]
        /// </summary>
        new public double StepFinalLevel
        {
            get { return base.StepFinalLevel; }
        }

        /// <summary>
        /// Length of data in matrix [m*n], m = number of repeat, n = number of samples in one period input signal.
        /// LengthInterestData = n
        /// Length data from one period of input signal
        /// </summary>
        public UInt32 LengthData
        {
            get { return i_LengthData; }
        }

        /// <summary>
        /// Length of data in matrix [m*n], m = number of repeat, n = number of samples in one period input signal in time of sampling.
        /// LengthInterestData = n 
        /// Length data from one period of input signal
        /// </summary>
        public UInt32 LengthInterestData
        {
            get { return i_LengthInterestData; }
        }

        /// <summary>
        /// Get/Set Id of this data set.
        /// </summary>
        public int ID
        {
            set
            {
                i_ID = value;
            }
            get { return i_ID; }
        }

        /// <summary>
        /// Get name for saved item
        /// </summary>
        /// <returns></returns>
        public string GetSaveName()
        {
            string FileName = i_ID.ToString() + "_" + NameAxis + "_" + DateTimeMd.Year.ToString() + "_" + DateTimeMd.Month.ToString() + '_' + DateTimeMd.Day.ToString() + '_' + DateTimeMd.Hour.ToString() + '_' + DateTimeMd.Minute.ToString() + '_' + DateTimeMd.Second.ToString() + '_' + ControlMode.ToString() + @".identdat";
            return FileName;
        }

        /// <summary>
        /// Save measured data
        /// </summary>
        /// <param name="Path"> Path for new file with measured data </param>
        public void SaveData(string Path)
        {
            Position = 0;
            MData varData = new MData();
            System.Globalization.CultureInfo invC = System.Globalization.CultureInfo.InvariantCulture;
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.NewLineOnAttributes = true;
            XmlWriter xmlWriter = XmlWriter.Create(Path, settings);
            xmlWriter.WriteStartDocument();
            xmlWriter.WriteStartElement("IdentifiData");
            xmlWriter.WriteStartElement("Properties");
            xmlWriter.WriteAttributeString("Version", "1");
            xmlWriter.WriteAttributeString("AmsNetId", AmsNetId);
            xmlWriter.WriteAttributeString("AdsPort", AdsPort.ToString(invC));
            xmlWriter.WriteAttributeString("PathExtGen", PathExtGen);
            xmlWriter.WriteAttributeString("NameAxis", NameAxis);
            xmlWriter.WriteAttributeString("NameInputSignal", NameInputSignal);
            xmlWriter.WriteAttributeString("IdInputSignal", Convert.ToString(IdInputSignal, invC));
            xmlWriter.WriteAttributeString("PeriodInputSignal", PeriodInputSignal.ToString(invC));
            xmlWriter.WriteAttributeString("CountRepeat", CountRepeat.ToString());
            xmlWriter.WriteAttributeString("ControlMode", Convert.ToString((int)ControlMode, invC));
            xmlWriter.WriteAttributeString("Units", Convert.ToString((int)Units, invC));
            xmlWriter.WriteAttributeString("DateTime", DateTimeMd.ToString(invC));
            xmlWriter.WriteAttributeString("IsLinear", IsLinear.ToString(invC));
            xmlWriter.WriteAttributeString("xScale", xScale.ToString(invC));
            xmlWriter.WriteAttributeString("vScale", vScale.ToString(invC));
            xmlWriter.WriteAttributeString("FeedConstant", FeedConstant.ToString(invC));
            xmlWriter.WriteAttributeString("TaskTime", TaskTime.ToString(invC));
            xmlWriter.WriteAttributeString("AmplitudeInputSignal", AmplitudeInputSignal.ToString(invC));
            xmlWriter.WriteAttributeString("AmplitudeAuxiliaryMoveSignal", AmplAuxMove.ToString(invC));
            xmlWriter.WriteAttributeString("FrequencyAuxiliaryMoveSignal", FreqAuxMove.ToString(invC));
            xmlWriter.WriteAttributeString("StepPreTrigger", StepPreTrigger.ToString(invC));
            xmlWriter.WriteAttributeString("StepTime", StepTime.ToString(invC));
            xmlWriter.WriteAttributeString("StepInitLevel", StepInitLevel.ToString(invC));
            xmlWriter.WriteAttributeString("StepFinalLevel", StepFinalLevel.ToString(invC));
            xmlWriter.WriteAttributeString("LengthWindow", LengthWindow.ToString(invC));
            xmlWriter.WriteAttributeString("ModeSpectAnalys", Convert.ToString((int)ModeSpectAnalys, invC));
            xmlWriter.WriteAttributeString("CheckPhaseOverflow", CheckPhaseOverflow.ToString(invC));
            xmlWriter.WriteAttributeString("OffsetIOdata", OffsetIOdata.ToString(invC));
            xmlWriter.WriteAttributeString("AllowedPositionRange", AllowedPositionRange.ToString(invC));
            xmlWriter.WriteAttributeString("EnablePositionRangeCheck", EnablePositionRangeCheck.ToString(invC));
            xmlWriter.WriteAttributeString("AllowedTorqueRangeIdent", AllowedTorqueRangeIdent.ToString(invC));
            xmlWriter.WriteAttributeString("EnableTorqueRangeCheckIdent", EnableTorqueRangeCheckIdent.ToString(invC));
            xmlWriter.WriteAttributeString("AllowedTorqueRangeStRes", AllowedTorqueRangeStRes.ToString(invC));
            xmlWriter.WriteAttributeString("EnableTorqueRangeCheckStRes", EnableTorqueRangeCheckStRes.ToString(invC));
            xmlWriter.WriteEndElement();
            xmlWriter.WriteStartElement("Data");

            xmlWriter.WriteStartElement("CommandValues");
            xmlWriter.WriteAttributeString("Count", Convert.ToString(Count(), invC));
            xmlWriter.WriteAttributeString("ValueType", "Int32");
            xmlWriter.WriteStartElement("Data");
            byte[] BufferCommandValues = new byte[Count() * 4];
            Int32[] BufferCommandValuesInt = GetAllCommandData();
            for (int i = 0; i < Count(); i++)
            {
                byte[] TempBuf = BitConverter.GetBytes(BufferCommandValuesInt[i]);
                Buffer.BlockCopy(TempBuf, 0, BufferCommandValues, i * 4, 4);
            }
            xmlWriter.WriteBase64(BufferCommandValues, 0, (Count() * 4));
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();

            xmlWriter.WriteStartElement("FeedbackValues");
            xmlWriter.WriteAttributeString("Count", Convert.ToString(Count(), invC));
            xmlWriter.WriteAttributeString("ValueType", "Int32");
            xmlWriter.WriteStartElement("Data");
            byte[] BufferFeedbackValues = new byte[Count() * 4];
            Int32[] BufferFeedbackValuesInt = GetAllFeedbackData();
            for (int i = 0; i < Count(); i++)
            {
                byte[] TempBuf = BitConverter.GetBytes(BufferFeedbackValuesInt[i]);
                Buffer.BlockCopy(TempBuf, 0, BufferFeedbackValues, i * 4, 4);
            }
            xmlWriter.WriteBase64(BufferFeedbackValues, 0, (Count() * 4));
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();

            xmlWriter.WriteStartElement("StepResCommandValues");
            xmlWriter.WriteAttributeString("Count", iStepResValue.Count.ToString());
            xmlWriter.WriteAttributeString("ValueType", "Int32");
            xmlWriter.WriteStartElement("Data");
            byte[] BufferStepResCommandValues = new byte[iStepResValue.Count * 4];
            Int32[] BufferStepResCommandValuesInt = GetAllStepResCommandData();
            for (int i = 0; i < iStepResValue.Count; i++)
            {
                byte[] TempBuf = BitConverter.GetBytes(BufferStepResCommandValuesInt[i]);
                Buffer.BlockCopy(TempBuf, 0, BufferStepResCommandValues, i * 4, 4);
            }
            xmlWriter.WriteBase64(BufferStepResCommandValues, 0, iStepResValue.Count * 4);
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();

            xmlWriter.WriteStartElement("StepResFeedbackValues");
            xmlWriter.WriteAttributeString("Count", iStepResValue.Count.ToString());
            xmlWriter.WriteAttributeString("ValueType", "Int32");
            xmlWriter.WriteStartElement("Data");
            byte[] BufferStepResFeedbackValues = new byte[iStepResValue.Count * 4];
            Int32[] BufferStepResFeedbackValuesInt = GetAllStepResFeedbackData();
            for (int i = 0; i < iStepResValue.Count; i++)
            {
                byte[] TempBuf = BitConverter.GetBytes(BufferStepResFeedbackValuesInt[i]);
                Buffer.BlockCopy(TempBuf, 0, BufferStepResFeedbackValues, i * 4, 4);
            }
            xmlWriter.WriteBase64(BufferStepResFeedbackValues, 0, iStepResValue.Count * 4);
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();

            xmlWriter.WriteStartElement("CurrentValues");
            xmlWriter.WriteAttributeString("Count", iCurrentValue.Count.ToString());
            xmlWriter.WriteAttributeString("ValueType", "Int16");
            xmlWriter.WriteStartElement("Data");
            byte[] BufferCurrentValues = new byte[iCurrentValue.Count * 2];
            short[] BufferCurrentValuesInt = GetAllCurrentData();
            for (int i = 0; i < iCurrentValue.Count; i++)
            {
                byte[] TempBuf = BitConverter.GetBytes(BufferCurrentValuesInt[i]);
                Buffer.BlockCopy(TempBuf, 0, BufferCurrentValues, i * 2, 2);
            }
            xmlWriter.WriteBase64(BufferCurrentValues, 0, iCurrentValue.Count * 2);
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();

            xmlWriter.WriteStartElement("Notes");
            xmlWriter.WriteAttributeString("Count", Convert.ToString(Notes.Length));
            xmlWriter.WriteAttributeString("ValueType", "string");
            xmlWriter.WriteStartElement("Data");
            byte[] BufferNotes = Encoding.Unicode.GetBytes(Notes);
            xmlWriter.WriteBase64(BufferNotes, 0, Notes.Length * 2);
            xmlWriter.WriteEndDocument();
            xmlWriter.Close();
        }

    }

    /// <summary>
    /// Class for data input/output storage
    /// </summary>
    public class MData
    {
        /// <summary>
        /// Command value - input value
        /// </summary>
        public Int32 CommandValue;
        /// <summary>
        /// Feedback value - output value
        /// </summary>
        public Int32 FeedbackValue;

        /// <summary>
        /// Constructor
        /// </summary>
        public MData()
        {
            CommandValue = 0;
            FeedbackValue = 0;
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="Command"> Command value - input value </param>
        /// <param name="Feedback"> Feedback value - output value </param>
        public MData(Int32 Command, Int32 Feedback)
        {
            CommandValue = Command;
            FeedbackValue = Feedback;
        }

    }

    /// <summary>
    /// Class for signal for indentification storage
    /// </summary>
    public class InputIdentSignal
    {
        /// <summary>
        /// Name of identification signal 
        /// </summary>
        public string Name;
        /// <summary>
        /// Values of identification signal
        /// </summary>
        public Collection<double> SignalData = new Collection<double>();
    }

    /// <summary>
    /// Class for control of external generator in PLC
    /// </summary>
    public class TwinCatExtGen
    {
        #region Events

        /// <summary>
        /// Event for changing of actual generator state.
        /// </summary>
        public event StateEventHandler ChangedState;

        /// <summary>
        /// Data arrived from IPC
        /// </summary>
        public event ActionEventHandler DataArrived;

        /// <summary>
        /// Values of current arrived from IPC
        /// </summary>
        public event ActionEventHandler CurrentArrived;

        /// <summary>
        /// Occured error in some event
        /// </summary>
        public event ActionEventHandler ErrorOccured;

        #endregion

        #region Constants

        /// <summary>
        /// Maximal number of item in one part of buffer
        /// </summary>
        private const Int32 MaxNumberItemsInBuffer = 10000;
        /// <summary>
        /// Maximal number parts of buffer
        /// </summary>
        private const Int32 MaxNumberPartsOfBuffers = 3;

        /// <summary>
        /// Maximal number of item in one part of current buffer
        /// </summary>
        private const Int32 MaxNumberItemsInCurrentBuffer = 1000;
        /// <summary>
        /// maximal number parts of current buffer
        /// </summary>
        private const Int32 MaxNumberPartsOfCurrentBuffers = 3;

        #endregion

        #region Variables

        /// <summary>
        /// List of input identification signal
        /// </summary>
        public Collection<InputIdentSignal> ListInputIdentSignal = new Collection<InputIdentSignal>();

        /// <summary>
        /// Actual state of External Generator in PLC
        /// </summary>
        private eStateExtGen ActStateExtGen = eStateExtGen.Offline;

        /// <summary>
        /// Handlers of variables in PLC
        /// </summary>
        private int h_bInit, h_bStart, h_bStop, h_bIdentDone, h_bStepResDone, h_nLengthInputSignal, h_nNumberRepeat, h_nPeriodInputSignal, h_nAmplitudeInputSignal, h_eModeControl, h_aBufferInputSignal, h_nAmplAuxMove, h_fFreqAuxMove, h_aFullBuffer,
            h_aWriteFullBuffer, h_nIndexItemBuffer, h_bSendFinished, h_aFullCurrentBuffer, h_aWriteFullCurrentBuffer, h_nIndexItemCurrentBuffer, h_bError, h_nErrorId, h_nStepResPreTrigger, h_nStepResTime, h_nStepResInitLevel, h_nStepResFinalLevel, h_fCycleTimeTask,
            h_bCycleTimeDone, h_nAllowPosRange, h_bEnablePosRangCheck, h_nAllowTorqueRangeIdent, h_bEnableTorqueRangCheckIdent, h_nAllowTorqueRangeStRes, h_bEnableTorqueRangCheckStRes, h_StatePlc, h_bWatchdogIn, h_bWatchdogOut, h_tTimeout, h_bEnableTimeout;
        /// <summary>
        /// Handlers of buffers(arrays) in PLC
        /// </summary>
        private int[] h_aBufferCommandValue, h_aBufferFeedbackValue, h_aBufferCurrentValue;
        /// <summary>
        /// ADS TwinCAT Client for connection to PLC
        /// </summary>
        private TcAdsClient tcClient;
        /// <summary>
        /// ADS streams for event driven reading
        /// </summary>
        private AdsStream dataStreamaFullBuffer, dataStreamaFullCurrentBuffer, dataStreambIdentDone, dataStreambStepResDone, dataStreambCycleTimeDone, dataStreambError, dataStreamStatePlc, dataStreambWatchdogIn;
        /// <summary>
        /// Auxiliary readers for ADS streams
        /// </summary>
        private BinaryReader binReadaFullBuffer, binReadaFullCurrentBuffer, binReadbIdentDone, binReadbStepResDone, binReadbCycleTimeDone, binReadbError, binReadStatePlc, binReadbWatchdogIn;

        /// <summary>
        /// Path to external generator in PLC
        /// </summary>
        private string ActExePath;
        /// <summary>
        /// Flags for full/empty buffers in PLC
        /// </summary>
        private bool[] FullBuffer, FullCurrentBuffer;
        /// <summary>
        /// Actual number of part of buffer for reading [0..2]
        /// </summary>
        private UInt16 ActualNumberReadBuffer;
        /// <summary>
        /// Actual number of part of current buffer for reading [0..2]
        /// </summary>   
        private UInt16 ActualNumberReadCurrentBuffer;

        /// <summary>
        /// Storage data
        /// </summary>
        public List<MeasureData> MeasuredData = new List<MeasureData>();

        /// <summary>
        /// If identification is done so var = true (only for internal use during Running state)
        /// </summary>
        private bool IdentificationDone = false;

        /// <summary>
        /// Cycle time of actual measurement (only for internal use during Online->Running state). If var is 0 = it is uninitialized.
        /// </summary>
        private double iActCycleTime = 0;

        /// <summary>
        /// Get cycle time of actual measurement (task). If var is 0 -> it is uninitialized.
        /// </summary>
        public double ActCycleTime
        {
            get { return iActCycleTime; }
        }

        /// <summary>
        /// Watchdog - timer
        /// </summary>
        DispatcherTimer WatchdogTimer = new DispatcherTimer();

        /// <summary>
        /// Timeout for comunication between App and PLC 
        /// </summary>
        private TimeSpan Timeout = new TimeSpan(0);

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor
        /// </summary>
        public TwinCatExtGen()
        {
            // Get actual path to exe
            ActExePath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
            FullBuffer = new bool[3];
            FullCurrentBuffer = new bool[3];
            h_aBufferCommandValue = new int[3];
            h_aBufferFeedbackValue = new int[3];
            h_aBufferCurrentValue = new int[3];
            ActualNumberReadBuffer = 0;
            ActualNumberReadCurrentBuffer = 0;
            WatchdogTimer.Tick += WatchdogTimer_Tick;
        }


        #endregion

        #region Methods

        /// <summary>
        /// Connect to PLC(create handles)
        /// </summary>
        /// <param name="AdsNetId"></param>
        /// <param name="AdsPort"></param>
        /// <param name="PathExtGen">path to external generator in PLC</param>
        public void Connect(string AdsNetId, int AdsPort, string PathExtGen)
        {
            if (ActStateExtGen == eStateExtGen.Offline)
            {
                dataStreamaFullBuffer = new AdsStream(3);
                dataStreamaFullCurrentBuffer = new AdsStream(3);
                dataStreambIdentDone = new AdsStream(1);
                dataStreambStepResDone = new AdsStream(1);
                dataStreambCycleTimeDone = new AdsStream(1);
                dataStreambError = new AdsStream(1);
                dataStreamStatePlc = new AdsStream(2);
                dataStreambWatchdogIn = new AdsStream(1);
                binReadaFullBuffer = new BinaryReader(dataStreamaFullBuffer);
                binReadaFullCurrentBuffer = new BinaryReader(dataStreamaFullCurrentBuffer);
                binReadbIdentDone = new BinaryReader(dataStreambIdentDone);
                binReadbStepResDone = new BinaryReader(dataStreambStepResDone);
                binReadbCycleTimeDone = new BinaryReader(dataStreambCycleTimeDone);
                binReadbError = new BinaryReader(dataStreambError);
                binReadStatePlc = new BinaryReader(dataStreamStatePlc);
                binReadbWatchdogIn = new BinaryReader(dataStreambWatchdogIn);

                // Create instance of class TvAdsClient
                tcClient = new TcAdsClient();
                try
                {
                    iActCycleTime = 0; //Init - Cycle time of acaual task in PLC

                    // Connect to PLC 
                    tcClient.Connect(AdsNetId, AdsPort);
                    // Create handle on variable in PLC

                    h_bInit = tcClient.CreateVariableHandle(PathExtGen + ".bInit");
                    h_bStart = tcClient.CreateVariableHandle(PathExtGen + ".bStart");
                    h_bStop = tcClient.CreateVariableHandle(PathExtGen + ".bStop");
                    h_bIdentDone = tcClient.AddDeviceNotification(PathExtGen + ".bIdentDone", dataStreambIdentDone, 0, 1, AdsTransMode.OnChange, 100, 0, null);
                    h_bStepResDone = tcClient.AddDeviceNotification(PathExtGen + ".bStepResDone", dataStreambStepResDone, 0, 1, AdsTransMode.OnChange, 100, 0, null);
                    h_bCycleTimeDone = tcClient.AddDeviceNotification(PathExtGen + ".bCycleTimeDone", dataStreambCycleTimeDone, 0, 1, AdsTransMode.OnChange, 100, 0, null);
                    h_nLengthInputSignal = tcClient.CreateVariableHandle(PathExtGen + ".nLengthInputSignal");
                    h_nNumberRepeat = tcClient.CreateVariableHandle(PathExtGen + ".nNumberRepeat");
                    h_nPeriodInputSignal = tcClient.CreateVariableHandle(PathExtGen + ".nPeriodInputSignal");
                    h_nAmplitudeInputSignal = tcClient.CreateVariableHandle(PathExtGen + ".nAmplitudeInputSignal");
                    h_eModeControl = tcClient.CreateVariableHandle(PathExtGen + ".eModeControl");
                    h_aBufferInputSignal = tcClient.CreateVariableHandle(PathExtGen + ".aBufferInputSignal");
                    h_nAmplAuxMove = tcClient.CreateVariableHandle(PathExtGen + ".nAmplAuxMove");
                    h_fFreqAuxMove = tcClient.CreateVariableHandle(PathExtGen + ".fFreqAuxMove");
                    h_nStepResPreTrigger = tcClient.CreateVariableHandle(PathExtGen + ".nStepResPreTrigger");
                    h_nStepResTime = tcClient.CreateVariableHandle(PathExtGen + ".nStepResTime");
                    h_nStepResInitLevel = tcClient.CreateVariableHandle(PathExtGen + ".nStepResInitLevel");
                    h_nStepResFinalLevel = tcClient.CreateVariableHandle(PathExtGen + ".nStepResFinalLevel");
                    h_fCycleTimeTask = tcClient.CreateVariableHandle(PathExtGen + ".fCycleTimeTask");
                    h_nAllowPosRange = tcClient.CreateVariableHandle(PathExtGen + ".nAllowPosRange");
                    h_bEnablePosRangCheck = tcClient.CreateVariableHandle(PathExtGen + ".bEnablePosRangCheck");
                    h_nAllowTorqueRangeIdent = tcClient.CreateVariableHandle(PathExtGen + ".nAllowTorqueRangeIdent");
                    h_bEnableTorqueRangCheckIdent = tcClient.CreateVariableHandle(PathExtGen + ".bEnableTorqueRangCheckIdent");
                    h_nAllowTorqueRangeStRes = tcClient.CreateVariableHandle(PathExtGen + ".nAllowTorqueRangeStRes");
                    h_bEnableTorqueRangCheckStRes = tcClient.CreateVariableHandle(PathExtGen + ".bEnableTorqueRangCheckStRes");
                    h_nErrorId = tcClient.CreateVariableHandle(PathExtGen + ".nErrorId");
                    h_bError = tcClient.AddDeviceNotification(PathExtGen + ".bError", dataStreambError, 0, 1, AdsTransMode.OnChange, 100, 0, null);

                    h_aBufferCommandValue[0] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferCommandValue[1]");
                    h_aBufferCommandValue[1] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferCommandValue[2]");
                    h_aBufferCommandValue[2] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferCommandValue[3]");
                    h_aBufferFeedbackValue[0] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferFeedbackValue[1]");
                    h_aBufferFeedbackValue[1] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferFeedbackValue[2]");
                    h_aBufferFeedbackValue[2] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferFeedbackValue[3]");
                    h_aBufferCurrentValue[0] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferCurrentValue[1]");
                    h_aBufferCurrentValue[1] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferCurrentValue[2]");
                    h_aBufferCurrentValue[2] = tcClient.CreateVariableHandle(PathExtGen + ".aBufferCurrentValue[3]");

                    h_aFullBuffer = tcClient.AddDeviceNotification(PathExtGen + ".aFullBuffer", dataStreamaFullBuffer, 0, 3, AdsTransMode.OnChange, 100, 0, null);
                    h_nIndexItemBuffer = tcClient.CreateVariableHandle(PathExtGen + ".nIndexItemBuffer");
                    h_bSendFinished = tcClient.CreateVariableHandle(PathExtGen + ".bSendFinished");
                    h_aWriteFullBuffer = tcClient.CreateVariableHandle(PathExtGen + ".aWriteFullBuffer");

                    h_aFullCurrentBuffer = tcClient.AddDeviceNotification(PathExtGen + ".aFullCurrentBuffer", dataStreamaFullCurrentBuffer, 0, 3, AdsTransMode.OnChange, 100, 0, null);
                    h_nIndexItemCurrentBuffer = tcClient.CreateVariableHandle(PathExtGen + ".nIndexItemCurrentBuffer");
                    h_aWriteFullCurrentBuffer = tcClient.CreateVariableHandle(PathExtGen + ".aWriteFullCurrentBuffer");

                    h_StatePlc = tcClient.AddDeviceNotification((int)AdsReservedIndexGroups.DeviceData, /* index group of the device state*/ (int)AdsReservedIndexOffsets.DeviceDataAdsState, /*index offsset of the device state */
                                                                dataStreamStatePlc, AdsTransMode.OnChange, 100, 0, null);
                    h_bWatchdogIn = tcClient.AddDeviceNotification(PathExtGen + ".bWatchdogIn", dataStreambWatchdogIn, 0, 1, AdsTransMode.OnChange, 100, 0, null);
                    h_bWatchdogOut = tcClient.CreateVariableHandle(PathExtGen + ".bWatchdogOut");
                    h_tTimeout = tcClient.CreateVariableHandle(PathExtGen + ".tTimeout");
                    h_bEnableTimeout = tcClient.CreateVariableHandle(PathExtGen + ".bEnableTimeout");

                    tcClient.AdsNotification += new AdsNotificationEventHandler(OnNotification);

                    // Read Timeout
                    AdsStream adsTimeStream = new AdsStream(4);
                    AdsBinaryReader readerTime = new AdsBinaryReader(adsTimeStream);
                    tcClient.Read(h_tTimeout, adsTimeStream);
                    Timeout = readerTime.ReadPlcTIME();
                    // Init watchdog-timer
                    WatchdogTimer.Interval = Timeout;

                    //Write bInit = false
                    AdsStream dataBytStream = new AdsStream(1);
                    BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                    dataBytStream.Position = 0;
                    binBytWrite.Write(false);
                    tcClient.Write(h_bInit, dataBytStream);

                    //Write bInit = true
                    dataBytStream.Position = 0;
                    binBytWrite.Write(true);
                    tcClient.Write(h_bInit, dataBytStream);
                }
                catch (Exception err)
                {
                    if (tcClient.IsConnected)
                    {
                        tcClient.Dispose();
                    }
                    throw new Exception("Can't connect to PLC.\n" + err.Message);
                }
            }

        }

        /// <summary>
        /// Load input signal from Files\\InputSignal.xml
        /// </summary>
        public void LoadInputSignal()
        {
            XmlReader xmlReader = XmlReader.Create("Files\\InputSignal.xml");
            int NumInputSignals = 0;
            while (xmlReader.Read())
            {
                if ((xmlReader.NodeType == XmlNodeType.Element) && (xmlReader.Name == "Signals"))
                {
                    if (xmlReader.HasAttributes)
                    {
                        NumInputSignals = Convert.ToInt32(xmlReader.GetAttribute("NumberOfInputSignals"));
                        break;
                    }
                }
            }

            for (int i = 0; i < NumInputSignals; i++)
            {
                InputIdentSignal tempInputIdentSignal = new InputIdentSignal();

                xmlReader.ReadToFollowing("DataSignal" + Convert.ToString(i + 1));
                int LengthInputSignals = Convert.ToInt32(xmlReader.GetAttribute("Length"));
                tempInputIdentSignal.Name = xmlReader.GetAttribute("Name");

                xmlReader.ReadToFollowing("Data");
                byte[] DataBuf = new byte[LengthInputSignals * 8];
                xmlReader.ReadElementContentAsBase64(DataBuf, 0, LengthInputSignals * 8);

                double num = 0;
                for (int j = 0; j < LengthInputSignals; j++)
                {
                    num = BitConverter.ToDouble(DataBuf, j * 8);
                    tempInputIdentSignal.SignalData.Add(num);
                }

                ListInputIdentSignal.Add(tempInputIdentSignal);
            }
        }

        /// <summary>
        /// Initialize external generator - send input signal to PLC
        /// </summary>
        /// <param name="SelectInSignal">Number of selected input signal from ListInputSignal</param> 
        public void InitializeInputSignal(int SelectInSignal)
        {
            if (SelectInSignal >= ListInputIdentSignal.Count)
            {
                throw new Exception("Wrong set of selected input signal for identification");
            }

            AdsStream dataStream = new AdsStream(ListInputIdentSignal[SelectInSignal].SignalData.Count * 8);
            BinaryWriter binWrite = new BinaryWriter(dataStream);
            AdsStream dataIntStream = new AdsStream(2);
            BinaryWriter binIntWrite = new BinaryWriter(dataIntStream);
            dataStream.Position = 0;
            try
            {
                //Fill stream according to the order with data from the text boxes
                for (int i = 0; i < ListInputIdentSignal[SelectInSignal].SignalData.Count; i++)
                {
                    binWrite.Write(ListInputIdentSignal[SelectInSignal].SignalData[i]);
                }
                //Write complete stream in the PLC
                tcClient.Write(h_aBufferInputSignal, dataStream);

                // Write length of input signal = count of samples
                binIntWrite.Write(Convert.ToUInt16(ListInputIdentSignal[SelectInSignal].SignalData.Count));
                tcClient.Write(h_nLengthInputSignal, dataIntStream);
            }
            catch (Exception err)
            {
                throw new Exception("Problem with sending of input signal for identification\n" + err.Message);
            }
        }

        /// <summary>
        /// Start external generator
        /// </summary>
        /// <param name="iDataProp"></param>
        public void Start(DataProperties iDataProp)
        {
            if (ActStateExtGen == eStateExtGen.Online)
            {
                try
                {
                    InitializeInputSignal(iDataProp.IdInputSignal);

                    uint LengthInputSignal = (uint)ListInputIdentSignal[iDataProp.IdInputSignal].SignalData.Count;
                    iDataProp.NameInputSignal = ListInputIdentSignal[iDataProp.IdInputSignal].Name;
                    iDataProp.LengthInputSignal = (uint)ListInputIdentSignal[iDataProp.IdInputSignal].SignalData.Count;

                    ActualNumberReadBuffer = 0;     //number of actual part of buffers for recieving
                    ActualNumberReadCurrentBuffer = 0;     //number of actual part of buffers for recieving
                    IdentificationDone = false;

                    //Write nNumberRepeat = count repeat of input signal
                    AdsStream dataInt16Stream = new AdsStream(2);
                    BinaryWriter binInt16Write = new BinaryWriter(dataInt16Stream);
                    dataInt16Stream.Position = 0;
                    binInt16Write.Write(iDataProp.CountRepeat);

                    tcClient.Write(h_nNumberRepeat, dataInt16Stream);

                    //Write nPeriodInputSignal = number of ticks of PLC task for one sample of input signal
                    UInt16 PlcPeriodInputSignal = Convert.ToUInt16(iDataProp.PeriodInputSignal / iActCycleTime);
                    dataInt16Stream.Position = 0;
                    binInt16Write.Write(PlcPeriodInputSignal);

                    tcClient.Write(h_nPeriodInputSignal, dataInt16Stream);

                    //Scale
                    double Scale = iDataProp.GetScale();

                    //Write nAmplitudeInputSignal = amplitude of InputSignal
                    AdsStream dataInt32Stream = new AdsStream(4);
                    BinaryWriter binInt32Write = new BinaryWriter(dataInt32Stream);
                    dataInt32Stream.Position = 0;
                    binInt32Write.Write(Convert.ToInt32(iDataProp.AmplitudeInputSignal / Scale));

                    tcClient.Write(h_nAmplitudeInputSignal, dataInt32Stream);


                    //Write nAmplAuxMove = Amplitude of auxiliary movement
                    dataInt32Stream.Position = 0;
                    binInt32Write.Write(Convert.ToInt32(iDataProp.AmplAuxMove / Scale));

                    tcClient.Write(h_nAmplAuxMove, dataInt32Stream);


                    //Write fFreqAuxMove = Frequency of auxiliary movement 
                    AdsStream dataDoubleStream = new AdsStream(8);
                    BinaryWriter binDoubleWrite = new BinaryWriter(dataDoubleStream);
                    dataDoubleStream.Position = 0;
                    binDoubleWrite.Write(iDataProp.FreqAuxMove);

                    tcClient.Write(h_fFreqAuxMove, dataDoubleStream);


                    //Write nModeControl
                    dataInt16Stream.Position = 0;
                    switch (iDataProp.ControlMode)
                    {
                        case eControlMode.PositionControl:
                            binInt16Write.Write(Convert.ToInt16(0));
                            break;
                        case eControlMode.VelocityControl:
                            binInt16Write.Write(Convert.ToInt16(1));
                            break;
                        case eControlMode.TorqueControl:
                            binInt16Write.Write(Convert.ToInt16(2));
                            break;
                        case eControlMode.UnknownControl:
                            binInt16Write.Write(Convert.ToInt16(3));
                            break;
                        default:
                            throw new Exception("Wrong set control mode");
                    }
                    tcClient.Write(h_eModeControl, dataInt16Stream);

                    //Write nStepResPreTrigger = Time Pre-trigger [count of cycles]. Time before step for stabilization of system output.
                    dataInt32Stream.Position = 0;
                    binInt32Write.Write(Convert.ToInt32(iDataProp.StepPreTrigger / iActCycleTime));

                    tcClient.Write(h_nStepResPreTrigger, dataInt32Stream);

                    //Write nStepResTime = Duration of step response [count of cycles]
                    dataInt32Stream.Position = 0;
                    binInt32Write.Write(Convert.ToInt32(iDataProp.StepTime / iActCycleTime));

                    tcClient.Write(h_nStepResTime, dataInt32Stream);

                    //Write nStepResInitLevel = Initial level of step response [unitless - after scaling]
                    dataInt32Stream.Position = 0;
                    binInt32Write.Write(Convert.ToInt32(iDataProp.StepInitLevel / Scale));

                    tcClient.Write(h_nStepResInitLevel, dataInt32Stream);

                    //Write nStepResFinalLevel = Final level of step response [unitless - after scaling]
                    dataInt32Stream.Position = 0;
                    binInt32Write.Write(Convert.ToInt32(iDataProp.StepFinalLevel / Scale));

                    tcClient.Write(h_nStepResFinalLevel, dataInt32Stream);

                    //Write nAllowPosRange = Allowed position range
                    dataInt32Stream.Position = 0;
                    binInt32Write.Write(Convert.ToInt32(iDataProp.AllowedPositionRange / (iDataProp.xScale * iDataProp.FeedConstant)));

                    tcClient.Write(h_nAllowPosRange, dataInt32Stream);

                    //Write bEnablePosRangCheck = enable check position range
                    AdsStream dataBytStream = new AdsStream(1);
                    BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                    dataBytStream.Position = 0;
                    binBytWrite.Write(iDataProp.EnablePositionRangeCheck);

                    tcClient.Write(h_bEnablePosRangCheck, dataBytStream);

                    //Write nAllowTorqueRangeIdent = Allowed torque range during identification
                    dataInt16Stream.Position = 0;
                    binInt16Write.Write(iDataProp.AllowedTorqueRangeIdent);

                    tcClient.Write(h_nAllowTorqueRangeIdent, dataInt16Stream);

                    //Write bEnableTorqueRangCheckIdent = enable check torque range during identification
                    dataBytStream.Position = 0;
                    binBytWrite.Write(iDataProp.EnableTorqueRangeCheckIdent);

                    tcClient.Write(h_bEnableTorqueRangCheckIdent, dataBytStream);

                    //Write nAllowTorqueRangeStRes = Allowed torque range during step response
                    dataInt16Stream.Position = 0;
                    binInt16Write.Write(iDataProp.AllowedTorqueRangeStRes);

                    tcClient.Write(h_nAllowTorqueRangeStRes, dataInt16Stream);

                    //Write bEnableTorqueRangCheckStRes = enable check torque range during step response
                    dataBytStream.Position = 0;
                    binBytWrite.Write(iDataProp.EnableTorqueRangeCheckStRes);

                    tcClient.Write(h_bEnableTorqueRangCheckStRes, dataBytStream);

                    //Write bStart = start external generator
                    dataBytStream.Position = 0;
                    binBytWrite.Write(true);

                    tcClient.Write(h_bStart, dataBytStream);

                    MeasureData tempMeasuredData = new MeasureData(iDataProp);
                    MeasuredData.Add(tempMeasuredData);

                    // create event when generating was started
                    ActStateExtGen = eStateExtGen.Running;
                    ChangedState(this, new StateEvent(eStateExtGen.Running));

                }
                catch (Exception err)
                {
                    // Stop watchdog-timer
                    WatchdogTimer.Stop();

                    //Write bInit = false
                    AdsStream dataBytStream = new AdsStream(1);
                    BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                    dataBytStream.Position = 0;
                    binBytWrite.Write(false);
                    try
                    {
                        tcClient.Write(h_bInit, dataBytStream);
                    }
                    catch (Exception err2)
                    {
                        if (tcClient.IsConnected)
                        {
                            tcClient.Dispose();
                        }
                        ActStateExtGen = eStateExtGen.Error;
                        ChangedState(this, new StateEvent(eStateExtGen.Error));
                        throw new Exception("Can not start external generator in PLC and can not reinitialize external generator.\n" + err.Message + err2.Message);
                    }

                    if (tcClient.IsConnected)
                    {
                        tcClient.Dispose();
                    }
                    ActStateExtGen = eStateExtGen.Error;
                    ChangedState(this, new StateEvent(eStateExtGen.Error));
                    throw new Exception("Can not start external generator in PLC\n" + err.Message);
                }
            }
        }

        /// <summary>
        /// Stop external generator
        /// </summary>
        public void Stop()
        {
            if (ActStateExtGen == eStateExtGen.Running)
            {
                //Write bStop = stop external generator
                AdsStream dataBytStream = new AdsStream(1);
                BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                dataBytStream.Position = 0;
                binBytWrite.Write(true);

                try
                {
                    tcClient.Write(h_bStop, dataBytStream);
                    // remove last uncoplete data set
                    MeasuredData.RemoveAt(MeasuredData.Count - 1);
                    // create event when generating was stop
                    ActStateExtGen = eStateExtGen.Online;
                    ChangedState(this, new StateEvent(eStateExtGen.Online));
                }
                catch (Exception err)
                {
                    // Stop watchdog-timer
                    WatchdogTimer.Stop();

                    //Write bInit = false
                    dataBytStream.Position = 0;
                    binBytWrite.Write(false);
                    try
                    {
                        tcClient.Write(h_bInit, dataBytStream);
                    }
                    catch (Exception err2)
                    {
                        if (tcClient.IsConnected)
                        {
                            tcClient.Dispose();
                        }
                        // If PLC is sending data, remove last uncomplete data set
                        if (ActStateExtGen == eStateExtGen.Running)
                        {
                            MeasuredData.RemoveAt(MeasuredData.Count - 1);
                        }
                        ActStateExtGen = eStateExtGen.Error;
                        ChangedState(this, new StateEvent(eStateExtGen.Error));
                        throw new Exception("Can not stop external generator in PLC and can not reinitialize external generator.\n" + err.Message + err2.Message);
                    }

                    if (tcClient.IsConnected)
                    {
                        tcClient.Dispose();
                    }
                    // If PLC is sending data, remove last uncomplete data set
                    if (ActStateExtGen == eStateExtGen.Running)
                    {
                        MeasuredData.RemoveAt(MeasuredData.Count - 1);
                    }
                    ActStateExtGen = eStateExtGen.Error;
                    ChangedState(this, new StateEvent(eStateExtGen.Error));
                    throw new Exception("Can not stop external generator in PLC\n" + err.Message);
                }
            }
        }


        /// <summary>
        /// Event read data from PLC 
        /// </summary>
        public void OnNotification(object sender, AdsNotificationEventArgs e)
        {
            try
            {
                AdsStream dataCommandBuffer = new AdsStream(4 * MaxNumberItemsInBuffer * MaxNumberPartsOfBuffers);
                BinaryReader binCommandBuffer = new BinaryReader(dataCommandBuffer);
                AdsStream dataFeedbackBuffer = new AdsStream(4 * MaxNumberItemsInBuffer * MaxNumberPartsOfBuffers);
                BinaryReader binFeedbackBuffer = new BinaryReader(dataFeedbackBuffer);
                AdsStream dataCurrentBuffer = new AdsStream(2 * MaxNumberItemsInCurrentBuffer * MaxNumberPartsOfCurrentBuffers);
                BinaryReader binCurrentBuffer = new BinaryReader(dataCurrentBuffer);


                AdsStream dataBytStream = new AdsStream(1);
                BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                AdsStream data3BytStream = new AdsStream(3);
                BinaryWriter bin3BytWrite = new BinaryWriter(data3BytStream);

                e.DataStream.Position = e.Offset;

                if (e.NotificationHandle == h_aFullBuffer)  // one part of buffers is full
                {
                    if (IdentificationDone == false)
                    {
                        // Identification

                        FullBuffer[0] = binReadaFullBuffer.ReadBoolean();
                        FullBuffer[1] = binReadaFullBuffer.ReadBoolean();
                        FullBuffer[2] = binReadaFullBuffer.ReadBoolean();

                        if (FullBuffer[ActualNumberReadBuffer]) // read full part of buffers
                        {
                            tcClient.Read(h_aBufferCommandValue[ActualNumberReadBuffer], dataCommandBuffer, ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer, 4 * MaxNumberItemsInBuffer);       // read data from PLC - command value
                            tcClient.Read(h_aBufferFeedbackValue[ActualNumberReadBuffer], dataFeedbackBuffer, ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer, 4 * MaxNumberItemsInBuffer);     // read data from PLC - feedback value
                            dataCommandBuffer.Position = ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer;
                            dataFeedbackBuffer.Position = ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer;

                            for (int i = 0; i < MaxNumberItemsInBuffer; i++) //adding read data from PLC to collection in PC
                            {
                                MeasuredData.Last().Add(binCommandBuffer.ReadInt32(), binFeedbackBuffer.ReadInt32());
                            }

                            //Write aWriteFullBuffer[] = recieving data is finished from specific part of buffers for stroing data - write true to specific part of buffer aWriteFullBuffer
                            data3BytStream.Position = 0;    // clear data3BytStream
                            bin3BytWrite.Write(false);
                            bin3BytWrite.Write(false);
                            bin3BytWrite.Write(false);
                            data3BytStream.Position = ActualNumberReadBuffer;    // set position to actual reading part of buffer;
                            bin3BytWrite.Write(true);
                            data3BytStream.Position = 0;

                            tcClient.Write(h_aWriteFullBuffer, data3BytStream);

                            ++ActualNumberReadBuffer;
                            if (ActualNumberReadBuffer > 2)
                                ActualNumberReadBuffer = 0;
                        }
                    }
                    else
                    {
                        // Step response

                        FullBuffer[0] = binReadaFullBuffer.ReadBoolean();
                        FullBuffer[1] = binReadaFullBuffer.ReadBoolean();
                        FullBuffer[2] = binReadaFullBuffer.ReadBoolean();

                        if (FullBuffer[ActualNumberReadBuffer]) // read full part of buffers
                        {
                            tcClient.Read(h_aBufferCommandValue[ActualNumberReadBuffer], dataCommandBuffer, ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer, 4 * MaxNumberItemsInBuffer);       // read data from PLC - command value
                            tcClient.Read(h_aBufferFeedbackValue[ActualNumberReadBuffer], dataFeedbackBuffer, ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer, 4 * MaxNumberItemsInBuffer);     // read data from PLC - feedback value
                            dataCommandBuffer.Position = ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer;
                            dataFeedbackBuffer.Position = ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer;

                            for (int i = 0; i < MaxNumberItemsInBuffer; i++) //adding read data from PLC to collection in PC
                            {
                                MeasuredData.Last().AddStepResValue(binCommandBuffer.ReadInt32(), binFeedbackBuffer.ReadInt32());
                            }

                            //Write aWriteFullBuffer[] = recieving data is finished from specific part of buffers for stroing data - write true to specific part of buffer aWriteFullBuffer
                            data3BytStream.Position = 0;    // clear data3BytStream
                            bin3BytWrite.Write(false);
                            bin3BytWrite.Write(false);
                            bin3BytWrite.Write(false);
                            data3BytStream.Position = ActualNumberReadBuffer;    // set position to actual reading part of buffer;
                            bin3BytWrite.Write(true);
                            data3BytStream.Position = 0;

                            tcClient.Write(h_aWriteFullBuffer, data3BytStream);

                            ++ActualNumberReadBuffer;
                            if (ActualNumberReadBuffer > 2)
                                ActualNumberReadBuffer = 0;
                        }
                    }
                }
                else if (e.NotificationHandle == h_aFullCurrentBuffer)  // one part of current buffers is full
                {
                    FullCurrentBuffer[0] = binReadaFullCurrentBuffer.ReadBoolean();
                    FullCurrentBuffer[1] = binReadaFullCurrentBuffer.ReadBoolean();
                    FullCurrentBuffer[2] = binReadaFullCurrentBuffer.ReadBoolean();

                    if (FullCurrentBuffer[ActualNumberReadCurrentBuffer]) // read full part of current buffers
                    {
                        tcClient.Read(h_aBufferCurrentValue[ActualNumberReadCurrentBuffer], dataCurrentBuffer, ActualNumberReadCurrentBuffer * 2 * MaxNumberItemsInCurrentBuffer, 2 * MaxNumberItemsInCurrentBuffer);       // read data from PLC - current value
                        dataCurrentBuffer.Position = ActualNumberReadCurrentBuffer * 2 * MaxNumberItemsInCurrentBuffer;

                        for (int i = 0; i < MaxNumberItemsInCurrentBuffer; i++) //adding read data from PLC to collection in PC
                        {
                            MeasuredData.Last().AddCurrentValue(binCurrentBuffer.ReadInt16());
                        }

                        //Write aWriteFullCurrentBuffer[] = recieving data is finished from specific part of current buffers for stroing data - write true to specific part of buffer aWriteFullCurrentBuffer
                        data3BytStream.Position = 0;    // clear data3BytStream
                        bin3BytWrite.Write(false);
                        bin3BytWrite.Write(false);
                        bin3BytWrite.Write(false);
                        data3BytStream.Position = ActualNumberReadCurrentBuffer;    // set position to actual reading part of buffer;
                        bin3BytWrite.Write(true);
                        data3BytStream.Position = 0;

                        tcClient.Write(h_aWriteFullCurrentBuffer, data3BytStream);


                        ++ActualNumberReadCurrentBuffer;
                        if (ActualNumberReadCurrentBuffer > 2)
                            ActualNumberReadCurrentBuffer = 0;

                        // Event - current arrived
                        CurrentArrived(this);
                    }
                }
                else if (e.NotificationHandle == h_bIdentDone)   // identification is finished
                {
                    IdentificationDone = binReadbIdentDone.ReadBoolean();

                    if (IdentificationDone == true)
                    {
                        AdsStream dataInt16Stream = new AdsStream(2);
                        BinaryReader binInt16Read = new BinaryReader(dataInt16Stream);
                        tcClient.Read(h_nIndexItemBuffer, dataInt16Stream);
                        dataInt16Stream.Position = 0;
                        AdsStream dataInt16Stream2 = new AdsStream(2);
                        BinaryReader binInt16Read2 = new BinaryReader(dataInt16Stream2);
                        tcClient.Read(h_nIndexItemCurrentBuffer, dataInt16Stream2);
                        dataInt16Stream2.Position = 0;
                        UInt16 NumberStoredItems = binInt16Read.ReadUInt16();   // number of stored items in last used part of buffers
                        NumberStoredItems -= 1;                  // decrement NumberStoredItems because nIndexItemBuffer is pointing to next item in buffer

                        tcClient.Read(h_aBufferCommandValue[ActualNumberReadBuffer], dataCommandBuffer, ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer, NumberStoredItems * 4);        // read data from PLC - command value
                        tcClient.Read(h_aBufferFeedbackValue[ActualNumberReadBuffer], dataFeedbackBuffer, ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer, NumberStoredItems * 4);      // read data from PLC - feedback value

                        dataCommandBuffer.Position = ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer;
                        dataFeedbackBuffer.Position = ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer;

                        for (int i = 0; i < NumberStoredItems; i++) //adding read data from PLC to collection
                        {
                            MeasuredData.Last().Add(binCommandBuffer.ReadInt32(), binFeedbackBuffer.ReadInt32());
                        }
                        ActualNumberReadBuffer = 0;

                        //Write bSendFinished = recieving data is finished
                        dataBytStream.Position = 0;
                        binBytWrite.Write(true);

                        tcClient.Write(h_bSendFinished, dataBytStream);
                    }
                }
                else if (e.NotificationHandle == h_bStepResDone)   // step response is finished => All process is finished
                {
                    if (binReadbStepResDone.ReadBoolean() == true)
                    {
                        // create event when generating was stoped
                        ActStateExtGen = eStateExtGen.Online;
                        ChangedState(this, new StateEvent(eStateExtGen.Online));

                        AdsStream dataInt16Stream = new AdsStream(2);
                        BinaryReader binInt16Read = new BinaryReader(dataInt16Stream);
                        tcClient.Read(h_nIndexItemBuffer, dataInt16Stream);
                        dataInt16Stream.Position = 0;
                        AdsStream dataInt16Stream2 = new AdsStream(2);
                        BinaryReader binInt16Read2 = new BinaryReader(dataInt16Stream2);
                        tcClient.Read(h_nIndexItemCurrentBuffer, dataInt16Stream2);
                        dataInt16Stream2.Position = 0;
                        UInt16 NumberStoredItems = binInt16Read.ReadUInt16();   // number of stored items in last used part of buffers
                        UInt16 NumberStoredCurrentItems = binInt16Read2.ReadUInt16();   // number of stored items in last used part of current buffers
                        NumberStoredItems -= 1;                  // decrement NumberStoredItems because nIndexItemBuffer is pointing to next item in buffer
                        NumberStoredCurrentItems -= 1;

                        tcClient.Read(h_aBufferCommandValue[ActualNumberReadBuffer], dataCommandBuffer, ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer, NumberStoredItems * 4);        // read data from PLC - command value
                        tcClient.Read(h_aBufferFeedbackValue[ActualNumberReadBuffer], dataFeedbackBuffer, ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer, NumberStoredItems * 4);      // read data from PLC - feedback value
                        tcClient.Read(h_aBufferCurrentValue[ActualNumberReadCurrentBuffer], dataCurrentBuffer, ActualNumberReadCurrentBuffer * 2 * MaxNumberItemsInCurrentBuffer, 2 * MaxNumberItemsInCurrentBuffer);       // read data from PLC - current value

                        dataCommandBuffer.Position = ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer;
                        dataFeedbackBuffer.Position = ActualNumberReadBuffer * 4 * MaxNumberItemsInBuffer;
                        dataCurrentBuffer.Position = ActualNumberReadCurrentBuffer * 2 * MaxNumberItemsInCurrentBuffer;

                        for (int i = 0; i < NumberStoredItems; i++) //adding read data from PLC to collection
                        {
                            MeasuredData.Last().AddStepResValue(binCommandBuffer.ReadInt32(), binFeedbackBuffer.ReadInt32());
                        }
                        for (int i = 0; i < NumberStoredCurrentItems; i++) //adding read data from PLC to collection
                        {
                            MeasuredData.Last().AddCurrentValue(binCurrentBuffer.ReadInt16());
                        }
                        ActualNumberReadBuffer = 0;
                        ActualNumberReadCurrentBuffer = 0;


                        //Write bSendFinished = recieving data is finished
                        dataBytStream.Position = 0;
                        binBytWrite.Write(true);

                        tcClient.Write(h_bSendFinished, dataBytStream);

                        // Event - data arrived
                        DataArrived(this);
                    }
                }
                else if (e.NotificationHandle == h_bCycleTimeDone)
                {
                    if (binReadbCycleTimeDone.ReadBoolean() == true)
                    {
                        // Read fCycleTimeTask = cycle time of task
                        AdsStream dataDoubleStream = new AdsStream(8);
                        BinaryReader binDoubleRead = new BinaryReader(dataDoubleStream);
                        dataDoubleStream.Position = 0;
                        tcClient.Read(h_fCycleTimeTask, dataDoubleStream);
                        dataDoubleStream.Position = 0;
                        iActCycleTime = binDoubleRead.ReadDouble();

                        // create event when succesful connect to drive
                        ActStateExtGen = eStateExtGen.Online;
                        ChangedState(this, new StateEvent(eStateExtGen.Online));

                        // Start watchdog-timer
                        EnableTimeout();
                    }
                    else
                    {
                        iActCycleTime = 0;
                    }
                }
                else if (e.NotificationHandle == h_bError)   // generator is failed
                {
                    bool ErrorExtGen = binReadbError.ReadBoolean();
                    if (ErrorExtGen == true)
                    {
                        AdsStream dataInt16Stream = new AdsStream(2);
                        BinaryReader binInt16Read = new BinaryReader(dataInt16Stream);
                        dataInt16Stream.Position = 0;
                        tcClient.Read(h_nErrorId, dataInt16Stream);
                        dataInt16Stream.Position = 0;
                        UInt16 ErrorId = binInt16Read.ReadUInt16();   // error ID of generator in PLC

                        if (ErrorId != (UInt16)eErrorIdExtGenerator.eNoError)
                        {
                            // Stop watchdog-timer
                            WatchdogTimer.Stop();

                            // If PLC is sending data, remove last uncomplete data set
                            if (ActStateExtGen == eStateExtGen.Running)
                            {
                                MeasuredData.RemoveAt(MeasuredData.Count - 1);
                            }

                            if (tcClient.IsConnected)
                            {
                                tcClient.Dispose();
                            }

                            // create event
                            ActStateExtGen = eStateExtGen.Error;
                            ChangedState(this, new StateEvent(eStateExtGen.Error));
                            ErrorOccured("Error in External generator in PLC. Check it in PLC.\nError ID in PLC: " + (eErrorIdExtGenerator)ErrorId);
                        }
                    }
                }
                else if (e.NotificationHandle == h_StatePlc)   // PLC changed state
                {
                    Int16 PlcState = binReadStatePlc.ReadInt16();

                    if (PlcState != (Int16)AdsState.Run)
                    {
                        // Stop watchdog-timer
                        WatchdogTimer.Stop();

                        // If PLC is sending data, remove last uncomplete data set
                        if (ActStateExtGen == eStateExtGen.Running)
                        {
                            MeasuredData.RemoveAt(MeasuredData.Count - 1);
                        }

                        if (tcClient.IsConnected)
                        {
                            tcClient.Dispose();
                        }

                        // create event
                        ActStateExtGen = eStateExtGen.Error;
                        ChangedState(this, new StateEvent(eStateExtGen.Error));
                        ErrorOccured("PLC is not in run mode or is not running!");
                    }
                }
                else if (e.NotificationHandle == h_bWatchdogIn)   // Reset watchdog
                {
                    if (binReadbWatchdogIn.ReadBoolean() == true)
                    {
                        // Reset watchdog in App
                        WatchdogTimer.Stop();
                        WatchdogTimer.Start();

                        // Reset watchdog in PLC
                        dataBytStream.Position = 0;
                        binBytWrite.Write(true);

                        tcClient.Write(h_bWatchdogOut, dataBytStream);
                    }
                }
            }
            catch (Exception err)
            {
                // Stop watchdog-timer
                WatchdogTimer.Stop();

                if (e.NotificationHandle != h_bError)
                {
                    //Write bInit = false
                    AdsStream dataBytStream = new AdsStream(1);
                    BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                    dataBytStream.Position = 0;
                    binBytWrite.Write(false);
                    try
                    {
                        tcClient.Write(h_bInit, dataBytStream);
                    }
                    catch (Exception err2)
                    {
                        ErrorOccured("Can't read data from PLC and reinitialize external generator.\n" + err.Message + err2.Message);
                    }
                }
                // If PLC is sending data, remove last uncomplete data set
                if (ActStateExtGen == eStateExtGen.Running)
                {
                    MeasuredData.RemoveAt(MeasuredData.Count - 1);
                }
                if (tcClient.IsConnected)
                {
                    tcClient.Dispose();
                }
                // create event
                ActStateExtGen = eStateExtGen.Error;
                ChangedState(this, new StateEvent(eStateExtGen.Error));

                ErrorOccured("Can't read data from PLC\n" + err.Message);
            }
        }

        /// <summary>
        /// Timeout elapsed - error
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void WatchdogTimer_Tick(object sender, EventArgs e)
        {
            // Stop watchdog-timer
            WatchdogTimer.Stop();

            // If PLC is sending data, remove last uncomplete data set
            if (ActStateExtGen == eStateExtGen.Running)
            {
                MeasuredData.RemoveAt(MeasuredData.Count - 1);
            }

            // create event
            ActStateExtGen = eStateExtGen.Error;
            ChangedState(this, new StateEvent(eStateExtGen.Error));

            ErrorOccured("Communication timeout - communication error between SysIdentifi and PLC. Please wait until TwinCAT ADS Client is not released.");

            if (tcClient.IsConnected)
            {
                tcClient.Dispose();
            }

            ErrorOccured("TwinCAT ADS Client was successful released. After reset error you can continue.");

        }

        /// <summary>
        /// Enable check timeout
        /// </summary>
        public void EnableTimeout()
        {
            if (ActStateExtGen == eStateExtGen.Online || ActStateExtGen == eStateExtGen.Running)
            {
                try
                {
                    // Start watchdog-timer
                    WatchdogTimer.Start();

                    //Write bEnableTimeout = true
                    AdsStream dataBytStream = new AdsStream(1);
                    BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                    dataBytStream.Position = 0;
                    binBytWrite.Write(true);
                    tcClient.Write(h_bEnableTimeout, dataBytStream);
                }
                catch (Exception err)
                {
                    // Stop watchdog-timer
                    WatchdogTimer.Stop();

                    //Write bInit = false
                    AdsStream dataBytStream = new AdsStream(1);
                    BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                    dataBytStream.Position = 0;
                    binBytWrite.Write(false);
                    try
                    {
                        tcClient.Write(h_bInit, dataBytStream);
                    }
                    catch (Exception err2)
                    {
                        ErrorOccured("Can't read data from PLC and reinitialize external generator.\n" + err.Message + err2.Message);
                    }
 
                    // If PLC is sending data, remove last uncomplete data set
                    if (ActStateExtGen == eStateExtGen.Running)
                    {
                        MeasuredData.RemoveAt(MeasuredData.Count - 1);
                    }
                    if (tcClient.IsConnected)
                    {
                        tcClient.Dispose();
                    }
                    // create event
                    ActStateExtGen = eStateExtGen.Error;
                    ChangedState(this, new StateEvent(eStateExtGen.Error));

                    ErrorOccured("Can't read data from PLC\n" + err.Message);
                }
            }
        }

        /// <summary>
        /// Disable check timeout
        /// </summary>
        public void DisableTimeout()
        {
            if (ActStateExtGen == eStateExtGen.Online || ActStateExtGen == eStateExtGen.Running)
            {
                try
                {
                    // Stop watchdog-timer
                    WatchdogTimer.Stop();

                    //Write bEnableTimeout = false
                    AdsStream dataBytStream = new AdsStream(1);
                    BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                    dataBytStream.Position = 0;
                    binBytWrite.Write(false);
                    tcClient.Write(h_bEnableTimeout, dataBytStream);
                }
                catch (Exception err)
                {
                    // Stop watchdog-timer
                    WatchdogTimer.Stop();

                    //Write bInit = false
                    AdsStream dataBytStream = new AdsStream(1);
                    BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
                    dataBytStream.Position = 0;
                    binBytWrite.Write(false);
                    try
                    {
                        tcClient.Write(h_bInit, dataBytStream);
                    }
                    catch (Exception err2)
                    {
                        ErrorOccured("Can't read data from PLC and reinitialize external generator.\n" + err.Message + err2.Message);
                    }

                    // If PLC is sending data, remove last uncomplete data set
                    if (ActStateExtGen == eStateExtGen.Running)
                    {
                        MeasuredData.RemoveAt(MeasuredData.Count - 1);
                    }
                    if (tcClient.IsConnected)
                    {
                        tcClient.Dispose();
                    }
                    // create event
                    ActStateExtGen = eStateExtGen.Error;
                    ChangedState(this, new StateEvent(eStateExtGen.Error));

                    ErrorOccured("Can't read data from PLC\n" + err.Message);
                }
            }
        }

        /// <summary>
        /// Disconnect from PLC
        /// </summary>
        public void Disconnect()
        {

            // Stop watchdog-timer
            WatchdogTimer.Stop();

            //Write bInit = false
            AdsStream dataBytStream = new AdsStream(1);
            BinaryWriter binBytWrite = new BinaryWriter(dataBytStream);
            dataBytStream.Position = 0;
            binBytWrite.Write(false);
            try
            {
                tcClient.DeleteVariableHandle(h_aBufferCommandValue[1]);
                tcClient.DeleteVariableHandle(h_aBufferFeedbackValue[1]);
                tcClient.DeleteVariableHandle(h_aBufferCommandValue[2]);
                tcClient.DeleteVariableHandle(h_aBufferFeedbackValue[2]);
                tcClient.DeleteVariableHandle(h_aBufferCommandValue[0]);
                tcClient.DeleteVariableHandle(h_aBufferFeedbackValue[0]);
                tcClient.DeleteVariableHandle(h_aBufferCurrentValue[0]);
                tcClient.DeleteVariableHandle(h_aBufferCurrentValue[1]);
                tcClient.DeleteVariableHandle(h_aBufferCurrentValue[2]);
                tcClient.DeleteVariableHandle(h_aBufferInputSignal);
                tcClient.DeleteVariableHandle(h_bStart);
                tcClient.DeleteVariableHandle(h_eModeControl);
                tcClient.DeleteVariableHandle(h_nAmplitudeInputSignal);
                tcClient.DeleteVariableHandle(h_nIndexItemBuffer);
                tcClient.DeleteVariableHandle(h_nNumberRepeat);
                tcClient.DeleteVariableHandle(h_nPeriodInputSignal);
                tcClient.DeleteVariableHandle(h_nLengthInputSignal);
                tcClient.DeleteVariableHandle(h_aWriteFullBuffer);
                tcClient.DeleteVariableHandle(h_nAmplAuxMove);
                tcClient.DeleteVariableHandle(h_fFreqAuxMove);
                tcClient.DeleteVariableHandle(h_aWriteFullCurrentBuffer);
                tcClient.DeleteVariableHandle(h_nIndexItemCurrentBuffer);
                tcClient.DeleteVariableHandle(h_nStepResPreTrigger);
                tcClient.DeleteVariableHandle(h_nStepResTime);
                tcClient.DeleteVariableHandle(h_nStepResInitLevel);
                tcClient.DeleteVariableHandle(h_nStepResFinalLevel);
                tcClient.DeleteVariableHandle(h_fCycleTimeTask);
                tcClient.DeleteVariableHandle(h_nAllowPosRange);
                tcClient.DeleteVariableHandle(h_bEnablePosRangCheck);
                tcClient.DeleteVariableHandle(h_nAllowTorqueRangeIdent);
                tcClient.DeleteVariableHandle(h_bEnableTorqueRangCheckIdent);
                tcClient.DeleteVariableHandle(h_nAllowTorqueRangeStRes);
                tcClient.DeleteVariableHandle(h_bEnableTorqueRangCheckStRes);
                tcClient.DeleteVariableHandle(h_nErrorId);
                tcClient.DeleteVariableHandle(h_bWatchdogOut);
                tcClient.DeleteVariableHandle(h_tTimeout);
                tcClient.DeleteVariableHandle(h_bEnableTimeout);

                tcClient.DeleteDeviceNotification(h_aFullBuffer);
                tcClient.DeleteDeviceNotification(h_bIdentDone);
                tcClient.DeleteDeviceNotification(h_bStepResDone);
                tcClient.DeleteDeviceNotification(h_bCycleTimeDone);
                tcClient.DeleteDeviceNotification(h_aFullCurrentBuffer);
                tcClient.DeleteDeviceNotification(h_bError);
                tcClient.DeleteDeviceNotification(h_StatePlc);
                tcClient.DeleteDeviceNotification(h_bWatchdogIn);

                tcClient.Write(h_bInit, dataBytStream);
                tcClient.DeleteVariableHandle(h_bInit);

                tcClient.Dispose();
            }
            catch (Exception err)
            {
                // If PLC is sending data, remove last uncomplete data set
                if (ActStateExtGen == eStateExtGen.Running)
                {
                    MeasuredData.RemoveAt(MeasuredData.Count - 1);
                }
                if (tcClient.IsConnected)
                {
                    tcClient.Dispose();
                }
                // create event - error
                ActStateExtGen = eStateExtGen.Error;
                ChangedState(this, new StateEvent(eStateExtGen.Error));
                throw new Exception("Disconnect from PLC was not corect.\n" + err.Message);
            }

            // If PLC is sending data, remove last uncomplete data set
            if (ActStateExtGen == eStateExtGen.Running)
            {
                MeasuredData.RemoveAt(MeasuredData.Count - 1);
            }

            // create event when disconnect from drive
            ActStateExtGen = eStateExtGen.Offline;
            ChangedState(this, new StateEvent(eStateExtGen.Offline));

        }



        /// <summary>
        /// Load data from file to class
        /// </summary>
        /// <param name="Path"></param>
        public void LoadData(string Path)
        {
            DataProperties iLoadedData = new DataProperties();
            int iVersion;
            Int32[] iCommandValues, iFeedbackValues, iStepCommandValues, iStepFeedbackValues;
            Int16[] iCurrentValues;

            System.Globalization.CultureInfo invC = System.Globalization.CultureInfo.InvariantCulture;

            try
            {
                XmlReader xmlReader = XmlReader.Create(Path);

                // Read Properties
                xmlReader.ReadToFollowing("Properties");
                iVersion = Convert.ToInt32(xmlReader.GetAttribute("Version"), invC);
                iLoadedData.AmsNetId = xmlReader.GetAttribute("AmsNetId");
                iLoadedData.AdsPort = Convert.ToInt32(xmlReader.GetAttribute("AdsPort"), invC);
                iLoadedData.PathExtGen = xmlReader.GetAttribute("PathExtGen");
                iLoadedData.NameAxis = xmlReader.GetAttribute("NameAxis");
                iLoadedData.NameInputSignal = xmlReader.GetAttribute("NameInputSignal");
                iLoadedData.IdInputSignal = Convert.ToInt32(xmlReader.GetAttribute("IdInputSignal"), invC);
                iLoadedData.PeriodInputSignal = Convert.ToDouble(xmlReader.GetAttribute("PeriodInputSignal"), invC);
                iLoadedData.CountRepeat = Convert.ToUInt16(xmlReader.GetAttribute("CountRepeat"));
                iLoadedData.ControlMode = (eControlMode)Convert.ToInt32(xmlReader.GetAttribute("ControlMode"), invC);
                iLoadedData.Units = (eUnits)Convert.ToInt32(xmlReader.GetAttribute("Units"), invC);
                iLoadedData.DateTimeMd = Convert.ToDateTime(xmlReader.GetAttribute("DateTime"), invC);
                string tempString = xmlReader.GetAttribute("IsLinear");
                if (tempString == "True")
                {
                    iLoadedData.IsLinear = true;
                }
                else
                {
                    iLoadedData.IsLinear = false;
                }
                iLoadedData.xScale = Convert.ToDouble(xmlReader.GetAttribute("xScale"), invC);
                iLoadedData.vScale = Convert.ToDouble(xmlReader.GetAttribute("vScale"), invC);
                iLoadedData.FeedConstant = Convert.ToDouble(xmlReader.GetAttribute("FeedConstant"), invC);
                iLoadedData.TaskTime = Convert.ToDouble(xmlReader.GetAttribute("TaskTime"), invC);
                iLoadedData.AmplitudeInputSignal = Convert.ToDouble(xmlReader.GetAttribute("AmplitudeInputSignal"), invC);
                iLoadedData.AmplAuxMove = Convert.ToDouble(xmlReader.GetAttribute("AmplitudeAuxiliaryMoveSignal"), invC);
                iLoadedData.FreqAuxMove = Convert.ToDouble(xmlReader.GetAttribute("FrequencyAuxiliaryMoveSignal"), invC);
                iLoadedData.StepPreTrigger = Convert.ToDouble(xmlReader.GetAttribute("StepPreTrigger"), invC);
                iLoadedData.StepTime = Convert.ToDouble(xmlReader.GetAttribute("StepTime"), invC);
                iLoadedData.StepInitLevel = Convert.ToDouble(xmlReader.GetAttribute("StepInitLevel"), invC);
                iLoadedData.StepFinalLevel = Convert.ToDouble(xmlReader.GetAttribute("StepFinalLevel"), invC);
                iLoadedData.LengthWindow = Convert.ToInt32(xmlReader.GetAttribute("LengthWindow"), invC);
                iLoadedData.ModeSpectAnalys = (eModeAnalysis)Convert.ToInt32(xmlReader.GetAttribute("ModeSpectAnalys"), invC);
                tempString = xmlReader.GetAttribute("CheckPhaseOverflow");
                if (tempString == "True")
                {
                    iLoadedData.CheckPhaseOverflow = true;
                }
                else
                {
                    iLoadedData.CheckPhaseOverflow = false;
                }
                iLoadedData.OffsetIOdata = Convert.ToInt32(xmlReader.GetAttribute("OffsetIOdata"), invC);
                iLoadedData.AllowedPositionRange = Convert.ToDouble(xmlReader.GetAttribute("AllowedPositionRange"), invC);
                tempString = xmlReader.GetAttribute("EnablePositionRangeCheck");
                if (tempString == "True")
                {
                    iLoadedData.EnablePositionRangeCheck = true;
                }
                else
                {
                    iLoadedData.EnablePositionRangeCheck = false;
                }
                iLoadedData.AllowedTorqueRangeIdent = Convert.ToInt16(xmlReader.GetAttribute("AllowedTorqueRangeIdent"), invC);
                tempString = xmlReader.GetAttribute("EnableTorqueRangeCheckIdent");
                if (tempString == "True")
                {
                    iLoadedData.EnableTorqueRangeCheckIdent = true;
                }
                else
                {
                    iLoadedData.EnableTorqueRangeCheckIdent = false;
                }
                iLoadedData.AllowedTorqueRangeStRes = Convert.ToInt16(xmlReader.GetAttribute("AllowedTorqueRangeStRes"), invC);
                tempString = xmlReader.GetAttribute("EnableTorqueRangeCheckStRes");
                if (tempString == "True")
                {
                    iLoadedData.EnableTorqueRangeCheckStRes = true;
                }
                else
                {
                    iLoadedData.EnableTorqueRangeCheckStRes = false;
                }

                // Read Command values
                xmlReader.ReadToFollowing("CommandValues");
                int CountSamples = Convert.ToInt32(xmlReader.GetAttribute("Count"));
                xmlReader.ReadToFollowing("Data");
                byte[] DataBuf = new byte[CountSamples * 4];
                xmlReader.ReadElementContentAsBase64(DataBuf, 0, CountSamples * 4);
                iCommandValues = new Int32[CountSamples];
                for (int i = 0; i < CountSamples; i++)
                {
                    iCommandValues[i] = BitConverter.ToInt32(DataBuf, i * 4);
                }

                if (iLoadedData.CountRepeat == 0)
                {
                    iLoadedData.LengthInputSignal = 0;
                }
                else
                {
                    iLoadedData.LengthInputSignal = Convert.ToUInt32(CountSamples / (int)(iLoadedData.PeriodInputSignal / iLoadedData.TaskTime) / iLoadedData.CountRepeat);
                }

                // Read Feedback values
                xmlReader.ReadToFollowing("FeedbackValues");
                CountSamples = Convert.ToInt32(xmlReader.GetAttribute("Count"));
                xmlReader.ReadToFollowing("Data");
                xmlReader.ReadElementContentAsBase64(DataBuf, 0, CountSamples * 4);
                iFeedbackValues = new Int32[CountSamples];
                for (int i = 0; i < CountSamples; i++)
                {
                    iFeedbackValues[i] = BitConverter.ToInt32(DataBuf, i * 4);
                }

                // Read step response Command values
                xmlReader.ReadToFollowing("StepResCommandValues");
                CountSamples = Convert.ToInt32(xmlReader.GetAttribute("Count"));
                xmlReader.ReadToFollowing("Data");
                byte[] StepDataBuf = new byte[CountSamples * 4];
                xmlReader.ReadElementContentAsBase64(StepDataBuf, 0, CountSamples * 4);
                iStepCommandValues = new Int32[CountSamples];
                for (int i = 0; i < CountSamples; i++)
                {
                    iStepCommandValues[i] = BitConverter.ToInt32(StepDataBuf, i * 4);
                }

                // Read step response Feedback values
                xmlReader.ReadToFollowing("StepResFeedbackValues");
                CountSamples = Convert.ToInt32(xmlReader.GetAttribute("Count"));
                xmlReader.ReadToFollowing("Data");
                xmlReader.ReadElementContentAsBase64(StepDataBuf, 0, CountSamples * 4);
                iStepFeedbackValues = new Int32[CountSamples];
                for (int i = 0; i < CountSamples; i++)
                {
                    iStepFeedbackValues[i] = BitConverter.ToInt32(StepDataBuf, i * 4);
                }

                // Read Current values
                xmlReader.ReadToFollowing("CurrentValues");
                CountSamples = Convert.ToInt32(xmlReader.GetAttribute("Count"));
                xmlReader.ReadToFollowing("Data");
                byte[] DataBufCurrent = new byte[CountSamples * 2];
                xmlReader.ReadElementContentAsBase64(DataBufCurrent, 0, CountSamples * 2);
                iCurrentValues = new Int16[CountSamples];
                for (int i = 0; i < CountSamples; i++)
                {
                    iCurrentValues[i] = BitConverter.ToInt16(DataBufCurrent, i * 2);
                }

                // Read Notes 
                xmlReader.ReadToFollowing("Notes");
                CountSamples = Convert.ToInt32(xmlReader.GetAttribute("Count"));
                xmlReader.ReadToFollowing("Data");
                byte[] DataNotes = new byte[CountSamples * 2];
                xmlReader.ReadElementContentAsBase64(DataNotes, 0, CountSamples * 2);
                string iNotes = Encoding.Unicode.GetString(DataNotes);

                // Insert data to class MeasuredData
                MeasuredData.Add(new MeasureData(iLoadedData));
                for (int i = 0; i < iCommandValues.Length; i++)
                {
                    MeasuredData.Last().Add(iCommandValues[i], iFeedbackValues[i]);
                }
                for (int i = 0; i < iStepCommandValues.Length; i++)
                {
                    MeasuredData.Last().AddStepResValue(iStepCommandValues[i], iStepFeedbackValues[i]);
                }
                for (int i = 0; i < iCurrentValues.Length; i++)
                {
                    MeasuredData.Last().AddCurrentValue(iCurrentValues[i]);
                }
                MeasuredData.Last().Notes = iNotes;

            }
            catch (Exception e)
            {
                throw new Exception("Wrong file" + e.Message);
            }
        }

        /// <summary>
        /// Get actual state of external generator
        /// </summary>
        public eStateExtGen GetActStateExtGen
        {
            get { return ActStateExtGen; }
        }

        /// <summary>
        /// Reset error state to Offline state
        /// </summary>
        public void ResetErrorState()
        {
            ActStateExtGen = eStateExtGen.Offline;
            ChangedState(this, new StateEvent(eStateExtGen.Offline));
        }

        #endregion

    }


}


