Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Loop for schedule
#1
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.
#2
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.
#3
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.

Code:
Copy      Help
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!!.
#4
No comments on that, so i consider it good and add new
things to it.
#5
The code is distorted and not colored. Difficult to read or test.
#6
Sorry. Post corrected.
#7
My version.
Little tested.
Function scheduler
Code:
Copy      Help
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}''"
#8
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?
#9
Can be global. Not important.
The rescheduling code in my function is incorrect, may set date in the past.
#10
Corrected rescheduling code.
#11
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?
#12
The function is not called multiple times, unless you end thread and start again.
#13
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?
#14
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.
#15
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?
#16
2. Good idea.

3. Why need it? The code is fast.
#17
3. Yes, the question was stupid. Sorry.
#18
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
Code:
Copy      Help
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?????
#19
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
Code:
Copy      Help
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
Code:
Copy      Help
;/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.
#20
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?
#21
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.
#22
OK, that's fine for the moment.


Forum Jump:


Users browsing this thread: 1 Guest(s)