2

I have WPF application with PcapDotNet DLL's that measure my machine Interface Rate.

This is the Model:

public class Interface
{
    public PacketDevice PacketDevice { get { return livePacketDevice; } }
    private DateTime _lastTimestamp;
    private double _bitsPerSecond;
    private double _packetsPerSecond;
    private DateTime _lastTimestamp;
    private static List<Interface> _machineInterfaces; // list of all machine interfaces

    public void Start(Interface inf)
    {
        OpenAdapterForStatistics(inf.PacketDevice);
    }

    public void OpenAdapterForStatistics(PacketDevice selectedOutputDevice)
    {
        if (selectedOutputDevice != null)
        {
            using (PacketCommunicator statCommunicator = selectedOutputDevice.Open(100, PacketDeviceOpenAttributes.Promiscuous, 1000)) //open the output adapter
            {
                try
                {
                    statCommunicator.Mode = PacketCommunicatorMode.Statistics; //put the interface in statstics mode
                    statCommunicator.ReceiveStatistics(0, StatisticsHandler); //start the main loop
                }
                catch (Exception)
                { }
            }
        }
    }

    private void StatisticsHandler(PacketSampleStatistics statistics)
    {
        DateTime currentTimestamp = statistics.Timestamp; //current sample time
        DateTime previousTimestamp = _lastTimestamp; //previous sample time
        _lastTimestamp = currentTimestamp; //set _lastTimestamp for the next iteration

        if (previousTimestamp == DateTime.MinValue) //if there wasn't a previous sample than skip this iteration (it's the first iteration)
            return;

        double delayInSeconds = (currentTimestamp - previousTimestamp).TotalSeconds; //calculate the delay from the last sample
        _bitsPerSecond = statistics.AcceptedBytes * 8 / delayInSeconds; //calculate bits per second
        _packetsPerSecond = statistics.AcceptedPackets / delayInSeconds; //calculate packets per second

        if (NewPointEventHandler != null)
            NewPointEventHandler(_bitsPerSecond);
        double value = _packetsPerSecond;
    }

As you can see Start method start to measure the Interface rate and put the values into 2 fields:

_bitsPerSecond and _packetsPerSecond.

So after the application start i have this field:

List<Interface> _machineInterfaces; 

That read all my machine interfaces.

After that i start my Start method:

    private void StartStatistics()
    {
        int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
        Interface inf = new Interface();
        ThreadStart tStarter = delegate
        {              
            inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
        };
        Thread thread = new Thread(tStarter);
        thread.IsBackground = true;
        thread.Start();

        statisticsTimer.Start(); // start my timer
    }
  • OK so now this is my Question:

This is my Timer Tick Event:

public RadObservableCollection<double> mbitPerSecondValue { get; private set; }

If my BitsPerSecond Class Interface member define as regular and not Static it's value is always zero:

    private void statisticsTimer_Tick(object sender, EventArgs e)
    {
        int index = listview.SelectedIndex;

        double bps = Interface.MachineInterfaces[index].BitsPerSecond; // always zero !
        mbitPerSecondValue.Add(bps);
    }

In case BitsPerSecond define as static all good:

    private void statisticsTimer_Tick(object sender, EventArgs e)
    {
        int index = listview.SelectedIndex;
        double bps = Interface.BitsPerSecond;
        mbitPerSecondValue.Add(bps);
    }

So my question is why ?

Edit

Currently i changed my function:

private void StartStatistics()
        {
            int index = lvAdapters.SelectedIndex;
            Interface inf = new Interface();
            ThreadStart tStarter = delegate
            {
                foreach (Interface item in Interface.MachineInterfaces)
                    item.Start();
            };

            Thread thread = new Thread(tStarter);
            thread.IsBackground = true;
            thread.Start();

            statisticsTimer.Start();
        }

What i want to achieve is to open statistics on each interface on my machine , but again in the first interface (i have 2) i can see the traffic changing (BitsPerSecond) but in the second interface it's always zero (i make sure to generate some traffic through this interface so it not supposed to be zero)

mark yer
  • 403
  • 6
  • 12
  • Your edit is a different question, and maybe it should be made as such, since it's no longer related to whether the class member is static or not (which is what the question title is about). – almulo Jul 02 '15 at 08:09
  • Also... I don't see nothing strange there. Have you debugged your app and made sure that `StatisticsHandler` is being called and actually updates the values in the second Interface? Maybe it is zero, even if it's not supposed to. – almulo Jul 02 '15 at 08:11
  • You're saying that my code now is open all my interfaces and this looks good ? – mark yer Jul 02 '15 at 09:43

2 Answers2

2

For your second question, try to call each Interface's Start from different threads. The only suspicious thing I see is that maybe statCommunicator.ReceiveStatistics is blocking the thread and preventing the other Interfaces from being Started.

This should avoid that problem:

private void StartStatistics()
{
    foreach (Interface item in Interface.MachineInterfaces)
    {
        ThreadStart tStarter = delegate
        {
            item.Start();
        };

        Thread thread = new Thread(tStarter);
        thread.IsBackground = true;
        thread.Start();
    }

    statisticsTimer.Start();
}
almulo
  • 4,918
  • 1
  • 20
  • 29
0

Well, it's obvious why it's working when defined as static: all instances of Interface are sharing the same property, so when you increment its value from one place, the new value is automatically available everywhere.

But as a regular non-static property, you have to make sure you're reading from the same instance you've previously modified. And you're not.

First of all, you're creating a new Interface (let's call it Interface A) and then calling its Start, passing another Interface (that we'll call Interface B) that you get from Interface.MachineInterfaces, as parameter:

private void StartStatistics()
{
    ...
    Interface inf = new Interface();
    ThreadStart tStarter = delegate
    {              
        inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
    };
    ...
}

Inside the Start method of Interface A, you're subscribing to the statistics of Interface B, but the handler is still in Interface A:

public void Start(Interface inf)
{
    OpenAdapterForStatistics(inf.PacketDevice);
}

public void OpenAdapterForStatistics(PacketDevice selectedOutputDevice)
{
    ...
    statCommunicator.ReceiveStatistics(0, StatisticsHandler); //start the main loop
    ...
}

And when the handler in Interface A is called, it increments its own _bitsPerSecond value. Not Interface B's, but Interface A's.

private void StatisticsHandler(PacketSampleStatistics statistics)
{
    ...
    _bitsPerSecond = statistics.AcceptedBytes * 8 / delayInSeconds; //calculate bits per second
    ...
}

But in the end, you're checking the value of BitsPerSecond in Interface B, taken again from Interface.MachineInterfaces!

private void statisticsTimer_Tick(object sender, EventArgs e)
{
    ...
    double bps = Interface.MachineInterfaces[index].BitsPerSecond; // always zero !
    ...
}

-- SUGGESTED SOLUTION 1 --

Why don't you just make it so Start uses its own instance, so you don't have to create a new Interface just to use it?

public void Start()
{
    OpenAdapterForStatistics(this.PacketDevice);
}

That way you can just do:

private void StartStatistics()
{
    int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
    ThreadStart tStarter = delegate
    {              
        Interface.MachineInterfaces[index].Start(); // send the selected interface
    };
    Thread thread = new Thread(tStarter);
    thread.IsBackground = true;
    thread.Start();

    statisticsTimer.Start(); // start my timer
}

...And you should get the desired output in your Timer Tick callback.

-- SUGGESTED SOLUTION 2 --

If you don't want to call Start from the original Interfaces inside Interface.MachineInterfaces, then you'll have to store the new Interface in some kind of Dictionary, so you can access it later to get BitsPerSecond from it:

private Dictionary<Interface, Interface> InterfaceDictionary = new Dictionary<Interface, Interface>();

private void StartStatistics()
{
    int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
    Interface inf = new Interface();
    ThreadStart tStarter = delegate
    {              
        inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
    };
    Thread thread = new Thread(tStarter);
    thread.IsBackground = true;
    thread.Start();

    statisticsTimer.Start(); // start my timer

    if (InterfaceDictionary.ContainsKey(Interface.MachineInterfaces[index]))
        InterfaceDictionary[Interface.MachineInterfaces[index]] = inf;
    else
        InterfaceDictionary.Add(Interface.MachineInterfaces[index], inf);
}

And in your Timer Tick callback, grab the data from the associated Interface, not from the one in Interface.MachineInterfaces:

private void statisticsTimer_Tick(object sender, EventArgs e)
{
    int index = listview.SelectedIndex;
    var interface = InterfaceDictionary[Interface.MachineInterfaces[index]];
    double bps = interface.BitsPerSecond;
    mbitPerSecondValue.Add(bps);
}
almulo
  • 4,918
  • 1
  • 20
  • 29
  • So the method Start() should be called on each Interface that i have in my Interface.MachineInterfaces ? – mark yer Jul 01 '15 at 15:43
  • Not necessarily... All I'm saying is that calling `Start` from one Interface and then checking `BitsPerSecond` from another Interface is not gonna work. You need both objects to be the same, so either you call `Start` from the original Interface object in MachineInterfaces, or you store the new Interface you create for calling `Start`, so you can check its `BitsPerSecond` later (I'll add that opiton to my answer, too). – almulo Jul 01 '15 at 15:54
  • All i want is to call Start() with all my machine interfaces to show all it's values in my chart in the same time, so what is the best way to do that ? – mark yer Jul 01 '15 at 15:56
  • That's not what your code is doing, nor what the question is about... Right? :/ Right now it seems like you're only calling `Start` for the selected Interface (via SelectedIndex of some ListView), and you asked why it worked when the property was static. – almulo Jul 01 '15 at 16:36
  • That's because this didn't work so i changed this to something that works, i want in my StartStatistics() function to open statistics for all my machine interfaces and show all it's values ar the same time – mark yer Jul 01 '15 at 18:23