How L5X Format Can Accelerate Engineering Processes
Maxim Krylov
Co-founder of Kiptr.com
This is the third part of an article about L5X file format.
When we say "Script" we normally mean a relatively small program that is written inside another software package by the user. It is utilizing the programming language that a software package offers. For example, Excel allows scripting in Visual Basic. Google Sheets uses JavaScript. Scripting (and macros) allows to close the gaps between the user desires and the software functionality. It is impossible to create a totally universal software product that will cover the needs of absolutely all the users. There will always be someone who is happy with 90% of the functionality but needs another 10% that are not existing.

I will not focus on how to create scripts in Excel. There are plenty of resources devoted to this. Instead, let's analyze how scripting can help us and what is the best way of using it.

As we have already found out – we need to build a specific line of text and insert the required values. So, if we want to create a complete L5X file in Excel, we will need to go line after line from the very beginning of the L5X file and to the very end of it.
Quite some text to write!
And what if we want to do the opposite operation – read the existing L5X file and, say, populate the list of alarms?

One of the ways to do it is to scan every line of L5X, identify the beginning of the tags element (<Tags> … </Tags>), and start to search for the beginning of every new tag (<Tag>). Once found – search for the tag type, then search for the alarm message, etc. And this is just one of the many things we want to do with the code!

Excel is an excellent tool for some purposes. However, it will still require quite some coding because you will have to go through the text and process it on a low level. It becomes even more challenging if you want to start adding new tags, routines, or other elements.
What can ease up our life significantly is a so-called parser. Parsers are software libraries that already "know" the structures they were built to work with. You may think of it as a function that converts a text to an object.
Let's take any L5X file and run it through the parser. Commands to convert may look different, depending on the language you use. But it will be something similar to:

myParsedObject = XMLParser(L5X_Text)
Here "myParsedObject" is becoming a structure that you later reference in your script code, "L5X_Text" is the variable that holds the text of your L5X file. And XMLParser() is the parser itself.
You can search for "online XML parser" to better understand how the structure of parsed object looks. Here is my result with one of them:
Let's assume that we named our object "myParsedObject." To get to the controller tags, we may simply type "myParsedObject.RSLogix5000Content.Controller.Tags". This by itself is an object. It contains an array of tags. The size of the array equals the number of tags that we have. Let's take one of the tags:

someTag = myParsedObject.RSLogix5000Content.Controller.Tags[2]
Now we can reference the structure of the tag directly through the variable "someTag". Let's take a look at where is our HH alarm message in this structure:
As you can see the message text is here:

someTag.Data.AlarmConfig.Messages.Message[0].Text.__cdata
So, we will have to reference this particular path to change the value of the message for the High-High alarm. Well, it is still easier than scanning through the text and finding the correct place, especially when it comes to creating new, non-existing structures.

But there is one huge disadvantage of all XML parsers when it comes to L5X files. Objects that are created after parsing the L5X will most likely lose the order of all parameters and tags. It is absolutely not a problem for software like web browsers that don't care about the order of parameters. But Studio 5000 does and will be spitting error after error if you try to import the project with the incorrect order of sections or tag parameters. So, our only option is to preserve the order of everything and sort it the way Allen Bradley wants. And this is not a trivial task.

Luckily there is a parser that treats L5X natively and even simplifies the process of code creation by eliminating the need to reference the exact structure of the objects.

This parser is part of Kiptr.com – an online engineering platform.

This platform allows to structure the project data, edit and even generate Word and Excel documents online, and create and modify L5X files. Programming is done in JavaScript language. Since the parser is part of Kiptr SDK, all the commands start with referencing this SDK – "kSDKv1". Using the same example from above, here is the code to get the High-High alarm message:
Luckily there is a parser that treats L5X natively and even simplifies the process of code creation by eliminating the need to reference the exact structure of the objects.
This parser is part of Kiptr.com – an online engineering platform.

This platform allows to structure the project data, edit and even generate Word and Excel documents online, and create and modify L5X files. Programming is done in JavaScript language. Since the parser is part of Kiptr SDK, all the commands start with referencing this SDK – "kSDKv1". Using the same example from above, here is the code to get the High-High alarm message:

const myParsedObject = new kSDKv1.l5xProject(L5X_Text);
const someTag = myParsedObject.getTag(“AlarmA”);
let almHHText = someTag. getALMMessages()[0].Text;

  • The first line is converting the text into an object with the type "l5xProject".
  • The second line is creating a new object with the type "l5xTag" by extracting all the information about a tag with the name "AlarmA".
  • The third line is extracting the message text. Variable almHHText will now hold the text.
In this example we assume that we know the name of the tag ("AlarmA"). But we can also extract all the tags from the project and read their type. It can be done like this:

const allTags = myParsedObject.getTags();
let tagType = allTags[0]. getDataType();

Here, the last line is reading the type of the first tag in the array.
We can loop through all of them and do what is needed for only the types we want.

As you can see, all the heavy lifting has already been done – you can extract data, insert new members, delete existing ones without thinking about the data structure.