Link to home
Start Free TrialLog in
Avatar of judico
judico

asked on

How can data resulting from checksum exceptions in VC++ /NET be ignored

Occasionally, when communicating with an external device the incoming data is corrupted and a checksum exception occurs. I'd like to ignore such data. What is the best way to do that?
Avatar of drichards
drichards

When checksum error occurs, set the data value to a well-known value outside the normal range and then ignore all data with that well-known value.
Avatar of judico

ASKER

drichards,

I tried to do all you advise me to do in:

https://www.experts-exchange.com/questions/21092168/Passing-of-values-between-functions.html

1) In MultiMeter.cpp I replaced return 0.0; with

return System::Double::MaxValue;

2) in temperature.h I placed if-else selection statement in the OnTimeEvent function:


private:
            void OnTimedEvent(Object* source, System::Timers::ElapsedEventArgs* e)
                  {
         try
            {
                        BYTE *pBuf = NULL;
                    dMeterReading = mmeter->ReadData();
                        
                              if ( dMeterReading == System::Double::MaxValue)
                                    ;
                              else

                        evtCounter += 1;
                        
                        yVALUE[ evtCounter ] = dMeterReading;
                        Invalidate();
                textBox2->Text = System::Convert::ToString(dMeterReading);
                        
                        xVALUE[ evtCounter ] = evtCounter;
                textBox1->Text = System::Convert::ToString(evtCounter,10);
            
                        // Restart timer
                aTimer->Start();
            }
            catch (System::Exception *ex)
            {
                System::Console::WriteLine(ex->Message);
            }  
        }


but these changes still don’t ignore the faulty data from being plotted. Maybe there is something more I should do because I don’t understand very well all points in your instruction:

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.
Avatar of judico

ASKER

I applied a palliative solution. Just avoing plotting a point that's zero:

 for (int iii = 1; iii <= 500; iii++)
                         {
                              if ( yVALUE[ iii ]  == 0)
                                    
                                            ;
                              
                                       else
                  
               graphicsObject->DrawEllipse( pen, xVALUE[ iii ], yVALUE[ iii] * 10, 7, 7 );
                        }      
Like this:

private:
          void OnTimedEvent(Object* source, System::Timers::ElapsedEventArgs* e)
               {
         try
            {
                    BYTE *pBuf = NULL;
                  dMeterReading = mmeter->ReadData();
                   
                         if ( dMeterReading == System::Double::MaxValue) {}
                         else
                         {
                             evtCounter += 1;
                             yVALUE[ evtCounter ] = dMeterReading;
                             Invalidate();
                             textBox2->Text = System::Convert::ToString(dMeterReading);
               
                             xVALUE[ evtCounter ] = evtCounter;
                             textBox1->Text = System::Convert::ToString(evtCounter,10);
                          }
                    // Restart timer
                aTimer->Start();
            }
            catch (System::Exception *ex)
            {
                System::Console::WriteLine(ex->Message);
            }  
        }
Initially when the window paints it will paint before the initial value has even been read.  The value will be 0 at that time.  You should probably initialize evtCounter to -1 in the constructor and don't plot if evtCounter < 0.  That way you won't plot anything until you've read a data point.  Also, as your code sits (will be OK with initial evtCounter value of -1) you increment evtCounter BEFORE loading the values into the arrays, so you are skipping the 0 index of the array.  You never load a value into the 0 index.

    void OnPaint( PaintEventArgs *paintEvent )    
      {
         if ( evtCounter >= 0 )
         {
            // 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 );

            for ( int ii = 0; ii <= evtCOunter; ii++)
            {
                graphicsObject->DrawEllipse( pen,xVALUE[ii], yValue[ii] * 10, 7, 7 );
            }
         }
    }

Also, in previous post, you should check if evtCounter is 500 (your array size, I assume) and reset it to 0 so you don't do off the array.
Avatar of judico

ASKER

Still plots the zero point. After your advice I placed evtCounter = -1 in the constructor:

temperature(void)
            {
                  InitializeComponent();
                  evtCounter = -1;
            }


and then placed an if-else selection statement where the plotting occurs


if (evtCounter < 0 ) {}
                              else
                              {
                                                graphicsObject->DrawEllipse( pen, xVALUE[ iii ], yVALUE[ iii] * 10, 7, 7 );
                              }

bit, as I said, this doesn't remove the zero point.
Avatar of judico

ASKER

See, it works with the palliative solution I mentioned before but I prefer it to work the way you explained it, with the flag.
If you're still looping from 0 to 499, you're going to get lots of zeros because the data is not yet filled in.

Also, if there are 0 values in positions less than evtCounter, do they come from the data read from the meter?  Check the return value for debug purposes.  Before checking if dMeterReading is MaxValue, check if it's 0.  If you see the dialog box, you know that the meter reading was 0.  If the zeros are coming from somewhere else, it's because you are not manipulating the array correctly.

Test code to see if meter reads 0:

private:
          void OnTimedEvent(Object* source, System::Timers::ElapsedEventArgs* e)
               {
         try
            {
                    BYTE *pBuf = NULL;
                  dMeterReading = mmeter->ReadData();
                   
                         // Code for debugging
                         if ( dMeterreading == 0 ) System::Windows::Forms::MessageBox(S"dMeterReading is 0!");

                         if ( dMeterReading == System::Double::MaxValue) {}
                         else
                         {
                             evtCounter += 1;
                             yVALUE[ evtCounter ] = dMeterReading;
                             Invalidate();
                             textBox2->Text = System::Convert::ToString(dMeterReading);
               
                             xVALUE[ evtCounter ] = evtCounter;
                             textBox1->Text = System::Convert::ToString(evtCounter,10);
                          }
                    // Restart timer
                aTimer->Start();
            }
            catch (System::Exception *ex)
            {
                System::Console::WriteLine(ex->Message);
            }  
        }
Avatar of judico

ASKER

For some reason

if ( dMeterReading == 0 ) System::Windows::Forms::MessageBox(S"dMeterReading is 0!");

gives the error

temperature.h(154): error C2440: 'type cast' : cannot convert from 'System::String __gc *' to 'System::Windows::Forms::MessageBox'

and I can't do the debuggigng.
Avatar of judico

ASKER

OK, it should be

if ( dMeterReading == 0 ) System::Windows::Forms::MessageBox::Show(S"dMeterReading is 0!");
Avatar of judico

ASKER

No, the debugging shows that the zero value isn't coming from the multimeter and is due to what you already suggested -- the first time the form paints it paints with zero values because the program hasn't yet called the multimeter. The flag -1 should be put before that first painting. I tried placing it in temperature.cpp:

System::Threading::Thread::CurrentThread->ApartmentState = System::Threading::ApartmentState::STA;
      evtCounter = -1;

      Application::Run(new temperature());
      return 0;

declaring static int evtCounter; at the head of temperature.h but it didnt help as well.
It's probably because you have this code which resets evtCOnter to 0 after the program starts.:

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;   // <----------------Problem here
                    }
           }


};
Avatar of judico

ASKER

I changed that line to evtCounter = -1; but the problem persists. Somehow, the first time the OnPaint method is invoked the values of xVALUE and yVALUE are zero because the multimeter hasn't been called yet. Can't this first invoking of the OnPaint method be overridden somehow?
Avatar of judico

ASKER

Can't the first values of xVALUE and yVALUE be obtained in the constructor before initializing the component?
If the multimeter read hasn't been called yet, how is evtCOunter changing from -1 to 0 since the only place that is supposed to happen is in the timer method where the read happens?  I'd put a break in the constructor and set a watch on evtCOunter and see when it gets changed.
They could as long as you've initialized the multimeter class so the serial port is open.
Avatar of judico

ASKER

Yeah, that's logical. I don't know how to set a watch on evtCounter but I did the following -- I set breaks in the constructor:

temperature(void)

and in the line where evtCounter changes:

evtCounter += 1;

and I saw the following:



When F11 is depressed, after certain waiting period, the program goes to temperature.cpp, as expected:

System::Threading::Thread::CurrentThread->ApartmentState = System::Threading::ApartmentState::STA;

then

Application::Run(new temperature());

after which gets into temperature.h into the constructor:

temperature(void)

followed by

InitializeComponent();

and then goes through the whole list of the initialization parameters, untill it reaches the end of the constructor. Then, back to temperature.cpp

Application::Run(new temperature());

And now, the most interesting -- the form appears on the screen momentarily, then disappears, and the program reaches:

evtCounter += 1;

Thus, it appears that evtCounter gets changed only after one painting of the form has already occurred.




Avatar of judico

ASKER

How can this first painting of the form be gotten rid of?
Avatar of judico

ASKER

Something else ... Insertion of another breakpoint in the line

 evtCounter = -1;

showed that above assignment of -1 to evtCounter occurs only after the first painting of the form.
You can't get rid of first painting.  When the form is rendered, it needds to paint.  Anytime the form is uncovered, unminimized, or moved (or first shown) it will do OnPaint to render the form surface.

When you say

>> And now, the most interesting -- the form appears on the screen momentarily, then disappears, and the program reaches:

>> evtCounter += 1;

What method is that in?  The only place that line should appear is in the timer callback where you are reading the meter.  That, combined with the "if ( evtCounter >= 0 ) ..." in OnPaint should prevent plotting data until the meter has been read at least once.


The form has to be constructed before it is painted, so where is the "evtCounter = -1;" that happens after the first painting?  Perhaps you should post the whole code again so I can see what's going on.  My version works perfectly except that I have a random number generator in place of the meter - or should I just post mine so you can look at it?
Compare this to what you have...

    public __gc class temperature : public System::Windows::Forms::Form
    {    
    public:
        temperature(void)
        {
            InitializeComponent();
            evtCounter = -1;
            xValues = new int __gc[500];
            yValues = new double __gc[500];
            rnd = new Random();
            mmeter = new MultiMeter;
            mmeter->ConfigureSerialInterface(1);
            aTimer = new System::Timers::Timer(500);
            aTimer->Elapsed+=new System::Timers::ElapsedEventHandler(this, OnTimedEvent);
            aTimer->AutoReset = false;  // Don't automatically restart timer
            aTimer->Enabled = true;
        }
 
    protected:
        void Dispose(Boolean disposing)
        {
            if (disposing && components)
            {
                        components->Dispose();
            }
            __super::Dispose(disposing);
        }

    private: System::Windows::Forms::TextBox *  textBox1;

    private: System::Windows::Forms::TextBox *  textBox2;
    private: System::Windows::Forms::TextBox *  textBox3;
    private: System::ComponentModel::IContainer *  components;

    private:
        System::Timers::Timer *aTimer;
        double yValues __gc[];
        int xValues __gc[];
        int evtCounter;
        System::Random *rnd;
        MultiMeter *mmeter;
     
    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->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;
                    //
                    // 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;
                //double dMeterReading = mmeter->ReadData();
                double  dMeterReading = rnd->NextDouble()*200.0 + 100;
                xValues[evtCounter] = evtCounter+20;
                yValues[evtCounter] = dMeterReading;
                textBox2->Text = System::Convert::ToString(dMeterReading);
                // Restart timer
                this->Invalidate();
                aTimer->Start();
            }
            catch (System::Exception *ex)
            {
                System::Console::WriteLine(ex->Message);
            }  
        }

    protected:
        void OnPaint( PaintEventArgs *paintEvent )    
        {
            // call base OnPaint method
            __super::OnPaint( paintEvent );
            if ( evtCounter >= 0 )
            {
                // get graphics object
                Graphics *graphicsObject = paintEvent->Graphics;
                SolidBrush *brush = new SolidBrush( Color::Blue );
                Pen *pen = new Pen( Color::Black );
      
                for ( int ii = 0; ii <= evtCounter; ii++ )
                {
                    graphicsObject->DrawEllipse( pen,xValues[ii], yValues[ii], 7, 7 );
                }
            }
        }
};
Avatar of judico

ASKER

Let me first mention this, before posting the code. I again inserted evtCounter = -1; (evtCounter being declared globally) in temperature.cpp just above Application::Run(new temperature());, so that I can be sure that evtCounter will be initialized with value -1 before anything else, especially before the form is initialized. Now, having done that I took it for granted that first time drawing of the ellipse would not occur because I had the following:

for (int iii = 1; iii <= 500; iii++)
                         {
                              if (evtCounter == -1 )      
                              {
                              }
                              else
                  graphicsObject->DrawEllipse( pen, xVALUE[ iii ], yVALUE[ iii] * 10, 7, 7 );
                        }

(this 500 is just in this example; I'll take care of it later)

Curiously, the program didn't heed that 'if (evtCounter == -1 )' and performed as if evtCounter had a value different from -1. Thus, something had interfered between the time evtCounter was assigned the value -1 and the moment the program reached for the first time 'if (evtCounter == -1 )'.

I tried placing a breakpoint also in evtCounter += 1; but for some reason depressing F11 never stopped me at that point.       
I think you've got too many evtCounter's running around.  Do not put it as a global.  Put it in the temperature class and initialize it to  -1 in the constructor.  See the code I just posted.  This is the correct way to handle it.  

The code I posted should be functionally identical to yours except that instead of reading the meter I generate a random number between 100.0 and 300.0.  If you copy it and switch the "double dMeterReading = rnd->NextDouble()..." line with the commented-out lne right above it, it should work.  You will probably need to change the scaling on the plot as well.
Avatar of judico

ASKER

OK, I was able to compile your above code. However, it still works in a similarly curious way as the code I have. In your code continuous plotting occurs at the very top of the form. In addition to that you have an immobile point at position x = 1 and y somewhere in the tens of form scale units. This second point changes its position along the y-axis as the values coming from the multimeter change, its x-axis position remaining unchanged.

Also, in your code you have placed the timer in the constructor. Let me post what I have. It at least correctly has the point moving in the plane of the form (one thing I'm noticing is the less selection statements there are the fewer instances of faulty data there are). Here is the code I have


#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;

static int evtCounter = -1;

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();
            }
 
static double xVALUE __gc[] = new double __gc[ 1000 ];
static double yVALUE __gc[] = new double __gc[ 1000 ];

      


      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::Windows::Forms::TextBox *  textBox4;
      private: System::ComponentModel::IContainer *  components;
      private: System::Windows::Forms::Label *  label1;

      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->textBox4 = new System::Windows::Forms::TextBox();
                  this->label1 = new System::Windows::Forms::Label();
                  this->SuspendLayout();
                  //
                  // textBox1
                  //
                  this->textBox1->Location = System::Drawing::Point(40, 8);
                  this->textBox1->Name = S"textBox1";
                  this->textBox1->ReadOnly = true;
                  this->textBox1->Size = System::Drawing::Size(104, 20);
                  this->textBox1->TabIndex = 3;
                  this->textBox1->Text = S"";
                  this->textBox1->TextAlign = System::Windows::Forms::HorizontalAlignment::Center;
                  //
                  // timer1
                  //
                  this->timer1->Enabled = true;
                  this->timer1->Interval = 500;
                  this->timer1->Tick += new System::EventHandler(this, timer1_Tick);
                  //
                  // textBox2
                  //
                  this->textBox2->Location = System::Drawing::Point(40, 32);
                  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(296, 32);
                  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;
                  //
                  // textBox4
                  //
                  this->textBox4->Location = System::Drawing::Point(296, 64);
                  this->textBox4->Multiline = true;
                  this->textBox4->Name = S"textBox4";
                  this->textBox4->ReadOnly = true;
                  this->textBox4->Size = System::Drawing::Size(104, 24);
                  this->textBox4->TabIndex = 6;
                  this->textBox4->Text = S"";
                  this->textBox4->TextAlign = System::Windows::Forms::HorizontalAlignment::Center;
                  //
                  // label1
                  //
                  this->label1->Location = System::Drawing::Point(280, 8);
                  this->label1->Name = S"label1";
                  this->label1->Size = System::Drawing::Size(136, 24);
                  this->label1->TabIndex = 7;
                  this->label1->Text = S"Values passed to OnPaint";
                  //
                  // temperature
                  //
                  this->AutoScaleBaseSize = System::Drawing::Size(5, 13);
                  this->ClientSize = System::Drawing::Size(632, 466);
                  this->Controls->Add(this->label1);
                  this->Controls->Add(this->textBox4);
                  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
            {
                        BYTE *pBuf = NULL;
                    dMeterReading = mmeter->ReadData();
                         
                        // Code for debugging
                        if ( dMeterReading == 0 ) System::Windows::Forms::MessageBox::Show(S"dMeterReading is 0!");

   
       if ( dMeterReading == System::Double::MaxValue)
                                 {
                                             }                  
                                     else
                                     {
                                                
                                    evtCounter += 1;
                        
                        yVALUE[ evtCounter ] = dMeterReading;
                        Invalidate();
                textBox2->Text = System::Convert::ToString(dMeterReading);
                        
                        xVALUE[ evtCounter ] = evtCounter;
                textBox1->Text = System::Convert::ToString(evtCounter,10);
                                    }
                        // 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);
             textBox4->Text = System::Convert::ToString(evtCounter);
             
             for (int iii = 1; iii <= 500; iii++)
                         {
                              if (evtCounter == -1 )      
                              {
                              }
                              else
                  graphicsObject->DrawEllipse( pen, xVALUE[ iii ], yVALUE[ iii] * 10, 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 = -1;
                        }
             }


};
}
:

Interestingly, that is not at all the behavior my code exhibits on my system here.  I get a nice plot going across the middle of my form (that's why I offset all the y coords by 100).  Like I said, if you switch to use the multimeter readings instead of the random number generator, you'll nned to change the plot scaling.  Did you try it with the random number?

Anyway, the reason you keep getting the evtCounter not -1 in OnPaint is because you did not put the evtCounter=-1 in the constructor as I keep mentioning.  You have it in timer1_tick method which doesn't get called until long (in CPU time) after the form has been painted.  All that code you have in the timer1_tick method beliongs in the constructor right after the call to IntializeComponents.  That's what constructors are for.
I ran your version of the code and made one change - you need to set the xVALUE array BEFORE calling invalidate.  Other than that, except that it's plotting hundreds of points at 0,0 and plots one time before you've even read in any data (see previous post), it seems to work.  Again, I replaced

                  dMeterReading = mmeter->ReadData();
 
with

                  dMeterReading = rnd->NextDouble()*100+50;

What exactly is not right?
Avatar of judico

ASKER

See, the code above is only one variant of the many ways I tried to place evtCounter = -1;. I placed it in the temperature.cpp, in the constructor, you name it. I just tried again placing it in the constructor -- didn't do any good. The reason, I think, why the code doesn't work is because I don't fully understand the method OnPaint. Somehow, I feel uncomfortable that I have to create sequential events through selection statements. This is one recurrent problem I have  which shows itself here as well.

As for your code, I didn't use any special scaling. I'm using the natural scale of the form and I just replaced the random numbers with the data coming from the multimeter. Let me take a look again.
Avatar of judico

ASKER

Indeed, the program otherwise works just fine. It is that first point you mention that bothers me. I can't understand how is it that something which is declared to be evtCounter = -1 at the onset later cannot be used as a condition and is ignored.
We seem to be homing in on something here.  Now that I've run your code and it works exactly as I would expect based on what I see, what is your issue?  I've kind of lost that in the shuffle here, but I can probably tell you how to solve it now.
Avatar of judico

ASKER

See, suppose there is an array of 10 non-zero experimental points to be plotted on the screen. lt will be incorrect if the first of these non-zero points is plotted on the screen (and is saved in a text file) as if it is of zero value.

The way the program works now gives an impression that the first point is always zero. That has to be corrected.
Avatar of judico

ASKER

Mind you, it may happaen that one of the experimental points is indeed zero, therefore, what I did, writing

 for (int iii = 1; iii <= 500; iii++)
                     {
                         if ( yVALUE[ iii ]  == 0)
                             
                                            ;
                         
                                       else
               
               graphicsObject->DrawEllipse( pen, xVALUE[ iii ], yVALUE[ iii] * 10, 7, 7 );
                    }    

is incorrect in the general case because it will ignore legitimate points of zero value.
>> lt will be incorrect if the first of these non-zero points is plotted on the screen (and is saved in a text file) as if it is of zero value.

True enough, but plotting can be easily fixed by initializing evtCounter properly before OnPaint is called.  Another approach might be to add another variable in the form:

    bool haveData;

initialized to false in the constructor.  Then in OnPaint check "if (haveData)..." instead of "if (evtCounter >= 0)...".  It's basically the same thing but perhaps less confusing.  Then in OnTimedEvent, do "haveData = true;" after reading valid data from the meter.  This is the signal to start plotting.

Also, C++ arrays start at 0.  You should be iterating from 0 to 500, not 1.  If you declare an array ...[n], the valid index values are 0 to n-1.  Also a reminder, your code is plotting 400+ points at 0,0 since you iterate to 500 every time.  You should really stop at evtCounter since that's how many points you have at any moment.
Avatar of judico

ASKER

I just tried initializing bool haveData as false in the constructor, then having it become true after reading valid data and checking it in the OnPaint with if (haveData ...). Still gives the first point at 0,0 position ... How can this be?

Also,  I don't understand what you mean by saying "your code is plotting 400+ points at 0,0 since you iterate to 500 every time". The code seems to be plotting only one, the first one, point at 0,0 and all other points are plotted at non-zero x and y's.
No, it's not the first one - it's actually evtCounter+1 to 500 that are being plotted right on top of each other at 0,0.  Run the code for a couple of seconds and the set a break in OnPaint.  Step through and you'll see that after the first few ponts which have valid values, all the rest have x and y values of 0.  I think that's what's confusing you.
Avatar of judico

ASKER

Now, this sounds confusing, indeed. I think it's very important to be clarified and corrected, if that's the case. Otherwise the program will be useless.
Here's a test you can run to see if things are going as expected (assumes you've changed iteration in OnPaint):

1) Put a breakpoint in OnTimedEvent
2) Put a breakpoint at the start of the "for (iii = 0;..." loop in OnPaint which should be inside the "if (haveData)..." block.
3) Run the program
4) You should hit the OnTimedEvent breakpoint first.  Validate that haveData is set to true.  Step through a few lines if you have to.
5) Continue execution
6) You should hit the OnPaint breakpoint next.
7) Expand xVALUE and yVALUE arrays and validata that yVALUE[0] is non-zero.  xValue[0] will be 0 unless you add an offset like I did in my code.  I wanted to start plotting a little into the form, so I used evtCounter+20 as my xVALUE - my lowest xVALUE was 20.

If you can do that test successfully, then the code is correct.  I think you will find this to be the case.  What is confusing you is that you are seeing points plotted at 0,0 and you assume that this is the first point in the array when in fact it is all the unused points at teh end of the array.
Avatar of judico

ASKER

I tried the test point by point as you asked me to do in your previous post. Unfortunately, I had problems applying it. I set up the breakpoints, as you explained, and then started the program. I don't know, however, what you mean by "Expand xVALUE and yVALUE arrays and validata that yVALUE[0] is non-zero." I don't know how to expand it. Thus, I didn't get too far with that test.

To explore the issue I approached it this way. I added some code which writes the values of xVALUE[ iii ] and yVALUE[ iii ] in a text file named TestFile.txt. The code goes as follows:



if (haveData  ==  false)
                     ;
               else
               {
             for (int iii = 0; iii <= 9; iii++)
                     {
                graphicsObject->DrawEllipse( pen, xVALUE[ iii ], yVALUE[ iii] * 20, 7, 7 );
                               }                              
               }
      
StreamWriter* pwriter = new StreamWriter( S"c:/AAAAAAVisual C++/TestFile.txt" );
      for ( int jjj = 0; jjj <= 9; jjj++ )
                        {      
                  String *xxx = xVALUE [ jjj ].ToString();
                  String *yyy = yVALUE [ jjj ].ToString();
      
                  String *xxxyyy = String::Concat(xxx, "     ", yyy);
      
                  pwriter->WriteLine( xxxyyy );
                        }
                  pwriter->Close();
        

and this is what I obtain in the text file when I run the program until the counter is increased from 0 to 9::

0     10.71
1     10.71
2     10.72
3     10.73
4     10.73
5     10.73
6     9.84
7     9.65
8     9.17
9     8.46

These are the actual values of xVALUE[ iii ] and yVALUE[ iii ] coming in from the multimeter. They are being plotted on the screen and can be observed (in addition to the misterious point at 0,0 which we seen not to be able to get rid of). As you can see there are no 0,0 couples in these data. That's why I was telling you that I don't understand when you're saying:

"What is confusing you is that you are seeing points plotted at 0,0 and you assume that this is the first point in the array when in fact it is all the unused points at teh end of the array."

(I don't know what the term 'teh' means, could you explain, please).

Thus, it seems to me that the whole problem stems from the application of the OnPaint method. It boggles the mind how come one can have have evtCounter assigned to -1 at the onset of the program and yet when the selection statement in the OnPaint method is to obey it, it in fact disregards it. I think that's what is unclear in this problem.
ASKER CERTIFIED SOLUTION
Avatar of drichards
drichards

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of judico

ASKER

OK, here’s the solution of that “mystery”. Believe it or not it consists in changing just one little thing. In the for loop one should have evtCounter instead of 500, namely

for (int iii = 0; iii <= evtCounter; iii++)
                     {
                graphicsObject->DrawEllipse( pen, xVALUE[ iii ], yVALUE[ iii] * 20, 7, 7 );
                               }                              
               }

And it stands to reason. As you explained, the loop containing 500 as a parameter will inevitably loop 500 times every time the OnPaint method is called. This will lead to multiple 0,0 points in the initial stages of the acquisition of the points (in fact, until 500 actual points are acquired). Thus, a “mysterious” 0,0 point will always be seen plotted on the screen as many times as there are zeros (due to still not acquired data) coming from the 500-turn for-loop.

Now, with the above code that 0,0 point is gone because the for-loop turns only as many times as the multimeter has actually been called (accounted for by the value of the evtCounted). Undoubtedly, evtCounter has to be initialized as –1 so that even initially no bogus 0,0 point is seen on the graph.

Now, due to the discussion with you this problem is taken care of. What remains is the checksum problem. Obviously, if one needs to retain the actual number of points taken one mustn’t write code which skips unpleasant points. Probably, correction of the point values should occur somehow manually, after all data has been acquired.

I will open later a special question to discuss that. Now I’m closing this question and would like to thank you so much for the great help.



P.S. Please note, I’m increasing your points to 250. Please let me know if you have problems in getting the increased points.
Avatar of judico

ASKER

Small correction. To avoid the momentary appearance of the 0,0 point at the beginning the above loop should start from iii = 1:

for (int iii = 1; iii <= evtCounter; iii++)