GGavinfix a bug
76a75d69创建于 2020年11月16日历史提交
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using DataService;

namespace ClientDriver
{
    [Description("客户端驱动")]
    public class ClientReader : IDriver//客户端存在对TLV数据的字节序转换问题,需测试
    {
        short _id;
        public short ID
        {
            get
            {
                return _id;
            }
        }

        private int _timeout = 0;
        public int TimeOut
        {
            get { return _timeout; }
            set { _timeout = value; }
        }

        string _name;
        public string Name
        {
            get
            {
                return _name;
            }
        }

        string _ip;
        public string ServerName
        {
            get { return _ip; }
            set { _ip = value; }
        }

        internal Socket tcpSend;
        internal Socket tcpRecive;

        public bool IsClosed
        {
            get
            {
                //return tcpASynCl.Poll(-1, SelectMode.SelectRead);
                return tcpSend == null || tcpRecive == null || !tcpSend.Connected || !tcpRecive.Connected;
            }
        }

        List<IGroup> _grps = new List<IGroup>(1);
        public IEnumerable<IGroup> Groups
        {
            get { return _grps; }
        }

        IDataServer _server;
        public IDataServer Parent
        {
            get { return _server; }
        }

        public ClientReader(IDataServer server, short id, string name)
        {
            _id = id;
            _server = server;
            _name = name;
        }

        public bool Connect()
        {
            int port = 6543;
            lock (this)
            {
                try
                {
                    if (tcpRecive != null)
                    {
                        tcpRecive.Dispose();
                    }
                    if (tcpSend != null)
                    {
                        tcpSend.Dispose();
                    }
                    tcpRecive = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    tcpRecive.Connect(_ip, port);
                    tcpRecive.SendTimeout = _timeout;
                    tcpRecive.ReceiveTimeout = -1;

                    tcpSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    tcpSend.Connect(_ip, port);
                    tcpSend.SendTimeout = _timeout;
                    tcpSend.ReceiveTimeout = _timeout;
                    return true;
                }
                catch (SocketException error)
                {
                    if (OnError != null)
                        OnError(this, new IOErrorEventArgs(error.Message));
                    return false;
                }
            }
        }

        public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false)
        {
            ClientGroup grp = new ClientGroup(id, name, updateRate, active, this);
            _grps.Add(grp);
            return grp;
        }

        public bool RemoveGroup(IGroup grp)
        {
            grp.IsActive = false;
            return _grps.Remove(grp);
        }

        public event IOErrorEventHandler OnError;

        public void Dispose()
        {
            foreach (IGroup grp in _grps)
            {
                grp.Dispose();
            }
            _grps.Clear();
            try
            {
                if (tcpRecive != null)
                {
                    if (tcpRecive.Connected)
                        tcpRecive.Shutdown(SocketShutdown.Both);
                    tcpRecive.Dispose();
                }
                if (tcpSend != null)
                {
                    if (tcpSend.Connected)
                        tcpSend.Shutdown(SocketShutdown.Both);
                    tcpSend.Dispose();
                }
            }
            catch (SocketException err)
            {
                if (OnError != null)
                    OnError(this, new IOErrorEventArgs(err.Message));
            }
        }
    }

    public class ClientGroup : IGroup
    {
        bool _active = false;
        public bool IsActive
        {
            get
            {
                return _active;
            }
            set
            {
                _active = value;
                if (value)
                {
                    if (_plcReader != null && _plcReader.tcpSend != null)
                    {
                        this._tcpRecive = _plcReader.tcpRecive;
                        this._tcpSend = _plcReader.tcpSend;
                        try
                        {
                            _addr = (_tcpSend.RemoteEndPoint as IPEndPoint).Address;
                        }
                        catch { }
                        Thread workItem = new Thread(new ThreadStart(ReciveData));
                        workItem.Priority = ThreadPriority.Highest;
                        workItem.Start();
                        if (_updateRate > 0)
                            ThreadPool.QueueUserWorkItem(new WaitCallback(this.OnUpdate));
                        else
                            Init();
                    }
                }
            }
        }

        protected short _id;
        public short ID
        {
            get
            {
                return _id;
            }
        }

        protected int _updateRate;
        public int UpdateRate
        {
            get
            {
                return _updateRate;
            }
            set
            {
                _updateRate = value;
            }
        }

        protected DeviceAddress _start;
        public DeviceAddress Start
        {
            get
            {
                return _start;
            }
        }

        protected string _name;
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }

        protected float _deadband;
        public float DeadBand
        {
            get
            {
                return _deadband;
            }
            set
            {
                _deadband = value;
            }
        }

        protected ClientReader _plcReader;
        public IDriver Parent
        {
            get
            {
                return _plcReader;
            }
        }

        protected Dictionary<short, ITag> _items;
        public IEnumerable<ITag> Items
        {
            get { return _items.Values; }
        }

        IDataServer _server;
        public IDataServer Server
        {
            get
            {
                return _server;
            }
        }

        Socket _tcpSend, _tcpRecive;
        byte[] tcpBuffer;

        IPAddress _addr;
        public IPAddress RemoteAddress
        {
            get { return _addr; }
        }

        public ClientGroup(short id, string name, int updateRate, bool active, ClientReader plcReader)
        {
            this._id = id;
            this._name = name;
            this._updateRate = updateRate;
            this._active = active;
            this._plcReader = plcReader;
            this._server = plcReader.Parent;
            tcpBuffer = new byte[8192];
        }

        object sendasync = new object();
        private byte[] ReadSingleData(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            if (_tcpSend == null) return null;
            short ID = (short)address.Start;
            byte type = (byte)address.VarType;
            byte len = (byte)(address.DataSize);
            byte[] idbits = BitConverter.GetBytes(ID);
            byte[] write_data = new byte[6] { FCTCOMMAND.fctHead, FCTCOMMAND.fctReadSingle,
                source == DataSource.Cache?(byte)0:(byte)1, idbits[0], idbits[1] ,len};
            byte[] data = new byte[len];
            SocketError error;
            lock (sendasync)
            {
                _tcpSend.Send(write_data, 0, 6, SocketFlags.None, out error);
                int result = _tcpSend.Receive(tcpBuffer, 0, data.Length + 5, SocketFlags.None, out error);
            }
            Array.Copy(tcpBuffer, 5, data, 0, data.Length);
            if (error == SocketError.Success)
                return data;
            else
            {
                return null;
            }
        }

        private int WriteSingleData(DeviceAddress address, byte[] value)
        {
            if (_tcpSend == null) return -1;
            short ID = (short)address.Start;
            byte type = (byte)address.VarType;
            byte[] idbits = BitConverter.GetBytes(ID);
            byte[] write_data = new byte[6] { FCTCOMMAND.fctHead, FCTCOMMAND.fctWriteSingle, 1, idbits[0], idbits[1], (byte)(value.Length) };
            byte[] data = new byte[6 + value.Length];
            write_data.CopyTo(data, 0);
            value.CopyTo(data, 6);
            SocketError error;
            lock (sendasync)
            {
                _tcpSend.Send(data, 0, data.Length, SocketFlags.None, out error);
                _tcpSend.Receive(tcpBuffer, 0, 2, SocketFlags.None, out error);
            }
            if (error == SocketError.Success)
                return tcpBuffer[1];//可在此处返回一个错误号
            else
            {
                return (int)error;
            }
        }

        public void Init()
        {
            if (_items != null && _tcpSend != null)
            {
                int len = 0;
                List<ITag> tags = new List<ITag>();
                foreach (var tag in _items.Values)
                {
                    len += (3 + tag.Address.DataSize);
                    if (len >= tcpBuffer.Length - 10)
                    {
                        len = 0;
                        BatchRead(DataSource.Cache, true, tags.ToArray());
                        tags.Clear();
                    }
                    tags.Add(tag);
                }
                BatchRead(DataSource.Cache, true, tags.ToArray());
            }
        }

        private void ReciveData()
        {
            if (!_active || _plcReader.tcpRecive == null) return;
            byte[] bytes = new byte[ushort.MaxValue];
            byte[] temp = new byte[ushort.MaxValue];
            Storage value = Storage.Empty;
            int start = 0;
            SocketError error;
            int result = 0;
            do
            {
                if (!_tcpRecive.Connected || !_active) return;
                try
                {
                    result = _tcpRecive.Receive(bytes, 0, bytes.Length, SocketFlags.None, out error);
                    if (error == SocketError.Success)
                    {
                        List<HistoryData> historys = new List<HistoryData>(); ;
                        if (start != 0 && temp[0] == FCTCOMMAND.fctHead)
                        {
                            int j = 3;
                            if (start < 0)
                            {
                                Array.Copy(bytes, 0, temp, -start, 5 + start);
                            }
                            short tc = BitConverter.ToInt16(temp, j);//总字节数
                            if (start < 0)
                                start += tc;
                            Array.Copy(bytes, 0, temp, tc - start, start);
                            j += 2;
                            while (j < tc)
                            {
                                short id = BitConverter.ToInt16(temp, j);//标签ID、数据长度、数据值(T,L,V)
                                j += 2;
                                byte length = temp[j++];
                                ITag tag;
                                if (_items.TryGetValue(id, out tag))
                                {
                                    //数据类型
                                    switch (tag.Address.VarType)
                                    {
                                        case DataType.BOOL:
                                            value.Boolean = BitConverter.ToBoolean(temp, j);
                                            break;
                                        case DataType.BYTE:
                                            value.Byte = temp[j];
                                            break;
                                        case DataType.WORD:
                                            value.Word = BitConverter.ToUInt16(temp, j);//需测试
                                            break;
                                        case DataType.SHORT:
                                            value.Int16 = BitConverter.ToInt16(temp, j);//需测试
                                            break;
                                        case DataType.DWORD:
                                            value.DWord = BitConverter.ToUInt32(temp, j);//需测试
                                            break;
                                        case DataType.INT:
                                            value.Int32 = BitConverter.ToInt32(temp, j);//需测试
                                            break;
                                        case DataType.FLOAT:
                                            value.Single = BitConverter.ToSingle(temp, j);
                                            break;
                                        case DataType.STR:
                                            StringTag strTag = tag as StringTag;
                                            if (strTag != null)
                                            {
                                                strTag.String = Encoding.ASCII.GetString(temp, j, length).Trim((char)0);
                                            }
                                            break;
                                        default:
                                            break;
                                    }
                                    j += length;
                                    try
                                    {
                                        DateTime time = DateTime.FromFileTime(BitConverter.ToInt64(temp, j));
                                        //tag.Update(value, time, QUALITIES.QUALITY_GOOD);
                                        //if (historys != null)
                                        historys.Add(new HistoryData(id, QUALITIES.QUALITY_GOOD, value, time));
                                    }
                                    catch (Exception exp)
                                    {
                                    }
                                    j += 8;
                                }
                                else
                                    j += length + 8;
                            }
                        }
                        byte head = bytes[start];
                        int count = start;
                        while (head == FCTCOMMAND.fctHead && result > count)
                        {
                            if (count + 5 > bytes.Length)
                            {
                                start = count - bytes.Length;
                                Array.Copy(bytes, count, temp, 0, -start);
                                break;
                            }
                            int j = count + 3;
                            short tc = BitConverter.ToInt16(bytes, j);//总标签数
                            count += tc;
                            if (count >= bytes.Length)
                            {
                                start = count - bytes.Length;
                                Array.Copy(bytes, count - tc, temp, 0, tc - start);
                                break;
                            }
                            else start = 0;
                            j += 2;
                            while (j < count)
                            {
                                short id = BitConverter.ToInt16(bytes, j);//标签ID、数据长度、数据值(T,L,V)
                                j += 2;
                                byte length = bytes[j++];
                                ITag tag;
                                if (_items.TryGetValue(id, out tag))
                                {
                                    //数据类型
                                    switch (tag.Address.VarType)
                                    {
                                        case DataType.BOOL:
                                            value.Boolean = BitConverter.ToBoolean(bytes, j);
                                            break;
                                        case DataType.BYTE:
                                            value.Byte = bytes[j];
                                            break;
                                        case DataType.WORD:
                                            value.Word = BitConverter.ToUInt16(bytes, j);//需测试
                                            break;
                                        case DataType.SHORT:
                                            value.Int16 = BitConverter.ToInt16(bytes, j);//需测试
                                            break;
                                        case DataType.DWORD:
                                            value.DWord = BitConverter.ToUInt32(bytes, j);//需测试
                                            break;
                                        case DataType.INT:
                                            value.Int32 = BitConverter.ToInt32(bytes, j);//需测试
                                            break;
                                        case DataType.FLOAT:
                                            value.Single = BitConverter.ToSingle(bytes, j);
                                            break;
                                        case DataType.STR:
                                            StringTag strTag = tag as StringTag;
                                            if (strTag != null)
                                            {
                                                strTag.String = Encoding.ASCII.GetString(bytes, j, length).Trim((char)0);
                                            }
                                            break;
                                        default:
                                            break;
                                    }
                                    j += length;
                                    try
                                    {
                                        DateTime time = DateTime.FromFileTime(BitConverter.ToInt64(bytes, j));
                                        //tag.Update(value, time, QUALITIES.QUALITY_GOOD);
                                        //if (historys != null)
                                        historys.Add(new HistoryData(id, QUALITIES.QUALITY_GOOD, value, time));
                                    }
                                    catch (Exception exp)
                                    {
                                    }
                                    j += 8;
                                }
                                else
                                    j += length + 8;
                            }
                            head = bytes[count];
                        }
                        ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.OnRecieve), historys);
                        //if (DataChange != null && historys.Count > 0)
                        //    DataChange(this, new DataChangeEventArgs(1, historys));

                    }
                    else if (error == SocketError.ConnectionReset || error == SocketError.Interrupted
                        || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
                    {
                        _tcpRecive.Dispose();
                        return;
                    }
                }
                catch (Exception)
                {
                    _tcpRecive.Dispose();
                    return;
                }
            }
            while (result > 0);
            try
            {
                _tcpRecive.Dispose();
            }
            catch (Exception)
            {
            }
        }

        private void OnRecieve(object stateInfo)
        {
            var historys = stateInfo as List<HistoryData>;
            if (historys == null) return;
            if (DataChange != null && historys.Count > 0)
                DataChange(this, new DataChangeEventArgs(1, historys));
            for (int i = 0; i < historys.Count; i++)
            {
                var data = historys[i];
                ITag tag;
                if (_items.TryGetValue(data.ID, out tag))
                {
                    tag.Update(data.Value, data.TimeStamp, data.Quality);
                }
            }
        }

        public void OnUpdate(object stateInfo)
        {
            while (true)
            {
                Thread.Sleep(_updateRate);
                lock (this)
                {
                    if (!_active)
                    {
                        return;
                    }
                    Init();
                }
            }
        }

        public bool AddItems(IList<TagMetaData> items)
        {
            int count = items.Count;
            if (_items == null) _items = new Dictionary<short, ITag>(count);
            lock (_server.SyncRoot)
            {
                for (int i = 0; i < count; i++)
                {
                    ITag dataItem = null;
                    TagMetaData meta = items[i];
                    DeviceAddress addr = new DeviceAddress(0, 0, 0, meta.ID, meta.Size, 0, meta.DataType);
                    switch (meta.DataType)
                    {
                        case DataType.BOOL:
                            dataItem = new BoolTag(meta.ID, addr, this);
                            break;
                        case DataType.BYTE:
                            dataItem = new ByteTag(meta.ID, addr, this);
                            break;
                        case DataType.WORD:
                            dataItem = new UShortTag(meta.ID, addr, this);
                            break;
                        case DataType.SHORT:
                            dataItem = new ShortTag(meta.ID, addr, this);
                            break;
                        case DataType.INT:
                            dataItem = new IntTag(meta.ID, addr, this);
                            break;
                        case DataType.DWORD:
                            dataItem = new UIntTag(meta.ID, addr, this);
                            break;
                        case DataType.FLOAT:
                            dataItem = new FloatTag(meta.ID, addr, this);
                            break;
                        case DataType.STR:
                            dataItem = new StringTag(meta.ID, addr, this);
                            break;
                        default:
                            dataItem = new BoolTag(meta.ID, addr, this);
                            break;
                    }
                    _items.Add(meta.ID, dataItem);
                    _server.AddItemIndex(meta.Name, dataItem);
                }
            }
            //Init();
            return true;
        }

        public bool AddTags(IEnumerable<ITag> tags)
        {
            if (_items == null)
            {
                _items = new Dictionary<short, ITag>();
            }
            foreach (ITag tag in tags)
            {
                if (tag != null)
                {
                    _items.Add(tag.ID, tag);
                }
            }
            //Init();
            return true;
        }

        public bool RemoveItems(params ITag[] items)
        {
            foreach (var item in items)
            {
                _server.RemoveItemIndex(item.GetTagName());
                _items.Remove(item.ID);
            }
            return true;
        }

        public bool SetActiveState(bool active, params short[] items)
        {
            return true;
        }

        public ITag FindItemByAddress(DeviceAddress addr)
        {
            ITag tag;
            if (_items.TryGetValue((short)addr.Start, out tag))
                return tag;
            return _server[(short)addr.Start];
        }

        public HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray)
        {
            if (itemArray.Length == 0 || _tcpSend == null || !_tcpSend.Connected) return null;
            short len = (short)itemArray.Length;
            byte[] bt = new byte[2];
            byte[] data = new byte[5 + len * 2];
            int j = 0;
            data[j++] = FCTCOMMAND.fctHead;
            data[j++] = FCTCOMMAND.fctReadMultiple;
            data[j++] = source == DataSource.Cache ? (byte)0 : (byte)1;
            bt = BitConverter.GetBytes(len);
            data[j++] = bt[0];
            data[j++] = bt[1];
            for (int i = 0; i < len; i++)
            {
                bt = BitConverter.GetBytes(itemArray[i].ID);
                data[j++] = bt[0];
                data[j++] = bt[1];
            }
            SocketError error;
            lock (sendasync)
            {
                _tcpSend.Send(data, 0, data.Length, SocketFlags.None, out error);
                int result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
            }
            //!!!!此处的协议应注意,如批量读入的数据量超过缓存,必须分批合并;在协议头应加入总字节数,以和result比较是否需要循环读入。
            j = 5;
            if (error == SocketError.Success)
            {
                DateTime time = DateTime.Now;
                HistoryData[] values = new HistoryData[len];
                for (int i = 0; i < len; i++)
                {
                    short id = BitConverter.ToInt16(tcpBuffer, j);
                    j += 2;
                    ITag tag = _server[id];
                    byte length = tcpBuffer[j++];
                    if (tag != null && length > 0)
                    {
                        switch (tag.Address.VarType)
                        {
                            case DataType.BOOL:
                                values[i].Value.Boolean = BitConverter.ToBoolean(tcpBuffer, j);
                                break;
                            case DataType.BYTE:
                                values[i].Value.Byte = tcpBuffer[j];
                                break;
                            case DataType.WORD:
                                values[i].Value.Word = BitConverter.ToUInt16(tcpBuffer, j);
                                break;
                            case DataType.SHORT:
                                values[i].Value.Int16 = BitConverter.ToInt16(tcpBuffer, j);
                                break;
                            case DataType.DWORD:
                                values[i].Value.DWord = BitConverter.ToUInt32(tcpBuffer, j);
                                break;
                            case DataType.INT:
                                values[i].Value.Int32 = BitConverter.ToInt32(tcpBuffer, j);
                                break;
                            case DataType.FLOAT:
                                values[i].Value.Single = BitConverter.ToSingle(tcpBuffer, j);
                                break;
                            case DataType.STR:
                                StringTag strTag = tag as StringTag;
                                if (strTag != null)
                                {
                                    strTag.String = Encoding.ASCII.GetString(tcpBuffer, j, length).Trim();
                                }
                                break;
                        }
                        if (values[i].Value != tag.Value)
                            tag.Update(values[i].Value, time, QUALITIES.QUALITY_GOOD);
                    }
                    j += length;
                }
                return values;
            }
            else
                return null;
        }

        public int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true)
        {
            if (_tcpSend == null || !_tcpSend.Connected) return -1;
            List<byte> list = new List<byte>(new byte[] { FCTCOMMAND.fctHead, FCTCOMMAND.fctWriteMultiple });
            list.AddRange(BitConverter.GetBytes((short)items.Count));
            foreach (var item in items)
            {
                ITag tag = item.Key;
                list.AddRange(BitConverter.GetBytes(tag.ID));
                var addr = tag.Address;
                if (addr.VarType != DataType.STR)
                    list.Add((byte)(addr.DataSize));//此处存疑
                switch (addr.VarType)
                {
                    case DataType.BOOL:
                        list.Add(Convert.ToBoolean(item.Value) ? (byte)1 : (byte)0);
                        break;
                    case DataType.BYTE:
                        list.Add(Convert.ToByte(item.Value));
                        break;
                    case DataType.WORD:
                        list.AddRange(BitConverter.GetBytes(Convert.ToUInt16(item.Value)));
                        break;
                    case DataType.SHORT:
                        list.AddRange(BitConverter.GetBytes(Convert.ToInt16(item.Value)));
                        break;
                    case DataType.DWORD:
                        list.AddRange(BitConverter.GetBytes(Convert.ToUInt32(item.Value)));
                        break;
                    case DataType.INT:
                        list.AddRange(BitConverter.GetBytes(Convert.ToInt32(item.Value)));
                        break;
                    case DataType.FLOAT:
                        list.AddRange(BitConverter.GetBytes(Convert.ToSingle(item.Value)));
                        break;
                    case DataType.STR:
                        var bts = Encoding.ASCII.GetBytes(Convert.ToString(item.Value));
                        list.Add((byte)bts.Length);
                        list.AddRange(bts);
                        break;
                }
            }
            SocketError error;
            lock (sendasync)
            {
                _tcpSend.Send(list.ToArray(), 0, list.Count, SocketFlags.None, out error);
                _tcpSend.Receive(tcpBuffer, 0, 2, SocketFlags.None, out error);
            }
            if (error == SocketError.Success)
                return tcpBuffer[1];
            else
            {
                return (int)error;
            }
        }

        public IEnumerable<HistoryData> SendHdaRequest(DateTime start, DateTime end)
        {
            if (_tcpSend == null || !_tcpSend.Connected) yield break;
            byte[] hdaReq = new byte[18];
            hdaReq[0] = FCTCOMMAND.fctHead;
            hdaReq[1] = FCTCOMMAND.fctHdaRequest;
            byte[] startbuffer = BitConverter.GetBytes(start.ToFileTime());
            startbuffer.CopyTo(hdaReq, 2);
            byte[] endbuffer = BitConverter.GetBytes(end.ToFileTime());
            endbuffer.CopyTo(hdaReq, 10);
            SocketError error;
            HistoryData data = HistoryData.Empty;
            short tempid = short.MinValue;
            byte[] temp = new byte[14];
            ITag tag = null;
            int index = 0;
            int size = 0;
            int result = 0;
            short id = 0;
            lock (sendasync)
            {
                _tcpSend.Send(hdaReq);
                do
                {
                    result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
                    if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
                    {
                        _tcpSend.Dispose();
                        yield break;
                    }
                    if (index != 0)
                    {
                        Array.Copy(tcpBuffer, 0, temp, 14 - index, index);
                        id = BitConverter.ToInt16(temp, 0);
                        tempid = id;
                        tag = _server[id];
                        if (tag == null) yield break;
                        data.ID = id;
                        size = tag.Address.DataSize;
                        index -= (4 - size);
                        switch (tag.Address.VarType)
                        {
                            case DataType.BOOL:
                                data.Value.Boolean = BitConverter.ToBoolean(temp, 2);
                                break;
                            case DataType.BYTE:
                                data.Value.Byte = tcpBuffer[index];
                                break;
                            case DataType.WORD:
                                data.Value.Word = BitConverter.ToUInt16(temp, 2);
                                break;
                            case DataType.SHORT:
                                data.Value.Int16 = BitConverter.ToInt16(temp, 2);
                                break;
                            case DataType.DWORD:
                                data.Value.DWord = BitConverter.ToUInt32(temp, 2);
                                break;
                            case DataType.INT:
                                data.Value.Int32 = BitConverter.ToInt32(temp, 2);
                                break;
                            case DataType.FLOAT:
                                data.Value.Single = BitConverter.ToSingle(temp, 2);
                                break;
                        }
                        long fileTime = BitConverter.ToInt64(temp, 2 + size);
                        if (fileTime == -1) yield break;
                        data.TimeStamp = DateTime.FromFileTime(fileTime);
                        yield return data;
                    }
                    while (result >= index + 2)
                    {
                        id = BitConverter.ToInt16(tcpBuffer, index);
                        if (tempid != id)
                        {
                            tempid = id;
                            tag = _server[id];
                            if (tag == null) yield break;
                            size = tag.Address.DataSize;
                        }
                        if (index + 10 + size > result)
                            break;
                        data.ID = id;
                        index += 2;
                        switch (tag.Address.VarType)
                        {
                            case DataType.BOOL:
                                data.Value.Boolean = BitConverter.ToBoolean(tcpBuffer, index);
                                break;
                            case DataType.BYTE:
                                data.Value.Byte = tcpBuffer[index];
                                break;
                            case DataType.WORD:
                                data.Value.Word = BitConverter.ToUInt16(tcpBuffer, index);
                                break;
                            case DataType.SHORT:
                                data.Value.Int16 = BitConverter.ToInt16(tcpBuffer, index);
                                break;
                            case DataType.DWORD:
                                data.Value.DWord = BitConverter.ToUInt32(tcpBuffer, index);
                                break;
                            case DataType.INT:
                                data.Value.Int32 = BitConverter.ToInt32(tcpBuffer, index);
                                break;
                            case DataType.FLOAT:
                                data.Value.Single = BitConverter.ToSingle(tcpBuffer, index);
                                break;
                        }
                        index += size;
                        long fileTime = BitConverter.ToInt64(tcpBuffer, index);
                        if (fileTime == -1) yield break;
                        data.TimeStamp = DateTime.FromFileTime(fileTime);
                        index += 8;
                        yield return data;
                    }
                    if (index == result)
                        index = 0;
                    else
                    {
                        Array.Copy(tcpBuffer, index, temp, 0, result - index);
                        index += 14 - result;
                    }
                } while (result > 0);
            }
            yield break;
        }

        public IEnumerable<HistoryData> SendHdaRequest(DateTime start, DateTime end, short id)
        {
            if (_tcpSend == null || !_tcpSend.Connected) yield break;
            ITag tag = _server[id];
            if (tag == null) yield break;
            byte[] hdaReq = new byte[20];
            hdaReq[0] = FCTCOMMAND.fctHead;
            hdaReq[1] = FCTCOMMAND.fctHdaIdRequest;
            byte[] startbuffer = BitConverter.GetBytes(start.ToFileTime());
            startbuffer.CopyTo(hdaReq, 2);
            byte[] endbuffer = BitConverter.GetBytes(end.ToFileTime());
            endbuffer.CopyTo(hdaReq, 10);
            byte[] idbuffer = BitConverter.GetBytes(id);
            idbuffer.CopyTo(hdaReq, 18);
            SocketError error;
            int index = 0;
            HistoryData data = HistoryData.Empty;
            data.ID = id;
            int result = 0;
            lock (sendasync)
            {
                _tcpSend.Send(hdaReq);
                switch (tag.Address.VarType)
                {
                    case DataType.FLOAT:
                        do
                        {
                            result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
                            if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
                            {
                                _tcpSend.Dispose();
                                yield break;
                            }
                            while (index + 12 <= result)
                            {
                                data.Value.Single = BitConverter.ToSingle(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
                                index += 4;
                                long fileTime = BitConverter.ToInt64(tcpBuffer, index);
                                if (fileTime == -1) yield break;
                                data.TimeStamp = DateTime.FromFileTime(fileTime);
                                index += 8;
                                yield return data;
                            }
                            if (index == result)
                                index = 0;
                            else
                                index += 12 - result;//丢弃一个值
                        } while (result > 0);
                        break;
                    case DataType.SHORT:
                        do
                        {
                            result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
                            if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
                            {
                                _tcpSend.Dispose();
                                yield break;
                            }
                            while (index + 10 <= result)
                            {
                                data.Value.Int16 = BitConverter.ToInt16(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
                                index += 2;
                                long fileTime = BitConverter.ToInt64(tcpBuffer, index);
                                if (fileTime == -1) yield break;
                                data.TimeStamp = DateTime.FromFileTime(fileTime);
                                index += 8;
                                yield return data;
                            }
                            if (index == result)
                                index = 0;
                            else
                                index += 10 - result;//丢弃一个值
                        } while (result > 0);
                        break;
                    case DataType.WORD:
                        do
                        {
                            result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
                            if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
                            {
                                _tcpSend.Dispose();
                                yield break;
                            }
                            while (index + 10 <= result)
                            {
                                data.Value.Word = BitConverter.ToUInt16(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
                                index += 2;
                                long fileTime = BitConverter.ToInt64(tcpBuffer, index);
                                if (fileTime == -1) yield break;
                                data.TimeStamp = DateTime.FromFileTime(fileTime);
                                index += 8;
                                yield return data;
                            }
                            if (index == result)
                                index = 0;
                            else
                                index += 10 - result;//丢弃一个值
                        } while (result > 0);
                        break;
                    case DataType.DWORD:
                        do
                        {
                            result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
                            if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
                            {
                                _tcpSend.Dispose();
                                yield break;
                            }
                            while (index + 12 <= result)
                            {
                                data.Value.DWord = BitConverter.ToUInt32(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
                                index += 4;
                                long fileTime = BitConverter.ToInt64(tcpBuffer, index);
                                if (fileTime == -1) yield break;
                                data.TimeStamp = DateTime.FromFileTime(fileTime);
                                index += 8;
                                yield return data;
                            }
                            if (index == result)
                                index = 0;
                            else
                                index += 12 - result;//丢弃一个值
                        } while (result > 0);
                        break;
                    case DataType.INT:
                        do
                        {
                            result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
                            if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
                            {
                                _tcpSend.Dispose();
                                yield break;
                            }
                            while (index + 12 <= result)
                            {
                                data.Value.Int32 = BitConverter.ToInt32(tcpBuffer, index);//未来可考虑量程转换和其他数据类型
                                index += 4;
                                long fileTime = BitConverter.ToInt64(tcpBuffer, index);
                                if (fileTime == -1) yield break;
                                data.TimeStamp = DateTime.FromFileTime(fileTime);
                                index += 8;
                                yield return data;
                            }
                            if (index == result)
                                index = 0;
                            else
                                index += 12 - result;//丢弃一个值
                        } while (result > 0);
                        break;
                    case DataType.BOOL://布尔量一个都不能少
                        {
                            byte[] temp = new byte[9];
                            do
                            {
                                result = _tcpSend.Receive(tcpBuffer, 0, tcpBuffer.Length, SocketFlags.None, out error);
                                if (error == SocketError.ConnectionReset || error == SocketError.Interrupted || error == SocketError.HostDown || error == SocketError.NetworkDown || error == SocketError.Shutdown)
                                {
                                    _tcpSend.Dispose();
                                    yield break;
                                }
                                if (index != 0)
                                {
                                    Array.Copy(tcpBuffer, 0, temp, 9 - index, index);
                                    data.Value.Boolean = BitConverter.ToBoolean(temp, 0);
                                    long fileTime = BitConverter.ToInt64(temp, 1);
                                    if (fileTime == -1) yield break;
                                    data.TimeStamp = DateTime.FromFileTime(fileTime);
                                    yield return data;
                                }
                                while (index + 9 <= result)
                                {
                                    data.Value.Boolean = BitConverter.ToBoolean(tcpBuffer, index);
                                    index += 1;
                                    long fileTime = BitConverter.ToInt64(tcpBuffer, index);
                                    if (fileTime == -1) yield break;
                                    data.TimeStamp = DateTime.FromFileTime(fileTime);
                                    index += 8;
                                    yield return data;
                                }
                                if (index == result)
                                    index = 0;
                                else
                                {
                                    Array.Copy(tcpBuffer, index, temp, 0, result - index);
                                    index += 9 - result;
                                }
                            } while (result > 0);
                        }
                        break;
                }
            }
        }

        public int SendResetRequest()
        {
            if (_tcpSend != null && _tcpSend.Connected)
            {
                var ipaddr = (_tcpSend.LocalEndPoint as IPEndPoint).Address;
                byte[] resetReq = new byte[6];
                resetReq[0] = FCTCOMMAND.fctHead;
                resetReq[1] = FCTCOMMAND.fctReset;
                ipaddr.GetAddressBytes().CopyTo(resetReq, 2);
                lock (sendasync)
                {
                    return _tcpSend.Send(resetReq);
                }
            }
            return -1;
        }

        public int SendAlarmRequest(DateTime? start, DateTime? end)
        {
            if (_tcpSend != null && _tcpSend.Connected)
            {
                byte[] alarmReq = new byte[18];
                alarmReq[0] = FCTCOMMAND.fctHead;
                alarmReq[1] = FCTCOMMAND.fctAlarmRequest;
                if (start.HasValue)
                {
                    byte[] startbuffer = BitConverter.GetBytes(start.Value.ToFileTime());
                    startbuffer.CopyTo(alarmReq, 2);
                }
                if (end.HasValue)
                {
                    byte[] endbuffer = BitConverter.GetBytes(end.Value.ToFileTime());
                    endbuffer.CopyTo(alarmReq, 10);
                }
                SocketError error;
                lock (sendasync)
                {
                    _tcpSend.Send(alarmReq);
                    _tcpSend.Receive(tcpBuffer, 0, 2, SocketFlags.None, out error);
                }
                return (int)error;
            }
            return -1;
        }

        public ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            var data = ReadSingleData(address, source);
            return data == null ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) :
                new ItemData<int>(BitConverter.ToInt32(data, 0), 0, QUALITIES.QUALITY_GOOD);
        }

        public ItemData<uint> ReadUInt32(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            var data = ReadSingleData(address, source);
            return data == null ? new ItemData<uint>(0, 0, QUALITIES.QUALITY_BAD) :
                new ItemData<uint>(BitConverter.ToUInt32(data, 0), 0, QUALITIES.QUALITY_GOOD);
        }

        public ItemData<ushort> ReadUInt16(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            var data = ReadSingleData(address, source);
            return data == null ? new ItemData<ushort>(0, 0, QUALITIES.QUALITY_BAD) :
                new ItemData<ushort>(BitConverter.ToUInt16(data, 0), 0, QUALITIES.QUALITY_GOOD);
        }

        public ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            var data = ReadSingleData(address, source);
            return data == null ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) :
                new ItemData<short>(BitConverter.ToInt16(data, 0), 0, QUALITIES.QUALITY_GOOD);
        }

        public ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            var data = ReadSingleData(address, source);
            return data == null ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) :
                new ItemData<byte>(data[0], 0, QUALITIES.QUALITY_GOOD);
        }

        public unsafe ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            var data = ReadSingleData(address, source);
            if (data == null)
                return new ItemData<float>(0.0f, 0, QUALITIES.QUALITY_BAD);
            else
            {
                int value = BitConverter.ToInt32(data, 0);
                return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD);
            }
        }

        public ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            var data = ReadSingleData(address, source);
            return data == null ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) :
                new ItemData<bool>(BitConverter.ToBoolean(data, address.Bit), 0, QUALITIES.QUALITY_GOOD);
        }

        public ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache)
        {
            var data = ReadSingleData(address, source);
            return data == null ? new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD) :
                new ItemData<string>(Encoding.ASCII.GetString(data, 0, Math.Min((int)address.DataSize, 254)).Trim((char)0), 0, QUALITIES.QUALITY_GOOD);
        }

        public int WriteInt32(DeviceAddress address, int value)
        {
            return WriteSingleData(address, BitConverter.GetBytes(value));
        }

        public int WriteUInt32(DeviceAddress address, uint value)
        {
            return WriteSingleData(address, BitConverter.GetBytes(value));
        }

        public int WriteUInt16(DeviceAddress address, ushort value)
        {
            return WriteSingleData(address, BitConverter.GetBytes(value));
        }

        public int WriteInt16(DeviceAddress address, short value)
        {
            return WriteSingleData(address, BitConverter.GetBytes(value));
        }

        public int WriteFloat(DeviceAddress address, float value)
        {
            return WriteSingleData(address, BitConverter.GetBytes(value));
        }

        public int WriteString(DeviceAddress address, string value)
        {
            return WriteSingleData(address, Encoding.ASCII.GetBytes(value));
        }

        public int WriteBit(DeviceAddress address, bool value)
        {
            return WriteSingleData(address, new byte[] { (byte)(value ? 1 : 0) });
        }

        public int WriteBits(DeviceAddress address, byte value)
        {
            return WriteSingleData(address, new byte[] { value });
        }

        public event DataChangeEventHandler DataChange;

        public void Dispose()
        {
            if (_items != null)
            {
                _items.Clear();
            }
            _items = null;
        }
    }
}