Source code for moddef.command
# SPDX-License-Identifier: Apache-2.0
"""Command (multi-step register procedure) helpers, spec §11.7.
The executor itself is `Device.run_command` in device.py; this module holds
the pure pieces: poll-condition evaluation, single-PDU chunking caps, and
the synthetic points that let command params and raw trigger/poll values
reuse the shared codec.
"""
from __future__ import annotations
from collections.abc import Sequence
from moddef import schema
from moddef.codec.decode import decode_point_raw, is_signed
# Modbus single-PDU practical caps (FC03/FC16); larger transfers chunk.
MAX_READ_WORDS = 125
MAX_WRITE_WORDS = 123
# Default poll interval when a PollStep omits interval_ms.
DEFAULT_POLL_INTERVAL_MS = 250
[docs]
def condition_met(c: schema.Condition, raw: int) -> bool:
"""Evaluate a §11.7 poll exit condition against a raw integer."""
if c.op == schema.ConditionOp.EQ:
return raw == c.value
if c.op == schema.ConditionOp.NE:
return raw != c.value
if c.op == schema.ConditionOp.MASK:
return (raw & c.mask) == c.value
if c.op == schema.ConditionOp.RANGE:
return c.min <= raw <= c.max
return False
[docs]
def param_point(cp: schema.CommandParam) -> schema.Point:
"""Synthetic point carrying a CommandParam's wire mapping for the codec."""
return schema.Point(
point_id=f"param:{cp.field}",
storage_type=cp.storage_type,
value_type=cp.value_type,
mapping=cp.mapping,
)
[docs]
def raw_point(p: schema.Point) -> schema.Point:
"""Storage/mapping-only copy of a point: trigger writes and poll reads are
raw register values (§11.7), bypassing transform and value_type."""
return schema.Point(
point_id=p.point_id,
storage_type=p.storage_type,
mapping=p.mapping if p.HasField("mapping") else None,
)
[docs]
def raw_int(p: schema.Point, regs: Sequence[int]) -> int:
"""Decode a point's raw (pre-transform) integer, sign-extended per storage."""
raw, bits = decode_point_raw(p, regs)
if is_signed(p.storage_type) and raw >= 1 << (bits - 1):
raw -= 1 << bits
return raw