diff --git a/.dev/test_controller.py b/.dev/test_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..80eb14ba38af71f8c707ce48cac773994bc7a5ae --- /dev/null +++ b/.dev/test_controller.py @@ -0,0 +1,6 @@ +from ohmpi.hardware_components.raspberry_pi import Ctl + +ctl = Ctl() +print(ctl.interfaces) + + diff --git a/doc/source/_static/css/my_theme.css b/doc/source/_static/css/my_theme.css index f28d9d496f5195e69155b3af79bb4054c2a795d0..603ec5e18210d53888ef03223ce7424cab023df9 100644 --- a/doc/source/_static/css/my_theme.css +++ b/doc/source/_static/css/my_theme.css @@ -1,6 +1,5 @@ +@import url("theme.css"); + .wy-nav-content { -max-width: 2000px !important; +max-width: 90%; } - -.wy-side-nav-search, .wy-nav-top { - background: #00ff00; \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index a5a5ab7a1024060c41eb9482e6d4c96dfbf4014b..cf220b0463c71a2918be2a30cabb307b2161af22 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -64,6 +64,7 @@ html_logo = 'img/logo/ohmpi/LOGO_OHMPI.png' # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' +html_style = 'css/my_theme.css' #html_theme = 'sphinxawesome_theme' #html_theme = 'bootstrap' #html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() @@ -73,6 +74,7 @@ html_theme = 'sphinx_rtd_theme' # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] + html_context = { "display_gitlab": True, # Integrate Gitlab "gitlab_repo": "ohmpi/ohmpi", # Repo name @@ -82,6 +84,3 @@ html_context = { master_doc = 'index' - - - diff --git a/ohmpi/hardware_components/abstract_hardware_components.py b/ohmpi/hardware_components/abstract_hardware_components.py index 8767d4b6b2b29490b5447f21187f6cf22d923bdc..ecd9ae97fcb7cfd47fd67a237dae6031cda7e4af 100644 --- a/ohmpi/hardware_components/abstract_hardware_components.py +++ b/ohmpi/hardware_components/abstract_hardware_components.py @@ -6,48 +6,55 @@ from ohmpi.logging_setup import create_stdout_logger from ohmpi.utils import enforce_specs SPECS = {'ctl': {'model': {'default': 'unknown CTL hardware'}, - 'exec_logger': {'default': None}, - 'soh_logger': {'default': None}, - 'connection': {'default': None}}, + 'exec_logger': {'default': None}, + 'soh_logger': {'default': None}, + 'connect': {'default': True}, + 'connection': {'default': None}}, 'pwr': {'model': {'default': 'unknown PWR hardware'}, - 'exec_logger': {'default': None}, - 'soh_logger': {'default': None}, - 'current_min': {'default': 0.}, - 'current_max': {'default': 0.}, - 'voltage_min': {'default': 0.}, - 'voltage_max': {'default': 0.}, - 'power_max': {'default': 0.}, - 'voltage_adjustable': {'default': False}, - 'current_adjustable': {'default': False}, - 'connection': {'default': None}}, + 'exec_logger': {'default': None}, + 'soh_logger': {'default': None}, + 'current_min': {'default': 0.}, + 'current_max': {'default': 0.}, + 'voltage_min': {'default': 0.}, + 'voltage_max': {'default': 0.}, + 'power_max': {'default': 0.}, + 'voltage_adjustable': {'default': False}, + 'current_adjustable': {'default': False}, + 'connect': {'default': True}, + 'connection': {'default': None}, + 'interface_name':{'default': None}}, 'mux': {'model': {'default': 'unknown MUX hardware'}, - 'exec_logger': {'default': None}, - 'soh_logger': {'default': None}, - 'id': {'default': None}, - 'connection': {'default': None}, - 'cabling': {'default': None}, - 'addresses': {'default': None}, - 'barrier': {'default': Barrier(1)}, - 'activation_delay': {'default': 0.}, # in s - 'release_delay': {'default': 0.}}, # in s + 'exec_logger': {'default': None}, + 'soh_logger': {'default': None}, + 'id': {'default': None}, + 'connect': {'default': True}, + 'connection': {'default': None}, + 'cabling': {'default': None}, + 'addresses': {'default': None}, + 'barrier': {'default': Barrier(1)}, + 'activation_delay': {'default': 0.}, # in s + 'release_delay': {'default': 0.}}, # in s 'tx': {'model': {'default': 'unknown TX hardware'}, - 'injection_duration': {'default': 1.}, - 'exec_logger': {'default': None}, - 'soh_logger': {'default': None}, - 'connection': {'default': None}, - 'pwr': {'default': None}, - 'latency': {'default': 0.}, - 'tx_sync': {'default': Event()}}, + 'injection_duration': {'default': 1.}, + 'exec_logger': {'default': None}, + 'soh_logger': {'default': None}, + 'connect': {'default': True}, + 'connection': {'default': None}, + 'pwr': {'default': None}, + 'latency': {'default': 0.}, + 'tx_sync': {'default': Event()}}, 'rx': {'model': {'default': 'unknown RX hardware'}, - 'exec_logger': {'default': None}, - 'soh_logger': {'default': None}, - 'connection': {'default': None}, - 'sampling_rate': {'default': 1., 'max': np.inf}, - 'latency': {'default': 0.}, - 'voltage_max': {'default': 0.}, - 'bias': {'default': 0.}, - 'vmn_hardware_offset': {'default': 0.}} - } + 'exec_logger': {'default': None}, + 'soh_logger': {'default': None}, + 'connect': {'default': True}, + 'connection': {'default': None}, + 'sampling_rate': {'default': 1., 'max': np.inf}, + 'latency': {'default': 0.}, + 'voltage_max': {'default': 0.}, + 'bias': {'default': 0.}, + 'vmn_hardware_offset': {'default': 0.}} + } + class CtlAbstract(ABC): """CTlAbstract Class @@ -56,7 +63,8 @@ class CtlAbstract(ABC): for key in SPECS['ctl'].keys(): kwargs = enforce_specs(kwargs, SPECS['ctl'], key) self.model = kwargs['model'] - self.interfaces = None + self.interfaces = dict() + self.interfaces['none'] = None self.exec_logger = kwargs['exec_logger'] if self.exec_logger is None: self.exec_logger = create_stdout_logger('exec_ctl') @@ -66,6 +74,7 @@ class CtlAbstract(ABC): self.exec_logger.debug(f'{self.model} Ctl initialization') self._cpu_temp_available = False self.max_cpu_temp = np.inf + self.connect = kwargs['connect'] self.connection = kwargs['connection'] self.specs = kwargs @@ -92,8 +101,6 @@ class PwrAbstract(ABC): def __init__(self, **kwargs): for key in SPECS['pwr'].keys(): kwargs = enforce_specs(kwargs, SPECS['pwr'], key) - kwargs.update({'connect': kwargs.pop('connect', True)}) - self.model = kwargs['model'] self.exec_logger = kwargs['exec_logger'] if self.exec_logger is None: @@ -111,6 +118,7 @@ class PwrAbstract(ABC): self._voltage_min = kwargs['voltage_min'] self._voltage_max = kwargs['voltage_max'] self.switchable = False + self.interface_name = kwargs['interface_name'] self.connection = kwargs['connection'] self._battery_voltage = np.nan self._pwr_discharge_latency = np.nan @@ -190,7 +198,6 @@ class MuxAbstract(ABC): def __init__(self, **kwargs): for key in SPECS['mux'].keys(): kwargs = enforce_specs(kwargs, SPECS['mux'], key) - kwargs.update({'connect': kwargs.pop('connect', True)}) self.model = kwargs['model'] self.exec_logger = kwargs['exec_logger'] if self.exec_logger is None: @@ -246,7 +253,7 @@ class MuxAbstract(ABC): Either 'on' or 'off'. bypass_check: bool, optional Bypasses checks for A==M or A==M or B==M or B==N (i.e. used for rs-check) - bypass_check: bool, optional + bypass_ab_check: bool, optional Bypasses checks for A==B (i.e. used for testing r_shunt). Should be used with caution. """ status = True @@ -349,7 +356,6 @@ class TxAbstract(ABC): def __init__(self, **kwargs): for key in SPECS['tx'].keys(): kwargs = enforce_specs(kwargs, SPECS['tx'], key) - kwargs.update({'connect': kwargs.pop('connect', True)}) self.model = kwargs['model'] self.exec_logger = kwargs['exec_logger'] @@ -553,7 +559,6 @@ class RxAbstract(ABC): def __init__(self, **kwargs): for key in SPECS['rx'].keys(): kwargs = enforce_specs(kwargs, SPECS['rx'], key) - kwargs.update({'connect': kwargs.pop('connect', True)}) self.model = kwargs['model'] self.exec_logger = kwargs['exec_logger'] if self.exec_logger is None: diff --git a/ohmpi/hardware_components/mb_2023_0_X.py b/ohmpi/hardware_components/mb_2023_0_X.py index ced53d93142cd598694cba2a3b6be03617b99822..08dea0885dd25ca28fc0cd52f2fdba2bd11b2142 100644 --- a/ohmpi/hardware_components/mb_2023_0_X.py +++ b/ohmpi/hardware_components/mb_2023_0_X.py @@ -297,6 +297,6 @@ class Rx(RxAbstract): """ 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, ads.P1).voltage * self._coef_p2 * 1000. - self._bias # TODO: check if it should be negated + 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.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 2f40154d47d5cc34b3567dc854bbfa86bc92f8df..73f8baf6f12009747eef2fed951b029c1007cb9f 100644 --- a/ohmpi/hardware_components/mb_2024_0_2.py +++ b/ohmpi/hardware_components/mb_2024_0_2.py @@ -293,6 +293,6 @@ class Rx(Rx_mb_2023): """ 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 + 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/pwr_batt.py b/ohmpi/hardware_components/pwr_batt.py index e07376f796c3666edfb9f07e438882af5252bd47..05d13b0ed9325034bb07d3e3ec54bd131bfd1e76 100644 --- a/ohmpi/hardware_components/pwr_batt.py +++ b/ohmpi/hardware_components/pwr_batt.py @@ -9,7 +9,7 @@ SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')}, 'voltage': {'default': 12., 'max': 13., 'min': 1.}, 'current_adjustable': {'default': False}, 'voltage_adjustable': {'default': False}, - 'interface': {'default': 'none'}, + 'interface_name': {'default': 'none'}, } diff --git a/ohmpi/hardware_components/pwr_dph5005.py b/ohmpi/hardware_components/pwr_dph5005.py index e2b1ad741ddd2b66d84f38694acd375fa019981e..700bf137a37a8c0de7ebdeb586d9a8a82a66941e 100644 --- a/ohmpi/hardware_components/pwr_dph5005.py +++ b/ohmpi/hardware_components/pwr_dph5005.py @@ -17,7 +17,8 @@ SPECS = {'model': {'default': os.path.basename(__file__).rstrip('.py')}, 'current_adjustable': {'default': False}, 'voltage_adjustable': {'default': True}, 'pwr_latency': {'default': 4.}, - 'pwr_discharge_latency': {'default': 1.} + 'pwr_discharge_latency': {'default': 1.}, + 'interface_name': {'default': 'modbus'} } @@ -44,9 +45,14 @@ class Pwr(PwrAbstract): self._pwr_latency = kwargs['pwr_latency'] self._pwr_discharge_latency = kwargs['pwr_discharge_latency'] self._pwr_state = 'off' - if self.connect: - assert isinstance(self.connection, Instrument) + print(f'dph connection : {self.connection}') + if self.interface_name == 'modbus': + assert isinstance(self.connection, Instrument) + elif self.interface_name == 'bluetooth': + raise Warning('Bluetooth communication with dph5050 is not implemented') + elif self.interface_name == 'none': + raise IOError('dph interface cannot be set to none') # self.pwr_state = self._pwr_state if not subclass_init: diff --git a/ohmpi/hardware_components/raspberry_pi.py b/ohmpi/hardware_components/raspberry_pi.py index aa32bed4256ae30a278a2d2e66ad442eee411295..bb28fa8757eeede9f3661c22f458e76281d19cbb 100644 --- a/ohmpi/hardware_components/raspberry_pi.py +++ b/ohmpi/hardware_components/raspberry_pi.py @@ -32,28 +32,28 @@ class Ctl(CtlAbstract): subclass_init = True super().__init__(**kwargs) - self.interfaces = dict() - - # None interface for battery - self.interfaces['none'] = None + if self.connect: + self._connect_i2c() + self._connect_i2c_ext() + self._reset_modbus(**kwargs) + def _connect_i2c(self): # I2C try: self.interfaces['i2c'] = busio.I2C(board.SCL, board.SDA) # noqa except Exception as e: self.exec_logger.warning(f'Could not initialize I2C:\n{e}') - # warnings.resetwarnings() + def _connect_i2c_ext(self): # Extended I2C - warnings.filterwarnings(action='ignore', category=RuntimeWarning, module='adafruit_blinka') # to filter out adafruit warning about setting I2C frequency + warnings.filterwarnings(action='ignore', category=RuntimeWarning, + module='adafruit_blinka') # to filter out adafruit warning about setting I2C frequency try: self.interfaces['i2c_ext'] = ExtendedI2C(4) # 4 is defined except Exception as e: self.exec_logger.warning(f'Could not initialize Extended I2C:\n{e}') - self.reset_modbus(**kwargs) - - def reset_modbus(self,**kwargs): + def _reset_modbus(self, **kwargs): # modbus try: self.interfaces['modbus'] = minimalmodbus.Instrument(port=kwargs['modbus_port'], diff --git a/ohmpi/hardware_system.py b/ohmpi/hardware_system.py index 62679ce97ff142641d5619c0948e0bfd6401749b..d8a2afeab8a7b56fdaf2f8534bd7d8f683243130 100644 --- a/ohmpi/hardware_system.py +++ b/ohmpi/hardware_system.py @@ -21,6 +21,7 @@ except ModuleNotFoundError: 'installed ohmpi : \npython3 setup_config.py\n' 'If you deleted your config.py file by mistake, you should find a backup in configs/config_backup.py') sys.exit(-1) +from ohmpi.utils import enforce_specs from threading import Thread, Event, Barrier, BrokenBarrierError import warnings @@ -115,6 +116,7 @@ class OhmPiHardware: 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']) + print(HARDWARE_CONFIG['rx']) HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection', HARDWARE_CONFIG['rx']['ctl'].interfaces[ @@ -157,9 +159,7 @@ class OhmPiHardware: ctl_mod = importlib.import_module(f'ohmpi.hardware_components.{ctl_mod}') HARDWARE_CONFIG['pwr']['ctl'] = ctl_mod.Ctl(**HARDWARE_CONFIG['pwr']['ctl']) - HARDWARE_CONFIG['pwr'].update({ - 'interface_name': HARDWARE_CONFIG['pwr'].pop('interface_name', None)}) - + HARDWARE_CONFIG['pwr'] = enforce_specs(HARDWARE_CONFIG['pwr'], pwr_module.SPECS, 'interface_name') HARDWARE_CONFIG['pwr'].update({ 'connection': HARDWARE_CONFIG['pwr'].pop( 'connection', HARDWARE_CONFIG['pwr']['ctl'].interfaces[ diff --git a/ohmpi/logging_setup.py b/ohmpi/logging_setup.py index 19a13434d053a4d01e53f03c448be39c77c5d14f..99845f7055f77ce14e2e2231e1df6afa0ca72e0a 100644 --- a/ohmpi/logging_setup.py +++ b/ohmpi/logging_setup.py @@ -30,7 +30,7 @@ def add_logging_level(level_name, level_num, method_name=None): raise an `AttributeError` if the level name is already an attribute of the `logging` module or if the method name is already present - comes from https://stackoverflow.com/questions/2183233 + comes from https://stackoverflow.com/questions/2183233 modified Example ------- @@ -45,11 +45,11 @@ def add_logging_level(level_name, level_num, method_name=None): method_name = level_name.lower() if hasattr(logging, level_name): - raise AttributeError('{} already defined in logging module'.format(level_name)) + raise AttributeError(f'{level_name} already defined in logging module') if hasattr(logging, method_name): - raise AttributeError('{} already defined in logging module'.format(method_name)) + raise AttributeError(f'{method_name} already defined in logging module') if hasattr(logging.getLoggerClass(), method_name): - raise AttributeError('{} already defined in logger class'.format(method_name)) + raise AttributeError(f'{method_name} already defined in logger class') # This method was inspired by the answers to Stack Overflow post # http://stackoverflow.com/q/2183233/2988730, especially diff --git a/ohmpi/tests.py b/ohmpi/tests.py index 6c6e1098cacab4b3a390dec9ccaf5b4f9e96ecc4..71b1bc430761d34e579ddef52392cf2eb8ca4de2 100644 --- a/ohmpi/tests.py +++ b/ohmpi/tests.py @@ -481,7 +481,7 @@ def test_pwr_accessibility(hw_nc, test_logger): if tx.pwr.voltage_adjustable: try: if pwr.specs['interface_name'] == 'modbus': - pwr.specs['ctl'].reset_modbus() + pwr.specs['ctl']._reset_modbus() test_logger(colored( f"PWR: {pwr.specs['model']} is accessible via modbus.", "green"))