Friday, November 2, 2018

D365 Extend OPOS MSR Hardware Station

Open Hardware Station sample from Retail SDK. Path of Retail SDK will be something like below:

C:\RetailSDK\Code\SampleExtensions\HardwareStation\SampleHardwareStation.sln



Create new project in this solution "MyNewMSRExtension".
Right click on "SampleHardwareStation" Solution and select "Class Library (Portable)":



Now select following targets and click OK:
  • .NET Framework 4.5
  • Windows 8
  • ASP.NET Core 1.0
  • Windows Phone 8.1

Now you can see your new project in this solution:


Now add new class "OposMagneticSwipeReader".


Add below references in your newly created project:


Now add below code in your class:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Interop.OposMSR;
using Microsoft.Dynamics.Commerce.HardwareStation;
using Microsoft.Dynamics.Commerce.HardwareStation.CardPayment;
using Microsoft.Dynamics.Commerce.HardwareStation.Peripherals;
using Microsoft.Dynamics.Commerce.HardwareStation.Peripherals.Entities;
using Microsoft.Dynamics.Commerce.Runtime.Handlers;
using Microsoft.Dynamics.Commerce.Runtime.Messages;
using Microsoft.Dynamics.Retail.Diagnostics;

namespace MyNewMSRExtension
{
    public sealed class OposMagneticSwipeReader : INamedRequestHandler
    {
        private const string MsrInstanceNameTemplate = "OposMsr_{0}";
        private OPOSMSR oposMsr;
        private string msrInstanceName;

        /// <summary>
        /// Occurs when card is swiped.
        /// </summary>
        private event EventHandler<MagneticCardSwipeInfo> MsrSwipeEvent;

        /// <summary>
        /// Gets the unique name for this request handler.
        /// </summary>
        public string HandlerName
        {
            get { return PeripheralType.Opos; }
        }

        /// <summary>
        /// Gets the collection of supported request types by this handler.
        /// </summary>
        public IEnumerable<Type> SupportedRequestTypes
        {
            get
            {
                return new[]
                {
                        typeof(OpenMagneticSwipeReaderDeviceRequest),
                        typeof(SwipeMagneticSwipeReaderDeviceRequest),
                        typeof(CloseMagneticSwipeReaderDeviceRequest)
                    };
            }
        }

        /// <summary>
        /// Represents the entry point for the magnetic swipe reader device request handler.
        /// </summary>
        /// <param name="request">The incoming request message.</param>
        /// <returns>The outgoing response message.</returns>
        public Response Execute(Request request)
        {
            ThrowIf.Null(request, "request");

            Type requestType = request.GetType();

            if (requestType == typeof(OpenMagneticSwipeReaderDeviceRequest))
            {
                var openRequest = (OpenMagneticSwipeReaderDeviceRequest)request;

                this.Open(openRequest.DeviceName);
            }
            else if (requestType == typeof(SwipeMagneticSwipeReaderDeviceRequest))
            {
                var swipeRequest = (SwipeMagneticSwipeReaderDeviceRequest)request;

                MagneticCardSwipeInfo swipeInfo = this.WaitForSwipe(swipeRequest.TimeoutInSeconds);

                return new SwipeMagneticSwipeReaderDeviceResponse(swipeInfo);
            }
            else if (requestType == typeof(CloseMagneticSwipeReaderDeviceRequest))
            {
                this.Close();
            }
            else
            {
                throw new NotSupportedException(string.Format("Request '{0}' is not supported.", requestType));
            }

            return new NullResponse();
        }

        /// <summary>
        /// Opens a peripheral.
        /// </summary>
        /// <param name="peripheralName">Name of the peripheral.</param>
        private void Open(string peripheralName)
        {
            this.msrInstanceName = string.Format(MsrInstanceNameTemplate, peripheralName);

            this.oposMsr = OPOSDeviceManager<OPOSMSR>.Instance.AcquireDeviceHandle<OPOSMSRClass>();

            if (!this.oposMsr.DeviceEnabled)
            {
                // Open the device only if the device is not claimed.
                if (!this.oposMsr.Claimed)
                {
                    // Open
                    RetailLogger.Log.HardwareStationOposMethodCall(this.msrInstanceName, "Open");
                    this.oposMsr.Open(peripheralName);
                    OposHelper.CheckResultCode(this, this.oposMsr.ResultCode);

                    // Claim
                    RetailLogger.Log.HardwareStationOposMethodCall(this.msrInstanceName, "Claimed");
                    this.oposMsr.ClaimDevice(OposHelper.ClaimTimeOut);
                    OposHelper.CheckResultCode(this, this.oposMsr.ResultCode);

                    // Enable/Configure
                    this.oposMsr.DeviceEnabled = true;

                    // Set the decode data - so that the device decodes the scanned data
                    this.oposMsr.DecodeData = true;

                    // Note: there are two properties that look similar
                    // ParseDecodedData and ParseDecodeData
                    // Both do the same as per the OPOS spec.
                    // Setting this property makes the device return data
                    // in individual fields.
                    this.oposMsr.ParseDecodedData = true;

                    // Set Transmit Sentinels to true
                    // so that when the data is sent, we can get the sentinels
                    // and can parse the data of the tracks.
                    this.oposMsr.TransmitSentinels = true;

                    // Plug in handlers for data events
                    this.oposMsr.DataEvent += this.OnMsrDataEvent;
                    this.oposMsr.ErrorEvent += this.OnMsrErrorEvent;

                    // Set autodisable to false
                    this.oposMsr.AutoDisable = false;

                    // Enable data events
                    this.oposMsr.DataEventEnabled = true;
                }
                else
                {
                    RetailLogger.Log.HardwareStationPeripheralInteraction(this.msrInstanceName, "Prevent MSR device open call, as device is already claimed.");
                }
            }
        }

        /// <summary>
        /// Waits for a swipe during the specified <paramref name="timeoutInSeconds"/>.
        /// </summary>
        /// <param name="timeoutInSeconds">The timeout in seconds to wait for a swipe to occur.</param>
        /// <returns>Swipe info.</returns>
        private MagneticCardSwipeInfo WaitForSwipe(int timeoutInSeconds)
        {
            var eventCompletionSource = new TaskCompletionSource<MagneticCardSwipeInfo>();
            EventHandler<MagneticCardSwipeInfo> handler = null;

            handler = (sender, args) =>
            {
                eventCompletionSource.SetResult(args);

                this.MsrSwipeEvent -= handler;
            };

            this.MsrSwipeEvent += handler;

            var timeout = TimeSpan.FromSeconds(timeoutInSeconds);

            var completeTaskIndex = Task.WaitAny(new[] { (Task)eventCompletionSource.Task }, timeout);

            var isSwipeOccurred = completeTaskIndex == 0;

            return isSwipeOccurred
                ? eventCompletionSource.Task.Result
                : null;
        }

        /// <summary>
        /// Closes a connection with MSR.
        /// </summary>
        private void Close()
        {
            try
            {
                this.oposMsr = OPOSDeviceManager<OPOSMSR>.Instance.AcquireDeviceHandle<OPOSMSRClass>();

                if (this.MsrSwipeEvent != null)
                {
                    this.MsrSwipeEvent(this, null);
                }

                if (this.oposMsr != null)
                {
                    this.oposMsr.DataEvent -= this.OnMsrDataEvent;
                    this.oposMsr.ErrorEvent -= this.OnMsrErrorEvent;
                    this.oposMsr.DeviceEnabled = false;

                    RetailLogger.Log.HardwareStationOposMethodCall(this.msrInstanceName, "Released");
                    this.oposMsr.ReleaseDevice();

                    RetailLogger.Log.HardwareStationOposMethodCall(this.msrInstanceName, "Closed");
                    this.oposMsr.Close();
                }
            }
            finally
            {
                if (this.oposMsr != null)
                {
                    OPOSDeviceManager<OPOSMSR>.Instance.ReleaseDeviceHandle(this.oposMsr);
                }
            }
        }

        private void OnMsrErrorEvent(int resultCode, int resultCodeExtended, int errorLocus, ref int errorResponse)
        {
            var ex = new PeripheralException(PeripheralException.PeripheralEventError, "Error Event from Peripheral with error code {0}", resultCode);
            RetailLogger.Log.HardwareStationPerpheralError("Magnetic Swipe Reader (MSR)", resultCode, resultCodeExtended, ex);

            throw ex;
        }

        private void OnMsrDataEvent(int status)
        {
            if (this.MsrSwipeEvent != null)
            {
                var swipeInfo = new MagneticCardSwipeInfo();

                //Do your work here. Here you can find MSR scanned data

                // Store the account number in the temporary storage and mask the accout number only if it's the payment card.
                if (swipeInfo.IsPaymentCard)
                {
                    // Store the swipe info into the temporary memory storage.
                    var temporaryMemoryManager = new TemporaryMemoryManager<string, string>();
                    temporaryMemoryManager.AddCardInfoToMemory(MagneticCardSwipeInfo.MsrCardKey, swipeInfo.AccountNumber, DateTime.UtcNow, swipeInfo.AccountNumber);

                    // Mask the account number using mask chars.
                    string maskedAccountNumber = Utilities.GetMaskedCardNumber(swipeInfo.AccountNumber);
                    swipeInfo.AccountNumber = maskedAccountNumber;
                }

                this.MsrSwipeEvent(this, swipeInfo);

            }

            // This should not be required since the auto disable is set to false
            // but apparently some Msrs require this explictly enabled
            // every time.
            this.oposMsr.DataEventEnabled = true;
        }
    }
}

You can also write your custom logic in above code after comments "Do your work here. Here you can find MSR scanned data".


No comments:

Post a Comment

D365 Modern POS DLLHost.exe file detailed description

Everyone knows that Modern POS is being running by application that will be Windows, Android or IOS. Means that it will be install with Ope...