The example I gave you above does what you're asking for, but in an event driven way.
Instead of one single script controlling everything (which means locking up an entire OS thread, busy-waiting for 30 seconds - hopefully you can see how that won't scale as you have more and more sessions running and more and more audio clips potentially playing), you're breaking down the control into smaller pieces of logic, and the only scripts and expressions involved will be event driven and complete in milliseconds.
I literally posted an example you can copy and paste directly into your project, but here's some more detail. There's exactly 3 things involved - one tag binding, one expression binding, and one property change script. All of them are basic, fundamental tools you'll have to become comfortable with in Perspective development.
In step 2, I'm just using a simple direct tag binding to my test boolean trigger tag:
In step 3, I'm right clicking the condition
property and selecting 'Add Change Script':
The script I'm adding is just this: Changing the value of the
timestamp
custom property to the new value that was just written to the tag. This should be ~identical to the current time, which you could also retrieve via
system.date.now()
- they're equivalent.
def valueChanged(self, previousValue, currentValue, origin, missedEvents):
self.custom.timestamp = currentValue.timestamp
In step 4, I'm adding that expression, the most complicated piece of this whole thing. Expressions are, by definition, simple units that unconditionally return some value. In this case, we're only ever going to return true
or false
, because we're using this expression to control the play
property.
{this.custom.condition}
&& secondsBetween({this.custom.timestamp}, now(1000)) % 15 <= 2
There's two elements to the expression
{this.custom.condition}
is just a reference to the existing custom property. This, combined with the subsequent &&
operator, combines to a boolean logical AND operation - thus our expression will always evaluate to true
or false
.
secondsBetween({this.custom.timestamp}, now(1000)) % 30 <= 2
Walking this from the innermost piece out, you've got:
now(1000)
. This just means the whole expression will poll at the given rate. This is the most "magic" piece of this operation, and generally falls into the "you just have to learn this" category. See also: now | Ignition User Manual
secondsBetween({this.custom.timestamp}, now(1000))
. As the name implies, it's just giving you the integer seconds between two timestamps. This delta forms the basis of the following logic.
% 30
is using the modulo operator to give you the remainder of a division operation. So 1 mod 30 = 1
, 30 mod 30 == 0
, 31 mod 30 == 1
, 45 mod 30 == 15
, and so on. The end result is a constant sawtooth ramp - we go from 0 to 30, then drop back to 0.
<= 2
then, check whether that modulo result is less than or equal to 2. Thus, for 2 seconds out of every 30, the second half of the expression will be true.
Here's the same logic, except with a 15 second modulo to make the recording a little shorter, and with all the intermediate logic broken out in separate custom properties: