l5x-js
v2.0.1
Published
A tool to help with parsing and generating Allen Bradley L5X files.
Maintainers
Readme
L5XJS
A modern TypeScript library for building and parsing Allen Bradley .L5X files to automate PLC code generation.
🚀 Version 2.0 - Now with TypeScript!
This major release brings full TypeScript support, modern ES2020 syntax, improved type safety, and a cleaner API.
Prerequisites
- Node.js 18+ or 20+
- TypeScript 5.0+ (for development)
Installation
npm install l5x-jsQuick Start
import { 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());Advanced Example: Excel to L5X Automation
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}`);What This Example Does:
- 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
Excel File Format Expected:
| 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.
API Documentation
Document Class
The main class for creating and manipulating L5X files.
Constructor
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');Methods
addTag(tag: Tag): void
Adds a tag to the controller.
const tag = new Tag({ name: 'MyTag', datatype: 'BOOL' });
doc.addTag(tag);getTags(): Tag[]
Returns all tags in the document.
const tags = doc.getTags();
tags.forEach(tag => console.log(tag.name));toString(): string
Generates the L5X XML string.
const xmlContent = doc.toString();findOne(type: string, attributes?: object): XmlElement | null
Finds the first element of specified type.
const controller = doc.findOne('Controller');
const specificTag = doc.findOne('Tag', { Name: 'MyTag' });findAll(type: string, attributes?: object): XmlElement[]
Finds all elements of specified type.
const allTags = doc.findAll('Tag');
const boolTags = doc.findAll('Tag', { DataType: 'BOOL' });Tag Class
Represents PLC tags with type safety and validation.
Constructor
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'
});Properties
name: string (readonly)
Gets the tag name.
datatype: string (readonly)
Gets the tag data type.
description: string | undefined (readonly)
Gets the tag description.
Static Methods
Tag.isTag(obj: unknown): boolean
Type guard to check if object is a Tag instance.
if (Tag.isTag(someObject)) {
console.log(someObject.name); // TypeScript knows it's a Tag
}Member Class
Represents PLC tag members for structured data types.
Constructor
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
});Static Methods
Member.isMember(obj: unknown): boolean
Type guard to check if object is a Member instance.
if (Member.isMember(someObject)) {
console.log(someObject.name); // TypeScript knows it's a Member
}Element Class
Base class for all L5X elements with XML manipulation capabilities.
Methods
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.
toString(): string
Converts element to XML string.
Utility Functions
import { Util } from 'l5x-js';
// Generate hash
const id = Util.hash('some-string');
// Validation helpers
Util.validateString(value, 'parameterName');
Util.validateObject(value, 'parameterName');Type Definitions
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[];
}Common PLC Data Types
| 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" |
Error Handling
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>"
}Migration from v1.x
Breaking Changes
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
Development
# Install dependencies
npm install
# Run tests
npm test
# Build for production
npm run build
# Lint code
npm run lintBuilt With
- TypeScript - Language & Type Safety
- Node.js - Runtime Environment
- xml-js - XML Processing
- Jest - Testing Framework
Contributors
Want to contribute? Check out our Contributing Guide!
License
This project is licensed under the MIT License - see the LICENSE file for details.
