import os from typing import Union, Tuple from UniTAP.libs.lib_tsi.tsi import * from UniTAP.libs.lib_tsi.tsi_io import PortIO class DPCDRegion: """ Class `DPCDRegion` describe a byte range of DPCD registers. Allows saving DPCD data to dpd file format `save_to_dpd`, hex files format `save_to_hex`, dsc file format `save_to_csv`. Arguments: base - start DPCD address of byte range. data - DPCD data of byte range. """ def __init__(self, base: int, data: Union[bytearray, int]): if not isinstance(data, bytearray): self.data = bytearray() self.data.append(data) else: self.data = data self.base = base @property def size(self) -> int: """ Returns length fo data. Returns: result of int object """ return len(self.data) def save_to_dpd(self, path: str): """ Save DPCD data to 'dpd' file format. Args: path (str) - full path to file """ if not path.lower().endswith('.dpd'): path += '.DPD' with open(path, 'bw') as file: dpd_mark = 1 file.write(dpd_mark.to_bytes(4, 'little')) file.write(self.base.to_bytes(4, 'little')) file.write(len(self.data).to_bytes(4, 'little')) file.write(self.data) def save_to_hex(self, path: str): """ Save DPCD data to 'hex' file format. Args: path (str) - full path to file """ if not path.lower().endswith('.hex'): path += '.HEX' with open(path, 'w') as file: file.write(f"\nRange 1. Start {self.base:#0{8}x}, Length {len(self.data):#0{8}x}\n") file.write( '\n'.join( ' '.join( f'{value:#0{2}x}' for value in self.data[i:i + 16]) for i in range(0, len(self.data), 16) ) ) def save_to_csv(self, path: str): """ Save DPCD data to 'csv' file format. Args: path (str) - full path to file """ if not path.lower().endswith('.csv'): path += '.csv' with open(path, 'w') as file: file.write("Address (A), Data A+0, Data A+1, Data A+2, Data A+3, Data A+4, Data A+5," " Data A+6, Data A+7\n") file.write( '\n'.join( f'{self.base + 8 * i:#0{8}x},' + ','.join(f'{value:#0{2}x}' for value in self.data[i:i + 8]) for i in range(0, len(self.data), 8) ) ) class DPCDRegisters: """ Class `DPCDRegisters` allows working with DPCD registers: writing `write` DPCD data to device, reading `read` DPCD data from device, loading `load_from_file` DPCD data from file. """ def __init__(self, port_io: PortIO, base_ci: int, data_ci: int): self.__io = port_io self.__base_ci = base_ci self.__data_ci = data_ci def write(self, base: int, data: Union[bytearray, int, list]) -> int: """ Write transferred DPCD data to device from base address. Args: base (int) - start (base) address. data (Union[bytearray, int]) - DPCD data. Returns: result of operation """ if isinstance(data, int): data = bytearray(data.to_bytes(1, "big")) elif isinstance(data, list): data = bytearray(data) result = self.__io.set(self.__base_ci, base) if result < TSI_SUCCESS: return result result = self.__io.set(self.__data_ci, data, c_ubyte, len(data)) return result def read(self, base: int, count: int) -> DPCDRegion: """ Read DPCD data from base address in a certain quantity. Args: base (int) - start (base) address. count (int) - quantity of DPCD bytes. Returns: object of `DPCDRegion` """ self.__io.set(self.__base_ci, base) result = self.__io.get(self.__data_ci, c_ubyte, count) return DPCDRegion(base, result[1]) @staticmethod def load_from_file(path: str) -> Tuple[DPCDRegion, DPCDRegion]: """ Read DPCD data from file. Supported formats: - DPD. Args: path (str) - full path to file. Returns: object of tuple with two `DPCDRegion` objects """ if not os.path.exists(path): raise FileNotFoundError(f"DPCD load_from_file function can't find file: {path}") if '.DPD' not in os.path.splitext(path): raise TypeError("Wrong file extension. It should be .DPD") with open(path, 'rb') as file: file.read(4) base1 = int.from_bytes(file.read(4)[::-1], 'little') size1 = int.from_bytes(file.read(4)[::-1], 'little') data1 = bytearray(file.read(size1)) region1 = DPCDRegion(base1, data1) base2 = int.from_bytes(file.read(4)[::-1], 'little') size2 = int.from_bytes(file.read(4)[::-1], 'little') data2 = bytearray(file.read(size2)) region2 = DPCDRegion(base2, data2) return region1, region2