Contact and Coil | Nearly In Control



Ladder Logic vs. C#

PC programming and PLC programming are radically different paradigms. I know I’ve talked about this before, but I wanted to explore something that perplexes me… why do so many PC programmers hate ladder logic when they are first introduced to it? Ladder logic programmers don’t seem to have the same reaction when they’re introduced to a language like VB or C.

I mean, PC programmers really look down their noses at ladder logic. Here’s one typical quote:

Relay Ladder Logic is a fairly primitive langauge. Its hard to be as productive. Most PLC programmers don’t use subroutines; its almost as if the PLC world is one that time and software engineering forgot. You can do well by applying simple software engineering methods as a consequence, e.g., define interfaces between blocks of code, even if abstractly.

I’m sorry, but I don’t buy that. Ladder logic and, say C#, are designed for solving problems in two very different domains. In industrial automation, we prefer logic that’s easy to troubleshoot without taking down the system.

In the world of C#, troubleshooting is usually done in an offline environment.

My opinion is that Ladder Logic looks a lot like “polling” and every PC programmer knows that polling is bad, because it’s an inefficient use of processor power. PC programmers prefer event-driven programming, which is how all modern GUI frameworks react to user-initiated input. They want to see something that says, “when input A turns on, turn on output B”. If you’re familiar with control systems, your first reaction to that statement is, “sure, but what if B depends on inputs C, D, and E as well”? You’re right – it doesn’t scale, and that’s the first mistake most people make when starting with event-driven programming: they put all their logic in the event handlers (yeah, I did that too).

Still, there are lots of situations where ladder logic is so much more concise than say, C#, at implementing the same functionality, I just don’t buy all the hate directed at ladder logic. I decided to describe it with an example. Take this relatively simple ladder logic rung:

What would it take to implement the same logic in C#? You could say all you really need to write is D = ((A && B) || D) && C; but that’s not exactly true. When you’re writing an object oriented program, you have to follow the SOLID principles. We need to separate our concerns. Any experienced C# programmer will say that we need to encapsulate this logic in a class (let’s call it “DController” – things that contain business logic in C# applications are frequently called Controller or Manager). We also have to make sure that DController only depends on abstract interfaces. In this case, the logic depends on access to three inputs and one output. I’ve gone ahead and defined those interfaces:

    public interface IDiscreteInput
        bool GetValue();
        event EventHandler InputChanged;

    public interface IDiscreteOutput
        void SetValue(bool value);

Simple enough. Our controller needs to be able to get the value of an input, and be notified when any input changes. It needs to be able to change the value of the output.

In order to follow the D in the SOLID principles, we have to inject the dependencies into the DController class, so it has to look something like this:

    internal class DController
        public DController(IDiscreteInput inputA, 
            IDiscreteInput inputB, IDiscreteInput inputC, 
            IDiscreteOutput outputD)

That’s a nice little stub of a class. Now, as an experienced C# developer, I follow test-driven development, or TDD. Before I can write any actual logic, I have to write a test that fails. I break open my unit test suite, and write my first test:

        public void Writes_initial_state_of_false_to_outputD_when_initial_inputs_are_all_false()
            var mockInput = MockRepository.GenerateStub<IDiscreteInput>();
            mockInput.Expect(i => i.GetValue()).Return(false);
            var mockOutput = MockRepository.GenerateStrictMock<IDiscreteOutput>();
            mockOutput.Expect(o => o.SetValue(false));

            var test = new DController(mockInput, mockInput, mockInput, mockOutput);


Ok, so what’s going on here? First, I’m using a mocking framework called Rhino Mocks to generate “stub” and “mock” objects that implement the two dependency interfaces I defined earlier. This first test just checks that the first thing my class does when it starts up is to write a value to output D (in this case, false, because all the inputs are false). When I run my test it fails, because my DController class doesn’t actually call the SetValue method on my output object. That’s easy enough to remedy:

    internal class DController
        public DController(IDiscreteInput inputA, IDiscreteInput inputB, 
            IDiscreteInput inputC, IDiscreteOutput outputD)
            if (outputD == null) throw new ArgumentOutOfRangeException("outputD");

That’s the simplest logic I can write to make the test pass. I always set the value of the output to false when I start up. Since I’m calling a method on a dependency, I also have to include a guard clause in there to check for null, or else my tools like ReSharper might start complaining at me.

Now that my tests pass, I need to add some more tests. My second test validates when my output should turn on (only when all three inputs are on). In order to write this test, I had to write a helper class called MockDiscreteInputPatternGenerator. I won’t go into the details of that class, but I’ll just say it’s over 100 lines long, just so that I can write a reasonably fluent test:

        public void Inputs_A_B_C_must_all_be_true_for_D_to_turn_on()
            MockDiscreteInput inputA;
            MockDiscreteInput inputB;
            MockDiscreteInput inputC;
            MockDiscreteOutput outputD;

            var tester = new MockDiscreteInputPatternGenerator()
                .InitialCondition(out inputA, false)
                .InitialCondition(out inputB, false)
                .InitialCondition(out inputC, false)
                .CreateSimulatedOutput(out outputD)







                .AssertThat(outputD).ShouldBe(true); // finally turns on

            var test = new DController(inputA, inputB, inputC, outputD);


What this does is cycle through all the combinations of inputs that don’t cause the output to turn on, and then I finally turn them all on, and verify that it did turn on in that last case.

I’ll spare you the other two tests. One check that the output initializes to on when all the inputs are on initially, and the last test checks the conditions that turn the output off (only C turning off, with A and B having no effect). In order to get all of these tests to pass, here’s my final version of the DController class:

    internal class DController
        private readonly IDiscreteInput inputA;
        private readonly IDiscreteInput inputB;
        private readonly IDiscreteInput inputC;
        private readonly IDiscreteOutput outputD;

        private bool D; // holds last state of output D

        public DController(IDiscreteInput inputA, IDiscreteInput inputB, 
            IDiscreteInput inputC, IDiscreteOutput outputD)
            if (inputA == null) throw new ArgumentOutOfRangeException("inputA");
            if (inputB == null) throw new ArgumentOutOfRangeException("inputB");
            if (inputC == null) throw new ArgumentOutOfRangeException("inputC");
            if (outputD == null) throw new ArgumentOutOfRangeException("outputD");

            this.inputA = inputA;
            this.inputB = inputB;
            this.inputC = inputC;
            this.outputD = outputD;

            inputA.InputChanged += new EventHandler((s, e) => setOutputDValue());
            inputB.InputChanged += new EventHandler((s, e) => setOutputDValue());
            inputC.InputChanged += new EventHandler((s, e) => setOutputDValue());


        private void setOutputDValue()
            bool A = inputA.GetValue();
            bool B = inputB.GetValue();
            bool C = inputC.GetValue();

            bool newValue = ((A && B) || D) && C;
            D = newValue;

So if you’re just counting the DController class itself, that’s approaching 40 lines of code, and the only really important line is this:

    bool newValue = ((A && B) || D) && C;

It’s true that as you wrote more logic, you’d refactor more and more repetitive code out of the Controller classes, but ultimately most of the overhead never really goes away. The best you’re going to do is develop some kind of domain specific language which might look like this:

    var dController = new OutputControllerFor(outputD)
        .WithInputs(inputA, inputB, inputC)
        .DefinedAs((A, B, C, D) => ((A && B) || D) && C);

…or maybe…

    var dController = new OutputControllerFor(outputD)
        .WithInputs(inputA, inputB, inputC)
        .TurnsOnWhen((A, B, C) => A && B && C)
        .StaysOnWhile((A, B, C) => C);

…and how is that any better than the original ladder logic? That’s not even getting into the fact that you wouldn’t be able to use breakpoints in C# when doing online troubleshooting. This code would be a real pain to troubleshoot if the sensor connected to inputA was becoming flaky. With ladder logic, you can just glance at it and see the current values of A, B, C, and D.

Testing: the C# code is complex enough that it needs tests to prove that it works right, but the ladder logic is so simple, so declarative, that it’s obvious to any Controls Engineer or Electrician exactly what it does: turn on when A, B, and C are all on, and then stay on until C turns off. It doesn’t need a test!

Time-wise: it took me about a minute to get the ladder editor open and write that ladder logic, but about an hour to put together this C# example in Visual Studio.

So, I think when someone gets a hate on for ladder logic, it just has to be fear. Ladder logic is a great tool to have in your toolbox. Certainly don’t use ladder logic to write an ERP system, but do use it for discrete control.



  • Andy H · July 12, 2011 at 9:25 am

    You’re dead on Scott! As a dev that stared at ladder logic for a while I’d add that the hardest thing I found to deal with is that every rung is independent. When you expected to set a value and then have something run once, it just keeps running over and over. However, with a bit of time you learn how to manage those situations and know how to handle the same cases.


  • Aled · July 21, 2011 at 7:30 am

    A fascinating post giving a bit of insight into Ladder Logic! It’s an area I’m thinking of pursuing (PLCs, systems control and automation etc.) after I graduate and this was a nice and informative post. Thank you.

  • AJ · August 22, 2011 at 8:54 am

    I’ve read both your post and StackOverflow question, and I don’t buy your reasoning. Using C# the way you’ve shown is – well – not the best idea.

    Consider something more real. A system with say 40 pumps, 80 valves, half of them working with simple start/stop/open/close principle, the others are controlled with PIDs, inverter drivers etc.

    For such a system, “You can do well by applying simple software engineering methods as a consequence, e.g., define interfaces between blocks of code, even if abstractly.” is a perfect argument in my opinion.

    Abstract a kind of process devices using whatever you can – function blocks, subroutines… Define interface contract for the kind of devices. Build “higher layer” software upon this contract. Ensure that high level code do not depend on low level details.

    You can express all of this with ladder logic if you want. Now, compare the time you need to implement and test such a system in ladder logic, using or not using abstractions.

  • Author comment by Scott Whitlock · August 22, 2011 at 9:38 am

    @AJ: If you’re only thinking about implementation time, you’re missing the point. On an assembly line where downtime costs $5000 per minute, troubleshooting time far outweighs the time it takes to implement something. This is something that traditional PC programmers never seem to be able to understand about industrial automation until they’re standing at a laptop next to an assembly line with a production manager asking them how long it’s going to take to fix the problem, every 30 seconds. If you haven’t been in that situation, then I question your qualifications.

  • Oliver · December 21, 2011 at 12:22 pm

    The C language is a language much more powerful than straight, if I see it after spending more than 6 years in the area of ​​programming in both software applications for PC and PLCs, as well as hardware design automation. The language was designed for a ladder ON / OFF and used by electricians craft operators. Today’s electronic control and logic as that used by the C language is more efficient and effective programming time as well as being simpler, occupies less area and can get to program the PLC Hardware outlining whether to change the environment of programming. Try to set a signal processing, vector control, etc.. with the language ladder and see the difference.

  • Peter · December 19, 2012 at 8:17 am

    I Like the way he says compare it to C#
    I got news for you, most micro controllers use C or C+ not C# The reasons for that is, C# is bloated, and under a bunch of patents that scare the bgezers out of manufacturers. They don’t trust Microcrap, they have been burned by them in the past. Even hobbyists use things like Arduino that are open source, and running C.
    Microcrap is trying hard to brake into the micro controller world, but from what I can see, they are losing the war. As for ladder line, I think its a dead language. Flow chart languages are as fast and can do anything that C can or even C# can do. Its just that there are still a bunch of old timers out there that are still using Ladder.

  • Brian · January 10, 2013 at 3:22 pm

    I agree, Ladder is far more suited for the Industrial Automation world than C. They’re different languages for different applications.

    Besides, the language has evolved quite a bit in RS Logix 5000. I use both the 5000 and 500 platforms at work and I pull my hair out when I have to use 500. 5000 actually gives you the ability to use “subroutines” of a sort in their Add-On instructions. This avoids having to program the same ladders over and over again with different addresses. Instead, you create the ladder once with parameters, and then call the blocks in your main ladder, providing addresses to “fill in” the parameters for each block. You can reduce a program of hundreds of rungs down to one or two because the program is just “recycling” the logic with different values.

  • Ravi · April 27, 2015 at 8:25 am

    Can you provide source fro this example

  • Ryan · December 20, 2015 at 10:53 pm

    I don’t mean to resurrect a dead post, and while I see and understand where you are coming from, I think you are missing the point of what is practically impossible for Ladder Logic to do in any space-efficient manner. Sure it is fine for simple if-this-then-that style programming, but lets talk about data-driven programming and sequential programming for a second. What if I want to program an elevator using it’s basic if-this-then-that style: if user presses up button, go to their floor, stop, open door, wait 10 seconds, try close door, loop (if door blocked, stop, open, wait 10s try close), if inside button pressed, go to that floor, stop along the way if a user presses the up button as you reach it, etc…. This is a really huge state machine in ladder logic, but a simple sequential algorithm in a procedural language, even if it does require a few event listeners.

    Here is another practical example, lets say that an assembly line needs a part-selection interface for the operators, which handles an SQL database of information for each part. Write a piece of ladder logic code to handle the transfer of bits from an RS-232 connection from this machine into the appropriate registers to use the data. Also, much of this data requires mathematical processing to handle effectively on the PLC side… Good luck. I’ve seen code like this take thousands of rungs, and I’ve personally been spending months dianosing just two of these such codes at my factory.

    All this to say that ladder logic is fine for systems that you would EXPECT electricians to be capable of maintaining, such as simple processes, and code AFTER data has been processed and placed in proper registers simply waiting to be used, but if you want to apply real COMPUTATION to your assembly line, rather than just signaling, it’s absolutely dreadful.

  • Author comment by Scott Whitlock · December 21, 2015 at 4:47 am

    @Ryan – Thanks for you comments. Your first paragraph is completely incorrect. Elevator control systems are definitely coded in ladder logic and it wouldn’t take much effort. I would consider such a system to be a toy compared with other machines I’ve programmed. If you want things to happen in a sequence, you just use the Step Pattern. If you want to choose between which sequence to execute, you use the Mission Pattern.

    If you don’t like doing that in Ladder Logic, you can use Sequential Function Chart. But that’s not necessary.

    Your idea of looping until an input turns on is usually wrong because the rest of your logic stops while you’re looping and waiting, and in ladder logic the other parts of your program keep running. Never loop on an input. And if you don’t loop on an input then you’ll have to keep a state machine going anyway, so you haven’t improved the situation.

    Your second paragraph is correct. Use the right tool for the job.

    Third paragraph: I would expect an electrician to be able to diagnose an elevator system. I think that’s pretty obvious. Also, if you need decent computation, why not just use Structured Text? All modern PLCs support that. I agree none of them do database access well yet, so I would definitely go to some other system or language to integrate with a database. In my case that’s typically C# and .NET, but could be anything.

  • Neil Highley · February 14, 2016 at 12:14 am

    Great post, Scott. As an n-tier C# developer for the last few years (understatement..) I am dipping a toe in PLC and automation.
    Your explanations of the ladder are very clear, and being a developer for a while I appreciate the push back that you will always be getting from an industry that puts so much focus on almost tribal loyalty of esoterics.
    I’m with you, insofar as you must use the right tool for the job, as I have seen so much over engineered nonsense in my time that seems to have been done merely to further a current fad, rather than looking at the clients concerns such as maintenance and readability.

Leave a Reply



Theme Design by