I am trying to make a timer in perspective that can start timing when a toggle switch is toggled, and will stop timing when a toggle switch is untoggled. The timer needs to maintain its time, and not reset when the switch is untoggled. Additionally, it needs to resume timing again if the toggle switch is toggled a second time.
And the most complex part: I need the timer to not use tags, as I need it to be specific to a session, rather than to the entire gateway. This was multiple devices can connect and have separate timers, instead of everyone seeing the same one.
I have tried many things, such as using custom properties and scripting, and nothing I did worked. I was wondering if anyone knew a simple way to do this. Thanks.
Use session custom properties and bindings tied to the custom prop. The easiest way Ive found is to write the value to a database with the user information. Utilize on startup and change scripts when writing to the DB. You can query the information and write to the session prop on startup to get the last value.
This will allow you to have persistence across sessions and each user will have its own time.
Before offering any advice, a couple some questions:
-
What do you want if a user opens another browser tab? Separate timer or shared timer?
-
What do you want to happen if a tab is closed while the timer is running?
-
What value should the timer have if a user returns to this project after disconnecting for an hour? Or several hours? A day? Several days?
3 Likes
- I would want any tabs on a certain device to show the same timer, but tabs on different devices to show different timers. So a tab on PC1 would be different than a tab on PC2, but two tabs on PC1 would show the same timer.
- If possible, the timer should continue to count up, even if the tab is closed while the timer is running. That way a user could run the timer, go do another task, and come back and stop the timer later.
- Going along with your second question, the timer keeps timing as long as the toggle switch is on, and only stops timing after the toggle switch is off, so if it has been 12 hours since the timer started timing, the value would be 12 hours, even if the user was away for most or all of that time.
Hope this helps clarify, and thanks for any advice you can provide.
Yes, that helps. You will need to use a database to hold last changed information using the session's device ID as a key. Every time the toggle changes, you will need to do arithmetic on the timer, current timestamp, and prior state to update the database with a "running" boolean, and either an arithmetic base timestamp (for running) or a prior accumulation (for not running).
The user display for the timer would show prior accumulation when not running, and show deltaSeconds({baseTimestamp}, now())
when running. With formatting into interval style (days HH:MM:SS).
Currently I have two custom properties associated with a view I have. (screenshot attached) The first custom property "isTiming" is a bool, and is controlled by the toggle switch. When the switch is toggled, isTiming is true. The second custom property is "timingValue," (integer) which has this expression binded to it:
if({[~]isTiming.value},if(IsGood({[~]timerValue.value}),
{[~]timerValue.value},0)+1,if(IsGood({[~]timerValue.value}),{[~]timerValue.value},0))
And then I have a label binded to timingValue, so that it shows its current value. The issue with this setup is I have an error saying:
"If function got a null first argument"
![Screenshot 2024-07-16 145643](https://global.discourse-cdn.com/business4/uploads/inductiveautomation/original/3X/1/8/185b4a1eef85036b7cf5b846912af3bb9ca20e81.png)
With that background aside, I assume you meant to write the timingValue to the database, but I was wondering how you would do that at a certain interval, such as every 1second, which is ideally what I would want for the timer to display a live value.
Thanks
Yeah, no. Don't do that. Accumulating integers is imprecise, and won't work when the session goes down. Store actual datetime objects to permanent storage, indicating the start of "running". Then the UI just has to show the live delta from that.
Only use integer state for prior accumulation when not running. When you have prior accumulation, and are switching back to running, use that prior value to shift now()
backwards that much so following computations include the prior time.
You need a database table with four columns: device ID (varchar, I think), running (boolean), start_ts (timestamptz), and prior_millis (bigint).
Use a named query binding with a device ID parameter to retrieve the last known state into the session.
How would I go about getting the device ID? Is that stored somewhere in the view, or is it external to that?
It is a session property. Stored in devices in a persistent cookie, or for mobile or workstation, in a more durable form (IIRC).
If your users have access to multiple different browses, they will be considered different devices.
Ok, I will have to play around with that.
But thanks for all the help!