using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.ComponentModel;

namespace FLY.ModbusModule
{
    public class ServerTCP:INotifyPropertyChanged
    {
        PLCRegister mPLCRegs;
        TCPManager.TCPListen mTCPListen;
        DateTime lastcommdt= DateTime.MinValue;
        public DateTime LastCommDT 
        {
            get { return lastcommdt; }
            set {
                if (lastcommdt != value)
                {
                    lastcommdt = value;
                    NotifyPropertyChanged("LastCommDT");
                }
            }
        }

        public ServerTCP(PLCRegister plcRegs):this(plcRegs, 502)
        {

        }
        public ServerTCP(PLCRegister plcRegs, UInt16 port)
        {
            mPLCRegs = plcRegs;
            mTCPListen = new TCPManager.TCPListen(new IPEndPoint(IPAddress.Any, port), ParsePacketInServer);
            mTCPListen.Enable = true;
        }
        /// <summary>
        /// 从异端接收的数据,解释数据
        /// </summary>
        /// <param name="packet"></param>
        /// <param name="conn"></param>
        /// <returns></returns>
        int ParsePacketInServer(TCPManager.TCPConn conn, byte[] inBuf)
        {
            //格式——————————————                
            //MBAP
            //域                         长度          描述
            //事务标识符         2byte           MODBUS请求/响应的识别码    
            //协议标识符         2byte           0=MODBUS协议
            //——长度—         2byte           以下字节的数量
            //单元标识符         1byte           串行链路或其它总线过来的识别码
            

            //PDU
            //功能码——         1byte
            //数据                    nbyte
            int startIndex=0;
            do
            {
                Pack_Proto p = new Pack_Proto();
                int rlen;
                if (!p.TryParse(inBuf, startIndex, out rlen))
                {
                    //处理失败
                    return startIndex;
                }

                ParsePacket(conn, p);

                startIndex += rlen;

            } while (startIndex < inBuf.Length);
            return startIndex;
        }
        void ParsePacket(TCPManager.TCPConn conn, Pack_Proto p) 
        {
            switch (p.func)
            {
                case 0x03:		// read holding registers
                    Do_03(conn,p);
                    break;
                case 0x01:		// read coils
                    Do_01(conn, p);
                    break;
                case 0x10:		// write multiple registers
                    Do_10(conn, p);
                    break;
                case 0x06:		// Write single register
                    Do_06(conn, p);
                    break;
                case 0x05:		// Write single coil
                    Do_05(conn, p);
                    break;
                case 0x0f:		// Write mutiple coils
                    Do_0f(conn, p);
                    break;
                default:
                    Do_Exception(conn, p, 0x01);
                    break;
            }
            LastCommDT = DateTime.Now;
        }

        #region 处理
        /// <summary>
        /// 异常处理
        /// </summary>
        /// <param name="ptran"></param>
        /// <param name="e_code"></param>
        void Do_Exception(TCPManager.TCPConn conn, Pack_Proto p, byte e_code)
        {
            byte func = p.func;
            Misc.MyBase.SIGNBIT( ref func, 7);
            conn.SendPacket(
                new Pack_Proto()
                {
                    tranid = p.tranid,
                    unitid = p.unitid,
                    func = func,
                    buf = new byte[1] { e_code }
                }.ToBytes());
        }
        #region 功能
        /// <summary>
        /// 读多个 COIL
        /// </summary>
        /// <param name="ptran"></param>
        void Do_01(TCPManager.TCPConn conn, Pack_Proto p)
        {
            int index = 0;
            //开始地址
            UInt16 start = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;
            //数量
            UInt16 num = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;

            if ((num < 0x01) || (num > 0x07D0))
            {
                Do_Exception(conn, p, 0x03);
                return;
            }
            if (start + num > mPLCRegs.CoilsCount)
            {
                Do_Exception(conn, p, 0x02);
                return;
            }
            bool[] value = new bool[num];

            mPLCRegs.GetValue(start, value);


            List<byte> data = new List<byte>();
            data.Add((byte)(Math.Ceiling(num / 8.0)));//返回的字节数

            for (int i = 0,j=0; i < num; i++)
            {
                if (j == 0)
                    data.Add(0);

                if (value[i])
                {
                    data[data.Count - 1] = (byte)(data[data.Count - 1] | Misc.MyBase.BIT(i % 8));
                }
                j++;
                if (j == 8)
                {
                    j = 0;
                }
            }

            conn.SendPacket(
                new Pack_Proto(){
                    tranid = p.tranid,
                    unitid = p.unitid,
                    func = p.func,
                    buf = data.ToArray()}.ToBytes());
            
        }

        /// <summary>
        /// 读多个REG
        /// </summary>
        /// <param name="ptran"></param>
        void Do_03(TCPManager.TCPConn conn, Pack_Proto p)
        {

            int index = 0;
            //开始地址
            UInt16 start = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;
            //数量
            UInt16 num = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;





            if ((num < 0x01) || (num > 0x007D))
            {
                Do_Exception(conn, p, 0x03);
                return;
            }
            if (start + num > mPLCRegs.RegsCount)
            {
                Do_Exception(conn, p, 0x02);
                return;
            }


            UInt16[] value = new UInt16[num];
            mPLCRegs.GetValue(start, value);


            List<byte> data = new List<byte>();
            data.Add((byte)(num * 2));
            for (int i = 0; i < num; i++)
            {
                data.AddRange(value[i].GetBytes_Big_endian());
            }

            conn.SendPacket(
                new Pack_Proto()
                {
                    tranid = p.tranid,
                    unitid = p.unitid,
                    func = p.func,
                    buf = data.ToArray()
              }.ToBytes());
        }

        /// <summary>
        /// 写单个coil
        /// </summary>
        /// <param name="ptran"></param>
        void Do_05(TCPManager.TCPConn conn, Pack_Proto p)
        {
            int index = 0;
            //开始地址
            UInt16 start = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;
            //数量
            UInt16 value = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;


            if ((value != 0xff00) && (value != 0x0000))
            {
                Do_Exception(conn, p, 0x03);
                return;
            }
            if (start >= mPLCRegs.CoilsCount)
            {
                Do_Exception(conn, p, 0x02);
                return;
            }




            bool[] v = new bool[1];

            if (value == 0xff00)
                v[0] = true;
            else
                v[0] = false;
            mPLCRegs.SetValue(start, v);

            conn.SendPacket(p.ToBytes());
        }
        /// <summary>
        /// 写单个REG
        /// </summary>
        /// <param name="ptran"></param>
        void Do_06(TCPManager.TCPConn conn, Pack_Proto p)
        {

            int index = 0;
            //开始地址
            UInt16 start = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;
            //数量
            UInt16 value = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;



            if (start >= mPLCRegs.RegsCount)
                Do_Exception(conn, p, 0x02);

            UInt16[] v = new UInt16[1];
            v[0] = value;
            mPLCRegs.SetValue(start, v);

            conn.SendPacket(p.ToBytes());
        }
        /// <summary>
        /// 写多个coil
        /// </summary>
        /// <param name="ptran"></param>
        void Do_0f(TCPManager.TCPConn conn, Pack_Proto p)
        {
            int index = 0;
            //开始地址
            UInt16 start = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;
            //数量
            UInt16 num = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;

            if ((num < 0x01) || (num > 0x007D))
            {
                Do_Exception(conn, p, 0x03);
                return;
            }
            if (start + num > mPLCRegs.RegsCount)
            {
                Do_Exception(conn, p, 0x02);
                return;
            }

            byte byte_cnt = p.buf[index];
            index++;

            bool[] v = new bool[num];
            for (int i = 0,j=0; i < num; i++)
            {
                if (Misc.MyBase.CHECKBIT(p.buf[index],j))
                    v[i] = true;
                else
                    v[i] = false;
                j++;
                if (j == 8)
                {
                    j = 0;
                    index++;
                }
            }
            mPLCRegs.SetValue(start, v);

            List<byte> data = new List<byte>();
            data.AddRange(start.GetBytes_Big_endian());
            data.AddRange(num.GetBytes_Big_endian());

            conn.SendPacket(
                new Pack_Proto()
                {
                    tranid = p.tranid,
                    unitid = p.unitid,
                    func = p.func,
                    buf = data.ToArray()
                }.ToBytes());
        }
        /// <summary>
        /// 写多个REG
        /// </summary>
        /// <param name="ptran"></param>
        void Do_10(TCPManager.TCPConn conn, Pack_Proto p)
        {
            int index = 0;
            //开始地址
            UInt16 start = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;
            //数量
            UInt16 num = COMMON.ToUInt16_Big_endian(p.buf, index);
            index += 2;
            byte byte_cnt = p.buf[index];
            index++;

            if ((num < 0x01) || (num > 0x007D))
            {
                Do_Exception(conn, p, 0x03);
                return;
            }
            if (start + num > mPLCRegs.RegsCount)
            {
                Do_Exception(conn, p, 0x02);
                return;
            }


            UInt16[] v = new UInt16[num];
            for (int i = 0; i < num; i++)
            {
                v[i] = COMMON.ToUInt16_Big_endian(p.buf, index);
                index += 2;
            }
            mPLCRegs.SetValue(start, v);

            List<byte> data = new List<byte>();
            data.AddRange(start.GetBytes_Big_endian());
            data.AddRange(num.GetBytes_Big_endian());

            conn.SendPacket(
                new Pack_Proto()
                {
                    tranid = p.tranid,
                    unitid = p.unitid,
                    func = p.func,
                    buf = data.ToArray()
                }.ToBytes());
        }
        #endregion
        #endregion

        protected void NotifyPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}