Source code for spinnman.messages.eieio.data_messages.eieio_data_message

from spinnman.exceptions import SpinnmanInvalidPacketException,\
    SpinnmanInvalidParameterException
from spinnman.messages.eieio.abstract_messages import AbstractEIEIOMessage
from spinnman.messages.eieio import EIEIOType, EIEIOPrefix
from spinnman.constants import UDP_MESSAGE_MAX_SIZE
from .eieio_data_header import EIEIODataHeader
from .key_data_element import KeyDataElement
from .key_payload_data_element import KeyPayloadDataElement

import math
import struct


[docs]class EIEIODataMessage(AbstractEIEIOMessage): """ An EIEIO Data message """ def __init__(self, eieio_header, data=None, offset=0): """ :param eieio_header: The header of the message :type eieio_header:\ :py:class:`spinnman.messages.eieio.data_messages.eieio_data_header.EIEIODataHeader` :param data: Optional data contained within the packet :type data: str :param offset: Optional offset where the valid data starts :type offset: int """ # The header self._header = eieio_header # Elements to be written self._elements = b"" # Keeping track of the reading of the data self._data = data self._offset = offset self._elements_read = 0
[docs] @staticmethod def create( eieio_type, count=0, data=None, offset=0, key_prefix=None, payload_prefix=None, timestamp=None, prefix_type=EIEIOPrefix.LOWER_HALF_WORD): """ Create a data message :param eieio_type: The EIEIOType of the message :param count: The number of items in the message :param data: The data in the message :param offset: The offset in the data where the actual data starts :param key_prefix: The prefix of the keys :param payload_prefix: The prefix of the payload :param timestamp: The timestamp of the packet :param prefix_type: The type of the key prefix if 16-bits """ payload_base = payload_prefix if timestamp is not None: payload_base = timestamp return EIEIODataMessage( eieio_header=EIEIODataHeader( eieio_type, count=count, prefix=key_prefix, payload_base=payload_base, prefix_type=prefix_type, is_time=timestamp is not None), data=data, offset=offset)
@property def eieio_header(self): return self._header
[docs] @staticmethod def min_packet_length( eieio_type, is_prefix=False, is_payload_base=False, is_timestamp=False): """ The minimum length of a message with the given header, in bytes :param eieio_type: the type of message :type eieio_type:\ :py:class:`spinnman.spinnman.messages.eieio.eieio_type.EIEIOType` :param is_prefix: True if there is a prefix, False otherwise :type is_prefix: bool :param is_payload_base: True if there is a payload base, False\ otherwise :type is_payload_base: bool :param is_timestamp: True if there is a timestamp, False otherwise :return: The minimum size of the packet in bytes :rtype: int """ header_size = EIEIODataHeader.get_header_size( eieio_type, is_prefix, is_payload_base | is_timestamp) return header_size + eieio_type.payload_bytes
[docs] def get_min_packet_length(self): """ Get the minimum length of a message instance in bytes :rtype: int """ return EIEIODataMessage.min_packet_length( eieio_type=self._header.eieio_type, is_prefix=self._header.prefix is not None, is_payload_base=self._header.payload_base is not None)
@property def max_n_elements(self): """ The maximum number of elements that can fit in the packet :rtype: int """ return int(math.floor((UDP_MESSAGE_MAX_SIZE - self._header.size) / (self._header.eieio_type.key_bytes + self._header.eieio_type.payload_bytes))) @property def n_elements(self): """ The number of elements in the packet """ return self._header.count @property def size(self): """ The size of the packet with the current contents """ return (self._header.size + ((self._header.eieio_type.key_bytes + self._header.eieio_type.payload_bytes) * self._header.count))
[docs] def add_key_and_payload(self, key, payload): """ Adds a key and payload to the packet :param key: The key to add :type key: int :param payload: The payload to add :type payload: int :raise SpinnmanInvalidParameterException:\ If the key or payload is too big for the format, or the format\ doesn't expect a payload """ if key > self._header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "key", key, "Larger than the maximum allowed of {}".format( self._header.eieio_type.max_value)) if payload > self._header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "payload", payload, "Larger than the maximum allowed of {}".format( self._header.eieio_type.max_value)) EIEIODataMessage.add_element( self, KeyPayloadDataElement( key, payload, self._header.is_time))
[docs] def add_key(self, key): """ Add a key to the packet :param key: The key to add :type key: int :raise SpinnmanInvalidParameterException:\ If the key is too big for the format, or the format expects a\ payload """ if key > self._header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "key", key, "Larger than the maximum allowed of {}".format( self._header.eieio_type.max_value)) EIEIODataMessage.add_element(self, KeyDataElement(key))
[docs] def add_element(self, element): """ Add an element to the message. The correct type of element must\ be added, depending on the header values :param element: The element to be added :type element:\ :py:class:`spinnman.messages.eieio.data_messages.abstract_eieio_data_element.AbstractEIEIODataElement` :raise SpinnmanInvalidParameterException: If the element is not\ compatible with the header :raise SpinnmanInvalidPacketException: If the message was created to\ read data """ if self._data is not None: raise SpinnmanInvalidPacketException( "EIEIODataMessage", "This packet is read-only") self._elements += element.get_bytestring(self._header.eieio_type) self._header.increment_count()
@property def is_next_element(self): """ Determine if there is another element to be read :return: True if the message was created with data, and there are more\ elements to be read :rtype: bool """ return (self._data is not None and self._elements_read < self._header.count) @property def next_element(self): """ The next element to be read, or None if no more elements. The\ exact type of element returned depends on the packet type :rtype:\ :py:class:`spinnman.messages.eieio.data_messages.abstract_eieio_data_element.AbstractEIEIODataElement` """ if not self.is_next_element: return None self._elements_read += 1 key = None payload = None if self._header.eieio_type == EIEIOType.KEY_16_BIT: key = struct.unpack_from("<H", self._data, self._offset)[0] self._offset += 2 if self._header.eieio_type == EIEIOType.KEY_32_BIT: key = struct.unpack_from("<I", self._data, self._offset)[0] self._offset += 4 if self._header.eieio_type == EIEIOType.KEY_PAYLOAD_16_BIT: key, payload = struct.unpack_from("<HH", self._data, self._offset) self._offset += 4 if self._header.eieio_type == EIEIOType.KEY_PAYLOAD_32_BIT: key, payload = struct.unpack_from("<II", self._data, self._offset) self._offset += 8 if self._header.prefix is not None: if self._header.prefix_type == EIEIOPrefix.UPPER_HALF_WORD: key |= self._header.prefix << 16 else: key = key | self._header.prefix if self._header.payload_base is not None: if payload is not None: payload |= self._header.payload_base else: payload = self._header.payload_base if payload is None: return KeyDataElement(key) return KeyPayloadDataElement(key, payload, self._header.is_time) @property def bytestring(self): return self._header.bytestring + self._elements def __str__(self): if self._data is not None: return "EIEIODataMessage:{}:{}".format( self._header, self._header.count) return "EIEIODataMessage:{}:{}".format( self._header, self._elements) def __repr__(self): return self.__str__()