예제 #1
0
 def __init__(self, lib_location=None):
     if self.cdll:
         return
     self.lib_location = lib_location or self.lib_location or find_library('snap7')
     if not self.lib_location:
         msg = "can't find snap7 library. If installed, try running ldconfig"
         raise Snap7Exception(msg)
     self.cdll = cdll.LoadLibrary(self.lib_location)
예제 #2
0
    def set_value(
            self, byte_index: Union[str, int], type: str,
            value: Union[bool, str, int, float]) -> Union[bytearray, None]:
        bytearray_ = self.get_bytearray()

        if type == 'BOOL' and isinstance(value, bool):
            """
            mypy conform style:
            if isinstance(byte_index, str):
                byte_index, bool_index = byte_index.split('.')
                return set_bool(bytearray_, self.get_offset(byte_index),
                                int(bool_index), value)
            """
            byte_index, bool_index = str(byte_index).split(".")
            return set_bool(bytearray_, self.get_offset(byte_index),
                            int(bool_index), value)

        byte_index = self.get_offset(byte_index)

        if type.startswith('STRING') and isinstance(value, str):
            max_size = re.search(r'\d+', type)
            if max_size is None:
                raise Snap7Exception(
                    "Max size could not be determinate. re.search() returned None"
                )
            max_size_grouped = max_size.group(0)
            max_size_int = int(max_size_grouped)
            return set_string(bytearray_, byte_index, value, max_size_int)

        elif type == 'REAL':
            return set_real(bytearray_, byte_index, value)

        elif type == 'DWORD' and isinstance(value, int):
            return set_dword(bytearray_, byte_index, value)

        elif type == 'DINT' and isinstance(value, int):
            return set_dint(bytearray_, byte_index, value)

        elif type == 'INT' and isinstance(value, int):
            return set_int(bytearray_, byte_index, value)

        elif type == 'WORD' and isinstance(value, int):
            return set_word(bytearray_, byte_index, value)

        elif type == 'USINT' and isinstance(value, int):
            return set_usint(bytearray_, byte_index, value)

        elif type == 'SINT' and isinstance(value, int):
            return set_sint(bytearray_, byte_index, value)

        if type == 'USINT' and isinstance(value, int):
            return set_usint(bytearray_, byte_index, value)

        if type == 'SINT' and isinstance(value, int):
            return set_sint(bytearray_, byte_index, value)

        raise ValueError
예제 #3
0
def check_error(code: int, context: str = "client"):
    """
    check if the error code is set. If so, a Python log message is generated
    and an error is raised.
    """
    if code and code != 1:
        error = error_text(code, context)
        logger.error(error)
        raise Snap7Exception(error)
예제 #4
0
 def as_list_blocks_of_type(self, blocktype, data, count) -> int:
     blocktype = snap7.types.block_types.get(blocktype)
     if not blocktype:
         raise Snap7Exception("The blocktype parameter was invalid")
     result = self._library.Cli_AsListBlocksOfType(self._pointer, blocktype,
                                                   byref(data),
                                                   byref(count))
     check_error(result, context="client")
     return result
예제 #5
0
 def __init__(self, lib_location: Optional[str] = None):
     if self.cdll:  # type: ignore
         return
     self.lib_location = (lib_location or self.lib_location
                          or find_in_package() or find_library('snap7')
                          or find_locally('snap7'))
     if not self.lib_location:
         msg = "can't find snap7 library. If installed, try running ldconfig"
         raise Snap7Exception(msg)
     self.cdll = cdll.LoadLibrary(self.lib_location)
예제 #6
0
 def _as_check_loop(self, check_times=20) -> int:
     check_status = ctypes.c_int(-1)
     # preparing Server values
     for i in range(check_times):
         self.client.check_as_completion(ctypes.byref(check_status))
         if check_status.value == 0:
             break
         time.sleep(0.5)
     else:
         raise Snap7Exception(f"Async Request not finished after {check_times} times - Fail")
     return check_status.value
예제 #7
0
    def set_connection_type(self, connection_type):
        """
        Sets the connection resource type, i.e the way in which the Clients
        connects to a PLC.

        :param connection_type: 1 for PG, 2 for OP, 3 to 10 for S7 Basic
        """
        result = self._library.Cli_SetConnectionType(self._pointer,
                                                     c_uint16(connection_type))
        if result != 0:
            raise Snap7Exception("The parameter was invalid")
예제 #8
0
    def get_cpu_state(self) -> str:
        """
        Retrieves CPU state from client
        """
        state = c_int(0)
        self._library.Cli_GetPlcStatus(self._pointer, byref(state))
        try:
            status_string = cpu_statuses[state.value]
        except KeyError:
            raise Snap7Exception(f"The cpu state ({state.value}) is invalid")

        logger.debug(f"CPU state is {status_string}")
        return status_string
예제 #9
0
    def set_connection_type(self, connection_type: int):
        """Sets the connection resource type, i.e the way in which the Clients
            connects to a PLC.

        Args:
            connection_type: 1 for PG, 2 for OP, 3 to 10 for S7 Basic

        Raises:
            :obj:`Snap7Exception`: if the snap7 error code is diferent from 0.
        """
        result = self.library.Cli_SetConnectionType(self.pointer,
                                                    c_uint16(connection_type))
        if result != 0:
            raise Snap7Exception("The parameter was invalid")
예제 #10
0
    def set_connection_params(self, address, local_tsap, remote_tsap):
        """
        Sets internally (IP, LocalTSAP, RemoteTSAP) Coordinates.
        This function must be called just before Cli_Connect().

        :param address: PLC/Equipment IPV4 Address, for example "192.168.1.12"
        :param local_tsap: Local TSAP (PC TSAP)
        :param remote_tsap: Remote TSAP (PLC TSAP)
        """
        assert re.match(ipv4, address), f'{address} is invalid ipv4'
        result = self._library.Cli_SetConnectionParams(self._pointer, address,
                                                       c_uint16(local_tsap),
                                                       c_uint16(remote_tsap))
        if result != 0:
            raise Snap7Exception("The parameter was invalid")
예제 #11
0
def check_error(code: int, context: str = "client") -> None:
    """Check if the error code is set. If so, a Python log message is generated
        and an error is raised.

    Args:
        code: error code number.
        context: context in which is called.

    Raises:
        Snap7Exception: if the code exists and is diferent from 1.
    """
    if code and code != 1:
        error = error_text(code, context)
        logger.error(error)
        raise Snap7Exception(error)
예제 #12
0
    def get_block_info(self, blocktype, db_number) -> TS7BlockInfo:
        """Returns the block information for the specified block."""

        blocktype = snap7.types.block_types.get(blocktype)

        if not blocktype:
            raise Snap7Exception("The blocktype parameter was invalid")
        logger.debug(
            f"retrieving block info for block {db_number} of type {blocktype}")

        data = TS7BlockInfo()

        result = self._library.Cli_GetAgBlockInfo(self._pointer, blocktype,
                                                  db_number, byref(data))
        check_error(result, context="client")
        return data
예제 #13
0
    def set_connection_params(self, ip_address, tsap_snap7, tsap_logo):
        """
        Sets internally (IP, LocalTSAP, RemoteTSAP) Coordinates.
        This function must be called just before Cli_Connect().

        :param ip_address: IP ip_address of server
        :param tsap_snap7: TSAP SNAP7 Client (e.g. 10.00 = 0x1000)
        :param tsap_logo: TSAP Logo Server (e.g. 20.00 = 0x2000)
        """
        assert re.match(ipv4, ip_address), f'{ip_address} is invalid ipv4'
        result = self.library.Cli_SetConnectionParams(self.pointer,
                                                      ip_address.encode(),
                                                      c_uint16(tsap_snap7),
                                                      c_uint16(tsap_logo))
        if result != 0:
            raise Snap7Exception("The parameter was invalid")
예제 #14
0
    def check_as_b_send_completion(self) -> Tuple[str, c_int32]:
        """
        Checks if the current asynchronous send job was completed and terminates
        immediately.
        """
        op_result = c_int32()
        result = self._library.Par_CheckAsBSendCompletion(
            self._pointer, byref(op_result))
        return_values = {
            0: "job complete",
            1: "job in progress",
            -2: "invalid handled supplied",
        }

        if result == -2:
            raise Snap7Exception("The Client parameter was invalid")

        return return_values[result], op_result
예제 #15
0
    def __init__(self, lib_location: Optional[str] = None):
        """ Loads the snap7 library using ctypes cdll.

        Args:
            lib_location: full path to the `snap7.dll` file. Optional.

        Raises:
            Snap7Exception: if `lib_location` is not found.
        """
        if self.cdll:  # type: ignore
            return
        self.lib_location = (lib_location or self.lib_location
                             or find_in_package() or find_library('snap7')
                             or find_locally('snap7'))
        if not self.lib_location:
            msg = "can't find snap7 library. If installed, try running ldconfig"
            raise Snap7Exception(msg)
        self.cdll = cdll.LoadLibrary(self.lib_location)
예제 #16
0
    def list_blocks_of_type(self, blocktype, size: int) -> Union[int, Array]:
        """This function returns the AG list of a specified block type."""

        blocktype = snap7.types.block_types.get(blocktype)
        if not blocktype:
            raise Snap7Exception("The blocktype parameter was invalid")

        logger.debug(f"listing blocks of type: {blocktype} size: {size}")

        if size == 0:
            return 0

        data = (c_uint16 * size)()
        count = c_int(size)
        result = self._library.Cli_ListBlocksOfType(self._pointer, blocktype,
                                                    byref(data), byref(count))

        logger.debug(f"number of items found: {count}")

        check_error(result, context="client")
        return data
예제 #17
0
    def set_connection_params(self, ip_address: str, tsap_snap7: int,
                              tsap_logo: int):
        """Sets internally (IP, LocalTSAP, RemoteTSAP) Coordinates.

        Notes:
            This function must be called just before Cli_Connect().

        Args:
            ip_address: IP ip_address of server
            tsap_snap7: TSAP SNAP7 Client (e.g. 10.00 = 0x1000)
            tsap_logo: TSAP Logo Server (e.g. 20.00 = 0x2000)

        Raises:
            :obj:`ValueError`: if the `ip_address` is not an IPV4.
            :obj:`Snap7Exception`: if the snap7 error code is diferent from 0.
        """
        if not re.match(ipv4, ip_address):
            raise ValueError(f"{ip_address} is invalid ipv4")
        result = self.library.Cli_SetConnectionParams(self.pointer,
                                                      ip_address.encode(),
                                                      c_uint16(tsap_snap7),
                                                      c_uint16(tsap_logo))
        if result != 0:
            raise Snap7Exception("The parameter was invalid")
예제 #18
0
    def get_value(self, byte_index: Union[str, int],
                  type_: str) -> Union[ValueError, int, float, str, datetime]:
        """ Gets the value for a specific type.

        Args:
            byte_index: byte index from where start reading.
            type_: type of data to read.

        Raises:
            :obj:`Snap7Exception`: if reading a `string` when checking the lenght of the string.
            :obj:`ValueError`: if the `type_` is not handled.

        Returns:
            Value read according to the `type_`
        """

        bytearray_ = self.get_bytearray()

        # set parsing non case-sensitive
        type_ = type_.upper()

        if type_ == 'BOOL':
            byte_index, bool_index = str(byte_index).split('.')
            return get_bool(bytearray_, self.get_offset(byte_index),
                            int(bool_index))

        # remove 4 from byte index since
        # first 4 bytes are used by db
        byte_index = self.get_offset(byte_index)

        if type_.startswith('STRING'):
            max_size = re.search(r'\d+', type_)
            if max_size is None:
                raise Snap7Exception(
                    "Max size could not be determinate. re.search() returned None"
                )
            max_size_grouped = max_size.group(0)
            max_size_int = int(max_size_grouped)
            return get_string(bytearray_, byte_index, max_size_int)

        elif type_ == 'REAL':
            return get_real(bytearray_, byte_index)

        elif type_ == 'DWORD':
            return get_dword(bytearray_, byte_index)

        elif type_ == 'UDINT':
            return get_udint(bytearray_, byte_index)

        elif type_ == 'DINT':
            return get_dint(bytearray_, byte_index)

        elif type_ == 'UINT':
            return get_uint(bytearray_, byte_index)

        elif type_ == 'INT':
            return get_int(bytearray_, byte_index)

        elif type_ == 'WORD':
            return get_word(bytearray_, byte_index)

        elif type_ == 'BYTE':
            return get_byte(bytearray_, byte_index)

        elif type_ == 'S5TIME':
            data_s5time = get_s5time(bytearray_, byte_index)
            return data_s5time

        elif type_ == 'DATE_AND_TIME':
            data_dt = get_dt(bytearray_, byte_index)
            return data_dt

        elif type_ == 'USINT':
            return get_usint(bytearray_, byte_index)

        elif type_ == 'SINT':
            return get_sint(bytearray_, byte_index)

        # add these three not implemented data typ to avoid
        # 'Unable to get repr for class<snap7.util.DB_ROW>' error
        elif type_ == 'TIME':
            return get_time(bytearray_, byte_index)

        elif type_ == 'DATE':
            return 'read DATE not implemented'

        elif type_ == 'TIME_OF_DAY':
            return 'read TIME_OF_DAY not implemented'

        raise ValueError
예제 #19
0
    def set_value(
            self, byte_index: Union[str, int], type: str,
            value: Union[bool, str, int, float]) -> Union[bytearray, None]:
        """Sets the value for a specific type in the specified byte index.

        Args:
            byte_index: byte index to start writing to.
            type: type of value to write.
            value: value to write.

        Raises:
            :obj:`Snap7Exception`: if reading a `string` when checking the lenght of the string.
            :obj:`ValueError`: if the `type_` is not handled.

        Returns:
            Buffer data with the value written. Optional.
        """
        bytearray_ = self.get_bytearray()

        if type == 'BOOL' and isinstance(value, bool):
            byte_index, bool_index = str(byte_index).split(".")
            return set_bool(bytearray_, self.get_offset(byte_index),
                            int(bool_index), value)

        byte_index = self.get_offset(byte_index)

        if type.startswith('STRING') and isinstance(value, str):
            max_size = re.search(r'\d+', type)
            if max_size is None:
                raise Snap7Exception(
                    "Max size could not be determinate. re.search() returned None"
                )
            max_size_grouped = max_size.group(0)
            max_size_int = int(max_size_grouped)
            return set_string(bytearray_, byte_index, value, max_size_int)

        elif type == 'REAL':
            return set_real(bytearray_, byte_index, value)

        elif type == 'DWORD' and isinstance(value, int):
            return set_dword(bytearray_, byte_index, value)

        elif type == 'UDINT' and isinstance(value, int):
            return set_udint(bytearray_, byte_index, value)

        elif type == 'DINT' and isinstance(value, int):
            return set_dint(bytearray_, byte_index, value)

        elif type == 'UINT' and isinstance(value, int):
            return set_uint(bytearray_, byte_index, value)

        elif type == 'INT' and isinstance(value, int):
            return set_int(bytearray_, byte_index, value)

        elif type == 'WORD' and isinstance(value, int):
            return set_word(bytearray_, byte_index, value)

        elif type == 'USINT' and isinstance(value, int):
            return set_usint(bytearray_, byte_index, value)

        elif type == 'SINT' and isinstance(value, int):
            return set_sint(bytearray_, byte_index, value)

        elif type == 'USINT' and isinstance(value, int):
            return set_usint(bytearray_, byte_index, value)

        elif type == 'SINT' and isinstance(value, int):
            return set_sint(bytearray_, byte_index, value)

        elif type == 'TIME' and isinstance(value, str):
            return set_time(bytearray_, byte_index, value)

        raise ValueError
예제 #20
0
    def get_value(self, byte_index: Union[str, int],
                  type_: str) -> Union[ValueError, int, float, str, datetime]:
        bytearray_ = self.get_bytearray()

        if type_ == 'BOOL':
            byte_index, bool_index = str(byte_index).split('.')
            return get_bool(bytearray_, self.get_offset(byte_index),
                            int(bool_index))

        # remove 4 from byte index since
        # first 4 bytes are used by db
        byte_index = self.get_offset(byte_index)

        if type_.startswith('STRING'):
            max_size = re.search(r'\d+', type_)
            if max_size is None:
                raise Snap7Exception(
                    "Max size could not be determinate. re.search() returned None"
                )
            max_size_grouped = max_size.group(0)
            max_size_int = int(max_size_grouped)
            return get_string(bytearray_, byte_index, max_size_int)

        elif type_ == 'REAL':
            return get_real(bytearray_, byte_index)

        elif type_ == 'DWORD':
            return get_dword(bytearray_, byte_index)

        elif type_ == 'DINT':
            return get_dint(bytearray_, byte_index)

        elif type_ == 'INT':
            return get_int(bytearray_, byte_index)

        elif type_ == 'WORD':
            return get_word(bytearray_, byte_index)

        elif type_ == 'S5TIME':
            data_s5time = get_s5time(bytearray_, byte_index)
            return data_s5time

        elif type_ == 'DATE_AND_TIME':
            data_dt = get_dt(bytearray_, byte_index)
            return data_dt

        elif type_ == 'USINT':
            return get_usint(bytearray_, byte_index)

        elif type_ == 'SINT':
            return get_sint(bytearray_, byte_index)

        # add these three not implemented data typ to avoid
        # 'Unable to get repr for class<snap7.util.DB_ROW>' error
        elif type_ == 'TIME':
            return 'read TIME not implemented'

        elif type_ == 'DATE':
            return 'read DATE not implemented'

        elif type_ == 'TIME_OF_DAY':
            return 'read TIME_OF_DAY not implemented'

        raise ValueError