Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I was struggling with implementation of a timer more than i should so i have decided to post a question here.
This is my class:
public static class CurrentPoint
{
public static Single X { get; set; }
public static Single Y { get; set; }
public static Single Z { get; set; }
public static long ID { get; set; }
public static float CalcVar { get; set; }
public static float CalcStatic { get; set; }
public static bool StartTimeOut()
{
return true;
}
}
I should have a method implemented in it StartTimeOut(). StartTimeOut() will be called from another class when some other method will execute.
In StartTimeOut() i should have a timer that would check if CalcVar will change in next 30 seconds.
Now if it will, i will receive a TRUE from my StartTimeOut() and timer should exit if not StartTimeOut() will return a false.
This check for CalcVar will be done under same CurrentPoint.ID. That means if ID changes durring timer checks timer should exit and StartTimeOut() would return a TRUE.
There should also be a check if timer is already running and again STOP timer if CalcVar reaches 0 under 30 seconds under same ID
and StartTimeOut() again returns TRUE.
I hope i haven't complicated this problem too much.
I created a small sample, do understand that whenever you call this function it will remain in the while loop as long as it is running. Perhaps you should call the StartTimeOut() function from inside another thread...
//do not forget
using System.Timers;
private Timer _timer;
private static long _id;
private static bool _returnValue;
private static int _achievedTime;
public static bool StartTimeOut()
{
//set your known values (you need to check these values over time
_id = ID;
_achievedTime = 0;
_returnValue = true;
//start your timer
Start();
while(_timer.Enabled)
{
//an empty while loop that will run as long as the timer is enabled == running
}
//as soon as it stops
return _returnValue;
}
//sets the timer values
private static void Start()
{
_timer = new Times(100); //describes your interval for every 100ms
_timer.Elapsed += HandleTimerElapsed;
_timer.AutoReset = true; //will reset the timer whenever the Elapsed event is called
//start your timer
_timer.Enabled = true; //Could also be _timer.Start();
}
//handle your timer event
//Checks all your values
private static void HandleTimerElapsed(object sender, ElapsedEventArgs e)
{
_achievedTime += _timer.Interval;
//the ID changed => return true and stop the timer
if(ID!= _id)
{
_returnValue = true;
_timer.enabled = false; //Could also be _timer.Stop();
}
if(CalcVar == 0) //calcVar value reached 0 => return false and stop the timer
{
_returnValue = false;
_timer.Enabled = false;
}
if(_achievedTime = 30000) //your 30s passed, stop the timer
_timer.Enabled = false;
}
this is what you call simply code do not test!
Related
When the command .checked is not done for 15 minutes, I would like a timer to spam a message every minute. Right now I have it set to some ridiculously fast amount for testing purposes. Check() is called when .checked is done.
I tried doing something like this:
public static void Check()
{
MinecraftClient.ChatBots.DiscordWallTimer.TimerOn = false;
Program.StartTimer();
}
public static System.Timers.Timer EnableTimer;
public static Task StartTimer()
{
EnableTimer = new Timer()
{
Interval = 15 * 1000,
AutoReset = false,
Enabled = true
};
EnableTimer.Elapsed += OnTimerTicked;
return Task.CompletedTask;
}
public static void OnTimerTicked(object sender, ElapsedEventArgs e)
{
MinecraftClient.ChatBots.DiscordWallTimer.TimerOn = true;
MinecraftClient.ChatBots.DiscordWallTimer.StartTimer();
}
And having this in my timer class:
public class DiscordWallTimer
{
public static bool TimerOn;
public static System.Timers.Timer wallTimer;
internal static Task StartTimer()
{
Console.WriteLine("Wall timer has started");
wallTimer = new Timer()
{
Interval = 5*1000,
AutoReset = true,
Enabled = TimerOn
};
wallTimer.Elapsed += OnTimerTicked;
return Task.CompletedTask;
}
private static void OnTimerTicked(object sender, ElapsedEventArgs e)
{
if (TimerOn == true)
{
Program.SendAlertDiscord();
}
}
}
Only problem is that the alert still sends if .check has been done recently (I think for testing I set it to 15 seconds.)
Thanks in advance!
I'd suggest using a library designed to do this kind of thing rather than mucking around with timers - which can be hard.
Try Microsoft's Reactive Framework:
private static Subject<Unit> _check = new Subject<Unit>();
private static IDisposable _subscription = null;
private static void SetUp()
{
_subscription =
_check
.Select(x => Observable.Timer(TimeSpan.FromMinutes(15.0), TimeSpan.FromMinutes(1.0)))
.Switch()
.Subscribe(x => Program.SendAlertDiscord());
}
public static void Check()
{
_check.OnNext(Unit.Default);
}
That's it. Just call SetUp once and then whenever you call Check() you'll start a 15 minute timer that then spams every minute. Any call to Check() will reset the timer automatically.
And call _subscription.Dispose(); if you want to shut down the code.
Just NuGet "System.Reactive" to get the bits and then add using System.Reactive.Linq; to your code.
If you have any threading issues let me know any I'll help get the code to marshall to the right thread for you.
in fact you dont stop the timer DiscordWallTimer, you just do some modifications and all will be ok and
You dont need the variable TimerOn
public static void Check()
{
// the first time walltimer doesnt exist
if (MinecraftClient.ChatBots.DiscordWallTimer.wallTimer != null)
{
MinecraftClient.ChatBots.DiscordWallTimer.wallTimer.Stop();
}
Program.StartTimer();
}
public class DiscordWallTimer
{
public static System.Timers.Timer wallTimer;
internal static Task StartTimer()
{
Console.WriteLine("Wall timer has started");
wallTimer = new Timer()
{
Interval = 5*1000,
AutoReset = true,
Enabled = true // <- keep True
};
wallTimer.Elapsed += OnTimerTicked;
return Task.CompletedTask;
}
private static void OnTimerTicked(object sender, ElapsedEventArgs e)
{
Program.SendAlertDiscord();
}
}
I have a event in my code that can possibly get fired multiple times a second at some moment.
However I would like to implement a way to make that method wait 500ms before really firing, if the method gets called again before those 500ms are over, reset the timer and wait for 500ms again.
Coming from javascript I know this is possible with setTimeout or setInterval. However I'm having trouble figuring out how I could implement such a thing in C#.
You could use a System.Timers.Timer wrapped in a class to get the behaviour you need:
public class DelayedMethodCaller
{
int _delay;
Timer _timer = new Timer();
public DelayedMethodCaller(int delay)
{
_delay = delay;
}
public void CallMethod(Action action)
{
if (!_timer.Enabled)
{
_timer = new Timer(_delay)
{
AutoReset = false
};
_timer.Elapsed += (object sender, ElapsedEventArgs e) =>
{
action();
};
_timer.Start();
}
else
{
_timer.Stop();
_timer.Start();
}
}
}
This can then be used in the following manner:
public class Program
{
static void HelloWorld(int i)
{
Console.WriteLine("Hello World! " + i);
}
public static void Main(string[] args)
{
DelayedMethodCaller methodCaller = new DelayedMethodCaller(500);
methodCaller.CallMethod(() => HelloWorld(123));
methodCaller.CallMethod(() => HelloWorld(123));
while (true)
;
}
}
If you run the example, you will note that "Hello World! 123" is only displayed once - the second call simply resets the timer.
If you need to reset the timer when the method is called again, consider looking at the ManualResetEvent class:
https://msdn.microsoft.com/en-us/library/system.threading.manualresetevent(v=vs.110).aspx
You can use this to notify one or more waiting threads that an event has occurred.
You can use Thread.Sleep() with locking
private object locking = new object();
lock (locking )
{
Thread.Sleep(500);
//Your code to run here
}
https://msdn.microsoft.com/en-us/library/system.threading.thread.sleep(v=vs.110).aspx
Just writen super simple class with System.Threading.Thread; With a little different approach Usage.
var delayedCaller = new DelayedTimeout(() => HelloWorld(123), 500, false);
delayedCaller.ResetTimer();
delayedCaller.ResetTimer();
Currently, you can do it very simple with the following class
public class DelayedTimeout
{
readonly Timer _timer;
readonly int _timeoutMs;
public DelayedTimeout(TimerCallback callback, int timeoutMs, bool startNow)
{
_timeoutMs = timeoutMs;
// Should we start now
var currentTimeoutMs = startNow ? _timeoutMs : Timeout.Infinite;
_timer = new Timer(callback, null, currentTimeoutMs, Timeout.Infinite);
}
// Constructor overloading
public DelayedTimeout(Action callback, int timeoutMs, bool startNow) :
this(delegate (object? obj) { callback.Invoke(); }, timeoutMs, startNow)
{}
public void ResetTimer()
{
_timer.Change(Timeout.Infinite, Timeout.Infinite); // Stop the timer
_timer.Change(_timeoutMs, Timeout.Infinite); // Stop the timer
}
}
I tell the Timer to start in the constructor. It starts, but when it reaches its Timer.Elapsed event it only runs the first if statement in the method. I've checked to see if isWatching is true, and it is, but it still skips it entirely. It doesn't even reach the if(isWatching) line.
Code:
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public SessionManager SM { get; private set; }
public MainWindow()
{
SM = new SessionManager();
SM.NewDayEvent += SplitSession;
///code
}
}
SessionManager.cs (Some variables have been omitted from this post):
public class SessionManager : INotifyPropertyChanged
{
public delegate void NewDayEventHandler(object sender, EventArgs ea);
public event NewDayEventHandler NewDayEvent;
private bool _isWatching;
private Timer _timer;
private bool isWatching
{
get
{
return _isWatching;
}
set
{
_isWatching = value;
if (!_isWatching)
{
_clockWatch.Stop();
}
else
{
_clockWatch.Start();
}
}
}
#endregion
public SessionManager()
{
_clockWatch = new Stopwatch();
_timer = new Timer(1000);
_timer.Elapsed += timerElapsed;//focus on this here
_isWatching = false;
current_time = new DateTime();
CurrentTime = DateTime.Now;
_timer.Start();
}
public void timerElapsed(object sender, ElapsedEventArgs e)
{
CurrentTime = DateTime.Now;
if (CurrentTime.TimeOfDay == TimeSpan.Parse("9:32 AM") && NewDayEvent != null)
{
NewDayEvent(this, new EventArgs());
}
if (isWatching)
{
if (CurrentSession != null)
{
//update the timespent variable of the current timeEntry
if (CurrentSession.currentTimeEntry != null)
{
CurrentSession.currentTimeEntry.TimeSpent = _clockWatch.Elapsed;
calculateTotalTime();
CalculateFilteredTimeSpent();
}
}
}
}
}
You aren't using the correct format while calling TimeSpan.Parse(). The right way to do what you are trying to is:
TimeSpan.Parse("9:32")
Your current code snippet throws a System.FormatException:
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
However for what you are trying to achieve, trigger an action once every day at a particular time, the above method might not be the best since the chances of it succeeding are very less. The timer will run every 1000 ms and then return the current time of day which has milliseconds included. So the timer elapsed event could be called at 9:32.0001 and might never pass the condition. A better alternative is probably:
if (CurrentTime.TimeOfDay >= TimeSpan.Parse("9:32") && NewDayEvent != null)
This will trigger more than once after that time has elapsed, so you could add a flag which keeps track of what day the last event was processed.
Alternatively you could also look at ScheduleAction in .NET 4.5 or some of the solutions here.
I have this code in my Form1:
public partial class Form1 : Form
{
public static int hours;
public static int minutes;
public static int seconds;
FinishGate finishgate = new FinishGate();
public Form1()
{
InitializeComponent();
txtHours.MaxLength = 2;
txtMinutes.MaxLength = 2;
txtSeconds.MaxLength = 2;
lblFinished.Text = Convert.ToString(gate.Total);
}
private void btnFinish_Click(object sender, EventArgs e)
{
hours = Convert.ToInt32(txtHours.Text);
minutes = Convert.ToInt32(txtMinutes.Text);
seconds = Convert.ToInt32(txtSeconds.Text);
lblFinished.Text = Convert.ToString(gate.Total);
// Check if a runner has been selected
if (lstRunners.SelectedIndex > -1)
{
// Obtain selected runner
Runner selectedRunner = (Runner)lstRunners.SelectedItem;
// If runner hasn't finished
if (selectedRunner.HasFinished == false)
{
// Call the method in FinishGate class to process the runner
FinishGate.ProcessRunner(selectedRunner);
}
else
{
// Runner has finished / been processed so increase the total that have completed the climb by one
finishgate.Total++;
}
}
}
}
Here is the FinishGate.cs:
class Gate
{
private int total;
public int Total
{
get { return total; }
set { total = value; }
}
public static void ProcessRunner(Runner selectedRunner)
{
}
}
What I want to happen is that when a runner in the listbox is selected and the Process button is clicked, the boolean hasFinished in ProcessRunner is changed to true and the Total integer is increased by one, which then updates the lblFinished to also increase by one, but I can't get it to work.
My two main issues are: I'm not sure what the code would be in ProcessRunner() to say if hasFinished == false change it to true, else leave the boolean the way it is. The other issue is getting the lblFinished to update accordingly when the integer increments.
Any advice over where I'm going wrong and how to prevent this in the future would be great.
You could try this:
Put this line
lblFinished.Text = Convert.ToString(gate.Total);
at the end of the btnFinish_Click() event instead of the beginning. This way it will update after ProcessRunner() is run.
Also, add this to your ProcessRunner() event in your Gate class:
if (selectedRunner.hasFinished == false)
{
selectedRunner.hasFinished = true;
}
//You don't need to do anything if it isn't false.
I hope this is of use to you. Let me know if I need to be more specific.
I have a WPF user control called TimerUserControl where contains a timer. And I have another user control where show questions, this one has a NextQuestion function.
The timer has 2 minutes like an interval, and I'd like to invoke the NextQuestion function when it has done. I think I have to use delegates, but I'm not sure.
UPDATE 1:
public partial class TimeUserControl : UserControl
{
public int _totalSeconds;
public int _secondsRemaining;
public DispatcherTimer timerSecondsLeft;
public TimeUserControl()
{
InitializeComponent();
timerSecondsLeft = new DispatcherTimer();
timerSecondsLeft.Tick += new EventHandler(timerSecondsLeft_Tick);
timerSecondsLeft.Interval = new TimeSpan(0, 0, 1);
}
public bool TimesUp
{
get;
set;
}
public void SetSeconds(int seconds)
{
timerSecondsLeft.Stop();
if (seconds == 0)
{
TimeTextBlock.Text = "There's no time! Hurray";
}
else
{
_totalSeconds = seconds;
_secondsRemaining = seconds;
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _totalSeconds);
timerSecondsLeft.Start();
}
}
public void timerSecondsLeft_Tick(object sender, EventArgs e)
{
_secondsRemaining--;
if (_secondsRemaining <= 0)
{
timerSecondsLeft.Stop();
TimesUp = true;
TimeTextBlock.Text = "Time's up. Press Enter to next problem.";
// HERE WILL INVOKE NEXTQUESTION FUNCTION
}
else
{
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _secondsRemaining);
}
}
}
Look in the code, the comment is this possible ussing delegates?
So you need to do a few things. You have to add some code to you're user control.
// Declare this outside your usercontrol class
public delegate void TimerExpiredEventHandler(object sender, EventArgs e);
This is what needs to be added to your code for the user control.
public partial class TimerUserControl : UserControl
{
public event TimerExpiredEventHandler Expired;
public void OnExpired(EventArgs e)
{
if (Expired != null)
Expired(this, e);
}
public void timerSecondsLeft_Tick(object sender, EventArgs e)
{
_secondsRemaining--;
if (_secondsRemaining <= 0)
{
timerSecondsLeft.Stop();
TimesUp = true;
TimeTextBlock.Text = "Time's up. Press Enter to next problem.";
// Fire the event here.
OnExpired(EventArgs.Empty);
}
else
{
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _secondsRemaining);
}
}
}
Now you need to subscribe to this event inside whatever is calling this usercontrol in the first place.
public partial class ParentForm : Form
{
private void ParentForm_Load(object sender, EventArgs e)
{
var timer = new TimerUserControl();
//Subscribe to the expired event that we defined above.
timer.Expired += new EventArgs(Timer_Expired);
}
public void Timer_Expired(object sender, EventArgs e)
{
//Handle the timer expiring here. Sounds like you are calling another function, so do that here.
}
}
Use the TreeHelper to hunt up the tree for a shared Parent and then down the tree for the User Control you want. Something like this pseudo code:
this.Timer = new System.Windows.Threading.DispatcherTimer
{
Interval = new TimeSpan(0, 0, 1)
};
this.Timer.Tick += (s, e) =>
{
var _Control = s as MyFirstControl;
var _Other = LogicalTreeHelper.GetChildren(_Control.Parent)
.Cast<FrameworkElement>().Where(x => x.Name == "FindIt")
.First<MySecondControl>();
_Other.DoMethod();
};
Best of luck!
i would probably break out the functionality of the timer control here; something like this (note: i am writing this on-the-fly so let me know if it doesn't work as-is, and i will help correct any issues):
// a simple delegate to report the amount of time remaining
// prior to the expiration of the major tick interval; zero
// indicates that this major tick has elapsed.
public delegate void delegateMajorMinorTimerTick
(
int TimeRemaining_sec, ref bool CancelTimer
);
// you could use milliseconds for the interval settings to get
// better granularity, or you could switch to setting the major
// interval instead, however that approach would require a bit
// more checking to make sure the control has sane settings.
public class MajorMinorTimer
{
// this sets the interval in seconds between the
// "minor" ticks used for intermediate processing
// these are the "inner" intervals of the timer
private int myMinorTickInterval_sec;
public int MinorTickInterval_sec
{
get { return myMinorTickInterval_sec; }
}
// this sets the number of minor ticks between the
// expiration of the major interval of the timer.
// the "outer" interval of the timer
private int myMinorTicksPerMajorTick;
public int MinorTicksPerMajorTick
{
get { return myMinorTicksPerMajorTick; }
}
public MajorMinorTimer
(
int parMinorTickInterval_sec,
int parMinorTicksPerMajorTick
)
{
MinorTickInterval_sec = parMinorTickInterval_sec;
MinorTicksPerMajorTick = parMinorTicksPerMajorTick;
}
private DispatcherTimer myBackingTimer;
private int myMinorTickCount;
public void Start()
{
// reset the minor tick count and start the dispatcher
// timer with some reasonable defaults.
myMinorTickCount = 0;
myBackingTimer =
new DispatcherTimer
(
TimeSpan.FromSeconds(MinorTickInterval_sec),
DispatcherPriority.Normal,
new EventHandler(myBackingTimer_Tick),
Dispatcher.CurrentDispatcher
);
myBackingTimer.Start();
}
public event delegateMajorMinorTimerTick onTick;
private bool FireMajorMinorTimerTick(int TimeRemaining_sec)
{
// allows the timer sink to cancel the timer after this
// call; just as an idea, also could be handled with a
// call to Stop() during the event, but this
// simplifies handling a bit (at least to my tastes)
bool CancelTimer = false;
if (onTick != null)
onTick(TimeRemaining_sec, ref CancelTimer);
return CancelTimer;
}
private void myBackingTimer_Tick(object Sender, EventArgs e)
{
// since we are using a DispatchTimer with settings that should
// do not suggest the possibility of synchronization issues,
// we do not provide further thread safety. this could be
// accomplished in the future if necessary with a lock() call or
// Mutex, among other methods.
++myMinorTickCount;
int TicksRemaining = myMinorTickCount - MinorTicksPerMajorTick;
bool Cancel =
FireMajorMinorTimerTick(TicksRemaining * MinorTickInterval_sec);
if (TicksRemaining == 0)
myMinorTickCount = 0;
if (Cancel)
Stop();
}
public void Stop()
{
myBackingTimer.Stop();
}
}
then, assuming, say, a Quiz control, the timer is used like so:
public void QuestionTimerSetup()
{
// sets up a timer to fire a minor tick every second
// with a major interval of 5 seconds
MajorMinorTimer timerQuestion = new MajorMinorTimer(1, 5);
timerQuestion.onTick +=
new delegateMajorMinorTimerTick(QuestionControl_QuestionTimerTick);
}
// ...
public void QuestionControl_OnTick(int TimeRemaining_sec, ref bool CancelTimer)
{
if (TimeRemaining_sec > 0)
{
tblockQuizStatus.Text =
string.Format("There are {0} seconds remaining.", TimeRemaining_sec);
}
else
{
// just for an example
if (NoMoreQuestions)
{
CancelTimer = true;
HandleEndOfQuiz();
tblockQuizStatus.Text =
"Time's up! The quiz is complete!";
}
else
{
tblockQuizStatus.Text =
"Time's up! Press Enter to continue to the next problem.";
}
}
}
another option (rather than, or in addition to, events) in implementing this might be to add an Action taking the time remaining in the major interval for the minor interval action, an Action for the major interval action, and a Func<bool> that checks the stop condition, allowing the user to perform the desired actions in that way. like this:
public class MajorMinorTimer
{
public MajorMinorTimer
(
int parMinorTimerInterval_sec,
int parMinorTicksPerMajorTick,
Action<int> parMinorTickAction,
Action parMajorTickAction,
Func<bool> parShouldStopFunc
)
{
myMinorTimerInterval_sec = parMinorTimerInterval_sec;
myMinorTicksPerMajorTick = parMinorTicksPerMajorTick;
myMinorTickAction = parMinorTickAction;
myMajorTickAction = parMajorTickAction;
myShouldStopFunc = parShouldStopFunc;
}
private Action<int> myMinorTickAction;
private Action myMajorTickAction;
private Func<bool> myShouldStopFunc;
private void myBackingTimer_OnTick()
{
++myMinorTickCount;
int TicksRemaining = myMinorTickCount - MinorTicksPerMajorTick;
if (TicksRemaining == 0)
myMajorTickAction();
else
myMinorTickAction(TicksRemaining * MinorTickInterval_sec);
bool Cancel = myShouldStopFunc();
if (TicksRemaining == 0)
myMinorTickCount = 0;
if (Cancel)
Stop();
}
}
and then in the quiz code instead of hooking up the event do something like:
public void QuestionTimerSetup()
{
MajorMinorTimer timerQuestion =
new MajorMinorTimer
(
1,
5,
// major interval action
(SecsRemaining) =>
{
tblockQuizStatus.Text =
string.Format
(
"There are {0} seconds remaining.", SecsRemaining
);
},
// minor interval action
() =>
{
if (NoMoreQuestions)
{
tblockQuizStatus.Text =
"Time's up! This completes the quiz!";
HandleEndOfQuiz();
}
else
{
tblockQuizStatus.Text =
"Time's up! Press Enter to continue to next question.";
}
},
// timer cancel check function
() =>
IsEndOfQuizHandled()
);
}