Click here to Skip to main content
15,867,308 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I've written a program which could connect to several RFID Reader and start to detect tags simultaneously. But because all of them are working on UI-Thread (Main Thread) my both UI and reading function become too slow every time I add a new reader...
I need to create a new thread for each reader and let that reader works with the new worker thread.
But I don't know where have I import the new Thread() function in my c# program.
I have a windows form timer for reading tags.

What I have tried:

C#
 void timer_Tick(object sender, EventArgs e)
        {
            Reader();
        }

Public void Reader(){
int fCmdRet = StaticClassReaderB.Inventory_G2(ref fComAdr, Qvalue, Session, AdrTID, LenTID, TIDFlag, EPC, ref Totallen, ref CardNum, frmcomportindex);

                if ((fCmdRet == 1) | (fCmdRet == 2) | (fCmdRet == 3) | (fCmdRet == 4) | (fCmdRet == 0xFB))//The search is over
                {
                    if (CardNum == 0)
                    {
                        return;
                    }
                    byte[] daw = new byte[Totallen];
                    Array.Copy(EPC, daw, Totallen);
                    temps = ByteArrayToHexString(daw);
                    //fInventory_EPC_List = temps;            //Store records
                    if (temps == "") return;
                    m = 0;
                    for (CardIndex = 0; CardIndex < CardNum; CardIndex++)
                    {
                        EPClen = daw[m];
                        string TID = temps.Substring(m * 2 + 2, EPClen * 2);
                        lastEPC = TID;
                        m = m + EPClen + 2;
                        value = TID;
                        //OnScan(this, new EventArgs());
                    }
               }


I have two class as below:
C#
public partial class RfControl : UserControl
    {
        RfReader rf = new RfReader();
        public RfControl()
        {
            InitializeComponent();
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            try
            {
                rf.type = this.cbType.Text;
                rf.IPAddr = this.txtIP.Text.Trim();
                rf.port = Convert.ToInt32(this.txtPort.Text.Trim());
                rf.OnScan += new EventHandler(rf_OnScan);
                rf.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        void rf_OnScan(object sender, EventArgs e)
        {
            try
            {
                string value = ((RfReader)sender).value;
                if (this.lstContent.Items.IndexOf(value) == -1)
                {
                    this.lstContent.Items.Add(value);
                }
                this.txtCount.Text = this.lstContent.Items.Count.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            try
            {
                rf.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            this.lstContent.Items.Clear();
            this.txtCount.Text = "0";
        }

        private void btnQuery_Click(object sender, EventArgs e)
        {
           rf.timer.Enabled = !rf.timer.Enabled;
        }
}

C++
class RfReader
    {
        Api api = new Api();
        ArrayList idList = new ArrayList();
        byte[,] tagData = null;
        byte v1 = 0;
        byte v2 = 0;
        int status = 0;
        int iMembankMask = 0, iStartAddr_Reserve = 0, iStartAddr_EPC, iStartAddr_TID, iStartAddr_User, reserveLen, epcLen, tidLen, userLen, readCnt, wordLen = 0;
        public int frmcomportindex;
        public byte readerAddr = 0xff;
        private byte fComAdr = 0xff;
        public int port = -1, openresult = 0;
        public string IPAddr = "";
        public string type = "";
        public string value = "";
        public string X = "";
        public string Y = "";
        public string Name = "";
        public System.Windows.Forms.Timer timer = null;
        private static object _locker = new object();
        public RfReader()
        {
            timer = new System.Windows.Forms.Timer();
            timer.Interval = 1;
            timer.Tick += new EventHandler(timer_Tick);
          
        }
        void timer_Tick(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem((object state) => { Reader(); });
                
            
        }
        public void Open()
        {
            if (type == "AR")
            {
                if (!api.isNetWorkConnect(IPAddr))
                {
                    api.TcpCloseConnect();
                    throw new Exception(IPAddr + " Open failed");
                }
                status = api.TcpConnectReader(IPAddr, Convert.ToInt32(port));
                if (status != ReaderApi.Api.SUCCESS_RETURN)
                {
                    api.TcpCloseConnect();
                    throw new Exception(IPAddr + " Open failed");
                }
                status = api.GetFirmwareVersion(255, ref v1, ref v2);
                if (status != ReaderApi.Api.SUCCESS_RETURN)
                {
                    api.TcpCloseConnect();
                    throw new Exception(IPAddr + " Open failed");
                }
                iStartAddr_Reserve = iStartAddr_EPC = iStartAddr_TID = iStartAddr_User = reserveLen = epcLen = tidLen = userLen = readCnt = 0;
                iMembankMask = 0;
                wordLen = 6;
                iMembankMask += 1;
                wordLen += 4;
                iStartAddr_Reserve = 0;
                reserveLen = 4;
                iMembankMask += 2;
                wordLen += 8;
                iStartAddr_EPC = 0;
                epcLen = 8;
                iMembankMask += 4;
                wordLen += 12;
                iStartAddr_TID = 0;
                tidLen = 12;
                iMembankMask += 8;
                wordLen += 10;
                iStartAddr_User = 0;
                userLen = 10;
                tagData = new byte[500, wordLen * 4];
                idList.Clear();
                throw new Exception(IPAddr + " Open Success");
            }
            else if (type == "ZK")
            {
                byte readerAddr = Convert.ToByte("FF", 16); // $FF;
                openresult = StaticClassReaderB.OpenNetPort(port, IPAddr, ref fComAdr, ref frmcomportindex);
                if ((openresult == 0x35) || (openresult == 0x30))
                {
                    StaticClassReaderB.CloseNetPort(frmcomportindex);
                    throw new Exception(IPAddr + " Open failed");
                }
                if (openresult == 0)
                {
                    throw new Exception(IPAddr + " Open Success");
                }
            }
        }
        public void Close()
        {
            if (type == "AR")
            {
                api.TcpCloseConnect();
            }
            else if (type == "ZK")
            {
                StaticClassReaderB.CloseNetPort(frmcomportindex);
                frmcomportindex = -1;
            }
        }
        public event EventHandler OnScan;
        public void Reader()
        {
            if (type == "AR")
            {
                readCnt = 0;
                int getCount = 0;
                string strTemp = "", strAnteNo = "", strEPC = "", strSubEPC = "", strTID = "", strReserve = "", strUser = "";
                status = api.Gen2MultiTagRead(255, (byte)iMembankMask, (byte)iStartAddr_Reserve, (byte)reserveLen, (byte)iStartAddr_EPC, (byte)epcLen, (byte)iStartAddr_TID, (byte)tidLen, (byte)iStartAddr_User, (byte)userLen, ref readCnt);

                if (status == Api.SUCCESS_RETURN && readCnt > 0)
                {
                    if (api.GetTagData(255, ref tagData, readCnt, ref getCount) == Api.SUCCESS_RETURN && getCount > 0)
                    {
                        for (int loop = 0; loop < getCount; loop++)
                        {
                            strTemp = "";
                            for (int j = 1; j <= (int)tagData[loop, 0]; j++)
                                strTemp += string.Format("{0:X2}", tagData[loop, j]);
                            strEPC = strTemp.Substring(2, 24);
                            strReserve = strTemp.Substring(26, reserveLen * 4);
                            strSubEPC = strTemp.Substring(26 + reserveLen * 4, epcLen * 4);
                            value = strTemp.Substring((26 + reserveLen * 4 + epcLen * 4) + 8, 16);
                            strUser = strTemp.Substring(26 + reserveLen * 4 + epcLen * 4 + tidLen * 4, userLen * 4);
                            OnScan(this, new EventArgs());
                        }
                    }
                }
            }
            else if (type == "ZK")
            {
                
                int CardNum = 0, Totallen = 0, EPClen, m, CardIndex;
                byte[] EPC = new byte[5000];
                string temps, s;
                byte AdrTID = 0, LenTID = 0, TIDFlag = 0;
                AdrTID = 2;
                LenTID = 4;
                TIDFlag = 1;
                byte Qvalue = 4;
                byte Session = 0;
                string lastEPC = "";
                int fCmdRet = StaticClassReaderB.Inventory_G2(ref fComAdr, Qvalue, Session, AdrTID, LenTID, TIDFlag, EPC, ref Totallen, ref CardNum, frmcomportindex);

                if ((fCmdRet == 1) | (fCmdRet == 2) | (fCmdRet == 3) | (fCmdRet == 4) | (fCmdRet == 0xFB))//The search is over
                {
                    if (CardNum == 0)
                    {
                        return;
                    }
                    byte[] daw = new byte[Totallen];
                    Array.Copy(EPC, daw, Totallen);
                    temps = ByteArrayToHexString(daw);
                    //fInventory_EPC_List = temps;            //Store records
                    if (temps == "") return;
                    m = 0;
                    for (CardIndex = 0; CardIndex < CardNum; CardIndex++)
                    {
                        EPClen = daw[m];
                        string TID = temps.Substring(m * 2 + 2, EPClen * 2);
                        lastEPC = TID;
                        m = m + EPClen + 2;
                        value = TID;
                        //OnScan(this, new EventArgs());
                    }
                }
                //else
                //{
                //    return;
                //}

            }
        }

i jsut added new functions as below instead of that timer :
C#
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
 {
     while (true)
     {
         rf.Reader();
     }

 }

C#
private void btnQuery_Click(object sender, EventArgs e)
{
        backgroundWorker1.RunWorkerAsync();
}
Posted
Updated 20-Sep-20 3:55am
v8

I sure hope I understood your question correctly. Here goes:

First of all, I'd consider using the Task API as opposed to a thread directly

Inside your timer_Tick() function

C#
// make sure to do "using System.Threading.Tasks;"
Task.Run(()=>{Reader();});


Another decent option is using the threadpool

C#
// make sure to do "using System.Threading;"
ThreadPool.QueueUserWorkItem((state)=>{Reader();});


Another option is to use the BackgroundWorker component that is part of the winforms stuff. I personally don't like using it because it's heavy handed but to each their own

Using Thread directly, particularly in a Winforms app isn't such a great idea since running foreground threads will prevent your app from exiting when the main form is closed. In your case it doesn't matter since Reader() is relatively speaking, short running but I'm trying to show you for other scenarios as well.

One caveat is that since Reader() is now fired from another thread you must not communicate with the form or its controls directly since they are not thread safe.

Here's how marshal any user interface updates inside Reader() back to the main thread using message passing:
C#
// anything inside the anonymous method will be executed on the UI thread:
this.Invoke(new Action(()=>{ ++myProgressBar.Value;}));


One potential problem with your code:
It looks like you're using ref inside your reader routine and passing in member fields from the form. This is not thread safe. If you have to modify member variables on the form you must synchronize access to them! You can use the this.Invoke technique to modify them from the main thread. There are better ways but teaching them here would make this answer way too long

Anyway, I hope that helps!
 
Share this answer
 
v5
Comments
Mohammad Razmi 20-Sep-20 4:48am    
Both methods which you provided just unable me to read from multi readers at the same time.
moreover, when one reader is reading tags and a disconnection occurs both methods you provided try to create new threads over and over and it makes my program get stuck...
honey the codewitch 20-Sep-20 5:11am    
It looks like you're going to have to modify your code extensively then because your reader routine uses member fields from the looks of it. That means one reader per form which is not what you want. You should be putting those members you are using in a class, and using one class instance per reader call. Something like that anyway. I can't see enough of your code to be sure exactly what you're doing there but basically, you have a design issue from the looks of it. Your member variables are one per form, and i think you need them to be one per reader.
Mohammad Razmi 20-Sep-20 5:19am    
I just added my two classes
see them up
honey the codewitch 20-Sep-20 5:26am    
Let me see if I can untangle this code and figure out where your per instance data needs to be. I might not have a solution for you right away, since it's 2:30am and I need to go to sleep again at some point but I'll try to figure this out based on what you posted.

Edit: Can you use more than one instance of RfReader() at a time? if you can then I may modify the code to create one RfReader() per call and then eliminate the timer call from RfReader() entirely. If you spin a loop in your thread you won't need a timer anyway.
Mohammad Razmi 22-Sep-20 7:11am    
each form is related to just one reader...
so there is no difference between one instance per form or reader
To add to the possibilities Honey the codewitch has mentioned a version which is pretty simple for "threading novices" to use is the BackgroundWorker Class[^] - it's event driven, and provides progress reporting back to the UI thread for updates via it's ProgressChanged event.
This would allow you to set up a worker thread for each reader, which signals back to the UI thread when an RFID device is scanned, including the RFID data via the ProgressChangedEventArgs.UserState property[^].
 
Share this answer
 
Comments
honey the codewitch 20-Sep-20 4:45am    
I mentioned it, but it's actually more code to use it, which is one of the reasons I don't like it. More code = more potential for bugs, and frankly, BackgroundWorker kind of obscures things a little too much for my taste. Just my $0.02 but I don't think the class should even exist.
OriginalGriff 20-Sep-20 5:05am    
It's more code to set up, yes - but it's less code to use for a novice who wants to update UI stuff. And it's all "familiar code" - events and handlers - which makes it a lot easier to understand what is going on.
And since it's a Background thread it automatically gets terminated when the app dies - a foreground thread has to be terminated before the process is unloaded from the system (another mistake rookies often make!)
honey the codewitch 20-Sep-20 5:10am    
It is familiar, but it's also a pain to demonstrate the use of it in this format because there's just a lot to hooking it up. And every method I gave him spawns a background thread. Either way, it looks like his issue is a design one since he is using member variables in Reader() and wants multiple instances of those variables (1 per reader instead of 1 per form)
Mohammad Razmi 20-Sep-20 4:46am    
could you please more explain by clarifying where have I import the class in my project?
and how to use it?
when press the open button or when enable the timer?
OriginalGriff 20-Sep-20 5:08am    
You don't involve a Timer at all: you set up a thread that handles a single reader and start that off when your app starts. When it reads an RFID code, it reports back to the UI thread with the ID information and goes back to waiting for another RFID activation.
Since each RFID reader has it's own thread, they don't mess with each other, or the UI thread.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900