Real Time chart performance issue c# winform - c#

I am currently using liveChart to plot a real time graph of 3 values: a position, a load and a deformation. The program is based on the Doli.DoPE library (a proprietary dll)
In MainForm.cs, there is an event that is triggered everytime the sensor records a new value (every millisecond or so).
public void Initialisation()
{
//...
MyEdc.Eh.OnDataHdlr += new DoPE.OnDataHdlr(OnData)
//...
}
with
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;
}
The function UpdateValues is part of a second form RealTimeChart.cs called in the MainForm through a button click event:
private void btnGraph_Click(object sender, EventArgs e)
{
var thread = new Thread(() =>
{
NewINstanceOfChart = new RealTimeChart(ListData);
NewINstanceOfChart.Show();
});
thread.Start();
}
the form RealTimeCharts.cs is initalised this way:
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();
}
And the UpdateValues function is defined as followed:
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) { }
}
After a minute, the programme starts to be really laggy. I tried putting the second winform (RealTimeCharts) on another thread so the MainForm does not lag (it is piloting a machine, it has to be responsive), but no success.
I would like to know if the whole thing is laggy because the code is way too bad, or if it is liveChart that reached its (free) limits. Would you advice another way to plot real time data ?

In MainForm.cs, there is an event that is triggered everytime the sensor records a new value (every millisecond or so).
That is natturally way higher then what Winforms Drawing can take. See, drawing a GUI is expensive. If you only do it once per user-triggered event, you will never notice that. But do it from a loop - including sampling a sensor every MS - and you can quickly overlord the UI. My first Multithreading tests actually appeared to have failed on big numbers, becaus I ended up sending so many updates I plain overloaded the GUI thread. Since then I know not to go past progress bars.
You can add data to a background collection as quickly as you can sample them, but you can not draw that quickly. And honestly drawing more often then 30-60 times/second (every ~17 ms) is not really going to help anyone anyway. Usually you can not use a timer, as the Tick might happen more often then it can be processed - again, a GUI Thread with a overflowing Event Queue.
I do not have any rate limiting code for WindowsForms. But I would guess an Event that re-queues itself at the end of the EventQueue after finishing the work would work.

Related

Implementing a "Continuous Capture" with single image snaps

I have some code below that can take the current Image and displaying it whenever I click a PushSnap Button. How can I go about having it continuously capturing and displaying the updated image in an interval (say 100 ms)
private void PushSnap_Click(object sender, EventArgs e)
{
if (mycam == null)
{
MessageBox.Show("Internal Error: mycam is null");
return; // internal error
}
string text = "";
if (IsMyFormStatus_Opened())
{
if (!mydcam.buf_alloc(3))
{
MessageBox.Show("Frame allocation failed");
return;
}
}
// start acquisition
mycam.m_capmode = CAMCAP_START.SNAP; //one time capturing where Acquisition will start after m_nFrameCount frames
if (!mycam.cap_start())
{
return;
}
MyFormStatus_Acquiring();
MyThreadCapture_Start();
}
The Following MyThreadCapture_Start()
private void MyThreadCapture_Start()
{
m_threadCapture = new Thread(new ThreadStart(OnThreadCapture));
m_threadCapture.IsBackground = true;
m_threadCapture.Start();
}
and the following OnThreadCapture()
private void OnThreadCapture()
{
using (mycamwait = new MycamWait())
{
while (True)
{
CAMWAIT eventmask = CAMWAIT.CAPEVENT.FRAMEREADY | CAMWAIT.CAPEVENT.STOPPED;
DCAMWAIT eventhappened = DCAMWAIT.NONE;
if (mycamwait.start(eventmask, ref eventhappened))
{
if (eventhappened & CAMWAIT.CAPEVENT.FRAMEREADY)
{
int NewestFrame = 0;
int FrameCount = 0;
if (mycam.cap_transferinfo(ref NewestFrame, ref FrameCount))
{
MyUpdateImage(iNewestFrame);
}
}
if (eventhappened & CAMWAIT.CAPEVENT.STOPPED)
{
bContinue = false;
if (m_cap_stopping == false && mycam.m_capmode == CAMCAP_START.SNAP)
{
//cap_stop() happens automatically, therefore update the main dialog
//MySnapCaptureFinished();
}
}
}
}
}
Afterwards the Display is updated in the following way
private void UpdateDisplay()
{
Image oldImg = PicDisplay.Image;
if (m_bitmap != null)
{
// Show center of image
Int32 y0 = (m_image.height - PicDisplay.Height) / 2;
Int32 x0 = (m_image.width - PicDisplay.Width) / 2;
Rectangle rc = new Rectangle(x0, y0, PicDisplay.Width, PicDisplay.Height);
Bitmap bmp = new Bitmap(PicDisplay.Width, PicDisplay.Height, PixelFormat.Format24bppRgb);
using (var gr = Graphics.FromImage(bmp))
{
gr.DrawImage(m_bitmap, 0, 0, rc, GraphicsUnit.Pixel);
}
PicDisplay.Image = bmp;
PicDisplay.Refresh();
}
else
{
PicDisplay.Image = null;
}
if (oldImg != null)
oldImg.Dispose();
}
I would really recommend using Tasks instead of Threads. That should let you update your methods to take parameters and return results:
Image CaptureImage(); // I.e. similar code to OnThreadCapture
UpdateDisplay(Image);
And capture an image like:
public async void PushSnap_Click(){
...
try{
var image = await Task.Run(CaptureImage);
DisplayImage(image);
}
catch{
// Handle exceptions
}
That should let you simply rewrite your capture code to use a loop if you want live capture:
while(showLiveImagesBool){
var image = await Task.Run(CaptureImage);
DisplayImage(image);
}
The await-part should ensure the UI thread is not blocked while waiting for an image to be captured. If you do not want to show every image you might use a timer instead of a loop, just pick a timer that runs on the UI thread.
Keep in mind that showing live images will require a bit more performance, so you might need to optimize display and/or capture code. Ideally you should reuse image buffers when doing the capturing and displaying, otherwise you will allocate a fair amount of large objects that require a 2 gen GC to clean up.

How to constantly sort and refresh elements (e.g. elevator target floor)

I was wondering if any of you Guys could help me out with my school project. I'm working on elevator simulator in WPF. It's quite simple how it works, you press a button on certain floor and elevator goes there. I managed to achieve animation effect by using async method for animation and waiting between iterations like await Task.Delay(10).
This is how my code looks:
6 buttons on different floors (from 4 to -1). Every button is calling animation(x) method, where x = floor where button is placed. This is button 4 for example:
async private void buttonna4_Click(object sender, RoutedEventArgs e)
{
animation(4);
}
So from clicking this button we go to animation(x) method, which looks like that:
public async Task animation(int go_where_from_button)
{
await semafor.WaitAsync();
{
go_where = convert_floor(go_where_from_button);
try
{
if (go_where < Windajo.Margin.Top) //going up
{
for (int i = Convert.ToInt16(Windajo.Margin.Top); i >= go_where; i--)
{
await gui_elements(i);
}
}
if (go_where > Windajo.Margin.Top) //going down
{
for (int i = Convert.ToInt16(Windajo.Margin.Top); i <= go_where; i++)
{
await gui_elements(i);
}
}
}
catch (Exception)
{
label.Content = "some exception";
}
finally
{
semafor.Release();
}
}
}
where:
SemaphoreSlim semafor = new SemaphoreSlim(1); which I use to prevent elevator from going upwards and downwards at once.
go_where is converted value of how far elevator should go till it stops (because I use margins to determine whether elevator moved to desired floor or not).
await gui_elements(i) is a method where I've put all my elevator things like elevator itself, doors, screen with current floor etc so it looks like:
public async Task gui_elements(int i)
{
Windajo.Margin = new Thickness(Windajo.Margin.Left, i, Windajo.Margin.Right, Windajo.Margin.Bottom);
//OTHER ELEMENTS................
await Task.Delay(10);
}
Now it works like that: lets assume that we are on floor 0 I push buttonna3, buttonna4, buttonna1. Elevator will go in exact that order: floor 3 -> floor 4 -> floor 1.
And here is my question: how to make it sort floors correctly to work like normal elevator? So in this case to go floor 4 -> floor 3 -> floor 1 (or floor 1 -> floor 3 -> floor 4). And while it's already moving for example from floor 1 to floor 3 to react if someone pushes button on floor 2.
I was struggling with this all day long. First I tried to make List() of floors desired and then Distinct not unique floors and Sort them but it was not working as it should because (I used foreach and it couldnt update the list while doing operations, at least this is what I think).
Could anyone give me a hint of not complicated and correct way of solving this problem?
I really appreciate any help you can provide.
I think you shouldn't call animation method directly from buttonDown event.
Instead of this, you can create List of inputs and create a Timer and put all the elevator logic in Timer.Tick handler.
The main idea and the main issue is how to calculate next floor (targetFloor).
I suggest to create a list of inputs List<int> inputs, flag that shows is elevator go down or up bool IsGoingUp and two int variables - currentFloor and targetFloor and recalculate target floor every time when user clicks a button, based on direction and on inputs.
Something like this.
List<int> inputs = new List<int>();
int currentFloor = 1;
int targetFloor = 1;
bool IsGoingUp = true;
public MainWindow()
{
InitializeComponent();
System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0,100);
timer.Tick += Timer_Tick;
timer.Start ();
}
private void buttonna4_Click(object sender, RoutedEventArgs e)
{
inputs.Add(4);
// Recalculating target floor based on input
RecalculateTargetFloor();
}
private void Timer_Tick(object sender, EventArgs e)
{
DrawAnimation(); // Draw small step of elevator to targetFloor
if (currentFloor == targetFloor)
RecalculateTargetFloor();
//waiting for another tick of timer.
}
Method DrawAnimation () draws everything based on currentFloor and targetFloor. It also should recalculate target floor to handle when elevator reachs another target floor.
private void RecalculateTargetFloor()
{
if (targetFloor > currentFloor)
{
//going up
GetNextUpperFloor();
return;
}
else if (targetFloor < currentFloor)
{
//going down
GetNextLowerFloor();
return;
}
// Elevator reached target
inputs.RemoveAll(x => x == targetFloor);
if (IsGoingUp)
{
// if there is no any floors to go up - go down
if (!inputs.Any(x => x > currentFloor))
{
IsGoingUp = false;
}
else
{
// or continue journey
GetNextUpperFloor();
}
}
else
{
if (!inputs.Any(x => x < currentFloor))
{
IsGoingUp = true;
}
else { GetNextLowerFloor(); }
}
}
And methods that calculate next station
void GetNextUpperFloor()
{
var newTargetFloors = inputs.Where(x => x < targetFloor && x > currentFloor);
if (newTargetFloors.Any())
{
targetFloor = newTargetFloors.Min();
}
else if (inputs.Any(x => x > currentFloor))
{
targetFloor = inputs.Where(x => x > currentFloor).Min();
}
}
void GetNextLowerFloor()
{
var newTargetFloors = inputs.Where(x => x > targetFloor && x < currentFloor);
if (newTargetFloors.Any())
{
targetFloor = newTargetFloors.Max();
}
else if (inputs.Any(x => x < currentFloor))
{
targetFloor = inputs.Where(x => x < currentFloor).Max();
}
}
You should be carreful with multitasking because you can break List, put some lockers if you have problems with deleting/inserting item from the list simultaneously.

Dynamic Data Display LineGraph not updating

I am trying to use Dynamic Data Display for my senior design project, but I'm having difficulty.
I can create the graph just fine, but I can't the line to update at all unless I am zooming in and out.
In most examples, all they had to do was create the graph, then when they manipulate the data, the graph updates itself.
So here are the main functions I am using with regards to the charts:
private List<WindDAQ.WindDataPoint> Chart1Data;
private EnumerableDataSource<WindDAQ.WindDataPoint> Chart1DataSource;
private void InitializeCharts()
{
Chart1Data = new List<WindDAQ.WindDataPoint>();
Chart1DataSource = new EnumerableDataSource<WindDAQ.WindDataPoint>(Chart1Data);
Chart1DataSource.SetXMapping(x => Chart1XAxis.ConvertToDouble(x.Time));
Chart1DataSource.SetYMapping(y => Chart1XAxis.ConvertToDouble(y.Lift));
Chart1.AddLineGraph(Chart1DataSource, Colors.Blue, 2, "Lift");
Chart1.Viewport.AutoFitToView = true;
}
private void UpdateChart()
{
for (int i = 0, count = itsDAQ.getStreamCount(); i < count; i++)
{
Chart1Data.Add(itsDAQ.getValue());
if(Chart1Data.Count >= 300)
{ Chart1Data.RemoveAt(0); }
}
}
InitializeCharts() is called once when creating the window.
UpdateChart() is called on a timer event.
WindDAQ.WindDataPoint contains Lift, Drag, Velocity, and Time data. Lift and Time are shown selected.
you should use the AppendAsync method of your observableCollection.
You'r updating only the list used to create the observable one, not the source of your graph.
private void UpdateChart()
{
for (int i = 0, count = itsDAQ.getStreamCount(); i < count; i++)
{
Chart1DataSource.AppendAsync(itsDAQ.getValue());
if (Chart1DataSource.Collection.Count >= 300) // this part should work in a thread-safe
{
Chart1DataSource.Collection.RemoveAt(0); // context and with some precaution
}
}
}

Why when thread starts, the form crashes?Is Thread-safe?

I've a game with 90 grey numbers and, periodically, some of them become blue.(Simplified!)
Now i use:
Game-->Timer-->Thread-->coloring numbers
because i want use my game also when the numbers in coloration.
the code:
some variables..
public FormHome()
{
InitializeComponent();
TEstrazione = new System.Timers.Timer(10000);
TEstrazione.AutoReset = false;
TEstrazione.Elapsed += timerEstrazioneElapsed;
TEstrazione.Start();
}
private void timerEstrazioneElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
ThreadEstrazione = new Thread(cThread);
ThreadEstrazione.IsBackground = true;
ThreadEstrazione.Start();
}
private void cThread()
{
//other methods that change others controls..
coloring();
TEstrazione.Start();
}
void coloring()
{
for (int i = 0; i < 20; i++)//20 numbers to make colored
{
int num = *get random number*;
Label lbl;
lbl = (Label)panelNumembers.Controls["n" + num.ToString()];
if (lbl.InvokeRequired)
{
lbl.Invoke(new Action(coloring), new object[] { });
return;
}
lbl.BackColor = Color.DodgerBlue;
lbl.ForeColor = Color.Navy;
lbl.Refresh();
Thread.Sleep(800);
}
}
excuse me if some names are in italian but i think they aren't important.
The problem is that when the thread is coloring the label, the program locks: i can't push buttons and even close it! I think is a thread-safe problem but i don't know other ways.
As i've written, there are other methods inside a cThread, all change the controls. One of theese is:
void refreshLabel()
{
//here I always used one of the controls for the InvokeRequired
because in some cases I have so many and if I do the InvokeRequired
for each control becomes too long the code
if (label1.InvokeRequired)
{
ne.Invoke(new Action(refreshLabel), new object[] { });
return;
}
label1.Text = "xxxxx";
label1.Refresh();
label2.Text = "xxxxxxxxxxxxxxxx";
label2.Refresh();
}
In conclusion I have a lot of methods started by cThread that change a lot of controls, there is a way to make it thread safe without stretching too much code?
I apologize for the English but I'm Italian and is not easy explain the problems in an another language.
I'm using my girlfriend laptop so I'll edit my answer and give you a much better answer ASAP. the problem occurred because most of the execution is in the UI thread. you should execute most of your execution in background. when you want to update the UI you need to change context to the UI thread(only updates!).
Change coloring to:
void coloring()
{
for (int i = 0; i < 20; i++)//20 numbers to make colored
{
int num = *get random number*;
Label lbl;
lbl = (Label)panelNumembers.Controls["n" + num.ToString()];
var updateUI = new Action(() => {
lbl.BackColor = Color.DodgerBlue;
lbl.ForeColor = Color.Navy;
lbl.Refresh();
});
if (lbl.InvokeRequired)
{
Invoke(updateUI);
}
else
{
updateUI();
}
Thread.Sleep(800);
}
if there is no other blocking operations inside your code then this might work

C# Threading - an array of threads, where each thread contains a form with an image

I have an array of five threads. Each thread contains the same form, each form is put on to the screen in a different location (still working on that method :P).
I am trying to have each form load its contents (an image) before the other forms have finishing being placed. At the moment this works for the first form, but the others are blank or disappear :P
Originally each form would be placed but the method would need to finish before all the forms contents were displayed.
Any help would be appreciated, thanks :)
public partial class TrollFrm : Form
{
int number = 0;
public TrollFrm()
{
InitializeComponent();
startThreads();
}
private void TrollFrm_Load(object sender, EventArgs e)
{
}
private void TrollFrm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
public void startThreads()
{
Thread[] ThreadArray = new Thread[5];
for (int i = 0; i < 5; i++)
{
ThreadArray[i] = new Thread(new ThreadStart(createForm));
ThreadArray[i].Start();
}
}
public void createForm()
{
Form frm = new TrollChildFrm();
Random randomX = new Random();
Random randomY = new Random();
number++;
int xValue;
int yValue;
if (number % 2 == 0) //number is even.
{
xValue = (Convert.ToInt32(randomX.Next(1, 1920))) + 200;
yValue = (Convert.ToInt32(randomY.Next(1, 1080))) - 200;
}
else //number is not even.
{
xValue = (Convert.ToInt32(randomX.Next(1, 1920))) - 200;
yValue = (Convert.ToInt32(randomY.Next(1, 1080))) + 200;
}
frm.Show();
frm.Location = new Point(xValue, yValue);
Thread.Sleep(1000);
}
Your forms are not displaying correctly because they are not running on a thread with a message loop. The general rule is that all UI element accesses must occur on the main UI thread.
Since you have a call to Thread.Sleep(1000) I am going to assume that you want to wait 1 second between the initial display of each form. In that case I would use a System.Windows.Forms.Timer who's Tick event will call createForm directly. Enable the timer, let 5 Tick events come through, and then disable the timer. I see no need to create any threads at all.
The reason your forms aren't displaying is because you are running inside one method on the main UI thread. Instead, you could create a method that spawns a new form and launch that at certain intervals on another thread (making sure the form handling is done on the main UI thread). So you could do something like:
(Pseudo Code)
private const int TIME_THRESHOLD = 100;
int mElapsedTime = 0;
Timer mTimer = new Timer();
.ctor
{
mTimer.Elapsed += mTimer_Elapsed;
}
private void mTimer_Elapsed(...)
{
mElapsedTime++;
if (mElapsedTime >= TIME_THRESHOLD)
{
mElapsedTime = 0;
SpawnForm();
}
}
private void SpawnForm()
{
// Make sure your running on the UI thread
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(SpawnForm));
return;
}
// ... spawn the form ...
}
This is just an example of what I was proposing - it would not look exactly like this in the code, but this should give you an idea of the execution steps.
I would suggest to use Thread.Sleep(1000) in this manner
Caller section
for (int i = 0; i < 5; i++)
{
ThreadArray[i] = new Thread(new ThreadStart(createForm));
ThreadArray[i].Start();
}
Thread.Sleep(1000);
Also in the method that executing the work for the thread.
while(!something)
{
Thread.Sleep(1000)
}

Categories

Resources