MaselliSimulatorApp/protocol_handler.py

122 lines
4.4 KiB
Python

"""
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', '<CR>').replace('\n', '<LF>').replace('\t', '<TAB>')