Dispelling Myths and Misconceptions about Modern Alternatives to Traditional PLCs: Part 2 – Operating Systems
From Part 1: “Myth #2: Only PLC programming languages are suitable for industrial automation”
This article was supposed to directly address programming languages, but really, we need to look at what the programs run on first–operating systems.
For those unfamiliar, the image for this article is the Python programming language logo, since Python seems to be at the center of these debates. While I don’t think it’s a good option for industrial control (mainly because of duck typing and garbage collection), in theory, it could be used for real-time control. You may roll your eyes, but bear with me. The high-level reasons for this are:
- The operating system (OS) running the control program is much more important than the program itself when it comes to real-time deterministic execution.
- The behavior of any programming language depends on its implementation.
If you’re wondering what language I’m speaking in, never fear, all will be explained. Read on.
What’s an operating system (OS)?
An operating system (e.g., Windows, Android, iOS, Linux, etc.) is the software that acts as a bridge between programs and the underlying hardware. It handles memory, I/O, file storage, scheduling, etc. Not every computer system needs an operating system, but most use them, including PLCs. In a PLC, the OS (which is part of the firmware) handles the I/O, serial and Ethernet communications, faults, etc. PLCs and other mission-critical devices use a specialized type of OS designed for reliable timing, a real-time OS (RTOS).
What is real-time deterministic execution?
One of the goals of PLCs, especially in motion control and safety applications, is to make sure that the program runs in a consistent manner i.e., every time it runs, it runs in roughly the same amount of time–no pauses during or delays to its start. There are some key terms here:
- Real-time: means the program needs to respond within a predictable and specific time constraint (the deadline).
- Determinism: how consistently the program runs within the time constraint.
- Jitter: the variation between the expected execution time and the actual execution time.
There are varying levels of “real-time” depending on the use case. In safety situations, missing the deadline (exceeding the time constraint) could result in loss of life–this is called hard real-time. In less critical applications where missing the deadline is simply undesirable, it’s called soft. It’s not exactly black and white though–as I said, there are varying levels.
Most computer systems, whether a PLC or a Windows PC, must multitask. Nowadays computers have multiple CPU cores so they can truly do things simultaneously, but what’s happening behind the scenes when you run several programs “at the same time” is that the OS is letting each program run a little bit before doing something else. This is called scheduling. We could spend a ton of time on this topic, but what’s important is just to understand that each processor/core is running one thing at a time. It just switches between what’s running so rapidly that we don’t notice. This is called context switching.
The ability for a computer to do something real-time depends on how the scheduling is set up, and most importantly, how the OS’s scheduler is allowed to interrupt what is currently running to do something else. This is called preemption. Most operating systems align these scheduling events with hardware “ticks” (like the ticking of a clock) that typically happen every 1-10 milliseconds (100 to 1000 Hz).
Regardless of the programming language or amount of work to be done, the program scan/cycle will probably complete many times before the next tick…but because it’s always running, at some point it will get paused so something else can run. Without the proper setup, the pause in between cycles will align with the OS kernel ticks instead of what you set in your program.
This is the point–whether you’ve got a program in Python, ladder, C, Rust, etc., chances are that its execution will get paused and the jitter will be as least as much as the time between ticks.
You can play around with this using the following C program:
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#define DELAY 5e5L // 500,000 nanoseconds == 0.5 milliseconds
static volatile sig_atomic_t running = 1;
void handle_sigint(int sig)
{
running = 0;
}
int main(void)
{
const struct timespec delay =
(const struct timespec){.tv_sec = 0, .tv_nsec = DELAY};
struct timespec ts1, ts2;
long int diff, maxdiff = 0;
signal(SIGINT, handle_sigint);
while (running)
{
clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
nanosleep(&delay, NULL); // simulate a periodic task
clock_gettime(CLOCK_MONOTONIC_RAW, &ts2);
diff = 1e9L * (long int)(ts2.tv_sec - ts1.tv_sec) +
(ts2.tv_nsec - ts1.tv_nsec);
if (diff > maxdiff)
{
maxdiff = diff;
printf("\rJitter: %.1f ms", (double)(maxdiff - DELAY) * 1e-6);
fflush(stdout);
}
}
printf("\n");
return 0;
}
Now, in a hard real-time system, the program in question can preempt anything else in the schedule, including operating system tasks, in order to ensure deadlines are met. In a soft real-time system, the program can preempt most everything, but usually not OS tasks. In that case, deadlines are met most of the time.
This is all in contrast to general-purpose operating systems (GPOS’s) like Windows, which are designed to optimize user experience.
That all being said, a GPOS can be made to run real-time programs. Windows with certain real-time extensions like IntervalZero’s RTX64 or Beckhoff Automation’s TwinCAT 3 runtime allow it to run programs in real-time. Windows IoT edition has this support built-in. Linux can do it as well with the CONFIG_PREEMPT (soft) and CONFIG_PREEMPT_RT (hard) options. This is a key point–you can achieve the same or better real-time performance with non-PLC hardware as long as the OS is set up correctly. As mentioned in part 1, all kinds of devices all around you are doing this: the microcontroller in a pacemaker, the computer in an airplane, etc.
So, to wrap up, whether you have an extremely optimized control program written in C, a sloppy one written in Python, or an average one written in ladder logic, it doesn’t really matter if the OS isn’t set up correctly. The OS must be set up to match the real-time requirements of the application. The execution speed and even the variation in the program execution aren’t going to matter compared to how the OS schedules and pauses your program.
You can of course opt to just load a program directly onto a microcontroller without an RTOS, but then I/O, communication, etc. becomes your problem and then you have to schedule those tasks yourself.