Circuit blocks

Two types of blocks

There are two types of building blocks. Each block is either combinational or sequential. The main differences are:

combinational

sequential

has inputs connected to other blocks

no inputs

no internal state

has an internal state (memory, time) and thus requires initialization

the output depends on input values

the output depends on the internal state

does not accept events

reacts to events

can generate events only on output change

can generate events also on internal state changes

Common features

class edzed.Block(name: str | None, *, comment: str = '', on_output=None, debug: bool = False, **x_kwargs)

Create a block and add it to the current circuit.

The Block implements common features of all circuit blocks, combinational and sequential. It cannot be instantiated directly (it is an abstract class). Concrete blocks must be derived either from the combinational base class CBlock or from the sequential base class SBlock.

The mandatory argument name is block’s unique identifier, a non-empty string. Names prefixed by an underscore are reserved for automatically created blocks. Enter None to request a generated name; use this feature only for auxiliary blocks that you will not need to reference by name.

The optional comment may be any arbitrary text and is not used internally.

The optional on_output argument specifies events to be sent on each output change.

The debug argument initializes the debug attribute.

All keyword arguments starting with 'x_' or 'X_' are accepted and stored as block’s attributes. These names are reserved for storing arbitrary application data.

Keyword arguments other than those mentioned above are not accepted.

Following attributes and methods are defined. Do not modify any block attributes unless explicitly permitted.

circuit: Circuit

The Circuit object the block belongs to. Usually there is only one circuit. An application code should use get_circuit() to get a reference to it.

comment: str = ""

Block’s text description or comment. May be modified.

debug: bool = False

Allow this block to log debugging messages. May be modified.

name: str

The assigned block’s name.

output: Any

Block’s output value, a read-only property.

Each block has exactly one output value of any type.

A special UNDEF value is assigned to newly created blocks.

x_anyname: Any
X_ANYNAME: Any

(with any arbitrary name) Reserved for application data, ignored by edzed. May be added/removed/modified.

get_conf() dict[str, Any]

Return a summary of static block information.

Example output:

{
  'class': 'Counter',
  'debug': False,
  'comment': '',
  'name': 'cnt1',
  'persistent': False,
  'type': 'sequential'
}

All items are self-explaining. Not applicable items are excluded, e.g. ‘inputs’ is shown for combinational blocks in a finalized circuit only. New items may be added in future releases. Note that items like name or comment can be accessed also as block attributes.

edzed.UNDEF

A constant representing an undefined output. All other output values are valid, including None. It is an error when edzed.UNDEF value appears on block’s output after the circuit initialization.

Constants

class edzed.Const(value: Any)

A pseudo-block with a constant value on its output. Const objects are not registered as members of the circuit and are not derived from the Block base class. A Const object can hold any value except the UNDEF.

name: str

The automatically generated block’s name.

output: Any

Block’s constant output value, a read-only property.

Combinational blocks

The output of a combinational block depends only on its present input values.

class edzed.CBlock(name, **block_kwargs)

The base class for combinational blocks does not add any new arguments compared to Block.

connect(*unnamed_inputs, **named_inputs)

Connect block’s inputs. Return self in order to allow a ‘fluent interface’.

An input is either a single input or a multiple input called group. A group consists of any number (zero or more) of single inputs.

All inputs given as positional arguments (i.e. unnamed) will be stored as a group named "_". This group is created only if unnamed inputs exist, i.e. it cannot be empty.

All inputs given as keyword arguments will have the given names. Avoid the reserved name "_".

To connect a single named input, add a keyword argument:

name=single_input  # defined below

To connect a group:

name=(input1, input2, ...)  # a sequence (tuple, list, ...) of single inputs

A single input could be connected:

  1. to another block’s output specified with:

  1. or to a constant value given as:

  • a Const object

  • any value that does not specify an input or a group, i.e. not a string, tuple, list or similar. The value will be automatically wrapped into a Const.

It is recommended to use Const(value) explicitly for all values except the None, True, False and numbers.

connect() must be called before the circuit initialization takes place and may be called only once.

All block’s inputs must be connected. A group may have no inputs, but it must be explicitly connected as such: group=() or group=[].

Sequential blocks

class edzed.SBlock(name, *, initdef=edzed.UNDEF, persistent=False, sync_state=True, expiration=None, init_timeout=None, stop_timeout=None, on_every_output=None, **block_kwargs)

The base class for all sequential blocks. A subclass of Block.

The optional argument on_every_output specifies events to be sent on each output event. It differs slightly from the on_output, more details in output events.

Important

Only applicable arguments from the list below are accepted by concrete sequential block types. Refer to descriptions of individual blocks for details which arguments are supported by the given block.

  • Setting the initial state:

    Argument initdef (type: Any) specifies the initial or the default internal state. Its precise meaning varies depending on the block:

    • initdef is not accepted, because the internal state is not adjustable (e.g. determined by current date or time).

    • initdef is the primary initial value used to initialize the block. In this case is the argument mandatory for the given block.

    • initdef is the default value just for the case the regular initialization fails. In this case is the argument optional, but highly recommended for the given block.

    If accepted, the initdef value is saved as an attribute:

    initdef: Any

    Saved value of the initdef argument or UNDEF, if the argument was not given. Only present if the block accepts this argument. This attribute allows to implement a reset if need be.

  • Enabling persistent state:

    Persistent state means that the internal state is saved (most likely to a file) when the application stops and is restored on the next start. The data persistence is only possible in a circuit having a proper persistent storage. The settings below will have no effect without the storage.

    If a block supports this feature, it is controlled by these parameters:

    • persistent (bool):

      Enable the persistent state. Default is False.

    • sync_state (bool):

      Save the state also after each event. Default is True.

    • expiration (int or float or str or None):

      Expiration time measured since the program stop. An expired state is disregarded. Expiration value may be None, number of seconds, or a string with time units.

      The expiration value defaults to None which means that the saved state never expires.

  • Timeouts for asynchronous initialization and cleanup:

    Some blocks perform asynchronous operations. These arguments control the timeouts:

    • init_timeout (int or float or str or None):

      initialization timeout in seconds. Default timeout is 10 seconds. Value 0.0 or negative disables the async initialization.

    • stop_timeout (int or float or str or None):

      cleanup timeout in seconds. Default timeout is 10 seconds. Value 0.0 or negative disables the async cleanup.

    The timeout values must be given as a number of seconds, None for the default timeout, or a string with time units.

    The timeouts should be explicitly set. A warning is logged, when the default is used.

Internal state

The internal state consists of all data a sequential block maintains in order to correctly perform the task it was designed to.

In its simplest form is the internal state equal to the output value. Such blocks (e.g. the Input) act like a memory cell.

The internal state is affected by:

  • events sent from other blocks,

  • events coming from external sources,

  • the block’s own activity like timers, or readouts of sensors and gauges

Initialization rules

By definition a block is deemed initialized when its output is not UNDEF. The output is closely related to the internal state, so block initialization basically means internal state initialization.

Blocks are initialized at the beginning of circuit simulation. During the process available block’s sources of the initial state are utilized in the order listed below. Which sources are defined depends on the particular block.

  1. from saved persistent data

  2. by the asynchronous initialization routine; this step is skipped if an incoming event (see item 5) is pending

  3. by the regular (i.e. not async) initialization routine

  4. only if still not initialized: from the initdef parameter;

  5. as a result of an incoming event triggered by other circuit block’s initialization

When the routines from list items 2 and 3 are called, the block may have been initialized already. In such case the routine may keep the state or it may overwrite it.

The simulation fails if any block remains uninitialized.