From 4be236b55b6b6eaa7064b9c2170d053a2211f4fe Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Wed, 16 Mar 2022 12:12:54 -0600 Subject: [PATCH 1/6] Adds support for reading a web relay sensor value. Removes ?relay from base_url inclusion and adds where necessary. Modifies default config to match SEA preselector config. Increment version to 2.0.1. --- README.md | 4 ++-- config/config.json | 7 +++---- setup.cfg | 4 ++-- src/its_preselector/preselector.py | 4 +++- src/its_preselector/web_relay_preselector.py | 12 +++++++++++- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 240856b..d4266b0 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,11 @@ To install this Python package, clone the repository and enter the directory of ``` Windows: py –m build -py -m pip install dist/its-preselector-2.0.0.tar.gz +py -m pip install dist/its-preselector-2.0.1.tar.gz Linux: python3 -m build -Python3 –m pip install dist/its-preselector-2.0.0.tar.gz +Python3 –m pip install dist/its-preselector-2.0.1.tar.gz ``` # WebRelayPreselector Configuration diff --git a/config/config.json b/config/config.json index 81adf82..f3f966e 100644 --- a/config/config.json +++ b/config/config.json @@ -1,9 +1,8 @@ { - "base_url" : "http://192.168.130.32/state.xml?relay", + "base_url" : "http://192.168.1.2/state.xml", "noise_diode_on" : "1State=1,2State=1,3State=0,4State=0", - "noise_diode_off" : "1State=0,2State=1,3State=0,4State=0", - "antenna" : "1State=0,2State=0,3State=0,4State=0", - "1" : "1State=0,2State=1,3State=0,4State=0" + "noise_diode_off" : "1State=1,2State=0,3State=0,4State=0", + "antenna" : "1State=0,2State=0,3State=0,4State=0" } diff --git a/setup.cfg b/setup.cfg index 32a8e69..18262e5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = its-preselector -version = 2.0.0 +version = 2.0.1 author = ITS author_email = dboulware@ntia.gov description = A package to control the ITS web relay based preselector @@ -19,7 +19,7 @@ package_dir = = src packages = its_preselector -python_requires = >=3.6 +python_requires = >=3.7 install_requires = requests>=2.25.1 diff --git a/src/its_preselector/preselector.py b/src/its_preselector/preselector.py index 46cb18c..e44f255 100644 --- a/src/its_preselector/preselector.py +++ b/src/its_preselector/preselector.py @@ -135,5 +135,7 @@ def __get_amplifier(self, amp_id): return None - + @abstractmethod + def get_sensor_value(sensor): + pass diff --git a/src/its_preselector/web_relay_preselector.py b/src/its_preselector/web_relay_preselector.py index c48a8f1..aad542c 100644 --- a/src/its_preselector/web_relay_preselector.py +++ b/src/its_preselector/web_relay_preselector.py @@ -1,5 +1,6 @@ from its_preselector.preselector import Preselector import requests +import xml.etree.ElementTree as ET class WebRelayPreselector(Preselector): @@ -15,8 +16,17 @@ def set_rf_path(self, i): switches = self.config[str(i)].split(',') if self.base_url: for i in range(len(switches)): - command = self.base_url + switches[i] + command = self.base_url + '?relay'+ switches[i] print(command) requests.get(command) else: raise Exception("RF path " + key + " configuration does not exist.") + + + def get_sensor_value(self, sensor_num): + sensor_num_string = str(sensor_num) + response = requests.get(self.base_url + '?sensor' +sensor_num_string) + sensor_tag = 'sensor' + sensor_num_string + root = ET.fromstring(response.text) + sensor = root.find(sensor_tag) + return sensor.text From 77b68e36bafb77c715b08fca9baca6b997edf7d8 Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Wed, 16 Mar 2022 12:57:27 -0600 Subject: [PATCH 2/6] config update in readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4266b0..e5d3c12 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The WebRelayPreselector requires a [SigMF metadata file](https://Github.com/NTIA metadata and for any other desired sources. Below is an example config file for the WebRelayPreselector to describe how it works: ``` { - "base_url" : "http://192.168.130.32/state.xml?relay", + "base_url" : "http://192.168.130.32/state.xml", "noise_diode_on" : "1State=1,2State=1,3State=0,4State=0", "noise_diode_off" : "1State=0,2State=1,3State=0,4State=0", "antenna" : "1State=0,2State=0,3State=0,4State=0" From ff1a69c422ad27c0fd6ab632255c31e563328a16 Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Thu, 14 Apr 2022 13:26:02 -0600 Subject: [PATCH 3/6] Renames set_rf_path to set_state. Adds healthy method, and raises Exception if blank_url is None or blank in set_state or if the get request fails. --- README.md | 6 ++-- src/its_preselector/preselector.py | 2 +- .../test/test_web_relay_preselector.py | 36 +++++++++++++++++++ src/its_preselector/web_relay_preselector.py | 19 ++++++++-- 4 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/its_preselector/test/test_web_relay_preselector.py diff --git a/README.md b/README.md index 42f01be..465d8e3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ regardless of their components and control mechanisms. This software will grow over time to support additional components and control mechanisms. Currently, this API provides a general abstract Preselector class that uses an rf_path array to describe the available combinations of calibration sources, filters, -and amplifiers. A simple set_rf_path method allows users to specify the rf path by index or name. +and amplifiers. A simple set_state method allows users to specify the state of the preselector +by the state key specified in the preselector config. Finally, different switching control mechanisms are supported by extending the base Preseelctor class. Currently, this repository provides an implementation for a WebRelayPreselector that includes an [x310 WebRelay](https://www.controlbyweb.com/x310/). See below for additional details on using the WebRelayPreslector. @@ -62,6 +63,7 @@ with open('config/config.json') as config_file: preselector_config = json.load(config_file) preselector = WebRelayPreselector(sensor_def, preselector_config) +preselector.set_state('antenna') ``` # Preselector Interactions @@ -84,7 +86,7 @@ preselector = WebRelayPreselector(sensor_def, preselector_config) ## Control:
    -
  • preselector.set_rf_path(rf_path_name)
  • +
  • preselector.set_state(rf_path_name)
# License diff --git a/src/its_preselector/preselector.py b/src/its_preselector/preselector.py index e44f255..9dded99 100644 --- a/src/its_preselector/preselector.py +++ b/src/its_preselector/preselector.py @@ -116,7 +116,7 @@ def get_amplifier_noise_figure(self, rf_path_index): return None @abstractmethod - def set_rf_path(self, i): + def set_state(self, i): pass def __get_filter(self, filter_id): diff --git a/src/its_preselector/test/test_web_relay_preselector.py b/src/its_preselector/test/test_web_relay_preselector.py new file mode 100644 index 0000000..746b57d --- /dev/null +++ b/src/its_preselector/test/test_web_relay_preselector.py @@ -0,0 +1,36 @@ +import unittest +import json + +from its_preselector.web_relay_preselector import WebRelayPreselector + + +class MyTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + file = open('test_metadata.sigmf-meta') + cls.sensor_def = json.load(file) + file.close() + + def test_blank_base_url(self): + preselector = WebRelayPreselector(self.sensor_def, {'base_url': '', 'antenna' : '1State=0,2State=0,3State=0,4State=0' }) + with self.assertRaises(Exception): + preselector.set_state('antenna') + + def test_none_base_url(self): + preselector = WebRelayPreselector(self.sensor_def, {'base_url': None, 'antenna' : '1State=0,2State=0,3State=0,4State=0' }) + with self.assertRaises(Exception): + preselector.set_state('antenna') + + def test_invalid_base_url(self): + preselector = WebRelayPreselector(self.sensor_def, {'base_url': 'http://badpreselector.gov', 'antenna' : '1State=0,2State=0,3State=0,4State=0' }) + with self.assertRaises(Exception): + preselector.set_state('antenna') + + def test_healthy_false(self): + preselector = WebRelayPreselector(self.sensor_def, {'base_url': 'http://bad_preselector.gov', + 'antenna': '1State=0,2State=0,3State=0,4State=0'}) + self.assertFalse(preselector.healthy()) + +if __name__ == '__main__': + unittest.main() diff --git a/src/its_preselector/web_relay_preselector.py b/src/its_preselector/web_relay_preselector.py index aad542c..ea9dc92 100644 --- a/src/its_preselector/web_relay_preselector.py +++ b/src/its_preselector/web_relay_preselector.py @@ -1,7 +1,9 @@ +import logging from its_preselector.preselector import Preselector import requests import xml.etree.ElementTree as ET +logger = logging.getLogger(__name__) class WebRelayPreselector(Preselector): @@ -10,15 +12,19 @@ def __init__(self, sigmf, config): if 'base_url' in config: self.base_url = config['base_url'] - def set_rf_path(self, i): + def set_state(self, i): key = str(i) if key in self.config: switches = self.config[str(i)].split(',') - if self.base_url: + if self.base_url and self.base_url != '': for i in range(len(switches)): command = self.base_url + '?relay'+ switches[i] print(command) - requests.get(command) + response = requests.get(command) + if response.status_code != requests.codes.ok: + raise Exception('Unable to set preselector state. Verify configuration and connectivity.') + else: + raise Exception('base_url is None or blank') else: raise Exception("RF path " + key + " configuration does not exist.") @@ -30,3 +36,10 @@ def get_sensor_value(self, sensor_num): root = ET.fromstring(response.text) sensor = root.find(sensor_tag) return sensor.text + + def healthy(self): + try: + response = requests.get(self.base_url) + return response.status_code == requests.codes.ok + except: + logger.error("Unable to connect to preselector") \ No newline at end of file From 90c397d8109a7ad33cd6b98c03d253c77bcba51d Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Fri, 22 Apr 2022 08:28:40 -0600 Subject: [PATCH 4/6] Return False from healthy if unhealthy. --- src/its_preselector/web_relay_preselector.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/its_preselector/web_relay_preselector.py b/src/its_preselector/web_relay_preselector.py index ea9dc92..d7bf79b 100644 --- a/src/its_preselector/web_relay_preselector.py +++ b/src/its_preselector/web_relay_preselector.py @@ -42,4 +42,5 @@ def healthy(self): response = requests.get(self.base_url) return response.status_code == requests.codes.ok except: - logger.error("Unable to connect to preselector") \ No newline at end of file + logger.error("Unable to connect to preselector") + return False \ No newline at end of file From e39a6cd295f1ce5ae7fec34627c5c8d355dc12c1 Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Fri, 22 Apr 2022 08:30:21 -0600 Subject: [PATCH 5/6] format --- src/its_preselector/web_relay_preselector.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/its_preselector/web_relay_preselector.py b/src/its_preselector/web_relay_preselector.py index d7bf79b..19f76c0 100644 --- a/src/its_preselector/web_relay_preselector.py +++ b/src/its_preselector/web_relay_preselector.py @@ -5,6 +5,7 @@ logger = logging.getLogger(__name__) + class WebRelayPreselector(Preselector): def __init__(self, sigmf, config): @@ -18,7 +19,7 @@ def set_state(self, i): switches = self.config[str(i)].split(',') if self.base_url and self.base_url != '': for i in range(len(switches)): - command = self.base_url + '?relay'+ switches[i] + command = self.base_url + '?relay' + switches[i] print(command) response = requests.get(command) if response.status_code != requests.codes.ok: @@ -28,10 +29,9 @@ def set_state(self, i): else: raise Exception("RF path " + key + " configuration does not exist.") - def get_sensor_value(self, sensor_num): - sensor_num_string = str(sensor_num) - response = requests.get(self.base_url + '?sensor' +sensor_num_string) + sensor_num_string = str(sensor_num) + response = requests.get(self.base_url + '?sensor' + sensor_num_string) sensor_tag = 'sensor' + sensor_num_string root = ET.fromstring(response.text) sensor = root.find(sensor_tag) @@ -43,4 +43,4 @@ def healthy(self): return response.status_code == requests.codes.ok except: logger.error("Unable to connect to preselector") - return False \ No newline at end of file + return False From 8d65307b628e18d3416bb2d58018b9c8259f205c Mon Sep 17 00:00:00 2001 From: Doug Boulware Date: Fri, 22 Apr 2022 09:00:31 -0600 Subject: [PATCH 6/6] changed print to logger.debug --- src/its_preselector/web_relay_preselector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/its_preselector/web_relay_preselector.py b/src/its_preselector/web_relay_preselector.py index 19f76c0..fa23ffc 100644 --- a/src/its_preselector/web_relay_preselector.py +++ b/src/its_preselector/web_relay_preselector.py @@ -20,7 +20,7 @@ def set_state(self, i): if self.base_url and self.base_url != '': for i in range(len(switches)): command = self.base_url + '?relay' + switches[i] - print(command) + logger.debug(command) response = requests.get(command) if response.status_code != requests.codes.ok: raise Exception('Unable to set preselector state. Verify configuration and connectivity.')