diff --git a/config.py b/config.py index 7c1baee1858c1e072e71bd41b963cf9fa2fee115..ff738cad2081d561e71aacab27a14306a561ef45 100644 --- a/config.py +++ b/config.py @@ -1,5 +1,5 @@ import logging -from utils import get_platform +from OhmPi.utils import get_platform from paho.mqtt.client import MQTTv31 @@ -27,27 +27,23 @@ OHMPI_CONFIG = { } # TODO: add a dictionary with INA models and associated gain values HARDWARE_CONFIG = { - {'controller': {'model' : 'raspberry_pi_3' - } - }, - {'tx' : {'model' : 'mb_2024_rev_0_0', + 'controller': {'model' : 'raspberry_pi_3' + }, + 'tx' : {'model' : 'mb_2024_rev_0_0', 'mcp_board_address': 0x20, 'Imax': 4800 / 50 / 2, # Maximum current 'R_shunt': 2 # Shunt resistance in Ohms - } - }, - {'rx' : {'model': 'mb_2024_rev_0_0', + }, + 'rx' : {'model': 'mb_2024_rev_0_0', '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... - } - }, - {'mux': {'model' : 'mux_2021', + }, + 'mux': {'model' : 'mux_2021', 'max_elec': 64, 'board_addresses': {'A': 0x73, 'B': 0x72, 'M': 0x71, 'N': 0x70}, # CHECK IF YOUR BOARDS HAVE THESE ADDRESSES '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... - } - } + } } # SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS diff --git a/hardware/__init__.py b/hardware/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a9b071208d59f8e9bc4e50287005296680c59f73 --- /dev/null +++ b/hardware/__init__.py @@ -0,0 +1 @@ +from OhmPi.hardware.abstract_hardware import TxAbstract, RxAbstract, MuxAbstract, ControllerAbstract \ No newline at end of file diff --git a/hardware/hardware.py b/hardware/abstract_hardware.py similarity index 64% rename from hardware/hardware.py rename to hardware/abstract_hardware.py index 5845d2655aaf13731aed0945a6f1ef10a75c63dd..9316f7d78e14c6eb6ba3e617be6856b95b1e5872 100644 --- a/hardware/hardware.py +++ b/hardware/abstract_hardware.py @@ -1,6 +1,5 @@ -from abc import ABC -import os -from ..logging_setup import create_default_logger +from abc import ABC, abstractmethod +from OhmPi.logging_setup import create_default_logger class ControllerAbstract(ABC): def __init__(self, **kwargs): @@ -11,31 +10,49 @@ class MuxAbstract(ABC): class TxAbstract(ABC): def __init__(self, **kwargs): + self.board_name = kwargs.pop('board_name', 'unknown TX hardware') polarity = kwargs.pop('polarity', 1) inj_time = kwargs.pop('inj_time', 1.) - self.exec_logger = kwargs.pop('exec_logger', create_default_logger('exec')) - self.soh_logger = kwargs.pop('soh_logger', create_default_logger('soh')) + self.exec_logger = kwargs.pop('exec_logger', create_default_logger('exec_tx')) + self.soh_logger = kwargs.pop('soh_logger', create_default_logger('soh_tx')) self._polarity = None self._inj_time = None self._dps_state = 'off' + self._adc_gain = 1. self.polarity = polarity self.inj_time = inj_time - self.board_name = os.path.basename(__file__) - self.exec_logger.debug(f'TX {self.board_name} initialization') + self.exec_logger.debug(f'{self.board_name} TX initialization') + + @property + def adc_gain(self): + return self._adc_gain + + @adc_gain.setter + def adc_gain(self, value): + self._adc_gain = value + self.exec_logger.debug(f'Setting TX ADC gain to {value}') + + @abstractmethod + def adc_gain_auto(self): + pass @property + @abstractmethod def current(self): # add actions to read the DPS current and return it return None @current.setter + @abstractmethod def current(self, value, **kwargs): # add actions to set the DPS current pass + @abstractmethod def current_pulse(self, **kwargs): pass + @abstractmethod def inject(self, state='on'): assert state in ['on', 'off'] @@ -67,15 +84,23 @@ class TxAbstract(ABC): self._dps_state = 'on' @property + @abstractmethod def voltage(self): # add actions to read the DPS voltage and return it return None @voltage.setter + @abstractmethod def voltage(self, value, **kwargs): # add actions to set the DPS voltage pass + @property + @abstractmethod + def tx_bat(self): + pass + + @abstractmethod def voltage_pulse(self, voltage, length, polarity): """ Generates a square voltage pulse @@ -95,6 +120,27 @@ class RxAbstract(ABC): def __init__(self, **kwargs): self.exec_logger = kwargs.pop('exec_logger', create_default_logger('exec')) self.soh_logger = kwargs.pop('soh_logger', create_default_logger('soh')) - self.board_name = os.path.basename(__file__) - self.exec_logger.debug(f'RX {self.board_name} initialization') + self.board_name = kwargs.pop('board_name', 'unknown RX hardware') + self.exec_logger.debug(f'{self.board_name} RX initialization') + self._adc_gain = 1. + @property + def adc_gain(self): + return self._adc_gain + + + @adc_gain.setter + def adc_gain(self, value): + self._adc_gain = value + self.exec_logger.debug(f'Setting RX ADC gain to {value}') + + @abstractmethod + def adc_gain_auto(self): + pass + + @property + @abstractmethod + def voltage(self): + """ Gets the voltage VMN in Volts + """ + pass \ No newline at end of file diff --git a/hardware/dummy_rx.py b/hardware/dummy_rx.py new file mode 100644 index 0000000000000000000000000000000000000000..daa0172d6753cc9ab10585edcf8051e9f266d5a7 --- /dev/null +++ b/hardware/dummy_rx.py @@ -0,0 +1,38 @@ +from OhmPi.config import HARDWARE_CONFIG +import numpy as np +import os +from OhmPi.hardware import RxAbstract +RX_CONFIG = HARDWARE_CONFIG['rx'] + +# hardware limits +voltage_min = 10. # mV +voltage_max = 4500. +RX_CONFIG['voltage_min'] = voltage_min # mV +RX_CONFIG['voltage_max'] = voltage_max + +class Rx(RxAbstract): + def __init__(self, **kwargs): + kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) + super().__init__(**kwargs) + self._adc_gain = 1. + + @property + def adc_gain(self): + return self._adc_gain + + @adc_gain.setter + def adc_gain(self, value): + self.exec_logger.debug(f'Setting RX ADC gain to {value}') + + def adc_gain_auto(self): + gain = 1 + self.exec_logger.debug(f'Setting TX ADC gain automatically to {gain}') + self.adc_gain = gain + + @property + def voltage(self): + """ Gets the voltage VMN in Volts + """ + u = np.random.uniform(-.200,.200) # gets the max between u0 & u2 and set the sign + self.exec_logger.debug(f'Reading random voltage on RX. Returning {u} V') + return u \ No newline at end of file diff --git a/hardware/dummy_tx.py b/hardware/dummy_tx.py new file mode 100644 index 0000000000000000000000000000000000000000..c2f14d0678cffb55e223026d4e76102bc0dcccde --- /dev/null +++ b/hardware/dummy_tx.py @@ -0,0 +1,111 @@ +from OhmPi.config import HARDWARE_CONFIG +import time +import os +import numpy as np +from OhmPi.hardware import TxAbstract + +TX_CONFIG = HARDWARE_CONFIG['tx'] + +# hardware limits +voltage_min = 10. # mV +voltage_max = 4500. + +TX_CONFIG['current_min'] = voltage_min / (TX_CONFIG['R_shunt'] * 50) # mA +TX_CONFIG['current_max'] = voltage_max / (TX_CONFIG['R_shunt'] * 50) +TX_CONFIG['default_voltage'] = 5. # V +TX_CONFIG['voltage_max'] = 50. # V +TX_CONFIG['dps_switch_on_warm_up'] = 4. # 4 seconds + +class Tx(TxAbstract): + def inject(self, state='on'): + 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']) + + self._adc_gain = 1 + + self.polarity = 0 + self.turn_on() + time.sleep(TX_CONFIG['dps_switch_on_warm_up']) + self.exec_logger.info(f'TX battery: {self.tx_bat:.1f} V') + self.turn_off() + + @property + def adc_gain(self): + return self._adc_gain + + @adc_gain.setter + def adc_gain(self, value): + self._adc_gain = value + self.exec_logger.debug(f'Setting TX ADC gain to {value}') + + def adc_gain_auto(self): + gain = 1. + self.exec_logger.debug(f'Setting TX ADC gain automatically to {gain}') + self.adc_gain = gain + + def current_pulse(self, **kwargs): + super().current_pulse(**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 + """ + current = np.abs(np.random.normal(0.7, 0.2)) + self.exec_logger.debug(f'Reading random current on TX. Returning {current} A') + return current + + @ 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') + + @property + def voltage(self): + return self._voltage + @voltage.setter + def voltage(self, value): + if value > TX_CONFIG['voltage_max']: + self.exec_logger.warning(f'Sorry, cannot inject more than {TX_CONFIG["voltage_max"]} V, ' + f'set it back to {TX_CONFIG["default_voltage"]} V (default value).') + value = TX_CONFIG['default_voltage'] + if value < 0.: + self.exec_logger.warning(f'Voltage should be given as a positive number. ' + f'Set polarity to -1 to reverse voltage...') + value = np.abs(value) + + + @property + def tx_bat(self): + tx_bat = np.random.uniform(10.9, 13.4) + if tx_bat < 12.: + self.soh_logger.debug(f'Low TX Battery: {tx_bat:.1f} V') + return tx_bat + + def voltage_pulse(self, voltage=TX_CONFIG['default_voltage'], length=None, polarity=None): + """ 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 + """ + + if length is None: + length = self.inj_time + if polarity is None: + polarity = self.polarity + self.polarity = polarity + self.voltage = voltage + self.exec_logger.debug(f'Voltage pulse of {polarity*voltage:.3f} V for {length:.3f} s') + self.inject(state='on') + time.sleep(length) + self.inject(state='off') diff --git a/hardware/mb_2024_rev_0_0.py b/hardware/mb_2024_rev_0_0.py index 2f2b072ee67918f8b582773cbf748ecf53ce864c..373556a0baa4fba808429a91e01d1cdc3f7f949b 100644 --- a/hardware/mb_2024_rev_0_0.py +++ b/hardware/mb_2024_rev_0_0.py @@ -1,5 +1,5 @@ import importlib -from ..config import OHMPI_CONFIG +from OhmPi.config import OHMPI_CONFIG import adafruit_ads1x15.ads1115 as ads # noqa from adafruit_ads1x15.analog_in import AnalogIn # noqa from adafruit_mcp230xx.mcp23008 import MCP23008 # noqa @@ -7,11 +7,12 @@ from digitalio import Direction # noqa import minimalmodbus # noqa import time import numpy as np -from hardware import TxAbstract, RxAbstract +import os +from OhmPi.hardware import TxAbstract, RxAbstract controller_module = importlib.import_module(f'{OHMPI_CONFIG["hardware"]["controller"]["model"]}') -TX_CONFIG = OHMPI_CONFIG['hardware']['TX'] -RX_CONFIG = OHMPI_CONFIG['hardware']['RX'] +TX_CONFIG = OHMPI_CONFIG['rx'] +RX_CONFIG = OHMPI_CONFIG['tx'] # hardware limits voltage_min = 10. # mV @@ -51,6 +52,7 @@ def _gain_auto(channel): class Tx(TxAbstract): 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']) self.controller = kwargs.pop('controller', controller_module.Controller()) @@ -60,9 +62,9 @@ class Tx(TxAbstract): # ADS1115 for current measurement (AB) self._adc_gain = 2/3 - self.ads_current_address = 0x48 - self.ads_current = ads.ADS1115(self.controller.bus, gain=self.adc_gain, data_rate=860, - address=self.ads_current_address) + self._ads_current_address = 0x48 + self._ads_current = ads.ADS1115(self.controller.bus, gain=self.adc_gain, data_rate=860, + address=self._ads_current_address) # Relays for pulse polarity self.pin0 = self.mcp_board.get_pin(0) @@ -85,7 +87,7 @@ class Tx(TxAbstract): self.DPS.debug = False # self.DPS.serial.parity = 'N' # No parity self.DPS.mode = minimalmodbus.MODE_RTU # RTU mode - self.DPS.write_register(0x0001, 1000, 0) # max current allowed (100 mA for relays) + self.DPS.write_register(0x0001, 1000, 0) # max current allowed (100 mA for relays) : # (last number) 0 is for mA, 3 is for A # I2C connexion to MCP23008, for current injection @@ -93,12 +95,7 @@ class Tx(TxAbstract): self.pin4.direction = Direction.OUTPUT self.pin4.value = True - tx_bat = self.DPS.read_register(0x05, 2) - if self.exec_logger is not None: - self.exec_logger.info(f'TX battery: {tx_bat:.1f} V') - if tx_bat < 12.: - if self.soh_logger is not None: - self.soh_logger.debug(f'Low TX Battery: {tx_bat:.1f} V') # TODO: SOH logger + self.exec_logger.info(f'TX battery: {self.tx_bat:.1f} V') self.turn_off() @property @@ -109,12 +106,12 @@ class Tx(TxAbstract): def adc_gain(self, value): assert value in [2/3, 2, 4, 8, 16] self._adc_gain = value - self.ads_current = ads.ADS1115(self.controller.bus, gain=self.adc_gain, data_rate=860, - address=self.ads_current_address) + self._ads_current = ads.ADS1115(self.controller.bus, gain=self.adc_gain, data_rate=860, + address=self._ads_current_address) self.exec_logger.debug(f'Setting TX ADC gain to {value}') def adc_gain_auto(self): - gain = _gain_auto(AnalogIn(self.ads_current, ads.P0)) + 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 @@ -126,7 +123,7 @@ class Tx(TxAbstract): def current(self): """ Gets the current IAB in Amps """ - return AnalogIn(self.ads_current, ads.P0).voltage * 1000. / (50 * TX_CONFIG['R_shunt']) # noqa measure current + return AnalogIn(self._ads_current, ads.P0).voltage * 1000. / (50 * TX_CONFIG['R_shunt']) # noqa measure current @ current.setter def current(self, value): @@ -184,6 +181,13 @@ class Tx(TxAbstract): self.pin2.value = True self.pin3.value = True + @property + def tx_bat(self): + tx_bat = self.DPS.read_register(0x05, 2) + if tx_bat < 12.: + self.soh_logger.debug(f'Low TX Battery: {tx_bat:.1f} V') + return tx_bat + def voltage_pulse(self, voltage=TX_CONFIG['default_voltage'], length=None, polarity=None): """ Generates a square voltage pulse @@ -210,31 +214,30 @@ class Tx(TxAbstract): class Rx(RxAbstract): def __init__(self, **kwargs): - self.controller = kwargs.pop('controller', controller_module.Controller()) - self._adc_gain = [2/3, 2/3] + kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) super().__init__(**kwargs) - self.ads_voltage_address = 0x49 - # ADS1115 for voltage measurement (MN) - self.ads_voltage = ads.ADS1115(self.controller.bus, gain=2/3, data_rate=860, address=self.ads_voltage_address) + self.controller = kwargs.pop('controller', controller_module.Controller()) + # ADS1115 for voltage measurement (MN) + self._ads_voltage_address = 0x49 + self._adc_gain = 2/3 + self._ads_voltage = ads.ADS1115(self.controller.bus, gain=self._adc_gain, data_rate=860, address=self._ads_voltage_address) @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] + assert value in [2/3, 2, 4, 8, 16] self._adc_gain = value - self.ads_voltage = ads.ADS1115(self.controller.bus, gain=self.adc_gain, data_rate=860, - address=self.ads_voltage_address) + self._ads_voltage = ads.ADS1115(self.controller.bus, gain=self.adc_gain, data_rate=860, + address=self._ads_voltage_address) self.exec_logger.debug(f'Setting RX ADC gain to {value}') - def adc_gain_auto(self): - gain_0 = _gain_auto(AnalogIn(self.ads_voltage, ads.P0)) - gain_2 = _gain_auto(AnalogIn(self.ads_voltage, ads.P2)) + 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])[0] self.exec_logger.debug(f'Setting TX ADC gain automatically to {gain}') self.adc_gain = gain @@ -243,8 +246,8 @@ class Rx(RxAbstract): def voltage(self): """ Gets the voltage VMN in Volts """ - u0 = AnalogIn(self.ads_voltage, ads.P0).voltage * 1000. - u2 = AnalogIn(self.ads_voltage, ads.P2).voltage * 1000. + u0 = AnalogIn(self._ads_voltage, ads.P0).voltage * 1000. + u2 = AnalogIn(self._ads_voltage, ads.P2).voltage * 1000. u = np.max([u0,u2]) * (np.heaviside(u0-u2, 1.) * 2 - 1.) # gets the max between u0 & u2 and set the sign self.exec_logger.debug(f'Reading voltages {u0} V and {u2} V on RX. Returning {u} V') return u \ No newline at end of file diff --git a/logging_setup.py b/logging_setup.py index 8178dd5adc30423204c5f377cd18c1d6c3dbd224..dc93066f9abc3b83e5edb3d0be34bac648f4fc33 100644 --- a/logging_setup.py +++ b/logging_setup.py @@ -1,10 +1,10 @@ import json -from config import EXEC_LOGGING_CONFIG, DATA_LOGGING_CONFIG, MQTT_LOGGING_CONFIG, MQTT_CONTROL_CONFIG +from OhmPi.config import EXEC_LOGGING_CONFIG, DATA_LOGGING_CONFIG, MQTT_LOGGING_CONFIG, MQTT_CONTROL_CONFIG from os import path, mkdir, statvfs from time import gmtime import logging -from mqtt_handler import MQTTHandler -from compressed_sized_timed_rotating_handler import CompressedSizedTimedRotatingFileHandler +from OhmPi.mqtt_handler import MQTTHandler +from OhmPi.compressed_sized_timed_rotating_handler import CompressedSizedTimedRotatingFileHandler import sys from termcolor import colored @@ -17,7 +17,7 @@ def create_default_logger(name): handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) logger.addHandler(handler) - logger.setLevel('debug') + logger.setLevel(logging.DEBUG) return logger def setup_loggers(mqtt=True): diff --git a/measure.py b/measure.py index 2491eddb3cc24362bd3db3e704be4ef9b3df1639..50c218f891665ec29474d6533bd33a1647ba8f66 100644 --- a/measure.py +++ b/measure.py @@ -1,11 +1,11 @@ import importlib import numpy as np -from logging_setup import create_default_logger -from config import OHMPI_CONFIG -controller_module = importlib.import_module(f'{OHMPI_CONFIG["hardware"]["controller"]["model"]}') -tx_module = importlib.import_module(f'{OHMPI_CONFIG["hardware"]["tx"]["model"]}') -rx_module = importlib.import_module(f'{OHMPI_CONFIG["hardware"]["rx"]["model"]}') -mux_module = importlib.import_module(f'{OHMPI_CONFIG["hardware"]["mux"]["model"]}') +from OhmPi.logging_setup import create_default_logger +from OhmPi.config import OHMPI_CONFIG +controller_module = importlib.import_module(f'OhmPi.{OHMPI_CONFIG["hardware"]["controller"]["model"]}') +tx_module = importlib.import_module(f'OhmPi.{OHMPI_CONFIG["hardware"]["tx"]["model"]}') +rx_module = importlib.import_module(f'OhmPi.{OHMPI_CONFIG["hardware"]["rx"]["model"]}') +mux_module = importlib.import_module(f'OhmPi.{OHMPI_CONFIG["hardware"]["mux"]["model"]}') TX_CONFIG = tx_module.TX_CONFIG RX_CONFIG = rx_module.RX_CONFIG MUX_CONFIG = mux_module.MUX_CONFIG diff --git a/utils.py b/utils.py index 3e7bc184cbaf314231120ba9e627f8a6bcde2e24..6f5596322f4730fd5f5f02a843d88bdc116e2b6c 100644 --- a/utils.py +++ b/utils.py @@ -1,6 +1,5 @@ import io - def get_platform(): """Gets platform name and checks if it is a raspberry pi