private static void Send_Heartbeat(object state)
        {
            _numBeat++;

#if RELAY_NODE || CLIENT_NODE
            var size = NetManagerGlobal.MoteMessages.Compose.Heartbeat(NetManagerGlobal.MsgBytes, _netManagerPipe.MACRadioObj.RadioAddress, (ushort)_numBeat, SystemGlobal.NodeType, RoutingGlobal.Parent, RoutingGlobal.Infinity);

            // If in a reset, do not forward TODO: Change this to "spray"
            if (RoutingGlobal._color == Color.Red)
            {
#if DBG_VERBOSE
                Debug.Print("\tIn a Reset wave... not forwarded");
#endif
                return;
            }
            // If parent is available, pass it on
            if (RoutingGlobal.IsParent)
            {
                var status = RoutingGlobal.SendToParent(_netManagerPipe, NetManagerGlobal.MsgBytes, size);
                if (status != 999)
                {
                    RoutingGlobal.UpdateNumTriesInCurrentWindow_Parent(1);
#if !DBG_LOGIC
                    Debug.Print("Updated numTriesInCurrentWindow for Parent " + RoutingGlobal.Parent + "; new value = " + RoutingGlobal.GetNumTriesInCurrentWindow_Parent());
#endif
                }
                else //Retry once
                {
#if !DBG_LOGIC
                    Debug.Print("Retrying packet");
#endif
                    RoutingGlobal.CleanseCandidateTable(_netManagerPipe);
                    Candidate tmpBest = CandidateTable.GetBestCandidate(false);
                    NetManagerGlobal.TempParent = tmpBest.GetMacID();
                    status = NetManagerGlobal.SendToTempParent(_netManagerPipe, NetManagerGlobal.MsgBytes, size);
                    if (status != 999)
                    {
                        tmpBest.UpdateNumTriesInCurrentWindow(1);
#if !DBG_LOGIC
                        Debug.Print("Updated numTriesInCurrentWindow for TempParent " + NetManagerGlobal.TempParent + "; new value = " + tmpBest.GetNumTriesInCurrentWindow());
#endif
                    }
                }
            }
            #region unused
            // Otherwise, broadcast it
            //            {
            //                SystemGlobal.BroadcastBeacon(_netManagerPipe, NetManagerGlobal.MsgBytes, size);

            //#if DBG_VERBOSE
            //                DebuggingSupport.PrintMessageSent(_netManagerPipe, "(broadcast) Heartbeat #" + _numBeat);
            //                SystemGlobal.PrintNumericVals("Net Manager ", NetManagerGlobal.MsgBytes, size);
            //#elif DBG_SIMPLE
            //                Debug.Print("\tBroadcastBeacon heartbeat # " + _numBeat);
            //#endif
            //            }
            #endregion
#endif
#if BASE_STATION
            var msg = NetManagerGlobal.PCMessages.Compose.Heartbeat(_netManagerPipe.MACRadioObj.RadioAddress, _numBeat,
                                                                    SystemGlobal.NodeTypes.Base, RoutingGlobal.Parent);
            try
            {
                var status = _serialComm.Write(msg);
                if (status)
                {
#if DBG_VERBOSE
                    Debug.Print("\n************ Heartbeat generated to PC " + msg.Substring(1, msg.Length - 2));
#elif DBG_SIMPLE
                    Debug.Print("Heartbeat generated");
#endif
                }
                else
                {
                    Debug.Print("Error sending [" + msg + "] to PC");
                }
            }
            catch (Exception ex)
            {
                Debug.Print("SerialComm exception for Heartbeat message [" + msg + "]\n" + ex);
            }
#endif
        }
        private static void OnSendStatus(IMAC macInstance, DateTime time, SendPacketStatus ACKStatus, uint transmitDestination, ushort index)
        {
            var pipe = macInstance as MACPipe;

            switch (ACKStatus)
            {
            case SendPacketStatus.SendACKed:
#if DBG_DIAGNOSTIC
                Debug.Print("\t\tNet Manager: Retry queue length = " + _retriedPackets.Count);
#endif
#if !DBG_LOGIC
                Debug.Print("Heartbeat to " + transmitDestination.ToString() + " ACKed");
#endif
                // Update link metrics
                if ((ushort)transmitDestination == RoutingGlobal.Parent)
                {
                    RoutingGlobal.UpdateNumReceivedInCurrentWindow_Parent(1);
#if !DBG_LOGIC
                    Debug.Print("Updated numReceivedInCurrentWindow for parent " + transmitDestination + "; new value = " + RoutingGlobal.GetNumReceivedInCurrentWindow_Parent());
#endif
                }
                else
                {
                    byte cindex = CandidateTable.findIndex((ushort)transmitDestination);
                    if (cindex < byte.MaxValue)
                    {
                        CandidateTable._candidateList[cindex].UpdateNumReceivedInCurrentWindow(1);
#if !DBG_LOGIC
                        Debug.Print("Updated numReceivedInCurrentWindow for candidate " + transmitDestination + "; new value = " + CandidateTable._candidateList[cindex].GetNumReceivedInCurrentWindow());
#endif
                    }
                }

                if (_retriedPackets.Contains(index))     // If this was a re-try, remove packet from queue
                {
                    _retriedPackets.Remove(index);
                }
                break;

            case SendPacketStatus.SendNACKed:
                    #if !DBG_LOGIC
                Debug.Print("Heartbeat to " + transmitDestination.ToString() + " NACKed");
#endif
                // Update link metrics
                if ((ushort)transmitDestination == RoutingGlobal.Parent)
                {
                    RoutingGlobal.UpdateNumTriesInCurrentWindow_Parent(1);
#if !DBG_LOGIC
                    Debug.Print("Updated numTriesInCurrentWindow for parent " + transmitDestination + "; new value = " + RoutingGlobal.GetNumTriesInCurrentWindow_Parent());
#endif
                }
                else
                {
                    byte cindex = CandidateTable.findIndex((ushort)transmitDestination);
                    if (cindex < byte.MaxValue)
                    {
                        CandidateTable._candidateList[cindex].UpdateNumTriesInCurrentWindow(1);
#if !DBG_LOGIC
                        Debug.Print("Updated numTriesInCurrentWindow for candidate " + transmitDestination + "; new value = " + CandidateTable._candidateList[cindex].GetNumTriesInCurrentWindow());
#endif
                    }
                }
                break;

            case SendPacketStatus.SendFailed:
#if DBG_DIAGNOSTIC
                Debug.Print("\t\tNet Manager: Retry queue length = " + _retriedPackets.Count);
#endif

#if !DBG_LOGIC
                Debug.Print("Heartbeat to " + transmitDestination.ToString() + " failed");
#endif
                // Update link metrics
                if ((ushort)transmitDestination == RoutingGlobal.Parent)
                {
                    RoutingGlobal.UpdateNumTriesInCurrentWindow_Parent(1);
#if !DBG_LOGIC
                    Debug.Print("Updated numTriesInCurrentWindow for parent " + transmitDestination + "; new value = " + RoutingGlobal.GetNumTriesInCurrentWindow_Parent());
#endif
                }
                else
                {
                    byte cindex = CandidateTable.findIndex((ushort)transmitDestination);
                    if (cindex < byte.MaxValue)
                    {
                        CandidateTable._candidateList[cindex].UpdateNumTriesInCurrentWindow(1);
#if !DBG_LOGIC
                        Debug.Print("Updated numTriesInCurrentWindow for candidate " + transmitDestination + "; new value = " + CandidateTable._candidateList[cindex].GetNumTriesInCurrentWindow());
#endif
                    }
                }

                // Retry
                if (!_retriedPackets.Contains(index) && RoutingGlobal._color == Color.Green)     // If packet not there, enqueue it and retry it once
                {
                    RoutingGlobal.CleanseCandidateTable(pipe);
                    Candidate tmpBst = CandidateTable.GetBestCandidate(false);
                    NetManagerGlobal.TempParent = tmpBst.GetMacID();
                    byte[] msg = new byte[NetManagerGlobal.HeartbeatMessageSize];
                    if (pipe.GetMsgWithMsgID(ref msg, index) == DeviceStatus.Success)
                    {
                        NetManagerGlobal.SendToTempParent(pipe, msg, msg.Length);
                        tmpBst.UpdateNumTriesInCurrentWindow(1);
#if !DBG_LOGIC
                        Debug.Print("Updated numTriesInCurrentWindow for TempParent " + transmitDestination + "; new value = " + tmpBst.GetNumTriesInCurrentWindow());
#endif
                        _retriedPackets.Add(index);
                    }
                }
                else     // Retried once; drop packet
                {
                    _retriedPackets.Remove(index);
                }
                break;

            default:
                break;
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="macBase"></param>
        /// <param name="dateTime"></param>
        private static void NetManagerStreamReceive(IMAC macBase, DateTime dateTime, Packet packet)
        {
#if DBG_VERBOSE
            DebuggingSupport.PrintMessageReceived(macBase, "Net Manager");
#elif DBG_SIMPLE
            Debug.Print("");
#endif

            //Debug.Print("\ton " + packet.PayloadType);
            var rcvPayloadBytes = packet.Payload;

            //var payload = new string(Encoding.UTF8.GetChars(rcvPayloadBytes));

#if DBG_VERBOSE
            SystemGlobal.PrintNumericVals("Net Manager Rcv: ", rcvPayloadBytes);
#elif DBG_SIMPLE
            Debug.Print("");
#endif

            switch ((NetManagerGlobal.MessageIds)rcvPayloadBytes[0])
            {
            case NetManagerGlobal.MessageIds.Heartbeat:
                ushort originator;
                ushort numBeat;
                SystemGlobal.NodeTypes nodeType;
                ushort parent;
                byte   TTL;

                NetManagerGlobal.MoteMessages.Parse.HeartBeat(rcvPayloadBytes, out originator, out numBeat, out nodeType, out parent, out TTL);
                // NetManagerGlobal.MoteMessages.Parse.HeartBeat(rcvPayloadBytes, out originator, out numBeat, out nodeType, out parent, out bestetx, out neighbors, out nbrStatus, out numSamplesRec, out numSyncSent, out avgRSSI, out ewrnp);
                // NetManagerGlobal.MoteMessages.Parse.HeartBeat(rcvPayloadBytes, out originator, out numBeat, out nodeType, out parent, out bestetx, out num_nbrs, out neighbors, out nbrStatus, out numSamplesRec, out numSyncSent, out avgRSSI, out ewrnp, out isAvailableForUpperLayers, out TTL);
                Debug.Print("\t>>> Heartbeat #" + numBeat + " from neighbor " + packet.Src + " by " + originator + " with TTL " + TTL);

#if DBG_DIAGNOSTIC
                Debug.Print("Parent: " + parent);
                Debug.Print("");
#endif

#if RELAY_NODE || CLIENT_NODE
                // If we're the originator of the message, or if (TTL-1) is 0, do not pass it on.
                if (originator == _netManagerPipe.MACRadioObj.RadioAddress || --TTL == 0)
                {
                    return;
                }
                RoutingGlobal.AddChild(originator, packet.Src);
                #region Uncomment when not using scheduler
                // TODO: Uncomment lines when not using scheduler
                // If in a reset, do not forward TODO: Change this to "spray"
                if (RoutingGlobal._color == Color.Red)
                {
#if DBG_VERBOSE
                    Debug.Print("\tIn a Reset wave... not forwarded");
#endif
                    return;
                }

                // If parent is available, pass it on
                if (RoutingGlobal.IsParent)
                {
                    byte[] routedMsg = new byte[rcvPayloadBytes.Length];
                    var    size      = NetManagerGlobal.MoteMessages.Compose.Heartbeat(routedMsg, originator, numBeat, nodeType, parent, TTL);
                    var    status    = RoutingGlobal.SendToParent(_netManagerPipe, routedMsg, size);
                    if (status != 999)
                    {
                        RoutingGlobal.UpdateNumTriesInCurrentWindow_Parent(1);
#if !DBG_LOGIC
                        Debug.Print("Updated numTriesInCurrentWindow for Parent " + RoutingGlobal.Parent + "; new value = " + RoutingGlobal.GetNumTriesInCurrentWindow_Parent());
#endif
                    }
                    else     //Retry once
                    {
#if !DBG_LOGIC
                        Debug.Print("Retrying packet");
#endif
                        RoutingGlobal.CleanseCandidateTable(_netManagerPipe);
                        Candidate tmpBest = CandidateTable.GetBestCandidate(false);
                        NetManagerGlobal.TempParent = tmpBest.GetMacID();
                        status = NetManagerGlobal.SendToTempParent(_netManagerPipe, routedMsg, size);
                        if (status != 999)
                        {
                            tmpBest.UpdateNumTriesInCurrentWindow(1);
#if !DBG_LOGIC
                            Debug.Print("Updated numTriesInCurrentWindow for TempParent " + NetManagerGlobal.TempParent + "; new value = " + tmpBest.GetNumTriesInCurrentWindow());
#endif
                        }
                    }
                }
                #endregion
                #region unused
                // If parent is not available, broadcast it
                //{
                //    //var size = NetManagerGlobal.ComposeMessages.CreateHeartbeat(NetManagerGlobal.MsgBytes, _netManagerPipe.MACRadioObj.RadioAddress);
                //    SystemGlobal.BroadcastBeacon(_netManagerPipe, rcvPayloadBytes, packet.Size);
                //}

                //if (payload.Substring(0, 9).Equals("Heartbeat")) //Relay heartbeats, generated hourly
                //{
                //	Debug.Print("\tReceived Heartbeat: " + payload.Substring(0, 9) + "; source: " + payload.Substring(9) + "; from neighbor: " + packet.Src);
                //	if (RoutingGlobal.Parent == SystemGlobal.NoParent)
                //	{
                //		return;
                //	}
                //	var toSendByte = Encoding.UTF8.GetBytes(payload);
                //	var status = _netManagerPipe.Send(RoutingGlobal.Parent, toSendByte, 0, (ushort)toSendByte.Length);
                //	if (status != NetOpStatus.S_Success)
                //	{
                //		Debug.Print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Send status: " + status);
                //	}

                //	SystemGlobal.PrintNumericVals("Net Manager Snd: ", toSendByte);

                //	Debug.Print("Forwarded Heartbeat: " + payload.Substring(0, 9) + "; source: " + payload.Substring(9) + "; from Node: " + packet.Src + " to Node: " + RoutingGlobal.Parent);
                //}
                #endregion
#endif
#if BASE_STATION
                string msg = NetManagerGlobal.PCMessages.Compose.Heartbeat(originator, numBeat, nodeType, parent);
                try
                {
                    _serialComm.Write(msg);
#if DBG_VERBOSE
                    Debug.Print("\n************ Heartbeat forwarded to PC " + msg.Substring(1, msg.Length - 2));
#endif
                }
                catch (Exception ex)
                {
                    Debug.Print("SerialComm exception for Heartbeat message [" + msg + "]\n" + ex);
                }
#endif
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        /*CAUTION: Any change in the advanced heartbeat structure should be accounted for in variables
         * NetManagerGlobal.AdvHeartbeatFixedSize and NetManagerGlobal.EachNeighborInfoSize
         */
        private static void Send_AdvancedHeartbeat(object state)
        {
            ushort[] neighbors = MACBase.NeighborListArray();
            _neighborInfoManagerPipe.MACBase.MACNeighborList(neighbors);

            // Find the number of neighbors
            byte num_nbrs = 0;

            for (int i = 0; i < neighbors.Length; i++)
            {
                if (neighbors[i] == 0) // At the end
                {
                    break;
                }
                num_nbrs++;
            }

            if (num_nbrs == 0)
            {
                return;
            }

            _numBeat++;

            ushort[] valid_nbrs;
            byte[]   nbrStatus;
            ushort[] numSamplesRec;
            ushort[] numSyncSent;
            byte[]   avgRSSI;
            byte[]   ewrnp;
            byte[]   isAvailableForUpperLayers;

            // TODO: Make this if-else more compact
            if (num_nbrs <= NetManagerGlobal.MaxNeighborsPerHeartbeat) // Send all information
            {
                valid_nbrs                = new ushort[num_nbrs];
                nbrStatus                 = new byte[num_nbrs];
                numSamplesRec             = new ushort[num_nbrs];
                numSyncSent               = new ushort[num_nbrs];
                avgRSSI                   = new byte[num_nbrs];
                ewrnp                     = new byte[num_nbrs];
                isAvailableForUpperLayers = new byte[num_nbrs];

                // Initialize ewrnp array with maxEtx
                for (int i = 0; i < ewrnp.Length; i++)
                {
                    ewrnp[i] = RoutingGlobal.MaxEtx;
                }

                for (int i = 0; i < num_nbrs; i++)
                {
                    var      nbr_name = neighbors[i];
                    Neighbor nbr      = _neighborInfoManagerPipe.NeighborStatus(nbr_name);

                    valid_nbrs[i]    = nbr_name;
                    nbrStatus[i]     = (byte)nbr.NeighborStatus;
                    numSamplesRec[i] = nbr.NumOfTimeSamplesRecorded;
                    numSyncSent[i]   = nbr.NumTimeSyncMessagesSent;
                    avgRSSI[i]       = (byte)((nbr.ReceiveLink.AverageRSSI + nbr.SendLink.AverageRSSI)); // * 0.5;

                    int index = CandidateTable.findIndex(nbr_name);
                    if (RoutingGlobal.Parent == nbr_name)
                    {
                        ewrnp[i] = RoutingGlobal.GetPathEWRNP();
                    }
                    else if (index < byte.MaxValue)
                    {
                        ewrnp[i] = (byte)CandidateTable._candidateList[index].GetPathEWRNP();
                    }

                    isAvailableForUpperLayers[i] = nbr.IsAvailableForUpperLayers ? (byte)1 : (byte)0;
                }
            }
            else // Starting with the current head pointer, use neighbor list as a circular array to send out info for NetManagerGlobal.MaxNeighborsPerHeartbeat consecutive neighbors
            {
                valid_nbrs                = new ushort[NetManagerGlobal.MaxNeighborsPerHeartbeat];
                nbrStatus                 = new byte[NetManagerGlobal.MaxNeighborsPerHeartbeat];
                numSamplesRec             = new ushort[NetManagerGlobal.MaxNeighborsPerHeartbeat];
                numSyncSent               = new ushort[NetManagerGlobal.MaxNeighborsPerHeartbeat];
                avgRSSI                   = new byte[NetManagerGlobal.MaxNeighborsPerHeartbeat];
                ewrnp                     = new byte[NetManagerGlobal.MaxNeighborsPerHeartbeat];
                isAvailableForUpperLayers = new byte[NetManagerGlobal.MaxNeighborsPerHeartbeat];

                // Initialize ewrnp array with maxEtx
                for (int i = 0; i < ewrnp.Length; i++)
                {
                    ewrnp[i] = RoutingGlobal.MaxEtx;
                }

                for (int i = 0; i < NetManagerGlobal.MaxNeighborsPerHeartbeat; i++)
                {
                    // If current head pointer has a higher index than number of neighbors (owing to loss), restart at index 0; otherwise, start at current head pointer
                    circ_headptr = (circ_headptr < num_nbrs) ? (byte)(circ_headptr % num_nbrs) : (byte)0;
                    var      nbr_name = neighbors[circ_headptr];
                    Neighbor nbr      = _neighborInfoManagerPipe.NeighborStatus(nbr_name);

                    valid_nbrs[i]    = nbr_name;
                    nbrStatus[i]     = (byte)nbr.NeighborStatus;
                    numSamplesRec[i] = nbr.NumOfTimeSamplesRecorded;
                    numSyncSent[i]   = nbr.NumTimeSyncMessagesSent;
                    avgRSSI[i]       = (byte)((nbr.ReceiveLink.AverageRSSI + nbr.SendLink.AverageRSSI)); // * 0.5;

                    int index = CandidateTable.findIndex(nbr_name);
                    if (RoutingGlobal.Parent == nbr_name)
                    {
                        ewrnp[i] = RoutingGlobal.GetPathEWRNP();
                    }
                    else if (index < byte.MaxValue)
                    {
                        ewrnp[i] = (byte)CandidateTable._candidateList[index].GetPathEWRNP();
                    }

                    isAvailableForUpperLayers[i] = nbr.IsAvailableForUpperLayers ? (byte)1 : (byte)0;

                    circ_headptr = (byte)((circ_headptr + 1) % num_nbrs);
                }

                // Adjust circular buffer head pointer at the end
                circ_headptr = (byte)((circ_headptr + 1) % num_nbrs);
            }

#if DBG_DIAGNOSTIC
            SystemGlobal.PrintNumericVals("Neighbor names: ", valid_nbrs);
            SystemGlobal.PrintNumericVals("Neighbor status: ", nbrStatus);
            SystemGlobal.PrintNumericVals("Avg RSSI: ", avgRSSI);
            SystemGlobal.PrintNumericVals("Routing EWRNP: ", ewrnp);
            SystemGlobal.PrintNumericVals("# samples rcvd: ", numSamplesRec);
            SystemGlobal.PrintNumericVals("# timesync sent: ", numSyncSent);
            SystemGlobal.PrintNumericVals("Available for upper layers: ", isAvailableForUpperLayers);
            Debug.Print("Parent: " + RoutingGlobal.Parent);
            Debug.Print("");
#endif

            var size = NetManagerGlobal.MoteMessages.Compose.Heartbeat(NetManagerGlobal.MsgBytes, _neighborInfoManagerPipe.MACRadioObj.RadioAddress, (ushort)_numBeat, SystemGlobal.NodeType, RoutingGlobal.Parent, (byte)RoutingGlobal.GetPathEWRNP(), valid_nbrs, nbrStatus, numSamplesRec, numSyncSent, avgRSSI, ewrnp, isAvailableForUpperLayers, RoutingGlobal.Infinity);
            //var size = NetManagerGlobal.MoteMessages.Compose.Heartbeat(NetManagerGlobal.MsgBytes, _neighborInfoManagerPipe.MACRadioObj.RadioAddress, (ushort)_numBeat, SystemGlobal.NodeType, RoutingGlobal.Parent, (byte)RoutingGlobal.BestEtx, neighbors, nbrStatus, avgRSSI, ewrnp);
#if !DBG_LOGIC
            Debug.Print("NeighborInfo#" + _numBeat + " size: " + size);
#endif

            #region Uncomment when not using scheduler
            // If in a reset, do not forward TODO: Change this to "spray"
            if (RoutingGlobal._color == Color.Red)
            {
#if DBG_VERBOSE
                Debug.Print("\tIn a Reset wave... not forwarded");
#endif
                return;
            }

            // If parent is available, pass it on
            if (RoutingGlobal.IsParent)
            {
                var status = RoutingGlobal.SendToParent(_neighborInfoManagerPipe, NetManagerGlobal.MsgBytes, size);
                if (status != 999)
                {
                    RoutingGlobal.UpdateNumTriesInCurrentWindow_Parent(1);
#if !DBG_LOGIC
                    Debug.Print("Updated numTriesInCurrentWindow for Parent " + RoutingGlobal.Parent + "; new value = " + RoutingGlobal.GetNumTriesInCurrentWindow_Parent());
#endif
                    if (!_sentPacketSizes.Contains(status))
                    {
                        _sentPacketSizes.Add(status, size);
                    }
                }
                else //Retry once
                {
#if !DBG_LOGIC
                    Debug.Print("Retrying packet");
#endif
                    RoutingGlobal.CleanseCandidateTable(_neighborInfoManagerPipe);
                    Candidate tmpBest = CandidateTable.GetBestCandidate(false);
                    NetManagerGlobal.TempParent = tmpBest.GetMacID();
                    status = NetManagerGlobal.SendToTempParent(_neighborInfoManagerPipe, NetManagerGlobal.MsgBytes, size);
                    if (status != 999)
                    {
                        tmpBest.UpdateNumTriesInCurrentWindow(1);
#if !DBG_LOGIC
                        Debug.Print("Updated numTriesInCurrentWindow for TempParent " + NetManagerGlobal.TempParent + "; new value = " + tmpBest.GetNumTriesInCurrentWindow());
#endif
                        if (!_sentPacketSizes.Contains(status))
                        {
                            _sentPacketSizes.Add(status, size);
                        }
                    }
                }
            }
            #endregion
        }