Search code examples
c#chartstimerdevexpressmodbus

C# WinForm ChartControl sometimes shows zero in real time


I have an application reading values from PLC and uses thouse values to draw charts for every second and every 45 seconds records the date to MySQL database. It was working fine so far but when I put it to test I have realized it is sometimes reading the wrong value which is 0. For example PLC value is constantly 45 but time by time I see my chart draw 0 value. My chart draws itself every 1000ms and I tried to increase it to 2000 But it is still same. I have realized this happenes when I try to open another application such as windows explorer. I have 13 values to be read and drawn on 13 different Chart Controls. Here are the codes I used to fetch the values and drawing the chart for only one PLC value and Chart Control. I

DataPoint Class:

public class DataPoint
        {
            public DateTime Argument { get; set; }
            public double Value { get; set; }
            public DataPoint(DateTime argument, double value)
            {
                Argument = argument;
                Value = value;
            }
        }

Form Load event

            System.Drawing.Font myfont = new System.Drawing.Font("Microsoft Sans Serif", 20);
            System.Drawing.Font baslikFont = new System.Drawing.Font("Microsoft YaHei", 20,System.Drawing.FontStyle.Bold);
            
            //tb1
            tb1.Titles.Add(new ChartTitle { Text = basliklar[1],Font=baslikFont,TextColor=System.Drawing.Color.DeepSkyBlue });
            Series s1 = new Series();
            s1.ChangeView(ViewType.Spline);
            s1.DataSource = dp1;
            s1.DataSourceSorted = true;
            s1.ArgumentDataMember = "Argument";
            s1.ValueDataMembers.AddRange("Value");
            tb1.Series.Add(s1);
            LineSeriesView sv1 = (LineSeriesView)s1.View;
            sv1.LastPoint.LabelDisplayMode = SidePointDisplayMode.DiagramEdge;
            sv1.LastPoint.Label.TextPattern = "{V:f2}";
            sv1.LastPoint.Label.Font = myfont;

            XYDiagram dg1 = (XYDiagram)tb1.Diagram;
            dg1.AxisX.DateTimeScaleOptions.ScaleMode = ScaleMode.Continuous;
            dg1.AxisX.Label.ResolveOverlappingOptions.AllowRotate = false;
            dg1.AxisX.Label.ResolveOverlappingOptions.AllowStagger = false;
            dg1.AxisX.WholeRange.SideMarginsValue = 0;
            dg1.AxisY.ConstantLines.Add(new ConstantLine("Alt limit", altlimit[1]));
            dg1.AxisY.ConstantLines.Add(new ConstantLine("Üst limit", ustlimit[1]));
            dg1.AxisY.ConstantLines[0].Color = System.Drawing.Color.Red;
            dg1.AxisY.ConstantLines[1].Color = System.Drawing.Color.Red;
            dg1.DependentAxesYRange = DefaultBoolean.True;
            dg1.AxisY.WholeRange.AlwaysShowZeroLevel = false;

            timer = new System.Threading.Timer(_ => Timer_Tick(), null, 0, 1000);
            timer2 = new System.Threading.Timer(_ => Timer1_Tick(), null, 45000, 45000);

Reading value set from PLC

        try
            {
                atolyevals = atolye.ReadHoldingRegisters(2001, 32);
                if (a_s != null)
                {
                    a_s = null;
                    lineAdd(simdi() + " Atölye PLC bağlantısı sağlandı","g");
                }
            }
        catch
       {
          //Error checking methods
        }

       //Adding new point to the Chart Control
        dp1.Add(new DataPoint(DateTime.Now, gazatolyevals[0]));
                if (dp1.Count > ViewportPointCount)
                    dp1.RemoveAt(0); //if out of viewport remove the first entrance

Solution

  • You are using a System.Threading.Timer that I would assume triggers your reading code. The problem is that this timer runs on a background thread, and you are updating the UI from this thread. This is not allowed and can lead to all sorts of problems. UI-objects are only allowed to be changed from the UI thread.

    The solution would be to either use something like a winforms timer to run the read-code on the UI thread, or to post the UI-update part to the main thread, using for example Control.BeginInvoke.