/// <summary>
 /// Releases packet so it can be recycled.
 /// </summary>
 /// <param name="packet">Released packet.</param>
 public void ReleasePacket(GXPacket packet)
 {
     lock (PacketsSync)
     {
         if (Packets.Count < 100)
         {
             packet.Clear();
             Packets.Push(packet);
         }
     }
 }
		private bool IsReadoutComplete(object sender, GXPacket packet)
		{
			byte cField = (byte)packet.ExtractData(typeof(System.Byte), 3, 1);
			byte fcv = (byte) (cField & MASK_FCV);
			byte fcb = (byte) (cField & MASK_FCB);

			if (sender is GXMBusCategory)
			{
				ParseReadoutReply(packet, sender as GXMBusCategory);
			}
			else
			{
				//This should never happen
				throw new Exception("Unknown parameter \"sender\" in IsReadoutComplete.");
			}
			packet.Clear();
			if (fcv == 0)
			{
				return true;
			}
			else
			{
				m_PreviousFCB = fcb;
				return false;
			}
		}
 public bool IsTransactionComplete(object sender, string command, GXPacket packet)
 {
     if (command == "IsAARQSend")
     {                
         bool complete = AARQRequest.Length == AARQRequestPos;
         return complete;
     }
     else if (command == "IsAllDataReceived")//Is all data read.
     {
         byte[] data = packet.ExtractPacket();
         CheckErrors(data, false);
         RequestTypes tmp = parser.GetDataFromPacket(data, ref ReceivedData);
         if (tmp == RequestTypes.None && IsMoreDataAvailable != RequestTypes.None)
         {
             if ((IsMoreDataAvailable & RequestTypes.Frame) != 0)
             {
                 IsMoreDataAvailable &= ~RequestTypes.Frame;
             }
             else
             {
                 IsMoreDataAvailable &= ~RequestTypes.DataBlock;
             }
         }
         else
         {
             IsMoreDataAvailable |= tmp;
         }
         return IsMoreDataAvailable == Gurux.DLMS.RequestTypes.None;
     }
     else if (command == "IsAllTableInfoReceived")//Is all data read.
     {
         byte[] data = packet.ExtractPacket();
         CheckErrors(data, true);
         RequestTypes tmp = parser.GetDataFromPacket(data, ref ReceivedData);
         if (tmp == RequestTypes.None && IsMoreDataAvailable != RequestTypes.None)
         {
             if ((IsMoreDataAvailable & RequestTypes.Frame) != 0)
             {
                 IsMoreDataAvailable &= ~RequestTypes.Frame;
             }
             else
             {
                 IsMoreDataAvailable &= ~RequestTypes.DataBlock;
             }
         }
         else
         {
             IsMoreDataAvailable |= tmp;
         }
         if (IsMoreDataAvailable != Gurux.DLMS.RequestTypes.None)
         {                    
             return true;
         }
         ExtraInfo[ExtraInfo.Keys.ElementAt(ExtraInfoPos)] = parser.GetValue(ReceivedData);
         ReceivedData = null;
         //Read next item.
         if (++ExtraInfoPos < ExtraInfo.Count)
         {
             return false;                    
         }                
         return true;                
     }                
     else if (command == "IsAllTableDataReceived")//Are all columns read.
     {                
         Gurux.Device.GXTable table = sender as Gurux.Device.GXTable;
         Gurux.Device.GXDevice device = table.Device;
         int progress = 0;                
         byte[] data = packet.ExtractPacket();
         CheckErrors(data, true);
         //Clear packet so we are not saved it for later use.
         packet.Clear();
         RequestTypes tmp = parser.GetDataFromPacket(data, ref ReceivedData);
         if (tmp == RequestTypes.None && IsMoreDataAvailable != RequestTypes.None)
         {
             if ((IsMoreDataAvailable & RequestTypes.Frame) != 0)
             {
                 IsMoreDataAvailable &= ~RequestTypes.Frame;
             }
             else
             {
                 IsMoreDataAvailable &= ~RequestTypes.DataBlock;
             }
         }
         else
         {
             IsMoreDataAvailable |= tmp;
         }
         bool complete = IsMoreDataAvailable == Gurux.DLMS.RequestTypes.None;
         //Notify only percent to increase traffic.
         int maxValue = parser.GetMaxProgressStatus(ReceivedData);
         //Notify progess.
         if (maxValue == 0)
         {
             progress = 0;
         }
         else
         {
             double val = parser.GetCurrentProgressStatus(ReceivedData);                    
             progress = (int)(val / maxValue * 100);
         }
         if (LastNotifiedTransactionProgress != progress)
         {
             if (complete)
             {
                 device.NotifyTransactionProgress(this, new Gurux.Device.GXTransactionProgressEventArgs(sender, 100, 100, Gurux.Device.DeviceStates.ReadEnd));
             }
             else
             {
                 device.NotifyTransactionProgress(this, new Gurux.Device.GXTransactionProgressEventArgs(sender, progress, 100, Gurux.Device.DeviceStates.ReadStart));                        
             }
             LastNotifiedTransactionProgress = progress;
         }
         //Try Parse DLMS objexts.
         if (TryParse)
         {
             GXDLMSTable table2 = sender as GXDLMSTable;
             if (this.ReceivedRows == 0)
             {
                 table2.ClearRows();                        
             }                    
             Array reply = (Array)parser.TryGetValue(ReceivedData);
             // If there is data.
             if (reply != null && reply.Length != 0)
             {
                 int count = pg.Buffer.Count;
                 pg.SetValue(2, reply);                        
                 List<object> receivedData = new List<object>(pg.Buffer.ToArray());
                 //Remove old rows. We must keep latest because some meters returns null as date time.
                 if (count != 0)
                 {                            
                     receivedData.RemoveRange(0, count);
                     pg.Buffer.RemoveRange(0, count - 1);
                 }
                 List<object[]> rows = new List<object[]>(receivedData.Count);
                 bool updated = false;
                 if (Extension != null)
                 {
                     updated = Extension.UpdateTableData(TableColumns, table2, reply, rows);
                 }
                 if (!updated)
                 {
                     foreach (object row in receivedData)
                     {
                         List<object> cols = new List<object>();
                         foreach (int it in ColumnIndexs)
                         {
                             cols.Add(((object[])row)[it]);
                         }
                         rows.Add(cols.ToArray());
                     }
                 }
                 table2.AddRows(-1, rows, false);
                 //Save latest read time. Note we must add Capture Period or we will read last values again.
                 Gurux.Device.Editor.IGXPartialRead partialRead = table as Gurux.Device.Editor.IGXPartialRead;
                 if (partialRead.Type == Gurux.Device.Editor.PartialReadType.New && rows[rows.Count - 1][0] is GXDateTime)
                 {                            
                     DateTime tm = (rows[rows.Count - 1][0] as GXDateTime).Value;
                     partialRead.Start = tm.AddSeconds(pg.CapturePeriod);
                 }
                 ReceivedRows += reply.Length;
             }                  
         }
         return complete;
     }
     else
     {
         throw new NotImplementedException();
     }
 }