# Creating a Visual Feedback Dashboard that Executes a Ping Script Continuously

**URL:** <https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864>\
**Category:** Ignition\
**Tags:** scripting, perspective\
**Created:** [October 31, 2022, 9:46am UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864 "2022-10-31T09:46:44Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tyler\_Ellis](https://avatars.discourse-cdn.com/v4/letter/t/ad7895/32.png) [@Tyler\_Ellis](https://forum.inductiveautomation.com/u/Tyler_Ellis)\
**Post date:** [October 31, 2022, 9:46am UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/1 "2022-10-31T09:46:44Z")

</div>

Hello all,

Relatively new to designing in Ignition, so I want to apologize in advance if I come off as vague when discussing my project. I will be periodically checking this thread to answer any questions or provide feedback on the suggestions.

**The Details of the Project**

Here is the dashboard being displayed, the text is present showing the device name, I am only crossing it out for privacy reasons:

![ui](https://us1.discourse-cdn.com/inductiveautomation/original/3X/b/6/b60fa0c1165495ff8a80c77756724d845c04a9c1.jpeg)

As the title suggests, I'm creating a view in perspective that will show all of our connected devices' current state (connected or not). This script that pings the device is set to run every 10 seconds via a previously configured gateway script that counts seconds, bound to a custom property called "Seconds" that modifies the value of another property called "PingEvery10":

![dashcustomprops](https://us1.discourse-cdn.com/inductiveautomation/original/3X/7/8/7834986f1310656925bd430c6c63d9d84d2c6fcc.jpeg)

here is the change script to determine every 10 second increment:

```auto
if self.custom.Seconds % 10 == 0:
	self.custom.PingEvery10 = self.custom.PingEvery10 + 1

```

the change script of "PingEvery10" to reset to 0 after 1 day's worth of time has passed (6 time per minute \* 60 \* 24 = 8640 times), the custom property that every device component is referencing:

```auto
if self.custom.PingEvery10 > 8640:
	self.custom.PingEvery10 = 0

```

Lastly, every device I currently have configured (only 7 devices in comparison to thousands that will be needed) is using a button component with two custom properties: "ip\_Address" and "pinger". Every instance of "pinger" has a binding to the "PingEvery10" custom property on the view. Here is the change script that executes when "pinger"'s value changes, assigning a specified style class based on the result:

```auto
def ping(address):
	import subprocess
	command = 'ping', '%s' % address
	process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
	process_out = process.communicate()[0].strip().split('\r\n')
	print process_out[1:5]
	if any("TTL" in word for word in process_out):
		self.props.style.classes = "Connected"
	elif any("Pinging" in word for word in process_out):
		self.props.style.classes = "NoComms"
	else:
		self.props.text = "Ping Execution Error"
		self.props.style.classes = ""
	
address = self.custom.ip_Address
ping("%s" % address)

```

**The Problem**

Now for the reasoning for the thread. When I first created this script I had one device to trial the script for success. The typical response time for changes to show up in the session view was about 30 - 40 seconds, which is satisfactory in terms of what I was expecting; however, as more devices are added, that time gets much longer. With 7 devices, the changes take about 3 minutes to show up. This makes me theorize that it is pinging every device before showing updates to any single one.

I've read some similar scenarios, most of which point to using a gateway script with a system.util.invokeasynchronous command. I have not yet tried this due to my lack of knowledge on gateway scripts.

**Related Questions**

- If the above-mentioned system utility is the way to resolve this, then how do I "pass" the "ip\_Address" of each device button to the gateway script to be executed.
- If the response time is able to be faster, what ways could I improve it?
- If/When this is working, how would I "future proof " it for adding new devices? I'm assuming there would be a way in which to make an "Add New Device/Remove Device" tab that could collect information from the user and generate a button in the next available spot based on the details applied or conversely remove the device.

Thank you all in advance for your time! I am open to any other recommendations you might have to help me make this great.

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [October 31, 2022, 10:12am UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/2 "2022-10-31T10:12:05Z")

</div>

I have done a similar job and I'll see if I can dig out the details.

In the meantime, have a look on the Ignition Exchange and [search for "ping"](https://www.inductiveautomation.com/exchange?search=ping). There are a couple of projects there which may suit or give you some ideas.

'Network Device Status' looks like what you want.

> **[Ignition Exchange](https://www.inductiveautomation.com/exchange/2192/overview)**
>
> Discover, share, and download community made Ignition resources in collaboration with your organization or the Ignition community.

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [October 31, 2022, 12:39pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/3 "2022-10-31T12:39:38Z")

</div>

Also instead of shelling out check out this classic Ignition answer using java built-ins to ping -

> [@Script to ping computer](https://forum.inductiveautomation.com/t/script-to-ping-computer/16612/10):
>
> Shelling out for this makes me feel very icky, especially when Java already has the native ability to do this and that’s an invitation to accidentally send horrible things to the command line. Why not just: def ping(address,timeout=2000): from java.net import InetAddress ip = InetAddress.getByName(address) return ip.isReachable(timeout) This will throw UnknownHostException if the host can’t be looked up, or IOException if the network is broken, so you can handle that case as well, instead o…

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [October 31, 2022, 1:23pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/4 "2022-10-31T13:23:41Z")

</div>

`system.device.listDevices()` returns a dataset which has a `State` column, why isn't that sufficient for this?

I assume that `State` is equivalent to the `Status` from the `Device Connections` status page on the Gateway.

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [October 31, 2022, 1:47pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/5 "2022-10-31T13:47:28Z")

</div>

> [@lrose](#):
>
> system.device.listDevices()

One reason might be that the OP is testing connection to devices that are not OPC connections or Ignition devices. I have an Ignition application that tracks our IP address asignments for PLCs, HMIs, robots, vision systems, and various other devices. Having a ping status for each of these is very useful but most won't appear in Ignition's device list.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [October 31, 2022, 1:59pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/6 "2022-10-31T13:59:34Z")

</div>

> [@Transistor](#):
>
> One reason might be that the OP is testing connection to devices that are not OPC connections or Ignition devices.

Yeah, I guess, that since the OP didn't specify why it didn't work, I wanted to insure that they weren't overcomplicating something for which there is already a built in system function.

If a ping is needed, then great, if not then don't make it harder than it should 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:** [October 31, 2022, 2:40pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/7 "2022-10-31T14:40:02Z")

</div>

> [@bkarabinchak.psi](#):
>
> using java built-ins to ping

Do note that this requires raw packet permissions. In linux, if not root, your user needs `CAP_NET_RAW`, which can be easily added (with `AmbientCapabilities=`) to your SystemD service with an override file.

---

<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:** [October 31, 2022, 4:25pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/8 "2022-10-31T16:25:30Z")

</div>

Also, worth calling out:  
You don't want to run this script directly from Perspective, otherwise each Perspective session you open is going to increase comm load unnecessarily.

---

<div class="post-metadata">

**Author:** ![Tyler\_Ellis](https://avatars.discourse-cdn.com/v4/letter/t/ad7895/32.png) [@Tyler\_Ellis](https://forum.inductiveautomation.com/u/Tyler_Ellis)\
**Post date:** [November 2, 2022, 6:26am UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/9 "2022-11-02T06:26:21Z")

</div>

That is correct,

This would be pinging devices that are not all OPC or I/O type. Examples of this would be desktop computers, switches, scanners, etc.

---

<div class="post-metadata">

**Author:** ![Tyler\_Ellis](https://avatars.discourse-cdn.com/v4/letter/t/ad7895/32.png) [@Tyler\_Ellis](https://forum.inductiveautomation.com/u/Tyler_Ellis)\
**Post date:** [November 2, 2022, 6:31am UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/10 "2022-11-02T06:31:10Z")

</div>

> [@Transistor](#):
>
> I have done a similar job and I'll see if I can dig out the details.
> 
> In the meantime, have a look on the Ignition Exchange and [search for "ping"](https://www.inductiveautomation.com/exchange?search=ping). There are a couple of projects there which may suit or give you some ideas.
> 
> 'Network Device Status' looks like what you want.  
> [Ignition Exchange | Inductive Automation](https://www.inductiveautomation.com/exchange/2192/overview)

Thank you, this option looks to be a cleaner version of the code I had to open up the command shell. Looking at it, I'm curious about what you implemented with the project you referenced to allow this to work for many devices. I though maybe converting the parameters on the root container from objects to an array of objects (one for each ip) was the answer, and it may be, but I am struggling to get it to function with more than one device.

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [November 2, 2022, 10:06am UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/11 "2022-11-02T10:06:23Z")

</div>

Here you go, for what it's worth.  
I run this on a gateway timer event set to 30,000 ms. This timer value depends on the number of pings to be executed so may need to be extended as each ping is done sequentially.

The list of devices to be pinged is obtained from a database query.  
The results are written to a memory tag. A couple of other tags keep track of the script status.

```python
# Gateway Timer Script - Ping test
# Author:	Transistor
# Date: 2020-11-28
# Desc: A series of ping tests is carried out on the devices included in resultsTag.
# The ping tests are carried out from the gateway - not the client.
# The ping tests are carried out by a gateway timer.
# The ping test timer MUST BE RUN AS A DEDICATED THREAD.
import datetime
import os # Required for ping operation
import re # Required to strip leading zeros from IP address octets.

# Run a query on the routers table every 30s or so. Be awware that enough time should be allowed to run the maximum number of pings.
# Generate a dataset of all the form
#[
#['10.166.28.200', 'Area 1', 'Router', 'Machine 1'],
#['10.166.28.201', 'Area 1', 'PLC', 'Machine 1'],
#... ]
# from the router table. Initialise the dataset with the last seen and status columns.
pingList = system.db.runQuery("SELECT wan_ip, location, device, system, 0 AS 'last ping', 0 AS status FROM routers WHERE ping > 0", 'dbLAN')

# Read in the resultsTag. We'll need this to get the previous ping time
prevScanResults = system.dataset.toPyDataSet(system.tag.read('[default]BAPL_ping/Results').value)

# Prepare the dataset for output.
headers = ['wan_ip', 'location', 'device', 'system', 'last ping', 'status']
data = []

# Record the script start time.
startTime = datetime.datetime.now()
system.tag.write('[default]BAPL_Ping/LastPingStartTime', startTime)
system.tag.write('[default]BAPL_Ping/PingBusy', True)

for row in pingList:
	ip = row['wan_ip']
	shortIp = re.sub('(^|\.)0+(?=[^.])', r'\1', ip) # Strip the leading zeros from each octet or Windows ping won't find it!	
	# For Windows: -n 1 = ping once. -w 200 = timeout value.
	# For Linux: -c 1 = ping once. -w 1 = 1 s timeout.
	r = os.system("ping -n 1 -w 150 " + shortIp) == 0
	if r: # Good result.
		lastPing = 'OK at ' + str(datetime.datetime.now())[10:19]
		status = 1
	else: # No response.
		lastPing = 'Unknown' # Default.
		status = -1 # Default.
		for row_p in prevScanResults:
			if row['wan_ip'] == row_p['wan_ip']:# This device was on the previous scan list.
				lastPing = row_p['last ping']
				status = 0
				break

	data.append([row['wan_ip'], row['location'], row['device'], row['system'], lastPing, status])

# Prepare the dataset for output.
dataOut = system.dataset.toDataSet(headers, data)
# Update the resultsTag for availability for all clients.
system.tag.write('[default]BAPL_Ping/Results', dataOut)

system.tag.write('[default]BAPL_Ping/PingBusy', False)
# Record the completion timestamp.
system.tag.write('[default]BAPL_Ping/LastPingEndTime', datetime.datetime.now())
timeDelta = datetime.datetime.now() - startTime
system.tag.write('[default]BAPL_Ping/TotalPingTime', float(timeDelta.seconds)) # Convert to seconds.

```

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [November 2, 2022, 12:33pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/12 "2022-11-02T12:33:02Z")

</div>

Looks good my only pet peeve is importing and using `datetime` instead of using `system.date.*` functions.

---

<div class="post-metadata">

**Author:** ![Transistor](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/transistor/32/3380_2.png) [@Transistor](https://forum.inductiveautomation.com/u/Transistor)\
**Post date:** [November 2, 2022, 12:43pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/13 "2022-11-02T12:43:24Z")

</div>

> [@bkarabinchak.psi](#):
>
> using `datetime` instead of using `system.date.*`

I was young then. The code was put together from various bits of information from the web. I've learned all my Python on Ignition. I've since learned to use `system.date.*` but never updated this. Thanks for the alert!

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [November 2, 2022, 12:50pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/14 "2022-11-02T12:50:04Z")

</div>

Haha fair enough I too have old scripts using datetime that are in production somewhere 🙃

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [November 2, 2022, 1:29pm UTC](https://forum.inductiveautomation.com/t/creating-a-visual-feedback-dashboard-that-executes-a-ping-script-continuously/65864/15 "2022-11-02T13:29:08Z")

</div>

Might want to scrub the comments to remove your private IP Addresses?

Should probably also, convert all of those `system.tag.write()` into `system.tag.writeAsync()` or combine them all into a single `system.tag.writeBlocking()`. And the `system.tag.read()` to the non-deprecated `system.tag.readBlocking()`
