Source code for cqc.Protocol

# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#    This product includes software developed by Stephanie Wehner, QuTech.
# 4. Neither the name of the QuTech organization nor the
#    names of its contributors may be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import logging

from twisted.internet.defer import inlineCallbacks
from twisted.internet.protocol import Protocol, connectionDone

from cqc.cqcHeader import CQC_VERSION, CQCHeader

###############################################################################
#
# CQC Protocol
#
# Execute the CQC Protocol giving access to the SimulaQron backend via the
# universal interface.
#


[docs]class CQCProtocol(Protocol): # Dictionary storing the next unique qubit id for each used app_id _next_q_id = {} # Dictionary storing the next unique entanglement id for each used # (host_app_id,remote_node,remote_app_id) _next_ent_id = {} def __init__(self, factory): # CQC Factory, including our connection to the SimulaQron backend self.factory = factory # Default application ID, typically one connection per application but # we will deliberately NOT check for that since this is the task of # higher layers or an OS self.app_id = 0 # Define the backend to use. self.messageHandler = factory.backend # Flag to determine whether we already received _all_ of the CQC header self.gotCQCHeader = False # Header for which we are currently processing a packet self.currHeader = None # Buffer received data (which may arrive in chunks) self.buf = None # Convenience self.name = self.factory.name logging.debug("CQC %s: Initialized Protocol", self.name)
[docs] def connectionMade(self): pass
[docs] def connectionLost(self, reason=connectionDone): pass
[docs] def dataReceived(self, data): """ Receive data. We will always wait to receive enough data for the header, and then the entire packet first before commencing processing. """ # Read whatever we received into a buffer if self.buf: self.buf = self.buf + data else: self.buf = data # If we don't have the CQC header yet, try and read it in full. if not self.gotCQCHeader: if len(self.buf) < CQCHeader.HDR_LENGTH: # Not enough data for CQC header, return and wait for the rest return # Got enough data for the CQC Header so read it in self.gotCQCHeader = True raw_header = self.buf[0:CQCHeader.HDR_LENGTH] self.currHeader = CQCHeader(raw_header) # Remove the header from the buffer self.buf = self.buf[CQCHeader.HDR_LENGTH: len(self.buf)] logging.debug("CQC %s: Read CQC Header: %s", self.name, self.currHeader.printable()) # Check whether we already received all the data if len(self.buf) < self.currHeader.length: # Still waiting for data logging.debug( "CQC %s: Incomplete data. Waiting. Current length %s, " "required length %s", self.name, len(self.buf), self.currHeader.length, ) return # We got the header and all the data for this packet. Start processing. # Update our app ID self.app_id = self.currHeader.app_id # Invoke the relevant message handler, processing the possibly # remaining data try: self._parseData(self.currHeader, self.buf[0: self.currHeader.length]) except Exception as e: print(e) import traceback traceback.print_exc() # if self.currHeader.tp in self.messageHandlers: # self.messageHandlers[self.currHeader.tp](self.currHeader, ) # else: # self._send_back_cqc(self.currHeader, CQC_ERR_UNSUPP) # Reset and await the next packet self.gotCQCHeader = False # Check if we received data already for the next packet, if so save it if self.currHeader.length < len(self.buf): self.buf = self.buf[self.currHeader.length: len(self.buf)] self.dataReceived(b"") else: self.buf = None
@inlineCallbacks def _parseData(self, header, data): try: yield self.messageHandler.handle_cqc_message(header, data) messages = self.messageHandler.retrieve_return_messages(header.app_id) except Exception as e: raise e if messages: # self.factory._lock.acquire() for msg in messages: self.transport.write(msg) # self.factory._lock.release() def _send_back_cqc(self, header, msgType, length=0): """ Return a simple CQC header with the specified type. header CQC header of the packet we respond to msgType Message type to return length Length of additional message """ hdr = CQCHeader() hdr.setVals(CQC_VERSION, msgType, header.app_id, length) msg = hdr.pack() self.transport.write(msg)