diff --git a/pyproject.toml b/pyproject.toml index 18148b3..78da6f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,11 +4,11 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "pyx2cscope" -version = "0.4.2" +version = "0.4.3" description = "python implementation of X2Cscope" authors = [ - "Yash Agarwal ", - "Edras Pacola", + "Yash Agarwal", + "Edras Pacola ", "Christoph Baumgartner", "Mark Wendler", ] @@ -28,6 +28,7 @@ pyx2cscope = 'pyx2cscope.__main__:main' python = "^3.10" pyserial = "^3.5" pyelftools = "^0.31" +pyyaml ="^6.0.1" numpy = "^1.26.0" matplotlib = "^3.7.2" PyQt5 = "^5.15.9" diff --git a/pyx2cscope/__init__.py b/pyx2cscope/__init__.py index 570f901..64f3101 100644 --- a/pyx2cscope/__init__.py +++ b/pyx2cscope/__init__.py @@ -1,11 +1,11 @@ """This module contains the pyx2cscope package. -Version: 0.4.2 +Version: 0.4.3 """ import logging -__version__ = "0.4.2" +__version__ = "0.4.3" def set_logger( diff --git a/pyx2cscope/examples/demo.py b/pyx2cscope/examples/demo.py index 159ef95..1c1eb4a 100644 --- a/pyx2cscope/examples/demo.py +++ b/pyx2cscope/examples/demo.py @@ -1,7 +1,7 @@ """Demo scripting for user to get started.""" from pyx2cscope.x2cscope import X2CScope -elf_file =r"C:\Users\m67250\OneDrive - Microchip Technology Inc\Desktop\Training_Domel\motorbench_demo_domel.X\dist\default\production\motorbench_demo_domel.X.production.elf" +elf_file =r"path to your elf file.elf" x2cscope = X2CScope(port="COM39", elf_file=elf_file) diff --git a/pyx2cscope/gui/generic_gui/detachable_gui.py b/pyx2cscope/gui/generic_gui/detachable_gui.py index 5c7134f..5d8a9f6 100644 --- a/pyx2cscope/gui/generic_gui/detachable_gui.py +++ b/pyx2cscope/gui/generic_gui/detachable_gui.py @@ -38,7 +38,7 @@ from pyx2cscope.gui import img as img_src from pyx2cscope.x2cscope import TriggerConfig, X2CScope -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.ERROR) matplotlib.use("QtAgg") @@ -100,9 +100,9 @@ def initialize_variables(self): self.plot_window_open = False self.settings = QSettings("MyCompany", "MyApp") self.file_path: str = self.settings.value("file_path", "", type=str) - self.initi_variables() + self.init_variables() - def initi_variables(self): + def init_variables(self): """Some extra variables define.""" self.selected_var_indices = [ 0, @@ -355,11 +355,12 @@ def create_variable_selection_group(self): grid_layout_variable = QGridLayout() variable_layout.addLayout(grid_layout_variable) + number_of_variables = 8 - self.scope_var_lines = [QLineEdit() for _ in range(7)] - self.trigger_var_checkbox = [QCheckBox() for _ in range(7)] - self.scope_channel_checkboxes = [QCheckBox() for _ in range(7)] - self.scope_scaling_boxes = [QLineEdit("1") for _ in range(7)] + self.scope_var_lines = [QLineEdit() for _ in range(number_of_variables)] + self.trigger_var_checkbox = [QCheckBox() for _ in range(number_of_variables)] + self.scope_channel_checkboxes = [QCheckBox() for _ in range(number_of_variables)] + self.scope_scaling_boxes = [QLineEdit("1") for _ in range(number_of_variables)] for checkbox in self.scope_channel_checkboxes: checkbox.setChecked(True) @@ -434,7 +435,7 @@ def handle_scope_checkbox_change(self, state, index): if i != index: checkbox.setChecked(False) self.triggerVariable = self.scope_var_combos[index].currentText() - print(f"Checked variable: {self.scope_var_combos[index].currentText()}") + logging.debug(f"Checked variable: {self.scope_var_combos[index].currentText()}") else: self.triggerVariable = None @@ -1309,7 +1310,7 @@ def configure_trigger(self): else: try: trigger_level = float(trigger_level_text) - print(trigger_level) + logging.debug(trigger_level) except ValueError: logging.error( f"Invalid trigger level value: {trigger_level_text}" diff --git a/pyx2cscope/gui/generic_gui/generic_gui.py b/pyx2cscope/gui/generic_gui/generic_gui.py index 7664916..f4c70f5 100644 --- a/pyx2cscope/gui/generic_gui/generic_gui.py +++ b/pyx2cscope/gui/generic_gui/generic_gui.py @@ -2,7 +2,7 @@ import logging -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.ERROR) import json import os import sys @@ -487,11 +487,11 @@ def create_variable_selection_group(self): grid_layout_variable = QGridLayout() variable_layout.addLayout(grid_layout_variable) - - self.scope_var_lines = [QLineEdit() for _ in range(7)] - self.trigger_var_checkbox = [QCheckBox() for _ in range(7)] - self.scope_channel_checkboxes = [QCheckBox() for _ in range(7)] - self.scope_scaling_boxes = [QLineEdit("1") for _ in range(7)] + number_of_variables = 8 + self.scope_var_lines = [QLineEdit() for _ in range(number_of_variables)] + self.trigger_var_checkbox = [QCheckBox() for _ in range(number_of_variables)] + self.scope_channel_checkboxes = [QCheckBox() for _ in range(number_of_variables)] + self.scope_scaling_boxes = [QLineEdit("1") for _ in range(number_of_variables)] for checkbox in self.scope_channel_checkboxes: checkbox.setChecked(True) @@ -568,7 +568,7 @@ def handle_scope_checkbox_change(self, state, index): if i != index: checkbox.setChecked(False) self.triggerVariable = self.scope_var_lines[index].text() - print(f"Checked variable: {self.scope_var_lines[index].text()}") + logging.debug(f"Checked variable: {self.scope_var_lines[index].text()}") else: self.triggerVariable = None @@ -1563,9 +1563,7 @@ def start_sampling(self): self.real_sampletime = self.x2cscope.scope_sample_time( scope_sample_time_us ) - print( - f"Real sample time: {self.real_sampletime} µs" - ) # Check this value + logging.debug(f"Real sample time: {self.real_sampletime} µs") # Check this value # Update the Total Time display self.total_time_value.setText(str(self.real_sampletime)) @@ -1579,7 +1577,7 @@ def start_sampling(self): single_shot=self.single_shot_checkbox.isChecked() ) b = time.time() - print("time execution", b - a) + logging.debug(f"time execution '{b - a}'") except Exception as e: error_message = f"Error starting sampling: {e}" logging.error(error_message) @@ -1603,7 +1601,7 @@ def configure_trigger(self): else: try: trigger_level = int(trigger_level_text) # YA - print(trigger_level) + logging.debug(trigger_level) except ValueError: logging.error( f"Invalid trigger level value: {trigger_level_text}" @@ -2287,7 +2285,7 @@ def eventFilter(self, source, event): # noqa: N802 #Overriding 3rd party functi ], ) except Exception as e: - print(e) + logging.debug(e) return super().eventFilter(source, event) diff --git a/pyx2cscope/utils.py b/pyx2cscope/utils.py index ef2af00..f998525 100644 --- a/pyx2cscope/utils.py +++ b/pyx2cscope/utils.py @@ -5,7 +5,7 @@ get_elf_file_path(key="path") -> str: Gets the path to the ELF file from the configuration. get_com_port(key="com_port") -> str: Gets the COM port from the configuration. """ - +import logging import os from configparser import ConfigParser @@ -28,7 +28,7 @@ def get_config_file() -> ConfigParser: config["COM_PORT"] = default_com with open(config_file, "w") as configfile: config.write(configfile) - print(f"Config file '{config_file}' created with default values") + logging.debug(f"Config file '{config_file}' created with default values") return config diff --git a/pyx2cscope/variable/variable.py b/pyx2cscope/variable/variable.py index c7eb21c..c1c3c80 100644 --- a/pyx2cscope/variable/variable.py +++ b/pyx2cscope/variable/variable.py @@ -58,23 +58,23 @@ class Variable: """Represents a variable in the MCU data memory.""" def __init__( - self, l_net: LNet, address: int, array_size: int, name: str = None + self, l_net: LNet, info: VariableInfo ) -> None: """Initialize the Variable object. + On var_info, the minimal information needed are name, address, and array_size. This is valid + for most of the variables. Enums will need enum_list, and unions having elements which are smaller + than a byte. + Args: l_net (LNet): LNet protocol that handles the communication with the target device. - address (int): Address of the variable in the MCU memory. - array_size (int): The number of elements in the array, 0 in case of a plain variable. - name (str, optional): Name of the variable. Defaults to None. + info (VariableInfo): Address of the variable in the MCU memory. """ if type(self) == Variable: # protect super class to be initiated directly raise Exception(" must be subclassed.") super().__init__() self.l_net = l_net - self.address = address - self.name = name - self.array_size = array_size + self.info = info def __getitem__(self, item): """Retrieve value regarding an indexed address from the variable's base address. @@ -90,10 +90,10 @@ def __getitem__(self, item): Returns: the value of the variable's index position. """ - if abs(item) > self.array_size: + if abs(item) > self.info.array_size: raise IndexError("Index outside scope") try: - idx = self.array_size + item if item < 0 else item + idx = self.info.array_size + item if item < 0 else item bytes_data = self._get_value_raw(index=idx) return self.bytes_to_value(bytes_data) except Exception as e: @@ -106,14 +106,14 @@ def __setitem__(self, key, value): key (int): the index of the variable. value (Number): The value to be stored in the MCU. """ - if abs(key) > self.array_size: + if abs(key) > self.info.array_size: raise IndexError("Index outside scope") try: - tmp_address = self.address - idx = self.array_size + key if key < 0 else key - self.address = self.address + idx * self.get_width() + tmp_address = self.info.address + idx = self.info.array_size + key if key < 0 else key + self.info.address = self.info.address + idx * self.get_width() self.set_value(value) - self.address = tmp_address + self.info.address = tmp_address except Exception as e: logging.error(e) @@ -126,7 +126,7 @@ def __len__(self): Returns: int: The number of elements in the array or 0 for a single variable. """ - return self.array_size + return self.info.array_size def __repr__(self): """String representation of the Variable. @@ -134,7 +134,7 @@ def __repr__(self): When printing the variable on a terminal or with str() operator, instead of printing the object and class attributes, the name of the variable will be printed. """ - return self.name + return self.info.name def _get_array_values(self): """Retrieve all values of the array from the MCU memory. @@ -144,13 +144,13 @@ def _get_array_values(self): """ chunk_data = bytearray() data_type = self.get_width() # width of the array elements. - chunk_size = self.array_size * data_type + chunk_size = self.info.array_size * data_type array_byte_size = chunk_size max_chunk = 253 i = 0 while i < array_byte_size: size_to_read = chunk_size if chunk_size < max_chunk else max_chunk - data = self.l_net.get_ram_array(self.address + i, size_to_read, 1) + data = self.l_net.get_ram_array(self.info.address + i, size_to_read, 1) chunk_data.extend(data) chunk_size -= max_chunk i += size_to_read @@ -161,6 +161,19 @@ def _get_array_values(self): # convert bytearray to number on every element of chunk_data return [self.bytes_to_value(k) for k in chunk_data] + def _get_bit_value(self, byte_value: Number): + """Extract the valid data in case of a union with bit size and offset.""" + shift = (8 * self.info.byte_size) - (self.info.bit_offset + self.info.bit_size) + return (byte_value >> shift) & ((1 << self.info.bit_size) - 1) + + def _set_bit_value(self, value: Number): + """Insert data into the correct bit position in case of a union with bit size and offset.""" + current_data = self.bytes_to_value(self._get_value_raw()) + shift = (8 * self.info.byte_size) - (self.info.bit_offset + self.info.bit_size) + mask = ((1 << self.info.bit_size) - 1) << shift + current_data &= ~mask + return current_data | ((value << shift) & mask) + def get_value(self): """Get the stored value from the MCU. @@ -172,9 +185,13 @@ def get_value(self): return self._get_array_values() else: bytes_data = self._get_value_raw() - return self.bytes_to_value(bytes_data) + value = self.bytes_to_value(bytes_data) + if self.info.bit_size != 0: + return self._get_bit_value(value) + return value except Exception as e: logging.error(e) + return None def _check_value_range(self, value: Number): """Check if the given value is in range of min and max variable values. @@ -226,10 +243,23 @@ def bytes_to_array(self, data: bytearray) -> List[Number]: array.append(self.bytes_to_value(data[i:j])) return array - @abstractmethod def set_value(self, new_value: Number): """Set the value to be stored in the MCU. + Args: + new_value (Number): The value to be stored in the MCU. + """ + self._check_value_range(new_value) + if self.info.bit_size != 0: + # for bit-size variables, we should first read the current value (byte-size), mask out the old bits, + # and insert the new value (bit-size). Then write back the byte-size data. + new_value = self._set_bit_value(new_value) + self._set_value(new_value) + + @abstractmethod + def _set_value(self, new_value: Number): + """Set the value to be stored in the MCU. + Args: new_value (Number): The value to be stored in the MCU. """ @@ -250,9 +280,9 @@ def is_array(self): Returns: bool: True if the variable is an array, False otherwise. """ - return True if self.array_size > 0 else False + return True if self.info.array_size > 0 else False - def _get_value_raw(self, index=0) -> bytearray: + def _get_value_raw(self, index=0) -> bytearray | None: """Ask LNet and get the raw "bytearray" value from the hardware. Subclasses will handle the conversion to the real value. @@ -268,7 +298,7 @@ def _get_value_raw(self, index=0) -> bytearray: """ try: # Calculate relative address in case of array element - address = self.address + index * self.get_width() + address = self.info.address + index * self.get_width() # Ask LNet to get the value from the target bytes_data = self.l_net.get_ram(address, self.get_width()) data_length = len(bytes_data) @@ -295,8 +325,8 @@ def _set_value_raw(self, bytes_data: bytes, index: int = 0) -> None: """ try: # Calculate relative address in case of array element - address = self.address + index * self.get_width() - self.l_net.put_ram(address, self.get_width(), bytes_data) + address = self.info.address + index * self.get_width() + self.l_net.put_ram(address, self.get_width(), bytearray(bytes_data)) except Exception as e: logging.error(f"Error setting value: {e}") @@ -353,14 +383,13 @@ def get_width(self) -> int: """ return 1 - def set_value(self, value: int): + def _set_value(self, value: int): """Set the value of the 8-bit signed integer. Args: value (int): The value to set. """ try: - self._check_value_range(value) int_value = int(value) bytes_data = int_value.to_bytes( length=1, byteorder="little", signed=True @@ -419,13 +448,12 @@ def get_width(self) -> int: """ return 1 - def set_value(self, value: int): + def _set_value(self, value: int): """Override: Set the value of the variable in the MCU memory. Checks if the value is within the allowed range and converts it to bytes representation. """ try: - self._check_value_range(value) int_value = int(value) bytes_data = int_value.to_bytes( length=1, byteorder="little", signed=False @@ -484,14 +512,13 @@ def get_width(self) -> int: """ return 2 - def set_value(self, value: int): + def _set_value(self, value: int): """Set the value of the 16-bit signed integer. Args: value (int): The value to set. """ try: - self._check_value_range(value) int_value = int(value) bytes_data = int_value.to_bytes( length=2, byteorder="little", signed=True @@ -550,14 +577,13 @@ def get_width(self) -> int: """ return 2 - def set_value(self, value: int): + def _set_value(self, value: int): """Set the value of the 16-bit unsigned integer. Args: value (int): The value to set. """ try: - self._check_value_range(value) int_value = int(value) bytes_data = int_value.to_bytes( length=2, byteorder="little", signed=False @@ -616,14 +642,13 @@ def get_width(self) -> int: """ return 4 - def set_value(self, value: int): + def _set_value(self, value: int): """Set the value of the 32-bit signed integer. Args: value (int): The value to set. """ try: - self._check_value_range(value) int_value = int(value) bytes_data = int_value.to_bytes( length=4, byteorder="little", signed=True @@ -682,14 +707,13 @@ def get_width(self) -> int: """ return 4 - def set_value(self, value: int): + def _set_value(self, value: int): """Set the value of the 32-bit unsigned integer. Args: value (int): The value to set. """ try: - self._check_value_range(value) int_value = int(value) bytes_data = int_value.to_bytes( length=4, byteorder="little", signed=False @@ -745,14 +769,13 @@ def get_width(self) -> int: """ return 8 - def set_value(self, value: int): + def _set_value(self, value: int): """Set the value of the 64-bit unsigned integer. Args: value (int): The value to set. """ try: - self._check_value_range(value) bytes_data = value.to_bytes( length=8, byteorder="little", signed=False ) # construct the bytes representation of the value @@ -807,14 +830,13 @@ def get_width(self) -> int: """ return 8 - def set_value(self, value: int): + def _set_value(self, value: int): """Set the value of the 64-bit signed integer. Args: value (int): The value to set. """ try: - self._check_value_range(value) bytes_data = value.to_bytes( length=8, byteorder="little", signed=True ) # construct the bytes representation of the value @@ -872,7 +894,7 @@ def get_width(self) -> int: """ return 4 - def set_value(self, value: float): + def _set_value(self, value: float): """Set the value of the 32-bit floating point. Args: @@ -902,33 +924,19 @@ def bytes_to_value(self, data: bytearray) -> Number: class VariableEnum(Variable): """Represents enum variable in the MCU data memory.""" - def __init__(self, l_net: LNet, address: int, array_size: int, name: str, - enum_list: dict[str, int]): - """Initialize the Variable object. But needs customised constructor due to enum list initialisation. - - Args: - l_net (LNet): LNet protocol that handles the communication with the target device. - address (int): Address of the variable in the MCU memory. - array_size (int): The number of elements in the array, 0 in case of a plain variable. - name (str, optional): Name of the variable. Defaults to None. - enum_list (dict[str, int]): The enumeration list with the values. - """ - super().__init__(l_net, address, array_size, name) - self.enum_list = enum_list - def _get_min_max_values(self) -> tuple[Number, Number]: """Get the minimum and maximum values for the 16-bit enum. Returns: tuple[Number, Number]: The minimum and maximum values. """ - return min(self.enum_list.values()), max(self.enum_list.values()) + return min(self.info.valid_values.values()), max(self.info.valid_values.values()) def is_integer(self) -> bool: """Check if the variable is an integer. Returns: - bool: Enumeration is stricktly an integer. + bool: Enumeration is strictly an integer. """ return True @@ -938,7 +946,7 @@ def is_signed(self) -> bool: Returns: bool: Depending on the enum values. """ - if min(self.enum_list.values()) < 0: + if min(self.info.valid_values.values()) < 0: return True else: return False @@ -953,14 +961,13 @@ def get_width(self) -> int: return self.l_net.device_info.uc_width - def set_value(self, value: int): + def _set_value(self, value: int): """Set the value of the 16-bit enum. Args: value (int): The value to set. """ try: - self._check_value_range(value) #TODO might be different than super class int_value = int(value) bytes_data = int_value.to_bytes( length=self.l_net.device_info.uc_width, byteorder="little", signed=self.is_signed() @@ -986,4 +993,4 @@ def get_enumerator_list(self) -> Dict[str, int]: Returns: Dict[str, int]: A dictionary of valid values for the enum variable. """ - return self.enum_list \ No newline at end of file + return self.info.valid_values \ No newline at end of file diff --git a/pyx2cscope/variable/variable_factory.py b/pyx2cscope/variable/variable_factory.py index 5f4ee28..2e118c8 100644 --- a/pyx2cscope/variable/variable_factory.py +++ b/pyx2cscope/variable/variable_factory.py @@ -124,7 +124,7 @@ def export_variables(self, filename: str = None, ext: FileType = FileType.YAML, export_dict = {} if items: for item in items: - variable_name = item.name if isinstance(item, Variable) else item + variable_name = item.info.name if isinstance(item, Variable) else item export_dict[variable_name] = self.parser.variable_map.get(variable_name) else: export_dict = self.parser.variable_map @@ -142,8 +142,6 @@ def import_variables(self, filename: str): """Import and load variables registered on the file. Currently supported files are Elf (.elf), Pickle (.pkl), and Yaml (.yml). - The flush parameter defaults to true and clears all previous loaded variables. This flag is - intended to be used when adding single variables to the parser. Args: filename (str): The name of the file and its path. @@ -188,6 +186,9 @@ def get_variable(self, name: str) -> Variable | None: """ try: variable_info = self.parser.get_var_info(name) + if variable_info is None: + logging.error(f"Variable '{name}' not found!") + return None return self.get_variable_raw(variable_info) except Exception as e: logging.error(f"Error while getting variable '{name}' : {str(e)}") @@ -236,15 +237,10 @@ def get_variable_raw(self, var_info: VariableInfo) -> Variable: } try: - var_type: str = var_info.type.lower().replace("_", "") - params = [self.l_net, var_info.address, var_info.array_size, var_info.name] - if "enum" in var_type: - var_type = "enum" - params.append(var_info.valid_values) - - return type_factory[var_type](*params) + var_type = "enum" if "enum" in var_type else var_type + return type_factory[var_type](self.l_net, var_info) except IndexError: raise ValueError( - f"Type {var_type} not found. Cannot select the right variable representation." + f"Type {var_info.type} not found. Cannot select the right variable representation." ) diff --git a/requirements.txt b/requirements.txt index 1a84813..911fa15 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/tests/test_parser.py b/tests/test_parser.py index 60c7af5..735908d 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -64,8 +64,8 @@ def test_union_variable_16(self, mocker): assert variable_lo is not None, "variable name not found" assert variable_hi is not None, "variable name not found" assert variable_union is not None, "variable name not found" - assert variable_lo.address == variable_union.address, "variable union should have the same address as segment" - assert variable_hi.address == variable_union.address + 2, "variable high should have address + 2" + assert variable_lo.info.address == variable_union.info.address, "variable union should have the same address as segment" + assert variable_hi.info.address == variable_union.info.address + 2, "variable high should have address + 2" def test_variable_dspic33ak(self, mocker, array_size_test=4900, address=22122): """Given a valid dspic33ak elf file, check if an array variable is read correctly.""" @@ -75,7 +75,7 @@ def test_variable_dspic33ak(self, mocker, array_size_test=4900, address=22122): variable = x2c_scope.get_variable("measureInputs.current.Ia") assert variable is not None, "variable name not found" assert variable.is_array() == False, "variable should be an array" - assert variable.address == address, "variable has wrong address, check offset calculation" + assert variable.info.address == address, "variable has wrong address, check offset calculation" # test array variable_array = x2c_scope.get_variable("X2C_BUFFER") assert variable_array is not None, "variable name not found" @@ -99,12 +99,12 @@ def test_variable_enum_32(self, mocker, size6=6, size3=3): variable = x2c_scope.get_variable("nextGlobalState") assert variable is not None, "variable name not found" assert variable.is_array() == False, "variable should not be an array" - assert len(variable.enum_list) == size6, "enum size should be 6" + assert len(variable.info.valid_values) == size6, "enum size should be 6" # test nested enum inside a structure variable = x2c_scope.get_variable("mcFoc_State_mds.FocState") assert variable is not None, "variable name not found" assert variable.is_array() == False, "variable should not be an array" - assert len(variable.enum_list) == size3, "enum size should be 3" + assert len(variable.info.valid_values) == size3, "enum size should be 3" def test_variable_enum_16(self, mocker, size6=6): """Given a valid dspic33ck elf file, check if an enum variable is read correctly.""" @@ -114,7 +114,7 @@ def test_variable_enum_16(self, mocker, size6=6): variable = x2c_scope.get_variable("motor.apiData.motorStatus") assert variable is not None, "variable name not found" assert variable.is_array() == False, "variable should not be an array" - assert len(variable.enum_list) == size6, "enum size should be 3" + assert len(variable.info.valid_values) == size6, "enum size should be 3" def test_variable_export_import(self, mocker): """Given a valid 32 bit elf file, check if export and import functions for variables are working.""" @@ -138,18 +138,18 @@ def test_variable_export_import(self, mocker): x2c_reloaded = X2CScope(port="COM14") x2c_reloaded.import_variables(filename="my_variables.yml") variable_reloaded = x2c_scope.get_variable("mcFocI_ModuleData_gds.dOutput.elecSpeed") - assert variable.name == variable_reloaded.name, "variables don't have the same name" - assert variable.address == variable_reloaded.address, "variables don't have the same address" - assert variable.array_size == variable_reloaded.array_size, "variables don't have the same array size" + assert variable.info.name == variable_reloaded.info.name, "variables don't have the same name" + assert variable.info.address == variable_reloaded.info.address, "variables don't have the same address" + assert variable.info.array_size == variable_reloaded.info.array_size, "variables don't have the same array size" # load generated pickle file with single variable x2c_reloaded = X2CScope(port="COM14") x2c_reloaded.import_variables(filename="my_single_variable.pkl") variable_reloaded = x2c_scope.get_variable("mcFocI_ModuleData_gds.dOutput.elecSpeed") assert len(x2c_reloaded.variable_factory.parser.variable_map) == 1, "import loaded more than 1 variable" - assert variable.name == variable_reloaded.name, "variables don't have the same name" - assert variable.address == variable_reloaded.address, "variables don't have the same address" - assert variable.array_size == variable_reloaded.array_size, "variables don't have the same array size" + assert variable.info.name == variable_reloaded.info.name, "variables don't have the same name" + assert variable.info.address == variable_reloaded.info.address, "variables don't have the same address" + assert variable.info.array_size == variable_reloaded.info.array_size, "variables don't have the same array size" # house keeping -> delete generated files os.remove("my_variables.yml")