Project file format (.forge)

Overview

A ForgeIEC project file uses the .forge extension (the legacy .forgeiec extension is still accepted on load) and is a regular PLCopen TC6 XML document with a few ForgeIEC-specific extensions attached through the standard <addData> mechanism. The file is UTF-8, human-readable, diff-able in any XML editor, and Git-friendly.

.forge file
  +-- <project>            ← PLCopen root element
        +-- <fileHeader>   tool meta (name, date)
        +-- <contentHeader> author, project name
        +-- <types>        data types + POUs (PROGRAM/FB/FUNCTION + GVLs)
        +-- <instances>    resource / task configuration
        +-- <addData>      ForgeIEC extensions (bus, pool, ...)

Root element

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.plcopen.org/xml/tc6_0201">
  <fileHeader companyName="ForgeIEC"
              creationDateTime="2026-04-30T12:00:00"
              productName="ForgeIEC"
              productVersion="0.1.0"/>
  <contentHeader author="Joerg Bernau" name="Ackersteuerung"/>
  ...
</project>
AttributeMeaning
xmlnsPLCopen TC6 namespace (fixed)
companyNameproducer tool — always "ForgeIEC" for ForgeIEC
creationDateTimeISO-8601 timestamp of first creation
productVersioneditor version that wrote the file
authorfree-form author label
nameproject name (UI label, not a filename)

<types> — library + POUs

Three sections:

<dataTypes>

User-defined data types (STRUCT, enumerations, aliases).

<dataTypes>
  <dataType name="ST_KcCurve">
    <baseType>
      <struct>
        <variable name="days_initial"><type><INT/></type></variable>
        <variable name="days_growing"><type><INT/></type></variable>
      </struct>
    </baseType>
  </dataType>
</dataTypes>

<pous>

All program organization units:

pouTypeMeaning
programPROGRAM (top-level code)
functionBlockFUNCTION_BLOCK (instantiable, stateful)
functionFUNCTION (stateless, has returnType)

Every POU has an <interface> (variable declarations grouped into <inputVars>, <outputVars>, <inOutVars>, <localVars>, <externalVars>, <tempVars>) and a <body> carrying ST source code in an <ST> element.

<pou name="PLC_PRG" pouType="program">
  <interface>
    <localVars>
      <variable name="counter"><type><INT/></type></variable>
    </localVars>
  </interface>
  <body>
    <ST>
      <xhtml xmlns="http://www.w3.org/1999/xhtml">counter := counter + 1;</xhtml>
    </ST>
  </body>
</pou>

Special POU types (ForgeIEC extension)

ForgeIEC introduces five list-shaped POUs stored with their own pouType:

pouTypePurpose
globalVarListGVL — programmer-visible variable pool
tempVarListTVL — VAR_TEMP list
persistVarListPVL — VAR_PERSIST list (RETAIN)
anvilVarListAnvilVarList — auto-generated PUBLISH/SUBSCRIBE
hmiVarListHmiVarList — Bellows HMI export

<instances> — resource / task

<instances>
  <configurations>
    <configuration name="config0">
      <resource name="resource0">
        <task name="task0" interval="T#20ms" priority="0"/>
        <pouInstance name="instance0" taskName="task0" typeName="PLC_PRG"/>
      </resource>
    </configuration>
  </configurations>
</instances>

interval sets the cycle time. Multiple tasks are supported; each task runs one or more program instances.

<addData> — ForgeIEC extensions

PLCopen TC6 allows vendor-specific data under <addData>. ForgeIEC uses several namespaces:

NamespaceContent
https://forgeiec.io/v2/bus-configbus segments + devices (detail)
https://forgeiec.io/v2/address-pooladdress pool: located variables, bus bindings, tags
https://forgeiec.io/v2/monitoringUI state: which variables are live-monitored

Typical layout:

<addData>
  <data name="https://forgeiec.io/v2/bus-config" handleUnknown="discard">
    <fi:busConfig xmlns:fi="https://forgeiec.io/v2">
      <fi:segment ...>
        <fi:device ...>
          <fi:module .../>
        </fi:device>
      </fi:segment>
    </fi:busConfig>
  </data>
</addData>

handleUnknown="discard" is the PLCopen directive: a tool that does not understand this block may discard it — the file is still valid PLCopen XML.

Address pool

The address pool is the single source of truth for the variables of a plant. Each entry has the IEC address as primary key (%IX0.0, %QW3, …) — names, tags and bus bindings are descriptors that may change without breaking identity.

<addData>
  <data name="https://forgeiec.io/v2/address-pool" handleUnknown="discard">
    <fi:pool xmlns:fi="https://forgeiec.io/v2">
      <fi:variable address="%IX0.0"
                   name="T_1"
                   anvilGroup="Pfirsich"
                   busDirection="in"
                   deviceId="0e5d5537-e328-44e6-8214-78d529b18ebd"
                   modbusAddress="0"
                   moduleSlot="0"/>
    </fi:pool>
  </data>
</addData>
AttributeRequiredMeaning
addressyesIEC address — primary key in the pool
namenoprogrammer-visible name (T_1, Motor_Run)
anvilGroupnoAnvil IPC group (hardware channel)
gvlNamespacenoGVL grouping (editor side)
hmiGroupnoBellows HMI group (HMI channel)
busDirectionnoin / out — polling direction
deviceIdnoUUID of the bus device this variable is bound to
modbusAddressnoModbus register offset relative to the slave
moduleSlotnoslot inside the device

A variable can carry several tags simultaneously (e.g. both anvilGroup="Pfirsich" and hmiGroup="Pfirsich"). For the ST compiler to accept the Bellows form Bellows.Pfirsich.T_1, the HMI export for that group must additionally be enabled — otherwise the hmiGroup tag stays a pure descriptor without effect on code generation.

ST language additions (body content)

The <ST> body carries IEC 61131-3 Structured Text wrapped in xhtml. ForgeIEC adds two convenience features on top:

Bit access on ANY_BIT types

var.<bit> extracts/sets a single bit, directly on BYTE/WORD/ DWORD/LWORD variables:

bButtons.0 := IN_Buttons.0 OR Bellows.Pfirsich.T_1;
OUT_Valves.3 := jk[3].Q;

The compiler lowers it to clean bit masking.

3-level qualified variables

<Category>.<Group>.<Variable> reaches pool entries without declaring GVLs explicitly:

Category prefixSource
Anvil.X.Ypool variable with anvilGroup="X"
Bellows.X.Ypool variable with hmiGroup="X"
GVL.X.Ypool variable with gvlNamespace="X"
HMI.X.Ysynonym for Bellows.X.Y
POOL.X.Yany tag

Anvil.X.Y and Bellows.X.Y may resolve to two distinct pool entries — the compiler emits separate C symbols whenever the entries have different IEC addresses.

Migration / compatibility

  • File extension: .forge is canonical. .forgeiec (legacy) is still accepted on load; on save the editor writes .forge.
  • The XML format is backward compatible: older ForgeIEC versions ignore unknown <addData> blocks (handleUnknown="discard") and open the file anyway.
  • Standard PLCopen tools open the standard part (<types>, <instances>) correctly; ForgeIEC extensions are missing there but survive a save cycle as long as the foreign tool round-trips <addData> blocks unchanged.