AMT’s Josh Galloway explores essential design considerations for enhancing system quality, focusing on stability, reliability, performance, and maintainability in systems using Ignition by Inductive Automation.
Blog by IT Lead and Digital Solutions Architect Josh Galloway
When designing well-thought-out systems, quality attributes must not be an afterthought. These are generally considered non-functional requirements as they don’t speak to the ‘what’ of what a system does, but instead address the ‘how.’ Generally, these quality or non-functional requirements can be partitioned into a few main categories:
A robust, well-designed system must address each of these categories. Careful consideration for each of the following areas during system design will contribute to the overall quality of a system.
By far the biggest area of improvement from a system stability and reliability perspective is to start adding error handling when and where appropriate. Scripts need to consider the reality that things will fail in unpredictable ways. Ignition scripts must address potential error states that can arise in the code. Every external call to an API or DB can (and will) fail and the context and consequences of failures in every single call must be dealt with.
Errors should be anticipated and expected and failing to address them should not be an option on a large production system. Not only should errors be expected, but the software architect should be able to contextualize and act on errors as appropriate and always have a contingency for unexpected errors. For example, any API call can fail for a myriad of reasons but a common one is simply transient unavailability due to networking hiccups. A well-designed error-handling strategy around external API calls can include retry mechanisms to see if the external service becomes available in a reasonable amount of time, displaying an error to users, throwing a system fault, or, in the worst cases, shutting down the system on purpose if an indeterminant state is detected or the context of the call and error makes it the safest course of action.
For more information, read this introduction to error handling in Ignition Python scripting.
After improving the stability and reliability of the system via the addition of error handling, the next big win also begins to address the performance piece. Mechanisms like tag transaction groups and polling intervals should be carefully considered within the context of the overall system. Adding a ton of tags on fast refresh intervals can start to overload devices like PLCs quickly. Once overloaded, these devices can start behaving in unpredictable ways, especially in the context of a large SCADA system where messaging is of very high importance.
As with error handling, the effects and ramifications of design choices to stay sympathetic to the hardware and different system components are one of the keys to quality.
Data integrity and consistency are fundamental requirements for stable, reliable, efficient systems. That starts with good schema design and a well-thought-out and understood query strategy. Properly modeling relational data, following standards, and adding constraints and indexes at the database (DB) level all help to support this.
Data modifications (updates, inserts, deletes) also need to be carefully considered. Databases support transactions that can be thought of as atomic units of work: even though an operation may span multiple queries, all of those queries need to succeed for any changes to be persisted to the DB. If any query fails, the entire transaction/operation can be rolled back safely with no changes made. This helps ensure data integrity and consistency over time.
Ignition supports DB transactions in its scripting so it should be used whenever these operations arise.
For more information and support, read this article on Ignition scripting.
Reliable systems have a certain degree of determinism and fault tolerance built in. Similarly, SCADA interfaces, handshakes, and data exchanges need to be thought out with quality concerns in mind. A good interface is well-defined, with both sides understanding the responsibilities and limitations. Data validation should be performed wherever possible, and errors should always be expected and acted on as appropriately as possible. A good interface or handshake is highly context-specific.