Sign in to follow this  
Followers 0
aleph01

Better way to write a task scheduler?

16 posts in this topic

I know I can use Windows Task Scheduler, but I decided to write one in AutoIt.  However, I am interested in gaining forum input as to whether my method is good, better, or best (or poor, worse, worst : .)  It works fine.  I'm posing a challenge to others to write one that works just as well, but using different methods.  Other methods will help me learn to be a better coder.  Also, feel free to point out what I could have done better, if you don't want to write a scheduler.

While 1
    $MIN = @MIN
    $HOUR = @HOUR
    $WDAY = @WDAY
    If $WDay = 2 OR $WDay = 3 OR $WDay = 4 OR $WDay = 5 Or $WDay = 6 OR $WDAY = 7 Then ; Monday through Saturday
        If $HOUR = 18 Then ; at 6:00 pm
            If @Min = 00 Then ; on the hour (subject to my loop time)
                Run ("notepad.exe", "", @SW_MAXIMIZE) ; perform some activities
                WinWait ( "Untitled")
                Sleep (40001)
                WinClose ("Untitled")
            EndIf
        EndIf
    EndIf
Sleep (40001) ; loop every 40 seconds so that 6:00 pm is always included, but only once a day
WEnd

Thanks in advance for making me better.  Btw, the sleep 40001 I just picked up along the way.  It sort of customizes my code to some extent, though I've seen it elsewhere.

_aleph_


Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Here's a start.

While 1
    If @WDAY >= 2 And @HOUR = 18 And @MIN = 0 Then ; Monday through Saturday
        Run("notepad.exe", "", @SW_MAXIMIZE) ; perform some activities
        WinWait("Untitled")
        Sleep(40001)
        WinClose("Untitled")
    EndIf
    Sleep(40001) ; loop every 40 seconds
WEnd

Just shortened your code.

Edited by JohnOne

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

JohnOne,

I appreciate your response, but I don't understand how

If @WDAY >= 2 And @HOUR = 18 And @MIN = 0 Then ; Monday through Saturday

works.  Nevermind, I recall some C++.  the >= does it.  I will incorporate.  Thanks for that!  I'm a better coder already!

Anybody else care to participate?  I can really learn from, well pretty much everyone.

I love this forum, and I often get a kick from forum rule-breaking posters who get "moderated".

Please, anyone with more input, post!


Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites

If this is a project you are going to expand and develop, and not just a one off, you should begin it as you mean to go on, and keep it modular.

; Keep your main loop easy to manage
While 1
    _Task1()
    _Task2()
    Sleep(40001) ; loop every 40 seconds
WEnd

Func _Task1()
    If @WDAY >= 2 And @HOUR = 18 And @MIN = 0 Then ; Monday through Saturday
        Run("notepad.exe", "", @SW_MAXIMIZE) ; perform some activities
        WinWait("Untitled")
        Sleep(40001)
        WinClose("Untitled")
    EndIf
EndFunc   ;==>_Task1

Func _Task2()
    ;code
EndFunc

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

JohnOne,  thanks for the input, and WOW! the do it in functions is great!  Thanks for the ideas.  Seeing your expertise in coding, I doubt I'll ever be able to return the favor, but let me wish you every spiritual enhancement, just as you have given me coding enhancements beyond my expectations.

_aleph_

btw, anyone who wants to contribute beyond, is very welcome to contribute.  I'm trying to be a sponge.

_aleph_


Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites

Aleph, your code looks OK except that your method of preventing repeats within the same minute might be unreliable. The right way to do this is to create am $armed flag for each event, and initalise it to false.

In your main loop:

IF the current time is equal to or later than the event time THEN

....IF $armed is true THEN

.......fire the event

.......reset $armed to false

....ENDIF

ELSE

....set $armed to true

ENDIF

SLEEP a few seconds and repeat

There is one special case, and that is an event scheduled for exactly midnight, which you may need to make allowance for. (There can be no time value less than 00:00) Though, not important if event is always at 18:00.

This is basically how a mechanical timeswitch works - the peg on the dial compresses a spring as the set time approaches. At the set time the peg is disengaged, releasing the spring which trips the contacts smartly from one state to the other. Once the set time is past, the action cannot repeat until re-arming occurs.

Also, you may find it easier to convert your times into 'hhmm' format and do a direct numeric comparison, rather than checking hour and minutes separately.

HTH.

Share this post


Link to post
Share on other sites

Hi Aleph.

Are you looking to use the application on a longterm basis? If you are, I would suggest using a dynamic script, and avoid hardcoding any tasks into the actual scheduler itself...

You could achieve this with an ini file, Which would contain all the data for tasks such as the frequency, Time, and directory of the program to execute.

The benefit of this is that you can add additional tasks to your task scheduler without having to worry about editing the source code for the scheduler itself, It also means you could build a snazzy GUI for adding/removing tasks etc, as You wouldn't need to manually add them to the script!

I've written a quick example to showcase what I mean:

local $ini = @workingdir & "\config.ini"
local $Tasks,$TaskInfo,$OldIni
iniwrite($ini,"Test1","Dir","C:\temp\test1.exe") ;Create the ini for testing purposes
iniwrite($ini,"Test1","Time","12:48")

while 1

   if FileRead($ini) <> $OldIni then ;Check for any updates to the ini file
      Console(FileRead($ini))
      Console($OldIni)
      $Tasks = IniReadSectionNames($ini) ;Gathers each section name from ini (the identifier used per task in this example)
      $OldIni = FileRead($ini) ;Stores Entire contents of Ini to variable (Cheap and ugly update check on the config)

      if IsArray($TaskInfo) then ;Avoid ReDim Errors by checking if array exists before declaration
         ReDim $TaskInfo[Ubound($Tasks)][4]
      Else
         Dim $TaskInfo[Ubound($Tasks)][4]
      EndIf

      For $i = 0 to uBound($Tasks) - 1 ; For Loop to gather all task data for each task into an array
         $TaskInfo[$i][0] = $Tasks[$i]
         $TaskInfo[$i][1] = iniRead($ini,$Tasks[$i],"Dir","")
         $TaskInfo[$i][2] = iniRead($ini,$Tasks[$i],"Time","")
         $TaskInfo[$i][3] = False ;Flag to ensure only run once
      Next
   EndIf

   For $x = 0 to Ubound($TaskInfo,1) - 1 ;For loop to check if any tasks are to be run.
      if @HOUR & ":" & @MIN = $TaskInfo[$x][2] and $TaskInfo[$x][3] == False Then
            Run($TaskInfo[$x][1]) ; Run the task using the Directory value stored in the config file
            $TaskInfo[$x][3] = True ; Set the run flag to true for this task
      EndIf
   Next
WEnd

This application, albeit very basic, will run any files mentioned in the config.ini. If you haven't tried using the native ini functions, I would really suggest playing with them, as they're really easy to use... I certainly haven't looked back. 

Just a suggestion :)

- Javi

1 person likes this

give a man an application, and he'll be frustrated for the day, Teach him how to program applications and he'll be frustrated for a lifetime.

Share this post


Link to post
Share on other sites

Great!  It looks like I have lots of "directed learning" to do.  JohnOne, thanks for the suggestions - you'll see many of them incorporated in the code I'm posting.  However, I am running into trouble with the conditional operator, though the If/Then/EndIf structure works.  More on that in a moment.

Anteaus, I see what you mean.  That'll probably be the next thing I'll incorporate, once I get straightened out on the conditional operator.

Javiwhite, I can see the advantages you pointed out about .ini files.  Writing one will be a first for me and feels over my head, but how will I ever learn if I don't take on more challenging things?  Having the code run the timer and pulling the tasks from an .ini is on my list too.

Here's my current problem.  Going from an If/Then/EndIf structure to the conditional operator gives me one of two messages.  Either I get "Error in expression" if I don't use brackets, or I get an error stating that I need an ending bracket, although there appears to be no case in which I haven't closed an opening bracket.  I've renamed Tasks 1 & 2 to something more descriptive and am trying to get this running with the Scheduler module totally separate from the Tasks module.  That should help when it's .ini time.  This script should stay in the scheduler loop most of the time, move on to the Tasks function and then loop back to the Scheduler function.  Here's the code:

While 1
    _Scheduler()
    _Tasks()
WEnd


Func _Scheduler()
    While 1
        (@WDAY >= 1 And @HOUR = 10 And @MIN = 50) ? (ExitLoop) : (Sleep (40001))
    WEnd
EndFunc     ;==>_Scheduler


Func _Tasks()
    Run("notepad.exe", "", @SW_MAXIMIZE) ; perform some tasks
    WinWait("Untitled")
    Sleep(40001)
    WinClose("Untitled")
EndFunc    ;==>_Tasks

It's the line in the scheduler function that gives the error.  It appears to do fine when it's sleeping and waiting  for the condition to be true.  When the condition is true, I get the errors below - couldn't seem to copy and paste it here. 

(@WDAY >= 1 And @HOUR = 11 And @MIN = 31 ? (ExitLoop): (Sleep

(40001))

^ERROR

Error: Missing right bracket ')' in expression.

OR

Error In Expression (located in the same place.)

Thanks, for the help.

_aleph_


Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites

Well, isn't that a disappointment!  Well, the If/Then/EndIf works, so I'm using the code below for the Scheduler function.  The Tasks function works like a charm.  Now to address the timing issue and then incorporating an .ini file...

I'll be back...

Thanks all!

_aleph_

Func _Scheduler()
    While 1
        If @WDAY >= 1 AND @HOUR = 16 AND @MIN = 30 Then
            ExitLoop
        Else
            Sleep (60001)
        EndIf
    WEnd
EndFunc     ;==>_Scheduler

Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites

javiwhite,

I realize I never answered your first question.  I'm not expecting to use this as anything other than a learning tool.  I have a problem with going through tutorials as dogma.  I never want to write another "Hello world" again.  I require a purpose to write code.  A school assignment qualifies, but alas, I'm not in school at this time, and when I was, I studied Biology and Chemistry (along with Philosophy and Math).

Having said that, I can't say why I chose this as a project, since Windows has this native ability, but if I can be motivated by this project, it suits my needs as far as learning, and AutoIt and this forum provide the means, since I really like AutoIt.  Thanks, Jon! ..and the contributing crew!

_aleph_


Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites

Anteaus, if you're still watching this thread, here's what me Scheduler function code looks like after incorporating your $armed variable suggestion.  Correct me if I don't appear to be using it right.

Func _Scheduler()
    $armed = false
    While 1
        If @WDAY >= 1 AND @HOUR = 16 AND @MIN = 30 Then
            If $armed = true Then ExitLoop
        Else
            $armed = true
            Sleep (40001)
        EndIf
    WEnd
EndFunc     ;==>_Scheduler

While looking at it and trying to figure out the flow of commands, I realized that I didn't need to reset $armed to false before ExitLoop because ExitLoop throws me out of the Scheduler function as well, and when the main script loops me back to the Scheduler function, $armed is set to false first thing.  Let me know if I'm not using the $armed flag correctly.  It looks like my Tasks function will need to have a >1 minute sleep time to assure that I don't return to the Scheduler task too soon after running Tasks.

This weekend, it will be looking at how to use .ini files for the Tasks function.

Thanks all!

_aleph_


Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

Hi Aleph.

Are you looking to use the application on a longterm basis? If you are, I would suggest using a dynamic script, and avoid hardcoding any tasks into the actual scheduler itself...

You could achieve this with an ini file, Which would contain all the data for tasks such as the frequency, Time, and directory of the program to execute.

The benefit of this is that you can add additional tasks to your task scheduler without having to worry about editing the source code for the scheduler itself, It also means you could build a snazzy GUI for adding/removing tasks etc, as You wouldn't need to manually add them to the script!

I've written a quick example to showcase what I mean:

local $ini = @workingdir & "\config.ini"
local $Tasks,$TaskInfo,$OldIni
iniwrite($ini,"Test1","Dir","C:\temp\test1.exe") ;Create the ini for testing purposes
iniwrite($ini,"Test1","Time","12:48")

while 1

   if FileRead($ini) <> $OldIni then ;Check for any updates to the ini file
      Console(FileRead($ini))
      Console($OldIni)
      $Tasks = IniReadSectionNames($ini) ;Gathers each section name from ini (the identifier used per task in this example)
      $OldIni = FileRead($ini) ;Stores Entire contents of Ini to variable (Cheap and ugly update check on the config)

      if IsArray($TaskInfo) then ;Avoid ReDim Errors by checking if array exists before declaration
         ReDim $TaskInfo[Ubound($Tasks)][4]
      Else
         Dim $TaskInfo[Ubound($Tasks)][4]
      EndIf

      For $i = 0 to uBound($Tasks) - 1 ; For Loop to gather all task data for each task into an array
         $TaskInfo[$i][0] = $Tasks[$i]
         $TaskInfo[$i][1] = iniRead($ini,$Tasks[$i],"Dir","")
         $TaskInfo[$i][2] = iniRead($ini,$Tasks[$i],"Time","")
         $TaskInfo[$i][3] = False ;Flag to ensure only run once
      Next
   EndIf

   For $x = 0 to Ubound($TaskInfo,1) - 1 ;For loop to check if any tasks are to be run.
      if @HOUR & ":" & @MIN = $TaskInfo[$x][2] and $TaskInfo[$x][3] == False Then
            Run($TaskInfo[$x][1]) ; Run the task using the Directory value stored in the config file
            $TaskInfo[$x][3] = True ; Set the run flag to true for this task
      EndIf
   Next
WEnd

This application, albeit very basic, will run any files mentioned in the config.ini. If you haven't tried using the native ini functions, I would really suggest playing with them, as they're really easy to use... I certainly haven't looked back. 

Just a suggestion :)

- Javi

This works great BUT  anytime something new is added to the INI file it restarts the program from the INI

 

resetting the flags to false is what you mentioned via PM

 

 

 

 

For $x = 0 to Ubound($TaskInfo,1) - 1 ;For loop to check if any tasks are to be run.
      if @HOUR & ":" & @MIN = $TaskInfo[$x][2] and $TaskInfo[$x][3] == False Then
            Run($TaskInfo[$x][1]) ; Run the task using the Directory value stored in the config file
            $TaskInfo[$x][3] = True ; Set the run flag to true for this task
      EndIf
   Next
WEnd
Edited by dynamitemedia
f

Share this post


Link to post
Share on other sites

Hey Dynamite,

Indeed, as mentioned over PM, the array is rebuilt every time the config file is updated, A way around this is to reserve the old information, until the task array is fully rebuilt (after which the old information should be discarded

I've updated the snippet, Try this out and see how you get on.

I've commented what's occurring, and left in the array checks, So you can work back through the changes, and learn how to resolve the issue.

#include<Array.au3>
local $ini = @workingdir & "\config.ini"
local $Tasks,$TaskInfo,$OldIni,$OldTaskInfo
iniwrite($ini,"Test1","Dir","C:\temp\test1.exe") ;Create the ini for testing purposes
iniwrite($ini,"Test1","Time","09:17")

while 1
sleep(10)
    if FileRead($ini) <> $OldIni then ;Check for any updates to the ini file
        ConsoleWrite(FileRead($ini) & @CR)
        ConsoleWrite($OldIni & @CR)
        $Tasks = IniReadSectionNames($ini) ;Gathers each section name from ini (the identifier used per task in this example)
        $OldIni = FileRead($ini) ;Stores Entire contents of Ini to variable (Cheap and ugly update check on the config)

        if IsArray($TaskInfo) then ;Avoid ReDim Errors by checking if array exists before declaration
            $OldTaskInfo = $TaskInfo ;temporarily store old information
            ReDim $TaskInfo[Ubound($Tasks)][4]
        Else
            Dim $TaskInfo[Ubound($Tasks)][4]
        EndIf

        For $i = 1 to uBound($Tasks) - 1 ; For Loop to gather all task data for each task into an array
            $TaskInfo[$i][0] = $Tasks[$i]
            $TaskInfo[$i][1] = iniRead($ini,$Tasks[$i],"Dir","")
            $TaskInfo[$i][2] = iniRead($ini,$Tasks[$i],"Time","")
            $TaskInfo[$i][3] = False ;Set the default state (Hasn't run)
            for $x = 1 to uBound($OldTaskInfo,1) - 1 ;Cycle through old task array
                if $OldTaskInfo[$x][0] = $TaskInfo[$i][0] Then ;If one of our tasks is from previous task array
                    $TaskInfo[$i][3] = $OldTaskInfo[$x][3] ;Gather the flag setting from the old array
 and replace default state
                    ExitLoop ;No need to cycle through further tasks, We've found the one we're looking for.
                EndIf
            Next
        Next
        $OldTaskInfo = '' ;Clear the variable containing outdated info
        _ArrayDisplay($TaskInfo) ;display results
    EndIf


   For $x = 0 to Ubound($TaskInfo,1) - 1 ;For loop to check if any tasks are to be run.
      if @HOUR & ":" & @MIN = $TaskInfo[$x][2] and $TaskInfo[$x][3] == False Then
            Run($TaskInfo[$x][1]) ; Run the task using the Directory value stored in the config file
            $TaskInfo[$x][3] = True ; Set the run flag to true for this task
            _ArrayDisplay($TaskInfo)
      EndIf
   Next

WEnd

 

Hope it helps

Javi


give a man an application, and he'll be frustrated for the day, Teach him how to program applications and he'll be frustrated for a lifetime.

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

Ok been making adjustments to fit what i have, but this is NOT displaying in console or array display when a new item is put in INI  is that normal?

 

EDIT:  just using as is does not show the updates in the console

EDIT:   it was cause the split of this line

;Gather the flag setting from the old array
 and replace default state

Thanks seems to be working thus far will come back and update when  i play with it some more!

Edited by dynamitemedia

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0