Проблема производительности диаграммы в реальном времени c# winform - PullRequest
1 голос
/ 12 февраля 2020

В настоящее время я использую liveChart для построения графика в реальном времени из 3 значений: положения, нагрузки и деформации. Программа основана на библиотеке Doli.DoPE (проприетарная dll)

В MainForm.cs происходит событие, которое запускается каждый раз, когда датчик записывает новое значение (каждую миллисекунду или около того).

public void Initialisation()
{
   //...    
   MyEdc.Eh.OnDataHdlr += new DoPE.OnDataHdlr(OnData)
   //...
}

с

private int OnData(ref DoPE.OnData Data, object Parameter)
{
    DoPE.Data Sample = Data.Data;
    if (Data.DoPError == DoPE.ERR.NOERROR)
    {
        Int32 Time = Environment.TickCount;
        if ((Time - LastTime) >= 250 /*ms*/)
        {
            // Send the data from the ondata handler inside of a global list
            ListData.time.Add(Sample.Time);
            ListData.position.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_S]);
            ListData.load.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_F]);
            ListData.extend.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_E]);

            Thread ThForUpdateChart = new Thread(() =>
            {
                if (NewINstanceOfChart != null)
                { NewINstanceOfChart.UpdateValues(ListData.time.Last(), ListData.position.Last(),ListData.load.Last(), ListData.extend.Last()); }
            });
            ThForUpdateChart.Start();
            LastTime = Time;
        }
    }
    return 0;
}

Функция UpdateValues является частью второй формы RealTimeChart.cs, вызываемой в MainForm через событие нажатия кнопки:

private void btnGraph_Click(object sender, EventArgs e)
{
    var thread = new Thread(() =>
    {
        NewINstanceOfChart = new RealTimeChart(ListData);
        NewINstanceOfChart.Show();
    });
    thread.Start();
}

форма RealTimeCharts.cs инициализируется следующим образом:

public RealTimeChart(Globals ListData)
{
    InitializeComponent();

    //measures = ListData;

    ListPosition = new ChartValues<ObservablePoint>();
    for (int i = 0; i < measures.load.Count(); i++)
    {
        ListPosition.Add(new ObservablePoint
        {
            X = measures.time[i],
            Y = measures.position[i]
        });
    }
    ListLoad = new ChartValues<ObservablePoint>();
    for (int i = 0; i < measures.load.Count(); i++)
    {
        ListLoad.Add(new ObservablePoint
        {
            X = measures.time[i],
            Y = measures.load[i]
        });
    }

    ListExtend = new ChartValues<ObservablePoint>();
    for (int i = 0; i < measures.load.Count(); i++)
    {
        ListExtend.Add(new ObservablePoint
        {
            X = measures.time[i],
            Y = measures.extend[i]
        });
    }

    resultChart.Series.Add(new LineSeries
    {
        LineSmoothness = 0,
        Values = ListPosition,
        PointGeometrySize = 2,
        StrokeThickness = 4
    });


    SetXAxisLimits();

}

И функция UpdateValues определяется следующим образом:

        public void UpdateValues(double time, double position, double load, double extend)
        {
            measures.time.Add(time-measures.TareTime);
            measures.position.Add(position);
            measures.load.Add(load);
            measures.extend.Add(extend);

            UpdateEnabledSequencialPartToTrue();

        }



        public void UpdateEnabledSequencialPartToTrue()
        {
            if (this.InvokeRequired)
                BeginInvoke(new System.Action(() => this.InternalUpdateEnabledSequencialPartToTrue()));
            else
                InternalUpdateEnabledSequencialPartToTrue();
        }
        private void InternalUpdateEnabledSequencialPartToTrue()
        {
            try
            {
                ListPosition.Add(new ObservablePoint
                {
                    X = measures.time.Last(),
                    Y = measures.position.Last()
                });

                ListLoad.Add(new ObservablePoint
                {
                    X = measures.time.Last(),
                    Y = measures.load.Last()
                });

                ListExtend.Add(new ObservablePoint
                {
                    X = measures.time.Last(),
                    Y = measures.extend.Last()
                });

                //LineSeries plot = new LineSeries();
                SetXAxisLimits();

                // lets only use the last 14400 values (1h long recording, 14400 values at frequency of 1 record very 250ms, see OnData function MainForm
                if (measures.time.Count > 14400)
                {
                    ListPosition.RemoveAt(0);
                    ListLoad.RemoveAt(0);
                    ListExtend.RemoveAt(0);
                }
            }
            catch (NullReferenceException) { }
        }

Через минуту программа начинает очень тормозить. Я попытался поместить второй winform (RealTimeCharts) в другой поток, чтобы MainForm не отставал (это пилотирование машины, он должен реагировать), но безуспешно.

Я хотел бы знать, если весь вещь запаздывает, потому что код слишком плохой, или если это liveChart, который достиг своих (бесплатных) пределов. Вы бы посоветовали другой способ построения данных в реальном времени?

1 Ответ

2 голосов
/ 12 февраля 2020

В MainForm.cs есть событие, которое срабатывает каждый раз, когда датчик записывает новое значение (каждую миллисекунду или около того).

Это естественно намного выше, чем то, что Winforms Drawing могу взять. Видите, рисунок GUI стоит дорого. Если вы делаете это только один раз для события, инициируемого пользователем, вы никогда этого не заметите. Но сделайте это с помощью oop - включая выборку датчика каждой MS - и вы сможете быстро изменить интерфейс. Мои первые тесты многопоточности действительно оказались неудачными на больших числах, потому что я закончил тем, что отправил так много обновлений Я просто перегрузил поток GUI. С тех пор я не знаю go прошлых индикаторов.

Вы можете добавлять данные в фоновую коллекцию так же быстро, как вы можете их сэмплировать, но вы не можете рисовать так быстро. И, честно говоря, рисование чаще, чем 30-60 раз в секунду (каждые ~ 17 мс) на самом деле никому не поможет. Обычно вы не можете использовать таймер, так как тик может происходить чаще, чем его можно обработать - опять поток GUI с переполненной очередью событий.

У меня нет кода ограничения скорости для WindowsForms , Но я бы предположил, что событие, которое помещает себя в очередь в конце EventQueue после завершения работы, будет работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...