Using and extending EUT monitoring

EUT monitoring is part of every immunity test. mpylab separates three responsibilities:

  • monitors observe the EUT and produce structured events;

  • the measurement kernel decides whether to continue, retry, or stop;

  • the measurement worker immediately performs safety actions such as RF-off.

A monitor therefore does not access MGraph or RF instruments directly. Manual intervention remains available alongside automatic monitoring. The complete event structure and assessment rules are documented separately.

Using existing measurement classes

MSC.Measure_Immunity and TEMCell.Measure_Immunity accept the same fundamental parameters:

eut_monitor

One automatic EUTMonitor or a sequence of monitors.

manual_eut_monitor

Optional manual monitor owned by a UI. When omitted, the measurement class creates a keyboard-backed fallback.

performance_criterion

Performance criterion "A", "B", or "C":

  • A: The EUT must operate as intended during and after exposure without unacceptable degradation.

  • B: Temporary degradation during exposure is permitted. Afterwards, the EUT must recover normal operation automatically; its operating mode and stored data must not be lost.

  • C: Temporary loss of function is permitted. Normal operation may be restored automatically, by operator intervention, or by reset.

The applicable product standard may refine these general criteria or add further requirements.

post_exposure_timeout

Observation time after RF-off. This is particularly relevant when assessing recovery under criterion B or C.

eut_event_policy

Application decision for degraded, failed, and not_evaluated. The safety_action="rf_off" request is handled independently of this policy.

Use a reproducible virtual monitor for the first run:

from mpylab.env.eut import VirtualEUTMonitor

monitor = VirtualEUTMonitor([{
    "status": "degraded",
    "reason": "virtual_output_tolerance",
    "safety_action": "none",
}])

status = measurement.Measure_Immunity(
    description="EUT",
    eut_monitor=monitor,
    performance_criterion="B",
    post_exposure_timeout=10.0,
    eut_event_policy={
        "degraded": "continue",
        "failed": "stop",
        "not_evaluated": "stop",
        "max_retries": 0,
    },
    # further method-specific parameters
)

The MSC threshold search receives its monitor in the ImmunityThresholdKernel parameter block. Qt applications pass their ManualEUTMonitor and automatic monitors to the measurement class. TEMField provides set_automatic_eut_monitor for this purpose. See the MSC and TEM guides for their complete workflows.

Developing a specific monitor

An application-specific monitor derives from EUTMonitor. Typical sources include camera images, communication responses, digital states, or process measurements. poll_event must be non-blocking or have a clearly bounded runtime:

from mpylab.env.eut import EUTMonitor, make_eut_event

class CommunicationEUTMonitor(EUTMonitor):
    def start_exposure(self, context):
        self.frequency = context.get("frequency")
        self.start_observation()

    def poll_event(self):
        observation = self.read_status_nonblocking()
        if observation is None:
            return None
        if observation == "ok":
            return make_eut_event(
                "passed",
                "communication_ok",
                source="eut_link",
            )
        return make_eut_event(
            "failed",
            "communication_lost",
            details={"frequency": self.frequency},
            safety_action="rf_off",
            source="eut_link",
        )

    def stop_exposure(self):
        self.stop_observation()

    def close(self):
        self.close_connection()

Wrap instrument access with significant latency:

from mpylab.env.eut import ThreadedEUTMonitor

monitor = ThreadedEUTMonitor(
    CommunicationEUTMonitor(),
    poll_interval=0.02,
    join_timeout=1.0,
)

The wrapped poll_event call must still have a finite maximum runtime so that the thread can terminate safely. Failure of an automatic monitor is stored as monitor_diagnostic and must not be reported as an observed EUT failure.

Integrating a new application

EUTMonitoringSession combines manual and automatic monitors and manages exposure lifecycle, phases, event validation, and diagnostics:

from mpylab.env.eut import EUTMonitoringSession, ManualEUTMonitor

manual = ManualEUTMonitor(poll_key=my_poll_key, keylist="sS")
session = EUTMonitoringSession.from_monitors(manual, [monitor])

try:
    session.start_exposure({"frequency": frequency, "position": position})
    event = session.poll_event()
    if event is not None and event["safety_action"] == "rf_off":
        measurement_worker.rf_off()  # immediate safety path

    session.start_phase(
        "post_exposure",
        {"frequency": frequency, "position": position, "rf_on": False},
    )
finally:
    session.close()

The session never switches RF itself. A new measurement application must perform safety actions in its worker, preserve monitor events in the measurement history, and call close from a finally block. RF-off must precede monitor cleanup that might fail.

Testing without hardware

  • VirtualEUTMonitor emits predefined events deterministically.

  • RandomEUTMonitor(seed=...) is useful for longer workflow and UI tests, but does not demonstrate real EUT behavior.

  • Tests must cover manual intervention, immediate RF-off, post-exposure, monitor diagnostics, and cleanup failures.

  • A virtual end-to-end run should use the same measurement-class parameters as the later hardware run.

The curated Python API documents every public monitor and session method. Thread and GUI rules are covered by UI adapters and worker interface.