A_Now versus FileGetTime() timestamps

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
TomL
Posts: 16
Joined: 12 Jan 2016, 21:02

A_Now versus FileGetTime() timestamps

05 Jul 2019, 08:36

When a script is launched, the launch time is obtained using the "A_Now" variable. A timer is then started to check the modification time of included files against the launch time and, if an included file has has a later date, the "Reload" command is issued. The issue is that the reloaded script, which again retrieves its launch time using the "A_Now" variable, often receives a time that is a second earlier then the modification time of the file which caused the reload, resulting is a second reload. Is there a known issue with the rounding of milliseconds in the timestamp returned by the "A_Now" command verses the timestamp returned by the FileGetTime() command that could cause a script started after a file was saved to report an "A_Now" time that is a second earlier than the saved file?
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: A_Now versus FileGetTime() timestamps

05 Jul 2019, 10:04

What is your question? Where is your script? It sounds like you want to reload a script automatically, when a included file is updated?
If No, I can't tell the question, but I can tell A_Now is not a command, and the launch time is not suitable for the task.
TomL
Posts: 16
Joined: 12 Jan 2016, 21:02

Re: A_Now versus FileGetTime() timestamps

05 Jul 2019, 18:10

Thanks wolf_II for taking the time to reply. Yes, A_Now is not a command. It's a variable as stated in the first sentence. My mistake.
My last sentence asks the question I have although I admit that it may be difficult to understand.

The following script shows the problem. Copy the script into a file and create an "externalFile.txt" file in the same directory. Launch the script and then update the external file's modification date (open the externalFile.txt in an editor, add some text, save the file). The script will detect that the external file's modification time has change and reload the script (as designed). The problem is that when the script is reloaded it detects again that the external file's modification timestamp is later than the new script launch time and reloads the file a second time. This can be observed by watching the script's tray icon flash twice. To see, what I believe to be the root cause, un-comment the "FileAppend ..." code. It will create a logFile.txt file in the same directory containing the compared timestamp values that caused the reloads to occur.

Code: Select all

#Persistent

;Check external file every second
SetTimer, ReloadCheck, 1000

ReloadCheck()
{
	;Script launch timestamp
	Static launchTime := A_Now
	;External file's modification time
	FileGetTime, fileTime, externalFile.txt

	;File modified after script launched?
	if( fileTime > launchTime )
	{
		;FileAppend, % fileTime . " > " . launchTime . "`n", logFile.txt
		Reload
	}
}
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: A_Now versus FileGetTime() timestamps

05 Jul 2019, 18:35

Thx for the clarification, My point was, IF the only reason for your work is To AUTO_Reload, I have a better way.
IMHO, A_Now and Launch time are not suitable for the task. As you found out.
File timestamps are ok, but you need to remember them, and compare. Independent from Lauch-time.

Try this : (by putting it in Main.ahk)

Code: Select all

#Include AutoReload.ahk
oARL.add("Path\to\File")

Code: Select all



;-------------------------------------------------------------------------------
AutoReload() { ; init
;-------------------------------------------------------------------------------
    oARL := new AutoReload

    ; auto-reload when any file in Include or Lib folder changes
    Loop, Parse, % "Include, Lib", `,, %A_Space%
        Loop, Files, %A_LoopField%\*.*
            oARL.Add(A_LoopFileLongPath)

    Return, oARL
}



;===============================================================================
class AutoReload { ; auto-reload script when a file changes
;===============================================================================


    ;---------------------------------------------------------------------------
    __New() { ; the constructor starts the timer
    ;---------------------------------------------------------------------------
        FileGetTime, TimeStamp, %A_ScriptFullPath%
        this.Push({File: A_ScriptFullPath, Time: TimeStamp})
        SetTimer, %this% ; uses Call()
    }


    ;---------------------------------------------------------------------------
    Add(FileFullPath) { ; add a file to monitor
    ;---------------------------------------------------------------------------
        FileGetTime, TimeStamp, %FileFullPath%
        this.Push({File: FileFullPath, Time: TimeStamp})
    }


    ;---------------------------------------------------------------------------
    Call() { ; check the time stamps of all the files
    ;---------------------------------------------------------------------------
        Loop, % this.MaxIndex() {
            File := this[A_Index].File

            FileGetTime, TimeStamp, %File%
            If (TimeStamp = this[A_Index].Time)
                Continue ; do nothing yet

            ; a file has changed
            SetTimer, %this%, Off
            If A_IconHidden {
                ToolTip, Reloading ...`n%File%
                SoundPlay, *-1
            } Else
                TrayTip, Reloading ..., %File%,, 1
            Sleep, 2000
            Reload

            ; if reload fails, turn timer back on
            this[A_Index].Time := TimeStamp
            SetTimer, %this%, On
            ToolTip ; off
            TrayTip ; off
        }
    }
}
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: A_Now versus FileGetTime() timestamps

05 Jul 2019, 19:06

I have not tested my script recently, I used to have some problems with TrayTip, which are gone now.
If have problems with my class, please come back, and paste a script of yours, and I can now start to test again.
There is an Init() function in there that might not be of interest to you.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: A_Now versus FileGetTime() timestamps

06 Jul 2019, 06:34

You should add return/Exit/ExitApp, as appropriate, after Reload. If you don't, the script may still perform some actions before finally closing.

Code: Select all

Reload
return
For this script, maybe ExitApp is necessary.
Alternatively, perhaps you should turn SetTimer off, before Reload.

Perhaps, a new script is starting, while the old script is still modifying the file. I haven't checked fully.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
flyingDman
Posts: 2791
Joined: 29 Sep 2013, 19:01

Re: A_Now versus FileGetTime() timestamps

06 Jul 2019, 14:53

IMHO the script's behavior is greatly impacted by the declaration of the static variable. The help file states Each static variable is initialized only once (before the script begins executing). And again in the example given: A static declaration's initializer still runs only once (upon startup).
so that unless reloaded, the LaunchTime only gets updated during the first time the script runs. So without the Reload at the end of the function fileTime > launchTime if true once, will remain true. If you were to replace static launchTime := A_Now with

Code: Select all

static launchTime := A_Now
launchTime := A_Now
fileTime > launchTime would never be true (unless the clock of the PC generating externalFile.txt goes haywire...)

I think this is what you're looking for:

Code: Select all

#Persistent
launchTime := A_Now
SetTimer, ReloadCheck, 1000
return

ReloadCheck:									;I don't think a function is needed 
FileGetTime, fileTime, externalFile.txt
if( fileTime > launchTime )
	{
	soundbeep,1000,50
	launchTime := A_Now
	;other stuff								; reload not necessary
	}
return	
To check if this works (it does here), run this after launching the script above:

Code: Select all

fileappend,abc`n,externalFile.txt	
The 1st script should generate a beep every time the second script runs. I.e. every time the file gets a new modified date.
Last edited by flyingDman on 07 Jul 2019, 10:17, edited 1 time in total.
14.3 & 1.3.7
TomL
Posts: 16
Joined: 12 Jan 2016, 21:02

Re: A_Now versus FileGetTime() timestamps

07 Jul 2019, 09:29

I appreciate the suggestions but my initial question has not been answered. In short, a script is reloaded and then 1 second later, when the first timer event occurs, the current system time is retrieved using A_Now and it is an earlier timestamp than the modification time of a file saved before the script was reloaded. Is there that much variability in the setting of a file's modification time and/or the A_Now timestamp?

Unless someone can point out a problem with the code, I'll work around the problem by increasing the timer period (delays the setting of the launch time) or by adding a couple of seconds to the launch time using Static launchTime := A_Now + 2.
User avatar
flyingDman
Posts: 2791
Joined: 29 Sep 2013, 19:01

Re: A_Now versus FileGetTime() timestamps

07 Jul 2019, 10:46

a script is reloaded and then 1 second later, when the first timer event occurs, the current system time is retrieved
I attempted to tell you, using quotes from the help file, that that is not the case. The current system time is retrieved once upon startup of the script, and not when the timer runs.
Run this to convince yourself:

Code: Select all

settimer,timer, 1000
return

esc::
msgbox % res
exitapp

timer()
{
global res	
static launchTime := A_Now
return res .= launchTime "`n"
}

Let this run for a few seconds and then press esc. Launchtime is not updated and the same time is shown multiple times. Add one line launchTime := A_Now just below the line static launchTime := A_Now and you'll see that launchtime does get updated.
So, your script as it is, does not accomplish what you want and giving unwanted results.
14.3 & 1.3.7
User avatar
flyingDman
Posts: 2791
Joined: 29 Sep 2013, 19:01

Re: A_Now versus FileGetTime() timestamps

07 Jul 2019, 12:16

Run this as well for a few seconds and then press esc. The first value is that of launchtime2, all values below that are of launchtime. They are all the same. So static launchTime := A_Now
is launched at the time the script is launched I.e at the same time launchTime2 := A_Now and not 1 second later.

Code: Select all

#persistent

launchTime2 := A_Now
settimer,timer, 1000
return

esc::
msgbox % launchtime2 "`n`n`" res
exitapp

timer()
{
global res	
static launchTime := A_Now
return res .= launchTime "`n"
}
14.3 & 1.3.7
TomL
Posts: 16
Joined: 12 Jan 2016, 21:02

Re: A_Now versus FileGetTime() timestamps

07 Jul 2019, 17:21

Thanks flyingDman for reminding me of the fact that launchTime is assigned when the script is launched and for taking the time to write the code to prove it. That helps to better explain, although still surprising, why 2 seconds needs to be added to A_Now in order to insure the launchTime timestamp is equal to or later than the fileTime timestamp.
TomL
Posts: 16
Joined: 12 Jan 2016, 21:02

Re: A_Now versus FileGetTime() timestamps

10 Sep 2019, 10:42

The 2 second difference between the actual file write time and the file's reported modified date was due to the file being stored on a FAT32 formatted drive. A search for "FAT32 timestamp resolution" returned the following:
NTFS stores all timestamps to 100 nanosecond resolution. By comparison, FAT32 stores the time portion of a timestamp to a 2 second resolution.
Running the test code on a NTFS file system worked without the need to add 2 seconds.

Note: USB flash drives, SD cards, and other removable drives still using FAT32.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: mikeyww, usser and 138 guests