Contact and Coil | Nearly In Control

Archive for March 2017

Mar/17

26

PLC Programming goes Imperative

Decades ago, computer science emerged from the dark ages of assembly language programming and created two new languages: Lisp and Fortran. These are two very important computer languages because they exist at opposite ends of an imagined spectrum in the eyes of computer scientists: functional languages vs. imperative languages.

Fortran “won” the first battle, not least because imperative languages are closer to how the CPU actually does things, so back in the day when every little CPU cycle mattered it was easier to understand the performance implications of a Fortran program than a Lisp program. Plus, if you were already programming in assembly, then you were already thinking about how the computer was executing your code. In fact, the next big imperative language, C, is often referred to as “portable assembly language.” Fast forward to now, and modern languages like C#, Java, Python and Ruby have all grafted a lot of functional programming features onto their imperative programming basic syntax. In C#, for instance, Linq is a direct rip-off of Lisp’s S-Expressions and it now has Closures and lambda functions. Functional languages provide ways to think at a higher level than imperative languages. In a functional program you describe what you want and in an imperative program you describe how to do it.

Here’s an example in C#, using imperative programming:

var data = new int[] { 1, 2, 3, 4, 5 };
var sumOfSquares = 0;
for(var i = 0; i < data.Length; i++)
{
    sumOfSquares += data[i] * data[i];
}

…and the same thing done functionally:

var data = new int[] { 1, 2, 3, 4, 5 };
var sumOfSquares = data.Select(x => x * x).Sum();

In the second case, I’m taking the list of numbers, using Select to translate that into a list of their squares (also known as a Map operation) and then using Sum on the resulting list to compute an aggregate sum (also known as a Reduce operation). It has some interesting advantages. For instance, the original code can’t be split across multiple cores, but the latter can. Also, if you know both syntaxes, the latter is easier to read and understand.

Now take ladder logic. I’ve made the claim before that basic ladder logic (with contacts and coils) is actually a functional language. A simple example might be ANDing two inputs to get an output, which in C# would look like this:

var output = inputA && inputB;

That’s actually functional. If I wanted to write it imperatively I’d have to do something like:

var output = false;
if(inputA && inputB)
{
  output = true;
}

In ladder logic, that would be the equivalent of using an unlatch (or reset) instruction to turn off an output and then using a latch (or set) instruction to turn on the output if the A and B contacts were true. Clearly that’s not considered “good” ladder logic.

Similarly, a start/stop circuit goes like this:

var run = (start || run) && !stop;

Now historically, mathematicians and physicists preferred functional languages because they just wanted to describe what they wanted, not how to do it. It’s worth noting that electricians, looking at ladder logic, prefer to see functional logic (with contacts and coils) rather than imperative logic (with sets, resets, and move instructions).

In recent years we’ve seen all major PLC brands start to include the full set of IEC-61131-3 languages, and the most popular alternative to ladder logic is structured text. Now that it’s available, there are a lot of newer automation programmers who only ever knew imperative programming and never took the time to learn ladder logic properly, and they just start writing all of their logic in structured text. That’s why we’re seeing automation programming slowly shift away from the functional language (ladder) towards the imperative language (structured text).

Now I’m not suggesting that structured text is bad. I prefer to have more tools at my disposal, and there are definitely times when structured text is the correct choice for automation programming. However, I’d like to point out that the history of computer science has been a progressive shift away from Fortran-like imperative languages towards Lisp-like functional languages. At the same time, we’re seeing automation programming move in the opposite direction, and I think alarm bells should be going off.

It’s up to each of us to make an intelligent decision about what language to choose. In that respect, I want everyone to think about how your brain is working when you program in an imperative style vs. a functional style.

When you’re doing imperative programming, you’re holding a model of the computer in your mind, with its memory locations and CPU and you’re “playing computer” in your head, simulating the effect of each instruction on the overall state of the CPU and memory. It’s only your intimate knowledge of how computers work that actually allows you to do this, and it’s the average electrician’s inability to do this which makes them dislike structured text, sets, resets, and move instruction. They know how relays work, and they don’t know how CPUs work.

If you know how CPUs work, then I understand why you want to use structured text for everything. However, if you want electricians to read your logic, then you can’t wish-away the fact that they aren’t going to “get” it.

As always, be honest with yourself about who will read your logic, and choose your implementation appropriately.

·

When traditional PC programmers see ladder logic, they think ladder logic programmers are terrible programmers. Being both a .NET developer and a ladder logic programmer, this has caused me a lot of frustration and confusion over the years. I have one foot in each world, and yet I choose to write C# programs one way and ladder logic programs another. Why?

Let’s ignore the fact that most traditional programmers just don’t grok ladder logic at all, because their minds think about programs sequentially rather than in parallel. The real reason they hate ladder logic is because ladder logic programmers avoid things like loops, indexed addressing and subroutines. To them, this means you’re programming at the level of an 8 year old.

The thing is, I know how to use loops, arrays, and subroutines, not to mention object oriented programming and functional programming constructs like s-expressions, closures and delegates. Still, I choose to write simple and straightforward ladder logic. Why would I, an experienced programmer, choose to write programs like an 8 year old? Do I know something they don’t?

I spend a lot of time trying to get people to think about why they do things a certain way. Everyone wants that simple rule of thumb, but it’s far more valuable to understand the first principles so you can apply that rule intelligently. Decades of computer science has given us some amazing tools. Unfortunately, a carpenter with twice as many tools in her tool box is simply twice as likely to pick the wrong tool for the job if she doesn’t understand the problem the person who invented that tool was trying to solve.

The first time you show a new programmer a “for” loop, they think, “Amazing! Instead of typing the same line out a hundred times, I can just type 3 lines and the computer does the same thing! I can save so much typing!” They think this because they’re still an idiot. Don’t get me wrong, I was an idiot about this too, and I’m still an idiot about most things. What I do know, however, is that for loops solve a much more important problem than saving you keystrokes. For loops are one of many tools for following the Once and Only Once (OAOO) Principle of software development.

The OAOO principle focuses on removing duplication from software. This is one of the most fundamental principles of software development, to the point where it’s followed religiously. This principle is why PC programmers look at ladder logic and instantly feel disgust. Ladder logic is full of duplication. I mean, insanely full of duplication. So how can you blame them? God said, “let there not be duplication in software,” and ladder logic is full of duplication, thus ladder logic is the spawn of Satan.

That’s because programmers who believe the OAOO principle is about removing duplication are idiots too. Don’t they ever wonder, “why is it so important to remove duplication from our code?” Should we really worry about saving a few bytes or keystrokes? NO! We focus on:

  1. Making it do what it’s supposed to do
  2. Making it obvious to the reader what the program does
  3. Making it easy to make changes when the requirements change

… in that order.

In fact, #3 is the real kicker. First of all, satisfying #3 implies you must have satisfied #2, so ease of understanding is doubly important, and secondly, satisfying #3 implies you can predict what will change.

Imagine if you have to print the numbers from 1 to 5. If I asked a C# programmer to write this, they’d likely write something like this:

for(var i = 1; i <= 5; i++)
{
    Console.WriteLine("{0}", i);
}

… of course I could write this:

Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
Console.WriteLine("4");
Console.WriteLine("5");

Why is the first way better? Is it because it uses fewer keystrokes? No. To answer this question, you need to know how the requirements of this piece of code might change in the future. The for loop is better because many things that might change are only expressed once. For instance:

  • The starting number (1)
  • The ending number (5)
  • What to repeat (write something to the screen)
  • What number to print
  • How to format the number it prints

If the requirements of any of these things change, it’s easy to change the software to meet the new requirements in the first case. If you wanted to change the code so it prints every number with one decimal place, the second way clearly requires 5 changes, where the first way only requires one change.

However, what if the requirements changed like this: print the numbers from 1 to 5, but for the number 2, spell out the number instead of printing the digit.

Okay, so here’s the first way:

for(var i = 1; i <= 5; i++)
{
    if(i == 2)
    {
        Console.WriteLine("two");
    }
    else
    {
        Console.WriteLine("{0}", i);
    }
}

… or if you wanted to be more concise (but not much more readable):

for(var i = 1; i <= 5; i++)
{
    Console.WriteLine(i == 2 ? "two" : i.ToString());
}

Here’s the change using the second way:

Console.WriteLine("1");
Console.WriteLine("two");
Console.WriteLine("3");
Console.WriteLine("4");
Console.WriteLine("5");

Here’s the thing… given the new requirements, the second way is actually more readable and more clearly highlights the “weirdness”. Does the code do what it’s supposed to do? Yes. Can you understand what it does? Yes. Would you be able to easily make changes to it in the future? Well, that depends what the changes are…

Now think about some real-life ladder logic examples. Let’s say you have a machine with some pumps… maybe a coolant pump and an oil pump. Your programmer mind immediately starts listing off the things that these pumps have in common… both have motor starters with an overload, and both likely have a pressure switch, and we might have filters with sensors to detect if the filters need changing, etc. Clearly we should just make a generic “pump” function block that can control both and use it twice, right?

NO!

Look, I admit that there might be some advantage to this approach during the design phase if you had a system with 25 identical coolant pumps and your purchasing guy says, “Hey, they don’t have the MCP-1250 model in stock so it’s going to be 8 weeks lead time, but they have the newer model 2100 in stock and he can give them to us for the same price.” Maybe it turns out the 2100 model has two extra sensors you have to monitor so having a common function block means it takes you… 20 minutes to make this change instead of an hour. We all know how much you hate repetitive typing and clicking.

On the other hand, when this system goes live, making an identical change to every single pump at exactly the same time is very rare. In fact, it’s so rare that it’s effectively never. And even if that were to ever actually happen, the amount of programming time it actually saves you is so tiny compared to the labor cost of actually physically modifying all those pumps that it’s effectively zero.

However, since these are physically different pumps, you’re very likely to have a problem with one pump. When your machine is down and you’re trying to troubleshoot that pump, do you want to be reading through some generic function block that’s got complicated conditional code in it for controlling all 50 different types of pumps you’ve ever used in your facility, or do you want to look at code that’s specific to that pump? And maybe the motor overload on that pump is acting up and you need to put a temporary bypass in to override that fault. Do you really want to modify a common function block that affects all the other pumps, or do you want to modify the logic that only deals with this one pump? What’s more likely to cause unintended consequences?

So this is why ladder logic written by experienced automation programmers looks like it was written by an 8 year old who just started learning Visual Basic .NET last week. Because it’s better and we actually know why.

·

Theme Design by devolux.nh2.me