Memory Configuration & CDI

Read and write a node’s configuration, CDI and identity through Memory Configuration datagrams, and lay out its CDI.

from pyolcb import MemoryConfiguration, cdi

mc = MemoryConfiguration(node, 0x020157000099)
layout = cdi.parse(mc.read_cdi())
for field in layout.fields():
    print("/".join(field.path), field.decode(mc.read(field.space, field.address, field.size)))
mc.write(0xFB, 1, b"Yard throat\0")
mc.update_complete()

memory_config

Memory Configuration protocol: reading and writing a node’s configuration, CDI and identity through datagrams whose first byte is 0x20.

The codec functions build requests and decode replies without any I/O, and MemoryConfiguration runs them against a remote node through pyolcb.Node.datagram_exchange():

mc = MemoryConfiguration(node, 0x020157000099)
cdi = mc.read_cdi()
mc.write(SPACE_ACDI_USER, 1, b"Yard throat\0")
mc.update_complete()
class pyolcb.memory_config.MemoryConfiguration(node, destination, timeout: float = 3.0)

A Memory Configuration client for one remote node.

Parameters:
  • node (pyolcb.Node) – The local node to send from.

  • destination (Address | int) – The node to configure, as an Address or 48-bit node ID.

  • timeout (float) – Seconds to wait for each answer.

factory_reset(node_id: int)

Reset the node’s configuration to factory defaults. node_id must be the node’s own ID.

lock(node_id: int) int

Reserve the node for node_id (0 releases). Returns the node ID now holding the lock.

options() dict

Get Configuration Options.

read(space: int, address: int, length: int) bytes

Read length bytes in as many 64-byte requests as it takes. Stops early, returning fewer bytes, at the end of the space.

read_cdi() str

Read the node’s Configuration Description Information XML.

read_space(space: int, stop_at_nul: bool = False, limit: int = 1048576) bytes

Read a whole space, sized with Get Address Space Information. With stop_at_nul reading ends at the first NUL (for the CDI).

reboot()

Ask the node to restart. A node that restarts before acknowledging is not an error.

space_info(space: int) SpaceInfo

Get Address Space Information.

update_complete()

Tell the node the tool has finished writing, so it commits and applies the settings.

write(space: int, address: int, data: bytes)

Write data in as many 64-byte requests as it takes.

class pyolcb.memory_config.SpaceInfo(space: int, present: bool, highest: int = 0, lowest: int = 0, read_only: bool = True, description: str = '')

What Get Address Space Information reports about a memory space.

property size: int

Bytes from the lowest to the highest address, inclusive.

pyolcb.memory_config.describe(data: bytes) str

A one-line summary of a Memory Configuration datagram, for traffic monitors.

pyolcb.memory_config.factory_reset_request(node_id: int) bytes

Factory reset must name the target node, so a stray datagram cannot wipe the wrong one.

pyolcb.memory_config.is_reply_to(request: bytes, reply: bytes) bool

True if reply is the Memory Configuration answer to request.

pyolcb.memory_config.lock_request(node_id: int) bytes

Reserve a node for configuration (node ID 0 releases it).

pyolcb.memory_config.parse_options_reply(reply: bytes) dict

Decode Get Configuration Options Reply.

pyolcb.memory_config.parse_read_reply(reply: bytes) tuple[int, int, bytes]

Decode a read reply into (space, address, data). Raises MemoryConfigError for a Read Failed reply.

pyolcb.memory_config.parse_space_info_reply(reply: bytes) SpaceInfo

Decode [0x20, 0x86|present, space, highest(4), flags, lowest(4)?, description?]. Flag bit 0 is read-only, bit 1 says a lowest address follows.

pyolcb.memory_config.parse_write_reply(reply: bytes) None

Check a write reply; raises MemoryConfigError for Write Failed.

pyolcb.memory_config.read_request(space: int, address: int, count: int) bytes

A read of count (1-64) bytes, always in the explicit-space form.

pyolcb.memory_config.write_request(space: int, address: int, data: bytes) bytes

A write of up to 64 bytes, in the explicit-space form.

CDI

cdi

Configuration Description Information: the XML a node serves from memory space 0xFF to describe its configuration memory.

parse() turns the XML into a layout tree in which every field carries its absolute memory space and address, with group replication expanded:

layout = cdi.parse(xml)
for field in layout.fields():
    print(field.path, field.space, hex(field.address), field.size)

Layout rules, per the OpenLCB CDI standard:

  • A <segment> sets the memory space and starts at its origin (default 0).

  • Any element may have an offset attribute: bytes skipped (or, if negative, backed up) before the element.

  • A <group> is as large as its contents; with replication N it is laid out N times back to back, each copy named from its <repname> plus a number.

  • <int> is 1, 2, 4 or 8 bytes (default 1), big-endian, with optional <min>, <max>, <default> and a <map> of named values.

  • <string> has a size and holds NUL-terminated UTF-8.

  • <eventid> is always 8 bytes.

  • <float> is 2, 4 or 8 bytes (default 4), IEEE 754 big-endian.

  • <action> (a button that writes <value>) and <blob> take size bytes.

class pyolcb.cdi.Cdi(identification: dict, acdi: bool, segments: list[Segment])

A parsed CDI document.

fields() list[Field]

Every field in memory order, replication expanded.

ranges() list[tuple[int, int, int]]

(space, start, length) spans covering every field, merged where they touch.

class pyolcb.cdi.Field(kind, name, description, space, address, size, path, minimum=None, maximum=None, default=None, map=None, hints=None, button_text=None, dialog_text=None, value=None)

One configurable value at a fixed place in a node’s memory.

kind

int, string, eventid, float, action or blob.

Type:

str

name, description
Type:

str

space, address, size

Where the value lives.

Type:

int

path

Names of the enclosing segment and groups, then the field’s own name.

Type:

list[str]

minimum, maximum, default
Type:

int | float | None

map

(property, label) pairs; an int with a map is a choice.

Type:

list[tuple[str, str]]

decode(data: bytes)

Turn the field’s bytes into a Python value (int, str, float, or dotted-hex event ID).

encode(value) bytes

Turn a value back into exactly size bytes.

class pyolcb.cdi.Group(name, description, children, label=None, index=None)

A named block of fields and groups; a replicated group has one Group per copy.

class pyolcb.cdi.Replicated(name, description, replicas)

A group with replication > 1: the copies, in memory order.

class pyolcb.cdi.Segment(space, origin, name, description, children, end)

One <segment>: fields in a single memory space.

pyolcb.cdi.parse(xml: str | bytes) Cdi

Parse CDI XML into a Cdi layout. Raises ValueError on malformed input.

Simple Node Information

snip

Simple Node Information Protocol (SNIP): the identity strings a node reports in reply to a Simple Node Ident Info Request.

The reply is a version byte (4), manufacturer, model, hardware version and software version as NUL-terminated strings, a second version byte (2), then the user-assigned name and description. Older nodes send version 1 in either place; both are accepted.

class pyolcb.snip.SimpleNodeInfo(manufacturer: str = '', model: str = '', hardware_version: str = '', software_version: str = '', user_name: str = '', user_description: str = '')

The identity of a node.

Parameters:
  • manufacturer (str) – Fixed at build time by the node’s maker.

  • model (str) – Fixed at build time by the node’s maker.

  • hardware_version (str) – Fixed at build time by the node’s maker.

  • software_version (str) – Fixed at build time by the node’s maker.

  • user_name (str) – Set by the user through Memory Configuration space 0xFB.

  • user_description (str) – Set by the user through Memory Configuration space 0xFB.

classmethod from_bytes(data: bytes) SimpleNodeInfo

Decode a SNIP reply payload, tolerating short or truncated replies.

to_bytes() bytes

Encode as a SNIP reply payload.