puzzle-piece-simpleExtend with a Custom Component

SPX lets you add custom components so you can model behaviors that go beyond the built‑ins. In this guide we’ll create a tiny extension that simulates contact faults on a PT100 sensor (random spikes or drop‑to‑zero events), load it into a running SPX Server, and use it in a model.

This article is part of Getting Started. It focuses on a minimal, code‑defined workflow.


What you’ll build

  • A YAML model that references a new action called contact_fault

  • A Python implementation of that action (a custom extension)

  • A short script that:

    • connects to the SPX Server via spx_python

    • reloads extensions from the configured directories (e.g. ./extensions)

    • runs a deterministic time simulation (you control the clock)


1) Define the model in YAML

We’ll model a PT100‑like sensor and apply a few actions: a saw, a ramp with overshoot, proportional noise, and our new contact_fault action that injects spikes/drops.

models:
  pt100_sensor:
    attributes:
      temperature: 0.0
    actions:
      - { saw: $attr(temperature), stop_value: 14, period: 5 }
      - { ramp: $attr(temperature), stop_value: 150, duration: 5, type: overshoot, overshoot: 5 }
      - { noise: $ext(temperature), std: 0.01, mode: proportional }
      - { contact_fault: $ext(temperature), spike_value: 500.0 }  # custom extension

Why $ext(...)? We typically map fault injections to the external view of an attribute so core logic remains stable while the presented value exhibits faults.


2) Implement the custom action (contact_fault.py)

Create ./extensions/contact_fault.py:

You can organize extensions as single files or full folders/repositories. If an extension folder contains a requirements.txt, SPX will attempt to install those dependencies (subject to your server configuration/permissions).


3) Load the extension in a running SPX Server

Use spx_python to connect and then reload modules. By default the server scans the configured extensions directories (e.g., ./extensions in the working directory). Ensure your contact_fault.py lives in one of those directories.

Note If your extension lives elsewhere, add that directory to the server’s extension search paths (per your deployment settings) before calling reload_modules().


4) Create a model and run a deterministic time simulation

Deterministic means you control the clock — the simulation advances when you set timer.time and call run().

(Optional) Plot with Plotly:


Troubleshooting

  • “Module not found / class not registered” Ensure your file is inside a configured extensions directory and that you called wrapper.reload_modules() after adding it. Check server logs for import errors.

  • Dependencies missing If your extension needs packages, provide a requirements.txt in the extension folder. Whether installs are allowed depends on your server configuration.

  • No spikes visible Increase probability or set a lower drop_ratio to bias spikes. You can also set seed for reproducible runs.

  • Real‑time vs deterministic time This example uses deterministic control (timer.time + run()). For real‑time/polling modes, you would enable polling and use start()/stop() instead.


Next steps

  • Package your extension as a reusable repo and point the server to it

  • Add unit tests for your custom component

  • Use labels/notes in snapshots to track experiments and reload setups quickly

Happy extending! 🎛️

Last updated