Example #1
0
        public static string SaveSimpleReadPlan(Object value)
        {
            string   readPlan = string.Empty;
            ReadPlan rp       = (ReadPlan)value;

            readPlan += "SimpleReadPlan:[";
            SimpleReadPlan srp = (SimpleReadPlan)rp;

            readPlan += "Antennas=" + ArrayToString(srp.Antennas);
            readPlan += "," + "Protocol=" + srp.Protocol.ToString();
            if (srp.Filter != null)
            {
                if (srp.Filter is Gen2.Select)
                {
                    Gen2.Select sf = (Gen2.Select)srp.Filter;
                    readPlan += "," + string.Format("Filter=Gen2.Select:[Invert={0},Bank={1},BitPointer={2},BitLength={3},Mask={4}]",
                                                    (sf.Invert?"true" : "false"), sf.Bank, sf.BitPointer, sf.BitLength, ByteFormat.ToHex(sf.Mask));
                }
                else
                {
                    Gen2.TagData td = (Gen2.TagData)srp.Filter;
                    readPlan += "," + string.Format("Filter=TagData:[EPC={0}]", td.EpcString);
                }
            }
            else
            {
                readPlan += ",Filter=null";
            }
            if (srp.Op != null)
            {
                if (srp.Op is Gen2.ReadData)
                {
                    Gen2.ReadData rd = (Gen2.ReadData)srp.Op;
                    readPlan += "," + string.Format("Op=ReadData:[Bank={0},WordAddress={1},Len={2}]", rd.Bank, rd.WordAddress, rd.Len);
                }
                else
                {
                    readPlan += ",Op=null";
                }
            }
            else
            {
                readPlan += ",Op=null";
            }
            readPlan += "," + "UseFastSearch=" + srp.UseFastSearch.ToString();
            readPlan += "," + "Weight=" + srp.Weight.ToString() + "]";
            return(readPlan);
        }
Example #2
0
        public static string SaveReadPlan(Object value)
        {
            string   readPlan = string.Empty;
            ReadPlan rp       = (ReadPlan)value;

            if (rp is SimpleReadPlan)
            {
                return(SaveSimpleReadPlan(value));
            }
            else
            {
                MultiReadPlan   mrp = (MultiReadPlan)rp;
                List <ReadPlan> MRP = new List <ReadPlan>(mrp.Plans);
                readPlan += "MultiReadPlan:[";
                foreach (ReadPlan rap in MRP)
                {
                    readPlan += SaveSimpleReadPlan(rap) + ",";
                }
                readPlan  = readPlan.Remove(readPlan.Length - 1, 1);
                readPlan += "]";
                return(readPlan);
            }
        }
Example #3
0
 /// <summary>
 /// Create WHERE clauses representing a ReadPlan
 /// </summary>
 /// <param name="readPlan">Read plan</param>
 /// <returns>List of strings to be incorporated into "WHERE ... AND ..." phrase.
 /// List may be empty.</returns>
 private List<string> ReadPlanToWhereClause(ReadPlan readPlan)
 {
     List<string> wheres = new List<string>();
     
     if (readPlan is SimpleReadPlan)
     {
         SimpleReadPlan srp = (SimpleReadPlan) readPlan;
         wheres.AddRange(TagProtocolToWhereClause(srp.Protocol));
         wheres.AddRange(AntennasToWhereClause(srp.Antennas));
         if (null != srp.Op && srp.Op is Gen2.ReadData)
         {
             wheres.Add(String.Format("mem_bank={0:D}", ((Gen2.ReadData)(srp.Op)).Bank));
             wheres.Add(String.Format("block_count={0:D}", ((Gen2.ReadData)(srp.Op)).Len));
             wheres.Add(String.Format("block_number={0:D}", (((Gen2.ReadData)(srp.Op)).WordAddress)).ToString());
         }
     }
     else
         throw new ArgumentException("Unrecognized /reader/read/plan type " + typeof(ReadPlan).ToString());
     
     return wheres;
 }
Example #4
0
        /// <summary>
        /// Convert ReadPlan to RQL SELECT statements
        /// </summary>
        /// <param name="rp">ReadPlan to convert</param>
        /// <param name="milliseconds">Total number of milliseconds to allocate to ReadPlan</param>
        /// <param name="subTimeouts">Optional output of milliseconds allocated to each RQL subquery</param>
        /// <returns>List of RQL subqueries</returns>
        private List<string> GenerateRql(ReadPlan rp, int milliseconds, out List<int> subTimeouts)
        {
            List<string> queries = new List<String>();
            List<int> timeoutList = new List<int>();

            if (rp is MultiReadPlan)
            {
                MultiReadPlan mrp = (MultiReadPlan)rp;
                foreach (ReadPlan r in mrp.Plans)
                {
		    // Ideally, totalWeight=0 would allow reader to
		    // dynamically adjust timing based on tags observed.
		    // For now, just divide equally.
                    int subtimeout = 
			(mrp.TotalWeight != 0) ? (int)milliseconds * r.Weight / mrp.TotalWeight
			: milliseconds / mrp.Plans.Length;
                    subtimeout = Math.Min(subtimeout, UInt16.MaxValue);
                    List<int> subTimeoutList = new List<int>();
                    queries.AddRange(GenerateRql(r, subtimeout, out subTimeoutList));
                    timeoutList.AddRange(subTimeoutList);
                }
            }
            else if (rp is SimpleReadPlan)
            {
                List<string> wheres = new List<string>();
                wheres.AddRange(ReadPlanToWhereClause(rp));
                wheres.AddRange(TagFilterToWhereClause(((SimpleReadPlan)rp).Filter));
                DateTime baseTime = DateTime.Now;
                TagOp op = ((SimpleReadPlan)rp).Op;
                String query;
                if(op!=null && op is Gen2.ReadData)
                {
                    _readFieldNames = _readMetaData;                
                }
               
                query = MakeSelect(_readFieldNames, "tag_id", wheres, milliseconds);
                queries.Add(query);
                timeoutList.Add(milliseconds);
            }
            else
            {
                throw new ArgumentException("Unrecognized /reader/read/plan type " + typeof(ReadPlan).ToString());
            }
            subTimeouts = timeoutList;
            return queries;
        }
Example #5
0
 private List<string> GenerateRql(ReadPlan rp, int milliseconds)
 {
     List<int> timeoutList = null;
     return GenerateRql(rp, milliseconds, out timeoutList);
 }
Example #6
0
        private List<string> SetupCursors(ReadPlan rp, int timeout, out List<int> ctimesOut)
        {
            List<int> ctimes = null;
            List<string> rql = GenerateRql(rp, timeout, out ctimes);
            int cnum = 0;
            List<string> cnames = new List<string>();

            for (int i = 0; i < rql.Count; i++)
            {
                string line = rql[i];
                int ctime = ctimes[i];

                cnum++;
                string cname = String.Format("mapic{0}", cnum);
                cnames.Add(cname);
                string decl = String.Format(
                    "DECLARE {0} CURSOR FOR {1}", cname, line);
                Query(decl);
            }
            ctimesOut = ctimes;
            return cnames;
        }
Example #7
0
        private void ReadInternal(ReadPlan rp, int milliseconds, ref List<TagReadData> reads)
        {
            ResetRql();

            List<int> timeouts;
            List<string> cnames = SetupCursors(rp, milliseconds, out timeouts);

            string cmd = String.Format(
                "FETCH {0}", String.Join(",", cnames.ToArray()));
            SendQuery(cmd, 0);

            for (int i=0; i<cnames.Count; i++)
            {
                DateTime baseTime = DateTime.Now;
                String[] rows = ReceiveBatch(timeouts[i]);
                foreach (string row in rows)
                {
                    if (0 < row.Length)
                    {
                        reads.Add(ParseRqlResponse(row, baseTime));
                    }
                }
            }
            reads = RemoveDuplicates(reads);
            ResetRql();
        }
Example #8
0
 private void ReadInternal(int timeOut, ReadPlan rp)
 {
     roSpecId = 0;
     endOfAISpec = false;
     DeleteRoSpec();
     DeleteAccessSpecs();
     TagQueueEmptyEvent.Reset();
     List<PARAM_ROSpec> roSpecList = new List<PARAM_ROSpec>();
     roSpecProtcolTable = new Hashtable();
     BuildRoSpec(rp, timeOut, roSpecList,false);            
     foreach (PARAM_ROSpec roSpec in roSpecList)
     {
         if (AddRoSpec(roSpec))
         {
             if (EnableRoSpec(roSpec.ROSpecID))
             {
                 if (!StartRoSpec(roSpec.ROSpecID))
                     return;
                 WaitForSearchStart();
                 WaitForSearchEnd(timeOut);
             }
             else
             {
                 return;
             }
         }
         else
         {
             return;
         }
     }
     llrp.OnRoAccessReportReceived -= new delegateRoAccessReport(OnRoAccessReportReceived);
 }
Example #9
0
        private void BuildRoSpec(ReadPlan rp, int timeOut, List<PARAM_ROSpec> roSpecList, bool isStandaloneOp)
        {
            string response = string.Empty;
            if (rp is MultiReadPlan)
            {
                MultiReadPlan mrp = (MultiReadPlan)rp;
                numPlans = mrp.Plans.Length;
                foreach (ReadPlan r in mrp.Plans)
                {
                    // Ideally, totalWeight=0 would allow reader to
                    // dynamically adjust timing based on tags observed.
                    // For now, just divide equally.
                    int subtimeout =
                        (mrp.TotalWeight != 0) ? (int)timeOut * r.Weight / mrp.TotalWeight
                        : timeOut / mrp.Plans.Length;
                    totalWeight = (uint)mrp.TotalWeight;
                    subtimeout = Math.Min(subtimeout, UInt16.MaxValue);
                    BuildRoSpec(r, subtimeout, roSpecList,false);
                }
            }
            else if (rp is SimpleReadPlan)
            {
                //MSG_ADD_ROSPEC msg = new MSG_ADD_ROSPEC();
                // Create a Reader Operation Spec (ROSpec).
                PARAM_ROSpec roSpec = new PARAM_ROSpec();
                roSpec.CurrentState = ENUM_ROSpecState.Disabled;
                roSpec.Priority = 0;
                roSpec.ROSpecID = ++roSpecId;
                //Add rospec id and protocol in the hashtable. So that it can be used to populate the tagdata's protocol member with the read tag protocol.
                roSpecProtcolTable.Add(roSpec.ROSpecID,((SimpleReadPlan)rp).Protocol);
                // Set up the ROBoundarySpec
                // This defines the start and stop triggers.
                roSpec.ROBoundarySpec = new PARAM_ROBoundarySpec();
                // Set the start trigger to null.
                // This means the ROSpec will start as soon as it is enabled.
                roSpec.ROBoundarySpec.ROSpecStartTrigger = new PARAM_ROSpecStartTrigger();
                uint asyncOnTime = Convert.ToUInt16(ParamGet("/reader/read/asyncOnTime"));
                if (continuousReading && numPlans > 1)
                {                    
                    roSpec.ROBoundarySpec.ROSpecStartTrigger.ROSpecStartTriggerType = ENUM_ROSpecStartTriggerType.Periodic;
                    PARAM_PeriodicTriggerValue pValue = new PARAM_PeriodicTriggerValue();
                    pValue.Offset = 0;
                    pValue.Period = asyncOnTime;                    
                    roSpec.ROBoundarySpec.ROSpecStartTrigger.PeriodicTriggerValue = pValue;
                }
                else
                {
                    roSpec.ROBoundarySpec.ROSpecStartTrigger.ROSpecStartTriggerType = ENUM_ROSpecStartTriggerType.Null;
                }

                // Set the stop trigger is null. This means the ROSpec
                // will keep running until an STOP_ROSPEC message is sent.
                roSpec.ROBoundarySpec.ROSpecStopTrigger = new PARAM_ROSpecStopTrigger();
                roSpec.ROBoundarySpec.ROSpecStopTrigger.ROSpecStopTriggerType = ENUM_ROSpecStopTriggerType.Null;

                roSpec.SpecParameter = new UNION_SpecParameter();
                PARAM_AISpec aiSpec = new PARAM_AISpec();
                // Select which antenna ports we want to use based on the read plan settings
                aiSpec.AntennaIDs = new LTKD.UInt16Array();
                SimpleReadPlan srp = (SimpleReadPlan)rp;
                //Check for fast search enable                
                isFastSearch = srp.UseFastSearch;
                //Validate protocol
                ValidateProtocol(srp.Protocol);
                int[] antennas = srp.Antennas;
                if (null == antennas)
                {
                    aiSpec.AntennaIDs.Add(0);               //0 :  applys to all antennae, 
                }
                else
                {
                    foreach (int antenna in antennas)
                        aiSpec.AntennaIDs.Add((ushort)antenna);
                }
                //Specify AI spec trigger type
                aiSpec.AISpecStopTrigger = new PARAM_AISpecStopTrigger();
                //Specify Report spec
                roSpec.ROReportSpec = new PARAM_ROReportSpec();
                if (continuousReading)
                {
                    // ASYNC Mode - Set the AI stop trigger to null. AI spec will run until the ROSpec stops.
                    if(numPlans  > 1)
                    {
                         // ASYNC Mode - Set the AI stop trigger to Duration - AsyncOnTime. AI spec will run until the Disable ROSpec is sent.
                        aiSpec.AISpecStopTrigger.AISpecStopTriggerType = ENUM_AISpecStopTriggerType.Duration;
                        aiSpec.AISpecStopTrigger.DurationTrigger = (uint)timeOut;
                    }
                    else
                    {
                        aiSpec.AISpecStopTrigger.AISpecStopTriggerType = ENUM_AISpecStopTriggerType.Null;
                        aiSpec.AISpecStopTrigger.DurationTrigger = 0;
                    }
                    // Receive a report every time a tag is read.
                    roSpec.ROReportSpec.N = 1;
                }
                else
                {
                    // SYNC Mode - Set the AI stop trigger to inputted duration. AI spec will run for particular duration
                    aiSpec.AISpecStopTrigger.AISpecStopTriggerType = ENUM_AISpecStopTriggerType.Duration;
                    aiSpec.AISpecStopTrigger.DurationTrigger = (uint)timeOut;
                    roSpec.ROReportSpec.N = 0;                    
                }
                PARAM_InventoryParameterSpec inventoryParam = new PARAM_InventoryParameterSpec();
                List<PARAM_InventoryParameterSpec> invParamList = new List<PARAM_InventoryParameterSpec>();
                inventoryParam.InventoryParameterSpecID = 1;
                TagFilter tagFilter = srp.Filter;
                //Filter
                if(tagFilter != null)
                {
                    List<PARAM_AntennaConfiguration> antennaConfigList = new List<PARAM_AntennaConfiguration>();
                    PARAM_AntennaConfiguration antConfig = new PARAM_AntennaConfiguration();
                    antConfig.AntennaID = 0;
                    if (TagProtocol.GEN2.Equals(srp.Protocol))
                    {
                    List<PARAM_C1G2Filter> filterList = new List<PARAM_C1G2Filter>();
                    PARAM_C1G2Filter filter = new PARAM_C1G2Filter();
                    PARAM_C1G2TagInventoryMask mask;
                    filter.T = ENUM_C1G2TruncateAction.Do_Not_Truncate;

                    PARAM_C1G2InventoryCommand inventoryCommand = new PARAM_C1G2InventoryCommand();
                    inventoryCommand.TagInventoryStateAware = false;

                    PARAM_C1G2TagInventoryStateUnawareFilterAction unAwareAction = new PARAM_C1G2TagInventoryStateUnawareFilterAction();
                    unAwareAction.Action = ENUM_C1G2StateUnawareAction.Select_Unselect;            

                    if (tagFilter is Gen2.Select)
                    {
                        Gen2.Select selectFilter = (Gen2.Select)tagFilter;
                        mask = new PARAM_C1G2TagInventoryMask();

                        // Memory Bank
                        mask.MB = new LTKD.TwoBits((ushort)selectFilter.Bank);
                        mask.TagMask = LTKD.LLRPBitArray.FromHexString(ByteFormat.ToHex(selectFilter.Mask).Split('x')[1].ToString());
                        mask.Pointer = (ushort)selectFilter.BitPointer;
                        filter.C1G2TagInventoryMask = mask;

                        if (selectFilter.Invert)
                        {
                            unAwareAction.Action = ENUM_C1G2StateUnawareAction.Unselect_Select;
                        }
                        filter.C1G2TagInventoryStateUnawareFilterAction = unAwareAction;                                        
                    }
                    else if (tagFilter is TagData)
                    {
                        TagData tagDataFilter = (TagData)tagFilter;
                        mask = new PARAM_C1G2TagInventoryMask();

                        // EPC Memory Bank 
                        mask.MB = new LTKD.TwoBits((ushort)Gen2.Bank.EPC);
                        mask.TagMask = LTKD.LLRPBitArray.FromHexString(tagDataFilter.EpcString);
                        //For epc bit pointer is 32
                        mask.Pointer = 32;

                        filter.C1G2TagInventoryMask = mask;
                        filter.C1G2TagInventoryStateUnawareFilterAction = unAwareAction;                    
                    }
                    else
                    {
                        throw new Exception("Unsupported operation");
                    }
                    filterList.Add(filter);
                    inventoryCommand.C1G2Filter = filterList.ToArray();
                    antConfig.AirProtocolInventoryCommandSettings.Add(inventoryCommand);
                    antennaConfigList.Add(antConfig);
                    inventoryParam.AntennaConfiguration = antennaConfigList.ToArray();
                    }
                    else if (TagProtocol.ISO180006B.Equals(srp.Protocol))
                    {
                        if (tagFilter is Iso180006b.Select)
                        {
                            PARAM_ThingMagicISO180006BTagPattern tagPattern = new PARAM_ThingMagicISO180006BTagPattern();
                            //Filter type
                            tagPattern.FilterType = ENUM_ThingMagicISO180006BFilterType.ISO180006BSelect;
                            //Invert
                            tagPattern.Invert = ((Iso180006b.Select)tagFilter).Invert;
                            //Address
                            tagPattern.Address = Convert.ToByte(((Iso180006b.Select)tagFilter).Address.ToString("X"));
                            //Mask
                            tagPattern.Mask = ((Iso180006b.Select)tagFilter).Mask;
                            //SelectOp
                            tagPattern.SelectOp = new Org.LLRP.LTK.LLRPV1.DataType.TwoBits(Convert.ToUInt16(((Iso180006b.Select)tagFilter).Op));
                            //TagData
                            tagPattern.TagData = LTKD.ByteArray.FromHexString(ByteFormat.ToHex(((Iso180006b.Select)tagFilter).Data).Split('x')[1]);
                            PARAM_ThingMagicISO180006BInventoryCommand iso18k6bInventoryCmd = new PARAM_ThingMagicISO180006BInventoryCommand();
                            iso18k6bInventoryCmd.ThingMagicISO180006BTagPattern = tagPattern;
                            antConfig.AirProtocolInventoryCommandSettings.Add(iso18k6bInventoryCmd);
                            antennaConfigList.Add(antConfig);
                            inventoryParam.AntennaConfiguration = antennaConfigList.ToArray();
                        }
                        else if(tagFilter is TagData)
                        {
                            PARAM_ThingMagicISO180006BTagPattern tagPattern = new PARAM_ThingMagicISO180006BTagPattern();
                            //Filter type
                            tagPattern.FilterType = ENUM_ThingMagicISO180006BFilterType.ISO180006BTagData;
                            //Invert
                            tagPattern.Invert = false;
                            //Address
                            tagPattern.Address = 0;
                            //Mask
                            tagPattern.Mask = 0xff;
                            //SelectOp
                            tagPattern.SelectOp = new Org.LLRP.LTK.LLRPV1.DataType.TwoBits(Convert.ToUInt16(((Iso180006b.SelectOp.EQUALS))));
                            //TagData
                            tagPattern.TagData = LTKD.ByteArray.FromHexString(((TagData)tagFilter).EpcString);
                            PARAM_ThingMagicISO180006BInventoryCommand iso18k6bInventoryCmd = new PARAM_ThingMagicISO180006BInventoryCommand();
                            iso18k6bInventoryCmd.ThingMagicISO180006BTagPattern = tagPattern;
                            antConfig.AirProtocolInventoryCommandSettings.Add(iso18k6bInventoryCmd);
                            antennaConfigList.Add(antConfig);
                            inventoryParam.AntennaConfiguration = antennaConfigList.ToArray();
                        }
                        else
                        {
                            throw new Exception("Unsupported operation");
                        }
                    }
                }
                if (isFastSearch)
                {
                    List<PARAM_AntennaConfiguration> antennaConfigList = new List<PARAM_AntennaConfiguration>();
                    PARAM_AntennaConfiguration antConfig = new PARAM_AntennaConfiguration();                    
                    PARAM_ThingMagicFastSearchMode fastSearch = new PARAM_ThingMagicFastSearchMode();
                    fastSearch.ThingMagicFastSearch = ENUM_ThingMagicFastSearchValue.Enabled;
                    PARAM_C1G2InventoryCommand inventoryCommandFastSearch = new PARAM_C1G2InventoryCommand();
                    inventoryCommandFastSearch.AddCustomParameter(fastSearch);
                    antConfig.AirProtocolInventoryCommandSettings.Add(inventoryCommandFastSearch);
                    antennaConfigList.Add(antConfig);
                    inventoryParam.AntennaConfiguration = antennaConfigList.ToArray();
                }

                //Emebeded tagops
                TagOp tagOperation = srp.Op;
                PARAM_AccessCommand accessCommand = new PARAM_AccessCommand();
                PARAM_AccessSpec accessSpec = new PARAM_AccessSpec();
                if (null != tagOperation)
                {
                    
                    accessSpec.AccessSpecID = ++AccessSpecID;
                    accessSpec.AccessCommand = accessCommand;
                    accessSpec.ROSpecID = roSpecId;

                    PARAM_AccessSpecStopTrigger trigger = new PARAM_AccessSpecStopTrigger();
                    if (!isStandaloneOp)//Embedded operation
                    {
                        if (tagOperation is Gen2.NxpGen2TagOp.EasAlarm)
                        {
                            throw new FeatureNotSupportedException("Gen2.NxpGen2TagOp.EasAlarm command can be standalone tag operation ");
                        }
                        if (tagOperation is Gen2.NXP.G2X.ResetReadProtect)
                        {
                            throw new FeatureNotSupportedException("NXP Reset Read protect command can be embedded only if the chip-type is G2il");
                        }
                        accessSpec.AntennaID = 0;
                        trigger.AccessSpecStopTrigger = ENUM_AccessSpecStopTriggerType.Null;
                        trigger.OperationCountValue = 0;
                    }
                    else
                    { //standalone operation
                        if (tagOperation is Gen2.Alien.Higgs2.PartialLoadImage)
                        {
                            if (null != tagFilter)
                            {
                                throw new ReaderException("Filter is not supported on this operation.");
                            }
                        }
                        accessSpec.AntennaID = Convert.ToUInt16(ParamGet("/reader/tagop/antenna"));
                        trigger.AccessSpecStopTrigger = ENUM_AccessSpecStopTriggerType.Operation_Count;
                        trigger.OperationCountValue = 1;
                    }

                    accessCommand.AccessCommandOpSpec.Add(BuildOpSpec(srp));
                    accessSpec.ProtocolID = ENUM_AirProtocols.EPCGlobalClass1Gen2;
                    accessSpec.CurrentState = ENUM_AccessSpecState.Disabled;
                    accessSpec.AccessSpecStopTrigger = trigger;
                    
                    // Add a list of target tags to the tag spec.

                    PARAM_C1G2TagSpec tagSpec = new PARAM_C1G2TagSpec();
                    PARAM_C1G2TargetTag targetTag = new PARAM_C1G2TargetTag();
                    targetTag.MB = new LTKD.TwoBits(0);
                    targetTag.Match = false;
                    targetTag.Pointer = 0;
                    targetTag.TagData = LTKD.LLRPBitArray.FromBinString("0");
                    targetTag.TagMask = LTKD.LLRPBitArray.FromBinString("0");

                    List<PARAM_C1G2TargetTag> targetTagList = new List<PARAM_C1G2TargetTag>();
                    targetTagList.Add(targetTag);
                    tagSpec.C1G2TargetTag = targetTagList.ToArray();
                    

                     //Add the tag spec to the access command.
                    accessCommand.AirProtocolTagSpec.Add(tagSpec);
                    AddAccessSpec(accessSpec);
                    EnableAccessSpec(accessSpec.AccessSpecID);
                }
                
                // Reading Gen2 Tags, specify in InventorySpec
                if (TagProtocol.GEN2 == srp.Protocol)
                {
                    inventoryParam.ProtocolID = ENUM_AirProtocols.EPCGlobalClass1Gen2;
                }
                else if (TagProtocol.ISO180006B == srp.Protocol)
                {
                    inventoryParam.ProtocolID = ENUM_AirProtocols.Unspecified;
                    PARAM_ThingMagicCustomAirProtocols airProtocol = new PARAM_ThingMagicCustomAirProtocols();
                    airProtocol.customProtocolId = ENUM_ThingMagicCustomAirProtocolList.Iso180006b;
                    inventoryParam.Custom.Add(airProtocol);
                }
                else
                {
                    throw new FeatureNotSupportedException("Only GEN2 and ISO18K6B protocol is supported as of now");
                }
                invParamList.Add(inventoryParam);
                aiSpec.InventoryParameterSpec = invParamList.ToArray();
                // Specify what type of tag reports we want to receive and when we want to receive them.
                roSpec.ROReportSpec.ROReportTrigger = ENUM_ROReportTriggerType.Upon_N_Tags_Or_End_Of_ROSpec;

                roSpec.SpecParameter.Add(aiSpec);
                // Selecting which fields we want in the report.
                roSpec.ROReportSpec.TagReportContentSelector = new PARAM_TagReportContentSelector();
                roSpec.ROReportSpec.TagReportContentSelector.EnableAccessSpecID = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnableAntennaID = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnableChannelIndex = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnableFirstSeenTimestamp = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnableInventoryParameterSpecID = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnableLastSeenTimestamp = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnablePeakRSSI = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnableROSpecID = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnableSpecIndex = true;
                roSpec.ROReportSpec.TagReportContentSelector.EnableTagSeenCount = true;                

                // By default both PC and CRC bits are set, so sent from tmmpd
                PARAM_C1G2EPCMemorySelector gen2MemSelector = new PARAM_C1G2EPCMemorySelector();
                gen2MemSelector.EnableCRC = true;
                gen2MemSelector.EnablePCBits = true;
                roSpec.ROReportSpec.TagReportContentSelector.AirProtocolEPCMemorySelector.Add(gen2MemSelector);
                // Since Spruce release firmware doesn't support phase, don't add 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))
                {
                        PARAM_ThingMagicTagReportContentSelector tagReportContentSelector = new PARAM_ThingMagicTagReportContentSelector();
                        tagReportContentSelector.PhaseMode = ENUM_ThingMagicPhaseMode.Enabled;
                        roSpec.ROReportSpec.AddCustomParameter(tagReportContentSelector);                
                }
                roSpecList.Add(roSpec);
            }
        }