A modern TypeScript library for building and parsing Allen Bradley .L5X files to automate PLC code generation.
This major release brings full TypeScript support, modern ES2020 syntax, improved type safety, and a cleaner API.
- Node.js 18+ or 20+
- TypeScript 5.0+ (for development)
npm install l5x-jsimport { Document, Tag, Member } from 'l5x-js';
// Create a new L5X document
const doc = new Document();
// Create tags
const boolTag = new Tag({
name: 'StartButton',
datatype: 'BOOL',
description: 'Start button input'
});
const intTag = new Tag({
name: 'Counter',
datatype: 'DINT',
description: 'Production counter'
});
// Add tags to document
doc.addTag(boolTag);
doc.addTag(intTag);
// Generate L5X XML
console.log(doc.toString());This example demonstrates how to read tag data from an Excel file and automatically generate PLC ladder logic rungs. This is particularly useful for converting alarm lists, I/O schedules, or tag databases into PLC code.
import { Document, Program, Routine, Rung } from 'l5x-js';
import * as xlsx from 'xlsx';
// Define configuration constants
const WORKSHEET_NAME = 'IO_Schedule';
const ROUTINE_NAME = 'AutoGeneratedLogic';
// Read Excel file containing tag definitions
const workbook = xlsx.readFile('./Tags.xlsm');
const worksheet = workbook.Sheets[WORKSHEET_NAME];
const tagData = xlsx.utils.sheet_to_json(worksheet);
// Initialize L5X document structure
const doc = new Document();
const program = new Program({ name: 'MainProgram' });
const routine = new Routine({ name: ROUTINE_NAME });
// Build the hierarchy: Document -> Program -> Routine
program.addRoutine(routine);
doc.addProgram(program);
// Generate ladder logic rungs from Excel data
for (const row of tagData) {
const tagName = row['Tag'] as string;
const isInput = row['Input/Output'] === 'Input';
// Create ladder logic based on I/O type:
// Input: |---||---------(tagName)---| (XIC -> OTE)
// Output: |----|tagName|-------()----| (XIC -> OTE)
const rungContent = isInput
? `XIC()OTE(${tagName});` // Examine if closed, output energize
: `XIC(${tagName})OTE();`; // Examine if closed, output energize
// Create and add rung to routine
const rung = new Rung({ logic: rungContent });
routine.addRung(rung);
}
// Set import target for Rockwell software
doc.setTarget(routine);
// Export L5X file ready for Studio 5000 import
doc.export(`./${WORKSHEET_NAME}_Generated.L5X`);
console.log(`Generated ${tagData.length} rungs in ${ROUTINE_NAME}`);- Reads Excel Data: Imports tag information from an Excel spreadsheet containing I/O definitions
- Creates PLC Structure: Builds a complete L5X document with Program and Routine containers
- Generates Ladder Logic: Automatically creates ladder rungs based on whether tags are inputs or outputs
- Exports L5X File: Produces a file ready for import into Rockwell Automation Studio 5000
| Tag | Input/Output | Description |
|---|---|---|
| StartButton | Input | Main start button |
| StopButton | Input | Emergency stop |
| RunningLight | Output | Status indicator |
This automation can save hours of manual PLC programming when dealing with large I/O lists or repetitive logic patterns.
The main class for creating and manipulating L5X files.
new Document(filepath?: string)Parameters:
filepath(optional): Path to existing L5X file to load
Examples:
// Create new document
const doc = new Document();
// Load existing document
const doc = new Document('./existing.L5X');Adds a tag to the controller.
const tag = new Tag({ name: 'MyTag', datatype: 'BOOL' });
doc.addTag(tag);Returns all tags in the document.
const tags = doc.getTags();
tags.forEach(tag => console.log(tag.name));Generates the L5X XML string.
const xmlContent = doc.toString();Finds the first element of specified type.
const controller = doc.findOne('Controller');
const specificTag = doc.findOne('Tag', { Name: 'MyTag' });Finds all elements of specified type.
const allTags = doc.findAll('Tag');
const boolTags = doc.findAll('Tag', { DataType: 'BOOL' });Represents PLC tags with type safety and validation.
new Tag(options: TagOptions)TagOptions Interface:
interface TagOptions {
name: string; // Tag name
datatype: string; // PLC data type (BOOL, DINT, REAL, etc.)
description?: string; // Optional description
alias?: string; // Optional alias reference
safety?: boolean; // Safety tag flag
dimension?: number; // Array dimension
}Examples:
// Basic tag
const tag = new Tag({
name: 'Motor1_Run',
datatype: 'BOOL'
});
// Tag with all options
const tag = new Tag({
name: 'Temperature',
datatype: 'REAL',
description: 'Reactor temperature in °C',
safety: false,
dimension: 10
});
// Alias tag
const alias = new Tag({
name: 'START_BTN',
datatype: 'BOOL',
alias: 'Local:1:I.Data.0'
});Gets the tag name.
Gets the tag data type.
Gets the tag description.
Type guard to check if object is a Tag instance.
if (Tag.isTag(someObject)) {
console.log(someObject.name); // TypeScript knows it's a Tag
}Represents PLC tag members for structured data types.
new Member(options: MemberOptions)MemberOptions Interface:
interface MemberOptions {
name: string; // Member name
datatype: string; // PLC data type (BOOL, DINT, REAL, etc.)
description?: string; // Optional description
hidden?: boolean; // Hidden member flag
target?: string; // Target reference for BOOL/BIT types
bitNumber?: number; // Bit number for BOOL/BIT types
}Examples:
// Basic member
const member = new Member({
name: 'Status',
datatype: 'DINT'
});
// Boolean member with target
const boolMember = new Member({
name: 'Running',
datatype: 'BOOL',
target: 'StatusWord',
bitNumber: 0
});
// Member with description
const member = new Member({
name: 'Temperature',
datatype: 'REAL',
description: 'Current temperature value',
hidden: false
});Type guard to check if object is a Member instance.
if (Member.isMember(someObject)) {
console.log(someObject.name); // TypeScript knows it's a Member
}Base class for all L5X elements with XML manipulation capabilities.
findOne(type: string, attributes?: object, ignore?: string[], searchTree?: XmlElement): XmlElement | null
Finds first matching element.
Parameters:
type: Element type to search forattributes: Optional attribute filtersignore: Array of element names to ignoresearchTree: Optional subtree to search in
findAll(type: string, attributes?: object, ignore?: string[], searchTree?: XmlElement): XmlElement[]
Finds all matching elements.
Converts element to XML string.
import { Util } from 'l5x-js';
// Generate hash
const id = Util.hash('some-string');
// Validation helpers
Util.validateString(value, 'parameterName');
Util.validateObject(value, 'parameterName');The library includes comprehensive TypeScript definitions:
interface XmlElement {
type?: string;
name?: string;
attributes?: Record<string, string | number | boolean>;
elements?: XmlElement[];
text?: string;
}
interface XmlDocument {
declaration?: {
attributes: {
version: string;
encoding: string;
standalone?: string;
};
};
elements: XmlElement[];
}| Type | Description | Example |
|---|---|---|
BOOL |
Boolean | true/false |
SINT |
Short Integer | -128 to 127 |
INT |
Integer | -32,768 to 32,767 |
DINT |
Double Integer | -2,147,483,648 to 2,147,483,647 |
REAL |
Floating Point | 3.14159 |
STRING |
Text String | "Hello World" |
The library provides descriptive error messages with type information:
try {
const tag = new Tag({
name: 123, // Invalid type
datatype: 'BOOL'
});
} catch (error) {
console.log(error.message); // "Tag name expected type <string> but got <number>"
}-
Constructor Changes: Tag constructor now uses options object
// v1.x new Tag('MyTag', 'BOOL', 'Description'); // v2.x new Tag({ name: 'MyTag', datatype: 'BOOL', description: 'Description' });
-
Import Changes: Now uses ES6 imports
// v1.x const { Document, Tag } = require('l5x-js'); // v2.x import { Document, Tag, Member } from 'l5x-js';
-
TypeScript: Full type safety - invalid usage caught at compile time
# Install dependencies
npm install
# Run tests
npm test
# Build for production
npm run build
# Lint code
npm run lint- TypeScript - Language & Type Safety
- Node.js - Runtime Environment
- xml-js - XML Processing
- Jest - Testing Framework
Want to contribute? Check out our Contributing Guide!
This project is licensed under the MIT License - see the LICENSE file for details.
