Can I change a timer's interval without resetting it? - c#

I have a timer that is supposed to generate a Console.Beep at the rate of my Heart Rate in beats/minute. I am using the following simple formula to calculate the Timer.Interval in my C# timer:
ServerVarValues = new ws ServerVarValues();
this.timerHeartBeatSound = new System.Windows.Forms.Timer(this.components);
public class ServerVarValues
{
public int HR;
public DateTime LastHRUpdate;
public int SpO2;
public DateTime LastO2Update;
public double Temperature;
public DateTime LastTempUpdate;
}
//... I plug in my heart rate to calculate my new timer interval...
int tVal = (int)Math.Round((ws.HR / 60.0) * 1000,0);
timerHeartBeatSound.Interval = tVal;
// and with the new interval, the timer generates a Tick Event that runs this
private void PlayHeartBeatSound(object sender, EventArgs e)
{
Console.Beep();
}
I am reading my heart rate from a wearable. The problem is that every time that my HR changes, the timer is reset when I change the timer's interval. Thus, I hear hiccups often instead of a smooth changing heart rate sound.
Any ideas on how to avoid resetting the timer every time the heart rate changes?

Yes, this is by design. When you change the Interval property, and it is not the same, then the Timer resets itself. You cannot alter this behavior.
So what you must do is not update Interval. Not until the next Tick happens. Which is fine, the heart-beats happen quickly enough. You'll need another variable:
public class ServerVarValues {
public int NextInterval;
// etc...
}
And initialize it when you start the timer or update the heart-rate value:
...
int tVal = (int)Math.Round((ws.HR / 60.0) * 1000,0);
if (timerHeartBeatSound.Enabled) ws.NextInterval = tval;
else {
ws.NextInterval = 0;
timerHeartBeatSound.Interval = tval;
}
And in the Tick event handler you need to check if you have to make the new interval effective:
private void PlayHeartBeatSound(object sender, EventArgs e)
{
if (ws.NextInterval != 0) timerHeartBeatSound.Interval = ws.NextInterval;
ws.NextInterval = 0;
Console.Beep();
}

Try the following, except instead of "ASecond" use the number of milliseconds corresponding to the current heart rate. This will recalculate the number of milliseconds until the next beat, based on the current rate and do it for every beat. I hope it is smoother.
DateTime LastTime = DateTime.Now;
...
DateTime NextTime = LastTime + ASecond;
// Just in case we have gone past the next time (perhaps you put the system to sleep?)
if (DateTime.Now > NextTime)
NextTime = DateTime.Now + ASecond;
TimeSpan Duration = NextTime - DateTime.Now;
LastTime = NextTime;
timer1.Interval = (int)Duration.TotalMilliseconds;

Related

Use a Timer to increment a ProgressBar and show a count-down

I need some help in making a count-down Timer that also increments a ProgressBar.
I have a Button that generates a random time (expressed in minutes) that will increment a ProgressBar, to use as a form of loader.
When the ProgressBar increments its value, I would like to show, in a Label, the time remaining.
Here is my code:
int RandomNumber;
int MinutesElapsed;
private void button1_Click(object sender, EventArgs e)
{
Random random = new Random((int)DateTime.Now.Ticks);
RandomNumber = random.Next(1, 121); // at (x, x) there is a limit from x to x+1
MinutesElapsed = 0;
QuestTimer1.Start(); // starts the timer
}
private void QuestTimer1_Tick(object sender, EventArgs e)
{
MinutesElapsed++;
double minutes = MinutesElapsed / 1.2;
progressBar1.Value = (int)minutes;
}
This may seem counterintuitive, but when implementing a "timer," there is no need for an actual timer to keep the time. You certainly don't need to set a timer and increment a variable every second-- your system clock already does that! Your program just needs to know the start time, end time, and current time, and at any given moment, in can compute the progress % and the time remaining.
When you start the process, take note of the current time and store the target completion time.
DateTime _startTime;
DateTime _endTime;
private void StartTimer_Click(object sender, EventArgs e)
{
_startTime = DateTime.Now;
var duration = random.Next(1, 121);
_endTime = _startTime.AddSeconds(duration);
}
Then when you wish to know the time remaining or progress, compute them:
public int SecondsRemaining => (_endTime - DateTime.Now).TotalSeconds;
public double PercentComplete => ((double)(_endTime - DateTime.Now).TotalMilliseconds) / (_endTime - _startTime).TotalMilliseconds;
You can call either of these properties at any time and they will always give you the percent complete and time remaining accurately. You don't need a separate timer at all!
However, you probably want to update the display every now and then. You need a timer for that, but it can run at any interval, and it's easy to write:
void Timer_Tick(object sender, EventArgs e)
{
this.TimeRemainingLabel.Text = SecondsRemaining.ToString() + " seconds";
this.ProgressBar.Value = (int)(PercentComplete * 100);
}
A few notes that may be useful:
Your Button (you should give it a proper name) can be used to start/stop the Timer and generate a new random number, representing minutes, using a Random object (declared as a static Field - not so important here, but because of its internal functionality, it's more reliable this way).
The generated random value initializes a TimeSpan object (totalTime) that store the total time, to keep as reference.
You can set a measure of time directly in the TimeSpan constructor: if the values - expressed in minutes here - overflows the normal measure (60 minutes in this case), the TimeSpan class computes the correct value (i.e., if you set the Minutes component to 90 in the constructor, the TimeSpan will be automatically compute 1 Hour and 30 minutes).
Another TimeSpan (minutesElapsed) will store the time elapsed.
The Timer's Interval is set to 1000ms.
The ProgrssBar should keep its standard range of values: (0 - 100).
You can see it as the percentage of completion. Each time the Timer ticks, we'll adapt the difference between the total time and the elapsed time, squeezing it in this range of values .
TimeSpan totalTime;
TimeSpan minutesElapsed;
static Random rnd = new Random();
private void button1_Click(object sender, EventArgs e)
{
QuestTimer1.Stop();
minutesElapsed = new TimeSpan(0, 0, 0);
// 0 Hours, N Minutes, 0 Seconds
totalTime = new TimeSpan(0, rnd.Next(1, 121), 0);
lblTimeLeft.Text = totalTime.ToString(#"hh\:mm\:ss");
QuestTimer1.Start();
}
The Timer ticks - 1 second has passed - so increment the elapsed time by one second and calculate the difference between the total time and the current number of elapsed seconds.
TimeSpan objects support common operators to perform addition and subtraction and provide different properties to evaluate their value in Ticks, Milliseconds, Seconds etc.
Each second, a Label (here named lblTimeLeft) is updated, showing how many seconds are left (acts as a count-down), while the ProgressBar increments it's Value.
When minutesElapsed >= totalTime, the Timer is stopped.
private void QuestTimer1_Tick(object sender, EventArgs e)
{
minutesElapsed += TimeSpan.FromSeconds(1);
lblTimeLeft.Text = (totalTime - minutesElapsed).ToString(#"hh\:mm\:ss");
progressBar1.Value = (int)(minutesElapsed.TotalMinutes / totalTime.TotalMinutes * 100D);
if (minutesElapsed.TotalMinutes >= totalTime.TotalMinutes) {
QuestTimer1.Stop();
}
}

C# Timer Interval Every 24 Hours

I'm following a tutorial on how to create a Windows Service that will send automated emails on my web server. I've got the tutorial working, however, the example code executes the service every 60mins, instead, I'd like the service executed once a day, every 24 hours, say at 9am every morning.
Below is the sample code
private Timer scheduleTimer = null;
private DateTime lastRun;
private bool flag;
public StarEmailService()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("EmailSource"))
{
System.Diagnostics.EventLog.CreateEventSource("EmailSource", "EmailLog");
}
eventLogEmail.Source = "EmailSource";
eventLogEmail.Log = "EmailLog";
scheduleTimer = new Timer();
scheduleTimer.Interval = 1 * 5 * 60 * 1000;
scheduleTimer.Elapsed += new ElapsedEventHandler(scheduleTimer_Elapsed);
}
protected override void OnStart(string[] args)
{
flag = true;
lastRun = DateTime.Now;
scheduleTimer.Start();
eventLogEmail.WriteEntry("Started");
}
protected void scheduleTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (flag == true)
{
ServiceEmailMethod();
lastRun = DateTime.Now;
flag = false;
}
else if (flag == false)
{
if (lastRun.Date < DateTime.Now.Date)
{
ServiceEmailMethod();
}
}
}
The line scheduleTimer.Interval = 1 * 5 * 60 * 1000; appears to be the code which sets the interval to 60mins, however, am unsure what would I need to amend this to in order to make it run every 24 hours at 9am?
Any advice would be appreciated.
Thanks.
You have couple of options:
Use Quartz .NET
Use Windows Schedule tasks
Don't rely on other timers as they will get out of sync in (near) future.
It is probably better to set your timer to smaller interval, and check the system time similar to your code does now. This code should send an email once a day on or after 9 am. The smaller your timer interval, the more accurate it will be to 9 am. For example, if you keep the timer interval at 60 minutes, the service will check the system time once an hour and the email will be sent between 9am and 10am. If you set the timer to 10 minutes, the service will check the system time once every tem minutes and send the email between 9:00 and 9:10am.
This method does not go out of sync over time, because it uses the system clock, not the timer interval to know when to fire.
Remove lastRun DateTime field and all references to it. Remove flag field and references. Add a DateTime field called nextRun:
private DateTime nextRun = DateTime.MinValue;
Add a function GetNextRun:
private static DateTime GetNextRun(DateTime lastRun)
{
var next = lastRun.AddDays(1);
return new DateTime(next.Year, next.Month, next.Day, 9, 0, 0);
}
Change ScheduleTimer Elapsed to:
protected void scheduleTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (DateTime.Now < nextRun) return;
nextRun = GetNextRun(DateTime.Now);
ServiceEmailMethod();
}

Doing an event for half the time of the first timer?

So What I'm trying to do is basically.. Have 2 events, Lets called these Event 1 and Event 2, When Event 1 starts, a timer becomes activated, in which when it ends (the amount varies every time), It stores the elapsed time into a double variable
(Up to this stage it is all coded fine)
But my problem is.. I want Event 2 to run for half of the time that event 1 did.. And therefore I decided to store this value in another double variable, and setting the value divided by 2..
From here I'm wanting to then set up a sort of 'Countdown timer' So that once the amount of time has expired from the double variable with half the time stored, the event will stop.
Here is what I have so far..
//Creation of timer
Stopwatch Timer1 = new Stopwatch();
//Start the timer
Timer1.Start();
//Event 1 carries out here.. (Ain't going to bore you with this code)
//(Once ended) (ends once a condition is matched.. but cutting it short)..
//Timer1.Stop();
//Store amount in a double variable..
double dfull = Timer1.ElapsedMilliseconds;
//Half the amount in variable dhalf
double dhalf;
dhalf = dfull / 2;
//From here I want EVENT 2 to perform for the time stored in dC2B
You can do this using something like the following
System.Timers.Timer readyUpTimer = new System.Timers.Timer(100);
readyUpTimer.Elapsed -= readyUpTimer_Elapsed;
readyUpTimer.Elapsed += readyUpTimer_Elapsed;
DateTime readyUpInitialised = DateTime.Now;
readyUpTimer.Start();
Then in your event handler
void readyUpTimer_Elapsed(object sender, ElapsedEventArgs e)
{
TimeSpan elapsed = DateTime.Now - this.readyUpInitialised;
if (elapsed.TotalMilliseconds > dhalf)
{
readyUpTimer.Stop();
readyUpTimer.Dispose();
// Do other things...
}
}
I hope this helps.

Accurate method to track time

For my application, I've to track the time change, to "smooth" the time change.
A system time change can occurs for several reasons:
The user change its system time
The OS NTP Server updates the local time
...
So actually we have a "TimeProvider", which provide to the whole application the current time.
The goal is to detect if a time shift occurs and correct our local time smoothly(like, if we have a "time jump" of one hour, correct 100ms every second until this is fully corrected).
Here is basically what I've to provide the time(please note that currently I absolutely don't smooth the time change, but not my current issue)
internal class TimeChange : IDisposable
{
private readonly Timer _timer;
private readonly Stopwatch _watch = new Stopwatch();
private DateTime _currentTime;
public DateTime CurrentTime
{
get { return _currentTime + _watch.Elapsed; }
}
public TimeChange()
{
_timer = new Timer(1000);
_timer.Elapsed += OnTimerElapsed;
_timer.Start();
_watch.Start();
_currentTime = DateTime.UtcNow;
}
public void Dispose()
{
_timer.Stop();
_timer.Elapsed -= OnTimerElapsed;
}
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
DateTime currentTime = DateTime.UtcNow;
TimeSpan timeDerivation = currentTime - _currentTime - _watch.Elapsed;
_watch.Restart();
_currentTime = currentTime;
Console.WriteLine("Derivation: " + timeDerivation.TotalMilliseconds + "ms");
}
}
But when doing some test, I noticed that I've differences even without doing anything on my local time. Not huge differences(<1ms), but still:
Press enter to stop
Derivation: -0.1367ms
Derivation: 0.9423ms
Derivation: 0.0437ms
Derivation: 0.0617ms
Derivation: 0.0095ms
Derivation: 0.0646ms
Derivation: -0.0149ms
And this is derivation for 1 second, if I just replace the 1000ms by 10000ms, I quickly have a time derivation between 1ms and 0.5ms.
So my question(Finally :P):
Why between two Utc.DateTime gave me so much differences? They are both based on clock Tick no?
Isn't there a way to get this time shift more precisely ?
No they are not both based on clock tick. Stopwatch maybe either high or low res. If low res, then it uses DateTime.UtcNow underneith. Unfortuantely you cannot choose if it's high or low, so:
Create own "Stopwatch" that always uses DateTime.UtcNow underneith.
EDIT
That's a stupid suggestion in (2.), you obviously need to avoid DateTime.UtcNow as that's what you are trying to correct. I suggest you look at working in ticks, by which I mean 1/10000 of a second, to match high-res Stopwatch. This is because TimeSpan is only accurate to 1/1000 of a second.
Number 1. in more detail:
Stopwatch uses this method:
public static long GetTimestamp()
{
if (!Stopwatch.IsHighResolution)
{
DateTime utcNow = DateTime.UtcNow;
return utcNow.Ticks; //There are 10,000 of these ticks in a second
}
else
{
long num = (long)0;
SafeNativeMethods.QueryPerformanceCounter(out num);
return num; //These ticks depend on the processor, and
//later will be converted to 1/10000 of a second
}
}
But like I say, IsHighResolution appears to be not settable and as a static applies system wide anyway, so write your own.

Reset the count of inactive time in C#

I found this post and created a class that used it to detect inactive time and it works great. I set it for one minute and after one minute I can get it to "do stuff". What I am trying to do is only do something every "x" minutes of inactive time; i.e. every 5 minutes do this if things have been inactive and do not repeat again 'til X time has elapsed.
Now, I could set my timer to fire every 5 minutes instead of every second, but I would like to be able to "reset" the count of inactive time instead. Any suggestions?
This is for using the DispatchTimer in C# and WPF.
Just create a class level variable, increment it on your timer, and reset it when you get activity. Create a timer, say tmrDelay with an increment of 10000 milliseconds, and a button, btnActivity to reset the count, and do this:
private int tickCount = 0;
private const int tick_wait = 30;
private void tmrDelay_Tick(object sender, EventArgs e)
{
tickCount++;
if (tickCount > tick_wait)
{
DoSomething();
tickCount = 0;
}
}
private void btnActivity_Click(object sender, EventArgs e)
{
tickCount = 0;
}
It sounds like you want something like the following:
static DispatcherTimer dt = new DispatcherTimer();
static LastInput()
{
dt.Tick += dt_Tick;
}
static void dt_Tick(object sender, EventArgs e)
{
var timer = (DispatcherTimer)sender;
var timeSinceInput = TimeSpan.FromTicks(GetLastInputTime());
if (timeSinceInput < TimeSpan.FromMinutes(5))
{
timer.Interval = TimeSpan.FromMinutes(5) - timeSinceInput;
}
else
{
timer.Interval = TimeSpan.FromMinutes(5);
//Do stuff here
}
}
This will poll every 5 minutes to see if the system has been idle for 5 minutes or more. If it's been idle for less than 5 minutes it will adjust the time so that it will go off again at exactly the 5 minute mark. Obviously then if there has been activity since the timer was set it will be adjusted again so it will always aim for 5 minutes of idleness.
If you really want to reset the active time then you will actually need to trigger some activity either by moving the mouse or sending a keypress

Categories

Resources