timer stop activating during while statement - c#

I have a timer and it pull data from a PLC. I have a part where I write data to a PLC and then I write for a reply from the PLC.
there a flag and I am waiting for it to change to true.
my timer is reading the flag...
but when I get to my while statement, the timer stops firing up.
why ?
here is my code :
void movePixalLinkCam(int pos)
{
bool flag;
if (state == 2)
{
flag = true;
tcClient.WriteAny(Pos0number, Convert.ToDouble(pos));
tcClient.WriteAny(Axis0MoveStartnumber, flag);
while(!Axis0MoveFlag)
{
}
flag = false;
tcClient.WriteAny(Axis0MoveStartnumber, flag);
}
}
the timer seem to stop during the while statement .
why?
should I put this function on a different thread or something ?
Update
private void timer1_Tick(object sender, EventArgs e)
{
try
{
dataStream.Position = 0;
tcClient.Read(Axis0MoveFlagnumber, dataStream);
Axis0MoveFlag = binRead.ReadBoolean();
dataStream.Position = 0;}
catch (Exception err)
{
MessageBox.Show(err.Message.ToString());
}

Related

Await a value change in c#

I'm currently working on a project that involve a stepper motor using c# forms and arduino.
I need to know in c# when the motor stops his movement. To do that, I programmed arduino to send a string trough Serial when the movement is finished. Then the string is interpreted by c# so that the app can do other things after that.
I now want to have the same result as the example below, but using async await.
I stored the information that the motor stopped in:
movimentFinished = true;
I solved the problem in a terrible way using: while(movimentFinished = false){await Task.Delay(1)}
The difference between a while loop and an await is going to effect other stuff on my WinApp. The while blocks all the application until movimentFinished == true;
The logic that I want is simple:
MoveMotor();
await "value change"
//do stuff after it finishes
Any help/tip to do that?
Thank you for reading
//this funcion will receive an array from arduino as a reply
private void risposta(object sender, SerialDataReceivedEventArgs e)
{
riceved = port.ReadLine();
rispos = riceved;
this.Invoke(new EventHandler(Reply));
}
//this function will run every time a reply is sent from arduino through serial
private void Reply(object sender, EventArgs e)
{
//string risposValore = rispos.Substring(2, rispos.Length - 2);
char[] pcr = rispos.ToCharArray();
rispos = rispos.Trim();
if (pcr[0] == 'M' && pcr[1] == 'F' && pcr[2] == '1')
{
movementFinished = true;
}
}
//this funcion will send a serial command to arduino to run the motor
public void MoveTheMotor(int position)
{
int command = 1000;
int movement = command + (position)
string com1 = movement.ToString();
port.Write(com1 + "\n");
movementFinished = false;
}
private async void Demo()
{
MoveTheMotor(800);
//this while blocks everything until the value is true
while (movementFinished == false) { await Task.Delay(1); }
//do something after the motor finished
}
Instead of using a boolean, you need a signal. In the blocking world, this would be a ManualResetEventSlim or similar. In the asynchronous world, this would be some form of TaskCompletionSource. As such:
private TaskCompletionSource movementFinished;
private void Reply(object sender, EventArgs e)
{
...
if (pcr[0] == 'M' && pcr[1] == 'F' && pcr[2] == '1')
{
movementFinished.TrySetResult();
}
}
public void MoveTheMotor(int position)
{
...
movementFinished = new TaskCompletionSource();
}
private async void Demo()
{
MoveTheMotor(800);
await movementFinished.Task;
//do something after the motor finished
}

C# BackgroundWorker.CancelAsync() vs DoWorkEventArgs.Cancel

I want to use the RunWorkerCompletedEventArgs.Cancelled value in my BackgroundWorker completed handler, but the documentation isn't clear how how BackgroundWorker.CancelAsync() and DoWorkEventArgs.Cancel (in the background worker do work handler) will each effect it. Are they functionally the same?
For example, is this...
private void _Worker_DoWork(object sender, DoWorkEventArgs e)
{
(sender as BackgroundWorker).CancelAsync();
}
...equivalent to this?
private void _Worker_DoWork(object sender, DoWorkEventArgs e)
{
e.Cancel = true;
}
Will the latter result in subsequent evaluations of CancellationPending() to evaluate as true? Also, if the background worker is cancelled externally (i.e. myBW.CancelAsync(); outside the do work handler), will e.Cancel = false cause an evaluation of CancellationPending() to be false?
BackgroundWorker.CancelAsync() will set the value of BackgroundWorker.CancellationPending to true, so the DoEvent code can check it.
DoWorkEventArgs.Cancel is there to tell RunWorkerCompleted Event that the process was Canceled. You are not supposed to use the result of a operation that was aborted or ended in Exception. Setting DoWorkEventArgs.Cancel to true will set RunWorkerCompletedEventArgs.Canceled to true. Wich will also force RunWorkerCompletedEventArgs.Result to throw an exception if accessed.
I have some pretty old example code from when I learned Multithrading with BGW. It should help you.
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
Personally I consider the BackgroundWorker class in a GUI to be good Multitasking "Training Wheels".
No, they are not the same. The "CancelAsync()" method runs from outside of the "backgroundworker"'s code. The "CancellationPending" can be check in the "DoWork" body and "e.Cancel" is set in "DoWork" to be used in the "Completed" method.
Please see the page below for more information:
(https://www.c-sharpcorner.com/uploadfile/mahesh/backgroundworker-in-C-Sharp/)

C# - Wait for 2s before doing next task

private void materialRaisedButton16_Click(object sender, EventArgs e)
{
foreach (var process in Process.GetProcessesByName("RobloxPlayerBeta"))
{
process.Kill();
}
materialRaisedButton16.Text = "Successfully killed process!";
// sleep for 2s WITHOUT freezing GUI
materialRaisedButton16.Text = "Click to kill process";
}
Hi, my code is above. I need the text of the button to change for 2s then change back to the original. How is this possible?
Thanks,
Tim
Implement like this
private async Task DelayTask()
{
await Task.Delay(2000); //2000 = 2sec
DoyourStuffHere();
materialRaisedButton16.Text = "Click to kill process";
}
And Call It Like This
private void materialRaisedButton16_Click(object sender, EventArgs e)
{
foreach (var process in Process.GetProcessesByName("RobloxPlayerBeta"))
{
process.Kill();
}
materialRaisedButton16.Text = "Successfully killed process!";
// sleep for 2s WITHOUT freezing GUI
Task taketime = this.DelayTask();
}
Not freezing the GUI requires some form of Mutlitasking. Possibly even Multithreading. Very strictly speaking calling a bunch of helper processes is a primitive form of Multithreading already. Possibly the oldest one, we invented just as we came off Cooperative Multitasking back in the days.
You have many Options to do Multitasking (inlcuding Multithreading) in .NET Async...await. Tasks. Threads. For beginners in Multithreading, I would advise BackgroundWorker generally. I wrote this little intro examples a few years back that I link often:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
Of course in your case, something much simpler could work: A baseline Timer. All you really want is a 2 second delay? Make a TImer set to 2 seconds, no repeat, start it in "materialRaisedButton16_Click". And let it's tick to the rest. All true multithreading really does is allow you to write the same stuff in slightly more readable form (with some tradeoffs for performance).
the easiest way would be:
foreach (var process in Process.GetProcessesByName("RobloxPlayerBeta"))
{
process.Kill();
}
materialRaisedButton16.Text = "Successfully killed process!";
// sleep for 2s WITHOUT freezing GUI
Task.Delay(2000).ContinueWith(()=>{
materialRaisedButton16.Text = "Click to kill process";
}, TaskScheduler.FromCurrentSynchronizationContext()); // this is to make it run in the UI thread again
You can use Timer.
On its tick event you update the text of the button back to the value u need.

How to find out if my Windows timer is running perfect and not causing any memory leak

I am calling two timers in one WinForm for different processes, both are using the same try-catch format. It works fine for the first few days, then starts to become slow. I can see some load on the server. Is my timer event correct? Is this event not putting much load?
In my try-catch, I only stop the timer if my code catches any exception. I doubt if not exception, and my time is not stopped. If I start the timer the second time, it will put more load or its just reset. Please give your valuable advice. Thanks a bunch in advance.
My code is below.
Timer 1 Tick:
private void timerMain_Tick(object sender, EventArgs e)
{
try
{
// Retrieve some status and Update
}
catch (Exception ex)
{
timerMain.Stop();
MessageBoxHelper.ShowException(LanguagePack.LanguageHelper.GetString(GlobalParameters.Language, "Error_ExecutionEx"), ex);
}
finally
{
timerMain.Start();
}
}
Timer 2 Tick:
private void timerWorkStep_Tick(object sender, EventArgs e)
{
try
{
// Retreive Some value from web proxy and set to label
}
catch (Exception ex)
{
timerWorkStep.Stop();
MessageBoxHelper.ShowException(LanguagePack.LanguageHelper.GetString(GlobalParameters.Language, "Error_ExecutionEx"), ex);
}
finally
{
timerWorkStep.Start();
}
}
I missed the problem but it was pointed out to me in the comments by the OP so here is my answer.
I wouldn't say you have exactly a memory leak because you are eventually releasing the memory and resources you are using, but as you pointed out in the comment, I would definitely agree that only stopping your timer in the catch and always starting it in the finally could certainly cause your tick to run more often that on a fixed interval. If you run the example below you will see what I mean.
Timer Running Many Ticks At Once Example
void Main()
{
_timer = new System.Timers.Timer(100);
_timer.Elapsed += Timer_Elapsed;
_timer.Start();
}
private void Timer_Elapsed(object sender, EventArgs e)
{
try
{
for (int i = 0; i < 15; i++)
{
Console.Write(i + " ");
Thread.Sleep(10);
}
Console.WriteLine();
}
catch (Exception ex)
{
_timer.Stop();
Console.WriteLine(ex.Message);
}
finally
{
_timer.Start();
}
}
private System.Timers.Timer _timer;
I use timers on a regular basis for most of my windows services and we have adopted the pattern of stopping the timer as the first thing in the tick and restarting it once the tick has finished. The problem with the code directly below it that it will not run every defined interval because of the time it takes to actually process
Timer Pattern
private void Timer_Elapsed(object sender, EventArgs e)
{
try
{
_timer.Stop();
//some work
}
catch(Exception ex)
{
}
finally
{
_timer.Start();
}
}
If you want more of a reliable run every hour at 5 after the hour, you will need to do something like this:
Timer Pattern on Reliable Interval
public class NextRunCalculator
{
public double CalculateTimeUntilNextRun()
{
List<int> processingMinutes = new List<int> { ServiceConstants.RunMinute5 };//this is a list of minutes during the hour you want the service to run
int t = int.MaxValue;
foreach (var processingMinute in processingMinutes)
{
var minutesRemaining = processingMinute - DateTime.Now.Minute;
if (minutesRemaining <= 0)
{
minutesRemaining = 60 + minutesRemaining;//change 60 to however often you want the timer to run
}
if (minutesRemaining < t)
{
t = minutesRemaining;
}
}
return TimeSpan.FromMinutes(t).TotalMilliseconds;
}
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
_timer.Stop();
//do work
_timer.Interval = _nextRunCalculator.CalculateTimeUntilNextRun();
_timer.Start();
}

How to pause for loop when pause button is clicked?

When I run the program and try to click the pause button, nothing happens. I am not sure how I can get this to work exactly. I have a bool variable called pause and pause is set to false. Once the pause button is clicked it should set that variable to true. Then the loop checks for that and should display a message to the user. Any help is greatly appreciated!
namespace Practice2
{
public partial class Form1 : Form
{
photocopier printer = new photocopier(500, 2500);
bool pause = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnStart_Click(object sender, EventArgs e)
{
if (checkText(txtNumberCopies.Text) == true)
{
int numberCopies = Convert.ToInt32(txtNumberCopies.Text);
int toner = Convert.ToInt32(lblTonerAmount.Text);
int paperCapacity = Convert.ToInt32(lblPaperAmount.Text);
if (toner <= 625 && paperCapacity <= 125)
{
txtMessage.Text = "Printer is low on Toner and Paper!";
}
else if (toner <= 625){
txtMessage.Text = "Printer Toner is low!";
}
else if (paperCapacity <= 125)
{
txtMessage.Text = "Printer Paper is low!";
}
else
{
txtMessage.Text = "Printing...";
txtMessage.Refresh();
for (int i = numberCopies; i != 0; i--)
{
int paper = Convert.ToInt32(lblPaperAmount.Text);
paper--;
if (paper == 480 || paper == 380 || paper == 400 || paper == 200)
{
MessageBox.Show("There is a paper Jam! Please remove the Jam and then hit the ok button to continue!", "Important Message", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
if (pause == true)
{
MessageBox.Show("Press the ok button when ready to continue", "Important Message", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
lblPaperAmount.Text = Convert.ToString(Convert.ToInt32(lblPaperAmount.Text) - 1);
lblTonerAmount.Text = Convert.ToString(Convert.ToInt32(lblTonerAmount.Text) - 1);
Thread.Sleep(1000);
}
txtMessage.Text = "Job is completed!";
}
}
}
private void btnAddPaper_Click(object sender, EventArgs e)
{
int paperAmount = Convert.ToInt32(lblPaperAmount.Text);
if (checkText(txtAddPaper.Text) == true && paperAmount <= 500)
{
lblPaperAmount.Text = Convert.ToString(paperAmount + Convert.ToInt32(txtAddPaper.Text));
}
else
{
txtMessage.Text = "Printer paper is at capacity!";
}
}
private bool checkText(string textBox)
{
if (textBox.Equals("") || textBox == null)
{
txtMessage.Text = "Please enter a value in the text box!";
return false;
}
return true;
}
private void btnReplaceToner_Click(object sender, EventArgs e)
{
lblTonerAmount.Text = Convert.ToString(printer.Toner);
}
private void btnPauseCancel_Click(object sender, EventArgs e)
{
pause = true;
}
}
}
The problem is that you're doing the work on the UI thread, so the UI thread is busy and can't process messages (e.g. button click). You need to do the work on a worker thread instead, using BackgroundWorker or Task.Run for instance.
A for loop is on the UI Thread so while the for loop is running you can't do anything with the UI. I suggest that you use a System.Windows.Forms.Timer to do the job. You set the interval to 1 and that will run pretty quickly, but not as quickly as a for loop, though. But interval = 1 is enough for you.
Let me show you:
Timer timer = new Timer () {Interval=1};
to create a new timer object.
enter
timer.Tick +=
in the constructer and press TAB twice and that should generate an event handler. Write the stuff you want to do in the event handler.
Call timer.Stop to pause the timer and timer.Start to start the timer.

Categories

Resources