From 1d082c28c332ae25b9ed372d1dee8040ccc1ebc1 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 5 Aug 2021 15:05:55 +0200 Subject: [PATCH 1/7] Reorganizing miniconf, adding loopback test --- hitl/loopback.py | 148 ++++++++++++++++++ miniconf-py/README.md | 6 + miniconf-py/miniconf.egg-info/PKG-INFO | 10 ++ miniconf-py/miniconf.egg-info/SOURCES.txt | 8 + .../miniconf.egg-info/dependency_links.txt | 1 + miniconf-py/miniconf.egg-info/requires.txt | 1 + miniconf-py/miniconf.egg-info/top_level.txt | 1 + miniconf-py/miniconf/__init__.py | 4 + miniconf-py/miniconf/__main__.py | 53 +++++++ .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 233 bytes .../__pycache__/miniconf.cpython-38.pyc | Bin 0 -> 4837 bytes .../miniconf/miniconf.py | 52 +----- miniconf-py/setup.py | 14 ++ 13 files changed, 247 insertions(+), 51 deletions(-) create mode 100644 hitl/loopback.py create mode 100644 miniconf-py/README.md create mode 100644 miniconf-py/miniconf.egg-info/PKG-INFO create mode 100644 miniconf-py/miniconf.egg-info/SOURCES.txt create mode 100644 miniconf-py/miniconf.egg-info/dependency_links.txt create mode 100644 miniconf-py/miniconf.egg-info/requires.txt create mode 100644 miniconf-py/miniconf.egg-info/top_level.txt create mode 100644 miniconf-py/miniconf/__init__.py create mode 100644 miniconf-py/miniconf/__main__.py create mode 100644 miniconf-py/miniconf/__pycache__/__init__.cpython-38.pyc create mode 100644 miniconf-py/miniconf/__pycache__/miniconf.cpython-38.pyc rename miniconf.py => miniconf-py/miniconf/miniconf.py (60%) create mode 100644 miniconf-py/setup.py diff --git a/hitl/loopback.py b/hitl/loopback.py new file mode 100644 index 0000000..df768be --- /dev/null +++ b/hitl/loopback.py @@ -0,0 +1,148 @@ +#!/usr/bin/python3 +""" +Author: Vertigo Designs, Ryan Summers + +Description: Loop-back integration tests for Stabilizer hardware +""" +import argparse +import asyncio +import json +import sys + +from gmqtt import Client as MqttClient +from miniconf import Miniconf + +# The minimum allowably loopback voltage error (difference between output set point and input +# measured value). +MINIMUM_VOLTAGE_ERROR = 0.010 + +def _voltage_to_machine_units(voltage): + """ Convert a voltage to IIR machine units. + + Args: + voltage: The voltage to convert + + Returns: + The IIR machine-units associated with the voltage. + """ + dac_range = 4.096 * 2.5 + assert abs(voltage) <= dac_range, 'Voltage out-of-range' + return voltage / dac_range * 0x7FFF + + +def static_iir_output(output_voltage): + """ Generate IIR configuration for a static output voltage. + + Args: + output_voltage: The desired static IIR output voltage. + + Returns + The IIR configuration to send over Miniconf. + """ + machine_units = _voltage_to_machine_units(output_voltage) + return { + 'y_min': machine_units, + 'y_max': machine_units, + 'y_offset': 0, + 'ba': [1, 0, 0, 0, 0], + } + + +class TelemetryReader: + """ Helper utility to read Stabilizer telemetry. """ + + @classmethod + async def create(cls, prefix, broker): + """Create a connection to the broker and an MQTT device using it.""" + client = MqttClient(client_id='') + await client.connect(broker) + return cls(client, prefix) + + + def __init__(self, client, prefix): + """ Constructor. """ + self.client = client + self._telemetry = [] + self.client.on_message = self.handle_telemetry + self._telemetry_topic = f'{prefix}/telemetry' + self.client.subscribe(self._telemetry_topic) + + + def handle_telemetry(self, _client, topic, payload, _qos, _properties): + """ Handle incoming telemetry messages over MQTT. """ + assert topic == self._telemetry_topic + self._telemetry.append(json.loads(payload)) + + + def get_latest(self): + """ Get the latest telemetry message received. """ + return self._telemetry[-1] + + +async def test_loopback(miniconf, telemetry, set_point): + """ Test loopback operation of Stabilizer. + + Note: + Loopback is tested by configuring DACs for static output and verifying telemetry reports the + ADCs are measuring those values. Output OUTx should be connected in a loopback configuration + to INx on the device. + + Args: + miniconf: The miniconf configuration interface. + telemetry: a helper utility to read inbound telemetry. + set_point: The desired output voltage to test. + """ + print(f'Testing loopback for Vout = {set_point:.2f}') + print('---------------------------------') + # Configure the IIRs to output at the set point + assert (await miniconf.command('iir_ch/0/0', static_iir_output(set_point)))['code'] == 0 + assert (await miniconf.command('iir_ch/1/0', static_iir_output(set_point)))['code'] == 0 + assert (await miniconf.command('telemetry_period', 1))['code'] == 0 + + # Wait for telemetry values to update. + await asyncio.sleep(2.0) + + # Verify the ADCs are receiving the setpoint voltage. + tolerance = max(0.05 * set_point, MINIMUM_VOLTAGE_ERROR) + latest_values = telemetry.get_latest() + print(f'Latest telemtry: {latest_values}') + + assert abs(latest_values['adcs'][0] - set_point) < tolerance + assert abs(latest_values['adcs'][1] - set_point) < tolerance + print('PASS') + print('') + + +def main(): + """ Main program entry point. """ + parser = argparse.ArgumentParser(description='Loopback tests for Stabilizer HITL testing',) + parser.add_argument('prefix', type=str, + help='The MQTT topic prefix of the target') + parser.add_argument('--broker', '-b', default='mqtt', type=str, + help='The MQTT broker address') + + args = parser.parse_args() + + async def test(): + """ The actual testing being completed. """ + interface = await Miniconf.create(args.prefix, args.broker) + telemetry = await TelemetryReader.create(args.prefix, args.broker) + + # Test loopback with a static 1V output of the DACs. + await test_loopback(interface, telemetry, 1.0) + + # Repeat test with AFE = 2x + print('Configuring AFEs to 2x input') + assert (await interface.command('afe/0', "G2"))['code'] == 0 + assert (await interface.command('afe/1', "G2"))['code'] == 0 + await test_loopback(interface, telemetry, 1.0) + + # Test with 0V output + await test_loopback(interface, telemetry, 0.0) + + loop = asyncio.get_event_loop() + sys.exit(loop.run_until_complete(test())) + + +if __name__ == '__main__': + main() diff --git a/miniconf-py/README.md b/miniconf-py/README.md new file mode 100644 index 0000000..ebfa798 --- /dev/null +++ b/miniconf-py/README.md @@ -0,0 +1,6 @@ +# Miniconf Python Utility + +This directory contains a Python package for interacting with Miniconf utilities. + +## Installation +Run `pip install .` from this directory to install the `miniconf` package. diff --git a/miniconf-py/miniconf.egg-info/PKG-INFO b/miniconf-py/miniconf.egg-info/PKG-INFO new file mode 100644 index 0000000..5d1def7 --- /dev/null +++ b/miniconf-py/miniconf.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: miniconf +Version: 1.0.0 +Summary: Utilities for configuring Miniconf-configurable devices +Home-page: https://github.com/quartiq/miniconf +Author: Ryan Summers, Robert Jördens +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/miniconf-py/miniconf.egg-info/SOURCES.txt b/miniconf-py/miniconf.egg-info/SOURCES.txt new file mode 100644 index 0000000..76eb2ab --- /dev/null +++ b/miniconf-py/miniconf.egg-info/SOURCES.txt @@ -0,0 +1,8 @@ +setup.py +miniconf/__init__.py +miniconf/miniconf.py +miniconf.egg-info/PKG-INFO +miniconf.egg-info/SOURCES.txt +miniconf.egg-info/dependency_links.txt +miniconf.egg-info/requires.txt +miniconf.egg-info/top_level.txt \ No newline at end of file diff --git a/miniconf-py/miniconf.egg-info/dependency_links.txt b/miniconf-py/miniconf.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/miniconf-py/miniconf.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/miniconf-py/miniconf.egg-info/requires.txt b/miniconf-py/miniconf.egg-info/requires.txt new file mode 100644 index 0000000..a3d7fef --- /dev/null +++ b/miniconf-py/miniconf.egg-info/requires.txt @@ -0,0 +1 @@ +gmqtt diff --git a/miniconf-py/miniconf.egg-info/top_level.txt b/miniconf-py/miniconf.egg-info/top_level.txt new file mode 100644 index 0000000..6919ef8 --- /dev/null +++ b/miniconf-py/miniconf.egg-info/top_level.txt @@ -0,0 +1 @@ +miniconf diff --git a/miniconf-py/miniconf/__init__.py b/miniconf-py/miniconf/__init__.py new file mode 100644 index 0000000..1d47069 --- /dev/null +++ b/miniconf-py/miniconf/__init__.py @@ -0,0 +1,4 @@ +#!/usr/bin/python3 +from .miniconf import Miniconf + +__version__ = '1.0.0' diff --git a/miniconf-py/miniconf/__main__.py b/miniconf-py/miniconf/__main__.py new file mode 100644 index 0000000..fcc7078 --- /dev/null +++ b/miniconf-py/miniconf/__main__.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 + +import sys +import argparse +from .miniconf import Miniconf + + +def main(): + """ Main program entry point. """ + parser = argparse.ArgumentParser( + description='Miniconf command line interface.', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog='''Examples: +%(prog)s dt/sinara/dual-iir/00-11-22-33-aa-bb iir_ch/0/0=\ +'{"y_min":-32767,"y_max":32767,"y_offset":0,"ba":[1.0,0,0,0,0]}' +%(prog)s dt/sinara/lockin/00-11-22-33-aa-bb afe/0='"G2"'\ +''') + parser.add_argument('-v', '--verbose', action='count', default=0, + help='Increase logging verbosity') + parser.add_argument('--broker', '-b', default='mqtt', type=str, + help='The MQTT broker address') + parser.add_argument('--no-retain', '-n', default=False, + action='store_true', + help='Do not retain the affected settings') + parser.add_argument('prefix', type=str, + help='The MQTT topic prefix of the target') + parser.add_argument('settings', metavar="PATH=VALUE", nargs='+', + help='JSON encoded values for settings path keys.') + + args = parser.parse_args() + + logging.basicConfig( + format='%(asctime)s [%(levelname)s] %(name)s: %(message)s', + level=logging.WARN - 10*args.verbose) + + loop = asyncio.get_event_loop() + + async def configure_settings(): + interface = await Miniconf.create(args.prefix, args.broker) + for setting in args.settings: + path, value = setting.split("=", 1) + response = await interface.command(path, json.loads(value), + not args.no_retain) + print(f'{path}: {response}') + if response['code'] != 0: + return response['code'] + return 0 + + sys.exit(loop.run_until_complete(configure_settings())) + + +if __name__ == '__main__': + main() diff --git a/miniconf-py/miniconf/__pycache__/__init__.cpython-38.pyc b/miniconf-py/miniconf/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e3ce58f0aecb3d21ee2b058f44250db3e6184d9 GIT binary patch literal 233 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 r5KGiZ5MLM)u|EKO` Date: Thu, 5 Aug 2021 15:08:34 +0200 Subject: [PATCH 2/7] Cleaning up files --- .gitignore | 1 + hitl/run.sh | 7 +++++-- miniconf-py/.gitignore | 3 +++ miniconf-py/miniconf.egg-info/PKG-INFO | 10 ---------- miniconf-py/miniconf.egg-info/SOURCES.txt | 8 -------- .../miniconf.egg-info/dependency_links.txt | 1 - miniconf-py/miniconf.egg-info/requires.txt | 1 - miniconf-py/miniconf.egg-info/top_level.txt | 1 - .../miniconf/__pycache__/__init__.cpython-38.pyc | Bin 233 -> 0 bytes .../miniconf/__pycache__/miniconf.cpython-38.pyc | Bin 4837 -> 0 bytes 10 files changed, 9 insertions(+), 23 deletions(-) create mode 100644 miniconf-py/.gitignore delete mode 100644 miniconf-py/miniconf.egg-info/PKG-INFO delete mode 100644 miniconf-py/miniconf.egg-info/SOURCES.txt delete mode 100644 miniconf-py/miniconf.egg-info/dependency_links.txt delete mode 100644 miniconf-py/miniconf.egg-info/requires.txt delete mode 100644 miniconf-py/miniconf.egg-info/top_level.txt delete mode 100644 miniconf-py/miniconf/__pycache__/__init__.cpython-38.pyc delete mode 100644 miniconf-py/miniconf/__pycache__/miniconf.cpython-38.pyc diff --git a/.gitignore b/.gitignore index a6eef6e..03e9bf0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target docs/_site/ +**/*.pyc diff --git a/hitl/run.sh b/hitl/run.sh index 739b6ed..9064c13 100755 --- a/hitl/run.sh +++ b/hitl/run.sh @@ -27,6 +27,9 @@ sleep 30 ping -c 5 -w 20 stabilizer-hitl # Test the MQTT interface. -python3 miniconf.py dt/sinara/dual-iir/04-91-62-d9-7e-5f afe/0='"G2"' -python3 miniconf.py dt/sinara/dual-iir/04-91-62-d9-7e-5f afe/0='"G1"' iir_ch/0/0=\ +python3 -m miniconf dt/sinara/dual-iir/04-91-62-d9-7e-5f afe/0='"G2"' +python3 -m miniconf dt/sinara/dual-iir/04-91-62-d9-7e-5f afe/0='"G1"' iir_ch/0/0=\ '{"y_min": -32767, "y_max": 32767, "y_offset": 0, "ba": [1.0, 0, 0, 0, 0]}' + +# Test the ADC/DACs connected via loopback. +python3 hitl/loopback.py dt/sinara/dual-iir/04-91-62-d9-7e-5f diff --git a/miniconf-py/.gitignore b/miniconf-py/.gitignore new file mode 100644 index 0000000..ab7ceb0 --- /dev/null +++ b/miniconf-py/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +*.egg +*.egg-info/ diff --git a/miniconf-py/miniconf.egg-info/PKG-INFO b/miniconf-py/miniconf.egg-info/PKG-INFO deleted file mode 100644 index 5d1def7..0000000 --- a/miniconf-py/miniconf.egg-info/PKG-INFO +++ /dev/null @@ -1,10 +0,0 @@ -Metadata-Version: 1.0 -Name: miniconf -Version: 1.0.0 -Summary: Utilities for configuring Miniconf-configurable devices -Home-page: https://github.com/quartiq/miniconf -Author: Ryan Summers, Robert Jördens -Author-email: UNKNOWN -License: UNKNOWN -Description: UNKNOWN -Platform: UNKNOWN diff --git a/miniconf-py/miniconf.egg-info/SOURCES.txt b/miniconf-py/miniconf.egg-info/SOURCES.txt deleted file mode 100644 index 76eb2ab..0000000 --- a/miniconf-py/miniconf.egg-info/SOURCES.txt +++ /dev/null @@ -1,8 +0,0 @@ -setup.py -miniconf/__init__.py -miniconf/miniconf.py -miniconf.egg-info/PKG-INFO -miniconf.egg-info/SOURCES.txt -miniconf.egg-info/dependency_links.txt -miniconf.egg-info/requires.txt -miniconf.egg-info/top_level.txt \ No newline at end of file diff --git a/miniconf-py/miniconf.egg-info/dependency_links.txt b/miniconf-py/miniconf.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/miniconf-py/miniconf.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/miniconf-py/miniconf.egg-info/requires.txt b/miniconf-py/miniconf.egg-info/requires.txt deleted file mode 100644 index a3d7fef..0000000 --- a/miniconf-py/miniconf.egg-info/requires.txt +++ /dev/null @@ -1 +0,0 @@ -gmqtt diff --git a/miniconf-py/miniconf.egg-info/top_level.txt b/miniconf-py/miniconf.egg-info/top_level.txt deleted file mode 100644 index 6919ef8..0000000 --- a/miniconf-py/miniconf.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -miniconf diff --git a/miniconf-py/miniconf/__pycache__/__init__.cpython-38.pyc b/miniconf-py/miniconf/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 5e3ce58f0aecb3d21ee2b058f44250db3e6184d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233 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 r5KGiZ5MLM)u|EKO` Date: Thu, 5 Aug 2021 15:23:47 +0200 Subject: [PATCH 3/7] Updating run script --- .gitignore | 1 - hitl/run.sh | 2 +- requirements.txt | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 03e9bf0..a6eef6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target docs/_site/ -**/*.pyc diff --git a/hitl/run.sh b/hitl/run.sh index 9064c13..582c0f0 100755 --- a/hitl/run.sh +++ b/hitl/run.sh @@ -13,7 +13,7 @@ set -eux # Set up python for testing python3 -m venv --system-site-packages py . py/bin/activate -python3 -m pip install -r requirements.txt +python3 -m pip install miniconf-py/ cargo flash --chip STM32H743ZITx --elf target/thumbv7em-none-eabihf/release/dual-iir diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a3d7fef..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -gmqtt From 1b1a5052c23dd503b6bf3bd1a9af0be4208f994e Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 5 Aug 2021 15:43:00 +0200 Subject: [PATCH 4/7] Updating version definition --- miniconf-py/miniconf/__init__.py | 3 +-- miniconf-py/miniconf/version.py | 3 +++ miniconf-py/setup.py | 11 +++++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 miniconf-py/miniconf/version.py diff --git a/miniconf-py/miniconf/__init__.py b/miniconf-py/miniconf/__init__.py index 1d47069..227f258 100644 --- a/miniconf-py/miniconf/__init__.py +++ b/miniconf-py/miniconf/__init__.py @@ -1,4 +1,3 @@ #!/usr/bin/python3 from .miniconf import Miniconf - -__version__ = '1.0.0' +from .version import __version__ diff --git a/miniconf-py/miniconf/version.py b/miniconf-py/miniconf/version.py new file mode 100644 index 0000000..1ae5549 --- /dev/null +++ b/miniconf-py/miniconf/version.py @@ -0,0 +1,3 @@ +#!/usr/bin/python3 + +__version__ = '1.0.0' diff --git a/miniconf-py/setup.py b/miniconf-py/setup.py index 9e07048..220b3c3 100644 --- a/miniconf-py/setup.py +++ b/miniconf-py/setup.py @@ -1,9 +1,16 @@ +#!/usr/bin/python3 +""" +Author: Vertigo Designs, Ryan Summers + +Description: Setup file for Miniconf packaging. +""" from setuptools import setup, find_packages -import miniconf +# Load the version string from the version file. +execfile('miniconf/version.py') setup(name='miniconf', - version=miniconf.__version__, + version=__version__, author='Ryan Summers, Robert Jördens', description='Utilities for configuring Miniconf-configurable devices', url='https://github.com/quartiq/miniconf', From 7b52722626a2082bc6806ee79ce69ef033988396 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Fri, 6 Aug 2021 13:41:53 +0200 Subject: [PATCH 5/7] Refactoring loopback HITL test --- hitl/loopback.py | 12 ++--- hitl/run.sh | 5 +- miniconf-py/.gitignore | 3 -- miniconf-py/README.md | 6 --- miniconf-py/miniconf/__init__.py | 3 -- miniconf-py/miniconf/__main__.py | 53 ------------------ miniconf-py/miniconf/miniconf.py | 93 -------------------------------- miniconf-py/miniconf/version.py | 3 -- miniconf-py/setup.py | 21 -------- 9 files changed, 10 insertions(+), 189 deletions(-) delete mode 100644 miniconf-py/.gitignore delete mode 100644 miniconf-py/README.md delete mode 100644 miniconf-py/miniconf/__init__.py delete mode 100644 miniconf-py/miniconf/__main__.py delete mode 100644 miniconf-py/miniconf/miniconf.py delete mode 100644 miniconf-py/miniconf/version.py delete mode 100644 miniconf-py/setup.py diff --git a/hitl/loopback.py b/hitl/loopback.py index df768be..679d2b7 100644 --- a/hitl/loopback.py +++ b/hitl/loopback.py @@ -95,12 +95,12 @@ async def test_loopback(miniconf, telemetry, set_point): print(f'Testing loopback for Vout = {set_point:.2f}') print('---------------------------------') # Configure the IIRs to output at the set point - assert (await miniconf.command('iir_ch/0/0', static_iir_output(set_point)))['code'] == 0 - assert (await miniconf.command('iir_ch/1/0', static_iir_output(set_point)))['code'] == 0 - assert (await miniconf.command('telemetry_period', 1))['code'] == 0 + await miniconf.command('iir_ch/0/0', static_iir_output(set_point), retain=False) + await miniconf.command('iir_ch/1/0', static_iir_output(set_point), retain=False) + await miniconf.command('telemetry_period', 1, retain=False) # Wait for telemetry values to update. - await asyncio.sleep(2.0) + await asyncio.sleep(5.0) # Verify the ADCs are receiving the setpoint voltage. tolerance = max(0.05 * set_point, MINIMUM_VOLTAGE_ERROR) @@ -133,8 +133,8 @@ def main(): # Repeat test with AFE = 2x print('Configuring AFEs to 2x input') - assert (await interface.command('afe/0', "G2"))['code'] == 0 - assert (await interface.command('afe/1', "G2"))['code'] == 0 + await interface.command('afe/0', "G2", retain=False) + await interface.command('afe/1', "G2", retain=False) await test_loopback(interface, telemetry, 1.0) # Test with 0V output diff --git a/hitl/run.sh b/hitl/run.sh index 582c0f0..a468089 100755 --- a/hitl/run.sh +++ b/hitl/run.sh @@ -13,7 +13,10 @@ set -eux # Set up python for testing python3 -m venv --system-site-packages py . py/bin/activate -python3 -m pip install miniconf-py/ + +# Install Miniconf utilities for configuring stabilizer. +python3 -m pip install git+https://github.com/quartiq/miniconf#subdirectory=miniconf-py +python3 -m pip install gmqtt cargo flash --chip STM32H743ZITx --elf target/thumbv7em-none-eabihf/release/dual-iir diff --git a/miniconf-py/.gitignore b/miniconf-py/.gitignore deleted file mode 100644 index ab7ceb0..0000000 --- a/miniconf-py/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -__pycache__/ -*.egg -*.egg-info/ diff --git a/miniconf-py/README.md b/miniconf-py/README.md deleted file mode 100644 index ebfa798..0000000 --- a/miniconf-py/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Miniconf Python Utility - -This directory contains a Python package for interacting with Miniconf utilities. - -## Installation -Run `pip install .` from this directory to install the `miniconf` package. diff --git a/miniconf-py/miniconf/__init__.py b/miniconf-py/miniconf/__init__.py deleted file mode 100644 index 227f258..0000000 --- a/miniconf-py/miniconf/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/python3 -from .miniconf import Miniconf -from .version import __version__ diff --git a/miniconf-py/miniconf/__main__.py b/miniconf-py/miniconf/__main__.py deleted file mode 100644 index fcc7078..0000000 --- a/miniconf-py/miniconf/__main__.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/python3 - -import sys -import argparse -from .miniconf import Miniconf - - -def main(): - """ Main program entry point. """ - parser = argparse.ArgumentParser( - description='Miniconf command line interface.', - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog='''Examples: -%(prog)s dt/sinara/dual-iir/00-11-22-33-aa-bb iir_ch/0/0=\ -'{"y_min":-32767,"y_max":32767,"y_offset":0,"ba":[1.0,0,0,0,0]}' -%(prog)s dt/sinara/lockin/00-11-22-33-aa-bb afe/0='"G2"'\ -''') - parser.add_argument('-v', '--verbose', action='count', default=0, - help='Increase logging verbosity') - parser.add_argument('--broker', '-b', default='mqtt', type=str, - help='The MQTT broker address') - parser.add_argument('--no-retain', '-n', default=False, - action='store_true', - help='Do not retain the affected settings') - parser.add_argument('prefix', type=str, - help='The MQTT topic prefix of the target') - parser.add_argument('settings', metavar="PATH=VALUE", nargs='+', - help='JSON encoded values for settings path keys.') - - args = parser.parse_args() - - logging.basicConfig( - format='%(asctime)s [%(levelname)s] %(name)s: %(message)s', - level=logging.WARN - 10*args.verbose) - - loop = asyncio.get_event_loop() - - async def configure_settings(): - interface = await Miniconf.create(args.prefix, args.broker) - for setting in args.settings: - path, value = setting.split("=", 1) - response = await interface.command(path, json.loads(value), - not args.no_retain) - print(f'{path}: {response}') - if response['code'] != 0: - return response['code'] - return 0 - - sys.exit(loop.run_until_complete(configure_settings())) - - -if __name__ == '__main__': - main() diff --git a/miniconf-py/miniconf/miniconf.py b/miniconf-py/miniconf/miniconf.py deleted file mode 100644 index f82b72f..0000000 --- a/miniconf-py/miniconf/miniconf.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/python -""" -Author: Vertigo Designs, Ryan Summers - Robert Jördens - -Description: Provides an API for controlling Miniconf devices over MQTT. -""" -import asyncio -import json -import logging -import uuid - -from gmqtt import Client as MqttClient - -LOGGER = logging.getLogger(__name__) - - -class Miniconf: - """An asynchronous API for controlling Miniconf devices using MQTT.""" - - @classmethod - async def create(cls, prefix, broker): - """Create a connection to the broker and a Miniconf device using it.""" - client = MqttClient(client_id='') - await client.connect(broker) - return cls(client, prefix) - - def __init__(self, client, prefix): - """Constructor. - - Args: - client: A connected MQTT5 client. - prefix: The MQTT toptic prefix of the device to control. - """ - self.request_id = 0 - self.client = client - self.prefix = prefix - self.inflight = {} - self.client.on_message = self._handle_response - self.response_topic = f'{prefix}/response/{uuid.uuid1().hex}' - self.client.subscribe(self.response_topic) - - def _handle_response(self, _client, topic, payload, _qos, properties): - """Callback function for when messages are received over MQTT. - - Args: - _client: The MQTT client. - topic: The topic that the message was received on. - payload: The payload of the message. - _qos: The quality-of-service level of the received packet - properties: A dictionary of properties associated with the message. - """ - if topic == self.response_topic: - # Extract request_id corrleation data from the properties - request_id = int.from_bytes( - properties['correlation_data'][0], 'big') - - self.inflight[request_id].set_result(json.loads(payload)) - del self.inflight[request_id] - else: - LOGGER.warning('Unexpected message on "%s"', topic) - - async def command(self, path, value, retain=True): - """Write the provided data to the specified path. - - Args: - path: The path to write the message to. - value: The value to write to the path. - retain: Retain the MQTT message changing the setting - by the broker. - - Returns: - The response to the command as a dictionary. - """ - topic = f'{self.prefix}/settings/{path}' - - fut = asyncio.get_running_loop().create_future() - - # Assign unique correlation data for response dispatch - assert self.request_id not in self.inflight - self.inflight[self.request_id] = fut - correlation_data = self.request_id.to_bytes(4, 'big') - self.request_id += 1 - - payload = json.dumps(value) - LOGGER.info('Sending "%s" to "%s"', value, topic) - - self.client.publish( - topic, payload=payload, qos=0, retain=retain, - response_topic=self.response_topic, - correlation_data=correlation_data) - - return await fut diff --git a/miniconf-py/miniconf/version.py b/miniconf-py/miniconf/version.py deleted file mode 100644 index 1ae5549..0000000 --- a/miniconf-py/miniconf/version.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/python3 - -__version__ = '1.0.0' diff --git a/miniconf-py/setup.py b/miniconf-py/setup.py deleted file mode 100644 index 220b3c3..0000000 --- a/miniconf-py/setup.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/python3 -""" -Author: Vertigo Designs, Ryan Summers - -Description: Setup file for Miniconf packaging. -""" -from setuptools import setup, find_packages - -# Load the version string from the version file. -execfile('miniconf/version.py') - -setup(name='miniconf', - version=__version__, - author='Ryan Summers, Robert Jördens', - description='Utilities for configuring Miniconf-configurable devices', - url='https://github.com/quartiq/miniconf', - packages=find_packages(), - install_requires=[ - 'gmqtt' - ], -) From a6b98ab877ce719adbaf3f6e76f1ed49afde1489 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Fri, 6 Aug 2021 13:46:46 +0200 Subject: [PATCH 6/7] Updating docs for Miniconf --- docs/pages/usage.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/pages/usage.md b/docs/pages/usage.md index 3e1807d..d6f2c1d 100644 --- a/docs/pages/usage.md +++ b/docs/pages/usage.md @@ -25,15 +25,22 @@ to another. Disambiguation of devices is done by using Stabilizer's MAC address. Settings are specific to an application. If two identical settings exist for two different applications, each application maintains its own independent value. -### Setup +### Installation +Install the Miniconf configuration utilities: +``` +python -m pip install git+https://github.com/quartiq/miniconf#subdirectory=miniconf-py +``` -In order to use `miniconf.py`, run the following command: +To use `miniconf`, execute it as follows: ``` -python -m pip install gmqtt +python -m miniconf --help ``` +Miniconf also exposes a programmatic Python API, so it's possible to write automation scripting of +Stabilizer as well. + ### Usage -The `miniconf.py` script utilizes a unique "device prefix". The device prefix is always of the +The Miniconf Python utility utilizes a unique "device prefix". The device prefix is always of the form `dt/sinara//`, where `` is the name of the application and `` is the MAC address of the device, formatted with delimiting dashes. @@ -46,7 +53,7 @@ used: * `value` = `{"ip": [192, 168, 0, 1], "port": 4000}` ``` -python miniconf.py --broker 10.34.16.10 dt/sinara/dual-iir/00-11-22-33-44-55 stream_target='{"ip": [10, 34, 16, 123], "port": 4000}' +python -m miniconf --broker 10.34.16.10 dt/sinara/dual-iir/00-11-22-33-44-55 stream_target='{"ip": [10, 34, 16, 123], "port": 4000}' Where `10.34.16.10` is the MQTT broker address that matches the one configured in the source code and `10.34.16.123` and `4000` are the desire stream target IP and port. ``` @@ -61,7 +68,7 @@ The rules for constructing `path` values are documented in [`miniconf`'s documentation](https://github.com/quartiq/miniconf#settings-paths) Refer to the documentation for [Miniconf]({{site.baseurl}}/firmware/miniconf/enum.Error.html) for a -description of the possible error codes that `miniconf.py` may return if the settings update was +description of the possible error codes that Miniconf may return if the settings update was unsuccessful. ## Telemetry From b019563485301d2137216b63467203e9bd210167 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 9 Aug 2021 12:50:58 +0200 Subject: [PATCH 7/7] Updating after review --- hitl/loopback.py | 69 +++++++++++++++++++++++++++++------------------- hitl/run.sh | 2 +- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/hitl/loopback.py b/hitl/loopback.py index 679d2b7..475cd63 100644 --- a/hitl/loopback.py +++ b/hitl/loopback.py @@ -12,7 +12,7 @@ import sys from gmqtt import Client as MqttClient from miniconf import Miniconf -# The minimum allowably loopback voltage error (difference between output set point and input +# The minimum allowable loopback voltage error (difference between output set point and input # measured value). MINIMUM_VOLTAGE_ERROR = 0.010 @@ -52,34 +52,30 @@ class TelemetryReader: """ Helper utility to read Stabilizer telemetry. """ @classmethod - async def create(cls, prefix, broker): + async def create(cls, prefix, broker, queue): """Create a connection to the broker and an MQTT device using it.""" client = MqttClient(client_id='') await client.connect(broker) - return cls(client, prefix) + return cls(client, prefix, queue) - def __init__(self, client, prefix): + def __init__(self, client, prefix, queue): """ Constructor. """ self.client = client self._telemetry = [] self.client.on_message = self.handle_telemetry self._telemetry_topic = f'{prefix}/telemetry' self.client.subscribe(self._telemetry_topic) + self.queue = queue def handle_telemetry(self, _client, topic, payload, _qos, _properties): """ Handle incoming telemetry messages over MQTT. """ assert topic == self._telemetry_topic - self._telemetry.append(json.loads(payload)) + self.queue.put_nowait(json.loads(payload)) - def get_latest(self): - """ Get the latest telemetry message received. """ - return self._telemetry[-1] - - -async def test_loopback(miniconf, telemetry, set_point): +async def test_loopback(miniconf, telemetry_queue, set_point, gain=1, channel=0): """ Test loopback operation of Stabilizer. Note: @@ -91,24 +87,27 @@ async def test_loopback(miniconf, telemetry, set_point): miniconf: The miniconf configuration interface. telemetry: a helper utility to read inbound telemetry. set_point: The desired output voltage to test. + channel: The loopback channel to test on. Either 0 or 1. + gain: The desired AFE gain. """ - print(f'Testing loopback for Vout = {set_point:.2f}') + print(f'Testing loopback for Vout = {set_point:.2f}, Gain = x{gain}') print('---------------------------------') - # Configure the IIRs to output at the set point - await miniconf.command('iir_ch/0/0', static_iir_output(set_point), retain=False) - await miniconf.command('iir_ch/1/0', static_iir_output(set_point), retain=False) - await miniconf.command('telemetry_period', 1, retain=False) + # Configure the AFE and IIRs to output at the set point + await miniconf.command(f'afe/{channel}', f'G{gain}', retain=False) + await miniconf.command(f'iir_ch/{channel}/0', static_iir_output(set_point), retain=False) - # Wait for telemetry values to update. + # Configure signal generators to not affect the test. + await miniconf.command('signal_generator/0/amplitude', 0, retain=False) + + # Wait for telemetry to update. await asyncio.sleep(5.0) # Verify the ADCs are receiving the setpoint voltage. tolerance = max(0.05 * set_point, MINIMUM_VOLTAGE_ERROR) - latest_values = telemetry.get_latest() + latest_values = await telemetry_queue.get() print(f'Latest telemtry: {latest_values}') - assert abs(latest_values['adcs'][0] - set_point) < tolerance - assert abs(latest_values['adcs'][1] - set_point) < tolerance + assert abs(latest_values['adcs'][channel] - set_point) < tolerance print('PASS') print('') @@ -123,22 +122,38 @@ def main(): args = parser.parse_args() + telemetry_queue = asyncio.LifoQueue() + + async def telemetry(): + await TelemetryReader.create(args.prefix, args.broker, telemetry_queue) + try: + while True: + await asyncio.sleep(1) + except asyncio.CancelledError: + pass + + + telemetry_task = asyncio.Task(telemetry()) + async def test(): """ The actual testing being completed. """ interface = await Miniconf.create(args.prefix, args.broker) - telemetry = await TelemetryReader.create(args.prefix, args.broker) + + # Disable IIR holds and configure the telemetry rate. + await interface.command('allow_hold', False, retain=False) + await interface.command('force_hold', False, retain=False) + await interface.command('telemetry_period', 1, retain=False) # Test loopback with a static 1V output of the DACs. - await test_loopback(interface, telemetry, 1.0) + await test_loopback(interface, telemetry_queue, 1.0) # Repeat test with AFE = 2x - print('Configuring AFEs to 2x input') - await interface.command('afe/0', "G2", retain=False) - await interface.command('afe/1', "G2", retain=False) - await test_loopback(interface, telemetry, 1.0) + await test_loopback(interface, telemetry_queue, 1.0, gain=2) # Test with 0V output - await test_loopback(interface, telemetry, 0.0) + await test_loopback(interface, telemetry_queue, 0.0) + + telemetry_task.cancel() loop = asyncio.get_event_loop() sys.exit(loop.run_until_complete(test())) diff --git a/hitl/run.sh b/hitl/run.sh index a468089..ad75102 100755 --- a/hitl/run.sh +++ b/hitl/run.sh @@ -15,7 +15,7 @@ python3 -m venv --system-site-packages py . py/bin/activate # Install Miniconf utilities for configuring stabilizer. -python3 -m pip install git+https://github.com/quartiq/miniconf#subdirectory=miniconf-py +python3 -m pip install git+https://github.com/quartiq/miniconf#subdirectory=py/miniconf-mqtt python3 -m pip install gmqtt cargo flash --chip STM32H743ZITx --elf target/thumbv7em-none-eabihf/release/dual-iir