Instruction Set API
Tier: Stable Core
Instruction blocks, conditions, and copy/casting modifiers.
pyrung.out
out(
target: Tag | BlockRange | ImmediateRef,
*,
oneshot: bool = False,
) -> Tag | BlockRange | ImmediateRef
Output coil instruction (OUT).
Sets target to True when rung is true. Resets to False when rung goes false.
Example
with Rung(Button): out(Light) out(Y.select(1, 4))
pyrung.latch
Latch/Set instruction (SET).
Sets target to True. Unlike OUT, does NOT reset when rung goes false. Use reset() to turn off.
Example
with Rung(StartButton): latch(MotorRunning) latch(C.select(1, 8))
pyrung.reset
Reset/Unlatch instruction (RST).
Sets target to its default value (False for bits, 0 for ints).
Example
with Rung(StopButton): reset(MotorRunning) reset(C.select(1, 8))
pyrung.copy
copy(
source: Any,
target: Tag | IndirectRef | IndirectExprRef,
*,
convert: CopyConverter | None = None,
oneshot: bool = False,
) -> Tag | IndirectRef | IndirectExprRef
Copy instruction (CPY/MOV).
Copies source value to target. Pass convert= for text/numeric
conversion (see :mod:pyrung.core.copy_converters).
Example
with Rung(Button): copy(5, StepNumber)
pyrung.run_function
run_function(
fn: Callable[..., dict[str, Any]],
*,
ins: dict[
str, Tag | IndirectRef | IndirectExprRef | Any
]
| None = None,
outs: dict[str, Tag | IndirectRef | IndirectExprRef]
| None = None,
oneshot: bool = False,
) -> None
Execute a synchronous function when rung power is true.
pyrung.run_enabled_function
run_enabled_function(
fn: Callable[..., dict[str, Any]],
*,
ins: dict[
str, Tag | IndirectRef | IndirectExprRef | Any
]
| None = None,
outs: dict[str, Tag | IndirectRef | IndirectExprRef]
| None = None,
) -> None
Execute a synchronous function every scan with rung enabled state.
pyrung.blockcopy
blockcopy(
source: Any,
dest: Any,
*,
convert: CopyConverter | None = None,
oneshot: bool = False,
) -> None
Block copy instruction.
Copies values from source BlockRange to dest BlockRange. Both ranges must have the same length.
Pass convert=to_value or convert=to_ascii for text→numeric
block conversion. Only value and ascii modes are supported.
Example
with Rung(CopyEnable): blockcopy(DS.select(1, 10), DD.select(1, 10))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Any
|
Source BlockRange or IndirectBlockRange from .select(). |
required |
dest
|
Any
|
Dest BlockRange or IndirectBlockRange from .select(). |
required |
convert
|
CopyConverter | None
|
Optional converter (to_value or to_ascii only). |
None
|
oneshot
|
bool
|
If True, execute only once per rung activation. |
False
|
pyrung.fill
Fill instruction.
Writes a constant value to every element in a BlockRange.
Example
with Rung(ClearEnable): fill(0, DS.select(1, 100))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
Value to write (literal, Tag, or Expression). |
required |
dest
|
Any
|
Dest BlockRange or IndirectBlockRange from .select(). |
required |
oneshot
|
bool
|
If True, execute only once per rung activation. |
False
|
pyrung.pack_bits
Pack BOOL tags from a BlockRange into a register destination.
pyrung.pack_text
pack_text(
source_range: Any,
dest: Any,
*,
allow_whitespace: bool = False,
oneshot: bool = False,
) -> None
Pack Copy text mode: parse a TXT/CHAR range into a numeric destination.
pyrung.pack_words
Pack two 16-bit tags from a BlockRange into a 32-bit destination.
pyrung.unpack_to_bits
Unpack a register source into BOOL tags in a BlockRange.
pyrung.unpack_to_words
Unpack a 32-bit register source into two 16-bit tags in a BlockRange.
pyrung.calc
Calc instruction.
Evaluates an expression and stores the result in dest, with truncation to the destination tag's bit width (modular wrapping).
Key differences from copy(): - Truncates result to destination tag's type width - Division by zero produces 0 (not infinity) - Infers decimal/hex behavior from referenced tag types
Example
with Rung(Enable): calc(DS1 * DS2 + DS3, Result) calc(MaskA & MaskB, MaskResult)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expression
|
Any
|
Expression, Tag, or literal to evaluate. |
required |
dest
|
Tag
|
Destination tag (type determines truncation width). |
required |
oneshot
|
bool
|
If True, execute only once per rung activation. |
False
|
Returns:
| Type | Description |
|---|---|
Tag
|
The dest tag. |
pyrung.call
Call a subroutine instruction.
Executes the named subroutine when the rung is true. Accepts either a string name or a @subroutine-decorated function.
Example
with Rung(Button): call("init_sequence")
with subroutine("init_sequence"): with Rung(): out(Light)
Or with decorator:
@subroutine("init") def init_sequence(): with Rung(): out(Light)
with Program() as logic: with Rung(Button): call(init_sequence)
pyrung.return_early
Return from the current subroutine.
Example
with subroutine("my_sub"): with Rung(Abort): return_early()
pyrung.count_up
Count Up instruction (CTU).
Creates a counter that increments every scan while the rung condition is True.
Use rise() on the condition for edge-triggered counting.
Example
with Rung(rise(PartSensor)): count_up(Counter[1], preset=100).reset(ResetBtn)
This is a terminal instruction. Requires .reset() chaining.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
counter
|
DoneAccUDT
|
Counter instance (must have |
required |
preset
|
Tag | int
|
Target value (Tag or int). |
required |
Returns:
| Type | Description |
|---|---|
CountUpBuilder
|
Builder for chaining .down() and .reset(). |
pyrung.count_down
Count Down instruction (CTD).
Creates a counter that decrements every scan while the rung condition is True.
Use rise() on the condition for edge-triggered counting.
Example
with Rung(rise(Dispense)): count_down(Counter[1], preset=25).reset(Reload)
This is a terminal instruction. Requires .reset() chaining.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
counter
|
DoneAccUDT
|
Counter instance (must have |
required |
preset
|
Tag | int
|
Target value (Tag or int). |
required |
Returns:
| Type | Description |
|---|---|
CountDownBuilder
|
Builder for chaining .reset(). |
pyrung.event_drum
event_drum(
*,
outputs: Sequence[Tag],
events: Sequence[Condition | Tag],
pattern: Sequence[Sequence[bool | int]],
current_step: Tag,
completion_flag: Tag,
) -> EventDrumBuilder
pyrung.search
search(
comparison: RangeComparison,
*,
result: Tag,
found: Tag,
continuous: bool = False,
oneshot: bool = False,
) -> Tag
Search instruction.
Scans a selected range and writes the first matching address into result.
Writes found True on hit; on miss writes result=-1 and found=False.
The first argument is a comparison expression built from a .select() range::
search(DS.select(1, 100) >= 100, result=FoundAddr, found=FoundFlag)
search(Txt.select(1, 50) == "A", result=FoundAddr, found=FoundFlag)
pyrung.shift
Shift register instruction builder.
Data input comes from current rung power. Use .clock(...) then .reset(...) to finalize and add the instruction.
Example
with Rung(DataBit): shift(C.select(2, 7)).clock(ClockBit).reset(ResetBit)
pyrung.on_delay
On-Delay Timer instruction (TON/RTON).
Accumulates time while rung is true.
Example
with Rung(MotorRunning): on_delay(Timer[1], preset=5000) # TON keyword on_delay(Timer[1], 5000) # TON positional on_delay(Timer[1], 5, "s") # TON 5 seconds on_delay(Timer[1], preset=5000).reset(ResetBtn) # RTON
Without .reset(), this is TON and remains composable in-rung. With .reset(), this is RTON and becomes terminal in the current flow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timer
|
DoneAccUDT
|
Timer instance (must have |
required |
preset
|
Tag | int
|
Target value in time units (Tag or int). |
required |
unit
|
TimeUnitStr
|
Time unit for accumulator (default: |
'ms'
|
Returns:
| Type | Description |
|---|---|
OnDelayBuilder
|
Builder for optional .reset() chaining. |
pyrung.off_delay
Off-Delay Timer instruction (TOF).
Done bit is True while enabled. After disable, counts until preset, then done bit goes False. Auto-resets when re-enabled.
Example
with Rung(MotorCommand): off_delay(Timer[2], preset=10000) off_delay(Timer[2], 10, "s") # 10 seconds
Off-delay timers are composable in-rung (not terminal).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timer
|
DoneAccUDT
|
Timer instance (must have |
required |
preset
|
Tag | int
|
Delay time in time units (Tag or int). |
required |
unit
|
TimeUnitStr
|
Time unit for accumulator (default: |
'ms'
|
Returns:
| Type | Description |
|---|---|
OffDelayBuilder
|
Builder for the off_delay instruction. |
pyrung.time_drum
time_drum(
*,
outputs: Sequence[Tag],
presets: Sequence[Tag | int],
unit: TimeUnitStr = "ms",
pattern: Sequence[Sequence[bool | int]],
current_step: Tag,
accumulator: Tag,
completion_flag: Tag,
) -> TimeDrumBuilder
pyrung.send
send(
*,
target: str | ModbusTcpTarget | ModbusRtuTarget,
remote_start: str | ModbusAddress,
source: Tag | BlockRange,
sending: Tag,
success: Tag,
error: Tag,
exception_response: Tag,
count: int | None = None,
word_swap: bool = False,
) -> None
Modbus send instruction (write local values to a remote device).
target may be a :class:ModbusTcpTarget or :class:ModbusRtuTarget
(live simulation) or a plain string name (codegen placeholder).
remote_start may be a Click address string (e.g. "DS1") for Click
PLCs, or a :class:ModbusAddress for raw Modbus devices.
pyrung.receive
receive(
*,
target: str | ModbusTcpTarget | ModbusRtuTarget,
remote_start: str | ModbusAddress,
dest: Tag | BlockRange,
receiving: Tag,
success: Tag,
error: Tag,
exception_response: Tag,
count: int | None = None,
word_swap: bool = False,
) -> None
Modbus receive instruction (read remote device values into local tags).
target may be a :class:ModbusTcpTarget or :class:ModbusRtuTarget
(live simulation) or a plain string name (codegen placeholder).
remote_start may be a Click address string (e.g. "DS1") for Click
PLCs, or a :class:ModbusAddress for raw Modbus devices.
pyrung.ModbusAddress
dataclass
Modbus register address.
address accepts:
- MODBUS 984
int— e.g.400001(prefix encodes register type) - MODBUS Hex
strwithhsuffix — e.g."0h","FFFEh" - Raw
int0–0xFFFE — low-level register offset
pyrung.ModbusRtuTarget
dataclass
Connection details for a remote Modbus RTU (serial) device.
pyrung.ModbusTcpTarget
dataclass
Connection details for a remote Modbus TCP device.
pyrung.RegisterType
Bases: Enum
Modbus register / coil type.
pyrung.WordOrder
Bases: Enum
Word ordering for 32-bit values across register pairs.
pyrung.rise
Rising edge contact (RE).
True only on 0->1 transition. Requires PLC to track previous values.
Example
with Rung(rise(Button)): latch(MotorRunning) # Latches on button press, not while held
pyrung.fall
Falling edge contact (FE).
True only on 1->0 transition. Requires PLC to track previous values.
Example
with Rung(fall(Button)): reset(MotorRunning) # Resets when button is released
pyrung.And
And(
*conditions: Condition
| Tag
| ImmediateRef
| tuple[Condition | Tag | ImmediateRef, ...]
| list[Condition | Tag | ImmediateRef],
) -> AllCondition
AND condition - true when all sub-conditions are true.
This is equivalent to comma-separated rung conditions, but useful when building grouped condition trees with Or().
Example
with Rung(And(Ready, AutoMode)): out(StartPermissive)
with Rung(Or(And(Ready, AutoMode), RemoteStart)): out(StartPermissive)
pyrung.Or
OR condition - true when any sub-condition is true.
Use this to combine multiple conditions with OR logic within a rung. Multiple conditions passed directly to Rung() are ANDed together.
Example
with Rung(Step == 1, Or(Start, CmdStart)): out(Light) # True if Step==1 AND (Start OR CmdStart)
Grouped AND inside OR (explicit):
with Rung(Or(Start, And(AutoMode, Ready), RemoteStart)): out(Light)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conditions
|
Condition | Tag | ImmediateRef
|
Conditions to OR together. |
()
|
Returns:
| Type | Description |
|---|---|
AnyCondition
|
AnyCondition that evaluates True if any sub-condition is True. |
pyrung.immediate
Wrap a tag or block range as an immediate operand.
pyrung.to_value
module-attribute
Text → Numeric conversion using the character's face value.
Corresponds to Click PLC "Copy Character Value" (Option 4b).
Example::
# CHAR '5' → numeric 5
copy(ModeChar, DS[1], convert=to_value)
pyrung.to_ascii
module-attribute
Text → Numeric conversion using the ASCII code.
Corresponds to Click PLC "Copy ASCII Code Value" (Option 4b).
Example::
# CHAR '5' → ASCII 53
copy(ModeChar, DS[1], convert=to_ascii)
pyrung.to_text
to_text(
*,
suppress_zero: bool = True,
exponential: bool = False,
termination_code: int | str | None = None,
) -> CopyConverter
Numeric → Text conversion.
Corresponds to the Click PLC Copy Option 4a (Numeric→Text) and Option 4c (Float→Text).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
suppress_zero
|
bool
|
When True (default), leading zeros are omitted (Click PLC "Suppress zero"). When False, leading zeros fill the full digit width of the source data type. |
True
|
exponential
|
bool
|
When True, use scientific notation (Click PLC "Exponential Numbering", Option 4c). Only applicable to Float sources. |
False
|
termination_code
|
int | str | None
|
An ASCII code (int 0–127), single character,
or |
None
|
Examples::
copy(DS[1], Txt[1], convert=to_text())
copy(DS[1], Txt[1], convert=to_text(suppress_zero=False))
copy(DF[1], Txt[1], convert=to_text(exponential=True))
copy(DS[1], Txt[1], convert=to_text(termination_code=0))
copy(DS[1], Txt[1], convert=to_text(termination_code="$0D"))
pyrung.to_binary
module-attribute
Numeric → Text conversion as raw binary.
Corresponds to Click PLC "Copy Binary" (Option 4a). The numeric value is stored directly as an ASCII character.
Example::
# DS1=123 → '{' (ASCII 123)
copy(DS[1], Txt[1], convert=to_binary)