""" Manejador del protocolo ADAM/Maselli Contiene las funciones para formatear mensajes, calcular checksums y parsear respuestas """ class ProtocolHandler: @staticmethod def calculate_checksum(message_part): """Calcula el checksum de un mensaje ADAM""" s = sum(ord(c) for c in message_part) checksum_byte = s % 256 return f"{checksum_byte:02X}" @staticmethod def format_ma_value(ma_val): """Formatea un valor mA al formato ADAM: XX.XXX (6 caracteres)""" return f"{ma_val:06.3f}" @staticmethod def scale_to_ma(brix_value, min_brix_map, max_brix_map): """Convierte valor Brix a mA usando el mapeo configurado""" if max_brix_map == min_brix_map: return 4.0 percentage = (brix_value - min_brix_map) / (max_brix_map - min_brix_map) percentage = max(0.0, min(1.0, percentage)) ma_value = 4.0 + percentage * 16.0 return ma_value @staticmethod def ma_to_brix(ma_value, min_brix_map, max_brix_map): """Convierte valor mA a Brix usando el mapeo configurado""" try: if ma_value <= 4.0: return min_brix_map elif ma_value >= 20.0: return max_brix_map else: # Interpolación lineal percentage = (ma_value - 4.0) / 16.0 return min_brix_map + percentage * (max_brix_map - min_brix_map) except: return 0.0 @staticmethod def create_adam_message(adam_address, brix_value, min_brix_map, max_brix_map): """Crea un mensaje completo ADAM a partir de un valor Brix""" ma_val = ProtocolHandler.scale_to_ma(brix_value, min_brix_map, max_brix_map) ma_str = ProtocolHandler.format_ma_value(ma_val) message_part = f"#{adam_address}{ma_str}" checksum = ProtocolHandler.calculate_checksum(message_part) full_message = f"{message_part}{checksum}\r" return full_message, ma_val @staticmethod def parse_adam_message(data): """ Parsea un mensaje del protocolo ADAM y retorna el valor en mA Formato esperado: #AA[valor_mA][checksum]\r Donde: - # : Carácter inicial (opcional en algunas respuestas) - AA : Dirección del dispositivo (2 caracteres) - valor_mA : Valor en mA (6 caracteres, formato XX.XXX) - checksum : Suma de verificación (2 caracteres hex) - \r : Carácter de fin (opcional) Retorna: dict con 'address' y 'ma', o None si no es válido """ try: # Formato esperado: #AA[valor_mA][checksum]\r # Pero también manejar respuestas sin # inicial o sin \r final data = data.strip() # Si empieza con #, es un mensaje estándar if data.startswith('#'): data = data[1:] # Remover # # Si termina con \r, removerlo if data.endswith('\r'): data = data[:-1] # Verificar longitud mínima if len(data) < 8: # 2 addr + 6 valor mínimo return None address = data[:2] value_str = data[2:8] # 6 caracteres para el valor (XX.XXX) # Verificar si hay checksum checksum_valid = True if len(data) >= 10: checksum = data[8:10] # 2 caracteres para checksum # Verificar checksum message_part = f"#{address}{value_str}" calculated_checksum = ProtocolHandler.calculate_checksum(message_part) if checksum != calculated_checksum: checksum_valid = False # Convertir valor a float try: ma_value = float(value_str) return { 'address': address, 'ma': ma_value, 'checksum_valid': checksum_valid } except ValueError: return None except Exception: return None @staticmethod def format_for_display(message): """Formatea un mensaje para mostrar en el log (reemplaza caracteres no imprimibles)""" return message.replace('\r', '').replace('\n', '').replace('\t', '')