Basic Python Troubleshooting
When learning how to code in Python, most Ignition users tend to place most of their learning efforts on memorizing syntax or other aspects of the language. While being comfortable with the language is useful, there are plenty of references available: countless books and websites that describe syntax and usage already exist.
In truth, the best thing you can do to make yourself a better programmer is to learn some basic troubleshooting behaviors. While syntax examples are all over the Internet, examples detailing exactly what you want your code to do, to the extent you want it to, will be difficult if not impossible to find.
This section details how to troubleshoot a script in Ignition. You won't walk away from this section having exact answers to specific problems, but rather examples and concepts that you can apply to your own scripts.
Coding Best Practices
The following are some general and helpful best practices that can help minimize the amount of time you spend troubleshooting, as they can help better direct you to a problem.
Look for Errors
When code fails mid-execution, it always generates an error message. Where the message appears depends on where the script executed:
- Client: The Client Console will contain any errors generated in the client: press Ctrl + Shift + F7 to open the console, or using the menubar in the client to go to Help > Diagnostics then click on the Console Tab. Additionally, a red Error Box should appear with details on the error if a Component Event Handler threw the exception: Extension Functions do not generate the Error Box. If you don't see the error box then it might be minimized, or open in the background (behind the Client).
- Designer: The Designer reports errors in a similar manner to Clients: errors appear in the Designer's Console (Ctrl + Shift + c), Component Event Handlers will generate a red Error Message. Note that events some events, such as Client Startup Scripts, will not trigger in the Designer, so get in the habit of launching a Client when testing non-component scripting events.
- Gateway: Scripts on Gateway-scoped resources (Tags, Alarm Pipelines, SFCs, etc.,) will appear on the Logs page of the Status section on the Gateway. Additionally, the wrapper.log file in Ignition's installation directory will have these messages. Here are the default wrapper.log file paths for each operating system:
- Windows: Program Files/Inductive Automation/Ignition/logs
- Linux: /var/log/ignition
- Mac OS X: /Users/UserName/Documents/Ignition-osx-x.x.x/logs
What happens if I don't see any errors?
If there truly isn't an error message somewhere, but your code isn't doing what you expected, then ask yourself the following questions:
- What is your script supposed to do?
- What is it actually doing?
Question #2 is harder to answer: if you knew what it was doing, you wouldn't be stuck! The best way to answer this question is by adding print
statements to your code.
When a script doesn't perform to expectations, it can suggest a problem with the script's workflow. Some of the pages in this section can offer some suggestions on what to do.
If You Find an Error, Read It!
It is common for users that are new to scripting to see an error message, immediately close it without much thought, and then stare at their code as if the problem will politely make it self known. While looking over your code line-by-line will eventually lead you to the problem, the error messages can provide you with a shortcut to the issue. Check out some of the pages in this section for more information on reading an error message.
Use Print Statements
When testing your scripts, the print command can help you verify that your code is behaving the way it should. This allows you to reconstruct what your code did when it executed. Don't be afraid to add helpful print statements to your code.
Once your code works as expected, remember to either remove or comment out the print statements, so they don't flood the console during normal use as this makes troubleshooting other issue more difficult.
myVar = system.tag.read("folder/tag")
# printing out variables you are going to use in an if-statement later allows you to confirm that the values are what you expect them to be.
print "myVar is set to: " + str(myVar)
# Sometimes viewing the data type of the variable can prove helpful.
print "myVar is a: " + type(myVar)
# Should your code have multiple if-statements, adding a print statement before and after can show you where the flow of the script went.
print "Starting if-statement"
if myVar > 100:
# If you don't see this print statement, then your if-statement evaluated to False.
print "Inside if-statement"
doWork()
# Printing the end of your script doesn't give you any useful troubleshooting information, but if you need to trigger the script multiple times, it helps delineate each execution.
print "Script Ended"
Add Comments
While comments are useful to remind you how your code works, you can also use them to plan your script before you write any code. Break down what you want the code to do into several smaller steps, and then leave comments describing those steps in order. This provides you a chance to review the script's workflow before worrying about syntax. It also provides natural points to stop and test your code, to make sure it is doing what you think it should do.
Test Early, Test Often
Unless the script is very simple, avoid writing the entire script and then testing at the end. You may have missed an important line early on, so now you have to adjust all of the code below that line to make the script run. As mentioned above, add print statements, and run your script to make sure it is doing what you think it should.
Additionally, stopping to test your code provides an excellent opportunity to save your project. Get into the habit of saving before you execute any new code.
Avoid Hard-Coding Arguments: Use Variables Instead
Instead of doing this:
system.gui.messageBox("Hello you. Glad to see you")
Try to get into the habit of doing this:
message = "Hello you. Glad to see you"
system.gui.messageBox(message)
Simple examples like the above don't make for the best use case, but when you have a large script that references a value multiple times, it is easier to declare the value once in a variable, and then just reference the variable throughout your code. If you need to change the value later, then you can simply change it once where you initialize the variable, and you don't have to search every line of your code looking for the value.
Decide on a Naming Convention
When creating variables, try to adopt a naming convention that comes natural to you, and stick to it. If you consistently write variables using the same conventions, you will be less likely to end up with a typo when referencing that variable later in your code. Remember that code is case-sensitive, so something as simple as forgetting to capitalize a letter will cause an error.
This is especially important if you are working in a group on the same project. It's better to get together with your colleagues and agree upon some naming conventions before you write any code.
Ignition's system.* functions use the Camel Case (camelCase) naming convention. That is, the first letter of each word is capitalized except for the very first letter. We recommend that you use it for variable names because It is easy to remember to use Camel Case for both functions and variable names instead of for just one.
The Simplest Approach Really Is the Best Approach
When learning how to code, it's not uncommon to run into multiple issues that require you to find a workaround. However, these workarounds can cascade into other issues and make your script more complicated. Consider the following:
"The goal of my script is to access A and then output B... Shoot, B requires C, so I'll add C... Wait, C requires that D exists, so I'll create that... Oh, D needs interfaces E, F, and G, so let's add those in... Hmm. E needs H and I, F needs J and K, while G needs Y, Z and...A again?!?"
Take a step back and ask yourself "what is this script doing"? If you can't do that in a sentence or two, you may want to rethink the script. If the scope of the script is too large, then it considerably increases the complexity of the code, which in turn could add a plethora of problems later.
If you keep adding workarounds, but your code is not getting any closer to achieving its end goal, there may be an easier way to accomplish what you're trying to do with a different approach.