diff --git a/README.md b/README.md index 25aeda550050520c27e9e0c7312d34fe0b2b9538..243403d4ef388883a3e09fbc844d2e7e3958e256 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ We strongly recommend users to create a virtual environment to run the code and * you should run you code within the virtual environment * to leave the virtual environment simply type: `deactivate` +To enable to Python module, we need to add the 'ohmpi' folder to the python path. This can be done by `source .env` or manually by adding `export PYTHONPATH=$PYTHONPATH:/home/<username>/OhmPi` to /home/<username>/.bashrc. + ## First run with four electrodes In the examples folder, you will find the code "simple_measurement.py". diff --git a/concepts_and_ideas/Prepa Hackathon.pdf b/concepts_and_ideas/Prepa Hackathon.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9f41a520b7b9f20f8f930e8e251a9375e9f5a02b Binary files /dev/null and b/concepts_and_ideas/Prepa Hackathon.pdf differ diff --git a/configs/config_mb_2023_4_mux_2023.py b/configs/config_mb_2023.py similarity index 56% rename from configs/config_mb_2023_4_mux_2023.py rename to configs/config_mb_2023.py index 7d7fa2fda28ae3cddbf33f04bdcf9b4c240960f5..8bd970d8096ec3515ced1a1c9d11ea5e2aeabbc7 100644 --- a/configs/config_mb_2023_4_mux_2023.py +++ b/configs/config_mb_2023.py @@ -1,7 +1,7 @@ import logging from ohmpi.utils import get_platform -from paho.mqtt.client import MQTTv31 +from paho.mqtt.client import MQTTv31 # noqa _, on_pi = get_platform() # DEFINE THE ID OF YOUR OhmPi @@ -19,47 +19,24 @@ OHMPI_CONFIG = { HARDWARE_CONFIG = { 'ctl': {'model': 'raspberry_pi'}, - 'pwr': {'model': 'pwr_batt', 'voltage': 12.}, + 'pwr': {'model': 'pwr_batt', 'voltage': 12., 'interface_name': 'none'}, 'tx': {'model': 'mb_2023_0_X', - 'mcp_board_address': 0x20, 'voltage_max': 12., # Maximum voltage supported by the TX board [V] - 'current_max': 4800 / 50 / 2, # Maximum current supported by the TX board [mA] - 'r_shunt': 2 # Shunt resistance in Ohms + 'adc_voltage_max': 4800., # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2., # Shunt resistance in Ohms + 'interface_name': 'i2c', }, 'rx': {'model': 'mb_2023_0_X', - 'coef_p2': 2.50, # slope for current conversion for ADS.P2, measurement in V/V - 'nb_samples': 20, # Max value 10 # was named integer before... + 'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V + 'sampling_rate': 50., # number of samples per second + 'interface_name': 'i2c', }, - 'mux': # default properties are system properties that will be - # overwritten by board properties defined at the board level within the board model file - # both will be overwritten by properties specified in the board dict below. Use with caution... - {'boards': - {'mux_1': - {'model': 'mux_2023_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': 0x70, - 'roles': {'A': 'X'}, - 'cabling': {(i, j): ('mux_1', i) for j in ['A'] for i in range(1, 65)}, - 'voltage_max': 12.}, - 'mux_2': - {'model': 'mux_2023_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': 0x71, - 'roles': {'B': 'X'}, - 'cabling': {(i, j): ('mux_2', i) for j in ['B'] for i in range(1, 65)}, - 'voltage_max': 12.}, - 'mux_3': - {'model': 'mux_2023_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': 0x72, - 'roles': {'M': 'X'}, - 'cabling': {(i, j): ('mux_3', i) for j in ['M'] for i in range(1, 65)}, - 'voltage_max': 12.}, - 'mux_4': - {'model': 'mux_2023_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': 0x73, - 'roles': {'N': 'X'}, - 'cabling': {(i, j): ('mux_4', i) for j in ['N'] for i in range(1, 65)}, - 'voltage_max': 12.}, - }, - 'default': {'connection': 'i2c', + 'mux': # default properties given in config are system properties that will be + # overwritten by properties defined in each the board dict below. + # if defined in board specs, values out of specs will be bounded to remain in specs + # omitted properties in config will be set to board specs default values if they exist + {'boards': {}, + 'default': {'interface_name': 'i2c', 'voltage_max': 100., 'current_max': 3.} } @@ -68,7 +45,7 @@ HARDWARE_CONFIG = { # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS # Execution logging configuration EXEC_LOGGING_CONFIG = { - 'logging_level': logging.INFO, + 'logging_level': logging.DEBUG, # TODO: set logging level back to INFO 'log_file_logging_level': logging.DEBUG, 'logging_to_console': True, 'file_name': f'exec{logging_suffix}.log', diff --git a/configs/config_mb_2023_3_mux_2024.py b/configs/config_mb_2023_3_mux_2024.py index 78673566fccbe93641fcd012a407b05c80557aa8..7f83b2a4977c0a9fd0d85460d083e51b0d451065 100644 --- a/configs/config_mb_2023_3_mux_2024.py +++ b/configs/config_mb_2023_3_mux_2024.py @@ -21,40 +21,41 @@ HARDWARE_CONFIG = { 'ctl': {'model': 'raspberry_pi'}, 'pwr': {'model': 'pwr_batt', 'voltage': 12.}, 'tx': {'model': 'mb_2023_0_X', - 'mcp_board_address': 0x20, 'voltage_max': 12., # Maximum voltage supported by the TX board [V] - 'current_max': 4800 / 50 / 2, # Maximum current supported by the TX board [mA] - 'r_shunt': 2 # Shunt resistance in Ohms + 'adc_voltage_max': 4800., # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2., # Shunt resistance in Ohms + 'interface_name': 'i2c', }, 'rx': {'model': 'mb_2023_0_X', - 'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V - 'latency': 0.010, # latency in seconds in continuous mode - 'sampling_rate': 50 # number of samples per second + 'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V + 'sampling_rate': 50., # number of samples per second + 'interface_name': 'i2c', }, - 'mux': # default properties are system properties that will be - # overwritten by board properties defined at the board level within the board model file - # both will be overwritten by properties specified in the board dict below. Use with caution... + 'mux': # default properties given in config are system properties that will be + # overwritten by properties defined in each the board dict below. + # if defined in board specs, values out of specs will be bounded to remain in specs + # omitted properties in config will be set to board specs default values if they exist {'boards': {'mux_02': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', + {'model': 'mux_2024_0_X', 'tca_address': None, 'tca_channel': 0, - 'mcp_0': '0x22', # TODO: Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user... + 'mcp_0': '0x22', # TODO: Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...) 'mcp_1': '0x23', # TODO: Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...) 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, 'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, 'voltage_max': 12.}, 'mux_00': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', + {'model': 'mux_2024_0_X', 'tca_address': None, 'tca_channel': 0, - 'mcp_0': '0x24', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user... + 'mcp_0': '0x24', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...) 'mcp_1': '0x25', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...) 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, 'cabling': {(i+16, j): ('mux_00', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, 'voltage_max': 12.}, 'mux_04': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', + {'model': 'mux_2024_0_X', 'tca_address': None, 'tca_channel': 0, 'mcp_0': '0x26', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user... @@ -63,7 +64,7 @@ HARDWARE_CONFIG = { 'cabling': {(i+24, j): ('mux_04', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, 'voltage_max': 12.} }, - 'default': {'connection': 'i2c', + 'default': {'interface_name': 'i2c', 'voltage_max': 100., 'current_max': 3.} } @@ -72,7 +73,7 @@ HARDWARE_CONFIG = { # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS # Execution logging configuration EXEC_LOGGING_CONFIG = { - 'logging_level': logging.INFO, + 'logging_level': logging.DEBUG, # TODO: set logging level back to INFO 'log_file_logging_level': logging.DEBUG, 'logging_to_console': True, 'file_name': f'exec{logging_suffix}.log', diff --git a/configs/config_mb_2023_4_mux_2024.py b/configs/config_mb_2023_4_mux_2024.py deleted file mode 100644 index 23689580c1aa06dbf6e962c85f926ca5e27f54ce..0000000000000000000000000000000000000000 --- a/configs/config_mb_2023_4_mux_2024.py +++ /dev/null @@ -1,151 +0,0 @@ -import logging -from ohmpi.utils import get_platform - -from paho.mqtt.client import MQTTv31 - -_, on_pi = get_platform() -# DEFINE THE ID OF YOUR OhmPi -ohmpi_id = '0001' if on_pi else 'XXXX' -# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost') -mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE' -# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES -logging_suffix = '' - -# OhmPi configuration -OHMPI_CONFIG = { - 'id': ohmpi_id, # Unique identifier of the OhmPi board (string) - 'settings': 'ohmpi_settings.json', # INSERT YOUR FAVORITE SETTINGS FILE HERE -} - -HARDWARE_CONFIG = { - 'ctl': {'model': 'raspberry_pi'}, - 'pwr': {'model': 'pwr_batt', 'voltage': 12.}, - 'tx': {'model': 'mb_2023_0_X', - 'mcp_board_address': 0x20, - 'voltage_max': 12., # Maximum voltage supported by the TX board [V] - 'current_max': 4800 / 50 / 2, # Maximum current supported by the TX board [mA] - 'r_shunt': 2 # Shunt resistance in Ohms - }, - 'rx': {'model': 'mb_2023_0_X', - 'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V - 'latency': 0.010, # latency in seconds in continuous mode - 'sampling_rate': 50 # number of samples per second - }, - 'mux': # default properties are system properties that will be - # overwritten by board properties defined at the board level within the board model file - # both will be overwritten by properties specified in the board dict below. Use with caution... - {'boards': - {'mux_02': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': 0x77, - 'tca_channel': 0, - 'mcp_0': '0x22', # NOTE: Check pos of jumper on MUX board (refer to doc) - 'mcp_1': '0x23', # NOTE: Check pos of jumper on MUX board (refer to doc) - 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, - 'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, # TODO: avoid redundency of mux_id - 'voltage_max': 12.}, - 'mux_05': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': 0x77, - 'tca_channel': 0, - 'mcp_0': '0x26', # NOTE: Check pos of jumper on MUX board (refer to doc) - 'mcp_1': '0x27', # NOTE: Check pos of jumper on MUX board (refer to doc) - 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, - 'cabling': {(i+16, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, - 'voltage_max': 12.}, - 'mux_04': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': 0x77, - 'tca_channel': 1, - 'mcp_0': '0x24', # NOTE: Check pos of jumper on MUX board (refer to doc) - 'mcp_1': '0x25', # NOTE: Check pos of jumper on MUX board (refer to doc) - 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, - 'cabling': {(i+24, j): ('mux_04', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, - 'voltage_max': 12.}, - 'mux_03': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': 0x77, - 'tca_channel': 1, - 'mcp_0': '0x26', # NOTE: Check pos of jumper on MUX board (refer to doc) - 'mcp_1': '0x27', # NOTE: Check pos of jumper on MUX board (refer to doc) - 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, - 'cabling': {(i+32, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, - 'voltage_max': 12.} - }, - 'default': {'voltage_max': 100., - 'current_max': 3.} - } -} - -# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS -# Execution logging configuration -EXEC_LOGGING_CONFIG = { - 'logging_level': logging.INFO, - 'log_file_logging_level': logging.DEBUG, - 'logging_to_console': True, - 'file_name': f'exec{logging_suffix}.log', - 'max_bytes': 262144, - 'backup_count': 30, - 'when': 'd', - 'interval': 1 -} - -# Data logging configuration -DATA_LOGGING_CONFIG = { - 'logging_level': logging.INFO, - 'logging_to_console': True, - 'file_name': f'data{logging_suffix}.log', - 'max_bytes': 16777216, - 'backup_count': 1024, - 'when': 'd', - 'interval': 1 -} - -# State of Health logging configuration (For a future release) -SOH_LOGGING_CONFIG = { - 'logging_level': logging.INFO, - 'logging_to_console': True, - 'log_file_logging_level': logging.DEBUG, - 'file_name': f'soh{logging_suffix}.log', - 'max_bytes': 16777216, - 'backup_count': 1024, - 'when': 'd', - 'interval': 1 -} - -# MQTT logging configuration parameters -MQTT_LOGGING_CONFIG = { - 'hostname': mqtt_broker, - 'port': 1883, - 'qos': 2, - 'retain': False, - 'keepalive': 60, - 'will': None, - 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, - 'tls': None, - 'protocol': MQTTv31, - 'transport': 'tcp', - 'client_id': f'{OHMPI_CONFIG["id"]}', - 'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec', - 'exec_logging_level': logging.DEBUG, - 'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data', - 'data_logging_level': DATA_LOGGING_CONFIG['logging_level'], - 'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh', - 'soh_logging_level': SOH_LOGGING_CONFIG['logging_level'] -} - -# MQTT control configuration parameters -MQTT_CONTROL_CONFIG = { - 'hostname': mqtt_broker, - 'port': 1883, - 'qos': 2, - 'retain': False, - 'keepalive': 60, - 'will': None, - 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, - 'tls': None, - 'protocol': MQTTv31, - 'transport': 'tcp', - 'client_id': f'{OHMPI_CONFIG["id"]}', - 'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl' -} diff --git a/configs/config_mb_2024_0_2.py b/configs/config_mb_2024_0_2.py index f9fa2ec67c1a8ff324fb3d69a975645d4c7835cf..f99c214d235e821f4b55fca27db02d6b5bff2760 100644 --- a/configs/config_mb_2024_0_2.py +++ b/configs/config_mb_2024_0_2.py @@ -1,7 +1,6 @@ import logging from ohmpi.utils import get_platform - -from paho.mqtt.client import MQTTv31 +from paho.mqtt.client import MQTTv31 # noqa _, on_pi = get_platform() # DEFINE THE ID OF YOUR OhmPi @@ -19,18 +18,23 @@ OHMPI_CONFIG = { HARDWARE_CONFIG = { 'ctl': {'model': 'raspberry_pi'}, - 'pwr': {'model': 'pwr_batt', 'voltage': 12.}, - 'tx': {'model': 'mb_2024_rev_0_2', + 'pwr': {'model': 'pwr_batt', 'voltage': 12., 'interface_name': 'none'}, + 'tx': {'model': 'mb_2024_0_2', 'voltage_max': 50., # Maximum voltage supported by the TX board [V] - 'current_max': 4800 / 50 / 2, # Maximum current supported by the TX board [mA] - 'r_shunt': 2 # Shunt resistance in Ohms + 'current_max': 4800, # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2, # Shunt resistance in Ohms + 'interface_name': 'i2c' }, - 'rx': {'model': 'mb_2024_rev_0_2', - 'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V + 'rx': {'model': 'mb_2024_0_2', + 'coef_p2': 1.00, # slope for conversion for ADS, measurement in V/V 'latency': 0.010, # latency in seconds in continuous mode - 'sampling_rate': 50 # number of samples per second - } -} + 'sampling_rate': 50, # number of samples per second + 'interface_name': 'i2c' + }, + 'mux': {'boards': {}, + 'default': {} + } + } # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS # Execution logging configuration @@ -39,7 +43,7 @@ EXEC_LOGGING_CONFIG = { 'log_file_logging_level': logging.DEBUG, 'logging_to_console': True, 'file_name': f'exec{logging_suffix}.log', - 'max_bytes': 262144, + 'max_bytes': 1048576, 'backup_count': 30, 'when': 'd', 'interval': 1 diff --git a/configs/config_mb_2023_mux_2024.py b/configs/config_mb_2024_0_2__1_mux_2024.py similarity index 62% rename from configs/config_mb_2023_mux_2024.py rename to configs/config_mb_2024_0_2__1_mux_2024.py index e78a44a6ebd3839240ca7d56d03b1ff13e80936a..a9a91e4d052bb426c4f5c9e3765578b0a7f73a19 100644 --- a/configs/config_mb_2023_mux_2024.py +++ b/configs/config_mb_2024_0_2__1_mux_2024.py @@ -1,7 +1,6 @@ import logging from ohmpi.utils import get_platform - -from paho.mqtt.client import MQTTv31 +from paho.mqtt.client import MQTTv31 # noqa _, on_pi = get_platform() # DEFINE THE ID OF YOUR OhmPi @@ -18,36 +17,38 @@ OHMPI_CONFIG = { } HARDWARE_CONFIG = { - 'ctl': {'model' : 'raspberry_pi' - }, - 'pwr': {'model' : 'pwr_batt', 'voltage': 12.}, - 'tx' : {'model' : 'mb_2023_0_X', - 'mcp_board_address': 0x20, - 'voltage_max': 12., # Maximum voltage supported by the TX board [V] - 'current_max': 4800 / 50 / 2, # Maximum current supported by the TX board [mA] - 'r_shunt': 2 # Shunt resistance in Ohms + 'ctl': {'model': 'raspberry_pi'}, + 'pwr': {'model': 'pwr_batt', 'voltage': 12., 'interface_name': 'none'}, + 'tx': {'model': 'mb_2024_0_2', + 'voltage_max': 50., # Maximum voltage supported by the TX board [V] + 'current_max': 4800, # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2, # Shunt resistance in Ohms + 'interface_name': 'i2c' }, - 'rx': {'model': 'mb_2023_0_X', - 'coef_p2': 2.50, # slope for current conversion for ADS.P2, measurement in V/V - 'sampling_rate': 100., # Hz - 'nb_samples': 20, # Max value 10 # was named integer before... + 'rx': {'model': 'mb_2024_0_2', + 'coef_p2': 1.00, # slope for conversion for ADS, measurement in V/V + 'latency': 0.010, # latency in seconds in continuous mode + 'sampling_rate': 50, # number of samples per second + 'interface_name': 'i2c' }, - 'mux': # default properties are system properties that will be - # overwritten by board properties defined at the board level within the board model file - # both will be overwritten by properties specified in the board dict below. Use with caution... - {'boards': - {'mux_1': - {'model' : 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', + 'mux': {'boards': + {'mux_03': + {'model': 'mux_2024_0_X', 'tca_address': None, 'tca_channel': 0, - 'mcp_0' : '0x22', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user... - 'mcp_1' : '0x23', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...) - 'roles' : {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, - 'voltage_max': 12. - } - }, - 'default': {'voltage_max': 100., 'current_max': 3.}} -} + 'addr2': 'down', + 'addr1': 'down', + # 'mcp_0': '0x26', + # 'mcp_1': '0x27', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+0, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 12.} + }, + 'default': {'interface_name': 'i2c_ext', + 'voltage_max': 100., + 'current_max': 3.} + } + } # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS # Execution logging configuration diff --git a/configs/config_mb_2023_2_mux_2024.py b/configs/config_mb_2024_0_2__1_mux_2024_dps5005.py similarity index 54% rename from configs/config_mb_2023_2_mux_2024.py rename to configs/config_mb_2024_0_2__1_mux_2024_dps5005.py index 74645aafee9880eecf27d67c321cfb5ce98dadf5..86f5a3b59abe9c0d1e56d88463b892688749078c 100644 --- a/configs/config_mb_2023_2_mux_2024.py +++ b/configs/config_mb_2024_0_2__1_mux_2024_dps5005.py @@ -1,7 +1,6 @@ import logging from ohmpi.utils import get_platform - -from paho.mqtt.client import MQTTv31 +from paho.mqtt.client import MQTTv31 # noqa _, on_pi = get_platform() # DEFINE THE ID OF YOUR OhmPi @@ -19,45 +18,37 @@ OHMPI_CONFIG = { HARDWARE_CONFIG = { 'ctl': {'model': 'raspberry_pi'}, - 'pwr': {'model': 'pwr_batt', 'voltage': 12.}, - 'tx': {'model': 'mb_2023_0_X', - 'mcp_board_address': 0x20, - 'voltage_max': 12., # Maximum voltage supported by the TX board [V] - 'current_max': 4800 / 50 / 2, # Maximum current supported by the TX board [mA] - 'r_shunt': 2 # Shunt resistance in Ohms + 'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'}, + 'tx': {'model': 'mb_2024_0_2', + 'voltage_max': 50., # Maximum voltage supported by the TX board [V] + 'current_max': 4800, # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2, # Shunt resistance in Ohms + 'interface_name': 'i2c' }, - 'rx': {'model': 'mb_2023_0_X', - 'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V + 'rx': {'model': 'mb_2024_0_2', + 'coef_p2': 1.00, # slope for conversion for ADS, measurement in V/V 'latency': 0.010, # latency in seconds in continuous mode - 'sampling_rate': 50 # number of samples per second + 'sampling_rate': 50, # number of samples per second + 'interface_name': 'i2c' }, - 'mux': # default properties are system properties that will be - # overwritten by board properties defined at the board level within the board model file - # both will be overwritten by properties specified in the board dict below. Use with caution... - {'boards': - {'mux_1': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': None, - 'tca_channel': 0, - 'mcp_0': '0x22', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user... - 'mcp_1': '0x23', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...) - 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, - 'cabling': {(i+8, j): ('mux_1', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, - 'voltage_max': 12.}, - 'mux_2': - {'model': 'mux_2024_0_X', # 'ohmpi_i2c_mux64_v1.01', - 'tca_address': None, - 'tca_channel': 0, - 'mcp_0': '0x24', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user... - 'mcp_1': '0x25', # TODO : Replace this with pos of jumper on MUX board (address doesn't mean anything for the average user...) - 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, - 'cabling': {(i+16, j): ('mux_2', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, - 'voltage_max': 12.} - }, - 'default': {'voltage_max': 100., + 'mux': {'boards': + {'mux_03': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'down', + 'addr1': 'down', + # 'mcp_0': '0x26', + # 'mcp_1': '0x27', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+0, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 12.} + }, + 'default': {'interface_name': 'i2c_ext', + 'voltage_max': 100., 'current_max': 3.} - } -} + } + } # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS # Execution logging configuration diff --git a/configs/config_mb_2024_0_2__2_mux_2024_dps5005.py b/configs/config_mb_2024_0_2__2_mux_2024_dps5005.py new file mode 100644 index 0000000000000000000000000000000000000000..6466bc801ceace9ab4c1cbf89f7b9877b9ee4969 --- /dev/null +++ b/configs/config_mb_2024_0_2__2_mux_2024_dps5005.py @@ -0,0 +1,133 @@ +import logging +from ohmpi.utils import get_platform +from paho.mqtt.client import MQTTv31 # noqa + +_, on_pi = get_platform() +# DEFINE THE ID OF YOUR OhmPi +ohmpi_id = '0001' if on_pi else 'XXXX' +# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost') +mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE' +# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES +logging_suffix = '' + +# OhmPi configuration +OHMPI_CONFIG = { + 'id': ohmpi_id, # Unique identifier of the OhmPi board (string) + 'settings': 'ohmpi_settings.json', # INSERT YOUR FAVORITE SETTINGS FILE HERE +} + +HARDWARE_CONFIG = { + 'ctl': {'model': 'raspberry_pi'}, + 'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'}, + 'tx': {'model': 'mb_2024_0_2', + 'voltage_max': 50., # Maximum voltage supported by the TX board [V] + 'current_max': 4800, # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2, # Shunt resistance in Ohms + 'interface_name': 'i2c' + }, + 'rx': {'model': 'mb_2024_0_2', + 'coef_p2': 1.00, # slope for conversion for ADS, measurement in V/V + 'latency': 0.010, # latency in seconds in continuous mode + 'sampling_rate': 50, # number of samples per second + 'interface_name': 'i2c' + }, + 'mux': {'boards': + {'mux_02': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'up', + 'addr1': 'up', + # 'mcp_0': '0x26', + # 'mcp_1': '0x27', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+0, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 12.}, + 'mux_05': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'up', + 'addr1': 'down', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+8, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 12.} + }, + 'default': {'interface_name': 'i2c_ext', + 'voltage_max': 100., + 'current_max': 3.} + } + } + +# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS +# Execution logging configuration +EXEC_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'log_file_logging_level': logging.DEBUG, + 'logging_to_console': True, + 'file_name': f'exec{logging_suffix}.log', + 'max_bytes': 262144, + 'backup_count': 30, + 'when': 'd', + 'interval': 1 +} + +# Data logging configuration +DATA_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'logging_to_console': True, + 'file_name': f'data{logging_suffix}.log', + 'max_bytes': 16777216, + 'backup_count': 1024, + 'when': 'd', + 'interval': 1 +} + +# State of Health logging configuration (For a future release) +SOH_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'logging_to_console': True, + 'log_file_logging_level': logging.DEBUG, + 'file_name': f'soh{logging_suffix}.log', + 'max_bytes': 16777216, + 'backup_count': 1024, + 'when': 'd', + 'interval': 1 +} + +# MQTT logging configuration parameters +MQTT_LOGGING_CONFIG = { + 'hostname': mqtt_broker, + 'port': 1883, + 'qos': 2, + 'retain': False, + 'keepalive': 60, + 'will': None, + 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, + 'tls': None, + 'protocol': MQTTv31, + 'transport': 'tcp', + 'client_id': f'{OHMPI_CONFIG["id"]}', + 'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec', + 'exec_logging_level': logging.DEBUG, + 'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data', + 'data_logging_level': DATA_LOGGING_CONFIG['logging_level'], + 'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh', + 'soh_logging_level': SOH_LOGGING_CONFIG['logging_level'] +} + +# MQTT control configuration parameters +MQTT_CONTROL_CONFIG = { + 'hostname': mqtt_broker, + 'port': 1883, + 'qos': 2, + 'retain': False, + 'keepalive': 60, + 'will': None, + 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, + 'tls': None, + 'protocol': MQTTv31, + 'transport': 'tcp', + 'client_id': f'{OHMPI_CONFIG["id"]}', + 'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl' +} diff --git a/configs/config_mb_2024_0_2__3_mux_2024_dps5005.py b/configs/config_mb_2024_0_2__3_mux_2024_dps5005.py new file mode 100644 index 0000000000000000000000000000000000000000..5b8f4f56a4aff2271e1be00f89a8ae79b86b68bb --- /dev/null +++ b/configs/config_mb_2024_0_2__3_mux_2024_dps5005.py @@ -0,0 +1,144 @@ +import logging +from ohmpi.utils import get_platform +from paho.mqtt.client import MQTTv31 # noqa + +_, on_pi = get_platform() +# DEFINE THE ID OF YOUR OhmPi +ohmpi_id = '0001' if on_pi else 'XXXX' +# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost') +mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE' +# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES +logging_suffix = '' + +# OhmPi configuration +OHMPI_CONFIG = { + 'id': ohmpi_id, # Unique identifier of the OhmPi board (string) + 'settings': 'ohmpi_settings.json', # INSERT YOUR FAVORITE SETTINGS FILE HERE +} + +HARDWARE_CONFIG = { + 'ctl': {'model': 'raspberry_pi'}, + 'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'}, + 'tx': {'model': 'mb_2024_0_2', + 'voltage_max': 50., # Maximum voltage supported by the TX board [V] + 'current_max': 4800, # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2, # Shunt resistance in Ohms + 'interface_name': 'i2c' + }, + 'rx': {'model': 'mb_2024_0_2', + 'coef_p2': 1.00, # slope for conversion for ADS, measurement in V/V + 'latency': 0.010, # latency in seconds in continuous mode + 'sampling_rate': 50, # number of samples per second + 'interface_name': 'i2c' + }, + 'mux': {'boards': + {'mux_02': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'up', + 'addr1': 'up', + # 'mcp_0': '0x26', + # 'mcp_1': '0x27', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 50.}, + 'mux_03': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'down', + 'addr1': 'up', + # 'mcp_0': '0x26', + # 'mcp_1': '0x27', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+16, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 50.}, + 'mux_05': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'up', + 'addr1': 'down', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+0, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 50.}, + }, + 'default': {'interface_name': 'i2c_ext', + 'voltage_max': 100., + 'current_max': 3.} + } + } + +# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS +# Execution logging configuration +EXEC_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'log_file_logging_level': logging.DEBUG, + 'logging_to_console': True, + 'file_name': f'exec{logging_suffix}.log', + 'max_bytes': 262144, + 'backup_count': 30, + 'when': 'd', + 'interval': 1 +} + +# Data logging configuration +DATA_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'logging_to_console': True, + 'file_name': f'data{logging_suffix}.log', + 'max_bytes': 16777216, + 'backup_count': 1024, + 'when': 'd', + 'interval': 1 +} + +# State of Health logging configuration (For a future release) +SOH_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'logging_to_console': True, + 'log_file_logging_level': logging.DEBUG, + 'file_name': f'soh{logging_suffix}.log', + 'max_bytes': 16777216, + 'backup_count': 1024, + 'when': 'd', + 'interval': 1 +} + +# MQTT logging configuration parameters +MQTT_LOGGING_CONFIG = { + 'hostname': mqtt_broker, + 'port': 1883, + 'qos': 2, + 'retain': False, + 'keepalive': 60, + 'will': None, + 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, + 'tls': None, + 'protocol': MQTTv31, + 'transport': 'tcp', + 'client_id': f'{OHMPI_CONFIG["id"]}', + 'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec', + 'exec_logging_level': logging.DEBUG, + 'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data', + 'data_logging_level': DATA_LOGGING_CONFIG['logging_level'], + 'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh', + 'soh_logging_level': SOH_LOGGING_CONFIG['logging_level'] +} + +# MQTT control configuration parameters +MQTT_CONTROL_CONFIG = { + 'hostname': mqtt_broker, + 'port': 1883, + 'qos': 2, + 'retain': False, + 'keepalive': 60, + 'will': None, + 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, + 'tls': None, + 'protocol': MQTTv31, + 'transport': 'tcp', + 'client_id': f'{OHMPI_CONFIG["id"]}', + 'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl' +} diff --git a/configs/config_mb_2024_0_2__4_mux_2023_dps5005.py b/configs/config_mb_2024_0_2__4_mux_2023_dps5005.py new file mode 100644 index 0000000000000000000000000000000000000000..5d9b5091e868aa1ed100e349ea2cf8d9b8964052 --- /dev/null +++ b/configs/config_mb_2024_0_2__4_mux_2023_dps5005.py @@ -0,0 +1,137 @@ +import logging +from ohmpi.utils import get_platform +from paho.mqtt.client import MQTTv31 # noqa + +_, on_pi = get_platform() +# DEFINE THE ID OF YOUR OhmPi +ohmpi_id = '0001' if on_pi else 'XXXX' +# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost') +mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE' +# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES +logging_suffix = '' + +# OhmPi configuration +OHMPI_CONFIG = { + 'id': ohmpi_id, # Unique identifier of the OhmPi board (string) + 'settings': 'ohmpi_settings.json', # INSERT YOUR FAVORITE SETTINGS FILE HERE +} + +HARDWARE_CONFIG = { + 'ctl': {'model': 'raspberry_pi'}, + 'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'}, + 'tx': {'model': 'mb_2024_0_2', + 'voltage_max': 50., # Maximum voltage supported by the TX board [V] + 'current_max': 4800, # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2, # Shunt resistance in Ohms + 'interface_name': 'i2c' + }, + 'rx': {'model': 'mb_2024_0_2', + 'coef_p2': 1.00, # slope for conversion for ADS, measurement in V/V + 'latency': 0.010, # latency in seconds in continuous mode + 'sampling_rate': 50, # number of samples per second + 'interface_name': 'i2c' + }, + 'mux': {'boards': + {'mux_A': + {'model': 'mux_2023_0_X', + 'mux_tca_address': 0x70, + 'roles': {'A': 'X'}, + 'cabling': {(i, j): ('mux_A', i) for j in ['A'] for i in range(1, 65)}, + 'voltage_max': 12.}, + 'mux_B': + {'model': 'mux_2023_0_X', + 'mux_tca_address': 0x71, + 'roles': {'B': 'X'}, + 'cabling': {(i, j): ('mux_B', i) for j in ['B'] for i in range(1, 65)}, + 'voltage_max': 12.}, + 'mux_M': + {'model': 'mux_2023_0_X', + 'mux_tca_address': 0x72, + 'roles': {'M': 'X'}, + 'cabling': {(i, j): ('mux_M', i) for j in ['M'] for i in range(1, 65)}, + 'voltage_max': 12.}, + 'mux_N': + {'model': 'mux_2023_0_X', + 'mux_tca_address': 0x73, + 'roles': {'N': 'X'}, + 'cabling': {(i, j): ('mux_N', i) for j in ['N'] for i in range(1, 65)}, + 'voltage_max': 12.}, + }, + 'default': {'interface_name': 'i2c_ext', + 'voltage_max': 100., + 'current_max': 3.} + } + } + +# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS +# Execution logging configuration +EXEC_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'log_file_logging_level': logging.DEBUG, + 'logging_to_console': True, + 'file_name': f'exec{logging_suffix}.log', + 'max_bytes': 262144, + 'backup_count': 30, + 'when': 'd', + 'interval': 1 +} + +# Data logging configuration +DATA_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'logging_to_console': True, + 'file_name': f'data{logging_suffix}.log', + 'max_bytes': 16777216, + 'backup_count': 1024, + 'when': 'd', + 'interval': 1 +} + +# State of Health logging configuration (For a future release) +SOH_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'logging_to_console': True, + 'log_file_logging_level': logging.DEBUG, + 'file_name': f'soh{logging_suffix}.log', + 'max_bytes': 16777216, + 'backup_count': 1024, + 'when': 'd', + 'interval': 1 +} + +# MQTT logging configuration parameters +MQTT_LOGGING_CONFIG = { + 'hostname': mqtt_broker, + 'port': 1883, + 'qos': 2, + 'retain': False, + 'keepalive': 60, + 'will': None, + 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, + 'tls': None, + 'protocol': MQTTv31, + 'transport': 'tcp', + 'client_id': f'{OHMPI_CONFIG["id"]}', + 'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec', + 'exec_logging_level': logging.DEBUG, + 'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data', + 'data_logging_level': DATA_LOGGING_CONFIG['logging_level'], + 'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh', + 'soh_logging_level': SOH_LOGGING_CONFIG['logging_level'] +} + +# MQTT control configuration parameters +MQTT_CONTROL_CONFIG = { + 'hostname': mqtt_broker, + 'port': 1883, + 'qos': 2, + 'retain': False, + 'keepalive': 60, + 'will': None, + 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, + 'tls': None, + 'protocol': MQTTv31, + 'transport': 'tcp', + 'client_id': f'{OHMPI_CONFIG["id"]}', + 'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl' +} diff --git a/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py b/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py new file mode 100644 index 0000000000000000000000000000000000000000..4fd7c4c0c8d55fe865591568382a533566ce3136 --- /dev/null +++ b/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py @@ -0,0 +1,153 @@ +import logging +from ohmpi.utils import get_platform +from paho.mqtt.client import MQTTv31 # noqa + +_, on_pi = get_platform() +# DEFINE THE ID OF YOUR OhmPi +ohmpi_id = '0001' if on_pi else 'XXXX' +# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost') +mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE' +# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES +logging_suffix = '' + +# OhmPi configuration +OHMPI_CONFIG = { + 'id': ohmpi_id, # Unique identifier of the OhmPi board (string) + 'settings': 'ohmpi_settings.json', # INSERT YOUR FAVORITE SETTINGS FILE HERE +} + +HARDWARE_CONFIG = { + 'ctl': {'model': 'raspberry_pi'}, + 'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'}, + 'tx': {'model': 'mb_2024_0_2', + 'voltage_max': 50., # Maximum voltage supported by the TX board [V] + 'current_max': 4800, # Maximum voltage read by the current ADC on the TX board [mA] + 'r_shunt': 2, # Shunt resistance in Ohms + 'interface_name': 'i2c' + }, + 'rx': {'model': 'mb_2024_0_2', + 'coef_p2': 1.00, # slope for conversion for ADS, measurement in V/V + 'latency': 0.010, # latency in seconds in continuous mode + 'sampling_rate': 50, # number of samples per second + 'interface_name': 'i2c' + }, + 'mux': {'boards': + {'mux_02': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'up', + 'addr1': 'up', + # 'mcp_0': '0x26', + # 'mcp_1': '0x27', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 12.}, + 'mux_03': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'down', + 'addr1': 'up', + # 'mcp_0': '0x26', + # 'mcp_1': '0x27', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+24, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 12.}, + 'mux_05': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'up', + 'addr1': 'down', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+0, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 12.}, + 'mux_06': + {'model': 'mux_2024_0_X', + 'tca_address': None, + 'tca_channel': 0, + 'addr2': 'down', + 'addr1': 'down', + 'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'}, + 'cabling': {(i+16, j): ('mux_06', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)}, + 'voltage_max': 12.}, + }, + 'default': {'interface_name': 'i2c_ext', + 'voltage_max': 100., + 'current_max': 3.} + } + } + +# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS +# Execution logging configuration +EXEC_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'log_file_logging_level': logging.DEBUG, + 'logging_to_console': True, + 'file_name': f'exec{logging_suffix}.log', + 'max_bytes': 262144, + 'backup_count': 30, + 'when': 'd', + 'interval': 1 +} + +# Data logging configuration +DATA_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'logging_to_console': True, + 'file_name': f'data{logging_suffix}.log', + 'max_bytes': 16777216, + 'backup_count': 1024, + 'when': 'd', + 'interval': 1 +} + +# State of Health logging configuration (For a future release) +SOH_LOGGING_CONFIG = { + 'logging_level': logging.INFO, + 'logging_to_console': True, + 'log_file_logging_level': logging.DEBUG, + 'file_name': f'soh{logging_suffix}.log', + 'max_bytes': 16777216, + 'backup_count': 1024, + 'when': 'd', + 'interval': 1 +} + +# MQTT logging configuration parameters +MQTT_LOGGING_CONFIG = { + 'hostname': mqtt_broker, + 'port': 1883, + 'qos': 2, + 'retain': False, + 'keepalive': 60, + 'will': None, + 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, + 'tls': None, + 'protocol': MQTTv31, + 'transport': 'tcp', + 'client_id': f'{OHMPI_CONFIG["id"]}', + 'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec', + 'exec_logging_level': logging.DEBUG, + 'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data', + 'data_logging_level': DATA_LOGGING_CONFIG['logging_level'], + 'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh', + 'soh_logging_level': SOH_LOGGING_CONFIG['logging_level'] +} + +# MQTT control configuration parameters +MQTT_CONTROL_CONFIG = { + 'hostname': mqtt_broker, + 'port': 1883, + 'qos': 2, + 'retain': False, + 'keepalive': 60, + 'will': None, + 'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'}, + 'tls': None, + 'protocol': MQTTv31, + 'transport': 'tcp', + 'client_id': f'{OHMPI_CONFIG["id"]}', + 'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl' +} diff --git a/configure_second_I2C_bus.sh b/configure_second_I2C_bus.sh new file mode 100644 index 0000000000000000000000000000000000000000..43e8b4509f750f3fb975bdec257e47c00b62bd92 --- /dev/null +++ b/configure_second_I2C_bus.sh @@ -0,0 +1,2 @@ +sudo apt-get install -y i2c-tools +echo -e "[all]\ndtoverlay=i2c-gpio,bus=4,i2c_gpio_delay_us=1,i2c_gpio_sda=22,i2c_gpio_scl=23" | sudo tee -a /boot/config.txt diff --git a/dev/test_2_mux_2024.py b/dev/test_2_mux_2024.py deleted file mode 100644 index a033c7199327f94589ceeafe276a654a9280861d..0000000000000000000000000000000000000000 --- a/dev/test_2_mux_2024.py +++ /dev/null @@ -1,60 +0,0 @@ -import time -from ohmpi.utils import change_config -from ohmpi.plots import plot_exec_log -import logging -change_config('../configs/config_mb_2023_2_mux_2024.py', verbose=False) -from ohmpi.hardware_components.mux_2024_0_X import Mux, MUX_CONFIG -from ohmpi.hardware_components import raspberry_pi as ctl_module -# from ohmpi.config import HARDWARE_CONFIG - -stand_alone_mux = False -part_of_hardware_system = False -within_ohmpi = True -# Stand alone mux -if stand_alone_mux: - MUX_CONFIG['ctl'] = ctl_module.Ctl() - MUX_CONFIG['id'] = 'mux_1' - MUX_CONFIG['cabling'] = {(i+8, j) : ('mux_1', i) for j in ['A', 'B', 'M', 'N'] for i in range(1,9)} - mux = Mux(**MUX_CONFIG) - mux.switch_one(elec=9, role='M', state='on') - time.sleep(2) - mux.switch_one(elec=9, role='M', state='off') - mux.switch({'A': [9], 'B': [12], 'M': [10], 'N': [11]}, state='on') - time.sleep(8) - # mux.switch({'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off') - mux.reset() - mux.test({'A': [9, 10, 11, 12, 13, 14, 15, 16], 'B': [9, 10, 11, 12, 13, 14, 15, 16], - 'M': [9, 10, 11, 12, 13, 14, 15, 16], 'N': [9, 10, 11, 12, 13, 14, 15, 16]}, activation_time=.1) - -# mux as part of a OhmPiHardware system -if part_of_hardware_system: - from ohmpi.hardware_system import OhmPiHardware - print('Starting test of mux as part of a OhmPiHardware system.') - - k = OhmPiHardware() - k.exec_logger.setLevel(logging.DEBUG) - - # Test mux switching - k.reset_mux() - k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') - time.sleep(1.) - k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') - -if within_ohmpi: - from ohmpi.ohmpi import OhmPi - print('Starting test of mux within OhmPi.') - k = OhmPi() - k.reset_mux() - k._hw.switch_mux([9, 12, 10, 11], state='on') - k._hw.vab_square_wave(3.,1) - k._hw.switch_mux([9, 12, 10, 11], state='off') - k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit - # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') - # k._hw._plot_readings() - k._hw.switch_mux([9, 12, 10, 11], state='on') - k._hw.vab_square_wave(3.,1) - k._hw.switch_mux([9, 12, 10, 11], state='off') - print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') - k._hw._plot_readings() - plot_exec_log('ohmpi/logs/exec.log') -change_config('../configs/config_default.py', verbose=False) \ No newline at end of file diff --git a/dev/test_4_mux_2024.py b/dev/test_4_mux_2024.py deleted file mode 100644 index 1798a59c8757e118c3402f7c679399146d299449..0000000000000000000000000000000000000000 --- a/dev/test_4_mux_2024.py +++ /dev/null @@ -1,65 +0,0 @@ -import time -from ohmpi.utils import change_config -from ohmpi.plots import plot_exec_log -import logging -change_config('../configs/config_mb_2023_4_mux_2024.py', verbose=False) -from ohmpi.hardware_components.mux_2024_0_X import Mux, MUX_CONFIG -from ohmpi.hardware_components import raspberry_pi as ctl_module -from ohmpi.config import HARDWARE_CONFIG - -stand_alone_mux = False -part_of_hardware_system = True -within_ohmpi = False - - -# Stand alone mux -if stand_alone_mux: - mux_id = 'mux_05' - first = 8 - print(MUX_CONFIG) - MUX_CONFIG.update(HARDWARE_CONFIG['mux']['boards'][mux_id]) - MUX_CONFIG.update({'id': mux_id}) - MUX_CONFIG['ctl'] = ctl_module.Ctl() - mux = Mux(**MUX_CONFIG) - mux.switch_one(elec=1+first, role='M', state='on') - time.sleep(2) - mux.switch_one(elec=1+first, role='M', state='off') - mux.switch({'A': [1], 'B': [2], 'M': [3], 'N': [4]}, state='on') - time.sleep(8) - # mux.switch({'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off') - mux.reset() - mux.test({'A': [i+first for i in range(1, 9)], 'B': [i+first for i in range(1, 9)], - 'M': [i+first for i in range(1, 9)], 'N': [i+first for i in range(1, 9)]}, activation_time=.1) - -# mux as part of a OhmPiHardware system -if part_of_hardware_system: - from ohmpi.hardware_system import OhmPiHardware - print('Starting test of mux as part of a OhmPiHardware system.') - - k = OhmPiHardware() - k.exec_logger.setLevel(logging.DEBUG) - - # Test mux switching - k.reset_mux() - k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') - time.sleep(1.) - k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') - -if within_ohmpi: - from ohmpi.ohmpi import OhmPi - print('Starting test of mux within OhmPi.') - k = OhmPi() - k.reset_mux() - k._hw.switch_mux([1, 4, 2, 3], state='on') - k._hw.vab_square_wave(3.,1) - k._hw.switch_mux([1, 4, 2, 3], state='off') - k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit - # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') - # k._hw._plot_readings() - k._hw.switch_mux([1, 4, 2, 3], state='on') - k._hw.vab_square_wave(3.,1) - k._hw.switch_mux([1, 4, 2, 3], state='off') - print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') - k._hw._plot_readings() - plot_exec_log('ohmpi/logs/exec.log') -change_config('../configs/config_default.py', verbose=False) \ No newline at end of file diff --git a/dev/test_fullwave.py b/dev/test_fullwave.py deleted file mode 100644 index 6d9eb03ca3b5b34a49ec4a484e01ee4b1636b6fa..0000000000000000000000000000000000000000 --- a/dev/test_fullwave.py +++ /dev/null @@ -1,28 +0,0 @@ -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt -import os - -fnames = sorted(os.listdir('data')) -df = pd.read_csv('out.csv', engine='python') -df - -ct = df.columns[df.columns.str.match('t\d+')] -ci = df.columns[df.columns.str.match('i\d+')] -cu = df.columns[df.columns.str.match('u\d+')] - -fig, axs = plt.subplots(2, 1, sharex=True) -for i in range(df.shape[0]): - print(i) - data = np.c_[df.loc[i, ct], df.loc[i, ci], df.loc[i, cu]].astype(float) - inan = ~(np.isnan(data).any(1)) - label = ', '.join(df.loc[i, ['A', 'B', 'M', 'N']].astype(str).to_list()) - ax = axs[0] - ax.plot(data[inan,0], data[inan,1], '.-', label=label) - ax.set_ylabel('Current AB [mA]') - ax.legend() - ax = axs[1] - ax.plot(data[inan,0], data[inan,2], '.-', label=label) - ax.set_ylabel('Voltage MN [mV]') - ax.set_xlabel('Time [s]') -plt.show() diff --git a/dev/test_inv.py b/dev/test_inv.py new file mode 100644 index 0000000000000000000000000000000000000000..b212d585b5f0fa9156a9b84fce37ddf10f95d479 --- /dev/null +++ b/dev/test_inv.py @@ -0,0 +1,12 @@ + +# +from ohmpi.utils import change_config +change_config('../configs/config_mb_2023.py', verbose=False) + +from ohmpi.ohmpi import OhmPi +k = OhmPi() +k.run_inversion(['measurement_20220206T194552.csv']) + + +# restore default config +change_config('../configs/config_default.py', verbose=False) diff --git a/dev/test_mb_2023_0_X.py b/dev/test_mb_2023_0_X.py deleted file mode 100644 index 04dfe529d679a698b2137ed915ccdb97009589e6..0000000000000000000000000000000000000000 --- a/dev/test_mb_2023_0_X.py +++ /dev/null @@ -1,29 +0,0 @@ -# import sys -# sys.path.extend(['/home/su530201/PycharmProjects/ohmpi_reversaal/OhmPi']) -from ohmpi.hardware_components.mb_2023_0_X import Tx -from ohmpi.hardware_components.mb_2023_0_X import Rx -from ohmpi.logging_setup import create_stdout_logger -import numpy as np - -exec_logger = create_stdout_logger(name='exec') -soh_logger = create_stdout_logger(name='soh') - -print('\nCreating TX...') -tx = Tx(exec_logger= exec_logger, soh_logger= soh_logger) -print('\nCreating RX...') -rx = Rx(exec_logger= exec_logger, soh_logger= soh_logger) - -print(f'TX current: {tx.current:.3f} mA') -print(f'RX voltage: {rx.voltage:.3f} mV') - -tx.inject(state='on', polarity=1) -tx.adc_gain_auto() -rx.adc_gain_auto() -r = [] -for i in range(30): - r.append(rx.voltage/tx.current) - print(f'Resistance: {r[-1]:.3f}') -r = np.array(r) -print(f'Mean resistance: {np.mean(r):.3f} Ohms') -print(f'Resistance std: {np.std(r):.3f} Ohms') -print(f'Dev. {100. * np.std(r)/np.mean(r):.1} %') diff --git a/dev/test_mb_2023_0_mux.py b/dev/test_mb_2023_0_mux.py new file mode 100644 index 0000000000000000000000000000000000000000..4dfafdf74eeb36d7368fdfd1c67018929b840e5f --- /dev/null +++ b/dev/test_mb_2023_0_mux.py @@ -0,0 +1,117 @@ +import matplotlib +matplotlib.use('TkAgg') +from ohmpi.utils import change_config +change_config('../configs/config_mb_2023.py', verbose=False) +import importlib +import time +import logging +from ohmpi.config import HARDWARE_CONFIG + +stand_alone = False +part_of_hardware_system = False +within_ohmpi = True + +# Stand alone +if stand_alone: + + ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}') + pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}') + tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}') + rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}') + + ctl = ctl_module.Ctl() + HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['tx'].pop( + 'interface_name', 'i2c')])}) + HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['rx'].pop( + 'interface_name', 'i2c')])}) + + HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['pwr'].pop( + 'interface_name', None)])}) + + + rx = rx_module.Rx(**HARDWARE_CONFIG['rx']) + tx = tx_module.Tx(**HARDWARE_CONFIG['tx']) + pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr']) + + # mux_ids = ['mux_02', 'mux_05'] + # for m,mux_id in enumerate(mux_ids): + # mux_module = importlib.import_module( + # f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}') + + # MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id] + + # MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[ + # MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger, + # 'soh_logger': ctl.soh_logger}) + # MUX_CONFIG.update({'id': mux_id}) + # mux = mux_module.Mux(**MUX_CONFIG) + + # # tx.polarity = 1 + # # time.sleep(1) + # # tx.polarity = 0 + # # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on') + # # time.sleep(1) + # # voltage = rx.voltage + # # current = tx.current + # # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off') + # # print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA') + # mux.reset() + # mux.test({'A': [i+8*m for i in range(1, 9)], 'B': [i+8*m for i in range(1, 9)], + # 'M': [i+8*m for i in range(1, 9)], 'N': [i+8*m for i in range(1, 9)]}, activation_time=.1) + # mux.reset() + +# mux as part of a OhmPiHardware system +if part_of_hardware_system: + from ohmpi.hardware_system import OhmPiHardware + print('Starting test of as part of an OhmPiHardware system.') + # mux_id = 'mux_03' + k = OhmPiHardware() + k.exec_logger.setLevel(logging.DEBUG) + # Test mux switching + k.reset_mux() + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') + # time.sleep(1.) + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') + # k.mux_boards[mux_id].test(activation_time=.4) + k.test_mux() + k.reset_mux() + +if within_ohmpi: + from ohmpi.ohmpi import OhmPi + # from ohmpi.plots import plot_exec_log + + print('Starting test with OhmPi.') + k = OhmPi() + # A, B, M, N = (32, 29, 31, 30) + k.reset_mux() + # k.test_mux(mux_id='mux_03') + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12.,1., cycles=2) + # k._hw.switch_mux([A, B, M, N], state='off') + # k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit + # k._hw.rx._bias = -1.38 + # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + A, B, M, N = (0, 0, 0, 0) + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3) + # k._hw.switch_mux([A, B, M, N], state='off') + # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + print('using OhmPi') + d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5) + print(d) + # k._hw._plot_readings() + print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') + print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + k._hw._plot_readings(save_fig=False) + # plot_exec_log('ohmpi/logs/exec.log') +change_config('../configs/config_default.py', verbose=False) diff --git a/dev/test_3_mux_2024.py b/dev/test_mb_2023_3_mux_2024.py similarity index 98% rename from dev/test_3_mux_2024.py rename to dev/test_mb_2023_3_mux_2024.py index 8410aa75f731f30a36f346eb6f585b99663fad93..7851dff3cfc86546e4ce88a18db79ea2e27f74bd 100644 --- a/dev/test_3_mux_2024.py +++ b/dev/test_mb_2023_3_mux_2024.py @@ -58,7 +58,7 @@ if within_ohmpi: #k._hw.rx._bias = -1.38 #print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') # k._hw._plot_readings() - A, B, M, N = (28, 25, 27, 26) + A, B, M, N = (1, 4, 2, 3) # k._hw.switch_mux([A, B, M, N], state='on') # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3) # k._hw.switch_mux([A, B, M, N], state='off') diff --git a/dev/test_mb_2024_0_mux_2024.py b/dev/test_mb_2024_0_mux_2024.py new file mode 100644 index 0000000000000000000000000000000000000000..0f781ec843cf75d4ffdc8c8f641b5b18a29b55b4 --- /dev/null +++ b/dev/test_mb_2024_0_mux_2024.py @@ -0,0 +1,83 @@ +import matplotlib +matplotlib.use('TkAgg') +from ohmpi.utils import change_config +change_config('../configs/config_mb_2024_0_2.py', verbose=False) +import importlib +import time +import logging +from ohmpi.config import HARDWARE_CONFIG + +stand_alone = False +part_of_hardware_system = False +within_ohmpi = True + +# Stand alone +if stand_alone: + ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}') + pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}') + tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}') + rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}') + + ctl = ctl_module.Ctl() + HARDWARE_CONFIG['tx'].update({'ctl': ctl}) # HARDWARE_CONFIG['tx'].pop('ctl', ctl_module.Ctl())}) + HARDWARE_CONFIG['rx'].update({'ctl': ctl}) # HARDWARE_CONFIG['rx'].pop('ctl', ctl_module.Ctl())}) + HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['tx'].pop( + 'interface_name', 'i2c')])}) + HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['rx'].pop( + 'interface_name', 'i2c')])}) + + rx = rx_module.Rx(**HARDWARE_CONFIG['rx']) + tx = tx_module.Tx(**HARDWARE_CONFIG['tx']) + pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr']) + + tx.polarity = 1 + time.sleep(1) + tx.polarity = 0 + +# mux as part of a OhmPiHardware system +if part_of_hardware_system: + from ohmpi.hardware_system import OhmPiHardware + print('Starting test of as part of an OhmPiHardware system.') + + k = OhmPiHardware() + k.exec_logger.setLevel(logging.DEBUG) + # Test mux switching + k.reset_mux() + k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') + time.sleep(1.) + k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') + +if within_ohmpi: + from ohmpi.ohmpi import OhmPi + # from ohmpi.plots import plot_exec_log + + print('Starting test with OhmPi.') + k = OhmPi() + #A, B, M, N = (32, 29, 31, 30) + k.reset_mux() + #k._hw.switch_mux([A, B, M, N], state='on') + #k._hw.vab_square_wave(12.,1., cycles=2) + #k._hw.switch_mux([A, B, M, N], state='off') + #k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit + #k._hw.rx._bias = -1.38 + #print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + A, B, M, N = (0, 0, 0, 0) + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3) + # k._hw.switch_mux([A, B, M, N], state='off') + # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + print('using OhmPi') + d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5) + print(d) + #k._hw._plot_readings() + print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') + print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + k._hw._plot_readings(save_fig=False) + # plot_exec_log('ohmpi/logs/exec.log') +change_config('../configs/config_default.py', verbose=False) diff --git a/dev/test_mb_2024_1_mux_2024.py b/dev/test_mb_2024_1_mux_2024.py new file mode 100644 index 0000000000000000000000000000000000000000..2d086183953fa43503ca156c11ad7b6cfa19d09f --- /dev/null +++ b/dev/test_mb_2024_1_mux_2024.py @@ -0,0 +1,111 @@ +import matplotlib +matplotlib.use('TkAgg') +from ohmpi.utils import change_config +change_config('../configs/config_mb_2024_0_2__1_mux_2024_dps5005.py', verbose=False) +import importlib +import time +import logging +from ohmpi.config import HARDWARE_CONFIG + +stand_alone = False +part_of_hardware_system = True +within_ohmpi = False + +# Stand alone +if stand_alone: + mux_id = 'mux_03' + ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}') + pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}') + tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}') + rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}') + mux_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}') + + ctl = ctl_module.Ctl() + HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['tx'].pop( + 'interface_name', 'i2c')])}) + HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['rx'].pop( + 'interface_name', 'i2c')])}) + + HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['pwr'].pop( + 'interface_name', None)])}) + + MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id] + MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[ + MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger, + 'soh_logger': ctl.soh_logger}) + MUX_CONFIG.update({'id': mux_id}) + + rx = rx_module.Rx(**HARDWARE_CONFIG['rx']) + tx = tx_module.Tx(**HARDWARE_CONFIG['tx']) + pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr']) + mux = mux_module.Mux(**MUX_CONFIG) + + tx.polarity = 1 + time.sleep(1) + tx.polarity = 0 + mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on') + time.sleep(1) + voltage = rx.voltage + current = tx.current + mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off') + print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA') + mux.reset() + # mux.test({'A': [i for i in range(1, 9)], 'B': [i for i in range(1, 9)], + # 'M': [i for i in range(1, 9)], 'N': [i for i in range(1, 9)]}, activation_time=.1) + # mux.reset() + +# mux as part of a OhmPiHardware system +if part_of_hardware_system: + from ohmpi.hardware_system import OhmPiHardware + print('Starting test of as part of an OhmPiHardware system.') + mux_id = 'mux_03' + k = OhmPiHardware() + k.exec_logger.setLevel(logging.DEBUG) + # Test mux switching + k.reset_mux() + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') + # time.sleep(1.) + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') + # k.mux_boards[mux_id].test(activation_time=.4) + k.test_mux() + k.reset_mux() + +if within_ohmpi: + from ohmpi.ohmpi import OhmPi + # from ohmpi.plots import plot_exec_log + + print('Starting test with OhmPi.') + k = OhmPi() + # A, B, M, N = (32, 29, 31, 30) + k.reset_mux() + # k.test_mux(mux_id='mux_03') + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12.,1., cycles=2) + # k._hw.switch_mux([A, B, M, N], state='off') + # k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit + # k._hw.rx._bias = -1.38 + # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + A, B, M, N = (1, 4, 2, 3) + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3) + # k._hw.switch_mux([A, B, M, N], state='off') + # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + print('using OhmPi') + d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5) + print(d) + # k._hw._plot_readings() + print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') + print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + k._hw._plot_readings(save_fig=False) + # plot_exec_log('ohmpi/logs/exec.log') +change_config('../configs/config_default.py', verbose=False) diff --git a/dev/test_mb_2024_2_mux_2024.py b/dev/test_mb_2024_2_mux_2024.py new file mode 100644 index 0000000000000000000000000000000000000000..9792e6c62bef60c4ecee59508112d264d807ecbf --- /dev/null +++ b/dev/test_mb_2024_2_mux_2024.py @@ -0,0 +1,116 @@ +import matplotlib +matplotlib.use('TkAgg') +from ohmpi.utils import change_config +change_config('../configs/config_mb_2024_0_2__2_mux_2024_dps5005.py', verbose=False) +import importlib +import time +import logging +from ohmpi.config import HARDWARE_CONFIG + +stand_alone = False +part_of_hardware_system = True +within_ohmpi = False + +# Stand alone +if stand_alone: + + ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}') + pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}') + tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}') + rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}') + + ctl = ctl_module.Ctl() + HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['tx'].pop( + 'interface_name', 'i2c')])}) + HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['rx'].pop( + 'interface_name', 'i2c')])}) + + HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['pwr'].pop( + 'interface_name', None)])}) + + + rx = rx_module.Rx(**HARDWARE_CONFIG['rx']) + tx = tx_module.Tx(**HARDWARE_CONFIG['tx']) + pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr']) + mux_ids = ['mux_02', 'mux_05'] + for m,mux_id in enumerate(mux_ids): + mux_module = importlib.import_module( + f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}') + + MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id] + + MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[ + MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger, + 'soh_logger': ctl.soh_logger}) + MUX_CONFIG.update({'id': mux_id}) + mux = mux_module.Mux(**MUX_CONFIG) + + # tx.polarity = 1 + # time.sleep(1) + # tx.polarity = 0 + # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on') + # time.sleep(1) + # voltage = rx.voltage + # current = tx.current + # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off') + # print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA') + mux.reset() + mux.test({'A': [i+8*m for i in range(1, 9)], 'B': [i+8*m for i in range(1, 9)], + 'M': [i+8*m for i in range(1, 9)], 'N': [i+8*m for i in range(1, 9)]}, activation_time=.1) + mux.reset() + +# mux as part of a OhmPiHardware system +if part_of_hardware_system: + from ohmpi.hardware_system import OhmPiHardware + print('Starting test of as part of an OhmPiHardware system.') + # mux_id = 'mux_03' + k = OhmPiHardware() + k.exec_logger.setLevel(logging.DEBUG) + # Test mux switching + k.reset_mux() + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') + # time.sleep(1.) + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') + # k.mux_boards[mux_id].test(activation_time=.4) + k.test_mux() + k.reset_mux() + +if within_ohmpi: + from ohmpi.ohmpi import OhmPi + # from ohmpi.plots import plot_exec_log + + print('Starting test with OhmPi.') + k = OhmPi() + # A, B, M, N = (32, 29, 31, 30) + k.reset_mux() + # k.test_mux(mux_id='mux_03') + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12.,1., cycles=2) + # k._hw.switch_mux([A, B, M, N], state='off') + # k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit + # k._hw.rx._bias = -1.38 + # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + A, B, M, N = (1, 4, 2, 3) + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3) + # k._hw.switch_mux([A, B, M, N], state='off') + # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + print('using OhmPi') + d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5) + print(d) + # k._hw._plot_readings() + print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') + print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + k._hw._plot_readings(save_fig=False) + # plot_exec_log('ohmpi/logs/exec.log') +change_config('../configs/config_default.py', verbose=False) diff --git a/dev/test_mb_2024_3_mux_2024.py b/dev/test_mb_2024_3_mux_2024.py new file mode 100644 index 0000000000000000000000000000000000000000..5d3a5e748cadb832e1e34626aa285f8719525ee9 --- /dev/null +++ b/dev/test_mb_2024_3_mux_2024.py @@ -0,0 +1,104 @@ +import matplotlib +matplotlib.use('TkAgg') +from ohmpi.utils import change_config +change_config('../configs/config_mb_2024_0_2__3_mux_2024_dps5005.py', verbose=False) +import importlib +import time +import logging +from ohmpi.config import HARDWARE_CONFIG + +stand_alone = False +part_of_hardware_system = True +within_ohmpi = False + +# Stand alone +if stand_alone: + + ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}') + pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}') + tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}') + rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}') + + ctl = ctl_module.Ctl() + HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['tx'].pop( + 'interface_name', 'i2c')])}) + HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['rx'].pop( + 'interface_name', 'i2c')])}) + + HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['pwr'].pop( + 'interface_name', None)])}) + + + rx = rx_module.Rx(**HARDWARE_CONFIG['rx']) + tx = tx_module.Tx(**HARDWARE_CONFIG['tx']) + pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr']) + mux_ids = ['mux_02', 'mux_05'] + for m,mux_id in enumerate(mux_ids): + mux_module = importlib.import_module( + f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}') + + MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id] + + MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[ + MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger, + 'soh_logger': ctl.soh_logger}) + MUX_CONFIG.update({'id': mux_id}) + mux = mux_module.Mux(**MUX_CONFIG) + + # tx.polarity = 1 + # time.sleep(1) + # tx.polarity = 0 + # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on') + # time.sleep(1) + # voltage = rx.voltage + # current = tx.current + # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off') + # print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA') + mux.reset() + mux.test({'A': [i+8*m for i in range(1, 9)], 'B': [i+8*m for i in range(1, 9)], + 'M': [i+8*m for i in range(1, 9)], 'N': [i+8*m for i in range(1, 9)]}, activation_time=.1) + mux.reset() + +# mux as part of a OhmPiHardware system +if part_of_hardware_system: + from ohmpi.hardware_system import OhmPiHardware + print('Starting test of as part of an OhmPiHardware system.') + # mux_id = 'mux_03' + k = OhmPiHardware() + k.exec_logger.setLevel(logging.DEBUG) + # Test mux switching + k.reset_mux() + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') + # time.sleep(1.) + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') + # k.mux_boards[mux_id].test(activation_time=.4) + k.test_mux() + k.reset_mux() + +if within_ohmpi: + from ohmpi.ohmpi import OhmPi + # from ohmpi.plots import plot_exec_log + + print('Starting test with OhmPi.') + k = OhmPi() + # A, B, M, N = (32, 29, 31, 30) + k.reset_mux() + k.load_sequence('sequences/ABMN2.txt') + k.run_sequence(tx_volt=50, injection_duration=1., nb_stack=2, duty_cycle=0.5) + print('using OhmPi') + #d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5) + # print(d) + # k._hw._plot_readings() + print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') + print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings(save_fig=False) + # plot_exec_log('ohmpi/logs/exec.log') +change_config('../configs/config_default.py', verbose=False) diff --git a/dev/test_mb_2024_4_mux_2023.py b/dev/test_mb_2024_4_mux_2023.py new file mode 100644 index 0000000000000000000000000000000000000000..ceceb8041908281afe7c1c9df930dc63aad601f1 --- /dev/null +++ b/dev/test_mb_2024_4_mux_2023.py @@ -0,0 +1,109 @@ +import matplotlib +matplotlib.use('TkAgg') +from ohmpi.utils import change_config +change_config('../configs/config_mb_2024_0_2__4_mux_2023_dps5005.py', verbose=False) +import importlib +import time +import logging +from ohmpi.config import HARDWARE_CONFIG + +stand_alone = True +part_of_hardware_system = False +within_ohmpi = False + +# Stand alone +if stand_alone: + + ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}') + pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}') + tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}') + rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}') + + ctl = ctl_module.Ctl() + HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['tx'].pop( + 'interface_name', 'i2c')])}) + HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['rx'].pop( + 'interface_name', 'i2c')])}) + + HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['pwr'].pop( + 'interface_name', None)])}) + + + rx = rx_module.Rx(**HARDWARE_CONFIG['rx']) + tx = tx_module.Tx(**HARDWARE_CONFIG['tx']) + pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr']) + role = 'A' + mux_id = f'mux_{role}' + mux_boards = [] + mux_module = importlib.import_module( + f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}') + + MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id] + + MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[ + MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger, + 'soh_logger': ctl.soh_logger}) + MUX_CONFIG.update({'id': mux_id}) + mux = mux_module.Mux(**MUX_CONFIG) + mux.reset() + + mux.test({role: [i for i in range(1, 65)]}, activation_time=.1) + mux.reset() + +# mux as part of a OhmPiHardware system +if part_of_hardware_system: + from ohmpi.hardware_system import OhmPiHardware + print('Starting test of as part of an OhmPiHardware system.') + # mux_id = 'mux_03' + k = OhmPiHardware() + k.exec_logger.setLevel(logging.DEBUG) + # Test mux switching + k.reset_mux() + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') + # time.sleep(1.) + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') + # k.mux_boards[mux_id].test(activation_time=.4) + k.test_mux() + k.reset_mux() + +if within_ohmpi: + from ohmpi.ohmpi import OhmPi + # from ohmpi.plots import plot_exec_log + + print('Starting test with OhmPi.') + k = OhmPi() + # A, B, M, N = (32, 29, 31, 30) + k.reset_mux() + # k.test_mux(mux_id='mux_03') + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12.,1., cycles=2) + # k._hw.switch_mux([A, B, M, N], state='off') + # k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit + # k._hw.rx._bias = -1.38 + # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + A, B, M, N = (1, 4, 2, 3) + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3) + # k._hw.switch_mux([A, B, M, N], state='off') + # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + k.load_sequence('sequences/9991_GRAD_16_s1_a1.txt') + k.run_sequence(tx_volt=5, injection_duration=1., nb_stack=2, duty_cycle=0.5) + print('using OhmPi') + #d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5) + # print(d) + # k._hw._plot_readings() + print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') + print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings(save_fig=False) + # plot_exec_log('ohmpi/logs/exec.log') +change_config('../configs/config_default.py', verbose=False) diff --git a/dev/test_mb_2024_4_mux_2024.py b/dev/test_mb_2024_4_mux_2024.py new file mode 100644 index 0000000000000000000000000000000000000000..f25c5d58ef10e6862af0c32d93ac5c9abc5579ab --- /dev/null +++ b/dev/test_mb_2024_4_mux_2024.py @@ -0,0 +1,118 @@ +import matplotlib +matplotlib.use('TkAgg') +from ohmpi.utils import change_config +change_config('../configs/config_mb_2024_0_2__4_mux_2024_dps5005.py', verbose=False) +import importlib +import time +import logging +from ohmpi.config import HARDWARE_CONFIG + +stand_alone = False +part_of_hardware_system = True +within_ohmpi = False + +# Stand alone +if stand_alone: + + ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}') + pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}') + tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}') + rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}') + + ctl = ctl_module.Ctl() + HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger}) + HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['tx'].pop( + 'interface_name', 'i2c')])}) + HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['rx'].pop( + 'interface_name', 'i2c')])}) + + HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection', + ctl.interfaces[ + HARDWARE_CONFIG['pwr'].pop( + 'interface_name', None)])}) + + + rx = rx_module.Rx(**HARDWARE_CONFIG['rx']) + tx = tx_module.Tx(**HARDWARE_CONFIG['tx']) + pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr']) + mux_ids = ['mux_02', 'mux_05'] + for m,mux_id in enumerate(mux_ids): + mux_module = importlib.import_module( + f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}') + + MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id] + + MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[ + MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger, + 'soh_logger': ctl.soh_logger}) + MUX_CONFIG.update({'id': mux_id}) + mux = mux_module.Mux(**MUX_CONFIG) + + # tx.polarity = 1 + # time.sleep(1) + # tx.polarity = 0 + # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='on') + # time.sleep(1) + # voltage = rx.voltage + # current = tx.current + # mux.switch(elec_dict={'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off') + # print(f'Resistance: {voltage / current :.2f} ohm, voltage: {voltage:.2f} mV, current: {current:.2f} mA') + mux.reset() + mux.test({'A': [i+8*m for i in range(1, 9)], 'B': [i+8*m for i in range(1, 9)], + 'M': [i+8*m for i in range(1, 9)], 'N': [i+8*m for i in range(1, 9)]}, activation_time=.1) + mux.reset() + +# mux as part of a OhmPiHardware system +if part_of_hardware_system: + from ohmpi.hardware_system import OhmPiHardware + print('Starting test of as part of an OhmPiHardware system.') + # mux_id = 'mux_03' + k = OhmPiHardware() + k.exec_logger.setLevel(logging.DEBUG) + # Test mux switching + k.reset_mux() + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') + # time.sleep(1.) + # k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') + # k.mux_boards[mux_id].test(activation_time=.4) + k.test_mux() + k.reset_mux() + +if within_ohmpi: + from ohmpi.ohmpi import OhmPi + # from ohmpi.plots import plot_exec_log + + print('Starting test with OhmPi.') + k = OhmPi() + # A, B, M, N = (32, 29, 31, 30) + k.reset_mux() + # k.test_mux(mux_id='mux_03') + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12.,1., cycles=2) + # k._hw.switch_mux([A, B, M, N], state='off') + # k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit + # k._hw.rx._bias = -1.38 + # print(f'Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + A, B, M, N = (1, 4, 2, 3) + # k._hw.switch_mux([A, B, M, N], state='on') + # k._hw.vab_square_wave(12., cycle_duration=10., cycles=3) + # k._hw.switch_mux([A, B, M, N], state='off') + # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + k.load_sequence('sequences/9991_GRAD_16_s1_a1.txt') + k.run_sequence(tx_volt=5, injection_duration=1., nb_stack=2, duty_cycle=0.5) + print('using OhmPi') + #d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5) + # print(d) + # k._hw._plot_readings() + print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') + print(f'OhmPi: Resistance: {d["R [ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings(save_fig=False) + # plot_exec_log('ohmpi/logs/exec.log') +change_config('../configs/config_default.py', verbose=False) diff --git a/dev/test_measure_with_mb_2023_0_X.py b/dev/test_measure_with_mb_2023_0_X.py deleted file mode 100644 index e20f4802e2398c9c939fdb2822ada152e89a3b61..0000000000000000000000000000000000000000 --- a/dev/test_measure_with_mb_2023_0_X.py +++ /dev/null @@ -1,53 +0,0 @@ -import numpy as np -import logging -import matplotlib.pyplot as plt -from ohmpi.utils import change_config -change_config('../configs/config_mb_2023_mux_2024.py', verbose=False) -from ohmpi.hardware_system import OhmPiHardware - -k = OhmPiHardware() -k.exec_logger.setLevel(logging.INFO) - -# Test _vab_pulse: -print('Testing positive _vab_pulse') -k._vab_pulse(vab=12, duration=1., sampling_rate=k.rx.sampling_rate, polarity=1) -r = k.readings[:,4]/k.readings[:,3] -print(f'Mean resistance: {np.mean(r):.3f} Ohms, Dev. {100*np.std(r)/np.mean(r):.1f} %') -print(f'sampling rate: {k.rx.sampling_rate:.1f} ms, mean sample spacing: {np.mean(np.diff(k.readings[:,0]))*1000.:.1f} ms') -print('\nTesting negative _vab_pulse') -k._vab_pulse(vab=12, duration=1., sampling_rate=k.rx.sampling_rate, polarity=-1) -r = k.readings[:,4]/k.readings[:,3] -print(f'Mean resistance: {np.mean(r):.3f} Ohms, Dev. {100*np.std(r)/np.mean(r):.1f} %') -print(f'sampling rate: {k.rx.sampling_rate:.1f} ms, mean sample spacing: {np.mean(np.diff(k.readings[:,0]))*1000.:.1f} ms') - -# Test vab_square_wave: -print('\n\nTesting vab_square_wave') -cycles=3 -cycle_length = 1. -k.vab_square_wave(vab=12, cycle_length=cycle_length, sampling_rate=k.rx.sampling_rate, cycles=cycles) -r = k.readings[:,4]/k.readings[:,3] -print(f'Mean resistance: {np.mean(r):.3f} Ohms, Dev. {100*np.std(r)/np.mean(r):.1f} %') -print(f'sampling rate: {k.rx.sampling_rate:.1f} ms, mean sample spacing: {np.mean(np.diff(k.readings[:,0]))*1000.:.1f} ms') -print(f'length of array: {len(r)}, expected length: {cycle_length*cycles*1000./k.rx.sampling_rate}') - -# Plot graphs -fig, ax = plt.subplots() -ax.plot(k.readings[:,0], k.readings[:,3], '-r', marker='.', label='iab') -ax.set_ylabel('Iab [mA]') -ax2 = ax.twinx() -ax2.plot(k.readings[:,0], k.readings[:,2]*k.readings[:,4], '-b', marker='.', label='vmn') -ax2.set_ylabel('Vmn [mV]') -fig.legend() -plt.show() - -# Compute resistances corrected for SP -print(f'SP: {k.sp} mV') -r = ((k.readings[:,4]-k.readings[:,2]*k.sp)/k.readings[:,3]) -print(f'Mean resistance with sp correction : {np.mean(r):.3f} Ohms, Dev. {100*np.std(r)/np.mean(r):.1f} %') -print('\nTesting with pulses') -r = [np.abs((k.pulses[i]['polarity']*k.pulses[i]['vmn']-k.sp)/k.pulses[i]['iab']) for i in k.pulses.keys()] -for i in range(len(r)): - print(f'Mean resistance with sp correction for pulse{i}: {np.mean(r[i]):.3f} Ohms, Dev. {100*np.std(r[i])/np.mean(r[i]):.1f} %') - -change_config('../configs/config_default.py', verbose=False) - diff --git a/dev/test_mux_2024.py b/dev/test_mux_2024.py deleted file mode 100644 index da950eb9ccabb20c8071678eaecd9a0c98f20c9a..0000000000000000000000000000000000000000 --- a/dev/test_mux_2024.py +++ /dev/null @@ -1,50 +0,0 @@ -import time -from ohmpi.utils import change_config -import logging -change_config('../configs/config_mb_2023_mux_2024.py', verbose=False) -from ohmpi.hardware_components.mux_2024_0_X import Mux, MUX_CONFIG -from ohmpi.hardware_components import raspberry_pi as ctl_module -# from ohmpi.config import HARDWARE_CONFIG - -stand_alone_mux = False # Testing hardware component alone -part_of_hardware_system = False # Testing hardware component as a part of the hardware system -within_ohmpi = True # Testing hardware component as a part of the hardware system through the ohmpi object -# Stand alone mux -if stand_alone_mux: - MUX_CONFIG['ctl'] = ctl_module.Ctl() - MUX_CONFIG['id'] = 'mux_1' - MUX_CONFIG['cabling'] = {(i+8, j) : ('mux_1', i) for j in ['A', 'B', 'M', 'N'] for i in range(1,9)} - mux = Mux(**MUX_CONFIG) - mux.switch_one(elec=9, role='M', state='on') - time.sleep(2) - mux.switch_one(elec=9, role='M', state='off') - mux.switch({'A': [9], 'B': [12], 'M': [10], 'N': [11]}, state='on') - time.sleep(8) - # mux.switch({'A': [1], 'B': [4], 'M': [2], 'N': [3]}, state='off') - mux.reset() - mux.test({'A': [9, 10, 11, 12, 13, 14, 15, 16], 'B': [9, 10, 11, 12, 13, 14, 15, 16], - 'M': [9, 10, 11, 12, 13, 14, 15, 16], 'N': [9, 10, 11, 12, 13, 14, 15, 16]}, activation_time=.1) - -# mux as part of a OhmPiHardware system -if part_of_hardware_system: - from ohmpi.hardware_system import OhmPiHardware - print('Starting test of mux as part of a OhmPiHardware system.') - - k = OhmPiHardware() - k.exec_logger.setLevel(logging.DEBUG) - - # Test mux switching - k.reset_mux() - k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on') - time.sleep(1.) - k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off') - -if within_ohmpi: - from ohmpi.ohmpi import OhmPi - print('Starting test of mux within OhmPi.') - k = OhmPi() - k.switch_mux_on([1, 4, 2, 3]) - time.sleep(1.) - k.reset_mux() - -change_config('../configs/config_default.py', verbose=False) \ No newline at end of file diff --git a/html/index-mqtt.html b/html/index-mqtt.html index 070d4fe13ea81700590c04d23493fbab8dcb512a..7c121aea79adcce5d3177f0c3e56e78e122f244a 100755 --- a/html/index-mqtt.html +++ b/html/index-mqtt.html @@ -59,7 +59,6 @@ <!-- Additional buttons --> <button id="downloadBtn" type="button" class="btn btn-primary">Download data</button> - <!-- <button id="invertBtn" type="button" class="btn btn-primary">Invert</button> --> <a id="download"></a> <!-- Modal for configuration --> diff --git a/index-mqtt.html b/index-mqtt.html deleted file mode 100755 index 070d4fe13ea81700590c04d23493fbab8dcb512a..0000000000000000000000000000000000000000 --- a/index-mqtt.html +++ /dev/null @@ -1,654 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta charset="utf8"/> - <title>OhmPi Acquisition Board</title> - <link rel="shortcut icon" type="image/jpg" href="logo_ohmpi.jpg"/> - - <!-- dependencies (need to be local as no internet in AP mode)--> - <script src="js/plotly-basic-2.8.3.min.js"></script> - <script src="js/jquery-3.4.1.min.js"></script> - <link type="text/css" href="css/bootstrap.min.css" rel="stylesheet"> - <!-- <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> --> - <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous"> --> - <!-- <script src="js/danfojs/bundle.min.js"></script> --> - <!-- <script src="js/mqtt.min.js"></script> --> - <script src="js/paho/paho-mqtt.js"></script> -</head> -<body> - <div class='container'> - <h1>OhmPi Acquisition Board</h1> - <!-- nb stacks, on-time --> - <button id="update_settingsBtn" type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModal">Settings</button> - <button id='runBtn' type="button" class="btn btn-primary">▶</button> - <button id='stopBtn' type="button" class="btn btn-warning">◼</button> - <!-- upload button for csv which display the table ABMN --> - <button id="removeDataBtn" type="button" class="btn btn-danger">Clear data</button> - <button id="getDataBtn" type="button" class="btn btn-info">Get data</button> - <div class="form-check"> - <input id="dataRetrievalCheck" class="form-check-input" type="checkbox" value=""> - <label class="form-check-label" for="dataRetrievalCheck"> - Automaticaly get data every 1 secondStart - </label> - </div> - <div id='output'>Status: idle</div> - - <!-- Pseudo section --> - <select id='surveySelect' class='custom-select'> - </select> - <input id="cmin" type="number" value="0"/> - <input id="cmax" type="number" value="150"/> - <button id="capplyBtn" type="button" class="btn btn-info">Apply</button> - <div id="gd"></div> - <div class="mb3 row"> - <label for="quadSelect">Quadrupole:</label> - <div class="col-sm-10"> - <select id='quadSelect' class='custom-select'></select> - </div> - </div> - - <!-- trace figure --> - <button id="addTraceBtn" type="button" class="btn btn-info">Add trace</button> - <button id="removeTracesBtn" type="button" class="btn btn-info">Remove all traces</button> - <div id="ts"></div> - - <!-- RS check --> - <button id="rsBtn" type="button" class="btn btn-info">Check contact resistance</button> - <button id="rsClearBtn" type="button" class="btn btn-info">Clear plot</button> - <div id="rs"></div> - - <!-- Additional buttons --> - <button id="downloadBtn" type="button" class="btn btn-primary">Download data</button> - <!-- <button id="invertBtn" type="button" class="btn btn-primary">Invert</button> --> - <a id="download"></a> - - <!-- Modal for configuration --> - <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> - <div class="modal-dialog" role="document"> - <div class="modal-content"> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel">OhmPi configuration</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - <div class="modal-body"> - <form> - <div class="form-group row"> - <label for="nbElectrodes" class="col-sm-2 col-form-label">Nb electrodes</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbElectrodes" value="64"> - </div> - </div> - <div class="form-group row"> - <label for="injectionDuration" class="col-sm-2 col-form-label">Injection duration [s]</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="injectionDuration" value="0.2"> - </div> - </div> - <div class="form-group row"> - <label for="nbMeasurements" class="col-sm-2 col-form-label">Nb Measurements</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbMeasurements" value="1"> - </div> - </div> - <div class="form-group row"> - <label for="sequenceDelay" class="col-sm-2 col-form-label">Sequence delay [s]</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="sequenceDelay" value="100"> - </div> - </div> - <div class="form-group row"> - <label for="nbStack" class="col-sm-2 col-form-label">Nb stack</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbStack" value="1"> - </div> - </div> - <div class="form-group row"> - <label for="sequence" class="col-sm-2 col-form-label">Sequence</label> - <div class="col-sm-10"> - <input type="file" class="form-control" id="sequence"> - </div> - </div> - <div class="form-group row"> - <label for="elecSpacing" class="col-sm-2 col-form-label">Electrode spacing [m]</label> - <div class="col-sm-10"> - <input type="number" class="form-control" id="elecSpacing", value="1"> - </div> - </div> - </form> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> - <button id="saveConfigBtn" type="button" data-dismiss="modal" class="btn btn-primary">Save</button> - </div> - </div> - </div> - </div> - <button id="restartBtn" type="button" class="btn btn-danger">Restart</button> - <button id="shutdownBtn" type="button" class="btn btn-danger">Shutdown</button> - <footer>v0.2.0</footer> - </div> - - <script type="text/javascript"> - //let serverUrl = 'http://10.3.141.1:8080' - //let serverUrl = 'http://0.0.0.0:8080' - //let serverUrl = 'http://localhost:8080' - let serverUrl = 'http://' + window.location.host - console.log('serverUrl =', serverUrl) - let output = document.getElementById('output') - let data = {} // hold data of all surveys - let interv = null // hold interval for automatic data retrieval - let quads = [] // available quadrupoles for time-serie figure - let squads = [] // selected quadrupoles for time-serie figure - let commands = {} // store commands and their id - let callbacks = {} // store callback (might not be needed) - - // function with MQTT - let topic = 'ohmpi_0001' // we could change this through a drop-down to connect to a different ohmpi - let topic_ctrl = topic + '/ctrl' - let topic_exec = topic + '/exec' - let topic_data = topic + '/data' - let hostname = location.hostname - let port = 9001 - let clientId = 'ohmpi_0001_html' - let message = null - let msg = '' - - // create client - client = new Paho.MQTT.Client(hostname, port, clientId); - client.onConnectionLost = onConnectionLost; - client.onMessageArrived = onMessageArrived; - client.connect({onSuccess:onConnect}); - - function onConnect() { - console.log("onConnect") - client.subscribe(topic_data) - client.subscribe(topic_exec) - - // send welcome message - message = new Paho.MQTT.Message("Hello from index.html") - message.destinationName = topic_ctrl - client.send(message) - } - - function onConnectionLost(responseObject) { - if (responseObject.errorCode !== 0) - console.log("onConnectionLost:" + responseObject.errorMessage) - } - - function onMessageArrived(message) { - console.log("onMessageArrived:" + message.payloadString) - try { - let payload = message.payloadString - if (message.topic == topic_data) { - // process data - msg = payload // for accessing the variable from the console - console.log('DATA', payload) - let ddic = JSON.parse(payload.split('INFO:')[1]) - - // check cmd_id is any - processData(ddic) - - // usually these don't have a cmd_id so we are not sure when - - } else if (message.topic == topic_exec) { - // display it in the log - console.log('EXEC LOG:', payload) - } - - // let response = JSON.parse(message.payloadString) - // console.log('response=', response) - // // check ID of message against our dictionnary of callback - // let cmd_id = response['cmd_id'] - // if (callbacks.hasOwnProperty(cmd_id)) { - // console.log('++ execute callback') - // callbacks[cmd_id](response['content']) // execute callback - // } - } catch (e) { - console.log(e) - } - // client.disconnect() - } - - // useful functions - function generateUniqSerial() { - return 'xxxx-xxxx-xxx-xxxx'.replace(/[x]/g, (c) => { - const r = Math.floor(Math.random() * 16); - return r.toString(16); - }); - } - - // sending commands to the OhmPi - function sendCommand(query, callback=null) { - // dic in the form: {'cmd': X, ...} as JSON - if (callback == null) { - function callback(x) { - console.log('default callback:', x) - } - } - - /* - let xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if (this.readyState == 4) { - if (xhr.status == 200) { - callback(JSON.parse(xhr.response)) - } - } - } - xhr.open('POST', serverUrl) - xhr.setRequestHeader('Content-Type', 'application/json') - xhr.send(query) - */ - - // generate a unique command id to be associated with the commands - let uuid = generateUniqSerial() - commands[uuid] = query - callbacks[uuid] = callback // store the callback to be processed later when message arrives - let payload = '{"cmd_id": "' + uuid + '",' + query.slice(1) - console.log('sendCommand()', payload) - message = new Paho.MQTT.Message(payload) - message.destinationName = topic_ctrl - client.send(message) - } - - // run button - function runBtnFunc() { - sendCommand('{"cmd": "run_multiple_sequences"}', function(x) { - console.log(x['status']) - if (x['status'] == 'running') { - output.innerHTML = 'Status: measuring...' - } - }) - } - let runBtn = document.getElementById('runBtn') - runBtn.addEventListener('click', runBtnFunc) - - // interrupt button - function stopBtnFunc() { - sendCommand('{"cmd": "interrupt"}', function(x) { - output.innerHTML = 'Status: ' + x['status'] - clearInterval(interv) - getData() - }) - } - let stopBtn = document.getElementById('stopBtn') - stopBtn.addEventListener('click', stopBtnFunc) - - // set configuration - function saveConfigBtnFunc() { - // collect values from modal - let formVals = {} - for (let field of ['nbElectrodes', 'injectionDuration', - 'nbMeasurements', 'sequenceDelay', 'nbStack']) { - formVals[field] = document.getElementById(field).value - } - console.log(formVals) - - // define callback to send settings to Pi - function configCallback() { - sendCommand(JSON.stringify({ - 'cmd': 'update_settings', - 'kwargs': { - 'config': formVals - } - }), function(x) { - console.log('update_settings', x) - }) - } - - // deal with the potential file containing the sequence - // https://stackoverflow.com/questions/19038919/is-it-possible-to-upload-a-text-file-to-input-in-html-js - if (!window.FileReader) { - alert('Your browser is not supported'); - return false; - } - let input = document.getElementById('sequence') - if (input.files.length) { - const reader = new FileReader() - reader.readAsText(input.files[0]) - reader.addEventListener('load', () => { - formVals['sequence'] = reader.result - console.log('file==', reader.result) - configCallback() - }, false) - } else { - console.log('no sequence uploaded') - formVals['sequence'] = '' - configCallback() - } - - - } - let saveConfigBtn = document.getElementById('saveConfigBtn') - saveConfigBtn.addEventListener('click', saveConfigBtnFunc) - - // make pseudo plot - var trace = { - x: [], - y: [], - mode: 'markers', - marker: { - size: 40, - color: [], - colorbar: { - title: 'App. res. [Ohm.m]', - cmin: 0, - cmax: 100, - } - } - } - let layout = { - title: 'Pseudo-section', - yaxis: { - title: 'Pseudo-depth', - autorange: 'reversed' - }, - xaxis: { - title: 'X' - } - - } - Plotly.newPlot('gd', [trace], layout) - - // make time-serie plot - let tdata = [] - let layout2 = { - title: 'Time-serie', - yaxis: { - title: 'App. res. [Ohm.m]' - }, - xaxis: { - title: 'Sampling time' - } - } - Plotly.newPlot('ts', tdata, layout2) - - // add trace to time-serie plot - function addTraceBtnFunc() { - let val = document.getElementById('quadSelect').value - squads.push(val.split(', ')) - tdata.push({ - x: [], - y: [], - name: val, - type: 'scatter' - }) - Plotly.newPlot('ts', tdata, layout2) - getData() - } - let addTraceBtn = document.getElementById('addTraceBtn') - addTraceBtn.addEventListener('click', addTraceBtnFunc) - - // remove all traces from time-serie plot - function removeTracesBtnFunc() { - squads = [] - tdata = [] - Plotly.newPlot('ts', tdata, layout2) - } - let removeTracesBtn = document.getElementById('removeTracesBtn') - removeTracesBtn.addEventListener('click', removeTracesBtnFunc) - - // callback function to draw the plot - function surveySelectFunc(el) { - let surveyName = el['target'].value - let df = data[surveyName] - if (df != undefined) { - let a = df['a'] - let b = df['b'] - let m = df['m'] - let n = df['n'] - // let's assume electrodes are 1 m distance - // compute pseudo-depth (assume no topo) - // compute app res (assumping flat, line survey) - let xpos = [] - let ypos = [] - let app = [] - for (let i = 0; i < a.length; i++) { - let ab = (a[i] + b[i])/2 - let mn = (m[i] + n[i])/2 - let dist = Math.abs(ab - mn) - xpos.push(Math.min(ab, mn) + dist/2) - ypos.push(Math.sqrt(2)/2*dist) - let am = Math.abs(a[i] - m[i]) - let bm = Math.abs(b[i] - m[i]) - let an = Math.abs(a[i] - n[i]) - let bn = Math.abs(a[i] - n[i]) - let K = (2*Math.PI)/((1/am)-(1/an)-(1/an)+(1/bn)) - app.push(df['rho'][i]*-K) - } - console.log(app) - // update the trace and redraw the figure - trace['x'] = xpos - trace['y'] = ypos - trace['marker']['color'] = app - trace['marker']['cmax'] = document.getElementById('cmax').value - trace['marker']['cmin'] = document.getElementById('cmin').value - Plotly.redraw('gd') - } - } - let surveySelect = document.getElementById('surveySelect') - - // bar chart for contact resistance - let rsdata = [] - let rslayout = { - title: 'Contact resistances', - yaxis: { - title: 'Resistance [kOhm]' - }, - xaxis: { - title: 'Consecutive electrodes' - } - } - Plotly.newPlot('rs', rsdata, rslayout) - - // run RS check - function rsBtnFunc() { - sendCommand('{"cmd": "rsCheck"}', function (res) { - // update the bar plot - rsdata.push({ - x: res['data']['AB'], - y: res['data']['res'], - name: 'RS', - type: 'bar' - }) - Plotly.redraw('rs') - }) - } - let rsBtn = document.getElementById('rsBtn') - rsBtn.addEventListener('click', rsBtnFunc) - - // clear RS graph - function rsClearBtnFunc() { - rsdata = [] - Plotly.newPlot('rs', rsdata, rslayout) - } - let rsClearBtn = document.getElementById('rsClearBtn') - rsClearBtn.addEventListener('click', rsClearBtnFunc) - - // getData - function getData() { - sendCommand(JSON.stringify({ - 'cmd': 'get_data', - 'survey_names': Object.keys(data).slice(0, -1) - // last survey is often partial so we download it again - }), console.log('processData(ddic)') - ) - } - - // processData - function processData(ddic) { - // update status - output.innerHTML = 'Status: ' + ddic['status'] - - // update data dic with new data - data = { // destructuring assignement (magic! :o) - ...data, - ...ddic['data'] // value from second dic are preferred - } - - // dropdown with number of surveys and +++ - let surveyNames = Object.keys(data).sort() - - // remove listener as we will replace the choices - surveySelect.removeEventListener('change', surveySelectFunc) - surveySelect.innerHTML = '' // clearing all child nodes - - // add choices again - for (let surveyName of surveyNames) { - let option = document.createElement('option') - option.innerText = surveyName - option.value = surveyName - surveySelect.appendChild(option) - } - - // listener again - surveySelect.addEventListener('change', surveySelectFunc) - - // plot last one by default - surveySelect.value = surveyNames[surveyNames.length - 1] - - // call the function directly - // (as progammatically chaging the value does not trigger the event) - surveySelectFunc({'target': surveySelect}) - - // update list of quadrupoles if any - if (quads.length == 0) { - console.log('updating list of quadrupoles') - let df = data[surveyNames[0]] - let quadSelect = document.getElementById('quadSelect') - quadSelect.innerHTML = '' - for (let i = 0; i < df['a'].length; i++) { - quad = [df['a'][i], df['b'][i], df['m'][i], df['n'][i]] - quads.push(quad) - let option = document.createElement('option') - option.value = quad.join(', ') - option.innerText = quad.join(', ') - quadSelect.appendChild(option) - } - console.log('quads=', quads) - } - - // update time-serie figure - if (squads.length > 0) { - - // transform all surveyNames to datetime - let xt = [] - for (surveyName of surveyNames) { - let a = surveyName.split('_').slice(-1)[0] - xt.push(a.slice(0, 4) + '-' - + a.slice(4, 6) + '-' - + a.slice(6, 8) + ' ' - + a.slice(9, 11) + ':' - + a.slice(11, 13) + ':' - + a.slice(13, 15)) - } - //console.log(xt) - - // create one new trace per selected quadrupole - for (let k = 0; k < squads.length; k++) { - squad = squads[k] - let x = [] - let y = [] - for (let i = 0; i < surveyNames.length; i++) { - df = data[surveyNames[i]] - for (let j = 0; j < df['a'].length; j++) { - if (df['a'][j] == squad[0] - && df['b'][j] == squad[1] - && df['m'][j] == squad[2] - && df['n'][j] == squad[3]) { - y.push(df['rho'][j]) - x.push(xt[i]) - break - } - } - } - - // update trace dictionnary - tdata[k]['x'] = x - tdata[k]['y'] = y - } - //console.log(tdata) - Plotly.redraw('ts') - } - } - - let getDataBtn = document.getElementById('getDataBtn') - getDataBtn.addEventListener('click', getData) - - // apply new colorscale - let capplyBtn = document.getElementById('capplyBtn') - capplyBtn.addEventListener('click', function() { - surveySelectFunc({'target': surveySelect}) - }) - - // checkbox interaction for data download - function dataRetrievalCheckFunc(x) { - if (x['target'].checked == true) { - interv = setInterval(getData, 1000) // every 5s - } else { - clearInterval(interv) - } - } - let dataRetrievalCheck = document.getElementById('dataRetrievalCheck') - dataRetrievalCheck.addEventListener('change', dataRetrievalCheckFunc) - - // remove data - function removeDataBtnFunc() { - sendCommand('{"cmd": "removeData"}',function(x) { - data = {} - output.innerHTML = 'Status: ' + x['status'] + ' (all data cleared)' - console.log('all data removed') - }) - } - let removeDataBtn = document.getElementById('removeDataBtn') - removeDataBtn.addEventListener('click', removeDataBtnFunc) - - // shutdown Pi - function shutdownBtnFunc() { - sendCommand('{"cmd": "shutdown"}', function(x) { - console.log('shuting down...') - }) - } - let shutdownBtn = document.getElementById('shutdownBtn') - shutdownBtn.addEventListener('click', shutdownBtnFunc) - - // restart Pi - function restartBtnFunc() { - sendCommand('{"cmd": "restart"}', function(x) { - console.log('rebooting...') - }) - } - let restartBtn = document.getElementById('restartBtn') - restartBtn.addEventListener('click', restartBtnFunc) - - // invert data - // function invertBtnFunc() { - // sendCommand('{"cmd": "invert"}', function(x) { - // console.log('inversion results', x) - // }) - // } - // let invertBtn = document.getElementById('invertBtn') - // invertBtn.addEventListener('click', invertBtnFunc) - - // download data - function downloadBtnFunc() { - sendCommand('{"cmd": "download"}', function(x) { - let dwl = document.getElementById('download') - dwl.setAttribute('href', serverUrl + '/data.zip') - dwl.setAttribute('download', 'data.zip') - dwl.click() - }) - } - let downloadBtn = document.getElementById('downloadBtn') - downloadBtn.addEventListener('click', downloadBtnFunc) - - - </script> - - <!-- Boostrap scripts (at the end of the page for faster loading time)--> - <script src="js/bootstrap.bundle.min.js"></script> - <!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script> --> -</body> -</html> diff --git a/index.html b/index.html deleted file mode 100644 index b334d18b47d1f8ab64e32028a15fac4c0b513d7e..0000000000000000000000000000000000000000 --- a/index.html +++ /dev/null @@ -1,704 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta charset="utf8"/> - <title>OhmPi Acquisition Board</title> - <link rel="shortcut icon" type="image/jpg" href="logo_ohmpi.jpg"/> - - <!-- dependencies (need to be local as no internet in AP mode)--> - <script src="js/plotly-basic-2.8.3.min.js"></script> - <script src="js/jquery-3.4.1.min.js"></script> - <link type="text/css" href="css/bootstrap.min.css" rel="stylesheet"> - <!-- <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> --> - <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous"> --> - <!-- <script src="js/danfojs/bundle.min.js"></script> --> -</head> -<body> - <div class='container'> - <h1>OhmPi Acquisition Board</h1> - <!-- nb stacks, on-time --> - <button id="update_settingsBtn" type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModal">Settings</button> - <button id='runBtn' type="button" class="btn btn-primary">▶</button> - <button id='stopBtn' type="button" class="btn btn-warning">◼</button> - <!-- upload button for csv which display the table ABMN --> - <button id="removeDataBtn" type="button" class="btn btn-danger">Clear data</button> - <button id="getDataBtn" type="button" class="btn btn-info">Get data</button> - <div class="form-check"> - <input id="dataRetrievalCheck" class="form-check-input" type="checkbox" value=""> - <label class="form-check-label" for="dataRetrievalCheck"> - Automaticaly get data every 1 second - </label> - </div> - <div id='output'>Status: idle</div> - - <!-- Pseudo section --> - <select id='surveySelect' class='custom-select'> - </select> - <input id="cmin" type="number" value=""/> - <input id="cmax" type="number" value=""/> - <button id="capplyBtn" type="button" class="btn btn-info">Apply</button> - <div id="gd"></div> - <div id="hoverinfo" style="margin-left:80px;"></div> - <div class="mb3 row"> - <label for="quadSelect">Quadrupole:</label> - <div class="col-sm-10"> - <select id='quadSelect' class='custom-select'></select> - </div> - </div> - - <!-- trace figure --> - <button id="addTraceBtn" type="button" class="btn btn-info">Add trace</button> - <button id="removeTracesBtn" type="button" class="btn btn-info">Remove all traces</button> - <div id="ts"></div> - - <!-- RS check --> - <button id="rsBtn" type="button" class="btn btn-info">Check contact resistance</button> - <button id="getRsBtn" type="button" class="btn btn-info">Get contact resistance</button> - <button id="rsClearBtn" type="button" class="btn btn-info">Clear plot</button> - <div id="rs"></div> - - <!-- Additional buttons --> - <button id="downloadBtn" type="button" class="btn btn-primary">Download data</button> - <!-- <button id="invertBtn" type="button" class="btn btn-primary">Invert</button> --> - <a id="download"></a> - - <!-- Modal for settings --> - <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> - <div class="modal-dialog" role="document"> - <div class="modal-content"> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel">OhmPi settings</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - <div class="modal-body"> - <form> - <div class="form-group row"> - <label for="nbElectrodes" class="col-sm-2 col-form-label">Nb electrodes</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbElectrodes" value=64> - </div> - </div> - <div class="form-group row"> - <label for="injectionDuration" class="col-sm-2 col-form-label">Injection duration [s]</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="injectionDuration" value=0.2> - </div> - </div> - <div class="form-group row"> - <label for="nbMeasurements" class="col-sm-2 col-form-label">Nb Measurements</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbMeasurements" value="1"> - </div> - </div> - <div class="form-group row"> - <label for="sequenceDelay" class="col-sm-2 col-form-label">Sequence delay [s]</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="sequenceDelay" value="100"> - </div> - </div> - <div class="form-group row"> - <label for="nbStack" class="col-sm-2 col-form-label">Nb stack</label> - <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbStack" value="1"> - </div> - </div> - <div class="form-group row"> - <label for="sequence" class="col-sm-2 col-form-label">Sequence</label> - <div class="col-sm-10"> - <input type="file" class="form-control" id="sequence"> - </div> - </div> - <div class="form-group row"> - <label for="elecSpacing" class="col-sm-2 col-form-label">Electrode spacing [m]</label> - <div class="col-sm-10"> - <input type="number" class="form-control" id="elecSpacing", value="1"> - </div> - </div> - </form> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> - <button id="saveConfigBtn" type="button" data-dismiss="modal" class="btn btn-primary">Save</button> - </div> - </div> - </div> - </div> - <button id="restartBtn" type="button" class="btn btn-danger">Restart</button> - <button id="shutdownBtn" type="button" class="btn btn-danger">Shutdown</button> - <footer>v0.2.0</footer> - </div> - - <script type="text/javascript"> - //let serverUrl = 'http://10.3.141.1:8080' - //let serverUrl = 'http://0.0.0.0:8080' - //let serverUrl = 'http://localhost:8080' - let serverUrl = 'http://' + window.location.host - console.log('serverUrl =', serverUrl) - let output = document.getElementById('output') - let data = {} // hold data of all surveys - let interv = null // hold interval for automatic data retrieval - let quads = [] // available quadrupoles for time-serie figure - let squads = [] // selected quadrupoles for time-serie figure - let elecSpacing = 1 // 1 m - - // useful functions - function sendCommand(query, callback=null) { - // dic in the form: {'cmd': X, ...} as JSON - if (callback == null) { - function callback(x) { - console.log('default callback:', x) - } - } - let xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if (this.readyState == 4) { - if (xhr.status == 200) { - callback(JSON.parse(xhr.response)) - } - } - } - xhr.open('POST', serverUrl) - xhr.setRequestHeader('Content-Type', 'application/json') - xhr.send(query) - } - - // run button - function runBtnFunc() { - sendCommand('{"cmd": "run_multiple_sequences"}', function(x) { - console.log(x['ohmpi_status']) - if (x['ohmpi_status'] == 'running') { - output.innerHTML = 'Status: measuring...' - } - }) - } - let runBtn = document.getElementById('runBtn') - runBtn.addEventListener('click', runBtnFunc) - - // interrupt button - function stopBtnFunc() { - sendCommand('{"cmd": "interrupt"}', function(x) { - output.innerHTML = 'Status: ' + x['ohmpi_status'] - clearInterval(interv) - getData() - }) - } - let stopBtn = document.getElementById('stopBtn') - stopBtn.addEventListener('click', stopBtnFunc) - - // set configuration - function saveSettingsBtnFunc() { - // collect values from modal - let formVals = {} - formVals['nb_electrodes'] = parseInt(document.getElementById('nbElectrodes').value) - formVals['injection_duration'] = parseFloat(document.getElementById('injectionDuration').value) - formVals['nb_meas'] = parseInt(document.getElementById('nbMeasurements').value) - formVals['sequence_delay'] = parseInt(document.getElementById('sequenceDelay').value) - formVals['nb_stack'] = parseInt(document.getElementById('nbStack').value) - formVals['elec_spacing'] = parseFloat(document.getElementById('elecSpacing').value) - console.log(formVals) - elecSpacing = formVals['elec_spacing'] - - // define callback to send settigs to Pi - function settingsCallback() { - sendCommand(JSON.stringify({ - 'cmd': 'update_settings', - 'config': formVals - }), function(x) { - console.log('update_settings', x) - }) - } - - // deal with the potential file containing the sequence - // https://stackoverflow.com/questions/19038919/is-it-possible-to-upload-a-text-file-to-input-in-html-js - if (!window.FileReader) { - alert('Your browser is not supported'); - return false; - } - let input = document.getElementById('sequence') - if (input.files.length) { - const reader = new FileReader() - reader.readAsText(input.files[0]) - reader.addEventListener('load', () => { - formVals['sequence'] = reader.result - console.log('file==', reader.result) - settingsCallback() - }, false) - } else { - console.log('no sequence uploaded') - formVals['sequence'] = '' - settingsCallback() - } - - - } - let saveConfigBtn = document.getElementById('saveConfigBtn') - saveConfigBtn.addEventListener('click', saveSettingsBtnFunc) - - // make pseudo plot - var trace = {} - let layout = {} - let tdata = [] - let layout2 = {} - let rsdata = [] - let rslayout = {} - - // initialize all plots - function initPlots() { - trace = { - x: [], - y: [], - mode: 'markers', - marker: { - size: 40, - color: [], - colorbar: { - title: 'App. res. [Ohm.m]', - cmin: 0, - cmax: 100, - } - } - } - layout = { - title: 'Pseudo-section', - yaxis: { - title: 'Pseudo-depth', - autorange: 'reversed' - }, - xaxis: { - title: 'X' - } - - } - Plotly.newPlot('gd', [trace], layout) - - // make time-serie plot - tdata = [] - layout2 = { - title: 'Time-serie', - yaxis: { - title: 'App. res. [Ohm.m]' - }, - xaxis: { - title: 'Sampling time' - } - } - Plotly.newPlot('ts', tdata, layout2) - - // bar chart for contact resistance - rsdata = [] - rslayout = { - title: 'Contact resistances', - yaxis: { - title: 'Resistance [kOhm]' - }, - xaxis: { - title: 'Consecutive electrodes' - } - } - Plotly.newPlot('rs', rsdata, rslayout) - } - initPlots() - - // hover function - var hoverInfo = document.getElementById('hoverinfo') - document.getElementById('gd').on('plotly_hover', function(data){ - var infotext = data.points.map(function(d){ - return (Math.round(d.data.marker.color[d.pointIndex], 2) + ' Ohm.m'); - }); - hoverInfo.innerHTML = infotext.join('<br/>'); - }) - .on('plotly_unhover', function(data){ - hoverInfo.innerHTML = ''; - }); - - // add trace to time-serie plot - function addTraceBtnFunc() { - let val = document.getElementById('quadSelect').value - squads.push(val.split(', ')) - tdata.push({ - x: [], - y: [], - name: val, - type: 'scatter' - }) - Plotly.newPlot('ts', tdata, layout2) - getData() - } - let addTraceBtn = document.getElementById('addTraceBtn') - addTraceBtn.addEventListener('click', addTraceBtnFunc) - - // remove all traces from time-serie plot - function removeTracesBtnFunc() { - squads = [] - tdata = [] - Plotly.newPlot('ts', tdata, layout2) - } - let removeTracesBtn = document.getElementById('removeTracesBtn') - removeTracesBtn.addEventListener('click', removeTracesBtnFunc) - - // callback function to draw the plot - function surveySelectFunc(el) { - let surveyName = el['target'].value - let df = data[surveyName] - if (df != undefined) { - // let's assume electrodes are 1 m distance - // compute pseudo-depth (assume no topo) - // compute app res (assumping flat, line survey) - let xpos = [] - let ypos = [] - let app = [] - for (let i = 0; i < df['a'].length; i++) { - let a = df['a'][i] - let b = df['b'][i] - let m = df['m'][i] - let n = df['n'][i] - - // compute geometric factor assuming flat 2D surface - let am = Math.abs(a - m)*elecSpacing - let bm = Math.abs(b - m)*elecSpacing - let an = Math.abs(a - n)*elecSpacing - let bn = Math.abs(b - n)*elecSpacing - let K = 2*Math.PI/((1/am)-(1/bm)-(1/an)+(1/bn)) - app.push(df['rho'][i]*K) - //console.log(K) // same as resipy for the wenner case - - // computing pseudo-depth assuming 2D flat array - // let's sort the electrodes AB are the two left, MN, the two right - let abmn = [a, b, m, n] - abmn = abmn.sort((a, b) => a - b) - let ab = (abmn[0] + abmn[1])/2 - let mn = (abmn[2] + abmn[3])/2 - let dist = Math.abs(ab - mn) - xpos.push((Math.min(ab, mn) + dist/2)*elecSpacing) - ypos.push((Math.sqrt(2)/2*dist)*elecSpacing) - - /* - lookupDict = dict(zip(self.elec['label'], np.arange(self.elec.shape[0]))) - array = self.df[['a','b','m','n']].replace(lookupDict).values.astype(int) - elecm = self.elec[['x','y','z']].values.astype(float).copy() # electrode matrix - should be array of floats so np.inf work properly - - ### first determine if measurements are nested ### - #find mid points of AB - AB = (elecm[array[:,0]] + elecm[array[:,1]]) / 2 # mid points of AB - MN = (elecm[array[:,2]] + elecm[array[:,3]]) / 2 # mid points of MN - ABrad = np.sqrt(np.sum((elecm[array[:,0]] - AB)**2,axis=1)) # radius of AB circle - MNrad = np.sqrt(np.sum((elecm[array[:,2]] - MN)**2,axis=1)) # radius of MN circle - - Amn = np.sqrt(np.sum((elecm[array[:,0]] - MN)**2,axis=1)) # distance of A to mid point of MN - Bmn = np.sqrt(np.sum((elecm[array[:,1]] - MN)**2,axis=1)) # distance of B to mid point of MN - Nab = np.sqrt(np.sum((elecm[array[:,2]] - AB)**2,axis=1)) # distance of N to mid point of AB - Mab = np.sqrt(np.sum((elecm[array[:,3]] - AB)**2,axis=1)) # distance of M to mid point of AB - - iABinMN = (Amn < MNrad) & (Bmn < MNrad) - iMNinAB = (Nab < ABrad) & (Mab < ABrad) - inested = iABinMN | iMNinAB #if AB encompasses MN or MN encompasses AB - - # so it will never be taken as minimium - elecm[self.elec['remote'].values,:] = np.inf - - # compute midpoint position of AB and MN dipoles - elecx = elecm[:,0] - elecy = elecm[:,1] - - #CURRENT ELECTRODE MIDPOINTS - caddx = np.abs(elecx[array[:,0]]-elecx[array[:,1]])/2 - caddy = np.abs(elecy[array[:,0]]-elecy[array[:,1]])/2 - caddx[np.isinf(caddx)] = 0 - caddy[np.isinf(caddy)] = 0 - cmiddlex = np.min([elecx[array[:,0]], elecx[array[:,1]]], axis=0) + caddx - cmiddley = np.min([elecy[array[:,0]], elecy[array[:,1]]], axis=0) + caddy - - #POTENTIAL ELECTRODE MIDPOINTS - paddx = np.abs(elecx[array[:,2]]-elecx[array[:,3]])/2 - paddy = np.abs(elecy[array[:,2]]-elecy[array[:,3]])/2 - paddx[np.isinf(paddx)] = 0 - paddy[np.isinf(paddy)] = 0 - pmiddlex = np.min([elecx[array[:,2]], elecx[array[:,3]]], axis=0) + paddx - pmiddley = np.min([elecy[array[:,2]], elecy[array[:,3]]], axis=0) + paddy - - - # for non-nested measurements - xposNonNested = np.min([cmiddlex, pmiddlex], axis=0) + np.abs(cmiddlex-pmiddlex)/2 - yposNonNested = np.min([cmiddley, pmiddley], axis=0) + np.abs(cmiddley-pmiddley)/2 - pcdist = np.sqrt((cmiddlex-pmiddlex)**2 + (cmiddley-pmiddley)**2) - - # zposNonNested = np.sqrt(2)/2*pcdist - zposNonNested = pcdist/4 - - if np.all(cmiddley-pmiddley == 0): - zposNonNested = 0.25*pcdist - else: # for 3D arrays where there are mid-line measurements, this works closer to inversion results - zposNonNested = np.sqrt(2)/2*pcdist - - # for nested measurements use formula of Dalhin 2006 - xposNested = np.zeros(len(pmiddlex)) - yposNested = np.zeros(len(pmiddlex)) - outerElec1 = np.zeros((len(pmiddlex), 2)) # position of one electrode of outer dipole - outerElec2 = np.zeros((len(pmiddlex), 2)) # position of one electrode of outer dipole - # innerMid = np.zeros((len(pmiddlex), 2)) # middle of inner dipole - if np.sum(iMNinAB) > 0: - xposNested[iMNinAB] = pmiddlex[iMNinAB] - yposNested[iMNinAB] = pmiddley[iMNinAB] - outerElec1[iMNinAB] = np.c_[elecx[array[iMNinAB,0]], elecy[array[iMNinAB,0]]] - outerElec2[iMNinAB] = np.c_[elecx[array[iMNinAB,1]], elecy[array[iMNinAB,1]]] - - if np.sum(iABinMN) > 0: - xposNested[iABinMN] = cmiddlex[iABinMN] - yposNested[iABinMN] = cmiddley[iABinMN] - outerElec1[iABinMN] = np.c_[elecx[array[iABinMN,2]], elecy[array[iABinMN,2]]] - outerElec2[iABinMN] = np.c_[elecx[array[iABinMN,3]], elecy[array[iABinMN,3]]] - - innerMid = np.c_[pmiddlex, pmiddley] # always use potential dipole - - apdist = np.sqrt(np.sum((outerElec1-innerMid)**2, axis=1)) - bpdist = np.sqrt(np.sum((outerElec2-innerMid)**2, axis=1)) - zposNested = np.min([apdist, bpdist], axis=0)/3 - - xpos = np.zeros_like(pmiddlex) - ypos = np.zeros_like(pmiddlex) - zpos = np.zeros_like(pmiddlex) - - xpos[~inested] = xposNonNested[~inested] - xpos[inested] = xposNested[inested] - - ypos[~inested] = yposNonNested[~inested] - ypos[inested] = yposNested[inested] - - zpos[~inested] = zposNonNested[~inested] - zpos[inested] = zposNested[inested] - - */ - } - //console.log(app) - // update the trace and redraw the figure - trace['x'] = xpos - trace['y'] = ypos - trace['marker']['color'] = app - trace['marker']['cmax'] = document.getElementById('cmax').value - trace['marker']['cmin'] = document.getElementById('cmin').value - Plotly.redraw('gd') - } - } - let surveySelect = document.getElementById('surveySelect') - - // run RS check - function rsBtnFunc() { - sendCommand('{"cmd": "rsCheck"}', function (a) {}) - } - let rsBtn = document.getElementById('rsBtn') - rsBtn.addEventListener('click', rsBtnFunc) - - // get RS check data - function getRsBtnFunc() { - sendCommand('{"cmd": "getRsCheck"}', function(res) { - // update the bar plot - rsdata.push({ - x: res['data']['AB'], - y: res['data']['res'], - name: 'RS', - type: 'bar' - }) - Plotly.redraw('rs') - }) - } - let getRsBtn = document.getElementById('getRsBtn') - getRsBtn.addEventListener('click', getRsBtnFunc) - - // clear RS graph - function rsClearBtnFunc() { - rsdata = [] - Plotly.newPlot('rs', rsdata, rslayout) - } - let rsClearBtn = document.getElementById('rsClearBtn') - rsClearBtn.addEventListener('click', rsClearBtnFunc) - - // getData - function getData() { - let surveyNames = [] - sendCommand(JSON.stringify({ - 'cmd': 'getData', - 'surveyNames': surveyNames - // last survey is often partial so we download it again - }), function(ddic) { - // update status - //output.innerHTML = 'Status: ' + ddic['status'] - - // update data dic with new data - data = { // destructuring assignement (magic! :o) - ...data, - ...ddic['data'] // value from second dic are preferred - } - - // dropdown with number of surveys - surveyNames = Object.keys(data).sort() - - // remove listener as we will replace the choices - surveySelect.removeEventListener('change', surveySelectFunc) - surveySelect.innerHTML = '' // clearing all child nodes - - // add choices again - for (let surveyName of surveyNames) { - let option = document.createElement('option') - option.innerText = surveyName - option.value = surveyName - surveySelect.appendChild(option) - } - - // listener again - surveySelect.addEventListener('change', surveySelectFunc) - - // plot last one by default - surveySelect.value = surveyNames[surveyNames.length - 1] - - // call the function directly - // (as progammatically chaging the value does not trigger the event) - surveySelectFunc({'target': surveySelect}) - - // update list of quadrupoles if any - let idiff = false - if (data[surveyNames[0]] != undefined) { - idiff = quads.length != data[surveyNames[0]]['a'].length - } - //console.log('idiff=', idiff, quads.length, data[surveyNames[0]]['a'].length) - if (((quads.length == 0) | idiff) & (data[surveyNames[0]] != undefined)){ - console.log('updating list of quadrupoles') - quads = [] - let df = data[surveyNames[0]] - let quadSelect = document.getElementById('quadSelect') - quadSelect.innerHTML = '' - for (let i = 0; i < df['a'].length; i++) { - quad = [df['a'][i], df['b'][i], df['m'][i], df['n'][i]] - quads.push(quad) - let option = document.createElement('option') - option.value = quad.join(', ') - option.innerText = quad.join(', ') - quadSelect.appendChild(option) - } - console.log('quads=', quads) - } - - // update time-serie figure - if (squads.length > 0) { - - // transform all surveyNames to datetime - let xt = [] - for (surveyName of surveyNames) { - let a = surveyName.split('_').slice(-1)[0] - xt.push(a.slice(0, 4) + '-' - + a.slice(4, 6) + '-' - + a.slice(6, 8) + ' ' - + a.slice(9, 11) + ':' - + a.slice(11, 13) + ':' - + a.slice(13, 15)) - } - //console.log(xt) - - // create one new trace per selected quadrupole - for (let k = 0; k < squads.length; k++) { - squad = squads[k] - let x = [] - let y = [] - for (let i = 0; i < surveyNames.length; i++) { - df = data[surveyNames[i]] - for (let j = 0; j < df['a'].length; j++) { - if (df['a'][j] == squad[0] - && df['b'][j] == squad[1] - && df['m'][j] == squad[2] - && df['n'][j] == squad[3]) { - y.push(df['rho'][j]) - x.push(xt[i]) - break - } - } - } - - // update trace dictionnary - tdata[k]['x'] = x - tdata[k]['y'] = y - } - //console.log(tdata) - Plotly.redraw('ts') - } - }) - } - let getDataBtn = document.getElementById('getDataBtn') - getDataBtn.addEventListener('click', getData) - - // apply new colorscale - let capplyBtn = document.getElementById('capplyBtn') - capplyBtn.addEventListener('click', function() { - surveySelectFunc({'target': surveySelect}) - }) - - // checkbox interaction for data download - function dataRetrievalCheckFunc(x) { - if (x['target'].checked == true) { - interv = setInterval(getData, 1000) // every 5s - } else { - clearInterval(interv) - } - } - let dataRetrievalCheck = document.getElementById('dataRetrievalCheck') - dataRetrievalCheck.addEventListener('change', dataRetrievalCheckFunc) - - // remove data - function removeDataBtnFunc() { - sendCommand('{"cmd": "removeData"}',function(x) { - data = {} - output.innerHTML = 'Status: ' + x['ohmpi_status'] + ' (all data cleared)' - console.log('all data removed') - initPlots() // reset all plots - }) - } - let removeDataBtn = document.getElementById('removeDataBtn') - removeDataBtn.addEventListener('click', removeDataBtnFunc) - - // shutdown Pi - function shutdownBtnFunc() { - sendCommand('{"cmd": "shutdown"}', function(x) { - console.log('shuting down...') - }) - } - let shutdownBtn = document.getElementById('shutdownBtn') - shutdownBtn.addEventListener('click', shutdownBtnFunc) - - // restart Pi - function restartBtnFunc() { - sendCommand('{"cmd": "restart"}', function(x) { - console.log('rebooting...') - }) - } - let restartBtn = document.getElementById('restartBtn') - restartBtn.addEventListener('click', restartBtnFunc) - - // invert data - // function invertBtnFunc() { - // sendCommand('{"cmd": "invert"}', function(x) { - // console.log('inversion results', x) - // }) - // } - // let invertBtn = document.getElementById('invertBtn') - // invertBtn.addEventListener('click', invertBtnFunc) - - // download data - function downloadBtnFunc() { - sendCommand('{"cmd": "download"}', function(x) { - let dwl = document.getElementById('download') - dwl.setAttribute('href', serverUrl + '/data.zip') - dwl.setAttribute('download', 'data.zip') - dwl.click() - }) - } - let downloadBtn = document.getElementById('downloadBtn') - downloadBtn.addEventListener('click', downloadBtnFunc) - - - </script> - - <!-- Boostrap scripts (at the end of the page for faster loading time)--> - <script src="js/bootstrap.bundle.min.js"></script> - <!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script> --> -</body> -</html> diff --git a/ohmpi/__init__.py b/ohmpi/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d1c1cf1ce4567059e2106febb97432bcdad4cfa1 100644 --- a/ohmpi/__init__.py +++ b/ohmpi/__init__.py @@ -0,0 +1,2 @@ +# from .ohmpi import OhmPi +#from ohmpi.ohmpi import OhmPi diff --git a/ohmpi/hardware_components/abstract_hardware_components.py b/ohmpi/hardware_components/abstract_hardware_components.py index 4868a0081c309a0e44abb9768cd88c986fe83f95..54c56fb466a19742185f600cff3ace373f3c48e7 100644 --- a/ohmpi/hardware_components/abstract_hardware_components.py +++ b/ohmpi/hardware_components/abstract_hardware_components.py @@ -1,5 +1,4 @@ from abc import ABC, abstractmethod - import numpy as np from ohmpi.logging_setup import create_stdout_logger import time @@ -8,28 +7,28 @@ from threading import Event, Barrier, BrokenBarrierError class CtlAbstract(ABC): def __init__(self, **kwargs): - self.board_name = kwargs.pop('board_name', 'unknown CTL hardware') - self.interfaces = None # TODO: allow for several buses + self.model = kwargs.pop('model', 'unknown CTL hardware') + self.interfaces = None self.exec_logger = kwargs.pop('exec_logger', None) if self.exec_logger is None: self.exec_logger = create_stdout_logger('exec_ctl') self.soh_logger = kwargs.pop('soh_logger', None) if self.soh_logger is None: self.soh_logger = create_stdout_logger('soh_ctl') - self.exec_logger.debug(f'{self.board_name} Ctl initialization') + self.exec_logger.debug(f'{self.model} Ctl initialization') self._cpu_temp_available = False self.max_cpu_temp = np.inf - self.connection = None + self.connection = kwargs.pop('connection', None) @property def cpu_temperature(self): if not self._cpu_temp_available: - self.exec_logger.warning(f'CPU temperature reading is not available for {self.board_name}') + self.exec_logger.warning(f'CPU temperature reading is not available for {self.model}') cpu_temp = np.nan else: cpu_temp = self._cpu_temp if cpu_temp > self.max_cpu_temp: - self.soh_logger.warning(f'CPU temperature of {self.board_name} is over the limit!') + self.soh_logger.warning(f'CPU temperature of {self.model} is over the limit!') return cpu_temp @property @@ -40,7 +39,7 @@ class CtlAbstract(ABC): class PwrAbstract(ABC): def __init__(self, **kwargs): - self.board_name = kwargs.pop('board_name', 'unknown PWR hardware') + self.model = kwargs.pop('model', 'unknown PWR hardware') self.exec_logger = kwargs.pop('exec_logger', None) if self.exec_logger is None: self.exec_logger = create_stdout_logger('exec_mux') @@ -49,15 +48,15 @@ class PwrAbstract(ABC): self.soh_logger = create_stdout_logger('soh_mux') self.voltage_adjustable = kwargs.pop('voltage_adjustable', False) self._voltage = np.nan - self._current_adjustable = kwargs.pop('current_adjustable', False) + self.current_adjustable = kwargs.pop('current_adjustable', False) self._current = np.nan - self._state = 'off' + self._pwr_state = 'off' self._current_min = kwargs.pop('current_min', 0.) self._current_max = kwargs.pop('current_max', 0.) self._voltage_min = kwargs.pop('voltage_min', 0.) self._voltage_max = kwargs.pop('voltage_max', 0.) - self.ctl = kwargs.pop('ctl', None) - self.connection = kwargs.pop('io', None) + self.connection = kwargs.pop('connection', None) + self._battery_voltage = np.nan @property @abstractmethod @@ -70,16 +69,30 @@ class PwrAbstract(ABC): def current(self, value, **kwargs): # add actions to set the DPS current pass + # + # @abstractmethod + # def turn_off(self): + # self.exec_logger.debug(f'Switching {self.model} off') + # self._state = 'off' + # + # @abstractmethod + # def turn_on(self): + # self.exec_logger.debug(f'Switching {self.model} on') + # self._state = 'on' - @abstractmethod - def turn_off(self): - self.exec_logger.debug(f'Switching {self.board_name} off') - self._state = 'off' + @property + def pwr_state(self): + return self._pwr_state + + @pwr_state.setter + def pwr_state(self, state): + if state == 'on': + self._pwr_state = 'on' + self.exec_logger.debug(f'{self.model} cannot be switched on') + elif state == 'off': + self._pwr_state = 'off' + self.exec_logger.debug(f'{self.model} cannot be switched off') - @abstractmethod - def turn_on(self): - self.exec_logger.debug(f'Switching {self.board_name} on') - self._state = 'on' @property @abstractmethod @@ -92,27 +105,33 @@ class PwrAbstract(ABC): def voltage(self, value): assert isinstance(value, float) if not self.voltage_adjustable: - self.exec_logger.debug(f'Voltage cannot be set on {self.board_name}...') + self.exec_logger.debug(f'Voltage cannot be set on {self.model}...') else: - assert self._voltage_min < value < self._voltage_max + assert self._voltage_min <= value <= self._voltage_max # add actions to set the DPS voltage self._voltage = value + def battery_voltage(self): + # add actions to read the DPS voltage + self.exec_logger.debug(f'Battery voltage cannot be read on {self.model}...') + return self._battery_voltage + + def reset_voltage(self): + if not self.voltage_adjustable: + self.exec_logger.debug(f'Voltage cannot be set on {self.model}...') + else: + self.voltage = self._voltage_min class MuxAbstract(ABC): def __init__(self, **kwargs): - self.board_name = kwargs.pop('board_name', 'unknown MUX hardware') - self.exec_logger = kwargs.pop('exec_logger', None) - if self.exec_logger is None: - self.exec_logger = create_stdout_logger('exec_mux') - self.soh_logger = kwargs.pop('soh_logger', None) - if self.soh_logger is None: - self.soh_logger = create_stdout_logger('soh_mux') + self.model = kwargs.pop('model', 'unknown MUX hardware') + self.exec_logger = kwargs.pop('exec_logger', create_stdout_logger('exec_mux')) + self.soh_logger = kwargs.pop('soh_logger', create_stdout_logger('soh_mux')) self.board_id = kwargs.pop('id', None) if self.board_id is None: - self.exec_logger.error(f'MUX {self.board_name} should have an id !') - self.exec_logger.debug(f'MUX {self.board_id} ({self.board_name}) initialization') - self.connection = kwargs.pop('io', None) + self.exec_logger.error(f'MUX {self.model} should have an id !') + self.exec_logger.debug(f'MUX {self.model}: {self.board_id} initialization') + self.connection = kwargs.pop('connection', None) cabling = kwargs.pop('cabling', None) self.cabling = {} if cabling is not None: @@ -157,7 +176,7 @@ class MuxAbstract(ABC): """ status = True if elec_dict is not None: - self.exec_logger.debug(f'Switching {self.board_name} ') + self.exec_logger.debug(f'Switching {self.model} ') # check to prevent A == B (SHORT-CIRCUIT) if 'A' in elec_dict.keys() and 'B' in elec_dict.keys(): out = np.in1d(elec_dict['A'], elec_dict['B']) @@ -203,7 +222,7 @@ class MuxAbstract(ABC): self.exec_logger.debug(f'Barrier error {self.board_id} switching aborted.') status = False else: - self.exec_logger.warning(f'Missing argument for {self.board_name}.switch: elec_dict is None.') + self.exec_logger.warning(f'Missing argument for {self.model}.switch: elec_dict is None.') status = False if state == 'on': time.sleep(self._activation_delay) @@ -234,7 +253,7 @@ class MuxAbstract(ABC): activation_time : float, optional Time in seconds during which the relays are activated. """ - self.exec_logger.debug(f'Starting {self.board_name} test...') + self.exec_logger.debug(f'Starting {self.model} test...') self.reset() for role in elec_dict.keys(): @@ -248,7 +267,7 @@ class MuxAbstract(ABC): class TxAbstract(ABC): def __init__(self, **kwargs): - self.board_name = kwargs.pop('board_name', 'unknown TX hardware') + self.model = kwargs.pop('model', 'unknown TX hardware') injection_duration = kwargs.pop('injection_duration', 1.) self.exec_logger = kwargs.pop('exec_logger', None) if self.exec_logger is None: @@ -256,8 +275,7 @@ class TxAbstract(ABC): self.soh_logger = kwargs.pop('soh_logger', None) if self.soh_logger is None: self.soh_logger = create_stdout_logger('soh_tx') - self.ctl = kwargs.pop('ctl', None) - self.connection = kwargs.pop('io', None) + self.connection = kwargs.pop('connection', None) self.pwr = kwargs.pop('pwr', None) self._polarity = 0 self._injection_duration = None @@ -265,7 +283,8 @@ class TxAbstract(ABC): self.injection_duration = injection_duration self._latency = kwargs.pop('latency', 0.) self.tx_sync = kwargs.pop('tx_sync', Event()) - self.exec_logger.debug(f'{self.board_name} TX initialization') + self.exec_logger.debug(f'{self.model} TX initialization') + self._pwr_state = 'off' @property def adc_gain(self): @@ -283,15 +302,15 @@ class TxAbstract(ABC): self.exec_logger.debug(f'Setting TX ADC gain to {value}') @abstractmethod - def adc_gain_auto(self): + def _adc_gain_auto(self): pass @abstractmethod - def current_pulse(self, **kwargs): + def current_pulse(self, **kurwargs): pass @abstractmethod - def inject(self, polarity=1, injection_duration=None): + def inject(self, polarity=1, injection_duration=None, switch_pwr=False): """ Abstract method to define injection Parameters @@ -300,20 +319,26 @@ class TxAbstract(ABC): Injection polarity, can be eiter 1, 0 or -1 injection_duration: float, default None Injection duration in seconds + switch_pwr: bool + switches on and off tx.pwr """ assert polarity in [-1, 0, 1] if injection_duration is None: injection_duration = self._injection_duration if np.abs(polarity) > 0: - self.pwr.turn_on() + if switch_pwr: + self.pwr.pwr_state('on') self.tx_sync.set() time.sleep(injection_duration) - self.pwr.turn_off() + self.tx_sync.clear() + if switch_pwr: + self.pwr.pwr_state('off') else: self.tx_sync.set() - self.pwr.turn_off() + if switch_pwr: + self.pwr.pwr_state('off') time.sleep(injection_duration) - self.tx_sync.clear() + self.tx_sync.clear() @property def injection_duration(self): @@ -365,6 +390,18 @@ class TxAbstract(ABC): self.exec_logger.debug(f'Voltage pulse of {polarity * self.pwr.voltage:.3f} V for {length:.3f} s') self.inject(polarity=polarity, injection_duration=length) + @property + def pwr_state(self): + return self._pwr_state + + @pwr_state.setter + def pwr_state(self, state): + if state == 'on': + self._pwr_state = 'on' + self.exec_logger.debug(f'{self.model} cannot switch on power source') + elif state == 'off': + self._pwr_state = 'off' + self.exec_logger.debug(f'{self.model} cannot switch off power source') class RxAbstract(ABC): def __init__(self, **kwargs): @@ -374,16 +411,16 @@ class RxAbstract(ABC): self.soh_logger = kwargs.pop('soh_logger', None) if self.soh_logger is None: self.soh_logger = create_stdout_logger('soh_rx') - self.ctl = kwargs.pop('ctl', None) - self.connection = kwargs.pop('io', None) - self.board_name = kwargs.pop('board_name', 'unknown RX hardware') + self.connection = kwargs.pop('connection', None) + self.model = kwargs.pop('model', 'unknown RX hardware') self._sampling_rate = kwargs.pop('sampling_rate', 1) # ms - self.exec_logger.debug(f'{self.board_name} RX initialization') + self.exec_logger.debug(f'{self.model} RX initialization') self._voltage_max = kwargs.pop('voltage_max', 0.) self._adc_gain = 1. self._max_sampling_rate = np.inf self._latency = kwargs.pop('latency', 0.) self._bias = kwargs.pop('bias', 0.) + self._vmn_hardware_offset = kwargs.pop('vmn_hardware_offset', 0.) @property def adc_gain(self): @@ -401,7 +438,7 @@ class RxAbstract(ABC): self.exec_logger.debug(f'Setting RX ADC gain to {value}') @abstractmethod - def adc_gain_auto(self): + def _adc_gain_auto(self): pass @property diff --git a/ohmpi/hardware_components/dummy_ctl.py b/ohmpi/hardware_components/dummy_ctl.py index 9eeb98e0a4ee56f075ccc061c2feb58e13cfba90..0045bd726c551db024d5362b75182eeaed327ad0 100644 --- a/ohmpi/hardware_components/dummy_ctl.py +++ b/ohmpi/hardware_components/dummy_ctl.py @@ -6,5 +6,5 @@ CTL_CONFIG = HARDWARE_CONFIG['ctl'] class Ctl(CtlAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) + kwargs.update({'model': os.path.basename(__file__).rstrip('.py')}) super().__init__(**kwargs) diff --git a/ohmpi/hardware_components/dummy_mux.py b/ohmpi/hardware_components/dummy_mux.py index 21468e2dccbf7385f62afcbb923c5201db22653a..bb8a5a7d118aa5d132ffe6bb6aeaaba33066767c 100644 --- a/ohmpi/hardware_components/dummy_mux.py +++ b/ohmpi/hardware_components/dummy_mux.py @@ -6,7 +6,6 @@ MUX_CONFIG = HARDWARE_CONFIG['mux'].pop('default', {}) class Mux(MuxAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) super().__init__(**kwargs) def _get_addresses(self): diff --git a/ohmpi/hardware_components/dummy_rx.py b/ohmpi/hardware_components/dummy_rx.py index 386b193ced9411f0b048305e42910c08d195b2bd..249a2e49170661195662736ed2abe2f65a054345 100644 --- a/ohmpi/hardware_components/dummy_rx.py +++ b/ohmpi/hardware_components/dummy_rx.py @@ -12,9 +12,9 @@ voltage_adc_voltage_max = 4500. RX_CONFIG['voltage_min'] = np.min([voltage_adc_voltage_min, RX_CONFIG.pop('voltage_min', np.inf)]) # mV RX_CONFIG['voltage_max'] = np.min([voltage_adc_voltage_max, RX_CONFIG.pop('voltage_max', np.inf)]) # mV + class Rx(RxAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) super().__init__(**kwargs) self._adc_gain = 1. diff --git a/ohmpi/hardware_components/dummy_tx.py b/ohmpi/hardware_components/dummy_tx.py index 067c1465978814876e4dba25e64f182b83d1d150..cef80c8e7dfdfb386487add40f79e50579fda93c 100644 --- a/ohmpi/hardware_components/dummy_tx.py +++ b/ohmpi/hardware_components/dummy_tx.py @@ -28,7 +28,6 @@ class Tx(TxAbstract): pass def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) super().__init__(**kwargs) self._voltage = kwargs.pop('voltage', TX_CONFIG['default_voltage']) diff --git a/ohmpi/hardware_components/mb_2023_0_X.py b/ohmpi/hardware_components/mb_2023_0_X.py index 3e0cd90466f8095444e1a0f4db87ef97c8ef0b72..dd1933602033173960eb0781119be869bf782d46 100644 --- a/ohmpi/hardware_components/mb_2023_0_X.py +++ b/ohmpi/hardware_components/mb_2023_0_X.py @@ -1,65 +1,45 @@ import datetime -import importlib -from ohmpi.config import HARDWARE_CONFIG # TODO: Remove references at config here -> move it in ohmpi_hardware as done for mux_2024 import adafruit_ads1x15.ads1115 as ads # noqa from adafruit_ads1x15.analog_in import AnalogIn # noqa from adafruit_ads1x15.ads1x15 import Mode # noqa from adafruit_mcp230xx.mcp23008 import MCP23008 # noqa from digitalio import Direction # noqa +from busio import I2C # noqa import time -import numpy as np import os +import numpy as np from ohmpi.hardware_components import TxAbstract, RxAbstract -ctl_name = HARDWARE_CONFIG['ctl'].pop('board_name', 'raspberry_pi') -ctl_connection = HARDWARE_CONFIG['ctl'].pop('connection', 'i2c') -ctl_module = importlib.import_module(f'ohmpi.hardware_components.{ctl_name}') - -TX_CONFIG = HARDWARE_CONFIG['tx'] -RX_CONFIG = HARDWARE_CONFIG['rx'] +from ohmpi.utils import enforce_specs # hardware characteristics and limitations -# *** RX *** -# ADC for voltage -voltage_adc_voltage_min = 10. # mV -voltage_adc_voltage_max = 4500. # mV -sampling_rate = 20. # Hz -data_rate = 860. # S/s? - -RX_CONFIG['voltage_min'] = np.min([voltage_adc_voltage_min, RX_CONFIG.pop('voltage_min', np.inf)]) # mV -RX_CONFIG['voltage_max'] = np.min([voltage_adc_voltage_max, RX_CONFIG.pop('voltage_max', np.inf)]) # mV -RX_CONFIG['sampling_rate'] = RX_CONFIG.pop('sampling_rate', sampling_rate) -RX_CONFIG['data_rate'] = RX_CONFIG.pop('data_rate', data_rate) -RX_CONFIG['coef_p2'] = RX_CONFIG.pop('coef_p2', 2.5) -RX_CONFIG['latency'] = RX_CONFIG.pop('latency', 0.01) -RX_CONFIG['bias'] = RX_CONFIG.pop('bias', 0.) - - -# *** TX *** -# ADC for current -current_adc_voltage_min = 10. # mV -current_adc_voltage_max = 4500. # mV -low_battery = 12. # V (conventional value as it is not measured on this board) -tx_mcp_board_address = 0x20 # -# pwr_voltage_max = 12. # V -# pwr_default_voltage = 12. # V -# pwr_switch_on_warmup = 0. # seconds - -TX_CONFIG['current_min'] = np.min([current_adc_voltage_min / (TX_CONFIG['r_shunt'] * 50), - TX_CONFIG.pop('current_min', np.inf)]) # mA -TX_CONFIG['current_max'] = np.min([current_adc_voltage_max / (TX_CONFIG['r_shunt'] * 50), - TX_CONFIG.pop('current_max', np.inf)]) # mA -# TX_CONFIG['voltage_max'] = np.min([pwr_voltage_max, TX_CONFIG.pop('voltage_max', np.inf)]) # V -TX_CONFIG['voltage_max'] = TX_CONFIG.pop('voltage_max', np.inf) # V -TX_CONFIG['voltage_min'] = -TX_CONFIG['voltage_max'] # V -TX_CONFIG['default_voltage'] = np.min([TX_CONFIG.pop('default_voltage', np.inf), TX_CONFIG['voltage_max']]) # V -# TX_CONFIG['pwr_switch_on_warm_up'] = TX_CONFIG.pop('pwr_switch_on_warmup', pwr_switch_on_warmup) -TX_CONFIG['mcp_board_address'] = TX_CONFIG.pop('mcp_board_address', tx_mcp_board_address) -TX_CONFIG['low_battery'] = TX_CONFIG.pop('low_battery', low_battery) -TX_CONFIG['latency'] = TX_CONFIG.pop('latency', 0.01) -TX_CONFIG['bias'] = TX_CONFIG.pop('bias', 0.) - - -def _gain_auto(channel): +# voltages are given in mV, currents in mA, sampling rates in Hz and data_rate in S/s +SPECS = {'rx': {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'sampling_rate': {'min': 2., 'default': 10., 'max': 100.}, + 'data_rate': {'default': 860.}, + 'bias': {'min': -5000., 'default': 0., 'max': 5000.}, + 'coef_p2': {'default': 2.50}, + 'mcp_address': {'default': None}, + 'ads_address': {'default': 0x49}, + 'voltage_min': {'default': 10.0}, + 'vmn_hardware_offset': {'default': 0.} + }, + 'tx': {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'adc_voltage_min': {'default': 10.}, # Minimum voltage value used in vmin strategy + 'adc_voltage_max': {'default': 4500.}, # Maximum voltage on ads1115 used to measure current + 'voltage_max': {'min': 0., 'default': 12., 'max': 12.}, # Maximum input voltage + 'data_rate': {'default': 860.}, + 'mcp_address': {'default': 0x20}, + 'ads_address': {'default': 0x48}, + 'compatible_power_sources': {'default': ['pwr_batt', 'dps5005']}, + 'r_shunt': {'min': 0., 'default': 2. }, + 'activation_delay': {'default': 0.005}, # Max turn on time of 211EH relays = 5ms + 'release_delay': {'default': 0.001}, # Max turn off time of 211EH relays = 1ms + }} + +# TODO: move low_battery spec in pwr + + +def _ads_1115_gain_auto(channel): # Make it a class method ? """Automatically sets the gain on a channel Parameters @@ -87,25 +67,36 @@ def _gain_auto(channel): class Tx(TxAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) + if 'model' not in kwargs.keys(): + for key in SPECS['tx'].keys(): + kwargs = enforce_specs(kwargs, SPECS['tx'], key) + subclass_init = False + else: + subclass_init = True super().__init__(**kwargs) - self.exec_logger.event(f'{self.board_name}\ttx_init\tbegin\t{datetime.datetime.utcnow()}') - self._voltage = kwargs.pop('voltage', TX_CONFIG['default_voltage']) + if not subclass_init: + self.exec_logger.event(f'{self.model}\ttx_init\tbegin\t{datetime.datetime.utcnow()}') + assert isinstance(self.connection, I2C) + kwargs.update({'pwr': kwargs.pop('pwr', SPECS['tx']['compatible_power_sources']['default'][0])}) + if (kwargs['pwr'] not in SPECS['tx']['compatible_power_sources']['default']): + self.exec_logger.warning(f'Incompatible power source specified check config') + assert kwargs['pwr'] in SPECS['tx'] + self._activation_delay = kwargs['activation_delay'] + self._release_delay = kwargs['release_delay'] self.voltage_adjustable = False self.current_adjustable = False - if self.ctl is None: - self.ctl = ctl_module.Ctl() - # elif isinstance(self.ctl, dict): - # self.ctl = ctl_module.Ctl(**self.ctl) - self.connection = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)] # I2C connexion to MCP23008, for current injection - self.mcp_board = MCP23008(self.connection, address=TX_CONFIG['mcp_board_address']) + self.mcp_board = MCP23008(self.connection, address=kwargs['mcp_address']) # ADS1115 for current measurement (AB) - self._ads_current_address = 0x48 - self._ads_current = ads.ADS1115(self.connection, gain=self.adc_gain, data_rate=860, + self._ads_current_address = kwargs['ads_address'] + self._ads_current_data_rate = kwargs['data_rate'] + self._ads_current = ads.ADS1115(self.connection, gain=self.adc_gain, data_rate=self._ads_current_data_rate, address=self._ads_current_address) self._ads_current.mode = Mode.CONTINUOUS + self.r_shunt = kwargs['r_shunt'] + self.adc_voltage_min = kwargs['adc_voltage_min'] + self.adc_voltage_max = kwargs['adc_voltage_max'] # Relays for pulse polarity self.pin0 = self.mcp_board.get_pin(0) @@ -113,59 +104,54 @@ class Tx(TxAbstract): self.pin1 = self.mcp_board.get_pin(1) self.pin1.direction = Direction.OUTPUT self.polarity = 0 - self.adc_gain = 2 / 3 - - self.pwr = None # TODO: set a list of compatible power system with the tx - - # MCP23008 pins for LEDs - self.pin4 = self.mcp_board.get_pin(4) # TODO: Delete me? No LED on this version of the board - self.pin4.direction = Direction.OUTPUT - self.pin4.value = True - - self._latency = kwargs.pop('latency', TX_CONFIG['latency']) - self._bias = kwargs.pop('bias', TX_CONFIG['bias']) - self.exec_logger.event(f'{self.board_name}\ttx_init\tend\t{datetime.datetime.utcnow()}') + self.gain = 2 / 3 + if not subclass_init: + self.exec_logger.event(f'{self.model}\ttx_init\tend\t{datetime.datetime.utcnow()}') @property - def adc_gain(self): + def gain(self): return self._adc_gain - @adc_gain.setter - def adc_gain(self, value): + @gain.setter + def gain(self, value): assert value in [2/3, 2, 4, 8, 16] self._adc_gain = value - self._ads_current = ads.ADS1115(self.connection, gain=self.adc_gain, data_rate=860, + self._ads_current = ads.ADS1115(self.connection, gain=self.adc_gain, + data_rate=SPECS['tx']['data_rate']['default'], address=self._ads_current_address) self._ads_current.mode = Mode.CONTINUOUS self.exec_logger.debug(f'Setting TX ADC gain to {value}') - def adc_gain_auto(self): - self.exec_logger.event(f'{self.board_name}\ttx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}') - gain = _gain_auto(AnalogIn(self._ads_current, ads.P0)) + def _adc_gain_auto(self): + self.exec_logger.event(f'{self.model}\ttx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}') + gain = _ads_1115_gain_auto(AnalogIn(self._ads_current, ads.P0)) self.exec_logger.debug(f'Setting TX ADC gain automatically to {gain}') - self.adc_gain = gain - self.exec_logger.event(f'{self.board_name}\ttx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}') + self.gain = gain + self.exec_logger.event(f'{self.model}\ttx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}') def current_pulse(self, **kwargs): TxAbstract.current_pulse(self, **kwargs) - self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board') + self.exec_logger.warning(f'Current pulse is not implemented for the {self.model} board') @property def current(self): """ Gets the current IAB in Amps """ - iab = AnalogIn(self._ads_current, ads.P0).voltage * 1000. / (50 * TX_CONFIG['r_shunt']) # measure current + iab = AnalogIn(self._ads_current, ads.P0).voltage * 1000. / (50 * self.r_shunt) # measure current self.exec_logger.debug(f'Reading TX current: {iab} mA') return iab @ current.setter def current(self, value): - assert TX_CONFIG['current_min'] <= value <= TX_CONFIG['current_max'] - self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board') + assert self.adc_voltage_min / (50 * self.r_shunt) <= value <= self.adc_voltage_max / (50 * self.r_shunt) + self.exec_logger.warning(f'Current pulse is not implemented for the {self.model} board') - def inject(self, polarity=1, injection_duration=None): + def gain_auto(self): + self._adc_gain_auto() + + def inject(self, polarity=1, injection_duration=None, switch_pwr=False): self.polarity = polarity - TxAbstract.inject(self, polarity=polarity, injection_duration=injection_duration) + TxAbstract.inject(self, polarity=polarity, injection_duration=injection_duration, switch_pwr=switch_pwr) @property def polarity(self): @@ -178,29 +164,33 @@ class Tx(TxAbstract): if polarity == 1: self.pin0.value = True self.pin1.value = False - time.sleep(0.005) # Max turn on time of 211EH relays = 5ms + time.sleep(self._activation_delay) elif polarity == -1: self.pin0.value = False self.pin1.value = True - time.sleep(0.005) # Max turn on time of 211EH relays = 5ms + time.sleep(self._activation_delay) else: self.pin0.value = False self.pin1.value = False - time.sleep(0.001) # Max turn off time of 211EH relays = 1ms - - def turn_off(self): - self.pwr.turn_off(self) - - def turn_on(self): - self.pwr.turn_on(self) + time.sleep(self._release_delay) + # + # def turn_off(self): + # self.pwr.turn_off(self) + # + # def turn_on(self): + # self.pwr.turn_on(self) @property def tx_bat(self): - self.soh_logger.warning(f'Cannot get battery voltage on {self.board_name}') - self.exec_logger.debug(f'{self.board_name} cannot read battery voltage. Returning default battery voltage.') - return TX_CONFIG['low_battery'] + if np.isnan(self.tx.pwr.battery_voltage): + self.soh_logger.warning(f'Cannot get battery voltage on {self.model}') + self.exec_logger.debug(f'{self.model} cannot read battery voltage. Returning default battery voltage.') + return self.pwr.voltage + else: + return self.tx.pwr.battery_voltage + - def voltage_pulse(self, voltage=TX_CONFIG['default_voltage'], length=None, polarity=1): + def voltage_pulse(self, voltage=None, length=None, polarity=1): """ Generates a square voltage pulse Parameters @@ -212,65 +202,72 @@ class Tx(TxAbstract): polarity: 1,0,-1 Polarity of the pulse """ - self.exec_logger.event(f'{self.board_name}\ttx_voltage_pulse\tbegin\t{datetime.datetime.utcnow()}') + self.exec_logger.event(f'{self.model}\ttx_voltage_pulse\tbegin\t{datetime.datetime.utcnow()}') # self.exec_logger.info(f'injection_duration: {length}') # TODO: delete me if length is None: length = self.injection_duration - self.pwr.voltage = voltage + if voltage is not None: + self.pwr.voltage = voltage self.exec_logger.debug(f'Voltage pulse of {polarity*self.pwr.voltage:.3f} V for {length:.3f} s') self.inject(polarity=polarity, injection_duration=length) - self.exec_logger.event(f'{self.board_name}\ttx_voltage_pulse\tend\t{datetime.datetime.utcnow()}') + self.exec_logger.event(f'{self.model}\ttx_voltage_pulse\tend\t{datetime.datetime.utcnow()}') class Rx(RxAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) + if 'model' not in kwargs.keys(): + for key in SPECS['rx'].keys(): + kwargs = enforce_specs(kwargs, SPECS['rx'], key) + subclass_init = False + else: + subclass_init = True super().__init__(**kwargs) - self.exec_logger.event(f'{self.board_name}\trx_init\tbegin\t{datetime.datetime.utcnow()}') - if self.ctl is None: - self.ctl = ctl_module.Ctl() - self.connection = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)] + if not subclass_init: + self.exec_logger.event(f'{self.model}\trx_init\tbegin\t{datetime.datetime.utcnow()}') + assert isinstance(self.connection, I2C) # ADS1115 for voltage measurement (MN) - self._ads_voltage_address = 0x49 + self._ads_voltage_address = kwargs['ads_address'] self._adc_gain = 2/3 - self._ads_voltage = ads.ADS1115(self.connection, gain=self._adc_gain, data_rate=860, + self._ads_voltage = ads.ADS1115(self.connection, gain=self._adc_gain, + data_rate=SPECS['rx']['data_rate']['default'], address=self._ads_voltage_address) self._ads_voltage.mode = Mode.CONTINUOUS - self._coef_p2 = kwargs.pop('coef_p2', RX_CONFIG['coef_p2']) - self._voltage_max = kwargs.pop('voltage_max', RX_CONFIG['voltage_max']) - self._sampling_rate = kwargs.pop('sampling_rate', sampling_rate) - self._latency = kwargs.pop('latency', RX_CONFIG['latency']) - self._bias = kwargs.pop('bias', RX_CONFIG['bias']) - self.exec_logger.event(f'{self.board_name}\trx_init\tend\t{datetime.datetime.utcnow()}') + self._coef_p2 = kwargs['coef_p2'] + # self._voltage_max = kwargs['voltage_max'] + self._sampling_rate = kwargs['sampling_rate'] + self._bias = kwargs['bias'] + if not subclass_init: + self.exec_logger.event(f'{self.model}\trx_init\tend\t{datetime.datetime.utcnow()}') @property - def adc_gain(self): + def gain(self): #TODO: should be in abstract_hardware_components return self._adc_gain - @adc_gain.setter - def adc_gain(self, value): + @gain.setter + def gain(self, value): assert value in [2/3, 2, 4, 8, 16] self._adc_gain = value - self._ads_voltage = ads.ADS1115(self.connection, gain=self.adc_gain, data_rate=860, + self._ads_voltage = ads.ADS1115(self.connection, gain=self.adc_gain, + data_rate=SPECS['rx']['data_rate']['default'], address=self._ads_voltage_address) self._ads_voltage.mode = Mode.CONTINUOUS self.exec_logger.debug(f'Setting RX ADC gain to {value}') - def adc_gain_auto(self): - self.exec_logger.event(f'{self.board_name}\trx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}') - gain_0 = _gain_auto(AnalogIn(self._ads_voltage, ads.P0)) - gain_2 = _gain_auto(AnalogIn(self._ads_voltage, ads.P2)) - gain = np.min([gain_0, gain_2]) + def _adc_gain_auto(self): + self.exec_logger.event(f'{self.model}\trx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}') + gain = _ads_1115_gain_auto(AnalogIn(self._ads_voltage, ads.P0, ads.P1)) self.exec_logger.debug(f'Setting RX ADC gain automatically to {gain}') - self.adc_gain = gain - self.exec_logger.event(f'{self.board_name}\trx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}') + self._adc_gain = gain + self.exec_logger.event(f'{self.model}\trx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}') + def gain_auto(self): + self._adc_gain_auto() @property def voltage(self): """ Gets the voltage VMN in Volts """ - self.exec_logger.event(f'{self.board_name}\trx_voltage\tbegin\t{datetime.datetime.utcnow()}') + self.exec_logger.event(f'{self.model}\trx_voltage\tbegin\t{datetime.datetime.utcnow()}') u = -AnalogIn(self._ads_voltage, ads.P0, ads.P1).voltage * self._coef_p2 * 1000. - self._bias # TODO: check if it should be negated - self.exec_logger.event(f'{self.board_name}\trx_voltage\tend\t{datetime.datetime.utcnow()}') + self.exec_logger.event(f'{self.model}\trx_voltage\tend\t{datetime.datetime.utcnow()}') return u diff --git a/ohmpi/hardware_components/mb_2024_0_2.py b/ohmpi/hardware_components/mb_2024_0_2.py index 0375baf575b6903b348fe07b5a3f974db67b9aa2..bd09972fa8abba6e4c949155dbed6ca1830ff36f 100644 --- a/ohmpi/hardware_components/mb_2024_0_2.py +++ b/ohmpi/hardware_components/mb_2024_0_2.py @@ -1,67 +1,46 @@ import datetime -import importlib -from ohmpi.config import HARDWARE_CONFIG # TODO: Remove references at config here -> move it in ohmpi_hardware as done for mux_2024 import adafruit_ads1x15.ads1115 as ads # noqa from adafruit_ads1x15.analog_in import AnalogIn # noqa from adafruit_ads1x15.ads1x15 import Mode # noqa from adafruit_mcp230xx.mcp23008 import MCP23008 # noqa from digitalio import Direction # noqa -import minimalmodbus # noqa -import time -import numpy as np +from busio import I2C # noqa import os -from ohmpi.hardware_components import TxAbstract, RxAbstract -ctl_name = HARDWARE_CONFIG['ctl'].pop('board_name', 'raspberry_pi') -ctl_connection = HARDWARE_CONFIG['ctl'].pop('connection', 'i2c') -ctl_module = importlib.import_module(f'ohmpi.hardware_components.{ctl_name}') - -TX_CONFIG = HARDWARE_CONFIG['tx'] -RX_CONFIG = HARDWARE_CONFIG['rx'] +import time +from ohmpi.utils import enforce_specs +from ohmpi.hardware_components.mb_2023_0_X import Tx as Tx_mb_2023 +from ohmpi.hardware_components.mb_2023_0_X import Rx as Rx_mb_2023 # hardware characteristics and limitations -# *** RX *** -# ADC for voltage -voltage_adc_voltage_min = 10. # mV -voltage_adc_voltage_max = 4500. # mV -sampling_rate = 20. # Hz -data_rate = 860. # S/s? -rx_mcp_board_address = 0x27 -RX_CONFIG['voltage_min'] = np.min([voltage_adc_voltage_min, RX_CONFIG.pop('voltage_min', np.inf)]) # mV -RX_CONFIG['voltage_max'] = np.min([voltage_adc_voltage_max, RX_CONFIG.pop('voltage_max', np.inf)]) # mV -RX_CONFIG['sampling_rate'] = RX_CONFIG.pop('sampling_rate', sampling_rate) -RX_CONFIG['data_rate'] = RX_CONFIG.pop('data_rate', data_rate) -# RX_CONFIG['coef_p2'] = RX_CONFIG.pop('coef_p2', 2.5) -RX_CONFIG['latency'] = RX_CONFIG.pop('latency', 0.01) -RX_CONFIG['bias'] = RX_CONFIG.pop('bias', 0.) -RX_CONFIG['mcp_board_address'] = TX_CONFIG.pop('mcp_board_address', tx_mcp_board_address) - - -# *** TX *** -# ADC for current -current_adc_voltage_min = 10. # mV -current_adc_voltage_max = 4500. # mV -low_battery = 12. # V (conventional value as it is not measured on this board) -tx_mcp_board_address = 0x21 # -# pwr_voltage_max = 12. # V -# pwr_default_voltage = 12. # V -# pwr_switch_on_warmup = 0. # seconds - -TX_CONFIG['current_min'] = np.min([current_adc_voltage_min / (TX_CONFIG['r_shunt'] * 50), - TX_CONFIG.pop('current_min', np.inf)]) # mA -TX_CONFIG['current_max'] = np.min([current_adc_voltage_max / (TX_CONFIG['r_shunt'] * 50), - TX_CONFIG.pop('current_max', np.inf)]) # mA -# TX_CONFIG['voltage_max'] = np.min([pwr_voltage_max, TX_CONFIG.pop('voltage_max', np.inf)]) # V -TX_CONFIG['voltage_max'] = TX_CONFIG.pop('voltage_max', np.inf) # V -TX_CONFIG['voltage_min'] = -TX_CONFIG['voltage_max'] # V -TX_CONFIG['default_voltage'] = np.min([TX_CONFIG.pop('default_voltage', np.inf), TX_CONFIG['voltage_max']]) # V -# TX_CONFIG['pwr_switch_on_warm_up'] = TX_CONFIG.pop('pwr_switch_on_warmup', pwr_switch_on_warmup) -TX_CONFIG['mcp_board_address'] = TX_CONFIG.pop('mcp_board_address', tx_mcp_board_address) -TX_CONFIG['low_battery'] = TX_CONFIG.pop('low_battery', low_battery) -TX_CONFIG['latency'] = TX_CONFIG.pop('latency', 0.01) -TX_CONFIG['bias'] = TX_CONFIG.pop('bias', 0.) - - -def _gain_auto(channel): +# voltages are given in mV, currents in mA, sampling rates in Hz and data_rate in S/s +SPECS = {'rx': {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'sampling_rate': {'min': 2., 'default': 10., 'max': 100.}, + 'data_rate': {'default': 860.}, + 'bias': {'min': -5000., 'default': 0., 'max': 5000.}, + 'coef_p2': {'default': 1.00}, + 'mcp_address': {'default': 0x27}, + 'ads_address': {'default': 0x49}, + 'voltage_min': {'default': 10.0}, + 'vmn_hardware_offset': {'default': 2500.}, + }, + 'tx': {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'adc_voltage_min': {'default': 10.}, # Minimum voltage value used in vmin strategy + 'adc_voltage_max': {'default': 4500.}, # Maximum voltage on ads1115 used to measure current + 'voltage_max': {'min': 0., 'default': 12., 'max': 12.}, # Maximum input voltage + 'data_rate': {'default': 860.}, + 'mcp_address': {'default': 0x21}, + 'ads_address': {'default': 0x48}, + 'compatible_power_sources': {'default': ['pwr_batt', 'dps5005']}, + 'r_shunt': {'min': 0., 'default': 2.}, + 'activation_delay': {'default': 0.010}, # Max turn on time of OMRON G5LE-1 5VDC relays + 'release_delay': {'default': 0.005}, # Max turn off time of OMRON G5LE-1 5VDC relays = 1ms + 'pwr_latency': {'default': 4.} + }} + +# TODO: move low_battery spec in pwr + + +def _ads_1115_gain_auto(channel): # Make it a class method ? """Automatically sets the gain on a channel Parameters @@ -87,227 +66,137 @@ def _gain_auto(channel): return gain -class Tx(TxAbstract): +class Tx(Tx_mb_2023): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) + if 'model' not in kwargs.keys(): + for key in SPECS['tx'].keys(): + kwargs = enforce_specs(kwargs, SPECS['tx'], key) + subclass_init = False + else: + subclass_init = True super().__init__(**kwargs) - self.exec_logger.event(f'{self.board_name}\ttx_init\tbegin\t{datetime.datetime.utcnow()}') - self._voltage = kwargs.pop('voltage', TX_CONFIG['default_voltage']) - self.voltage_adjustable = False - self.current_adjustable = False - if self.ctl is None: - self.ctl = ctl_module.Ctl() - # elif isinstance(self.ctl, dict): - # self.ctl = ctl_module.Ctl(**self.ctl) - self.io = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)] - - # I2C connexion to MCP23008, for current injection - self.mcp_board = MCP23008(self.io, address=TX_CONFIG['mcp_board_address']) - # ADS1115 for current measurement (AB) - self._ads_current_address = 0x48 - self._ads_current = ads.ADS1115(self.ctl.bus, gain=self.adc_gain, data_rate=860, - address=self._ads_current_address) - self._ads_current.mode = Mode.CONTINUOUS - - # Relays for pulse polarity - self.pin0 = self.mcp_board.get_pin(0) - self.pin0.direction = Direction.OUTPUT - self.pin1 = self.mcp_board.get_pin(1) - self.pin1.direction = Direction.OUTPUT - self.polarity = 0 - self.adc_gain = 2 / 3 - - self.pwr = None # TODO: set a list of compatible power system with the tx + if not subclass_init: + self.exec_logger.event(f'{self.model}\ttx_init\tbegin\t{datetime.datetime.utcnow()}') + self._pwr_latency = kwargs['pwr_latency'] # Initialize LEDs self.pin4 = self.mcp_board.get_pin(4) # Ohmpi_run self.pin4.direction = Direction.OUTPUT self.pin4.value = True - - self._latency = kwargs.pop('latency', TX_CONFIG['latency']) - self._bias = kwargs.pop('bias', TX_CONFIG['bias']) - self.exec_logger.event(f'{self.board_name}\ttx_init\tend\t{datetime.datetime.utcnow()}') - - @property - def adc_gain(self): - return self._adc_gain - - @adc_gain.setter - def adc_gain(self, value): - assert value in [2/3, 2, 4, 8, 16] - self._adc_gain = value - self._ads_current = ads.ADS1115(self.ctl.bus, gain=self.adc_gain, data_rate=860, - address=self._ads_current_address) - self._ads_current.mode = Mode.CONTINUOUS - self.exec_logger.debug(f'Setting TX ADC gain to {value}') - - def adc_gain_auto(self): - self.exec_logger.event(f'{self.board_name}\ttx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}') - gain = _gain_auto(AnalogIn(self._ads_current, ads.P0)) - self.exec_logger.debug(f'Setting TX ADC gain automatically to {gain}') - self.adc_gain = gain - self.exec_logger.event(f'{self.board_name}\ttx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}') - - def current_pulse(self, **kwargs): - TxAbstract.current_pulse(self, **kwargs) - self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board') - - @property - def current(self): - """ Gets the current IAB in Amps - """ - iab = AnalogIn(self._ads_current, ads.P0).voltage * 1000. / (50 * TX_CONFIG['r_shunt']) # measure current - self.exec_logger.debug(f'Reading TX current: {iab} mA') - return iab - - @ current.setter - def current(self, value): - assert TX_CONFIG['current_min'] <= value <= TX_CONFIG['current_max'] - self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board') + self.pin6 = self.mcp_board.get_pin(6) + self.pin6.direction = Direction.OUTPUT + self.pin6.value = False + self.pin2 = self.mcp_board.get_pin(2) # dsp - + self.pin2.direction = Direction.OUTPUT + self.pin2.value = False + self.pin3 = self.mcp_board.get_pin(3) # dsp - + self.pin3.direction = Direction.OUTPUT + self.pin3.value = False + + if not subclass_init: + self.exec_logger.event(f'{self.model}\ttx_init\tend\t{datetime.datetime.utcnow()}') def inject(self, polarity=1, injection_duration=None): - self.polarity = polarity - TxAbstract.inject(self, polarity=polarity, injection_duration=injection_duration) - - @property - def polarity(self): - return self._polarity - - @polarity.setter - def polarity(self, polarity): - assert polarity in [-1, 0, 1] - self._polarity = polarity - if polarity == 1: - self.pin0.value = True - self.pin1.value = False - time.sleep(0.005) # Max turn on time of 211EH relays = 5ms - elif polarity == -1: - self.pin0.value = False - self.pin1.value = True - time.sleep(0.005) # Max turn on time of 211EH relays = 5ms - else: - self.pin0.value = False - self.pin1.value = False - time.sleep(0.001) # Max turn off time of 211EH relays = 1ms - - def turn_off(self): - self.pwr.turn_off(self) - - def turn_on(self): - self.pwr.turn_on(self) + # add leds? + self.pin6.value = True + Tx_mb_2023.inject(self, polarity=polarity, injection_duration=injection_duration) + self.pin6.value = False @property - def tx_bat(self): - self.soh_logger.warning(f'Cannot get battery voltage on {self.board_name}') - self.exec_logger.debug(f'{self.board_name} cannot read battery voltage. Returning default battery voltage.') - return TX_CONFIG['low_battery'] - - def voltage_pulse(self, voltage=TX_CONFIG['default_voltage'], length=None, polarity=1): - """ Generates a square voltage pulse - - Parameters - ---------- - voltage: float, optional - Voltage to apply in volts, tx_v_def is applied if omitted. - length: float, optional - Length of the pulse in seconds - polarity: 1,0,-1 - Polarity of the pulse - """ - self.exec_logger.event(f'{self.board_name}\ttx_voltage_pulse\tbegin\t{datetime.datetime.utcnow()}') - # self.exec_logger.info(f'injection_duration: {length}') # TODO: delete me - if length is None: - length = self.injection_duration - self.pwr.voltage = voltage - self.exec_logger.debug(f'Voltage pulse of {polarity*self.pwr.voltage:.3f} V for {length:.3f} s') - self.inject(polarity=polarity, injection_duration=length) - self.exec_logger.event(f'{self.board_name}\ttx_voltage_pulse\tend\t{datetime.datetime.utcnow()}') - - -class Rx(RxAbstract): + def pwr_state(self): + return self._pwr_state + + @pwr_state.setter + def pwr_state(self, state): + """Switches pwr on or off. + + Parameters + ---------- + state : str + 'on', 'off' + """ + if state == 'on': + self.pin2.value = True + self.pin3.value = True + self.exec_logger.debug(f'Switching DPS on') + self._pwr_state = 'on' + time.sleep(self._pwr_latency) # from pwr specs + elif state == 'off': + self.pin2.value = False + self.pin3.value = False + self.exec_logger.debug(f'Switching DPS off') + self._pwr_state = 'off' + + +class Rx(Rx_mb_2023): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) + if 'model' not in kwargs.keys(): + for key in SPECS['rx'].keys(): + kwargs = enforce_specs(kwargs, SPECS['rx'], key) + subclass_init = False + else: + subclass_init = True super().__init__(**kwargs) - self.exec_logger.event(f'{self.board_name}\trx_init\tbegin\t{datetime.datetime.utcnow()}') - if self.ctl is None: - self.ctl = ctl_module.Ctl() - self.io = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)] - - # I2C connexion to MCP23008, for DG411 - self.mcp_board = MCP23008(self.io, address=RX_CONFIG['mcp_board_address']) - + if not subclass_init: + self.exec_logger.event(f'{self.model}\trx_init\tbegin\t{datetime.datetime.utcnow()}') + # I2C connection to MCP23008, for voltage + self.mcp_board = MCP23008(self.connection, address=kwargs['mcp_address']) # ADS1115 for voltage measurement (MN) - self._ads_voltage_address = 0x49 - self._adc_gain = 2/3 - self._ads_voltage = ads.ADS1115(self.ctl.bus, gain=self._adc_gain, data_rate=860, - address=self._ads_voltage_address) - self._ads_voltage.mode = Mode.CONTINUOUS - self._coef_p2 = kwargs.pop('coef_p2', RX_CONFIG['coef_p2']) - self._voltage_max = kwargs.pop('voltage_max', RX_CONFIG['voltage_max']) - self._sampling_rate = kwargs.pop('sampling_rate', sampling_rate) - self._latency = kwargs.pop('latency', RX_CONFIG['latency']) - self._bias = kwargs.pop('bias', RX_CONFIG['bias']) - self.exec_logger.event(f'{self.board_name}\trx_init\tend\t{datetime.datetime.utcnow()}') - + self._coef_p2 = 1. + # Define default DG411 gain + self._dg411_gain = 1/2 + # Define pins for DG411 self.pin_DG0 = self.mcp_board.get_pin(0) self.pin_DG0.direction = Direction.OUTPUT self.pin_DG1 = self.mcp_board.get_pin(1) self.pin_DG1.direction = Direction.OUTPUT self.pin_DG2 = self.mcp_board.get_pin(2) self.pin_DG2.direction = Direction.OUTPUT - self.pin_DG0.value = True # open self.pin_DG1.value = True # open gain 1 inactive self.pin_DG2.value = False # close gain 0.5 active - self._voltage_gain = 0.5 + self.gain = 1/3 + if not subclass_init: # TODO: try to only log this event and not the one created by super() + self.exec_logger.event(f'{self.model}\trx_init\tend\t{datetime.datetime.utcnow()}') - @property - def adc_gain(self): - return self._adc_gain - - @adc_gain.setter - def adc_gain(self, value): - assert value in [2/3, 2, 4, 8, 16] - self._adc_gain = value - self._ads_voltage = ads.ADS1115(self.ctl.bus, gain=self.adc_gain, data_rate=860, - address=self._ads_voltage_address) - self._ads_voltage.mode = Mode.CONTINUOUS - self.exec_logger.debug(f'Setting RX ADC gain to {value}') - - def adc_gain_auto(self): - self.exec_logger.event(f'{self.board_name}\trx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}') - gain = 2/3 + def _adc_gain_auto(self): + self.exec_logger.event(f'{self.model}\trx_adc_auto_gain\tbegin\t{datetime.datetime.utcnow()}') + gain = _ads_1115_gain_auto(AnalogIn(self._ads_voltage, ads.P0)) self.exec_logger.debug(f'Setting RX ADC gain automatically to {gain}') - self.adc_gain = gain - self.exec_logger.event(f'{self.board_name}\trx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}') + self._adc_gain = gain + self.exec_logger.event(f'{self.model}\trx_adc_auto_gain\tend\t{datetime.datetime.utcnow()}') - @property - def voltage(self): - """ Gets the voltage VMN in Volts - """ - self.exec_logger.event(f'{self.board_name}\trx_voltage\tbegin\t{datetime.datetime.utcnow()}') - u = -AnalogIn(self._ads_voltage, ads.P0, ads.P1).voltage * self._coef_p2 * 1000. - self._bias # TODO: check if it should be negated - self.exec_logger.event(f'{self.board_name}\trx_voltage\tend\t{datetime.datetime.utcnow()}') - return u + def _dg411_gain_auto(self): + if self.voltage < self._vmn_hardware_offset : + self._dg411_gain = 1. + else: + self._dg411_gain = 1/2 + self.exec_logger.debug(f'Setting RX DG411 gain automatically to {self._dg411_gain}') @property - def voltage_gain(self): - return self._voltage_gain - - @voltage_gain.setter - def voltage_gain(self,value): - assert value in [0.5, 1] - self._voltage_gain = value - if self._voltage_gain == 1: + def gain(self): + return self._adc_gain*self._dg411_gain + + @gain.setter + def gain(self, value): + assert value in [1/3, 2/3] + self._dg411_gain = value / self._adc_gain + if self._dg411_gain == 1.: self.pin_DG1.value = False # closed gain 1 active self.pin_DG2.value = True # open gain 0.5 inactive - elif self._voltage_gain == 0.5: + elif self._dg411_gain == 1/2: self.pin_DG1.value = True # closed gain 1 active self.pin_DG2.value = False # open gain 0.5 inactive - def voltage_gain_auto(self): - u = ((AnalogIn(self.ads_voltage, ads.P0).voltage * 1000) - self.vmn_hardware_offset) / self.voltage_gain - if abs(vmn1) < 2500 and abs(vmn2) < 2500: ###TODO change voltage gain auto logic - self.voltage_gain = 1 - else: - self.voltage_gain = 0.5 + def gain_auto(self): + self._dg411_gain_auto() + self.exec_logger.debug(f'Setting RX gain automatically to {self.gain}') + + @property + def voltage(self): + """ Gets the voltage VMN in Volts + """ + self.exec_logger.event(f'{self.model}\trx_voltage\tbegin\t{datetime.datetime.utcnow()}') + u = (AnalogIn(self._ads_voltage, ads.P0).voltage * self._coef_p2 * 1000. - self._vmn_hardware_offset) / self._dg411_gain - self._bias # TODO: check how to handle bias and _vmn_hardware_offset + self.exec_logger.event(f'{self.model}\trx_voltage\tend\t{datetime.datetime.utcnow()}') + return u diff --git a/ohmpi/hardware_components/mux_2023_0_X.py b/ohmpi/hardware_components/mux_2023_0_X.py index be1ec5a1ac30ab4f686e015a0c652fe863e274ee..1e88aef3f757bf1dafd718243fa602694bc72e6f 100644 --- a/ohmpi/hardware_components/mux_2023_0_X.py +++ b/ohmpi/hardware_components/mux_2023_0_X.py @@ -1,84 +1,95 @@ -import time - -from ohmpi.config import HARDWARE_CONFIG import os import numpy as np +import datetime from ohmpi.hardware_components import MuxAbstract import adafruit_tca9548a # noqa from adafruit_mcp230xx.mcp23017 import MCP23017 # noqa from digitalio import Direction # noqa +from busio import I2C # noqa +from ohmpi.utils import enforce_specs + +# TODO: manage the case when a tca is added to handle more mux_2023 boards # hardware characteristics and limitations -SPECS = {'voltage_max': 50., 'current_max': 3., 'activation_delay': 0.01, 'release_delay': 0.005} +SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'id': {'default': 'mux_??'}, + 'voltage_max': {'default': 50.}, + 'current_max': {'default': 3.}, + 'activation_delay': {'default': 0.01}, + 'release_delay': {'default': 0.005}, + 'mux_tca_address': {'default': 0x70} + } -default_mux_cabling = {(elec, role) : ('mux_1', elec) for role in ['A', 'B', 'M', 'N'] for elec in range(1,9)} # defaults to 4 roles cabling electrodes from 1 to 8 +# defaults to role 'A' cabling electrodes from 1 to 64 +default_mux_cabling = {(elec, role): ('mux_1', elec) for role in ['A'] for elec in range(1, 64)} + +inner_cabling = {'1_role': {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (2, 'X'): {'MCP': 0, 'MCP_GPIO': 1}, + (3, 'X'): {'MCP': 0, 'MCP_GPIO': 2}, (4, 'X'): {'MCP': 0, 'MCP_GPIO': 3}, + (5, 'X'): {'MCP': 0, 'MCP_GPIO': 4}, (6, 'X'): {'MCP': 0, 'MCP_GPIO': 5}, + (7, 'X'): {'MCP': 0, 'MCP_GPIO': 6}, (8, 'X'): {'MCP': 0, 'MCP_GPIO': 7}, + (9, 'X'): {'MCP': 0, 'MCP_GPIO': 8}, (10, 'X'): {'MCP': 0, 'MCP_GPIO': 9}, + (11, 'X'): {'MCP': 0, 'MCP_GPIO': 10}, (12, 'X'): {'MCP': 0, 'MCP_GPIO': 11}, + (13, 'X'): {'MCP': 0, 'MCP_GPIO': 12}, (14, 'X'): {'MCP': 0, 'MCP_GPIO': 13}, + (15, 'X'): {'MCP': 0, 'MCP_GPIO': 14}, (16, 'X'): {'MCP': 0, 'MCP_GPIO': 15}, + (17, 'X'): {'MCP': 1, 'MCP_GPIO': 0}, (18, 'X'): {'MCP': 1, 'MCP_GPIO': 1}, + (19, 'X'): {'MCP': 1, 'MCP_GPIO': 2}, (20, 'X'): {'MCP': 1, 'MCP_GPIO': 3}, + (21, 'X'): {'MCP': 1, 'MCP_GPIO': 4}, (22, 'X'): {'MCP': 1, 'MCP_GPIO': 5}, + (23, 'X'): {'MCP': 1, 'MCP_GPIO': 6}, (24, 'X'): {'MCP': 1, 'MCP_GPIO': 7}, + (25, 'X'): {'MCP': 1, 'MCP_GPIO': 8}, (26, 'X'): {'MCP': 1, 'MCP_GPIO': 9}, + (27, 'X'): {'MCP': 1, 'MCP_GPIO': 10}, (28, 'X'): {'MCP': 1, 'MCP_GPIO': 11}, + (29, 'X'): {'MCP': 1, 'MCP_GPIO': 12}, (30, 'X'): {'MCP': 1, 'MCP_GPIO': 13}, + (31, 'X'): {'MCP': 1, 'MCP_GPIO': 14}, (32, 'X'): {'MCP': 1, 'MCP_GPIO': 15}, + (33, 'X'): {'MCP': 2, 'MCP_GPIO': 0}, (34, 'X'): {'MCP': 2, 'MCP_GPIO': 1}, + (35, 'X'): {'MCP': 2, 'MCP_GPIO': 2}, (36, 'X'): {'MCP': 2, 'MCP_GPIO': 3}, + (37, 'X'): {'MCP': 2, 'MCP_GPIO': 4}, (38, 'X'): {'MCP': 2, 'MCP_GPIO': 5}, + (39, 'X'): {'MCP': 2, 'MCP_GPIO': 6}, (40, 'X'): {'MCP': 2, 'MCP_GPIO': 7}, + (41, 'X'): {'MCP': 2, 'MCP_GPIO': 8}, (42, 'X'): {'MCP': 2, 'MCP_GPIO': 9}, + (43, 'X'): {'MCP': 2, 'MCP_GPIO': 10}, (44, 'X'): {'MCP': 2, 'MCP_GPIO': 11}, + (45, 'X'): {'MCP': 2, 'MCP_GPIO': 12}, (46, 'X'): {'MCP': 2, 'MCP_GPIO': 13}, + (47, 'X'): {'MCP': 2, 'MCP_GPIO': 14}, (48, 'X'): {'MCP': 2, 'MCP_GPIO': 15}, + (49, 'X'): {'MCP': 3, 'MCP_GPIO': 0}, (50, 'X'): {'MCP': 3, 'MCP_GPIO': 1}, + (51, 'X'): {'MCP': 3, 'MCP_GPIO': 2}, (52, 'X'): {'MCP': 3, 'MCP_GPIO': 3}, + (53, 'X'): {'MCP': 3, 'MCP_GPIO': 4}, (54, 'X'): {'MCP': 3, 'MCP_GPIO': 5}, + (55, 'X'): {'MCP': 3, 'MCP_GPIO': 6}, (56, 'X'): {'MCP': 3, 'MCP_GPIO': 7}, + (57, 'X'): {'MCP': 3, 'MCP_GPIO': 8}, (58, 'X'): {'MCP': 3, 'MCP_GPIO': 9}, + (59, 'X'): {'MCP': 3, 'MCP_GPIO': 10}, (60, 'X'): {'MCP': 3, 'MCP_GPIO': 11}, + (61, 'X'): {'MCP': 3, 'MCP_GPIO': 12}, (62, 'X'): {'MCP': 3, 'MCP_GPIO': 13}, + (63, 'X'): {'MCP': 3, 'MCP_GPIO': 14}, (64, 'X'): {'MCP': 3, 'MCP_GPIO': 15} + } + } -inner_cabling = {'1_role' : {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (2, 'X'): {'MCP': 0, 'MCP_GPIO': 1}, - (3, 'X'): {'MCP': 0, 'MCP_GPIO': 2}, (4, 'X'): {'MCP': 0, 'MCP_GPIO': 3}, - (5, 'X'): {'MCP': 0, 'MCP_GPIO': 4}, (6, 'X'): {'MCP': 0, 'MCP_GPIO': 5}, - (7, 'X'): {'MCP': 0, 'MCP_GPIO': 6}, (8, 'X'): {'MCP': 0, 'MCP_GPIO': 7}, - (9, 'X'): {'MCP': 0, 'MCP_GPIO': 8}, (10, 'X'): {'MCP': 0, 'MCP_GPIO': 9}, - (11, 'X'): {'MCP': 0, 'MCP_GPIO': 10}, (12, 'X'): {'MCP': 0, 'MCP_GPIO': 11}, - (13, 'X'): {'MCP': 0, 'MCP_GPIO': 12}, (14, 'X'): {'MCP': 0, 'MCP_GPIO': 13}, - (15, 'X'): {'MCP': 0, 'MCP_GPIO': 14}, (16, 'X'): {'MCP': 0, 'MCP_GPIO': 15}, - (17, 'X'): {'MCP': 1, 'MCP_GPIO': 0}, (18, 'X'): {'MCP': 1, 'MCP_GPIO': 1}, - (19, 'X'): {'MCP': 1, 'MCP_GPIO': 2}, (20, 'X'): {'MCP': 1, 'MCP_GPIO': 3}, - (21, 'X'): {'MCP': 1, 'MCP_GPIO': 4}, (22, 'X'): {'MCP': 1, 'MCP_GPIO': 5}, - (23, 'X'): {'MCP': 1, 'MCP_GPIO': 6}, (24, 'X'): {'MCP': 1, 'MCP_GPIO': 7}, - (25, 'X'): {'MCP': 1, 'MCP_GPIO': 8}, (26, 'X'): {'MCP': 1, 'MCP_GPIO': 9}, - (27, 'X'): {'MCP': 1, 'MCP_GPIO': 10}, (28, 'X'): {'MCP': 1, 'MCP_GPIO': 11}, - (29, 'X'): {'MCP': 1, 'MCP_GPIO': 12}, (30, 'X'): {'MCP': 1, 'MCP_GPIO': 13}, - (31, 'X'): {'MCP': 1, 'MCP_GPIO': 14}, (32, 'X'): {'MCP': 1, 'MCP_GPIO': 15}, - (33, 'X'): {'MCP': 2, 'MCP_GPIO': 0}, (34, 'X'): {'MCP': 2, 'MCP_GPIO': 1}, - (35, 'X'): {'MCP': 2, 'MCP_GPIO': 2}, (36, 'X'): {'MCP': 2, 'MCP_GPIO': 3}, - (37, 'X'): {'MCP': 2, 'MCP_GPIO': 4}, (38, 'X'): {'MCP': 2, 'MCP_GPIO': 5}, - (39, 'X'): {'MCP': 2, 'MCP_GPIO': 6}, (40, 'X'): {'MCP': 2, 'MCP_GPIO': 7}, - (41, 'X'): {'MCP': 2, 'MCP_GPIO': 8}, (42, 'X'): {'MCP': 2, 'MCP_GPIO': 9}, - (43, 'X'): {'MCP': 2, 'MCP_GPIO': 10}, (44, 'X'): {'MCP': 2, 'MCP_GPIO': 11}, - (45, 'X'): {'MCP': 2, 'MCP_GPIO': 12}, (46, 'X'): {'MCP': 2, 'MCP_GPIO': 13}, - (47, 'X'): {'MCP': 2, 'MCP_GPIO': 14}, (48, 'X'): {'MCP': 2, 'MCP_GPIO': 15}, - (49, 'X'): {'MCP': 3, 'MCP_GPIO': 0}, (50, 'X'): {'MCP': 3, 'MCP_GPIO': 1}, - (51, 'X'): {'MCP': 3, 'MCP_GPIO': 2}, (52, 'X'): {'MCP': 3, 'MCP_GPIO': 3}, - (53, 'X'): {'MCP': 3, 'MCP_GPIO': 4}, (54, 'X'): {'MCP': 3, 'MCP_GPIO': 5}, - (55, 'X'): {'MCP': 3, 'MCP_GPIO': 6}, (56, 'X'): {'MCP': 3, 'MCP_GPIO': 7}, - (57, 'X'): {'MCP': 3, 'MCP_GPIO': 8}, (58, 'X'): {'MCP': 3, 'MCP_GPIO': 9}, - (59, 'X'): {'MCP': 3, 'MCP_GPIO': 10}, (60, 'X'): {'MCP': 3, 'MCP_GPIO': 11}, - (61, 'X'): {'MCP': 3, 'MCP_GPIO': 12}, (62, 'X'): {'MCP': 3, 'MCP_GPIO': 13}, - (63, 'X'): {'MCP': 3, 'MCP_GPIO': 14}, (64, 'X'): {'MCP': 3, 'MCP_GPIO': 15} -}} class Mux(MuxAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) + if 'model' not in kwargs.keys(): + for key in SPECS.keys(): + kwargs = enforce_specs(kwargs, SPECS, key) + subclass_init = False + else: + subclass_init = True kwargs.update({'cabling': kwargs.pop('cabling', default_mux_cabling)}) - kwargs.update({'activation_delay': max(kwargs.pop('activation_delay', SPECS['activation_delay']), - SPECS['activation_delay'])}) - kwargs.update({'release_delay': max(kwargs.pop('release_delay', SPECS['release_delay']), - SPECS['activation_delay'])}) - kwargs.update({'voltage_max': max(0., min(kwargs.pop('voltage_max', SPECS['voltage_max']), - SPECS['voltage_max']))}) - kwargs.update({'current_max': max(0., min(kwargs.pop('current_max', SPECS['current_max']), - SPECS['current_max']))}) super().__init__(**kwargs) + if not subclass_init: + self.exec_logger.event(f'{self.model}: {self.board_id}\tmux_init\tbegin\t{datetime.datetime.utcnow()}') assert isinstance(self.connection, I2C) self.exec_logger.debug(f'configuration: {kwargs}') - tca_address = kwargs.pop('tca_address', 0x70) - # tca_channel = kwargs.pop('tca_channel', 0) self._roles = kwargs.pop('roles', None) if self._roles is None: - self._roles = {'A': 'X'} # NOTE: defaults to 4-roles + self._roles = {'A': 'X'} # NOTE: defaults to 1-role if np.alltrue([j in self._roles.values() for j in set([i[1] for i in list(inner_cabling['1_role'].keys())])]): self._mode = '1_role' else: - self.exec_logger.error(f'Invalid role assignment for {self.board_name}: {self._roles} !') + self.exec_logger.error(f'Invalid role assignment for {self.model}: {self._roles} !') self._mode = '' - self._tca = [adafruit_tca9548a.TCA9548A(self.connection, tca_address)[i] for i in np.arange(7, 3, -1)] + self._tca = [adafruit_tca9548a.TCA9548A(self.connection, kwargs['mux_tca_address'])[i] for i in np.arange(7, 3, -1)] # self._mcp_addresses = (kwargs.pop('mcp', '0x20')) # TODO: add assert on valid addresses.. self._mcp = [None, None, None, None] self.reset() if self.addresses is None: self._get_addresses() self.exec_logger.debug(f'{self.board_id} addresses: {self.addresses}') + if not subclass_init: + self.exec_logger.event(f'{self.model}: {self.board_id}\tmux_init\tend\t{datetime.datetime.utcnow()}') def _get_addresses(self): """ Converts inner cabling addressing into (electrodes, role) addressing """ @@ -106,7 +117,5 @@ class Mux(MuxAbstract): d = self.addresses[elec, role] if state == 'on': activate_relay(self._mcp[d['MCP']], d['MCP_GPIO'], True) - # time.sleep(MUX_CONFIG['activation_delay']) # NOTE: moved to MuxAbstract switch if state == 'off': activate_relay(self._mcp[d['MCP']], d['MCP_GPIO'], False) - # time.sleep(MUX_CONFIG['release_delay']) # NOTE: moved to MuxAbstract switch diff --git a/ohmpi/hardware_components/mux_2024_0_X.py b/ohmpi/hardware_components/mux_2024_0_X.py index 48fed4101b90252702ce5430689585ea7d392a81..49c528a19e38c54c56733da2ffa476d46051429f 100644 --- a/ohmpi/hardware_components/mux_2024_0_X.py +++ b/ohmpi/hardware_components/mux_2024_0_X.py @@ -1,20 +1,26 @@ -from ohmpi.config import HARDWARE_CONFIG import os +import datetime import numpy as np from ohmpi.hardware_components import MuxAbstract import adafruit_tca9548a # noqa from adafruit_mcp230xx.mcp23017 import MCP23017 # noqa from digitalio import Direction # noqa -from busio import I2C -# import time +from busio import I2C # noqa +from ohmpi.utils import enforce_specs # hardware characteristics and limitations -SPECS = {'voltage_max': 50., 'current_max': 3., 'activation_delay': 0.01, 'release_delay': 0.005} +SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'id': {'default': 'mux_??'}, + 'voltage_max': {'default': 50.}, + 'current_max': {'default': 3.}, + 'activation_delay': {'default': 0.01}, + 'release_delay': {'default': 0.005} + } # defaults to 4 roles cabling electrodes from 1 to 8 -default_mux_cabling = {(elec, role) : ('mux_1', elec) for role in ['A', 'B', 'M', 'N'] for elec in range(1,9)} +default_mux_cabling = {(elec, role): ('mux_1', elec) for role in ['A', 'B', 'M', 'N'] for elec in range(1, 9)} -inner_cabling = {'4_roles' : {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'MCP': 0, 'MCP_GPIO': 8}, +inner_cabling = {'4_roles': {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'MCP': 0, 'MCP_GPIO': 8}, (2, 'X'): {'MCP': 0, 'MCP_GPIO': 1}, (2, 'Y'): {'MCP': 0, 'MCP_GPIO': 9}, (3, 'X'): {'MCP': 0, 'MCP_GPIO': 2}, (3, 'Y'): {'MCP': 0, 'MCP_GPIO': 10}, (4, 'X'): {'MCP': 0, 'MCP_GPIO': 3}, (4, 'Y'): {'MCP': 0, 'MCP_GPIO': 11}, @@ -30,7 +36,7 @@ inner_cabling = {'4_roles' : {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'M (6, 'XX'): {'MCP': 1, 'MCP_GPIO': 2}, (6, 'YY'): {'MCP': 1, 'MCP_GPIO': 10}, (7, 'XX'): {'MCP': 1, 'MCP_GPIO': 1}, (7, 'YY'): {'MCP': 1, 'MCP_GPIO': 9}, (8, 'XX'): {'MCP': 1, 'MCP_GPIO': 0}, (8, 'YY'): {'MCP': 1, 'MCP_GPIO': 8}}, - '2_roles': # TODO: WARNING check 2_roles table, it has not been verified yet !!! + '2_roles': # TODO: WARNING check 2_roles table, it has not been verified yet !!! {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'MCP': 0, 'MCP_GPIO': 8}, (2, 'X'): {'MCP': 0, 'MCP_GPIO': 1}, (2, 'Y'): {'MCP': 0, 'MCP_GPIO': 9}, (3, 'X'): {'MCP': 0, 'MCP_GPIO': 2}, (3, 'Y'): {'MCP': 0, 'MCP_GPIO': 10}, @@ -47,26 +53,22 @@ inner_cabling = {'4_roles' : {(1, 'X'): {'MCP': 0, 'MCP_GPIO': 0}, (1, 'Y'): {'M (11, 'X'): {'MCP': 1, 'MCP_GPIO': 2}, (11, 'Y'): {'MCP': 1, 'MCP_GPIO': 10}, (10, 'X'): {'MCP': 1, 'MCP_GPIO': 1}, (10, 'Y'): {'MCP': 1, 'MCP_GPIO': 9}, (9, 'X'): {'MCP': 1, 'MCP_GPIO': 0}, (9, 'Y'): {'MCP': 1, 'MCP_GPIO': 8}} - } + } class Mux(MuxAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) - kwargs.update({'cabling': kwargs.pop('cabling', default_mux_cabling)}) - kwargs.update({'activation_delay': max(kwargs.pop('activation_delay', SPECS['activation_delay']), - SPECS['activation_delay'])}) - kwargs.update({'release_delay': max(kwargs.pop('release_delay', SPECS['release_delay']), - SPECS['activation_delay'])}) - kwargs.update({'voltage_max': max(0., min(kwargs.pop('voltage_max', SPECS['voltage_max']), - SPECS['voltage_max']))}) - kwargs.update({'current_max': max(0., min(kwargs.pop('current_max', SPECS['current_max']), - SPECS['current_max']))}) + if 'model' not in kwargs.keys(): + for key in SPECS.keys(): + kwargs = enforce_specs(kwargs, SPECS, key) + subclass_init = False + else: + subclass_init = True super().__init__(**kwargs) + if not subclass_init: + self.exec_logger.event(f'{self.model}: {self.board_id}\tmux_init\tbegin\t{datetime.datetime.utcnow()}') assert isinstance(self.connection, I2C) self.exec_logger.debug(f'configuration: {kwargs}') - tca_address = kwargs.pop('tca_address', None) - tca_channel = kwargs.pop('tca_channel', 0) self._roles = kwargs.pop('roles', None) if self._roles is None: self._roles = {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'} # NOTE: defaults to 4-roles @@ -75,18 +77,39 @@ class Mux(MuxAbstract): elif np.alltrue([j in self._roles.values() for j in set([i[1] for i in list(inner_cabling['2_roles'].keys())])]): self._mode = '2_roles' else: - self.exec_logger.error(f'Invalid role assignment for {self.board_name}: {self._roles} !') + self.exec_logger.error(f'Invalid role assignment for {self.model}: {self._roles} !') self._mode = '' + + # Setup TCA + tca_address = kwargs.pop('tca_address', None) + tca_channel = kwargs.pop('tca_channel', 0) if tca_address is None: self._tca = self.connection else: self._tca = adafruit_tca9548a.TCA9548A(self.connection, tca_address)[tca_channel] - self._mcp_addresses = (kwargs.pop('mcp_0', '0x22'), kwargs.pop('mcp_1', '0x23')) # TODO: add assert on valid addresses.. + + # Setup MCPs + self._mcp_jumper_pos = {'addr2': kwargs.pop('addr2', None), 'addr1': kwargs.pop('addr1', None)} + self._mcp_addresses = (kwargs.pop('mcp_0', None), kwargs.pop('mcp_1', None)) + if self._mcp_addresses[0] is None and self._mcp_addresses[1] is None: + if self._mcp_jumper_pos['addr2'] is not None and self._mcp_jumper_pos['addr1'] is not None: + self._mcp_jumper_pos_to_addr() + self.exec_logger.debug(f"{self.board_id} assigned mcp_addresses {self._mcp_addresses[0]} and " + f"{self._mcp_addresses[1]} from jumper positions.") + else: + self.exec_logger.debug(f'MCP addresses nor jumper positions for {self.board_id} not in config file...') + # TODO: if no addresses defined, should abort or should we set default mcp addresses? + for addr in self._mcp_addresses: + assert addr in ['0x20', '0x21', '0x22', '0x23', '0x24', '0x25', '0x26', '0x27'] self._mcp = [None, None] self.reset() + if self.addresses is None: self._get_addresses() + self.exec_logger.debug(f'{self.board_id} addresses: {self.addresses}') + if not subclass_init: # TODO: try to only log this event and not the one created by super() + self.exec_logger.event(f'{self.model}: {self.board_id}\tmux_init\tend\t{datetime.datetime.utcnow()}') def _get_addresses(self): """ Converts inner cabling addressing into (electrodes, role) addressing """ @@ -116,3 +139,9 @@ class Mux(MuxAbstract): if state == 'off': activate_relay(self._mcp[d['MCP']], d['MCP_GPIO'], False) # time.sleep(MUX_CONFIG['release_delay']) # NOTE: moved to MuxAbstract switch + + def _mcp_jumper_pos_to_addr(self): + d = {'up': 0, 'down': 1} + mcp_0 = hex(int(f"0100{d[self._mcp_jumper_pos['addr2']]}{d[self._mcp_jumper_pos['addr1']]}0", 2)) + mcp_1 = hex(int(f"0100{d[self._mcp_jumper_pos['addr2']]}{d[self._mcp_jumper_pos['addr1']]}1", 2)) + self._mcp_addresses = (mcp_0, mcp_1) \ No newline at end of file diff --git a/ohmpi/hardware_components/pwr_batt.py b/ohmpi/hardware_components/pwr_batt.py index dd811385307fb63e0c3b01e4f3f2de8f790f0c5c..dc67c8b88a742df803691446e0474d5f9c557e83 100644 --- a/ohmpi/hardware_components/pwr_batt.py +++ b/ohmpi/hardware_components/pwr_batt.py @@ -1,18 +1,34 @@ from ohmpi.hardware_components.abstract_hardware_components import PwrAbstract import numpy as np +import datetime import os +from ohmpi.utils import enforce_specs + +# hardware characteristics and limitations +SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'voltage': {'default': 12., 'max': 12., 'min': 12.}, + 'current_adjustable': {'default': False}, + 'voltage_adjustable': {'default': False}, + 'interface': {'default': 'none'}, + } class Pwr(PwrAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) - voltage = kwargs.pop('voltage', 12.) + if 'model' not in kwargs.keys(): + for key in SPECS.keys(): + kwargs = enforce_specs(kwargs, SPECS, key) + subclass_init = False + else: + subclass_init = True super().__init__(**kwargs) - self.voltage_adjustable = False - self._voltage = voltage - self._current_adjustable = False + if not subclass_init: + self.exec_logger.event(f'{self.model}\tpwr_init\tbegin\t{datetime.datetime.utcnow()}') + self._voltage = kwargs['voltage'] self._current = np.nan - self._state = 'on' + # self._state = 'on' + if not subclass_init: + self.exec_logger.event(f'{self.model}\tpwr_init\tend\t{datetime.datetime.utcnow()}') @property def current(self): @@ -20,13 +36,13 @@ class Pwr(PwrAbstract): @current.setter def current(self, value, **kwargs): - self.exec_logger.debug(f'Current cannot be set on {self.board_name}') - - def turn_off(self): - self.exec_logger.debug(f'{self.board_name} cannot be turned off') - - def turn_on(self): - self.exec_logger.debug(f'{self.board_name} is always on') + self.exec_logger.debug(f'Current cannot be set on {self.model}') + # + # def turn_off(self): + # self.exec_logger.debug(f'{self.model} cannot be turned off') + # + # def turn_on(self): + # self.exec_logger.debug(f'{self.model} is always on') @property def voltage(self): @@ -34,4 +50,4 @@ class Pwr(PwrAbstract): @voltage.setter def voltage(self, value): - PwrAbstract.voltage.fset(self, value) \ No newline at end of file + PwrAbstract.voltage.fset(self, value) diff --git a/ohmpi/hardware_components/pwr_dps5005.py b/ohmpi/hardware_components/pwr_dps5005.py index 7e4d7ceac90172f0944f78f67e5923565e8b3acf..dd2b0d8e85c638c40b3dd27feea931303ba13d1b 100644 --- a/ohmpi/hardware_components/pwr_dps5005.py +++ b/ohmpi/hardware_components/pwr_dps5005.py @@ -1,38 +1,50 @@ from ohmpi.hardware_components.abstract_hardware_components import PwrAbstract -from ohmpi.config import HARDWARE_CONFIG -import importlib import numpy as np -import minimalmodbus # noqa +import datetime import os +import time +from ohmpi.utils import enforce_specs +from minimalmodbus import Instrument # noqa -CTL_CONFIG = HARDWARE_CONFIG['ctl'] -ctl_name = HARDWARE_CONFIG['ctl'].pop('board_name', 'raspberry_pi') -ctl_connection = HARDWARE_CONFIG['ctl'].pop('connection', 'modbus') -ctl_module = importlib.import_module(f'ohmpi.hardware_components.{ctl_name}') -CTL_CONFIG['baudrate'] = CTL_CONFIG.pop('baudrate', 9600) -CTL_CONFIG['bitesize'] = CTL_CONFIG.pop('bitesize', 8) -CTL_CONFIG['timeout'] = CTL_CONFIG.pop('timeout', 1) -CTL_CONFIG['debug'] = CTL_CONFIG.pop('debug', False) -CTL_CONFIG['parity'] = CTL_CONFIG.pop('parity', 'N') -CTL_CONFIG['mode'] = CTL_CONFIG.pop('mode', minimalmodbus.MODE_RTU) -CTL_CONFIG['port'] = CTL_CONFIG.pop('port', '/dev/ttyUSB0') -CTL_CONFIG['slave_address'] = CTL_CONFIG.pop('slave_address', 1) +# hardware characteristics and limitations +SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'voltage': {'default': 12., 'max': 50., 'min': 0.}, + 'voltage_min': {'default': 0}, + 'voltage_max': {'default': 0}, + 'current_max': {'default': 100.}, + 'current_adjustable': {'default': False}, + 'voltage_adjustable': {'default': True}, + 'pwr_latency': {'default': .3} + } + +# TODO: Complete this code... handle modbus connection class Pwr(PwrAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) - voltage = kwargs.pop('voltage', 12.) + if 'model' not in kwargs.keys(): + for key in SPECS.keys(): + kwargs = enforce_specs(kwargs, SPECS, key) + subclass_init = False + else: + subclass_init = True super().__init__(**kwargs) + if not subclass_init: + self.exec_logger.event(f'{self.model}\tpwr_init\tbegin\t{datetime.datetime.utcnow()}') + assert isinstance(self.connection, Instrument) # if a controller is passed in kwargs, it will be instantiated - if self.ctl is None: - self.ctl = ctl_module.Ctl(**CTL_CONFIG) - self.connection = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)] + #if self.ctl is None: + # self.ctl = ctl_module.Ctl(**CTL_CONFIG) + #self.connection = self.ctl.interfaces[kwargs.pop('connection', ctl_connection)] + self._voltage = kwargs['voltage'] + self._current_max = kwargs['current_max'] self.voltage_adjustable = True - self._voltage = voltage - self._current_adjustable = False + self.current_adjustable = False self._current = np.nan - self._current_max = kwargs.pop('current_max', 100.) + self._pwr_state = 'off' + self._pwr_latency = kwargs['pwr_latency'] + if not subclass_init: + self.exec_logger.event(f'{self.model}\tpwr_init\tend\t{datetime.datetime.utcnow()}') @property def current(self): @@ -40,27 +52,54 @@ class Pwr(PwrAbstract): @current.setter def current(self, value, **kwargs): - self.exec_logger.debug(f'Current cannot be set on {self.board_name}') - - def turn_off(self): - self.connection.write_register(0x09, 1) - self.exec_logger.debug(f'{self.board_name} is off') + self.exec_logger.debug(f'Current cannot be set on {self.model}') - def turn_on(self): - self.connection.write_register(0x09, 1) - self.exec_logger.debug(f'{self.board_name} is on') + # def turn_off(self): + # self.connection.write_register(0x09, 0) + # self.exec_logger.debug(f'{self.model} is off') + # + # def turn_on(self): + # self.connection.write_register(0x09, 1) + # self.exec_logger.debug(f'{self.model} is on') + # time.sleep(.3) @property def voltage(self): - return PwrAbstract.voltage.fget(self) + # return PwrAbstract.voltage.fget(self) + return self._voltage @voltage.setter def voltage(self, value): self.connection.write_register(0x0000, value, 2) - + self._voltage = value + def battery_voltage(self): - self.connection.read_register(0x05, 2) + self._battery_voltage = self.connection.read_register(0x05, 2) + return self._battery_voltage - @property - def current_max(self,value): + def current_max(self, value): self.connection.write_register(0x0001, value * 10, 0) + + @property + def pwr_state(self): + return self._pwr_state + + @pwr_state.setter + def pwr_state(self, state): + """Switches pwr on or off. + + Parameters + ---------- + state : str + 'on', 'off' + """ + if state == 'on': + self.connection.write_register(0x09, 1) + self._pwr_state = 'on' + self.exec_logger.debug(f'{self.model} is on') + time.sleep(self._pwr_latency) # from pwr specs + + elif state == 'off': + self.connection.write_register(0x09, 0) + self._pwr_state = 'off' + self.exec_logger.debug(f'{self.model} is off') diff --git a/ohmpi/hardware_components/raspberry_pi.py b/ohmpi/hardware_components/raspberry_pi.py index 86c6f00a4a8c3005a0b4aceaecb31dc4e301bd2b..d45ad095c3cf7f7ef977d8a67677378f9d38521b 100644 --- a/ohmpi/hardware_components/raspberry_pi.py +++ b/ohmpi/hardware_components/raspberry_pi.py @@ -4,47 +4,71 @@ import busio # noqa from adafruit_extended_bus import ExtendedI2C # noqa import minimalmodbus # noqa import os -from ohmpi.utils import get_platform +from ohmpi.utils import get_platform, enforce_specs from gpiozero import CPUTemperature # noqa +import warnings + +# hardware characteristics and limitations +SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')}, + 'voltage': {'default': 12., 'max': 50., 'min': 0.}, + 'modbus_baudrate': {'default': 9600}, + 'modbus_bitesize': {'default': 8}, + 'modbus_timeout': {'default': 1}, + 'modbus_debug': {'default': False}, + 'modbus_parity': {'default': 'N'}, + 'modbus_mode': {'default': minimalmodbus.MODE_RTU}, + 'modbus_port': {'default': '/dev/ttyUSB0'}, + 'modbus_slave_address': {'default': 1} + } class Ctl(CtlAbstract): def __init__(self, **kwargs): - kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) - modbus_baudrate = kwargs.pop('modbus_baudrate', 9600) - modbus_bitesize = kwargs.pop('modbus_bitesize', 8) - modbus_timeout = kwargs.pop('modbus_timeout', 1) - modbus_debug = kwargs.pop('modbus_debug', False) - modbus_parity = kwargs.pop('modbus_parity', 'N') - modbus_mode = kwargs.pop('modbus_mode', minimalmodbus.MODE_RTU) - modbus_port = kwargs.pop('modbus_port', '/dev/ttyUSB0') - modbus_slave_address = kwargs.pop('modbus_slave_address', 1) + if 'model' not in kwargs.keys(): + for key in SPECS.keys(): + kwargs = enforce_specs(kwargs, SPECS, key) + subclass_init = False + else: + subclass_init = True super().__init__(**kwargs) self.interfaces = dict() + + # None interface for battery + self.interfaces['none'] = None + + # warnings.filterwarnings("error") # to filter out adafruit warning about setting I2C frequency # I2C - self.interfaces['i2c'] = busio.I2C(board.SCL, board.SDA) # noqa + try: + self.interfaces['i2c'] = busio.I2C(board.SCL, board.SDA) # noqa + except RuntimeWarning: + pass # Extended I2C try: self.interfaces['i2c_ext'] = ExtendedI2C(4) # 4 is defined + except RuntimeWarning: + pass except Exception as e: - self.exec_logger.warning('Could not initialize Extended I2C:\n{e}') + self.exec_logger.warning(f'Could not initialize Extended I2C:\n{e}') + warnings.resetwarnings() + # modbus try: - self.interfaces['modbus'] = minimalmodbus.Instrument(port=modbus_port, slaveaddress=modbus_slave_address) - self.interfaces['modbus'].serial.baudrate = modbus_baudrate # Baud rate 9600 as listed in doc - self.interfaces['modbus'].serial.bytesize = modbus_bitesize # - self.interfaces['modbus'].serial.timeout = modbus_timeout # greater than 0.5 for it to work - self.interfaces['modbus'].debug = modbus_debug # - self.interfaces['modbus'].serial.parity = modbus_parity # No parity - self.interfaces['modbus'].mode = modbus_mode # RTU mode + self.interfaces['modbus'] = minimalmodbus.Instrument(port=kwargs['modbus_port'], + slaveaddress=kwargs['modbus_slave_address']) + self.interfaces['modbus'].serial.baudrate = kwargs['modbus_baudrate'] # Baud rate 9600 as listed in doc + self.interfaces['modbus'].serial.bytesize = kwargs['modbus_bitesize'] # + self.interfaces['modbus'].serial.timeout = kwargs['modbus_timeout'] # greater than 0.5 for it to work + self.interfaces['modbus'].debug = kwargs['modbus_debug'] # + self.interfaces['modbus'].serial.parity = kwargs['modbus_parity'] # No parity + self.interfaces['modbus'].mode = kwargs['modbus_mode'] # RTU mode except Exception as e: - self.exec_logger.warning('Could not initialize Extended modbus:\n{e}') + self.exec_logger.warning(f'Could not initialize Extended modbus:\n{e}') platform, on_pi = get_platform() assert on_pi - self.board_name = platform + self.model = platform self._cpu_temp_available = True self.max_cpu_temp = 85. # °C diff --git a/ohmpi/hardware_system.py b/ohmpi/hardware_system.py index f3fe7ba648a64a416e331b2c4185b4811de9c4ca..dde487a0561da4cc8bda3eb476eddfeb34244ace 100644 --- a/ohmpi/hardware_system.py +++ b/ohmpi/hardware_system.py @@ -11,6 +11,7 @@ from ohmpi.logging_setup import create_stdout_logger from ohmpi.utils import update_dict from ohmpi.config import HARDWARE_CONFIG from threading import Thread, Event, Barrier, BrokenBarrierError +import warnings # plt.switch_backend('agg') # for thread safe operations... @@ -28,11 +29,24 @@ for k, v in MUX_CONFIG.items(): for k2, v2 in MUX_DEFAULT.items(): MUX_CONFIG[k].update({k2: MUX_CONFIG[k].pop(k2, v2)}) -TX_CONFIG = tx_module.TX_CONFIG -RX_CONFIG = rx_module.RX_CONFIG - -current_max = np.min([TX_CONFIG['current_max'], np.min([MUX_CONFIG[i].pop('current_max', np.inf) for i in MUX_CONFIG.keys()])]) -voltage_max = np.min([TX_CONFIG['voltage_max'], np.min([MUX_CONFIG[i].pop('voltage_max', np.inf) for i in MUX_CONFIG.keys()])]) +TX_CONFIG = HARDWARE_CONFIG['tx'] +for k, v in tx_module.SPECS['tx'].items(): + try: + TX_CONFIG.update({k: TX_CONFIG.pop(k, v['default'])}) + except Exception as e: + print(f'Cannot set value {v} in TX_CONFIG[{k}]:\n{e}') + +RX_CONFIG = HARDWARE_CONFIG['rx'] +for k, v in rx_module.SPECS['rx'].items(): + try: + RX_CONFIG.update({k: RX_CONFIG.pop(k, v['default'])}) + except Exception as e: + print(f'Cannot set value {v} in RX_CONFIG[{k}]:\n{e}') + +current_max = np.min([TX_CONFIG['voltage_max']/50/TX_CONFIG['r_shunt'], # TODO: replace 50 by a TX config + np.min(np.hstack((np.inf, [MUX_CONFIG[i].pop('current_max', np.inf) for i in MUX_CONFIG.keys()])))]) +voltage_max = np.min([TX_CONFIG['voltage_max'], + np.min(np.hstack((np.inf, [MUX_CONFIG[i].pop('voltage_max', np.inf) for i in MUX_CONFIG.keys()])))]) voltage_min = RX_CONFIG['voltage_min'] @@ -43,17 +57,14 @@ def elapsed_seconds(start_time): class OhmPiHardware: def __init__(self, **kwargs): - self.exec_logger = kwargs.pop('exec_logger', None) + # OhmPiHardware initialization + self.exec_logger = kwargs.pop('exec_logger', create_stdout_logger('exec_hw')) self.exec_logger.event(f'OhmPiHardware\tinit\tbegin\t{datetime.datetime.utcnow()}') - if self.exec_logger is None: - self.exec_logger = create_stdout_logger('exec_hw') - self.data_logger = kwargs.pop('exec_logger', None) - if self.data_logger is None: - self.data_logger = create_stdout_logger('data_hw') - self.soh_logger = kwargs.pop('soh_logger', None) - if self.soh_logger is None: - self.soh_logger = create_stdout_logger('soh_hw') + self.data_logger = kwargs.pop('exec_logger', create_stdout_logger('data_hw')) + self.soh_logger = kwargs.pop('soh_logger', create_stdout_logger('soh_hw')) self.tx_sync = Event() + + # Main Controller initialization HARDWARE_CONFIG['ctl'].pop('model') HARDWARE_CONFIG['ctl'].update({'exec_logger': self.exec_logger, 'data_logger': self.data_logger, 'soh_logger': self.soh_logger}) @@ -65,17 +76,26 @@ class OhmPiHardware: ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}') self.ctl = ctl_mod.Ctl(**self.ctl) + # Initialize RX HARDWARE_CONFIG['rx'].pop('model') - HARDWARE_CONFIG['rx'].update(**HARDWARE_CONFIG['rx']) + HARDWARE_CONFIG['rx'].update(**HARDWARE_CONFIG['rx']) # TODO: delete me ? HARDWARE_CONFIG['rx'].update({'ctl': HARDWARE_CONFIG['rx'].pop('ctl', self.ctl)}) if isinstance(HARDWARE_CONFIG['rx']['ctl'], dict): ctl_mod = HARDWARE_CONFIG['rx']['ctl'].pop('model', self.ctl) if isinstance(ctl_mod, str): ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}') HARDWARE_CONFIG['rx']['ctl'] = ctl_mod.Ctl(**HARDWARE_CONFIG['rx']['ctl']) + HARDWARE_CONFIG['rx'].update({'connection': + HARDWARE_CONFIG['rx'].pop('connection', + HARDWARE_CONFIG['rx']['ctl'].interfaces[ + HARDWARE_CONFIG['rx'].pop( + 'interface_name', 'i2c')])}) HARDWARE_CONFIG['rx'].update({'exec_logger': self.exec_logger, 'data_logger': self.data_logger, 'soh_logger': self.soh_logger}) + HARDWARE_CONFIG['tx'].pop('ctl', None) self.rx = kwargs.pop('rx', rx_module.Rx(**HARDWARE_CONFIG['rx'])) + + # Initialize power source HARDWARE_CONFIG['pwr'].pop('model') HARDWARE_CONFIG['pwr'].update(**HARDWARE_CONFIG['pwr']) # NOTE: Explain why this is needed or delete me HARDWARE_CONFIG['pwr'].update({'ctl': HARDWARE_CONFIG['pwr'].pop('ctl', self.ctl)}) @@ -84,19 +104,39 @@ class OhmPiHardware: if isinstance(ctl_mod, str): ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}') HARDWARE_CONFIG['pwr']['ctl'] = ctl_mod.Ctl(**HARDWARE_CONFIG['pwr']['ctl']) + #if 'interface_name' in HARDWARE_CONFIG['pwr']: + HARDWARE_CONFIG['pwr'].update({ + 'connection': HARDWARE_CONFIG['pwr'].pop( + 'connection', HARDWARE_CONFIG['pwr']['ctl'].interfaces[ + HARDWARE_CONFIG['pwr'].pop('interface_name', None)])}) + HARDWARE_CONFIG['pwr'].update({'exec_logger': self.exec_logger, 'data_logger': self.data_logger, 'soh_logger': self.soh_logger}) self.pwr = kwargs.pop('pwr', pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])) + + # Initialize TX HARDWARE_CONFIG['tx'].pop('model') HARDWARE_CONFIG['tx'].update(**HARDWARE_CONFIG['tx']) HARDWARE_CONFIG['tx'].update({'tx_sync': self.tx_sync}) - HARDWARE_CONFIG['tx'].update({'ctl': self.ctl}) + HARDWARE_CONFIG['tx'].update({'ctl': HARDWARE_CONFIG['tx'].pop('ctl', self.ctl)}) + if isinstance(HARDWARE_CONFIG['tx']['ctl'], dict): + ctl_mod = HARDWARE_CONFIG['tx']['ctl'].pop('model', self.ctl) + if isinstance(ctl_mod, str): + ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}') + HARDWARE_CONFIG['tx']['ctl'] = ctl_mod.Ctl(**HARDWARE_CONFIG['tx']['ctl']) + HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection', + HARDWARE_CONFIG['tx']['ctl'].interfaces[ + HARDWARE_CONFIG['tx'].pop( + 'interface_name', 'i2c')])}) + HARDWARE_CONFIG['tx'].pop('ctl', None) HARDWARE_CONFIG['tx'].update({'exec_logger': self.exec_logger, 'data_logger': self.data_logger, 'soh_logger': self.soh_logger}) self.tx = kwargs.pop('tx', tx_module.Tx(**HARDWARE_CONFIG['tx'])) if isinstance(self.tx, dict): self.tx = tx_module.Tx(**self.tx) self.tx.pwr = self.pwr + + # Initialize Muxes self._cabling = kwargs.pop('cabling', {}) self.mux_boards = {} for mux_id, mux_config in MUX_CONFIG.items(): @@ -106,45 +146,83 @@ class OhmPiHardware: mux_config.update({'ctl': mux_config.pop('ctl', self.ctl)}) mux_module = importlib.import_module(f'ohmpi.hardware_components.{mux_config["model"]}') - if isinstance(mux_config['ctl'], dict): ### TODO: is this needed? + if isinstance(mux_config['ctl'], dict): mux_ctl_module = importlib.import_module(f'ohmpi.hardware_components.{mux_config["ctl"]["model"]}') mux_config['ctl'] = mux_ctl_module.Ctl(**mux_config['ctl']) # (**self.ctl) assert issubclass(type(mux_config['ctl']), CtlAbstract) - io = mux_config.pop('io', mux_config['ctl'].interfaces[mux_config.pop('connection', 'i2c')]) - mux_config.update({'io': io}) + mux_config.update({'connection': mux_config.pop('connection', mux_config['ctl'].interfaces[mux_config.pop('interface_name', 'i2c')])}) mux_config['id'] = mux_id self.mux_boards[mux_id] = mux_module.Mux(**mux_config) - # self.mux_boards = kwargs.pop('mux', {'mux_1': mux_module.Mux(id='mux_1', - # exec_logger=self.exec_logger, - # data_logger=self.data_logger, - # soh_logger=self.soh_logger, - # ctl=self.ctl, - # cabling=self._cabling)}) - self.mux_barrier = Barrier(len(self.mux_boards) + 1) self._cabling = {} for mux_id, mux in self.mux_boards.items(): mux.barrier = self.mux_barrier for k, v in mux.cabling.items(): update_dict(self._cabling, {k: (mux_id, k[0])}) + + # Complete OhmPiHardware initialization self.readings = np.array([]) # time series of acquired data self._start_time = None # time of the beginning of a readings acquisition self._pulse = 0 # pulse number self.exec_logger.event(f'OhmPiHardware\tinit\tend\t{datetime.datetime.utcnow()}') + self._pwr_state = 'off' + + @property + def pwr_state(self): + return self._pwr_state + + @pwr_state.setter + def pwr_state(self, state): + if state == 'on': + self.tx.pwr_state = 'on' + self._pwr_state = 'on' + elif state == 'off': + self.tx.pwr_state = 'off' + self._pwr_state = 'off' def _clear_values(self): self.readings = np.array([]) self._start_time = None self._pulse = 0 - def _gain_auto(self): # TODO: improve _gain_auto + def _gain_auto(self, polarities=(1, -1), vab=5., switch_pwr_off=False): #TODO: improve _gain_auto self.exec_logger.event(f'OhmPiHardware\ttx_rx_gain_auto\tbegin\t{datetime.datetime.utcnow()}') - self.tx_sync.wait() - self.tx.adc_gain_auto() - self.rx.adc_gain_auto() - self.rx.voltage_gain_auto() + current, voltage = 0., 0. + if self.tx.pwr.voltage_adjustable: + self.tx.pwr.voltage = vab + if self.tx.pwr.pwr_state == 'off': + self.tx.pwr.pwr_state = 'on' + switch_pwr_off = True + tx_gains = [] + rx_gains = [] + for pol in polarities: + # self.tx.polarity = pol + # set gains automatically + injection = Thread(target=self._inject, kwargs={'injection_duration': 0.2, 'polarity': pol}) + # readings = Thread(target=self._read_values) + get_tx_gain = Thread(target=self.tx.gain_auto) + get_rx_gain = Thread(target=self.rx.gain_auto) + injection.start() + self.tx_sync.wait() + get_tx_gain.start() # TODO: add a barrier to synchronize? + get_rx_gain.start() + get_tx_gain.join() + get_rx_gain.join() + injection.join() + tx_gains.append(self.tx.gain) + rx_gains.append(self.rx.gain) + + # v = self.readings[:, 2] != 0 + # current = max(current, np.mean(self.readings[v, 3])) + # voltage = max(voltage, np.abs(np.mean(self.readings[v, 2] * self.readings[v, 4]))) + self.tx.polarity = 0 + self.tx.gain = min(tx_gains) + self.rx.gain = min(rx_gains) + # self.rx.gain_auto(voltage) + if switch_pwr_off: + self.tx.pwr.pwr_state = 'off' self.exec_logger.event(f'OhmPiHardware\ttx_rx_gain_auto\tend\t{datetime.datetime.utcnow()}') def _inject(self, polarity=1, injection_duration=None): # TODO: deal with voltage or current pulse @@ -158,7 +236,7 @@ class OhmPiHardware: mux.barrier = self.mux_barrier @property - def pulses(self): + def pulses(self): # TODO: is this obsolete? pulses = {} for i in np.unique(self.readings[:, 1]): r = self.readings[self.readings[:, 1] == i, :] @@ -204,20 +282,18 @@ class OhmPiHardware: self._pulse += 1 self.exec_logger.event(f'OhmPiHardware\tread_values\tend\t{datetime.datetime.utcnow()}') - @property - def last_rho(self): - v = self.readings[:, 2] != 0 + def last_resistance(self, delay=0.): + v = np.where((self.readings[:, 0] >= delay) & (self.readings[:, 2] != 0))[0] if len(v) > 1: # return np.mean(np.abs(self.readings[v, 4] - self.sp) / self.readings[v, 3]) return np.mean(self.readings[v, 2] * (self.readings[v, 4] - self.sp) / self.readings[v, 3]) else: return np.nan - @property - def last_dev(self): - if len(self.readings) > 1: - v = self.readings[:, 2] != 0 # exclude sample where there is no injection - return 100. * np.std(self.readings[v, 2] * (self.readings[v, 4] - self.sp) / self.readings[v, 3]) / self.last_rho + def last_dev(self, delay=0.): + v = np.where((self.readings[:, 0] >= delay) & (self.readings[:, 2] != 0))[0] + if len(v) > 1: + return 100. * np.std(self.readings[v, 2] * (self.readings[v, 4] - self.sp) / self.readings[v, 3]) / self.last_resistance(delay=delay) else: return np.nan @@ -241,7 +317,7 @@ class OhmPiHardware: sp = np.mean(mean_vmn[np.ix_(polarity == 1)] + mean_vmn[np.ix_(polarity == -1)]) / 2 return sp - def _compute_tx_volt(self, pulse_duration=0.1, strategy='vmax', tx_volt=5, + def _compute_tx_volt(self, pulse_duration=0.1, strategy='vmax', tx_volt=5., vab_max=voltage_max, vmn_min=voltage_min): """Estimates best Tx voltage based on different strategies. At first a half-cycle is made for a short duration with a fixed @@ -285,7 +361,14 @@ class OhmPiHardware: vab_max = np.abs(vab_max) vmn_min = np.abs(vmn_min) vab = np.min([np.abs(tx_volt), vab_max]) - self.tx.turn_on() + # self.tx.turn_on() + switch_pwr_off, switch_tx_pwr_off = False, False #TODO: check if these should be moved in kwargs + if self.tx.pwr_state == 'off': + self.tx.pwr_state = 'on' + switch_tx_pwr_off = True + if self.tx.pwr.pwr_state == 'off': + self.tx.pwr.pwr_state = 'on' + switch_pwr_off = True if 1. / self.rx.sampling_rate > pulse_duration: sampling_rate = 1. / pulse_duration # TODO: check this... else: @@ -307,7 +390,11 @@ class OhmPiHardware: self.tx.exec_logger.warning(f'Unknown strategy {strategy} for setting VAB! Using {vab} V') else: self.tx.exec_logger.debug(f'Constant strategy for setting VAB, using {vab} V') - self.tx.turn_off() + # self.tx.turn_off() + if switch_pwr_off: + self.tx.pwr.pwr_state = 'off' + if switch_tx_pwr_off: + self.tx.pwr_state = 'off' rab = (np.abs(vab) * 1000.) / iab self.exec_logger.debug(f'RAB = {rab:.2f} Ohms') if vmn < 0: @@ -318,10 +405,11 @@ class OhmPiHardware: def _plot_readings(self, save_fig=False): # Plot graphs + warnings.filterwarnings("ignore", category=DeprecationWarning) fig, ax = plt.subplots(nrows=5, sharex=True) ax[0].plot(self.readings[:, 0], self.readings[:, 3], '-r', marker='.', label='iab') ax[0].set_ylabel('Iab [mA]') - ax[1].plot(self.readings[:, 0], self.readings[:, 2] * (self.readings[:, 4] - self.sp) , '-b', marker='.', label='vmn') + ax[1].plot(self.readings[:, 0], self.readings[:, 4] - self.sp , '-b', marker='.', label='vmn') ax[1].set_ylabel('Vmn [mV]') ax[2].plot(self.readings[:, 0], self.readings[:, 2], '-g', marker='.', label='polarity') ax[2].set_ylabel('polarity [-]') @@ -329,13 +417,14 @@ class OhmPiHardware: ax[3].plot(self.readings[v, 0], (self.readings[v, 2] * (self.readings[v, 4] - self.sp)) / self.readings[v, 3], '-m', marker='.', label='R [ohm]') ax[3].set_ylabel('R [ohm]') - ax[4].plot(self.readings[v, 0], np.ones_like(self.readings[v,0]) * self.sp, '-k', marker='.', label='SP [mV]') + ax[4].plot(self.readings[v, 0], np.ones_like(self.readings[v, 0]) * self.sp, '-k', marker='.', label='SP [mV]') ax[4].set_ylabel('SP [mV]') - fig.legend() + # fig.legend() if save_fig: fig.savefig(f'figures/test.png') else: plt.show() + warnings.resetwarnings() def calibrate_rx_bias(self): self.rx._bias += (np.mean(self.readings[self.readings[:, 2] == 1, 4]) @@ -344,19 +433,19 @@ class OhmPiHardware: def vab_square_wave(self, vab, cycle_duration, sampling_rate=None, cycles=3, polarity=1, duty_cycle=1., append=False): self.exec_logger.event(f'OhmPiHardware\tvab_square_wave\tbegin\t{datetime.datetime.utcnow()}') - self.tx.polarity = polarity ### TODO: inject on both polarities for gain auto? - durations = [cycle_duration/2]*2*cycles - # set gains automatically - gain_auto = Thread(target=self._gain_auto) - injection = Thread(target=self._inject, kwargs={'injection_duration': 0.2, 'polarity': polarity}) - gain_auto.start() - injection.start() - gain_auto.join() - injection.join() + switch_pwr_off, switch_tx_pwr_off = False, False + if self.pwr_state == 'off': + self.pwr_state = 'on' + switch_tx_pwr_off = True + # if self.tx.pwr.pwr_state == 'off': + # self.tx.pwr.pwr_state = 'on' + # switch_pwr_off = True + self._gain_auto(vab=vab) assert 0. <= duty_cycle <= 1. if duty_cycle < 1.: - durations = [cycle_duration/2 * duty_cycle, cycle_duration/2*(1.-duty_cycle)] * 2 * cycles - pol = [-self.tx.polarity * np.heaviside(i % 2, -1.) for i in range(2 * cycles)] + durations = [cycle_duration/2 * duty_cycle, cycle_duration/2 * (1.-duty_cycle)] * 2 * cycles + pol = [-int(polarity * np.heaviside(i % 2, -1.)) for i in range(2 * cycles)] + # pol = [-int(self.tx.polarity * np.heaviside(i % 2, -1.)) for i in range(2 * cycles)] polarities = [0] * (len(pol) * 2) polarities[0::2] = pol else: @@ -364,15 +453,20 @@ class OhmPiHardware: polarities = None self._vab_pulses(vab, durations, sampling_rate, polarities=polarities, append=append) self.exec_logger.event(f'OhmPiHardware\tvab_square_wave\tend\t{datetime.datetime.utcnow()}') + if switch_pwr_off: + self.tx.pwr.pwr_state = 'off' + if switch_tx_pwr_off: + self.pwr_state = 'off' - def _vab_pulse(self, vab, duration, sampling_rate=None, polarity=1, append=False): + def _vab_pulse(self, vab=None, duration=1., sampling_rate=None, polarity=1, append=False): """ Gets VMN and IAB from a single voltage pulse """ - self.tx.polarity = polarity + #self.tx.polarity = polarity if sampling_rate is None: sampling_rate = RX_CONFIG['sampling_rate'] if self.tx.pwr.voltage_adjustable: - self.tx.pwr.voltage = vab + if self.tx.pwr.voltage != vab: + self.tx.pwr.voltage = vab else: vab = self.tx.pwr.voltage # reads current and voltage during the pulse @@ -382,23 +476,38 @@ class OhmPiHardware: injection.start() readings.join() injection.join() - self.tx.polarity = 0 + self.tx.polarity = 0 #TODO: is this necessary? def _vab_pulses(self, vab, durations, sampling_rate, polarities=None, append=False): + switch_pwr_off, switch_tx_pwr_off = False, False + if self.pwr_state == 'off': + self.pwr_state = 'on' + switch_pwr_off = True n_pulses = len(durations) self.exec_logger.debug(f'n_pulses: {n_pulses}') + if self.tx.pwr.voltage_adjustable: + self.tx.pwr.voltage = vab + else: + vab = self.tx.pwr.voltage + if self.tx.pwr.pwr_state == 'off': + self.tx.pwr.pwr_state = 'on' + switch_pwr_off = True if sampling_rate is None: sampling_rate = RX_CONFIG['sampling_rate'] if polarities is not None: assert len(polarities) == n_pulses else: - polarities = [-self.tx.polarity * np.heaviside(i % 2, -1.) for i in range(n_pulses)] + polarities = [-int(self.tx.polarity * np.heaviside(i % 2, -1.)) for i in range(n_pulses)] if not append: self._clear_values() for i in range(n_pulses): - self._vab_pulse(self, duration=durations[i], sampling_rate=sampling_rate, polarity=polarities[i], + self._vab_pulse(vab=vab, duration=durations[i], sampling_rate=sampling_rate, polarity=polarities[i], append=True) - + if switch_pwr_off: + self.tx.pwr.pwr_state = 'off' + if switch_tx_pwr_off: + self.pwr_state = 'off' + def switch_mux(self, electrodes, roles=None, state='off', **kwargs): """Switches on multiplexer relays for given quadrupole. @@ -437,7 +546,7 @@ class OhmPiHardware: # Create a new thread to perform some work self.mux_boards[mux].barrier = b kwargs.update({'elec_dict': elec_dict, 'state': state}) - mux_workers[idx] = Thread(target=self.mux_boards[mux].switch, kwargs=kwargs) + mux_workers[idx] = Thread(target=self.mux_boards[mux].switch, kwargs=kwargs) # TODO: handle minimum delay between two relays activation (to avoid lagging during test_mux at high speed) mux_workers[idx].start() try: self.mux_barrier.wait() @@ -453,7 +562,7 @@ class OhmPiHardware: self.exec_logger.event(f'OhmPiHardware\tswitch_mux\tend\t{datetime.datetime.utcnow()}') return status - def test_mux(self, channel=None, activation_time=1.0): + def test_mux(self, channel=None, activation_time=1.0): #TODO: add test in reverse order on each mux board """Interactive method to test the multiplexer. Parameters @@ -476,11 +585,19 @@ class OhmPiHardware: time.sleep(activation_time) self.switch_mux(electrodes, roles, state='off') else: - for c in self._cabling.keys(): - self.exec_logger.info(f'Testing electrode {c[0]} with role {c[1]}.') - self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='on') - time.sleep(activation_time) - self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='off') + list_of_muxes = [i for i in self.mux_boards.keys()] + list_of_muxes.sort() + for m_id in list_of_muxes: + for c in self.mux_boards[m_id].cabling.keys(): + self.exec_logger.info(f'Testing electrode {c[0]} with role {c[1]}.') + self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='on') + time.sleep(activation_time) + self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='off') + # for c in self._cabling.keys(): + # self.exec_logger.info(f'Testing electrode {c[0]} with role {c[1]}.') + # self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='on') + # time.sleep(activation_time) + # self.switch_mux(electrodes=[c[0]], roles=[c[1]], state='off') self.exec_logger.info('Test finished.') def reset_mux(self): diff --git a/ohmpi/logging_setup.py b/ohmpi/logging_setup.py index e2606b024a70b57843cf8ef2890d24b30c13afc7..33927655e620ea488ad99e3e05b1001cf0b6dbeb 100644 --- a/ohmpi/logging_setup.py +++ b/ohmpi/logging_setup.py @@ -10,6 +10,11 @@ import sys from termcolor import colored +def get_logging_levels(): + """Gets a list of the logging levels loaded""" + return [logging.getLevelName(x) for x in range(1,101) if not logging.getLevelName(x).startswith('Level')] + + def add_logging_level(level_name, level_num, method_name=None): """ Comprehensively adds a new logging level to the `logging` module and the @@ -72,6 +77,8 @@ def create_stdout_logger(name): handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(logging.DEBUG) + if 'EVENT' not in get_logging_levels(): + add_logging_level('EVENT', logging.DEBUG + 1) return logger diff --git a/ohmpi/ohmpi.py b/ohmpi/ohmpi.py index c176d901dc21f9c236d11573318ad1d7962c31ec..2035c81d14c505ca8becc2e761880c3a24c8737f 100644 --- a/ohmpi/ohmpi.py +++ b/ohmpi/ohmpi.py @@ -86,11 +86,17 @@ class OhmPi(object): 'nb_meas': 1, 'sequence_delay': 1, 'nb_stack': 1, - 'export_path': 'data/measurement.csv' + 'sampling_interval': 2, + 'tx_volt': 5, + 'duty_cycle': 0.5, + 'strategy': 'constant', + 'export_path': None, + 'export_dir': 'data', + 'export_name': 'measurement.csv' } # read in acquisition settings - if settings is not None: - self.update_settings(settings) + # if settings is not None: + self.update_settings(settings) self.exec_logger.debug('Initialized with settings:' + str(self.settings)) @@ -162,6 +168,7 @@ class OhmPi(object): @staticmethod def append_and_save(filename: str, last_measurement: dict, cmd_id=None): + # TODO: find alternative approach to save full data (zip, hdf5 or mseed?) """Appends and saves the last measurement dict. Parameters @@ -410,12 +417,13 @@ class OhmPi(object): else: self.exec_logger.warning('Not on Raspberry Pi, skipping reboot...') - def run_measurement(self, quad=None, nb_stack=None, injection_duration=None, + def run_measurement(self, quad=None, nb_stack=None, injection_duration=None, duty_cycle=None, autogain=True, strategy='constant', tx_volt=5., best_tx_injtime=0.1, cmd_id=None, **kwargs): # TODO: add sampling_interval -> impact on _hw.rx.sampling_rate (store the current value, change the _hw.rx.sampling_rate, do the measurement, reset the sampling_rate to the previous value) # TODO: default value of tx_volt and other parameters set to None should be given in config.py and used in function definition # TODO: add rs_check option (or propose an other way to do this) + # TODO: implement compute_tx_volt for injection strategies """Measures on a quadrupole and returns transfer resistance. Parameters @@ -443,28 +451,43 @@ class OhmPi(object): cmd_id : str, optional Unique command identifier """ + # check pwr is on, if not, let's turn it on + switch_power_off = False + if self._hw.pwr_state == 'off': + self._hw.pwr_state = 'on' + switch_power_off = True + self.exec_logger.debug('Starting measurement') self.exec_logger.debug('Waiting for data') # check arguments if quad is None: - quad = [0, 0, 0, 0] + quad = np.array([0, 0, 0, 0]) if nb_stack is None: nb_stack = self.settings['nb_stack'] if injection_duration is None: - injection_duration = self.settings['injection_duration'] - tx_volt = float(tx_volt) + injection_duration = self.settings['injection_duration'] + if duty_cycle is None: + duty_cycle = self.settings['duty_cycle'] + # quad = kwargs.pop('quad', [0,0,0,0]) + # nb_stack = kwargs.pop('nb_stack', self.settings['nb_stack']) + # injection_duration = kwargs.pop('injection_duration', self.settings['injection_duration']) + # duty_cycle = kwargs.pop('duty_cycle', self.settings['duty_cycle']) + # tx_volt = float(kwargs.pop('tx_volt', self.settings['tx_volt'])) bypass_check = kwargs['bypass_check'] if 'bypass_check' in kwargs.keys() else False if self.switch_mux_on(quad, bypass_check=bypass_check, cmd_id=cmd_id): - self._hw.vab_square_wave(tx_volt, cycle_duration=injection_duration*2, cycles=nb_stack, duty_cycle=kwargs.pop('duty_cycle', 1.)) + self._hw.vab_square_wave(tx_volt, cycle_duration=injection_duration*2/duty_cycle, cycles=nb_stack, duty_cycle=duty_cycle) if 'delay' in kwargs.keys(): delay = kwargs['delay'] else: - delay = 0. + delay = injection_duration * 2/3 # TODO: check if this is ok and if last point is not taken the end of injection x = np.where((self._hw.readings[:, 0] >= delay) & (self._hw.readings[:, 2] != 0)) - print(f'length of series: {len(x)}') - R = np.mean((self._hw.readings[x, 2] * (self._hw.readings[x, 4] - self._hw.sp)) / self._hw.readings[x, 3]) - R_std = 100. * np.std((self._hw.readings[x, 2] * (self._hw.readings[x, 4] - self._hw.sp)) / self._hw.readings[x, 3]) / R + Vmn = np.mean(self._hw.readings[x, 2] * (self._hw.readings[x, 4] - self._hw.sp)) + Vmn_std = 100. * np.std(self._hw.readings[x, 2] * (self._hw.readings[x, 4])) # - self._hw.sp)) + I = np.mean(self._hw.readings[x, 3]) + I_std = 100. * np.std(self._hw.readings[x, 3]) + R = self._hw.last_resistance(delay=delay) + R_std = self._hw.last_dev(delay=delay) d = { "time": datetime.now().isoformat(), "A": quad[0], @@ -472,8 +495,8 @@ class OhmPi(object): "M": quad[2], "N": quad[3], "inj time [ms]": injection_duration, # NOTE: check this - # "Vmn [mV]": sum_vmn / (2 * nb_stack), - # "I [mA]": sum_i / (2 * nb_stack), + "Vmn [mV]": Vmn, + "I [mA]": I, "R [ohm]": R, "R_std [%]": R_std, "Ps [mV]": self._hw.sp, @@ -483,10 +506,10 @@ class OhmPi(object): "Nb samples [-]": len(self._hw.readings), # TODO: use only samples after a delay in each pulse "fulldata": self._hw.readings[:, [0, -2, -1]], # "I_stack [mA]": i_stack_mean, - # "I_std [mA]": i_std, + "I_std [%]": I_std, # "I_per_stack [mA]": np.array([np.mean(i_stack[i*2:i*2+2]) for i in range(nb_stack)]), # "Vmn_stack [mV]": vmn_stack_mean, - # "Vmn_std [mV]": vmn_std, + "Vmn_std [%]": Vmn_std, # "Vmn_per_stack [mV]": np.array([np.diff(np.mean(vmn_stack[i*2:i*2+2], axis=1))[0] / 2 for i in range(nb_stack)]), # "R_stack [ohm]": r_stack_mean, # "R_std [ohm]": r_stack_std, @@ -515,6 +538,11 @@ class OhmPi(object): else: self.exec_logger.info(f'Skipping {quad}') self.switch_mux_off(quad, cmd_id) + + # if power was off before measurement, let's turn if off + if switch_power_off: + self._hw.pwr_state = 'off' + return d def run_multiple_sequences(self, cmd_id=None, sequence_delay=None, nb_meas=None, **kwargs): # NOTE : could be renamed repeat_sequence @@ -571,6 +599,8 @@ class OhmPi(object): cmd_id : str, optional Unique command identifier """ + # switch power on + self._hw.pwr_state = 'on' self.status = 'running' self.exec_logger.debug(f'Status: {self.status}') self.exec_logger.debug(f'Measuring sequence: {self.sequence}') @@ -578,7 +608,11 @@ class OhmPi(object): self.reset_mux() # create filename with timestamp - filename = self.settings["export_path"].replace('.csv', + if self.settings["export_path"] is None: + filename = self.settings['export_path'].replace( + '.csv', f'_{datetime.now().strftime("%Y%m%dT%H%M%S")}.csv') + else: + filename = self.settings["export_path"].replace('.csv', f'_{datetime.now().strftime("%Y%m%dT%H%M%S")}.csv') self.exec_logger.debug(f'Saving to {filename}') @@ -614,7 +648,7 @@ class OhmPi(object): # self.switch_mux_on(quad) # run a measurement if self.on_pi: - acquired_data = self.run_measurement(quad, **kwargs) + acquired_data = self.run_measurement(quad=quad, **kwargs) else: # for testing, generate random data sum_vmn = np.random.rand(1)[0] * 1000. sum_i = np.random.rand(1)[0] * 100. @@ -648,7 +682,7 @@ class OhmPi(object): self.append_and_save(filename, acquired_data) self.exec_logger.debug(f'quadrupole {i + 1:d}/{n:d}') - # self.switch_dps('off') + self._hw.pwr_state = 'off' self.status = 'idle' def run_sequence_async(self, cmd_id=None, **kwargs): @@ -789,7 +823,10 @@ class OhmPi(object): self.exec_logger.debug(f'tx pwr voltage: {self._hw.tx.pwr.voltage}, rx max voltage: {self._hw.rx._voltage_max}') return False else: - return self._hw.switch_mux(electrodes=quadrupole, state='on', bypass_check=bypass_check) + if np.array(quadrupole).all() == np.array([0, 0, 0, 0]).all(): # NOTE: No mux + return True + else: + return self._hw.switch_mux(electrodes=quadrupole, state='on', bypass_check=bypass_check) def switch_mux_off(self, quadrupole, cmd_id=None): """Switches off multiplexer relays for given quadrupole. @@ -840,7 +877,13 @@ class OhmPi(object): - nb_meas (total number of times the sequence will be run) - sequence_delay (delay in second between each sequence run) - nb_stack (number of stack for each quadrupole measurement) - - export_path (path where to export the data, timestamp will be added to filename) + - strategy (injection strategy: constant, vmax, vmin) + - duty_cycle (injection duty cycle comprised between 0.5 - 1) + - export_dir (directory where to export the data) + - export_name (name of exported file, timestamp will be added to filename) + - export_path (path where to export the data, timestamp will be added to filename ; + if export_path is given, it goes over export_dir and export_name) + Parameters ---------- @@ -865,8 +908,113 @@ class OhmPi(object): status = False else: self.exec_logger.warning('Settings are missing...') + + if self.settings['export_path'] is None: + self.settings['export_path'] = os.path.join(self.settings['export_dir'], self.settings['export_name']) + else: + self.settings['export_dir'] = os.path.split(self.settings['export_path'])[0] + self.settings['export_name'] = os.path.split(self.settings['export_path'])[1] + return status + def run_inversion(self, survey_names=[], elec_spacing=1, **kwargs): + """Run a simple 2D inversion using ResIPy. + + Parameters + ---------- + survey_names : list of string, optional + Filenames of the survey to be inverted (including extension). + elec_spacing : float (optional) + Electrode spacing in meters. We assume same electrode spacing everywhere. + kwargs : optional + Additiona keyword arguments passed to `resipy.Project.invert()`. For instance + `reg_mode` == 0 for batch inversion, `reg_mode == 2` for time-lapse inversion. + See ResIPy document for more information on options available + (https://hkex.gitlab.io/resipy/). + + Returns + ------- + xzv : list of dict + Each dictionnary with key 'x' and 'z' for the centroid of the elements and 'v' + for the values in resistivity of the elements. + """ + # check if we have any files to be inverted + if len(survey_names) == 0: + self.exec_logger('No file to invert') + return [] + + # check if user didn't provide a single string instead of a list + if isinstance(survey_names, str): + survey_names = [survey_names] + + # check kwargs for reg_mode + if 'reg_mode' in kwargs: + reg_mode = kwargs['reg_mode'] + else: + reg_mode = 0 + kwargs['reg_mode'] = 0 + + pdir = os.path.dirname(__file__) + # import resipy if available + try: + import pandas as pd + import sys + sys.path.append(os.path.join(pdir, '../../resipy/src/')) + from resipy import Project + except Exception as e: + self.exec_logger.error('Cannot import ResIPy or Pandas, error: ' + str(e)) + return [] + + # get absolule filename + fnames = [] + for survey_name in survey_names: + fname = os.path.join(pdir, '../data', survey_name) + if os.path.exists(fname): + fnames.append(fname) + else: + self.exec_logger.warning(fname + ' not found') + + # define a parser for the "ohmpi" format + def ohmpiParser(fname): + df = pd.read_csv(fname) + df = df.rename(columns={'A':'a', 'B':'b', 'M':'m', 'N':'n'}) + df['vp'] = df['Vmn [mV]'] + df['i'] = df['I [mA]'] + df['resist'] = df['vp']/df['i'] + df['ip'] = np.nan + emax = np.max(df[['a', 'b', 'm', 'n']].values) + elec = np.zeros((emax, 3)) + elec[:, 0] = np.arange(emax) * elec_spacing + return elec, df[['a','b','m','n','vp','i','resist','ip']] + + # run inversion + self.exec_logger.info('ResIPy: import surveys') + k = Project(typ='R2') # invert in a temporary directory that will be erased afterwards + if len(survey_names) == 1: + k.createSurvey(fnames[0], parser=ohmpiParser) + elif len(survey_names) > 0 and reg_mode == 0: + k.createBatchSurvey(fnames, parser=ohmpiParser) + elif len(survey_names) > 0 and reg_mode > 0: + k.createTimeLapseSurvey(fnames, parser=ohmpiParser) + self.exec_logger.info('ResIPy: generate mesh') + k.createMesh('trian') + self.exec_logger.info('ResIPy: invert survey') + k.invert(param=kwargs) + + # read data + self.exec_logger.info('Reading inverted surveys') + k.getResults() + xzv = [] + for m in k.meshResults: + df = m.df + xzv.append({ + 'x': df['X'].values.tolist(), + 'z': df['Z'].values.tolist(), + 'rho': df['Resistivity(ohm.m)'].values.tolist(), + }) + + return xzv + # Properties @property def sequence(self): diff --git a/ohmpi/utils.py b/ohmpi/utils.py index 6d2b718d36018af8ab37e49404178c368c07aab4..142d4fb40fa4627bebaeb5f84e578adb52291cfa 100644 --- a/ohmpi/utils.py +++ b/ohmpi/utils.py @@ -3,8 +3,26 @@ import os import shutil import collections.abc import numpy as np +from numbers import Number +def enforce_specs(kwargs, specs, key): + + kwargs.update({key: kwargs.pop(key, specs[key]['default'])}) + + if isinstance(kwargs[key], Number): + s = specs.copy() + min_value = s[key].pop('min', -np.inf) + s[key]['min'] = min_value + max_value = s[key].pop('max', np.inf) + s[key]['max'] = max_value + if kwargs[key] < min_value: + kwargs[key] = min_value + elif kwargs[key] > max_value: + kwargs[key] = max_value + + return kwargs + def update_dict(d, u): """Updates a dictionary by adding elements to collection items associated to existing keys diff --git a/requirements.txt b/requirements.txt index 8010a45145eadd15c5e76e6b1f8b836c62734c38..4c649e24d14cd66c5bc3460be933dd9a686ffc60 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,9 +3,11 @@ adafruit-blinka adafruit-circuitpython-ads1x15 adafruit-circuitpython-tca9548a adafruit-circuitpython-mcp230xx +adafruit_extended_bus minimalmodbus gpiozero numpy paho-mqtt termcolor pandas +matplotlib \ No newline at end of file diff --git a/uml_diagrams/classes_uml_ohmpi.dot b/uml_diagrams/classes_uml_ohmpi.dot index 2071ce04e004cfe6ade11f8de935531f3d3e3386..42176748e50c7c3c6eec2fa10fd8d0084f8e38b8 100644 --- a/uml_diagrams/classes_uml_ohmpi.dot +++ b/uml_diagrams/classes_uml_ohmpi.dot @@ -2,28 +2,28 @@ digraph "classes_uml_ohmpi" { charset="utf-8" rankdir=BT "0" [label="{CompressedSizedTimedRotatingFileHandler|maxBytes : int\lstream\lzip_mode : int\l|doRollover()\lfind_last_rotated_file()\lshouldRollover(record)\l}", shape="record"]; -"1" [label="{Ctl|board_name : str\linterfaces : dict\lmax_cpu_temp : float\l|}", shape="record"]; +"1" [label="{Ctl|interfaces : dict\lmax_cpu_temp : float\lmodel : str\l|}", shape="record"]; "2" [label="{Ctl|\l|}", shape="record"]; -"3" [label="{CtlAbstract|board_name\lconnection : NoneType\lcpu_temperature\lexec_logger : NoneType, RootLogger\linterfaces : NoneType\lmax_cpu_temp\lsoh_logger : NoneType, RootLogger\l|}", shape="record"]; +"3" [label="{CtlAbstract|connection\lcpu_temperature\lexec_logger : RootLogger, NoneType\linterfaces : NoneType\lmax_cpu_temp\lmodel\lsoh_logger : RootLogger, NoneType\l|}", shape="record"]; "4" [label="{MQTTHandler|auth : NoneType\lclient_id : str\lhostname\lkeepalive : int\lport : int\lprotocol\lqos : int\lretain : bool\ltls : NoneType\ltopic\ltransport : str\lwill : NoneType\l|emit(record)\l}", shape="record"]; -"5" [label="{Mux|addresses : dict\lio\l|reset()\lswitch_one(elec, role, state)\l}", shape="record"]; +"5" [label="{Mux|addresses : dict\l|reset()\lswitch_one(elec, role, state)\l}", shape="record"]; "6" [label="{Mux|\l|reset()\lswitch_one()\ltest()\l}", shape="record"]; "7" [label="{Mux|addresses : dict\l|reset()\lswitch_one(elec, role, state)\l}", shape="record"]; -"8" [label="{MuxAbstract|addresses\lbarrier\lboard_id\lboard_name\lcabling : dict\lconnection\lexec_logger : NoneType, RootLogger\lsoh_logger : NoneType, RootLogger\l|reset()\lswitch(elec_dict, state, bypass_check)\lswitch_one(elec, role, state)\ltest(elec_dict, activation_time)\l}", shape="record"]; +"8" [label="{MuxAbstract|addresses\lbarrier\lboard_id\lcabling : dict\lconnection\lexec_logger\lmodel\lsoh_logger\l|reset()\lswitch(elec_dict, state, bypass_check)\lswitch_one(elec, role, state)\ltest(elec_dict, activation_time)\l}", shape="record"]; "9" [label="{MyServer|\l|do_POST()\l}", shape="record"]; -"10" [label="{OhmPi|cmd_id : NoneType\lcontroller : NoneType\ldata_logger : NoneType, RootLogger\lexec_logger : NoneType, RootLogger\lid : str\lmqtt : bool\lnb_samples : int\lon_pi : NoneType, bool\lsequence\lsequence : ndarray, NoneType\lsettings : dict\lsoh_logger : NoneType, RootLogger\lstatus : str\lthread : Thread, NoneType\l|append_and_save(filename, last_measurement, cmd_id)\lget_data(survey_names, cmd_id)\lget_deprecated_methods(cls)\linterrupt(cmd_id)\lload_sequence(filename, cmd_id)\lquit(cmd_id)\lremove_data(cmd_id)\lreset_mux(cmd_id)\lrestart(cmd_id)\lrs_check(tx_volt, cmd_id)\lrun_measurement(quad, nb_stack, injection_duration, autogain, strategy, tx_volt, best_tx_injtime, cmd_id)\lrun_multiple_sequences(cmd_id, sequence_delay, nb_meas)\lrun_sequence(cmd_id)\lrun_sequence_async(cmd_id)\lset_sequence(sequence, cmd_id)\lswitch_mux_off(quadrupole, cmd_id)\lswitch_mux_on(quadrupole, bypass_check, cmd_id)\ltest_mux(activation_time, mux_id, cmd_id)\lupdate_settings(settings, cmd_id)\l}", shape="record"]; -"11" [label="{OhmPiHardware|ctl\ldata_logger : NoneType, RootLogger\lexec_logger : NoneType, RootLogger\llast_dev\llast_rho\lmux_barrier : Barrier\lmux_boards : dict\lpulses\lpwr\lreadings : ndarray\lrx\lsoh_logger : NoneType, RootLogger\lsp\ltx\ltx_sync : Event\l|calibrate_rx_bias()\lreset_mux()\lswitch_mux(electrodes, roles, state)\ltest_mux(channel, activation_time)\lvab_square_wave(vab, cycle_duration, sampling_rate, cycles, polarity, duty_cycle, append)\l}", shape="record"]; -"12" [label="{Pwr|connection\lctl\lcurrent\lcurrent_max\lvoltage\lvoltage_adjustable : bool\l|battery_voltage()\lturn_off()\lturn_on()\l}", shape="record"]; -"13" [label="{Pwr|current\lvoltage\lvoltage_adjustable : bool\l|turn_off()\lturn_on()\l}", shape="record"]; -"14" [label="{PwrAbstract|board_name\lconnection\lctl\lcurrent\lexec_logger : NoneType, RootLogger\lsoh_logger : NoneType, RootLogger\lvoltage\lvoltage_adjustable\l|turn_off()\lturn_on()\l}", shape="record"]; -"15" [label="{Rx|adc_gain\ladc_gain\lconnection\lctl\lvoltage\l|adc_gain_auto()\l}", shape="record"]; +"10" [label="{OhmPi|cmd_id : NoneType\lcontroller : NoneType\ldata_logger : RootLogger, NoneType\lexec_logger : RootLogger, NoneType\lid : str\lmqtt : bool\lnb_samples : int\lon_pi : bool, NoneType\lsequence\lsequence : NoneType, ndarray\lsettings : dict\lsoh_logger : RootLogger, NoneType\lstatus : str\lthread : Thread, NoneType\l|append_and_save(filename, last_measurement, cmd_id)\lget_data(survey_names, cmd_id)\lget_deprecated_methods(cls)\linterrupt(cmd_id)\lload_sequence(filename, cmd_id)\lquit(cmd_id)\lremove_data(cmd_id)\lreset_mux(cmd_id)\lrestart(cmd_id)\lrs_check(tx_volt, cmd_id)\lrun_measurement(quad, nb_stack, injection_duration, autogain, strategy, tx_volt, best_tx_injtime, cmd_id)\lrun_multiple_sequences(cmd_id, sequence_delay, nb_meas)\lrun_sequence(cmd_id)\lrun_sequence_async(cmd_id)\lset_sequence(sequence, cmd_id)\lswitch_mux_off(quadrupole, cmd_id)\lswitch_mux_on(quadrupole, bypass_check, cmd_id)\ltest_mux(activation_time, mux_id, cmd_id)\lupdate_settings(settings, cmd_id)\l}", shape="record"]; +"11" [label="{OhmPiHardware|ctl\ldata_logger\lexec_logger\lmux_barrier : Barrier\lmux_boards : dict\lpulses\lpwr\lreadings : ndarray\lrx\lsoh_logger\lsp\ltx\ltx_sync : Event\l|calibrate_rx_bias()\llast_dev(delay)\llast_resistance(delay)\lreset_mux()\lswitch_mux(electrodes, roles, state)\ltest_mux(channel, activation_time)\lvab_square_wave(vab, cycle_duration, sampling_rate, cycles, polarity, duty_cycle, append)\l}", shape="record"]; +"12" [label="{Pwr|current\lcurrent_adjustable : bool\lvoltage\lvoltage_adjustable : bool\l|battery_voltage()\lcurrent_max(value)\lturn_off()\lturn_on()\l}", shape="record"]; +"13" [label="{Pwr|current\lvoltage\l|turn_off()\lturn_on()\l}", shape="record"]; +"14" [label="{PwrAbstract|connection\lcurrent\lcurrent_adjustable\lexec_logger : RootLogger, NoneType\lmodel\lsoh_logger : RootLogger, NoneType\lvoltage\lvoltage_adjustable\l|turn_off()\lturn_on()\l}", shape="record"]; +"15" [label="{Rx|gain\lgain : int, float\lvoltage\l|gain_auto()\l}", shape="record"]; "16" [label="{Rx|adc_gain\ladc_gain : float\lvoltage\l|adc_gain_auto()\l}", shape="record"]; -"17" [label="{Rx|adc_gain\ladc_gain : float\lctl\lio\lmcp_board : MCP23008\lpin_DG0 : DigitalInOut\lpin_DG1 : DigitalInOut\lpin_DG2 : DigitalInOut\lvoltage\lvoltage_gain\lvoltage_gain : int, float\l|adc_gain_auto()\lvoltage_gain_auto()\l}", shape="record"]; -"18" [label="{RxAbstract|adc_gain\lboard_name\lconnection\lctl\lexec_logger : NoneType, RootLogger\lsampling_rate\lsoh_logger : NoneType, RootLogger\lvoltage\l|adc_gain_auto()\l}", shape="record"]; -"19" [label="{Tx|adc_gain\ladc_gain : int, float\lconnection\lctl\lcurrent\lcurrent_adjustable : bool\lmcp_board : MCP23008\lpin0 : DigitalInOut\lpin1 : DigitalInOut\lpin4 : DigitalInOut\lpolarity\lpolarity : int\lpwr : NoneType\ltx_bat\lvoltage_adjustable : bool\l|adc_gain_auto()\lcurrent_pulse()\linject(polarity, injection_duration)\lturn_off()\lturn_on()\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"]; +"17" [label="{Rx|gain\lgain : int, float\lmcp_board : MCP23008\lpin_DG0 : DigitalInOut\lpin_DG1 : DigitalInOut\lpin_DG2 : DigitalInOut\lvoltage\l|gain_auto()\l}", shape="record"]; +"18" [label="{RxAbstract|adc_gain\lconnection\lexec_logger : RootLogger, NoneType\lmodel\lsampling_rate\lsoh_logger : RootLogger, NoneType\lvoltage\l|}", shape="record"]; +"19" [label="{Tx|adc_voltage_max\ladc_voltage_min\lcurrent\lcurrent_adjustable : bool\lgain\lgain : int, float\lmcp_board : MCP23008\lpin0 : DigitalInOut\lpin1 : DigitalInOut\lpolarity\lpolarity : int\lr_shunt\ltx_bat\lvoltage_adjustable : bool\l|current_pulse()\lgain_auto()\linject(polarity, injection_duration)\lturn_off()\lturn_on()\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"]; "20" [label="{Tx|adc_gain\ladc_gain : float\lcurrent\lpolarity : int\ltx_bat\lvoltage\l|adc_gain_auto()\lcurrent_pulse()\linject(state)\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"]; -"21" [label="{Tx|adc_gain\ladc_gain : int, float\lctl\lcurrent\lcurrent_adjustable : bool\lio\lmcp_board : MCP23008\lpin0 : DigitalInOut\lpin1 : DigitalInOut\lpin4 : DigitalInOut\lpolarity\lpolarity : int\lpwr : NoneType\ltx_bat\lvoltage_adjustable : bool\l|adc_gain_auto()\lcurrent_pulse()\linject(polarity, injection_duration)\lturn_off()\lturn_on()\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"]; -"22" [label="{TxAbstract|adc_gain\lboard_name\lconnection\lctl\lexec_logger : NoneType, RootLogger\linjection_duration\linjection_duration\lpolarity\lpwr\lsoh_logger : NoneType, RootLogger\ltx_bat\ltx_sync\l|adc_gain_auto()\lcurrent_pulse()\linject(polarity, injection_duration)\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"]; +"21" [label="{Tx|pin4 : DigitalInOut\lpin6 : DigitalInOut\l|inject(polarity, injection_duration)\l}", shape="record"]; +"22" [label="{TxAbstract|adc_gain\lconnection\lexec_logger : RootLogger, NoneType\linjection_duration\linjection_duration\lmodel\lpolarity\lpwr\lsoh_logger : RootLogger, NoneType\ltx_bat\ltx_sync\l|current_pulse()\linject(polarity, injection_duration, switch_pwr)\lvoltage_pulse(voltage, length, polarity)\l}", shape="record"]; "1" -> "3" [arrowhead="empty", arrowtail="none"]; "2" -> "3" [arrowhead="empty", arrowtail="none"]; "5" -> "8" [arrowhead="empty", arrowtail="none"]; @@ -33,9 +33,9 @@ rankdir=BT "13" -> "14" [arrowhead="empty", arrowtail="none"]; "15" -> "18" [arrowhead="empty", arrowtail="none"]; "16" -> "18" [arrowhead="empty", arrowtail="none"]; -"17" -> "18" [arrowhead="empty", arrowtail="none"]; +"17" -> "15" [arrowhead="empty", arrowtail="none"]; "19" -> "22" [arrowhead="empty", arrowtail="none"]; "20" -> "22" [arrowhead="empty", arrowtail="none"]; -"21" -> "22" [arrowhead="empty", arrowtail="none"]; +"21" -> "19" [arrowhead="empty", arrowtail="none"]; "11" -> "10" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="_hw", style="solid"]; } diff --git a/uml_diagrams/classes_uml_ohmpi.dot.png b/uml_diagrams/classes_uml_ohmpi.dot.png index aee047e1953c5924ff5078c615f9f46bffc3a7ae..119f5bc7235296e1369cbf350cb60fa17c5324b8 100644 Binary files a/uml_diagrams/classes_uml_ohmpi.dot.png and b/uml_diagrams/classes_uml_ohmpi.dot.png differ diff --git a/uml_diagrams/classes_uml_ohmpi.dot.svg b/uml_diagrams/classes_uml_ohmpi.dot.svg index a9efb920da2c9b15bcb79a3d04970923e89b84f5..d352fa4ce181488811b9a734deab1cf59a773b16 100644 --- a/uml_diagrams/classes_uml_ohmpi.dot.svg +++ b/uml_diagrams/classes_uml_ohmpi.dot.svg @@ -4,103 +4,102 @@ <!-- Generated by graphviz version 2.43.0 (0) --> <!-- Title: classes_uml_ohmpi Pages: 1 --> -<svg width="3998pt" height="949pt" - viewBox="0.00 0.00 3997.50 949.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> -<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 945)"> +<svg width="3415pt" height="1221pt" + viewBox="0.00 0.00 3414.50 1221.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1217)"> <title>classes_uml_ohmpi</title> -<polygon fill="white" stroke="transparent" points="-4,4 -4,-945 3993.5,-945 3993.5,4 -4,4"/> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-1217 3410.5,-1217 3410.5,4 -4,4"/> <!-- 0 --> <g id="node1" class="node"> <title>0</title> -<polygon fill="none" stroke="black" points="0,-113 0,-242 335,-242 335,-113 0,-113"/> -<text text-anchor="middle" x="167.5" y="-226.8" font-family="Times,serif" font-size="14.00">CompressedSizedTimedRotatingFileHandler</text> -<polyline fill="none" stroke="black" points="0,-219 335,-219 "/> -<text text-anchor="start" x="8" y="-203.8" font-family="Times,serif" font-size="14.00">maxBytes : int</text> -<text text-anchor="start" x="8" y="-188.8" font-family="Times,serif" font-size="14.00">stream</text> -<text text-anchor="start" x="8" y="-173.8" font-family="Times,serif" font-size="14.00">zip_mode : int</text> -<polyline fill="none" stroke="black" points="0,-166 335,-166 "/> -<text text-anchor="start" x="8" y="-150.8" font-family="Times,serif" font-size="14.00">doRollover()</text> -<text text-anchor="start" x="8" y="-135.8" font-family="Times,serif" font-size="14.00">find_last_rotated_file()</text> -<text text-anchor="start" x="8" y="-120.8" font-family="Times,serif" font-size="14.00">shouldRollover(record)</text> +<polygon fill="none" stroke="black" points="0,-105.5 0,-234.5 335,-234.5 335,-105.5 0,-105.5"/> +<text text-anchor="middle" x="167.5" y="-219.3" font-family="Times,serif" font-size="14.00">CompressedSizedTimedRotatingFileHandler</text> +<polyline fill="none" stroke="black" points="0,-211.5 335,-211.5 "/> +<text text-anchor="start" x="8" y="-196.3" font-family="Times,serif" font-size="14.00">maxBytes : int</text> +<text text-anchor="start" x="8" y="-181.3" font-family="Times,serif" font-size="14.00">stream</text> +<text text-anchor="start" x="8" y="-166.3" font-family="Times,serif" font-size="14.00">zip_mode : int</text> +<polyline fill="none" stroke="black" points="0,-158.5 335,-158.5 "/> +<text text-anchor="start" x="8" y="-143.3" font-family="Times,serif" font-size="14.00">doRollover()</text> +<text text-anchor="start" x="8" y="-128.3" font-family="Times,serif" font-size="14.00">find_last_rotated_file()</text> +<text text-anchor="start" x="8" y="-113.3" font-family="Times,serif" font-size="14.00">shouldRollover(record)</text> </g> <!-- 1 --> <g id="node2" class="node"> <title>1</title> -<polygon fill="none" stroke="black" points="353.5,-128 353.5,-227 521.5,-227 521.5,-128 353.5,-128"/> -<text text-anchor="middle" x="437.5" y="-211.8" font-family="Times,serif" font-size="14.00">Ctl</text> -<polyline fill="none" stroke="black" points="353.5,-204 521.5,-204 "/> -<text text-anchor="start" x="361.5" y="-188.8" font-family="Times,serif" font-size="14.00">board_name : str</text> -<text text-anchor="start" x="361.5" y="-173.8" font-family="Times,serif" font-size="14.00">interfaces : dict</text> -<text text-anchor="start" x="361.5" y="-158.8" font-family="Times,serif" font-size="14.00">max_cpu_temp : float</text> -<polyline fill="none" stroke="black" points="353.5,-151 521.5,-151 "/> -<text text-anchor="middle" x="437.5" y="-135.8" font-family="Times,serif" font-size="14.00"> </text> +<polygon fill="none" stroke="black" points="353.5,-120.5 353.5,-219.5 521.5,-219.5 521.5,-120.5 353.5,-120.5"/> +<text text-anchor="middle" x="437.5" y="-204.3" font-family="Times,serif" font-size="14.00">Ctl</text> +<polyline fill="none" stroke="black" points="353.5,-196.5 521.5,-196.5 "/> +<text text-anchor="start" x="361.5" y="-181.3" font-family="Times,serif" font-size="14.00">interfaces : dict</text> +<text text-anchor="start" x="361.5" y="-166.3" font-family="Times,serif" font-size="14.00">max_cpu_temp : float</text> +<text text-anchor="start" x="361.5" y="-151.3" font-family="Times,serif" font-size="14.00">model : str</text> +<polyline fill="none" stroke="black" points="353.5,-143.5 521.5,-143.5 "/> +<text text-anchor="middle" x="437.5" y="-128.3" font-family="Times,serif" font-size="14.00"> </text> </g> <!-- 3 --> <g id="node4" class="node"> <title>3</title> -<polygon fill="none" stroke="black" points="363,-594 363,-753 642,-753 642,-594 363,-594"/> -<text text-anchor="middle" x="502.5" y="-737.8" font-family="Times,serif" font-size="14.00">CtlAbstract</text> -<polyline fill="none" stroke="black" points="363,-730 642,-730 "/> -<text text-anchor="start" x="371" y="-714.8" font-family="Times,serif" font-size="14.00">board_name</text> -<text text-anchor="start" x="371" y="-699.8" font-family="Times,serif" font-size="14.00">connection : NoneType</text> -<text text-anchor="start" x="371" y="-684.8" font-family="Times,serif" font-size="14.00">cpu_temperature</text> -<text text-anchor="start" x="371" y="-669.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="371" y="-654.8" font-family="Times,serif" font-size="14.00">interfaces : NoneType</text> -<text text-anchor="start" x="371" y="-639.8" font-family="Times,serif" font-size="14.00">max_cpu_temp</text> -<text text-anchor="start" x="371" y="-624.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text> -<polyline fill="none" stroke="black" points="363,-617 642,-617 "/> -<text text-anchor="middle" x="502.5" y="-601.8" font-family="Times,serif" font-size="14.00"> </text> +<polygon fill="none" stroke="black" points="363.5,-579 363.5,-738 641.5,-738 641.5,-579 363.5,-579"/> +<text text-anchor="middle" x="502.5" y="-722.8" font-family="Times,serif" font-size="14.00">CtlAbstract</text> +<polyline fill="none" stroke="black" points="363.5,-715 641.5,-715 "/> +<text text-anchor="start" x="371.5" y="-699.8" font-family="Times,serif" font-size="14.00">connection</text> +<text text-anchor="start" x="371.5" y="-684.8" font-family="Times,serif" font-size="14.00">cpu_temperature</text> +<text text-anchor="start" x="371.5" y="-669.8" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="371.5" y="-654.8" font-family="Times,serif" font-size="14.00">interfaces : NoneType</text> +<text text-anchor="start" x="371.5" y="-639.8" font-family="Times,serif" font-size="14.00">max_cpu_temp</text> +<text text-anchor="start" x="371.5" y="-624.8" font-family="Times,serif" font-size="14.00">model</text> +<text text-anchor="start" x="371.5" y="-609.8" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text> +<polyline fill="none" stroke="black" points="363.5,-602 641.5,-602 "/> +<text text-anchor="middle" x="502.5" y="-586.8" font-family="Times,serif" font-size="14.00"> </text> </g> <!-- 1->3 --> <g id="edge1" class="edge"> <title>1->3</title> -<path fill="none" stroke="black" d="M443.91,-227.22C454.81,-310.02 477.09,-479.36 490.81,-583.69"/> -<polygon fill="none" stroke="black" points="487.38,-584.43 492.16,-593.89 494.32,-583.52 487.38,-584.43"/> +<path fill="none" stroke="black" d="M444.03,-219.87C454.92,-301.38 476.94,-466.18 490.63,-568.66"/> +<polygon fill="none" stroke="black" points="487.18,-569.23 491.97,-578.68 494.11,-568.31 487.18,-569.23"/> </g> <!-- 2 --> <g id="node3" class="node"> <title>2</title> -<polygon fill="none" stroke="black" points="539.5,-142.5 539.5,-212.5 593.5,-212.5 593.5,-142.5 539.5,-142.5"/> -<text text-anchor="middle" x="566.5" y="-197.3" font-family="Times,serif" font-size="14.00">Ctl</text> -<polyline fill="none" stroke="black" points="539.5,-189.5 593.5,-189.5 "/> -<polyline fill="none" stroke="black" points="539.5,-165.5 593.5,-165.5 "/> -<text text-anchor="middle" x="566.5" y="-150.3" font-family="Times,serif" font-size="14.00"> </text> +<polygon fill="none" stroke="black" points="539.5,-135 539.5,-205 593.5,-205 593.5,-135 539.5,-135"/> +<text text-anchor="middle" x="566.5" y="-189.8" font-family="Times,serif" font-size="14.00">Ctl</text> +<polyline fill="none" stroke="black" points="539.5,-182 593.5,-182 "/> +<polyline fill="none" stroke="black" points="539.5,-158 593.5,-158 "/> +<text text-anchor="middle" x="566.5" y="-142.8" font-family="Times,serif" font-size="14.00"> </text> </g> <!-- 2->3 --> <g id="edge2" class="edge"> <title>2->3</title> -<path fill="none" stroke="black" d="M562.05,-212.84C552.22,-288.7 528.4,-472.57 514.04,-583.4"/> -<polygon fill="none" stroke="black" points="510.54,-583.2 512.73,-593.56 517.48,-584.1 510.54,-583.2"/> +<path fill="none" stroke="black" d="M562,-205.21C552.18,-279.88 528.55,-459.48 514.2,-568.56"/> +<polygon fill="none" stroke="black" points="510.72,-568.2 512.88,-578.57 517.66,-569.11 510.72,-568.2"/> </g> <!-- 4 --> <g id="node5" class="node"> <title>4</title> -<polygon fill="none" stroke="black" points="611.5,-60.5 611.5,-294.5 745.5,-294.5 745.5,-60.5 611.5,-60.5"/> -<text text-anchor="middle" x="678.5" y="-279.3" font-family="Times,serif" font-size="14.00">MQTTHandler</text> -<polyline fill="none" stroke="black" points="611.5,-271.5 745.5,-271.5 "/> -<text text-anchor="start" x="619.5" y="-256.3" font-family="Times,serif" font-size="14.00">auth : NoneType</text> -<text text-anchor="start" x="619.5" y="-241.3" font-family="Times,serif" font-size="14.00">client_id : str</text> -<text text-anchor="start" x="619.5" y="-226.3" font-family="Times,serif" font-size="14.00">hostname</text> -<text text-anchor="start" x="619.5" y="-211.3" font-family="Times,serif" font-size="14.00">keepalive : int</text> -<text text-anchor="start" x="619.5" y="-196.3" font-family="Times,serif" font-size="14.00">port : int</text> -<text text-anchor="start" x="619.5" y="-181.3" font-family="Times,serif" font-size="14.00">protocol</text> -<text text-anchor="start" x="619.5" y="-166.3" font-family="Times,serif" font-size="14.00">qos : int</text> -<text text-anchor="start" x="619.5" y="-151.3" font-family="Times,serif" font-size="14.00">retain : bool</text> -<text text-anchor="start" x="619.5" y="-136.3" font-family="Times,serif" font-size="14.00">tls : NoneType</text> -<text text-anchor="start" x="619.5" y="-121.3" font-family="Times,serif" font-size="14.00">topic</text> -<text text-anchor="start" x="619.5" y="-106.3" font-family="Times,serif" font-size="14.00">transport : str</text> -<text text-anchor="start" x="619.5" y="-91.3" font-family="Times,serif" font-size="14.00">will : NoneType</text> -<polyline fill="none" stroke="black" points="611.5,-83.5 745.5,-83.5 "/> -<text text-anchor="start" x="619.5" y="-68.3" font-family="Times,serif" font-size="14.00">emit(record)</text> +<polygon fill="none" stroke="black" points="611.5,-53 611.5,-287 745.5,-287 745.5,-53 611.5,-53"/> +<text text-anchor="middle" x="678.5" y="-271.8" font-family="Times,serif" font-size="14.00">MQTTHandler</text> +<polyline fill="none" stroke="black" points="611.5,-264 745.5,-264 "/> +<text text-anchor="start" x="619.5" y="-248.8" font-family="Times,serif" font-size="14.00">auth : NoneType</text> +<text text-anchor="start" x="619.5" y="-233.8" font-family="Times,serif" font-size="14.00">client_id : str</text> +<text text-anchor="start" x="619.5" y="-218.8" font-family="Times,serif" font-size="14.00">hostname</text> +<text text-anchor="start" x="619.5" y="-203.8" font-family="Times,serif" font-size="14.00">keepalive : int</text> +<text text-anchor="start" x="619.5" y="-188.8" font-family="Times,serif" font-size="14.00">port : int</text> +<text text-anchor="start" x="619.5" y="-173.8" font-family="Times,serif" font-size="14.00">protocol</text> +<text text-anchor="start" x="619.5" y="-158.8" font-family="Times,serif" font-size="14.00">qos : int</text> +<text text-anchor="start" x="619.5" y="-143.8" font-family="Times,serif" font-size="14.00">retain : bool</text> +<text text-anchor="start" x="619.5" y="-128.8" font-family="Times,serif" font-size="14.00">tls : NoneType</text> +<text text-anchor="start" x="619.5" y="-113.8" font-family="Times,serif" font-size="14.00">topic</text> +<text text-anchor="start" x="619.5" y="-98.8" font-family="Times,serif" font-size="14.00">transport : str</text> +<text text-anchor="start" x="619.5" y="-83.8" font-family="Times,serif" font-size="14.00">will : NoneType</text> +<polyline fill="none" stroke="black" points="611.5,-76 745.5,-76 "/> +<text text-anchor="start" x="619.5" y="-60.8" font-family="Times,serif" font-size="14.00">emit(record)</text> </g> <!-- 5 --> <g id="node6" class="node"> <title>5</title> -<polygon fill="none" stroke="black" points="763.5,-128 763.5,-227 979.5,-227 979.5,-128 763.5,-128"/> -<text text-anchor="middle" x="871.5" y="-211.8" font-family="Times,serif" font-size="14.00">Mux</text> -<polyline fill="none" stroke="black" points="763.5,-204 979.5,-204 "/> -<text text-anchor="start" x="771.5" y="-188.8" font-family="Times,serif" font-size="14.00">addresses : dict</text> -<text text-anchor="start" x="771.5" y="-173.8" font-family="Times,serif" font-size="14.00">io</text> +<polygon fill="none" stroke="black" points="763.5,-128 763.5,-212 979.5,-212 979.5,-128 763.5,-128"/> +<text text-anchor="middle" x="871.5" y="-196.8" font-family="Times,serif" font-size="14.00">Mux</text> +<polyline fill="none" stroke="black" points="763.5,-189 979.5,-189 "/> +<text text-anchor="start" x="771.5" y="-173.8" font-family="Times,serif" font-size="14.00">addresses : dict</text> <polyline fill="none" stroke="black" points="763.5,-166 979.5,-166 "/> <text text-anchor="start" x="771.5" y="-150.8" font-family="Times,serif" font-size="14.00">reset()</text> <text text-anchor="start" x="771.5" y="-135.8" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text> @@ -108,407 +107,376 @@ <!-- 8 --> <g id="node9" class="node"> <title>8</title> -<polygon fill="none" stroke="black" points="907,-564 907,-783 1194,-783 1194,-564 907,-564"/> -<text text-anchor="middle" x="1050.5" y="-767.8" font-family="Times,serif" font-size="14.00">MuxAbstract</text> -<polyline fill="none" stroke="black" points="907,-760 1194,-760 "/> -<text text-anchor="start" x="915" y="-744.8" font-family="Times,serif" font-size="14.00">addresses</text> -<text text-anchor="start" x="915" y="-729.8" font-family="Times,serif" font-size="14.00">barrier</text> -<text text-anchor="start" x="915" y="-714.8" font-family="Times,serif" font-size="14.00">board_id</text> -<text text-anchor="start" x="915" y="-699.8" font-family="Times,serif" font-size="14.00">board_name</text> +<polygon fill="none" stroke="black" points="907,-549 907,-768 1194,-768 1194,-549 907,-549"/> +<text text-anchor="middle" x="1050.5" y="-752.8" font-family="Times,serif" font-size="14.00">MuxAbstract</text> +<polyline fill="none" stroke="black" points="907,-745 1194,-745 "/> +<text text-anchor="start" x="915" y="-729.8" font-family="Times,serif" font-size="14.00">addresses</text> +<text text-anchor="start" x="915" y="-714.8" font-family="Times,serif" font-size="14.00">barrier</text> +<text text-anchor="start" x="915" y="-699.8" font-family="Times,serif" font-size="14.00">board_id</text> <text text-anchor="start" x="915" y="-684.8" font-family="Times,serif" font-size="14.00">cabling : dict</text> <text text-anchor="start" x="915" y="-669.8" font-family="Times,serif" font-size="14.00">connection</text> -<text text-anchor="start" x="915" y="-654.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="915" y="-639.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text> -<polyline fill="none" stroke="black" points="907,-632 1194,-632 "/> -<text text-anchor="start" x="915" y="-616.8" font-family="Times,serif" font-size="14.00">reset()</text> -<text text-anchor="start" x="915" y="-601.8" font-family="Times,serif" font-size="14.00">switch(elec_dict, state, bypass_check)</text> -<text text-anchor="start" x="915" y="-586.8" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text> -<text text-anchor="start" x="915" y="-571.8" font-family="Times,serif" font-size="14.00">test(elec_dict, activation_time)</text> +<text text-anchor="start" x="915" y="-654.8" font-family="Times,serif" font-size="14.00">exec_logger</text> +<text text-anchor="start" x="915" y="-639.8" font-family="Times,serif" font-size="14.00">model</text> +<text text-anchor="start" x="915" y="-624.8" font-family="Times,serif" font-size="14.00">soh_logger</text> +<polyline fill="none" stroke="black" points="907,-617 1194,-617 "/> +<text text-anchor="start" x="915" y="-601.8" font-family="Times,serif" font-size="14.00">reset()</text> +<text text-anchor="start" x="915" y="-586.8" font-family="Times,serif" font-size="14.00">switch(elec_dict, state, bypass_check)</text> +<text text-anchor="start" x="915" y="-571.8" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text> +<text text-anchor="start" x="915" y="-556.8" font-family="Times,serif" font-size="14.00">test(elec_dict, activation_time)</text> </g> <!-- 5->8 --> <g id="edge3" class="edge"> <title>5->8</title> -<path fill="none" stroke="black" d="M889.15,-227.22C916.48,-302.64 969.82,-449.83 1007.68,-554.32"/> -<polygon fill="none" stroke="black" points="1004.48,-555.77 1011.18,-563.98 1011.06,-553.38 1004.48,-555.77"/> +<path fill="none" stroke="black" d="M886.61,-212.06C912.91,-283.55 968.04,-433.38 1007.08,-539.49"/> +<polygon fill="none" stroke="black" points="1003.83,-540.8 1010.57,-548.98 1010.4,-538.39 1003.83,-540.8"/> </g> <!-- 6 --> <g id="node7" class="node"> <title>6</title> -<polygon fill="none" stroke="black" points="997.5,-127.5 997.5,-227.5 1103.5,-227.5 1103.5,-127.5 997.5,-127.5"/> -<text text-anchor="middle" x="1050.5" y="-212.3" font-family="Times,serif" font-size="14.00">Mux</text> -<polyline fill="none" stroke="black" points="997.5,-204.5 1103.5,-204.5 "/> -<polyline fill="none" stroke="black" points="997.5,-180.5 1103.5,-180.5 "/> -<text text-anchor="start" x="1005.5" y="-165.3" font-family="Times,serif" font-size="14.00">reset()</text> -<text text-anchor="start" x="1005.5" y="-150.3" font-family="Times,serif" font-size="14.00">switch_one()</text> -<text text-anchor="start" x="1005.5" y="-135.3" font-family="Times,serif" font-size="14.00">test()</text> +<polygon fill="none" stroke="black" points="997.5,-120 997.5,-220 1103.5,-220 1103.5,-120 997.5,-120"/> +<text text-anchor="middle" x="1050.5" y="-204.8" font-family="Times,serif" font-size="14.00">Mux</text> +<polyline fill="none" stroke="black" points="997.5,-197 1103.5,-197 "/> +<polyline fill="none" stroke="black" points="997.5,-173 1103.5,-173 "/> +<text text-anchor="start" x="1005.5" y="-157.8" font-family="Times,serif" font-size="14.00">reset()</text> +<text text-anchor="start" x="1005.5" y="-142.8" font-family="Times,serif" font-size="14.00">switch_one()</text> +<text text-anchor="start" x="1005.5" y="-127.8" font-family="Times,serif" font-size="14.00">test()</text> </g> <!-- 6->8 --> <g id="edge4" class="edge"> <title>6->8</title> -<path fill="none" stroke="black" d="M1050.5,-227.67C1050.5,-303.01 1050.5,-449.22 1050.5,-553.45"/> -<polygon fill="none" stroke="black" points="1047,-553.72 1050.5,-563.72 1054,-553.72 1047,-553.72"/> +<path fill="none" stroke="black" d="M1050.5,-220.33C1050.5,-294.43 1050.5,-436.69 1050.5,-538.82"/> +<polygon fill="none" stroke="black" points="1047,-538.89 1050.5,-548.89 1054,-538.89 1047,-538.89"/> </g> <!-- 7 --> <g id="node8" class="node"> <title>7</title> -<polygon fill="none" stroke="black" points="1121.5,-135.5 1121.5,-219.5 1337.5,-219.5 1337.5,-135.5 1121.5,-135.5"/> -<text text-anchor="middle" x="1229.5" y="-204.3" font-family="Times,serif" font-size="14.00">Mux</text> -<polyline fill="none" stroke="black" points="1121.5,-196.5 1337.5,-196.5 "/> -<text text-anchor="start" x="1129.5" y="-181.3" font-family="Times,serif" font-size="14.00">addresses : dict</text> -<polyline fill="none" stroke="black" points="1121.5,-173.5 1337.5,-173.5 "/> -<text text-anchor="start" x="1129.5" y="-158.3" font-family="Times,serif" font-size="14.00">reset()</text> -<text text-anchor="start" x="1129.5" y="-143.3" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text> +<polygon fill="none" stroke="black" points="1121.5,-128 1121.5,-212 1337.5,-212 1337.5,-128 1121.5,-128"/> +<text text-anchor="middle" x="1229.5" y="-196.8" font-family="Times,serif" font-size="14.00">Mux</text> +<polyline fill="none" stroke="black" points="1121.5,-189 1337.5,-189 "/> +<text text-anchor="start" x="1129.5" y="-173.8" font-family="Times,serif" font-size="14.00">addresses : dict</text> +<polyline fill="none" stroke="black" points="1121.5,-166 1337.5,-166 "/> +<text text-anchor="start" x="1129.5" y="-150.8" font-family="Times,serif" font-size="14.00">reset()</text> +<text text-anchor="start" x="1129.5" y="-135.8" font-family="Times,serif" font-size="14.00">switch_one(elec, role, state)</text> </g> <!-- 7->8 --> <g id="edge5" class="edge"> <title>7->8</title> -<path fill="none" stroke="black" d="M1214.55,-219.77C1188.18,-292.53 1132.52,-446.15 1093.37,-554.19"/> -<polygon fill="none" stroke="black" points="1089.99,-553.25 1089.87,-563.84 1096.57,-555.63 1089.99,-553.25"/> +<path fill="none" stroke="black" d="M1214.39,-212.06C1188.09,-283.55 1132.96,-433.38 1093.92,-539.49"/> +<polygon fill="none" stroke="black" points="1090.6,-538.39 1090.43,-548.98 1097.17,-540.8 1090.6,-538.39"/> </g> <!-- 9 --> <g id="node10" class="node"> <title>9</title> -<polygon fill="none" stroke="black" points="1356,-142.5 1356,-212.5 1447,-212.5 1447,-142.5 1356,-142.5"/> -<text text-anchor="middle" x="1401.5" y="-197.3" font-family="Times,serif" font-size="14.00">MyServer</text> -<polyline fill="none" stroke="black" points="1356,-189.5 1447,-189.5 "/> -<polyline fill="none" stroke="black" points="1356,-165.5 1447,-165.5 "/> -<text text-anchor="start" x="1364" y="-150.3" font-family="Times,serif" font-size="14.00">do_POST()</text> +<polygon fill="none" stroke="black" points="1356,-135 1356,-205 1447,-205 1447,-135 1356,-135"/> +<text text-anchor="middle" x="1401.5" y="-189.8" font-family="Times,serif" font-size="14.00">MyServer</text> +<polyline fill="none" stroke="black" points="1356,-182 1447,-182 "/> +<polyline fill="none" stroke="black" points="1356,-158 1447,-158 "/> +<text text-anchor="start" x="1364" y="-142.8" font-family="Times,serif" font-size="14.00">do_POST()</text> </g> <!-- 10 --> <g id="node11" class="node"> <title>10</title> -<polygon fill="none" stroke="black" points="1409,-406.5 1409,-940.5 2182,-940.5 2182,-406.5 1409,-406.5"/> -<text text-anchor="middle" x="1795.5" y="-925.3" font-family="Times,serif" font-size="14.00">OhmPi</text> -<polyline fill="none" stroke="black" points="1409,-917.5 2182,-917.5 "/> -<text text-anchor="start" x="1417" y="-902.3" font-family="Times,serif" font-size="14.00">cmd_id : NoneType</text> -<text text-anchor="start" x="1417" y="-887.3" font-family="Times,serif" font-size="14.00">controller : NoneType</text> -<text text-anchor="start" x="1417" y="-872.3" font-family="Times,serif" font-size="14.00">data_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="1417" y="-857.3" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="1417" y="-842.3" font-family="Times,serif" font-size="14.00">id : str</text> -<text text-anchor="start" x="1417" y="-827.3" font-family="Times,serif" font-size="14.00">mqtt : bool</text> -<text text-anchor="start" x="1417" y="-812.3" font-family="Times,serif" font-size="14.00">nb_samples : int</text> -<text text-anchor="start" x="1417" y="-797.3" font-family="Times,serif" font-size="14.00">on_pi : NoneType, bool</text> -<text text-anchor="start" x="1417" y="-782.3" font-family="Times,serif" font-size="14.00">sequence</text> -<text text-anchor="start" x="1417" y="-767.3" font-family="Times,serif" font-size="14.00">sequence : ndarray, NoneType</text> -<text text-anchor="start" x="1417" y="-752.3" font-family="Times,serif" font-size="14.00">settings : dict</text> -<text text-anchor="start" x="1417" y="-737.3" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="1417" y="-722.3" font-family="Times,serif" font-size="14.00">status : str</text> -<text text-anchor="start" x="1417" y="-707.3" font-family="Times,serif" font-size="14.00">thread : Thread, NoneType</text> -<polyline fill="none" stroke="black" points="1409,-699.5 2182,-699.5 "/> -<text text-anchor="start" x="1417" y="-684.3" font-family="Times,serif" font-size="14.00">append_and_save(filename, last_measurement, cmd_id)</text> -<text text-anchor="start" x="1417" y="-669.3" font-family="Times,serif" font-size="14.00">get_data(survey_names, cmd_id)</text> -<text text-anchor="start" x="1417" y="-654.3" font-family="Times,serif" font-size="14.00">get_deprecated_methods(cls)</text> -<text text-anchor="start" x="1417" y="-639.3" font-family="Times,serif" font-size="14.00">interrupt(cmd_id)</text> -<text text-anchor="start" x="1417" y="-624.3" font-family="Times,serif" font-size="14.00">load_sequence(filename, cmd_id)</text> -<text text-anchor="start" x="1417" y="-609.3" font-family="Times,serif" font-size="14.00">quit(cmd_id)</text> -<text text-anchor="start" x="1417" y="-594.3" font-family="Times,serif" font-size="14.00">remove_data(cmd_id)</text> -<text text-anchor="start" x="1417" y="-579.3" font-family="Times,serif" font-size="14.00">reset_mux(cmd_id)</text> -<text text-anchor="start" x="1417" y="-564.3" font-family="Times,serif" font-size="14.00">restart(cmd_id)</text> -<text text-anchor="start" x="1417" y="-549.3" font-family="Times,serif" font-size="14.00">rs_check(tx_volt, cmd_id)</text> -<text text-anchor="start" x="1417" y="-534.3" font-family="Times,serif" font-size="14.00">run_measurement(quad, nb_stack, injection_duration, autogain, strategy, tx_volt, best_tx_injtime, cmd_id)</text> -<text text-anchor="start" x="1417" y="-519.3" font-family="Times,serif" font-size="14.00">run_multiple_sequences(cmd_id, sequence_delay, nb_meas)</text> -<text text-anchor="start" x="1417" y="-504.3" font-family="Times,serif" font-size="14.00">run_sequence(cmd_id)</text> -<text text-anchor="start" x="1417" y="-489.3" font-family="Times,serif" font-size="14.00">run_sequence_async(cmd_id)</text> -<text text-anchor="start" x="1417" y="-474.3" font-family="Times,serif" font-size="14.00">set_sequence(sequence, cmd_id)</text> -<text text-anchor="start" x="1417" y="-459.3" font-family="Times,serif" font-size="14.00">switch_mux_off(quadrupole, cmd_id)</text> -<text text-anchor="start" x="1417" y="-444.3" font-family="Times,serif" font-size="14.00">switch_mux_on(quadrupole, bypass_check, cmd_id)</text> -<text text-anchor="start" x="1417" y="-429.3" font-family="Times,serif" font-size="14.00">test_mux(activation_time, mux_id, cmd_id)</text> -<text text-anchor="start" x="1417" y="-414.3" font-family="Times,serif" font-size="14.00">update_settings(settings, cmd_id)</text> +<polygon fill="none" stroke="black" points="1409,-391.5 1409,-925.5 2182,-925.5 2182,-391.5 1409,-391.5"/> +<text text-anchor="middle" x="1795.5" y="-910.3" font-family="Times,serif" font-size="14.00">OhmPi</text> +<polyline fill="none" stroke="black" points="1409,-902.5 2182,-902.5 "/> +<text text-anchor="start" x="1417" y="-887.3" font-family="Times,serif" font-size="14.00">cmd_id : NoneType</text> +<text text-anchor="start" x="1417" y="-872.3" font-family="Times,serif" font-size="14.00">controller : NoneType</text> +<text text-anchor="start" x="1417" y="-857.3" font-family="Times,serif" font-size="14.00">data_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="1417" y="-842.3" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="1417" y="-827.3" font-family="Times,serif" font-size="14.00">id : str</text> +<text text-anchor="start" x="1417" y="-812.3" font-family="Times,serif" font-size="14.00">mqtt : bool</text> +<text text-anchor="start" x="1417" y="-797.3" font-family="Times,serif" font-size="14.00">nb_samples : int</text> +<text text-anchor="start" x="1417" y="-782.3" font-family="Times,serif" font-size="14.00">on_pi : bool, NoneType</text> +<text text-anchor="start" x="1417" y="-767.3" font-family="Times,serif" font-size="14.00">sequence</text> +<text text-anchor="start" x="1417" y="-752.3" font-family="Times,serif" font-size="14.00">sequence : NoneType, ndarray</text> +<text text-anchor="start" x="1417" y="-737.3" font-family="Times,serif" font-size="14.00">settings : dict</text> +<text text-anchor="start" x="1417" y="-722.3" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="1417" y="-707.3" font-family="Times,serif" font-size="14.00">status : str</text> +<text text-anchor="start" x="1417" y="-692.3" font-family="Times,serif" font-size="14.00">thread : Thread, NoneType</text> +<polyline fill="none" stroke="black" points="1409,-684.5 2182,-684.5 "/> +<text text-anchor="start" x="1417" y="-669.3" font-family="Times,serif" font-size="14.00">append_and_save(filename, last_measurement, cmd_id)</text> +<text text-anchor="start" x="1417" y="-654.3" font-family="Times,serif" font-size="14.00">get_data(survey_names, cmd_id)</text> +<text text-anchor="start" x="1417" y="-639.3" font-family="Times,serif" font-size="14.00">get_deprecated_methods(cls)</text> +<text text-anchor="start" x="1417" y="-624.3" font-family="Times,serif" font-size="14.00">interrupt(cmd_id)</text> +<text text-anchor="start" x="1417" y="-609.3" font-family="Times,serif" font-size="14.00">load_sequence(filename, cmd_id)</text> +<text text-anchor="start" x="1417" y="-594.3" font-family="Times,serif" font-size="14.00">quit(cmd_id)</text> +<text text-anchor="start" x="1417" y="-579.3" font-family="Times,serif" font-size="14.00">remove_data(cmd_id)</text> +<text text-anchor="start" x="1417" y="-564.3" font-family="Times,serif" font-size="14.00">reset_mux(cmd_id)</text> +<text text-anchor="start" x="1417" y="-549.3" font-family="Times,serif" font-size="14.00">restart(cmd_id)</text> +<text text-anchor="start" x="1417" y="-534.3" font-family="Times,serif" font-size="14.00">rs_check(tx_volt, cmd_id)</text> +<text text-anchor="start" x="1417" y="-519.3" font-family="Times,serif" font-size="14.00">run_measurement(quad, nb_stack, injection_duration, autogain, strategy, tx_volt, best_tx_injtime, cmd_id)</text> +<text text-anchor="start" x="1417" y="-504.3" font-family="Times,serif" font-size="14.00">run_multiple_sequences(cmd_id, sequence_delay, nb_meas)</text> +<text text-anchor="start" x="1417" y="-489.3" font-family="Times,serif" font-size="14.00">run_sequence(cmd_id)</text> +<text text-anchor="start" x="1417" y="-474.3" font-family="Times,serif" font-size="14.00">run_sequence_async(cmd_id)</text> +<text text-anchor="start" x="1417" y="-459.3" font-family="Times,serif" font-size="14.00">set_sequence(sequence, cmd_id)</text> +<text text-anchor="start" x="1417" y="-444.3" font-family="Times,serif" font-size="14.00">switch_mux_off(quadrupole, cmd_id)</text> +<text text-anchor="start" x="1417" y="-429.3" font-family="Times,serif" font-size="14.00">switch_mux_on(quadrupole, bypass_check, cmd_id)</text> +<text text-anchor="start" x="1417" y="-414.3" font-family="Times,serif" font-size="14.00">test_mux(activation_time, mux_id, cmd_id)</text> +<text text-anchor="start" x="1417" y="-399.3" font-family="Times,serif" font-size="14.00">update_settings(settings, cmd_id)</text> </g> <!-- 11 --> <g id="node12" class="node"> <title>11</title> -<polygon fill="none" stroke="black" points="1465.5,-8 1465.5,-347 2125.5,-347 2125.5,-8 1465.5,-8"/> -<text text-anchor="middle" x="1795.5" y="-331.8" font-family="Times,serif" font-size="14.00">OhmPiHardware</text> -<polyline fill="none" stroke="black" points="1465.5,-324 2125.5,-324 "/> -<text text-anchor="start" x="1473.5" y="-308.8" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="1473.5" y="-293.8" font-family="Times,serif" font-size="14.00">data_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="1473.5" y="-278.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="1473.5" y="-263.8" font-family="Times,serif" font-size="14.00">last_dev</text> -<text text-anchor="start" x="1473.5" y="-248.8" font-family="Times,serif" font-size="14.00">last_rho</text> -<text text-anchor="start" x="1473.5" y="-233.8" font-family="Times,serif" font-size="14.00">mux_barrier : Barrier</text> -<text text-anchor="start" x="1473.5" y="-218.8" font-family="Times,serif" font-size="14.00">mux_boards : dict</text> -<text text-anchor="start" x="1473.5" y="-203.8" font-family="Times,serif" font-size="14.00">pulses</text> -<text text-anchor="start" x="1473.5" y="-188.8" font-family="Times,serif" font-size="14.00">pwr</text> -<text text-anchor="start" x="1473.5" y="-173.8" font-family="Times,serif" font-size="14.00">readings : ndarray</text> -<text text-anchor="start" x="1473.5" y="-158.8" font-family="Times,serif" font-size="14.00">rx</text> -<text text-anchor="start" x="1473.5" y="-143.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="1473.5" y="-128.8" font-family="Times,serif" font-size="14.00">sp</text> -<text text-anchor="start" x="1473.5" y="-113.8" font-family="Times,serif" font-size="14.00">tx</text> -<text text-anchor="start" x="1473.5" y="-98.8" font-family="Times,serif" font-size="14.00">tx_sync : Event</text> -<polyline fill="none" stroke="black" points="1465.5,-91 2125.5,-91 "/> -<text text-anchor="start" x="1473.5" y="-75.8" font-family="Times,serif" font-size="14.00">calibrate_rx_bias()</text> -<text text-anchor="start" x="1473.5" y="-60.8" font-family="Times,serif" font-size="14.00">reset_mux()</text> -<text text-anchor="start" x="1473.5" y="-45.8" font-family="Times,serif" font-size="14.00">switch_mux(electrodes, roles, state)</text> -<text text-anchor="start" x="1473.5" y="-30.8" font-family="Times,serif" font-size="14.00">test_mux(channel, activation_time)</text> -<text text-anchor="start" x="1473.5" y="-15.8" font-family="Times,serif" font-size="14.00">vab_square_wave(vab, cycle_duration, sampling_rate, cycles, polarity, duty_cycle, append)</text> +<polygon fill="none" stroke="black" points="1465.5,-0.5 1465.5,-339.5 2125.5,-339.5 2125.5,-0.5 1465.5,-0.5"/> +<text text-anchor="middle" x="1795.5" y="-324.3" font-family="Times,serif" font-size="14.00">OhmPiHardware</text> +<polyline fill="none" stroke="black" points="1465.5,-316.5 2125.5,-316.5 "/> +<text text-anchor="start" x="1473.5" y="-301.3" font-family="Times,serif" font-size="14.00">ctl</text> +<text text-anchor="start" x="1473.5" y="-286.3" font-family="Times,serif" font-size="14.00">data_logger</text> +<text text-anchor="start" x="1473.5" y="-271.3" font-family="Times,serif" font-size="14.00">exec_logger</text> +<text text-anchor="start" x="1473.5" y="-256.3" font-family="Times,serif" font-size="14.00">mux_barrier : Barrier</text> +<text text-anchor="start" x="1473.5" y="-241.3" font-family="Times,serif" font-size="14.00">mux_boards : dict</text> +<text text-anchor="start" x="1473.5" y="-226.3" font-family="Times,serif" font-size="14.00">pulses</text> +<text text-anchor="start" x="1473.5" y="-211.3" font-family="Times,serif" font-size="14.00">pwr</text> +<text text-anchor="start" x="1473.5" y="-196.3" font-family="Times,serif" font-size="14.00">readings : ndarray</text> +<text text-anchor="start" x="1473.5" y="-181.3" font-family="Times,serif" font-size="14.00">rx</text> +<text text-anchor="start" x="1473.5" y="-166.3" font-family="Times,serif" font-size="14.00">soh_logger</text> +<text text-anchor="start" x="1473.5" y="-151.3" font-family="Times,serif" font-size="14.00">sp</text> +<text text-anchor="start" x="1473.5" y="-136.3" font-family="Times,serif" font-size="14.00">tx</text> +<text text-anchor="start" x="1473.5" y="-121.3" font-family="Times,serif" font-size="14.00">tx_sync : Event</text> +<polyline fill="none" stroke="black" points="1465.5,-113.5 2125.5,-113.5 "/> +<text text-anchor="start" x="1473.5" y="-98.3" font-family="Times,serif" font-size="14.00">calibrate_rx_bias()</text> +<text text-anchor="start" x="1473.5" y="-83.3" font-family="Times,serif" font-size="14.00">last_dev(delay)</text> +<text text-anchor="start" x="1473.5" y="-68.3" font-family="Times,serif" font-size="14.00">last_resistance(delay)</text> +<text text-anchor="start" x="1473.5" y="-53.3" font-family="Times,serif" font-size="14.00">reset_mux()</text> +<text text-anchor="start" x="1473.5" y="-38.3" font-family="Times,serif" font-size="14.00">switch_mux(electrodes, roles, state)</text> +<text text-anchor="start" x="1473.5" y="-23.3" font-family="Times,serif" font-size="14.00">test_mux(channel, activation_time)</text> +<text text-anchor="start" x="1473.5" y="-8.3" font-family="Times,serif" font-size="14.00">vab_square_wave(vab, cycle_duration, sampling_rate, cycles, polarity, duty_cycle, append)</text> </g> <!-- 11->10 --> <g id="edge14" class="edge"> <title>11->10</title> -<path fill="none" stroke="black" d="M1795.5,-347.13C1795.5,-362.38 1795.5,-378.08 1795.5,-393.96"/> -<polygon fill="black" stroke="black" points="1795.5,-394.14 1799.5,-400.14 1795.5,-406.14 1791.5,-400.14 1795.5,-394.14"/> -<text text-anchor="middle" x="1809.5" y="-376.8" font-family="Times,serif" font-size="14.00" fill="green">_hw</text> +<path fill="none" stroke="black" d="M1795.5,-339.79C1795.5,-352.61 1795.5,-365.73 1795.5,-378.99"/> +<polygon fill="black" stroke="black" points="1795.5,-379.21 1799.5,-385.21 1795.5,-391.21 1791.5,-385.21 1795.5,-379.21"/> +<text text-anchor="middle" x="1809.5" y="-361.8" font-family="Times,serif" font-size="14.00" fill="green">_hw</text> </g> <!-- 12 --> <g id="node13" class="node"> <title>12</title> -<polygon fill="none" stroke="black" points="2143.5,-90.5 2143.5,-264.5 2337.5,-264.5 2337.5,-90.5 2143.5,-90.5"/> -<text text-anchor="middle" x="2240.5" y="-249.3" font-family="Times,serif" font-size="14.00">Pwr</text> -<polyline fill="none" stroke="black" points="2143.5,-241.5 2337.5,-241.5 "/> -<text text-anchor="start" x="2151.5" y="-226.3" font-family="Times,serif" font-size="14.00">connection</text> -<text text-anchor="start" x="2151.5" y="-211.3" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="2151.5" y="-196.3" font-family="Times,serif" font-size="14.00">current</text> -<text text-anchor="start" x="2151.5" y="-181.3" font-family="Times,serif" font-size="14.00">current_max</text> -<text text-anchor="start" x="2151.5" y="-166.3" font-family="Times,serif" font-size="14.00">voltage</text> -<text text-anchor="start" x="2151.5" y="-151.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text> -<polyline fill="none" stroke="black" points="2143.5,-143.5 2337.5,-143.5 "/> -<text text-anchor="start" x="2151.5" y="-128.3" font-family="Times,serif" font-size="14.00">battery_voltage()</text> -<text text-anchor="start" x="2151.5" y="-113.3" font-family="Times,serif" font-size="14.00">turn_off()</text> -<text text-anchor="start" x="2151.5" y="-98.3" font-family="Times,serif" font-size="14.00">turn_on()</text> +<polygon fill="none" stroke="black" points="2193.5,-90.5 2193.5,-249.5 2387.5,-249.5 2387.5,-90.5 2193.5,-90.5"/> +<text text-anchor="middle" x="2290.5" y="-234.3" font-family="Times,serif" font-size="14.00">Pwr</text> +<polyline fill="none" stroke="black" points="2193.5,-226.5 2387.5,-226.5 "/> +<text text-anchor="start" x="2201.5" y="-211.3" font-family="Times,serif" font-size="14.00">current</text> +<text text-anchor="start" x="2201.5" y="-196.3" font-family="Times,serif" font-size="14.00">current_adjustable : bool</text> +<text text-anchor="start" x="2201.5" y="-181.3" font-family="Times,serif" font-size="14.00">voltage</text> +<text text-anchor="start" x="2201.5" y="-166.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text> +<polyline fill="none" stroke="black" points="2193.5,-158.5 2387.5,-158.5 "/> +<text text-anchor="start" x="2201.5" y="-143.3" font-family="Times,serif" font-size="14.00">battery_voltage()</text> +<text text-anchor="start" x="2201.5" y="-128.3" font-family="Times,serif" font-size="14.00">current_max(value)</text> +<text text-anchor="start" x="2201.5" y="-113.3" font-family="Times,serif" font-size="14.00">turn_off()</text> +<text text-anchor="start" x="2201.5" y="-98.3" font-family="Times,serif" font-size="14.00">turn_on()</text> </g> <!-- 14 --> <g id="node15" class="node"> <title>14</title> -<polygon fill="none" stroke="black" points="2257,-579 2257,-768 2536,-768 2536,-579 2257,-579"/> -<text text-anchor="middle" x="2396.5" y="-752.8" font-family="Times,serif" font-size="14.00">PwrAbstract</text> -<polyline fill="none" stroke="black" points="2257,-745 2536,-745 "/> -<text text-anchor="start" x="2265" y="-729.8" font-family="Times,serif" font-size="14.00">board_name</text> -<text text-anchor="start" x="2265" y="-714.8" font-family="Times,serif" font-size="14.00">connection</text> -<text text-anchor="start" x="2265" y="-699.8" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="2265" y="-684.8" font-family="Times,serif" font-size="14.00">current</text> -<text text-anchor="start" x="2265" y="-669.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="2265" y="-654.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="2265" y="-639.8" font-family="Times,serif" font-size="14.00">voltage</text> -<text text-anchor="start" x="2265" y="-624.8" font-family="Times,serif" font-size="14.00">voltage_adjustable</text> -<polyline fill="none" stroke="black" points="2257,-617 2536,-617 "/> -<text text-anchor="start" x="2265" y="-601.8" font-family="Times,serif" font-size="14.00">turn_off()</text> -<text text-anchor="start" x="2265" y="-586.8" font-family="Times,serif" font-size="14.00">turn_on()</text> +<polygon fill="none" stroke="black" points="2200.5,-564 2200.5,-753 2478.5,-753 2478.5,-564 2200.5,-564"/> +<text text-anchor="middle" x="2339.5" y="-737.8" font-family="Times,serif" font-size="14.00">PwrAbstract</text> +<polyline fill="none" stroke="black" points="2200.5,-730 2478.5,-730 "/> +<text text-anchor="start" x="2208.5" y="-714.8" font-family="Times,serif" font-size="14.00">connection</text> +<text text-anchor="start" x="2208.5" y="-699.8" font-family="Times,serif" font-size="14.00">current</text> +<text text-anchor="start" x="2208.5" y="-684.8" font-family="Times,serif" font-size="14.00">current_adjustable</text> +<text text-anchor="start" x="2208.5" y="-669.8" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="2208.5" y="-654.8" font-family="Times,serif" font-size="14.00">model</text> +<text text-anchor="start" x="2208.5" y="-639.8" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="2208.5" y="-624.8" font-family="Times,serif" font-size="14.00">voltage</text> +<text text-anchor="start" x="2208.5" y="-609.8" font-family="Times,serif" font-size="14.00">voltage_adjustable</text> +<polyline fill="none" stroke="black" points="2200.5,-602 2478.5,-602 "/> +<text text-anchor="start" x="2208.5" y="-586.8" font-family="Times,serif" font-size="14.00">turn_off()</text> +<text text-anchor="start" x="2208.5" y="-571.8" font-family="Times,serif" font-size="14.00">turn_on()</text> </g> <!-- 12->14 --> <g id="edge6" class="edge"> <title>12->14</title> -<path fill="none" stroke="black" d="M2267.67,-264.55C2294.41,-349.23 2335.27,-478.62 2363.78,-568.9"/> -<polygon fill="none" stroke="black" points="2360.5,-570.13 2366.85,-578.61 2367.18,-568.02 2360.5,-570.13"/> +<path fill="none" stroke="black" d="M2298.43,-249.71C2306.76,-332.39 2319.89,-462.83 2329.06,-553.85"/> +<polygon fill="none" stroke="black" points="2325.59,-554.32 2330.07,-563.92 2332.55,-553.62 2325.59,-554.32"/> </g> <!-- 13 --> <g id="node14" class="node"> <title>13</title> -<polygon fill="none" stroke="black" points="2355.5,-120.5 2355.5,-234.5 2549.5,-234.5 2549.5,-120.5 2355.5,-120.5"/> -<text text-anchor="middle" x="2452.5" y="-219.3" font-family="Times,serif" font-size="14.00">Pwr</text> -<polyline fill="none" stroke="black" points="2355.5,-211.5 2549.5,-211.5 "/> -<text text-anchor="start" x="2363.5" y="-196.3" font-family="Times,serif" font-size="14.00">current</text> -<text text-anchor="start" x="2363.5" y="-181.3" font-family="Times,serif" font-size="14.00">voltage</text> -<text text-anchor="start" x="2363.5" y="-166.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text> -<polyline fill="none" stroke="black" points="2355.5,-158.5 2549.5,-158.5 "/> -<text text-anchor="start" x="2363.5" y="-143.3" font-family="Times,serif" font-size="14.00">turn_off()</text> -<text text-anchor="start" x="2363.5" y="-128.3" font-family="Times,serif" font-size="14.00">turn_on()</text> +<polygon fill="none" stroke="black" points="2406,-120.5 2406,-219.5 2489,-219.5 2489,-120.5 2406,-120.5"/> +<text text-anchor="middle" x="2447.5" y="-204.3" font-family="Times,serif" font-size="14.00">Pwr</text> +<polyline fill="none" stroke="black" points="2406,-196.5 2489,-196.5 "/> +<text text-anchor="start" x="2414" y="-181.3" font-family="Times,serif" font-size="14.00">current</text> +<text text-anchor="start" x="2414" y="-166.3" font-family="Times,serif" font-size="14.00">voltage</text> +<polyline fill="none" stroke="black" points="2406,-158.5 2489,-158.5 "/> +<text text-anchor="start" x="2414" y="-143.3" font-family="Times,serif" font-size="14.00">turn_off()</text> +<text text-anchor="start" x="2414" y="-128.3" font-family="Times,serif" font-size="14.00">turn_on()</text> </g> <!-- 13->14 --> <g id="edge7" class="edge"> <title>13->14</title> -<path fill="none" stroke="black" d="M2446.13,-234.69C2436.93,-315.82 2419.78,-467.17 2408.25,-568.89"/> -<polygon fill="none" stroke="black" points="2404.76,-568.55 2407.11,-578.88 2411.72,-569.34 2404.76,-568.55"/> +<path fill="none" stroke="black" d="M2436.65,-219.87C2419.39,-297.61 2385.32,-451.12 2362.46,-554.05"/> +<polygon fill="none" stroke="black" points="2359.04,-553.33 2360.29,-563.85 2365.87,-554.85 2359.04,-553.33"/> </g> <!-- 15 --> <g id="node16" class="node"> <title>15</title> -<polygon fill="none" stroke="black" points="2567.5,-113 2567.5,-242 2695.5,-242 2695.5,-113 2567.5,-113"/> -<text text-anchor="middle" x="2631.5" y="-226.8" font-family="Times,serif" font-size="14.00">Rx</text> -<polyline fill="none" stroke="black" points="2567.5,-219 2695.5,-219 "/> -<text text-anchor="start" x="2575.5" y="-203.8" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="2575.5" y="-188.8" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="2575.5" y="-173.8" font-family="Times,serif" font-size="14.00">connection</text> -<text text-anchor="start" x="2575.5" y="-158.8" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="2575.5" y="-143.8" font-family="Times,serif" font-size="14.00">voltage</text> -<polyline fill="none" stroke="black" points="2567.5,-136 2695.5,-136 "/> -<text text-anchor="start" x="2575.5" y="-120.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> +<polygon fill="none" stroke="black" points="2642.5,-609 2642.5,-708 2764.5,-708 2764.5,-609 2642.5,-609"/> +<text text-anchor="middle" x="2703.5" y="-692.8" font-family="Times,serif" font-size="14.00">Rx</text> +<polyline fill="none" stroke="black" points="2642.5,-685 2764.5,-685 "/> +<text text-anchor="start" x="2650.5" y="-669.8" font-family="Times,serif" font-size="14.00">gain</text> +<text text-anchor="start" x="2650.5" y="-654.8" font-family="Times,serif" font-size="14.00">gain : int, float</text> +<text text-anchor="start" x="2650.5" y="-639.8" font-family="Times,serif" font-size="14.00">voltage</text> +<polyline fill="none" stroke="black" points="2642.5,-632 2764.5,-632 "/> +<text text-anchor="start" x="2650.5" y="-616.8" font-family="Times,serif" font-size="14.00">gain_auto()</text> </g> <!-- 18 --> <g id="node19" class="node"> <title>18</title> -<polygon fill="none" stroke="black" points="2638,-586.5 2638,-760.5 2917,-760.5 2917,-586.5 2638,-586.5"/> -<text text-anchor="middle" x="2777.5" y="-745.3" font-family="Times,serif" font-size="14.00">RxAbstract</text> -<polyline fill="none" stroke="black" points="2638,-737.5 2917,-737.5 "/> -<text text-anchor="start" x="2646" y="-722.3" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="2646" y="-707.3" font-family="Times,serif" font-size="14.00">board_name</text> -<text text-anchor="start" x="2646" y="-692.3" font-family="Times,serif" font-size="14.00">connection</text> -<text text-anchor="start" x="2646" y="-677.3" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="2646" y="-662.3" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="2646" y="-647.3" font-family="Times,serif" font-size="14.00">sampling_rate</text> -<text text-anchor="start" x="2646" y="-632.3" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="2646" y="-617.3" font-family="Times,serif" font-size="14.00">voltage</text> -<polyline fill="none" stroke="black" points="2638,-609.5 2917,-609.5 "/> -<text text-anchor="start" x="2646" y="-594.3" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> +<polygon fill="none" stroke="black" points="2469.5,-1008.5 2469.5,-1167.5 2747.5,-1167.5 2747.5,-1008.5 2469.5,-1008.5"/> +<text text-anchor="middle" x="2608.5" y="-1152.3" font-family="Times,serif" font-size="14.00">RxAbstract</text> +<polyline fill="none" stroke="black" points="2469.5,-1144.5 2747.5,-1144.5 "/> +<text text-anchor="start" x="2477.5" y="-1129.3" font-family="Times,serif" font-size="14.00">adc_gain</text> +<text text-anchor="start" x="2477.5" y="-1114.3" font-family="Times,serif" font-size="14.00">connection</text> +<text text-anchor="start" x="2477.5" y="-1099.3" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="2477.5" y="-1084.3" font-family="Times,serif" font-size="14.00">model</text> +<text text-anchor="start" x="2477.5" y="-1069.3" font-family="Times,serif" font-size="14.00">sampling_rate</text> +<text text-anchor="start" x="2477.5" y="-1054.3" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="2477.5" y="-1039.3" font-family="Times,serif" font-size="14.00">voltage</text> +<polyline fill="none" stroke="black" points="2469.5,-1031.5 2747.5,-1031.5 "/> +<text text-anchor="middle" x="2608.5" y="-1016.3" font-family="Times,serif" font-size="14.00"> </text> </g> <!-- 15->18 --> <g id="edge8" class="edge"> <title>15->18</title> -<path fill="none" stroke="black" d="M2650.29,-242.07C2675.37,-326.93 2720.01,-477.97 2749.18,-576.68"/> -<polygon fill="none" stroke="black" points="2745.85,-577.76 2752.04,-586.36 2752.56,-575.78 2745.85,-577.76"/> +<path fill="none" stroke="black" d="M2692.69,-708.12C2677.03,-778.61 2647.76,-910.35 2628.19,-998.38"/> +<polygon fill="none" stroke="black" points="2624.73,-997.84 2625.98,-1008.36 2631.56,-999.36 2624.73,-997.84"/> </g> <!-- 16 --> <g id="node17" class="node"> <title>16</title> -<polygon fill="none" stroke="black" points="2713.5,-128 2713.5,-227 2841.5,-227 2841.5,-128 2713.5,-128"/> -<text text-anchor="middle" x="2777.5" y="-211.8" font-family="Times,serif" font-size="14.00">Rx</text> -<polyline fill="none" stroke="black" points="2713.5,-204 2841.5,-204 "/> -<text text-anchor="start" x="2721.5" y="-188.8" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="2721.5" y="-173.8" font-family="Times,serif" font-size="14.00">adc_gain : float</text> -<text text-anchor="start" x="2721.5" y="-158.8" font-family="Times,serif" font-size="14.00">voltage</text> -<polyline fill="none" stroke="black" points="2713.5,-151 2841.5,-151 "/> -<text text-anchor="start" x="2721.5" y="-135.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> +<polygon fill="none" stroke="black" points="2496.5,-609 2496.5,-708 2624.5,-708 2624.5,-609 2496.5,-609"/> +<text text-anchor="middle" x="2560.5" y="-692.8" font-family="Times,serif" font-size="14.00">Rx</text> +<polyline fill="none" stroke="black" points="2496.5,-685 2624.5,-685 "/> +<text text-anchor="start" x="2504.5" y="-669.8" font-family="Times,serif" font-size="14.00">adc_gain</text> +<text text-anchor="start" x="2504.5" y="-654.8" font-family="Times,serif" font-size="14.00">adc_gain : float</text> +<text text-anchor="start" x="2504.5" y="-639.8" font-family="Times,serif" font-size="14.00">voltage</text> +<polyline fill="none" stroke="black" points="2496.5,-632 2624.5,-632 "/> +<text text-anchor="start" x="2504.5" y="-616.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> </g> <!-- 16->18 --> <g id="edge9" class="edge"> <title>16->18</title> -<path fill="none" stroke="black" d="M2777.5,-227.22C2777.5,-308.02 2777.5,-471.22 2777.5,-576.01"/> -<polygon fill="none" stroke="black" points="2774,-576.27 2777.5,-586.27 2781,-576.27 2774,-576.27"/> +<path fill="none" stroke="black" d="M2565.96,-708.12C2573.87,-778.61 2588.67,-910.35 2598.55,-998.38"/> +<polygon fill="none" stroke="black" points="2595.08,-998.81 2599.67,-1008.36 2602.03,-998.03 2595.08,-998.81"/> </g> <!-- 17 --> <g id="node18" class="node"> <title>17</title> -<polygon fill="none" stroke="black" points="2859.5,-60.5 2859.5,-294.5 3047.5,-294.5 3047.5,-60.5 2859.5,-60.5"/> -<text text-anchor="middle" x="2953.5" y="-279.3" font-family="Times,serif" font-size="14.00">Rx</text> -<polyline fill="none" stroke="black" points="2859.5,-271.5 3047.5,-271.5 "/> -<text text-anchor="start" x="2867.5" y="-256.3" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="2867.5" y="-241.3" font-family="Times,serif" font-size="14.00">adc_gain : float</text> -<text text-anchor="start" x="2867.5" y="-226.3" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="2867.5" y="-211.3" font-family="Times,serif" font-size="14.00">io</text> -<text text-anchor="start" x="2867.5" y="-196.3" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text> -<text text-anchor="start" x="2867.5" y="-181.3" font-family="Times,serif" font-size="14.00">pin_DG0 : DigitalInOut</text> -<text text-anchor="start" x="2867.5" y="-166.3" font-family="Times,serif" font-size="14.00">pin_DG1 : DigitalInOut</text> -<text text-anchor="start" x="2867.5" y="-151.3" font-family="Times,serif" font-size="14.00">pin_DG2 : DigitalInOut</text> -<text text-anchor="start" x="2867.5" y="-136.3" font-family="Times,serif" font-size="14.00">voltage</text> -<text text-anchor="start" x="2867.5" y="-121.3" font-family="Times,serif" font-size="14.00">voltage_gain</text> -<text text-anchor="start" x="2867.5" y="-106.3" font-family="Times,serif" font-size="14.00">voltage_gain : int, float</text> -<polyline fill="none" stroke="black" points="2859.5,-98.5 3047.5,-98.5 "/> -<text text-anchor="start" x="2867.5" y="-83.3" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> -<text text-anchor="start" x="2867.5" y="-68.3" font-family="Times,serif" font-size="14.00">voltage_gain_auto()</text> -</g> -<!-- 17->18 --> +<polygon fill="none" stroke="black" points="2609.5,-90.5 2609.5,-249.5 2797.5,-249.5 2797.5,-90.5 2609.5,-90.5"/> +<text text-anchor="middle" x="2703.5" y="-234.3" font-family="Times,serif" font-size="14.00">Rx</text> +<polyline fill="none" stroke="black" points="2609.5,-226.5 2797.5,-226.5 "/> +<text text-anchor="start" x="2617.5" y="-211.3" font-family="Times,serif" font-size="14.00">gain</text> +<text text-anchor="start" x="2617.5" y="-196.3" font-family="Times,serif" font-size="14.00">gain : int, float</text> +<text text-anchor="start" x="2617.5" y="-181.3" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text> +<text text-anchor="start" x="2617.5" y="-166.3" font-family="Times,serif" font-size="14.00">pin_DG0 : DigitalInOut</text> +<text text-anchor="start" x="2617.5" y="-151.3" font-family="Times,serif" font-size="14.00">pin_DG1 : DigitalInOut</text> +<text text-anchor="start" x="2617.5" y="-136.3" font-family="Times,serif" font-size="14.00">pin_DG2 : DigitalInOut</text> +<text text-anchor="start" x="2617.5" y="-121.3" font-family="Times,serif" font-size="14.00">voltage</text> +<polyline fill="none" stroke="black" points="2609.5,-113.5 2797.5,-113.5 "/> +<text text-anchor="start" x="2617.5" y="-98.3" font-family="Times,serif" font-size="14.00">gain_auto()</text> +</g> +<!-- 17->15 --> <g id="edge10" class="edge"> -<title>17->18</title> -<path fill="none" stroke="black" d="M2912.12,-294.63C2881.51,-380.56 2840.32,-496.18 2811.59,-576.81"/> -<polygon fill="none" stroke="black" points="2808.21,-575.88 2808.15,-586.47 2814.8,-578.22 2808.21,-575.88"/> +<title>17->15</title> +<path fill="none" stroke="black" d="M2703.5,-249.71C2703.5,-347.26 2703.5,-511.28 2703.5,-598.62"/> +<polygon fill="none" stroke="black" points="2700,-598.88 2703.5,-608.88 2707,-598.88 2700,-598.88"/> </g> <!-- 19 --> <g id="node20" class="node"> <title>19</title> -<polygon fill="none" stroke="black" points="3065.5,-0.5 3065.5,-354.5 3361.5,-354.5 3361.5,-0.5 3065.5,-0.5"/> -<text text-anchor="middle" x="3213.5" y="-339.3" font-family="Times,serif" font-size="14.00">Tx</text> -<polyline fill="none" stroke="black" points="3065.5,-331.5 3361.5,-331.5 "/> -<text text-anchor="start" x="3073.5" y="-316.3" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="3073.5" y="-301.3" font-family="Times,serif" font-size="14.00">adc_gain : int, float</text> -<text text-anchor="start" x="3073.5" y="-286.3" font-family="Times,serif" font-size="14.00">connection</text> -<text text-anchor="start" x="3073.5" y="-271.3" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="3073.5" y="-256.3" font-family="Times,serif" font-size="14.00">current</text> -<text text-anchor="start" x="3073.5" y="-241.3" font-family="Times,serif" font-size="14.00">current_adjustable : bool</text> -<text text-anchor="start" x="3073.5" y="-226.3" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text> -<text text-anchor="start" x="3073.5" y="-211.3" font-family="Times,serif" font-size="14.00">pin0 : DigitalInOut</text> -<text text-anchor="start" x="3073.5" y="-196.3" font-family="Times,serif" font-size="14.00">pin1 : DigitalInOut</text> -<text text-anchor="start" x="3073.5" y="-181.3" font-family="Times,serif" font-size="14.00">pin4 : DigitalInOut</text> -<text text-anchor="start" x="3073.5" y="-166.3" font-family="Times,serif" font-size="14.00">polarity</text> -<text text-anchor="start" x="3073.5" y="-151.3" font-family="Times,serif" font-size="14.00">polarity : int</text> -<text text-anchor="start" x="3073.5" y="-136.3" font-family="Times,serif" font-size="14.00">pwr : NoneType</text> -<text text-anchor="start" x="3073.5" y="-121.3" font-family="Times,serif" font-size="14.00">tx_bat</text> -<text text-anchor="start" x="3073.5" y="-106.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text> -<polyline fill="none" stroke="black" points="3065.5,-98.5 3361.5,-98.5 "/> -<text text-anchor="start" x="3073.5" y="-83.3" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> -<text text-anchor="start" x="3073.5" y="-68.3" font-family="Times,serif" font-size="14.00">current_pulse()</text> -<text text-anchor="start" x="3073.5" y="-53.3" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text> -<text text-anchor="start" x="3073.5" y="-38.3" font-family="Times,serif" font-size="14.00">turn_off()</text> -<text text-anchor="start" x="3073.5" y="-23.3" font-family="Times,serif" font-size="14.00">turn_on()</text> -<text text-anchor="start" x="3073.5" y="-8.3" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text> +<polygon fill="none" stroke="black" points="3110.5,-489 3110.5,-828 3406.5,-828 3406.5,-489 3110.5,-489"/> +<text text-anchor="middle" x="3258.5" y="-812.8" font-family="Times,serif" font-size="14.00">Tx</text> +<polyline fill="none" stroke="black" points="3110.5,-805 3406.5,-805 "/> +<text text-anchor="start" x="3118.5" y="-789.8" font-family="Times,serif" font-size="14.00">adc_voltage_max</text> +<text text-anchor="start" x="3118.5" y="-774.8" font-family="Times,serif" font-size="14.00">adc_voltage_min</text> +<text text-anchor="start" x="3118.5" y="-759.8" font-family="Times,serif" font-size="14.00">current</text> +<text text-anchor="start" x="3118.5" y="-744.8" font-family="Times,serif" font-size="14.00">current_adjustable : bool</text> +<text text-anchor="start" x="3118.5" y="-729.8" font-family="Times,serif" font-size="14.00">gain</text> +<text text-anchor="start" x="3118.5" y="-714.8" font-family="Times,serif" font-size="14.00">gain : int, float</text> +<text text-anchor="start" x="3118.5" y="-699.8" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text> +<text text-anchor="start" x="3118.5" y="-684.8" font-family="Times,serif" font-size="14.00">pin0 : DigitalInOut</text> +<text text-anchor="start" x="3118.5" y="-669.8" font-family="Times,serif" font-size="14.00">pin1 : DigitalInOut</text> +<text text-anchor="start" x="3118.5" y="-654.8" font-family="Times,serif" font-size="14.00">polarity</text> +<text text-anchor="start" x="3118.5" y="-639.8" font-family="Times,serif" font-size="14.00">polarity : int</text> +<text text-anchor="start" x="3118.5" y="-624.8" font-family="Times,serif" font-size="14.00">r_shunt</text> +<text text-anchor="start" x="3118.5" y="-609.8" font-family="Times,serif" font-size="14.00">tx_bat</text> +<text text-anchor="start" x="3118.5" y="-594.8" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text> +<polyline fill="none" stroke="black" points="3110.5,-587 3406.5,-587 "/> +<text text-anchor="start" x="3118.5" y="-571.8" font-family="Times,serif" font-size="14.00">current_pulse()</text> +<text text-anchor="start" x="3118.5" y="-556.8" font-family="Times,serif" font-size="14.00">gain_auto()</text> +<text text-anchor="start" x="3118.5" y="-541.8" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text> +<text text-anchor="start" x="3118.5" y="-526.8" font-family="Times,serif" font-size="14.00">turn_off()</text> +<text text-anchor="start" x="3118.5" y="-511.8" font-family="Times,serif" font-size="14.00">turn_on()</text> +<text text-anchor="start" x="3118.5" y="-496.8" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text> </g> <!-- 22 --> <g id="node23" class="node"> <title>22</title> -<polygon fill="none" stroke="black" points="3379.5,-534 3379.5,-813 3675.5,-813 3675.5,-534 3379.5,-534"/> -<text text-anchor="middle" x="3527.5" y="-797.8" font-family="Times,serif" font-size="14.00">TxAbstract</text> -<polyline fill="none" stroke="black" points="3379.5,-790 3675.5,-790 "/> -<text text-anchor="start" x="3387.5" y="-774.8" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="3387.5" y="-759.8" font-family="Times,serif" font-size="14.00">board_name</text> -<text text-anchor="start" x="3387.5" y="-744.8" font-family="Times,serif" font-size="14.00">connection</text> -<text text-anchor="start" x="3387.5" y="-729.8" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="3387.5" y="-714.8" font-family="Times,serif" font-size="14.00">exec_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="3387.5" y="-699.8" font-family="Times,serif" font-size="14.00">injection_duration</text> -<text text-anchor="start" x="3387.5" y="-684.8" font-family="Times,serif" font-size="14.00">injection_duration</text> -<text text-anchor="start" x="3387.5" y="-669.8" font-family="Times,serif" font-size="14.00">polarity</text> -<text text-anchor="start" x="3387.5" y="-654.8" font-family="Times,serif" font-size="14.00">pwr</text> -<text text-anchor="start" x="3387.5" y="-639.8" font-family="Times,serif" font-size="14.00">soh_logger : NoneType, RootLogger</text> -<text text-anchor="start" x="3387.5" y="-624.8" font-family="Times,serif" font-size="14.00">tx_bat</text> -<text text-anchor="start" x="3387.5" y="-609.8" font-family="Times,serif" font-size="14.00">tx_sync</text> -<polyline fill="none" stroke="black" points="3379.5,-602 3675.5,-602 "/> -<text text-anchor="start" x="3387.5" y="-586.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> -<text text-anchor="start" x="3387.5" y="-571.8" font-family="Times,serif" font-size="14.00">current_pulse()</text> -<text text-anchor="start" x="3387.5" y="-556.8" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text> -<text text-anchor="start" x="3387.5" y="-541.8" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text> +<polygon fill="none" stroke="black" points="2797.5,-963.5 2797.5,-1212.5 3145.5,-1212.5 3145.5,-963.5 2797.5,-963.5"/> +<text text-anchor="middle" x="2971.5" y="-1197.3" font-family="Times,serif" font-size="14.00">TxAbstract</text> +<polyline fill="none" stroke="black" points="2797.5,-1189.5 3145.5,-1189.5 "/> +<text text-anchor="start" x="2805.5" y="-1174.3" font-family="Times,serif" font-size="14.00">adc_gain</text> +<text text-anchor="start" x="2805.5" y="-1159.3" font-family="Times,serif" font-size="14.00">connection</text> +<text text-anchor="start" x="2805.5" y="-1144.3" font-family="Times,serif" font-size="14.00">exec_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="2805.5" y="-1129.3" font-family="Times,serif" font-size="14.00">injection_duration</text> +<text text-anchor="start" x="2805.5" y="-1114.3" font-family="Times,serif" font-size="14.00">injection_duration</text> +<text text-anchor="start" x="2805.5" y="-1099.3" font-family="Times,serif" font-size="14.00">model</text> +<text text-anchor="start" x="2805.5" y="-1084.3" font-family="Times,serif" font-size="14.00">polarity</text> +<text text-anchor="start" x="2805.5" y="-1069.3" font-family="Times,serif" font-size="14.00">pwr</text> +<text text-anchor="start" x="2805.5" y="-1054.3" font-family="Times,serif" font-size="14.00">soh_logger : RootLogger, NoneType</text> +<text text-anchor="start" x="2805.5" y="-1039.3" font-family="Times,serif" font-size="14.00">tx_bat</text> +<text text-anchor="start" x="2805.5" y="-1024.3" font-family="Times,serif" font-size="14.00">tx_sync</text> +<polyline fill="none" stroke="black" points="2797.5,-1016.5 3145.5,-1016.5 "/> +<text text-anchor="start" x="2805.5" y="-1001.3" font-family="Times,serif" font-size="14.00">current_pulse()</text> +<text text-anchor="start" x="2805.5" y="-986.3" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration, switch_pwr)</text> +<text text-anchor="start" x="2805.5" y="-971.3" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text> </g> <!-- 19->22 --> <g id="edge11" class="edge"> <title>19->22</title> -<path fill="none" stroke="black" d="M3325.53,-354.75C3361.06,-410.64 3399.94,-471.81 3434.02,-525.43"/> -<polygon fill="none" stroke="black" points="3431.13,-527.4 3439.44,-533.97 3437.03,-523.65 3431.13,-527.4"/> +<path fill="none" stroke="black" d="M3164.19,-828.18C3144.34,-861.17 3122.9,-895.1 3101.5,-926 3094.87,-935.57 3087.83,-945.31 3080.6,-955"/> +<polygon fill="none" stroke="black" points="3077.69,-953.04 3074.48,-963.13 3083.29,-957.25 3077.69,-953.04"/> </g> <!-- 20 --> <g id="node21" class="node"> <title>20</title> -<polygon fill="none" stroke="black" points="3379.5,-83 3379.5,-272 3675.5,-272 3675.5,-83 3379.5,-83"/> -<text text-anchor="middle" x="3527.5" y="-256.8" font-family="Times,serif" font-size="14.00">Tx</text> -<polyline fill="none" stroke="black" points="3379.5,-249 3675.5,-249 "/> -<text text-anchor="start" x="3387.5" y="-233.8" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="3387.5" y="-218.8" font-family="Times,serif" font-size="14.00">adc_gain : float</text> -<text text-anchor="start" x="3387.5" y="-203.8" font-family="Times,serif" font-size="14.00">current</text> -<text text-anchor="start" x="3387.5" y="-188.8" font-family="Times,serif" font-size="14.00">polarity : int</text> -<text text-anchor="start" x="3387.5" y="-173.8" font-family="Times,serif" font-size="14.00">tx_bat</text> -<text text-anchor="start" x="3387.5" y="-158.8" font-family="Times,serif" font-size="14.00">voltage</text> -<polyline fill="none" stroke="black" points="3379.5,-151 3675.5,-151 "/> -<text text-anchor="start" x="3387.5" y="-135.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> -<text text-anchor="start" x="3387.5" y="-120.8" font-family="Times,serif" font-size="14.00">current_pulse()</text> -<text text-anchor="start" x="3387.5" y="-105.8" font-family="Times,serif" font-size="14.00">inject(state)</text> -<text text-anchor="start" x="3387.5" y="-90.8" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text> +<polygon fill="none" stroke="black" points="2796.5,-564 2796.5,-753 3092.5,-753 3092.5,-564 2796.5,-564"/> +<text text-anchor="middle" x="2944.5" y="-737.8" font-family="Times,serif" font-size="14.00">Tx</text> +<polyline fill="none" stroke="black" points="2796.5,-730 3092.5,-730 "/> +<text text-anchor="start" x="2804.5" y="-714.8" font-family="Times,serif" font-size="14.00">adc_gain</text> +<text text-anchor="start" x="2804.5" y="-699.8" font-family="Times,serif" font-size="14.00">adc_gain : float</text> +<text text-anchor="start" x="2804.5" y="-684.8" font-family="Times,serif" font-size="14.00">current</text> +<text text-anchor="start" x="2804.5" y="-669.8" font-family="Times,serif" font-size="14.00">polarity : int</text> +<text text-anchor="start" x="2804.5" y="-654.8" font-family="Times,serif" font-size="14.00">tx_bat</text> +<text text-anchor="start" x="2804.5" y="-639.8" font-family="Times,serif" font-size="14.00">voltage</text> +<polyline fill="none" stroke="black" points="2796.5,-632 3092.5,-632 "/> +<text text-anchor="start" x="2804.5" y="-616.8" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> +<text text-anchor="start" x="2804.5" y="-601.8" font-family="Times,serif" font-size="14.00">current_pulse()</text> +<text text-anchor="start" x="2804.5" y="-586.8" font-family="Times,serif" font-size="14.00">inject(state)</text> +<text text-anchor="start" x="2804.5" y="-571.8" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text> </g> <!-- 20->22 --> <g id="edge12" class="edge"> <title>20->22</title> -<path fill="none" stroke="black" d="M3527.5,-272.29C3527.5,-343.13 3527.5,-441.77 3527.5,-523.39"/> -<polygon fill="none" stroke="black" points="3524,-523.77 3527.5,-533.77 3531,-523.77 3524,-523.77"/> +<path fill="none" stroke="black" d="M2950.41,-753.05C2954.11,-811.6 2958.93,-887.97 2963.03,-952.9"/> +<polygon fill="none" stroke="black" points="2959.56,-953.46 2963.68,-963.22 2966.54,-953.02 2959.56,-953.46"/> </g> <!-- 21 --> <g id="node22" class="node"> <title>21</title> -<polygon fill="none" stroke="black" points="3693.5,-0.5 3693.5,-354.5 3989.5,-354.5 3989.5,-0.5 3693.5,-0.5"/> -<text text-anchor="middle" x="3841.5" y="-339.3" font-family="Times,serif" font-size="14.00">Tx</text> -<polyline fill="none" stroke="black" points="3693.5,-331.5 3989.5,-331.5 "/> -<text text-anchor="start" x="3701.5" y="-316.3" font-family="Times,serif" font-size="14.00">adc_gain</text> -<text text-anchor="start" x="3701.5" y="-301.3" font-family="Times,serif" font-size="14.00">adc_gain : int, float</text> -<text text-anchor="start" x="3701.5" y="-286.3" font-family="Times,serif" font-size="14.00">ctl</text> -<text text-anchor="start" x="3701.5" y="-271.3" font-family="Times,serif" font-size="14.00">current</text> -<text text-anchor="start" x="3701.5" y="-256.3" font-family="Times,serif" font-size="14.00">current_adjustable : bool</text> -<text text-anchor="start" x="3701.5" y="-241.3" font-family="Times,serif" font-size="14.00">io</text> -<text text-anchor="start" x="3701.5" y="-226.3" font-family="Times,serif" font-size="14.00">mcp_board : MCP23008</text> -<text text-anchor="start" x="3701.5" y="-211.3" font-family="Times,serif" font-size="14.00">pin0 : DigitalInOut</text> -<text text-anchor="start" x="3701.5" y="-196.3" font-family="Times,serif" font-size="14.00">pin1 : DigitalInOut</text> -<text text-anchor="start" x="3701.5" y="-181.3" font-family="Times,serif" font-size="14.00">pin4 : DigitalInOut</text> -<text text-anchor="start" x="3701.5" y="-166.3" font-family="Times,serif" font-size="14.00">polarity</text> -<text text-anchor="start" x="3701.5" y="-151.3" font-family="Times,serif" font-size="14.00">polarity : int</text> -<text text-anchor="start" x="3701.5" y="-136.3" font-family="Times,serif" font-size="14.00">pwr : NoneType</text> -<text text-anchor="start" x="3701.5" y="-121.3" font-family="Times,serif" font-size="14.00">tx_bat</text> -<text text-anchor="start" x="3701.5" y="-106.3" font-family="Times,serif" font-size="14.00">voltage_adjustable : bool</text> -<polyline fill="none" stroke="black" points="3693.5,-98.5 3989.5,-98.5 "/> -<text text-anchor="start" x="3701.5" y="-83.3" font-family="Times,serif" font-size="14.00">adc_gain_auto()</text> -<text text-anchor="start" x="3701.5" y="-68.3" font-family="Times,serif" font-size="14.00">current_pulse()</text> -<text text-anchor="start" x="3701.5" y="-53.3" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text> -<text text-anchor="start" x="3701.5" y="-38.3" font-family="Times,serif" font-size="14.00">turn_off()</text> -<text text-anchor="start" x="3701.5" y="-23.3" font-family="Times,serif" font-size="14.00">turn_on()</text> -<text text-anchor="start" x="3701.5" y="-8.3" font-family="Times,serif" font-size="14.00">voltage_pulse(voltage, length, polarity)</text> -</g> -<!-- 21->22 --> +<polygon fill="none" stroke="black" points="3129.5,-128 3129.5,-212 3387.5,-212 3387.5,-128 3129.5,-128"/> +<text text-anchor="middle" x="3258.5" y="-196.8" font-family="Times,serif" font-size="14.00">Tx</text> +<polyline fill="none" stroke="black" points="3129.5,-189 3387.5,-189 "/> +<text text-anchor="start" x="3137.5" y="-173.8" font-family="Times,serif" font-size="14.00">pin4 : DigitalInOut</text> +<text text-anchor="start" x="3137.5" y="-158.8" font-family="Times,serif" font-size="14.00">pin6 : DigitalInOut</text> +<polyline fill="none" stroke="black" points="3129.5,-151 3387.5,-151 "/> +<text text-anchor="start" x="3137.5" y="-135.8" font-family="Times,serif" font-size="14.00">inject(polarity, injection_duration)</text> +</g> +<!-- 21->19 --> <g id="edge13" class="edge"> -<title>21->22</title> -<path fill="none" stroke="black" d="M3729.47,-354.75C3693.94,-410.64 3655.06,-471.81 3620.98,-525.43"/> -<polygon fill="none" stroke="black" points="3617.97,-523.65 3615.56,-533.97 3623.87,-527.4 3617.97,-523.65"/> +<title>21->19</title> +<path fill="none" stroke="black" d="M3258.5,-212.06C3258.5,-270.69 3258.5,-382.01 3258.5,-478.7"/> +<polygon fill="none" stroke="black" points="3255,-478.9 3258.5,-488.9 3262,-478.9 3255,-478.9"/> </g> </g> </svg> diff --git a/uml_diagrams/packages_uml_ohmpi.dot b/uml_diagrams/packages_uml_ohmpi.dot index d57d3be5340fdd6260e8e1ad79514cddfe78b9ac..4787367abeedbbc969f197112ee8f264182f22ac 100644 --- a/uml_diagrams/packages_uml_ohmpi.dot +++ b/uml_diagrams/packages_uml_ohmpi.dot @@ -36,17 +36,18 @@ rankdir=BT "8" -> "21" [arrowhead="open", arrowtail="none"]; "9" -> "2" [arrowhead="open", arrowtail="none"]; "9" -> "4" [arrowhead="open", arrowtail="none"]; -"10" -> "2" [arrowhead="open", arrowtail="none"]; "10" -> "4" [arrowhead="open", arrowtail="none"]; -"11" -> "2" [arrowhead="open", arrowtail="none"]; -"11" -> "4" [arrowhead="open", arrowtail="none"]; -"12" -> "2" [arrowhead="open", arrowtail="none"]; +"10" -> "23" [arrowhead="open", arrowtail="none"]; +"11" -> "10" [arrowhead="open", arrowtail="none"]; +"11" -> "23" [arrowhead="open", arrowtail="none"]; "12" -> "4" [arrowhead="open", arrowtail="none"]; -"13" -> "2" [arrowhead="open", arrowtail="none"]; +"12" -> "23" [arrowhead="open", arrowtail="none"]; "13" -> "4" [arrowhead="open", arrowtail="none"]; +"13" -> "23" [arrowhead="open", arrowtail="none"]; "14" -> "5" [arrowhead="open", arrowtail="none"]; -"15" -> "2" [arrowhead="open", arrowtail="none"]; +"14" -> "23" [arrowhead="open", arrowtail="none"]; "15" -> "5" [arrowhead="open", arrowtail="none"]; +"15" -> "23" [arrowhead="open", arrowtail="none"]; "16" -> "4" [arrowhead="open", arrowtail="none"]; "16" -> "23" [arrowhead="open", arrowtail="none"]; "17" -> "2" [arrowhead="open", arrowtail="none"]; diff --git a/uml_diagrams/packages_uml_ohmpi.dot.png b/uml_diagrams/packages_uml_ohmpi.dot.png index f69d4ef240878b1c8cd6aa39ccc36c315bb221db..ac4fcbfe2fe7a3e9762808694e8ad277a777f9ec 100644 Binary files a/uml_diagrams/packages_uml_ohmpi.dot.png and b/uml_diagrams/packages_uml_ohmpi.dot.png differ