PortNumbers: not needed in the AIGR nor RPy

Summary

After a successful try with (optional) AutoNumbering, I decided that the port_number (a low int) is an unneeded leftover of the once “handCompiled-C” version. It isn’t needed in general. And so, -as we don’t store/use portNo– the auto-numbering option is removed (but documented, as it might be convenient later).

Background

In the (handCompiled) C version, everything is stored in an C-array; and we have to use (array) indexes to find the element. Therefore, those port numbers (as index) have to be unique, contiguous “low” numbers. In general, this is inconvenient. When a base-component(interface) changes the number of ports, all other have to be renumbered.
That is fine for (generated) C-code, but unwanted in e.g the AIGR-model.

Also the Sieve_in_rPython variant has those index portNo’s – although with a question-mark. And thy are never used.
Arrays in (r)Python are list and can be indexed with (e.g.) a name. Such a nameID is more convenient, and the Castle semantics already secures uniqueness.

Thus,the RPy backend doesn’t need to render ‘PortNo’ (see file:castle/writers/RPy/templates/parts/interface_DataStructures.jinja2) and hence doesn’t need in the AIGR either.

AutoNumbering

I have made a variant of castle/aigr/interfaces.py to support automatically numbering of port. It quite simple.

  • We need to store the port_no in Port and with a default marker: _AUTO_NUMBER.

  • In ComponentInterface, in the post_init a call to _number_auto_ports() is make

  • That method add portNo’s to the ports of that instance. After some basic checks.
    Note, the routine isn’t perfect - there are complex edge cases that are ignored.

@dataclass
class Port(AIGR):
    _AUTO_NUMBER=-1
    name: str
    _: KW_ONLY
    direction: PortDirection
    type: PortType
    port_no: int=_AUTO_NUMBER # automatically set in ComponentInterface
@dataclass
class ComponentInterface(AIGR):
...
    def __post_init__(self):
        self._number_auto_ports()

    def _number_auto_ports(self):
        all_auto  = all(p.port_no == Port._AUTO_NUMBER for p in self.ports)
        if all_auto:
            start = self.based_on._noPorts() if isinstance(self.based_on, ComponentInterface) else 0
            for n, p in enumerate(self.ports, start=start):
                p.port_no = n
        else:
            any_auto =any(p.port_no == Port._AUTO_NUMBER for p in self.ports)
            assert not any_auto, "Do not mix automatic port-numbering with pre-set ones. Typically, use set all!!"

Error

AutoNumbering is tricky

During the development, all kind of complex edge cases are found. As the feature is removed, those checks are (mostly) ignored. And covert by statements as “do not mix”, “use with care”, and “only during development”.

Conceptually, all (the inherited and own/defined) ports should be numbered: 0, 1, 2 …
Whenever a portNo can be preset, that isn’t possible. Some corner cases:

  • The pre-set numbers of two ports can be te same.

    • Ignore in auto-numbering, as it can’t be fixed (here)

    • Idea (not realised): write a checker (plugin) for the AIGR-model

    • As a variant: those non unique numbers can be set in anywhere in inherit tree of Components.

  • Some portNo’s are set, others not

    • We kind of need to find the available numbers first

    • Is there a reason that some numbers are skipped? Than do no use them – complex

    • Or, just continue at the highest port-no – more holes

  • We need all kind of auxiliary methods for ComponentInterface, to handle port-numbering

    • Not SOLID: code becomes hard to maintain and misleading

    • But, putting the in Port doesn’t solve it

At the end, instead of “solving” those issues, I reconsidered the portNo attribute. By abandoning that left-over, all those issues are solved too.

Comments

comments powered by Disqus