using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 欧姆龙EIP调试.EIP
{
    /// <summary>
    /// CIP通信工具类
    /// </summary>
    public class CIPHelper
    {
        /// <summary>
        /// IP地址
        /// </summary>
        public string IpAddress { get; set; }
        /// <summary>
        /// 通信端口号
        /// </summary>
        public int Port { get; set; }

        public bool IsConnected
        {
            get
            {
                if (client == null)
                    return false;
                return client.Connected;
            }
        }


        private Socket client;
        public int SessionHandle = 0;

        /// <summary>
        /// 默认无参构造
        /// </summary>
        public CIPHelper()
        {

        }



        /// <summary>
        /// 构造方法，传递通信IP与端口
        /// </summary>
        public CIPHelper(string ip, int port)
        {
            this.IpAddress = ip;
            this.Port = port;
        }

        /// <summary>
        /// 建立连接
        /// </summary>
        public void Connect()
        {
            //连接
            try
            {
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                client.Connect(new IPEndPoint(IPAddress.Parse(IpAddress), Port));
            }
            catch (Exception ex)
            {

            }
        }


        /// <summary>
        /// 注册会话
        /// </summary>
        public bool RegisterSession()
        {
            //注册会话 
            if (client != null && client.Connected)
            {
                byte[] registerSessionBytes = { (byte)0x65, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 };
                client.Send(registerSessionBytes);
                Thread.Sleep(20);
                byte[] rev = new byte[1024];
                try
                {
                    int length = client.Receive(rev);
                    byte[] data = new byte[length];
                    Array.Copy(rev, 0, data, 0, length);
                    if (data[0] == 0x65)
                        return GetSessionHandle(data);
                }
                catch
                {
                    return false;
                }
            }
            return false;
        }

        /// <summary>
        /// 读取标签值
        /// </summary>
        /// <param name="tag"></param>
        /// <returns></returns>
        public object ReadTag(string tag)
        {
            if (client == null)
                return null;
            if (!client.Connected)
                return null;
            if (SessionHandle == 0)
                return null;
            byte[] d = GetReadTagBytes(tag);
            client.Send(d);
            Thread.Sleep(20);
            byte[] rev = new byte[1024];
            try
            {
                int length = client.Receive(rev);
                byte[] data = new byte[length];
                Array.Copy(rev, 0, data, 0, length);
                if (data[0] == 0x6f)
                    return ReadTagVal(data);
                return null;
            }
            catch
            {
                return null;
            }
        }

        public bool WriteTag(string tag, object value)
        {
            if (client == null)
                return false;
            if (!client.Connected)
                return false;
            if (SessionHandle == 0)
                return false;
            byte[] d = GetWriteTagBytes(tag, value);
            client.Send(d);
            Thread.Sleep(20);
            byte[] rev = new byte[1024];
            try
            {
                int length = client.Receive(rev);
                byte[] data = new byte[length];
                Array.Copy(rev, 0, data, 0, length);
                if (data[0] == 0x6f)
                    return WriteTagVal(data);
                return false;
            }
            catch
            {
                return false;
            }
        }



        /// <summary>
        /// 注销会话
        /// </summary>
        /// <returns></returns>
        public bool UnRegisterSession()
        {
            if (client == null)
                return false;
            if (!client.Connected)
                return false;
            if (SessionHandle == 0)
                return false;
            List<byte> UnregisterSession = new List<byte>();
            UnregisterSession.AddRange(new List<byte> { (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x00 });
            UnregisterSession.AddRange(BitConverter.GetBytes(SessionHandle));
            UnregisterSession.AddRange(new List<byte> { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 });
            try
            {
                client.Send(UnregisterSession.ToArray());
                return true;
            }
            catch
            {
                return false;
            }
        }



        /// <summary>
        /// 断开连接
        /// </summary>
        public void DisConnect()
        {

            if (client == null)
                return;
            //断开 
            try
            {
                if (IsConnected)
                {
                    client.Close();
                }
                client = null;
                SessionHandle = 0;
            }
            catch (Exception ex)
            {
                client = null;
                SessionHandle = 0;
            }
        }


        #region Private

        /// <summary>
        /// 解析写标签数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private bool WriteTagVal(byte[] data)
        {
            int byte_index = 0;
            short var1 = BitConverter.ToInt16(data, 0);//命令码
            short var2 = BitConverter.ToInt16(data, 2);//去除Hearder后的长度  
            if ((var2 + 24) != data.Length)//24是Hearder封装头的长度   封装头长度+后面报文长度 应该等于总长度 否则报文错误     
            {
                return false;
            }
            int sessionHandle = BitConverter.ToInt32(data, 4);
            if (sessionHandle != this.SessionHandle)
            {
                //如果会话句柄不一致 返回
                return false;
            }
            int state = BitConverter.ToInt32(data, 8);
            if (state != 0)
            {
                //会话状态不正确 
                return false;
            }
            //00 00 00 00 01 00 02 00 00 00 00 00 B2 00 08 00 CC 00 00 00 C1 00 00 00
            //发送方描述
            byte_index = 12; //8
            //选项 4字节 
            byte_index = 20;
            //接口句柄 4字节 
            byte_index = 24;
            //超时 2字节 
            byte_index = 28;
            //项数 2字节
            byte_index = 30;
            //连接的地址项 2字节 
            byte_index = 32;
            //连接地址项长度 2字节 
            byte_index = 34;
            //未连接数据项 2字节 
            byte_index = 36;
            //连接长度 2字节
            byte_index = 38;
            //数据开始
            byte_index = 40;
            for (int i = byte_index; i < data.Length; i++)
            {
                if (data[i] != 0xCD)
                    continue;
                //填充字节  1字节  
                if (BitConverter.ToInt16(data, i + 2) != 0x00)
                {
                    return true;
                }
                return false;
            }
            return false;
        }

        /// <summary>
        /// 解析读标签数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private object ReadTagVal(byte[] data)
        {
            int byte_index = 0;
            short var1 = BitConverter.ToInt16(data, 0);//命令码
            short var2 = BitConverter.ToInt16(data, 2);//去除Hearder后的长度  
            if ((var2 + 24) != data.Length)//24是Hearder封装头的长度   封装头长度+后面报文长度 应该等于总长度 否则报文错误     
            {
                return null;
            }
            int sessionHandle = BitConverter.ToInt32(data, 4);
            if (sessionHandle != this.SessionHandle)
            {
                //如果会话句柄不一致 返回
                return null;
            }
            int state = BitConverter.ToInt32(data, 8);
            if (state != 0)
            {
                //会话状态不正确 
                return null;
            }
            //发送方描述
            byte_index = 12; //8
            //选项 4字节 
            byte_index = 20;
            //接口句柄 4字节 
            byte_index = 24;
            //超时 2字节 
            byte_index = 28;
            //项数 2字节
            byte_index = 30;
            //连接的地址项 2字节 
            byte_index = 32;
            //连接地址项长度 2字节 
            byte_index = 34;
            //未连接数据项 2字节 
            byte_index = 36;
            //连接长度 2字节
            byte_index = 38;
            //数据开始
            byte_index = 40;
            for (int i = byte_index; i < data.Length; i++)
            {
                if (data[i] != 0xCC)
                    continue;
                //服务标识  0xCC
                //填充字节  1字节  
                if (BitConverter.ToInt16(data, i + 2) != 0x00) //   data[var4] != 0x00 || data[var4 + 1] != 0x00)
                {
                    return null;
                }
                //数据类型
                short dataType = BitConverter.ToInt16(data, i + 4);
                if (dataType == 0x00C1)//BOOL
                {
                    bool value = Convert.ToBoolean(((short)data[i + 6] & 0x00ff) | ((short)data[i + 7] << 8));
                    return value;
                }
                if (dataType == 0x00D1)//byte
                {
                    byte value = data[i + 6];
                    return value;
                }
                if (dataType == 0x00C3)//int
                {
                    short value = BitConverter.ToInt16(data, i + 6);//    ((short)data[i+6] & 0x00ff) | ((short)data[i+7] << 8);
                    return value;
                }
                if (dataType == 0x00C4)//long型 
                {
                    int value = BitConverter.ToInt32(data, i + 6);
                    return value;
                }
                if (dataType == 0x00CA)//float 
                {
                    float value = BitConverter.ToSingle(data, i + 6);
                    return value;
                }
            }
            return null;
        }


        /// <summary>
        /// 写标签报文数据
        /// </summary>
        /// <param name="tag"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private byte[] GetWriteTagBytes(string tag, object value)
        {
            byte[] TagStringToAscii;
            byte[] Var4 = Encoding.Default.GetBytes(tag);
            if (Var4.Length % 2 == 0)
            {
                TagStringToAscii = new byte[Var4.Length];
                for (int i = 0; i < Var4.Length; i++)
                {
                    TagStringToAscii[i] = Var4[i];
                }
            }
            else
            {
                TagStringToAscii = new byte[Var4.Length + 1];
                for (int i = 0; i < Var4.Length; i++)
                {
                    TagStringToAscii[i] = Var4[i];
                }
                TagStringToAscii[Var4.Length] = 0x00;
            }


            //CIP协议数据  =================================================
            List<byte> Cip_Msg = new List<byte>();
            Cip_Msg.Add((byte)0x4D);//服务标识
            Cip_Msg.Add((byte)((TagStringToAscii.Length + 2) / 2));//CIP长度多少字  从标识开始 大读取标签长度结束 
            Cip_Msg.Add((byte)0x91);//固定
            Cip_Msg.Add((byte)TagStringToAscii.Length);//PLC标签长度 多少个字节
            Cip_Msg.AddRange(TagStringToAscii);//添加标签 
            //根据类型
            if (value is bool)
            {
                Cip_Msg.AddRange(new byte[] { 0xC1, 0x00 });
                if ((bool)value)
                {
                    Cip_Msg.AddRange(new byte[] { 0x01, 0x00 });
                }
                else
                {
                    Cip_Msg.AddRange(new byte[] { 0x00, 0x00 });
                }
            }
            else if (value is byte)
            {
                Cip_Msg.AddRange(new byte[] { 0xD1, 0x00 });
                Cip_Msg.Add((byte)value);
            }
            else if (value is short)
            {
                Cip_Msg.AddRange(new byte[] { 0xC3, 0x00 });
                Cip_Msg.AddRange(BitConverter.GetBytes((short)value));
            }
            else if (value is int)
            {
                Cip_Msg.AddRange(new byte[] { 0xC4, 0x00 });
                Cip_Msg.AddRange(BitConverter.GetBytes((int)value));
            }
            else if (value is float)
            {
                Cip_Msg.AddRange(new byte[] { 0xCA, 0x00 });
                Cip_Msg.AddRange(BitConverter.GetBytes((float)value));
            }
            List<byte> Slot = new List<byte> { (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00 }; //槽号
            List<byte> Cip_Msg0 = new List<byte>();
            Cip_Msg0.Add((byte)0x52);//命令
            Cip_Msg0.Add((byte)0X02);//请求路径长度
            Cip_Msg0.AddRange(new byte[] { 0x20, 0x06, 0x24, 0x01 });//默认请求路径 
            Cip_Msg0.AddRange(new byte[] { 0x0A, 0xF0 });//默认超时 
            Cip_Msg0.AddRange(BitConverter.GetBytes((short)Cip_Msg.Count)); //后面报文长度 不包含PLC槽号 
            Cip_Msg0.AddRange(Cip_Msg);//添加CIP报文 

            List<byte> C_S_D = new List<byte>();
            C_S_D.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//接口句柄 CIP
            C_S_D.AddRange(new byte[] { 0x01, 0x00 });//超时时间 默认 
            C_S_D.AddRange(new byte[] { 0x02, 0x00 });//项数 默认 
            C_S_D.AddRange(new byte[] { 0x00, 0x00 });//空地址项 默认 
            C_S_D.AddRange(new byte[] { 0x00, 0x00 });//空地址长度  默认
            C_S_D.AddRange(new byte[] { 0xB2, 0x00 });//未连接项 默认
            C_S_D.AddRange(BitConverter.GetBytes((short)(Cip_Msg0.Count + Slot.Count)));//CIP报文包的长度 包括槽号  


            List<byte> Header = new List<byte>();
            Header.AddRange(new byte[] { 0x6F, 0x00 });//命令码 
            Header.AddRange(BitConverter.GetBytes((short)(Cip_Msg0.Count + C_S_D.Count + Slot.Count)));//后面报文的长度 特定命令数据 和 CIP报文长度  和PLC槽号
            Header.AddRange(BitConverter.GetBytes(SessionHandle));//添加会话句柄 
            Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//添加状态 
            Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); //发送方描述
            Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//选项默认



            List<byte> EtherNet_IP_CIP_MSG = new List<byte>();//单标签读取完整报文 
            EtherNet_IP_CIP_MSG.AddRange(Header);//添加Header 封装头
            EtherNet_IP_CIP_MSG.AddRange(C_S_D);//添加特定命令数据
            EtherNet_IP_CIP_MSG.AddRange(Cip_Msg0);//添加CIP报文
            EtherNet_IP_CIP_MSG.AddRange(Slot);//添加槽号 
            return EtherNet_IP_CIP_MSG.ToArray();

        }
        /// <summary>
        /// 读标签报文数据
        /// </summary>
        /// <param name="tag"></param>
        /// <returns></returns>
        private byte[] GetReadTagBytes(string tag)
        {
            byte[] TagStringToAscii;
            byte[] Var4 = Encoding.Default.GetBytes(tag);
            if (Var4.Length % 2 == 0)
            {
                TagStringToAscii = new byte[Var4.Length];
                for (int i = 0; i < Var4.Length; i++)
                {
                    TagStringToAscii[i] = Var4[i];
                }
            }
            else
            {
                TagStringToAscii = new byte[Var4.Length + 1];
                for (int i = 0; i < Var4.Length; i++)
                {
                    TagStringToAscii[i] = Var4[i];
                }
                TagStringToAscii[Var4.Length] = 0x00;
            }


            //CIP协议数据  =================================================
            List<byte> Cip_Msg = new List<byte>();
            Cip_Msg.Add((byte)0x4C);//服务标识
            Cip_Msg.Add((byte)((TagStringToAscii.Length + 2) / 2));//CIP长度多少字  从标识开始 大读取标签长度结束 
            Cip_Msg.Add((byte)0x91);//固定
            Cip_Msg.Add((byte)TagStringToAscii.Length);//PLC标签长度 多少个字节
            Cip_Msg.AddRange(TagStringToAscii);//添加标签 
            Cip_Msg.Add((byte)0x01);
            Cip_Msg.Add((byte)0x00); // 读取长度

            List<byte> Slot = new List<byte> { (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00 }; //槽号

            List<byte> Cip_Msg0 = new List<byte>();
            Cip_Msg0.Add((byte)0x52);//命令
            Cip_Msg0.Add((byte)0X02);//请求路径长度
            Cip_Msg0.AddRange(new byte[] { 0x20, 0x06, 0x24, 0x01 });//默认请求路径 
            Cip_Msg0.AddRange(new byte[] { 0x0A, 0xF0 });//默认超时 
            Cip_Msg0.AddRange(BitConverter.GetBytes((short)Cip_Msg.Count)); //后面报文长度 不包含PLC槽号 
            Cip_Msg0.AddRange(Cip_Msg);//添加CIP报文 

            List<byte> C_S_D = new List<byte>();
            C_S_D.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//接口句柄 CIP
            C_S_D.AddRange(new byte[] { 0x01, 0x00 });//超时时间 默认 
            C_S_D.AddRange(new byte[] { 0x02, 0x00 });//项数 默认 
            C_S_D.AddRange(new byte[] { 0x00, 0x00 });//空地址项 默认 
            C_S_D.AddRange(new byte[] { 0x00, 0x00 });//空地址长度  默认
            C_S_D.AddRange(new byte[] { 0xB2, 0x00 });//未连接项 默认
            C_S_D.AddRange(BitConverter.GetBytes((short)(Cip_Msg0.Count + Slot.Count)));//CIP报文包的长度 包括槽号  


            List<byte> Header = new List<byte>();
            Header.Add((byte)0x6F); Header.Add((byte)0x00);//命令码 
            Header.AddRange(BitConverter.GetBytes((short)(Cip_Msg0.Count + C_S_D.Count + Slot.Count)));//后面报文的长度 特定命令数据 和 CIP报文长度  和PLC槽号
            Header.AddRange(BitConverter.GetBytes(SessionHandle));//添加会话句柄 
            Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//添加状态 
            Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); //发送方描述
            Header.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00 });//选项默认



            List<byte> EtherNet_IP_CIP_MSG = new List<byte>();//单标签读取完整报文 
            EtherNet_IP_CIP_MSG.AddRange(Header);//添加Header 封装头
            EtherNet_IP_CIP_MSG.AddRange(C_S_D);//添加特定命令数据
            EtherNet_IP_CIP_MSG.AddRange(Cip_Msg0);//添加CIP报文
            EtherNet_IP_CIP_MSG.AddRange(Slot);//添加槽号 
            return EtherNet_IP_CIP_MSG.ToArray();
        }


        /// <summary>
        /// 注册会话结果解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private bool GetSessionHandle(byte[] data)
        {
            //short sessionLength = BitConverter.ToInt16(data, 2);//  (Convert.ToInt16(data[3]) * 256) | Convert.ToInt16(data[2]);
            //byte[] var1 = new byte[4];
            //for (int i = 0; i < 4; i++)
            //{
            //    var1[i] = data[sessionLength + 4 + i];
            //}
            int state = BitConverter.ToInt32(data, 8);
            if (state == 0)//判断报文状态 
            {
                SessionHandle = BitConverter.ToInt32(data, 4);
                //SessionHandle = new byte[sessionLength];
                //for (int i = 0; i < sessionLength; i++)
                //{
                //    SessionHandle[i] = data[4 + i];
                //}
                return true;
            }
            else
            {
                return false;
            }
        }
        #endregion
    }
}
