Skip to content

PLCProject XML Schema Reference - Version 3.0

Document Version: 1.0 Schema Version: 3.0 Last Updated: 2026-01-25 Status: Active

Overview

The PLCProject XML format is the native file format for PiPLC ladder logic projects. Files use the .plcproj extension and contain complete project data including metadata, symbol tables, and ladder logic programs.

Version 3.0 introduces support for parallel branches in ladder logic, enabling OR logic representation directly in the data model.

What's New in 3.0

  • Branch Element: Represents parallel paths in ladder logic (OR logic)
  • Path Element: Represents a single path within a branch (AND logic within path)
  • Nested Branches: Full support for arbitrarily nested branch structures
  • Backward Compatibility: Loader accepts both v2.0 and v3.0 files

File Format Basics

  • Encoding: UTF-8 (with XML declaration)
  • Extension: .plcproj
  • MIME Type: application/x-plcproject+xml
  • XML Version: 1.0

Schema Version History

Version Date Changes
3.2 2026-01-29 Added optional address attribute to Decorator elements for T:/C: memory integration
3.0 2026-01-25 Added Branch and Path elements for parallel logic
2.0 2026-01-25 Initial documented schema with streaming XML support
1.0 (Legacy) Reserved for future backwards compatibility

Root Element

<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.0">
  <!-- Child elements -->
</PLCProject>

Attributes

Attribute Type Required Description
version string Yes Schema version (e.g., "3.0"). Required for compatibility checking.

Child Elements (in order)

  1. Metadata - Project metadata (required)
  2. SymbolTable - Variable definitions (required, may be empty)
  3. Programs - Ladder logic programs (required)

Metadata Element

Contains project identification and descriptive information.

<Metadata>
  <Name>Motor Control</Name>
  <Description>Optional description of the project</Description>
</Metadata>

Child Elements

Element Type Required Description
Name string No Project name. Empty string if not specified.
Description string No Project description. Omitted if empty.

Notes

  • Special XML characters (<, >, &, ", ') are automatically escaped
  • Unicode content is fully supported

SymbolTable Element

Contains variable/symbol definitions used in the ladder logic.

<SymbolTable>
  <Symbol name="StartButton" type="BOOL" address="I:0/0" description="Main start pushbutton" />
  <Symbol name="MotorRunning" type="BOOL" address="O:0/0" />
  <Symbol name="CycleCount" type="INT" address="N:10" description="Production counter" />
</SymbolTable>

Symbol Element Attributes

Attribute Type Required Description
name string Yes Unique variable name. Must follow naming rules (alphanumeric + underscore, no leading digits).
address string Yes PLC memory address. See Address Formats below.
type string No Data type: BOOL, INT, DINT, REAL, TIMER, COUNTER. Defaults to BOOL.
description string No Human-readable description. Omitted if empty.

Validation Rules

  • Variable names must be unique within the symbol table
  • Duplicate names will cause a load error
  • Address must be valid per the Address Formats specification

Programs Element

Contains one or more ladder logic programs.

<Programs>
  <Program name="MainProgram" type="Main">
    <Rungs>
      <!-- Rung elements -->
    </Rungs>
  </Program>
</Programs>

Program Element Attributes

Attribute Type Required Description
name string No Program name (informational, may differ from Metadata name)
type string No Program type: Main, Subroutine, Interrupt. Default: Main

Notes

  • Currently only single-program projects are supported
  • The authoritative program name comes from Metadata/Name, not the Program element's name attribute

Rungs Element

Contains the ladder rungs within a program.

<Rungs>
  <Rung id="0" comment="Start/stop circuit with seal-in">
    <!-- Instruction and Branch elements -->
  </Rung>
  <Rung id="1">
    <!-- Instruction and Branch elements -->
  </Rung>
</Rungs>

Rung Element Attributes

Attribute Type Required Description
id integer No Rung identifier (0-based). Used for reference only.
comment string No Rung comment/description. Omitted if empty.

Rung Child Elements

A rung can contain any combination of: - Instruction - Individual ladder instruction - Branch - Parallel branch structure (NEW in v3.0)


Instruction Element

Represents a single ladder logic instruction.

<Instruction type="XIC" address="I:0/0" column="0" />
<Instruction type="OTE" address="O:0/0" column="10" />

Attributes

Attribute Type Required Description
type string Yes Instruction type. See Instruction Types below.
address string Yes Target memory address. See Address Formats below.
column integer No Visual column position in ladder editor. Default: 0
preset integer No Preset value for timers (ms) and counters (count). Used by TON, TOF, RTO, CTU, CTD.
sourceA string No Source A address. Used by math, compare, and bitwise instructions.
sourceB string No Source B address. Used by binary math, compare, and bitwise instructions.
source string No Source address. Used by SCL (scale) instruction.
dest string No Destination address. Used by math, bitwise, and scale instructions.
ctrl string No Control address (B:x word). Used by BSL and BSR for IN/OV bits.
inMin integer No Input range minimum. Used by SCL.
inMax integer No Input range maximum. Used by SCL.
outMin integer No Output range minimum. Used by SCL.
outMax integer No Output range maximum. Used by SCL.

Instruction Types

Contact Instructions (Input)

Type Name Description
XIC Examine If Closed TRUE when addressed bit is 1
XIO Examine If Open TRUE when addressed bit is 0

Coil Instructions (Output)

Type Name Description
OTE Output Energize Sets bit to match rung continuity
OTL Output Latch Sets bit TRUE on rung TRUE, retains on FALSE
OTU Output Unlatch Sets bit FALSE on rung TRUE, retains on FALSE

Additional Instruction Types

Type Name Description
TON Timer On-Delay Output after input TRUE for preset time
TOF Timer Off-Delay Output remains TRUE after input goes FALSE
RTO Retentive Timer Like TON but retains accumulated time
CTU Count Up Increments on rising edge
CTD Count Down Decrements on rising edge
RES Reset Resets timer/counter at target address
ADD, SUB, MUL, DIV, MOD, NEG, ABS Math Arithmetic operations
SCL Scale Linear scaling
EQU, NEQ, GRT, GEQ, LES, LEQ Compare Comparison operations
BAND, BOR, BXOR, BNOT Bitwise Bitwise logic operations
N2B Integer to Bit Dest = (1 << A)
BSL Bit Shift Left Dest = (A << B) | IN
BSR Bit Shift Right Dest = (A >> B) | (IN << 31)

Decorator Child Elements

Contact instructions (XIC/XIO) may contain a <Decorators> child element with one or more <Decorator> entries:

<Instruction type="XIC" address="B:0/0">
  <Decorators>
    <Decorator type="TON" preset="2000" address="T:0"/>
    <Decorator type="OSR"/>
  </Decorators>
</Instruction>

Decorator Attributes

Attribute Type Required Description
type string Yes Decorator type: TON, TOF, OSR, OSF, Debounce, Counter
preset integer No Preset value (ms for timers, count for counters)
debounceTime integer No Debounce time in ms (Debounce type only)
address string No Assigned T:/C: memory address (auto-created if absent)

The address attribute enables timer/counter decorators to sync state with PLC memory, allowing RES instructions to reset decorator state. Addresses are auto-assigned on project load if not present (migration from pre-3.2 projects).


Branch Element (NEW in v3.0)

Represents parallel paths in ladder logic. Multiple paths are evaluated with OR logic - if any path has power, the branch has power.

<Branch>
  <Path>
    <!-- Elements in series (AND logic) -->
    <Instruction type="XIC" address="I:0/0" column="1" />
  </Path>
  <Path>
    <!-- Alternative path (OR logic with first path) -->
    <Instruction type="XIC" address="I:0/1" column="1" />
  </Path>
</Branch>

Child Elements

Element Required Description
Path At least one A single path within the branch. Multiple paths represent OR logic.

Execution Semantics

  1. If input power is FALSE, branch outputs FALSE (no paths evaluated)
  2. If branch is empty (no paths), outputs FALSE
  3. Each path is evaluated with the same input power
  4. If ANY path outputs TRUE, branch outputs TRUE (OR logic)
  5. All paths are evaluated regardless of earlier path results (for output execution)

Visual Representation

     +--[ I:0/0 ]--+
--+--|             |--+--( O:0/0 )--
     +--[ I:0/1 ]--+

Serialized as:

<Branch>
  <Path>
    <Instruction type="XIC" address="I:0/0" column="1" />
  </Path>
  <Path>
    <Instruction type="XIC" address="I:0/1" column="1" />
  </Path>
</Branch>
<Instruction type="OTE" address="O:0/0" column="10" />

Path Element (NEW in v3.0)

Represents a single path within a branch. Elements within a path are evaluated in series (AND logic).

<Path>
  <Instruction type="XIC" address="I:0/0" column="1" />
  <Instruction type="XIC" address="I:0/1" column="2" />
  <Instruction type="OTE" address="O:0/0" column="10" />
</Path>

Child Elements

A path can contain any combination of: - Instruction - Individual ladder instruction - Branch - Nested parallel branch (enables complex logic)

Execution Semantics

  1. If path is empty, passes input power unchanged (acts as wire)
  2. Elements are evaluated left-to-right in series (AND logic)
  3. Contact instructions affect power flow
  4. Output instructions are executed with current power but don't affect power flow
  5. Nested branches are evaluated recursively

Empty Path Behavior

An empty path acts as a direct wire, passing power through unchanged:

<Branch>
  <Path />  <!-- Always passes power -->
  <Path>
    <Instruction type="XIC" address="I:0/0" column="1" />
  </Path>
</Branch>

Nested Branches

Branches can be nested to arbitrary depth to represent complex logic.

Example: Nested OR within OR

     +--[ I:0/0 ]--+
--+--|             |--+--( O:0/0 )--
     |  +--[ I:0/1 ]--+  |
     +--|             |--+
        +--[ I:0/2 ]--+

Logic: (I:0/0) OR ((I:0/1) OR (I:0/2))

<Branch>
  <Path>
    <Instruction type="XIC" address="I:0/0" column="1" />
  </Path>
  <Path>
    <Branch>
      <Path>
        <Instruction type="XIC" address="I:0/1" column="1" />
      </Path>
      <Path>
        <Instruction type="XIC" address="I:0/2" column="1" />
      </Path>
    </Branch>
  </Path>
</Branch>
<Instruction type="OTE" address="O:0/0" column="10" />

Example: Series AND within OR paths

     +--[ I:0/0 ]--[ I:0/1 ]--+
--+--|                        |--+--( O:0/0 )--
     +--[ I:0/2 ]-------------+

Logic: ((I:0/0) AND (I:0/1)) OR (I:0/2)

<Branch>
  <Path>
    <Instruction type="XIC" address="I:0/0" column="1" />
    <Instruction type="XIC" address="I:0/1" column="2" />
  </Path>
  <Path>
    <Instruction type="XIC" address="I:0/2" column="1" />
  </Path>
</Branch>
<Instruction type="OTE" address="O:0/0" column="10" />

Address Formats

PiPLC uses Allen-Bradley/Rockwell-style PLC addressing.

Bit Addresses

Format: R:W/B where: - R = Region prefix - W = Word number (0-based) - B = Bit number (0-15)

Region Prefix Description Example
Input I Digital inputs I:0/0, I:1/15
Output O Digital outputs O:0/0, O:0/7
Bit B Internal relays B:3/0, B:10/8

Word Addresses

Format: R:W where: - R = Region prefix - W = Word/element number

Region Prefix Description Example
Integer N 16-bit signed integers N:0, N:100
Timer T Timer structures T:0, T:10
Counter C Counter structures C:0, C:5

Timer/Counter Sub-Elements

Format: R:W.S where S is:

Sub-element Description Type
.DN Done bit BOOL
.TT Timer timing / Counter counting BOOL
.EN Enabled BOOL
.ACC Accumulated value INT
.PRE Preset value INT

Examples: T:0.DN, C:5.ACC, T:2.PRE


Complete Example with Branches

<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.0">
  <Metadata>
    <Name>Motor Control with Dual Start</Name>
    <Description>Motor start/stop circuit with two start buttons (OR logic)</Description>
  </Metadata>
  <SymbolTable>
    <Symbol name="StartPB1" type="BOOL" address="I:0/0" description="Start pushbutton 1" />
    <Symbol name="StartPB2" type="BOOL" address="I:0/1" description="Start pushbutton 2" />
    <Symbol name="StopPB" type="BOOL" address="I:0/2" description="Stop pushbutton (NC)" />
    <Symbol name="MotorOut" type="BOOL" address="O:0/0" description="Motor contactor output" />
    <Symbol name="SealIn" type="BOOL" address="B:0/0" description="Motor seal-in relay" />
  </SymbolTable>
  <Programs>
    <Program name="Motor Control with Dual Start" type="Main">
      <Rungs>
        <Rung id="0" comment="Motor start circuit - (Start1 OR Start2 OR Seal-in) AND NOT Stop">
          <Branch>
            <Path>
              <Instruction type="XIC" address="I:0/0" column="0" />
            </Path>
            <Path>
              <Instruction type="XIC" address="I:0/1" column="0" />
            </Path>
            <Path>
              <Instruction type="XIC" address="B:0/0" column="0" />
            </Path>
          </Branch>
          <Instruction type="XIO" address="I:0/2" column="5" />
          <Instruction type="OTE" address="B:0/0" column="10" />
        </Rung>
        <Rung id="1" comment="Motor output from seal-in">
          <Instruction type="XIC" address="B:0/0" column="0" />
          <Instruction type="OTE" address="O:0/0" column="10" />
        </Rung>
      </Rungs>
    </Program>
  </Programs>
</PLCProject>

Backward Compatibility

Reading v2.0 Files

The v3.0 loader fully supports v2.0 files: - v2.0 files are loaded without modification - All v2.0 elements are interpreted identically - v2.0 files without branches work exactly as before

Version Detection

QString version = xml.attributes().value("version").toString();
// Accept versions 2.x and 3.x

Writing v3.0 Files

  • All new projects are saved as v3.0
  • Rungs without branches produce identical XML to v2.0 (just different version number)
  • Branch elements only appear when parallel logic exists

Error Handling

Load Errors

The loader reports errors with line and column numbers when possible:

Error Description
No PLCProject element found Root element missing or malformed XML
Missing version attribute Required version attribute not present
Symbol missing name attribute Symbol element lacks required name
Symbol missing address attribute Symbol element lacks required address
Invalid address: X:Y/Z Address format not recognized
Duplicate variable name: Name Symbol table contains duplicate names
Unknown instruction type: TYPE Instruction type not recognized
Instruction missing type attribute Instruction element lacks required type

Forward Compatibility

  • Unknown elements are silently skipped
  • Unknown attributes are ignored
  • This allows newer schema versions to be partially read by older software

Implementation Notes

Writing Projects

Use QXmlStreamWriter for generating XML: - Auto-formatting enabled with 2-space indentation - UTF-8 encoding with XML declaration - Empty elements written as <Element /> (self-closing)

Reading Projects

Use QXmlStreamReader for parsing: - Streaming parser for memory efficiency - Element-by-element processing - Error recovery via skipCurrentElement()

API Reference

// Save to file
bool ProjectSerializer::save(const Program *program, const QString &filePath, QString *error);

// Save to device (for testing)
bool ProjectSerializer::save(const Program *program, QIODevice *device, QString *error);

// Load from file
Program* ProjectSerializer::load(const QString &filePath, QString *error);

// Load from device (for testing)
Program* ProjectSerializer::load(QIODevice *device, QString *error);

Data Model Classes

// Abstract base for rung elements
class RungElement : public QObject {
    enum class ElementType { Instruction, Branch };
    virtual ElementType elementType() const = 0;
    virtual RungElement* clone(QObject *parent = nullptr) const = 0;
};

// Parallel paths container (OR logic)
class Branch : public RungElement {
    int pathCount() const;
    RungPath* path(int index) const;
    RungPath* addPath();
    void addPath(RungPath *path);
};

// Series element container (AND logic)
class RungPath : public QObject {
    int elementCount() const;
    RungElement* element(int index) const;
    void addElement(RungElement *element);
};

// Individual instruction (inherits from RungElement)
class Instruction : public RungElement {
    InstructionType type() const;
    Address address() const;
};

See Also