using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Helper;
using NetSDKCS;
using System.Threading;
using System.Collections.ObjectModel;
using System.IO;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Threading;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace RealLoadPicDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //manange waiting for connect device list.
        private List<Device> m_WaitDeviceList = new List<Device>();
        //manange connected device list.
        private List<Device> m_ConnectedDeviceList = new List<Device>();
        //manange connect failed device list.
        private List<Device> m_FailedDeviceList = new List<Device>();
        //manange failed to wait device list.
        private List<Device> m_FailedToWaitList = new List<Device>();
        //manange connected device loginhandle,realloadpicturehandle,ip,channel,etc.
        public ConcurrentDictionary<IntPtr, Device> m_DeviceDic = new ConcurrentDictionary<IntPtr, Device>();

        //event count.
        private int eventCount = 1;
        //error count.
        private int errorCount = 1;

        //device event callback.
        private fAnalyzerDataCallBack m_analyzerDataCallBack;
        //reconnect callback
        private fHaveReConnectCallBack m_ReConnectCallBack;

        //cancellation token source.
        private CancellationTokenSource m_CancellationTokenSource;

        //task factory.
        private TaskFactory m_TaskFactory;

        //check is cancel.
        private bool m_IsCancel = false;

        //login and subscibe device event task.
        private Task m_SubEventTask;
        //analysis device event thread.
        private Thread processEventThread;
        //stop device connected task
        private Task m_StopTask;

        //timer for get queue length.
        private DispatcherTimer dispatcherTimer = new DispatcherTimer();
        //event queue.thread is safe.
        private ConcurrentQueue<EventMsg> m_EventMsgQueue = new ConcurrentQueue<EventMsg>(); 

        //error list for update error ui.
        private ObservableCollection<ErrorInfo> ErrorList
        {
            get;
            set;
        }
        //event message information list for update device event ui.
        private ObservableCollection<EventMessagInfo> EventMessageInfoList
        {
            get;
            set;
        }
        //performance list for update performance ui.
        private ObservableCollection<PerformanceInfo> PerformanceList
        {
            get;
            set;
        }

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            towait_btn.IsEnabled = false;
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            connected_header.Header = string.Format((string)App.Current.Resources["ID_CONNECTED"], connected_listbox.Items.Count);
            failed_header.Header = string.Format((string)App.Current.Resources["ID_FAILED"], faild_listbox.Items.Count);
            m_analyzerDataCallBack = new fAnalyzerDataCallBack(AnalyzerDataCallBack);
            m_ReConnectCallBack = new fHaveReConnectCallBack(ReConnectCallBack);
            NETClient.SetAutoReconnect(m_ReConnectCallBack, IntPtr.Zero); //set reconnect callback.
            ErrorList = new ObservableCollection<ErrorInfo>();
            EventMessageInfoList = new ObservableCollection<EventMessagInfo>();
            PerformanceList = new ObservableCollection<PerformanceInfo>();
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000);
            LogHelper.WriteWithMethod("Leave");
        }

        //device event callback 
        private int AnalyzerDataCallBack(IntPtr lAnalyzerHandle, uint dwAlarmType, IntPtr pAlarmInfo, IntPtr pBuffer, uint dwBufSize, IntPtr dwUser, int nSequence, IntPtr reserved)
        {
            LogHelper.WriteWithMethod("Enter");
            EventMsg msg = new EventMsg();
            msg.LoginID = dwUser;
            msg.AnalysisHandle = lAnalyzerHandle;
            msg.AlarmType = (EM_EVENT_IVS_TYPE)dwAlarmType;
            msg.AlarmInfo = PtrToObj(pAlarmInfo, dwAlarmType);
            byte[] data = new byte[dwBufSize];
            Marshal.Copy(pBuffer, data, 0, (int)dwBufSize);
            msg.AlarmBuffer = data;
            msg.BufferSize = dwBufSize;
            m_EventMsgQueue.Enqueue(msg);
            LogHelper.WriteWithMethod("Leave");
            return 0;
        }
        private object PtrToObj(IntPtr source, uint dwAlarmType)
        {
            object obj = null;
            switch ((EM_EVENT_IVS_TYPE)dwAlarmType)
            {
                case EM_EVENT_IVS_TYPE.TRAFFIC_RUNREDLIGHT:
                    {
                        NET_DEV_EVENT_TRAFFIC_RUNREDLIGHT_INFO info = new NET_DEV_EVENT_TRAFFIC_RUNREDLIGHT_INFO();
                        info = (NET_DEV_EVENT_TRAFFIC_RUNREDLIGHT_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFIC_RUNREDLIGHT_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFICJUNCTION:
                    {
                        NET_DEV_EVENT_TRAFFICJUNCTION_INFO info = new NET_DEV_EVENT_TRAFFICJUNCTION_INFO();
                        info = (NET_DEV_EVENT_TRAFFICJUNCTION_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFICJUNCTION_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFICGATE:
                    {
                        NET_DEV_EVENT_TRAFFICGATE_INFO info = new NET_DEV_EVENT_TRAFFICGATE_INFO();
                        info = (NET_DEV_EVENT_TRAFFICGATE_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFICGATE_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFIC_TURNLEFT:
                    {
                        NET_DEV_EVENT_TRAFFIC_TURNLEFT_INFO info = new NET_DEV_EVENT_TRAFFIC_TURNLEFT_INFO();
                        info = (NET_DEV_EVENT_TRAFFIC_TURNLEFT_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFIC_TURNLEFT_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFIC_TURNRIGHT:
                    {
                        NET_DEV_EVENT_TRAFFIC_TURNRIGHT_INFO info = new NET_DEV_EVENT_TRAFFIC_TURNRIGHT_INFO();
                        info = (NET_DEV_EVENT_TRAFFIC_TURNRIGHT_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFIC_TURNRIGHT_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFIC_OVERSPEED:
                    {
                        NET_DEV_EVENT_TRAFFIC_OVERSPEED_INFO info = new NET_DEV_EVENT_TRAFFIC_OVERSPEED_INFO();
                        info = (NET_DEV_EVENT_TRAFFIC_OVERSPEED_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFIC_OVERSPEED_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFIC_UNDERSPEED:
                    {
                        NET_DEV_EVENT_TRAFFIC_UNDERSPEED_INFO info = new NET_DEV_EVENT_TRAFFIC_UNDERSPEED_INFO();
                        info = (NET_DEV_EVENT_TRAFFIC_UNDERSPEED_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFIC_UNDERSPEED_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFIC_MANUALSNAP:
                    {
                        NET_DEV_EVENT_TRAFFIC_MANUALSNAP_INFO info = new NET_DEV_EVENT_TRAFFIC_MANUALSNAP_INFO();
                        info = (NET_DEV_EVENT_TRAFFIC_MANUALSNAP_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFIC_MANUALSNAP_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFIC_PARKINGSPACEPARKING:
                    {
                        NET_DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO info = new NET_DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO();
                        info = (NET_DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO));
                        obj = info;
                        break;
                    }
                case EM_EVENT_IVS_TYPE.TRAFFIC_PARKINGSPACENOPARKING:
                    {
                        NET_DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO info = new NET_DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO();
                        info = (NET_DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO)Marshal.PtrToStructure(source, typeof(NET_DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO));
                        obj = info;
                        break;
                    }
                default:
                    break;
            }
            return obj;
        }
        //reconnect callback.
        private void ReConnectCallBack(IntPtr lLoginID, IntPtr pchDVRIP, int nDVRPort, IntPtr dwUser)
        {
            LogHelper.WriteWithMethod("Enter");
            string ip = Marshal.PtrToStringAnsi(pchDVRIP);
            Dispatcher.BeginInvoke((Action<string>)UpdateReConnectUI, ip);
            Device dev = null;
            m_DeviceDic.TryGetValue(lLoginID, out dev);
            if (dev != null)
            {
                SubDeviceEvent(dev,new CancellationToken());
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //update device reconnect status.
        private void UpdateReConnectUI(string ip)
        {
            LogHelper.WriteWithMethod("Enter");
            foreach (Device item in connected_listbox.Items)
            {
                if (item.IP == ip)
                {
                    item.DeviceStatus = Status.OnLine;
                    connected_listbox.Items.Refresh();
                    break;
                }
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //check whether is input number.
        private void Port_Input(object sender, TextCompositionEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            try
            {
                Convert.ToUInt32(e.Text);
                e.Handled = false;
            }
            catch (Exception ex)
            {
                LogHelper.WriteException(ex);
                e.Handled = true;
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //click add device event.
        private void AddDevice_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            bool ret = CheckDeviceInfo();
            if (!ret)
            {
                return;
            }
            Device item = m_WaitDeviceList.Find(p => p.IP == ip_tb.Text.Trim());
            if (item != null)
            {
                MessageBox.Show(this, (string)App.Current.Resources["ID_IP_ERROR1"]);
                return;
            }
            ushort port = 0;
            try
            {
                port=Convert.ToUInt16(port_tb.Text.Trim());
            }
            catch (Exception ex)
            {
                LogHelper.WriteException(ex);
                MessageBox.Show(this, (string)App.Current.Resources["ID_PORT_ERROR1"]);
                return;
            }
            Device dev = new Device();
            dev.IP = ip_tb.Text.Trim();
            dev.Port = Convert.ToUInt16(port_tb.Text.Trim());
            dev.UserName = username_tb.Text.Trim();
            dev.Password = password_tb.Password;
            dev.DeviceStatus = Status.UnKnow;
            m_WaitDeviceList.Add(dev);
            wait_listbox.Items.Add(dev);
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        //check ip,port,username
        private bool CheckDeviceInfo()
        {
            LogHelper.WriteWithMethod("Enter");
            if (ip_tb.Text.Trim() == "" || ip_tb.Text == null)
            {
                MessageBox.Show(this, (string)App.Current.Resources["ID_IP_ERROR"]);
                return false;
            }
            if (port_tb.Text.Trim() == "" || port_tb.Text == null)
            {
                MessageBox.Show(this, (string)App.Current.Resources["ID_PORT_ERROR"]);
                return false;
            }
            else
            {
                try
                {
                    Convert.ToUInt16(port_tb.Text.Trim());
                }
                catch (Exception ex)
                {
                    LogHelper.WriteException(ex);
                    MessageBox.Show(this, (string)App.Current.Resources["ID_PORT_ERROR1"]);
                    return false;
                }
            }
            if (username_tb.Text.Trim() == "" || username_tb.Text == null)
            {
                MessageBox.Show(this, (string)App.Current.Resources["ID_USERNAME_ERROR"]);
                return false;
            }
            LogHelper.WriteWithMethod("Leave");
            return true;
        }

        //click del device event.
        private void DelDevice_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            if (wait_listbox.SelectedItem == null)
            {
                LogHelper.WriteWithMethod("Leave null");
                return;
            }
            m_WaitDeviceList.Remove((Device)wait_listbox.SelectedItem);
            wait_listbox.Items.Remove(wait_listbox.SelectedItem);
            wait_listbox.SelectedItem = null;
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        //click start event 
        private void Start_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            m_IsCancel = false;
            if (m_WaitDeviceList.Count == 0)
            {
                LogHelper.WriteWithMethod("Leave");
                return;
            }

            add_device_bt.IsEnabled = false;
            del_device_bt.IsEnabled = false;
            start_bt.IsEnabled = false;
            import_bt.IsEnabled = false;
            export_bt.IsEnabled = false;

            m_CancellationTokenSource = new CancellationTokenSource();
            m_TaskFactory = new TaskFactory(m_CancellationTokenSource.Token);
            m_SubEventTask = m_TaskFactory.StartNew(new Action<object>(f =>
                {
                    LogHelper.WriteWithMethod("Enter");
                    CancellationToken ct = (f as TaskFactory).CancellationToken;
                    foreach (Device item in m_WaitDeviceList)
                    {
                        try
                        {
                            if (ct.IsCancellationRequested)
                            {
                                m_IsCancel = true;
                                LogHelper.WriteWithMethod("cancel");
                                break;
                            }
                            NET_DEVICEINFO_Ex deviceInfo = new NET_DEVICEINFO_Ex();
                            IntPtr loginID = NETClient.Login(item.IP, item.Port, item.UserName, item.Password, EM_LOGIN_SPAC_CAP_TYPE.TCP, IntPtr.Zero, ref deviceInfo);
                            item.LoginID = loginID;
                            item.DeviceStatus = Status.OnLine;
                            item.ChannelCount = deviceInfo.nChanNum;
                            item.ChannelDic = new ConcurrentDictionary<IntPtr, int>();
                            Dispatcher.BeginInvoke((Action<Device>)UpdateWaitUI, item);
                            Dispatcher.BeginInvoke((Action<Device>)UpdateConnectedUI, item);
                            Device dev = new Device();
                            dev = Device.Copy(item);
                            bool ret = m_DeviceDic.TryAdd(loginID, item);
                            if (!ret)
                            {
                                LogHelper.WriteWithMethod(loginID.ToString());
                            }
                            if (ct.IsCancellationRequested)
                            {
                                m_IsCancel = true;
                                LogHelper.WriteWithMethod("cancel");
                                break;
                            }
                            SubDeviceEvent(item,ct);
                            m_DeviceDic.TryUpdate(loginID, item, dev);
                        }
                        catch (Exception ex)
                        {
                            if (ex is NETClientExcetion)
                            {
                                Dispatcher.BeginInvoke((Action<Device>)UpdateWaitUI, item);
                                Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string, string>)UpdateError, ex as NETClientExcetion, "Login", item.IP, null);
                                Dispatcher.BeginInvoke((Action<Device>)UpdateConnectFailedUI, item);
                            }
                            else
                            {
                                LogHelper.WriteException(ex);
                            }
                        }
                    }
                    if (m_IsCancel == false)
                    {
                        Dispatcher.BeginInvoke((Action)UpdateBtn);
                    }
                    LogHelper.WriteWithMethod("Leave");
                }), m_TaskFactory, m_CancellationTokenSource.Token);

            ThreadStart processEventStart = new ThreadStart(ProcessEvent);
            processEventThread = new Thread(processEventStart);
            processEventThread.IsBackground = true;
            processEventThread.Start();

            PerformanceList.Clear();
            dispatcherTimer.Start();
            LogHelper.WriteWithMethod("Leave");
        }
        void UpdateBtn()
        {
            LogHelper.WriteWithMethod("Enter");
            if (m_FailedDeviceList.Count > 0)
            {
                towait_btn.IsEnabled = true;
            }
            LogHelper.WriteWithMethod("Leave");
        }
        //timer event for show current queue length.
        void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            SavePerformance();
            DateTime nowTime=DateTime.Now;
            PerformanceInfo performance = new PerformanceInfo();
            performance.Time = nowTime.ToString("yyyy-MM-dd HH:mm:ss");
            performance.Count = m_EventMsgQueue.Count.ToString();
            PerformanceList.Add(performance);
            performance_listView.ItemsSource = null;
            performance_listView.ItemsSource = PerformanceList;
        }

        private void SavePerformance()
        {
            if (PerformanceList.Count > 2000)
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + "SavePerformance";
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                path = path + "\\" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt";
                FileStream fileStream = new FileStream(path, FileMode.Create);
                using (StreamWriter sw = new StreamWriter(fileStream))
                {
                    foreach (PerformanceInfo item in PerformanceList)
                    {
                        sw.WriteLine(string.Format("Time:{0},Count:{1}", item.Time, item.Count));
                    }
                    sw.Flush();
                    sw.Close();
                }
                PerformanceList.Clear();
                performance_listView.ItemsSource = null;
                performance_listView.Items.Refresh();
            }
        }

        //subscibe device event
        private void SubDeviceEvent(Device dev,CancellationToken ct)
        {
            LogHelper.WriteWithMethod("Enter");
            if (dev == null)
            {
                LogHelper.WriteWithMethod("Leave");
                return;
            }
            for (int i = 0; i < dev.ChannelCount; i++)
            {
                if (ct.IsCancellationRequested)
                {
                    m_IsCancel = true;
                    LogHelper.WriteWithMethod("cancel");
                    break;
                }
                try
                {
                    IntPtr realLoadPicturHandle = NETClient.RealLoadPicture(dev.LoginID, i, (uint)EM_EVENT_IVS_TYPE.ALL, true, m_analyzerDataCallBack, dev.LoginID, IntPtr.Zero);
                    bool ret = dev.ChannelDic.TryAdd(realLoadPicturHandle, i);
                    if (!ret)
                    {
                        LogHelper.WriteWithMethod(i.ToString());
                    }
                }
                catch (Exception ex)
                {
                    if (ex is NETClientExcetion)
                    {
                        Dispatcher.BeginInvoke((Action<NETClientExcetion,string,string,string>)UpdateError, ex as NETClientExcetion, "RealLoadPicture", dev.IP, i.ToString());
                    }
                    else
                    {
                        LogHelper.WriteException(ex);
                    }
                }
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //update wait ui.
        private void UpdateWaitUI(Device dev)
        {
            LogHelper.WriteWithMethod("Enter");
            wait_listbox.Items.Remove(dev);
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        //update connected ui.
        private void UpdateConnectedUI(Device dev)
        {
            LogHelper.WriteWithMethod("Enter");
            Device item = m_ConnectedDeviceList.Find(p => p.IP == dev.IP);
            if( item ==null)
            {
                m_ConnectedDeviceList.Add(dev);
                connected_listbox.Items.Add(dev);
                connected_header.Header = string.Format((string)App.Current.Resources["ID_CONNECTED"], connected_listbox.Items.Count);
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //update failed ui.
        private void UpdateConnectFailedUI(Device dev)
        {
            LogHelper.WriteWithMethod("Enter");
            Device item = m_FailedDeviceList.Find(p => p.IP == dev.IP);
            if (item == null)
            {
                m_FailedDeviceList.Add(dev);
                faild_listbox.Items.Add(dev);
                failed_header.Header = string.Format((string)App.Current.Resources["ID_FAILED"], faild_listbox.Items.Count);
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //update error ui.
        private void UpdateError(NETClientExcetion ex, string type, string ip, string channel)
        {
            LogHelper.WriteWithMethod("Enter");
            ErrorInfo errorInfo = new ErrorInfo();
            errorInfo.ID = errorCount++.ToString();
            errorInfo.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            errorInfo.IP = ip;
            errorInfo.ChannelNumber = channel;
            errorInfo.ErrorType = type;
            errorInfo.ErrorCode = ex.ErrorCode.ToString("X");
            errorInfo.ErrorMessage = ex.Message;
            ErrorList.Add(errorInfo);
            error_listView.ItemsSource = null;
            error_listView.ItemsSource = ErrorList;
            LogHelper.WriteWithMethod("Leave");
        }

        //analysis device event.
        private void ProcessEvent()
        {
            while (true)
            {
				Thread.Sleep(20);
                if (m_EventMsgQueue.Count == 0)
                {
                    continue;
                }
                try
                {
                    EventMsg msg = null;
                    m_EventMsgQueue.TryDequeue(out msg);
                    if (msg == null)
                    {
                        continue;
                    }
                    EventMessagInfo eminfo = null;
                    switch (msg.AlarmType)
                    {
                        case EM_EVENT_IVS_TYPE.TRAFFIC_RUNREDLIGHT:
                            {
                                NET_DEV_EVENT_TRAFFIC_RUNREDLIGHT_INFO info = (NET_DEV_EVENT_TRAFFIC_RUNREDLIGHT_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_RUNREDLIGHT"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER"], GetPlateNumber(info.stuObject));
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFICJUNCTION:
                            {
                                NET_DEV_EVENT_TRAFFICJUNCTION_INFO info = (NET_DEV_EVENT_TRAFFICJUNCTION_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_JUNCTION"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER"], GetPlateNumber(info.stuObject));
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFICGATE:
                            {
                                NET_DEV_EVENT_TRAFFICGATE_INFO info = (NET_DEV_EVENT_TRAFFICGATE_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_GATE"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER"], GetPlateNumber(info.stuObject));
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFIC_TURNLEFT:
                            {
                                NET_DEV_EVENT_TRAFFIC_TURNLEFT_INFO info = (NET_DEV_EVENT_TRAFFIC_TURNLEFT_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_TURNLEFT"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER"], GetPlateNumber(info.stuObject));
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFIC_TURNRIGHT:
                            {
                                NET_DEV_EVENT_TRAFFIC_TURNRIGHT_INFO info = (NET_DEV_EVENT_TRAFFIC_TURNRIGHT_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_TURNRIGHT"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER"], GetPlateNumber(info.stuObject));
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFIC_OVERSPEED:
                            {
                                NET_DEV_EVENT_TRAFFIC_OVERSPEED_INFO info = (NET_DEV_EVENT_TRAFFIC_OVERSPEED_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_OVERSPEED"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER_SPEED"], GetPlateNumber(info.stuObject), info.nSpeed);
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFIC_UNDERSPEED:
                            {
                                NET_DEV_EVENT_TRAFFIC_UNDERSPEED_INFO info = (NET_DEV_EVENT_TRAFFIC_UNDERSPEED_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_UNDERSPEED"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER_SPEED"], GetPlateNumber(info.stuObject), info.nSpeed);
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFIC_MANUALSNAP:
                            {
                                NET_DEV_EVENT_TRAFFIC_MANUALSNAP_INFO info = (NET_DEV_EVENT_TRAFFIC_MANUALSNAP_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_MANUALSNAP"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER"], GetPlateNumber(info.stuObject));
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFIC_PARKINGSPACEPARKING:
                            {
                                NET_DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO info = (NET_DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_PARKINGSPACE"];
                                eminfo.Message = string.Format((string)App.Current.Resources["ID_EVENT_PLATENUMBER"], GetPlateNumber(info.stuObject));
                                break;
                            }
                        case EM_EVENT_IVS_TYPE.TRAFFIC_PARKINGSPACENOPARKING:
                            {
                                NET_DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO info = (NET_DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO)msg.AlarmInfo;
                                string ip = m_DeviceDic[msg.LoginID].IP;
                                int channel = m_DeviceDic[msg.LoginID].ChannelDic[msg.AnalysisHandle];
                                SavePicture(channel, ip, msg.AlarmBuffer, info.UTC);
                                eminfo = new EventMessagInfo();
                                eminfo.ID = eventCount++.ToString();
                                eminfo.Time = info.UTC.ToString();
                                eminfo.IP = ip;
                                eminfo.Channel = channel.ToString();
                                eminfo.Type = (string)App.Current.Resources["ID_EVENT_PARKINGSPACENO"];
                                eminfo.Message = "";
                                break;
                            }
                        default:
                            LogHelper.WriteWithMethod(Enum.GetName(typeof(EM_EVENT_IVS_TYPE), msg.AlarmType));
                            break;
                    }
                    if (eminfo != null)
                    {
                        Dispatcher.BeginInvoke((Action)SaveInfo);
                        Dispatcher.BeginInvoke((Action<EventMessagInfo>)UpdateMessageUI, eminfo);
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.WriteException(ex);
                }
            }
        }

        private void SaveInfo()
        {
            if (EventMessageInfoList.Count > 2000)
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + "SaveEventInfo";
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                path = path + "\\" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt";
                FileStream fileStream = new FileStream(path, FileMode.Create);
                using (StreamWriter sw = new StreamWriter(fileStream))
                {
                    foreach (EventMessagInfo item in EventMessageInfoList)
                    {
                        sw.WriteLine(string.Format("ID:{0},Time:{1},IP:{2},Channel:{3},AlarmType:{4},Message:{5}", item.ID, item.Time, item.IP, item.Channel, item.Type,item.Message));
                    }
                    sw.Flush();
                    sw.Close();
                }
                EventMessageInfoList.Clear();
                event_listview.ItemsSource = null;
                event_listview.Items.Refresh();
            }
        }

        //save picture.
        private void SavePicture(int devChannel,string ip, byte[] data,NET_TIME_EX UTC)
        {
            LogHelper.WriteWithMethod("Enter");
            try
            {
                string currentPath = AppDomain.CurrentDomain.BaseDirectory;
                currentPath = currentPath + "Capture\\";
                string path = currentPath + ip;
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                string fileName = string.Format("{0}-{1}-{2}-{3}-{4}-{5}-{6}--{7}", UTC.dwYear.ToString("D4"),UTC.dwMonth.ToString("D2"),
                    UTC.dwDay.ToString("D2"),UTC.dwHour.ToString("D2"),UTC.dwMinute.ToString("D2"),UTC.dwSecond.ToString("D2"),UTC.dwMillisecond.ToString("D3"), devChannel) + ".jpg";
                string filePath = path + "\\" + fileName;
                using (FileStream fileStream = new FileStream(filePath, FileMode.OpenOrCreate))
                {
                    fileStream.Write(data, 0, data.Length);
                    fileStream.Flush();
                    fileStream.Dispose();
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteException(ex);
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //get plate number
        private string GetPlateNumber(NET_MSG_OBJECT stuObject)
        {
            LogHelper.WriteWithMethod("Enter");
            string platenumber = System.Text.Encoding.Default.GetString(stuObject.szText);
            string[] plate = platenumber.Split('\0');
            if (plate.Length > 0)
            {
                if (plate[0] == "")
                {
                    return (string)App.Current.Resources["ID_UNKNOW"];
                }
                return plate[0];
            }
            LogHelper.WriteWithMethod("Leave");
            return (string)App.Current.Resources["ID_UNKNOW"];
        }

        //update event message ui.
        private void UpdateMessageUI(EventMessagInfo info)
        {
            LogHelper.WriteWithMethod("Enter");
            EventMessageInfoList.Add(info);
            event_listview.ItemsSource = null;
            event_listview.ItemsSource = EventMessageInfoList;
            LogHelper.WriteWithMethod("Leave");
        }

        //click stop event.
        private void Stop_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            stop_bt.IsEnabled = false;
            dispatcherTimer.Stop();
            
            m_StopTask = new Task(() =>
                {
                    processEventThread.Abort();
                    if (m_SubEventTask.Status == TaskStatus.Running)
                    {
                        m_CancellationTokenSource.Cancel();

                        while (true)
                        {
                            if (m_IsCancel == true)
                            {
                                break;
                            }
                            if (m_SubEventTask.Status == TaskStatus.RanToCompletion)
                            {
                                break;
                            }
                            Thread.Sleep(100);
                        }
                        m_IsCancel = false;
                    }
                    Stop();
                });
            m_StopTask.Start();
            LogHelper.WriteWithMethod("Leave");
        }

        //stoploadpic and logout.
        private void Stop()
        {
            LogHelper.WriteWithMethod("Enter");
            foreach(var item in m_DeviceDic)
            {
                IntPtr loginID=item.Key;
                Device dev = item.Value;
                foreach (var item1 in dev.ChannelDic)
                {
                    IntPtr analysisHandle = item1.Key;
                    int channel = item1.Value;
                    try
                    {
                        NETClient.StopLoadPic(analysisHandle);
                    }
                    catch (Exception ex)
                    {
                        if (ex is NETClientExcetion)
                        {
                            Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string, string>)UpdateError, ex as NETClientExcetion, "StopLoadPic", dev.IP, channel.ToString());
                        }
                        else
                        {
                            LogHelper.WriteException(ex);
                        }
                    }
                }
                try
                {
                    NETClient.Logout(dev.LoginID);
                }
                catch (Exception ex)
                {
                    if (ex is NETClientExcetion)
                    {
                        Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string, string>)UpdateError, ex as NETClientExcetion, "Logout", dev.IP, null);
                    }
                    else
                    {
                        LogHelper.WriteException(ex);
                    }
                }
            }
            m_DeviceDic.Clear();
            Dispatcher.BeginInvoke((Action)UpdateUI);
            LogHelper.WriteWithMethod("Leave");
        }

        //update failed and button ui.
        private void UpdateUI()
        {
            LogHelper.WriteWithMethod("Enter");
            m_ConnectedDeviceList.Clear();
            connected_listbox.Items.Clear();
            connected_header.Header = string.Format((string)App.Current.Resources["ID_CONNECTED"], connected_listbox.Items.Count);
            m_FailedDeviceList.Clear();
            faild_listbox.Items.Clear();
            failed_header.Header = string.Format((string)App.Current.Resources["ID_FAILED"], faild_listbox.Items.Count);

            add_device_bt.IsEnabled = true;
            del_device_bt.IsEnabled = true;
            start_bt.IsEnabled = true;
            stop_bt.IsEnabled = true;
            import_bt.IsEnabled = true;
            export_bt.IsEnabled = true;
            towait_btn.IsEnabled = false;

            wait_listbox.Items.Clear();
            foreach (Device item in m_WaitDeviceList)
            {
                item.DeviceStatus = Status.UnKnow;
                wait_listbox.Items.Add(item);
            }
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        //click import event.
        private void Import_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog();
            var res = openFileDialog.ShowDialog();
            if (res == System.Windows.Forms.DialogResult.OK)
            {
                m_WaitDeviceList.Clear();
                wait_listbox.Items.Clear();
                wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
                using(StreamReader sr=new StreamReader(openFileDialog.FileName,Encoding.Unicode))
                {
                    string str = sr.ReadLine();
                    DecodeString(str);
                    while (str!=null)
                    {
                        str = sr.ReadLine();
                        DecodeString(str);
                    }
                }
            }
            LogHelper.WriteWithMethod("Leave");
        }

        // decode import device list.
        private void DecodeString(string str)
        {
            LogHelper.WriteWithMethod("Enter");
            if (str == null)
            {
                LogHelper.WriteWithMethod("Leave");
                return;
            }
            try
            {
                Device dev = new Device();
                dev.IP = str.Substring(3, str.IndexOf(",port=") - 3);
                var item= m_WaitDeviceList.Find(p => p.IP == dev.IP);
                if (item != null)
                {
                    LogHelper.WriteWithMethod("Leave");
                    return;
                }
                string port = str.Substring(str.IndexOf(",port=") + 6, str.IndexOf(",username=") - str.IndexOf(",port=") - 6);
                dev.Port = Convert.ToUInt16(port);
                dev.UserName = str.Substring(str.IndexOf(",username=") + 10, str.IndexOf(",password=") - str.IndexOf(",username=") - 10);
                dev.Password = str.Substring(str.IndexOf(",password=") + 10);
                dev.DeviceStatus = Status.UnKnow;
                m_WaitDeviceList.Add(dev);
                wait_listbox.Items.Add(dev);
                wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            }
            catch (Exception ex)
            {
                LogHelper.WriteException(ex);
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //click export event.
        private void Export_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            System.Windows.Forms.SaveFileDialog saveFileDialog = new System.Windows.Forms.SaveFileDialog();
            saveFileDialog.FileName = "IP";
            saveFileDialog.Filter = "|*.txt";
            var res = saveFileDialog.ShowDialog();
            if (res == System.Windows.Forms.DialogResult.OK)
            {
                using (StreamWriter sw=new StreamWriter(saveFileDialog.FileName,false,Encoding.Unicode))
                {
                    foreach (Device item in m_WaitDeviceList)
                    {
                        string lineString = string.Format("ip={0},port={1},username={2},password={3}", item.IP, item.Port, item.UserName, item.Password);
                        sw.WriteLine(lineString);
                    }
                    sw.Flush();
                    sw.Dispose();
                }
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //for device login failed to re-login.
        private void ToWait_Click(object sender, RoutedEventArgs e)
        {
            m_FailedToWaitList.Clear();
            foreach (var item in m_FailedDeviceList)
            {
                m_FailedToWaitList.Add(item);
                wait_listbox.Items.Add(item);
                wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            }
            m_FailedDeviceList.Clear();
            faild_listbox.Items.Clear();
            failed_header.Header = string.Format((string)App.Current.Resources["ID_FAILED"], faild_listbox.Items.Count);
            towait_btn.IsEnabled = false;

            m_CancellationTokenSource = new CancellationTokenSource();
            m_TaskFactory = new TaskFactory(m_CancellationTokenSource.Token);
            m_SubEventTask = m_TaskFactory.StartNew(new Action<object>(f =>
            {
                LogHelper.WriteWithMethod("Enter");
                CancellationToken ct = (f as TaskFactory).CancellationToken;
                foreach (Device item in m_FailedToWaitList)
                {
                    try
                    {
                        if (ct.IsCancellationRequested)
                        {
                            m_IsCancel = true;
                            LogHelper.WriteWithMethod("cancel");
                            break;
                        }
                        NET_DEVICEINFO_Ex deviceInfo = new NET_DEVICEINFO_Ex();
                        IntPtr loginID = NETClient.Login(item.IP, item.Port, item.UserName, item.Password, EM_LOGIN_SPAC_CAP_TYPE.TCP, IntPtr.Zero, ref deviceInfo);
                        item.LoginID = loginID;
                        item.DeviceStatus = Status.OnLine;
                        item.ChannelCount = deviceInfo.nChanNum;
                        item.ChannelDic = new ConcurrentDictionary<IntPtr, int>();
                        Dispatcher.BeginInvoke((Action<Device>)UpdateWaitUI, item);
                        Dispatcher.BeginInvoke((Action<Device>)UpdateConnectedUI, item);
                        Device dev = new Device();
                        dev = Device.Copy(item);
                        bool ret = m_DeviceDic.TryAdd(loginID, item);
                        if (!ret)
                        {
                            LogHelper.WriteWithMethod(loginID.ToString());
                        }
                        if (ct.IsCancellationRequested)
                        {
                            m_IsCancel = true;
                            LogHelper.WriteWithMethod("cancel");
                            break;
                        }
                        SubDeviceEvent(item, ct);
                        m_DeviceDic.TryUpdate(loginID, item, dev);
                    }
                    catch (Exception ex)
                    {
                        if (ex is NETClientExcetion)
                        {
                            Dispatcher.BeginInvoke((Action<Device>)UpdateWaitUI, item);
                            Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string, string>)UpdateError, ex as NETClientExcetion, "Login", item.IP, null);
                            Dispatcher.BeginInvoke((Action<Device>)UpdateConnectFailedUI, item);
                        }
                        else
                        {
                            LogHelper.WriteException(ex);
                        }
                    }
                }
                if (m_IsCancel == false)
                {
                    Dispatcher.BeginInvoke((Action)UpdateBtn);
                }
                LogHelper.WriteWithMethod("Leave");
            }), m_TaskFactory, m_CancellationTokenSource.Token);
        }
    }

    public class Device
    {
        public string IP { get; set; }
        public ushort Port { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public Status DeviceStatus { get; set; }
        public int ChannelCount { get; set; }
        public IntPtr LoginID { get; set; }
        public ConcurrentDictionary<IntPtr, int> ChannelDic { get; set; }

        public static Device Copy(Device dev)
        {
            Device item = new Device();
            item.ChannelCount = dev.ChannelCount;
            item.ChannelDic = new ConcurrentDictionary<IntPtr, int>();
            foreach (var item1 in dev.ChannelDic)
            {
                item.ChannelDic.TryAdd(item1.Key, item1.Value);
            }
            item.DeviceStatus = dev.DeviceStatus;
            item.IP = dev.IP;
            item.LoginID = dev.LoginID;
            item.Password = dev.Password;
            item.Port = dev.Port;
            item.UserName = dev.UserName;
            return item;
        }

        public override string ToString()
        {
            switch (DeviceStatus)
            {
                case Status.OffLine:
                    return IP + (string)App.Current.Resources["ID_OFFLINE"];
                case Status.OnLine:
                    return IP + (string)App.Current.Resources["ID_ONLINE"];
                default:
                    return IP;
            }
        }
    }

    public enum Status:int
    {
        OffLine = -1,
        UnKnow = 0,
        OnLine = 1,
    }

    public class EventMsg
    {
        public IntPtr LoginID { get; set; }
        public IntPtr AnalysisHandle { get; set; }
        public EM_EVENT_IVS_TYPE AlarmType { get; set; }
        public object AlarmInfo { get; set; }
        public byte[] AlarmBuffer { get; set; }
        public uint BufferSize { get; set; }
    }

    public class ErrorInfo
    {
        public string ID { get; set; }
        public string Time { get; set; }
        public string IP { get; set; }
        public string ChannelNumber { get; set; }
        public string ErrorType { get; set; }
        public string ErrorCode { get; set; }
        public string ErrorMessage { get; set; }
    }

    public class EventMessagInfo
    {
        public string ID { get; set; }
        public string Time { get;set; }
        public string IP { get; set; }
        public string Channel { get; set; }
        public string Type { get; set; }
        public string Message { get; set; }
    }

    public class PerformanceInfo
    {
        public string Time { get; set; }
        public string Count { get; set; }
    }
}
