/// <summary>
        /// The AggregateEvent is used to indicate a change in the current tag list. For example if a tag is removed from a device's read 
        /// zone an AggregateEvent message would be published with a REMOVE and the tag removed. If a tag is added to a device's read zone 
        /// an AggregateEvent message would be published with an ADD and the tag added.
        /// </summary>
        /// <param name="aggregateEvent"></param>
        protected override void ProcessAggregateEvent(TersoSolutions.Jetstream.SDK.Application.Messages.AggregateEvent.Jetstream aggregateEvent)
        {
            string passRfid = string.Empty;
            string logicalDeviceId = aggregateEvent.Header.LogicalDeviceId;
            DateTime receiveDate = aggregateEvent.Header.EventTime;
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("ProcessAggregateEvent for LogicalDevice " + aggregateEvent.Header.LogicalDeviceId);

            // collect and record the PassRfid value associated with this event
            if (aggregateEvent.AggregateEvent.DeviceExtensionList.DeviceExtension != null)
            {
                foreach (var deviceExtension in aggregateEvent.AggregateEvent.DeviceExtensionList.DeviceExtension)
                {
                    if (deviceExtension.Name == "PassRfid") passRfid = deviceExtension.Value;
                    sb.AppendFormat("DeviceExtension {0} = {1}\r\n", deviceExtension.Name, deviceExtension.Value);
                }
            }

            // process each of the EPC actions
            if (aggregateEvent.AggregateEvent.ActionEPCLists.ActionEPCList != null)
            {
                foreach (var epcList in aggregateEvent.AggregateEvent.ActionEPCLists.ActionEPCList)
                {
                    if (epcList.Type == TersoSolutions.Jetstream.SDK.Application.Messages.AggregateEvent.JetstreamAggregateEventActionEPCListsActionEPCListType.ADD)
                    {
                        // collect and record each of the ADD operations 
                        foreach (var epc in epcList.EPC)
                        {
                            sb.AppendFormat("PassID {0} ADDED EPC [{1}]\r\n", passRfid, epc.Value);
                            JetstreamEventRepository.RecordInventory(logicalDeviceId, epc.Value, "In", receiveDate, passRfid);
                        }
                    }
                    else
                    {
                        // collect and record each of the REMOVE operations
                        foreach (var epc in epcList.EPC)
                        {
                            sb.AppendFormat("PassID {0} REMOV EPC [{1}]\r\n", passRfid, epc.Value);
                            JetstreamEventRepository.RecordInventory(logicalDeviceId, epc.Value, "Out", receiveDate, passRfid);
                        }
                    }
                }
            }

            // write the collected data to the event log
            EventLog.WriteEntry("JetstreamSDK ", sb.ToString());

        }
        /// <summary>
        /// Routine to save heartbeat data to the database
        /// </summary>
        /// <param name="heartbeatEvent"></param>
        public static void RecordHeartbeat(TersoSolutions.Jetstream.SDK.Application.Messages.HeartbeatEvent.Jetstream heartbeatEvent)
        {
            try
            {
                MySqlCommand cmd = new MySqlCommand("SaveHeartbeat", ConnectionStringMySql)
                {
                    CommandType = CommandType.StoredProcedure
                };
                cmd.Parameters.AddWithValue("@V_LogicalDeviceId", heartbeatEvent.Header.LogicalDeviceId);
                cmd.Parameters.AddWithValue("@V_LastHeartbeat", heartbeatEvent.Header.ReceivedTime);

                if (cmd.Connection.State == ConnectionState.Closed)
                    cmd.Connection.Open();
                cmd.ExecuteNonQuery();

                if (cmd.Connection.State == ConnectionState.Open)
                    cmd.Connection.Close();
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("JetstreamSDK ", ex.ToString(), EventLogEntryType.Error);
            }
        }
        /// <summary>
        /// The SensorReading event is published when a sensor has provided Jetstream™ with a new reading or a batch of readings.
        /// </summary>
        /// <param name="sensorReadingEvent"></param>
        protected override void ProcessSensorReadingEvent(TersoSolutions.Jetstream.SDK.Application.Messages.SensorReadingEvent.Jetstream sensorReadingEvent)
        {
            StringBuilder sb = new StringBuilder();
            string logicalDeviceId = sensorReadingEvent.Header.LogicalDeviceId;

            sb.AppendLine("ProcessSensorReadingEvent for Logical Device " + logicalDeviceId);
            if (sensorReadingEvent.SensorReadingEvent.ReadingList.Reading != null)
            {
                // collect and record the sensor reading name, value and read time
                foreach (var reading in sensorReadingEvent.SensorReadingEvent.ReadingList.Reading)
                {
                    sb.AppendFormat("Sensor Reading: {0} = {1}\r\n", reading.Name, reading.Value);
                    JetstreamEventRepository.RecordSensorReading(logicalDeviceId, reading.Name, reading.Value, reading.ReadingTime);
                }
            }

            // write the collected data to the event log
            EventLog.WriteEntry("JetstreamSDK ", sb.ToString());
        }
        /// <summary>
        /// The ObjectEvent is used to indicate a tag list that has been read by an RFID reader. This may be generated from a 
        /// triggered reading, a timed reading or a periodic reading based on the device type and the device's configuration.
        /// </summary>
        /// <param name="objectEvent"></param>
        protected override void ProcessObjectEvent(TersoSolutions.Jetstream.SDK.Application.Messages.ObjectEvent.Jetstream objectEvent)
        {
            string logicalDeviceId = objectEvent.Header.LogicalDeviceId;
            DateTime receiveDate = objectEvent.Header.EventTime;
            StringBuilder sb = new StringBuilder();
            List<string> epcsInObjectEvent = new List<string>();

            List<Inventory> inventoriesWithLastStatus = JetstreamEventRepository.GetItemsWithLastStatusInInventory(objectEvent.Header.LogicalDeviceId);
            inventoriesWithLastStatus = JetstreamEventRepository.GetItemsWithLastStatusInInventory(objectEvent.Header.LogicalDeviceId);

            sb.AppendLine("ProcessObjectEvent for Logical Device " + objectEvent.Header.LogicalDeviceId);
            if (objectEvent.ObjectEvent.ActionEPCList != null)
            {
                sb.AppendFormat("Object Event Type: {0}\r\n", objectEvent.ObjectEvent.ActionEPCList.Type);
                if (objectEvent.ObjectEvent.ActionEPCList.EPC != null)
                {
                    // collect and record the baseline reading
                    foreach (var epc in objectEvent.ObjectEvent.ActionEPCList.EPC)
                    {
                        var inventoryStatus = inventoriesWithLastStatus.Where(x => x.TagEPC == epc.Value && x.LogicalDeviceId == logicalDeviceId);
                        //Check if EPC exists in the inventory
                        if (inventoryStatus.Any())
                        {
                            //Chcek if status is remove then add object event to inventory else do nothing
                            if (inventoryStatus.FirstOrDefault().CurrentState.Equals("Remove"))
                            {
                                sb.AppendFormat("{0} EPC [{1}]\r\n", objectEvent.ObjectEvent.ActionEPCList.Type,
                                    epc.Value);
                                JetstreamEventRepository.RecordInventory(logicalDeviceId, epc.Value, "In", receiveDate,
                                    "BASELINE");
                            }
                            else
                            {
                                epcsInObjectEvent.Add(epc.Value);
                            }
                        }
                        else
                        {
                            sb.AppendFormat("{0} EPC [{1}]\r\n", objectEvent.ObjectEvent.ActionEPCList.Type,
                                      epc.Value);
                            JetstreamEventRepository.RecordInventory(logicalDeviceId, epc.Value, "In", receiveDate,
                                "BASELINE");
                        }
                    }

                }
            }

            inventoriesWithLastStatus.RemoveAll(x => epcsInObjectEvent.Contains(x.TagEPC));
            inventoriesWithLastStatus.RemoveAll(x => x.CurrentState == "Remove");

            foreach (var inventory in inventoriesWithLastStatus)
            {
                sb.AppendFormat("{0} EPC [{1}]\r\n", "Remove",
                                    inventory.TagEPC);
                JetstreamEventRepository.RecordInventory(logicalDeviceId, inventory.TagEPC, "Out", receiveDate,
                    "BASELINE");
            }

            if (objectEvent.ObjectEvent.DeviceExtensionList.DeviceExtension != null)
            {
                // collect the device extension name/value pairs
                foreach (var deviceExtension in objectEvent.ObjectEvent.DeviceExtensionList.DeviceExtension)
                {
                    sb.AppendFormat("Device Extension: {0} = {1}\r\n", deviceExtension.Name, deviceExtension.Value);
                }
            }

            // write the collected data to the event log
            EventLog.WriteEntry("JetstreamSDK ", sb.ToString());
        }
        /// <summary>
        /// The LogEntryEvent event data indicates when a device has added a record or multiple records to its syslog or eventlog.
        /// </summary>
        /// <param name="logEntryEvent"></param>
        protected override void ProcessLogEntryEvent(TersoSolutions.Jetstream.SDK.Application.Messages.LogEntryEvent.Jetstream logEntryEvent)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("ProcessLogEntryEvent for Logical Device " + logEntryEvent.Header.LogicalDeviceId);
            foreach (var entry in logEntryEvent.LogEntryEvent.LogEntryList.LogEntry)
            {
                sb.AppendFormat("Level: {0}\r\n", entry.Level.ToString());
                sb.AppendFormat("Log Time: {0} {1}\r\n", entry.LogTime.ToLongDateString(), entry.LogTime.ToLongTimeString());
                sb.AppendFormat("Message: {0}\r\n", entry.Message);
                sb.AppendFormat("Type: {0}\r\n", entry.Type);
                if (entry.ParameterList.Parameter != null)
                {
                    // collect the name/value pairs associated with the log entry event
                    foreach (var parm in entry.ParameterList.Parameter)
                    {
                        sb.AppendFormat("Parameter: {0} = {1}\r\n", parm.Name, parm.Value);
                    }
                }
            }

            // write the collected data to the event log
            EventLog.WriteEntry("JetstreamSDK ", sb.ToString());
        }
        /// <summary>
        /// The HeartbeatEvent is used to indicate that a device is still working properly and has internet connectivity. Each device will 
        /// send a HeartbeatEvent regardless of any other events at the interval specified in it's HeartbeatInterval parameter.
        /// </summary>
        /// <param name="heartbeatEvent"></param>
        protected override void ProcessHeartbeatEvent(TersoSolutions.Jetstream.SDK.Application.Messages.HeartbeatEvent.Jetstream heartbeatEvent)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("Heartbeat Event for Logical Device " + heartbeatEvent.Header.LogicalDeviceId);

            // record the heartbeat event
            JetstreamEventRepository.RecordHeartbeat(heartbeatEvent);

            // write the collected data to the event log
            EventLog.WriteEntry("JetstreamSDK ", sb.ToString());

        }
        /// <summary>
        /// The CommandQueuedEvent event data indicates when an application has queued an event.
        /// </summary>
        /// <param name="commandQueuedEvent"></param>
        protected override void ProcessCommandQueuedEvent(TersoSolutions.Jetstream.SDK.Application.Messages.CommandQueuedEvent.Jetstream commandQueuedEvent)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("ProcessCommandQueuedEvent for Logical Device " + commandQueuedEvent.Header.LogicalDeviceId);

            // record the data associated with the command queued event
            JetstreamEventRepository.RecordCommandQueuedEvent(commandQueuedEvent.CommandQueuedEvent.CommandId, commandQueuedEvent.CommandQueuedEvent.CommandName, commandQueuedEvent.CommandQueuedEvent.UserName);

            // write the collected data to the event log
            EventLog.WriteEntry("JetstreamSDK ", sb.ToString());
        }
        /// <summary>
        /// The CommandCompletionEvent is published when a command has been completed on a device. The CommandCompletionEvent will include any output parameters, 
        /// exceptions and device extensions for completing the command.
        /// </summary>
        /// <param name="commandCompletionEvent"></param>
        protected override void ProcessCommandCompletionEvent(TersoSolutions.Jetstream.SDK.Application.Messages.CommandCompletionEvent.Jetstream commandCompletionEvent)
        {
            string commandId = commandCompletionEvent.CommandCompletionEvent.CommandId;
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("ProcessCommandCompletionEvent for Command Id " + commandId);

            if (commandCompletionEvent.CommandCompletionEvent.DeviceExtensionList.DeviceExtension != null)
            {
                // collect and record each of the device extension name/value pairs
                foreach (var deviceExtension in commandCompletionEvent.CommandCompletionEvent.DeviceExtensionList.DeviceExtension)
                {
                    sb.AppendFormat("DeviceExtension {0} = {1}\r\n", deviceExtension.Name, deviceExtension.Value);
                    JetstreamEventRepository.RecordCommandCompletionEvent(commandId, "DeviceExtension", deviceExtension.Name, deviceExtension.Value);
                }
            }

            if (commandCompletionEvent.CommandCompletionEvent.ExceptionList.Exception != null)
            {
                // collect and record each of the exception name/value pairs
                foreach (var exception in commandCompletionEvent.CommandCompletionEvent.ExceptionList.Exception)
                {
                    sb.AppendFormat("Exception {0} = {1}\r\n", exception.Name, exception.Message);
                    JetstreamEventRepository.RecordCommandCompletionEvent(commandId, "Exception", exception.Name, exception.Message);
                }
            }

            if (commandCompletionEvent.CommandCompletionEvent.OutputParameterList.OutputParameter != null)
            {
                // collect and record each of the output parameter name/value pairs
                foreach (var outputParameter in commandCompletionEvent.CommandCompletionEvent.OutputParameterList.OutputParameter)
                {
                    sb.AppendFormat("Outuput Parameter {0} = {1}\r\n", outputParameter.Name, outputParameter.Value);
                    JetstreamEventRepository.RecordCommandCompletionEvent(commandId, "OutputParameter", outputParameter.Name, outputParameter.Value);
                }
            }

            // write the collected data to the event log
            EventLog.WriteEntry("JetstreamSDK ", sb.ToString());

        }