Drawing data obtained from Serial Port progresively getting slower - c#

I am working on voltmeter application, that draws voltage waveform. Hardware sends 1000 numbers (range 0 - 1023, always whole numbers) in string format per second through serial port.
public SerialPort serialPort = new SerialPort("COM3", 57600);
serialPort.Open();
String is converted into int and then drawn with DrawLine into PictureBox.
// variable declarations, all is int, runs in its own thread
while (blToMeasure) // true after clicking on button
{
iPrevY = iY;
iY = Int16.Parse(serialPort.ReadLine());
graphicsGraph.DrawLine(penBlack, iX, iPrevY, iX + 1, iY);
// only this thread is accessing PictureBox
iX++;
if (iX > picBoxGraph.Width)
{
graphicsGraph.Clear(SystemColors.Control);
iX = 0;
}
if (iY > picBoxGraph.Height)
{
}
}
Issue is that drawing lines itself is fast as it should be only for a couple of seconds, but gets gradually slower.
I tried Int.Parse, Int32.Parse and splitting thread function multiple ways using lock (graphicsGraph) (moving conditions with Clear into another thread) or using BlockingCollection<int> (moving DrawLine into another thread, away from Parse). Nothing seems to work and app still gets slower a couple of times after like a minute of running.
There isn't issue with hardware itself, checked with another software. Is this too fast for C#?

Solution:
I got the best results using Port.ReadTimeout = 1 and Port.DiscardInBuffer(). Also using Form.DoubleBuffered = true, but it doesn't make a huge difference in this particular case.
// constructor
Port.ReadTimeout = 1;
Form.DoubleBuffered = true;
Here is the loop itself:
btn.Click() // click to start measuring
{
Port.DiscardInBuffer();
blToMeasure = true;
}
while (blToMeasure) // true after clicking on button
{
iPrevY = iY;
try {
iY = Int16.Parse(serialPort.ReadLine());
}
catch
{
// exception logic
}
graphicsGraph.DrawLine(penBlack, iX, iPrevY, iX + 1, iY);
// only this thread is accessing PictureBox
iX++;
if (iX > picBoxGraph.Width)
{
graphicsGraph.Clear(SystemColors.Control);
iX = 0;
}
if (iY > picBoxGraph.Height)
{
}
}
When the app starts to read from the port, there is always accumulated data, because my hardware is sending numbers all the time, so I get rid of the buffer. Than the drawing of lines is not executed in differing spikes and the speed is constant. Analyzing the issue with Watch, I found out, that it occasionaly takes much longer to read this data and because of 1000 reads per second, it slows down. So to prevent slowing down, I used Port.ReadTimeout, that skips the read, if it takes too long.
The difference is visible, drawing no longer slows down and it keeps the same pace for minutes from what I've tried. I consider this sufficient solution for my issue, thank you!

Related

Working on each frame from a Vimba camera

I am working with an Allied Vision Vimba camera to monitor an experiment involving a laser.
I have written a code that allows me to take one frame from the camera live imaging (displayed in pictureBoxLiveCamera, in black and white), put it in another PictureBox (pictureBoxFixe1) and work on it. The code works fine for the most part, even though it is not finished.
My problem is that, eventually, I will need to work continuously, meaning on every frame from the camera (about 15-20 per second), and I am starting to feel like this is going to be complicated.
I am not asking you to help me through the whole process, but I have a precise exemple of something that is not working and I would like to understand. I am working with the Vimba .NET API, so some of the functions called might not be familiar to most of you (and they are not much more to me, really), but I'll try my best to explain them in the code (the API manual is kinda cryptic).
My code has a part that, when I isolate one frame from the camera to work on it (via a button-clickevent, draws an histogram that graphs the number of pixels on the vertical axis and digital number value on the horizontal axis. This way I know if some of the pixels are saturated (at 255), and I can lower the exposure time. This works fine, here is the code for the button that isolate one frame and draws the histogram :
private void fixButton_Click(object sender, EventArgs e)
{
if (m_Acquiring == true) //mm_Acquiring being true indicates that the camera is sending images continuously, and that they are toggled in pictureBoxLiveCamera
{
pictureBoxFixe1.Image = SaveBitmap; //SaveBitmap is the bitmap isolated from the live camera in an earlier code portion
Array.Clear(PixelColorCount, 0, 256);
foreach (var Series in chartHist.Series)
{
Series.Points.Clear();
}
//Creating a bitmap from pictureBoxFixe1 to draw the histogram from
Bitmap Couleur = new Bitmap(pictureBoxFixe1.Image);
//Collecting data from pictureBoxFixe1
for (int i = 0; i < 1023; i++)
{
for (int j = 157; j < 288; j++)
{
PixelColorCount[Couleur.GetPixel(i + 1, j).B] += 1;
}
for (int j = 484; j < 615; j++)
{
PixelColorCount[Couleur.GetPixel(i + 1, j).B] += 1;
}
}
//Plotting the pixel counter, to detect saturation
for (int i = 0; i < 256; i++)
{
chartHist.Series["Pixel count"].Points.AddXY(i, PixelColorCount[i]);
}
//If there are saturated pixels : toggle a title on chart 1 to warn the user
if (PixelColorCount[255] > 1)
{
chartHist.Titles["Title1"].Visible = false;
chartHist.Titles["Title2"].Visible = true;
}
else
{
chartHist.Titles["Title1"].Visible = true;
chartHist.Titles["Title2"].Visible = false;
}
}
else
{
MessageBox.Show("Acquisition is not running.", "Something went wrong !", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
Now what I want is to make this work live, with every frame incoming from the camera. But it doesn't work and I can't find why. Here is what I tried :
private void BtAcquisitionLiveCam_Click(object sender, EventArgs e)
{
//Here is the part of the code that starts the live acquisition when I click the button, I don't copy it completely because I don't think this is relevant.
this.m_Acquiring = true;
mycamera.OnFrameReceived += new Camera.OnFrameReceivedHandler(this.OnFrameReceived); //This is the event handler for the reception of a frame for the camera, it is part of the Vimba API
mycamera.StartContinuousImageAcquisition(1); //According to the manual, this "Starts streaming and allocates the needed frames", with the argument being "count of Frame(s) which should be used for this method"
}
private void OnFrameReceived(Frame frame) //Frame is the class in which the code puts the data from the camera, frame is the last pack of data received from the camera
{
Bitmap myBitmap = null;
if (true == m_Acquiring)
{
try
{
mycamera.QueueFrame(frame);
}
catch (Exception)
{
MessageBox.Show("Frame queuing failed.", "Something went wrong !", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
else
{
MessageBox.Show("Acquisition is not running.", "Something went wrong !", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
featureExposureTime.FloatValue = (double)hScrollBarLiveExposureTime.Value;
frame.Fill(ref myBitmap);
pictureBoxLiveCamera.Image = myBitmap;
SaveBitmap = myBitmap; //Up until here the code works perfectly, because I can use SaveBitmap without problem later in the code (not shown here)
Array.Clear(PixelColorCount, 0, 256);
foreach (var Series in chartHist.Series)
{
Series.Points.Clear();
}
//Collecting data from pictureBoxFixe1
for (int i = 1; i < 1023; i++)
{
for (int j = 1; j < 767; j++)
{
PixelColorCount[SaveBitmap.GetPixel(i, j).B] += 1;
}
}
//Plotting the pixel counter, to detect saturation
for (int i = 0; i < 256; i++)
{
chartHist.Series["Pixel count"].Points.AddXY(i, PixelColorCount[i]);
}
//If there are saturated pixels : toggle a title on chart 1 to warn the user
if (PixelColorCount[255] > 1)
{
chartInit.Titles["Title1"].Visible = true;
}
else { }
}
Sorry for the amount of code, but I thought I couldn't do otherwise. My problem is that the histogram doesn't show at all (well it is set as Visible, I see the title, but nothing gets plotted in it). Does anyone have any idea ?
The thing that is driving me crazy is that the VimbaViewer program that's provided with the camera does exactly what I want in a flawless way, but I don't have access to it's source code...
Thanks !
If I follow correctly, your OnFrameReceived event is firing correctly, but the WinForm application UI is not updating after each frame is processed.
It appears that "chartInit" is being referenced in your OnFrameReceived instead of "chartHist", is that supposed to be the case?
Whatever your chart control is, try "chart.Refresh()" or "chart.Update()". It might not be updating the UI thread from the event loop.
Another thing you'll need to think about is how fast these images are coming in. Will it draw and redraw the chart 15-20 times per second? Is that reasonable? Might need some sort of queue/averaging mechanism to only update the chart every x seconds, or perhaps if some threshold value is exceeded with respect to change in the images. Just a thought.

How does the DoWork method work in C# for Async Communication (ELI5)

I am using Visual Studio C# 2010 on Windows 10.
EDIT: My objective is to create an abort button which allows me to use the UI to cancel a loop.
I'm using an example I found online which is similar to this:
Here is the code:
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = "";
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(50); //do some intense task here.
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
(There was a bit of code sharing the date/time but I removed it since it was superfluous to the basic example).
How I'm understanding this code is that, 100 times, the code pauses the main thread and works on "some intense task" for 50ms. Is this the case?
If so, I think I've ran into a problem. I have this code I want to run:
private void btn_runtest_Click(object sender, EventArgs e)
{
// Exterior Platform Loop
Output.AppendText("Starting Test \n");
for (int i = 0; i <= 3200; i += 178)
{
// Interior Platform Loop
for (int ii = 0; i <= 6400; ii += 178)
{
comport7_interior.Write("#1N178\r"); //change to have actual length?
comport7_interior.Write("#1G\r");
Output.AppendText("Interior shift 5 degrees \n");
Thread.Sleep(4000);
// ***********************************************************************************
// Read data from comport2_lightsensor, Parse byte, store Lux value, store angle (i,ii)
// ***********************************************************************************
}
comport8_exterior.Write("#0N178\r");
comport8_exterior.Write("#0G\r");
Output.AppendText("Exterior shift 5 degrees \n");
Thread.Sleep(4000);
//flip direction for internal
if (IsOdd(jjj))
{
//
comport7_interior.Write("#1-\r");
Output.AppendText("Interior switching direction to counter clockwise \n");
}
else
{
comport7_interior.Write("#1+\r");
Output.AppendText("Interior switching direction to clockwise \n");
}
jjj = jjj + 1;
// ***********************************************************************************
// Read data from compart2_lightsensor, Parse byte, store Lux value, store angle (i,ii)
// ***********************************************************************************
}
Output.AppendText("Loop Ended");
// manually reverse mount direction
// repeat the whole double for loop
}
This is pretty "fatty" code, so to summarize, it controls two stepper motors, rotates them to follow the desired path, pauses for 4 seconds, and will eventually log data. We could do the exact math but simply estimating we can realize this code will take 1-2 hours. With all of this pausing, it does not seem conducive to being split into 100 little chunks on worked on separately (if my understanding of the previously stated code is correct). Correct me I'm wrong but I don't think this code would fit in the above code.
Does this mean I'm looking at the wrong approach?
Thank you all in advance.

Stop a looped thread method after x seconds

I'm creating a console game as simple as "I generate a random number, find it", but with many options.
My current code (without what I want here) is availlable on GitHub: https://github.com/crakmaniaque/trouvezmoi
What I want is to create a version of my game which will be timed, so the computer generates numbers, the user finds it, it generates a new one and the player have 90 seconds to find a max lot of random numbers. I can code this easily.
What I will need help is to stop the game (a thread) after 90 seconds and retrieve the number of answers founded from the thread. The Console.Title should also show time remaining. The attempt I've tried works, but the thread is not interrupted if console is asking for number input (Console.ReadLine()). But the timer is for the entire process, not only user input.
private static void timerb()
{
int t = 90;
for (int i = 0; i < 90; i++)
{
Console.Title = t + " seconds remaining";
Thread.Sleep(1000);
t--;
}
}
private static void cGame()
{
Thread t = new Thread(timerb);
t.Start();
while (t.IsAlive)
{
bool good = false;
int rnd = new Random().Next(0,10); // 0 and 10 are sample
while (!good)
{
try
{
Console.Write("Enter a number between x and y >");
int i = int.Parse(Console.ReadLine());
if (i == rnd)
{
good = true;
}
}
catch (FormatException)
{
Console.WriteLine("Invalid answer.");
}
}
}
}
I don't know much about threading and at that point I'm stuck.
Can someone help me with my problem? I'm using .NET 2.0.
Perhaps you are looking for a timer? You could register an event, that would fire after 90 seconds, that would run while the loop is happening. The documentation can be found here: Timer class MSDN documentation.
I believe the usage would be:
Timer timer = new Timer { Interval = new Timespan (0,1,30);
timer.elapsed += //function to fire to kill the app or the game
You'd need to make each console read with a timeout equal to the amount of time left in the game. That solves that issue.
Then, you need a way to signal the timerb thread to shut down when the main game loop has ended. I think the simplest way would be to end the game loop when the remaining time is <= zero. Alternatively, you could make timerb singnal the main thread to shut down when t == 0. Inter-thread communication is always complicated and error-prone, though.
You can signal the timerb thread to shut down by setting a volatile bool shutdown to true and by making timerb poll that variable and shut itself down.

Thread for my user control

I have a user control that shows a speed in a dial format (An image).
It has a single method: SetSpeed(int speed);
It then makes the dial move to the desired speed, from the last set speed. It does then in incriments. That is, it moves to the new speed.
Problem is, if the car is going 10km/h, and then goes (very quickly) to 100km/h, it takes the dial maybe 2 seconds to reach that speed... (It's not 100% real time - which is realistic .. it moves to the new speed).
But, if, before the dial gets to 100km/h, the person slows to 50km/h, I need to interupt the movement to 100, and start moving to 50. Regardless of the current position (not speed) of the dial, I need to change the 'target speed'.
My control is using a BackgroundWorker to handle the refreshing, and not lock the UI.
So, my call is very basic from the UI:
dial1.SetSpeed(int.Parse(value));
And then the user control does this:
BackgroundWorker bw = new BackgroundWorker();
public void SetSpeed(int speed)
{
while(bw.IsBusy)
{
Thread.Sleep(50);
}
bw.RunWorkerAsync(speed);
}
And the method that does the work:
private void UpdateSpeed(object sender, DoWorkEventArgs e)
{
var oldSpeed = airSpeed;
var newSpeed = (int) e.Argument;
if(oldSpeed <= newSpeed)
{
for (int i = oldSpeed; i < newSpeed; i++)
{
airSpeed++;
this.Invoke(new MethodInvoker(Refresh));
}
}
else
{
for (int i = oldSpeed; i > newSpeed; i--)
{
airSpeed--;
this.Invoke(new MethodInvoker(Refresh));
}
}
airSpeed = newSpeed;
}
It locks when I send it two values in quick succession...
How do I interrupt the thread, if it's running, and change the value?
(Also, I think my code to change the speed is bad - can that be made neater?)
You do not. You handle it in a proper way, with locks, and checking whether the value needs changing. You do NOT interrupt it.
Basically, you need a lock area between SetSpeed and the Refresh method, so that one blocks the other. Then, when you set speed and the thread is currently in a critical area, it simply waits until the update is finished.
And your UpdateSpeed makes no sense - the change (airspeed-- and airspeed++) should be timer driven... you currently change them in a "arbitrary" speed, depending on processor speed. No timing.

Why does it seem like operations are not being performed in the order of the code?

Here's some background. I'm working on game similar to "Collapse." Blocks fill up at the bottom and when all twelve blocks have been filled they push up on to the playfield. I have a counter called (intNextSpawn) that not only tells when to "push up" the next row, as well as calculating vectors for the graphics. It resets to 0 when the blocks have been pushed up.
I've added some debug text on the screen to try and see what is happening, but I can't seem to hunt down the issue. It almost seems like it is still incrementing the counter while trying to randomize the the block that's supposed to appear (things acting out of order). I end up getting "blank" blocks and it causes some really screwy effects while testing. It gets worse when jack up the speed.
I'm willing to post any additional code that might help. Below are the two main blocks where this is could happening. Is there something I might be doing wrong or may there be a way I can prevent this from happening (if that's what it's doing)?
Any help would be greatly appreciated.
Edit... The first code block is in the "Update" method
// Calculate time between spawning bricks
float spawnTick = fltSpawnSpeed * fltSpawnSpeedModifier;
fltSpawn += elapsed;
if (fltSpawn > spawnTick)
{
// Fetch a new random block.
poNextLayer[intNextSpawn] = RandomSpawn();
// Increment counter
intNextSpawn++;
// Max index reached
if (intNextSpawn == 12)
{
// Push the line up. Returns true if lines go over the top.
if (PushLine())
{
gmStateNew = GameState.GameOver;
gmStateOld = GameState.Playing;
}
// Game still in play.
else
{
// Reset spawn row to empty bricks.
for (int i = 0; i < 12; i++)
poNextLayer[i] = new PlayObject(ObjectType.Brick, PlayColor.Neutral, Vector2.Zero);
intNextSpawn = 0; // Reset spawn counter.
intLines--; // One less line to go...
}
}
fltSpawn -= spawnTick;
}
private bool PushLine()
{
// Go through the playfield top down.
for (int y = 14; y >= 0; y--)
{
// and left to right
for (int x = 0; x < 12; x++)
{
// Top row contains an active block (GameOver)
if ((y == 14) && (poPlayField[x, y].Active))
// Stop here
return true;
else
{
// Not bottom row
if (y > 0)
{
// Copy from block below
poPlayField[x, y] = poPlayField[x, y - 1];
// Move drawing position up 32px
poPlayField[x, y].MoveUp();
}
// Bottom row
else
{
// Copy from spawning row
poPlayField[x, y] = poNextLayer[x];
// Move drawing position up 32px (plus 4 more)
poPlayField[x, y].MoveUp(4);
// Make the block active (clickable)
poPlayField[x, y].Active = true;
}
}
}
}
// Game still in play.
return false;
}
So, the largest part of your problem is that you are decrementing some timers based on the size of a tick, and then running some comparisons. Wrather than do all that for timers (especially with the rounding and precision loss of float, etc...) instead just to time-basic comparisons.
Example, you do fltSpawn += elapsed and later fltSpawn -= spawnTick which will lead to floating point rounding / precission errors.
Try something more like this:
int64 spawnDelay = 1000000; // 10,000 ticks per ms, so this is 100ms
...
if (DateTime.Now() > nextSpawn)
{
// Fetch a new random block.
poNextLayer[intNextSpawn] = RandomSpawn();
...
// At some point set the next spawn time
nextSpawn += new TimeSpan(spawnDelay * spawnSpeedModifier);
}

Categories

Resources