Idle detection

Just starting out? Need help? Post your questions and find answers here.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6161
Joined: Sat May 17, 2003 11:31 am
Contact:

Idle detection

Post by blueznl »

Idle detection in Windows 7 pretty much s*cks.

It's a little better in Windows 8, but when I rebuild my homeserver I went back to 7. I could have moved on to 8, but Microsoft's 'privacy' rules and 'automatic enforced updates' are not entirely my cup of tea.

The Microsoft task scheduler on Windows 7 (and 8, and perhaps 10) makes a few assumptions, such as 'n minutes with CPU activity lower than 10%' equals idling. This causes some troubles if you want to use a scheduled task to enforce a sleep or standby.

I understand it is pretty hard to detect sleep / idling mode on homeservers. For example, my homeserver is a simple but recent Pentium, and when serving music for example (running Logitech Squeezebox Server) the actual CPU load is so low that the system does detect it as idle.

I have been trying to cook up something that uses multiple different detection schemes as a kind of idle detection, but not yet with much success.

Anyone have any suggestions?

I am able to detect idling time versus kernel time (using the GetSystemTimes WinApi) but activity is so low, that I talk out things like 'idling 99.9% of the time means the PC has nothing to do, idling 99.8% means something is still going on'.

Does anyone have any additional suggestions for additional detection mechanisms? Possibilities... disk activity, memory activity... what else?

My currect code looks like this (ugly and uses some procedures that you can easily take out):

Code: Select all

EnableExplicit
IncludeFile("..\x_lib\x_lib.pb")
x_init()
;
Global kernel_nr.i
Global kernel_h.i
Global p_getsystemtimes.i
Global idle.FILETIME
Global kernel.FILETIME
Global user.FILETIME
Global idletime.q, kerneltime.q, usertime.q, totaltime.q
Global idletime_now.q, kerneltime_now.q, usertime_now.q
Global idletime_old.q, kerneltime_old.q, usertime_old.q
Global fast.q, smooth.q, tick.q, count.q, cputreshold.i, idleperiod.q
Global time.q, time_old.q, ticktime.q, downfactor.i, scanperiod.i, smoothfactor.i
Global event.i

Enumeration
  #tick_title_nr
  #tick_bar_nr
  #tick_val_nr
  #kernel_title_nr
  #kernel_bar_nr
  #kernel_val_nr
  #smooth_title_nr
  #smooth_bar_nr
  #smooth_val_nr
  #count_title_nr
  #count_bar_nr
  #count_val_nr
EndEnumeration
;
kernel_nr = 1
kernel_h = OpenLibrary(kernel_nr,"KERNEL32.DLL")
p_getsystemtimes = GetFunction(kernel_nr,"GetSystemTimes")
;
OpenWindow(1,100,100,280,110,"Idle Scanner")
ProgressBarGadget(#tick_bar_nr,45,5,170,20,0,1000)
ProgressBarGadget(#kernel_bar_nr,45,30,170,20,0,1000)
ProgressBarGadget(#smooth_bar_nr,45,55,170,20,0,1000)
ProgressBarGadget(#count_bar_nr,45,80,170,20,0,1000)
TextGadget(#tick_title_nr,5,10,40,20,"Tick")
TextGadget(#kernel_title_nr,5,35,40,20,"Kernel")
TextGadget(#smooth_title_nr,5,60,40,20,"Idle")
TextGadget(#count_title_nr,5,85,40,20,"Period")
TextGadget(#tick_val_nr,220,10,50,20,"")
TextGadget(#kernel_val_nr,220,35,50,20,"")
TextGadget(#smooth_val_nr,220,60,50,20,"")
TextGadget(#count_val_nr,220,85,50,20,"")
;
cputreshold = 796               ; minimal idle level in promille (1000 = 100%)
idleperiod = 5 * 60             ; scanperiod system must be idle before considered really idle, in seconds
downfactor = 10                 ; ramp quicker down when activity detected
scanperiod = 2000               ; scanperiod, period between each check, affects ramping up / down, in milliseconds
smoothfactor = 10               ; average samples over a number of scanperiods
;
idleperiod = idleperiod*1000/scanperiod
;
Repeat
  event = WaitWindowEvent(scanperiod)
  time = x_timer()
  ticktime = time - time_old
  If ticktime > scanperiod
    time_old = time
    ;
    CallFunctionFast(p_getsystemtimes,@idle,@kernel,@user)
    PokeL(@idletime_now,PeekL(@idle\dwLowDateTime))
    PokeL(@idletime_now+4,PeekL(@idle\dwHighDateTime))
    PokeL(@kerneltime_now,PeekL(@kernel\dwLowDateTime))
    PokeL(@kerneltime_now+4,PeekL(@kernel\dwHighDateTime))
    idletime = x_max(1,( idletime_now - idletime_old ) / 32768)
    kerneltime = x_max(1, ( kerneltime_now - kerneltime_old ) / 32768)
    idletime_old = idletime_now
    kerneltime_old = kerneltime_now
    ;
    tick = kerneltime * 1000 / ticktime
    fast = idletime * 1000 / kerneltime
    smooth = ( smooth * smoothfactor - smooth - smooth + tick + fast + 1 ) / smoothfactor
    If smooth < cputreshold
      count = count - downfactor
      SetGadgetColor(#smooth_val_nr,#PB_Gadget_FrontColor,RGB(255,0,0))
      If count <= 0
        count = 0
        SetGadgetColor(#count_val_nr,#PB_Gadget_FrontColor,RGB(255,0,0))
      EndIf
    Else
      count = count + 1
      SetGadgetColor(#smooth_val_nr,#PB_Gadget_FrontColor,#PB_Default)
      If count >= ( idleperiod * 1000 / scanperiod )
        count = idleperiod
        SetGadgetColor(#count_val_nr,#PB_Gadget_FrontColor,#PB_Default)
        SetWindowColor(1,RGB(255,0,0))
      EndIf
    EndIf
    ;
    SetGadgetState(#tick_bar_nr,tick)
    SetGadgetState(#kernel_bar_nr,fast)
    SetGadgetState(#smooth_bar_nr,smooth)
    SetGadgetState(#count_bar_nr,1000*count/idleperiod)
    SetGadgetText(#tick_val_nr,x_strex(tick,"  #.#")+"%")
    SetGadgetText(#kernel_val_nr,x_strex(fast,"  #.#")+"%")
    SetGadgetText(#smooth_val_nr,x_strex(smooth,"  #.#")+"%")
    SetGadgetText(#count_val_nr,x_strex(1000*count/idleperiod,"  #.#")+"%")
    ;
  EndIf
Until event = #PB_Event_CloseWindow
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Idle detection

Post by Dude »

So are you trying to detect the PC itself being idle, or the user being idle (ie. not typing or moving the mouse)?
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6161
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: Idle detection

Post by blueznl »

PC. I know how to detect user presence. (Used that in WallX.)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
USCode
Addict
Addict
Posts: 912
Joined: Wed Mar 24, 2004 11:04 pm
Location: Seattle, USA

Re: Idle detection

Post by USCode »

blueznl wrote:... I know how to detect user presence. ...
How is that done? Does it require Windows API calls or will just PureBasic commands do it?
Thanks!
User avatar
skywalk
Addict
Addict
Posts: 3972
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Idle detection

Post by skywalk »

Windows only...

Code: Select all

Procedure.d SL_IdleTime_Sec(Delay_ms.i=5000)
  ; If no input > given Delay_ms, then return idltm
  ; Else return 0
  ; SYNTAX: Debug SL_IdleTime_Sec()
  Protected idle.LASTINPUTINFO
  idle\cbSize = SizeOf(LASTINPUTINFO)
  Delay(Delay_ms)
  GetLastInputInfo_(@idle)
  Protected.d idltm = GetTickCount_() - idle\dwTime
  If idltm > Delay_ms
    idltm / 1000
  Else
    idltm = 0
  EndIf
  ProcedureReturn idltm
EndProcedure
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Idle detection

Post by Lunasole »

It should be possible to use also CPU cycles count (if you didn't tried it already), but surely need custom algorithm to detect idleness.
Maybe something like "if delta value is not changing in some range for some time, then system is idle" (that might work as for me, because if some process does something, that value just must fluctuate). But well it is all theories, I didn't played with it.

Following API should count cycles if I'm not remembering wrong ^_^

Code: Select all

EnableExplicit
DisableDebugger

OpenConsole("")
Define.q l, t
Repeat
	QueryPerformanceCounter_(@T)
	PrintN(Str(t - l))
	l = t
	Delay(512)
ForEver
Generally all that stuff with performance etc is not so simple in modern PC because of multiple cores + dynamically changing frequency and so on
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6161
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: Idle detection

Post by blueznl »

I strongly believe in building / programming if the factory solution doesn't suffice. So, after fooling around with different applications and versions of Windows I decided to give this another shot. (It's how Wakiewakie, SQB, WallX, CodeCaddy, REval etc. came into existence. Stuff I needed myself...)

So, how / when to shutdown a PC?


After more fooling around I came to the conclusion that simply CPU detection isn't enough. So I have to add some other mechanisms...

1. CPU activitiy - got that one mostly covered, though I saw some funny things on mutli core CPU's

2. Disk activity - this one is very interesting, does anybody have some sample code to show disk activity? I don't need a real time LED, but something like 'x bytes written during last 10 minutes' or 'last activity x minutes ago' would do well. Of course the % of activity would help :-) (Update: just found this: http://www.purebasic.fr/english/viewtop ... k+activity but haven't tested it yet...)

3. Network activity - same thing, here the amount of data is a little more interesting, so % would be very welcome.

4. Wifi activity - if one of the users is at home, I should be able to detect his / her phone. I probably need to do an ARP scan? (Though I noticed most phones can be pinged on the Wifi network.)

5. Profiling - self learning over time, been playing a bit with that, and I think I can get it to work if I can cover 2 and 3, 4 would be the icing on the cake.


I tried something called Winsleep which does a good idle detection but isn't suitable for my specific situation. So... sample code and suggestions welcome!
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Post Reply