Пример #1
0
def display_remote_io(ip_address):
    '''
    display remote I/O information at the given IP address
    '''
    try:
        client = ModbusTcpClient(ip_address)
        client.write_coil(HW_CY_006, False)

        ip_holding_regs = client.read_holding_registers(HR_CI_006_CV, 6)

        client.write_registers(HW_CI_006_PV, ip_holding_regs.registers)
        cur_ip = format_modbus_ip_address(ip_holding_regs.registers[0],
                                          ip_holding_regs.registers[1])
        cur_gateway = format_modbus_ip_address(ip_holding_regs.registers[2],
                                               ip_holding_regs.registers[3])
        cur_subnet = format_modbus_ip_address(ip_holding_regs.registers[4],
                                              ip_holding_regs.registers[5])

        ip_holding_regs = client.read_holding_registers(HR_CI_009_CV, 4)
        cur_mac = format_mac(ip_holding_regs.registers)
        ip_holding_regs = client.read_holding_registers(HR_KI_003, 2)
        cur_version = format_version(ip_holding_regs.registers[0])

        print("{0} - {1}, version:{2}.{3}.{4} ".format(
            ip_address, device_type_name(ip_holding_regs.registers[1]),
            cur_version[1], cur_version[2], cur_version[3]),
              end='')
        print("gateway:{0}, subnet:{1} mac:{2}".format(cur_gateway, cur_subnet,
                                                       cur_mac))
        client.close()
    except ConnectionException:
        print("{0} - unavailable".format(ip_address))
Пример #2
0
def get_1wire_config(ip_address):
    try:
        client = ModbusTcpClient(ip_address)

        wire_config = []
        for i in range(MAX_1WIRE):
            holding_regs = client.read_holding_registers(
                HR_TI_001_ID_H + i * 4, 4)
            cur_uuid = format_uuid(holding_regs.registers)
            holding_regs = client.read_holding_registers(HR_TI_001 + i, 1)
            wire_config.append(
                [i, cur_uuid, i,
                 to_signed(holding_regs.registers[0]) / 10.0])

        client.close()
        return wire_config
    except ConnectionException:
        print("{0} - unavailable".format(cuip))
Пример #3
0
        def __init__(self):
            if (self.logger == None):
                self.createLogger('generalPLCgateway')
            if len(self.plc_host) > 1:
                plc_client = ModbusTcpClient(self.plc_host)
            try:
                if len(self.plc_host) > 1:
                    result = plc_client.read_holding_registers(0, 5, unit=0X01)
                    if len(result.registers) == 5:
                        print("Gateway connected succesfully to PLC : " +
                              str(result))
                #self.update_conected_plc(self.plc_host)

                else:
                    print("connection is terminated ")
                    self.logger.error("connection is terminated ")
            except Exception as e:
                print("error sending data to PLC " + str(e))
                self.logger.error("error sending data to PLC " + str(e))
Пример #4
0
class Modbus:
    def __init__(self, uri='localhost', port=502):
        self.host = socket.gethostbyname(uri)
        self.client = ModbusClient(self.host, port=port)
        self.client.connect()
        # // TODO implement a fallback
        # if not self.client:
        #     raise RuntimeError('Error establishing connection with uri {}'.format(uri))

    def _read(self, response, typ='Float32'):
        if typ == 'Float32':
            raw = struct.pack('>HH', response.get_register(1), response.get_register(0))
            return struct.unpack('>f', raw)[0]

    def read(self, register, n, unit=1, typ='Float32'):
        response = self.client.read_holding_registers(register, n, unit=unit)
        return self._read(response, typ)

    def readN(self, registers, n, unit=1):
        # // TODO implement reading multiple registers
        pass
Пример #5
0
class HeliosTCP(SmartPlugin):

    PLUGIN_VERSION = "1.0.2"
    MODBUS_SLAVE = 180
    PORT = 502
    START_REGISTER = 1

    _items = {}

    def __init__(self, sh):
        from bin.smarthome import VERSION
        if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5':
            self.logger = logging.getLogger(__name__)

        self._helios_ip = self.get_parameter_value('helios_ip')
        self._client = ModbusTcpClient(self._helios_ip)
        self.alive = False
        self._is_connected = False
        self._update_cycle = self.get_parameter_value('update_cycle')

    def run(self):
        """
        Run method for the plugin
        """
        self.logger.debug("Run method called")
        self._is_connected = self._client.connect()
        if not self._is_connected:
            self.logger.error(
                "Helios TCP: Failed to connect to Modbus Server at {0}".format(
                    self._helios_ip))
        self.scheduler_add('Helios TCP',
                           self._update_values,
                           cycle=self._update_cycle)
        self.alive = True

    def stop(self):
        """
        Stop method for the plugin
        """
        self.logger.debug("Stop method called")
        self.scheduler_remove('Helios TCP')
        self._client.close()
        self.alive = False

    def parse_item(self, item):
        if 'helios_tcp' in item.conf:
            varname = item.conf['helios_tcp']
            if varname in VARLIST.keys():
                self._items[varname] = item
                return self.update_item
            else:
                self.logger.warning(
                    "Helios TCP: Ignoring unknown variable '{0}'".format(
                        varname))

    def _update_values(self):
        for item in self._items:
            self._read_value(self._items[item])

    @staticmethod
    def _string_to_registers(instr: str):
        l = bytearray(instr, 'ascii')
        return [k[0] * 256 + k[1] for k in zip(l[::2], l[1::2])] + [0]

    def _read_value(self, item):
        try:
            var = item.conf['helios_tcp']
        except ValueError:
            return

        try:
            varprop = VARLIST[var]
        except KeyError:
            self.logger.error(
                "Helios TCP: Failed to find variable '{0}'".format(var))
            return

        # At first we write the variable name to read into the input registers:
        payload = self._string_to_registers(varprop['var'])
        request = self._client.write_registers(self.START_REGISTER,
                                               payload,
                                               unit=self.MODBUS_SLAVE)
        if request is None:
            self.logger.warning(
                "Helios TCP: Failed to send read request for variable '{0}'".
                format(var))
            return

        # Now we may read the holding registers:
        response = self._client.read_holding_registers(self.START_REGISTER,
                                                       varprop['length'],
                                                       unit=self.MODBUS_SLAVE)
        if response is None:
            self.logger.warning(
                "Helios TCP: Failed to send read response for variable '{0}'".
                format(var))
            return

        # Now we may dedocde the result
        # Note that we immediatly strip the varname from the result.
        result = response.encode().decode('ascii')[8:]
        result = list(result)

        # Remove trailing zeros:
        while result[-1] == '\x00':
            result.pop()

        result = ''.join(result)

        # Finally we may cast the result and return the obtained value:
        try:
            item(varprop["type"](result), self.get_shortname())
        except ValueError:
            self.logger.warning(
                "Helios TCP: Could not assign {0} to item {1}".format(
                    varprop["type"](result), item.id()))
            return

    def update_item(self, item, caller=None, source=None, dest=None):
        """
        Item has been updated

        This method is called, if the value of an item has been updated by SmartHomeNG.
        It should write the changed value out to the device (hardware/interface) that
        is managed by this plugin.

        :param item: item to be updated towards the plugin
        :param caller: if given it represents the callers name
        :param source: if given it represents the source
        :param dest: if given it represents the dest
        """
        if self.alive and caller != self.get_shortname():
            try:
                var = item.conf['helios_tcp']
            except ValueError:
                return

            newval = item()

            try:
                varprop = VARLIST[var]
            except KeyError:
                self.logger.error(
                    "Helios TCP: Failed to find variable '{0}'".format(var))
                return

            if not varprop["write"]:
                return

            if type(newval) != varprop["type"]:
                self.logger.error(
                    "Helios TCP: Type mismatch for variable '{0}'".format(var))
                return

            if newval < varprop["min"] or newval > varprop["max"]:
                self.logger.error(
                    "Helios TCP: Variable '{0}' out of bounds. The allowed range is [{1}, {2}]"
                    .format(var, varprop["min"], varprop["max"]))
                return

            if varprop["type"] == bool:
                payload_string = "{0}={1}".format(varprop["var"], int(newval))
            elif varprop["type"] == int:
                payload_string = "{0}={1}".format(varprop["var"], int(newval))
            elif varprop["type"] == float:
                payload_string = "{0}={1:.1f}".format(varprop["var"], newval)
            else:
                self.logger.error(
                    "Helios TCP: Type {0} of varible '{1}' not known".format(
                        varprop["type"], var))
                return

            payload = self._string_to_registers(payload_string)
            request = self._client.write_registers(self.START_REGISTER,
                                                   payload,
                                                   unit=self.MODBUS_SLAVE)
            if request is None:
                self.logger.warning(
                    "Helios TCP: Failed to send write request for variable '{0}'"
                    .format(var))
                return
Пример #6
0
class MBPLC(PLC):
    """PLC con comunicación Modbus.

    Args:
    address (str): Dirección IP (o nombre) del controlador.
    port (int): Puerto de conexión. Típicamente el 502.
    unit (int): Número de esclavo.
    method (str): Método de conexión (RTU/ASCII)
    retries (int): Reintentos de lectura/escritura.
    client (ModbusClient): Cliente modbus.
    pollingtime (float): Segundos entre escaneos.

    Attributes:
    coil (Memory): Memoria de bobinas.
    input (Memory): Memoria de entradas digitales.
    holding (Memory): Memoria de registros de retención.
    register  (Memory): Memoria de registros de entrada.
    client (ModbusClient): Cliente Modbus.
    thread (Thread): Hebra de escaneo.
    
    """

    class COIL:
        """Tipo de memoria COIL."""
        pass
    class INPUT:
        """Tipo de memoria INPUT."""
        pass
    class HOLDING:
        """Tipo de memoria HOLDING."""
        pass
    class REGISTER:
        """Tipo de memoria REGISTER."""
        pass


    class Memory(PLC.Memory):
        ''' Representacióne un área de memoria.

        Args:
        plc (PLC): Controlador al que pertenece.
        memorytype (type): Tipo de memoria (COIL, INPUT, HOLDING, REGISTER).

        Attributes:
        minindex (int): Dirección más baja de la memoria leída.
        maxindex (int): Dirección más alta de la memoria leída.

        '''

        class Tag(PLC.Memory.Tag):
            ''' Variable de controlador Modbus.

            Args:
            memory (Memory): Memoria a la que pertenece.
            key (str): Nombre.
            description (str): Descripción.
            address: Dirección (None si no esta asociada).
            
            Attributes:
            value: Valor.
            subscriptor (Subcriptor[]): Objetos suscritos a los cambios.
            
            '''

            def __init__(self, memory:PLC.Memory, key:str, description:str="", address=None):
                if type(address)==str:
                    address=int(address)
                super().__init__(memory,key,description,address)
                if memory.minindex is None or memory.minindex>address:
                    memory.minindex=address
                if memory.maxindex is None or memory.maxindex<address:
                    memory.maxindex=address
                
            def set(self, value):
                ''' Modifica el valor de una variable.

                Args:
                value: Nuevo valor de la variable.

                '''

                plc=self.memory.plc
                if plc.connected:
                    try:
                        if self.memory.memorytype==MBPLC.COIL:
                            if isinstance(value,str):
                                if value.upper()=="TRUE" or value=="1":
                                    value=True
                                elif value.upper()=="FALSE" or value=="0":
                                    value=False
                            rw = plc.client.write_coil(self.address,value,unit=plc.unit)
                        if self.memory.memorytype==MBPLC.HOLDING:
                            if isinstance(value,str):
                                value=int(value)
                            rw = plc.client.write_register(self.address,value,unit=plc.unit)
                        self.update(value)
                    except Exception as e:
                        self.memory.plc.connected=False
                        printexception(e,"Error writing to PLC")
                        

                
        def __init__(self, plc:PLC, memorytype:type):
            self.memorytype=memorytype
            self.minindex=None
            self.maxindex=None
            super().__init__(plc)
    

    def __init__(self, address:str, port:int=502, unit:int=1, method:str="rtu", retries:int=3, pollingtime:float=1.0):
        super().__init__()
        self.coil=self.create("coil",MBPLC.COIL)
        self.input=self.create("input",MBPLC.INPUT)
        self.holding=self.create("holding",MBPLC.HOLDING)
        self.register=self.create("register",MBPLC.REGISTER)
        self.address=address
        self.unit=unit
        self.port=port
        self.method=method
        self.retries=retries
        self.pollingtime=pollingtime
        self.client = ModbusClient(address, port, method=method, retries=retries)
        self.thread=Thread(target=self.__Polling, args=())

    def create(self,memory_key, memorytype:type):
        ''' Crea una memoria en el controlador.

        Args:
        memory_key (str): Nombre o identificador de la memoria.
        memorytype (type): Tipo de memoria (COIL, INPUT, HOLDING, REGISTER).

        Returns (Memory):
        Memoria que se ha creado.
        
        ''' 
        return self.set(memory_key, self.Memory(self, memorytype))

    def connect(self):
        ''' Conexión con el controlador

        '''
        self.thread.start()

    def disconnect(self):
        ''' Termina la conexión con el controlador.

        ''' 
        self.connected=false
        self.client.close()

    def read(self):
        ''' Lectura de un área de memoria del controlador real.

        Si falla la lectura (tras el número de reintentos)
        se actualiza el estado de conexión a desconectado.

        '''
        try:
            if not self.coil.minindex is None:
                rr = self.client.read_coils(self.coil.minindex, self.coil.maxindex-self.coil.minindex+1,unit=self.unit)
                for i in range(self.coil.minindex,self.coil.maxindex+1):
                    if i in self.coil.tagbyaddress:
                        self.coil.tagbyaddress[i].update(rr.bits[i-self.coil.minindex])
            if not self.input.minindex is None:                
                rr = self.client.read_discrete_inputs(self.input.minindex, self.input.maxindex-self.input.minindex+1,unit=self.unit)
                for i in range(self.input.minindex,self.input.maxindex+1):
                    if i in self.input.tagbyaddress:
                        self.input.tagbyaddress[i].update(rr.bits[i-self.input.minindex])
            if not self.holding.minindex is None:                    
                rr = self.client.read_holding_registers(self.holding.minindex, self.holding.maxindex-self.holding.minindex+1,unit=self.unit)
                for i in range(self.holding.minindex,self.holding.maxindex+1):
                    if i in self.holding.tagbyaddress:
                        self.holding.tagbyaddress[i].update(rr.registers[i-self.holding.minindex])
            if not self.register.minindex is None:                    
                rr = self.client.read_input_registers(self.register.minindex, self.register.maxindex-self.register.minindex+1,unit=self.unit)
                for i in range(self.register.minindex,self.register.maxindex+1):
                    if i in self.register.tagbyaddress:
                        self.register.tagbyaddress[i].update(rr.registers[i-self.register.minindex])
        except Exception as e:
            self.coil.plc.disconnect()
            printexception(e,"Error reading from PLC")

    def __Polling(plc):
        ''' Lectura de todas las áreas de escaneo.

        Establece la conexión con los controladores,
        si no se ha hecho antes, o se ha perdido.

        '''
        while True:
            if plc.connected:
                plc.read()
                time.sleep(plc.pollingtime)
            else:
                print("Connecting to MBPLC "+plc.address+":"+str(plc.port)+"("+str(plc.unit)+")")
                plc.connected=plc.client.connect()
Пример #7
0
print("*"*30+"\n\n")

fd = open(FILENAME, 'w+')
csvrow = "timestamp;switch19;switch24;switch25;switch36;voltageb5;current19;current24;current25;current36\n"
fd.write(csvrow)
i=0
while i<10000:
  result  = client.read_coils(0, 4,  unit=1)
  ts = str(datetime.now())
  print("*"*10+" SWITCHES "+"*"*10)
  switches = [result.bits[0], result.bits[1], result.bits[2], result.bits[3]]
  print("Switch branch 19: {}\nSwitch branch 24: {}\nSwitch branch 25: {}\nSwitch branch 36: {}".format(switches[0], switches[1], switches[2], switches[3]))
  # Voltage and current
  address = 12
  count   = 20
  result  = client.read_holding_registers(address, count,  unit=1)
  decoder = BinaryPayloadDecoder.from_registers(result.registers, endian=Endian.Big)
  print("*"*10+" VOLTAGE "+"*"*10)
  voltage = decoder.decode_64bit_float()
  current = [decoder.decode_64bit_float(),
	decoder.decode_64bit_float(),
	decoder.decode_64bit_float(),
	decoder.decode_64bit_float()]
  address = 50
  count   = 4
  result  = client.read_holding_registers(address, count,  unit=1)
  decoder = BinaryPayloadDecoder.from_registers(result.registers, endian=Endian.Big)
  trafo = decoder.decode_64bit_float()
  print("Voltage bus b5: {}\n".format(voltage)+"*"*10+" CURRENTS "+"*"*10+"\nCurrent line 19: {}\nCurrent line 24: {}\nCurrent line 25: {}\nCurrent line 36: {}".format(current[0], current[1], current[2], current[3]))
  print("*"*10+" TRAFO "+"*"*10)
  print("Transformer tap position: {}".format(trafo))
Пример #8
0
#working ------- 1 seferde 124 tane cektik
from pymodbus3.client.sync import ModbusTcpClient
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

#defaults
ip="188.59.150.237"
start="0"
count="3"
unit_number="2"

#get necessary values


#modbus connection
client = ModbusTcpClient(ip)
connection = client.connect()

#read register
request = client.read_holding_registers(int(start),int(count),unit=int(unit_number))
result = request.registers
print (result) #result is the array of register values, can be accesses as result[i]



#ege
Пример #9
0
client.connect()

#---------------------------------------------------------------------------#
# example requests
#---------------------------------------------------------------------------#
# simply call the methods that you would like to use. An example session
# is displayed below along with some assert checks. Note that some modbus
# implementations differentiate holding/input discrete/coils and as such
# you will not be able to write to these, therefore the starting values
# are not known to these tests. Furthermore, some use the same memory
# blocks for the two sets, so a change to one is a change to the other.
# Keep both of these cases in mind when testing as the following will
# _only_ pass with the supplied async modbus server (script supplied).
#---------------------------------------------------------------------------#

while True:
    log.debug("Read a holding register")
    rr = client.read_holding_registers(0, 1, unit=4)
    print(rr.registers[0])
    f = open('../files/wls.in', 'w')
    new = str(rr.registers[0] / 6900)
    f.write(new)
    f.close()
    #time.sleep(1)
#assert(rr.registers[0] == 23334)       # test the expected value

#---------------------------------------------------------------------------#
# close the client
#---------------------------------------------------------------------------#
client.close()
Пример #10
0
def plugin_poll(handle):
    """ Poll readings from the modbus device and returns it in a JSON document as a Python dict.

    Available for poll mode only.

    Args:
        handle: handle returned by the plugin initialisation call
    Returns:
        returns a reading in a JSON document, as a Python dict, if it is available
        None - If no reading is available
    Raises:
    """

    try:
        global mbus_client
        if mbus_client is None:
            try:
                source_address = handle['address']['value']
                source_port = int(handle['port']['value'])
            except Exception as ex:
                e_msg = 'Failed to parse Modbus TCP address and / or port configuration.'
                _LOGGER.error('%s %s', e_msg, str(ex))
                raise ValueError(e_msg)
            try:
                mbus_client = ModbusTcpClient(host=source_address, port=source_port)
                mbus_client_connected = mbus_client.connect()
                if mbus_client_connected:
                    _LOGGER.info('Modbus TCP Client is connected. %s:%d', source_address, source_port)
                else:
                    raise RuntimeError("Modbus TCP Connection failed!")
            except:
                mbus_client = None
                _LOGGER.warn('Failed to connect! Modbus TCP host %s on port %d', source_address, source_port)
                return

        """ 
        read_coils(self, address, count=1, **kwargs)  
        read_discrete_inputs(self, address, count=1, **kwargs)
        read_holding_registers(self, address, count=1, **kwargs)
        read_input_registers(self, address, count=1, **kwargs)
        
            - address: The starting address to read from
            - count: The number of coils / discrete or registers to read
            - unit: The slave unit this request is targeting
            
            On TCP/IP, the MODBUS server is addressed using its IP address; therefore, the MODBUS Unit Identifier is useless. 

            Remark : The value 0 is also accepted to communicate directly to a MODBUS TCP device.
        """
        unit_id = UNIT
        modbus_map = json.loads(handle['map']['value'])

        readings = {}

        # Read coils
        coils_address_info = modbus_map['coils']
        if len(coils_address_info) > 0:
            for k, address in coils_address_info.items():
                coil_bit_values = mbus_client.read_coils(99 + int(address), 1, unit=unit_id)
                readings.update({k: coil_bit_values.bits[0]})

        # Discrete input
        discrete_input_info = modbus_map['inputs']
        if len(discrete_input_info) > 0:
            for k, address in discrete_input_info.items():
                read_discrete_inputs = mbus_client.read_discrete_inputs(99 + int(address), 1, unit=unit_id)
                readings.update({k:  read_discrete_inputs.bits[0]})

        # Holding registers
        holding_registers_info = modbus_map['registers']
        if len(holding_registers_info) > 0:
            for k, address in holding_registers_info.items():
                register_values = mbus_client.read_holding_registers(99 + int(address), 1, unit=unit_id)
                readings.update({k: register_values.registers[0]})

        # Read input registers
        input_registers_info = modbus_map['inputRegisters']
        if len(input_registers_info) > 0:
            for k, address in input_registers_info.items():
                read_input_reg = mbus_client.read_input_registers(99 + int(address), 1, unit=unit_id)
                readings.update({k: read_input_reg.registers[0] })

        wrapper = {
            'asset': handle['assetName']['value'],
            'timestamp': utils.local_timestamp(),
            'key': str(uuid.uuid4()),
            'readings': readings
        }

    except Exception as ex:
        _LOGGER.error('Failed to read data from modbus device. Got error %s', str(ex))
        raise ex
    else:
        return wrapper
Пример #11
0
        result = '-'
        if ri['type'] == 0:
            result = client.read_coils(ri['address'],
                                       ri['count'],
                                       unit=args.unit)
        if ri['type'] == 1:
            result = client.read_discrete_inputs(ri['address'],
                                                 ri['count'],
                                                 unit=args.unit)
        if ri['type'] == 3:
            result = client.read_input_registers(ri['address'],
                                                 ri['count'],
                                                 unit=args.unit)
        if ri['type'] == 4:
            result = client.read_holding_registers(ri['address'],
                                                   ri['count'],
                                                   unit=args.unit)

        if ri['type'] < 2 and result != '-':
            i = 0
            for r in result.bits:
                data[str(ri['type']) + '_' + str(ri['address'] + i)] = str(
                    int(r))
                i += 1

        if ri['type'] > 2 and result != '-':
            i = 0
            for r in result.registers:
                data[str(ri['type']) + '_' + str(ri['address'] + i)] = str(
                    int(r))
                i += 1
Пример #12
0
class COMx(MAQ20Module):
    """
    COM Module:
    Takes care of the communication backend and provides functions that read the COMx register map.
    """
    def __init__(self, ip_address, port):
        if ip_address is not None or port is not None:
            if MODBUS_BACKEND == PYMODBUS3:
                self._client = ModbusTcpClient(ip_address, port=port)
            if MODBUS_BACKEND == UMODBUS:
                self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self._sock.settimeout(2)  # seconds
                self._sock.connect((ip_address, port))
        super(COMx, self).__init__(com=self, registration_number=0)

    def read_register(self, address):
        """
        Low level register access.
        Performs a modbus read register request to the MAQ20
        :param address: requested address
        :return: int - [-32767, 32767]
        """
        return self.read_registers(address, 1)[0]

    def read_registers(self, address, number_of_registers):
        """
        Low level register access.
        Performs a modbus read registers request to the MAQ20
        :param address: starting address.
        :param number_of_registers: number of registers to be read in sequence.
        :return: list(int) [-32767, 32767]
        """
        if MODBUS_BACKEND == PYMODBUS3:
            result = self._client.read_holding_registers(
                address=address, count=number_of_registers)
            # converts the returned values to signed.
            try:
                return [
                    unsigned16_to_signed16(value) for value in result.registers
                ]
            except AttributeError:
                return None
        if MODBUS_BACKEND == UMODBUS:
            message = tcp.read_holding_registers(0, address,
                                                 number_of_registers)
            result = tcp.send_message(message, self._sock)
            return [unsigned16_to_signed16(value) for value in result]

    def write_register(self, address, value):
        """
        Low level register access.
        Performs a modbus write register request to the MAQ20
        :param address: starting address.
        :param value: int [-32767, 32767] or a str of size 1
        :return: modbus response.
        """
        if type(value) is str:
            value = ord(value[0])
        value = signed16_to_unsigned16(value)
        if MODBUS_BACKEND == PYMODBUS3:
            return self._client.write_register(address, value)
        if MODBUS_BACKEND == UMODBUS:
            message = tcp.write_single_register(slave_id=0,
                                                address=address,
                                                value=value)
            return tcp.send_message(message, self._sock)

    def write_registers(self, address, values=None):
        """
        Low level register access.
        Performs a modbus write registers request to the MAQ20
        :param address: starting address.
        :param values: list(int) [-32767, 32767] or a str
        :return: modbus response.
        """
        ints = []
        if type(values) is str:
            for c in values:
                ints.append(ord(c))
        else:
            ints = [signed16_to_unsigned16(x) for x in values]
        if MODBUS_BACKEND == PYMODBUS3:
            return self._client.write_registers(address, ints)
        if MODBUS_BACKEND == UMODBUS:
            message = tcp.write_multiple_registers(slave_id=0,
                                                   starting_address=address,
                                                   values=ints)
            return tcp.send_message(message, self._sock)

    def read_ip_address(self):
        return self._com.read_registers(50, 4)

    def write_ip_address(self, ip_address):
        if type(ip_address) is str:
            first = ip_address.partition('.')
            second = first[2].partition('.')
            third = second[2].partition('.')
            numbers = [first[0], second[0], third[0], third[2]]
            numbers = [int(number) for number in numbers]
            return self.write_registers(50, numbers)
        elif type(ip_address) is list:
            return self.write_registers(50, ip_address)
        return None

    def read_ethernet_subnet_mask(self):
        return self._com.read_registers(55, 4)

    def write_ethernet_subnet_mask(self, mask):
        if type(mask) is str:
            first = mask.partition('.')
            second = first[2].partition('.')
            third = second[2].partition('.')
            numbers = [first[0], second[0], third[0], third[2]]
            numbers = [int(number) for number in numbers]
            return self.write_registers(55, numbers)
        elif type(mask) is list:
            return self.write_registers(55, mask)
        return None

    def read_serial_port_baud(self) -> int:
        return {
            0: 1200,
            1: 2400,
            2: 4800,
            3: 9600,
            4: 19200,
            5: 38400,
            6: 57600,
            7: 115200,
            8: 230400,
            9: 460800,
            10: 921600
        }[self._com.read_register(60)]

    def write_serial_port_baud(self, baud):
        if baud > 10:
            baud = {
                1200: 0,
                2400: 1,
                4800: 2,
                9600: 3,
                19200: 4,
                38400: 5,
                57600: 6,
                115200: 7,
                230400: 8,
                460800: 9,
                921600: 10
            }[baud]
        return self.write_register(60, baud)

    def read_serial_port_parity(self) -> str:
        return {0: 'None', 1: 'Odd', 2: 'Even'}[self._com.read_register(65)]

    def write_serial_port_parity(self, parity):
        if type(parity) is str:
            parity = parity.lower()
            parity = {'none': 0, 'odd': 1, 'even': 2}[parity]
        self.write_register(65, parity)

    def read_rs485_type(self):
        return {0: '4-wire', 1: '2-wire'}[self.read_register(66)]

    def write_rs485_type(self, rs485_type):
        if type(rs485_type) is str:
            if rs485_type[0] == '4':
                rs485_type = 0
            elif rs485_type[1] == '2':
                rs485_type = 1
            else:
                return None
        return self.write_register(66, rs485_type)

    def read_termination(self):
        return {0: False, 1: True}[self.read_register(67)]

    def write_termination(self, termination):
        if type(termination) is str:
            if termination[0] == 'D' or termination[0] == 'd':
                termination = 0
            elif termination[0] == 'E' or termination[0] == 'e':
                termination = 1
            else:
                return None
        if type(termination) is bool:
            termination = {False: 0, True: 1}[termination]
        return self.write_register(67, termination)

    def read_slave_id(self):
        return self.read_register(68)

    def write_slave_id(self, slave_id):
        return self.write_register(68, slave_id)

    def write_save_port_and_server_settings(self):
        """Saves the com port and file server information into the COM's EEPROM memory.
        Note: Changes apply after power cycle."""
        return self.write_register(70, 1)

    def read_file_server_username(self) -> str:
        return response_to_string(self.read_registers(71, 10))

    def write_file_server_username(self, username):
        if type(username) is not str:
            return False
        else:
            return self.write_registers(71, username)

    def read_file_server_password(self):
        return response_to_string(self.read_registers(81, 10))

    def write_file_server_password(self, password):
        if type(password) is not str:
            return False
        else:
            return self.write_registers(81, password)

    def read_file_server_anonymous_login(self):
        return self.read_register(91)

    def write_file_server_anonymous_login(self, input_value):
        return self.write_register(91, input_value)

    #######################
    # Module Configuration.
    #######################

    def read_module_status(self):
        return self.read_registers(100, 24)

    def write_module_status(self, input_value):
        # TODO: Write this function.
        pass

    def read_ethernet_port_present(self):
        return self.read_register(130)

    def read_usb_port_present(self):
        return self.read_register(131)

    def read_rs485_port_present(self):
        return self.read_register(132)

    def read_rs232_port_present(self):
        return self.read_register(133)

    def read_can_port_present(self):
        return self.read_register(134)

    ###############################
    # Registration and Data Logger.
    ###############################

    # TODO: Decide how to handle manual registration.

    def auto_registration(self, enable):
        """
        Enables or disables auto registration.
        :param enable: Boolean
        :return: response from modbus backend.
        """
        result = None
        if enable is True:
            result = self.write_register(1020, 1)  # enable auto registration
        elif enable is False:
            result = self.write_register(1020, 0)  # disable auto registration
            # """This for loop """
            # for i in range(24):
            #     self.write_register(1022, i+1)
        return result

    def delete_registration_numbers(self, numbers):
        if numbers == "all":
            for i in range(24):
                self.write_register(1022, i + 1)
        try:
            for number in numbers:
                self.write_register(1022, number)
        except TypeError:
            self.write_register(1022, number)
        return True

    def register_module(self, serial_number, registration_number):
        pass

    # SD CARD

    def read_log_file_name(self):
        return utils.response_to_string(self.read_registers(1100, 11))

    def write_log_file_name(self, name: str):
        return self.write_registers(1100, name) if len(name) < 12 else False

    def read_log_start_address_1(self):
        """
        Default = 2000 (Start Address of I/O Module in Slot 1.  Data for this module is at Start Address 3000)
        :return: int
        """
        return self.read_register(1120)

    def write_log_start_address_1(self, value):
        """
        Default = 2000 (Start Address of I/O Module in Slot 1.  Data for this module is at Start Address 3000)
        :return: modbus response.
        """
        return self.write_register(1120, value)

    def read_number_of_registers_to_log_1(self):
        """
        Number of Registers to Log starting at Log Start Address 1.  Maximum = 100, Default = 8
        :return: int
        """
        return self.read_register(1121)

    def write_number_of_registers_to_log_1(self, value):
        """
        Number of Registers to Log starting at Log Start Address 1.  Maximum = 100, Default = 8
        :param value: number to write
        :return: modbus response
        """
        return self.write_register(1121, value)

    def read_log_start_address_2(self):
        """
        Default = 4000 (Start Address of I/O Module in Slot 2.  Data for this module is at Start Address 5000)
        :return: int
        """
        return self.read_register(1122)

    def write_log_start_address_2(self, value):
        """
        Default = 4000 (Start Address of I/O Module in Slot 2.  Data for this module is at Start Address 5000)
        :return: modbus response.
        """
        return self.write_register(1122, value)

    def read_number_of_registers_to_log_2(self):
        """
        Number of Registers to Log starting at Log Start Address 2.  Maximum = 100, Default = 8
        :return: int
        """
        return self.read_register(1123)

    def write_number_of_registers_to_log_2(self, value):
        """
        Number of Registers to Log starting at Log Start Address 2.  Maximum = 100, Default = 8
        :param value: number to write
        :return: modbus response
        """
        return self.write_register(1123, value)

    def read_log_start_address_3(self):
        """
        Default = 6000 (Start Address of I/O Module in Slot 3.  Data for this module is at Start Address 7000)
        :return: int
        """
        return self.read_register(1124)

    def write_log_start_address_3(self, value):
        """
        Default = 6000 (Start Address of I/O Module in Slot 3.  Data for this module is at Start Address 7000)
        :return: modbus response.
        """
        return self.write_register(1124, value)

    def read_number_of_registers_to_log_3(self):
        """
        Number of Registers to Log starting at Log Start Address 3.  Maximum = 100, Default = 8
        :return: int
        """
        return self.read_register(1125)

    def write_number_of_registers_to_log_3(self, value):
        """
        Number of Registers to Log starting at Log Start Address 3.  Maximum = 100, Default = 8
        :param value: number to write
        :return: modbus response
        """
        return self.write_register(1125, value)

    def read_log_start_address_4(self):
        """
        Default = 8000 (Start Address of I/O Module in Slot 4.  Data for this module is at Start Address 9000)
        :return: int
        """
        return self.read_register(1126)

    def write_log_start_address_4(self, value):
        """
        Default = 8000 (Start Address of I/O Module in Slot 4.  Data for this module is at Start Address 9000)
        :return: modbus response.
        """
        return self.write_register(1126, value)

    def read_number_of_registers_to_log_4(self):
        """
        Number of Registers to Log starting at Log Start Address 4.  Maximum = 100, Default = 8
        :return: int
        """
        return self.read_register(1127)

    def write_number_of_registers_to_log_4(self, value):
        """
        Number of Registers to Log starting at Log Start Address 4.  Maximum = 100, Default = 8
        :param value: number to write
        :return: modbus response
        """
        return self.write_register(1127, value)

    def read_log_interval(self):
        return utils.int16_to_int32(self.read_registers(1130, 2))

    def write_log_interval(self, value):
        return self.write_registers(1130, utils.int32_to_int16s(value))

    def read_log_number_of_samples(self):
        return utils.int16_to_int32(self.read_registers(1132, 2))

    def write_log_number_of_samples(self, value):
        return self.write_registers(1132, utils.int32_to_int16s(value))

    def read_log_enable(self):
        return self.read_register(1140)

    def write_log_enable(self, enable):
        return self.write_register(1140, enable)

    def read_card_available(self):
        return self.read_register(1150)

    def read_total_space(self):
        return utils.int16_to_int32(self.read_registers(1151, 2))

    def read_free_space(self):
        return utils.int16_to_int32(self.read_registers(1153, 2))

    #############################
    # Module RTC and Temperature.
    #############################

    def read_second(self):
        """0-59"""
        return self.read_register(1200)

    def write_second(self, second):
        """0-59"""
        return self.write_register(1200, second)

    def read_minute(self):
        """0-59"""
        return self.read_register(1201)

    def write_minute(self, minute):
        """0-59"""
        return self.write_register(1201, minute)

    def read_hour(self):
        """0-23"""
        return self.read_register(1202)

    def write_hour(self, hour):
        """0-23"""
        return self.write_register(1202, hour)

    def read_day(self):
        """1-7, 1 = Sunday"""
        return self.read_register(1203)

    def write_day(self, day):
        """1-7, 1 = Sunday"""
        return self.write_register(1203, day)

    def read_date(self):
        """1-31"""
        return self.read_register(1204)

    def write_date(self, date):
        """1-31"""
        return self.write_register(1204, date)

    def read_month(self):
        """1-12"""
        return self.read_register(1205)

    def write_month(self, month):
        """1-12"""
        return self.write_register(1205, month)

    def read_year(self):
        """0-99"""
        return self.read_register(1206)

    def write_year(self, year):
        """0-99"""
        return self.write_register(1206, year)

    def read_internal_temperature_sensor(self):
        """0-59, Degree C"""
        return self.read_register(1210)

    ######################
    # PID Loop Controllers
    ######################
    # TODO: Implement the write functions for PID loop controllers.
    # TODO: Document write functions.

    def read_pid_id(self):
        """
        Unique instance ID of Controller
        :return: 0 to 31
        """
        return self.read_register(1300)

    def write_pid_id(self, input_id):
        """
        Unique instance ID of Controller
        :return: modbus response
        """
        return self.write_register(1300,
                                   input_id) if 0 <= input_id <= 31 else False

    def read_pid_enable(self):
        """
        Enable/Disable Controller
        :return: 0 or 1
        """
        return self.read_register(1301)

    def write_pid_enable(self, enable):
        """
        Enable/Disable Controller
        :return: modbus response
        """
        return self.write_register(1301, enable)

    def read_pid_name(self):
        """
        PID Controller name, 10 characters max.
        :return: string of length 10
        """
        return response_to_string(self.read_registers(1310, 10))

    def write_pid_name(self, name):
        """
        PID Controller name, 10 characters max.
        :return: modbus response
        """
        return self.write_registers(1310, name)

    def read_pid_description(self):
        """
        PID Controller Description, 10 characters max.
        :return: string of length 10
        """
        return response_to_string(self.read_registers(1330, 10))

    def write_pid_description(self, description):
        """
        PID Controller Description, 10 characters max.
        :return: string of length 10
        """
        return self.write_registers(1330, description)

    def read_pid_engineering_units(self):
        """
        Engineering Units (EU) chosen for the Controller, 5 characters max.
        :return: string of length 5
        """
        return response_to_string(self.read_registers(1350, 5))

    def write_pid_engineering_units(self, engineering_units):
        """
        Engineering Units (EU) chosen for the Controller, 5 characters max.
        :return: string of length 5
        """
        return self.write_registers(1350, engineering_units)

    def read_pid_pv_range_unit(self):
        """
        Units chosen for Process Variable.  5 characters max.  Standard unit = "%".
        :return: string of length 5
        """
        return response_to_string(self.read_registers(1355, 5))

    def write_pid_pv_range_unit(self, pv_range_unit):
        """
        Units chosen for Process Variable.  5 characters max.  Standard unit = "%".
        :return: string of length 5
        """
        return self.write_registers(1355, pv_range_unit)

    def read_pid_co_range_unit(self):
        """
        Units chosen for Control Output.  5 characters max.  Standard unit = "%".
        :return: string of length 5
        """
        return response_to_string(self.read_registers(1360, 5))

    def write_pid_co_range_unit(self, co_range_unit):
        """
        Units chosen for Control Output.  5 characters max.  Standard unit = "%".
        :return: string of length 5
        """
        return self.write_registers(1360, co_range_unit)

    def read_pid_pv_modbus_address(self):
        """
        System Address where Process Variable is obtained from.
        :return: 0 to 65,535
        """
        return self.read_register(1366)

    def read_pid_co_modbus_address(self):
        """
        System Address where Control Output is sent to.
        :return: 0 to 65,535
        """
        return self.read_register(1367)

    def read_pid_pv_count_maximum(self):
        """
        Process Variable maximum count value.  MSB at Address 1368, LSB at Address 1369.
        :return: 0 to 2^32-1
        """
        return int16_to_int32(self.read_registers(1368, 2))

    def write_pid_pv_count_maximum(self, pv_count_maximum):
        """
        Process Variable maximum count value.  MSB at Address 1368, LSB at Address 1369.
        :return: 0 to 2^32-1
        """
        return self.write_registers(1368, int32_to_int16s(pv_count_maximum))

    def read_pid_pv_count_minimum(self):
        """
        Process Variable minimum count value.  MSB at Address 1370, LSB at Address 1371.
        :return: 0 to 2^32-1
        """
        return int16_to_int32(self.read_registers(1370, 2))

    def write_pid_pv_count_minimum(self, pv_count_minimum):
        """
        Process Variable minimum count value.  MSB at Address 1370, LSB at Address 1371.
        :return: 0 to 2^32-1
        """
        return self.write_registers(1370, int32_to_int16s(pv_count_minimum))

    def read_pid_co_count_maximum(self):
        """
        Control Output maximum count value.  MSB at Address 1372, LSB at Address 1373.
        :return: 0 to 2^32-1
        """
        return int16_to_int32(self.read_registers(1372, 2))

    def write_pid_co_count_maximum(self, co_count_maximum):
        """
        Control Output maximum count value.  MSB at Address 1372, LSB at Address 1373.
        :return: 0 to 2^32-1
        """
        return self.write_registers(1372, int32_to_int16s(co_count_maximum))

    def read_pid_co_count_minimum(self):
        """
        Control Output minimum count value.  MSB at Address 1374, LSB at Address 1375.
        :return: 0 to 2^32-1
        """
        return int16_to_int32(self.read_registers(1374, 2))

    def write_pid_co_count_minimum(self, co_count_minimum):
        """
        Control Output minimum count value.  MSB at Address 1374, LSB at Address 1375.
        :return: 0 to 2^32-1
        """
        return self.write_registers(1374, int32_to_int16s(co_count_minimum))

    def read_pid_pv_range_maximum(self):
        """
        Process Variable Range maximum value.  Integer part at Address 1376, fractional part at Address 1377.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1376, 2))

    def write_pid_pv_range_maximum(self, pv_range_maximum):
        """
        Process Variable Range maximum value.  Integer part at Address 1376, fractional part at Address 1377.
        :return: float type number
        """
        return self.write_registers(1376, float_to_ints(pv_range_maximum))

    def read_pid_pv_range_minimum(self):
        """
        Process Variable Range minimum value.  Integer part at Address 1378, fractional part at Address 1379.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1378, 2))

    def write_pid_pv_range_minimum(self, pv_range_minimum):
        """
        Process Variable Range minimum value.  Integer part at Address 1378, fractional part at Address 1379.
        :return: float type number
        """
        return self.write_registers(1378, float_to_ints(pv_range_minimum))

    def read_pid_co_range_maximum(self):
        """
        Control Output Range maximum value.  Integer part at Address 1380, fractional part at Address 1381.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1380, 2))

    def write_pid_co_range_maximum(self, co_range_maximum):
        """
        Control Output Range maximum value.  Integer part at Address 1380, fractional part at Address 1381.
        :return: float type number
        """
        return self.write_registers(1380, float_to_ints(co_range_maximum))

    def read_pid_co_range_minimum(self):
        """
        Control Output Range minimum value.  Integer part at Address 1382, fractional part at Address 1383.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1382, 2))

    def write_pid_co_range_minimum(self, co_range_minimum):
        """
        Control Output Range minimum value.  Integer part at Address 1382, fractional part at Address 1383.
        :return: float type number
        """
        return self.write_registers(1382, float_to_ints(co_range_minimum))

    def read_pid_algorithm(self):
        """
        PID Control Algorithm.  0 = Noninteractive, 1 = Parallel
        :return: string
        """
        return {0: "Noninteractive", 1: "Parallel"}[self.read_register(1386)]

    def write_pid_algorithm(self, algorithm):
        """
        PID Control Algorithm.  0 = Noninteractive, 1 = Parallel
        :return: string
        """
        return self.read_register(1386)  # TODO: Finish this.

    def read_pid_control_direction(self):
        """
        Control Direction.  0 = Reverse Acting, 1 = Direct Acting
        :return: string
        """
        return {
            0: "Reverse Acting",
            1: "Direct Acting"
        }[self.read_register(1387)]

    def read_pid_setpoint_action(self):
        """
        Setpoint Action.  0 = Proportional & Derivative on Error, 1 = Proportional on Error / Derivative on PV,
        2 = Proportional & Derivative on PV.
        :return: string
        """
        return {
            0: "Proportional & Derivative on Error",
            1: "Proportional on Error / Derivative on PV",
            2: "Proportional & Derivative on PV",
        }[self.read_register(1388)]

    def write_pid_setpoint_action(self, setpoint_action):
        """
        Setpoint Action.  0 = Proportional & Derivative on Error, 1 = Proportional on Error / Derivative on PV,
        2 = Proportional & Derivative on PV.
        :return: string
        """
        return self.read_register(1388)

    def read_pid_mode(self):
        """
        Operational Mode.  0 = Manual, 1 = Automatic
        :return: string
        """
        return {0: "Manual", 1: "Automatic"}[self.read_register(1389)]

    def read_pid_output_type(self):
        """
        Control Output Signal Type.  0 = Voltage, 1 = Current, 2 = Discrete Output (PWM)
        :return: string
        """
        return {
            0: "Voltage",
            1: "Current",
            2: "Discrete Output (PWM)"
        }[self.read_register(1390)]

    def read_pid_setpoint(self):
        """
        Setpoint.
        Integer part at Address 1396, fractional part at Address 1397.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1396, 2))

    def read_pid_process_variable(self):
        """
        Process Variable.
        Integer part at Address 1398, fractional part at Address 1399.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1398, 2))

    def read_pid_control_output(self):
        """
        Control Output.
        Integer part at Address 1400, fractional part at Address 1401.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1400, 2))

    def read_pid_pv_maximum(self):
        """
        Process Variable maximum value.
        Integer part at Address 1402, fractional part at Address 1403.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1402, 2))

    def read_pid_pv_minimum(self):
        """
        Process Variable minimum value.
        Integer part at Address 1404, fractional part at Address 1405.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1404, 2))

    def read_pid_co_maximum(self):
        """
        Control Output maximum value.
        Integer part at Address 1406, fractional part at Address 1407.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1406, 2))

    def read_pid_co_minimum(self):
        """
        Control Output minimum value.
        Integer part at Address 1408, fractional part at Address 1409.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1408, 2))

    def read_pid_kc(self):
        """
        Controller Gain (%/%).
        Integer part at Address 1410, fractional part at Address 1411.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1410, 2))

    def read_pid_ti(self):
        """
        Integral Time (minutes).
        Integer part at Address 1412, fractional part at Address 1413.  Fractional part is in 10,000ths of a second.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1412, 2))

    def read_pid_td(self):
        """
        Derivative Time (minutes).
        Integer part at Address 1414, fractional part at Address 1415.  Fractional part is in 10,000ths of a second.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1414, 2))

    def read_pid_scan_time(self):
        """
        PID Controller Update Rate (seconds).
        Integer part at Address 1416, fractional part at Address 1417.  This value is fixed at 1s.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1416, 2))

    def read_pid_co_high_clamp(self):
        """
        Controller Output upper limit (%).
        Integer part at Address 1418, fractional part at Address 1419.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1418, 2))

    def read_pid_co_low_clamp(self):
        """
        Controller Output lower limit (%).
        Integer part at Address 1420, fractional part at Address 1421.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1420, 2))

    def read_pid_pv_tracking(self):
        """
        Track Process Variable in Manual Mode. 0 = Do Not Track PV, 1 = Track PV
        :return: string
        """
        return {0: "Do Not Track PV", 1: "Track PV"}[self.read_register(1422)]

    def read_pid_gap_width(self):
        """
        Gap around setpoint (Engineering Units).
        Integer part at Address 1423, fractional part at Address 1424.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1423, 2))

    def read_pid_gap_multiplier(self):
        """
        Gain multiplier inside Gap.
        Integer part at Address 1425, fractional part at Address 1426.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1425, 2))

    def read_pid_filter_time_constant(self):
        """
        PV Filter Time Constant (minutes).
        Integer part at Address 1427, fractional part at Address 1428. Fractional part is in 10,000ths of a second.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1427, 2))

    def read_pid_active_alarm(self):
        """
        Indicates which alarm condition is active. 0 = Low-Low, 1 = Low, 2 = None, 3 = High-High, 4 = High.
        :return: string
        """
        return {
            0: "Low-Low",
            1: "Low",
            2: "None",
            3: "High-High",
            4: "High",
        }[self.read_register(1441)]

    def read_pid_alarm_deadband(self):
        """
        Deadband or Hysteresis.  Adds to low limits, subtracts from high limits.
        Integer part at Address 1442, fractional part at Address 1443.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1442, 2))

    def read_pid_high_high_alarm_limit(self):
        """
        High-High Alarm Limit (Engineering Units). Integer part at Address 1444, fractional part at Address 1445.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1444, 2))

    def read_pid_high_alarm_limit(self):
        """
        High Alarm Limit (Engineering Units). Integer part at Address 1446, fractional part at Address 1447.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1446, 2))

    def read_pid_low_alarm_limit(self):
        """
        Low Alarm Limit (Engineering Units). Integer part at Address 1448, fractional part at Address 1449.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1448, 2))

    def read_pid_low_low_alarm_limit(self):
        """
        Low-Low Alarm Limit (Engineering Units). Integer part at Address 1450, fractional part at Address 1451.
        :return: float type number
        """
        return ints_to_float(self.read_registers(1450, 2))

    ###################
    # Helper Functions.
    ###################

    def ftp_settings(self) -> str:
        # TODO: Document this function.
        result = "Username: "******"\n"
        result += "Password: "******"\n"
        result += "Anonymous Login: "******"Enabled"
        elif anonymous_login == 0:
            result += "Disabled"
        return result

    def __del__(self):
        if MODBUS_BACKEND == PYMODBUS3:
            self._client.close()
        if MODBUS_BACKEND == UMODBUS:
            self._sock.close()
Пример #13
0
from pymodbus3.client.sync import ModbusTcpClient

# instead of this

# 192.168.1.22:502
#PLC Mod Bus Regs 40001-40100

#40096-40100     monnth day hour .....
#Reg 2 Bytes --> not 4 bytes

client = ModbusTcpClient('192.168.1.22')
result = client.read_holding_registers(0, 5, unit=0X01)
print(str(result))
result = client.write_registers(0, [300, 305], unit=0X01)
print(str(result))

client.close()
Пример #14
0
class ModbusClient(threading.Thread):
    """Define Tag engine to poll MODBUS servers.

    """
    def __init__(self, asset_read_dict, asset_write_dict, interface_config):
        """ interface config
        ('ip_add', '0.0.0.0')
        ('endian', '>')
        ('registers', {
            'type': '32bit_float',
            'name': 'kw',
            'scale': 0.001,
            'mod_add': 50052})
        ('update_rate', 1)

        """
        threading.Thread.__init__(self)

        self.config = interface_config

        self.daemon = True  # TODO: this may be threading in 2.7, daemon is method now?

        self.client = None
        self.cvt = dict()
        self.process_stop = False
        self.connected = False
        self.timestamp = str()

        # Initialize Current Value Table (CVT)
        for reg in self.config['registers']:
            self.cvt.update({reg['name']: None})

    def __del__(self):
        """Teardown

        """
        print('MODBUS CLIENT:', self.process_name, '-- deconstructed')

    def run(self):
        """Connect to target and maintain client while loop.

        Call with Thread.start()
        """
        print('MODBUS CLIENT', self.process_name, '-- started')
        self.connect()

        while not self.process_stop:
            if self.connected:
                self._update()
            time.sleep(self.config['update_rate'])

        self.disconnect()
        self.__del__()

    def stop(self):
        """Stop process

        """
        self.process_stop = True

    def connect(self):
        """Connect to target MODBUS server.

        """
        try:
            self.client = ModbusTcpClient(self.config['ip_add'])
            self.client.connect()
            self.connected = True
        except:
            print('MODBUS CLIENT:', self.process_name,
                  '-- unable to connect to target server.')

    def disconnect(self):
        """Disconnect from target MODBUS server.

        """
        try:
            self.client.close()
            self.connected = False
            print('MODBUS CLIENT:', self.process_name, '-- disconnected')
        except:
            print('MODBUS CLIENT:', self.process_name,
                  '-- failed to disconnect from server')

    def _update(self):
        """Poll MODBUS target server.

        Store results in self.cvt
        """
        for reg in self.config['registers']:

            try:
                """TODO: filter by register_name_list"""

                if reg['type'] == '32bit_float':
                    read_data = self.client.read_holding_registers(
                        reg['mod_add'], 2, unit=1)
                    decoded_data = BinaryPayloadDecoder.from_registers(
                        list(reversed(read_data.registers)),
                        endian=self.config['endian'])
                    self.cvt[reg['name']] = decoded_data.decode_32bit_float(
                    ) * reg['scale']

                elif reg['type'] == '32bit_int':
                    read_data = self.client.read_holding_registers(
                        reg['mod_add'], 2, unit=1)
                    decoded_data = BinaryPayloadDecoder.from_registers(
                        read_data.registers, endian=self.config['endian'])
                    self.cvt[reg['name']] = decoded_data.decode_32bit_int(
                    ) * reg['scale']

                elif reg['type'] == '32bit_uint':
                    read_data = self.client.read_holding_registers(
                        reg['mod_add'], 2, unit=1)
                    decoded_data = BinaryPayloadDecoder.from_registers(
                        read_data.registers, endian=self.config['endian'])
                    self.cvt[reg['name']] = decoded_data.decode_32bit_uint(
                    ) * reg['scale']

                elif reg['type'] == '16bit_int':
                    read_data = self.client.read_holding_registers(
                        reg['mod_add'], 1, unit=1)
                    decoded_data = BinaryPayloadDecoder.from_registers(
                        read_data.registers, endian=self.config['endian'])
                    self.cvt[reg['name']] = decoded_data.decode_16bit_int(
                    ) * reg['scale']

                elif reg['type'] == '16bit_uint':
                    read_data = self.client.read_holding_registers(
                        reg['mod_add'], 1, unit=1)
                    decoded_data = BinaryPayloadDecoder.from_registers(
                        read_data.registers, endian=self.config['endian'])
                    self.cvt[reg['name']] = decoded_data.decode_16bit_uint(
                    ) * reg['scale']

                else:
                    print(reg['type'], 'data type not supported')

            except AttributeError:
                print(self.process_name, 'MODBUS CLIENT: Read error')
                # TODO: How to import pymobus3 exceptions?

        self.timestamp = time.ctime()

    def write(self):
        pass  # TODO: write holding registers
Пример #15
0
from pymodbus3.client.sync import ModbusTcpClient
import struct


def FloatConvert(num1, num2):
    str_output = '0x' + str(hex(num1))[2:6] + str(hex(num2))[2:6] + '000'
    return struct.unpack('<f', struct.pack('<I', int(str_output, 16)))


client = ModbusTcpClient('127.0.0.1', 5020)

regoutput = client.read_holding_registers(16, 10)

print(FloatConvert(regoutput.registers[0], regoutput.registers[1]))
print(FloatConvert(regoutput.registers[2], regoutput.registers[3]))
print(FloatConvert(regoutput.registers[4], regoutput.registers[5]))
print(FloatConvert(regoutput.registers[6], regoutput.registers[7]))
print(FloatConvert(regoutput.registers[8], regoutput.registers[9]))

client.close()