<commit_before>#!/usr/bin/env python

################################################################################

import sys
import errno
import socket
import struct
import time
from copy import copy
import logging

try:
    import threading
    import Queue
except ImportError:
    logging.warn("Your python installation does not support threads.")
    logging.warn("")
    logging.warn("Please install a version of python that supports threading.")
    raise

try:
    import serial
except ImportError:
    logging.warn("You do not have the pyserial library installed.")
    logging.warn("")
    logging.warn("For installation instructions + see:")
    logging.warn("\thttp://pyserial.sourceforge.net/pyserial.html#installation")
    raise

################################################################################

class ICE(object):
    VERSIONS = ((0,1),(0,2))
    ONEYEAR = 365 * 24 * 60 * 60

    class ICE_Error(Exception):
        '''
        A base class for all exceptions raised by this module
        '''
        pass

    class FormatError(ICE_Error):
        '''
        Something in the ICE protocol communicating with the board went wrong.

        This error should never be encountered in normal use.
        '''
        pass

    class ParameterError(ICE_Error):
        '''
        An illegal parameter was passed.

        This may be raised by the ICE library if it can determine in advance
        that the reqeust is illegal (e.g. out of range), or by the ICE board if
        the board rejects the desired setting (e.g. not configurable)
        '''

    class NAK_Error(ICE_Error):
        '''
        Raised when an unexpected NAK is returned
        '''
        pass

    def __init__(self):
        '''
        An ICE object.

        Most methods are not usuable until connect() has been called.
        '''

        self.event_id = 0
        self.last_event_id = -1
        self.sync_queue = Queue.Queue(1)

        self.msg_handler = {}
        self.msg_handler['d'] = self.d_defragger
        self.d_frag = ''
        self.d_lock = threading.Lock()

    def connect(self, serial_device, baudrate=115200):
        '''
        Opens a connection to the ICE board.

        The ICE object configuration (e.g. message handlers) cannot be safely
        changed after this method is invoked.
        '''
        self.dev = serial.Serial(serial_device, baudrate)
        if self.dev.isOpen():
            logging.info("Connected to serial device at " + self.dev.portstr)
        else:
            raise self.ICE_Error, "Failed to connect to serial device"

        self.comm_thread = threading.Thread(target=self.communicator)
        self.comm_thread.daemon = True
        self.comm_thread.start()

        self.negotiate_version()

    def spawn_handler(self, msg_type, event_id, length, msg):
        try:
            t = threading.Thread(target=self.msg_handler[msg_type],
                    args=(msg_type, event_id, length, msg))
            t.daemon = True
            t.start()
        except KeyError:
            logging.warn("WARNING: No handler registered for message type: " +
                    str(msg_type))
            logging.warn("Known Types:")
            for t,f in self.msg_handler.iteritems():
                logging.warn("%s\t%s" % (t + f))
            logging.warn("         Dropping packet:")
            logging.warn("")
            logging.warn("    Type: %s" % (msg_type))
            logging.warn("Event ID: %d" % (event_id))
            logging.warn("  Length: %d" % (length))
            logging.warn(" Message:" + msg.encode('hex'))

    def communicator(self):
        while True:
            msg_type, event_id, length = self.dev.read(3)
            msg_type = ord(msg_type)
            event_id = ord(event_id)
            length = ord(length)
            msg = self.dev.read(length)

            if event_id == self.last_event_id:
                logging.warn("WARNING: Duplicate event_id! THIS IS A BUG [somewhere]!!")
                logging.warn("         Dropping packet:")
                logging.warn("")
                logging.warn("    Type: %d" % (msg_type))
                logging.warn("Event ID: %d" % (event_id))
                logging.warn("  Length: %d" % (length))
                logging.warn(" Message:" + msg.encode('hex'))
            else:
                self.last_event_id = event_id

            if msg_type in (0,1):
                # Ack / Nack response from a synchronous message
                try:
                    if msg_type == 0:
                        logging.debug("Got an ACK packet. Event: " + str(event_id))
                    else:
                        logging.info("Got a NAK packet. Event:" + str(event_id))
                    self.sync_queue.put((msg_type, msg))
                except Queue.Full:
                    logging.warn("WARNING: Synchronization lost. Unsolicited ACK/NAK.")
                    logging.warn("         Dropping packet:")
                    logging.warn("")
                    logging.warn("    Type: %s" % (["ACK","NAK"][msg_type]))
                    logging.warn("Event ID: %d" % (event_id))
                    logging.warn("  Length: %d" % (length))
                    logging.warn(" Message:" + msg.encode('hex'))
            else:
                msg_type = chr(msg_type)
                logging.debug("Got an async message of type: " + msg_type)
                self.spawn_handler(msg_type, event_id, length, msg)

    def d_defragger(self, msg_type, event_id, length, msg):
        '''
        Helper function to defragment 'd' type I2C messages before forwarding.

        This helper is installed by default for 'd' messages. It will attempt to
        call a helper registered under the name 'd+' when a complete message has
        been received. The message will be assigned the event id of the last
        received fragment.

        It may be safely overridden.
        '''
        with self.d_lock:
            assert msg_type == 'd'
            self.d_frag += msg
            # XXX: Make version dependent
            if length != 255:
                sys.stdout.flush()
                logging.debug("Got a complete I2C transaction of length %d bytes. Forwarding..." % (len(self.d_frag)))
                sys.stdout.flush()
                self.spawn_handler('d+', event_id, len(self.d_frag), copy(self.d_frag))
                self.d_frag = ''
            else:
                logging.debug("Got a fragment message... thus far %d bytes received:" % (len(self.d_frag)))

    def send_message(self, msg_type, msg='', length=None):
        if len(msg_type) != 1:
            raise self.FormatError, "msg_type must be exactly 1 character"

        if len(msg) > 255:
            raise self.FormatError, "msg too long. Maximum msg is 255 bytes"

        if length is None:
            length = len(msg)

        buf = struct.pack("BBB", ord(msg_type), self.event_id, length)
        self.event_id = (self.event_id + 1) % 256
        self.dev.write(buf + msg)

        # Ugly hack so python allows keyboard interrupts
        return self.sync_queue.get(True, self.ONEYEAR)

    def send_message_until_acked(self, msg_type, msg='', length=None, tries=5):
        while tries:
            ack, msg = self.send_message(msg_type, msg, length)
            if ack == 0:
                return msg
            tries -= 1

        raise self.NAK_Error

    def negotiate_version(self):
        '''
        Establish communication with an ICE board.

        This function is called automatically by __init__ and should not be
        called directly.
        '''
        logging.info("This library supports versions...")
        for major, minor in ICE.VERSIONS:
            logging.info("\t%d.%d" % (major, minor))

        resp = self.send_message_until_acked('V')
        if (len(resp) is 0) or (len(resp) % 2):
            raise self.FormatError, "Version response: " + resp

        logging.info("This ICE board supports versions...")
        self.major = None
        self.minor = None
        while len(resp) > 0:
            major, minor = struct.unpack("BB", resp[:2])
            resp = resp[2:]
            if self.major is None and (major, minor) in ICE.VERSIONS:
                self.major = major
                self.minor = minor
                logging.info("\t%d.%d **Chosen version" % (major, minor))
            else:
                logging.info("\t%d.%d" % (major, minor))

        if self.major is None:
            logging.warn("No versions in common. Version negotiation failed.")
            raise self.ICE_Error

        self.send_message_until_acked('v', struct.pack("BB", self.major, self.minor))

    def _fragment_sender(self, msg_type, msg):
        '''
        Internal. (helper for {i2c,goc}_send
        '''
        # XXX: Make version dependent?
        FRAG_SIZE = 255

        sent = 0<commit_msg>Add a debug print<commit_after>#!/usr/bin/env python

################################################################################

import sys
import errno
import socket
import struct
import time
from copy import copy
import logging

try:
    import threading
    import Queue
except ImportError:
    logging.warn("Your python installation does not support threads.")
    logging.warn("")
    logging.warn("Please install a version of python that supports threading.")
    raise

try:
    import serial
except ImportError:
    logging.warn("You do not have the pyserial library installed.")
    logging.warn("")
    logging.warn("For installation instructions + see:")
    logging.warn("\thttp://pyserial.sourceforge.net/pyserial.html#installation")
    raise

################################################################################

class ICE(object):
    VERSIONS = ((0,1),(0,2))
    ONEYEAR = 365 * 24 * 60 * 60

    class ICE_Error(Exception):
        '''
        A base class for all exceptions raised by this module
        '''
        pass

    class FormatError(ICE_Error):
        '''
        Something in the ICE protocol communicating with the board went wrong.

        This error should never be encountered in normal use.
        '''
        pass

    class ParameterError(ICE_Error):
        '''
        An illegal parameter was passed.

        This may be raised by the ICE library if it can determine in advance
        that the reqeust is illegal (e.g. out of range), or by the ICE board if
        the board rejects the desired setting (e.g. not configurable)
        '''

    class NAK_Error(ICE_Error):
        '''
        Raised when an unexpected NAK is returned
        '''
        pass

    def __init__(self):
        '''
        An ICE object.

        Most methods are not usuable until connect() has been called.
        '''

        self.event_id = 0
        self.last_event_id = -1
        self.sync_queue = Queue.Queue(1)

        self.msg_handler = {}
        self.msg_handler['d'] = self.d_defragger
        self.d_frag = ''
        self.d_lock = threading.Lock()

    def connect(self, serial_device, baudrate=115200):
        '''
        Opens a connection to the ICE board.

        The ICE object configuration (e.g. message handlers) cannot be safely
        changed after this method is invoked.
        '''
        self.dev = serial.Serial(serial_device, baudrate)
        if self.dev.isOpen():
            logging.info("Connected to serial device at " + self.dev.portstr)
        else:
            raise self.ICE_Error, "Failed to connect to serial device"

        self.comm_thread = threading.Thread(target=self.communicator)
        self.comm_thread.daemon = True
        self.comm_thread.start()

        self.negotiate_version()

    def spawn_handler(self, msg_type, event_id, length, msg):
        try:
            t = threading.Thread(target=self.msg_handler[msg_type],
                    args=(msg_type, event_id, length, msg))
            t.daemon = True
            t.start()
        except KeyError:
            logging.warn("WARNING: No handler registered for message type: " +
                    str(msg_type))
            logging.warn("Known Types:")
            for t,f in self.msg_handler.iteritems():
                logging.warn("%s\t%s" % (t + f))
            logging.warn("         Dropping packet:")
            logging.warn("")
            logging.warn("    Type: %s" % (msg_type))
            logging.warn("Event ID: %d" % (event_id))
            logging.warn("  Length: %d" % (length))
            logging.warn(" Message:" + msg.encode('hex'))

    def communicator(self):
        while True:
            msg_type, event_id, length = self.dev.read(3)
            msg_type = ord(msg_type)
            event_id = ord(event_id)
            length = ord(length)
            msg = self.dev.read(length)

            if event_id == self.last_event_id:
                logging.warn("WARNING: Duplicate event_id! THIS IS A BUG [somewhere]!!")
                logging.warn("         Dropping packet:")
                logging.warn("")
                logging.warn("    Type: %d" % (msg_type))
                logging.warn("Event ID: %d" % (event_id))
                logging.warn("  Length: %d" % (length))
                logging.warn(" Message:" + msg.encode('hex'))
            else:
                self.last_event_id = event_id

            if msg_type in (0,1):
                # Ack / Nack response from a synchronous message
                try:
                    if msg_type == 0:
                        logging.debug("Got an ACK packet. Event: " + str(event_id))
                    else:
                        logging.info("Got a NAK packet. Event:" + str(event_id))
                    self.sync_queue.put((msg_type, msg))
                except Queue.Full:
                    logging.warn("WARNING: Synchronization lost. Unsolicited ACK/NAK.")
                    logging.warn("         Dropping packet:")
                    logging.warn("")
                    logging.warn("    Type: %s" % (["ACK","NAK"][msg_type]))
                    logging.warn("Event ID: %d" % (event_id))
                    logging.warn("  Length: %d" % (length))
                    logging.warn(" Message:" + msg.encode('hex'))
            else:
                msg_type = chr(msg_type)
                logging.debug("Got an async message of type: " + msg_type)
                self.spawn_handler(msg_type, event_id, length, msg)

    def d_defragger(self, msg_type, event_id, length, msg):
        '''
        Helper function to defragment 'd' type I2C messages before forwarding.

        This helper is installed by default for 'd' messages. It will attempt to
        call a helper registered under the name 'd+' when a complete message has
        been received. The message will be assigned the event id of the last
        received fragment.

        It may be safely overridden.
        '''
        with self.d_lock:
            assert msg_type == 'd'
            self.d_frag += msg
            # XXX: Make version dependent
            if length != 255:
                sys.stdout.flush()
                logging.debug("Got a complete I2C transaction of length %d bytes. Forwarding..." % (len(self.d_frag)))
                sys.stdout.flush()
                self.spawn_handler('d+', event_id, len(self.d_frag), copy(self.d_frag))
                self.d_frag = ''
            else:
                logging.debug("Got a fragment message... thus far %d bytes received:" % (len(self.d_frag)))

    def send_message(self, msg_type, msg='', length=None):
        if len(msg_type) != 1:
            raise self.FormatError, "msg_type must be exactly 1 character"

        if len(msg) > 255:
            raise self.FormatError, "msg too long. Maximum msg is 255 bytes"

        if length is None:
            length = len(msg)

        buf = struct.pack("BBB", ord(msg_type), self.event_id, length)
        self.event_id = (self.event_id + 1) % 256
        self.dev.write(buf + msg)

        # Ugly hack so python allows keyboard interrupts
        return self.sync_queue.get(True, self.ONEYEAR)

    def send_message_until_acked(self, msg_type, msg='', length=None, tries=5):
        while tries:
            ack, msg = self.send_message(msg_type, msg, length)
            if ack == 0:
                return msg
            tries -= 1

        raise self.NAK_Error

    def negotiate_version(self):
        '''
        Establish communication with an ICE board.

        This function is called automatically by __init__ and should not be
        called directly.
        '''
        logging.info("This library supports versions...")
        for major, minor in ICE.VERSIONS:
            logging.info("\t%d.%d" % (major, minor))

        logging.debug("Sending version probe")
        resp = self.send_message_until_acked('V')
        if (len(resp) is 0) or (len(resp) % 2):
            raise self.FormatError, "Version response: " + resp

        logging.info("This ICE board supports versions...")
        self.major = None
        self.minor = None
        while len(resp) > 0:
            major, minor = struct.unpack("BB", resp[:2])
            resp = resp[2:]
            if self.major is None and (major, minor) in ICE.VERSIONS:
                self.major = major
                self.minor = minor
                logging.info("\t%d.%d **Chosen version" % (major, minor))
            else:
                logging.info("\t%d.%d" % (major, minor))

        if self.major is None:
            logging.warn("No versions in common. Version negotiation failed.")
            raise self.ICE_Error

        self.send_message_until_acked('v', struct.pack("BB", self.major, self.minor))

    def _fragment_sender(self, msg_type, msg):
        '''
        Internal. (helper for {i2c,goc}_send
        '''
        # XXX: Make version dependent?
        FRAG_SIZE = 255

        sent = 0
{'fraction_non_alphanumeric': 0.12218055633290424, 'fraction_numerical': 0.006268539766049141, 'mean_word_length': 1.8791492104415082, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, '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': '5198446', 'n_tokens_mistral': 5119, 'n_tokens_neox': 4792, 'n_words': 1445}