122 lines
4.4 KiB
Python
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>')
|