import struct

def convert_registers_to_readable(registers, reg_info):
    """
    Convert raw Modbus register values into a readable format based on register metadata.

    :param registers: List of raw Modbus register values.
    :param reg_info: Dictionary containing register metadata (type, gain, etc.).
    :return: Interpreted value.
    """
    try:
        if reg_info['type'] == 'STR':
            # Convert registers to a string
            return ''.join(chr((reg >> 8) & 0xFF) + chr(reg & 0xFF) for reg in registers).rstrip('\x00')
        elif reg_info['type'] in ('U16', 'UINT16'):
            return registers[0] / reg_info['gain']
        elif reg_info['type'] in ('I16', 'INT16'):
            return struct.unpack('h', struct.pack('H', registers[0]))[0] / reg_info['gain']
        elif reg_info['type'] in ('U32', 'UINT32', 'I32', 'U64', 'I64', 'INT32'):
            value = 0
            for reg in registers:
                value = (value << 16) | reg
            if reg_info['type'] == 'I32':
                value = struct.unpack('i', struct.pack('I', value & 0xFFFFFFFF))[0]
            elif reg_info['type'] in ('U32', 'UINT32'):
                value = value & 0xFFFFFFFF
            elif reg_info['type'] == 'U64':
                value = struct.unpack('>Q', struct.pack('>4H', *registers))[0]
            elif reg_info['type'] == 'I64':
                value = struct.unpack('>q', struct.pack('>4H', *registers))[0]
            return value / reg_info['gain']
        elif reg_info['type'] == 'Bitfield16':
            return registers[0]
        elif reg_info['type'] == 'Bitfield32':
            return (registers[0] << 16) | registers[1]
        elif reg_info['type'] == 'MLD':
            return registers
        else:
            raise ValueError(f"Unknown data type: {reg_info['type']}")
    except Exception as e:
        raise ValueError(f"Error processing registers at address {reg_info.get('address', 'unknown')}: {e}")
