Skip to content

Data Model API

Tier: Stable Core

Structured tags, IEC data types, and memory block primitives.

pyrung.Field dataclass

Field metadata used by udt and named_array declarations.

pyrung.auto

auto(*, start: int = 1, step: int = 1) -> Any

Create a per-instance numeric default sequence descriptor.

pyrung.udt

udt(
    *, count: int = 1, numbered: bool = False
) -> Callable[[type[Any]], _StructRuntime]

Decorator that builds a mixed-type structured runtime from annotations.

pyrung.named_array

named_array(
    base_type: object, *, count: int = 1, stride: int = 1
) -> Callable[[type[Any]], _NamedArrayRuntime]

Decorator that builds a single-type, instance-interleaved structured runtime.

pyrung.TagType

Bases: Enum

Data types for tags (IEC 61131-3 naming).

pyrung.Bool dataclass

Bases: _TagTypeBase

Create a BOOL (1-bit boolean) tag.

Not retentive by default — resets to False on power cycle.

Example
Button = Bool("Button")
Light  = Bool("Light", retentive=True)

pyrung.Int dataclass

Bases: _TagTypeBase

Create an INT (16-bit signed integer, −32768 to 32767) tag.

Retentive by default — survives power cycles.

Example
Step     = Int("Step")
preset = Int("preset", retentive=False)

pyrung.Dint dataclass

Bases: _TagTypeBase

Create a DINT (32-bit signed integer, ±2 147 483 647) tag.

Retentive by default. Use for counters or values that exceed INT range.

Example
TotalCount = Dint("TotalCount")

pyrung.Real dataclass

Bases: _TagTypeBase

Create a REAL (32-bit IEEE 754 float) tag.

Retentive by default. Use for analog presets and process values.

Example
Temperature = Real("Temperature")
FlowRate    = Real("FlowRate", retentive=False)

pyrung.Char dataclass

Bases: _TagTypeBase

Create a CHAR (8-bit ASCII character) tag.

Retentive by default. Use for single-character text values. For multi-character strings, use a Block of CHAR tags. In the Click dialect, Txt is an alias for Char.

Example
ModeChar = Char("ModeChar")

pyrung.Word dataclass

Bases: _TagTypeBase

Create a WORD (16-bit unsigned integer, 0x0000–0xFFFF) tag.

Retentive by default. Use for bit-packed status registers or hex values. In the Click dialect, Hex is an alias for Word.

Example
StatusWord = Word("StatusWord")

pyrung.Block dataclass

Factory for creating Tags from a typed memory region.

Block defines a named, 1-indexed memory region where every address shares the same TagType. Indexing a Block returns a cached LiveTag. The block holds no runtime values — all values live in SystemState.tags.

Address bounds are inclusive on both ends: Block("DS", INT, 1, 100) defines addresses 1–100 (100 tags). Indexing outside this range raises IndexError. Slice syntax (block[1:10]) is rejected — use .select(start, end) instead.

For sparse blocks (e.g. Click X/Y banks with non-contiguous valid addresses), pass valid_ranges to restrict which addresses within [start, end] are legal.

Parameters:

Name Type Description Default
name str

Block prefix used to generate tag names (e.g. "DS""DS1", "DS2" …).

required
type TagType

TagType shared by all tags in this block.

required
start int

Inclusive lower bound address (must be ≥ 0).

required
end int

Inclusive upper bound address (must be ≥ start).

required
retentive bool

Whether tags in this block survive power cycles. Default False.

False
valid_ranges tuple[tuple[int, int], ...] | None

Optional tuple of (lo, hi) inclusive segments that constrain which addresses within [start, end] are accessible. Addresses outside all segments raise IndexError.

None
address_formatter Callable[[str, int], str] | None

Optional callable (block_name, addr) → str that overrides default tag name generation. Used by dialects for canonical display names like "X001".

None
Example
DS = Block("DS", TagType.INT, 1, 100)
DS[1]          # → LiveTag("DS1", TagType.INT)
DS[101]        # → IndexError

# Range for block operations:
DS.select(1, 10)   # → BlockRange, tags DS1..DS10

# Indirect (pointer) addressing:
idx = Int("Idx")
DS[idx]        # → IndirectRef, resolved at scan time
DS[idx + 1]    # → IndirectExprRef

rename_slot

rename_slot(addr: int, name: str) -> None

Set the first-class logical name for one slot before materialization.

clear_slot_name

clear_slot_name(addr: int) -> None

Clear a first-class slot name override for one address.

configure_slot

configure_slot(
    addr: int,
    *,
    retentive: bool | None = None,
    default: object = UNSET,
) -> None

Set per-slot runtime policy before this slot is materialized.

configure_range

configure_range(
    start: int,
    end: int,
    *,
    retentive: bool | None = None,
    default: object = UNSET,
) -> None

Set per-slot policy for all valid addresses in the inclusive window.

clear_slot_config

clear_slot_config(addr: int) -> None

Clear per-slot policy overrides for one address.

clear_range_config

clear_range_config(start: int, end: int) -> None

Clear per-slot policy overrides for all valid addresses in a window.

slot_config

slot_config(addr: int) -> SlotConfig

Return the effective runtime slot policy without materializing a Tag.

select

select(start: int, end: int) -> BlockRange
select(
    start: Tag | Expression, end: int | Tag | Expression
) -> IndirectBlockRange
select(
    start: int, end: Tag | Expression
) -> IndirectBlockRange
select(
    start: int | Tag | Any, end: int | Tag | Any
) -> BlockRange | IndirectBlockRange

Select an inclusive range of addresses for block operations.

Both start and end are inclusive: DS.select(1, 10) yields 10 tags (1, 2, … 10). This mirrors the block constructor convention and avoids the off-by-one confusion of Python's half-open slices.

For sparse blocks (valid_ranges set), returns only the valid addresses within the window — gaps are silently skipped.

Parameters:

Name Type Description Default
start int | Tag | Any

Start address. int for a static range; Tag or Expression for a dynamically-resolved range.

required
end int | Tag | Any

End address. int for a static range; Tag or Expression for a dynamically-resolved range.

required

Returns:

Type Description
BlockRange | IndirectBlockRange

BlockRange when both arguments are int (resolved at

BlockRange | IndirectBlockRange

definition time). IndirectBlockRange when either argument is a

BlockRange | IndirectBlockRange

Tag or Expression (resolved each scan at execution time).

Raises:

Type Description
ValueError

If start > end.

IndexError

If either bound is outside the block's [start, end].

Example
# Static range
DS.select(1, 100)              # BlockRange, DS1..DS100

# Sparse window (Click X bank)
x.select(1, 21)               # valid tags only: X001..X016, X021

# Dynamic range (resolved each scan)
DS.select(start_tag, end_tag)  # IndirectBlockRange

# Use with bulk instructions:
fill(0, DS.select(1, 10))
blockcopy(DS.select(1, 10), DD.select(1, 10))
search(">=", 100, DS.select(1, 100), result=Found, found=FoundFlag)

map_to

map_to(target: BlockRange) -> MappingEntry

Create a logical-to-hardware mapping entry.

pyrung.InputBlock dataclass

Bases: Block

Factory for creating InputTag instances from a physical input memory region.

InputBlock is identical to Block except:

  • Indexing returns LiveInputTag (not LiveTag), so elements have .immediate.
  • Always non-retentive — physical inputs do not survive power cycles.

Use InputBlock when the tags represent real hardware inputs (sensors, switches, etc.). In simulation, values are supplied via runner.patch() or runner.add_force() during the Read Inputs scan phase.

Example
X = InputBlock("X", TagType.BOOL, 1, 16)
X[1]           # → LiveInputTag("X1", BOOL)
X[1].immediate # → ImmediateRef — bypass image table
X.select(1, 8) # → BlockRange for bulk operations

pyrung.OutputBlock dataclass

Bases: Block

Factory for creating OutputTag instances from a physical output memory region.

OutputBlock is identical to Block except:

  • Indexing returns LiveOutputTag (not LiveTag), so elements have .immediate.
  • Always non-retentive — physical outputs do not survive power cycles.

Writes to OutputTag elements are immediately visible to subsequent rungs within the same scan (standard PLC behavior). The actual hardware write happens at the Write Outputs scan phase (phase 6).

Example
Y = OutputBlock("Y", TagType.BOOL, 1, 16)
Y[1]           # → LiveOutputTag("Y1", BOOL)
Y[1].immediate # → ImmediateRef — bypass image table
Y.select(1, 8) # → BlockRange for bulk operations

pyrung.SlotConfig dataclass

Effective runtime policy for one block slot.