I'm new to the coding game so I'm not too sure how to remedy this. My code pulls information from a thread that harvests data from a 9DOF sensor which comes out as three Euler angles. The data is then used to generate points from which a circle is made on screen with the drawing class. To works perfectly for a while, but eventually will always give this exception saying that there was an unhandled exception of type 'System.InvalidOperationException' that occurred in System.Drawing.dll with additional information saying that the object is currently in use elsewhere. After poking around a bit more and playing with what is drawn, I've come to the (possibly incorrect) conclusion that the thread is sending data faster than the main code can render the drawing. How can I prevent this? An excerpt of the code is below.
private void button3_Click(object sender, EventArgs e)
{
//single node test
Nodes.NodesList.Add(new RazorIMU("COM7"));
Nodes.NodesList[0].StartCollection('e');
Nodes.NodesList[0].CapturedData += new RazorDataCaptured(Fusion_CapturedData);
public void Fusion_CapturedData(float[] data, float deltaT)
{
int centerX = (int)(300 + (5 / 3) * data[0] + 0.5);
int centerY = (int)(300 + (5 / 3) * data[1] + 0.5);
int endPointX = (int)(centerX + 25 * Math.Sin(Math.PI / 180 * data[2]) + 0.5);
int endPointY = (int)(centerY + 25 * Math.Cos(Math.PI / 180 * data[2]) + 0.5);
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
/*g.DrawLine(new Pen(Color.Yellow), 300, 0, 300, 600);
g.DrawLine(new Pen(Color.Yellow), 0, 300, 600, 300);
g.DrawLine(new Pen(Color.LightYellow), 150, 0, 150, 600);
g.DrawLine(new Pen(Color.LightYellow), 0, 150, 600, 150);
g.DrawLine(new Pen(Color.LightYellow), 450, 0, 450, 600);
g.DrawLine(new Pen(Color.LightYellow), 0, 450, 600, 450);*/
g.DrawEllipse(new Pen(Color.Green), centerX - 25, centerY - 25, 50, 50);
g.DrawLine(new Pen(Color.Green), centerX, centerY, endPointX, endPointY);
}
pictureBox1.Image = bmp;
}
This is the main code. The thread simply sends in information at the speed at which it is received, so I don't think I need to put that in here unless anyone says otherwise.
{ public delegate void RazorDataCaptured(float[] data, float deltaT);
/// <summary>
///
/// Object for Sparkfun's 9 Degrees of Freedom - Razor IMU
/// Product ID SEN-10736
/// https://www.sparkfun.com/products/10736
///
/// Running Sample Firmware
/// https://github.com/a1ronzo/SparkFun-9DOF-Razor-IMU-Test-Firmware
///
/// </summary>
public class RazorIMU : IDisposable
{
private SerialPort _com;
private static byte[] TOGGLE_AUTORUN = new byte[] { 0x1A };
public string Port { get; private set; }
private Thread _updater;
private string[] _parts;
public event RazorDataCaptured CapturedData = delegate { };
private float[] _data = new float[9];
private static bool running = false;
/// <summary>
/// Create a new instance of a 9DOF Razor IMU
/// </summary>
/// <param name="portName">Serial port name. Ex: COM1.</param>
public RazorIMU(string portName)
{
// Create and open the port connection.
_com = new SerialPort(portName, 57600, Parity.None, 8, StopBits.One);
_com.Open();
Port = portName;
// Set the IMU to automatically collect data if it has not done yet.
//Thread.Sleep(3000);
//_com.Write("#s00");
//_com.DiscardInBuffer();
}
/// <summary>
/// Start continuous collection of data.
/// </summary>
public void StartCollection(char dataType)
{
running = true;
if (dataType == 'r') _updater = new Thread(new ThreadStart(ContinuousCollect));
else if (dataType == 'q') _updater = new Thread(new ThreadStart(ContinuousCollectQuat));
else if (dataType == 'e') _updater = new Thread(new ThreadStart(ContinuousCollectEuler));
_updater.Start();
}
/// <summary>
/// Stop continuous collect of data.
/// </summary>
public void StopCollection()
{
if (_updater != null)
{
running = false;
_updater.Join();
_updater = null;
}
}
/// <summary>
/// This method is extremely important. It continously updates the data array.
/// Data is read and the change in time since the last read is calculated.
/// The CapturedData event is triggered, sending the new data and the change in time.
/// </summary>
private void ContinuousCollect()
{
_com.WriteLine("#osr"); //Sets sensor output data to raw.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataRaw();
CapturedData(_data, 0.020F);
}
}
private void ContinuousCollectQuat()
{
_com.WriteLine("#ot"); //Sets sensor output data to quaternions.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataQuat();
CapturedData(_data, 0.020F);
}
}
private void ContinuousCollectEuler()
{
_com.WriteLine("#ob"); //Sets sensor output data to quaternions.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataEuler();
CapturedData(_data, 0.020F);
}
}
/// <summary>
/// Get a single sample of the 9DOF Razor IMU data.
/// <para>Format: [accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z]</para>
/// </summary>
/// <param name="result">double array of length 9 required.</param>
private void ReadDataRaw()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 9)
for (int i = 0; i < 9; i++)
_data[i] = float.Parse(_parts[i]);
}
private void ReadDataQuat()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 4)
for (int i = 0; i < 4; i++)
_data[i] = float.Parse(_parts[i]);
}
private void ReadDataEuler()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 3)
for (int i = 0; i < 3; i++)
_data[i] = float.Parse(_parts[i]);
}
/// <summary>
///
/// </summary>
public void Dispose()
{
StopCollection();
if (_com != null) //Make sure _com exists before closing it.
_com.Close();
_com = null;
_data = null;
_parts = null;
}
}
}