Eigene Messklassen entwickeln ============================= Eine Messklasse beschreibt den fachlichen Ablauf einer Messung. Sie koordiniert MGraph, Geräte, Messpunkte, Autosave und Auswertung, enthält aber keine gerätespezifischen SCPI-Befehle und keine Qt-Widgets. Von Measure erben ----------------- .. code-block:: python from mpylab.env.Measure import Measure class ExampleMeasurement(Measure): def __init__(self, SearchPaths=None): super().__init__(SearchPaths=SearchPaths) self.raw_data = {} self.processed_data = {} ``Measure`` stellt Logging, Bedienerinteraktion, Autosave, Geräteinitialisierung, RF-Off und Pickle-Wiederherstellung bereit. Neue Klassen sollten diese Infrastruktur verwenden, statt eigene Varianten davon einzuführen. Messablauf ---------- Ein robuster Ablauf hat folgende Form: #. Datensatz und Wiederanlaufinformation vorbereiten. #. ``MGraph`` mit Namen- und Condition-Mapping erzeugen. #. Geräte erstellen und über ``_init_measurement_devices`` initialisieren. #. Den logischen Graph-Context vor jedem frequenzabhängigen Zugriff explizit anwenden. Für Graphen ohne physischen Controller genügt weiterhin die ausdrückliche Condition-Auswertung. #. Nur vollständig gemessene Punkte in die Ergebnisstruktur übernehmen. #. Regelmäßig ``do_autosave`` aufrufen. #. In einem ``finally``-Pfad RF abschalten und Geräte beenden. Die gemeinsame Finalisierung erfolgt über ``_finalize_measurement_devices``. Fehler beim Messen dürfen diesen Sicherheitspfad nicht überspringen. .. _de-measurement-graph-context: Expliziten Graph-Context anwenden --------------------------------- Eigene Messanwendungen sollten Frequenz, Betriebsart und vergleichbare fachliche Größen gemeinsam an ``MGraph.ApplyContext`` übergeben. Dadurch bleiben Condition-Auswertung, Frequenzeinstellung, physische Umschaltung und Rücklesekontrolle eine zusammenhängende Operation: .. code-block:: python from mpylab.tools.mgraph import ( GraphStateMismatchError, MGraph, TEM_CONDITION_MAP, ) graph = MGraph( dotfile, themap=names, SearchPaths=search_paths, condition_map=TEM_CONDITION_MAP, allow_legacy_condition_context=False, ) graph.CreateDevices() graph.Init_Devices() requested_context = {"frequency": frequency, "mode": "GTEM"} try: context_result = graph.ApplyContext(requested_context) except GraphStateMismatchError as error: # MGraph hat RF bereits abgeschaltet. Die Anwendung protokolliert # error.mismatches und fragt den Bediener nach einer Entscheidung. if not operator_explicitly_selected_reapply(error.mismatches): raise graph.ReapplyContext() context_result = graph.ApplyContext(requested_context) ``GraphStateMismatchError.mismatches`` enthält je Controller den erwarteten und den gelesenen Zustand sowie gegebenenfalls Status- oder Abfragefehler. Die Anwendung soll diese Information protokollieren und in ihrer CLI oder Oberfläche neutral anzeigen. Sie darf keinen gerätespezifischen Relaiszustand interpretieren; diese Fachkenntnis bleibt im Controller-Treiber. Bei einer Abweichung hat ``MGraph`` bereits graphweit ``RFOff`` ausgeführt. Abbrechen ist deshalb die sichere Standardentscheidung. Nur eine ausdrückliche Bedienerwahl darf zuerst mit ``ReapplyContext`` den zuletzt geplanten Zustand wiederherstellen. Anschließend wird der ursprünglich angeforderte Context erneut angewendet. Ein bloßes Wiederholen von ``ApplyContext`` ist absichtlich nicht ausreichend, weil die externe Schaltaktion sonst unbemerkt überschrieben werden könnte. ``CheckContextState`` erzwingt eine sofortige Kontrolle; ``PollContextState`` begrenzt wiederholte Abfragen zeitlich. ``RFOn_Devices`` prüft immer unmittelbar vor dem Einschalten. ``Read`` und ``NBRead`` binden eine zeitlich begrenzte Kontrolle in Messschleifen ein. Das ersetzt keinen asynchronen Hardware-Watchdog, erkennt externe Eingriffe aber an den relevanten Programmgrenzen. Die erforderlichen DOT-Attribute und der Treibervertrag sind in der :doc:`DOT-Referenz <../configuration/dot>` beschrieben. Die kuratierte :doc:`MGraph-API <../../api/graph>` enthält Rückgabewert und Exceptions. Autosave- und Resume-Schema --------------------------- Vor dem ersten Autosave setzt die Routine eine strukturierte Wiederanlaufinformation: .. code-block:: python self.set_autosave_resume( measurement="example", method="Measure_Example", description=description, ) Die Messroutine muss vorhandene Daten beim Wiederanlauf erkennen und am ersten unvollständigen Punkt fortsetzen. Der zugehörige Test prüft daher Aufrufzahlen und Ergebnisdaten, nicht nur die Lesbarkeit des Pickles. Datenstruktur ------------- Frequenzabhängige Daten werden bevorzugt als ``dataset[frequency] = quantity`` oder in einer klar dokumentierten, gleichwertigen Struktur abgelegt. Rohdaten, Auswertedaten und Metadaten bleiben getrennt. Referenzen auf vorbereitende Messungen werden mit Datensatzschlüssel und Quelle gespeichert, damit Reports die Rückführbarkeit ohne Neuberechnung darstellen können. Auswertung ---------- Messung und Auswertung sind getrennte Methoden. Dadurch lassen sich Auswertealgorithmen erneut auf ein ``after-measure``-Pickle anwenden, ohne Hardware anzusprechen. Eine Auswertung verändert keine Rohdaten und dokumentiert verwendete Referenzdatensätze sowie relevante Parameter.