<commit_before>"""
UPEL Instrument Control Protocol (ICP)
======================================

Dependencies
------------

None

Detailed Operation
------------------

The UPEL ICP thread manages communication in and out of the network socket
on port 7968.

Message Queue
^^^^^^^^^^^^^

Packets to send out to remote devices are queued in the message queue and sent
one at a time. Queuing a message requires:

  * IP Address
  * TTL (Time to Live)
  * Response Queue
  * ICP Packet Object

The response packet object will be loaded into the response queue.

Routing Map
^^^^^^^^^^^

When a message is sent, it is assigned an ID within the packet header. If a
response is expected from an outgoing packet, an entry is made in the map
table to associate the packet ID of the response packet with the object that
sent the original packet.

The Arbiter will periodically scan the routing map for old entries. If an
entry has exceeded the TTL window, a signal is sent to the originating
object that a timeout condition has occurred. 

The Packet ID is an 8-bit value, so there are 256 possible ID codes. A 
Packet ID of 0x00 is reserved for "notification" packet where a response is 
not expected, and thus will never create an entry in the routing map, giving 
a maximum of 255 possible entries in the routing map.

The routing map has the format: { PacketID: Address }

Execution Strategy
^^^^^^^^^^^^^^^^^^

The arbiter will alternate between servicing the message queue, processing 
any data in the __socket buffer, and checking the status of entries in the 
routing map. If none of those tasks requires attention, the thread will 
sleep for a small time interval to limit loading the processor excessively.
"""

from Base_Interface import Base_Interface
from Base_Resource import Base_Resource

import struct
import time
import socket
import select
import threading
import Queue
from sets import Set

import numpy

import interfaces.upel as icp

#===========================================================================
# data_types_pack = {
#     'int8': lambda data: struct.pack('!b', int(data)),
#     'int16': lambda data: struct.pack('!h', int(data)),
#     'int32': lambda data: struct.pack('!i', int(data)),
#     'int64': lambda data: struct.pack('!q', int(data)),
#     'float': lambda data: struct.pack('!f', float(data)),
#     'double': lambda data: struct.pack('!d', float(data)) }
# 
# data_types_unpack = {
#     'int8': lambda data: struct.unpack('!b', data)[0],
#     'int16': lambda data: struct.unpack('!h', data)[0],
#     'int32': lambda data: struct.unpack('!i', data)[0],
#     'int64': lambda data: struct.unpack('!q', data)[0],
#     'float': lambda data: struct.unpack('!f', data)[0],
#     'double': lambda data: struct.unpack('!d', data)[0] }
#===========================================================================

class i_UPEL(Base_Interface):
    """
    Conforms to Rev 1.0 of the UPEL Instrument Control Protocol

    :author: <NAME>
    """
    
    info = {
        # Interface Author
        'author':               'KKENNEDY',
        # Interface Version
        'version':              '1.0',
        # Revision date
        'date':                 '2015-03-06'
    }
    
    # Dict: ResID -> Resource Object
    resources = {}
    
    # UPEL Controller Config
    broadcastIP = '192.168.1.255'
    DEFAULT_PORT = 7968
    
    e_conf = threading.Event()

    def open(self):
        # Init
        self.__messageQueue = Queue.Queue()
        self.__routingMap = {} # { PacketID: ResID }
        self.__expiration = {} # { PacketID: Expiration time }
        self.__availableIDs = Set(range(1,255))
        
        # Configure Socket
        # IPv4 UDP
        try:
            self.__socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.__socket.bind(('', self.DEFAULT_PORT))
            self.__socket.setblocking(0)
            self.__socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            _, port = self.__socket.getsockname()
            
            self.logger.debug("ICP Socket Bound to port %i", port)
            
            self.refresh()
            
        except:
            self.logger.exception("UPEL ICP Socket Exception")
            
        self.e_conf.set()
        
        return True
    
    def close(self):
        self.stop()
        
        self.__socket.shutdown(1)
        self.__socket.close()
        
        self.e_conf.clear()
        
    def run(self):
        while (self.e_alive.isSet()):
            if not self.e_conf.isSet():
                continue
             
            try:
                #===================================================================
                # Service the socket
                #===================================================================
                self._serviceSocket()
                
                #===================================================================<commit_msg>Added auto-refresh to UPEL interface<commit_after>"""
UPEL Instrument Control Protocol (ICP)
======================================

Dependencies
------------

None

Detailed Operation
------------------

The UPEL ICP thread manages communication in and out of the network socket
on port 7968.

Message Queue
^^^^^^^^^^^^^

Packets to send out to remote devices are queued in the message queue and sent
one at a time. Queuing a message requires:

  * IP Address
  * TTL (Time to Live)
  * Response Queue
  * ICP Packet Object

The response packet object will be loaded into the response queue.

Routing Map
^^^^^^^^^^^

When a message is sent, it is assigned an ID within the packet header. If a
response is expected from an outgoing packet, an entry is made in the map
table to associate the packet ID of the response packet with the object that
sent the original packet.

The Arbiter will periodically scan the routing map for old entries. If an
entry has exceeded the TTL window, a signal is sent to the originating
object that a timeout condition has occurred. 

The Packet ID is an 8-bit value, so there are 256 possible ID codes. A 
Packet ID of 0x00 is reserved for "notification" packet where a response is 
not expected, and thus will never create an entry in the routing map, giving 
a maximum of 255 possible entries in the routing map.

The routing map has the format: { PacketID: Address }

Execution Strategy
^^^^^^^^^^^^^^^^^^

The arbiter will alternate between servicing the message queue, processing 
any data in the __socket buffer, and checking the status of entries in the 
routing map. If none of those tasks requires attention, the thread will 
sleep for a small time interval to limit loading the processor excessively.
"""

from Base_Interface import Base_Interface
from Base_Resource import Base_Resource

import struct
import time
import socket
import select
import threading
import Queue
from sets import Set

import numpy

import interfaces.upel as icp

#===========================================================================
# data_types_pack = {
#     'int8': lambda data: struct.pack('!b', int(data)),
#     'int16': lambda data: struct.pack('!h', int(data)),
#     'int32': lambda data: struct.pack('!i', int(data)),
#     'int64': lambda data: struct.pack('!q', int(data)),
#     'float': lambda data: struct.pack('!f', float(data)),
#     'double': lambda data: struct.pack('!d', float(data)) }
# 
# data_types_unpack = {
#     'int8': lambda data: struct.unpack('!b', data)[0],
#     'int16': lambda data: struct.unpack('!h', data)[0],
#     'int32': lambda data: struct.unpack('!i', data)[0],
#     'int64': lambda data: struct.unpack('!q', data)[0],
#     'float': lambda data: struct.unpack('!f', data)[0],
#     'double': lambda data: struct.unpack('!d', data)[0] }
#===========================================================================

class i_UPEL(Base_Interface):
    """
    Conforms to Rev 1.0 of the UPEL Instrument Control Protocol

    :author: <NAME>
    """
    
    info = {
        # Interface Author
        'author':               'KKENNEDY',
        # Interface Version
        'version':              '1.0',
        # Revision date
        'date':                 '2015-03-06'
    }
    
    # Dict: ResID -> Resource Object
    resources = {}
    
    # UPEL Controller Config
    broadcastIP = '192.168.1.255'
    DEFAULT_PORT = 7968
    
    e_conf = threading.Event()

    def open(self):
        # Init
        self.__messageQueue = Queue.Queue()
        self.__routingMap = {} # { PacketID: ResID }
        self.__expiration = {} # { PacketID: Expiration time }
        self.__availableIDs = Set(range(1,255))
        
        # Configure Socket
        # IPv4 UDP
        try:
            self.__socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.__socket.bind(('', self.DEFAULT_PORT))
            self.__socket.setblocking(0)
            self.__socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            _, port = self.__socket.getsockname()
            
            self.logger.debug("ICP Socket Bound to port %i", port)
            
            self.refresh()
            
        except:
            self.logger.exception("UPEL ICP Socket Exception")
            
        self.e_conf.set()
        
        return True
    
    def close(self):
        self.stop()
        
        self.__socket.shutdown(1)
        self.__socket.close()
        
        self.e_conf.clear()
        
    def run(self):
        self.last_update = 0
        
        while (self.e_alive.isSet()):
            if not self.e_conf.isSet():
                continue
            
            # Auto-discover
            if time.time() - self.last_update > 10.0:
                self.last_update = time.time()
                self.refresh()
             
            try:
                #===================================================================
                # Service the socket
                #===================================================================
                self._serviceSocket()
                
                #===================================================================
{'fraction_non_alphanumeric': 0.1765802905378877, 'fraction_numerical': 0.013741656851197487, 'mean_word_length': 2.555129099790649, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6250009', 'n_tokens_mistral': 2876, 'n_tokens_neox': 2680, 'n_words': 942}