Example #1
0
    def handle_request(self, query, request):
        """
        when a request is received, handle it and returns the response pdu
        """
        request_pdu = ""
        try:
            #extract the pdu and the slave id
            (slave_id, request_pdu) = query.parse_request(request)

            #get the slave and let him executes the action
            if slave_id == 0:
                #broadcast
                for key in self._slaves:
                    self._slaves[key].handle_request(request_pdu, broadcast=True)
                return
            else:
                slave = self.get_slave(slave_id)
                response_pdu = slave.handle_request(request_pdu)
                #make the full response
                response = query.build_response(response_pdu)
                return response
        except Exception as excpt:
            call_hooks("modbus.Databank.on_error", (self, excpt, request_pdu))
            LOGGER.error("handle request failed: " + str(excpt))

        #If the request was not handled correctly, return a server error response
        func_code = 1
        if len(request_pdu) > 0:
            (func_code, ) = struct.unpack(">B", request_pdu[0])
        return struct.pack(">BB", func_code+0x80, defines.SLAVE_DEVICE_FAILURE)
Example #2
0
 def _do_close(self):
     """Close the connection with the Modbus Slave"""
     if self._sock:
         call_hooks("modbus_tcp.TcpMaster.before_close", (self, ))
         self._sock.close()
         call_hooks("modbus_tcp.TcpMaster.after_close", (self, ))
         self._sock = None
Example #3
0
    def _do_run(self):
        """main function of the server"""
        try:
            #check the status of every socket
            response = ""
            request = ""
            read_bytes = "dummy"
            while read_bytes:
                read_bytes = self._serial.read(128)
                request += read_bytes

            #parse the request
            if request:
                retval = call_hooks("modbus_rtu.RtuServer.after_read", (self, request))
                if retval is not None:
                    request = retval
                response = self._handle(request)

                #send back the response
                retval = call_hooks("modbus_rtu.RtuServer.before_write", (self, response))
                if retval is not None:
                    response = retval

                if response:
                    self._serial.write(response)
                    time.sleep(self.get_timeout())

        except Exception as excpt:
            LOGGER.error("Error while handling request, Exception occurred: %s", excpt)
            call_hooks("modbus_rtu.RtuServer.on_error", (self, excpt))
Example #4
0
    def _write_multiple_coils(self, request_pdu):
        """execute modbus function 15"""
        call_hooks("modbus.Slave.handle_write_multiple_coils_request", (self, request_pdu))
        # get the starting address and the number of items from the request pdu
        (starting_address, quantity_of_x, byte_count) = struct.unpack(">HHB", request_pdu[1:6])

        expected_byte_count = quantity_of_x / 8
        if (quantity_of_x % 8) > 0:
            expected_byte_count += 1

        if (quantity_of_x <= 0) or (quantity_of_x > 1968) or (byte_count != expected_byte_count):
            # maximum allowed size is 1968 coils
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.COILS, starting_address, quantity_of_x)

        count = 0
        for i in xrange(byte_count):
            if count >= quantity_of_x:
                break
            fmt = "B" if self.unsigned else "b"
            (byte_value,) = struct.unpack(">" + fmt, request_pdu[6 + i])
            for j in xrange(8):
                if count >= quantity_of_x:
                    break

                if byte_value & (1 << j):
                    block[offset + i * 8 + j] = 1
                else:
                    block[offset + i * 8 + j] = 0

                count += 1
        return struct.pack(">HH", starting_address, count)
Example #5
0
 def _do_open(self):
     """Connect to the Modbus slave"""
     if self._sock:
         self._sock.close()
     self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     self.set_timeout(self.get_timeout())
     self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     call_hooks("modbus_tcp.TcpMaster.before_connect", (self, ))
     self._sock.connect((self._host, self._port))
     call_hooks("modbus_tcp.TcpMaster.after_connect", (self, ))
Example #6
0
    def _write_single_register(self, request_pdu):
        """execute modbus function 6"""
        call_hooks("modbus.Slave.handle_write_single_register_request", (self, request_pdu))

        fmt = "H" if self.unsigned else "h"
        (data_address, value) = struct.unpack(">H" + fmt, request_pdu[1:5])
        block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, data_address, 1)
        block[offset] = value
        # returns echo of the command
        return request_pdu[1:]
Example #7
0
    def _handle(self, request):
        LOGGER.debug(self.get_log_buffer('-->', request))

        query = self._make_query()
        retval = call_hooks('modbus.Server.before_handle_request', (self, request))
        if retval:
            request = retval
        response = self._databank.handle_request(query, request)
        retval = call_hooks('modbus.Server.after_handle_request', (self, response))
        if retval:
            response = retval
        if response:
            LOGGER.debug(self.get_log_buffer('<--', response))
        return response
Example #8
0
    def _write_single_coil(self, request_pdu):
        """execute modbus function 5"""

        call_hooks("modbus.Slave.handle_write_single_coil_request", (self, request_pdu))
        (data_address, value) = struct.unpack(">HH", request_pdu[1:5])
        block, offset = self._get_block_and_offset(defines.COILS, data_address, 1)
        if value == 0:
            block[offset] = 0
        elif value == 0xff00:
            block[offset] = 1
        else:
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)
        # returns echo of the command
        return request_pdu[1:]
Example #9
0
    def _recv(self, expected_length=-1):
        """Receive the response from the slave"""
        response = utils.to_data("")
        start_time = time.time() if self.use_sw_timeout else 0
        readed_len = 0
        while True:
            if self._serial.timeout:
                # serial.read() says if a timeout is set it may return less characters as requested
                # we should update expected_length by readed_len
                read_bytes = self._serial.read(expected_length - readed_len if (expected_length - readed_len) > 0 else 1)
            else:
                read_bytes = self._serial.read(expected_length if expected_length > 0 else 1)
            if self.use_sw_timeout:
                read_duration = time.time() - start_time
            else:
                read_duration = 0
            if (not read_bytes) or (read_duration > self._serial.timeout):
                break
            response += read_bytes
            if expected_length >= 0 and len(response) >= expected_length:
                # if the expected number of byte is received consider that the response is done
                # improve performance by avoiding end-of-response detection by timeout
                break
            readed_len += len(read_bytes)

        retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response))
        if retval is not None:
            return retval
        return response
Example #10
0
    def _handle(self, request):
        LOGGER.debug(self.get_log_buffer('-->', request))

        query = self._make_query()
        retval = call_hooks('modbus.Server.before_handle_request',
                            (self, request))
        if retval:
            request = retval
        response = self._databank.handle_request(query, request)
        retval = call_hooks('modbus.Server.after_handle_request',
                            (self, response))
        if retval:
            response = retval
        if response:
            LOGGER.debug(self.get_log_buffer('<--', response))
        return response
Example #11
0
    def _write_single_coil(self, request_pdu):
        """execute modbus function 5"""

        call_hooks("modbus.Slave.handle_write_single_coil_request",
                   (self, request_pdu))
        (data_address, value) = struct.unpack(">HH", request_pdu[1:5])
        block, offset = self._get_block_and_offset(defines.COILS, data_address,
                                                   1)
        if value == 0:
            block[offset] = 0
        elif value == 0xff00:
            block[offset] = 1
        else:
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)
        # returns echo of the command
        return request_pdu[1:]
Example #12
0
    def handle_request(self, query, request):
        """
        when a request is received, handle it and returns the response pdu
        """
        request_pdu = ""
        try:
            # extract the pdu and the slave id
            (slave_id, request_pdu) = query.parse_request(request)

            # get the slave and let him executes the action
            if slave_id == 0:
                # broadcast
                for key in self._slaves:
                    self._slaves[key].handle_request(request_pdu, broadcast=True)
                return
            else:
                try:
                    slave = self.get_slave(slave_id)
                except MissingKeyError:
                    if self.error_on_missing_slave:
                        raise
                    else:
                        return ""

                response_pdu = slave.handle_request(request_pdu)
                # make the full response
                response = query.build_response(response_pdu)
                return response
        except ModbusInvalidRequestError as excpt:
            # Request is invalid, do not send any response
            LOGGER.error("invalid request: " + str(excpt))
            return ""
        except MissingKeyError as excpt:
            # No slave with this ID in server, do not send any response
            LOGGER.error("handle request failed: " + str(excpt))
            return ""
        except Exception as excpt:
            call_hooks("modbus.Databank.on_error", (self, excpt, request_pdu))
            LOGGER.error("handle request failed: " + str(excpt))

        # If the request was not handled correctly, return a server error response
        func_code = 1
        if len(request_pdu) > 0:
            (func_code, ) = struct.unpack(">B", request_pdu[0:1])

        return struct.pack(">BB", func_code + 0x80, defines.SLAVE_DEVICE_FAILURE)
Example #13
0
    def handle_request(self, request_pdu, broadcast=False):

        with self._data_lock:  #thread-safe
            try:
                retval = call_hooks("modbus.Slave.handle_request",
                                    (self, request_pdu))
                if retval <> None:
                    return retval

                # get the function code
                (function_code, ) = struct.unpack(">B", request_pdu[0])

                # check if the function code is valid. If not returns error response
                if not self._fn_code_map.has_key(function_code):
                    raise modbus.ModbusError(cst.ILLEGAL_FUNCTION)

                # if read query is broadcasted raises an error
                cant_be_broadcasted = (cst.READ_COILS,
                                       cst.READ_DISCRETE_INPUTS,
                                       cst.READ_INPUT_REGISTERS,
                                       cst.READ_HOLDING_REGISTERS)
                if broadcast and (function_code in cant_be_broadcasted):
                    raise modbus.ModbusInvalidRequestError(
                        "Function %d can not be broadcasted" % function_code)

                #execute the corresponding function
                response_pdu = self._fn_code_map[function_code](request_pdu)
                if response_pdu:
                    if broadcast:
                        call_hooks("modbus.Slave.on_handle_broadcast",
                                   (self, response_pdu))
                        LOGGER.debug(
                            "broadcast: %s" %
                            (utils.get_log_buffer("!!", response_pdu)))
                        return ""
                    else:
                        return struct.pack(">B", function_code) + response_pdu
                raise Exception("No response for function %d" % function_code)

            except modbus.ModbusError, excpt:
                LOGGER.debug(str(excpt))
                call_hooks("modbus.Slave.on_exception",
                           (self, function_code, excpt))
                return struct.pack(">BB", function_code + 128,
                                   excpt.get_exception_code())
Example #14
0
    def handle_request(self, request_pdu, broadcast=False):
        """
        parse the request pdu, makes the corresponding action
        and returns the response pdu
        """
        # thread-safe
        with self._data_lock:
            try:
                retval = call_hooks("modbus.Slave.handle_request", (self, request_pdu))
                if retval is not None:
                    return retval

                # get the function code
                (function_code,) = struct.unpack(">B", request_pdu[0])

                # check if the function code is valid. If not returns error response
                if function_code not in self._fn_code_map:
                    raise ModbusError(defines.ILLEGAL_FUNCTION)

                # if read query is broadcasted raises an error
                cant_be_broadcasted = (
                    defines.READ_COILS,
                    defines.READ_DISCRETE_INPUTS,
                    defines.READ_INPUT_REGISTERS,
                    defines.READ_HOLDING_REGISTERS,
                )
                if broadcast and (function_code in cant_be_broadcasted):
                    raise ModbusInvalidRequestError("Function %d can not be broadcasted" % function_code)

                # execute the corresponding function
                response_pdu = self._fn_code_map[function_code](request_pdu)
                if response_pdu:
                    if broadcast:
                        call_hooks("modbus.Slave.on_handle_broadcast", (self, response_pdu))
                        LOGGER.debug("broadcast: %s", get_log_buffer("!!", response_pdu))
                        return ""
                    else:
                        return struct.pack(">B", function_code) + response_pdu
                raise Exception("No response for function %d" % function_code)

            except ModbusError, excpt:
                LOGGER.debug(str(excpt))
                call_hooks("modbus.Slave.on_exception", (self, function_code, excpt))
                return struct.pack(">BB", function_code + 128, excpt.get_exception_code())
Example #15
0
    def _send(self, request):
        """Send request to the slave"""
        retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request))
        if retval is not None:
            request = retval

        self._serial.flushInput()
        self._serial.flushOutput()

        self._serial.write(request)
Example #16
0
    def _send(self, request):
        """Send request to the slave"""
        retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request))
        if retval is not None:
            request = retval

        self._serial.reset_input_buffer()
        self._serial.reset_output_buffer()

        self._serial.write(request)
Example #17
0
 def handle_request(self, query, request):
     request_pdu = ''
     try:
         (slave_id, request_pdu) = query.parse_request(request)
         if slave_id == 0:
             for key in self._slaves:
                 self._slaves[key].handle_request(request_pdu,
                                                  broadcast=True)
             return
         else:
             slave = self.get_slave(slave_id)
             response_pdu = slave.handle_request(request_pdu)
             response = query.build_response(response_pdu)
             return response
     except Exception as e:
         call_hooks('modbus.Databank.on_error', (self, e, request_pdu))
         LOGGER.error('handle_request failed: ' + str(e))
     except:
         LOGGER.error('handle_request failed: unknown exception')
Example #18
0
 def handle_request(self, query, request):
     request_pdu = ''
     try:
         (slave_id, request_pdu) = query.parse_request(request)
         if slave_id == 0:
             for key in self._slaves:
                 self._slaves[key].handle_request(request_pdu,
                                                  broadcast=True)
             return
         else:
             slave = self.get_slave(slave_id)
             response_pdu = slave.handle_request(request_pdu)
             response = query.build_response(response_pdu)
             return response
     except Exception as e:
         call_hooks('modbus.Databank.on_error', (self, e, request_pdu))
         LOGGER.error('handle_request failed: ' + str(e))
     except:
         LOGGER.error('handle_request failed: unknown exception')
Example #19
0
    def _write_multiple_registers(self, request_pdu):
        """execute modbus function 16"""
        call_hooks("modbus.Slave.handle_write_multiple_registers_request", (self, request_pdu))
        # get the starting address and the number of items from the request pdu
        (starting_address, quantity_of_x, byte_count) = struct.unpack(">HHB", request_pdu[1:6])

        if (quantity_of_x <= 0) or (quantity_of_x > 123) or (byte_count != (quantity_of_x * 2)):
            # maximum allowed size is 123 registers in one reading
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_address, quantity_of_x)

        count = 0
        for i in xrange(quantity_of_x):
            count += 1
            fmt = "H" if self.unsigned else "h"
            block[offset + i] = struct.unpack(">" + fmt, request_pdu[6 + 2 * i : 8 + 2 * i])[0]

        return struct.pack(">HH", starting_address, count)
Example #20
0
    def _write_multiple_registers(self, request_pdu):
        """execute modbus function 16"""
        call_hooks("modbus.Slave.handle_write_multiple_registers_request", (self, request_pdu))
        # get the starting address and the number of items from the request pdu
        (starting_address, quantity_of_x, byte_count) = struct.unpack(">HHB", request_pdu[1:6])

        if (quantity_of_x <= 0) or (quantity_of_x > 123) or (byte_count != (quantity_of_x * 2)):
            # maximum allowed size is 123 registers in one reading
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_address, quantity_of_x)

        count = 0
        for i in range(quantity_of_x):
            count += 1
            fmt = "H" if self.unsigned else "h"
            block[offset+i] = struct.unpack(">"+fmt, request_pdu[6+2*i:8+2*i])[0]

        return struct.pack(">HH", starting_address, count)
Example #21
0
    def _send(self, request):
        """Send request to the slave"""
        retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request))
        if retval is not None:
            request = retval

        self._serial.flushInput()
        self._serial.flushOutput()

        self._serial.write(request)
        time.sleep(self.get_timeout())
Example #22
0
    def _handle(self, request):
        """handle a received sentence"""

        if self._verbose:
            LOGGER.debug(get_log_buffer("-->", request))

        # gets a query for analyzing the request
        query = self._make_query()

        retval = call_hooks("modbus.Server.before_handle_request", (self, request))
        if retval:
            request = retval

        response = self._databank.handle_request(query, request)
        retval = call_hooks("modbus.Server.after_handle_request", (self, response))
        if retval:
            response = retval

        if response and self._verbose:
            LOGGER.debug(get_log_buffer("<--", response))
        return response
Example #23
0
    def _handle(self, request):
        """handle a received sentence"""

        if self._verbose:
            LOGGER.debug(get_log_buffer("-->", request))

        # gets a query for analyzing the request
        query = self._make_query()

        retval = call_hooks("modbus.Server.before_handle_request", (self, request))
        if retval:
            request = retval

        response = self._databank.handle_request(query, request)
        retval = call_hooks("modbus.Server.after_handle_request", (self, response))
        if retval:
            response = retval

        if response and self._verbose:
            LOGGER.debug(get_log_buffer("<--", response))
        return response
Example #24
0
    def _do_run(self):
        """main function of the server"""
        try:
            #check the status of every socket
            request = utils.to_data('')
            while True:
                try:
                    read_bytes = self._serial.read(128)
                    if not read_bytes:
                        break
                except Exception as e:
                    self._serial.close()
                    self._serial.open()
                    break
                request += read_bytes

            #parse the request
            if request:

                retval = call_hooks("modbus_rtu.RtuServer.after_read",
                                    (self, request))
                if retval is not None:
                    request = retval

                response = self._handle(request)

                #send back the response
                retval = call_hooks("modbus_rtu.RtuServer.before_write",
                                    (self, response))
                if retval is not None:
                    response = retval

                if response:
                    self._serial.write(response)
                    time.sleep(self.get_timeout())

        except Exception as excpt:
            LOGGER.error(
                "Error while handling request, Exception occurred: %s", excpt)
            call_hooks("modbus_rtu.RtuServer.on_error", (self, excpt))
Example #25
0
    def _write_multiple_coils(self, request_pdu):
        """execute modbus function 15"""
        call_hooks("modbus.Slave.handle_write_multiple_coils_request",
                   (self, request_pdu))
        # get the starting address and the number of items from the request pdu
        (starting_address, quantity_of_x,
         byte_count) = struct.unpack(">HHB", request_pdu[1:6])

        expected_byte_count = quantity_of_x // 8
        if (quantity_of_x % 8) > 0:
            expected_byte_count += 1

        if (quantity_of_x <= 0) or (quantity_of_x > 1968) or (
                byte_count != expected_byte_count):
            # maximum allowed size is 1968 coils
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.COILS,
                                                   starting_address,
                                                   quantity_of_x)

        count = 0
        for i in range(byte_count):
            if count >= quantity_of_x:
                break
            fmt = "B" if self.unsigned else "b"
            (byte_value, ) = struct.unpack(">" + fmt, request_pdu[6 + i:7 + i])
            for j in range(8):
                if count >= quantity_of_x:
                    break

                if byte_value & (1 << j):
                    block[offset + i * 8 + j] = 1
                else:
                    block[offset + i * 8 + j] = 0

                count += 1
        return struct.pack(">HH", starting_address, count)
Example #26
0
 def _send(self, request):
     """Send request to the slave"""
     retval = call_hooks("modbus_tcp.TcpMaster.before_send", (self, request))
     if retval is not None:
         request = retval
     try:
         flush_socket(self._sock, 3)
     except Exception as msg:
         #if we can't flush the socket successfully: a disconnection may happened
         #try to reconnect
         LOGGER.error('Error while flushing the socket: {0}'.format(msg))
         self._do_open()
     self._sock.send(request)
Example #27
0
 def _send(self, request):
     """Send request to the slave"""
     retval = call_hooks("modbus_tcp.TcpMaster.before_send", (self, request))
     if retval is not None:
         request = retval
     try:
         flush_socket(self._sock, 3)
     except Exception as msg:
         #if we can't flush the socket successfully: a disconnection may happened
         #try to reconnect
         LOGGER.error('Error while flushing the socket: {0}'.format(msg))
         self._do_open()
     self._sock.send(request)
Example #28
0
    def _read_write_multiple_registers(self, request_pdu):
        """execute modbus function 23"""
        call_hooks("modbus.Slave.handle_read_write_multiple_registers_request", (self, request_pdu))
        # get the starting address and the number of items from the request pdu
        (starting_read_address, quantity_of_x_to_read, starting_write_address, quantity_of_x_to_write, byte_count_to_write) = struct.unpack(">HHHHB", request_pdu[1:10])

        # read part
        if (quantity_of_x_to_read <= 0) or (quantity_of_x_to_read > 125):
            # maximum allowed size is 125 registers in one reading
            LOGGER.debug("quantity_of_x_to_read is %d", quantity_of_x_to_read)
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_read_address, quantity_of_x_to_read)

        # get the values
        values = block[offset:offset+quantity_of_x_to_read]
        # write the response header
        response = struct.pack(">B", 2 * quantity_of_x_to_read)
        # add the values of every register on 2 bytes
        for reg in values:
            fmt = "H" if self.unsigned else "h"
            response += struct.pack(">"+fmt, reg)

        # write part
        if (quantity_of_x_to_write <= 0) or (quantity_of_x_to_write > 123) or (byte_count_to_write != (quantity_of_x_to_write * 2)):
            # maximum allowed size is 123 registers in one reading
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_write_address, quantity_of_x_to_write)

        count = 0
        for i in range(quantity_of_x_to_write):
            count += 1
            fmt = "H" if self.unsigned else "h"
            block[offset+i] = struct.unpack(">"+fmt, request_pdu[10+2*i:12+2*i])[0]       
        
        return response
Example #29
0
    def handle_request(self, query, request):

        request_pdu = ""
        try:
            #extract the pdu and the slave id
            (slave_id, request_pdu) = query.parse_request(request)

            #get the slave and let him executes the action
            if slave_id == 0:
                #broadcast
                for key in self._slaves:
                    self._slaves[key].handle_request(request_pdu,
                                                     broadcast=True)
                return
            else:
                slave = self.get_slave(slave_id)
                response_pdu = slave.handle_request(request_pdu)
                #make the full response
                response = query.build_response(response_pdu)
                return response
        except Exception, excpt:
            call_hooks("modbus.Databank.on_error", (self, excpt, request_pdu))
            LOGGER.error("handle request failed: " + str(excpt))
Example #30
0
    def _do_run(self):
        """main function of the server"""
        try:
            # check the status of every socket
            request = utils.to_data('')
            if self._block_on_first_byte:
                # do a blocking read for first byte
                self._serial.timeout = None
                try:
                    read_bytes = self._serial.read(1)
                    request += read_bytes
                except Exception as e:
                    self._serial.close()
                    self._serial.open()
                self._serial.timeout = self._timeout

            # Read rest of the request
            while True:
                try:
                    read_bytes = self._serial.read(128)
                    if not read_bytes:
                        break
                except Exception as e:
                    self._serial.close()
                    self._serial.open()
                    break
                request += read_bytes

            # parse the request
            if request:

                retval = call_hooks("modbus_rtu.RtuServer.after_read", (self, request))
                if retval is not None:
                    request = retval

                response = self._handle(request)

                # send back the response
                retval = call_hooks("modbus_rtu.RtuServer.before_write", (self, response))
                if retval is not None:
                    response = retval

                if response:
                    if self._serial.in_waiting > 0:
                        # Most likely master timed out on this request and started a new one
                        # for which we already received atleast 1 byte
                        LOGGER.warning("Not sending response because there is new request pending")
                    else:
                        self._serial.write(response)
                        self._serial.flush()
                        time.sleep(self.get_timeout())

                call_hooks("modbus_rtu.RtuServer.after_write", (self, response))

        except Exception as excpt:
            LOGGER.error("Error while handling request, Exception occurred: %s", excpt)
            call_hooks("modbus_rtu.RtuServer.on_error", (self, excpt))
Example #31
0
    def _do_run(self):
        """main function of the server"""
        try:
            # check the status of every socket
            request = utils.to_data('')
            if self._block_on_first_byte:
                # do a blocking read for first byte
                self._serial.timeout = None
                try:
                    read_bytes = self._serial.read(1)
                    request += read_bytes
                except Exception as e:
                    self._serial.close()
                    self._serial.open()
                self._serial.timeout = self._timeout

            # Read rest of the request
            while True:
                try:
                    read_bytes = self._serial.read(128)
                    if not read_bytes:
                        break
                except Exception as e:
                    self._serial.close()
                    self._serial.open()
                    break
                request += read_bytes

            # parse the request
            if request:

                retval = call_hooks("modbus_rtu.RtuServer.after_read", (self, request))
                if retval is not None:
                    request = retval

                response = self._handle(request)

                # send back the response
                retval = call_hooks("modbus_rtu.RtuServer.before_write", (self, response))
                if retval is not None:
                    response = retval

                if response:
                    if self._serial.in_waiting > 0:
                        # Most likely master timed out on this request and started a new one
                        # for which we already received atleast 1 byte
                        LOGGER.warning("Not sending response because there is new request pending")
                    else:
                        self._serial.write(response)
                        self._serial.flush()
                        time.sleep(self.get_timeout())

                call_hooks("modbus_rtu.RtuServer.after_write", (self, response))

        except Exception as excpt:
            LOGGER.error("Error while handling request, Exception occurred: %s", excpt)
            call_hooks("modbus_rtu.RtuServer.on_error", (self, excpt))
Example #32
0
 def _recv(self, expected_length=-1):
     """Receive the response from the slave"""
     response = to_data('')
     length = 255
     while len(response) < length:
         rcv_byte = self._sock.recv(1)
         if rcv_byte:
             response += rcv_byte
         if expected_length >= 0 and len(response) >= expected_length:
             break
     retval = call_hooks("modbus_rtu_over_tcp.RtuOverTcpMaster.after_recv",
                         (self, response))
     if retval is not None:
         return retval
     return response
Example #33
0
    def handle_request(self, query, request):
        """
        when a request is received, handle it and returns the response pdu
        """
        request_pdu = ""
        try:
            # extract the pdu and the slave id
            (slave_id, request_pdu) = query.parse_request(request)

            # get the slave and let him executes the action
            if slave_id == 0:
                # broadcast
                for key in self._slaves:
                    self._slaves[key].handle_request(request_pdu, broadcast=True)
                return
            else:
                slave = self.get_slave(slave_id)
                response_pdu = slave.handle_request(request_pdu)
                # make the full response
                response = query.build_response(response_pdu)
                return response
        except Exception, excpt:
            call_hooks("modbus.Databank.on_error", (self, excpt, request_pdu))
            LOGGER.error("handle request failed: " + str(excpt))
Example #34
0
    def _send(self, request):
        """Send request to the slave"""
        retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request))
        if retval is not None:
            request = retval

        self._serial.reset_input_buffer()
        self._serial.reset_output_buffer()

        self._serial.write(request)
        self._serial.flush()

        # Read the echo data, and discard it
        if self.handle_local_echo:
            self._serial.read(len(request))
Example #35
0
    def _send(self, request):
        """Send request to the slave"""
        retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request))
        if retval is not None:
            request = retval

        self._serial.reset_input_buffer()
        self._serial.reset_output_buffer()

        self._serial.write(request)
        self._serial.flush()

        # Read the echo data, and discard it
        if self.handle_local_echo:
            self._serial.read(len(request))
Example #36
0
    def _recv(self, expected_length=-1):
        """Receive the response from the slave"""
        response = ""
        read_bytes = "dummy"
        while read_bytes:
            read_bytes = self._serial.read(expected_length if expected_length > 0 else 1)
            response += read_bytes
            if expected_length >= 0 and len(response) >= expected_length:
                #if the expected number of byte is received consider that the response is done
                #improve performance by avoiding end-of-response detection by timeout
                break

        retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response))
        if retval is not None:
            return retval
        return response
Example #37
0
    def _recv(self, expected_length=-1):
        """Receive the response from the slave"""
        response = utils.to_data("")
        while True:
            read_bytes = self._serial.read(expected_length if expected_length > 0 else 1)
            if not read_bytes:
                break
            response += read_bytes
            if expected_length >= 0 and len(response) >= expected_length:
                #if the expected number of byte is received consider that the response is done
                #improve performance by avoiding end-of-response detection by timeout
                break

        retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response))
        if retval is not None:
            return retval
        return response
Example #38
0
 def _recv(self, expected_length=-1):
     """
     Receive the response from the slave
     Do not take expected_length into account because the length of the response is
     written in the mbap. Used for RTU only
     """
     response = to_data('')
     length = 255
     while len(response) < length:
         rcv_byte = self._sock.recv(1)
         if rcv_byte:
             response += rcv_byte
             if len(response) == 6:
                 to_be_recv_length = struct.unpack(">HHH", response)[2]
                 length = to_be_recv_length + 6
         else:
             break
     retval = call_hooks("modbus_tcp.TcpMaster.after_recv", (self, response))
     if retval is not None:
         return retval
     return response
Example #39
0
 def _recv(self, expected_length=-1):
     """
     Receive the response from the slave
     Do not take expected_length into account because the length of the response is
     written in the mbap. Used for RTU only
     """
     response = to_data('')
     length = 255
     while len(response) < length:
         rcv_byte = self._sock.recv(1)
         if rcv_byte:
             response += rcv_byte
             if len(response) == 6:
                 to_be_recv_length = struct.unpack(">HHH", response)[2]
                 length = to_be_recv_length + 6
         else:
             break
     retval = call_hooks("modbus_tcp.TcpMaster.after_recv", (self, response))
     if retval is not None:
         return retval
     return response
Example #40
0
    def _recv(self, expected_length=-1):
        """Receive the response from the slave"""
        response = utils.to_data("")
        start_time = time.time() if self.use_sw_timeout else 0
        while True:
            read_bytes = self._serial.read(expected_length if expected_length > 0 else 1)
            if self.use_sw_timeout:
                read_duration = time.time() - start_time
            else:
                read_duration = 0
            if (not read_bytes) or (read_duration > self._serial.timeout):
                break
            response += read_bytes
            if expected_length >= 0 and len(response) >= expected_length:
                # if the expected number of byte is received consider that the response is done
                # improve performance by avoiding end-of-response detection by timeout
                break

        retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response))
        if retval is not None:
            return retval
        return response
Example #41
0
 def _do_close(self):
     """Close the serial port if still opened"""
     if self._serial.isOpen():
         self._serial.close()
         call_hooks("modbus_rtu.RtuMaster.after_close", (self, ))
Example #42
0
 def close(self):
     """close the serial communication"""
     if self._serial.is_open:
         call_hooks("modbus_rtu.RtuServer.before_close", (self, ))
         self._serial.close()
         call_hooks("modbus_rtu.RtuServer.after_close", (self, ))
Example #43
0
 def _do_open(self):
     """Open the given serial port if not already opened"""
     if not self._serial.is_open:
         call_hooks("modbus_rtu.RtuMaster.before_open", (self, ))
         self._serial.open()
Example #44
0
 def _do_init(self):
     """initialize the serial connection"""
     if not self._serial.isOpen():
         call_hooks("modbus_rtu.RtuServer.before_open", (self, ))
         self._serial.open()
         call_hooks("modbus_rtu.RtuServer.after_open", (self, ))
Example #45
0
 def _read_input_registers(self, request_pdu):
     """handle read coils modbus function"""
     call_hooks("modbus.Slave.handle_read_input_registers_request", (self, request_pdu))
     return self._read_registers(defines.ANALOG_INPUTS, request_pdu)
Example #46
0
 def _read_holding_registers(self, request_pdu):
     """handle read coils modbus function"""
     call_hooks("modbus.Slave.handle_read_holding_registers_request", (self, request_pdu))
     return self._read_registers(defines.HOLDING_REGISTERS, request_pdu)
Example #47
0
 def _read_discrete_inputs(self, request_pdu):
     """handle read discrete inputs modbus function"""
     call_hooks("modbus.Slave.handle_read_discrete_inputs_request", (self, request_pdu))
     return self._read_digital(defines.DISCRETE_INPUTS, request_pdu)
Example #48
0
 def _read_coils(self, request_pdu):
     """handle read coils modbus function"""
     call_hooks("modbus.Slave.handle_read_coils_request", (self, request_pdu))
     return self._read_digital(defines.COILS, request_pdu)
Example #49
0
 def __setitem__(self, item, value):
     """"""
     call_hooks("modbus.ModbusBlock.setitem", (self, item, value))
     return self._data.__setitem__(item, value)
Example #50
0
 def _read_holding_registers(self, request_pdu):
     """handle read coils modbus function"""
     call_hooks("modbus.Slave.handle_read_holding_registers_request", (self, request_pdu))
     return self._read_registers(defines.HOLDING_REGISTERS, request_pdu)
Example #51
0
 def close(self):
     """close the serial communication"""
     if self._serial.isOpen():
         call_hooks("modbus_rtu.RtuServer.before_close", (self, ))
         self._serial.close()
         call_hooks("modbus_rtu.RtuServer.after_close", (self, ))
Example #52
0
 def _read_coils(self, request_pdu):
     """handle read coils modbus function"""
     call_hooks("modbus.Slave.handle_read_coils_request", (self, request_pdu))
     return self._read_digital(defines.COILS, request_pdu)
Example #53
0
 def _do_open(self):
     """Open the given serial port if not already opened"""
     if not self._serial.isOpen():
         call_hooks("modbus_rtu.RtuMaster.before_open", (self, ))
         self._serial.open()
Example #54
0
 def __setitem__(self, item, value):
     """"""
     call_hooks("modbus.ModbusBlock.setitem", (self, item, value))
     return self._data.__setitem__(item, value)
Example #55
0
 def _do_close(self):
     """Close the serial port if still opened"""
     if self._serial.is_open:
         self._serial.close()
         call_hooks("modbus_rtu.RtuMaster.after_close", (self, ))
         return True
Example #56
0
 def _read_input_registers(self, request_pdu):
     """handle read coils modbus function"""
     call_hooks("modbus.Slave.handle_read_input_registers_request", (self, request_pdu))
     return self._read_registers(defines.ANALOG_INPUTS, request_pdu)
Example #57
0
 def _do_init(self):
     """initialize the serial connection"""
     if not self._serial.is_open:
         call_hooks("modbus_rtu.RtuServer.before_open", (self, ))
         self._serial.open()
         call_hooks("modbus_rtu.RtuServer.after_open", (self, ))
Example #58
0
    def _do_run(self):
        """called in a almost-for-ever loop by the server"""
        # check the status of every socket
        inputready = select.select(self._sockets, [], [], 1.0)[0]
        #print(inputready)

        # handle data on each a socket
        for sock in inputready:
            try:
                if sock == self._sock:
                    #print (self._sock)
                    # handle the server socket
                    #print ("Receiveeeeeeeeeeeeeeeeeeeee")
                    client, address = self._sock.accept()
                    client.setblocking(0)
                    LOGGER.debug("%s is connected with socket %d...",
                                 str(address), client.fileno())
                    self._sockets.append(client)
                    call_hooks("modbus_tcp.TcpServer.on_connect",
                               (self, client, address))
                    return
                else:
                    if len(sock.recv(1, socket.MSG_PEEK)) == 0:
                        # socket is disconnected
                        LOGGER.debug("%d is disconnected" % (sock.fileno()))
                        call_hooks("modbus_tcp.TcpServer.on_disconnect",
                                   (self, sock))
                        sock.close()
                        self._sockets.remove(sock)
                        break

                    # handle all other sockets
                    sock.settimeout(1.0)
                    request = to_data("")
                    is_ok = True

                    # read the 7 bytes of the mbap
                    while (len(request) < 7) and is_ok:
                        new_byte = sock.recv(1)
                        if len(new_byte) == 0:
                            is_ok = False
                        else:
                            request += new_byte

                    retval = call_hooks("modbus_tcp.TcpServer.after_recv",
                                        (self, sock, request))
                    if retval is not None:
                        request = retval

                    if is_ok:
                        # read the rest of the request
                        length = self._get_request_length(request)
                        while (len(request) < (length + 6)) and is_ok:
                            new_byte = sock.recv(1)
                            if len(new_byte) == 0:
                                is_ok = False
                            else:
                                request += new_byte

                    if is_ok:
                        response = ""
                        # parse the request
                        try:
                            response = self._handle(request)
                        except Exception as msg:
                            LOGGER.error(
                                "Error while handling a request, Exception occurred: %s",
                                msg)

                        # send back the response
                        if response:
                            try:
                                retval = call_hooks(
                                    "modbus_tcp.TcpServer.before_send",
                                    (self, sock, response))
                                if retval is not None:
                                    response = retval
                                sock.send(response)
                                call_hooks("modbus_tcp.TcpServer.after_send",
                                           (self, sock, response))
                            except Exception as msg:
                                is_ok = False
                                LOGGER.error(
                                    "Error while sending on socket %d, Exception occurred: %s",
                                    sock.fileno(), msg)
            except Exception as excpt:
                LOGGER.warning("Error while processing data on socket %d: %s",
                               sock.fileno(), excpt)
                call_hooks("modbus_tcp.TcpServer.on_error",
                           (self, sock, excpt))
                sock.close()
                self._sockets.remove(sock)
Example #59
0
    def execute(
        self,
        slave,
        function_code,
        starting_address,
        quantity_of_x=0,
        output_value=0,
        data_format="",
        expected_length=-1,
    ):
        """
        Execute a modbus query and returns the data part of the answer as a tuple
        The returned tuple depends on the query function code. see modbus protocol
        specification for details
        data_format makes possible to extract the data like defined in the
        struct python module documentation
        """

        pdu = ""
        is_read_function = False
        nb_of_digits = 0

        # open the connection if it is not already done
        self.open()

        # Build the modbus pdu and the format of the expected data.
        # It depends of function code. see modbus specifications for details.
        if function_code == defines.READ_COILS or function_code == defines.READ_DISCRETE_INPUTS:
            is_read_function = True
            pdu = struct.pack(">BHH", function_code, starting_address, quantity_of_x)
            byte_count = quantity_of_x / 8
            if (quantity_of_x % 8) > 0:
                byte_count += 1
            nb_of_digits = quantity_of_x
            if not data_format:
                data_format = ">" + (byte_count * "B")
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # #slave + func + bytcodeLen + bytecode + crc1 + crc2
                expected_length = byte_count + 5

        elif function_code == defines.READ_INPUT_REGISTERS or function_code == defines.READ_HOLDING_REGISTERS:
            is_read_function = True
            pdu = struct.pack(">BHH", function_code, starting_address, quantity_of_x)
            if not data_format:
                data_format = ">" + (quantity_of_x * "H")
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2
                expected_length = 2 * quantity_of_x + 5

        elif (function_code == defines.WRITE_SINGLE_COIL) or (function_code == defines.WRITE_SINGLE_REGISTER):
            if function_code == defines.WRITE_SINGLE_COIL:
                if output_value != 0:
                    output_value = 0xFF00
            fmt = ">BH" + ("H" if output_value >= 0 else "h")
            pdu = struct.pack(fmt, function_code, starting_address, output_value)
            if not data_format:
                data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + value1+value2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.WRITE_MULTIPLE_COILS:
            byte_count = len(output_value) / 8
            if (len(output_value) % 8) > 0:
                byte_count += 1
            pdu = struct.pack(">BHHB", function_code, starting_address, len(output_value), byte_count)
            i, byte_value = 0, 0
            for j in output_value:
                if j > 0:
                    byte_value += pow(2, i)
                if i == 7:
                    pdu += struct.pack(">B", byte_value)
                    i, byte_value = 0, 0
                else:
                    i += 1
            if i > 0:
                pdu += struct.pack(">B", byte_value)
            if not data_format:
                data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.WRITE_MULTIPLE_REGISTERS:
            byte_count = 2 * len(output_value)
            pdu = struct.pack(">BHHB", function_code, starting_address, len(output_value), byte_count)
            for j in output_value:
                fmt = "H" if j >= 0 else "h"
                pdu += struct.pack(">" + fmt, j)
            if not data_format:
                data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.READ_EXCEPTION_STATUS:
            pdu = struct.pack(">B", function_code)
            data_format = ">B"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                expected_length = 5

        elif function_code == defines.DIAGNOSTIC:
            # SubFuncCode  are in starting_address
            pdu = struct.pack(">BH", function_code, starting_address)
            if len(output_value) > 0:
                for j in output_value:
                    # copy data in pdu
                    pdu += struct.pack(">B", j)
                if not data_format:
                    data_format = ">" + (len(output_value) * "B")
                if expected_length < 0:
                    # No length was specified and calculated length can be used:
                    # slave + func + SubFunc1 + SubFunc2 + Data + crc1 + crc2
                    expected_length = len(output_value) + 6

        elif function_code == defines.READ_WRITE_MULTIPLE_REGISTERS:
            is_read_function = True
            byte_count = 2 * len(output_value)
            pdu = struct.pack(
                ">BHHHHB",
                function_code,
                starting_address,
                quantity_of_x,
                defines.READ_WRITE_MULTIPLE_REGISTERS,
                len(output_value),
                byte_count,
            )
            for j in output_value:
                fmt = "H" if j >= 0 else "h"
                # copy data in pdu
                pdu += struct.pack(">" + fmt, j)
            if not data_format:
                data_format = ">" + (quantity_of_x * "H")
            if expected_length < 0:
                # No lenght was specified and calculated length can be used:
                # slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2
                expected_length = 2 * quantity_of_x + 5
        else:
            raise ModbusFunctionNotSupportedError("The {0} function code is not supported. ".format(function_code))

        # instantiate a query which implements the MAC (TCP or RTU) part of the protocol
        query = self._make_query()

        # add the mac part of the protocol to the request
        request = query.build_request(pdu, slave)

        # send the request to the slave
        retval = call_hooks("modbus.Master.before_send", (self, request))
        if retval is not None:
            request = retval
        if self._verbose:
            LOGGER.debug(get_log_buffer("-> ", request))
        self._send(request)

        call_hooks("modbus.Master.after_send", (self,))

        if slave != 0:
            # receive the data from the slave
            response = self._recv(expected_length)
            retval = call_hooks("modbus.Master.after_recv", (self, response))
            if retval is not None:
                response = retval
            if self._verbose:
                LOGGER.debug(get_log_buffer("<- ", response))

            # extract the pdu part of the response
            response_pdu = query.parse_response(response)

            # analyze the received data
            (return_code, byte_2) = struct.unpack(">BB", response_pdu[0:2])

            if return_code > 0x80:
                # the slave has returned an error
                exception_code = byte_2
                raise ModbusError(exception_code)
            else:
                if is_read_function:
                    # get the values returned by the reading function
                    byte_count = byte_2
                    data = response_pdu[2:]
                    if byte_count != len(data):
                        # the byte count in the pdu is invalid
                        raise ModbusInvalidResponseError(
                            "Byte count is {0} while actual number of bytes is {1}. ".format(byte_count, len(data))
                        )
                else:
                    # returns what is returned by the slave after a writing function
                    data = response_pdu[1:]

                # returns the data as a tuple according to the data_format
                # (calculated based on the function or user-defined)
                result = struct.unpack(data_format, data)
                if nb_of_digits > 0:
                    digits = []
                    for byte_val in result:
                        for i in xrange(8):
                            if len(digits) >= nb_of_digits:
                                break
                            digits.append(byte_val % 2)
                            byte_val = byte_val >> 1
                    result = tuple(digits)
                return result
Example #60
0
 def _read_discrete_inputs(self, request_pdu):
     """handle read discrete inputs modbus function"""
     call_hooks("modbus.Slave.handle_read_discrete_inputs_request", (self, request_pdu))
     return self._read_digital(defines.DISCRETE_INPUTS, request_pdu)