dorisoy_HardwareMonitor/LibreHardwareMonitorLib/Hardware/Network/Network.cs
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// All Rights Reserved.

using System;
using System.Diagnostics;
using System.Net.NetworkInformation;

namespace LibreHardwareMonitor.Hardware.Network
{
    internal sealed class Network : Hardware
    {
        private readonly Sensor _dataDownloaded;
        private readonly Sensor _dataUploaded;
        private readonly Sensor _downloadSpeed;
        private readonly Sensor _networkUtilization;
        private readonly Sensor _uploadSpeed;
        private long _bytesDownloaded;
        private long _bytesUploaded;
        private long _lastTick;

        public Network(NetworkInterface networkInterface, ISettings settings)
            : base(networkInterface.Name, new Identifier("nic", networkInterface.Id), settings)
        {
            NetworkInterface = networkInterface;
            _dataUploaded = new Sensor("Data Uploaded", 2, SensorType.Data, this, settings);
            ActivateSensor(_dataUploaded);
            _dataDownloaded = new Sensor("Data Downloaded", 3, SensorType.Data, this, settings);
            ActivateSensor(_dataDownloaded);
            _uploadSpeed = new Sensor("Upload Speed", 7, SensorType.Throughput, this, settings);
            ActivateSensor(_uploadSpeed);
            _downloadSpeed = new Sensor("Download Speed", 8, SensorType.Throughput, this, settings);
            ActivateSensor(_downloadSpeed);
            _networkUtilization = new Sensor("Network Utilization", 1, SensorType.Load, this, settings);
            ActivateSensor(_networkUtilization);
            _bytesUploaded = NetworkInterface.GetIPStatistics().BytesSent;
            _bytesDownloaded = NetworkInterface.GetIPStatistics().BytesReceived;
            _lastTick = Stopwatch.GetTimestamp();
        }

        public override HardwareType HardwareType
        {
            get { return HardwareType.Network; }
        }

        internal NetworkInterface NetworkInterface { get; private set; }

        public override void Update()
        {
            try
            {
                if (NetworkInterface == null)
                    return;


                long newTick = Stopwatch.GetTimestamp();
                double dt = new TimeSpan(newTick - _lastTick).TotalSeconds;

                IPv4InterfaceStatistics interfaceStats = NetworkInterface.GetIPv4Statistics();

                // Report out the number of GB (2^30 Bytes) that this interface has up/downloaded. Note
                // that these values can reset back at zero (eg: after waking from sleep).
                _dataUploaded.Value = (float)(interfaceStats.BytesSent / (double)0x40000000);
                _dataDownloaded.Value = (float)(interfaceStats.BytesReceived / (double)0x40000000);

                // Detect a reset in interface stats if the new total is less than what was previously
                // seen. While setting the previous values to zero doesn't encapsulate the value the
                // instant before the reset, it is the best approximation we have.
                if (interfaceStats.BytesSent < _bytesUploaded || interfaceStats.BytesReceived < _bytesDownloaded)
                {
                    _bytesUploaded = 0;
                    _bytesDownloaded = 0;
                }

                long dBytesUploaded = interfaceStats.BytesSent - _bytesUploaded;
                long dBytesDownloaded = interfaceStats.BytesReceived - _bytesDownloaded;

                // Upload and download speeds are reported as the number of bytes transfered over the
                // time difference since the last report. In this way, the values represent the average
                // number of bytes up/downloaded in a second.
                _uploadSpeed.Value = (float)(dBytesUploaded / dt);
                _downloadSpeed.Value = (float)(dBytesDownloaded / dt);

                // Network speed is in bits per second, so when calculating the load on the NIC we first
                // grab the total number of bits up/downloaded
                long dbits = (dBytesUploaded + dBytesDownloaded) * 8;

                // Converts the ratio of total bits transferred over time over theoretical max bits
                // transfer rate into a percentage load
                double load = (dbits / dt / NetworkInterface.Speed) * 100;

                // Finally clamp the value between 0% and 100% to avoid reporting nonsensical numbers
                _networkUtilization.Value = (float)Math.Min(Math.Max(load, 0), 100);

                // Store the recorded values and time, so they can be used in the next update
                _bytesUploaded = interfaceStats.BytesSent;
                _bytesDownloaded = interfaceStats.BytesReceived;
                _lastTick = newTick;
            }
            catch (NetworkInformationException networkInformationException) when (unchecked(networkInformationException.HResult == (int)0x80004005))
            {
                foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
                {
                    if (networkInterface.Id.Equals(NetworkInterface?.Id))
                    {
                        NetworkInterface = networkInterface;
                        break;
                    }
                }
            }
        }
    }
}
