def construct_simple(self, tp): """Construct simple message. For example a HELLO message if tp=CQC_TP_HELLO. """ hdr = CQCHeader() hdr.setVals(CQC_VERSION, tp, self._appID, 0) msg = hdr.pack() return msg
def construct_command_headers(self, qID, command, **kwargs): """Construct a commmand consisting of a list of header objects. Extra arguments are only used if the command if of a type that needs them. - **Arguments** :qID: qubit ID :command: Command to be executed, eg CQC_CMD_H :nofify: Do we wish to be notified when done. :block: Do we want the qubit to be blocked """ # Construct extra header if needed. xtra_hdr = None if command == CQC_CMD_SEND or command == CQC_CMD_EPR: xtra_hdr = CQCCommunicationHeader() remote_appID = kwargs.get("remote_appID", 0) remote_node = kwargs.get("remote_node", 0) remote_port = kwargs.get("remote_port", 0) xtra_hdr.setVals(remote_appID, remote_node, remote_port) elif command == CQC_CMD_CNOT or command == CQC_CMD_CPHASE: xtra_hdr = CQCXtraQubitHeader() xtra_qID = kwargs.get("xtra_qID", 0) xtra_hdr.setVals(xtra_qID) elif (command == CQC_CMD_ROT_X or command == CQC_CMD_ROT_Y or command == CQC_CMD_ROT_Z): xtra_hdr = CQCRotationHeader() step = kwargs.get("step", 0) xtra_hdr.setVals(step) elif command == CQC_CMD_MEASURE or command == CQC_CMD_MEASURE_INPLACE: xtra_hdr = CQCAssignHeader() ref_id = kwargs.get("ref_id", 0) xtra_hdr.setVals(ref_id) # If xtra_hdr is None, we don't need an extra message. if xtra_hdr is None: header_length = CQCCmdHeader.HDR_LENGTH else: header_length = CQCCmdHeader.HDR_LENGTH + xtra_hdr.HDR_LENGTH # Construct Header hdr = CQCHeader() hdr.setVals(CQC_VERSION, CQC_TP_COMMAND, self._appID, header_length) # Construct Command cmd_hdr = CQCCmdHeader() notify = int(kwargs.get("notify", True)) block = int(kwargs.get("block", True)) action = int(kwargs.get("action", False)) cmd_hdr.setVals(qID, command, notify, block, action) headers = [hdr, cmd_hdr] if xtra_hdr is not None: headers.append(xtra_hdr) return headers
def create_return_message(app_id, msg_type, length=0, cqc_version=CQC_VERSION): """ Creates a messaage that the protocol should send back :param app_id: the app_id to which the message should be send :param msg_type: the type of message to return :param length: the length of additional message :param cqc_version: The cqc version of the message :return: a new header message to be send back """ hdr = CQCHeader() hdr.setVals(cqc_version, msg_type, app_id, length) return hdr.pack()
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)
def main(): # In this example, we are Alice. myName = "Alice" # Initialize the connection cqc = init(myName) # Send Hello message print("App {} tells CQC: 'HELLO'".format(myName)) hdr = CQCHeader() hdr.setVals(CQC_VERSION, CQC_TP_HELLO, 0, 0) msg = hdr.pack() cqc.send(msg) # Receive return message data = cqc.recv(192) hdr = CQCHeader(data) if hdr.tp == CQC_TP_HELLO: print("CQC tells App {}: 'HELLO'".format(myName)) else: print("Did not receive a hello message, but rather: {}".format( hdr.printable())) # Close the connection cqc.close()
def insert_cqc_header(self, cqc_type: CQCType, version=CQC_VERSION) -> None: """ Inserts a CQC Header at index 0 of self._pending_headers. Invoke this method *after* all other headers are pended, so that the correct message length is calculated. """ # Count the total message length message_length = 0 for header in self._pending_headers: message_length += header.HDR_LENGTH # Build the CQC Header cqc_header = CQCHeader() cqc_header.setVals(CQC_VERSION, cqc_type, self._appID, message_length) # Insert CQC Header at the front self._pending_headers.insert(0, cqc_header)
def sendGetTime(self, qID, notify=1, block=1, action=0): """Sends get-time message - **Arguments** :qID: qubit ID :command: Command to be executed, eg CQC_CMD_H :notify: Do we wish to be notified when done. :block: Do we want the qubit to be blocked :action: Are there more commands to be executed """ # Send Header hdr = CQCHeader() hdr.setVals(CQC_VERSION, CQC_TP_GET_TIME, self._appID, CQCCmdHeader.HDR_LENGTH) msg = hdr.pack() self.commit(msg) # Send Command cmd_hdr = CQCCmdHeader() cmd_hdr.setVals(qID, 0, notify, block, action) cmd_msg = cmd_hdr.pack() self.commit(cmd_msg)
def is_error_message(message: bytes): # Only CQCHeaders can be error messages, so if the length does not correspond it is not an error message try: header = CQCHeader(message) # A ValueError is raised by Header.__init__ if the message cannot be read as a CQCHeader. # Since only CQCHeaders can contain errors, this means the message is not an error except ValueError: return False error_types = { CQCType.ERR_GENERAL, CQCType.ERR_INUSE, CQCType.ERR_NOQUBIT, CQCType.ERR_TIMEOUT, CQCType.ERR_UNKNOWN, CQCType.ERR_UNSUPP } if header.tp in error_types: return True else: return False
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) < CQC_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:CQC_HDR_LENGTH] self.currHeader = CQCHeader(raw_header) # Remove the header from the buffer self.buf = self.buf[CQC_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
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. Is a setting in settings.ini 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) def connectionMade(self): pass def connectionLost(self, reason=connectionDone): pass 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) < CQC_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:CQC_HDR_LENGTH] self.currHeader = CQCHeader(raw_header) # Remove the header from the buffer self.buf = self.buf[CQC_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() 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) def new_qubit_id(self, app_id): """ Returns a new unique qubit id for the specified app_id. Used by cmd_new and cmd_recv """ if app_id in CQCProtocol._next_q_id: q_id = CQCProtocol._next_q_id[app_id] CQCProtocol._next_q_id[app_id] += 1 return q_id else: """ Returns a new unique qubit id for the specified app_id. Used by cmd_new and cmd_recv """ if app_id in CQCProtocol._next_q_id: q_id = CQCProtocol._next_q_id[app_id] CQCProtocol._next_q_id[app_id] += 1 return q_id else: CQCProtocol._next_q_id[app_id] = 1 return 0 def new_ent_id(self, host_app_id, remote_node, remote_app_id): """ Returns a new unique entanglement id for the specified host_app_id, remote_node and remote_app_id. Used by cmd_epr. """ pair_id = (host_app_id, remote_node, remote_app_id) if pair_id in CQCProtocol._next_ent_id: ent_id = CQCProtocol._next_ent_id[pair_id] CQCProtocol._next_ent_id[pair_id] += 1 return ent_id else: CQCProtocol._next_ent_id[pair_id] = 1 return 0
def readMessage(self, maxsize=192): # WHAT IS GOOD SIZE? """Receive the whole message from cqc server. Returns (CQCHeader,None,None), (CQCHeader,CQCNotifyHeader,None) or (CQCHeader,CQCNotifyHeader,EntInfoHeader) depending on the type of message. Maxsize is the max size of message. """ # Initialize checks gotCQCHeader = False if self.buf: checkedBuf = False else: checkedBuf = True while True: # If buf does not contain enough data, read in more if checkedBuf: # Receive data data = self._s.recv(maxsize) # Read whatever we received into a buffer if 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 gotCQCHeader: if len(self.buf) < CQCHeader.HDR_LENGTH: # Not enough data for CQC header, return and wait for the rest checkedBuf = True continue # Got enough data for the CQC Header so read it in gotCQCHeader = True rawHeader = self.buf[0:CQCHeader.HDR_LENGTH] currHeader = CQCHeader(rawHeader) # Remove the header from the buffer self.buf = self.buf[CQCHeader.HDR_LENGTH:len(self.buf)] # Check for error self.check_error(currHeader) # Check whether we already received all the data if len(self.buf) < currHeader.length: # Still waiting for data checkedBuf = True continue else: break # We got all the data, read other headers if there is any if currHeader.length == 0: return currHeader, None, None else: if currHeader.tp == CQC_TP_INF_TIME: timeinfo_header = self._extract_header(CQCTimeinfoHeader) return currHeader, timeinfo_header, None elif currHeader.tp == CQC_TP_MEASOUT: measout_header = self._extract_header(CQCMeasOutHeader) return currHeader, measout_header, None elif currHeader.tp in [CQC_TP_RECV, CQC_TP_NEW_OK, CQC_TP_EXPIRE]: xtra_qubit_header = self._extract_header(CQCXtraQubitHeader) return currHeader, xtra_qubit_header, None elif currHeader.tp == CQC_TP_EPR_OK: xtra_qubit_header = self._extract_header(CQCXtraQubitHeader) ent_info_hdr = self._extract_header(EntInfoHeader) return currHeader, xtra_qubit_header, ent_info_hdr