# --------------------------------------------------------------------------- #
from pymodbus import __version__ as pymodbus_version
#from pymodbus.exceptions import ModbusException
from pymodbus.server import ServerStop, StartSerialServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.framer import ModbusRtuFramer

import threading
import time

class Ito_modbus():
# --------------------------------------------------------------------------- #
# Constructor
# --------------------------------------------------------------------------- #
    def __init__(self):
        self.slave_id = 0x02
        self.store = {0x02: ModbusSlaveContext(
                    hr=ModbusSequentialDataBlock(0, [0]*32),)}
        self.context = ModbusServerContext(slaves=self.store, single=False)
        # Init modbus server
        self.init_time = time.perf_counter()
        self.modbus_task = threading.Thread(target=self.modbus_server_start)
        self.modbus_task.daemon = True
        self.modbus_task.start()

# --------------------------------------------------------------------------- #
# Methods
# --------------------------------------------------------------------------- #
    
    ## Función para comunicar desde Raspberry a PLC.
    # Una forma de entender el Modbus es que entre el PLC y la Raspy el USB almacena informacion en un Bus de data.
    # Bus que puede ser leido y escrito por ambas partes.
    def modbus_context_write(self, register, address, data):
        #self.context[self.slave_id].setValues(register, address, data)
        print('----------------------------------------------')
        print(self.context[self.slave_id].getValues(3, 0, 16))
        self.context[self.slave_id].setValues(3, 0, data)
        print(self.context[self.slave_id].getValues(3, 0, 16))
        #Register hace referencia los "registers" declarados en el ModbusSlaveContext(hr,ir,etc)
        #El register n°3 apunta a los holding  registers(hr)

    ## Función para comunicación desde PLC a Raspberry
    def modbus_context_read(self, register, address, count):
        # Escribe desde el poll modbus hacia python
        # Comunicacion desde PLC hacia RPi
        return self.context[self.slave_id].getValues(register, address, count)

    ## Función que reinicia la memoria del bus de comunicación
    def modbus_reset_data(self):
        self.store[0x02].reset()
        return True

    ## Función que inicia el puente de comunicación mediante el conversor RS-485
    def modbus_server_start(self):

        # Initialize the server information
        identity = ModbusDeviceIdentification()
        identity.VendorName = 'Pymodbus'
        identity.ProductCode = 'PM'
        identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
        identity.ProductName = 'Pymodbus Server'
        identity.ModelName = 'Pymodbus Server'
        identity.MajorMinorRevision = pymodbus_version
        try:
            # Run the server
            StartSerialServer(
                context=self.context,
                identity=identity, 
                framer=ModbusRtuFramer, #Por defecto es en RTU
                port='/dev/ttyUSB1',
                stopbits=1,
                bytesize=8,
                parity='N',  
                baudrate=9600,
                timeout=1
            )
        except:
            print("Modbus: No se detecta conexion del USB.\n")
            pass

"""
Despues de tanto tiempo sufriendo con el PLC y terminando esto con el archivo "adios_plc".
Me di cuenta que el PLC es más un maestro que
envia cartas de "no responder devuelta" a los dispositivos esclavos.
Lo que quiere decir que si bien, cada esclavo tiene un ID para mandarles
ordenes (char, bit o int) este nunca escucha a los esclavos.
Lo que a su vez me dio a entender, el maestro no tiene ID, por ende no hay forma de mandarle
una señal al maestro, si no, que este tiene que tener el interes de saber
de los esclavos.
Dicho de otro modo. En el PLC hay que configurar una lectura de datos del
cache de los esclavos. De otro modo no hay forma de hacer saber algo al
maestro.
La cosa es que el PLC mugroso funciono como el **** durante todo el tiempo
que intente trabajar con el. Perdi semanas de trabajo valioso por intentar
entender el como hacerlo trabajar. Incluso gente profesional en el area que consultamos
no entendia como poder hacer funcionar el proceso de comunicación y no los
culpo. La unica funcion en el PLC que permite la comunicacion RS485(La cosa de modbus)
que a pesar de tener un manual, era la basura menos descriptiva posible.
Pedia poner un ID, locacion de la cual leer y el metodo.
Pero anda tu a saber como y cuales hay que indicar para hacerla funcionar.
Cuando en el propio manual. QUE SOLO HABIA 1 EN TODO INTERNET y unos videos
que no ayudaban en nada mas que a perderse más y darse vueltas en lo mismo.
Agradezco a Modbus por hacer todo tan facil con un simple USB y algo de Python.
"""