Design Like a Pro: Scripting Best Practices

Using Coding Effectively to Customize Industrial Projects

66 min video  /  58 minute read View slides

About this Webinar

Scripting is a powerful tool. If you know how to use scripting properly, you can extend the capabilities of a software package beyond what is built into it. But like any powerful tool, scripting must be used responsibly. If you don’t approach scripting in the right way, it can backfire and cause damage to your project. By learning and applying best practices, you can use scripting effectively to achieve your ultimate goal.

In this webinar, the scripting experts from Inductive Automation will cover general best practices  that will help you add flexibility and customization to HMI, SCADA, IIoT, and other industrial applications. Some specific tips about using scripting in the Ignition platform will be included as well.

Learn more about:

• Common scripting pitfalls and how to avoid them

• The best programming languages to use

• Things to consider before using scripting

• How scripting environments work

• Scripting timesavers

• And more

Webinar Transcript

Don: Well, good morning everyone, and welcome to our webinar, Design Like a Pro: Scripting Best Practices, Using Coding Effectively to Customize Industrial Projects. Just to introduce myself briefly, I'm Don Pearson with Inductive Automation. I'll be serving as the moderator for today's webinar, and I will introduce the presenter of the webinar in just a couple of minutes. On today's agenda, we'll start by introducing you to Inductive Automation and the Ignition software platform, then I'll introduce our presenter, who will share some general principles that you should always remember when scripting, such as when you should use it, when you shouldn't use it, adding comments to your code and so forth. And then he'll give us some specific best practices such as variables, defining scripting functions, loops, threading, and so on. The Q&A, that we'll have at the end as we always do, a quick background on our company, Inductive Automation has been in business since 2003. Since then, we really are very pleased to say that industrial organizations now in over 100 countries have chosen our Ignition software for their HMI, SCADA, MES and IIoT needs. We have over 1500 integrators who have joined our integrator program, who are deploying those solutions in those countries. Ignition, it's used and it's trusted, really by thousands of companies, including 44% of Fortune 100 and 26% of Fortune 500, they have utilization projects with Ignition.

Don: If anybody wants more information on Inductive Automation or Ignition, they can go to the Inductive Automation website and go to the About Us section and can learn more. As I said, we have companies all over the world, a lot of leading companies and organizations that really cross virtually every industry, oil and gas, water and wastewater, food and beverage, government, transportation, packaging, and really, because it's an industrial application platform, it really is, it gives you that sort of integrated development environment where you can create the solution you want, so people with vertical expertise across multiple industries are certainly finding solutions with Ignition for those various industries. There's a couple of reasons why I think that's the case, I'm just gonna mention these bullet points about the Ignition platform and the business model, because our signature product is called Ignition, it's the world's first truly universal industrial application platform. As I mentioned, it's an integrated development environment for you to work with, for your HMI, SCADA, MES and IIoT needs. Unlimited licensing model, which means unlimited tags, clients, it's a server license, you get unlimited connections, unlimited projects, unlimited concurrent designers. So these are all things which accelerate the ability to innovate with the platform, it has a cross-platform compatibility based on IT standard technologies.

Don: It's a very scalable server client architecture, web-based, web-managed, web-launched designer and clients, modular configurability, which allows you to scale up, scale out, do what just what you want and just buy the modules you need for the functionality required for your project. And rapid development and deployment tools. So that's a snapshot. Again, I'll refer you to the website if you wanna dig in 'cause any one of those bullet points has lots more information on the site for those of you who may be new to Ignition. But because of the topic today, I have a sneaking suspicion a lot of you are not new to Ignition. Our presenter today is Kevin McClusky. Kevin is Co-Director of Sales Engineering at Inductive Automation. He previously served as Director of Design Services here. He joined the team in 2011, bringing years of expert experience in the field of industrial automation software integration. He oversees, creates and supports numerous HMI, SCADA, MES, IIoT projects for Ignition installations around the world in a wide variety of industries. He applies really, a very deep knowledge in all those years of experience to customer engagements, and he provides ongoing guidance to the Design Services Department.

Don: And I think you'll find today's topic extremely educational and useful, as many of you already have signaled by the numbers that wanted to be here today. So with that, Kevin, you are the presenter, I'm gonna turn it over to you and I'll just be here when you need me at the Q&A time.

Kevin: Alright, thanks, Don.

Don: Sure.

Kevin: Hello, everyone. I'm Kevin McClusky. It's good to be here with you today. I'm sure a number of you who are on the phone, I've talked to at some point in the past, and you may have seen me at the Inductive Automation conferences out here at Folsom. We've got one coming up in September, I'll be there, of course. And I've been involved with a lot of different customer projects of ours over the years. What we've decided to do today with this session is not necessarily to make it about Inductive Automation and about Ignition, but more about scripting in general, scripting best practices, and things that'll help you regardless of what platform that you're actually running on. There are a variety of different ways to approach scripting, and if you approach it in an intelligent way, it really helps with project sustainability, project future maintainability, maintenance and product and feature additions. You know, when projects later come up and there are new additions that are needed, really helps make that a lot easier than it might be if you weren't to keep these best practices in mind.

Kevin: So what we've done today is we put together a variety of different best practices across different areas of scripting, different approaches that you can take, and we'll just start going through those. So if we take a step all the way back and we say, Why does scripting exist in the first place? Well, it's possible to create complex, powerful projects inside HMI, SCADA software without writing code, without writing any scripting. And when we say code, we're talking about scripts. Scripts and code are going to be more or less interchangeable inside this webinar here, so you can do this without writing scripts, without writing code for a lot of different HMI, SCADA, IIoT, MES projects. But there are sometimes things that aren't built directly into the platform, so you can get 80%-90% of the functionality that you're actually looking for without scripting at all.

Kevin: Often, scripting is the only option to get that last 10%-20%, and often that's more advanced types of functionality. If you're just doing standard real-time status, real-time control, if you're doing visual displays that are simple and have connections to tags, maybe simple connections to databases, you probably don't need scripting at all, and we wouldn't suggest using scripting in those cases. But scripting exists for the more advanced things that you might need to do. So why would you actually use it? So more flexibility, you can do just about anything inside scripting, that's especially true inside Ignition, and that scripting platform gives you the ability to hook into a lot of different sections of the underlying platform. You can extend the logic of what something does beyond things that might be a standard expression language. You can do more with the product than what's built into it, so beyond the built-in tools, you can extend those tools, you can expand on what's available. And all that's important, but it really has to be used in the right way.

Kevin: You only want to use scripting when you can't accomplish a requirement with built-in tools. And I've worked with a lot of customers who have come to the Ignition platform from a controls background, and they're great at this because they look for the tools that are available and if there aren't tools then they'll grudgingly go over and start using scripting. I've also talked to a lot of customers who've come from the programming IT side, and their go-to is to jump right into scripting and not worry about finding the built-in tools. For those in the crowd, I would say, please take a step back and take a look at the Ignition platform, go through the Inductive University and understand if you're using Ignition, what those built-in tools are. If you're using another platform, understand the built-in tools in that platform.


Kevin: Those things are going to be much easier to use and much easier for future engineers to maintain if you're using those built-in pieces of functionality, if they accomplish everything that you're looking for. A classic example of this might be if you have a tag and you wanna take that tag value and show it on a screen. If you take that tag value, you can drag the tag from where the tag is inside Ignition, drag it out to a screen to show it on the screen. In other platforms, you can type in a tag path, you can pull up some sort of binding there and show it on the screen, or you could create a script when the window opens, that grabs that tag value and takes that and puts it into a screen, and that's the thing that you probably shouldn't do because that's completely avoiding the built-in tools that are inside the platform. So you wanna use those built-in tools wherever you can, but you don't wanna use scripting for scripting's sake. You may want to ask users in forums and call tech support, especially our tech support is gonna be really good at directing you toward when something is built-in, if you're trying to do something that you're not quite sure if there are tools inside the platform or if you should use scripting. Tech support is gonna be able to direct you in the right direction there.

Kevin: And as I mentioned, scripting can make it more difficult for another engineer to understand. So if we're talking about code maintainability, when somebody comes back in and they're adding to the code or they're taking a look at what exists inside the project, it can make time that spent later on to extend that project or to troubleshoot, it can make that take a lot longer than if you're just using the built-in tools. That said, there are a lot of cases where scripting does make a lot of sense, and that's what the webinar today is about. So, when do you actually want to use scripting? So if you're exporting data and a component might not have something that's built in to export that data, scripting is fantastic at that. You can take the data, pull it in, transport it, you can change it to maybe a CSV format or an Excel format or a different arbitrary format and then send that out to a file, give the user an access to download that type of thing. Especially when working with bits and bytes and kind of that way, scripting can be a great, great tool for you to have under your belt.

Kevin: Custom calculations are another good example, so if you want to run through and you wanna do something that's more advanced than a simple Excel formula calculation that you might do with an expression language, if you wanna take a square root of something, then you wanna take the square root of something else and then the cube root of something, and then add those together, you might be able to do that in a single Excel formula, if you think of it that way, but it's gonna be a really long line that it's gonna look a little bit funny. If you do it inside scripting, you're gonna get step by step, you'll be able to use variables, and it's gonna be a lot more readable for somebody who comes along later as well.

Kevin: When not to use scripting. If you're using Ignition especially, SQL is possible directly inside the platform. Absolutely don't use scripting if all you're doing is selecting some data out of the SQL database, putting it on a screen, and sometimes when you're doing updates back to SQL, you don't need scripting either. So don't do it unless you actually need it. If you're logging data contextually, especially inside Ignition, use transaction groups, don't use scripting. If you have to do things that are more complex than what transaction groups give you, or you have to do things that are more complex than what the tag historian gives you, that's the point that you would switch over to using scripting from the built-in-tools. But definitely start from those built-in tools and see if they will accomplish what you're looking for before exploring the route of scripting.

Kevin: Now, one of the things that we mentioned is, which scripting language do you use? The language choice is something that exists if you're developing something from scratch. So if you are developing your own entire application, your own HMI platform, your own SCADA platform, you could use any language that you want. If you're using a platform that exists right now, such as Ignition or anything else out there, your scripting language is generally a specific language and you need to use that language if you're using it inside the platform. Inside Ignition it is Python, we're very happy with Python, we think it's a great language and adoption around the world agrees with us, and it's very powerful. Other platforms have other scripting languages. So generally stick with those scripting languages that you're given. You could create utilities that are outside of the platform, but if you end up mixing and matching languages, then it's going to be a lot harder to maintain. Inside Ignition, it should be noted that through Python, you actually have access to Java as well, so you can use Java objects inside Ignition, but you're still using Python as the scripting language.

Kevin: And then this comes to how the script runs inside the platform. It's important to know where and when your code is running. So inside that platform, there are going to be different options for how to run that script, where the script runs, and you have the ability to take a look and decide where you're actually going to have the visibility. Essentially, your script can run inside a client, your script can run inside the server, your script can run elsewhere potentially, and keeping in mind where it's running and how often it's running is going to be important whenever you're creating a script.

Kevin: So at this point, we're talking about scripting best practices, right? So you want to track along what's happening inside Ignition, the server is called the Ignition Gateway, and so you're going to want to keep track if the script is running in the server, if the script is running in the Ignition Gateway, or if the script is running inside the client. If the script is running inside a client, you potentially have multiple copies of that script running at the same time because you have more than one client that's open, or you could have zero copies of it running because there are no clients open. So if there's anything that's a periodic task, you'll wanna run it inside the gateway. If you're running something inside the gateway, but it's supposed to update the clients, maybe you wanna move that instead over to be a client script. When you actually run the scripts, you'll have multiple different options, and when those scripts are run or when the scripts are invoked, that's going to come from where they're configured inside the platform.

Kevin: So in Ignition, you have timer scripts that will run on a periodic basis that you can run either in the gateway or the client, you have your choice. You have tag chain scripts that are going to execute every time that a tag changes. You'll have user scripts and those specifically happen inside a client where somebody presses a button or where they click something, or where they invoke that script in some way based on an action that the user takes. You have sequential function charts that allow for long-running processes and visual execution of the scripts that will survive gateway restarts and will resume execution where they left off. You have alarm pipelines which have the ability to run scripts inside them, and you have a variety of others. Inside Ignition, scripting is built really deeply into the platform, you have a lot of different options for the ways to invoke scripts and where they run. So it's really important to keep that in mind. And once you have scripts inside your platform, inside your product and inside your project, you want to keep in mind a variety of different things, you want to keep them organized, you want to keep standards in place that are going to help with development going forward.

Kevin: So basically, these standards, you're going to wanna develop a code standard for your overall code. You're going to want to organize that code and make sure that folders, functions and variables are named appropriately. If you don't do that, then your code is going to be tricky because code can really be written in a variety of different ways, and I'm sure everyone is familiar with the idea that you can have clean code and you can have messy code. Messy code is easy to make and it is hard to maintain, so if you're doing your first scripting project or your first script inside a project and you're writing just things based on examples that you find online maybe, and you're copy pasting code and you're putting it in, finally get it working. Chances are, you have a variety of different code styles, you have a variety of different naming, and you have a variety of different things that the code looks in different ways in different sections. You probably don't have any code comments either, and so that's going to create messy code that is pretty hard to maintain. If another engineer comes in or if you come in a year or two from then and you take a look, you're probably gonna have a hard time telling exactly what that code is doing, which makes maintenance of the project, any feature editions take longer and it ends up being more expensive for the user.

Kevin: So that's something, you wanna make sure that it's organized well, that code standard is part of that, and that code standard has to do with everything that we'll talk about right below this. So you wanna avoid language shortcuts as well, which should be part of your code standard. Language shortcuts are advanced pieces of language that essentially allow you to do things in single lines or a small side of lines rather than in a larger number of lines for the code, which sounds really nice and elegant, however, it is very hard for new engineers to read. We have a few good examples inside Python, so if we take a look at the example two first here, example without shortcuts, basically in Python, we have a list, we have a set of numbers that might be one, two, three, four, five, six. And we have another list that is all the even numbers, it starts out blank, and then we loop through every single one of the numbers in the numbers list there, so we're gonna loop through one and then if the number Modulo 2 is zero. So if it's an even number, then we're going to append to that even list. So if you read through that, it might take a minute, but you can see what's going on there, and then you can see that even list is going to end up with two, four, six at the end of it.

Kevin: Now, if you use a language shortcut called a Python list comprehension, you can take all of this code and convince it, and if you take a look at example one there, you'll see numbers one, two, three, four, five, six, and then that next line down there does everything that that loop does, the definition of the even list that you have there, the even variable, and basically it's saying that that new even list is going to be every number for numbering numbers if the number Modulo 2 is zero. So very, very condensed. And if you're a language expert, you might say, "Well, I like the list comprehension." It's okay, that's fair. But list comprehensions for new engineers are going to be extremely hard to understand. It's going to take them a little while to get up to speed with that. And it's not something that we have seen as a company that really makes a lot of sense for most organizations.

Kevin: There are a few organizations that do have a really advanced set of users, very advanced set of engineers who all have a very advanced level of Python understanding and if that's the case, then those people are going to be the only people who are maintaining the system, and there's going to be some sort of certification process for any new person who comes into design, and we're not talking mission certification, but a Python, separate Python certification to verify that they know a really advanced set of features inside Python, then it might be okay to do something like this. But in general, you wanna stay away from these, you wanna write code in a way that's a lot more easy to understand for any engineer who comes in later on for anybody who might dial in to a conversation, if the user needs to go in, make a change, open up the designer, you want the code to be understandable, and you want the code to be something that's accessible to users who might not be as expert in the language as you are.

Kevin: A few other notes on the organization standards. So indent style, tabs spaces that matters, programming style, which really has to do with indent style and comments and naming and all the rest of that. That's important. And a coach standard goes over that, and common conventions are pretty important as well. If we're talking about commenting code, basically, if you've never heard of code comment, a comment is a programmer readable explanation or annotation in the source code. Basically, it's a value that looks something like this, where at the very top, it says, "This is a comment." And started with the pound symbol, that tells the scripting engine Ignore This, or it's just plain English, it's something that somebody's going to write some words and they're about, and then you can see on the next line there a print, Hello World, another pound. This is also a valid comment. You can add comments to the ends of lines, or you can add comments for overall lines. So that's what a comment looks like, it's essentially added to a source code to make it easier for humans to understand.

Kevin: So you need to do it, you need to have code comments, you need to have intelligent code comments that people can read through and understand what's happening inside there, which is going to make the code easier to maintain. It helps developers, makes code easier to understand and troubleshoot as well in the future, plus it's one of those things that separates an amateur organization from a professional organization. If you're doing a professional product and a professional project, having code comments is one of those essential pieces inside the project to make sure that it's something that's going to be robust going into the future. And of course, comments aren't interpreted by the scripting language, but they are something that make it so that you have documentation built into the project, and it's something that's maintainable in the future. You can do block comments in pretty much every language as well, and on the bottom here it's showing those block comments off. There are a few ways to do code comments in large blocks, control-slash on most languages and most IDEs will add a comment to several lines of code at once. You highlight those lines, you hold the Control key and press slash and then it'll add that pound symbol in front of the lines of code.

Kevin: The last item on the list here is naming conventions, and basically naming conventions have to do with how you name variables. In this example, you can see that we're defining variables at the top of that script and the variable names all start with a lowercase letter, the variable names are simple, the variable names are descriptive, so they say essentially what they contain, and those naming conventions are pretty important. If you have a variable name that has two words, generally you'll use what's called camel case, so you might have one of these variables that has two different words in it, so you could say building configuration for example, building would be all lowercase and then there's no space, and then configuration would start with a capital C. And then in this case, IDs and other variable, and you can see that these are used in multiple different places.

Kevin: So one of other best practices is that you'll want to define script functions. So these functions are going to be code that you can put in multiple different locations. You essentially take code that you might be using in three or four or five different places, and you will take that code and put it in one central location, and then from each of those other locations, you'll call those script functions that are central. So it makes it so that you can maintain a set of functions that are available.

Kevin: You can also use other script functions that exist as well. What I just described are user-defined functions. You also have the option to use a variety of different functions that are inside the language. So in Python, there's the len function that's going to give you the length of a list or the length of a data set or a length of a variety of other things; essentially, the length of an array. You also have functions that are available as part of a software function, so inside that software package, you have system.gui.getmessageBox, for example, inside Ignition. In Ignition, you've got about 250 different script functions that are available through the scripting API, and each one of those has a variety of different options inside it, and they are all documented as well. So they're all documented in our documentation, and there's also a little code type of code completion. And in fact, if we show this for a second, and we take a look at a script function inside Ignition, if we have the script editor right here, we take a look at system dot, we're going to see things inside that. So if I switch over my sharing, I wanna make sure that you can see the script functions and there it is, system dot, and this is going to give a full variety of different things that are available inside Ignition. So I could say system.db, and you can see each one of these functions, there's a variety of things.

Kevin: System.db.runQuery is going to give us the ability to run a SQL query directly right here, specify some arguments that are going into it, and it has a return value right there. And each one of these script functions is available and documented inside Ignition, separated out into packages as well. So if you wanna do things with tags, use system.tag, you can read, you can write, you can edit tags. System.alarm will let you do things with the alarming system. There's a variety of OPC functions and a lot of different packages as well. So these are the scripting API that a software package might provide for you. Each one of those items inside that scripting API is important to understand what's available, just the more or less the high level packages, and then you can explore the API as you get more familiar with scripting.

Kevin: There are language standard libraries that are available as well. Inside Python, there's a math language library that you can import math and they can do math.sqrt to run the square root of a specific function or math.abs for the absolute value or a variety of other things. And Python has about 100 different script modules, as they call them, inside a standard library that are available. And then there are the user-defined functions like I was describing a few minutes ago, where you can define your own script functions and for any reusable code, you can call those.

Kevin: So it's often useful to define those script functions to keep code more organized, it makes it easier to find and troubleshoot, and these are user-defined script functions, and using the other script functions that exist inside the libraries. It's going to keep the windows and the tags cleaner if you're just calling those functions rather than have a really long script that's running through and doing code. And the ability to reuse things is pretty paramount. So when you're designing something, you might decide that you need a certain script function and then later on you wanna do the same thing and you wanna do the same thing and you wanna do the same thing, it helps with rapid design if you're able to reuse those same script functions rather than rewrite them every time. And you'll also note that if you're going through and you're writing a 10 or 20 or 30-line thing to do something that's pretty common, or that you feel like might be common, so two and 20, 30 line script, maybe for a CSV person, for example. Take a step back and think to yourself, "Does this already exist? Is there something already to do this for me?" And in Python, you can import the CSV module and you do an import CSV, and then you can use their CSV parsing functions so you don't have to write your own CSV parser. And that's a pretty good example of a place where it makes a lot of sense to use some of those built-in functions.

Kevin: If you're defining your own functions or if you're using other functions, you wanna take a look at things like keyword arguments, so arguments specified by position might look like check bounds right there with 150, 0, 250, so the first argument, second argument, third argument. And each one of those means something separately, and we're gonna pass it a value on this case, and then we're going to pass it an offset, and then we're gonna pass it a range, and it's gonna check specifically if that value is over the range and if it is, then it's going to essentially tell us that. If we specify arguments by keyword, you can see that value, we pass that in as the first argument, but we're skipping that second argument, we say, "Let's just use the default. If zero is the default, let's use the default," and then we'll specify in just the variable name what we want that third argument to be. So range is going to be 250, so it allows us to skip over those, we can specify just the keywords we care about.

Kevin: And if we're defining some of these functions ourselves, if we're creating check bounds, for example, and we take a look at the bottom here, this is how you would actually define it. So if you're defining keyword arguments, you have that first argument, that's value. And we're looking down on the slide where it says def check balance, I'll jump up to add in a second. Check balance, we have value, we have a keyword argument for offset, we have a keyword argument for range. Each one of these guys, if the value, you can see the basic code there at the value plus offset is a great range, we're going to return true, else, we're going to return false. And if we have that offset, if that's skipped like where we invoked it here at the bottom of which we say that we're not passing an offset at all, it's going to default to whatever the equals is there, so offset's going to be zero when we run through. You could also run this just passing in a value and the range would default to 200 in that case.

Kevin: So those are keyword arguments, the other option that you have with arguments is to pass in just kind of a list of arguments and arbitrary number of arguments where you could say I want to add the numbers one, two, three, four, five together and pass them in with commas in between them. And if you define a function like this in Python, it'll let you just pass in as many arguments as you want. So you have a first value there and then an arbitrary number of arguments that pass it that are going to be coming in later. And so you can take a look at the code right there, final value is gonna start from just that first value, and then every value inside args, it'll loop through and that final value will get the value from the args added to it, and then will return that final value.

Kevin: It's important to do exception handling inside scripts. Exceptions are errors that are determined and detected during execution of a script. Basically, if you're calling a function and that function doesn't do what it's expected to do, if you're running a database query and the database itself times out, for example, or throws an error back to you that says your SQL code was wrong, that's going to come back as an exception, and that exception is going to be informative. So it's going to tell you this failed, this is essentially why this failed, it'll have some sort of reason inside there. And if you don't have any what we call exception handling, then basically, that error will show up to the user. So that'll pop up, the user will see an error box, it'll say whatever that error was underneath behind the scenes and the user will hit Okay or Cancel. That error might not be user-readable; in other words, the user might not understand what it means if it says that the SQL server was disconnected and the query could not be executed, that might be really confusing to an average user. So you have the option of exception handling or when an exception happens, you can catch that exception and you could do things with that exception.

Kevin: In this example right down below, you can see that what we're doing is we're trying to open a window, so we're getting a window, and then we're trying to grab that open window and then we're closing it. So based on a user's navigation or based on a user's button press that says, "Close the overview," if they hit that button and somewhere else, it's gonna try to do that. If it fails, then it will go instead of showing that error to the user, it'll go through this exception block of code where it says, "Accept colon." And every exception will pop through there, you can test what the exception was, but in this case, in this example, we're just simply saying if there was an exception, we'll give the user a standard warning box that has some plain English that says, "The Overview window isn't open," I can't close it because it's not open. So that might be a nicer thing to present to the user than a standard error box that's going to have some technical details. There are other cases where maybe if something fails, you wanna retry or you wanna try on a different database connection, if one of the database connections fail, something like that, and you can use exception handling for those things too.

Kevin: The error console is pretty important in terms of how you actually communicate and how errors are communicated back to you as someone who is inside the designer. So when you're designing things inside Ignition or inside any other platform, really, you're going to have the ability to see what errors actually happen along the way, and hopefully do something about them. So inside an error console, it's going to feedback the information about what those errors were, it's going to feed back information about where they came from, and it shows a lot of information about what's actually happening at any given time. So it depends on where your script is running. In some cases, there may be a different console than a standard error console. We talked about scopes that scripts could be running in before, so if it's running in the gateway or if it's running in the Designer, that's going to look different. And if it's running in the client, it's going to look different. And each one is going to have a separate console.

Kevin: It's also important that you go through and you clean up any errors that you might have inside these script consoles. And if we show a basic quick example of what that looks like, it's going to... An error console inside the Ignition Designer is going to look something like this. So inside the Designer, we have the ability to take a look at what this output console has right here, if we go up to the console, we can see all the different things that are happening. It's not just errors that you get through here, often you'll get info messages, but if I came in and I did something inside a script and that script failed for some reason, then this is going to give me some sort of error when I try to run it, then we can see we get details here, we can see what that name error is right there, and then inside the error console, we get a lot more information. So we get a full stack trace, we can see that it happened on line one, and we can see the reason and if we cared to read through all of this that came where it came from inside the overall stack trace, things like that.

Kevin: Cleaning up the known errors is important in this case, because those known errors could fill up the error console and make it hard to see errors that are actually real errors. Those known errors, sometimes customers will add those in, they'll have errors inside their scripts, but the scripts are still running fine, and they'll think that, "Okay, well, we'll just let that error go through." We always suggest cleaning that up, either with exception handling or by changing the way the project is designed.

Kevin: It's often good to know how long your script takes as well, so your basic script execution is going to... Whatever you're doing is going to take a certain amount of time. Often, scripts run almost instantaneously where a user presses a button and that button press does something behind the scenes, and then the user immediately sees an update on the screen, or you're going through and looping through tags and writing through to a variety of things, and that takes maybe 5 msec, maybe 1 msec, so the execution duration doesn't seem to matter to you. But it is important to know how long it takes, especially for longer running things and things that are going to be communicating back and forth, so if you have database queries, for example, that are running. Or if you do have some OPC writes that are synchronous that are going back to different devices, or if you have some restful web services that are going out and getting information back, being able to know how long those take allows you to better design and plan how often they execute, and where do they execute.

Kevin: You'll wanna use loggers to effectively troubleshoot your code, and inside Ignition, you can use loggers in a variety of ways. You have a script function that's going to allow you to get a logger and use Ignition's standard logging system under system.util.getLogger. You give it a name, and then that's going to log anything that you want using that logger. And so I could say, this is a message, and whenever we run the script now, it's going to log a new info message into the console, and if we do a little more advanced diagnostics behind the scenes, we can actually split this in, so inside this log viewer, split it out, we can see that we've got that new logger, this is a message and it'll sort the messages for you.

Kevin: Inside loops, it's always a good idea to avoid loops that are going to have the potential of running infinitely. Loops are really important for using when you're iterating over multiple items and definitely use loops to do that type of thing. If you have an item inside a list, you could do four items and list, run this block of code, but you don't wanna use loops when you're making your time or your code wait. An old technique for doing looping might say, I'm going to add one to something every time and I'm gonna loop through this 10,000 times, and that's going to end up slowing down my code, I wanna wait for a certain period of time. We're not going to want to do that inside Ignition or really any other software platform because that ends up taking CPU, it spends up the overall system, and it's going to slow things down over time.

Kevin: You'll wanna use functions like a Thread.Sleep inside Ignition and that'll sleep for a certain period of time, if you want to actually have some sort of delay inside your script, or you can use multiple scripts that are essentially hooked together in different ways. As I mentioned, you wanna stay away from infinite loops, so you won't want loops to run through forever. If you do that, then you're going to potentially cause a client to stop responding, which is generally not a good idea, if you're running that loop inside a client and you do wanna use breaks effectively. So basically, if you're looping through a set of code and you're looking for something. In this example, we're going to loop through and we're going to look for each row. If that row matches then we're gonna set this found variable to true, and we're going to break. We're going to say, we're done here, we're not going to loop through the rest after we've already found these, because we're done.

Kevin: You wanna use timers and you wanna set timers to run at a specific interval. This is for anything that's a periodic task or periodic script, such as sinking information between different systems, and each one of these, you'll have the option to run at a fixed rate or a fixed delay inside Ignition. Basically, each one of these scripts can run at a specific set of time, so if it's a fixed rate, it's going to run at a specific amount of time, or it's trying to run at a specific amount of time between each execution. So between the start to the start of the next one, a fixed delay is going to be a certain amount of time after the first execution finishes, before the next execution starts. And that fixed delay is generally what you want to do, so basically it means that your script isn't going to be running all the time. You're guaranteed that you're going to have a certain amount of time from when that execution finishes till the next time that it starts.

Kevin: If you do a fixed rate, on the other hand, if it was a 60-second fixed rate, and let's say your script is really long and it was taking 120 seconds, your script would run that full 120 seconds after it finished, take a look and see that it was scheduled to run 60 seconds ago, and it would immediately restart and run again. It didn't hit that 60-second mark, so it couldn't run after 60 seconds. It's not going to stack up and have multiple copies of that script running, but it will run one right after the other, right after the other.

Kevin: Best practices within said scripts. If you're running timers, you wanna avoid scripts that run quickly and fill up cues, so tag writes are an example of this where you can queue up a variety of tag writes, you won't wanna run a script that's running every 50 msec writing out to tags, 'cause every time that it writes to a tag, it's going to just put that under a write queue, and then the tags will be written to when the driver determines that it's easiest to do that, and it'll normally be almost immediately, but that queue could be filled up, especially if you're writing 1000 tags or 10,000 tags at a time. And you'll wanna provide the ability to enable/disable scripts through a setting, so you wanna be able to, through a memory tag, toggle a bullion for example, and that script will check that bullion to see if it's enabled or not; if it's not enabled, it'll immediately cease, and if that script is enabled then it will run through. This makes it so that you don't have to open up a designer and toggle that script yourself every time that you wanna change it. Also useful for sync scripts or other things that are going to be communicating back and forth.

Kevin: Tag change scripts. You'll wanna run these. The best practice is behind these. Essentially, you'll want to have those tag scripts run when the value or quality of a tag changes, and that's how it works by default. Since you could get an execution when the quality or the value changes, you'll want to check both of those inside your tag change script. If you check and you see that the value has changed, then you can execute whatever you're doing. If you see the quality has changed, maybe you want to act differently based on that.

Kevin: You'll wanna avoid race conditions inside your scripts. This is a pretty common item that happens inside screens where you might have something that executes when the window opens and you have a binding that also executes, that updates the data set and your script runs and the binding runs at the same time. The binding doesn't update before the script actually grabs the data from that binding. So you wanna make sure that you have things rely on the other things to be there, that script that relies on that data set, you'd want to have as a property change script on the data set rather than a script that's going to open on window open to make sure that it only executes after that data set has been updated.

Kevin: So I just mentioned property change is a good idea for ways to avoid those race conditions. You want to do that if you're writing to tags and you're reading from tags, that's another race condition, where basically that write might be asynchronous back to the tags. And you actually have a variety of different race conditions, so you want to make sure that you execute things in the right order and that they're sequential, that you don't have things running at the same time. Because Ignition is inherently a multi-threaded type of application platform, you have the ability to run two things at the same time, but if anything relies on anything else, you wanna be careful about how you do that.

Kevin: Understanding threading is pretty important. A lot of languages are multi-threaded. Basically, what multithreaded means is that two parts can run at the same time. So you have two different scripts, those scripts could be running simultaneously. Inside modern computers, you have multi-core processors. Each core can run separate threads simultaneously, and then you also have multi-threaded operating systems and programming languages, and basically that means that your script might be running and be three quarters of the way through, it hits a certain point, it pauses execution, another script that's scheduled to run at the same time, runs now, it switches over that core, runs that script, runs through a certain amount, and then it maybe pauses that again and switches back to your original script, and the operating system handles this for you automatically. It does it behind the scenes, and it's going to make decisions as to which threads to run at which times.

Kevin: So the multi-threaded nature of modern operating systems allows you to do multi-processing, multiple applications at the same time that you're running multiple web browsers and different word processors and all of that, and it's very good, but it's important to understand how that works inside your platform as well, so that if you're doing multiple different threads that you're using them intelligently.

Kevin: Scripts can either run in a shared or dedicated thread. Shared threads are essentially a thread pool that all the different scripts that are inside that shared thread pool will share those same threads. Dedicated threads means that there will be dedicated processing power, essentially a dedicated thread that's set up for that specific script. Generally, short-running scripts, you'll wanna have in a shared thread; long-running scripts, you'll wanna have in a dedicated thread. And you do wanna understand what else could be affected if you are running long-running scripts inside a shared thread pool. For example, if you have your long-running script running and you have three copies of those that might exhaust the threads in the thread pool, and then the next thread that was intended to run, the next script that was intended to run won't actually be able to run until your scripts actually finish.

Kevin: Inside the client, you wanna keep in mind what is inside a UI thread or versus what is inside a separate thread. So each one of these guys, for example, is going to have the visual that... You have visuals that are updated, and let me just switch over and show you a quick example of what this looks like. So you can have different visuals inside Ignition that are going to be updated by different things inside your threads. So if we have a script that's running for a certain period of time, let's say this is some sort of a short task, so all this is doing is probably for a message box and it says that it's done, that's great. But if we're doing something where we're taking a little bit more time, and I'll simulate this with just a sleep in here, so I'll do from java.lang import thread and do a red.sleep of... Let's say 5 seconds, 5000 msec, I hit Okay right here, I press this button, and you can see I can't press anything else right now, it is stuck right there because it's running on the same thread that all of my visuals are running on, that's my UI thread.

Kevin: So this is essentially freezing the client, freezing the designer for me for that period of time, just because of how I did this. If I have a long-running task instead, probably what I wanna do is run this on a separate thread. You can do that on something inside Ignition with a system.util.invokeAsynchronous, and this is going to let you run things on a completely separate thread, you can run anything you want inside that separate thread, and then you can update the UI over time, and I put together a basic example right here, where it's basically taking that, it's looping through, it's going to loop through 10 times and it's going to run this asynchronously, and then it's gonna update that progress bar on the screen. And if I hit this button, you can see this is a long task, it's spun up another thread, behind the scenes, it's running and then it's updating my UI, I can still click on other things at the same time, and this runs and happens regardless of what else I'm doing.

Kevin: Scripts could be running in two different places. You wanna make sure to restore, disabled or have a code check if it is by a certain IP, or host name, if you're going to be making copies of those scripts run in multiple different places. If you're doing an export on an import of a project, the scripts might start up automatically if that's how you have configured to do this, so you might end up with double entries into databases or a variety of other things, unless you're intelligent with how you decide to run or not run scripts inside that backup copy.

Kevin: Other tips, you'll wanna use time savers, you'll wanna use the script package functions, libraries. There are language functions that are important, you wanna take a look at those language functions that are available. I gave the example earlier for the CSV library inside Python. I wanna use those instead of doing custom code, and you'll wanna use things like the system dot functions inside Ignition and use those first before you use script libraries that might exist inside Python, for example. The rest functions are much easier to use inside the script functions that we've generated and created for you.

Kevin: A few things to remember on basic recap here, things to remember when you're doing scripting and when you're thinking through scripting and when you're designing things. You do wanna make sure that you keep in mind the basic purposes of scripting, you wanna know when to use scripting and when not to, those are the foundation of all these. And when you do decide to use scripting, you wanna understand where and when your script is running, use your organization and standards of that script and the code standards. And you definitely want to keep comments in your code. The variable name conventions are important, you'll wanna define those common functions, use exception handling where you can. You'll wanna use the error console to help with debugging and you'll wanna keep that clear. You wanna avoid infinite loops. Use appropriate threads with timers, and keep in mind how the threading works. Plan out your tag change scripts and take into account quality changes. You wanna avoid race conditions where you can, and you'll wanna understand those pieces of threading and asynchronous execution that are behind the scenes.

Kevin: Now, if you're looking for more scripting help, there are a few good resources. We do have our tech support department, so if you have a support contract with us, just give us a call. They'll be able to point you in the right direction for individual things inside scripting. They're going to be able to help you with the basics of, "How do I do this? How do I use this kind of script function?" If you're looking for bigger picture things or even more basic things, there are a couple of other resources, our forum is great, we have a really active user community who does scripting for all sorts of different things, and then you'll also wanna take, if you're not familiar with Python, you'll wanna take a look at a Python language tutorial and go through the basics. And here at, this guy has a Python 2.5 language tutorial and that's the version that we use inside Ignition.

Kevin: All right, so with that, I'm going turn this back over to Don, and I know that we have a small number of questions here, which we will be able to get to in just a couple of minutes. As described earlier, we are going to have the ability to stick around for a few more minutes because this is a lot to cover, and a lot inside this topic. [chuckle]

Don: Well, Kevin, I just wanna give Kevin a round of applause. If all of you could please give Kevin a round of applause for covering a tremendous amount of material. We knew when we did this webinar, it was gonna be a challenge to go through all the best practices you wanted to in an hour. I'll say, at the start, we said we would stay around 'cause we just did our hour right now, we will stay around for 10 minutes to answer questions for those of you who want to be involved either 'cause you have a question or 'cause you'd like to listen to the answers to other people. I'm just gonna say a couple things quickly. One, if you are new to Ignition, please try it for yourself, download it, go to our website to download it and fully functional in three minutes. Second, I do wanna mention for those of you who are new to this, this may be your first design like a pro. If this is your very first design like a pro webinar, we have a lot of them, we've been doing this for a while. Gosh, I guess three, four years now. It's a series. It's designed to provide fundamental ideas, tips, practices, like today, to help you build the best automation projects possible. So the content includes topics like launching projects, alarming, HMI optimization, templates, reporting dashboards, IIoT and more, including obviously today's topic. Q&A, Kevin.

Kevin: Alright, so a whole variety of questions here. One of the questions, as I understand it, the script language in Ignition is actually Jython, not Python. Could you explain some of the differences? Basically, Python is a script language, and the Python language has multiple language interpreters. There are several different interpreters, CPython is one of the interpreters, that's probably the most common one. Jython is another interpreter. And then there are a few others that are out there that are common. So inside Ignition, it is Python, it is a real full bonafide Python that's running inside Ignition, just going through the Jython interpreter. The differences between Jython and CPython are that basically, there are a couple of differences in the net libraries, there are a few other things that are inside there, there are more or less full on limitations, but Jython also gives you access to Java classes that are underneath, which is a really nice feature, so you can do from java.lang import math, or I just showed from Java.util import thread. All of that's available through Jython.

Don: I will say that there's gonna be, looking at the fact that you have been a very active audience today, we've got several screens of questions, but I will go through a couple of them, and ask you to answer them as quickly as you can, Kevin. How do you run a script of a specific time, like 3:00 AM?

Kevin: Yeah, so two different ways to do that. One is that you can use timer scripts and check the time, so run the timer scripts quickly enough that they're going to run every hour or every minute and check to see if the time matches. The other way to do it, which I like a little bit better is, you can use transaction groups, you can schedule those transaction groups and those transaction groups can execute whatever scripts that you want. So you set up an expression item, inside that transaction group, you say run at 12 o'clock, and you actually give that inside the schedule and then inside that expression item, you do a run script of whatever you wanna run, or you do five or 10 or 20 different run scripts, it doesn't really matter.

Don: Okay, so how do you know where scripts run?

Kevin: So scripts are going to run in the place that you have selected, and more or less wherever you set up that script is going to be where it's running, when you have a button process, it's going to be running into the client. If you have timer scripts under the gateway, it's gonna be running in the gateway. If you have put timer scripts under the client, it's gonna be running the client. So, it's up to you to decide where to put those scripts and where to run those scripts.

Don: Okay, great. Thank you. This one is pretty simple, a couple of lines. I think it says, If I'm reading my Jython 2.7 for Ignition 8?

Kevin: The answer is yes. So Ignition 8 is going to have support for Jython 2.7.

Don: Okay, great. Do you think you will move to Python 3.0?

Kevin: We don't have any immediate plans for that. The latest version of Jython, which is the Python interpreter that we use is 2.7, so we're gonna stick on that when Jython comes out with 3.0 support or 3.6 or 3.7 we'll probably consider adding it to the platform. Python 2 and Python 3 are two different languages, unfortunately, so they changed some of the language constructs behind the scenes, they changed some of the fundamentals. So Python 2 scripts don't run in Python 3, Python 3 scripts don't run in Python 2. There are a few exceptions, there are some things that do run, but there are enough differences that you generally can't take a Python 3 script and run it in Python 2. So if we add Python 3 support we'll need to have the ability for users to choose which one, and we won't get rid of Python 2 support.

Don: That sounds great. This is a comment from HuqCoo who comments, "Coding standards make a lot of sense. I'd love just the Ignition community to develop an open set of standards and best practice. I'd be willing to participate in this." So thank you Huq. I appreciate the interest, and there's a lot of interest in that from some of our other attendees too. Yes, this presentation is available online. I think, David, you and the team will have an out by the end of day or tomorrow sometime? Okay, so you'll get that and that goes to everybody who attended and also anybody who registered that wasn't able to attend, so you can also obviously share the link with others too. So you wanna grab one or two more questions here, Kevin, and then we're gonna end off 'cause I said 10 minutes, and I will say though again, we're gonna get to as many questions as we can. Afterwards, we'll follow up and make sure all these questions get answered.

Kevin: Alright, so I'm taking a look at... So what version of the API has documentation? IntelliSense feature, that feature has never popped up for me once. So, we've actually heard this from a number of people in the past. Basically, there is a trick to it. So you'll see it inside our Inductive University videos, we talk about it there, but you have to come in and you do system dot and then hold down Control and hit space, and that's gonna pop it up. So, it is essentially code completion. Yeah, it's like IntelliSense inside Visual Studio, although I think that's a Microsoft registered trademark for the word IntelliSense. But yeah, it's the same basic type of idea, so you can pull this up, this has been in Ignition, I believe, since version 7.0, so we had it really early in Ignition and in whatever version you're running, it should be in there as well.

Don: Okay, so what version of Python is used and can third-party libraries be imported?

Kevin: So Python is Python 2.5. As mentioned, we'll have Python 2.7 support in Ignition 8. Ignition 7 is Python 2.5, and Python 2.5 libraries can be imported and can be used. If you take a look at Python 2.7 libraries, it's kind of a mixed bag because Python 2.7 has a couple of other language features like dictionary comprehensions, in addition to list comprehensions that some of these 2.7 libraries use. So you might not be able to import a 2.7 library. Sometimes you can, but Python 2.5 libraries can be imported generally as is, and you can take those and you can put them in a specific directory inside Ignition in order to import those Python libraries.

Don: So we got time for one last question or one final comment or anything you wanna do in wrapping up today, Kevin and I're reiterating that there's a lot of questions, we are gonna get to them, we'll answer them offline after we end off with the webinar. So your final question or final comments.

Kevin: Yeah, so we have one more question here.

Don: Let me... I'll read it to you.

Kevin: Sure.

Don: I have a server script that takes 20 seconds to run, many calls to external HTTP post API, will that run in its own thread or will it be in the same thread as tag event scripts? I don't want to miss a tag event while the server timer event is running.

Kevin: It depends on how you are running that, so if that is a gateway timer script, then that isn't a separate thread pool, that is going to be running in a separate set of threads than tag event scripts. Those threads that are inside the gateway timer scripts, you can actually specify whether you want it to be a shared thread or a dedicated thread, and those shared threads are shared threads for all the gateway timer scripts. So if you have anything that's running 20 seconds, I would highly recommend to run that inside a dedicated thread, that way you're not going to take a thread away from a thread pool for any other script that's running. If you do have that based on a tag change, in order to get it to run in a dedicated script, you can set it up so that it will do a system.util.invokeAsynchronous, and any time that you call that, Ignition gives you a new thread, so that will be a new dedicated thread that that script is running on, and that's one way to protect things. So if you do have 20 or 30 or 500 tag chain scripts, and you wanna make sure that all of them are going to run quickly, if there's anything long running inside those, you can spin up a new dedicated thread from directly inside those threads.

Don: Thanks, Kevin. Listen, I just wanna thank you as attendees, I'm pretty amazed at several hundred of you have stayed with us all the way to the end and still have questions coming in, so thank you for the interest. Kevin will move back to the screen with phone numbers on it, so you can basically take a look and see that if you want to get a demo, if you want to understand the Ignition better, here's some numbers where you can call Melanie or any of her team of account executives. Kevin is certainly available. I think you can tell from the educational webinar today and our Design Like A Pro Series, he's extremely willing to help out in any way possible to help you use script effectively with your projects. If you have a question that wasn't answered in the queue, you will hear back from us to get an answer to that question. With that, we sort of overshot our time, but really appreciate your interest. Thanks for the attention. We're concluded. Have a great day.

Posted on July 27, 2017