Troubleshooting Workflow
Troubleshooting a script is an iterative process. Since the script stops executing at the first error, we won't see if there are any other errors on later lines unless, of course, we spot them ourselves while writing the code. As a result, we will have to keep trying our script until it executes successfully.
It is important to understand that scripts always execute from top to bottom, and each line must complete before the next line may move on. In the event, our code returns an exception, we can assume that our troubleshooting process should always start at the line reported in the error, and then work up until we find the problem.
On this page, we will take an in depth look at a script with multiple problems, and work through each one. Note, that this is not a comprehensive list of all possible types of exceptions that could occur when writing a script, but instead, this page attempts to demonstrate the troubleshooting process.
To learn more about troubleshooting specific script errors, refer to the following sections:
Scenario Overview​
Here we have a window with two components: a Button and Power Table. The purpose of the button is to find which cell in the Power Table the user selected, and write the value to a Tag.
The code on the button is listed below.
# Create a variable that references the Power Table
table = event.source.parent.getComponent('Power Table')
# Find the cell the user currently has selected, and store the value in a variable
userSelectedValue = table.data.getValueAt(table.selectedrow, table.selectedColumn)
# Write the User Selected Value to a tag
system.tag.write("Scripting/ButtonError/WriteTarget", userSelectedValue)
First Error - NoneType Object​
When the button is pressed, we are presented with an error. The Message tab in the Error Box describes the Event Handler that encountered the exception.
The Details tab tells us where to start looking, specifically the line number where the exception occurred as well as the error message. Line 5 is referenced, so the troubleshooting process should start there.
Exception Error Explained​
When an error message refers to a NoneType object, that simply means a null, or None in Python. The word 'attribute' is used to reference a property on an object. In this case, this means the script was trying to access the 'data' property on nothing. The message is telling us that it couldn't find a property named 'data' on a nothing, which is correct since NoneType objects don't have any properties named 'data'.
If we were to reword this message to something a bit more straightforward, it would say the following: "I tried to access the 'data' property on 'null', but it doesn't have a property by that name."
What Should We Look For?​
If your exception is referring to a NoneType object, then some line of code probably tried to reference something else (i.e., Tag value, property, another variable, etc.), but couldn't find anything at the location you specified. It is also helpful to note what attribute was mentioned in the error, which was data. If we look at our code, we see the following on line 5.
userSelectedValue = table.data.getValueAt(table.selectedrow, table.selectedColumn)
Based on this information, we should take a closer look at the table variable since our code is specifying 'data' as an attribute of the table. The error states that it could not find the data attribute on a NoneType object, but our code only references data in regards to the table object, so maybe there is something wrong with how that table object was referenced or initialized. If we look further up in our code, we see that table
was initialized on line 2:
table = event.source.parent.getComponent('Power Table')
Since this script was placed on the Button component's actionPerformed event, we can trace back to the source of the issue:
- event = actionPerformed event
- source = Button this script is placed on
- parent = Container that the Button was placed in. Based on the Button's position in the window, this appears to be the Root Container.
- getComponent('Power Table') = Should return a reference to a component named 'Power Table' directly inside of the Root Container, assuming one exists. If we take a look at the Project Browser, we see the following:
There is no component named 'Power Table' in the Root Container. However, there is a Power Table component named 'My Table', so it appears someone renamed the component, which caused our script to initialize the variable table as a 'NoneType' instead of a reference to a component.
Solution​
To fix this issue, we can simply update our code to use the new name of the Power Table, which is 'My Table'. We can type this in manually, but since it is case sensitive, it must match exactly. It may be easier to find the Name property on the component, copy the name to the system clipboard (Ctrl-C), and paste it (Ctrl-V) into the script. Our code now looks like the following:
# Create a variable that references the Power Table
table = event.source.parent.getComponent('My Table')
# Find the cell the user currently has selected, and store the value in a variable
userSelectedValue = table.data.getValueAt(table.selectedrow, table.selectedColumn)
# Write the User Selected Value to a tag
system.tag.write("Scripting/ButtonError/WriteTarget", userSelectedValue)
Second Error - Checking Attributes​
If we try our button again, we get the following exception error.
Exception Error Explained​
Like the last exception, the problem has to do with our code trying to access an attribute (property) on an object, but the given attribute doesn't exist on the object.
What Should We Look For?​
Again, the exception refers to line 5, but note that the error mentions a different object and attribute this time. Without knowing what a com.inductiveautomation.factorypmi.application.com
object is, we can tell that our script is trying to reference an attribute named selectedrow
. This gives us a starting point for troubleshooting the error. If we check line 5, we see the following:
userSelectedValue = table.data.getValueAt(table.selectedrow, table.selectedColumn)
We see table.selectedrow
as the first parameter being passed to the getValueAt() function. We checked into the table
variable earlier, so this doesn't necessarily mean that the variable is the problem, although there is no harm in double checking, especially if you have not personally verified the variable. Assuming that the table variable is correct, let's check the attribute. We can check the Property Editor in the Designer to find a matching property, or head over to the Power Table page in the manual and check the property reference. We can see that there is a Selected Row property on the Power Table, but it should be spelled "selectedRow
" with a capital "R".
We also know that the getValueAt() function takes a row index as the first parameter, so it would make sense that our code has a typo.
Solution​
We should fix the typo in the attribute name.
userSelectedValue = table.data.getValueAt(table.selectedRow, table.selectedColumn)
Third Error - ArrayIndexOutOfBounds​
After making our most recent change, the button works great! Users are able to select a cell, and write to the Tag. The one exception, is if the user does not have a cell selected before clicking the button. In this case, the following error occurs:
Exception Error Explained​
Our code attempted to look up something in a collection of some sort (i.e., sequence, dataset, etc.,) by index, but was told to check index -1, which is out of bounds. Indexes in Ignition are typically zero-based, meaning they start at 0, and increment from there, so an index of -1 doesn't exist. Commonly, when a property in Ignition refers to index -1, that means nothing is selected, so our code failed because nothing was selected.
What Should We Look For?​
Fortunately, the exception told us what the issue is: the user did not have anything selected before pressing the button, so we don't have to search further.
Solution​
The solution to this issue is more open ended, as there are many ways to suggest to our user that they need to select a cell in the table first. The Selected Row and Selected Column properties on the Power Table will have -1 values if none of the cells are selected, so we could use an 'if' statement checking the values of one of those properties. Our new code could look like the following:
# Create a variable that references the Power Table
table = event.source.parent.getComponent('My Table')
# Make sure a cell in the Power Table is selected first
if table.selectedRow != -1:
# Find the cell the user currently has selected, and store the value in a variable
userSelectedValue = table.data.getValueAt(table.selectedRow, table.selectedColumn)
# Write the User Selected Value to a tag
system.tag.write("Scripting/ButtonError/WriteTarget", userSelectedValue)
# If a cell isn't selected, then let the user know
else:
system.gui.messageBox("Please select a cell in the table first!")