Writing to UI Thread in a Real time Application
-
I am building a system to record 6DOF information during an aerobatic flight. I grab data from a hardware set including an IMU, a GPS and a Baro altimeter and 20 to 30ms intervals. I attempted to use the WinForms Timer to schedule data recording using BW to allow me the collect the data in the DoWork method and then write the data to the WinForm in the WorkCompleted method. All this worked properly but the WinForms timer didn't provide the accuracy for scheduling the data properly. I did a test using a multimedia timer which provided the necessary update timing accuracy but when I used it to drive a BW the WorkCompleted method was no longer in the UI string and I get an error for trying to access a UI element from outside the UI string. I can make this work doing a BeginInvoke((MethodInvoker) delegate {all updates to UI elements in here}). What I would like to know if there is a way to force the WorkCompleted method back to the UI string. Also, not sure why changing the timer made the BW operation change and no longer have the WorkCompleted method in the UI string. The code for the program is below. The first set is my code and the second set it the C# code that implements the timer that I got off the internet. I'm not sure I used the
correctly but at least I tried. Appreciate any suggestions as I am still trying to figure out C# and the .net/Winforms environment.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;namespace Simple_BW_Timing_with_Winforms
{
public partial class Form1 : Form
{
Stopwatch systemTimer;
long time1 = 0, time2 = 0;
public long timeBetweenCalls;
int count;
BackgroundWorker updateInfoBW;public delegate void \_writeForm(); public readonly MicroLibrary.MicroTimer \_microTimer;
// public BackgroundWorker updateInfoBW;
public Form1() { updateInfoBW = new BackgroundWorker(); updateInfoBW.DoWork += updateInfoDoWork; updateInfoBW.RunWorkerCompleted += updateInfoWorkCompleted; InitializeComponent(); systemTimer = new Stopwatch(); systemTimer.Start(); \_microTimer = new MicroLibrary.MicroTimer(); \_microTimer.MicroTimerElapsed +=
-
I am building a system to record 6DOF information during an aerobatic flight. I grab data from a hardware set including an IMU, a GPS and a Baro altimeter and 20 to 30ms intervals. I attempted to use the WinForms Timer to schedule data recording using BW to allow me the collect the data in the DoWork method and then write the data to the WinForm in the WorkCompleted method. All this worked properly but the WinForms timer didn't provide the accuracy for scheduling the data properly. I did a test using a multimedia timer which provided the necessary update timing accuracy but when I used it to drive a BW the WorkCompleted method was no longer in the UI string and I get an error for trying to access a UI element from outside the UI string. I can make this work doing a BeginInvoke((MethodInvoker) delegate {all updates to UI elements in here}). What I would like to know if there is a way to force the WorkCompleted method back to the UI string. Also, not sure why changing the timer made the BW operation change and no longer have the WorkCompleted method in the UI string. The code for the program is below. The first set is my code and the second set it the C# code that implements the timer that I got off the internet. I'm not sure I used the
correctly but at least I tried. Appreciate any suggestions as I am still trying to figure out C# and the .net/Winforms environment.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;namespace Simple_BW_Timing_with_Winforms
{
public partial class Form1 : Form
{
Stopwatch systemTimer;
long time1 = 0, time2 = 0;
public long timeBetweenCalls;
int count;
BackgroundWorker updateInfoBW;public delegate void \_writeForm(); public readonly MicroLibrary.MicroTimer \_microTimer;
// public BackgroundWorker updateInfoBW;
public Form1() { updateInfoBW = new BackgroundWorker(); updateInfoBW.DoWork += updateInfoDoWork; updateInfoBW.RunWorkerCompleted += updateInfoWorkCompleted; InitializeComponent(); systemTimer = new Stopwatch(); systemTimer.Start(); \_microTimer = new MicroLibrary.MicroTimer(); \_microTimer.MicroTimerElapsed +=
What do you mean "not enough accuracy". "Timers" (in general) are "guaranteed" not to run "before" the interval has elapsed; but not how long "after" the interval has elapsed. I use DispatcherTimers and WPF apps; the DispatcherTimer runs on the UI thread; so that issue is handled. And I use more than one timer to create a "pipeline": 1) One retrieves new samples from the devices 2) Another backsup samples to disk 3) Another charts the samples in real-time 4) Another does a "grid" display update of the "sampled zones" 5) Another monitors device status independent of the sampling rate 6) Another "heart beat" monitors overall system health and restarts any paused processes.
"(I) am amazed to see myself here rather than there ... now rather than then". ― Blaise Pascal
-
I am building a system to record 6DOF information during an aerobatic flight. I grab data from a hardware set including an IMU, a GPS and a Baro altimeter and 20 to 30ms intervals. I attempted to use the WinForms Timer to schedule data recording using BW to allow me the collect the data in the DoWork method and then write the data to the WinForm in the WorkCompleted method. All this worked properly but the WinForms timer didn't provide the accuracy for scheduling the data properly. I did a test using a multimedia timer which provided the necessary update timing accuracy but when I used it to drive a BW the WorkCompleted method was no longer in the UI string and I get an error for trying to access a UI element from outside the UI string. I can make this work doing a BeginInvoke((MethodInvoker) delegate {all updates to UI elements in here}). What I would like to know if there is a way to force the WorkCompleted method back to the UI string. Also, not sure why changing the timer made the BW operation change and no longer have the WorkCompleted method in the UI string. The code for the program is below. The first set is my code and the second set it the C# code that implements the timer that I got off the internet. I'm not sure I used the
correctly but at least I tried. Appreciate any suggestions as I am still trying to figure out C# and the .net/Winforms environment.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;namespace Simple_BW_Timing_with_Winforms
{
public partial class Form1 : Form
{
Stopwatch systemTimer;
long time1 = 0, time2 = 0;
public long timeBetweenCalls;
int count;
BackgroundWorker updateInfoBW;public delegate void \_writeForm(); public readonly MicroLibrary.MicroTimer \_microTimer;
// public BackgroundWorker updateInfoBW;
public Form1() { updateInfoBW = new BackgroundWorker(); updateInfoBW.DoWork += updateInfoDoWork; updateInfoBW.RunWorkerCompleted += updateInfoWorkCompleted; InitializeComponent(); systemTimer = new Stopwatch(); systemTimer.Start(); \_microTimer = new MicroLibrary.MicroTimer(); \_microTimer.MicroTimerElapsed +=
First, Windows is NOT a real-time O/S. There's over a thousands threads running before your app starts. Timers are guaranteed to not fire before the specified interval, but there is no guarantee of them firing at all! The Windows Forms timer is probably the least accurate timer of them all. It fires on an event, which the applications message pump has to process and dispatch to your Tick event handling code. Well, messages in the pump are queued up waiting to be processed. There could be a ton of other messages, such as mouse changes, keyboard changes, painting, ... ahead of the Tick message. Processing those messages takes time, thereby knocking your Tick event off its interval, possibly even past the next Tick interval! I would probably have a microcontroller gathering this information at a set interval, storing it, and sending it in batches to your WinForms app when that app requests it. The drawback to this would be the limited memory of a microcontroller if it doesn't have something like SD card storage available.
System.ItDidntWorkException: Something didn't work as expected. A guide to posting questions on CodeProject
Click this: Asking questions is a skill. Seriously, do it.
Dave Kreskowiak -
I am building a system to record 6DOF information during an aerobatic flight. I grab data from a hardware set including an IMU, a GPS and a Baro altimeter and 20 to 30ms intervals. I attempted to use the WinForms Timer to schedule data recording using BW to allow me the collect the data in the DoWork method and then write the data to the WinForm in the WorkCompleted method. All this worked properly but the WinForms timer didn't provide the accuracy for scheduling the data properly. I did a test using a multimedia timer which provided the necessary update timing accuracy but when I used it to drive a BW the WorkCompleted method was no longer in the UI string and I get an error for trying to access a UI element from outside the UI string. I can make this work doing a BeginInvoke((MethodInvoker) delegate {all updates to UI elements in here}). What I would like to know if there is a way to force the WorkCompleted method back to the UI string. Also, not sure why changing the timer made the BW operation change and no longer have the WorkCompleted method in the UI string. The code for the program is below. The first set is my code and the second set it the C# code that implements the timer that I got off the internet. I'm not sure I used the
correctly but at least I tried. Appreciate any suggestions as I am still trying to figure out C# and the .net/Winforms environment.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;namespace Simple_BW_Timing_with_Winforms
{
public partial class Form1 : Form
{
Stopwatch systemTimer;
long time1 = 0, time2 = 0;
public long timeBetweenCalls;
int count;
BackgroundWorker updateInfoBW;public delegate void \_writeForm(); public readonly MicroLibrary.MicroTimer \_microTimer;
// public BackgroundWorker updateInfoBW;
public Form1() { updateInfoBW = new BackgroundWorker(); updateInfoBW.DoWork += updateInfoDoWork; updateInfoBW.RunWorkerCompleted += updateInfoWorkCompleted; InitializeComponent(); systemTimer = new Stopwatch(); systemTimer.Start(); \_microTimer = new MicroLibrary.MicroTimer(); \_microTimer.MicroTimerElapsed +=
The BackgroundWorker (BGW) uses a WindowFormsSynchronizationContext (SC) to invoke the completed and progress events on the UI thread. The SC comes from the thread that calls the RunWorkerAsync method. A non UI thread typically has no SC and then the BGW operates what is sometimes called the "free threaded model" using a ThreadPool thread to fire the completed and progress events. A BGW started from a new thread will behave properly if the thread is given the UI thread's SC. Some example code, I hope the missing bits are obvious.
private void StartFromThreadWithContext_Click(Object sender, EventArgs e) {
log.WriteLine("UI thread {0}", Thread.CurrentThread.ManagedThreadId);
Thread starterThread = new Thread(StarterThreadProc);
starterThread.Start(SynchronizationContext.Current);
}private void StarterThreadProc(Object context) {
log.WriteLine("Starting BGW from thread {0}", Thread.CurrentThread.ManagedThreadId);
WindowsFormsSynchronizationContext wfsc = context as WindowsFormsSynchronizationContext;
if (wfsc != null) {
SynchronizationContext.SetSynchronizationContext(wfsc); // THE IMPORTANT BIT!!
}
log.WriteLine("Thread has {0}", SynchronizationContext.Current);
worker.RunWorkerAsync();
}private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
log.WriteLine("RunWorkerCompleted thread {0}", Thread.CurrentThread.ManagedThreadId);
}private void Worker_DoWork(object sender, DoWorkEventArgs e) {
log.WriteLine("DoWork thread {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(300);
}Output to the log shows correct invocation of the completed event
UI thread 10
Starting BGW from thread 12
Thread has System.Windows.Forms.WindowsFormsSynchronizationContext
DoWork thread 7
RunWorkerCompleted thread 10Alan.
-
I am building a system to record 6DOF information during an aerobatic flight. I grab data from a hardware set including an IMU, a GPS and a Baro altimeter and 20 to 30ms intervals. I attempted to use the WinForms Timer to schedule data recording using BW to allow me the collect the data in the DoWork method and then write the data to the WinForm in the WorkCompleted method. All this worked properly but the WinForms timer didn't provide the accuracy for scheduling the data properly. I did a test using a multimedia timer which provided the necessary update timing accuracy but when I used it to drive a BW the WorkCompleted method was no longer in the UI string and I get an error for trying to access a UI element from outside the UI string. I can make this work doing a BeginInvoke((MethodInvoker) delegate {all updates to UI elements in here}). What I would like to know if there is a way to force the WorkCompleted method back to the UI string. Also, not sure why changing the timer made the BW operation change and no longer have the WorkCompleted method in the UI string. The code for the program is below. The first set is my code and the second set it the C# code that implements the timer that I got off the internet. I'm not sure I used the
correctly but at least I tried. Appreciate any suggestions as I am still trying to figure out C# and the .net/Winforms environment.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;namespace Simple_BW_Timing_with_Winforms
{
public partial class Form1 : Form
{
Stopwatch systemTimer;
long time1 = 0, time2 = 0;
public long timeBetweenCalls;
int count;
BackgroundWorker updateInfoBW;public delegate void \_writeForm(); public readonly MicroLibrary.MicroTimer \_microTimer;
// public BackgroundWorker updateInfoBW;
public Form1() { updateInfoBW = new BackgroundWorker(); updateInfoBW.DoWork += updateInfoDoWork; updateInfoBW.RunWorkerCompleted += updateInfoWorkCompleted; InitializeComponent(); systemTimer = new Stopwatch(); systemTimer.Start(); \_microTimer = new MicroLibrary.MicroTimer(); \_microTimer.MicroTimerElapsed +=
Anytime I do BW threads talking the UI, I have to use the Dispatcher.Invoke or Dispatcher.BeginInvoke to send the changes back to the UI thread. I am not sure if the Dispatcher specifically is available in the old WinForm, but the cross-thread issue be the right direction to think about to resolve your issue.
Ben Scharbach Temporalwars.Com YouTube:Ben Scharbach