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

namespace FLY.Modbus
{
    /// <summary>
    /// 通知命名数据改变是的参数
    /// </summary>
    public class NameDataChangedEventArgs : EventArgs
    {

        /// <summary>
        /// 初始化 NameDataChangedEventArgs 类的新实例。
        /// </summary>
        /// <param name="owner">数据类</param>
        /// <param name="propertyName">已更改的数据名。</param>
        public NameDataChangedEventArgs(object owner, string propertyName)
        {
            this.owner = owner;
            this.propertyName = propertyName;
        }

        object owner;
        string propertyName;

        /// <summary>
        ///  获取数据所属类
        /// </summary>
        public object Owener
        {
            get
            {
                return owner;
            }
        }

        /// <summary>
        /// 获取已更改的数据名名。
        /// </summary>
        public string PropertyName
        {
            get
            {
                return propertyName;
            }
        }
    }

    /// <summary>
    /// 通知Modbus数据改变的参数
    /// </summary>
    public class ModbusDataChangedEventArgs : EventArgs
    {
        PLCAddressArea dataarea;
        int addr;
        int num;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataarea">数据类型</param>
        /// <param name="addr">地址</param>
        /// <param name="num">数量</param>
        public ModbusDataChangedEventArgs(PLCAddressArea dataarea, int addr, int num)
        {
            this.dataarea = dataarea;
            this.addr = addr;
            this.num = num;
        }
        /// <summary>
        /// 数据类型
        /// </summary>
        public virtual PLCAddressArea DataArea
        {
            get { return dataarea; }
        }
        /// <summary>
        /// 地址
        /// </summary>
        public virtual int Addr
        {
            get { return addr; }
        }
        /// <summary>
        /// 数量
        /// </summary>
        public virtual int Num
        {
            get { return num; }
        }
    }

    /// <summary>
    /// NameData 改变事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void NameDataChangedEventHandler(object sender, NameDataChangedEventArgs e);
    /// <summary>
    /// ModbusData 改变事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void ModbusDataChangedEventHandler(object sender, ModbusDataChangedEventArgs e);

    /// <summary>
    /// 命名数据到Modbus数据的映射
    /// </summary>
    public class ModbusMapper : IModbusRegister, INotifyPropertyChanged
    {
        /// <summary>
        /// 一次读取的数据量
        /// </summary>
        const int MaxRegsOfOneRead = 50;
        const int MaxCoilsOfOneRead = 800;

        #region 数据变化通知
        /// <summary>
        /// 命名数据改变事件
        /// </summary>
        public event NameDataChangedEventHandler NameDataChanged;
        protected void NotifyNameDataChanged(object dataclass, string dataname)
        {
            NameDataChanged?.Invoke(this, new NameDataChangedEventArgs(dataclass, dataname));
        }

        public event ModbusDataChangedEventHandler ModbusDataChanged;

        
        public event PropertyChangedEventHandler PropertyChanged;

        
        protected void NotifyModbusDataChanged(PLCAddressArea dataarea, int addr, int num)
        {
            ModbusDataChanged?.Invoke(this, new ModbusDataChangedEventArgs(dataarea, addr, num));
        }

        #endregion

        #region 数据映射
        /// <summary>
        /// 数据映射的类
        /// </summary>
        public class DataToRegs
        {
            /// <summary>
            /// 所属的plc
            /// </summary>
            public ModbusMapper mapper;

            /// <summary>
            /// 对应 PLC寄存器区 coil or register
            /// </summary>
            public PLCAddressArea dataArea;

            /// <summary>
            /// PLC 首地址
            /// </summary>
            public int addr;

            /// <summary>
            /// value 的C#类型 float,int16,int32
            /// </summary>
            public REG_TYPE type;

            /// <summary>
            /// 放大倍数， value * scale 才是属性的值
            /// </summary>
            public double scale;

            /// <summary>
            /// object 
            /// </summary>
            public object owner;

            /// <summary>
            /// object 的 property 名称， 只能是 数字类型都是double， 剩下就是 bool
            /// </summary>
            public string propertyName;

            /// <summary>
            /// property 的值, 没有缩小. 只有 double 与 bool 
            /// </summary>
            public object value;

            /// <summary>
            /// 需要从PLC读取数据更新
            /// </summary>
            public bool isNeedUpdate = false;
            /// <summary>
            /// 
            /// </summary>
            /// <param name="mapper"></param>
            /// <param name="dataArea"></param>
            /// <param name="addr"></param>
            /// <param name="type"></param>
            /// <param name="scale"></param>
            /// <param name="owner"></param>
            /// <param name="propertyname"></param>
            public DataToRegs(ModbusMapper mapper, PLCAddressArea dataArea, int addr, REG_TYPE type, double scale, object owner, string propertyname)
            {
                this.mapper = mapper;
                this.dataArea = dataArea;
                this.addr = addr;
                this.type = type;
                this.scale = scale;
                this.owner = owner;
                this.propertyName = string.Copy(propertyname);
            }

            /// <summary>
            /// C# 类型数据 转 PLC寄存器
            /// </summary>
            /// <param name="value"></param>
            /// <returns></returns>
            public bool[] ToBools(object value)
            {
                bool b = (bool)value;
                return new bool[] { b };
            }
            /// <summary>
            /// C# 类型数据 转 PLC寄存器, 会/scale
            /// </summary>
            /// <param name="value"></param>
            /// <returns></returns>
            public ushort[] ToRegs(object value)
            {
                return RegTypeConverter.ToRegs(value, type, scale);
            }

            public override string ToString()
            {
                return propertyName+ " [" + ((int)dataArea).ToString() + "](" + addr + ")" ;
            }
        }

        /// <summary>
        /// 数据映射表，根据这个表，以后自动更新PLC寄存器，或更新property
        /// </summary>
        public List<DataToRegs> DRmap = new List<DataToRegs>();

        /// <summary>
        /// 查找数据-寄存器对应表
        /// </summary>
        public DataToRegs FindDataMap(object owner, string propertyName)
        {
            return DRmap.Find((dr) => { return (dr.propertyName == propertyName && dr.owner == owner); });
        }

        /// <summary>
        /// 数据映射到寄存器
        /// </summary>
        /// <param name="dataArea">modbus 寄存器类型</param>    
        /// <param name="addr">modbus 寄存器首地址</param> 
        /// <param name="type">plc 数据对应类型</param>
        /// <param name="scale">plc 数据放大倍数</param>
        /// <param name="obj">Property 所属对象</param>    
        /// <param name="propertyname">Property Name</param>   
        /// <returns></returns>
        public DataToRegs MapDataToRegs(PLCAddressArea dataArea, int addr, REG_TYPE type, double scale, object obj, string propertyname)
        {
            DataToRegs dr = FindDataMap(obj, propertyname);
            if (dr == null)
            {
                dr = new DataToRegs(this, dataArea, addr, type, scale, obj, propertyname);
                DRmap.Add(dr);
                DRmap.Sort(delegate (DataToRegs X, DataToRegs Y)
                {
                    int ct = X.dataArea.CompareTo(Y.dataArea);
                    if (ct != 0)
                        return ct;
                    
                    return X.addr.CompareTo(Y.addr);
                });
                return dr;
            }

            return null;
        }

        public ModbusMapper()
        {
            registers = new RegisterData(PLCAddressArea.Register, MaxRegsOfOneRead);
            coils = new CoilData(PLCAddressArea.Coil, MaxCoilsOfOneRead);
            mAreaManager.Add(registers);
            mAreaManager.Add(coils);
        }


        #endregion

        #region Modbus数据
        /// <summary>
        /// 每次向PLC读取寄存器的任务
        /// </summary>
        public class Plan
        {
            /// <summary>
            /// coil or register
            /// </summary>
            public PLCAddressArea area;

            /// <summary>
            /// 地址
            /// </summary>
            public int addr;

            /// <summary>
            /// 数量
            /// </summary>
            public int num;
            /// <summary>
            /// 是否正在读取
            /// </summary>
            public bool isDoing;

            /// <summary>
            /// 
            /// </summary>
            /// <param name="area"></param>
            /// <param name="addr"></param>
            /// <param name="num"></param>
            public Plan(PLCAddressArea area, int addr, int num)
            {
                this.area = area;
                this.addr = addr;
                this.num = num;
            }
        }

        public abstract class PLCRegObj
        {
            /// <summary>
            /// 该寄存器所属的 property
            /// </summary>
            public DataToRegs dr;

            /// <summary>
            /// 最后一个寄存器地址
            /// </summary>
            public int EndAddr;

            public PLCRegObj()
            {
                
            }
            public virtual void Init(DataToRegs dr)
            {
                this.dr = dr;
                EndAddr = dr.addr;
            }
            public override string ToString()
            {
                return dr.ToString();
            }
        }
        /// <summary>
        /// PLC中的寄存器对象
        /// </summary>
        public class Register : PLCRegObj
        {

            /// <summary>
            /// 寄存器当前值
            /// </summary>
            public UInt16[] value;

            /// <summary>
            /// 寄存器值是否改变
            /// </summary>
            public bool changed;


            public override void Init(DataToRegs dr)
            {
                base.Init(dr);
                value = new UInt16[RegSize(dr.type)];
                EndAddr = dr.addr + value.Count() - 1;
            }
        }


        /// <summary>
        /// PLC中的继电器对象， 与 Regs 对应
        /// </summary>
        public class Coil : PLCRegObj
        {
            /// <summary>
            /// Coil的值
            /// </summary>
            public bool value;

        }

        public abstract class AreaManagerBase
        {
            protected List<Plan> plan = new List<Plan>();
            protected int currIndex = 0;
            /// <summary>
            /// 获取下一个plan，返回下一个plan，若已经到了最后一个，则在开始
            /// 下一个之前，返回一个null
            /// </summary>
            /// <returns></returns>
            public Plan GetNextPlan()
            {
                int cnt = plan.Count();
                if (cnt <= 0) return null;
                if (currIndex >= cnt)
                {
                    currIndex = 0;
                    return null;
                }
                Plan p = plan.ElementAt(currIndex);
                currIndex += 1;
                return p;
            }

            /// <summary>
            /// 清除doing状态
            /// </summary>
            public void ClearPlanState()
            {
                foreach (Plan p in plan)
                {
                    p.isDoing = false;
                }
                currIndex = 0;
            }


        }
        public abstract class AreaManager<T> :AreaManagerBase
            where T : PLCRegObj, new()
        {
            /// <summary>
            /// 一个property 对应 N个 PLC寄存器;
            /// 地址必须是从小到大排列，不能有重叠
            /// </summary>
            public List<T> regs = new List<T>();
            protected PLCAddressArea area;
            protected int currIndex = 0;
            protected int maxOfOneRead = 50;
            public AreaManager(PLCAddressArea area, int maxOfOneRead)
            {
                this.area = area;
                this.maxOfOneRead = maxOfOneRead;

            }
            /// <summary>
            /// 根据DataToRegs列表构建数组
            /// </summary>
            public virtual void BuildArray(List<DataToRegs> DRmap)
            {
                List<DataToRegs> drs = DRmap.FindAll((c) =>
                {
                    return c.dataArea == area;
                });
                // 分配寄存器存储空间
                regs.Clear();
                foreach (DataToRegs dr in drs)
                 {
                    T r = new T();
                    r.Init(dr);
                    regs.Add(r);
                }
                regs.Sort((r1, r2) =>
                {
                    if (r1.dr.addr < r2.dr.addr)
                        return -1;
                    else if (r1.dr.addr > r2.dr.addr)
                        return 1;
                    else
                        return 0;
                });
            }
            /// <summary>
            /// 根据当前regs的配置指定读寄存器的计划
            /// </summary>
            public abstract void MakePlan();


        }

        /// <summary>
        /// property 与 PLC继电器 更新操作,与 RegsData 对应
        /// </summary>
        public class CoilData : AreaManager<Coil>
        {
            public CoilData(PLCAddressArea area, int maxOfOneRead) : base(area, maxOfOneRead)
            {

            }

            /// <summary>
            /// 根据当前regs的配置指定读寄存器的计划
            /// </summary>
            public override void MakePlan()
            {
                //this.maxOfOneRead = maxOfOneRead;
                if (regs == null)
                {
                    throw new Exception("还没有执行 BuildArray()");
                }
                plan.Clear();

                int addr = -1;
                int num = 0;
                for (int i = 0; i < regs.Count(); i++)
                {
                    if (!regs[i].dr.isNeedUpdate)
                        continue;

                    if (addr == -1)
                    {
                        addr = regs[i].dr.addr;
                        num = 1;
                    }
                    else
                    {
                        int n = regs[i].dr.addr - addr + 1;
                        if (n <= maxOfOneRead)
                        {
                            num = n;
                        }
                        else
                        {
                            plan.Add(new Plan(area, addr, num));
                            addr = -1;
                            i--;
                        }
                    }
                }
                if (addr != -1)
                {
                    plan.Add(new Plan(area, addr, num));
                    addr = -1;
                }
            }
        }
        /// <summary>
        /// property 与 PLC寄存器 更新操作
        /// </summary>
        public class RegisterData: AreaManager<Register>
        {

            /// <summary>
            /// 
            /// </summary>
            public RegisterData(PLCAddressArea area, int maxOfOneRead):base(area,maxOfOneRead)
            {

            }

            /// <summary>
            /// 根据当前regs的配置指定读寄存器的计划
            /// </summary>
            public override void MakePlan()
            {
                
                if (regs == null)
                {
                    throw new Exception("还没有执行 BuildArray()");
                }
                plan.Clear();

                int addr=-1;
                int num=0;
                for (int i = 0; i < regs.Count(); i++)
                {
                    if (!regs[i].dr.isNeedUpdate)
                        continue;

                    if (addr == -1)
                    {
                        addr = regs[i].dr.addr;
                        num = regs[i].value.Count();
                        if (num > maxOfOneRead)
                        {
                            //一片区域已经大于 最大读取寄存器数，分拆
                            while (num > maxOfOneRead)
                            {
                                plan.Add(new Plan(area, addr, maxOfOneRead));
                                addr += maxOfOneRead;
                                num -= maxOfOneRead;
                            }
                            if (num > 0)
                                plan.Add(new Plan(area, addr, num));
                            addr = -1;
                        }
                    }
                    else
                    {
                        int n = regs[i].dr.addr + regs[i].value.Count() - 1 - addr + 1;

                        if (n <= maxOfOneRead)
                        {
                            num = n;
                        }
                        else
                        {
                            plan.Add(new Plan(area, addr, num));
                            addr = -1;
                            i--;
                        }
                    }

                }

                if (addr != -1)
                {
                    plan.Add(new Plan(area, addr, num));
                    addr = -1;
                }
                
            }
        }


        /// <summary>
        /// PLC寄存器 对应的 property
        /// </summary>
        public RegisterData registers;

        /// <summary>
        /// PLC 继电器 对应的 property
        /// </summary>
        public CoilData coils;

        public List<AreaManagerBase> mAreaManager = new List<AreaManagerBase>();
        #endregion

        #region 数据读写
        /// <summary>
        /// 为命名数据赋值，会改变Modbus的寄存器值，并产生通知
        /// </summary>
        /// <param name="obj">属性所属的对象, 可为null</param>
        /// <param name=propertyName">属性名，不能为空</param>
        /// <param name="val">数据值，不能为空</param>
        /// <returns>赋值的结果</returns>
        public virtual bool SetNameData(object obj, string propertyName, object val)
        {

            DataToRegs dr = FindDataMap(obj, propertyName);
            if (dr == null) return false;
            return SetNameData(dr, val);
        }

        /// <summary>
        /// PC想向PLC写入数据；
        /// 为命名数据赋值，会改变Modbus的寄存器值，并产生通知
        /// </summary>
        /// <param name="dr">数据映射</param>
        /// <param name="val">数据值，不能为空</param>
        /// <returns></returns>
        public virtual bool SetNameData(DataToRegs dr, object val)
        {
            dr.value = val;
            switch (dr.dataArea)
            {
                case PLCAddressArea.Coil:
                    {
                        Coil c = coils.regs.Find(_c => { return _c.dr == dr; });
                        c.value = (bool)val;
                        NotifyModbusDataChanged(PLCAddressArea.Coil, dr.addr, 1);
                    }
                    return true;
                case PLCAddressArea.Register:
                    {
                        ushort[] regs_data = dr.ToRegs(val);
                        Register r = registers.regs.Find(_r => { return _r.dr == dr; });
                        if (regs_data.Count() != r.value.Count())
                        {
                            throw new Exception("DataToRegs转换的类型长度 与 Regs 的长度不一致");
                        }
                        Array.Copy(regs_data, r.value, regs_data.Count());
                        NotifyModbusDataChanged(PLCAddressArea.Register, dr.addr, regs_data.Count());
                    }
                    return true;

            }
            return false;
        }
        /// <summary>
        /// 取命名数据的值
        /// </summary>
        /// <param name="obj">属性所属 的对象</param>
        /// <param name="propertyName">属性名</param>
        /// <returns>属性的值</returns>
        public virtual object GetNameData(object obj, string propertyName)
        {
            DataToRegs dr = FindDataMap(obj, propertyName);
            if (dr == null) return null;
            return dr.value;
        } 

        #region IModbusRegister实现


        /// <summary>
        /// 是从PLC读取数据后；
        /// 设置Modbus的值，会改变相应命名数据的值，并产生通知
        /// </summary>
        /// <param name="dataarea">coil or register</param>
        /// <param name="addr">地址</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public virtual void SetModbusData(PLCAddressArea dataarea, int addr, object value)
        {
            switch (dataarea)
            {
                case PLCAddressArea.Register:
                    {
                        ushort[] vals = (ushort[])value;
                        //找寄存器
                        int endAddr = addr + vals.Count() - 1;

                        RegisterData area_regs = registers;

                        for (int i = 0; i < area_regs.regs.Count(); i++)
                        {
                            if (endAddr < area_regs.regs[i].dr.addr)
                            {
                                //在前面

                                break;
                            }
                            else if (addr <= area_regs.regs[i].EndAddr)//有交集
                            {
                                int addr_act = Math.Max(addr, area_regs.regs[i].dr.addr);
                                int endAddr_act = Math.Min(endAddr, area_regs.regs[i].EndAddr);
                                int num_act = endAddr_act - addr_act + 1;

                                for (int j = 0; j < num_act; j++)
                                {
                                    int idx1 = addr_act - area_regs.regs[i].dr.addr + j;
                                    int idx2 = addr_act - addr + j;
                                    
                                    if (area_regs.regs[i].value[idx1] != vals[idx2])
                                    {
                                        area_regs.regs[i].value[idx1] = vals[idx2];
                                        area_regs.regs[i].changed = true;
                                    }
                                }
                                if (endAddr_act == area_regs.regs[i].EndAddr)
                                {
                                    if (area_regs.regs[i].changed)
                                    {
                                        area_regs.regs[i].changed = false;
                                        DataToRegs dr = area_regs.regs[i].dr;
                                        //触发事件
                                        dr.value = RegTypeConverter.ToObject(
                                            area_regs.regs[i].value, dr.type, dr.scale);
                                        NotifyNameDataChanged(dr.owner, dr.propertyName);
                                    }
                                }
                            }
                            else
                            {
                                //在后面
                            }
                        }
                    }break;
                case PLCAddressArea.Coil:
                    {
                        bool[] vals = (bool[])value;
                        //找寄存器
                        int endAddr = addr + vals.Count() - 1;

                        CoilData area_coils = coils;

                        for (int i = 0; i < area_coils.regs.Count(); i++)
                        {
                            if (endAddr < area_coils.regs[i].dr.addr)
                            {
                                //在前面

                                break;
                            }
                            else if (addr <= area_coils.regs[i].dr.addr)//有交集
                            {
                                int addr_act = area_coils.regs[i].dr.addr;
                                
                                int idx2 = addr_act - addr;

                                if (area_coils.regs[i].value != vals[idx2])
                                {
                                    area_coils.regs[i].value = vals[idx2];
                                    
                                    DataToRegs dr = area_coils.regs[i].dr;
                                    //触发事件
                                    dr.value = area_coils.regs[i].value;
                                    NotifyNameDataChanged(dr.owner, dr.propertyName);
                                }
                            }
                            else
                            {
                                //在后面
                            }
                        }
                    }break;
            }
        }

        /// <summary>
        /// 获取Modbus的数据值
        /// </summary>
        /// <param name="dataarea">0，1，3，4数据模型</param>
        /// <param name="addr">地址</param>
        /// <param name="num">数量</param>
        /// <returns></returns>
        public virtual object GetModbusData(PLCAddressArea dataarea, int addr, int num)
        {
            switch (dataarea)
            {
                case PLCAddressArea.Register:
                    {
                        //多余，不需要实现的
                        //找寄存器
                        int endAddr = addr + num - 1;
                        ushort[] ret = new UInt16[num];
                        RegisterData area_regs = registers;

                        for (int i = 0; i < area_regs.regs.Count(); i++)
                        {
                            if (endAddr < area_regs.regs[i].dr.addr)
                            {
                                //在前面
                                break;
                            }
                            else if (addr <= area_regs.regs[i].EndAddr)//有交集
                            {
                                int addr_act = Math.Max(addr, area_regs.regs[i].dr.addr);
                                int endAddr_act = Math.Min(endAddr, area_regs.regs[i].EndAddr);
                                int num_act = endAddr_act - addr_act + 1;

                                for (int j = 0; j < num_act; j++)
                                {
                                    int idx1 = addr_act - area_regs.regs[i].dr.addr + j;
                                    int idx2 = addr_act - addr + j;
                                    ret[idx2] = area_regs.regs[i].value[idx1];
                                }
                            }
                            else
                            {
                                //在后面
                            }
                        }
                        return ret;
                    }break;
                case PLCAddressArea.Coil:
                    {
                        //多余，不需要实现的
                        //找寄存器
                        int endAddr = addr + num - 1;
                        bool[] ret = new bool[num];
                        CoilData area_regs = coils;

                        for (int i = 0; i < area_regs.regs.Count(); i++)
                        {
                            if (endAddr < area_regs.regs[i].dr.addr)
                            {
                                //在前面
                                break;
                            }
                            else if (addr <= area_regs.regs[i].dr.addr)//有交集
                            {
                                int addr_act = area_regs.regs[i].dr.addr;
                                int idx2 = addr_act - addr;
                                ret[idx2] = area_regs.regs[i].value;
                            }
                            else
                            {
                                //在后面
                            }
                        }
                        return ret;
                    }
                default:
                    throw new Exception("GetModbusData, datamode=" + dataarea + " 不存在的类型");
            }
        }
        #endregion
        #endregion

        #region 类的静态函数
        /// <summary>
        /// C#类型，对应PLC寄存器个数
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static int RegSize(REG_TYPE type)
        {
            switch (type)
            {
                case REG_TYPE.FLOAT:
                    return sizeof(float) / sizeof(Int16);
                case REG_TYPE.UINT16:
                    return sizeof(UInt16) / sizeof(Int16);
                case REG_TYPE.INT32:
                    return sizeof(Int32) / sizeof(Int16);
                case REG_TYPE.INT16:
                    return sizeof(Int16) / sizeof(Int16);
                case REG_TYPE.UINT32:
                    return sizeof(Int32) / sizeof(Int16);
                default:
                    {
                        throw new NotImplementedException("不支持类型 " + type);
                    }
            }
        }


        /// <summary>
        /// string 转 C#类型
        /// </summary>
        /// <param name="typename"></param>
        /// <returns></returns>
        public static REG_TYPE TranslateToREG_TYPE(string typename)
        {
            typename = typename.ToLower();
            if (typename == "int32")
                return REG_TYPE.INT32;
            if (typename == "uint32")
                return REG_TYPE.UINT32;
            if (typename == "bool")
                return REG_TYPE.BOOL;
            if (typename == "int16")
                return REG_TYPE.INT16;
            if (typename == "uint16")
                return REG_TYPE.UINT16;
            if (typename == "float")
                return REG_TYPE.FLOAT;
            return REG_TYPE.Unknown;
        }
        /// <summary>
        /// string 转modbus类型
        /// </summary>
        /// <param name="dm"></param>
        /// <returns></returns>
        public static PLCAddressArea TranslateToPLCAddressArea(string dm)
        {
            if (dm.CompareTo("0") == 0) return PLCAddressArea.Coil;
            if (dm.CompareTo("1") == 0) return PLCAddressArea.Coil;
            if (dm.CompareTo("3") == 0) return PLCAddressArea.Register;
            if (dm.CompareTo("4") == 0) return PLCAddressArea.Register;
            else {
                throw new Exception("{0} 不是合法的 PLCAddressArea类型");
            }
        }
        #endregion

        #region 寄存器读取更新计划
        Dictionary<object, List<DataToRegs>> plans = new Dictionary<object, List<DataToRegs>>();
        /// <summary>
        /// 登记寄存器更新计划
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="propertyName"></param>
        public void PlanAdd(object key, DataToRegs dr)
        {
            if (plans.ContainsKey(key))
            {
                if (!plans[key].Contains(dr))
                    plans[key].Add(dr);
            }
            else
            {
                plans.Add(key, new List<DataToRegs>());
                plans[key].Add(dr);
            }
        }
        /// <summary>
        /// 删除寄存器更新计划
        /// </summary>
        /// <param name="key"></param>
        public void PlanRemove(object key)
        {
            plans.Remove(key);
        }
        /// <summary>
        /// 删除全部寄存器更新计划
        /// </summary>
        public void PlanClear()
        {
            plans.Clear();
        }

        /// <summary>
        /// 需要被更新的 寄存器数量
        /// </summary>
        public int DRNeedUpdateCnt { get; private set; }
        /// <summary>
        /// 总寄存器数量
        /// </summary>
        public int DRCnt { get; private set; }
        /// <summary>
        /// 创建寄存器更新计划
        /// </summary>
        public void PlanMake()
        {
            foreach (DataToRegs dr in DRmap)
            {
                dr.isNeedUpdate = false;
            }

            foreach (KeyValuePair<object, List<DataToRegs>> kv in plans)
            {
                foreach (DataToRegs dr in kv.Value)
                {
                    dr.isNeedUpdate = true;
                }
            }
            DRNeedUpdateCnt = DRmap.Count((dr) => dr.isNeedUpdate);
            DRCnt = DRmap.Count;

            //TODO, 应该提前做！！！！
            registers.BuildArray(DRmap);
            coils.BuildArray(DRmap);

            registers.MakePlan();
            coils.MakePlan();
        }
        #endregion
    }

    /// <summary>
    /// C# 类型枚举
    /// </summary>
    public enum REG_TYPE
    {
        /// <summary>
        /// 出错
        /// </summary>
        Unknown = 0,
        /// <summary>
        /// float
        /// </summary>
        FLOAT = 1,
        /// <summary>
        /// uint16
        /// </summary>
        UINT16 = 2,
        /// <summary>
        /// int16
        /// </summary>
        INT16 = 3,
        /// <summary>
        /// uint32
        /// </summary>
        UINT32 = 4,

        /// <summary>
        /// int32
        /// </summary>
        INT32 = 5,

        /// <summary>
        /// bool
        /// </summary>
        BOOL = 6,
    }

    /// <summary>
    /// PLC寄存器 与 C# 类型数据 转换， 不含bool
    /// </summary>
    public class RegTypeConverter
    {

        /// <summary>
        /// PLC寄存器 转 C# 类型数据
        /// </summary>
        /// <param name="data">PLC 寄存器数据</param>
        /// <param name="type">PLC 数据类型</param>
        /// <param name="scale">PLC数据放大倍数</param>
        /// <returns></returns>
        public static object ToObject(ushort[] data, REG_TYPE type,double scale)
        {
            switch (type)
            {
                //case REG_TYPE.BOOL:
                //    {
                //        return data[0] != 0 ? true : false;
                //    }
                case REG_TYPE.INT16:
                    {
                        float v = (Int16)data[0];
                        v = (float)(v * scale);
                        return v;
                    }
                case REG_TYPE.UINT16:
                    {
                        float v = (UInt16)data[0];
                        v = (float)(v * scale);
                        return v;
                    }
                case REG_TYPE.UINT32:
                    {
                        float v = (UInt32)(data[0] | (data[1] << 16));
                        v = (float)(v * scale);
                        return v;
                    }
                case REG_TYPE.INT32:
                    {
                        float v = (Int32)(data[0] | (data[1] << 16));
                        v = (float)(v * scale);
                        return v;
                    }
                case REG_TYPE.FLOAT:
                    {
                        
                        byte[] dat = new byte[] { (byte)data[0], (byte)(data[0] >> 8), (byte)data[1], (byte)(data[1] >> 8) };
                        float v = BitConverter.ToSingle(dat, 0);

                        if ((v > 1000000) || (v < -1000000))
                            v = 0;

                        v = (float)(v * scale);
                        return v;
                    }
                default:
                    {
                        throw new Exception("ToObject type=" + type + "不支持的类型");
                    }
            }
        }

        /// <summary>
        /// C# 类型数据 转 PLC寄存器
        /// </summary>
        /// <param name="value">C#中数值 只能是 float or bool</param>
        /// <param name="type">PLC 数据类型</param>
        /// <param name="scale">PLC数据放大倍数</param>
        /// <returns></returns>
        public static ushort[] ToRegs(object value, REG_TYPE type, double scale)
        {
            byte[] bs;
            //if (type == REG_TYPE.BOOL)
            //{
            //    bool v = (bool)value;
            //    if (v)
            //    {
            //        return new ushort[1] { 1 };
            //    }
            //    else
            //    {
            //        return new ushort[1] { 0 };
            //    }
            //}
            float f = (float)value;
            f = (float)(f / scale);
            switch (type)
            {
                case REG_TYPE.INT16:
                    {
                        short v = (short)f;
                        return new ushort[] { (ushort)v };
                    }
                case REG_TYPE.UINT16:
                    {
                        ushort v = (ushort)f;
                        return new ushort[] { v };
                    }
                case REG_TYPE.FLOAT:
                    {
                        float v = (float)f;
                        bs = BitConverter.GetBytes(v);
                        return new ushort[] {
                            (ushort)((bs[1] << 8) | bs[0]),
                            (ushort)((bs[3] << 8) | bs[2]) };
                    }break;
                case REG_TYPE.INT32:
                    {
                        Int32 v = (Int32)f;
                        bs = BitConverter.GetBytes(v);
                        return new ushort[] {
                            (ushort)((bs[1] << 8) | bs[0]),
                            (ushort)((bs[3] << 8) | bs[2]) };
                    }
                case REG_TYPE.UINT32:
                    {
                        UInt32 v = (UInt32)f;
                        bs = BitConverter.GetBytes(v);
                        return new ushort[] {
                            (ushort)((bs[1] << 8) | bs[0]),
                            (ushort)((bs[3] << 8) | bs[2]) };
                    }
            }
            return null;
        }
    }


}
