def send_instruction(callsign, instr, consider_cpdlc): ''' Teacher sessions: direct control of selected aircraft with given instr (callsign match bypass). Other sessions: - perform manager-dependant instruction to callsign - give a cpdlcSuggGui to suggest sending through data link instead, if callsign is connected CAUTION: Instruction.encodeToStr raises NotImplementedError with non-encoded instruction types. ''' if settings.session_manager.session_type == SessionType.TEACHER: if selection.acft == None: QMessageBox.critical(settings.session_manager.gui, 'Traffic command error', 'No aircraft selected.') elif settings.teacher_ACFT_requesting_CPDLC_vectors == selection.acft.identifier: instr_str = instr.encodeToStr() settings.session_manager.sendCpdlcMsg( selection.acft.identifier, CpdlcMessage(True, CpdlcMessage.REQUEST, contents=instr_str)) QMessageBox.information( settings.session_manager.gui, 'CPDLC request sent', 'Requested through data link: ' + instr_str) else: settings.session_manager.instructAircraftByCallsign( selection.acft.identifier, instr) elif consider_cpdlc and env.cpdlc.isConnected(callsign) and yesNo_question(settings.session_manager.gui, \ 'Send CPDLC instruction', instr.encodeToStr(), 'Send this instruction through data link to %s?' % callsign): settings.session_manager.sendCpdlcMsg( callsign, CpdlcMessage(True, CpdlcMessage.INSTR, contents=instr.encodeToStr())) else: settings.session_manager.instructAircraftByCallsign(callsign, instr)
def sendFreeTextMessage(self): msg = CpdlcMessage(True, CpdlcMessage.FREE_TEXT, contents=self.freeText_edit.text()) self.freeText_edit.clear() settings.session_manager.sendCpdlcMsg( self.data_link_model.acftCallsign(), msg)
def rejectClicked(self): txt, ok = QInputDialog.getText(self, 'CPDLC reject message', 'Reason/message (optional):') if ok: msg = CpdlcMessage(True, CpdlcMessage.REJECT, contents=txt) settings.session_manager.sendCpdlcMsg( self.data_link_model.acftCallsign(), msg)
def sendCpdlcMsg(self, callsign, msg): link = env.cpdlc.currentDataLink(callsign) if link != None: link.appendMessage(msg) if msg.type( ) == CpdlcMessage.INSTR: # other message types ignored (unimplemented in solo) try: acft = next( a for a in self.controlled_traffic if a.identifier == callsign) # uncontrolled traffic is not in contact acft.instruct([Instruction.fromEncodedStr(msg.contents())]) # FUTURE ingest before instruct to allow exception raised or (delayed?) WILCO msg before actually executing except StopIteration: # ACFT not found or not connected print('WARNING: Aircraft %s not found.' % callsign) except Instruction.Error as err: # raised by ControlledAircraft.instruct link.appendMessage( CpdlcMessage(False, CpdlcMessage.REJECT, contents=str(err))) else: # instruction sent and already accepted link.appendMessage(CpdlcMessage(False, CpdlcMessage.ACK))
def ackButtonClicked(self): if self.data_link_model.msgCount() > 0: last_msg = self.data_link_model.lastMsg() if not last_msg.isFromMe() and last_msg.type( ) == CpdlcMessage.REQUEST: try: instr = Instruction.fromEncodedStr(last_msg.contents()) except ValueError: QMessageBox.critical( self, 'CPDLC comm error', 'Unable to decode request. Mismatching program versions?' ) else: selection.writeStripAssignment(instr) msg = CpdlcMessage(True, CpdlcMessage.INSTR, contents=instr.encodeToStr()) settings.session_manager.sendCpdlcMsg( self.data_link_model.acftCallsign(), msg) return msg = CpdlcMessage(True, CpdlcMessage.ACK) settings.session_manager.sendCpdlcMsg( self.data_link_model.acftCallsign(), msg)
def data(self, index, role): row = index.row() col = index.column() if role == Qt.DisplayRole: if row == 0: ## First row of a connection if col == 0: return rel_datetime_str(self.connectionTime(), seconds=True) elif col == 1 and self.initiator != None: return 'XFR' elif col == 2: if self.atc_pov: return 'ACFT log-on' if self.initiator == None else 'Received from %s' % self.initiator else: # ACFT point of view return 'Logged on' if self.initiator == None else '%s → %s' % (self.initiator, self.dataAuthority()) elif row == self.rowCount() - 1 and not self.isLive(): ## Last row of a terminated or transferred connection if col == 0: return rel_datetime_str(self.disconnectionTime(), seconds=True) elif col == 1 and self.transferred_to != None: return 'XFR' elif col == 2: return 'Terminated' if self.transferred_to == None else 'Transferred to %s' % self.transferred_to else: ## Regular message row imsg = row - 1 msg = self.messages[imsg] if col == 0: return rel_datetime_str(msg.timeStamp(), seconds=True) elif col == 1: prefix = '↓↑'[self.atc_pov] + ' ' if msg.isFromMe() else '' return prefix + CpdlcMessage.type2str(msg.type()) elif col == 2: return msg.contents() elif col == 3: if imsg in self.problems: return self.problems[imsg] elif imsg == self.msgCount() - 1 and self.expecting: return self.statusStr()
def receiveMsgFromStudent(self, msg): #DEBUG if msg.type != TeachingMsg.TRAFFIC: #DEBUG print('=== TEACHERS RECEIVES ===\n%s\n=== End ===' % msg.data) if msg.type == TeachingMsg.ATC_TEXT_CHAT: lines = msg.strData().split('\n') if len(lines) == 2: signals.incomingAtcTextMsg.emit( ChatMessage(student_callsign, lines[1], recipient=lines[0], private=True)) else: print( 'ERROR: Invalid format in received ATC text chat from student.' ) elif msg.type == TeachingMsg.STRIP_EXCHANGE: line_sep = msg.strData().split('\n', maxsplit=1) toATC = line_sep[0] strip = Strip.fromEncodedDetails( '' if len(line_sep) < 2 else line_sep[1]) strip.writeDetail(received_from_detail, student_callsign) if toATC != teacher_callsign: strip.writeDetail(sent_to_detail, toATC) signals.receiveStrip.emit(strip) elif msg.type == TeachingMsg.WEATHER: # requesting weather information if msg.strData() == settings.primary_METAR_station: self.sendWeather() elif msg.type == TeachingMsg.TRAFFIC: # acknowledging a traffic message if self.noACK_traffic_count > 0: self.noACK_traffic_count -= 1 else: print('ERROR: Student acknowledging unsent traffic?!') elif msg.type == TeachingMsg.CPDLC: # Msg format in 2 lines, first being ACFT callsign, second is either of the following: # - connect/disconnect: "0" or "1" # - data authority transfer: CPDLC_transfer_cmd_prefix + ATC callsign transferring to/from # - other: CPDLC_message_cmd_prefix + encoded message string try: acft_callsign, line2 = msg.strData().split('\n', maxsplit=1) if line2 == '0': # ACFT disconnected by student if env.cpdlc.isConnected(acft_callsign): env.cpdlc.endDataLink(acft_callsign) else: # student is rejecting a connection (unable CPDLC) QMessageBox.warning( self.gui, 'CPDLC connection failed', 'Student is not accepting CPDLC connections.') elif line2 == '1': # student confirming ACFT log-on env.cpdlc.beginDataLink(acft_callsign, student_callsign) elif line2.startswith( CPDLC_transfer_cmd_prefix ): # student transferring or confirming transfer atc = line2[len(CPDLC_transfer_cmd_prefix):] if env.cpdlc.isConnected( acft_callsign ): # student initiating transfer to next ATC env.cpdlc.endDataLink(acft_callsign, transferTo=atc) else: # student confirming proposed transfer env.cpdlc.beginDataLink(acft_callsign, student_callsign, transferFrom=atc) elif line2.startswith(CPDLC_message_cmd_prefix ): # student ATC sent a message encoded_msg = line2[len(CPDLC_message_cmd_prefix):] link = env.cpdlc.currentDataLink(acft_callsign) if link == None: print( 'Ignored CPDLC message sent to %s while not connected.' % acft_callsign) else: link.appendMessage( CpdlcMessage.fromText(False, encoded_msg)) else: print('Error decoding CPDLC command from student:', line2) except (IndexError, ValueError): print('Error decoding CPDLC message value from student') else: print('ERROR: Unhandled message type from student: %s' % msg.type)
def receiveMsgFromTeacher(self, msg): #DEBUG if msg.type != TeachingMsg.TRAFFIC:# #DEBUG print('=== STUDENT RECEIVES ===\n%s\n=== End ===' % msg.data) if msg.type == TeachingMsg.ACFT_KILLED: callsign = msg.strData() if env.cpdlc.isConnected(callsign): env.cpdlc.endDataLink(callsign) for acft in pop_all(self.traffic, lambda a: a.identifier == callsign): signals.aircraftKilled.emit(acft) elif msg.type == TeachingMsg.TRAFFIC: # traffic update; contains FGMS packet fgms_packet = msg.binData() update_FgmsAircraft_list(self.traffic, fgms_packet) send_packet_to_views(fgms_packet) self.teacher.sendMessage(TeachingMsg(TeachingMsg.TRAFFIC)) elif msg.type == TeachingMsg.SIM_PAUSED: self.teacher_paused_at = now() signals.sessionPaused.emit() elif msg.type == TeachingMsg.SIM_RESUMED: pause_delay = now() - self.teacher_paused_at for acft in self.traffic: acft.moveHistoryTimesForward(pause_delay) self.teacher_paused_at = None signals.sessionResumed.emit() elif msg.type == TeachingMsg.ATC_TEXT_CHAT: lines = msg.strData().split('\n') if len(lines) == 2: signals.incomingAtcTextMsg.emit( ChatMessage(lines[0], lines[1], recipient=student_callsign, private=True)) else: print( 'ERROR: Invalid format in received ATC text chat from teacher.' ) elif msg.type == TeachingMsg.STRIP_EXCHANGE: line_sep = msg.strData().split('\n', maxsplit=1) fromATC = line_sep[0] strip = Strip.fromEncodedDetails( '' if len(line_sep) < 2 else line_sep[1]) strip.writeDetail(received_from_detail, fromATC) signals.receiveStrip.emit(strip) elif msg.type == TeachingMsg.SX_LIST: to_remove = set(env.ATCs.knownATCs()) to_remove.discard(teacher_callsign) for line in msg.strData().split('\n'): if line != '': # last line is empty lst = line.rsplit('\t', maxsplit=1) try: frq = CommFrequency(lst[1]) if len(lst) == 2 else None except ValueError: frq = None env.ATCs.updateATC(lst[0], None, None, frq) to_remove.discard(lst[0]) for atc in to_remove: env.ATCs.removeATC(atc) env.ATCs.refreshViews() elif msg.type == TeachingMsg.WEATHER: metar = msg.strData() station = metar.split(' ', maxsplit=1)[0] if station == settings.primary_METAR_station and metar != self.known_METAR: self.known_METAR = metar signals.newWeather.emit(station, Weather(metar)) elif msg.type == TeachingMsg.PTT: # msg format: "b acft" where b is '1' or '0' for PTT on/off; acft is caller's identifier line_sep = msg.strData().split(' ', maxsplit=1) try: ptt = bool(int(line_sep[0])) caller = next(acft for acft in self.getAircraft() if acft.identifier == line_sep[1]) if ptt: env.rdf.receiveSignal(caller.identifier, lambda acft=caller: acft.coords()) else: env.rdf.dieSignal(caller.identifier) except StopIteration: print('Ignored PTT message from teacher (unknown ACFT %s).' % line_sep[1]) except (ValueError, IndexError): print('Error decoding PTT message value from teacher') elif msg.type == TeachingMsg.CPDLC: try: acft_callsign, line2 = msg.strData().split('\n', maxsplit=1) if line2 == '0': # ACFT is disconnecting env.cpdlc.endDataLink(acft_callsign) elif line2 == '1': # ACFT is connecting (CAUTION: teacher needs confirmation of accepted connection) if settings.controller_pilot_data_link: env.cpdlc.beginDataLink(acft_callsign, student_callsign) self.teacher.sendMessage( TeachingMsg( TeachingMsg.CPDLC, data=('%s\n%d' % (acft_callsign, settings.controller_pilot_data_link)))) elif line2.startswith(CPDLC_transfer_cmd_prefix ): # ACFT being transferred to me if settings.controller_pilot_data_link: # CAUTION: teacher needs confirmation of accepted connection xfr_auth = line2[len(CPDLC_transfer_cmd_prefix):] env.cpdlc.beginDataLink(acft_callsign, student_callsign, transferFrom=xfr_auth) self.teacher.sendMessage(TeachingMsg(TeachingMsg.CPDLC, \ data=('%s\n%s%s' % (acft_callsign, CPDLC_transfer_cmd_prefix, xfr_auth)))) else: self.teacher.sendMessage( TeachingMsg(TeachingMsg.CPDLC, data=('%s\n0' % acft_callsign))) elif line2.startswith( CPDLC_message_cmd_prefix): # ACFT sending a message encoded_msg = line2[len(CPDLC_message_cmd_prefix):] link = env.cpdlc.currentDataLink(acft_callsign) if link == None: print( 'Ignored CPDLC message sent from %s while not connected.' % acft_callsign) else: link.appendMessage( CpdlcMessage.fromText(False, encoded_msg)) else: print('Error decoding CPDLC command from teacher:', line2) except (IndexError, ValueError): print('Error decoding CPDLC message value from teacher') else: print('Unhandled message type from teacher: %s' % msg.type)