How PetriLLD Came About
I created PetriLLD because I didn't want to learn how to program PLCs in ladder logic diagram code. This is the story of why and how and what happened next.
Automating the automaters
If you don't know what ladder logic is, well it's sort of like assembler for a generic sort of robustised computer used in industrial automation called a PLC or programmable logic controller. Lots of people use ladder logic — even today. There are a number of alternatives, but they haven't caught on; perhaps because they are not universally available, perhaps because the programs being developed are not very complex. Possibly another reason stems from the fact that there are a lot of legacy PLCs installed in factories. They do their job, but they don't support any of these newfangled languages.
In 2003, at Cambridge University, manufacturing engineering students were being taught how to design ladder logic. The approach involved first designing a Petri net and then translating the net into ladder code. This seemed to me to be a good opportunity to take a manual process and automate it. Just as programmers for general purpose computers have gradually migrated to high-level languages that work for more than one processor, so we can expect that ladder logic programmers might eventually move to higher-level languages too.
The trouble was that the approach currently used by students to translate Petri nets into ladder logic had the potential to produce incorrect behaviour. Specifically the behaviour of the translated ladder logic didn't exactly match the well defined behaviour of the Petri net it was derived from. For most nets this was not a problem. The difficulty is that it could be problematic. For example, it would occasionally produce situations where two places were marked for a net that should have only allowed one to be marked. An automatic procedure cannot assume that the programmer checks that such side-effects are acceptable—it must not produce them in the first place.
So I decided to design the translation from scratch. For an authoritative source of information on Petri nets, I used an article on Elementary nets. Elementary nets are just like ordinary PNs but they only allow one mark per place. This is consistent with translating the net into ladder logic, since this logic generally works in terms of individual bits. This may seem limiting but actually working at the bit level is more natural when dealing with industrial sensors and actuators, which are, generally speaking, digital in nature.
As an aside, other work in this area has used Condition-Event nets. Strictly speaking, C/E nets are not appropriate as they are reversible. If you take away the reversibility, you end up with an Elementary net. So it is clearer to say "Elementary net" here. A further aside is that other work has looked at emulating ordinary Petri nets in a PLC, but this has the problem that it may use more memory than necessary in many cases. Also, it is not really clear how to handle the potential for overflow (when you have more than 2^w tokens per place, where w is the word size).
Although I tried to keep to the EN model as closely as possible, there was one area where a compromise had to be made, and this was PLC inputs. PLC inputs correspond to the state of a sensor and to the PLC program are read-only. In comparison, in the EN model, when a transition fires due to a place being marked, the place loses the mark. Transitions always update the state of the place that they are responding to. To allow for PLC inputs, a new kind of place was added to the model. This was termed an input place and represented graphically by a triangle.
It turns out that this is completely equivalent to changing the arc type to be an enabling (for arcs from an input place) or inhibiting arc (for arcs to an input place). From the developers point of view, it is sensible to think in terms of input places rather than enabling arcs because, from the point of view of the PLC, the place will always be read-only — there can never be a mix of normal and enabling/inhibiting arcs.
With the model worked out, I built the first version and experimented with converting some existing control logic used for a robotics demo to Petri nets with my new program. The first program that I did was to control a Montech Positioning Station. This is a docking area on the monorail track in the Cambridge lab. This was very successful; I was able to test the behaviour using the simple simulator my early version of PetriLLD and had working code on my second or third go.
This was all very exciting, but I quickly realised that if students were going to be using it then it had better be easy to use and able to do everything they needed. One of the biggest gaps at this stage was the ability to compile code for multiple different nets to a single PLC. To solve this, I came up with the idea of instances.
A Petri net encodes the behaviour of a device but we can keep separate the details of exactly which device and how the device is connected to the PLC. The net can be thought of as a subroutine, with the address details as parameters. Alternatively, using the object-oriented paradigm this time, you can think of a net as a class, with the address details being an instance of that class. By adding in the concept of instances, and by allowing multiple nets in a "project", PetriLLD could now code the complete program for a single PLC in one step—even when that PLC controlled many different devices.
Compiling to different PLC models
At this stage, PetriLLD compiled in a format that worked with a program called SYSWIN—the software supplied by OMRON for use with their PLCs. Everything seemed set to go for the students to be able to use PetriLLD for their lab work. Unfortunately, just before the lab was due to start, the PLCs were upgraded and with the upgrade came new software—a program called CX-Programmer. The good news was that it could read some of the file formats produced by SYSWIN. The bad news was that this didn't include the file format that PetriLLD produced. At about the same time, the lab received some Rockwell PLCs. I quickly realised that PetriLLD needed a generalised mechanism to producing code that would work for a variety of languages.
In the end, I produced two slightly different mechanisms. The first, suitable for PLCs, coded the Petri net as a series of boolean statements. To make it easy to derive plug-ins for different sorts of compilers, this form first develops the expression as a hierarchical object structure in memory. The classes used are determined by an AbstractFactory design pattern, thus allowing the different plug-ins to give different objects to represent AND, OR, NOT etc. This version only produced boolean statements, there was no flow control. This makes the code more readily understandable in ladder logic form.
The second mechanism produced if-then-else statements, suitable for general purposes languages such as Visual Basic and Java. This second mechanism grew out of the needs of the students. They had reached a stage where they could now quickly and easily complete all of their PLC code and were starting to do some very sophisticated things with the PLC. The problem had now shifted upwards.
The difficulty with Visual Basic
The Cambridge MET robotics lab calls for an integrated manufacturing system that makes use of RFID (radio frequency identification), built-to-order products, ordering systems and so forth. It can't all be done by a PLC—interfacing with order databases and RFID tag readers is a job for a general purpose computer. Therefore, some of the system is developed using Visual Basic and runs on a PC.
The difficulty comes in making the Visual Basic program responsive. That is, allowing user interaction while VB is waiting for the PLC to do something, or being able to wait for one PLC function while a separate function (or different PLC) demands attention. Traditional programmers tend to see this problem and assume that multi-threading is the only answer.
In the course of teaching the lab, the students and I came up with other ways of thinking about this problem that didn't involve threads. The first one was finite state automata. This approach works quite well but suffers from a state explosion. For example, if you have a process that involves 10 states, it is quite manageable but if there are 3 such processes, then you may need to write code for 1000 different states. Of course, it doesn't always get this bad, but the number of states can be quite high.
The second approach is to use a Petri net. It turned out that the Elementary Net structure used for PLCs, where each place can have only 0 or 1 mark, also works quite well for the high-level, PC control programs. Initially the students designed their Petri nets in PetriLLD and then manually translated this into VB code. This quickly became a nightmare, though. Every debugging episode became a laborious procedure of checking what the Petri net did in simulation, then checking whether the VB code corresponded, and so forth. I quickly realised that I was going to save a lot of difficulty by automating at least part of the job of converting to VB.
The main motivator for generating VB directly came from one hard working student that we'll call J. J was manually compiling Petri nets to ladder logic but was getting nowhere, despite staying up all hours of the night and spending hours checking his code. When I came to help him, I found a huge Petri net, hundreds of nodes, with lines going everywhere. Automating the process of converting the net, and making it so that he could make a change in the net and then compile that through to Visual Basic helped immensely. It wasn't the whole problem. The complexity of the net overwhelmed him. Obviously, J needed a way to express what he wanted in smaller chunks—in other words, he needed to be able to make Petri net modules.
Modularity in Petri nets has often been added by using sub-nets. Such "hierarchical" nets are modular in a sense but do not encourage module reuse; nor do they have very sophisticated interfaces between modules. This problem of making modular Petri nets (sometimes referred to as the issue of compositionality) becomes more important when you try to make use of them for non-trivial systems. Although one might have a situation where the expression of a particular behaviour is possible with a single net, in practise we want to have not only different nets for different devices but also different nets for logically separate parts of the behaviour within a single device.