/// <summary>
        /// Takes in a BlockingCollection and removes data. Sends data to be assessed elsewhere
        /// </summary>
        /// <param name="source">Blocking collection used to hold data for producer consumer pattern</param>
        public void ReceiveGreenhouseData(BlockingCollection <byte[]> source)
        {
            // Create a container for the TLH and moisture packets so we can deserialize them easily
            TLHPacketContainer      tlhContainer      = new TLHPacketContainer();
            MoisturePacketContainer moistureContainer = new MoisturePacketContainer();

            // Take the bytes out of the queue and turn them back into a string
            source.TryTake(out _data);
            string json = Encoding.ASCII.GetString(_data);

            // Take the string to a JObject and deserialize according to the appropriate Type value
            JObject received = JObject.Parse(json);

            Console.WriteLine(received.ToString());
            switch (received["Type"].Value <int>())
            {
            case 0:
                tlhContainer    = JsonConvert.DeserializeObject <TLHPacketContainer>(json);
                _tlhInformation = tlhContainer.Packets;
                break;

            case 1:
                moistureContainer    = JsonConvert.DeserializeObject <MoisturePacketContainer>(json);
                _moistureInformation = moistureContainer.Packets;
                break;

            case 2:
                _limits = JsonConvert.DeserializeObject <LimitPacket>(json);
                break;

            case 3:
                _manual = JsonConvert.DeserializeObject <ManualPacket>(json);
                break;
            }

            // If we have all the TLH information, moisture information, limit and manual information we need...
            if (_tlhInformation != null && _moistureInformation != null && _limits != null && _manual != null)
            {
                Console.WriteLine("Sending to analyzers");

                // Put everything into temporary variables and clear their values afterwards
                TLHPacket[] tlhToSend = new TLHPacket[_tlhInformation.Count];
                _tlhInformation.CopyTo(tlhToSend);
                _tlhInformation.Clear();

                MoisturePacket[] moistureToSend = new MoisturePacket[_moistureInformation.Count];
                _moistureInformation.CopyTo(moistureToSend);
                _moistureInformation.Clear();

                ManualPacket tempManual = _manual;
                _manual = null;
                LimitPacket tempLimits = _limits;
                _limits = null;

                // Send the temporary variables off to be analyzed
                DataAnalyzer data = new DataAnalyzer();
                data.ExecuteActions(tlhToSend, moistureToSend, tempManual, tempLimits);
            }
        }
        /// <summary>
        /// Processes the data received from the packets
        /// </summary>
        /// <param name="data">Array of Packet objects parsed from JSON sent via data server</param>
        private void AnalyzeData(TLHPacket[] tlhData, MoisturePacket[] moistData)
        {
            // Get the approximate current time from the packets
            _currentTime = GetCurrentTime(tlhData);

            // Make sure the Arduino is there
            ArduinoControlSender.Instance.CheckArduinoStatus();

            #region Automation Decision Making
            // Get the averages of greenhouse readings
            _avgTemp  = GetTemperatureAverage(tlhData);
            _avgLight = GetLightAverage(tlhData);

            // Determine what state we need to go to and then create a KVP for it and send it
            GreenhouseState goalTempState = StateMachineContainer.Instance.Temperature.DetermineState(_avgTemp);
            if (goalTempState == GreenhouseState.HEATING || goalTempState == GreenhouseState.COOLING || goalTempState == GreenhouseState.WAITING_FOR_DATA)
            {
                _tempState = new KeyValuePair <IStateMachine, GreenhouseState>(StateMachineContainer.Instance.Temperature, goalTempState);
                // Send the KVP to the control sender
                ArduinoControlSender.Instance.SendCommand(_tempState);
            }

            // Get state for lighting state machines, send commands
            foreach (LightingStateMachine stateMachine in StateMachineContainer.Instance.LightStateMachines)
            {
                // Get the packet from the zone we're currently operating on
                TLHPacket       packet         = tlhData.Where(p => p.ID == stateMachine.Zone).Single();
                double          lightingValue  = packet.Light;
                GreenhouseState goalLightState = stateMachine.DetermineState(_currentTime, lightingValue);
                if (goalLightState == GreenhouseState.LIGHTING || goalLightState == GreenhouseState.SHADING || goalLightState == GreenhouseState.WAITING_FOR_DATA)
                {
                    _lightState = new KeyValuePair <ITimeBasedStateMachine, GreenhouseState>(stateMachine, goalLightState);
                    ArduinoControlSender.Instance.SendCommand(_lightState);
                }
            }

            // Get states for watering state machines, send commands
            foreach (WateringStateMachine stateMachine in StateMachineContainer.Instance.WateringStateMachines)
            {
                // Get the packet from the zone we're currently operating on
                MoisturePacket packet        = moistData.Where(p => p.ID == stateMachine.Zone).Single();
                double         moistureValue = (packet.Probe1 + packet.Probe2) / 2;

                // Get the state we need to transition into, then go send a command appropriate to that
                GreenhouseState goalWaterState = stateMachine.DetermineState(_currentTime, moistureValue);
                if (goalWaterState == GreenhouseState.WATERING || goalWaterState == GreenhouseState.WAITING_FOR_DATA)
                {
                    _waterState = new KeyValuePair <ITimeBasedStateMachine, GreenhouseState>(stateMachine, goalWaterState);
                    ArduinoControlSender.Instance.SendCommand(_waterState);
                }
            }

            // Get state for shading state machine, send commands
            GreenhouseState goalShadeState = StateMachineContainer.Instance.Shading.DetermineState(_avgTemp);
            if (goalShadeState == GreenhouseState.SHADING || goalShadeState == GreenhouseState.WAITING_FOR_DATA)
            {
                _shadeState = new KeyValuePair <IStateMachine, GreenhouseState>(StateMachineContainer.Instance.Shading, goalShadeState);
                ArduinoControlSender.Instance.SendCommand(_shadeState);
            }
            #endregion
        }
        /// <summary>
        /// Takes in a BlockingCollection and removes data. Sends data to be assessed elsewhere
        /// </summary>
        /// <param name="source">Blocking collection used to hold data for producer consumer pattern</param>
        public void ReceiveGreenhouseData(BlockingCollection <byte[]> source)
        {
            // TODO: Fix this up so that it doesn't mess with things already happening within state machines, etc.
            if (source.Count != 0)
            {
                try
                {
                    source.TryTake(out _data);
                    var data = JObject.Parse(Encoding.ASCII.GetString(_data));

                    Console.WriteLine(data.ToString());


                    if (data["Type"].Value <int>() == 0)
                    {
                        _currentTime = data["TimeOfSend"].Value <DateTime>();
                        var deserializedData = JsonConvert.DeserializeObject <TLHPacket>(Encoding.ASCII.GetString(_data));

                        // Check for repeat zones, and if we have any, throw out the old zone data
                        if (_tlhInformation.Where(p => p.ID == deserializedData.ID) != null)
                        {
                            _tlhInformation.RemoveAll(p => p.ID == deserializedData.ID);
                        }

                        _tlhInformation.Add(deserializedData);
                    }
                    // if it's a moisture packet
                    else if (data["Type"].Value <int>() == 1)
                    {
                        var deserializedData = JsonConvert.DeserializeObject <MoisturePacket>(Encoding.ASCII.GetString(_data));

                        // Check for repeat zones, and if we have any, throw out the old zone data
                        if (_moistureInformation.Where(p => p.ID == deserializedData.ID) != null)
                        {
                            _moistureInformation.RemoveAll(p => p.ID == deserializedData.ID);
                        }

                        _moistureInformation.Add(deserializedData);
                    }
                    else if (data["Type"].Value <int>() == 2)
                    {
                        var deserializedData = JsonConvert.DeserializeObject <LimitPacket>(Encoding.ASCII.GetString(_data));

                        _limits = deserializedData;
                    }
                    else if (data["Type"].Value <int>() == 3)
                    {
                        var deserializedData = JsonConvert.DeserializeObject <ManualPacket>(Encoding.ASCII.GetString(_data));

                        _manual = deserializedData;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }

                if (_tlhInformation.Count == 5 && _moistureInformation.Count == 6 && _limits != null && _manual != null)
                {
                    TLHPacket[] tlhToSend = new TLHPacket[_tlhInformation.Count];
                    _tlhInformation.CopyTo(tlhToSend);
                    _tlhInformation.Clear();

                    MoisturePacket[] moistureToSend = new MoisturePacket[_moistureInformation.Count];
                    _moistureInformation.CopyTo(moistureToSend);
                    _moistureInformation.Clear();

                    ManualPacket tempManual = _manual;
                    _manual = null;
                    LimitPacket tempLimits = _limits;
                    _limits = null;

                    DataAnalyzer data = new DataAnalyzer();
                    Task.Run(() => data.ExecuteActions(tlhToSend, moistureToSend, tempManual, tempLimits));
                }
            }
        }