# 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)