4

I need help setting up a specific type of timer for my application. My scenario is this:

I want to run a timer at a specific time of day (say 4:00AM). That timer then executes a number of different events that each go off and grab data from a database and process it (using background workers). All the different events take different times to finish (could be 30 seconds, could be 5 minutes). I want to continuously enter the timer event every 5 minutes thereafter checking if all events have finished. If so, stop the timer and restart it again at 4:00AM the next morning and so on.

So the process would look like this:

04:00AM - Start Timer and initiate background workers
04:05AM - Check again to see if processes have completed (if so, stop timer)
04:10AM - Check again to see if processes have completed (if so, stop timer)
And so on....

I already have my background worker code that processes my database information. So I just need help setting up the timer.

So far I have the following in my Tick event:

Private Sub tmr_Maintenance_Tick(sender As Object, e As EventArgs) Handles tmr_Maintenance.Tick
    Dim CurrentTime As Date
    Dim CurrHour As Integer

        CurrentTime = DateTime.Now
        CurrHour = CurrentTime.Hour

        'Automatic Sales Import
        With My.Settings
            If CurrHour = 4 Then

                If Not .Maintenance_Ran Then
                    CreateLog("Maintenance Started")

                    If Not .Yesterdays_Sales_Imported Then
                        CreateLog("Importing Yesterday's Sales From Portal")
                        If Not YesterdaysSales_Worker.IsBusy Then YesterdaysSales_Worker.RunWorkerAsync()
                    End If

                    If Not .WTD_Sales_Imported Then
                        CreateLog("Importing Week To Date Sales From Portal")
                        If Not WeekToDateSales_Worker.IsBusy Then WeekToDateSales_Worker.RunWorkerAsync()
                    End If

                    If Not .LW_Sales_Imported Then
                        CreateLog("Importing Last Week Sales From Portal")
                        If Not LastWeekSales_Worker.IsBusy Then LastWeekSales_Worker.RunWorkerAsync()
                    End If

                    If .Yesterdays_Sales_Imported = True And .WTD_Sales_Imported = True And .LW_Sales_Imported = True Then
                        .Maintenance_Ran = True
                    End If
                End If
            Else
                .Maintenance_Ran = False
            End If
        End With

    My.Settings.Save()
End Sub

Any help appreciated thanks.

Riples
  • 1,167
  • 2
  • 21
  • 54
  • The code you listed doesn't seem related to your question. What does your timer code look like? – CoderDennis Feb 10 '15 at 21:19
  • The code I listed sits inside my `Timer_Tick` event. My Timer is set to execute every second and run through that code listed above. I was just showing the code that does all the data processing as I only needed help setting up the timer. I have updated my code to show my Timer event properly. – Riples Feb 10 '15 at 21:24
  • OK. Your data processing is irrelevant for the timer operation. Seeing what you're trying to do with the check against the current hour helps to know how to answer. – CoderDennis Feb 10 '15 at 21:37
  • It seems to me that a timer is the wrong choice for this. Instead write your application as a scheduled task set to run at 4 AM. – Chris Dunaway Feb 11 '15 at 14:32

2 Answers2

4

Here's a basic framework you can start from:

Public Class Form1

    Private Enum MaintenanceState
        WaitingToStart
        Started
    End Enum

    Private Target As DateTime
    Private state As MaintenanceState = MaintenanceState.WaitingToStart
    Private MaintenanceTime As New TimeSpan(4, 0, 0) ' 4:00 am
    Private WaitingInterval As New TimeSpan(0, 5, 0) ' Five minutes

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Target = GetNextMaintenanceTarget(MaintenanceTime)
        tmr_Maintenance.Interval = 1000
        tmr_Maintenance.Start()
    End Sub

    Private Sub tmr_Maintenance_Tick(sender As Object, e As EventArgs) Handles tmr_Maintenance.Tick
        ' optionally display time remaining until next target
        Dim ts As TimeSpan = Target.Subtract(DateTime.Now)
        Label1.Text = ts.ToString("hh\:mm\:ss")

        ' see if we've hit the target
        If DateTime.Now >= Target Then
            tmr_Maintenance.Stop()

            Select Case state
                Case MaintenanceState.WaitingToStart

                    ' ... start all the jobs ...

                    state = MaintenanceState.Started
                    Target = DateTime.Now.Add(WaitingInterval)

                Case MaintenanceState.Started

                    ' ... check to see if the jobs are all done ...

                    If Not AllJobsCompleted Then
                        Target = DateTime.Now.Add(WaitingInterval)
                    Else
                        state = MaintenanceState.WaitingToStart
                        Target = GetNextMaintenanceTarget(MaintenanceTime)
                    End If

            End Select

            tmr_Maintenance.Start()
        End If
    End Sub

    Private Function GetNextMaintenanceTarget(ByVal time As TimeSpan) As DateTime
        Dim dt As DateTime = DateTime.Today.Add(time)
        If DateTime.Now > dt Then
            dt = dt.AddDays(1) ' already past target time for today, next start is tomorrow
        End If
        Return dt
    End Function

End Class
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • Perfect thankyou. I have implemented this and it works great! Thanks for your time. Only issue was that you had a typo in your `MaintenanceTime` statement - setting it to 4 minutes past midnight instead of 4 am. Thanks again. – Riples Feb 19 '15 at 22:11
2

Instead of firing your timer every second and checking the current hour (which seems problematic anyway) why not calculate how many milliseconds until 4 am and set the timer to fire at that time?

Dim timer = New Timer()
timer.Interval = MillisecondsUntilNextFourAm

...

Function MillisecondsUntilNextFourAm() As Integer
    Dim now = DateTime.Now()
    If now.Hour < 4 Then
        Return (TimeSpan.FromHours(4) - now.TimeOfDay).TotalMilliseconds
    Else
        Return (TimeSpan.FromHours(28) - now.TimeOfDay).TotalMilliseconds
    End If
End Function

You could do something similar if you then want the timer to fire again in 5 minutes. I would set the timer's AutoReset property to False and call Start each time after setting the interval.

CoderDennis
  • 13,642
  • 9
  • 69
  • 105