Components
Foundation classes for every SPX object
1 . The big picture
SpxComponent <-- generic lifecycle, hierarchy, hooks
▲
│
SpxContainer <-- auto‑loads children from YAML/Python definitions
▲
└───> higher‑level bricks (Attributes, Actions, Conditions, …)SpxComponentis the protocol every runtime object obeys.SpxContaineradds a smart constructor that expands a YAML/JSON definition into an in‑memory tree of child components.
When you declare a model in YAML/JSON or Python dict, SPX silently manufactures a small forest of SpxContainers and SpxComponents; knowing the two base classes lets you extend the system with zero boilerplate.
SpxComponent — what you get out‑of‑the‑box
Lifecycle FSM
prepare → start → run → pause → stop → reset → destroy, with the current state stored in self.state (SpxComponentState enum).
Hierarchy
Parent/child pointers, depth‑first traversal helpers (get_root, get_hierarchy), dict‑like access (comp["pump"]).
Enable/disable flag
Flip component.enabled = False and all lifecycle calls become no‑ops without altering the tree.
Hook bus
Register hooks (register_hook) and fire them (trigger_hooks) on any event name you invent ("on_set", "after_run", …).
Pythonic sugar
len(comp), 'sensor' in comp, repr(comp) for painless debugging.
Definition hydration
Pass a dict/YAML block as definition= – every key is copied to an attribute, or raises if the attribute doesn’t exist (great for catching typos early).
Tip : reuse the embedded logger (self.logger.debug(...))—each component has a namespace like
spx_sdk.components.SpxContainer.MyModel.
SpxContainer — turbo‑charged constructor
SpxContainer inherits everything above and turns a data structure into live children at instantiation.
Filtered mode (type=SomeSubclass)
Only entries that map to
SpxAttribute(or its subclasses) become children.Scalars are promoted to the base class with a unique name.
Dict or list entries are looked up in the registry to find more specific subclasses.
Generic mode (default, no type)
Each key is treated as a class name registered with
@register_class.Instances are created automatically; nested dicts or single‑key list items are supported.
End‑to‑end example – bring your own Action
2) Definition snippet
During model load the
actionscontainer scans the list, spotsspring_mass_damper, pulls the matching class from the registry, and instantiates it under the parent.The new Action inherits lifecycle and hooks from
SpxComponent; parameters are auto‑hydrated; outputs are resolved to realSpxAttributeobjects.
Authoring guidelines
Keep subclasses thin—delegate cross‑cutting behaviour to hooks.
Deep inheritance chains; prefer composition via containers.
Use trigger_hooks("on_event") early in methods to keep extensibility.
Hard‑coding behaviour in run that can’t be overridden.
Validate definition dicts in _populate, raise early.
Silent failures when a user misspells a field.
Call super()._populate(definition) last if you mutate definition.
Mutating then forgetting to pass up—children won’t load.
Cheat‑sheet
Get root of tree
comp.get_root()
Walk children
for c in comp.get_children_list(): …
Access attribute value
model["attributes"]["temp"].external_value
Toggle feature
comp.enabled = False
TL;DR
SpxComponent→ lifecycle + hierarchy + hooks.SpxContainer→ “turn this YAML blob into living children”.
Master these two, and every other SPX brick—attributes, actions, conditions, full models—will feel like variations on a theme rather than brand‑new APIs.
Last updated

