From 5f558e4f03c710a13ed93d1c60d7aa422be7661a Mon Sep 17 00:00:00 2001 From: Markus Rosenstihl Date: Fri, 23 Oct 2026 11:11:18 +0200 Subject: [PATCH] make temperature proxy host and port configurable in the configuration tab --- doc/manual.rst | 5 ++-- src/damaris/gui/DamarisGUI.py | 34 +++++++++++++++++++++-- src/damaris/gui/TemperatureProxyClient.py | 17 ++++++++---- tests/test_TemperatureProxyClient.py | 18 ++++++++++-- 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index a7b3f7f..6acce01 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -51,12 +51,13 @@ Following is a short description of the options: 10. Filename of the data pool file 11. Write interval after which the data pool is rewritten 12. Compression settings -13. Authors, License and version numbers of used components +13. Host and port of the temperature proxy service +14. Authors, License and version numbers of used components Temperature History =================== -DAMARIS automatically retrieves one temperature history for the complete experiment from the local ``tempproxy`` service at ``http://localhost:8000/v1/readings`` after the experiment finishes. The proxy controls the temperature device; no controller, serial device, or polling interval needs to be configured in DAMARIS. Successful readings are stored under the ``temperature`` DataPool key with elapsed seconds on the x-axis and the proxy's ``pv`` values unchanged on the y-axis, so units depend on the configured controller. If the proxy is unavailable or has no readings for the experiment, the history is omitted and the experiment's other data is still saved. +DAMARIS automatically retrieves one temperature history for the complete experiment from the ``tempproxy`` service at ``http://:/v1/readings`` after the experiment finishes. Host and port are configured in the Configuration tab, the defaults are ``127.0.0.1`` and ``18007``. The proxy controls the temperature device; no controller, serial device, or polling interval needs to be configured in DAMARIS. Successful readings are stored under the ``temperature`` DataPool key with elapsed seconds on the x-axis and the proxy's ``pv`` values unchanged on the y-axis, so units depend on the configured controller. If the proxy is unavailable or has no readings for the experiment, the history is omitted and the experiment's other data is still saved. Experiment Script ================= diff --git a/src/damaris/gui/DamarisGUI.py b/src/damaris/gui/DamarisGUI.py index 0551294..f9041b0 100644 --- a/src/damaris/gui/DamarisGUI.py +++ b/src/damaris/gui/DamarisGUI.py @@ -871,7 +871,10 @@ class DamarisGUI: temperature_history_start = self.temperature_history_start self.temperature_history_start = None self.data.pop("temperature", None) - temperature = TemperatureProxyClient().get_history( + proxy_config = self.config.get( ) + temperature = TemperatureProxyClient( + host=proxy_config["tempproxy_host"], + port=proxy_config["tempproxy_port"]).get_history( temperature_history_start, time.time()) if temperature is not None: self.data["temperature"] = temperature @@ -2379,6 +2382,27 @@ class ConfigTab: backend_vbox.pack_start(self.config_adc_bit_depth_hbox, False, False, 0) self.config_adc_bit_depth_hbox.show_all() + # temperature proxy settings + self.config_tempproxy_hbox = gtk.Box(orientation=gtk.Orientation.HORIZONTAL, spacing=6) + self.config_tempproxy_host_label = gtk.Label(label="Temp proxy host:") + self.config_tempproxy_host_entry = gtk.Entry() + self.config_tempproxy_host_entry.set_text("127.0.0.1") + self.config_tempproxy_host_entry.set_tooltip_text("Host of the tempproxy service reading the temperature controller") + self.config_tempproxy_host_entry.connect("key-press-event", self.config_keypress) + self.config_tempproxy_port_label = gtk.Label(label="port:") + self.config_tempproxy_port_spinbutton = gtk.SpinButton.new_with_range(1, 65535, 1) + self.config_tempproxy_port_spinbutton.set_value(18007) + self.config_tempproxy_port_spinbutton.set_tooltip_text("Port of the tempproxy service, /v1/readings is requested there") + self.config_tempproxy_port_spinbutton.connect("key-press-event", self.config_keypress) + self.config_tempproxy_hbox.pack_start(self.config_tempproxy_host_label, False, False, 0) + self.config_tempproxy_hbox.pack_start(self.config_tempproxy_host_entry, False, False, 0) + self.config_tempproxy_hbox.pack_start(self.config_tempproxy_port_label, False, False, 0) + self.config_tempproxy_hbox.pack_start(self.config_tempproxy_port_spinbutton, False, False, 0) + frontend_vbox = self.xml_gui.get_object("vbox7") + if frontend_vbox: + frontend_vbox.pack_start(self.config_tempproxy_hbox, False, False, 0) + self.config_tempproxy_hbox.show_all() + # insert version informations components_text = """ operating system %(os)s @@ -2485,7 +2509,9 @@ pygobject version %(pygobject)s "data_pool_complib": complib, "data_pool_comprate": self.config_data_pool_comprate.get_value_as_int( ), "script_font": self.config_script_font_button.get_font_name( ), - "adc_bit_depth": self.config_adc_bit_depth_spinbutton.get_value_as_int() + "adc_bit_depth": self.config_adc_bit_depth_spinbutton.get_value_as_int(), + "tempproxy_host": self.config_tempproxy_host_entry.get_text( ), + "tempproxy_port": self.config_tempproxy_port_spinbutton.get_value_as_int( ) } ADC_Result.default_bit_depth = actual_config["adc_bit_depth"] return actual_config @@ -2520,6 +2546,10 @@ pygobject version %(pygobject)s ADC_Result.default_bit_depth = int(config["adc_bit_depth"]) except (ValueError, TypeError): pass + if "tempproxy_host" in config: + self.config_tempproxy_host_entry.set_text( str( config[ "tempproxy_host" ] ) ) + if "tempproxy_port" in config: + self.config_tempproxy_port_spinbutton.set_value( float( config[ "tempproxy_port" ] ) ) if "data_pool_complib" in config: # find combo-box entry and make it active... diff --git a/src/damaris/gui/TemperatureProxyClient.py b/src/damaris/gui/TemperatureProxyClient.py index d94473b..0361f34 100644 --- a/src/damaris/gui/TemperatureProxyClient.py +++ b/src/damaris/gui/TemperatureProxyClient.py @@ -1,7 +1,7 @@ -"""Read experiment temperature history from the local tempproxy service. +"""Read experiment temperature history from the tempproxy service. Example: - client = TemperatureProxyClient() + client = TemperatureProxyClient() # defaults to http://127.0.0.1:18007 temperature = client.get_history(start_epoch, end_epoch) """ @@ -18,11 +18,18 @@ logger = logging.getLogger(__name__) class TemperatureProxyClient: - """Retrieve experiment-bounded readings from ``localhost:8000``.""" + """Retrieve experiment-bounded readings from a configurable tempproxy host/port.""" - URL = "http://localhost:8000/v1/readings" TIMEOUT = 5 + def __init__(self, host: str = "127.0.0.1", port: int = 18007): + """Create a client for ``http://:/v1/readings``. + + Example: + client = TemperatureProxyClient(host="192.168.1.5", port=18007) + """ + self.url = f"http://{host}:{port}/v1/readings" + def get_history(self, start_epoch: float, end_epoch: float) -> TemperatureResult | None: """Return temperature history relative to the experiment start, or ``None``. @@ -33,7 +40,7 @@ class TemperatureProxyClient: temperature = TemperatureProxyClient().get_history(1000.0, 1060.0) """ query = urlencode({"start": start_epoch, "end": end_epoch}) - url = f"{self.URL}?{query}" + url = f"{self.url}?{query}" try: with urlopen(url, timeout=self.TIMEOUT) as response: diff --git a/tests/test_TemperatureProxyClient.py b/tests/test_TemperatureProxyClient.py index 2e1a8b7..888d054 100644 --- a/tests/test_TemperatureProxyClient.py +++ b/tests/test_TemperatureProxyClient.py @@ -22,7 +22,7 @@ class TestTemperatureProxyClient(unittest.TestCase): return io.BytesIO(json.dumps(payload).encode("utf-8")) @patch("damaris.gui.TemperatureProxyClient.urlopen") - def test_get_history_uses_fixed_endpoint_and_converts_readings(self, mock_urlopen): + def test_get_history_uses_default_endpoint_and_converts_readings(self, mock_urlopen): mock_urlopen.return_value = self.response({ "readings": [ {"t": 101.5, "pv": 293.15}, @@ -35,13 +35,25 @@ class TestTemperatureProxyClient(unittest.TestCase): args, kwargs = mock_urlopen.call_args parsed_url = urlparse(args[0]) self.assertEqual(parsed_url.scheme, "http") - self.assertEqual(parsed_url.netloc, "localhost:8000") + self.assertEqual(parsed_url.netloc, "127.0.0.1:18007") self.assertEqual(parsed_url.path, "/v1/readings") self.assertEqual(parse_qs(parsed_url.query), {"start": ["100.0"], "end": ["110.0"]}) self.assertEqual(kwargs, {"timeout": TemperatureProxyClient.TIMEOUT}) self.assertEqual(result.x, [1.5, 4.0]) self.assertEqual(result.y, [293.15, 294.25]) + @patch("damaris.gui.TemperatureProxyClient.urlopen") + def test_custom_host_and_port_are_used(self, mock_urlopen): + mock_urlopen.return_value = self.response({"readings": [{"t": 101.0, "pv": 293.0}]}) + + client = TemperatureProxyClient(host="proxy.example.org", port=9000) + client.get_history(100.0, 110.0) + + args, _ = mock_urlopen.call_args + parsed_url = urlparse(args[0]) + self.assertEqual(parsed_url.netloc, "proxy.example.org:9000") + self.assertEqual(parsed_url.path, "/v1/readings") + @patch("damaris.gui.TemperatureProxyClient.urlopen") def test_empty_readings_return_none(self, mock_urlopen): mock_urlopen.return_value = self.response({"readings": []}) @@ -62,7 +74,7 @@ class TestTemperatureProxyClient(unittest.TestCase): OSError("connection refused"), TimeoutError("request timed out"), http.client.BadStatusLine("not an HTTP response"), - HTTPError(TemperatureProxyClient.URL, 500, "server error", {}, None), + HTTPError(self.client.url, 500, "server error", {}, None), ) for failure in failures: with self.subTest(failure=failure):