/// <summary>
        /// Read object.
        /// </summary>
        /// <param name="InitialValues"></param>
        /// <param name="obj"></param>
        /// <param name="attribute">Attribute index to read.</param>
        /// <param name="forceRead">Force all attributes read.</param>
        public void Read(object sender, GXDLMSObject obj, int attribute, bool forceRead)
        {
            GXReplyData reply = new GXReplyData();

            if (forceRead)
            {
                obj.ClearReadTime();
            }
            foreach (int it in (obj as IGXDLMSBase).GetAttributeIndexToRead())
            {
                reply.Clear();
                if (obj is GXDLMSProfileGeneric && it == 2)
                {
                    if (OnBeforeRead != null)
                    {
                        OnBeforeRead(obj, it);
                    }
                    try
                    {
                        byte[][] tmp;
                        CurrentProfileGeneric = obj as GXDLMSProfileGeneric;
                        OnDataReceived       += new GXDLMSCommunicator.DataReceivedEventHandler(this.OnProfileGenericDataReceived);
                        if (CurrentProfileGeneric.AccessSelector == AccessRange.Range ||
                            CurrentProfileGeneric.AccessSelector == AccessRange.Last)
                        {
                            GXDateTime start = CurrentProfileGeneric.From as GXDateTime;
                            if (start == null)
                            {
                                start = Convert.ToDateTime(CurrentProfileGeneric.From);
                            }
                            GXDateTime end = CurrentProfileGeneric.To as GXDateTime;
                            if (end == null)
                            {
                                end = Convert.ToDateTime(CurrentProfileGeneric.To);
                            }
                            tmp = client.ReadRowsByRange(CurrentProfileGeneric, start, end);
                            ReadDataBlock(tmp, "Reading profile generic data", 1, reply);
                        }
                        else if (CurrentProfileGeneric.AccessSelector == AccessRange.Entry)
                        {
                            tmp = client.ReadRowsByEntry(CurrentProfileGeneric, Convert.ToInt32(CurrentProfileGeneric.From), Convert.ToInt32(CurrentProfileGeneric.To));
                            ReadDataBlock(tmp, "Reading profile generic data " + CurrentProfileGeneric.Name, 1, reply);
                        }
                        else //Read all.
                        {
                            tmp = client.Read(CurrentProfileGeneric, 2);
                            ReadDataBlock(tmp, "Reading profile generic data " + CurrentProfileGeneric.Name, 1, reply);
                        }
                    }
                    finally
                    {
                        if (OnAfterRead != null)
                        {
                            OnAfterRead(obj, it);
                        }
                        OnDataReceived -= new GXDLMSCommunicator.DataReceivedEventHandler(OnProfileGenericDataReceived);
                    }
                    continue;
                }
                else
                {
                    if (OnBeforeRead != null)
                    {
                        OnBeforeRead(obj, it);
                    }
                    byte[] data = client.Read(obj.Name, obj.ObjectType, it)[0];
                    try
                    {
                        ReadDataBlock(data, "Read object type " + obj.ObjectType + " index: " + it, reply);
                    }
                    catch (GXDLMSException ex)
                    {
                        if (ex.ErrorCode == (int)ErrorCode.ReadWriteDenied ||
                            ex.ErrorCode == (int)ErrorCode.UndefinedObject ||
                            ex.ErrorCode == (int)ErrorCode.UnavailableObject ||
                            //Actaris returns access violation error.
                            ex.ErrorCode == (int)ErrorCode.AccessViolated)
                        {
                            obj.SetAccess(it, AccessMode.NoAccess);
                            continue;
                        }
                        else
                        {
                            throw ex;
                        }
                    }
                    if (obj is IGXDLMSBase)
                    {
                        object   value = reply.Value;
                        DataType type;
                        if (value is byte[] && (type = obj.GetUIDataType(it)) != DataType.None)
                        {
                            value = GXDLMSClient.ChangeType((byte[])value, type);
                        }
                        client.UpdateValue(obj, it, value);
                    }
                    if (OnAfterRead != null)
                    {
                        OnAfterRead(obj, it);
                    }
                    obj.SetLastReadTime(it, DateTime.Now);
                    //If only selected attribute is read.
                    if (attribute != 0)
                    {
                        break;
                    }
                }
            }
        }