using DevComponents.AdvTree;
using System;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using Trio.UnifiedApi;

class Sample : IDisposable
{
    ITrioConnection _connection;  //trio class library
    AxisType _axisType;
    public driveErrorCode _currentAlarm;  //some ED3L alarms list
    public event EventHandler<driveErrorCode> AxisAlarmValueChanged;   //to get drive Alarm by event
    public Sample(string mcIP)
    {
        _connection = string.Equals(mcIP, "PCMCAT", StringComparison.OrdinalIgnoreCase) ?
                            TrioConnectionFactory.CreateConnectionPCMCAT() :
                      string.Equals(mcIP, "Flex7", StringComparison.OrdinalIgnoreCase) ?
                            TrioConnectionFactory.CreateConnectionFlex7() :
                            TrioConnectionFactory.CreateConnectionTCP(mcIP);
        _connection.ConnectionEvent += ConnectionCallback;
    }
    ~Sample()
    {
        Dispose();
    }

    #region ED3L Alarm definition
    public enum driveErrorCode : int
    {
        None = 0,
        A03_overSpeed = 3,
        A04_overload = 4,
        A12_overCurrent = 18,
        A13_overVoltage = 19,
        A14_underVoltage = 20,
        A15_regenrativeResistanceIsDamaged = 21,
        A20_mainLoopPowerLineIsOutOfPhase = 32,
        A47_encoderBatteryVoltageIsTooLow = 71,
        A50_EncoderIsDisconnected = 80,
        A52_errorInsideTheEncoder = 82,
        A70_DCSynchronizationError = 112,
        A73_EthercatProcessorInternalError = 115
    }
    public driveErrorCode CurrentAlarm   //currentAlarm property
    {
        get { return _currentAlarm; }
        set 
        { 
            if (_currentAlarm != value)
            {
                _currentAlarm = value;
                AxisAlarmValueChanged?.Invoke(this,_currentAlarm);
            }
        }
    }
    public void UpdateAlarmFromDrive()
    {
        //get axis alarm from table(102)
        int idx = int.Parse(readTable(102).ToString());
        if(Enum.IsDefined(typeof(driveErrorCode), idx))
        {
             CurrentAlarm = (driveErrorCode)idx;
        }
        else
        {
            CurrentAlarm = driveErrorCode.None;
        }
    }
    #endregion

    public void Dispose()
    {
        var connection = Interlocked.Exchange(ref _connection, null);
        connection?.Dispose();
    }
    //open connection command
    public void connectExecute()
    {
        var conn = _connection;

        if (conn == null)
            throw new TrioConnectionException(ErrorCode.ConnectionContext);

        /***********************Connect to the MC***********************/

        conn.OpenConnection();

    }

    //check the true connection(stability in port opening)
    public bool isConnectedFLG()
    {
        return _connection.IsConnected;
    }
    /***********************Disconnect from MC***********************/
    public void disconnectMC()
    {
        _connection.CloseConnection();
    }

    /*****************WDOG control*****************/
    //drive run/stop(bb) by master enable
    public void wdgCommand(bool wdog)
    {
        //Set SystemParamter - WDOG
        _connection.SetSystemParameter_WDOG(wdog);
        //Get SystemParameter - WDOG
        bool wdogRd = _connection.GetSystemParameter_WDOG();
        if (wdogRd != wdog)
        {
            MessageBox.Show($"Error active/deactive SystemParameter value - WDOG");
            return;
        }
        Debug.WriteLine($"WDOG = {wdog}");

    }

    /*****************Writing and reading variables(VR)*****************/
    public void writeVR(UInt32 vrIndex, double vrValue)
    {
        //UInt32 vrIndex = 100, tableIndex = 100;
        //double vrValue = 1.25, tableValue = 2.34;

        // Write VR value
        _connection.SetVrValue(vrIndex, vrValue);
        // Read VR value
        double valVr = _connection.GetVrValue(vrIndex);
        if (valVr != vrValue)
        {
            MessageBox.Show($"Error getting VR value");
            return;
        }
        Debug.WriteLine($"WriteVR {vrIndex} Done!");

    }
    public double readVR(UInt32 vrIndex)
    {
        Debug.WriteLine($"ReadVR {vrIndex} Done!");

        // Read VR value
        return _connection.GetVrValue(vrIndex);
    }

    /*****************Writing and reading table values*****************/
    public void writeTable(UInt32 tableIndex, double tableValue)
    {
        // Write TABLE value
        _connection.SetTableValue(tableIndex, tableValue);
        Debug.WriteLine($"Write {tableIndex} Done! = {tableValue}");
        // Read TABLE value
        double valTable = _connection.GetTableValue(tableIndex);
        if (valTable != tableValue)
        {
            MessageBox.Show($"Error getting TABLE value");
            return;
        }
        Debug.WriteLine($"Write {tableIndex} Done!");

    }
    public double readTable(UInt32 tableIndex)
    {
        Debug.WriteLine($"Read {tableIndex} Done!");
        // Read VR value
        return _connection.GetTableValue(tableIndex);
        
    }

    /*****************get/set axis parameters*****************/
    public void setAxisParam(int index, double units, double speed, double accel, double decel, double fe_Range,
        double fe_Limit, double fs_Limit, double rs_Limit, long drive_fe_Limit )
    {
        _connection.SetAxisBase(index);
        Debug.WriteLine($"axes {index} added");
        _connection.SetAxisParameter_UNITS(index, units);
        _connection.SetAxisParameter_SPEED(index, speed);
        _connection.SetAxisParameter_ACCEL(index, accel);
        _connection.SetAxisParameter_DECEL(index, decel);
        _connection.SetAxisParameter_FE_RANGE(index, fe_Range);
        _connection.SetAxisParameter_FE_LIMIT(index, fe_Limit);
        _connection.SetAxisParameter_FS_LIMIT(index, fs_Limit);
        _connection.SetAxisParameter_RS_LIMIT(index, rs_Limit);
        _connection.SetAxisParameter_DRIVE_FE_LIMIT(index, drive_fe_Limit);
        _connection.SetAxisParameter_SERVO(index, true);
        _connection.SetAxisParameter_AXIS_ENABLE(index, true);
    }
    public (double units,double pos, double speed, double accel, double decel, long drive_fe_Limit) getAxisParam<Tunits,Tposition,Tspeed,Taccel,Tdecel,TdriveFeLimit>(int index)
    {
        double idx1 = _connection.GetAxisParameter_UNITS(index);
        double idx2 = _connection.GetAxisParameter_DPOS(index);
        double idx3 = _connection.GetAxisParameter_SPEED(index);
        double idx4 = _connection.GetAxisParameter_ACCEL(index);
        double idx5 = _connection.GetAxisParameter_DECEL(index);
        long idx6 = _connection.GetAxisParameter_DRIVE_FE_LIMIT(index);
        return (idx1, idx2, idx3, idx4, idx5, idx6);
    }

    /*************************Digital IO*************************/
    //digital output write command
    public void setD_OUT(UInt32 channel, bool state)
    {
        //Set the digital output of channel 0.
        _connection.SetDOut(channel, state);
    }
    //digital output read command
    public bool getD_OUT(UInt32 channel)
    {
        //Get the status of the digital output of the channel 0.
        bool doutRd = _connection.GetDOut(channel);
        return doutRd;
    }
    //digital input read command
    public bool getD_IN(UInt32 channel)
    {
        //Get the status of the the digital input of the channel 0.
        bool dinRd = _connection.GetDIn(channel);
        return (dinRd);
    }

    /*************************Analog IO*************************/
    //analog output write command
    public void setA_OUT(UInt32 channel, int aoutVal)
    {
        //Set the analog output of channel
            _connection.SetAOut(channel, aoutVal);
    }
    //analog output read command
    public int getA_OUT(UInt32 channel)
    {
        //Get the status of the analog output of the channel 0.
        int aoutRd = _connection.GetAOut(channel);
        return aoutRd;
    }
    //analog input read command
    public int getA_IN(UInt32 channel)
    {
        //Get the status of the the analog input of the channel 0.
        int ainRd = _connection.GetAIn(channel);
        return (ainRd);
    }

    /***********************Motion commands***********************/
    //some samples of itroconnection library
    public void singleMoveRel(int axisIndex, double pos)
    {
        //Single axis move 
        _connection.MoveRel(pos, axisIndex); // Axis(idx) moves the "pos" distance ( relative position)
        Thread.Sleep(500);
    }
    public void multiMoveRel(Int32[] axes, double[] positions, int axisIndex)
    {
        //Two axes linear interpolation motion( relative position)
        //double[] positions = { 1000, 2000 };
        //Int32[] axes = { 0, 2 };
        _connection.SetMultiAxisBase(axes); // Set the base axes.        
        _connection.MoveRelMulti(positions, axisIndex); // Axis(0) moves the distance 1,000, while axis(2) moves 2,000
        Thread.Sleep(500);
    }
    public void singleMoveAbs(int axisIndex, double pos)
    {
        //Single axis move 
        _connection.MoveAbs(pos, axisIndex); // Axis(idx) moves the "pos" distance ( relative position)
        Thread.Sleep(500);
    }
    public void forwardMove(int axisIndex, int mSecVal)
    {
        //Sets continuous forward movement.
        int mode = 2;
        _connection.Cancel(mode, axisIndex); // Cancels all active and buffered moves
        _connection.Forward(axisIndex); // Axis(0) moves foward        
        Thread.Sleep(mSecVal); // Waiting for 3 seconds.
        mode = 0;
        _connection.Cancel(mode, axisIndex); // Cancel the currently executing move
    }
    public void reverseMove(int axisIndex, int mSecVal)
    {
        //Sets continuous forward movement.
        int mode = 2;
        _connection.Cancel(mode, axisIndex); // Cancels all active and buffered moves
        _connection.Reverse(axisIndex); // Axis(0) moves foward        
        Thread.Sleep(mSecVal); // Waiting for 3 seconds.
        mode = 0;
        _connection.Cancel(mode, axisIndex); // Cancel the currently executing move
    }
    public void multiCirculeMove(Int32[] axes)
    {
        //Two axes circular interpolation motion			
        _connection.SetMultiAxisBase(axes); // Set the base axes.
        _connection.MoveCirc(0, 2000, 0, 1000, 0, 1); // Semicircular trajectory(axis0 and axis2, clockwise direction), finish point (2000,0), centre point (1000,0)
        Thread.Sleep(5000); // waiting for 5 seconds
    }
    public void zeroDefPos(int axisIndex)
    {
      _connection.DefPos(0,axisIndex);
    }

    /*****************drive mode & profile initialize*****************/
    //this added on MC_CONFIG / MC duty
    public void setDriveMode(int axisIndex, int mode)
    {
        _connection.SetAxisParameter_DRIVE_MODE(axisIndex, mode);
        Thread.Sleep(50); // waiting for 50 mseconds
    }
    public void setDriveProfile(int axisIndex, int profile)
    {
        _connection.SetAxisParameter_DRIVE_PROFILE(axisIndex, profile);
        Thread.Sleep(50); // waiting for 50 mseconds
    }

    /*****************etherCAT command & status*****************/
    public void ethercatStopCommand(int slot)
    {
        _connection.Ethercat_StopNetwork(slot);
        Thread.Sleep(50); // waiting for 50 mseconds
    }
    public void autoEthercatCommand(int value)
    {
        //OFF = 0
        _connection.SetSystemParameter_AUTO_ETHERCAT(value);
        Thread.Sleep(50); // waiting for 50 mseconds
    }
    public void ethercatStartCommand(int slot, int ms_delay)
    {
        _connection.Ethercat_StartNetwork(slot);
        //Thread.Sleep(ms_delay); // waiting for 6 seconds
    }
    public string getEthercatState(int slot)
    {
        string st = _connection.Ethercat_GetState(slot).ToString();
        return st;
    }

    /*****************alarms command & status*****************/
    public void resetAxisAlarm(int axisIndex)
    {
        _connection.Ethercat_SendResetSequence(axisIndex,0);
        Thread.Sleep(50); // waiting for 50 mseconds
    }
    public void datumHoming(int axisIndex)
    {
        _connection.Datum(axisIndex);
        Thread.Sleep(50); // waiting for 50 mseconds

    }
    public void secondAxisCfg(int slot,int axisIndex)
    {
        _connection.Ethercat_CoWrite_Value(slot,axisIndex,0x6860,0,Co_ObjectType.Unsigned8,0);
        Thread.Sleep(50); // waiting for 50 mseconds
        _connection.Ethercat_CoWrite_Value(slot, axisIndex, 0x6860, 0, Co_ObjectType.Unsigned8, 8);

    }
    public string getECATState(int slot)
    {
        var st = _connection.Ethercat_GetState(slot);
        return st.ToString();
    }
    public string getAxisStatus(int slot)
    {
        var st =_connection.GetAxisParameter_AXISSTATUS(slot);
        return st.ToString();
    }
    /*****************ERROR CODE*****************/
    public string getErrorCode()
    {
        return null;
        
    }
    void ConnectionCallback(EventType event_type, long int_value, string str_value)
    {
        switch (event_type)
        {
            case EventType.Error:
                MessageBox.Show($"Error {int_value} occurred: {str_value}");
                break;

            case EventType.Message:
                MessageBox.Show($"Msg: {str_value}");
                break;
        }
    }
}


