3

I am writing a WinForm application to use SNMP calls either every 30 seconds or 1 minute.

I have a timer working for calling my SNMP commands, but I want to add a texbox counter that display the total time elapsed during the operation.

There are many problems I am having so here is a list:

  1. I want my SNMP timer 'timer' to execute once before waiting the allotted time so I have it going off at 3 seconds and then changing the interval in my handler. But this sometimes makes the timer go off multiple times which is not what I want.
  2. Every time 'timer' goes off and my SNMP calls execute my counter timer 'appTimer' becomes out of sync. I tried a work around where I check if it is in the other handler and then just jump the timer to its appropriate time. Which works but I feel this is making it too complicated.
  3. My last issue, that I know of, happens when I stop my application using my stop button which does not completely exit the app. When I go to start another run the time between both timers is becomes even greater and for some reason my counting timer 'appTimer' starts counting twice as fast.

I hope this description isn't too confusing but here is my code anyway:

using System;
using System.Net;
using SnmpSharpNet;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public static bool stop = false;
        static bool min = true, eye = false, firstTick = false;
        static string ipAdd = "", fileSaveLocation = "";
        static System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
        static System.Windows.Forms.Timer appTimer = new System.Windows.Forms.Timer();
        static int alarmCounter = 1, hours = 0, minutes = 0, seconds = 0, tenthseconds = 0, count = 0;
        static bool inSNMP = false;
        static TextBox textbox, timeTextbox;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            textbox = outputBox;
            timeTextbox = timeBox;
            ipAdd = "192.168.98.107";
            fileSaveLocation = "c:/Users/bshellnut/Desktop/Eye.txt";
            min = true;
            inSNMP = false;
        }

        private void IPtext_TextChanged(object sender, EventArgs e)
        {
            ipAdd = IPtext.Text;
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            stop = true;
            timer.Stop();
            appTimer.Stop();
            count = 0;
            hours = minutes = seconds = tenthseconds = 0;
            inSNMP = false;
        }

        // This is the method to run when the timer is raised.
        private static void TimerEventProcessor(Object myObject,
                                                EventArgs myEventArgs)
        {
            inSNMP = true;
            timer.Stop();

            if (firstTick == true)
            {
                // Sets the timer interval to 60 seconds or 1 second.
                if (min == true)
                {
                    timer.Interval = 1000 * 60;
                }
                else
                {
                    timer.Interval = 1000 * 30;
                }
            }

            // Displays a message box asking whether to continue running the timer.
            if (stop == false)
            {
                textbox.Clear();
                // Restarts the timer and increments the counter.
                alarmCounter += 1;
                timer.Enabled = true;

                System.IO.StreamWriter file;
                //if (eye == true)
                //{
                    file = new System.IO.StreamWriter(fileSaveLocation, true);
                /*}
                else
                {
                    file = new System.IO.StreamWriter(fileSaveLocation, true);
                }*/

                // SNMP community name
                OctetString community = new OctetString("public");

                // Define agent parameters class
                AgentParameters param = new AgentParameters(community);
                // Set SNMP version to 2 (GET-BULK only works with SNMP ver 2 and 3)
                param.Version = SnmpVersion.Ver2;
                // Construct the agent address object
                // IpAddress class is easy to use here because
                //  it will try to resolve constructor parameter if it doesn't
                //  parse to an IP address
                IpAddress agent = new IpAddress(ipAdd);

                // Construct target
                UdpTarget target = new UdpTarget((IPAddress)agent, 161, 2000, 1);

                // Define Oid that is the root of the MIB
                //  tree you wish to retrieve
                Oid rootOid;
                if (eye == true)
                {
                    rootOid = new Oid("1.3.6.1.4.1.128.5.2.10.14"); // ifDescr
                }
                else
                {
                    rootOid = new Oid("1.3.6.1.4.1.128.5.2.10.15");
                }

                // This Oid represents last Oid returned by
                //  the SNMP agent
                Oid lastOid = (Oid)rootOid.Clone();

                // Pdu class used for all requests
                Pdu pdu = new Pdu(PduType.GetBulk);

                // In this example, set NonRepeaters value to 0
                pdu.NonRepeaters = 0;
                // MaxRepetitions tells the agent how many Oid/Value pairs to return
                // in the response.
                pdu.MaxRepetitions = 5;

                // Loop through results
                while (lastOid != null)
                {
                    // When Pdu class is first constructed, RequestId is set to 0
                    // and during encoding id will be set to the random value
                    // for subsequent requests, id will be set to a value that
                    // needs to be incremented to have unique request ids for each
                    // packet
                    if (pdu.RequestId != 0)
                    {
                        pdu.RequestId += 1;
                    }
                    // Clear Oids from the Pdu class.
                    pdu.VbList.Clear();
                    // Initialize request PDU with the last retrieved Oid
                    pdu.VbList.Add(lastOid);
                    // Make SNMP request
                    SnmpV2Packet result;
                    try
                    {
                        result = (SnmpV2Packet)target.Request(pdu, param);
                    }
                    catch (SnmpSharpNet.SnmpException)
                    { 
                        timer.Stop();
                        textbox.Text = "Could not connect to the IP Provided.";
                        break;
                    }
                    // You should catch exceptions in the Request if using in real application.

                    // If result is null then agent didn't reply or we couldn't parse the reply.
                    if (result != null)
                    {
                        // ErrorStatus other then 0 is an error returned by 
                        // the Agent - see SnmpConstants for error definitions
                        if (result.Pdu.ErrorStatus != 0)
                        {
                            // agent reported an error with the request
                            textbox.Text = "Error in SNMP reply. " + "Error " + result.Pdu.ErrorStatus + " index " + result.Pdu.ErrorIndex;

                            lastOid = null;
                            break;
                        }
                        else
                        {
                            // Walk through returned variable bindings
                            foreach (Vb v in result.Pdu.VbList)
                            {
                                // Check that retrieved Oid is "child" of the root OID
                                if (rootOid.IsRootOf(v.Oid))
                                {
                                    count++;
                                    textbox.Text += "#" + count + " " + v.Oid.ToString() + " " + SnmpConstants.GetTypeName(v.Value.Type) +
                                                   " " + v.Value.ToString() + Environment.NewLine;

                                    file.WriteLine("#" + count + ", " + v.Oid.ToString() + ", " + SnmpConstants.GetTypeName(v.Value.Type) +
                                                   ", " + v.Value.ToString(), true);
                                    if (v.Value.Type == SnmpConstants.SMI_ENDOFMIBVIEW)
                                        lastOid = null;
                                    else
                                        lastOid = v.Oid;
                                }
                                else
                                {
                                    // we have reached the end of the requested
                                    // MIB tree. Set lastOid to null and exit loop
                                    lastOid = null;
                                }
                            }
                        }
                    }
                    else
                    {
                        //Console.WriteLine("No response received from SNMP agent.");
                        textbox.Text = "No response received from SNMP agent.";
                        //outputBox.Text = "No response received from SNMP agent.";
                    }
                }
                target.Close();
                file.Close();
            }
            else
            {
                // Stops the timer.
                //exitFlag = true;
                count = 0;
            }
        }

        private static void ApplicationTimerEventProcessor(Object myObject,
                                                EventArgs myEventArgs)
        {

            tenthseconds += 1;

            if (tenthseconds == 10)
            {
                seconds += 1;
                tenthseconds = 0;
            }

            if (inSNMP && !firstTick)
            {
                if (min)
                {
                    seconds = 60;
                }
                else
                {
                    textbox.Text += "IN 30 SECONDS!!!";
                    if (seconds < 30)
                    {
                        seconds = 30;
                    }
                    else
                    {
                        seconds = 60;
                    }
                }
            }

            if(seconds == 60)
            {
                seconds = 0;
                minutes += 1;
            }

            if(minutes == 60)
            {
                minutes = 0;
                hours += 1;
            }

            timeTextbox.Text = (hours < 10 ? "00" + hours.ToString() : hours.ToString()) + ":" + 
                               (minutes < 10 ? "0" + minutes.ToString() : minutes.ToString()) + ":" +
                               (seconds < 10 ? "0" + seconds.ToString() : seconds.ToString()) + "." +
                               (tenthseconds < 10 ? "0" + tenthseconds.ToString() : tenthseconds.ToString());
            inSNMP = false;
            firstTick = false;
        }

        private void eyeButton_Click(object sender, EventArgs e)
        {
            outputBox.Text = "Connecting...";
            eye = true;
            stop = false;
            count = 0;
            hours = minutes = seconds = tenthseconds = 0;

            timer.Tick += new EventHandler(TimerEventProcessor);

            timer.Interval = 3000;
            firstTick = true;

            appTimer.Tick += new EventHandler(ApplicationTimerEventProcessor);

            appTimer.Interval = 100;

            appTimer.Start();
            timer.Start();
        }

        private void jitterButton_Click(object sender, EventArgs e)
        {
            outputBox.Text = "Connecting...";
            eye = false;
            stop = false;
            count = 0;
            hours = minutes = seconds = tenthseconds = 0;

            timer.Tick += new EventHandler(TimerEventProcessor);

            timer.Interval = 3000;
            firstTick = true;

            appTimer.Tick += new EventHandler(ApplicationTimerEventProcessor);

            appTimer.Interval = 100;

            appTimer.Start();
            timer.Start();
        }

        private void Seconds_CheckedChanged(object sender, EventArgs e)
        {
            min = false;
        }

        private void Minutes_CheckedChanged(object sender, EventArgs e)
        {
            min = true;
        }

        private void exitButton_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void savetextBox_TextChanged(object sender, EventArgs e)
        {
            fileSaveLocation = savetextBox.Text;
        }
    }
}
RoyValentine
  • 143
  • 3
  • 14
  • 2
    I suspect that threading is a better substitute than timers in this case. – Chris Laplante Aug 06 '12 at 22:23
  • 1
    I strongly recommend splitting your code into multiple methodes (or even classes). Your `TimerEventProcessor` is way to big... – CodeZombie Aug 06 '12 at 22:47
  • @SimpleCoder I can and will end up doing that but that wouldn't change the fact that these things happen. – RoyValentine Aug 06 '12 at 23:03
  • @ZombieHunter Also threading might be an option but as far as accessing my textbox in the handler, well from what I've read I should be using Form Timer. – RoyValentine Aug 06 '12 at 23:04
  • Writing to a textbox or the form in general still possible. Instead of accessing the variables directly you use the `Invoke()` method of the form. Take a look at this: http://stackoverflow.com/questions/519233/writing-to-a-textbox-from-another-thread – CodeZombie Aug 06 '12 at 23:07
  • @ZombieHunter Alright. I originally had some Invoke() methods when I accidentally had threading going on. But now I guess I can try using a threading timer. – RoyValentine Aug 06 '12 at 23:18

1 Answers1

3

This is very easy to do with a single timer. The timer has a 1/10th second resolution (or so) and can be used directly to update the elapsed time. You can then use relative elapsed time within that timer to fire off your SNMP transaction, and you can reschedule the next one dynamically.

Here's a simple example

using System;
using System.Drawing;
using System.Windows.Forms;

class Form1 : Form
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    DateTime lastSnmpTime;
    TimeSpan snmpTime = TimeSpan.FromSeconds(30);
    DateTime startTime;
    TextBox elapsedTimeTextBox;
    Timer timer;

    public Form1()
    {
        timer = new Timer { Enabled = false, Interval = 10 };
        timer.Tick += new EventHandler(timer_Tick);

        elapsedTimeTextBox = new TextBox { Location = new Point(10, 10), ReadOnly = true };
        Controls.Add(elapsedTimeTextBox);
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        startTime = DateTime.Now;
        timer.Start();
    }

    void timer_Tick(object sender, EventArgs e)
    {
        // Update elapsed time
        elapsedTimeTextBox.Text = (DateTime.Now - startTime).ToString("g");

        // Send SNMP
        if (DateTime.Now - lastSnmpTime >= snmpTime)
        {
            lastSnmpTime = DateTime.Now;

            // Do SNMP

            // Adjust snmpTime as needed
        }
    }
}

Updated Q&A

With this code the timer fires once at the beginning where after I press the stop button and call timer.Stop() and then press my start button the timer doesn't fire until roughly 12 seconds later. Will resetting the DateTimes fix this?

When the user presses the Start button, set lastSnmpTime = DateTime.MinValue. This causes the TimeSpan of (DateTime.Now - lastSnmpTime) to be over 2,000 years, so it will be greater than snmpTime and will fire immediately.

Also my output time in the text box looks like this: 0:00:02.620262. Why is that? Is there a way to make it display only 0:00:02.62?

When you subtract two DateTime values, the result is a TimeSpan value. I used a standard TimeSpan formatting string of "g". You can use a custom TimeSpan formatting string of @"d\:hh\:mm\:ss\.ff" to get days:hours:minutes:seconds.fraction (2 decimal places).

Also will the timer go on and print out to the text box when it is run for over 9 hours? Because I plan to have this running for 24 hrs+

If you use the custom format with 'd' to show the number of days, it will run for TimeSpan.MaxValue which is slightly more than 10,675,199 days, which is more than 29,000 years.

Tergiver
  • 14,171
  • 3
  • 41
  • 68
  • Thank you. This seems to work very well. I knew there was a better way using DateTime but I couldn't find many good examples. I do have a few questions. With this code the timer fires once at the beginning where after I press the stop button and call timer.Stop() and then press my start button the timer doesn't fire until roughly 12 seconds later. Will resetting the DateTimes fix this? Also will the timer go on and print out to the text box when it is run for over 9 hours? Because I plan to have this running for 24 hrs+. – RoyValentine Aug 07 '12 at 16:57
  • Also my output time in the text box looks like this: 0:00:02.620262. Why is that? Is there a way to make it display only 0:00:02.62? – RoyValentine Aug 07 '12 at 17:08
  • Thank you. This fixes my problems and concerns. – RoyValentine Aug 07 '12 at 21:04
  • For some reason the time displayed in the text box shows the timer being called later by 1 second+ each time around. For example: On the first run it fires at roughly 30 seconds, second run 1:01, 1:32, 2:33, etc. Ultimately this would lead to a big time difference. I am updating lastSnmpTime after the if statement and at the end of the timer handler. Do you think that is the problem? Or does it have something to do with snmpTime? I haven't done anything with that other than assign it 30 like in your example code. – RoyValentine Aug 07 '12 at 22:43
  • @RoyValentine First you have to ask yourself, "Does it matter?" If it's critical to the operation of the application than fix it, but if it's not, don't worry about it. How do you fix it? `snmpTime = DateTime.Now - (lastSnmpTime + TimeSpan.FromSeconds(30));` Make sure you do this **after** all other processing in the timer handler so that we remove the processing time from the equation. – Tergiver Aug 08 '12 at 15:51
  • @RoyValentine Another way to do this: Instead of storing the last trigger time, store the DateTime value of the next trigger. Now you test `if (nextSnmpTime >= DateTime.Now)` and calculate it with `nextSnmpTime = nextSnmpTime + TimeSpan.FromSeconds(30)`. – Tergiver Aug 08 '12 at 16:00
  • Thank you for all the help. I used your second suggestion and it works like a charm. Should've thought of that myself but after staring at code for a while you start writing bad workarounds which is not good. Thanks again. – RoyValentine Aug 09 '12 at 17:24
  • Actually had to change it to `if (nextSnmpTime <= DateTime.Now)` in order to get it to go into the if statement at all. – RoyValentine Aug 09 '12 at 17:38
  • @RoyValentine funny.. I originally wrote `<=`, but quickly edited it. I should have thought about it for more than 3 seconds. – Tergiver Aug 09 '12 at 19:28
  • It's alright. Just took me a moment of "This should be working?" Other than that I was able to get it all working and my time updates correctly after the SNMP calls are made. Thanks again. – RoyValentine Aug 09 '12 at 19:32