C# Threading and events for pin pad device

2019-06-26 01:22发布

问题:

I am new in C# and currently working on the backend code to support PIN pad. Basically, my code

OpenDevice() -> RequestPIN() 
-> key in PIN on PIN PAD -> GetResultPIN() 
-> ConsolePrintOutPIN() -> Print keyed PIN on the Console 

I don't know how to write thread for this, so that once the "Enter key" is hit on the device after PIN, the system would automatically rolls to function GetResultPIN(). So, with my elementary knowledge, I wrote the following codes using Console.ReadLine() to separate each procedure:

    static void Main(string[] args)
    {
        // 1. Open PIN Pad device
        OpenDevice();

        Console.ReadLine();// to hold up from the previous procedure, it is *not* for input data purpose

        // 2. Request PIN from PIN Pad device.
        //    On the PIN Pad device, it reads:
        //    "Key in the PIN:     "
        RequestPIN();

        Console.ReadLine();// to hold up from the previous procedure, it is *not* for input data purpose

        // 3. get PIN from the device
        GetResultPIN();

        // 4. Print out on the Console 
        ConsolePrintOutPIN();

        Console.ReadLine();// to hold up from the previous procedure, it is *not* for input data purpose
    }

Question: Can anyone give me any suggestions on how to use threading/event/delegate that can avoid using Console.ReadLine()?

As commended above, Console.ReadLine() is used just to stop the procedure (sorry about my naivety of using it this way....) Once I use Console.ReadLine(), between RequestPIN() and GetResult(), the system would at least wait for me to Key in the PIN from the PIN PAD (connected to the computer through USB, not from key board), and then I would hit any key on the keyboard to pass Console.ReadLine() and GetResultPIN() would be able to get my PIN number from PIN Pad.....the whole program works now, it is not customer ready, because it is very choppy, doesn't flow due to Console.ReadLine() I added.....

So ideally, all the method would flow together. Once the device is opened, RequestPIN() should show on the PIN Pad screen asking for PIN number, some one can key in and hit enter on PIN Pad and it naturally flow to GetResultPIN() and read the result, and then it prints the PIN on the console...`

or

if the person doesn't key in PIN, the device would wait for 30s and directly goes to GetResultPIN() and print out "0000" on the Console

I have looked up treading and delegate, but am not sure how to use them in this situation.... Thank you!

Reference: RequestPin() and GetResultPIN are listed down below:

mIPAD.requestPIN(waitTime, pinMsg, minLen, maxLen, tone, option, ",");
//This function wraps device command 0x04.  
//It directs the device to prompt the user to enter a PIN 
//by displaying one of five predetermined messages and playing
// a specified sound.  
//The messages on the device’s screen look like the figures below.  
//The event associated with this function is 
//OnPINRequestCompleteEvent. 

waitTime: Time the device should wait for the user to begin PIN entry

pinMsg: Message to display as a user prompt, such as "Enter PIN", "ReEnter PIN", "Verify PIN", etc

minLen and maxLen: minimum length and maximum length of PIN

tone: beeping tone option

Option: Verify PIN, not Verify PIN, ISO0 FOrmat, ISO3 Format

Output would be: an integer, 0: Success, Non-Zero: Error

    public void GetResultPIN()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(mIPAD.pin.KSN); 
               // Key Serial Number: 
               //a given number from the device, unique for each device
        sb.Append("," + mIPAD.pin.EPB);
               // EPB: encryption of PIN after Dubpt TripleDES,
               // essentially, EPB is PIN
        sb.Append("," + mIPAD.getStatusCode());
               //status code: Zero is good/done
               //             None-Zero is Error
        sb.Append("\r\n");
        result = sb.ToString();
    }

Basically, the GetResultPIN() returns a string of random code, for example: 9A00030000047A2000AB,AD781711481B08A2,0 when PIN is successful. If the pin-input part is skipped, it would return ,,0.

回答1:

Really hard to know if this will work or not without hardware to play with...

This is the way I envisioned it working:

    static void Main()
    {
        OpenDevice();
        RequestPIN();
        if (GetResultPIN())
        {
            // do something with the PIN:
            var pin = mIPAD.pin.EPB;

            // ...

        }
        else
        {
            Console.WriteLine("0000");
        }
    }

    public static bool GetResultPIN()
    {
        TimeSpan timeout = TimeSpan.FromSeconds(30);
        System.Diagnostics.Stopwatch SW = new System.Diagnostics.Stopwatch();
        SW.Start();
        while (mIPAD.getStatusCode() != 0 && SW.Elapsed < timeout)
        {
            System.Threading.Thread.Sleep(50); // small call to prevent CPU usage ramping to 100%
        }
        return (mIPAD.getStatusCode() == 0);
    }


回答2:

You can rewrite your api to:

  • make GetResultPIN() return a value
  • use this value as input for ConsolePrintOutPIN()

In GetResultPIN you need to make a Task To ReadYour Pin and wait for it.

See : https://msdn.microsoft.com/en-us/library/dd537610(v=vs.110).aspx

You can do something like this:

public string GetResultPIN()
{
    StringBuilder sb = new StringBuilder();
    sb.Append(mIPAD.pin.KSN); 
           // Key Serial Number: 
           //a given number from the device, unique for each device
    sb.Append("," + mIPAD.pin.EPB);
           // EPB: encryption of PIN after Dubpt TripleDES,
           // essentially, EPB is PIN
    sb.Append("," + mIPAD.getStatusCode());
           //status code: Zero is good/done
           //             None-Zero is Error
    sb.Append("\r\n");
    Thread.Sleep(20*1000);  // it is in milliseconds
    return sb.ToString();
}


回答3:

Thanks for posting... The solution is still not ideal.... I also did some more testing regarding the function RequestPIN(). I have the following four scenarios:

  1. User finishes keying in PIN sooner than the waitTime goes out. onPINRequestComplete : OpStatus:0 KSN:9A00030000047A2000C8 EPB:39DED176D3EA40B9 ..............................
  2. User doesn't finish keying in PIN when the waitTime is going out.
    onPINRequestComplete : OpStatus:2 KSN:00000000000000000000 EPB:0000000000000000 ..............................

  3. User cancels the PIN pad option by pressing "Cancel X" key on the PIN Pad.

    onPINRequestComplete : OpStatus:1 KSN:00000000000000000000 EPB:0000000000000000 ..............................

  4. User doesn't key in PIN at all during the waitTime, and then waitTime goes out.

    onPINRequestComplete : OpStatus:2 KSN:00000000000000000000 EPB:0000000000000000 .............................. So, scenario 1 and 3 would require the thread to wake up right away, while 2 and 4 would require the thread to wake up when the waiTime goes out. So using Thread.sleep(20*1000) within GetResultPIN() would work perfectly for scenario 2 and 4. As for 1 and 3, the user has to wait for a long time....

On the other hand, I found some code about Event

Within Car.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WaitOnTasksToComplete
{
    class Car
    {
        public event Action OnChange;

        private double speed;
        public double Speed
        {
            get { return speed; }
            set { speed = value;
                if (speed >= 60)
                {
                    if (OnChange != null)
                    {
                        OnChange();
                    }
                }
            }
        }
    }
}

Within Program.cs:

using System;

namespace WaitOnTasksToComplete
{
    class Program
    {
        static void Main(string[] args)
        {
            Car c = new Car();
            c.OnChange += C_OnChange;

            c.Speed = 5;
            c.Speed = 55;
            c.Speed = 65;
            c.Speed = 75;

        }

        private static void C_OnChange()
        {
            Console.WriteLine("Event fired: Car goes higher than 60 MPH.");
        }
    }
}

So, basically once the Car.speed jumps above 60, the alarm would show. I am considering borrowing the condition into my situation: Initialize OpStatus = -999. When OpStatus=0 or 1, keep executing GetResultPIN() and PrintMessagePIN(). If OpStatus=2 or others, keep waiting...

That is just my thoughts.... still have no clue how to implement it.... Any related ideas or suggestions would be appreciated.....



回答4:

Ah, I figured out. I am basically using threading here. The main flow is OpenDevice()->RequestPIN()->Thread(()=>CheckOpStatus(getResultPIN)) -> Thread.Start(). Within the Thread, a loop is set to check every half second what the OpStatus is. Per my previous post, OpStatusis the output parameter of PIN Pad, zero- success; non-zero: failure. That said, the loop would proceed until either bool condition_WithinWaitTime or bool condition_NoKeyEvent breaks. After breaking out, invoke the getResultPIN and so on....

Here is my source code, as PIN input is one of my functions, the rest of which have very similar behavior in terms programming (request->manual operation->feedback), so I also included a delegate variable to represents all functions (card swiping, PIN, signature bla bla).

    static void Main(string[] args)
    {
        OpenDevice();
        EventGetPIN();

    }
    static void EventGetPIN()
    {
        myDel getResult = new myDel(GetResultPIN);
        Thread thread1 = new Thread(() => CheckOpStatus(getResult));

        myDel requestDel = new myDel(RequestPIN); requestDel();
        thread1.Start();
    }
    static void CheckOpStatus(Delegate getResult)
    {
        int count = 0;
        int checkingPeriod = 500;
        int totalWaitTime = waitTime * 1000 + offsetTime;
        string OpStatus;
        string ksnStart = mIPAD.getKSN();
        string ksn = ksnStart;
        bool condition_WithinWaitTime = true;
        bool condition_NoKeyEvent = true;
        while (condition_WithinWaitTime & condition_NoKeyEvent)
        {
            count++;
            OpStatus = mIPAD.getStatusCode().ToString();
            ksn = mIPAD.getKSN();
            //Console.WriteLine(OpStatus);
            condition_WithinWaitTime = (count * checkingPeriod) < totalWaitTime;
            condition_NoKeyEvent = (ksn == ksnStart);
            Thread.Sleep(checkingPeriod);
        }

        getResult.DynamicInvoke();
    }