# How to trigger a gateway timer to trigger immediately but no more than four times per minute?

**URL:** <https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476>\
**Category:** Ignition\
**Tags:** ignition81, scripting\
**Created:** [June 23, 2023, 8:08pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476 "2023-06-23T20:08:47Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![bmeyers](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmeyers/32/37453_2.png) [@bmeyers](https://forum.inductiveautomation.com/u/bmeyers)\
**Post date:** [June 23, 2023, 8:08pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/1 "2023-06-23T20:08:47Z")

</div>

I have a gateway timer script that is configured to run every 15 seconds.

```auto
ds = system.tag.read("[default]Queue").value

if ds.getRowCount() > 0:
	# Get value from queue
	message = ds.getValueAt(0, "message")

	# Complete some arbitrary stuff
	specialFunctions.myFunction(message)

	# Delete value from dataset
	new_ds = system.dataset.deleteRow(ds, 0)

	# Write new dataset back to queue
	system.tag.write("[default]Queue", new_ds)
```

I have up to 100 tags feeding a queue. The purpose of the gateway timer is to just read values from that queue, take some arbitrary actions, and then delete the value from the queue. This code works ok but if you are the only value in the queue then you might have to wait 14 seconds before the your turn to execute. Is there a good way to execute immediately if you are the first value in the queue?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 23, 2023, 8:20pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/2 "2023-06-23T20:20:00Z")

</div>

The tag system is fully multithreaded and only locks what **it** needs, not what some queuing algorithm needs. You will suffer trying to do this with a memory dataset tag.

Use a proper java concurrent queue. Set it up so that only a single copy is ever constructed (using system.util.getGlobals() and `.setdefault()`). Run a timer script that polls this queue, with a timeout the same as the fastest pace you want to run.

Only place python or java native data types in such a queue. (I usually use python tuples.)

---

<div class="post-metadata">

**Author:** ![bmeyers](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmeyers/32/37453_2.png) [@bmeyers](https://forum.inductiveautomation.com/u/bmeyers)\
**Post date:** [June 23, 2023, 8:26pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/3 "2023-06-23T20:26:09Z")

</div>

Well it seems i might be doomed to suffer... can you provide an example or documentation to look at that does a "proper java concurrent queue"? And how would I accomplish this in Ignition?

Would functions readBlocking and writeBlocking help the situation?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [June 23, 2023, 8:34pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/4 "2023-06-23T20:34:18Z")

</div>

[https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/LinkedBlockingDeque.html](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/LinkedBlockingDeque.html)

It's _essentially_ a list, but with lots of safety features.

---

<div class="post-metadata">

**Author:** ![bmeyers](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmeyers/32/37453_2.png) [@bmeyers](https://forum.inductiveautomation.com/u/bmeyers)\
**Post date:** [June 23, 2023, 8:42pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/5 "2023-06-23T20:42:06Z")

</div>

Ok, so I created one in the script console.

```auto
from collections import deque

# Create an empty deque
deque_obj = deque()

# Add elements to the deque
deque_obj.append("Element 1") # Adds element at the end of the deque
deque_obj.appendleft("Element 2") # Adds element at the front of the deque

# Access elements in the deque
first_element = deque_obj[0] # Access the first element
last_element = deque_obj[-1] # Access the last element

# Remove elements from the deque
removed_element = deque_obj.pop() # Removes and returns the last element
removed_element = deque_obj.popleft() # Removes and returns the first element

# Check the size of the deque
size = len(deque_obj)

# Check if the deque is empty
is_empty = len(deque_obj) == 0
```

And I assume it lives in the global namespace (on the gateway???). But then how do I write to it and how do I read it from different locations? For example, I might want to read/write to it from my alarm pipeline and then read/write to it from a gateway timer.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [June 23, 2023, 8:53pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/6 "2023-06-23T20:53:52Z")

</div>

1. Don't use the Python standard library `deque`, use the Java standard library one. One concern here is leaking Jython memory as Ignition shuts down and starts up script lifecycles around you.
2. Creating an instance doesn't do anything, yet. It's _just_ an instance in memory. Like I said, it's just a list with some guardrails. The 'superpower' here is when you create one _on the gateway_, pairing it with [`system.util.getGlobals()`](https://docs.inductiveautomation.com/display/DOC81/system.util.getGlobals). _That_ function creates a persistent dictionary that will persist for the entire lifetime of the current local Java process (the designer, or the gateway, or the Vision client). So _on the gateway_, you create a `LinkedBlockingDeque`, put it into `system.util.getGlobals()` at some well-known key, and then _anywhere else on the gateway_, you have access to a safe concurrent datastructure that you can push values into or pull values from. Phil recommends `setdefault` for this, because it means you avoid having to think about when the object is initialized.

Something like this:

```python
# in a script library called queue
from java.util.concurrent import LinkedBlockingDeque

def getQueue():
	return system.util.globals.setdefault(LinkedBlockingDeque())

```

So _anywhere on the Gateway_, you could then use `queue.getQueue()` to retrieve \_the same instance of a `LinkedBlockingDeque()` - so you can safely put events into it from any of your 100 tags, and safely retrieve items from it from your timer script.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 23, 2023, 9:06pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/7 "2023-06-23T21:06:43Z")

</div>

I usually use this construct at the top level of a script module:

```auto
from java.util.concurrent import LinkedBlockingDeque, TimeUnit

# Capacity = 100. The new instance is thrown away if already in globals.
deque = system.util.getGlobals().setdefault('MySpecialForeverQueue', LinkedBlockingDeque(100))

def someTimerEvent(event):
	next = deque.poll(15000, TimeUnit.MILLISECONDS)
	if next:
		# handle it
# Run the above at a fixed **rate** matching the poll timeout.

```

Use [ConcurrentLinkedDeque](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/ConcurrentLinkedDeque.html) if you don't want any capacity. (It'll run a bit faster that a `LinkedBlockingDeque` that doesn't have a limit, IIRC.)

Queues are a bit faster than Deques, if you don't need bidirectional traffic.

---

<div class="post-metadata">

**Author:** ![bmeyers](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmeyers/32/37453_2.png) [@bmeyers](https://forum.inductiveautomation.com/u/bmeyers)\
**Post date:** [June 23, 2023, 9:11pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/8 "2023-06-23T21:11:50Z")

</div>

Thanks @pturmel

I was going to throw something like this into a gateway startup event script but I'll implent your timer also into the gateway timer script.

```auto
from java.util.concurrent import LinkedBlockingDeque

def getQueue():
    return system.util.globals.setdefault("my_deque_key", LinkedBlockingDeque())

# Check if the deque is already set in the globals dictionary
if "my_deque_key" not in system.util.globals:
    # Create a new deque and set it in the globals dictionary
    system.util.globals["my_deque_key"] = LinkedBlockingDeque()

# Get the deque from the global dictionary
deque = getQueue()

# Add a value to the deque
value = "Example Value"
deque.add(value)

print(deque)
```

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 23, 2023, 9:14pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/9 "2023-06-23T21:14:20Z")

</div>

> [@bmeyers](#):
>
> gateway startup event script.

You absolutely may **not** put this directly into an event script. It **must** be in a project library script module.

(Paul's is buggy. Use mine. Exactly as shown. Do not use any form of check for existence of the key in globals other than the .setdefault() operation shown.)

You do not need to use a startup event. The timer event will cause this to be created properly on first use.

Sources of queue elements that are outside the script can use `path.to.script.deque.offer()` to put stuff into the queue safely.

---

<div class="post-metadata">

**Author:** ![bmeyers](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmeyers/32/37453_2.png) [@bmeyers](https://forum.inductiveautomation.com/u/bmeyers)\
**Post date:** [June 23, 2023, 9:19pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/10 "2023-06-23T21:19:21Z")

</div>

If you ever start offering classes on coding I'll be the first to sign up. I will test this out shortly. Thank you.

---

<div class="post-metadata">

**Author:** ![bmeyers](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmeyers/32/37453_2.png) [@bmeyers](https://forum.inductiveautomation.com/u/bmeyers)\
**Post date:** [June 23, 2023, 9:56pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/11 "2023-06-23T21:56:00Z")

</div>

@pturmel

What do I pass in for the event?

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/b/5/b59d49a8f5a9eb9d6396de14840d6d636d7fd6cc.png)

I've been able to add values into the queue but not get them out.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/9/d94352921be7918f5d54c07cab082c9f41cffd76.png)

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 23, 2023, 10:05pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/12 "2023-06-23T22:05:40Z")

</div>

Ah, yes. Timer events don't have an event object. I'm not using it, so just leave it out. Empty parentheses on both ends.

The script console is running in the designer, while your gateway event is running in the gateway. Totally different processes that do not share variables, even on the same machine.

(Don't test gateway scope from the designer script console, unless you are simply using system.util.sendRequest() to trigger a gateway message handler.)

---

<div class="post-metadata">

**Author:** ![bmeyers](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmeyers/32/37453_2.png) [@bmeyers](https://forum.inductiveautomation.com/u/bmeyers)\
**Post date:** [June 26, 2023, 7:50pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/13 "2023-06-26T19:50:12Z")

</div>

I have a gateway script timer executing every 9,000 milliseconds the following:

```auto
message = queue.subDeque1()

if message:
	# Send message to radio 
	specialFunctions.text_to_speech(message)
```

Which is intended to send a message over radio 1. However, when I trigger multiple events there are times where I have to wait up to 24 seconds for the next event.

```auto
# Capacity = 100. The new instance is thrown away if already in globals.
Deque1 = system.util.getGlobals().setdefault('RadioChannel1', LinkedBlockingDeque(100))

##################################################
# Deque 1 functions
# Return value from RadioChannel1
# Call this function at a fixed **rate** matching the poll timeout.
def subDeque1():
	next = Deque1.poll(9000, TimeUnit.MILLISECONDS)
	if next:
		# Write to memory tag.
		queue.updateDeque1()
		return next

# Add value to RadioChannel1 Queue
def addDeque1(channel, message):
	Deque1.add(message)
	
	# Write to memory tag.
	queue.updateDeque1()
	

# Write RadioChannel1 to memory tag RadioChannel1.
def updateDeque1():
	headers = ["message"]
	data = []
	for val in system.util.globals["RadioChannel1"]:
		data.append([val])
		
	ds = system.dataset.toDataSet(headers, data)
	system.tag.write('[AndonCallbox]RadioChannel1', ds)
##################################################
```

Any idea what the culprit might be?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 26, 2023, 8:04pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/14 "2023-06-26T20:04:51Z")

</div>

How are you/what is calling `addDeque1` ? And why are you using `.add()` instead of `.offer()` as I recommended?

(Get rid of your dataset tag. Iterating over your Deque to populated it might clash with poll().)

---

<div class="post-metadata">

**Author:** ![bmeyers](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmeyers/32/37453_2.png) [@bmeyers](https://forum.inductiveautomation.com/u/bmeyers)\
**Post date:** [June 26, 2023, 8:07pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/15 "2023-06-26T20:07:20Z")

</div>

An alarm pipeline script block is calling addDeque1.

> And why are you using `.add()` instead of `.offer()` as I recommended?

I forgot 😅

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 26, 2023, 8:09pm UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/16 "2023-06-26T20:09:46Z")

</div>

Be sure to check the return value of `offer()` and provide an alternate path when the deque is full.

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [July 2, 2023, 6:17am UTC](https://forum.inductiveautomation.com/t/how-to-trigger-a-gateway-timer-to-trigger-immediately-but-no-more-than-four-times-per-minute/75476/17 "2023-07-02T06:17:29Z")

</div>

While it doesn’t _actually_ matter in this case, because scoping, I wouldn’t use `next` as an identifier: it’s a Python builtin.  
It shouldn’t interfere with anything here, but it just feels weird reading `next = something`.
