Posts: 795
Threads: 136
Joined: Feb 2009
Hi Gintaras, Hi all
i want to try my own schedule from QM.
It would be minute, not second dependant.
I was thinking about a function launched at QM start, examining a variable to see if something to be done next minute.
something like:
str heure
str minutes
DateTime x
rep
x.FromComputerTime
_s=x.ToStr(10)
h.getcurrenthour
min.getcurrentminutes ;;homebrew methods
if(heure=h and min=minutes) Doaction ;;homebrew method
wait 30
Is it realistic in term of resource/memory usage/accuracy???? or should I forget this idea?
NB: It's supposed to handle about 35 actions per day.
Posts: 12,090
Threads: 142
Joined: Dec 2002
Realistic, does not require much CPU or memory. But quite difficult to make reliable. For example, consider all cases such as QM is not running or PC time changed.
Posts: 795
Threads: 136
Joined: Feb 2009
Here's the entire code
Seems to handle both situations you mention.
tested, seems to work flawlessly, some minor tweaks to go (recurrence is the thoughest).
QM is invaluable and a money saver.
out
int+ MinPrec
int Heures Minutes
str HeuresS MinutesS h
ARRAY(str) a
str Schedluded=
;23 32 "C:\Windows\regedit.exe" ""
;22 33 "C:\Windows\write.exe" ""
;23 34 "C:\Windows\notepad.exe" ""
out "lancé"
rep
,RetourneHeureMinutes(&Heures &Minutes)
,HeuresS=Heures
,MinutesS=Minutes
,if(Minutes=MinPrec)
,,out "Minute déjà faite"
,,goto h
,foreach h Schedluded
,,tok(h a -1 "" 4)
,,if(a[0].beg(HeuresS)=0) continue
,,if(StrCompare(a[1] MinutesS)=0)
,,,run a[2] a[3]
,,MinPrec=Minutes
,,;h
,,wait 60
If you have corrections or comments to improve it, they are welcome!!.
Posts: 795
Threads: 136
Joined: Feb 2009
No comments on that, so i consider it good and add new
things to it.
Posts: 12,090
Threads: 142
Joined: Dec 2002
The code is distorted and not colored. Difficult to read or test.
Posts: 795
Threads: 136
Joined: Feb 2009
Posts: 12,090
Threads: 142
Joined: Dec 2002
My version.
Little tested.
Function scheduler
lock schedule
str+ g_schedule=
;Macro1950, 2013.03.08 17:47, 5m, mac, "Macro1950", "command line"
;write, 2013.03.08 17:48, 0, run, "C:\Windows\write.exe"
;notepad, 2013.03.08 17:49, 1h, run, "C:\Windows\notepad.exe"
int+ g_scheduleUpdate=1
lock- schedule
;List of scheduled tasks in CSV format: name, date, action, path, arguments, flags
;;;name - task name. Must be unique in the list.
;;;date - start date. Any supported format. If in the past, anyway will run if interval is set.
;;;interval - repeat interval. Format like 1m, 1h, 1d, 1w, 1M. Use "" or 0 to run once.
;;;action - run (run program) or mac (start macro).
;;;path - program path or macro name or path.
;;;arguments (optional) - command line arguments for the program or macro.
;;;flags (optional): 1 disabled
;After updating schedule, run this function. If already running, it will catch the new schedule.
;After updating code, end thread and run again. Or move the loop body to other function, then will not need to end/run everytime, just compile.
;_____________________________________
if(getopt(nthreads)>1) ret ;;if already running, let just update schedule
type SCHEDULEDTASK
,str'name
,DateTime'tStart
,repeatEvery
,everyWhat ;;1 min, 2 hour, 3 day, 4 week, 5 month
,weekDays ;;flags: 1 mon, 2 tue, 4 wed, 8 thu, 16 fri, 32 sat, 64 sun
,action ;;1 run, 2 mac
,str'path str'args
,flags
ARRAY(SCHEDULEDTASK) a
SCHEDULEDTASK& t
int i nc; lpstr k
rep
,;first time or when schedule changed, parse schedule CSV and store in ARRAY(SCHEDULEDTASK) a
,if g_scheduleUpdate
,,lock schedule
,,g_scheduleUpdate=0
,,ICsv c._create; c.FromString(g_schedule)
,,lock- schedule
,,nc=c.ColumnCount
,,a.create(c.RowCount)
,,for i 0 a.len
,,,&t=a[i]
,,,t.name=c.Cell(i 0)
,,,t.tStart.FromStr(c.Cell(i 1)) ;;info: error if incorrect format
,,,k=c.Cell(i 2); t.repeatEvery=val(k 0 _i); if(t.repeatEvery) t.everyWhat=SelStr(0 k+_i "m" "h" "d" "w" "M"); if(!t.everyWhat) end F"invalid interval: {k}"
,,,k=c.Cell(i 3); t.action=SelStr(0 k "run" "mac"); if(!t.action) end F"invalid action: {k}"
,,,t.path=c.Cell(i 4)
,,,if(nc>5) t.args=c.Cell(i 5)
,,,if(nc>6) t.flags=val(c.Cell(i 6))
,,,;out _s.getstruct(t 1) ;;debug
,,out "Schedule updated" ;;debug
,
,;wait 1 s
,wait 1
,
,;get current time
,DateTime tNow.FromComputerTime
,int mNow mPrev; tNow.GetParts(0 0 0 0 mNow); if(mNow=mPrev) continue; else mPrev=mNow ;;check with precision of every 1 minute
,out tNow.ToStr(4) ;;debug
,int M d h m wd _M _d _h _m _wd
,tNow.GetParts(0 M d h m 0 0 0 wd)
,
,;for each scheduled task
,for i 0 a.len
,,&t=a[i]
,,;compare time
,,if(tNow<t.tStart or t.flags&1) continue
,,t.tStart.GetParts(0 _M _d _h _m 0 0 0 _wd)
,,int runNow=(_m=m and _h=h and _d=d and _M=M)
,,;reschedule if need
,,sel t.everyWhat
,,,case 0 t.flags|1
,,,case 1 rep() t.tStart.AddParts(0 0 t.repeatEvery); if(t.tStart>tNow) break ;;not the best way, but easy
,,,case 2 rep() t.tStart.AddParts(0 t.repeatEvery); if(t.tStart>tNow) break
,,,case 3 rep() t.tStart.AddParts(t.repeatEvery); if(t.tStart>tNow) break
,,,case 4 rep() t.tStart.AddParts(t.repeatEvery*7); if(t.tStart>tNow) break
,,,case 5 rep() t.tStart.AddMonths(t.repeatEvery); if(t.tStart>tNow) break
,,;is it late to run task now?
,,if !runNow
,,,sel t.action
,,,,case 1 out F"<>late: {t.name} at {t.tStart.ToStr}. <link ''{t.path} /{t.args}''>Run now</link>."
,,,,case 2 out F"<>late: {t.name} at {t.tStart.ToStr}. <macro ''{t.path} /{t.args}''>Run now</macro>."
,,,continue
,,;run task
,,sel t.action
,,,case 1 run t.path t.args; err out F"failed: run ''{t.path}'' ''{t.args}''"
,,,case 2 mac t.path t.args; err out F"failed: mac ''{t.path}'' ''{t.args}''"
Posts: 795
Threads: 136
Joined: Feb 2009
Great, lots of goods in there, will use as canvas and tailor to my needs.
At first glance, one thing surprises me : mPrev is not a global variable, why?
Posts: 12,090
Threads: 142
Joined: Dec 2002
Can be global. Not important.
The rescheduling code in my function is incorrect, may set date in the past.
Posts: 12,090
Threads: 142
Joined: Dec 2002
Corrected rescheduling code.
Posts: 795
Threads: 136
Joined: Feb 2009
I did not tried the code yet.
I thought mPrev should be global to keep value between calls, how can it keep last time between calls
if not global?
Posts: 12,090
Threads: 142
Joined: Dec 2002
The function is not called multiple times, unless you end thread and start again.
Posts: 795
Threads: 136
Joined: Feb 2009
Well i must dig into the code.
Q1: Is the code running endlessly (don't see wait or pause for the rep part).
Q2: can I launch it from previous function I created associated to QM start trigger or give to it it's own trigger, as TapTap function in previous thread?
Q3: what if I want to check interval by second or hour?
Posts: 12,090
Threads: 142
Joined: Dec 2002
1. Yes.
2. I would use trigger 'QM events / file loaded' for this function.
3. In GetParts mNow now receives minutes. It can receive seconds or hours. For seconds don't need that line.
Posts: 795
Threads: 136
Joined: Feb 2009
1. Ok, if then guess it's a continous loop, was afraid to try that because of the potentiel bootleneck it
could create in QM, and being too much ram/cpu consumer and interfere with other function. Good to know.
2. I once used init2 to launch at QM start. bad deal?
3. Can mNow be different for individual schedules, or the same for all? test some schedule on second basis, others minute, others hours?
Posts: 12,090
Threads: 142
Joined: Dec 2002
2. Good idea.
3. Why need it? The code is fast.
Posts: 795
Threads: 136
Joined: Feb 2009
3. Yes, the question was stupid. Sorry.
Posts: 795
Threads: 136
Joined: Feb 2009
Quote:Or move the loop body to other function, then will not need to end/run everytime, just compile.
Interesting.
I suppose the "loop" is from rep line after "rep" to end of function
case 2 mac t.path t.args; err out F"failed: mac ''{t.path}'' ''{t.args}''"
If I put the loop into something called RepeatLoop:
Q1: Should it be macro or a function (i suppose a function)
Q2: how to call it?
Rep
,RepeatLoop
or, mac "RepeatLoop"
or something else?
Q3: How g_scheduleUpdate knows I add or remove an item in g_schedule?????
Posts: 12,090
Threads: 142
Joined: Dec 2002
rep
,RepeatLoop
Then you can edit function RepeatLoop while scheduler is running. Click Compile button to apply changes. On syntax error the thread ends, then you can edit the function and click Run. But always need to restart thread when you change SCHEDULEDTASK type, ie add new members.
Some variables must exist between function calls. Yo can pass them to the function by reference, or use thread variables, like int- mPrev. Other ways exist.
Example:
Function scheduler
lock schedule
str+ g_schedule=
;Macro1950, 2013.03.08 17:47, 5m, mac, "Macro1950", "command line"
;write, 2013.03.08 17:48, 0, run, "C:\Windows\write.exe"
;notepad, 2013.03.08 17:49, 1h, run, "C:\Windows\notepad.exe"
int+ g_scheduleUpdate=1
lock- schedule
;List of scheduled tasks in CSV format: name, date, action, path, arguments, flags
;;;name - task name. Must be unique in the list.
;;;date - start date. Any supported format. If in the past, anyway will run if interval is set.
;;;interval - repeat interval. Format like 1m, 1h, 1d, 1w, 1M. Use "" or 0 to run once.
;;;action - run (run program) or mac (start macro).
;;;path - program path or macro name or path.
;;;arguments (optional) - command line arguments for the program or macro.
;;;flags (optional): 1 disabled
;After updating schedule, run this function. If already running, it will catch the new schedule.
;After updating code, end thread and run again. Or move the loop body to other function, then will not need to end/run everytime, just compile.
;_____________________________________
if(getopt(nthreads)>1) ret ;;if already running, let just update schedule
rep
,scheduler_Loop
Function scheduler_Loop
;/scheduler
function
type SCHEDULEDTASK
,str'name
,DateTime'tStart
,repeatEvery
,everyWhat ;;1 min, 2 hour, 3 day, 4 week, 5 month
,weekDays ;;flags: 1 mon, 2 tue, 4 wed, 8 thu, 16 fri, 32 sat, 64 sun
,action ;;1 run, 2 mac
,str'path str'args
,flags
ARRAY(SCHEDULEDTASK)- a
SCHEDULEDTASK& t
int i nc; lpstr k
;first time or when schedule changed, parse schedule CSV and store in ARRAY(SCHEDULEDTASK) a
if g_scheduleUpdate
,lock schedule
,g_scheduleUpdate=0
,ICsv c._create; c.FromString(g_schedule)
,lock- schedule
,nc=c.ColumnCount
,a.create(c.RowCount)
,for i 0 a.len
,,&t=a[i]
,,t.name=c.Cell(i 0)
,,t.tStart.FromStr(c.Cell(i 1)) ;;info: error if incorrect format
,,k=c.Cell(i 2); t.repeatEvery=val(k 0 _i); if(t.repeatEvery) t.everyWhat=SelStr(0 k+_i "m" "h" "d" "w" "M"); if(!t.everyWhat) end F"invalid interval: {k}"
,,k=c.Cell(i 3); t.action=SelStr(0 k "run" "mac"); if(!t.action) end F"invalid action: {k}"
,,t.path=c.Cell(i 4)
,,if(nc>5) t.args=c.Cell(i 5)
,,if(nc>6) t.flags=val(c.Cell(i 6))
,,;out _s.getstruct(t 1) ;;debug
,out "Schedule updated" ;;debug
;wait 1 s
wait 1
;get current time
DateTime tNow.FromComputerTime
int mNow; int-- mPrev; tNow.GetParts(0 0 0 0 mNow); if(mNow=mPrev) ret; else mPrev=mNow ;;check with precision of every 1 minute
out tNow.ToStr(4) ;;debug
int M d h m wd _M _d _h _m _wd
tNow.GetParts(0 M d h m 0 0 0 wd)
;for each scheduled task
for i 0 a.len
,&t=a[i]
,;compare time
,if(tNow<t.tStart or t.flags&1) continue
,t.tStart.GetParts(0 _M _d _h _m 0 0 0 _wd)
,int runNow=(_m=m and _h=h and _d=d and _M=M)
,;reschedule if need
,sel t.everyWhat
,,case 0 t.flags|1
,,case 1 rep() t.tStart.AddParts(0 0 t.repeatEvery); if(t.tStart>tNow) break ;;not the best way, but easy
,,case 2 rep() t.tStart.AddParts(0 t.repeatEvery); if(t.tStart>tNow) break
,,case 3 rep() t.tStart.AddParts(t.repeatEvery); if(t.tStart>tNow) break
,,case 4 rep() t.tStart.AddParts(t.repeatEvery*7); if(t.tStart>tNow) break
,,case 5 rep() t.tStart.AddMonths(t.repeatEvery); if(t.tStart>tNow) break
,;is it late to run task now?
,if !runNow
,,sel t.action
,,,case 1 out F"<>late: {t.name} at {t.tStart.ToStr}. <link ''{t.path} /{t.args}''>Run now</link>."
,,,case 2 out F"<>late: {t.name} at {t.tStart.ToStr}. <macro ''{t.path} /{t.args}''>Run now</macro>."
,,continue
,;run task
,sel t.action
,,case 1 run t.path t.args; err out F"failed: run ''{t.path}'' ''{t.args}''"
,,case 2 mac t.path t.args; err out F"failed: mac ''{t.path}'' ''{t.args}''"
When you run scheduler, it updates CSV string and sets g_scheduleUpdate=1. The loop runs every 1 s. If g_scheduleUpdate is 1 it sets it to 0 and creates new array.
Posts: 795
Threads: 136
Joined: Feb 2009
Ok make sense now, I now understand that in fact compiling make scheduler restart so g_scheduleUpdate is reset to 1
so next call of loop will detect the change and then reload the CSV thing. Copy that.
I suppose i don't need to declare int+ g_scheduleUpdate again in loop function because it's the same thread?
Posts: 12,090
Threads: 142
Joined: Dec 2002
Compiling does not make scheduler thread restart.
When you run scheduler while it is already running, runs second thread of it, simultaneously. It sets scheduleUpdate=1. It detects previous thread and exits.
Don't need to declare global variables everywhere, but not error.
Posts: 795
Threads: 136
Joined: Feb 2009
OK, that's fine for the moment.
|