private TagReadData ParseRqlResponse(string row, DateTime baseTime)
        {
            String[] fields = row.Split(_selFieldSeps);

            if (_readFieldNames.Length != fields.Length)
            {
                throw new ReaderParseException(String.Format("Unrecognized format."
                    + "  Got {0} fields in RQL response, expected {1}.",
                    fields.Length, _readFieldNames.Length));
            }
            byte[] epccrc = ByteFormat.FromHex(fields[ID].Substring(2));
            byte[] epc = new byte[epccrc.Length - 2];
            Array.Copy(epccrc, epc, epc.Length);
            byte[] crc = new byte[2];
            Array.Copy(epccrc, epc.Length, crc, 0, 2);

            TagProtocol proto = CodeToProtocol(fields[PROTOCOL_ID]);
            string idfield = fields[ID];
            TagData tag = null;

            switch (proto)
            {
                case TagProtocol.GEN2:
                    byte[] pcbits = null;
                    if (_readFieldNames.Length > 7)
                    {
                        pcbits = ByteFormat.FromHex(fields[METADATA].ToString());
                    }
                    tag = new Gen2.TagData(epc, crc,pcbits);
                    break;
                case TagProtocol.ISO180006B:
                    tag = new Iso180006b.TagData(epc, crc);
                    break;
                case TagProtocol.IPX64:
                    tag = new Ipx64.TagData(epc, crc);
                    break;
                case TagProtocol.IPX256:
                    tag = new Ipx256.TagData(epc, crc);
                    break;
                default:
                    throw new ReaderParseException("Unknown protocol code " + fields[PROTOCOL_ID]);
            }

            int antenna = int.Parse(fields[ANTENNA_ID]);
            TagReadData tr = new TagReadData();
            tr.Reader = this;
            tr._tagData = tag;
            tr._antenna = antenna;
            tr._baseTime = baseTime;
            tr.ReadCount = int.Parse(fields[READ_COUNT]);
            if (_readFieldNames.Length > 7)
            {
                tr._data = ByteFormat.FromHex(fields[DATA]);
                tr._phase = int.Parse(fields[PHASE]);
            }
            tr._readOffset = int.Parse(fields[DSPMICROS]) / 1000;
            tr._frequency = int.Parse(fields[FREQUENCY]);
            

            if ("Mercury5" != _model)
            {
                tr._lqi = int.Parse(fields[LQI]);
            }

            return tr;
        }
        private void ParseNotifyTag(MSG_RO_ACCESS_REPORT msg)
        {
            if (null == tagReads)
            {
                tagReads = new List<TagReadData>();
            }
            TagReadData tag = null;
            if (null != msg)
            {
                for (int i = 0; i < msg.TagReportData.Length; i++)
                {
                    try
                    {
                        tag = new TagReadData();
                        if (msg.TagReportData[i].EPCParameter.Count > 0)
                        {
                            string epc;
                            // reports come in two flavors.  Get the right flavor
                            if (msg.TagReportData[i].EPCParameter[0].GetType() == typeof(PARAM_EPC_96))
                            {
                                epc = ((PARAM_EPC_96)(msg.TagReportData[i].EPCParameter[0])).EPC.ToHexString();
                            }
                            else
                            {
                                epc = ((PARAM_EPCData)(msg.TagReportData[i].EPCParameter[0])).EPC.ToHexString();
                            }
                            TagData td = new TagData(ByteFormat.FromHex(epc));
                            TagProtocol tagProtocol = 0;
                            //Match the recieved rospec id with the rospec id stored in the hashtable at the time of setting readplan
                            if (roSpecProtcolTable.ContainsKey(msg.TagReportData[i].ROSpecID.ROSpecID))
                            {
                                tagProtocol = (TagProtocol)roSpecProtcolTable[msg.TagReportData[i].ROSpecID.ROSpecID];
                            }
                            if (TagProtocol.GEN2.Equals(tagProtocol))
                            {
                                //Get crc and pc bits
                                UNION_AirProtocolTagData tagdata = msg.TagReportData[i].AirProtocolTagData;
                                td = new Gen2.TagData(ByteFormat.FromHex(epc), ByteConv.EncodeU16(((PARAM_C1G2_CRC)tagdata[1]).CRC), ByteConv.EncodeU16(((PARAM_C1G2_PC)tagdata[0]).PC_Bits));
                            }
                            else if (TagProtocol.ISO180006B.Equals(tagProtocol))
                            {
                                td = new Iso180006b.TagData(ByteFormat.FromHex(epc));
                            }
                            else
                            {
                                td = new TagData(ByteFormat.FromHex(epc));
                            }
                            tag.Reader = this;
                            tag._tagData = td;
                            tag._antenna = (int)msg.TagReportData[i].AntennaID.AntennaID;
                            UInt64 usSinceEpoch = msg.TagReportData[i].LastSeenTimestampUTC.Microseconds;
                            tag._baseTime = epochTime.AddMilliseconds(usSinceEpoch / 1000);
                            tag._readOffset = 0;
                            // Since Spruce release firmware doesn't support phase, there won't be PARAM_ThingMagicTagReportContentSelector 
                            // custom paramter in ROReportSpec
                            string[] ver = softwareVersion.Split('.');
                            if (((Convert.ToInt32(ver[0]) == 4) && (Convert.ToInt32(ver[1]) >= 17)) ||
                                (Convert.ToInt32(ver[0]) > 4))
                            {
                                tag._phase = Convert.ToInt32(((PARAM_ThingMagicRFPhase)msg.TagReportData[i].Custom[0]).Phase);
                            }
                            tag.Rssi = Convert.ToInt32(msg.TagReportData[i].PeakRSSI.PeakRSSI.ToString());
                            tag.ReadCount = msg.TagReportData[i].TagSeenCount.TagCount;

                            int chIndex = Convert.ToInt32(msg.TagReportData[i].ChannelIndex.ChannelIndex);
                            List<uint> freq = frequencyHopTable[0].Frequency.data;
                            tag._frequency = Convert.ToInt32(freq[chIndex - 1]);
                            UNION_AccessCommandOpSpecResult opSpecResult = msg.TagReportData[i].AccessCommandOpSpecResult;

                            tag._data = EMPTY_DATA;
                            // Use try-finally to to keep failed tagops from preventing report of TagReadData
                            try
                            {
                                if (null != opSpecResult)
                                {
                                    if (opSpecResult.Count > 0)
                                    {
                                        ParseTagOpSpecResultType(opSpecResult, ref tag);
                                    }
                                }
                            }
                            finally
                            {
                                if (continuousReading)
                                {
                                    OnTagRead(tag);
                                }
                                else
                                {
                                    tagReads.Add(tag);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        ReaderException rx;
                        if (ex is ReaderException)
                        {
                            rx = (ReaderException)ex;
                        }
                        else
                        {
                            rx = new ReaderException(ex.ToString());
                        }
                        //Release the TagQueueEmptyEvent when parsing exception raised
                        TagQueueEmptyEvent.Set();
                        ReadExceptionPublisher expub = new ReadExceptionPublisher(this, rx);

                        Thread trd = new Thread(expub.OnReadException);
                        trd.Name = "OnReadException";
                        trd.Start();
                    }
                    finally
                    {
                        tag = null;
                    }
                }
                TagQueueEmptyEvent.Set();
            }
        }