Solved

Passing of values between functions

Posted on 2004-08-12
13
789 Views
Last Modified: 2012-05-05
I am trying to plot the incoming data from a multimeter as a function of time and for that purpose I’m trying to use the following code:



#pragma once

#include "MultiMeter.h"

static int evtCounter;
static double dMeterReading ;

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;


namespace Q307398
{
      /// <summary>
      /// Summary for Form1
      ///
      /// WARNING: If you change the name of this class, you will need to change the
      ///          'Resource File Name' property for the managed resource compiler tool
      ///          associated with all .resx files this class depends on.  Otherwise,
      ///          the designers will not be able to interact properly with localized
      ///          resources associated with this form.
      /// </summary>
      public __gc class temperature : public System::Windows::Forms::Form
      {      
      public:
            temperature(void)
            {
                  InitializeComponent();
            }
 

      protected:
            void Dispose(Boolean disposing)
            {
                  if (disposing && components)
                  {
                        components->Dispose();
                  }
                  __super::Dispose(disposing);
            }


    private: System::Windows::Forms::TextBox *  textBox1;
    private: System::Timers::Timer* aTimer;
    //private: int evtCounter;
             MultiMeter *mmeter;

   private: System::ComponentModel::IContainer *  components;

   private:
            /// <summary>
            /// Required designer variable.
            /// </summary>


            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            void InitializeComponent(void)
            {
                  this->textBox1 = new System::Windows::Forms::TextBox();
                  this->SuspendLayout();
                  //
                  // textBox1
                  //
                  this->textBox1->Location = System::Drawing::Point(520, 8);
                  this->textBox1->Name = S"textBox1";
                  this->textBox1->ReadOnly = true;
                  this->textBox1->TabIndex = 3;
                  this->textBox1->Text = S"";
                  this->textBox1->TextAlign = System::Windows::Forms::HorizontalAlignment::Center;
                  //
                  // temperature
                  //
                  this->AutoScaleBaseSize = System::Drawing::Size(5, 13);
                  this->ClientSize = System::Drawing::Size(632, 466);
                  this->Controls->Add(this->textBox1);
                  this->Name = S"temperature";
                  this->Text = S"Form1";
                  this->ResumeLayout(false);

            }      
   
     
      protected:
      void OnPaint( PaintEventArgs *paintEvent )      
      {
         
              // call base OnPaint method
              __super::OnPaint( paintEvent );
         
             // get graphics object
         Graphics *graphicsObject = paintEvent->Graphics;
         SolidBrush *brush = new SolidBrush( Color::Blue );
         Pen *pen = new Pen( Color::Black );
 



                mmeter = new MultiMeter;
            mmeter->ConfigureSerialInterface();
            aTimer = new System::Timers::Timer(500);
            aTimer->Elapsed+=new System::Timers::ElapsedEventHandler(this, OnTimedEvent);
            // Only raise the event the first time Interval elapses.
            aTimer->AutoReset = false;
            aTimer->Enabled = true;

textBox1->Text = System::Convert::ToString(dMeterReading);
graphicsObject->DrawEllipse( pen,evtCounter, dMeterReading * 100, 7, 7 );
                  
}


private:
      void OnTimedEvent(Object* source, System::Timers::ElapsedEventArgs* e)
       {
         try
            {
                        evtCounter += 1;
                //textBox1->Text = System::Convert::ToString(evtCounter,10);

                BYTE *pBuf = NULL;
                        dMeterReading = mmeter->ReadData();
                //textBox1->Text = System::Convert::ToString(dMeterReading);
            
                // Restart timer
                aTimer->Start();
            }
            catch (System::Exception *ex)
            {
                System::Console::WriteLine(ex->Message);
            }  
        }

     };
}



Unfortunately, The values of evtCounter and dMeterReading created in the 'void OnTimedEvent' function cannot be seen in the 'void OnPaint' despite the fact that both variables are declared as static and supposedly should be seen globally. How can these variables be made visible in the part of the program where the OnPaint method is applied?


P.S. Also, is there a way to avoid the above timer code and instead use the timer1 object from the toolbox and just refresh its clicks -- I tried that but it didn't work in this case.
0
Comment
Question by:judico
  • 8
  • 5
13 Comments
 
LVL 19

Expert Comment

by:drichards
ID: 11790035
Easy things first.  You can easily use the toolbox timer.  Just move the code from 'OnTimedEvent' into the auto-generated 'timer1_Tick' method.  The only difference is that you need to add a timer1->Stop() at the beginning of the method so you are not generating extra events while you are processing a previous event.  They stack up.

What didn't work when you tried it?  Worked OK for me.
0
 
LVL 19

Expert Comment

by:drichards
ID: 11790078
Next thing is that All the initialization code should not be in OnPaint which is called MANY times.  If you want it started automatically and not by the button click, it should go in the constructor right after the call to InitializComponent.  If you switch to the other timer, you can remove all the timer code shown here.  Constructor like this:

          temperature(void)
          {
               InitializeComponent();
              mmeter = new MultiMeter;
            mmeter->ConfigureSerialInterface();
            aTimer = new System::Timers::Timer(500);
            aTimer->Elapsed+=new System::Timers::ElapsedEventHandler(this, OnTimedEvent);
            // Only raise the event the first time Interval elapses.
            aTimer->AutoReset = false;
            aTimer->Enabled = true;
          }

and OnPaint like this:

     void OnPaint( PaintEventArgs *paintEvent )    
      {
         
            // call base OnPaint method
            __super::OnPaint( paintEvent );
         
           // get graphics object
         Graphics *graphicsObject = paintEvent->Graphics;
         SolidBrush *brush = new SolidBrush( Color::Blue );
         Pen *pen = new Pen( Color::Black );

         textBox1->Text = System::Convert::ToString(dMeterReading);
         graphicsObject->DrawEllipse( pen,evtCounter, dMeterReading * 100, 7, 7 );      
}

If yo put it anywhere but the constructor, do this:

     if ( mmeter == NULL )
     {
            mmeter = new MultiMeter;
            mmeter->ConfigureSerialInterface();
            aTimer = new System::Timers::Timer(500);
            aTimer->Elapsed+=new System::Timers::ElapsedEventHandler(this, OnTimedEvent);
            // Only raise the event the first time Interval elapses.
            aTimer->AutoReset = false;
            aTimer->Enabled = true;
     }

That will at least protect you from reinitializing over and over.  Now on to the other issues...
0
 
LVL 19

Expert Comment

by:drichards
ID: 11790185
Put 'evtCounter' and 'dMeterReading' inside the Form1 class (right under or above 'mmeter'.  There's a commented-out version of evtCounter there.  Also, don't make them static.  They will be member variables of Form1.  That should work just fine as all your methods using these values are defined in Form1.
0
 
LVL 19

Expert Comment

by:drichards
ID: 11790202
Sorry, for you it's the 'temperature' class.  Code will look like:

    private: System::Windows::Forms::TextBox *  textBox1;
    private: System::Timers::Timer* aTimer;
                int evtCounter;
                double dMeterReading;
                MultiMeter *mmeter;

They should have worked the way you had them, however, was there an error or did something just not work right?

You should also initialize evtCounter in the constructor.

Also normalize dMeterReading prior to drawing circle so it won't go off the drawing area.
0
 

Author Comment

by:judico
ID: 11790707
Unfortunately, the main problem remains -- no matter where and how one declares  'evtCounter' and 'dMeterReading' they remain invisible for the OnPaint method -- the value in textBox3 is zero and the ellipse doesn't move on the screen (I'm not doing normalization here because this is only an example to see if anything happens at all.)

Otherwise 'evtCounter' and 'dMeterReading' are properly created and their values can be seen in textBox1 and textBox2, respectively, which are in the void OnTimedEvent.

Here is the current code, if this can help:



#pragma once

#include "MultiMeter.h"

//static int evtCounter;
//static double dMeterReading;

using namespace System;
using namespace System::IO;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;

namespace Q307398
{
      /// <summary>
      /// Summary for Form1
      ///
      /// WARNING: If you change the name of this class, you will need to change the
      ///          'Resource File Name' property for the managed resource compiler tool
      ///          associated with all .resx files this class depends on.  Otherwise,
      ///          the designers will not be able to interact properly with localized
      ///          resources associated with this form.
      /// </summary>
      public __gc class temperature : public System::Windows::Forms::Form
      {      
      public:
            temperature(void)
            {
                  InitializeComponent();
            }
 
      protected:
            void Dispose(Boolean disposing)
            {
                  if (disposing && components)
                  {
                        components->Dispose();
                  }
                  __super::Dispose(disposing);
            }

    private: System::Windows::Forms::TextBox *  textBox1;
    private: System::Timers::Timer* aTimer;
                int evtCounter;
                double dMeterReading;
                MultiMeter *mmeter;
      private: System::Windows::Forms::Timer *  timer1;
      private: System::Windows::Forms::TextBox *  textBox2;
      private: System::Windows::Forms::TextBox *  textBox3;
      private: System::ComponentModel::IContainer *  components;

      
      private:
            /// <summary>
            /// Required designer variable.
            /// </summary>


            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            void InitializeComponent(void)
            {
                  this->components = new System::ComponentModel::Container();
                  this->textBox1 = new System::Windows::Forms::TextBox();
                  this->timer1 = new System::Windows::Forms::Timer(this->components);
                  this->textBox2 = new System::Windows::Forms::TextBox();
                  this->textBox3 = new System::Windows::Forms::TextBox();
                  this->SuspendLayout();
                  //
                  // textBox1
                  //
                  this->textBox1->Location = System::Drawing::Point(520, 8);
                  this->textBox1->Name = S"textBox1";
                  this->textBox1->ReadOnly = true;
                  this->textBox1->TabIndex = 3;
                  this->textBox1->Text = S"";
                  this->textBox1->TextAlign = System::Windows::Forms::HorizontalAlignment::Center;
                  //
                  // timer1
                  //
                  this->timer1->Enabled = true;
                  this->timer1->Tick += new System::EventHandler(this, timer1_Tick);
                  //
                  // textBox2
                  //
                  this->textBox2->Location = System::Drawing::Point(520, 48);
                  this->textBox2->Multiline = true;
                  this->textBox2->Name = S"textBox2";
                  this->textBox2->ReadOnly = true;
                  this->textBox2->Size = System::Drawing::Size(104, 24);
                  this->textBox2->TabIndex = 4;
                  this->textBox2->Text = S"";
                  this->textBox2->TextAlign = System::Windows::Forms::HorizontalAlignment::Center;
                  //
                  // textBox3
                  //
                  this->textBox3->Location = System::Drawing::Point(520, 136);
                  this->textBox3->Multiline = true;
                  this->textBox3->Name = S"textBox3";
                  this->textBox3->ReadOnly = true;
                  this->textBox3->Size = System::Drawing::Size(104, 24);
                  this->textBox3->TabIndex = 5;
                  this->textBox3->Text = S"";
                  this->textBox3->TextAlign = System::Windows::Forms::HorizontalAlignment::Center;
                  //
                  // temperature
                  //
                  this->AutoScaleBaseSize = System::Drawing::Size(5, 13);
                  this->ClientSize = System::Drawing::Size(632, 466);
                  this->Controls->Add(this->textBox3);
                  this->Controls->Add(this->textBox2);
                  this->Controls->Add(this->textBox1);
                  this->Name = S"temperature";
                  this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
                  this->Text = S"Form1";
                  this->ResumeLayout(false);

            }      
   
   
private:
            void OnTimedEvent(Object* source, System::Timers::ElapsedEventArgs* e)
                  {
         try
            {
                        evtCounter += 1;
                textBox1->Text = System::Convert::ToString(evtCounter,10);

                BYTE *pBuf = NULL;
                    dMeterReading = mmeter->ReadData();
                textBox2->Text = System::Convert::ToString(dMeterReading);
                        // Restart timer
                aTimer->Start();
            }
            catch (System::Exception *ex)
            {
                System::Console::WriteLine(ex->Message);
            }  
        }




protected:
      void OnPaint( PaintEventArgs *paintEvent )      
                              {
              // call base OnPaint method
              __super::OnPaint( paintEvent );
         
             // get graphics object
         Graphics *graphicsObject = paintEvent->Graphics;
         SolidBrush *brush = new SolidBrush( Color::Blue );
         Pen *pen = new Pen( Color::Black );
 
             textBox3->Text = System::Convert::ToString(dMeterReading);
         graphicsObject->DrawEllipse( pen,dMeterReading, dMeterReading * 100, 7, 7 );
                              }




private: System::Void timer1_Tick(System::Object *  sender, System::EventArgs *  e)
             {
  if ( mmeter == NULL )
                        {
            timer1->Stop();
            mmeter = new MultiMeter;
            mmeter->ConfigureSerialInterface();
            aTimer = new System::Timers::Timer(500);
            aTimer->Elapsed+=new System::Timers::ElapsedEventHandler(this, OnTimedEvent);
            // Only raise the event the first time Interval elapses.
            aTimer->AutoReset = false;
            aTimer->Enabled = true;
            evtCounter = 0;
                        }
             }


};
}


P.S. As far as the timer goes, I found out that the timer had been working all the time. The different variants you propose also work. Because the values of evtCounter are not passed to the  OnPaint function I mistakenly have thought that the timer doesn't work. So, that part is all set.
0
 
LVL 19

Accepted Solution

by:
drichards earned 250 total points
ID: 11793236
Why do you say "no matter where and how one declares  'evtCounter' and 'dMeterReading' they remain invisible for the OnPaint method"?  They are visible - set a breakpoint and look.  Your problem is that OnPaint is never called unles you invalidate the drawing surface.  Minimize the app and restore it and you will see a new value in textBox3 because you have forced a repaint.  Setting control values only forces a redraw of teh control.  It does not force an update of teh whole window.

After setting dMeterReading in 'OnTimedEvent' add this line:

    Invalidate();

That will force a repaint of the main form window and OnPaint will be called.  You can also control which area of the screen gets repainted - look at the docs for Control.Invalidate.
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Author Comment

by:judico
ID: 11793735
Thnaks very much. That really did it. One more thing -- how can I avoid the interruption when a checksum exception occurs? Can the program overcome the faulty event and then proceed with the next proper one? I tried remming out msg->Append(S"22-812 checksum failure"); but the program complains.

P.S. I increased your points to 500. Please let me know if you have any problems in receiving the increased points. Thanks again.
0
 
LVL 19

Expert Comment

by:drichards
ID: 11793890
Just comment out the line that displays the message box:

//            System::Windows::Forms::MessageBox::Show(msg->ToString());

You can add some other processing of the error if you want or just ignore it if you don't care.  I think you can just continue on and hopefully it will recover.

The problem is that if you get in a mode where the whole packet is not read in one chunk, the program will not work.  What you really want is to stream the bytes into a data buffer and remove bytes from the stream as they are processed.   That way you don't care if the whole packet gets received in one chunk.  If you have continued problems with checksum failure, you should look into alternatives like this for processing the data.  If they are just intermittent and the program recovers, you probably don't have to worry about it.
0
 

Author Comment

by:judico
ID: 11794134
Thanks. I hope that commenting out that line will be all I need to do now. If the data is not received in one chunk it will be ignored and the next package, hopefully intact, will be read. In this application occasionally ignoring of data will not be a problem. There's another application where it will be but I'll worry about it then. I'd like to finish this project first.

I forgot to ask you something important. When repainting the main form window through  Invalidate(); only the new point is depicted and the previous one is erased. Thus, you have a point moving in time across the screen. I need, however, to see a trace whereby all previous points are retained on the screen. I thought I should probably build an array of the the point values and a for-next loop should redraw it every tiime a new form is repainted. How would you suggext to do that?
0
 
LVL 19

Expert Comment

by:drichards
ID: 11794575
You already have the 'evtCounter' counting the data reads.  Just change dMeterReading to an array and index it with evtCounter.  You'll have to decide what do do when you reach the end of the array - either don't read more points or start over at the beginning.

so instead of "double dMeterReading;" you'd have "double dMeterReading __gc[];" and in initialization code "dMeterReading = new double __gc[1000];".  You can replace 100 with a value of your choice.  Then everywhere in the code where you use dMeterReading, replace it with dMeterReading[evtCounter].  And when you increment evtPointer, make sure it doesn't exceed 999 (or whatever value you choose).
0
 

Author Comment

by:judico
ID: 11794806
It happened so, I just did that before reading your posting. Works fine but the first point is always zero. Maybe that's where the checksum exception occurs. I don't want that first point but I can't get rid of it.
0
 
LVL 19

Expert Comment

by:drichards
ID: 11795006
You can modify the program to ignore points where checksum errors happen.

1) Set dMeterReading to System::Double::MaxValue on error and check this value on return
2)  Set an error flag in multimeter and read it after getting dMeterreading
3) Add a pointer parameter to the read function and pass a pointer to a value that will accept an error code
4) Have the read function return error status and pass dMeterReading to the read function as a pointer.

After the old messagebox line that you removed, do "return System::Double::MaxValue;" instead of "return 0.0;".

Then if the current value of dMeterReading is MaxValue, don't increment evtCounter, don't update textbox, and don't Invalidate().
0
 

Author Comment

by:judico
ID: 11795555
I think you deserve points for that so I posted a special question. Please take a look at the link:

http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/VisualC_PLUS_PLUS_DOT_NET/Q_21093154.html
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

The following diagram presents a diamond class hierarchy: As depicted, diamond inheritance denotes when two classes (e.g., CDerived1 and CDerived2), separately extending a common base class (e.g., CBase), are sub classed simultaneously by a fourt…
In Easy String Encryption Using CryptoAPI in C++ (http://www.experts-exchange.com/viewArticle.jsp?aid=1193) I described how to encrypt text and recommended that the encrypted text be stored as a series of hexadecimal digits -- because cyphertext may…
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now