Premise addtimer mishap? caught in debugview and repeatable...

Motorola Premise

etc6849

Senior Member
I'm working on a module where I really need a 1 second or less timer.

My code works by treating the timer as a watchdog. The simple two line example scriptmacro is attached in a jpeg. You can see in the jpeg that the timers legnth is not honored, eventhough it is 1 second and the help file specifies a timers units to be in seconds.

To run this example, I repeatedly triggered the scriptmacro with a mouse.
 

Attachments

  • trigger_misfire.JPG
    trigger_misfire.JPG
    74.8 KB · Views: 26
Example two: a simple one line of code that should result in a one second timer being triggered 5 times. However, the first trigger happens in less than .35 seconds?
 

Attachments

  • one_second_timer_issue.JPG
    one_second_timer_issue.JPG
    44.2 KB · Views: 20
I duplicated the test conditions and it certainly does seem like the first time period can be less than the requested amount. Subsequent periods appear to be correct but the first one is less than what is requested.

It looks like a bug. If the time resolution is milliseconds, the error is detectable. Maybe when they tested the code they used a time resolution of seconds so they never saw the mistake.

For most scripts, this bug is insignificant because there's not much need for a delay of precisely one second (sub-second delays are available in Macro commands). However, when writing a device driver, there is a greater need for very short intervals. Fortunately, thus far I haven't encountered any problems with this limitation but it is clear that any attempt to perform contact de-bouncing, or button hold periods, can become problematic.

I suspect they envisioned that complex drivers would be developed using the HSDK (i.e. in C++) and that Module-based drivers would handle simpler devices. Oh well.
 
I tried using addEvent to see how it would react, but kept getting errors (maybe because I was calling addEvent from a home script?). Addevent is a different type of timer that creates a timer inheriting from the Timer class. Addtimer creates a timer that inherits from the simpletimer class.

Despite not being able to use addevent, I've created a nifty work around that always gives me .53 seconds! I'm curious how well this works on other machines. You just create a new simple timer set to 0 seconds under the timer folder and only let it call any scripting once count = 5. I'm hoping someone with debugview installed will give it a try.

The xdo is attached. Import the xdo under modules, open debugview then go to the timer and set trigger count to 0. This should start the timer and you should see the results in debugview. It's 100% repeatable on my machine. Also see picture for guidance.
 

Attachments

  • simpleTimer_500ms.JPG
    simpleTimer_500ms.JPG
    77 KB · Views: 18
  • simpleTimerTest.zip
    843 bytes · Views: 16
That's an interesting discovery. I would've never thought of trying a time interval of zero.

The attached XDO (TestTimer) is another simple timer that runs one hundred times using an interval of zero. I wanted to learn what is the actual time interval since it obviously cannot be zero. You suggested it is approximately 0.1 seconds (i.e. 5 intervals runs for 0.5 seconds).

I ran TestTimer three times and put Debugview's results into Excel. I calculated the actual time intervals and graphed all three trials in a single scatter chart (see attached image).

You can see that many of the interval values are clustered around a value of 0.11 seconds and supports your results. However, what is interesting is that after 50 repetitions the interval values tend to vary (as low as 0 and as high as 0.23 seconds). In fact, the first ten repetitions are not as consistent as those between 10 and 50.

This is all beginning to feel like quantum physics where we can only talk about an electron's position in terms of probabilities! A rough rule-of thumb appears to be that a time interval of zero is interpreted as approximately 0.1 seconds (most of the time). :D

Last of all, Premise offers another kind of timer in the way of a DelayMacro. A SimpleTimer does not interrupt the flow of program execution and runs in parallel to the main script. The main script can create a timer and then immediately proceed to do other things. When the SimpleTimer expires, it runs its payload.

In contrast, a DelayMacro immediately pauses the flow of program execution. It behaves like VBscript's "Sleep" command and can be defined in milliseconds. The attached example contains a sequence of Script and Delay macros in Default > Macros > MacroFolder. Run the ScriptMacro located in Default > Macros > MacroFolder2. It simple runs all of the macros, in sequence, that are located in MacroFolder.

Debugview will show the results and you'll see a 250 ms delay (+/- 5%) between each ScriptMacro.
 

Attachments

  • TestTimer.zip
    878 bytes · Views: 13
  • scatter_diagram.png
    scatter_diagram.png
    33.8 KB · Views: 23
  • DelayMacros.zip
    992 bytes · Views: 18
Thanks for the information; it sounds like addtimer should be reliable enough if I use the 0 interval idea... I'm not sure I know how to use DelayMacro for the press/release/hold functionality of the W800 driver :D The delay macro seems very accurate compared to addtimer.


Although the code isn't clean, buttonState is much more responsive with the faster release value.

Process command from the remotes class:
Code:
'process the address command
'find remotes by searching for items of RFButton type that match the address
for each oRFButton in this.GetObjectsByTypeAndPropertyValue(Schema.Modules.WGLDesigns.Classes.RFButton.Path, "Address", method.Address, true) 
	'clean the id
	oRFButtonObjectID = oRFButton.ObjectID
	oRFButtonObjectID = Replace(oRFButtonObjectID, "-", "")
	oRFButtonObjectID = Replace(oRFButtonObjectID, "{", "")
	oRFButtonObjectID = Replace(oRFButtonObjectID, "}", "")

	'remove existing timer
	system.removeTimer "ReleaseTimer_" & oRFButtonObjectID
	
	'system.removeTimer "ReleaseTimer"
	'don't force command because native X10 driver does not repeat Press and Hold 
	'unless button is physically released for one second (verified in debugview)
	if oRFButton.ButtonState = 0 then
		oRFButton.ButtonState = 1
	end if
	
	'get path in dot format
	oRFButtonPath = Right(oRFButton.Path,Len(oRFButton.Path) - 6)
	oRFButtonPath = Replace(oRFButtonPath, "/", ".")
	oRFButtonPath = Replace(oRFButtonPath, " ", "_")

	'set release if nothing received for this button in the next ~.5 seconds
	system.addTimer 0, "on error resume next: if " & "Modules.Default.Timers.ReleaseTimer_" & oRFButtonObjectID & ".TriggerCount = 4 then " & oRFButtonPath & ".ButtonState = 0", 20, "ReleaseTimer_" & oRFButtonObjectID
next

set oRFButton = nothing

RFButton's OnChangeButtonState script:
Code:
if sysevent.newVal = 1 then
	'toggle status
	if this.Status = false then this.Status = true else this.Status = false
	system.addTimer 2, "on error resume next: if this.ButtonState = 1 then this.ButtonState = 2",1,"HoldTimer_" & this.ObjectID
	set oTimers = nothing
end if
 
VBScript's Replace command is a slow string-processing command and it's being employed three times in the following code snippet:
Code:
	'clean the id
	oRFButtonObjectID = oRFButton.ObjectID
	oRFButtonObjectID = Replace(oRFButtonObjectID, "-", "")
	oRFButtonObjectID = Replace(oRFButtonObjectID, "{", "")
	oRFButtonObjectID = Replace(oRFButtonObjectID, "}", "")

	'remove existing timer
	system.removeTimer "ReleaseTimer_" & oRFButtonObjectID
You can streamline it using Premise's GetUniqueID method:
Code:
	system.removeTimer "ReleaseTimer_" & system.GetUniqueID(oRFButton.ObjectID)
Example:
Let's say "oRFButton.ObjectID" returns "{F9A14201-FC77-4819-AABC-2770E54A4303}".
"system.GetUniqueID(oRFButton.ObjectID)" will return: "IDF9A14201FC774819AABC2770E54A4303"

The following code snippet isn't clear:
Code:
	'toggle status
	if this.Status = false then this.Status = true else this.Status = false
The comment indicates the code will toggle the Status property. Toggling means to flip to the alternate state. To toggle a Boolean value, simply use:
Code:
this.Status = not this.Status
If all you want to do is ensure Status is always true then simply use:
Code:
this.Status=true
If it is false, it'll be set to true. If it is already true, it'll remain true.
 
Back
Top