diff --git a/POCloud_Driver/pycomm_micro/__init__.py b/POCloud_Driver/pycomm_micro/__init__.py deleted file mode 100644 index 8c1f233..0000000 --- a/POCloud_Driver/pycomm_micro/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'agostino' diff --git a/POCloud_Driver/pycomm_micro/ab_comm/__init__.py b/POCloud_Driver/pycomm_micro/ab_comm/__init__.py deleted file mode 100644 index 28c38a3..0000000 --- a/POCloud_Driver/pycomm_micro/ab_comm/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -__author__ = 'agostino' -import logging diff --git a/POCloud_Driver/pycomm_micro/ab_comm/clx.py b/POCloud_Driver/pycomm_micro/ab_comm/clx.py deleted file mode 100644 index 0ba1586..0000000 --- a/POCloud_Driver/pycomm_micro/ab_comm/clx.py +++ /dev/null @@ -1,847 +0,0 @@ -# -*- coding: utf-8 -*- -# -# clx.py - Ethernet/IP Client for Rockwell PLCs -# -# -# Copyright (c) 2014 Agostino Ruscito -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -from pycomm_micro.cip.cip_base import * -from pycomm_micro.common import setup_logger -import logging - - -class Driver(Base): - """ - This Ethernet/IP client is based on Rockwell specification. Please refer to the link below for details. - - http://literature.rockwellautomation.com/idc/groups/literature/documents/pm/1756-pm020_-en-p.pdf - - The following services have been implemented: - - Read Tag Service (0x4c) - - Read Tag Fragment Service (0x52) - - Write Tag Service (0x4d) - - Write Tag Fragment Service (0x53) - - Multiple Service Packet (0x0a) - - The client has been successfully tested with the following PLCs: - - CompactLogix 5330ERM - - CompactLogix 5370 - - ControlLogix 5572 and 1756-EN2T Module - -""" - def __init__(self, debug=False, filename=None): - if debug: - super(Driver, self).__init__(setup_logger('ab_comm.clx', logging.DEBUG, filename)) - else: - super(Driver, self).__init__(setup_logger('ab_comm.clx', logging.INFO, filename)) - - self._buffer = {} - self._get_template_in_progress = False - self.__version__ = '0.2' - - def get_last_tag_read(self): - """ Return the last tag read by a multi request read - - :return: A tuple (tag name, value, type) - """ - return self._last_tag_read - - def get_last_tag_write(self): - """ Return the last tag write by a multi request write - - :return: A tuple (tag name, 'GOOD') if the write was successful otherwise (tag name, 'BAD') - """ - return self._last_tag_write - - def _parse_instance_attribute_list(self, start_tag_ptr, status): - """ extract the tags list from the message received - - :param start_tag_ptr: The point in the message string where the tag list begin - :param status: The status of the message receives - """ - tags_returned = self._reply[start_tag_ptr:] - tags_returned_length = len(tags_returned) - idx = 0 - instance = 0 - count = 0 - try: - while idx < tags_returned_length: - instance = unpack_dint(tags_returned[idx:idx+4]) - idx += 4 - tag_length = unpack_uint(tags_returned[idx:idx+2]) - idx += 2 - tag_name = tags_returned[idx:idx+tag_length] - idx += tag_length - symbol_type = unpack_uint(tags_returned[idx:idx+2]) - idx += 2 - count += 1 - self._tag_list.append({'instance_id': instance, - 'tag_name': tag_name, - 'symbol_type': symbol_type}) - except Exception as e: - raise DataError(e) - - if status == SUCCESS: - self._last_instance = -1 - elif status == 0x06: - self._last_instance = instance + 1 - else: - self._status = (1, 'unknown status during _parse_tag_list') - self._last_instance = -1 - - def _parse_structure_makeup_attributes(self, start_tag_ptr, status): - """ extract the tags list from the message received - - :param start_tag_ptr: The point in the message string where the tag list begin - :param status: The status of the message receives - """ - self._buffer = {} - - if status != SUCCESS: - self._buffer['Error'] = status - return - - attribute = self._reply[start_tag_ptr:] - idx = 4 - try: - if unpack_uint(attribute[idx:idx + 2]) == SUCCESS: - idx += 2 - self._buffer['object_definition_size'] = unpack_dint(attribute[idx:idx + 4]) - else: - self._buffer['Error'] = 'object_definition Error' - return - - idx += 6 - if unpack_uint(attribute[idx:idx + 2]) == SUCCESS: - idx += 2 - self._buffer['structure_size'] = unpack_dint(attribute[idx:idx + 4]) - else: - self._buffer['Error'] = 'structure Error' - return - - idx += 6 - if unpack_uint(attribute[idx:idx + 2]) == SUCCESS: - idx += 2 - self._buffer['member_count'] = unpack_uint(attribute[idx:idx + 2]) - else: - self._buffer['Error'] = 'member_count Error' - return - - idx += 4 - if unpack_uint(attribute[idx:idx + 2]) == SUCCESS: - idx += 2 - self._buffer['structure_handle'] = unpack_uint(attribute[idx:idx + 2]) - else: - self._buffer['Error'] = 'structure_handle Error' - return - - return self._buffer - - except Exception as e: - raise DataError(e) - - def _parse_template(self, start_tag_ptr, status): - """ extract the tags list from the message received - - :param start_tag_ptr: The point in the message string where the tag list begin - :param status: The status of the message receives - """ - tags_returned = self._reply[start_tag_ptr:] - bytes_received = len(tags_returned) - - self._buffer += tags_returned - - if status == SUCCESS: - self._get_template_in_progress = False - - elif status == 0x06: - self._byte_offset += bytes_received - else: - self._status = (1, 'unknown status {0} during _parse_template'.format(status)) - self.logger.warning(self._status) - self._last_instance = -1 - - def _parse_fragment(self, start_ptr, status): - """ parse the fragment returned by a fragment service. - - :param start_ptr: Where the fragment start within the replay - :param status: status field used to decide if keep parsing or stop - """ - try: - data_type = unpack_uint(self._reply[start_ptr:start_ptr+2]) - fragment_returned = self._reply[start_ptr+2:] - except Exception as e: - raise DataError(e) - - fragment_returned_length = len(fragment_returned) - idx = 0 - - while idx < fragment_returned_length: - try: - typ = I_DATA_TYPE[data_type] - value = UNPACK_DATA_FUNCTION[typ](fragment_returned[idx:idx+DATA_FUNCTION_SIZE[typ]]) - idx += DATA_FUNCTION_SIZE[typ] - except Exception as e: - raise DataError(e) - self._tag_list.append((self._last_position, value)) - self._last_position += 1 - - if status == SUCCESS: - self._byte_offset = -1 - elif status == 0x06: - self._byte_offset += fragment_returned_length - else: - self._status = (2, 'unknown status during _parse_fragment') - self._byte_offset = -1 - - def _parse_multiple_request_read(self, tags): - """ parse the message received from a multi request read: - - For each tag parsed, the information extracted includes the tag name, the value read and the data type. - Those information are appended to the tag list as tuple - - :return: the tag list - """ - offset = 50 - position = 50 - try: - number_of_service_replies = unpack_uint(self._reply[offset:offset+2]) - tag_list = [] - for index in range(number_of_service_replies): - position += 2 - start = offset + unpack_uint(self._reply[position:position+2]) - general_status = unpack_usint(self._reply[start+2:start+3]) - - if general_status == 0: - data_type = unpack_uint(self._reply[start+4:start+6]) - value_begin = start + 6 - value_end = value_begin + DATA_FUNCTION_SIZE[I_DATA_TYPE[data_type]] - value = self._reply[value_begin:value_end] - self._last_tag_read = (tags[index], UNPACK_DATA_FUNCTION[I_DATA_TYPE[data_type]](value), - I_DATA_TYPE[data_type]) - else: - self._last_tag_read = (tags[index], None, None) - - tag_list.append(self._last_tag_read) - - return tag_list - except Exception as e: - raise DataError(e) - - def _parse_multiple_request_write(self, tags): - """ parse the message received from a multi request writ: - - For each tag parsed, the information extracted includes the tag name and the status of the writing. - Those information are appended to the tag list as tuple - - :return: the tag list - """ - offset = 50 - position = 50 - try: - number_of_service_replies = unpack_uint(self._reply[offset:offset+2]) - tag_list = [] - for index in range(number_of_service_replies): - position += 2 - start = offset + unpack_uint(self._reply[position:position+2]) - general_status = unpack_usint(self._reply[start+2:start+3]) - - if general_status == 0: - self._last_tag_write = (tags[index] + ('GOOD',)) - else: - self._last_tag_write = (tags[index] + ('BAD',)) - - tag_list.append(self._last_tag_write) - return tag_list - except Exception as e: - raise DataError(e) - - def _check_reply(self): - """ check the replayed message for error - - """ - self._more_packets_available = False - try: - if self._reply is None: - self._status = (3, '%s without reply' % REPLAY_INFO[unpack_dint(self._message[:2])]) - return False - # Get the type of command - typ = unpack_uint(self._reply[:2]) - - # Encapsulation status check - if unpack_dint(self._reply[8:12]) != SUCCESS: - self._status = (3, "{0} reply status:{1}".format(REPLAY_INFO[typ], - SERVICE_STATUS[unpack_dint(self._reply[8:12])])) - return False - - # Command Specific Status check - if typ == unpack_uint(ENCAPSULATION_COMMAND["send_rr_data"]): - status = unpack_usint(self._reply[42:43]) - if status != SUCCESS: - self._status = (3, "send_rr_data reply:{0} - Extend status:{1}".format( - SERVICE_STATUS[status], get_extended_status(self._reply, 42))) - return False - else: - return True - elif typ == unpack_uint(ENCAPSULATION_COMMAND["send_unit_data"]): - status = unpack_usint(self._reply[48:49]) - if unpack_usint(self._reply[46:47]) == I_TAG_SERVICES_REPLY["Read Tag Fragmented"]: - self._parse_fragment(50, status) - return True - if unpack_usint(self._reply[46:47]) == I_TAG_SERVICES_REPLY["Get Instance Attributes List"]: - self._parse_instance_attribute_list(50, status) - return True - if unpack_usint(self._reply[46:47]) == I_TAG_SERVICES_REPLY["Get Attributes"]: - self._parse_structure_makeup_attributes(50, status) - return True - if unpack_usint(self._reply[46:47]) == I_TAG_SERVICES_REPLY["Read Template"] and \ - self._get_template_in_progress: - self._parse_template(50, status) - return True - if status == 0x06: - self._status = (3, "Insufficient Packet Space") - self._more_packets_available = True - elif status != SUCCESS: - self._status = (3, "send_unit_data reply:{0} - Extend status:{1}".format( - SERVICE_STATUS[status], get_extended_status(self._reply, 48))) - return False - else: - return True - - return True - except Exception as e: - raise DataError(e) - - def read_tag(self, tag): - """ read tag from a connected plc - - Possible combination can be passed to this method: - - ('Counts') a single tag name - - (['ControlWord']) a list with one tag or many - - (['parts', 'ControlWord', 'Counts']) - - At the moment there is not a strong validation for the argument passed. The user should verify - the correctness of the format passed. - - :return: None is returned in case of error otherwise the tag list is returned - """ - multi_requests = False - if isinstance(tag, list): - multi_requests = True - - if not self._target_is_connected: - if not self.forward_open(): - self._status = (6, "Target did not connected. read_tag will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. read_tag will not be executed.") - # multi_requests = False - if multi_requests: - rp_list = [] - for t in tag: - rp = create_tag_rp(t, multi_requests=True) - if rp is None: - self._status = (6, "Cannot create tag {0} request packet. read_tag will not be executed.".format(tag)) - raise DataError("Cannot create tag {0} request packet. read_tag will not be executed.".format(tag)) - else: - rp_list.append(chr(TAG_SERVICES_REQUEST['Read Tag']) + rp + pack_uint(1)) - message_request = build_multiple_service(rp_list, Base._get_sequence()) - - else: - rp = create_tag_rp(tag) - if rp is None: - self._status = (6, "Cannot create tag {0} request packet. read_tag will not be executed.".format(tag)) - return None - else: - # Creating the Message Request Packet - message_request = [ - pack_uint(Base._get_sequence()), - chr(TAG_SERVICES_REQUEST['Read Tag']), # the Request Service - chr(len(rp) / 2), # the Request Path Size length in word - rp, # the request path - pack_uint(1) - ] - - if self.send_unit_data( - build_common_packet_format( - DATA_ITEM['Connected'], - ''.join(message_request), - ADDRESS_ITEM['Connection Based'], - addr_data=self._target_cid, - )) is None: - raise DataError("send_unit_data returned not valid data") - - if multi_requests: - return self._parse_multiple_request_read(tag) - else: - # Get the data type - data_type = unpack_uint(self._reply[50:52]) - # print I_DATA_TYPE[data_type] - try: - return UNPACK_DATA_FUNCTION[I_DATA_TYPE[data_type]](self._reply[52:]), I_DATA_TYPE[data_type] - except Exception as e: - raise DataError(e) - - def read_array(self, tag, counts): - """ read array of atomic data type from a connected plc - - At the moment there is not a strong validation for the argument passed. The user should verify - the correctness of the format passed. - - :param tag: the name of the tag to read - :param counts: the number of element to read - :return: None is returned in case of error otherwise the tag list is returned - """ - if not self._target_is_connected: - if not self.forward_open(): - self._status = (7, "Target did not connected. read_tag will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. read_tag will not be executed.") - - self._byte_offset = 0 - self._last_position = 0 - - self._tag_list = [] - while self._byte_offset != -1: - rp = create_tag_rp(tag) - if rp is None: - self._status = (7, "Cannot create tag {0} request packet. read_tag will not be executed.".format(tag)) - return None - else: - # Creating the Message Request Packet - message_request = [ - pack_uint(Base._get_sequence()), - chr(TAG_SERVICES_REQUEST["Read Tag Fragmented"]), # the Request Service - chr(len(rp) / 2), # the Request Path Size length in word - rp, # the request path - pack_uint(counts), - pack_dint(self._byte_offset) - ] - - if self.send_unit_data( - build_common_packet_format( - DATA_ITEM['Connected'], - ''.join(message_request), - ADDRESS_ITEM['Connection Based'], - addr_data=self._target_cid, - )) is None: - raise DataError("send_unit_data returned not valid data") - - return self._tag_list - - def write_tag(self, tag, value=None, typ=None): - """ write tag/tags from a connected plc - - Possible combination can be passed to this method: - - ('tag name', Value, data type) as single parameters or inside a tuple - - ([('tag name', Value, data type), ('tag name2', Value, data type)]) as array of tuples - - At the moment there is not a strong validation for the argument passed. The user should verify - the correctness of the format passed. - - The type accepted are: - - BOOL - - SINT - - INT' - - DINT - - REAL - - LINT - - BYTE - - WORD - - DWORD - - LWORD - - :param tag: tag name, or an array of tuple containing (tag name, value, data type) - :param value: the value to write or none if tag is an array of tuple or a tuple - :param typ: the type of the tag to write or none if tag is an array of tuple or a tuple - :return: None is returned in case of error otherwise the tag list is returned - """ - multi_requests = False - if isinstance(tag, list): - multi_requests = True - - if not self._target_is_connected: - if not self.forward_open(): - self._status = (8, "Target did not connected. write_tag will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. write_tag will not be executed.") - - if multi_requests: - rp_list = [] - tag_to_remove = [] - idx = 0 - for name, value, typ in tag: - # Create the request path to wrap the tag name - rp = create_tag_rp(name, multi_requests=True) - if rp is None: - self._status = (8, "Cannot create tag{0} req. packet. write_tag will not be executed".format(tag)) - return None - else: - try: # Trying to add the rp to the request path list - val = PACK_DATA_FUNCTION[typ](value) - rp_list.append( - chr(TAG_SERVICES_REQUEST['Write Tag']) - + rp - + pack_uint(S_DATA_TYPE[typ]) - + pack_uint(1) - + val - ) - idx += 1 - except (LookupError, struct.error) as e: - self._status = (8, "Tag:{0} type:{1} removed from write list. Error:{2}.".format(name, typ, e)) - - # The tag in idx position need to be removed from the rp list because has some kind of error - tag_to_remove.append(idx) - - # Remove the tags that have not been inserted in the request path list - for position in tag_to_remove: - del tag[position] - # Create the message request - message_request = build_multiple_service(rp_list, Base._get_sequence()) - - else: - if isinstance(tag, tuple): - name, value, typ = tag - else: - name = tag - - rp = create_tag_rp(name) - if rp is None: - self._status = (8, "Cannot create tag {0} request packet. write_tag will not be executed.".format(tag)) - self.logger.warning(self._status) - return None - else: - # Creating the Message Request Packet - message_request = [ - pack_uint(Base._get_sequence()), - chr(TAG_SERVICES_REQUEST["Write Tag"]), # the Request Service - chr(len(rp) / 2), # the Request Path Size length in word - rp, # the request path - pack_uint(S_DATA_TYPE[typ]), # data type - pack_uint(1), # Add the number of tag to write - PACK_DATA_FUNCTION[typ](value) - ] - - ret_val = self.send_unit_data( - build_common_packet_format( - DATA_ITEM['Connected'], - ''.join(message_request), - ADDRESS_ITEM['Connection Based'], - addr_data=self._target_cid, - ) - ) - - if multi_requests: - return self._parse_multiple_request_write(tag) - else: - if ret_val is None: - raise DataError("send_unit_data returned not valid data") - return ret_val - - def write_array(self, tag, data_type, values): - """ write array of atomic data type from a connected plc - - At the moment there is not a strong validation for the argument passed. The user should verify - the correctness of the format passed. - - :param tag: the name of the tag to read - :param data_type: the type of tag to write - :param values: the array of values to write - """ - if not isinstance(values, list): - self._status = (9, "A list of tags must be passed to write_array.") - self.logger.warning(self._status) - raise DataError("A list of tags must be passed to write_array.") - - if not self._target_is_connected: - if not self.forward_open(): - self._status = (9, "Target did not connected. write_array will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. write_array will not be executed.") - - array_of_values = "" - byte_size = 0 - byte_offset = 0 - - for i, value in enumerate(values): - array_of_values += PACK_DATA_FUNCTION[data_type](value) - byte_size += DATA_FUNCTION_SIZE[data_type] - - if byte_size >= 450 or i == len(values)-1: - # create the message and send the fragment - rp = create_tag_rp(tag) - if rp is None: - self._status = (9, "Cannot create tag {0} request packet. \ - write_array will not be executed.".format(tag)) - return None - else: - # Creating the Message Request Packet - message_request = [ - pack_uint(Base._get_sequence()), - chr(TAG_SERVICES_REQUEST["Write Tag Fragmented"]), # the Request Service - chr(len(rp) / 2), # the Request Path Size length in word - rp, # the request path - pack_uint(S_DATA_TYPE[data_type]), # Data type to write - pack_uint(len(values)), # Number of elements to write - pack_dint(byte_offset), - array_of_values # Fragment of elements to write - ] - byte_offset += byte_size - - if self.send_unit_data( - build_common_packet_format( - DATA_ITEM['Connected'], - ''.join(message_request), - ADDRESS_ITEM['Connection Based'], - addr_data=self._target_cid, - )) is None: - raise DataError("send_unit_data returned not valid data") - array_of_values = "" - byte_size = 0 - - def _get_instance_attribute_list_service(self): - """ Step 1: Finding user-created controller scope tags in a Logix5000 controller - - This service returns instance IDs for each created instance of the symbol class, along with a list - of the attribute data associated with the requested attribute - """ - try: - if not self._target_is_connected: - if not self.forward_open(): - self._status = (10, "Target did not connected. get_tag_list will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. get_tag_list will not be executed.") - - self._last_instance = 0 - - self._get_template_in_progress = True - while self._last_instance != -1: - - # Creating the Message Request Packet - - message_request = [ - pack_uint(Base._get_sequence()), - chr(TAG_SERVICES_REQUEST['Get Instance Attributes List']), # STEP 1 - # the Request Path Size length in word - chr(3), - # Request Path ( 20 6B 25 00 Instance ) - CLASS_ID["8-bit"], # Class id = 20 from spec 0x20 - CLASS_CODE["Symbol Object"], # Logical segment: Symbolic Object 0x6B - INSTANCE_ID["16-bit"], # Instance Segment: 16 Bit instance 0x25 - '\x00', - pack_uint(self._last_instance), # The instance - # Request Data - pack_uint(2), # Number of attributes to retrieve - pack_uint(1), # Attribute 1: Symbol name - pack_uint(2) # Attribute 2: Symbol type - ] - - if self.send_unit_data( - build_common_packet_format( - DATA_ITEM['Connected'], - ''.join(message_request), - ADDRESS_ITEM['Connection Based'], - addr_data=self._target_cid, - )) is None: - raise DataError("send_unit_data returned not valid data") - - self._get_template_in_progress = False - - except Exception as e: - raise DataError(e) - - def _get_structure_makeup(self, instance_id): - """ - get the structure makeup for a specific structure - """ - if not self._target_is_connected: - if not self.forward_open(): - self._status = (10, "Target did not connected. get_tag_list will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. get_tag_list will not be executed.") - - message_request = [ - pack_uint(self._get_sequence()), - chr(TAG_SERVICES_REQUEST['Get Attributes']), - chr(3), # Request Path ( 20 6B 25 00 Instance ) - CLASS_ID["8-bit"], # Class id = 20 from spec 0x20 - CLASS_CODE["Template Object"], # Logical segment: Template Object 0x6C - INSTANCE_ID["16-bit"], # Instance Segment: 16 Bit instance 0x25 - '\x00', - pack_uint(instance_id), - pack_uint(4), # Number of attributes - pack_uint(4), # Template Object Definition Size UDINT - pack_uint(5), # Template Structure Size UDINT - pack_uint(2), # Template Member Count UINT - pack_uint(1) # Structure Handle We can use this to read and write UINT - ] - - if self.send_unit_data( - build_common_packet_format(DATA_ITEM['Connected'], - ''.join(message_request), ADDRESS_ITEM['Connection Based'], - addr_data=self._target_cid,)) is None: - raise DataError("send_unit_data returned not valid data") - - return self._buffer - - def _read_template(self, instance_id, object_definition_size): - """ get a list of the tags in the plc - - """ - if not self._target_is_connected: - if not self.forward_open(): - self._status = (10, "Target did not connected. get_tag_list will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. get_tag_list will not be executed.") - - self._byte_offset = 0 - self._buffer = "" - self._get_template_in_progress = True - - try: - while self._get_template_in_progress: - - # Creating the Message Request Packet - - message_request = [ - pack_uint(self._get_sequence()), - chr(TAG_SERVICES_REQUEST['Read Template']), - chr(3), # Request Path ( 20 6B 25 00 Instance ) - CLASS_ID["8-bit"], # Class id = 20 from spec 0x20 - CLASS_CODE["Template Object"], # Logical segment: Template Object 0x6C - INSTANCE_ID["16-bit"], # Instance Segment: 16 Bit instance 0x25 - '\x00', - pack_uint(instance_id), - pack_dint(self._byte_offset), # Offset - pack_uint(((object_definition_size * 4)-23) - self._byte_offset) - ] - - if not self.send_unit_data( - build_common_packet_format(DATA_ITEM['Connected'], ''.join(message_request), - ADDRESS_ITEM['Connection Based'], addr_data=self._target_cid,)): - raise DataError("send_unit_data returned not valid data") - - self._get_template_in_progress = False - return self._buffer - - except Exception as e: - raise DataError(e) - - def _isolating_user_tag(self): - try: - lst = self._tag_list - self._tag_list = [] - for tag in lst: - if tag['tag_name'].find(':') != -1 or tag['tag_name'].find('__') != -1: - continue - if tag['symbol_type'] & 0b0001000000000000: - continue - dimension = tag['symbol_type'] & 0b0110000000000000 >> 13 - template_instance_id = tag['symbol_type'] & 0b0000111111111111 - - if tag['symbol_type'] & 0b1000000000000000 : - tag_type = 'struct' - data_type = 'user-created' - self._tag_list.append({'instance_id': tag['instance_id'], - 'template_instance_id': template_instance_id, - 'tag_name': tag['tag_name'], - 'dim': dimension, - 'tag_type': tag_type, - 'data_type': data_type, - 'template': {}, - 'udt': {}}) - else: - tag_type = 'atomic' - data_type = I_DATA_TYPE[template_instance_id] - self._tag_list.append({'instance_id': tag['instance_id'], - 'tag_name': tag['tag_name'], - 'dim': dimension, - 'tag_type': tag_type, - 'data_type': data_type}) - except Exception as e: - raise DataError(e) - - def _parse_udt_raw(self, tag): - try: - buff = self._read_template(tag['template_instance_id'], tag['template']['object_definition_size']) - member_count = tag['template']['member_count'] - names = buff.split('\00') - lst = [] - - tag['udt']['name'] = 'Not an user defined structure' - for name in names: - if len(name) > 1: - - if name.find(';') != -1: - tag['udt']['name'] = name[:name.find(';')] - elif name.find('ZZZZZZZZZZ') != -1: - continue - elif name.isalpha(): - lst.append(name) - else: - continue - tag['udt']['internal_tags'] = lst - - type_list = [] - - for i in xrange(member_count): - # skip member 1 - - if i != 0: - array_size = unpack_uint(buff[:2]) - try: - data_type = I_DATA_TYPE[unpack_uint(buff[2:4])] - except Exception: - data_type = "None" - - offset = unpack_dint(buff[4:8]) - type_list.append((array_size, data_type, offset)) - - buff = buff[8:] - - tag['udt']['data_type'] = type_list - except Exception as e: - raise DataError(e) - - def get_tag_list(self): - self._tag_list = [] - # Step 1 - self._get_instance_attribute_list_service() - - # Step 2 - self._isolating_user_tag() - - # Step 3 - for tag in self._tag_list: - if tag['tag_type'] == 'struct': - tag['template'] = self._get_structure_makeup(tag['template_instance_id']) - - for idx, tag in enumerate(self._tag_list): - # print (tag) - if tag['tag_type'] == 'struct': - self._parse_udt_raw(tag) - - # Step 4 - - return self._tag_list - - - - diff --git a/POCloud_Driver/pycomm_micro/ab_comm/slc.py b/POCloud_Driver/pycomm_micro/ab_comm/slc.py deleted file mode 100644 index bfdb243..0000000 --- a/POCloud_Driver/pycomm_micro/ab_comm/slc.py +++ /dev/null @@ -1,446 +0,0 @@ -# -*- coding: utf-8 -*- -# -# clx.py - Ethernet/IP Client for Rockwell PLCs -# -# -# Copyright (c) 2014 Agostino Ruscito -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -from pycomm_micro.cip.cip_base import * -from pycomm_micro.common import setup_logger -import re -import logging -import math - - -def parse_tag(tag): - t = re.search(r"(?P[CT])(?P\d{1,3})" - r"(:)(?P\d{1,3})" - r"(.)(?PACC|PRE|EN|DN|TT|CU|CD|DN|OV|UN|UA)", tag, flags=re.IGNORECASE) - if t: - if (1 <= int(t.group('file_number')) <= 255) \ - and (0 <= int(t.group('element_number')) <= 255): - return True, t.group(0), {'file_type': t.group('file_type').upper(), - 'file_number': t.group('file_number'), - 'element_number': t.group('element_number'), - 'sub_element': PCCC_CT[t.group('sub_element').upper()], - 'read_func': '\xa2', - 'write_func': '\xab', - 'address_field': 3} - - t = re.search(r"(?P[FBN])(?P\d{1,3})" - r"(:)(?P\d{1,3})" - r"(/(?P\d{1,2}))?", - tag, flags=re.IGNORECASE) - if t: - if t.group('sub_element') is not None: - if (1 <= int(t.group('file_number')) <= 255) \ - and (0 <= int(t.group('element_number')) <= 255) \ - and (0 <= int(t.group('sub_element')) <= 15): - - return True, t.group(0), {'file_type': t.group('file_type').upper(), - 'file_number': t.group('file_number'), - 'element_number': t.group('element_number'), - 'sub_element': t.group('sub_element'), - 'read_func': '\xa2', - 'write_func': '\xab', - 'address_field': 3} - else: - if (1 <= int(t.group('file_number')) <= 255) \ - and (0 <= int(t.group('element_number')) <= 255): - - return True, t.group(0), {'file_type': t.group('file_type').upper(), - 'file_number': t.group('file_number'), - 'element_number': t.group('element_number'), - 'sub_element': t.group('sub_element'), - 'read_func': '\xa2', - 'write_func': '\xab', - 'address_field': 2} - - t = re.search(r"(?P[IO])(:)(?P\d{1,3})" - r"(.)(?P\d{1,3})" - r"(/(?P\d{1,2}))?", tag, flags=re.IGNORECASE) - if t: - if t.group('sub_element') is not None: - if (0 <= int(t.group('file_number')) <= 255) \ - and (0 <= int(t.group('element_number')) <= 255) \ - and (0 <= int(t.group('sub_element')) <= 15): - - return True, t.group(0), {'file_type': t.group('file_type').upper(), - 'file_number': t.group('file_number'), - 'element_number': t.group('element_number'), - 'sub_element': t.group('sub_element'), - 'read_func': '\xa2', - 'write_func': '\xab', - 'address_field': 3} - else: - if (0 <= int(t.group('file_number')) <= 255) \ - and (0 <= int(t.group('element_number')) <= 255): - - return True, t.group(0), {'file_type': t.group('file_type').upper(), - 'file_number': t.group('file_number'), - 'element_number': t.group('element_number'), - 'read_func': '\xa2', - 'write_func': '\xab', - 'address_field': 2} - - t = re.search(r"(?PS)" - r"(:)(?P\d{1,3})" - r"(/(?P\d{1,2}))?", tag, flags=re.IGNORECASE) - if t: - if t.group('sub_element') is not None: - if (0 <= int(t.group('element_number')) <= 255) \ - and (0 <= int(t.group('sub_element')) <= 15): - return True, t.group(0), {'file_type': t.group('file_type').upper(), - 'file_number': '2', - 'element_number': t.group('element_number'), - 'sub_element': t.group('sub_element'), - 'read_func': '\xa2', - 'write_func': '\xab', - 'address_field': 3} - else: - if 0 <= int(t.group('element_number')) <= 255: - return True, t.group(0), {'file_type': t.group('file_type').upper(), - 'file_number': '2', - 'element_number': t.group('element_number'), - 'read_func': '\xa2', - 'write_func': '\xab', - 'address_field': 2} - - t = re.search(r"(?PB)(?P\d{1,3})" - r"(/)(?P\d{1,4})", - tag, flags=re.IGNORECASE) - if t: - if (1 <= int(t.group('file_number')) <= 255) \ - and (0 <= int(t.group('element_number')) <= 4095): - bit_position = int(t.group('element_number')) - element_number = bit_position / 16 - sub_element = bit_position - (element_number * 16) - return True, t.group(0), {'file_type': t.group('file_type').upper(), - 'file_number': t.group('file_number'), - 'element_number': element_number, - 'sub_element': sub_element, - 'read_func': '\xa2', - 'write_func': '\xab', - 'address_field': 3} - - return False, tag - - -class Driver(Base): - """ - SLC/PLC_5 Implementation - """ - def __init__(self, debug=False, filename=None): - if debug: - super(Driver, self).__init__(setup_logger('ab_comm.slc', logging.DEBUG, filename)) - else: - super(Driver, self).__init__(setup_logger('ab_comm.slc', logging.INFO, filename)) - - self.__version__ = '0.1' - self._last_sequence = 0 - - def _check_reply(self): - """ - check the replayed message for error - """ - self._more_packets_available = False - try: - if self._reply is None: - self._status = (3, '%s without reply' % REPLAY_INFO[unpack_dint(self._message[:2])]) - return False - # Get the type of command - typ = unpack_uint(self._reply[:2]) - - # Encapsulation status check - if unpack_dint(self._reply[8:12]) != SUCCESS: - self._status = (3, "{0} reply status:{1}".format(REPLAY_INFO[typ], - SERVICE_STATUS[unpack_dint(self._reply[8:12])])) - return False - - # Command Specific Status check - if typ == unpack_uint(ENCAPSULATION_COMMAND["send_rr_data"]): - status = unpack_usint(self._reply[42:43]) - if status != SUCCESS: - self._status = (3, "send_rr_data reply:{0} - Extend status:{1}".format( - SERVICE_STATUS[status], get_extended_status(self._reply, 42))) - return False - else: - return True - - elif typ == unpack_uint(ENCAPSULATION_COMMAND["send_unit_data"]): - status = unpack_usint(self._reply[48:49]) - if unpack_usint(self._reply[46:47]) == I_TAG_SERVICES_REPLY["Read Tag Fragmented"]: - self._parse_fragment(50, status) - return True - if unpack_usint(self._reply[46:47]) == I_TAG_SERVICES_REPLY["Get Instance Attributes List"]: - self._parse_tag_list(50, status) - return True - if status == 0x06: - self._status = (3, "Insufficient Packet Space") - self._more_packets_available = True - elif status != SUCCESS: - self._status = (3, "send_unit_data reply:{0} - Extend status:{1}".format( - SERVICE_STATUS[status], get_extended_status(self._reply, 48))) - return False - else: - return True - - return True - except Exception as e: - raise DataError(e) - - def read_tag(self, tag, n=1): - """ read tag from a connected plc - - Possible combination can be passed to this method: - print c.read_tag('F8:0', 3) return a list of 3 registers starting from F8:0 - print c.read_tag('F8:0') return one value - - It is possible to read status bit - - :return: None is returned in case of error - """ - res = parse_tag(tag) - if not res[0]: - self._status = (1000, "Error parsing the tag passed to read_tag({0},{1})".format(tag, n)) - self.logger.warning(self._status) - raise DataError("Error parsing the tag passed to read_tag({0},{1})".format(tag, n)) - - bit_read = False - bit_position = 0 - sub_element = 0 - if int(res[2]['address_field'] == 3): - bit_read = True - bit_position = int(res[2]['sub_element']) - - if not self._target_is_connected: - if not self.forward_open(): - self._status = (5, "Target did not connected. read_tag will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. read_tag will not be executed.") - - data_size = PCCC_DATA_SIZE[res[2]['file_type']] - - # Creating the Message Request Packet - self._last_sequence = pack_uint(Base._get_sequence()) - - message_request = [ - self._last_sequence, - '\x4b', - '\x02', - CLASS_ID["8-bit"], - PATH["PCCC"], - '\x07', - self.attribs['vid'], - self.attribs['vsn'], - '\x0f', - '\x00', - self._last_sequence[1], - self._last_sequence[0], - res[2]['read_func'], - pack_usint(data_size * n), - pack_usint(int(res[2]['file_number'])), - PCCC_DATA_TYPE[res[2]['file_type']], - pack_usint(int(res[2]['element_number'])), - pack_usint(sub_element) - ] - - self.logger.debug("SLC read_tag({0},{1})".format(tag, n)) - if self.send_unit_data( - build_common_packet_format( - DATA_ITEM['Connected'], - ''.join(message_request), - ADDRESS_ITEM['Connection Based'], - addr_data=self._target_cid,)): - sts = int(unpack_usint(self._reply[58])) - try: - if sts != 0: - sts_txt = PCCC_ERROR_CODE[sts] - self._status = (1000, "Error({0}) returned from read_tag({1},{2})".format(sts_txt, tag, n)) - self.logger.warning(self._status) - raise DataError("Error({0}) returned from read_tag({1},{2})".format(sts_txt, tag, n)) - - new_value = 61 - if bit_read: - if res[2]['file_type'] == 'T' or res[2]['file_type'] == 'C': - if bit_position == PCCC_CT['PRE']: - return UNPACK_PCCC_DATA_FUNCTION[res[2]['file_type']]( - self._reply[new_value+2:new_value+2+data_size]) - elif bit_position == PCCC_CT['ACC']: - return UNPACK_PCCC_DATA_FUNCTION[res[2]['file_type']]( - self._reply[new_value+4:new_value+4+data_size]) - - tag_value = UNPACK_PCCC_DATA_FUNCTION[res[2]['file_type']]( - self._reply[new_value:new_value+data_size]) - return get_bit(tag_value, bit_position) - - else: - values_list = [] - while len(self._reply[new_value:]) >= data_size: - values_list.append( - UNPACK_PCCC_DATA_FUNCTION[res[2]['file_type']](self._reply[new_value:new_value+data_size]) - ) - new_value = new_value+data_size - - if len(values_list) > 1: - return values_list - else: - return values_list[0] - - except Exception as e: - self._status = (1000, "Error({0}) parsing the data returned from read_tag({1},{2})".format(e, tag, n)) - self.logger.warning(self._status) - raise DataError("Error({0}) parsing the data returned from read_tag({1},{2})".format(e, tag, n)) - else: - raise DataError("send_unit_data returned not valid data") - - def write_tag(self, tag, value): - """ write tag from a connected plc - - Possible combination can be passed to this method: - c.write_tag('N7:0', [-30, 32767, -32767]) - c.write_tag('N7:0', 21) - c.read_tag('N7:0', 10) - - It is not possible to write status bit - - :return: None is returned in case of error - """ - res = parse_tag(tag) - if not res[0]: - self._status = (1000, "Error parsing the tag passed to read_tag({0},{1})".format(tag, value)) - self.logger.warning(self._status) - raise DataError("Error parsing the tag passed to read_tag({0},{1})".format(tag, value)) - - if isinstance(value, list) and int(res[2]['address_field'] == 3): - self._status = (1000, "Function's parameters error. read_tag({0},{1})".format(tag, value)) - self.logger.warning(self._status) - raise DataError("Function's parameters error. read_tag({0},{1})".format(tag, value)) - - if isinstance(value, list) and int(res[2]['address_field'] == 3): - self._status = (1000, "Function's parameters error. read_tag({0},{1})".format(tag, value)) - self.logger.warning(self._status) - raise DataError("Function's parameters error. read_tag({0},{1})".format(tag, value)) - - bit_field = False - bit_position = 0 - sub_element = 0 - if int(res[2]['address_field'] == 3): - bit_field = True - bit_position = int(res[2]['sub_element']) - values_list = '' - else: - values_list = '\xff\xff' - - multi_requests = False - if isinstance(value, list): - multi_requests = True - - if not self._target_is_connected: - if not self.forward_open(): - self._status = (1000, "Target did not connected. write_tag will not be executed.") - self.logger.warning(self._status) - raise Error("Target did not connected. write_tag will not be executed.") - - try: - n = 0 - if multi_requests: - data_size = PCCC_DATA_SIZE[res[2]['file_type']] - for v in value: - values_list += PACK_PCCC_DATA_FUNCTION[res[2]['file_type']](v) - n += 1 - else: - n = 1 - if bit_field: - data_size = 2 - - if (res[2]['file_type'] == 'T' or res[2]['file_type'] == 'C') \ - and (bit_position == PCCC_CT['PRE'] or bit_position == PCCC_CT['ACC']): - sub_element = bit_position - values_list = '\xff\xff' + PACK_PCCC_DATA_FUNCTION[res[2]['file_type']](value) - else: - sub_element = 0 - if value > 0: - values_list = pack_uint(math.pow(2, bit_position)) + pack_uint(math.pow(2, bit_position)) - else: - values_list = pack_uint(math.pow(2, bit_position)) + pack_uint(0) - - else: - values_list += PACK_PCCC_DATA_FUNCTION[res[2]['file_type']](value) - data_size = PCCC_DATA_SIZE[res[2]['file_type']] - - except Exception as e: - self._status = (1000, "Error({0}) packing the values to write to the" - "SLC write_tag({1},{2})".format(e, tag, value)) - self.logger.warning(self._status) - raise DataError("Error({0}) packing the values to write to the " - "SLC write_tag({1},{2})".format(e, tag, value)) - - data_to_write = values_list - - # Creating the Message Request Packet - self._last_sequence = pack_uint(Base._get_sequence()) - - message_request = [ - self._last_sequence, - '\x4b', - '\x02', - CLASS_ID["8-bit"], - PATH["PCCC"], - '\x07', - self.attribs['vid'], - self.attribs['vsn'], - '\x0f', - '\x00', - self._last_sequence[1], - self._last_sequence[0], - res[2]['write_func'], - pack_usint(data_size * n), - pack_usint(int(res[2]['file_number'])), - PCCC_DATA_TYPE[res[2]['file_type']], - pack_usint(int(res[2]['element_number'])), - pack_usint(sub_element) - ] - - self.logger.debug("SLC write_tag({0},{1})".format(tag, value)) - if self.send_unit_data( - build_common_packet_format( - DATA_ITEM['Connected'], - ''.join(message_request) + data_to_write, - ADDRESS_ITEM['Connection Based'], - addr_data=self._target_cid,)): - sts = int(unpack_usint(self._reply[58])) - try: - if sts != 0: - sts_txt = PCCC_ERROR_CODE[sts] - self._status = (1000, "Error({0}) returned from SLC write_tag({1},{2})".format(sts_txt, tag, value)) - self.logger.warning(self._status) - raise DataError("Error({0}) returned from SLC write_tag({1},{2})".format(sts_txt, tag, value)) - - return True - except Exception as e: - self._status = (1000, "Error({0}) parsing the data returned from " - "SLC write_tag({1},{2})".format(e, tag, value)) - self.logger.warning(self._status) - raise DataError("Error({0}) parsing the data returned from " - "SLC write_tag({1},{2})".format(e, tag, value)) - else: - raise DataError("send_unit_data returned not valid data") \ No newline at end of file diff --git a/POCloud_Driver/pycomm_micro/cip/__init__.py b/POCloud_Driver/pycomm_micro/cip/__init__.py deleted file mode 100644 index 8c1f233..0000000 --- a/POCloud_Driver/pycomm_micro/cip/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'agostino' diff --git a/POCloud_Driver/pycomm_micro/cip/cip_base.py b/POCloud_Driver/pycomm_micro/cip/cip_base.py deleted file mode 100644 index 36999e1..0000000 --- a/POCloud_Driver/pycomm_micro/cip/cip_base.py +++ /dev/null @@ -1,827 +0,0 @@ -# -*- coding: utf-8 -*- -# -# cip_base.py - A set of classes methods and structures used to implement Ethernet/IP -# -# -# Copyright (c) 2014 Agostino Ruscito -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# - -import struct -import socket - -from os import getpid -from pycomm_micro.cip.cip_const import * -from pycomm_micro.common import PycommError - - -class CommError(PycommError): - pass - - -class DataError(PycommError): - pass - - -def pack_sint(n): - return struct.pack('b', n) - - -def pack_usint(n): - return struct.pack('B', n) - - -def pack_int(n): - """pack 16 bit into 2 bytes little endian""" - return struct.pack(' -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# - -ELEMENT_ID = { - "8-bit": '\x28', - "16-bit": '\x29', - "32-bit": '\x2a' -} - -CLASS_ID = { - "8-bit": '\x20', - "16-bit": '\x21', -} - -INSTANCE_ID = { - "8-bit": '\x24', - "16-bit": '\x25' -} - -ATTRIBUTE_ID = { - "8-bit": '\x30', - "16-bit": '\x31' -} - -# Path are combined as: -# CLASS_ID + PATHS -# For example PCCC path is CLASS_ID["8-bit"]+PATH["PCCC"] -> 0x20, 0x67, 0x24, 0x01. -PATH = { - 'Connection Manager': '\x06\x24\x01', - 'Router': '\x02\x24\x01', - 'Backplane Data Type': '\x66\x24\x01', - 'PCCC': '\x67\x24\x01', - 'DHCP Channel A': '\xa6\x24\x01\x01\x2c\x01', - 'DHCP Channel B': '\xa6\x24\x01\x02\x2c\x01' -} - -ENCAPSULATION_COMMAND = { # Volume 2: 2-3.2 Command Field UINT 2 byte - "nop": '\x00\x00', - "list_targets": '\x01\x00', - "list_services": '\x04\x00', - "list_identity": '\x63\x00', - "list_interfaces": '\x64\x00', - "register_session": '\x65\x00', - "unregister_session": '\x66\x00', - "send_rr_data": '\x6F\x00', - "send_unit_data": '\x70\x00' -} - -""" -When a tag is created, an instance of the Symbol Object (Class ID 0x6B) is created -inside the controller. - -When a UDT is created, an instance of the Template object (Class ID 0x6C) is -created to hold information about the structure makeup. -""" -CLASS_CODE = { - "Message Router": '\x02', # Volume 1: 5-1 - "Symbol Object": '\x6b', - "Template Object": '\x6c', - "Connection Manager": '\x06' # Volume 1: 3-5 -} - -CONNECTION_MANAGER_INSTANCE = { - 'Open Request': '\x01', - 'Open Format Rejected': '\x02', - 'Open Resource Rejected': '\x03', - 'Open Other Rejected': '\x04', - 'Close Request': '\x05', - 'Close Format Request': '\x06', - 'Close Other Request': '\x07', - 'Connection Timeout': '\x08' -} - -TAG_SERVICES_REQUEST = { - "Read Tag": 0x4c, - "Read Tag Fragmented": 0x52, - "Write Tag": 0x4d, - "Write Tag Fragmented": 0x53, - "Read Modify Write Tag": 0x4e, - "Multiple Service Packet": 0x0a, - "Get Instance Attributes List": 0x55, - "Get Attributes": 0x03, - "Read Template": 0x4c, -} - -TAG_SERVICES_REPLY = { - 0xcc: "Read Tag", - 0xd2: "Read Tag Fragmented", - 0xcd: "Write Tag", - 0xd3: "Write Tag Fragmented", - 0xce: "Read Modify Write Tag", - 0x8a: "Multiple Service Packet", - 0xd5: "Get Instance Attributes List", - 0x83: "Get Attributes", - 0xcc: "Read Template" -} - - -I_TAG_SERVICES_REPLY = { - "Read Tag": 0xcc, - "Read Tag Fragmented": 0xd2, - "Write Tag": 0xcd, - "Write Tag Fragmented": 0xd3, - "Read Modify Write Tag": 0xce, - "Multiple Service Packet": 0x8a, - "Get Instance Attributes List": 0xd5, - "Get Attributes": 0x83, - "Read Template": 0xcc -} - - -""" -EtherNet/IP Encapsulation Error Codes - -Standard CIP Encapsulation Error returned in the cip message header -""" -STATUS = { - 0x0000: "Success", - 0x0001: "The sender issued an invalid or unsupported encapsulation command", - 0x0002: "Insufficient memory", - 0x0003: "Poorly formed or incorrect data in the data portion", - 0x0064: "An originator used an invalid session handle when sending an encapsulation message to the target", - 0x0065: "The target received a message of invalid length", - 0x0069: "Unsupported Protocol Version" -} - -""" -MSG Error Codes: - -The following error codes have been taken from: - -Rockwell Automation Publication -1756-RM003P-EN-P - December 2014 -""" -SERVICE_STATUS = { - 0x01: "Connection failure (see extended status)", - 0x02: "Insufficient resource", - 0x03: "Invalid value", - 0x04: "IOI syntax error. A syntax error was detected decoding the Request Path (see extended status)", - 0x05: "Destination unknown, class unsupported, instance \nundefined or structure element undefined (see extended status)", - 0x06: "Insufficient Packet Space", - 0x07: "Connection lost", - 0x08: "Service not supported", - 0x09: "Error in data segment or invalid attribute value", - 0x0A: "Attribute list error", - 0x0B: "State already exist", - 0x0C: "Object state conflict", - 0x0D: "Object already exist", - 0x0E: "Attribute not settable", - 0x0F: "Permission denied", - 0x10: "Device state conflict", - 0x11: "Reply data too large", - 0x12: "Fragmentation of a primitive value", - 0x13: "Insufficient command data", - 0x14: "Attribute not supported", - 0x15: "Too much data", - 0x1A: "Bridge request too large", - 0x1B: "Bridge response too large", - 0x1C: "Attribute list shortage", - 0x1D: "Invalid attribute list", - 0x1E: "Request service error", - 0x1F: "Connection related failure (see extended status)", - 0x22: "Invalid reply received", - 0x25: "Key segment error", - 0x26: "Invalid IOI error", - 0x27: "Unexpected attribute in list", - 0x28: "DeviceNet error - invalid member ID", - 0x29: "DeviceNet error - member not settable", - 0xD1: "Module not in run state", - 0xFB: "Message port not supported", - 0xFC: "Message unsupported data type", - 0xFD: "Message uninitialized", - 0xFE: "Message timeout", - 0xff: "General Error (see extended status)" -} - -EXTEND_CODES = { - 0x01: { - 0x0100: "Connection in use", - 0x0103: "Transport not supported", - 0x0106: "Ownership conflict", - 0x0107: "Connection not found", - 0x0108: "Invalid connection type", - 0x0109: "Invalid connection size", - 0x0110: "Module not configured", - 0x0111: "EPR not supported", - 0x0114: "Wrong module", - 0x0115: "Wrong device type", - 0x0116: "Wrong revision", - 0x0118: "Invalid configuration format", - 0x011A: "Application out of connections", - 0x0203: "Connection timeout", - 0x0204: "Unconnected message timeout", - 0x0205: "Unconnected send parameter error", - 0x0206: "Message too large", - 0x0301: "No buffer memory", - 0x0302: "Bandwidth not available", - 0x0303: "No screeners available", - 0x0305: "Signature match", - 0x0311: "Port not available", - 0x0312: "Link address not available", - 0x0315: "Invalid segment type", - 0x0317: "Connection not scheduled" - }, - 0x04: { - 0x0000: "Extended status out of memory", - 0x0001: "Extended status out of instances" - }, - 0x05: { - 0x0000: "Extended status out of memory", - 0x0001: "Extended status out of instances" - }, - 0x1F: { - 0x0203: "Connection timeout" - }, - 0xff: { - 0x7: "Wrong data type", - 0x2001: "Excessive IOI", - 0x2002: "Bad parameter value", - 0x2018: "Semaphore reject", - 0x201B: "Size too small", - 0x201C: "Invalid size", - 0x2100: "Privilege failure", - 0x2101: "Invalid keyswitch position", - 0x2102: "Password invalid", - 0x2103: "No password issued", - 0x2104: "Address out of range", - 0x2105: "Address and how many out of range", - 0x2106: "Data in use", - 0x2107: "Type is invalid or not supported", - 0x2108: "Controller in upload or download mode", - 0x2109: "Attempt to change number of array dimensions", - 0x210A: "Invalid symbol name", - 0x210B: "Symbol does not exist", - 0x210E: "Search failed", - 0x210F: "Task cannot start", - 0x2110: "Unable to write", - 0x2111: "Unable to read", - 0x2112: "Shared routine not editable", - 0x2113: "Controller in faulted mode", - 0x2114: "Run mode inhibited" - - } -} -DATA_ITEM = { - 'Connected': '\xb1\x00', - 'Unconnected': '\xb2\x00' -} - -ADDRESS_ITEM = { - 'Connection Based': '\xa1\x00', - 'Null': '\x00\x00', - 'UCMM': '\x00\x00' -} - -UCMM = { - 'Interface Handle': 0, - 'Item Count': 2, - 'Address Type ID': 0, - 'Address Length': 0, - 'Data Type ID': 0x00b2 -} - -CONNECTION_SIZE = { - 'Backplane': '\x03', # CLX - 'Direct Network': '\x02' -} - -HEADER_SIZE = 24 -EXTENDED_SYMBOL = '\x91' -BOOL_ONE = 0xff -REQUEST_SERVICE = 0 -REQUEST_PATH_SIZE = 1 -REQUEST_PATH = 2 -SUCCESS = 0 -INSUFFICIENT_PACKETS = 6 -OFFSET_MESSAGE_REQUEST = 40 - - -FORWARD_CLOSE = '\x4e' -UNCONNECTED_SEND = '\x52' -FORWARD_OPEN = '\x54' -LARGE_FORWARD_OPEN = '\x5b' -GET_CONNECTION_DATA = '\x56' -SEARCH_CONNECTION_DATA = '\x57' -GET_CONNECTION_OWNER = '\x5a' -MR_SERVICE_SIZE = 2 - -PADDING_BYTE = '\x00' -PRIORITY = '\x0a' -TIMEOUT_TICKS = '\x05' -TIMEOUT_MULTIPLIER = '\x01' -TRANSPORT_CLASS = '\xa3' - -CONNECTION_PARAMETER = { - 'PLC5': 0x4302, - 'SLC500': 0x4302, - 'CNET': 0x4320, - 'DHP': 0x4302, - 'Default': 0x43f8, -} - -""" -Atomic Data Type: - - Bit = Bool - Bit array = DWORD (32-bit boolean aray) - 8-bit integer = SINT -16-bit integer = UINT -32-bit integer = DINT - 32-bit float = REAL -64-bit integer = LINT - -From Rockwell Automation Publication 1756-PM020C-EN-P November 2012: -When reading a BOOL tag, the values returned for 0 and 1 are 0 and 0xff, respectively. -""" - -S_DATA_TYPE = { - 'BOOL': 0xc1, - 'SINT': 0xc2, # Signed 8-bit integer - 'INT': 0xc3, # Signed 16-bit integer - 'DINT': 0xc4, # Signed 32-bit integer - 'LINT': 0xc5, # Signed 64-bit integer - 'USINT': 0xc6, # Unsigned 8-bit integer - 'UINT': 0xc7, # Unsigned 16-bit integer - 'UDINT': 0xc8, # Unsigned 32-bit integer - 'ULINT': 0xc9, # Unsigned 64-bit integer - 'REAL': 0xca, # 32-bit floating point - 'LREAL': 0xcb, # 64-bit floating point - 'STIME': 0xcc, # Synchronous time - 'DATE': 0xcd, - 'TIME_OF_DAY': 0xce, - 'DATE_AND_TIME': 0xcf, - 'STRING': 0xd0, # character string (1 byte per character) - 'BYTE': 0xd1, # byte string 8-bits - 'WORD': 0xd2, # byte string 16-bits - 'DWORD': 0xd3, # byte string 32-bits - 'LWORD': 0xd4, # byte string 64-bits - 'STRING2': 0xd5, # character string (2 byte per character) - 'FTIME': 0xd6, # Duration high resolution - 'LTIME': 0xd7, # Duration long - 'ITIME': 0xd8, # Duration short - 'STRINGN': 0xd9, # character string (n byte per character) - 'SHORT_STRING': 0xda, # character string (1 byte per character, 1 byte length indicator) - 'TIME': 0xdb, # Duration in milliseconds - 'EPATH': 0xdc, # CIP Path segment - 'ENGUNIT': 0xdd, # Engineering Units - 'STRINGI': 0xde # International character string -} - -I_DATA_TYPE = { - 0xc1: 'BOOL', - 0xc2: 'SINT', # Signed 8-bit integer - 0xc3: 'INT', # Signed 16-bit integer - 0xc4: 'DINT', # Signed 32-bit integer - 0xc5: 'LINT', # Signed 64-bit integer - 0xc6: 'USINT', # Unsigned 8-bit integer - 0xc7: 'UINT', # Unsigned 16-bit integer - 0xc8: 'UDINT', # Unsigned 32-bit integer - 0xc9: 'ULINT', # Unsigned 64-bit integer - 0xca: 'REAL', # 32-bit floating point - 0xcb: 'LREAL', # 64-bit floating point - 0xcc: 'STIME', # Synchronous time - 0xcd: 'DATE', - 0xce: 'TIME_OF_DAY', - 0xcf: 'DATE_AND_TIME', - 0xd0: 'STRING', # character string (1 byte per character) - 0xd1: 'BYTE', # byte string 8-bits - 0xd2: 'WORD', # byte string 16-bits - 0xd3: 'DWORD', # byte string 32-bits - 0xd4: 'LWORD', # byte string 64-bits - 0xd5: 'STRING2', # character string (2 byte per character) - 0xd6: 'FTIME', # Duration high resolution - 0xd7: 'LTIME', # Duration long - 0xd8: 'ITIME', # Duration short - 0xd9: 'STRINGN', # character string (n byte per character) - 0xda: 'SHORT_STRING', # character string (1 byte per character, 1 byte length indicator) - 0xdb: 'TIME', # Duration in milliseconds - 0xdc: 'EPATH', # CIP Path segment - 0xdd: 'ENGUNIT', # Engineering Units - 0xde: 'STRINGI' # International character string -} - -REPLAY_INFO = { - 0x4e: 'FORWARD_CLOSE (4E,00)', - 0x52: 'UNCONNECTED_SEND (52,00)', - 0x54: 'FORWARD_OPEN (54,00)', - 0x6f: 'send_rr_data (6F,00)', - 0x70: 'send_unit_data (70,00)', - 0x00: 'nop', - 0x01: 'list_targets', - 0x04: 'list_services', - 0x63: 'list_identity', - 0x64: 'list_interfaces', - 0x65: 'register_session', - 0x66: 'unregister_session', -} - -PCCC_DATA_TYPE = { - 'N': '\x89', - 'B': '\x85', - 'T': '\x86', - 'C': '\x87', - 'S': '\x84', - 'F': '\x8a', - 'ST': '\x8d', - 'A': '\x8e', - 'R': '\x88', - 'O': '\x8b', - 'I': '\x8c' -} - -PCCC_DATA_SIZE = { - 'N': 2, - 'B': 2, - 'T': 6, - 'C': 6, - 'S': 2, - 'F': 4, - 'ST': 84, - 'A': 2, - 'R': 6, - 'O': 2, - 'I': 2 -} - -PCCC_CT = { - 'PRE': 1, - 'ACC': 2, - 'EN': 15, - 'TT': 14, - 'DN': 13, - 'CU': 15, - 'CD': 14, - 'OV': 12, - 'UN': 11, - 'UA': 10 -} - -PCCC_ERROR_CODE = { - -2: "Not Acknowledged (NAK)", - -3: "No Reponse, Check COM Settings", - -4: "Unknown Message from DataLink Layer", - -5: "Invalid Address", - -6: "Could Not Open Com Port", - -7: "No data specified to data link layer", - -8: "No data returned from PLC", - -20: "No Data Returned", - 16: "Illegal Command or Format, Address may not exist or not enough elements in data file", - 32: "PLC Has a Problem and Will Not Communicate", - 48: "Remote Node Host is Missing, Disconnected, or Shut Down", - 64: "Host Could Not Complete Function Due To Hardware Fault", - 80: "Addressing problem or Memory Protect Rungs", - 96: "Function not allows due to command protection selection", - 112: "Processor is in Program mode", - 128: "Compatibility mode file missing or communication zone problem", - 144: "Remote node cannot buffer command", - 240: "Error code in EXT STS Byte" -} \ No newline at end of file diff --git a/POCloud_Driver/pycomm_micro/common.py b/POCloud_Driver/pycomm_micro/common.py deleted file mode 100644 index f44819c..0000000 --- a/POCloud_Driver/pycomm_micro/common.py +++ /dev/null @@ -1,32 +0,0 @@ -__author__ = 'Agostino Ruscito' -__version__ = "1.0.7" -__date__ = "08 03 2015" -import logging - - -logging.basicConfig( - filename="pycomm.log", - filemode='w', - level=logging.INFO, - format="%(name)-13s %(levelname)-10s %(asctime)s %(message)s", - # propagate=0, -) - -LOGGER = logging.getLogger('pycomm') - - -class PycommError(Exception): - pass - - -def setup_logger(name, level, filename=None): - log = logging.getLogger('pycomm.'+name) - log.setLevel(level) - if filename: - fh = logging.FileHandler(filename, mode='w') - fh.setFormatter(logging.Formatter("%(levelname)-10s %(asctime)s %(message)s")) - log.addHandler(fh) - log.propagate = False - - return log -