Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wisp
GitHub Repository: wisp/impinj-reader-app
Path: blob/master/ReaderLibrary/ReaderManager.cs
181 views
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using System.Net;
using System.Diagnostics;
using System.Xml;

using LLRP;
using ReaderLibrary;
using LLRP.DataType;

namespace ReaderLibrary
{
    public class ReaderManager
    {
        private RFIDReader reader;
        private ITagHandler handleTags;

        //Create an instance of LLRP client client.
        //public LLRPClient client = new LLRPClient();

        // Store reader-capable modulation modes
        public ReaderMode[] readerModes;

        //Configuration
        public ReaderConfig readerconfig = new ReaderConfig();
        protected InventoryConfig inventoryconfig = new InventoryConfig();
        protected ReadCmdSettings readcmdsettings = new ReadCmdSettings();

        private GuiModes currentMode;
        public enum GuiModes
        {
            Idle,  // reader disconnected.
            Ready, // reader is connected.
            UserInventory,
            AttenuatorTest
        }

        // Read/Write access settings can be added from ready or userinventory states.
        // So just keep as a separate variable.
        private bool readConfigured = false; 
        
        private IRFIDGUI gui;

        public ReaderManager(IRFIDGUI newgui, ITagHandler handleTagNew)
        {
            gui = newgui;
            SetDefaultReaderConfig();
            SetDefaultInventoryConfig();
            reader = new RFIDReader(this);
            handleTags = handleTagNew; 
        }

        // Helper method to connect Reader to MainForm
        public void HandleTagReceived(MyTag tag) 
        {
            if (tag == null)
            {
                // todo - some gui display thing
                //AppendToMainTextBox("No Tags Seen");
            }
            else if (IsInventoryRunning())  // if reader is running
            {
                handleTags.HandleTagReceived(tag);
            }
        }

        public void AppendToDebugTextBox(string message)
        {
            gui.AppendToDebugTextBox(message);
        }

        #region Public Reader Interfaces


        public GuiModes getCurrentMode()
        {
            return currentMode;
        }

        public void SetMode(GuiModes newMode, string txtIPAddress)
        {
            // Set current mode = new Mode,
            // this will be changed if there is an error.
            GuiModes oldMode = currentMode;

            if(newMode == GuiModes.AttenuatorTest)
                throw new Exception("Can't set mode to AttenuatorTest!");

            // Switch performs the action
            switch (newMode)
            {
                case GuiModes.Idle:
                    if (IsConnected())
                    {
                        Disconnect();
                    }
                    break;

                case GuiModes.Ready:
                    // If we were disconnected, connect.
                    if (oldMode == GuiModes.Idle || oldMode == GuiModes.Ready)
                    {
                        if (!IsConnected())
                        {
                            // CONNECT!
                            Connect(txtIPAddress);
                            // if connect fails, an exception will be thrown
                            // if success, currentMode will be set after this switch statement
                        }
                    }
                    // If we were running, stop various modes:
                    else if (oldMode == GuiModes.UserInventory)
                    {
                        if (IsConnected()) 
                        {
                            StopInventory();
                        }
                    }
                    break;


                case GuiModes.UserInventory:
                    if (!IsConnected())
                    {
                        currentMode = GuiModes.Idle;
                    }
                    else if (oldMode == GuiModes.Ready)
                    {
                        StartInventory();
                    }
                    break;

                default: 
                    throw new Exception("Can't set mode to unknown state!");
                    //break;
            }

            // if no exceptions have interrupted us, we successfully got to the new mode:
            currentMode = newMode;

        }



        /// <summary>
        /// Connect to Reader
        /// </summary>
        public void Connect(string ipAddress)
        {
            // check for dumb errors.
            if (IsConnected())
                throw new Exception("Already connected");

            if (ipAddress.Length == 0)
                throw new Exception("Bad IP");

            IPAddress address = null;

            // look for url-based addresses
            if (ipAddress.ToLower().Contains("speedway"))
            {
                IPAddress[] addresses;
                addresses = Dns.GetHostEntry(ipAddress).AddressList;

                if (addresses.Length >= 1)
                    address = addresses[0];
                else
                    throw new Exception("Hostname not found.");
            }
            else
            {
                if (!System.Net.IPAddress.TryParse(ipAddress, out address))
                    throw new Exception("Bad IP Address");
                address = System.Net.IPAddress.Parse(ipAddress);
            }

            if (address != null)
            {
                bool connect =  reader.connectTo(ipAddress);
                if (connect)
                {
                    currentMode = GuiModes.Ready;

                    readerModes = reader.Get_Reader_Capability();
                    SetDefaultReaderConfig();
                    SetDefaultInventoryConfig();

                    //WriteMessage(status.ToString());

                    reader.Initialize();
                }
                else
                {
                    throw new Exception("Bad IP or Reader in use.");
                }
            }

            else
            {
                // MessageBox.Show("Need IP address to connect to client", "LLRP Test", MessageBoxButtons.OK, MessageBoxIcon.Error);
                // txtIPAddress.Focus();
                throw new Exception("Need IP address to connect to client");
            }

        }

        
        /// <summary>
        /// disconnect from Reader
        /// </summary>
        public void Disconnect()
        {
            // check if doing inventory
            if (IsInventoryRunning())
            {
                StopInventory();
            }

            // check if doing read
            if (IsReadRunning())
            {
                StopRead();
            }

            if(IsConnected())
            {
                reader.CleanSubscriptionClient();
            }

            currentMode = GuiModes.Idle;

        }

        public void StartInventory()
        {
            if (!IsConnected())
            {
                gui.AppendToDebugTextBox("Can't start inventory: Reader not connected.");
                return;
            }

            //reader.DELETE_ACCESSSPEC();  // clear out any old read commands

            reader.Set_Reader_Config(readerconfig);   // Sets the client configuration

            currentMode = GuiModes.UserInventory;

            // Add a ROSpec
            reader.Add_RoSpec(inventoryconfig, readerconfig);
            reader.Enable_RoSpec();
        }

        // This guy checks to see if cmd is different than last time before executing.
        public void RestartRead(ReadCmdSettings cmd)
        {
            if (!IsConnected())
            {
                gui.AppendToDebugTextBox("Can't start read: Reader not connected.");
                return;
            }

            if (IsReadRunning() && !cmd.Equals(readcmdsettings))
            {
                StopRead();
                StartRead(cmd);
            }
            else if (!IsReadRunning()) // idiot proof
            {
                StartRead(cmd);
            }
        }

        public void StartRead(ReadCmdSettings cmd)
        {
            if(IsReadRunning())
            {
                gui.AppendToDebugTextBox("Read already configured - use ReStart Read");
            }
            else if(!IsConnected())
            {
                gui.AppendToDebugTextBox("Can't start read: Reader not connected.");
            }
            else
            {
                //AddReadAccessSpec("0D00", "0000000000000000", 1, 0);
                reader.AddReadAccessSpec(cmd.tagID, cmd.mask, cmd.wordCount, cmd.wordPtr, readerconfig);
                reader.ENABLE_ACCESSSPEC();
                readcmdsettings = cmd;
                readConfigured = true;
            }

            /*
            Set_Reader_Config();   // Sets the client configuration
            readmode = true;   // this flag is used for special settings in the Add_RoSpec method

            // stop all Specs
            Delete_RoSpec();
            DELETE_ACCESSSPEC();

            // add new roSpec and accessSpec
            AddReadAccessSpec("0D00", "0000000000000000", 1, 1);
            Add_RoSpec();

            // enable new accessSpec and RoSpec
            ENABLE_ACCESSSPEC();
            Enable_RoSpec();
            */
        }

        public void StopInventory()
        {
            if (IsInventoryRunning())
            {
                reader.Stop_RoSpec();
                reader.Delete_RoSpec();
                currentMode = GuiModes.Ready;
            }
        }

        public void StopRead()
        {
            if (IsReadRunning())
            {
                //Stop_RoSpec();
                //Delete_RoSpec();
                reader.DELETE_ACCESSSPEC();
                readConfigured = false;
            }
        }

        public bool IsInventoryRunning()
        {
            return (currentMode == GuiModes.UserInventory);
        }

        public bool IsReadRunning()
        {
            return readConfigured;
        }

        public bool IsConnected()
        {
            return (currentMode != GuiModes.Idle);
        }


        public ReaderConfig getReaderConfig()
        {
            return readerconfig;
        }
        public InventoryConfig getInventoryConfig()
        {
            return inventoryconfig;
        }

        public ReaderMode[] GetReaderModulationModes()
        {
            return readerModes;
        }

        public ReaderMode FindMode(uint modeIdentifier)
        {
            for (int i = 0; i < readerModes.Length; i++)
                if (readerModes[i].GetModeIdentifier() == modeIdentifier)
                    return readerModes[i];
            return null;
        }


        #endregion

        #region Data Structures


        public struct ReaderConfig
        {
            public ushort attenuation;
            public ushort modeIndex;
            public ushort tagPopulation;
            public ushort tagTransitTime;
            public bool[] antennaID;
            public ushort readerSensitivity;
            public ushort channelIndex;
            public ushort hopTableIndex;
            public ushort periodicTriggerValue;
        }

        public struct InventoryConfig
        {
            public LLRP.ENUM_ROSpecStartTriggerType startTrigger;
            public LLRP.ENUM_ROSpecStopTriggerType stopTrigger;
            public LLRP.ENUM_AISpecStopTriggerType AITriggerType;
            public ushort duration;
            public ushort numAttempts;
            public ushort numTags;
            public LLRP.ENUM_ROReportTriggerType reportTrigger;
            public ushort reportN;
            public uint AITimeout;
        }


        public void SetDefaultReaderConfig()
        {
            readerconfig.attenuation = 0;
            readerconfig.modeIndex = 2;
            readerconfig.tagPopulation = 1;
            readerconfig.tagTransitTime = 0;
            readerconfig.antennaID = new bool[] { true, false, false, false };
            readerconfig.readerSensitivity = 1;
            readerconfig.channelIndex = 0;
            readerconfig.hopTableIndex = 1;
            readerconfig.periodicTriggerValue = 0;
        }

        public void setReaderConfig(ReaderConfig config)
        {
            readerconfig.attenuation = config.attenuation;
            readerconfig.modeIndex = config.modeIndex;
            readerconfig.tagPopulation = config.tagPopulation;
            readerconfig.tagTransitTime = config.tagTransitTime;
            readerconfig.antennaID = config.antennaID;
            readerconfig.readerSensitivity = config.readerSensitivity;
            readerconfig.channelIndex = config.channelIndex;
            readerconfig.hopTableIndex = config.hopTableIndex;
            readerconfig.periodicTriggerValue = config.periodicTriggerValue;
        }

        public void SetDefaultInventoryConfig()
        {
            inventoryconfig.startTrigger = ENUM_ROSpecStartTriggerType.Immediate;
            inventoryconfig.stopTrigger = ENUM_ROSpecStopTriggerType.Null;
            inventoryconfig.AITriggerType = ENUM_AISpecStopTriggerType.Duration;
            inventoryconfig.duration = 100;
            inventoryconfig.numAttempts = 1;
            inventoryconfig.numTags = 1;
            inventoryconfig.reportTrigger = ENUM_ROReportTriggerType.Upon_N_Tags_Or_End_Of_AISpec;
            inventoryconfig.reportN = 1;
            inventoryconfig.AITimeout = 0;
        }

        public void setInventoryConfig(InventoryConfig config)
        {
            inventoryconfig.startTrigger = config.startTrigger;
            inventoryconfig.stopTrigger = config.stopTrigger;
            inventoryconfig.AITriggerType = config.AITriggerType;
            inventoryconfig.duration = config.duration;
            inventoryconfig.numAttempts = config.numAttempts;
            inventoryconfig.numTags = config.numTags;
            inventoryconfig.reportTrigger = config.reportTrigger;
            inventoryconfig.reportN = config.reportN;
            inventoryconfig.AITimeout = config.AITimeout;
        }

        public class ReaderMode
        {
            uint modeIndentifier;
            ENUM_C1G2DRValue dr;
            ENUM_C1G2MValue m;
            uint tagRate;
            ENUM_C1G2ForwardLinkModulation linkModulation;
            uint pie;
            uint minTari;
            uint maxTari;

            public ReaderMode(PARAM_C1G2UHFRFModeTableEntry mode)
            {
                modeIndentifier = mode.ModeIdentifier;
                dr = mode.DRValue;
                m = mode.MValue;
                tagRate = mode.BDRValue;
                linkModulation = mode.ForwardLinkModulation;
                pie = mode.PIEValue;
                minTari = mode.MinTariValue;
                maxTari = mode.MaxTariValue;
            }

            public uint GetModeIdentifier()
            {
                return modeIndentifier;
            }

            override public string ToString()
            {
                const int stop1 = 13;
                const int stop2 = stop1 + 8;
                const int stop3 = stop2 + 15;

                // Mode Identifier
                string outstr = "[" + modeIndentifier.ToString() + "]";
                outstr = PadSpaces(stop1, outstr);
                // Miller
                switch (m)
                {
                    case ENUM_C1G2MValue.MV_FM0:
                        outstr += "FM0";
                        break;
                    case ENUM_C1G2MValue.MV_2:
                        outstr += "M2";
                        break;
                    case ENUM_C1G2MValue.MV_4:
                        outstr += "M4";
                        break;
                    case ENUM_C1G2MValue.MV_8:
                        outstr += "M8";
                        break;

                }
                outstr = PadSpaces(stop2, outstr);

                // Divide Ratio
                switch (dr)
                {
                    case ENUM_C1G2DRValue.DRV_8:
                        outstr += "DR 64/3";
                        break;
                    case ENUM_C1G2DRValue.DRV_64_3:
                        outstr += "DR 8";
                        break;
                }

                outstr = PadSpaces(stop3, outstr);

                // Tari
                outstr += "Tari = " + ((double)(minTari / 1000.0)).ToString() + " us";

                return outstr;
            }
            private string PadSpaces(int desiredTotalLength, string input)
            {
                for (int i = input.Length; i < desiredTotalLength; i++)
                    input += " ";
                return input;
            }
        }

        public struct ReadCmdSettings
        {
            public string tagID;
            public string mask;
            public ushort wordPtr;
            public ushort wordCount;
            public ReadCmdSettings(string tagID, string mask, ushort wordCount, ushort wordPtr)
            {
                this.tagID = tagID;
                this.mask = mask;
                this.wordPtr = wordPtr;
                this.wordCount = wordCount;
            }
            public override bool Equals(object obj)
            {
                if (obj.GetType().Name != "ReadCmdSettings") return false;
                ReadCmdSettings compareTo = (ReadCmdSettings)obj;
                if (compareTo.tagID != this.tagID) return false;
                if (compareTo.mask != this.mask) return false;
                if (compareTo.wordPtr != this.wordPtr) return false;
                if (compareTo.wordCount != this.wordCount) return false;
                return true;
            }
            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
        }

        #endregion

        #region Printing and Debugging Methods

        private void WriteMessage(string rsp)
        {
            WriteMessage(rsp, null);
        }

        public void WriteMessage(string rsp, string source)
        {
            // Clear textbox
            //textBox2.clear();
            //gui.AppendToDebugTextBox("\r\n===================================\r\n");
            gui.AppendToDebugTextBox(source + "\r\n");

            // let user know operation completed
            gui.AppendToDebugTextBox(parseXML(rsp));
            gui.AppendToDebugTextBox("===================================\r\n");
        }

        public void WriteString(string info)
        {
            gui.AppendToDebugTextBox(info);
            gui.AppendToDebugTextBox("===================================\r\n");
        }

        private string parseXML(string msg)
        {
            // divide XML message into chunks
            string str = msg.ToString();
            string[] strArr = str.Split(new string[] { "><" }, StringSplitOptions.None);
            string message = null;

            // parse relevant string
            for (int i = 0; i < strArr.Length; i++)
            {
                if (strArr[i].Contains("</"))
                {
                    string[] temp = strArr[i].Split(new char[] { '>' });
                    message = message + temp[0] + " = " + (temp[1].Split(new string[] { "</" }, StringSplitOptions.None))[0] + "\r\n";
                }
            }

            return message;
        }

        #endregion

    }
}