Example #1
0
    def __init__(self, address, port, active, session_id, name, event_handler=None, custom_connection_handler=None):
        EventProducer.__init__(self, event_handler)

        self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__)
        self.communicationLogger = logging.getLogger("hsms_communication")

        self.address = address
        self.port = port
        self.active = active
        self.sessionID = session_id
        self.name = name

        self.connected = False

        # system id counter
        self.systemCounter = random.randint(0, (2 ** 32) - 1)

        # repeating linktest variables
        self.linktestTimer = None
        self.linktestTimeout = 30

        # response queues
        self._systemQueues = {}

        # hsms connection state fsm
        self.connectionState = Fysom({
            'initial': 'NOT_CONNECTED',
            'events': [
                {'name': 'connect', 'src': 'NOT_CONNECTED', 'dst': 'CONNECTED'},
                {'name': 'disconnect', 'src': ['CONNECTED', 'NOT_SELECTED', 'SELECTED'], 'dst': 'NOT_CONNECTED'},
                {'name': 'select', 'src': ['CONNECTED', 'NOT_SELECTED'], 'dst': 'SELECTED'},
                {'name': 'deselect', 'src': 'SELECTED', 'dst': 'NOT_SELECTED'},
                {'name': 'timeoutT7', 'src': ['CONNECTED', 'NOT_SELECTED'], 'dst': 'NOT_CONNECTED'},
            ],
            'callbacks': {
                'onNOT_SELECTED': self._on_state_connect,
                'onNOT_CONNECTED': self._on_state_disconnect,
                'onSELECTED': self._on_state_select,
            },
            'autoforward': [
                {'src': 'CONNECTED', 'dst': 'NOT_SELECTED'}
            ]
        })

        # setup connection
        if self.active:
            if custom_connection_handler is None:
                self.connection = HsmsActiveConnection(self.address, self.port, self.sessionID, self)
            else:
                self.connection = custom_connection_handler.create_connection(self.address, self.port, self.sessionID, self)
        else:
            if custom_connection_handler is None:
                self.connection = HsmsPassiveConnection(self.address, self.port, self.sessionID, self)
            else:
                self.connection = custom_connection_handler.create_connection(self.address, self.port, self.sessionID, self)
Example #2
0
    def __init__(self,
                 address,
                 port,
                 active,
                 session_id,
                 name,
                 event_handler=None,
                 custom_connection_handler=None):
        EventProducer.__init__(self, event_handler)

        self.logger = logging.getLogger(self.__module__ + "." +
                                        self.__class__.__name__)
        self.communicationLogger = logging.getLogger("hsms_communication")

        self.address = address
        self.port = port
        self.active = active
        self.sessionID = session_id
        self.name = name

        self.connected = False

        # system id counter
        self.systemCounter = random.randint(0, (2**32) - 1)

        # repeating linktest variables
        self.linktestTimer = None
        self.linktestTimeout = 30

        # response queues
        self._systemQueues = {}

        # hsms connection state fsm
        self.connectionState = Fysom({
            'initial':
            'NOT_CONNECTED',
            'events': [
                {
                    'name': 'connect',
                    'src': 'NOT_CONNECTED',
                    'dst': 'CONNECTED'
                },
                {
                    'name': 'disconnect',
                    'src': ['CONNECTED', 'NOT_SELECTED', 'SELECTED'],
                    'dst': 'NOT_CONNECTED'
                },
                {
                    'name': 'select',
                    'src': ['CONNECTED', 'NOT_SELECTED'],
                    'dst': 'SELECTED'
                },
                {
                    'name': 'deselect',
                    'src': 'SELECTED',
                    'dst': 'NOT_SELECTED'
                },
                {
                    'name': 'timeoutT7',
                    'src': ['CONNECTED', 'NOT_SELECTED'],
                    'dst': 'NOT_CONNECTED'
                },
            ],
            'callbacks': {
                'onNOT_SELECTED': self._on_state_connect,
                'onNOT_CONNECTED': self._on_state_disconnect,
                'onSELECTED': self._on_state_select,
            },
            'autoforward': [{
                'src': 'CONNECTED',
                'dst': 'NOT_SELECTED'
            }]
        })

        # setup connection
        if self.active:
            if custom_connection_handler is None:
                self.connection = HsmsActiveConnection(self.address, self.port,
                                                       self.sessionID, self)
            else:
                self.connection = custom_connection_handler.create_connection(
                    self.address, self.port, self.sessionID, self)
        else:
            if custom_connection_handler is None:
                self.connection = HsmsPassiveConnection(
                    self.address, self.port, self.sessionID, self)
            else:
                self.connection = custom_connection_handler.create_connection(
                    self.address, self.port, self.sessionID, self)
Example #3
0
class HsmsHandler(EventProducer):
    """Baseclass for creating Host/Equipment models. This layer contains the HSMS functionality. Inherit from this class and override required functions.

    :param address: IP address of remote host
    :type address: string
    :param port: TCP port of remote host
    :type port: integer
    :param active: Is the connection active (*True*) or passive (*False*)
    :type active: boolean
    :param session_id: session / device ID to use for connection
    :type session_id: integer
    :param name: Name of the underlying configuration
    :type name: string
    :param event_handler: object for event handling
    :type event_handler: :class:`secsgem.common.EventHandler`
    :param custom_connection_handler: object for connection handling (ie multi server)
    :type custom_connection_handler: :class:`secsgem.hsms.connections.HsmsMultiPassiveServer`

    **Example**::

        import secsgem

        def onConnect(event, data):
            print "Connected"

        client = secsgem.HsmsHandler("10.211.55.33", 5000, True, 0, "test", event_handler=secsgem.EventHandler(events={'hsms_connected': onConnect}))

        client.enable()

        time.sleep(3)

        client.disable()

    """
    def __init__(self, address, port, active, session_id, name, event_handler=None, custom_connection_handler=None):
        EventProducer.__init__(self, event_handler)

        self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__)
        self.communicationLogger = logging.getLogger("hsms_communication")

        self.address = address
        self.port = port
        self.active = active
        self.sessionID = session_id
        self.name = name

        self.connected = False

        # system id counter
        self.systemCounter = random.randint(0, (2 ** 32) - 1)

        # repeating linktest variables
        self.linktestTimer = None
        self.linktestTimeout = 30

        # response queues
        self._systemQueues = {}

        # hsms connection state fsm
        self.connectionState = Fysom({
            'initial': 'NOT_CONNECTED',
            'events': [
                {'name': 'connect', 'src': 'NOT_CONNECTED', 'dst': 'CONNECTED'},
                {'name': 'disconnect', 'src': ['CONNECTED', 'NOT_SELECTED', 'SELECTED'], 'dst': 'NOT_CONNECTED'},
                {'name': 'select', 'src': ['CONNECTED', 'NOT_SELECTED'], 'dst': 'SELECTED'},
                {'name': 'deselect', 'src': 'SELECTED', 'dst': 'NOT_SELECTED'},
                {'name': 'timeoutT7', 'src': ['CONNECTED', 'NOT_SELECTED'], 'dst': 'NOT_CONNECTED'},
            ],
            'callbacks': {
                'onNOT_SELECTED': self._on_state_connect,
                'onNOT_CONNECTED': self._on_state_disconnect,
                'onSELECTED': self._on_state_select,
            },
            'autoforward': [
                {'src': 'CONNECTED', 'dst': 'NOT_SELECTED'}
            ]
        })

        # setup connection
        if self.active:
            if custom_connection_handler is None:
                self.connection = HsmsActiveConnection(self.address, self.port, self.sessionID, self)
            else:
                self.connection = custom_connection_handler.create_connection(self.address, self.port, self.sessionID, self)
        else:
            if custom_connection_handler is None:
                self.connection = HsmsPassiveConnection(self.address, self.port, self.sessionID, self)
            else:
                self.connection = custom_connection_handler.create_connection(self.address, self.port, self.sessionID, self)

    def get_next_system_counter(self):
        """Returns the next System.

        :returns: System for the next command
        :rtype: integer
        """
        self.systemCounter += 1

        if self.systemCounter > ((2 ** 32) - 1):
            self.systemCounter = 0

        return self.systemCounter

    def _on_state_connect(self, _):
        """Connection state model got event connect

        :param data: event attributes
        :type data: object
        """
        # start linktest timer
        self.linktestTimer = threading.Timer(self.linktestTimeout, self._on_linktest_timer)
        self.linktestTimer.start()

        # start select process if connection is active
        if self.active:
            response = self.send_select_req()
            if response is None:
                self.logger.warning("select request failed")

    def _on_state_disconnect(self, _):
        """Connection state model got event disconnect

        :param data: event attributes
        :type data: object
        """
        # stop linktest timer
        if self.linktestTimer:
            self.linktestTimer.cancel()

        self.linktestTimer = None

    def _on_state_select(self, _):
        """Connection state model got event select

        :param data: event attributes
        :type data: object
        """
        # send event
        self.fire_event('hsms_selected', {'connection': self})

        # notify hsms handler of selection
        if hasattr(self, '_on_hsms_select') and callable(getattr(self, '_on_hsms_select')):
            self._on_hsms_select()

    def _on_linktest_timer(self):
        """Linktest time timed out, so send linktest request"""
        # send linktest request and wait for response
        self.send_linktest_req()

        # restart the timer
        self.linktestTimer = threading.Timer(self.linktestTimeout, self._on_linktest_timer)
        self.linktestTimer.start()

    def on_connection_established(self, _):
        """Connection was established"""
        self.connected = True

        # update connection state
        self.connectionState.connect()

        self.fire_event("hsms_connected", {'connection': self})

    def on_connection_before_closed(self, _):
        """Connection is about to be closed"""
        # send separate request
        self.send_separate_req()

    def on_connection_closed(self, _):
        """Connection was closed"""
        # update connection state
        self.connected = False
        self.connectionState.disconnect()

        self.fire_event("hsms_disconnected", {'connection': self})

    def on_connection_packet_received(self, _, packet):
        """Packet received by connection

        :param packet: received data packet
        :type packet: :class:`secsgem.hsms.packets.HsmsPacket`
        """
        # if packet.header.system > self.systemCounter or packet.header.system == 0:
        #     self.systemCounter = packet.header.system

        if packet.header.sType > 0:
            self.communicationLogger.info("< %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())

            # check if it is a select request
            if packet.header.sType == 0x01:
                # if we are disconnecting send reject else send response
                if self.connection.disconnecting:
                    self.send_reject_rsp(packet.header.system, packet.header.sType, 4)
                else:
                    self.send_select_rsp(packet.header.system)

                    # update connection state
                    self.connectionState.select()

            # check if it is a select response
            elif packet.header.sType == 0x02:
                # update connection state
                self.connectionState.select()

                if packet.header.system in self._systemQueues:
                    # send packet to request sender
                    self._systemQueues[packet.header.system].put_nowait(packet)

                # what to do if no sender for request waiting?

            # check if it is a deselect request
            elif packet.header.sType == 0x03:
                # if we are disconnecting send reject else send response
                if self.connection.disconnecting:
                    self.send_reject_rsp(packet.header.system, packet.header.sType, 4)
                else:
                    self.send_deselect_rsp(packet.header.system)
                    # update connection state
                    self.connectionState.deselect()

            # check if it is a deselect response
            elif packet.header.sType == 0x04:
                # update connection state
                self.connectionState.deselect()

                if packet.header.system in self._systemQueues:
                    # send packet to request sender
                    self._systemQueues[packet.header.system].put_nowait(packet)

                # what to do if no sender for request waiting?

            # check if it is a linktest request
            elif packet.header.sType == 0x05:
                # if we are disconnecting send reject else send response
                if self.connection.disconnecting:
                    self.send_reject_rsp(packet.header.system, packet.header.sType, 4)
                else:
                    self.send_linktest_rsp(packet.header.system)

            else:
                if packet.header.system in self._systemQueues:
                    # send packet to request sender
                    self._systemQueues[packet.header.system].put_nowait(packet)

                # what to do if no sender for request waiting?
        else:
            if hasattr(self, 'secs_decode') and callable(getattr(self, 'secs_decode')):
                message = self.secs_decode(packet)
                if message is None:
                    self.communicationLogger.info("< %s", packet, extra=self._get_log_extra())
                else:
                    self.communicationLogger.info("< %s\n%s", packet, message, extra=self._get_log_extra())
            else:
                self.communicationLogger.info("< %s", packet, extra=self._get_log_extra())

            if not self.connectionState.isstate("SELECTED"):
                self.logger.warning("received message when not selected")

                out_packet = HsmsPacket(HsmsRejectReqHeader(packet.header.system, packet.header.sType, 4))
                self.communicationLogger.info("> %s\n  %s", out_packet, hsmsSTypes[out_packet.header.sType], extra=self._get_log_extra())
                self.connection.send_packet(out_packet)

                return True

            # someone is waiting for this message
            if packet.header.system in self._systemQueues:
                # send packet to request sender
                self._systemQueues[packet.header.system].put_nowait(packet)
            # redirect packet to hsms handler
            elif hasattr(self, '_on_hsms_packet_received') and callable(getattr(self, '_on_hsms_packet_received')):
                self._on_hsms_packet_received(packet)
            # just log if nobody is interested
            else:
                self.logger.warning("packet unhandled")

    def _get_queue_for_system(self, system_id):
        """Creates a new queue to receive responses for a certain system

        :param system_id: system id to watch
        :type system_id: int
        :returns: queue to receive responses with
        :rtype: Queue.Queue
        """
        self._systemQueues[system_id] = Queue.Queue()
        return self._systemQueues[system_id]

    def _remove_queue(self, system_id):
        """Remove queue for system id from list

        :param system_id: system id to remove
        :type system_id: int
        """
        del self._systemQueues[system_id]

    def _serialize_data(self):
        """Returns data for serialization

        :returns: data to serialize for this object
        :rtype: dict
        """
        return {'address': self.address, 'port': self.port, 'active': self.active, 'sessionID': self.sessionID, 'name': self.name, 'connected': self.connected}

    def enable(self):
        """Enables the connection"""
        self.connection.enable()

    def disable(self):
        """Disables the connection"""
        self.connection.disable()

    def send_stream_function(self, packet):
        """Send the packet and wait for the response

        :param packet: packet to be sent
        :type packet: :class:`secsgem.secs.functionbase.SecsStreamFunction`
        """
        out_packet = HsmsPacket(HsmsStreamFunctionHeader(self.get_next_system_counter(), packet.stream, packet.function, True, self.sessionID), packet.encode())

        if packet is None:
            self.communicationLogger.info("> %s", out_packet, extra=self._get_log_extra())
        else:
            self.communicationLogger.info("> %s\n%s", out_packet, packet, extra=self._get_log_extra())

        return self.connection.send_packet(out_packet)

    def send_and_waitfor_response(self, packet):
        """Send the packet and wait for the response

        :param packet: packet to be sent
        :type packet: :class:`secsgem.secs.functionbase.SecsStreamFunction`
        :returns: Packet that was received
        :rtype: :class:`secsgem.hsms.packets.HsmsPacket`
        """
        system_id = self.get_next_system_counter()

        response_queue = self._get_queue_for_system(system_id)

        out_packet = HsmsPacket(HsmsStreamFunctionHeader(system_id, packet.stream, packet.function, True, self.sessionID), packet.encode())

        if packet is None:
            self.communicationLogger.info("> %s", out_packet, extra=self._get_log_extra())
        else:
            self.communicationLogger.info("> %s\n%s", out_packet, packet, extra=self._get_log_extra())

        if not self.connection.send_packet(out_packet):
            self.logger.error("Sending packet failed")
            self._remove_queue(system_id)
            return None

        try:
            response = response_queue.get(True, self.connection.T3)
        except Queue.Empty:
            response = None

        self._remove_queue(system_id)

        return response

    def send_response(self, function, system):
        """Send response function for system

        :param function: function to be sent
        :type function: :class:`secsgem.secs.functionbase.SecsStreamFunction`
        :param system: system to reply to
        :type system: integer
        """
        out_packet = HsmsPacket(HsmsStreamFunctionHeader(system, function.stream, function.function, False, self.sessionID), function.encode())

        if function is None:
            self.communicationLogger.info("> %s", out_packet, extra=self._get_log_extra())
        else:
            self.communicationLogger.info("> %s\n%s", out_packet, function, extra=self._get_log_extra())

        return self.connection.send_packet(out_packet)

    def send_select_req(self):
        """Send a Select Request to the remote host

        :returns: System of the sent request
        :rtype: integer
        """
        system_id = self.get_next_system_counter()

        response_queue = self._get_queue_for_system(system_id)

        packet = HsmsPacket(HsmsSelectReqHeader(system_id))
        self.communicationLogger.info("> %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())

        if not self.connection.send_packet(packet):
            self._remove_queue(system_id)
            return None

        try:
            response = response_queue.get(True, self.connection.T6)
        except Queue.Empty:
            response = None

        self._remove_queue(system_id)

        return response

    def send_select_rsp(self, system_id):
        """Send a Select Response to the remote host

        :param system_id: System of the request to reply for
        :type system_id: integer
        """
        packet = HsmsPacket(HsmsSelectRspHeader(system_id))
        self.communicationLogger.info("> %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())
        return self.connection.send_packet(packet)

    def send_linktest_req(self):
        """Send a Linktest Request to the remote host

        :returns: System of the sent request
        :rtype: integer
        """
        system_id = self.get_next_system_counter()

        response_queue = self._get_queue_for_system(system_id)

        packet = HsmsPacket(HsmsLinktestReqHeader(system_id))
        self.communicationLogger.info("> %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())

        if not self.connection.send_packet(packet):
            self._remove_queue(system_id)
            return None

        try:
            response = response_queue.get(True, self.connection.T6)
        except Queue.Empty:
            response = None

        self._remove_queue(system_id)

        return response

    def send_linktest_rsp(self, system_id):
        """Send a Linktest Response to the remote host

        :param system_id: System of the request to reply for
        :type system_id: integer
        """
        packet = HsmsPacket(HsmsLinktestRspHeader(system_id))
        self.communicationLogger.info("> %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())
        return self.connection.send_packet(packet)

    def send_deselect_req(self):
        """Send a Deselect Request to the remote host

        :returns: System of the sent request
        :rtype: integer
        """
        system_id = self.get_next_system_counter()

        response_queue = self._get_queue_for_system(system_id)

        packet = HsmsPacket(HsmsDeselectReqHeader(system_id))
        self.communicationLogger.info("> %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())

        if not self.connection.send_packet(packet):
            self._remove_queue(system_id)
            return None

        try:
            response = response_queue.get(True, self.connection.T6)
        except Queue.Empty:
            response = None

        self._remove_queue(system_id)

        return response

    def send_deselect_rsp(self, system_id):
        """Send a Deselect Response to the remote host

        :param system_id: System of the request to reply for
        :type system_id: integer
        """
        packet = HsmsPacket(HsmsDeselectRspHeader(system_id))
        self.communicationLogger.info("> %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())
        return self.connection.send_packet(packet)

    def send_reject_rsp(self, system_id, s_type, reason):
        """Send a Reject Response to the remote host

        :param system_id: System of the request to reply for
        :type system_id: integer
        :param s_type: s_type of rejected message
        :type s_type: integer
        :param reason: reason for rejection
        :type reason: integer
        """
        packet = HsmsPacket(HsmsRejectReqHeader(system_id, s_type, reason))
        self.communicationLogger.info("> %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())
        return self.connection.send_packet(packet)

    def send_separate_req(self):
        """Send a Separate Request to the remote host"""
        system_id = self.get_next_system_counter()

        packet = HsmsPacket(HsmsSeparateReqHeader(system_id))
        self.communicationLogger.info("> %s\n  %s", packet, hsmsSTypes[packet.header.sType], extra=self._get_log_extra())

        if not self.connection.send_packet(packet):
            return None

        return system_id

    # helpers

    def _get_log_extra(self):
        return {"address": self.address, "port": self.port, "sessionID": self.sessionID, "remoteName": self.name}
Example #4
0
class HsmsHandler(EventProducer):
    """Baseclass for creating Host/Equipment models. This layer contains the HSMS functionality. Inherit from this class and override required functions.

    :param address: IP address of remote host
    :type address: string
    :param port: TCP port of remote host
    :type port: integer
    :param active: Is the connection active (*True*) or passive (*False*)
    :type active: boolean
    :param session_id: session / device ID to use for connection
    :type session_id: integer
    :param name: Name of the underlying configuration
    :type name: string
    :param event_handler: object for event handling
    :type event_handler: :class:`secsgem.common.EventHandler`
    :param custom_connection_handler: object for connection handling (ie multi server)
    :type custom_connection_handler: :class:`secsgem.hsms.connections.HsmsMultiPassiveServer`

    **Example**::

        import secsgem

        def onConnect(event, data):
            print "Connected"

        client = secsgem.HsmsHandler("10.211.55.33", 5000, True, 0, "test", event_handler=secsgem.EventHandler(events={'hsms_connected': onConnect}))

        client.enable()

        time.sleep(3)

        client.disable()

    """
    def __init__(self,
                 address,
                 port,
                 active,
                 session_id,
                 name,
                 event_handler=None,
                 custom_connection_handler=None):
        EventProducer.__init__(self, event_handler)

        self.logger = logging.getLogger(self.__module__ + "." +
                                        self.__class__.__name__)
        self.communicationLogger = logging.getLogger("hsms_communication")

        self.address = address
        self.port = port
        self.active = active
        self.sessionID = session_id
        self.name = name

        self.connected = False

        # system id counter
        self.systemCounter = random.randint(0, (2**32) - 1)

        # repeating linktest variables
        self.linktestTimer = None
        self.linktestTimeout = 30

        # response queues
        self._systemQueues = {}

        # hsms connection state fsm
        self.connectionState = Fysom({
            'initial':
            'NOT_CONNECTED',
            'events': [
                {
                    'name': 'connect',
                    'src': 'NOT_CONNECTED',
                    'dst': 'CONNECTED'
                },
                {
                    'name': 'disconnect',
                    'src': ['CONNECTED', 'NOT_SELECTED', 'SELECTED'],
                    'dst': 'NOT_CONNECTED'
                },
                {
                    'name': 'select',
                    'src': ['CONNECTED', 'NOT_SELECTED'],
                    'dst': 'SELECTED'
                },
                {
                    'name': 'deselect',
                    'src': 'SELECTED',
                    'dst': 'NOT_SELECTED'
                },
                {
                    'name': 'timeoutT7',
                    'src': ['CONNECTED', 'NOT_SELECTED'],
                    'dst': 'NOT_CONNECTED'
                },
            ],
            'callbacks': {
                'onNOT_SELECTED': self._on_state_connect,
                'onNOT_CONNECTED': self._on_state_disconnect,
                'onSELECTED': self._on_state_select,
            },
            'autoforward': [{
                'src': 'CONNECTED',
                'dst': 'NOT_SELECTED'
            }]
        })

        # setup connection
        if self.active:
            if custom_connection_handler is None:
                self.connection = HsmsActiveConnection(self.address, self.port,
                                                       self.sessionID, self)
            else:
                self.connection = custom_connection_handler.create_connection(
                    self.address, self.port, self.sessionID, self)
        else:
            if custom_connection_handler is None:
                self.connection = HsmsPassiveConnection(
                    self.address, self.port, self.sessionID, self)
            else:
                self.connection = custom_connection_handler.create_connection(
                    self.address, self.port, self.sessionID, self)

    def get_next_system_counter(self):
        """Returns the next System.

        :returns: System for the next command
        :rtype: integer
        """
        self.systemCounter += 1

        if self.systemCounter > ((2**32) - 1):
            self.systemCounter = 0

        return self.systemCounter

    def _on_state_connect(self, _):
        """Connection state model got event connect

        :param data: event attributes
        :type data: object
        """
        # start linktest timer
        self.linktestTimer = threading.Timer(self.linktestTimeout,
                                             self._on_linktest_timer)
        self.linktestTimer.start()

        # start select process if connection is active
        if self.active:
            response = self.send_select_req()
            if response is None:
                self.logger.warning("select request failed")

    def _on_state_disconnect(self, _):
        """Connection state model got event disconnect

        :param data: event attributes
        :type data: object
        """
        # stop linktest timer
        if self.linktestTimer:
            self.linktestTimer.cancel()

        self.linktestTimer = None

    def _on_state_select(self, _):
        """Connection state model got event select

        :param data: event attributes
        :type data: object
        """
        # send event
        self.fire_event('hsms_selected', {'connection': self})

        # notify hsms handler of selection
        if hasattr(self, '_on_hsms_select') and callable(
                getattr(self, '_on_hsms_select')):
            self._on_hsms_select()

    def _on_linktest_timer(self):
        """Linktest time timed out, so send linktest request"""
        # send linktest request and wait for response
        self.send_linktest_req()

        # restart the timer
        self.linktestTimer = threading.Timer(self.linktestTimeout,
                                             self._on_linktest_timer)
        self.linktestTimer.start()

    def on_connection_established(self, _):
        """Connection was established"""
        self.connected = True

        # update connection state
        self.connectionState.connect()

        self.fire_event("hsms_connected", {'connection': self})

    def on_connection_before_closed(self, _):
        """Connection is about to be closed"""
        # send separate request
        self.send_separate_req()

    def on_connection_closed(self, _):
        """Connection was closed"""
        # update connection state
        self.connected = False
        self.connectionState.disconnect()

        self.fire_event("hsms_disconnected", {'connection': self})

    def on_connection_packet_received(self, _, packet):
        """Packet received by connection

        :param packet: received data packet
        :type packet: :class:`secsgem.hsms.packets.HsmsPacket`
        """
        # if packet.header.system > self.systemCounter or packet.header.system == 0:
        #     self.systemCounter = packet.header.system

        if packet.header.sType > 0:
            self.communicationLogger.info("< %s\n  %s",
                                          packet,
                                          hsmsSTypes[packet.header.sType],
                                          extra=self._get_log_extra())

            # check if it is a select request
            if packet.header.sType == 0x01:
                # if we are disconnecting send reject else send response
                if self.connection.disconnecting:
                    self.send_reject_rsp(packet.header.system,
                                         packet.header.sType, 4)
                else:
                    self.send_select_rsp(packet.header.system)

                    # update connection state
                    self.connectionState.select()

            # check if it is a select response
            elif packet.header.sType == 0x02:
                # update connection state
                self.connectionState.select()

                if packet.header.system in self._systemQueues:
                    # send packet to request sender
                    self._systemQueues[packet.header.system].put_nowait(packet)

                # what to do if no sender for request waiting?

            # check if it is a deselect request
            elif packet.header.sType == 0x03:
                # if we are disconnecting send reject else send response
                if self.connection.disconnecting:
                    self.send_reject_rsp(packet.header.system,
                                         packet.header.sType, 4)
                else:
                    self.send_deselect_rsp(packet.header.system)
                    # update connection state
                    self.connectionState.deselect()

            # check if it is a deselect response
            elif packet.header.sType == 0x04:
                # update connection state
                self.connectionState.deselect()

                if packet.header.system in self._systemQueues:
                    # send packet to request sender
                    self._systemQueues[packet.header.system].put_nowait(packet)

                # what to do if no sender for request waiting?

            # check if it is a linktest request
            elif packet.header.sType == 0x05:
                # if we are disconnecting send reject else send response
                if self.connection.disconnecting:
                    self.send_reject_rsp(packet.header.system,
                                         packet.header.sType, 4)
                else:
                    self.send_linktest_rsp(packet.header.system)

            else:
                if packet.header.system in self._systemQueues:
                    # send packet to request sender
                    self._systemQueues[packet.header.system].put_nowait(packet)

                # what to do if no sender for request waiting?
        else:
            if hasattr(self, 'secs_decode') and callable(
                    getattr(self, 'secs_decode')):
                message = self.secs_decode(packet)
                if message is None:
                    self.communicationLogger.info("< %s",
                                                  packet,
                                                  extra=self._get_log_extra())
                else:
                    self.communicationLogger.info("< %s\n%s",
                                                  packet,
                                                  message,
                                                  extra=self._get_log_extra())
            else:
                self.communicationLogger.info("< %s",
                                              packet,
                                              extra=self._get_log_extra())

            if not self.connectionState.isstate("SELECTED"):
                self.logger.warning("received message when not selected")

                out_packet = HsmsPacket(
                    HsmsRejectReqHeader(packet.header.system,
                                        packet.header.sType, 4))
                self.communicationLogger.info(
                    "> %s\n  %s",
                    out_packet,
                    hsmsSTypes[out_packet.header.sType],
                    extra=self._get_log_extra())
                self.connection.send_packet(out_packet)

                return True

            # someone is waiting for this message
            if packet.header.system in self._systemQueues:
                # send packet to request sender
                self._systemQueues[packet.header.system].put_nowait(packet)
            # redirect packet to hsms handler
            elif hasattr(self, '_on_hsms_packet_received') and callable(
                    getattr(self, '_on_hsms_packet_received')):
                self._on_hsms_packet_received(packet)
            # just log if nobody is interested
            else:
                self.logger.warning("packet unhandled")

    def _get_queue_for_system(self, system_id):
        """Creates a new queue to receive responses for a certain system

        :param system_id: system id to watch
        :type system_id: int
        :returns: queue to receive responses with
        :rtype: Queue.Queue
        """
        self._systemQueues[system_id] = Queue.Queue()
        return self._systemQueues[system_id]

    def _remove_queue(self, system_id):
        """Remove queue for system id from list

        :param system_id: system id to remove
        :type system_id: int
        """
        del self._systemQueues[system_id]

    def _serialize_data(self):
        """Returns data for serialization

        :returns: data to serialize for this object
        :rtype: dict
        """
        return {
            'address': self.address,
            'port': self.port,
            'active': self.active,
            'sessionID': self.sessionID,
            'name': self.name,
            'connected': self.connected
        }

    def enable(self):
        """Enables the connection"""
        self.connection.enable()

    def disable(self):
        """Disables the connection"""
        self.connection.disable()

    def send_stream_function(self, packet):
        """Send the packet and wait for the response

        :param packet: packet to be sent
        :type packet: :class:`secsgem.secs.functionbase.SecsStreamFunction`
        """
        out_packet = HsmsPacket(
            HsmsStreamFunctionHeader(self.get_next_system_counter(),
                                     packet.stream, packet.function, True,
                                     self.sessionID), packet.encode())

        if packet is None:
            self.communicationLogger.info("> %s",
                                          out_packet,
                                          extra=self._get_log_extra())
        else:
            self.communicationLogger.info("> %s\n%s",
                                          out_packet,
                                          packet,
                                          extra=self._get_log_extra())

        return self.connection.send_packet(out_packet)

    def send_and_waitfor_response(self, packet):
        """Send the packet and wait for the response

        :param packet: packet to be sent
        :type packet: :class:`secsgem.secs.functionbase.SecsStreamFunction`
        :returns: Packet that was received
        :rtype: :class:`secsgem.hsms.packets.HsmsPacket`
        """
        system_id = self.get_next_system_counter()

        response_queue = self._get_queue_for_system(system_id)

        out_packet = HsmsPacket(
            HsmsStreamFunctionHeader(system_id, packet.stream, packet.function,
                                     True, self.sessionID), packet.encode())

        if packet is None:
            self.communicationLogger.info("> %s",
                                          out_packet,
                                          extra=self._get_log_extra())
        else:
            self.communicationLogger.info("> %s\n%s",
                                          out_packet,
                                          packet,
                                          extra=self._get_log_extra())

        if not self.connection.send_packet(out_packet):
            self.logger.error("Sending packet failed")
            self._remove_queue(system_id)
            return None

        try:
            response = response_queue.get(True, self.connection.T3)
        except Queue.Empty:
            response = None

        self._remove_queue(system_id)

        return response

    def send_response(self, function, system):
        """Send response function for system

        :param function: function to be sent
        :type function: :class:`secsgem.secs.functionbase.SecsStreamFunction`
        :param system: system to reply to
        :type system: integer
        """
        out_packet = HsmsPacket(
            HsmsStreamFunctionHeader(system, function.stream,
                                     function.function, False, self.sessionID),
            function.encode())

        if function is None:
            self.communicationLogger.info("> %s",
                                          out_packet,
                                          extra=self._get_log_extra())
        else:
            self.communicationLogger.info("> %s\n%s",
                                          out_packet,
                                          function,
                                          extra=self._get_log_extra())

        return self.connection.send_packet(out_packet)

    def send_select_req(self):
        """Send a Select Request to the remote host

        :returns: System of the sent request
        :rtype: integer
        """
        system_id = self.get_next_system_counter()

        response_queue = self._get_queue_for_system(system_id)

        packet = HsmsPacket(HsmsSelectReqHeader(system_id))
        self.communicationLogger.info("> %s\n  %s",
                                      packet,
                                      hsmsSTypes[packet.header.sType],
                                      extra=self._get_log_extra())

        if not self.connection.send_packet(packet):
            self._remove_queue(system_id)
            return None

        try:
            response = response_queue.get(True, self.connection.T6)
        except Queue.Empty:
            response = None

        self._remove_queue(system_id)

        return response

    def send_select_rsp(self, system_id):
        """Send a Select Response to the remote host

        :param system_id: System of the request to reply for
        :type system_id: integer
        """
        packet = HsmsPacket(HsmsSelectRspHeader(system_id))
        self.communicationLogger.info("> %s\n  %s",
                                      packet,
                                      hsmsSTypes[packet.header.sType],
                                      extra=self._get_log_extra())
        return self.connection.send_packet(packet)

    def send_linktest_req(self):
        """Send a Linktest Request to the remote host

        :returns: System of the sent request
        :rtype: integer
        """
        system_id = self.get_next_system_counter()

        response_queue = self._get_queue_for_system(system_id)

        packet = HsmsPacket(HsmsLinktestReqHeader(system_id))
        self.communicationLogger.info("> %s\n  %s",
                                      packet,
                                      hsmsSTypes[packet.header.sType],
                                      extra=self._get_log_extra())

        if not self.connection.send_packet(packet):
            self._remove_queue(system_id)
            return None

        try:
            response = response_queue.get(True, self.connection.T6)
        except Queue.Empty:
            response = None

        self._remove_queue(system_id)

        return response

    def send_linktest_rsp(self, system_id):
        """Send a Linktest Response to the remote host

        :param system_id: System of the request to reply for
        :type system_id: integer
        """
        packet = HsmsPacket(HsmsLinktestRspHeader(system_id))
        self.communicationLogger.info("> %s\n  %s",
                                      packet,
                                      hsmsSTypes[packet.header.sType],
                                      extra=self._get_log_extra())
        return self.connection.send_packet(packet)

    def send_deselect_req(self):
        """Send a Deselect Request to the remote host

        :returns: System of the sent request
        :rtype: integer
        """
        system_id = self.get_next_system_counter()

        response_queue = self._get_queue_for_system(system_id)

        packet = HsmsPacket(HsmsDeselectReqHeader(system_id))
        self.communicationLogger.info("> %s\n  %s",
                                      packet,
                                      hsmsSTypes[packet.header.sType],
                                      extra=self._get_log_extra())

        if not self.connection.send_packet(packet):
            self._remove_queue(system_id)
            return None

        try:
            response = response_queue.get(True, self.connection.T6)
        except Queue.Empty:
            response = None

        self._remove_queue(system_id)

        return response

    def send_deselect_rsp(self, system_id):
        """Send a Deselect Response to the remote host

        :param system_id: System of the request to reply for
        :type system_id: integer
        """
        packet = HsmsPacket(HsmsDeselectRspHeader(system_id))
        self.communicationLogger.info("> %s\n  %s",
                                      packet,
                                      hsmsSTypes[packet.header.sType],
                                      extra=self._get_log_extra())
        return self.connection.send_packet(packet)

    def send_reject_rsp(self, system_id, s_type, reason):
        """Send a Reject Response to the remote host

        :param system_id: System of the request to reply for
        :type system_id: integer
        :param s_type: s_type of rejected message
        :type s_type: integer
        :param reason: reason for rejection
        :type reason: integer
        """
        packet = HsmsPacket(HsmsRejectReqHeader(system_id, s_type, reason))
        self.communicationLogger.info("> %s\n  %s",
                                      packet,
                                      hsmsSTypes[packet.header.sType],
                                      extra=self._get_log_extra())
        return self.connection.send_packet(packet)

    def send_separate_req(self):
        """Send a Separate Request to the remote host"""
        system_id = self.get_next_system_counter()

        packet = HsmsPacket(HsmsSeparateReqHeader(system_id))
        self.communicationLogger.info("> %s\n  %s",
                                      packet,
                                      hsmsSTypes[packet.header.sType],
                                      extra=self._get_log_extra())

        if not self.connection.send_packet(packet):
            return None

        return system_id

    # helpers

    def _get_log_extra(self):
        return {
            "address": self.address,
            "port": self.port,
            "sessionID": self.sessionID,
            "remoteName": self.name
        }