using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Security;
using System.Reflection;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using QuickType;
using RFID_Sample.Annotations;
using RFID_Sample.Properties;
using Tmds.MDns;
using WebSocketSharp;
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;


namespace RFID_Sample
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        #region Parameters

        #region Connection parameter
        private static string selectedIPAddress;
        private const string IpAddressPort = "8443";
        private readonly ConcurrentDictionary<string, string> IPAddressAndHostName = new ConcurrentDictionary<string, string>();

        string UserName = "root", Password = "";

        private CookieContainer cc;
        private WebSocket clientWebSocket;
        #endregion

        #region Log parameter
        private Task writeToLogConcurrentQueueTask;
        private CancellationTokenSource writeToLogConcurrentQueueTasktokenSource2;
        private CancellationToken writeToLogConcurrentQueueTaskct;

        private ConcurrentQueue<string> writeToLogConcurrentQueue;
        private const int WriteToLogWindowsLenghtLimit = 50000;
        #endregion

        private int tagOpID;
        private int tagOpSpecID;

        private const long DefaultDwellTime = 500;
        private const long DefaultCntOperation = 1;
        private const long DefaultFixQAlgorithm = 1;
        private const long DefaultQValue = 5;
        private const long DefaultRetryCount = 3;
        private const bool DefaultToggleTarget = true;
        private const bool DefaultRepeatUntilNoTags = true;

        private string _tagOpData;
        private string _tagOpTagID;

        private bool? isTagOpSuccess;

        public Getinventory setInventory;


        #region Lock
        private object IpListLock;

        private object ReceivedTagSetLock;
        private object TextBlockLogTextLock;
        private readonly object WebSocketRequestlock = new object();
        #endregion

        //private static readonly Mutex singleton = new Mutex(true,
        //    Assembly.GetExecutingAssembly().GetCustomAttribute<GuidAttribute>().Value.ToUpper());

        private static readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 2);
        private const int _semaphoreSlimTimeOut = 30000;
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Parameter Interface
        public ObservableCollection<string> IpList { get; set; }

        public ObservableCollection<string> TextBlockLogText { get; set; }

        public ObservableCollection<ReceivedTagData> ReceivedTagSet { get; set; }

        public string tagOpTagID
        {
            set
            {
                if (_tagOpTagID != value)
                {
                    _tagOpTagID = value;
                    OnPropertyChanged();
                }
            }
            get => _tagOpTagID;
        }

        public string tagOpData
        {
            set
            {
                if (_tagOpData != value)
                {
                    _tagOpData = value;
                    OnPropertyChanged();
                }
            }
            get => _tagOpData;
        }
        #endregion

        public MainWindow()
        {
            InitializeComponent();
            ToolTipService.ShowDurationProperty.OverrideMetadata(
                typeof(DependencyObject), new FrameworkPropertyMetadata(int.MaxValue));

            AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
            {
                var e = (Exception)eventArgs.ExceptionObject;
                WriteToLogWindows(e);
                WriteToLogWindows(e.StackTrace);
                MessageBox.Show(e.Message, Title + " error");
            };

            Loaded += MainWindow_Loaded;
            Initial();
            MonitorService();
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            ComboBoxIPAddress.SelectionChanged += ComboBoxIPAddress_OnSelectionChanged;

            tagDataGrid.SelectionChanged += TagDataGrid_OnSelectionChanged;

            TagOPReadButton.IsEnabled = false;
            SetProfile.IsEnabled = false;
            SetInventorySetting.IsEnabled = false;
            StartInventory.IsEnabled = false;
        }

        public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
            SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

        private void Initial()
        {
            writeToLogConcurrentQueueTasktokenSource2 = new CancellationTokenSource();
            writeToLogConcurrentQueueTaskct = writeToLogConcurrentQueueTasktokenSource2.Token;
            writeToLogConcurrentQueue = new ConcurrentQueue<string>();
            writeToLogConcurrentQueueTask = new Task(() =>
            {
                while (true)
                {
                    if (writeToLogConcurrentQueueTaskct.IsCancellationRequested)
                    {
                                // Clean up here, then...
                                break;
                    }
                    if (!writeToLogConcurrentQueue.IsEmpty)
                    {
                        string mySB;
                        if (writeToLogConcurrentQueue.TryDequeue(out mySB))
                        {
                            Task task1 = Task.Run(() =>
                            {
                                if (TextBlockLogText == null)
                                    return;
                                TextBlockLogText.Add(mySB);
                                ;

                                if (TextBlockLogText.Count > WriteToLogWindowsLenghtLimit)
                                            //lock(_syncLock)
                                            TextBlockLogText.RemoveAt(0);
                            });
                            Task task2 = Task.Run(() =>
                            {
                                if (ListBoxLog == null)
                                    return;
                                Application.Current.Dispatcher.InvokeAsync(() =>
                                {
                                    ListBoxLog.Items.Add(mySB);
                                    if (ListBoxLog.Items.Count > WriteToLogWindowsLenghtLimit)
                                                //lock(_syncLock)
                                                ListBoxLog.Items.RemoveAt(0);
                                    ListBoxLog.UpdateLayout();
                                    var item = ListBoxLog.Items.GetItemAt(ListBoxLog.Items.Count - 1);
                                    ListBoxLog.ScrollIntoView(item);
                                });
                            });
                            var taskWhenAll = Task.WhenAll(task1, task2);
                            taskWhenAll.Wait();
                        }
                    }
                    Thread.Sleep(1);
                }
            }, writeToLogConcurrentQueueTaskct, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
            writeToLogConcurrentQueueTask.Start();

            var entryAssemblyInfo = new AssemblyInfo(Assembly.GetEntryAssembly());
            var assembly = Assembly.GetExecutingAssembly();
            var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
            var version = fvi.FileVersion;

            cc = new CookieContainer();
            cc.Add(new Cookie(entryAssemblyInfo.ProductTitle.Replace(" ", "_"), version, "/",
                Dns.GetHostEntry("").HostName));

            int numProcs = Environment.ProcessorCount;
            int concurrencyLevel = numProcs * 2;

            tagOpID = 0;
            tagOpSpecID = 0;

            InitSetInventory();

            ServicePointManager.ServerCertificateValidationCallback +=
                (sender, cert, chain, sslPolicyErrors) => true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
            ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
            ServicePointManager.Expect100Continue = false;
            ServicePointManager.DefaultConnectionLimit = 10;

            IpList = new ObservableCollection<string>();
            TextBlockLogText = new ObservableCollection<string>();
            ReceivedTagSet = new ObservableCollection<ReceivedTagData>();

            IpListLock = new object();
            TextBlockLogTextLock = new object();
            ReceivedTagSetLock = new object();

            BindingOperations.EnableCollectionSynchronization(IpList, IpListLock);
            BindingOperations.EnableCollectionSynchronization(TextBlockLogText, TextBlockLogTextLock);
            BindingOperations.EnableCollectionSynchronization(ReceivedTagSet, ReceivedTagSetLock);

            ComboBoxIPAddress.SelectedIndex = 0;
        }

        /// <summary>
        /// Initialize the SetInventory
        /// </summary>
        private void InitSetInventory()
        {
            setInventory = new Getinventory();

            var invtStartCriteria = new InvtStartCriteria();
            invtStartCriteria.Type = 0;
            invtStartCriteria.OffsetMilsec = 0;
            invtStartCriteria.PeriodMilsec = 0;
            setInventory.InvtStartCriteria = invtStartCriteria;

            var invStopCriteria = new StopCriteria();
            invStopCriteria.Type = 0;
            invStopCriteria.DurationMilsec = 0;
            invStopCriteria.TriggerGpi = null;
            setInventory.InvtStopCriteria = invStopCriteria;

            var antennaInvtSpec = new AntennaInvtSpec();
            antennaInvtSpec.Id = 1;
            antennaInvtSpec.Antennas = new long?[4];
            antennaInvtSpec.AntennaConfigs = new AntennaConfig[antennaInvtSpec.Antennas.Length];
            for (var i = 0; i < antennaInvtSpec.Antennas.Length; i++)
            {
                antennaInvtSpec.Antennas[i] = i + 1;
                antennaInvtSpec.AntennaConfigs[i] = new AntennaConfig();
            }

            for (var i = 0; i < antennaInvtSpec.AntennaConfigs.Length; i++)
            {
                antennaInvtSpec.AntennaConfigs[i].AntennaId = i + 1;
                antennaInvtSpec.AntennaConfigs[i].RfTransmitter = new RfTransmitter();
                antennaInvtSpec.AntennaConfigs[i].Dtic = new Dtic();
                antennaInvtSpec.AntennaConfigs[i].Dtic.DwellTime = DefaultDwellTime;
                antennaInvtSpec.AntennaConfigs[i].Dtic.InventoryCycles = 0;
                antennaInvtSpec.AntennaConfigs[i].Sequence = new Sequence { SequenceSequence = i + 1 };
                antennaInvtSpec.AntennaConfigs[i].C1G2InventoryCommand = new C1G2InventoryCommand();
                antennaInvtSpec.AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec = new C1G2SingulationSpec();
                antennaInvtSpec.AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationSessionAndAction = new C1G2SingulationSessionAndAction();
                antennaInvtSpec.AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                        .C1G2SingulationSessionAndAction.C1G2TagInventoryStateAwareSingulationAction =
                    new C1G2TagInventoryStateAwareSingulationAction();
                antennaInvtSpec.AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl =
                    new C1G2SingulationControl();
                antennaInvtSpec.AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl
                    .SingulationAlgorithm = new SingulationAlgorithm();
                antennaInvtSpec.AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl
                    .SingulationAlgorithmParameters = new SingulationAlgorithmParameters();
            }

            antennaInvtSpec.AntennaStopCriteria = new StopCriteria();
            antennaInvtSpec.AntennaStopCriteria.Type = 0;
            antennaInvtSpec.AntennaStopCriteria.DurationMilsec = 0;
            antennaInvtSpec.AntennaStopCriteria.TriggerGpi = null;
            setInventory.AntennaInvtSpecs = new[] { antennaInvtSpec };

            var reportSpec = new ReportSpec();
            reportSpec.ReportCriteriaType = 2;
            reportSpec.N = 1;
            var reportSelector = new ReportSelector();
            reportSelector.AntennaId = reportSelector.AntennaInvtSpecId = reportSelector.RssiRaw =
                reportSelector.TimeFirstSeen = reportSelector.TimeLastSeen =
                    reportSelector.Count = reportSelector.Pc = true;
            reportSelector.TagId = false;
            reportSpec.ReportSelector = reportSelector;
            setInventory.ReportSpec = reportSpec;
        }

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        #region UI Callback Functions
        private async void TagDataGrid_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            try
            {
                await Task.Run(() =>
                {
                    try
                    {
                        var dg = (DataGrid)sender;

                        Application.Current.Dispatcher.InvokeAsync(() =>
                        {
                            var selectedItem = (ReceivedTagData)dg.SelectedItem;
                            if (selectedItem != null)
                            {
                                tagOpTagID = selectedItem.EPC;
                            }
                            else
                            {
                                tagOpTagID = string.Empty;
                            }
                        });
                    }
                    catch (Exception exception)
                    {
                        WriteToLogWindows(exception);
                    }
                });
            }
            catch (Exception exception)
            {
                WriteToLogWindows(exception);
            }
        }

        private void MainWindow_OnClosed(object sender, EventArgs e)
        {
            try
            {
                deleteAllTagOP();
                var responseFromServer = WebSocketRequest("deleteinventory");
            }
            catch (Exception exception)
            {
                WriteToLogWindows(exception);
            }

            try
            {
                writeToLogConcurrentQueueTasktokenSource2.Cancel();
                writeToLogConcurrentQueueTasktokenSource2.Dispose();
                writeToLogConcurrentQueueTasktokenSource2 = null;
            }
            catch (Exception exception)
            {
                WriteToLogWindows(exception);
            }
        }
        #endregion

        #region Connection
        private void ComboBoxIPAddress_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            try
            {
                var cb = sender as ComboBox;
                var selectedValue = cb.SelectedValue.ToString();
                var ip = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");
                var result = ip.Matches(selectedValue);
                selectedIPAddress = result[0].ToString();

                Connect.IsEnabled = true;
            }
            catch (Exception exception)
            {
                WriteToLogWindows(exception);
            }
        }

        private async void Connect_OnClick(object sender, RoutedEventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(selectedIPAddress))
                    return;
                var button = sender as Button;
                var content = button.Content.ToString();

                switch (content)
                {
                    case "Connect":
                        {
                            await Task.Run(async () =>
                            {
                                try
                                {
                                    if (clientWebSocket != null)
                                    {
                                        clientWebSocket.OnClose -= ClientWebSocket_OnClose;
                                        clientWebSocket.OnError -= ClientWebSocket_OnError;
                                        clientWebSocket.OnMessage -= ClientWebSocket_OnMessage;
                                        clientWebSocket.OnOpen -= ClientWebSocket_OnOpen;
                                        clientWebSocket.Close();
                                        clientWebSocket = null;
                                    }

                                    clientWebSocket = new WebSocket(@"wss://" + selectedIPAddress + @":" + IpAddressPort + @"/socket");
                                    clientWebSocket.WaitTime = new TimeSpan(0, 0, 0, 3);
                                    clientWebSocket.OnClose += ClientWebSocket_OnClose;
                                    clientWebSocket.OnError += ClientWebSocket_OnError;
                                    clientWebSocket.OnMessage += ClientWebSocket_OnMessage;
                                    clientWebSocket.OnOpen += ClientWebSocket_OnOpen;

                                    WriteToLogWindows("Start Connect");

                                    clientWebSocket.Connect();
                                }
                                catch (Exception exception)
                                {
                                    WriteToLogWindows("Connect Failed:" + exception.Message);

                                    clientWebSocket.Close();
                                    clientWebSocket = null;

                                    await Application.Current.Dispatcher.InvokeAsync(() =>
                                    {
                                        button.Content = "Connect";
                                        if (StartInventory.Content.Equals("Start Inventory"))
                                        {
                                            button.Content = content = "Connect";
                                            ConnectionStatusLabel.Text = "No RFID reader is connected";
                                            ComboBoxIPAddress.IsEnabled = true;
                                        }
                                    });
                                }
                            });
                        }
                        break;
                    case "Disconnect":
                        {
                            CloseWebSocket(true);
                        }
                        break;
                }
            }
            catch (Exception exception)
            {
                WriteToLogWindows(exception);
            }
        }

        private void ClientWebSocket_OnOpen(object sender, EventArgs e)
        {
            string responseFromServer = WebSocketRequest("stopinventory");

            if (responseFromServer == null)
                throw new Exception("Please check username/password");

            deleteAllTagOP();

            if (!GetReaderInfo())
            {
                throw new Exception("Reader is not ready, please wait a while then try connect again");
            }

            string hostname;
            if (IPAddressAndHostName.TryGetValue(selectedIPAddress, out hostname))
            {
                Application.Current.Dispatcher.Invoke(() =>
                {
                    ConnectionStatusLabel.Text = "Reader " + hostname + " is connected";
                });
            }

            Application.Current.Dispatcher.InvokeAsync(() =>
            {
                Connect.Content = "Disconnect";
                ComboBoxIPAddress.IsEnabled = false;
                TagOPReadButton.IsEnabled = true;
                SetProfile.IsEnabled = true;
                SetInventorySetting.IsEnabled = true;
            });

            do
            {
                lock (ReceivedTagSetLock)
                    ReceivedTagSet.Clear();
                Thread.Sleep(1000);
            } while (ReceivedTagSet.Count != 0);

            Application.Current.Dispatcher.Invoke(() =>
            {
                ResetInventoryUIData();
            });

            WriteToLogWindows("Connect Success");
        }

        private void ClientWebSocket_OnMessage(object sender, MessageEventArgs e)
        {
            Task.Run(() =>
            {
                try
                {
                    WriteToLogWindows(e.Data);
                }
                catch (Exception exception)
                {
                    WriteToLogWindows(exception);
                }
            });

            Task.Run(() => { dealReceivedMessage(e.Data); });
        }

        private void ClientWebSocket_OnError(object sender, ErrorEventArgs e)
        {
            WriteToLogWindows("ClientWebSocket_OnError(" + e.Message + ")" + e);
            CloseWebSocket(false, " due to " + e.Message);
        }

        private void ClientWebSocket_OnClose(object sender, CloseEventArgs e)
        {
            CloseWebSocket(false, " due to " + e.Reason);
        }

        private void CloseWebSocket(bool isNormal = false, string message = "")
        {
            try
            {
                ResetInventoryUIData();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            if (clientWebSocket != null)
            {
                clientWebSocket.OnClose -= ClientWebSocket_OnClose;
                clientWebSocket.OnError -= ClientWebSocket_OnError;
                clientWebSocket.OnMessage -= ClientWebSocket_OnMessage;
                clientWebSocket.OnOpen -= ClientWebSocket_OnOpen;
                clientWebSocket.Close();
            }

            clientWebSocket = null;

            if (isNormal)
                WriteToLogWindows("Disconnect Success" + message);
            else
                WriteToLogWindows("Connect Failed" + message);

            Application.Current.Dispatcher.InvokeAsync(() =>
            {
                antennaPowerLevel.Items.Clear();
                StartInventory.Content = "Start Inventory";
                Connect.Content = "Connect";
                ConnectionStatusLabel.Text = "No RFID reader is connected";
                ComboBoxIPAddress.IsEnabled = true;

                TagOPReadButton.IsEnabled = false;
                SetProfile.IsEnabled = false;
                SetInventorySetting.IsEnabled = false;
                StartInventory.IsEnabled = false;
            });
        }

        #region Monitor Service
        private void MonitorService()
        {
            var serviceType = "_http._tcp";
            var serviceBrowser = new ServiceBrowser();
            serviceBrowser.ServiceAdded += onServiceAdded;

            serviceBrowser.StartBrowse(serviceType);
        }

        private void onServiceAdded(object sender, ServiceAnnouncementEventArgs e)
        {
            var sa = e.Announcement;
            if (sa.Hostname.ToLower().Contains("rs804"))
            {
                var ip = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");
                var IPAddress = ip.Matches(string.Join(",", sa.Addresses));
                if (IPAddress.Count > 0)
                {
                    IpList.Add(IPAddress[0] + "(" + sa.Hostname + ")");

                    IPAddressAndHostName.AddOrUpdate(IPAddress[0].ToString(), sa.Hostname, (key, value) => sa.Hostname);

                    var message = "+ host:" + IPAddress[0] + "(" + sa.Hostname + ")";
                    var sb = new StringBuilder(2);
                    sb.Append(message);
                    WriteToLogWindows(sb.ToString());
                }
            }
        }
        #endregion

        private bool GetReaderInfo()
        {
            string responseFromServer = WebSocketRequest("getreaderinfo");

            if (responseFromServer == null)
                return false;

            ReaderInfo readerInfo = ReaderInfo.FromJson(responseFromServer);

            Application.Current.Dispatcher.InvokeAsync(() =>
            {
                antennaPowerLevel.Items.Clear();
                foreach (var powerLevel in readerInfo.AntennaPowersLevel)
                {
                    antennaPowerLevel.Items.Add(powerLevel);
                }

                antennaPowerLevel.SelectedValue = 2700L;
            });

            return true;
        }
        #endregion

        private async void StartInventory_OnClick(object sender, RoutedEventArgs e)
        {
            try
            {
                var button = sender as Button;
                var buttonContent = button.Content.ToString().ToLower();
                if (StartInventory.Content.Equals("Start Inventory"))
                {
                    if (Connect.Content.ToString() == "Disconnect")

                        await Task.Run(() =>
                        {
                            string responseFromServer;
                            responseFromServer = WebSocketRequest("getinventory");
                            var getinventory = Getinventory.FromJson(responseFromServer);

                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                ResetInventoryUIData();

                                StartInventory.IsEnabled = false;
                            });
                            responseFromServer = WebSocketRequest("startinventory");

                            WriteToLogWindows("User start Inventory");

                        });
                }
                else
                {
                    //stop Inventory
                    await Task.Run(() =>
                    {
                        Application.Current.Dispatcher.Invoke(() =>
                        {
                            StartInventory.IsEnabled = false;
                        });
                        var responseFromServer = WebSocketRequest("stopinventory");
                    });

                    try
                    {
                        WriteToLogWindows("User stop Inventory");

                        StartInventory.Content = "Start Inventory";
                    }
                    catch (Exception exception)
                    {
                        WriteToLogWindows(exception);
                    }
                }
            }
            catch (Exception exception)
            {
                WriteToLogWindows(exception);
            }
        }

        private string WebSocketRequest(string apicommand, string jsonData = null, string FormRawData = null)
        {
            lock (WebSocketRequestlock)
            {
                HttpWebRequest request = null;
                try
                {
                    var sb = new StringBuilder();
                    sb.Append("https://");
                    sb.Append(UserName);
                    sb.Append(":");
                    sb.Append(Password);
                    sb.Append("@");
                    sb.Append(selectedIPAddress);
                    sb.Append(":");
                    sb.Append(IpAddressPort);
                    sb.Append("/api/");
                    sb.Append(apicommand);

                    request = (HttpWebRequest)WebRequest.Create(sb.ToString());
                    request.Method = "POST";
                    request.Timeout = 30000;
                    request.PreAuthenticate = true;
                    request.CookieContainer = cc;
                    request.Headers.Add("Authorization",
                        "Basic " + Convert.ToBase64String(new ASCIIEncoding().GetBytes(UserName + ":" + Password)));

                    if (jsonData != null)
                    {
                        request.ContentType = "application/json";
                        using (var reauestStream = request.GetRequestStream())
                        {
                            using (var streamWriter = new StreamWriter(reauestStream))
                            {
                                streamWriter.Write(jsonData);
                            }
                        }
                    }
                    else
                    {
                        if (FormRawData != null)
                        {
                            request.ContentType = "application/x-www-form-urlencoded";
                            var bs = Encoding.ASCII.GetBytes(FormRawData);
                            request.ContentLength = bs.Length;
                            using (var reauestStream = request.GetRequestStream())
                            {
                                using (var streamWriter = new StreamWriter(reauestStream))
                                {
                                    streamWriter.BaseStream.Write(bs, 0, bs.Length);
                                }
                            }
                        }
                        else
                        {
                            request.ContentType = "text/plain";
                            using (var reauestStream = request.GetRequestStream())
                            {
                                using (var streamWriter = new StreamWriter(reauestStream))
                                {
                                    streamWriter.Write(string.Empty);
                                }
                            }
                        }
                    }

                    var responseFromServer = string.Empty;

                    using (var response = (HttpWebResponse)request.GetResponse())
                    {
                        using (var dataStream = response.GetResponseStream())
                        {
                            // Open the stream using a StreamReader for easy access.  
                            using (var reader = new StreamReader(dataStream))
                            {
                                // Read the content.
                                responseFromServer = reader.ReadToEnd();
                                reader.Close();
                            }

                            dataStream.Close();
                        }

                        response.Close();
                    }

                    try
                    {
                        if (request != null)
                            request.Abort();
                    }
                    catch (Exception exception)
                    {
                        WriteToLogWindows(exception);
                    }

                    return responseFromServer;
                }
                catch (Exception e)
                {
                    try
                    {
                        if (request != null)
                            request.Abort();
                    }
                    catch (Exception exception)
                    {
                        //Debug.WriteLine(exception);
                        WriteToLogWindows(exception);
                    }

                    WriteToLogWindows("Send command(" + apicommand + ")" + " to server(" + selectedIPAddress +
                                      ") error(" + e.Message + ")");
                    return null;
                }
            }
        }

        void dealReceivedMessage(string msg)
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            try
            {
                JObject o = null;

                if (!string.IsNullOrWhiteSpace(msg))
                    o = JObject.Parse(msg);

                if (o != null)
                {
                    var Class = (string)o["CLASS"];

                    if (Class != null)
                    {
                        switch (Class)
                        {
                            case "#RE":
                                {
                                    var e = (string)o["e"]; //evt
                                    var st = (string)o["st"]; //state

                                    e = e.Trim();
                                    st = st.Trim();
                                    var txt = string.Empty;

                                    if (o.ContainsKey("txt"))
                                        txt = (string)o["txt"];
                                    else if (o.ContainsKey("t"))
                                        txt = (string)o["t"];

                                    if (!txt.IsNullOrEmpty())
                                        txt = txt.Trim();

                                    switch (e)
                                    {
                                        case "249": //WAE_INVENTORY
                                            switch (st)
                                            {
                                                case "0": //st: 0=start, 1=stop
                                                    Application.Current.Dispatcher.InvokeAsync(() =>
                                                    {
                                                        StartInventory.IsEnabled = true;
                                                        WriteToLogWindows("Reader start Inventory");

                                                        StartInventory.Content = "Stop Inventory";

                                                        TagOPReadButton.IsEnabled = false;
                                                        SetProfile.IsEnabled = false;
                                                        SetInventorySetting.IsEnabled = false;
                                                    });
                                                    break;
                                                case "1":
                                                    Application.Current.Dispatcher.InvokeAsync(() =>
                                                    {
                                                        StartInventory.IsEnabled = true;
                                                        WriteToLogWindows("Reader stop Inventory");

                                                        StartInventory.Content = "Start Inventory";

                                                        TagOPReadButton.IsEnabled = true;
                                                        SetProfile.IsEnabled = true;
                                                        SetInventorySetting.IsEnabled = true;
                                                    });
                                                    break;
                                            }
                                            break;
                                        case "1001":
                                            Application.Current.Dispatcher.InvokeAsync(() =>
                                            {
                                                StartInventory.IsEnabled = true;
                                            });
                                            WriteToLogWindows("Reader error\n" + "status:" + st + "\n" + "Reason: " + txt);
                                            break;

                                        case "1004":
                                            WriteToLogWindows("Reader dead\n" + "Reason: " + txt);
                                            break;
                                        case "1115":
                                            {
                                                var p1 = (string)o["p1"];
                                                var opCmd = Enum.GetName(typeof(cmd), Enum.Parse(typeof(cmd), p1.Trim()));
                                                WriteToLogWindows("Tag Op. " + opCmd + " error\n" + "Reason: " + txt);
                                            }
                                            break;
                                        case "1500":
                                            {
                                                switch (st.ToLower())
                                                {
                                                    case "1": //RET_QUIT_APP 
                                                        Application.Current.Dispatcher.InvokeAsync(() =>
                                                        {
                                                            StartInventory.IsEnabled = true;
                                                        });
                                                        WriteToLogWindows("RET_QUIT_APP");
                                                        break;
                                                    case "2": //RET_RESET_RM300
                                                        Application.Current.Dispatcher.InvokeAsync(() =>
                                                        {
                                                            StartInventory.IsEnabled = true;
                                                        });
                                                        WriteToLogWindows("RET_RESET_RM300");
                                                        break;
                                                    case "3": //RET_TASK_COMPLETED
                                                        try
                                                        {
                                                            if (_semaphoreSlim.CurrentCount == 0)
                                                                _semaphoreSlim.Release(2);

                                                            var p1 = (string)o["p1"];
                                                            var p2 = (string)o["p2"];
                                                            WriteToLogWindows("RET_TASK_COMPLETED(" + p1 + "," + p2 + ")");
                                                        }
                                                        catch (Exception exception)
                                                        {
                                                            WriteToLogWindows(exception);
                                                            //throw;
                                                        }

                                                        break;
                                                }
                                            }
                                            break;
                                    }
                                }
                                break;
                            case "#TD":
                                {
                                    /*
                                 * <e>:epc
                                   <t>: tagID
                                   <a>: antennaID
                                   <f>: time first seen
                                   <tor>: list of object
                                   {ID: int, op: string, result: int, dataLen: int, dataBytes: list of byte}

                                 */

                                    var tagData = TagData.FromJson(msg);
                                    ConcurrentBag<ReceivedTagData> receivedTagDataBag = new ConcurrentBag<ReceivedTagData>();

                                    Parallel.ForEach(tagData.Ts, item =>
                                    {
                                        Thread.SpinWait(1);
                                        ReceivedTagData receivedTagData = null;

                                        if (item.O == null)
                                        {
                                            receivedTagData = new ReceivedTagData(item.E,
                                                item.C,
                                                item.R,
                                                epoch2string(item.L), epoch2string(item.F),
                                                item.A,
                                                item.PC);
                                        }

                                        if (receivedTagData != null)
                                            receivedTagDataBag.Add(receivedTagData);

                                    });

                                    var task1 = Task.Run(() =>
                                    {
                                        foreach (var receivedTagData in receivedTagDataBag)
                                        {
                                            if (receivedTagData != null)
                                            {
                                                lock (ReceivedTagSetLock)
                                                {
                                                    var tempArray = ReceivedTagSet.ToList();

                                                    var query = tempArray.Where(p => p != null &&
                                                    p.EPC == receivedTagData.EPC).FirstOrDefault();

                                                    if (query == null)
                                                    {
                                                                //new EPC
                                                                lock (ReceivedTagSetLock)
                                                        {
                                                            ReceivedTagSet.Add(receivedTagData);
                                                        }
                                                    }
                                                    else
                                                    {
                                                                //old EPC
                                                                try
                                                        {
                                                            query.Count += receivedTagData.Count;
                                                        }
                                                        catch (Exception ex)
                                                        {
                                                            WriteToLogWindows(ex);
                                                        }

                                                        query.RSSI = receivedTagData.RSSI;
                                                        query.LastSeen = receivedTagData.LastSeen;
                                                    }

                                                }
                                            }
                                        }
                                    });

                                    var task2 = Task.Run(() =>
                                    {
                                        foreach (var item in tagData.Ts)
                                        {
                                            if (item.O != null)
                                            {
                                                foreach (var tagOP in item.O)
                                                {
                                                    switch (tagOP.Op.ToLower())
                                                    {
                                                        case "read":
                                                            if (tagOP.Result.Equals(0))
                                                            {
                                                                isTagOpSuccess = true;

                                                                WriteToLogWindows("Tag OP:Read Success");
                                                                if (tagOP.DataLen > 0)
                                                                {
                                                                    try
                                                                    {
                                                                        var tempStrings = ByteArrayToString(tagOP.DataBytes).Split(' ');

                                                                        var sb = new StringBuilder();
                                                                        for (var i = 0; i < tempStrings.Length; i++)
                                                                        {
                                                                            if (i % 2 == 1)
                                                                            {
                                                                                sb.Append(tempStrings[i]);
                                                                                sb.Append(" ");
                                                                            }
                                                                            else
                                                                            {
                                                                                sb.Append(tempStrings[i]);
                                                                            }
                                                                        }

                                                                        tagOpData = sb.ToString();
                                                                        WriteToLogWindows("Data: " + tagOpData);

                                                                    }
                                                                    catch (Exception e)
                                                                    {
                                                                        WriteToLogWindows(e);
                                                                    }
                                                                }
                                                            }
                                                            else
                                                            {
                                                                isTagOpSuccess = false;

                                                                switch (tagOP.Result)
                                                                {
                                                                    case 1:
                                                                        WriteToLogWindows("Tag OP:Read Fail:Nonspecific_Tag_Error");
                                                                        break;
                                                                    case 2:
                                                                        WriteToLogWindows("Tag OP:Read Fail:No_Response_From_Tag");
                                                                        break;
                                                                    case 3:
                                                                        WriteToLogWindows("Tag OP:Read Fail:Nonspecific_Reader_Error");
                                                                        break;
                                                                    case 0xFF:
                                                                        WriteToLogWindows("Tag OP:Read Fail:Operation_Failed_Error");
                                                                        break;
                                                                }
                                                            }

                                                            break;
                                                    }
                                                }
                                            }
                                        }
                                    });

                                    Task.WaitAll(task1, task2);


                                }
                                break;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                WriteToLogWindows(e);
            }

            stopwatch.Stop();
        }

        private void ResetInventoryUIData()
        {
            lock (ReceivedTagSetLock)
                ReceivedTagSet.Clear();

            Application.Current.Dispatcher.InvokeAsync(() =>
            {
                tagOpData = "";
            });
        }

        private void SetProfile_OnClick(object sender, RoutedEventArgs e)
        {
            try
            {
                var responseFromServer = WebSocketRequest("setprofile", "{\"profile\":1}");

                var rfidReply = RfidReply.FromJson(responseFromServer);
                if (rfidReply.Result.Contains("Fail"))
                {
                    WriteToLogWindows("Set profile error:" + rfidReply.Desc);
                }
                else
                {
                    if (!string.IsNullOrWhiteSpace(rfidReply.Desc))
                        WriteToLogWindows("Warn: " + rfidReply.Desc);
                    else
                        WriteToLogWindows("Set profile finished");
                }

            }
            catch (Exception ex)
            {
                WriteToLogWindows(ex);
            }
        }

        #region Set Inventory Setting
        private void changeInventorySetting()
        {
            try
            {
                string responseFromServer;
                responseFromServer = WebSocketRequest("stopinventory");

                responseFromServer = WebSocketRequest("deleteinventory");

                var jsonOutput = JsonConvert.SerializeObject(setInventory, Formatting.None,
                    new JsonSerializerSettings
                    {
                        NullValueHandling = NullValueHandling.Ignore
                    });

                File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "setInventory.txt", jsonOutput);
                responseFromServer = WebSocketRequest("setinventory", jsonOutput);

                var rfidReply = RfidReply.FromJson(responseFromServer);
                if (rfidReply.Result.Contains("Fail"))
                {
                    WriteToLogWindows("Set Inventory error:" + rfidReply.Desc);
                }
                else
                {
                    if (!string.IsNullOrWhiteSpace(rfidReply.Desc))
                        WriteToLogWindows("Warn: " + rfidReply.Desc);
                    else
                    {
                        WriteToLogWindows("Set Inventory finished");
                        Application.Current.Dispatcher.InvokeAsync(() =>
                        {
                            StartInventory.IsEnabled = true;
                        });
                    }
                }
            }
            catch (Exception e)
            {
                WriteToLogWindows(e);
            }
        }



        //Prepare inventory
        private void SetInventorySetting_OnClick(object sender, RoutedEventArgs e)
        {
            #region set antenna enable or disable

            var enableAntennaInts = new List<int>();

            //Set the antenna to number to operate
            enableAntennaInts.Add(0);
            //enableAntennaInts.Add(1);
            //enableAntennaInts.Add(2);
            //enableAntennaInts.Add(3);

            if (setInventory != null)
            {
                if (setInventory.AntennaInvtSpecs != null)
                {
                    if (setInventory.AntennaInvtSpecs[0] != null)
                    {
                        #region Reset parameter
                        if (setInventory.AntennaInvtSpecs[0].AntennaConfigs != null)
                            for (var i = 0; i < setInventory.AntennaInvtSpecs[0].AntennaConfigs.Length; i++)
                                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i] = null;

                        setInventory.AntennaInvtSpecs[0].AntennaConfigs = null;
                        if (setInventory.AntennaInvtSpecs[0].Antennas != null)
                            for (var i = 0; i < setInventory.AntennaInvtSpecs[0].Antennas.Length; i++)
                                setInventory.AntennaInvtSpecs[0].Antennas[i] = null;

                        setInventory.AntennaInvtSpecs[0].Antennas = null;
                        #endregion

                        setInventory.AntennaInvtSpecs[0].Antennas = new long?[enableAntennaInts.Count];
                        setInventory.AntennaInvtSpecs[0].AntennaConfigs = new AntennaConfig[enableAntennaInts.Count];

                        #region Setup each antenna setting
                        for (var i = 0; i < enableAntennaInts.Count; i++)
                        {
                            setInventory.AntennaInvtSpecs[0].Antennas[i] = enableAntennaInts[i] + 1;
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i] = new AntennaConfig();
                        }

                        for (var i = 0; i < enableAntennaInts.Count; i++)
                        {
                            #region Antenna ID
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].AntennaId = enableAntennaInts[i] + 1;
                            #endregion

                            #region Antenna Power
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].RfTransmitter = new RfTransmitter();
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].RfTransmitter.ChannelIndex = 0;
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].RfTransmitter.TransmitPowerIndex = antennaPowerLevel.SelectedIndex + 1;
                            #endregion

                            #region Dwell Time and Inventory Cycle
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].Dtic = new Dtic();
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].Dtic.DwellTime = DefaultDwellTime;
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].Dtic.InventoryCycles = 0;
                            #endregion

                            #region Sequence
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].Sequence = new Sequence { SequenceSequence = i + 1 };
                            #endregion

                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand = new C1G2InventoryCommand();
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec = new C1G2SingulationSpec();
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                                .C1G2SingulationSessionAndAction = new C1G2SingulationSessionAndAction();
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand
                                    .C1G2SingulationSpec
                                    .C1G2SingulationSessionAndAction
                                    .C1G2TagInventoryStateAwareSingulationAction =
                                new C1G2TagInventoryStateAwareSingulationAction();
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand
                                    .C1G2SingulationSpec.C1G2SingulationControl =
                                new C1G2SingulationControl();
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand
                                .C1G2SingulationSpec.C1G2SingulationControl
                                .SingulationAlgorithm = new SingulationAlgorithm();
                            setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand
                                .C1G2SingulationSpec.C1G2SingulationControl
                                .SingulationAlgorithmParameters = new SingulationAlgorithmParameters();
                        }
                        #endregion
                    }
                }
            }
            #endregion

            for (var i = 0; i < setInventory.AntennaInvtSpecs[0].AntennaConfigs.Length; i++)
            {
                #region Session : Value is 0 to 3
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationSessionAndAction.SessionId = 0;
                #endregion

                #region State Aware - 0 for SL, 1 for ~SL, 2 for SL_All
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                        .C1G2SingulationSessionAndAction.C1G2TagInventoryStateAwareSingulationAction.S = 0;
                #endregion

                #region Toggle Target
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithmParameters.ToggleTarget = true;
                #endregion

                #region Target - 0 for A, 1 for B
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand
                    .C1G2SingulationSpec
                    .C1G2SingulationSessionAndAction.C1G2TagInventoryStateAwareSingulationAction.I = 0;
                #endregion

                #region Algorithm - 1 for Fixed Q, 2 for Dynamic Q
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithmParameters.Algorithm = 2;
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithm.Algorithm = 2;
                #endregion

                //Q Value
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithmParameters.QValue = 5;
                //Max Q Value
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithmParameters.QMaxValue = 15;
                //Min Q Value
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithmParameters.QMinValue = 0;
                //Retry Count
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithmParameters.RetryCount = 0;
                //Threshold
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithmParameters.ThresholdMultiplier = 4;
                //Repeat unitil no tag
                setInventory.AntennaInvtSpecs[0].AntennaConfigs[i].C1G2InventoryCommand.C1G2SingulationSpec
                    .C1G2SingulationControl.SingulationAlgorithmParameters.RepeatUntilNoTags = false;
            }


            #region RSSI Filter
            if (setInventory.AntennaInvtSpecs[0].TagRssiFilter10Ths == null)
                setInventory.AntennaInvtSpecs[0].TagRssiFilter10Ths = new TagRssiFilter10Ths();

            setInventory.AntennaInvtSpecs[0].TagRssiFilter10Ths.From = setInventory.AntennaInvtSpecs[0].TagRssiFilter10Ths.To = 0;
            #endregion

            #region  Setup start trigger type
            setInventory.InvtStartCriteria.Type = 0;
            setInventory.InvtStartCriteria.PeriodMilsec = 0;
            setInventory.InvtStartCriteria.OffsetMilsec = 0;
            setInventory.InvtStartCriteria.TriggerGpi = null;
            #endregion

            #region Setup stop trigger type
            setInventory.InvtStopCriteria.Type = 0;
            setInventory.InvtStopCriteria.DurationMilsec = 0;
            setInventory.InvtStopCriteria.TriggerGpi = null;
            setInventory.InvtStopCriteria.TagObservation = null;
            setInventory.AntennaInvtSpecs[0].AntennaStopCriteria.Type = 0;
            setInventory.AntennaInvtSpecs[0].AntennaStopCriteria.DurationMilsec = 0;
            setInventory.AntennaInvtSpecs[0].AntennaStopCriteria.TriggerGpi = null;
            setInventory.AntennaInvtSpecs[0].AntennaStopCriteria.TagObservation = null;
            #endregion

            if (setInventory.ReportSpec == null)
                setInventory.ReportSpec = new ReportSpec();

            //0 - "None"
            //2 - "Upon_N_Reads_Or_End_Of_ROSpec"
            //4 - "Upon_N_Seconds_Or_End_Of_ROSpec"
            setInventory.ReportSpec.ReportCriteriaType = 2;
            setInventory.ReportSpec.N = 1;

            changeInventorySetting();
        }
        #endregion

        #region Read Tag
        private uint? TagOPPassword(string mypassword)
        {
            try
            {
                if (mypassword == null)
                    return null;

                var password = mypassword.Trim().Replace(" ", "");

                if (password.Length == 8)
                {
                    var twoWords = password.Insert(4, " ");

                    var firstWord = twoWords.Substring(0, 4).Trim();
                    var secondWord = twoWords.Substring(5, 4).Trim();
                    var tagOpPassword = Convert.ToUInt32(firstWord + secondWord, 16);
                    return tagOpPassword;
                }

                WriteToLogWindows("Error:Wrong password format!");

                return null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private async void TagOPRead_onClick(object sender, RoutedEventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(tagOpTagID))
                {
                    WriteToLogWindows("Please input the target tag");
                    return;
                }
                #region fill tag operation

                var localAntennaInvtSpec = setInventory.AntennaInvtSpecs[0];

                foreach (var antennaConfigs in localAntennaInvtSpec.AntennaConfigs)
                {
                    antennaConfigs.C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl.SingulationAlgorithm
                        .Algorithm = DefaultFixQAlgorithm;
                    antennaConfigs.C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl
                        .SingulationAlgorithmParameters.Algorithm = DefaultFixQAlgorithm;
                    antennaConfigs.C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl
                        .SingulationAlgorithmParameters.QValue = 5;
                    antennaConfigs.C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl
                        .SingulationAlgorithmParameters.RetryCount = DefaultRetryCount;
                    antennaConfigs.C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl
                        .SingulationAlgorithmParameters.ToggleTarget = DefaultToggleTarget;
                    antennaConfigs.C1G2InventoryCommand.C1G2SingulationSpec.C1G2SingulationControl
                        .SingulationAlgorithmParameters.RepeatUntilNoTags = DefaultRepeatUntilNoTags;
                }

                var tagOperation = new TagOperation();
                tagOperation.AntennaInvtSpecs = localAntennaInvtSpec;

                tagOperation.TagOps = new[] { new TagOp() };
                tagOperation.TagOps[0].TagOpId = GetNewTagOpID();
                tagOperation.TagOps[0].TagOpStopCriteria = new TagOpStopCriteria();
                tagOperation.TagOps[0].TagOpStopCriteria.Type = 1;
                tagOperation.TagOps[0].TagOpStopCriteria.CntOperation = DefaultCntOperation;

                tagOperation.TagOps[0].TargetTag = new TargetTag();
                tagOperation.TagOps[0].TargetTag.MemoryBank = 0;
                tagOperation.TagOps[0].TargetTag.Pointer = 0;
                tagOperation.TagOps[0].TargetTag.Match = true;
                tagOperation.TagOps[0].TargetTag.TagPattern = ConvertToLongArray(tagOpTagID);       //Target tag

                WriteToLogWindows("Read");
                WriteToLogWindows("TargetEPC: " + tagOpTagID);

                tagOperation.TagOps[0].TagOpsSpec = new[] { new TagOpsSpec() };
                tagOperation.TagOps[0].TagOpsSpec[0].SpecId = GetNewTagOpSpecID();
                tagOperation.TagOps[0].TagOpsSpec[0].Op = "Read";
                tagOperation.TagOps[0].TagOpsSpec[0].WriteAny = false;
                tagOperation.TagOps[0].TagOpsSpec[0].Data = null;
                tagOperation.TagOps[0].TagOpsSpec[0].LockPayLoads = null;

                //Target Offset
                tagOperation.TagOps[0].TagOpsSpec[0].Pointer = 2;

                //Target Length
                tagOperation.TagOps[0].TagOpsSpec[0].Counter = 6;

                //Memory Bank
                //"Reserved":0
                //"EPC":1
                //"TID":2
                //"USER":3
                tagOperation.TagOps[0].TagOpsSpec[0].MemoryBank = 1;

                tagOperation.TagOps[0].TagOpsSpec[0].Password = TagOPPassword("00000000");
                if (tagOperation.TagOps[0].TagOpsSpec[0].Password == null)
                {
                    WriteToLogWindows("Read tag abort!");
                    return;
                }

                #endregion fill tag operation

                await Task.Run(() =>
                {
                    try
                    {
                        var indexOfAntennas = 0;

                        foreach (var antennaConfigs in localAntennaInvtSpec.AntennaConfigs)
                        {
                            try
                            {
                                _semaphoreSlim.Wait(_semaphoreSlimTimeOut);
                            }
                            catch (Exception exception)
                            {
                                throw exception;
                            }

                            var antennaInvtSpec = new AntennaInvtSpec();
                            antennaInvtSpec.AntennaConfigs = new AntennaConfig[1];
                            antennaInvtSpec.AntennaConfigs[0] = antennaConfigs;
                            antennaInvtSpec.Antennas = new[] { localAntennaInvtSpec.Antennas[indexOfAntennas++] };
                            tagOperation.AntennaInvtSpecs = antennaInvtSpec;

                            var jsonOutput = JsonConvert.SerializeObject(tagOperation);
                            WriteToLogWindows("Antenna " + antennaConfigs.AntennaId);

                            var responseFromServer = WebSocketRequest("exectagoperation", jsonOutput);

                            if (responseFromServer == null)
                            {
                                _semaphoreSlim.Release();
                                break;
                            }

                            var result = RfidReply.FromJson(responseFromServer);
                            if (result.Result.ToLower().Contains("fail"))
                            {
                                WriteToLogWindows("starttagoperation fail(" + result.Desc + ")");

                                _semaphoreSlim.Release();
                                break;
                            }

                            try
                            {
                                _semaphoreSlim.Wait(_semaphoreSlimTimeOut);
                            }
                            catch (Exception exception)
                            {
                                throw exception;
                            }

                            if (isTagOpSuccess != null)
                                if ((bool)isTagOpSuccess)
                                    break;
                        }
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        deleteAllTagOP();
                    }
                });
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        private long[] ConvertToLongArray(string target)
        {
            long[] tagPatternLongs = null;
            try
            {
                var tagPatternFromEPC = Split(target, 2);
                var tagPatternBytes = tagPatternFromEPC.Select(s => Convert.ToByte(s, 16)).ToArray();
                tagPatternLongs = new long[tagPatternBytes.Length];
                for (var i = 0; i < tagPatternLongs.Length; i++)
                    tagPatternLongs[i] = tagPatternBytes[i];
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return tagPatternLongs;
        }

        private int GetNewTagOpID()
        {
            Interlocked.Increment(ref tagOpID);
            if (tagOpID.Equals(0))
                Interlocked.Increment(ref tagOpID);
            return Math.Abs(tagOpID);
        }

        private int GetNewTagOpSpecID()
        {
            Interlocked.Increment(ref tagOpSpecID);
            if (tagOpSpecID.Equals(0))
                Interlocked.Increment(ref tagOpSpecID);
            return Math.Abs(tagOpSpecID);
        }
        #endregion

        private void deleteAllTagOP()
        {
            var param = "tagOpID=0";
            var responseFromServer = WebSocketRequest("deletetagoperation", null, param);
        }

        private enum cmd
        {
            Read = 16,
            Write,
            LockUnlock,
            Kill
        }

        private static IEnumerable<string> Split(string str, int chunkSize)
        {
            return Enumerable.Range(0, str.Length / chunkSize)
                .Select(i => str.Substring(i * chunkSize, chunkSize));
        }

        public static string ByteArrayToString(byte[] ba)
        {
            return BitConverter.ToString(ba).Replace("-", " ");
        }

        private static DateTime epoch2string(long? epoch)
        {
            if (epoch != null)
                return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
                    .AddTicks((long)((epoch << 3) + (epoch << 1)))
                    .ToLocalTime();

            return DateTime.Now;
        }

        #region Show Log
        private void WriteToLogWindows(string message)
        {
            var sb = new StringBuilder();
            sb.Append(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss "));
            sb.Append(message);
            var mySB = sb.ToString();
            writeToLogConcurrentQueue.Enqueue(mySB);
        }

        private void WriteToLogWindows(Exception ex)
        {
            WriteToLogWindows(ex.Message);
        }
        #endregion
    }
}