1import locale 2 3import numpy 4import numpy as np 5from PyQt5.QtCore import Qt, pyqtSlot 6from PyQt5.QtGui import QFontMetrics 7from PyQt5.QtWidgets import QInputDialog, QWidget, QUndoStack, QApplication 8 9from urh import settings 10from urh.controller.CompareFrameController import CompareFrameController 11from urh.controller.dialogs.ContinuousSendDialog import ContinuousSendDialog 12from urh.controller.dialogs.FuzzingDialog import FuzzingDialog 13from urh.controller.dialogs.ModulatorDialog import ModulatorDialog 14from urh.controller.dialogs.SendDialog import SendDialog 15from urh.models.GeneratorListModel import GeneratorListModel 16from urh.models.GeneratorTableModel import GeneratorTableModel 17from urh.models.GeneratorTreeModel import GeneratorTreeModel 18from urh.plugins.NetworkSDRInterface.NetworkSDRInterfacePlugin import NetworkSDRInterfacePlugin 19from urh.plugins.PluginManager import PluginManager 20from urh.plugins.RfCat.RfCatPlugin import RfCatPlugin 21from urh.signalprocessing.IQArray import IQArray 22from urh.signalprocessing.Message import Message 23from urh.signalprocessing.MessageType import MessageType 24from urh.signalprocessing.Modulator import Modulator 25from urh.signalprocessing.ProtocoLabel import ProtocolLabel 26from urh.signalprocessing.ProtocolAnalyzer import ProtocolAnalyzer 27from urh.ui.actions.Fuzz import Fuzz 28from urh.ui.ui_generator import Ui_GeneratorTab 29from urh.util import FileOperator, util 30from urh.util.Errors import Errors 31from urh.util.Formatter import Formatter 32from urh.util.Logger import logger 33from urh.util.ProjectManager import ProjectManager 34 35 36class GeneratorTabController(QWidget): 37 def __init__(self, compare_frame_controller: CompareFrameController, project_manager: ProjectManager, parent=None): 38 super().__init__(parent) 39 self.ui = Ui_GeneratorTab() 40 self.ui.setupUi(self) 41 util.set_splitter_stylesheet(self.ui.splitter) 42 43 self.project_manager = project_manager 44 45 self.ui.treeProtocols.setHeaderHidden(True) 46 self.tree_model = GeneratorTreeModel(compare_frame_controller) 47 self.tree_model.set_root_item(compare_frame_controller.proto_tree_model.rootItem) 48 self.tree_model.controller = self 49 self.ui.treeProtocols.setModel(self.tree_model) 50 51 self.table_model = GeneratorTableModel(compare_frame_controller.proto_tree_model.rootItem, 52 compare_frame_controller.decodings) 53 self.table_model.controller = self 54 self.ui.tableMessages.setModel(self.table_model) 55 56 self.label_list_model = GeneratorListModel(None) 57 self.ui.listViewProtoLabels.setModel(self.label_list_model) 58 59 self.network_sdr_button_orig_tooltip = self.ui.btnNetworkSDRSend.toolTip() 60 self.set_network_sdr_send_button_visibility() 61 self.set_rfcat_button_visibility() 62 self.network_sdr_plugin = NetworkSDRInterfacePlugin() 63 self.rfcat_plugin = RfCatPlugin() 64 self.init_rfcat_plugin() 65 66 self.modulation_msg_indices = [] 67 68 self.refresh_modulators() 69 self.on_selected_modulation_changed() 70 self.set_fuzzing_ui_status() 71 self.ui.prBarGeneration.hide() 72 self.create_connects(compare_frame_controller) 73 74 self.set_modulation_profile_status() 75 76 def __get_modulator_of_message(self, message: Message) -> Modulator: 77 if message.modulator_index > len(self.modulators) - 1: 78 message.modulator_index = 0 79 return self.modulators[message.modulator_index] 80 81 @property 82 def selected_message_index(self) -> int: 83 min_row, _, _, _ = self.ui.tableMessages.selection_range() 84 return min_row # 85 86 @property 87 def selected_message(self) -> Message: 88 selected_msg_index = self.selected_message_index 89 if selected_msg_index == -1 or selected_msg_index >= len(self.table_model.protocol.messages): 90 return None 91 92 return self.table_model.protocol.messages[selected_msg_index] 93 94 @property 95 def active_groups(self): 96 return self.tree_model.groups 97 98 @property 99 def modulators(self): 100 return self.project_manager.modulators 101 102 @property 103 def total_modulated_samples(self) -> int: 104 return sum(int(len(msg.encoded_bits) * self.__get_modulator_of_message(msg).samples_per_symbol + msg.pause) 105 for msg in self.table_model.protocol.messages) 106 107 @modulators.setter 108 def modulators(self, value): 109 assert type(value) == list 110 self.project_manager.modulators = value 111 112 def create_connects(self, compare_frame_controller): 113 compare_frame_controller.proto_tree_model.modelReset.connect(self.refresh_tree) 114 compare_frame_controller.participant_changed.connect(self.table_model.refresh_vertical_header) 115 self.ui.btnEditModulation.clicked.connect(self.show_modulation_dialog) 116 self.ui.cBoxModulations.currentIndexChanged.connect(self.on_selected_modulation_changed) 117 self.ui.tableMessages.selectionModel().selectionChanged.connect(self.on_table_selection_changed) 118 self.ui.tableMessages.encodings_updated.connect(self.on_table_selection_changed) 119 self.table_model.undo_stack.indexChanged.connect(self.on_undo_stack_index_changed) 120 self.table_model.protocol.qt_signals.line_duplicated.connect(self.refresh_pause_list) 121 self.table_model.protocol.qt_signals.fuzzing_started.connect(self.on_fuzzing_started) 122 self.table_model.protocol.qt_signals.current_fuzzing_message_changed.connect( 123 self.on_current_fuzzing_message_changed) 124 self.table_model.protocol.qt_signals.fuzzing_finished.connect(self.on_fuzzing_finished) 125 self.table_model.first_protocol_added.connect(self.on_first_protocol_added) 126 self.label_list_model.protolabel_fuzzing_status_changed.connect(self.set_fuzzing_ui_status) 127 self.ui.cbViewType.currentIndexChanged.connect(self.on_view_type_changed) 128 self.ui.btnSend.clicked.connect(self.on_btn_send_clicked) 129 self.ui.btnSave.clicked.connect(self.on_btn_save_clicked) 130 self.ui.btnOpen.clicked.connect(self.on_btn_open_clicked) 131 132 self.project_manager.project_updated.connect(self.on_project_updated) 133 134 self.table_model.vertical_header_color_status_changed.connect( 135 self.ui.tableMessages.on_vertical_header_color_status_changed) 136 137 self.label_list_model.protolabel_removed.connect(self.handle_proto_label_removed) 138 139 self.ui.lWPauses.item_edit_clicked.connect(self.edit_pause_item) 140 self.ui.lWPauses.edit_all_items_clicked.connect(self.edit_all_pause_items) 141 self.ui.lWPauses.itemSelectionChanged.connect(self.on_lWpauses_selection_changed) 142 self.ui.lWPauses.lost_focus.connect(self.on_lWPauses_lost_focus) 143 self.ui.lWPauses.doubleClicked.connect(self.on_lWPauses_double_clicked) 144 self.ui.btnGenerate.clicked.connect(self.generate_file) 145 self.label_list_model.protolabel_fuzzing_status_changed.connect(self.handle_plabel_fuzzing_state_changed) 146 self.ui.btnFuzz.clicked.connect(self.on_btn_fuzzing_clicked) 147 self.ui.tableMessages.create_label_triggered.connect(self.create_fuzzing_label) 148 self.ui.tableMessages.edit_label_triggered.connect(self.show_fuzzing_dialog) 149 self.ui.listViewProtoLabels.selection_changed.connect(self.handle_label_selection_changed) 150 self.ui.listViewProtoLabels.edit_on_item_triggered.connect(self.show_fuzzing_dialog) 151 152 self.ui.btnNetworkSDRSend.clicked.connect(self.on_btn_network_sdr_clicked) 153 self.ui.btnRfCatSend.clicked.connect(self.on_btn_rfcat_clicked) 154 155 self.network_sdr_plugin.sending_status_changed.connect(self.on_network_sdr_sending_status_changed) 156 self.network_sdr_plugin.sending_stop_requested.connect(self.on_network_sdr_sending_stop_requested) 157 self.network_sdr_plugin.current_send_message_changed.connect(self.on_send_message_changed) 158 159 @pyqtSlot() 160 def refresh_tree(self): 161 self.tree_model.beginResetModel() 162 self.tree_model.endResetModel() 163 self.ui.treeProtocols.expandAll() 164 165 @pyqtSlot() 166 def refresh_table(self): 167 self.table_model.update() 168 self.ui.tableMessages.resize_columns() 169 is_data_there = self.table_model.display_data is not None and len(self.table_model.display_data) > 0 170 self.ui.btnSend.setEnabled(is_data_there) 171 self.ui.btnGenerate.setEnabled(is_data_there) 172 173 @pyqtSlot() 174 def refresh_label_list(self): 175 self.label_list_model.message = self.selected_message 176 self.label_list_model.update() 177 178 @property 179 def generator_undo_stack(self) -> QUndoStack: 180 return self.table_model.undo_stack 181 182 @pyqtSlot() 183 def on_selected_modulation_changed(self): 184 cur_ind = self.ui.cBoxModulations.currentIndex() 185 min_row, max_row, _, _ = self.ui.tableMessages.selection_range() 186 if min_row > -1: 187 # set modulation for selected messages 188 for row in range(min_row, max_row + 1): 189 try: 190 self.table_model.protocol.messages[row].modulator_index = cur_ind 191 except IndexError: 192 continue 193 194 self.show_modulation_info() 195 196 def refresh_modulators(self): 197 current_index = 0 198 if type(self.sender()) == ModulatorDialog: 199 current_index = self.sender().ui.comboBoxCustomModulations.currentIndex() 200 self.ui.cBoxModulations.clear() 201 for modulator in self.modulators: 202 self.ui.cBoxModulations.addItem(modulator.name) 203 204 self.ui.cBoxModulations.setCurrentIndex(current_index) 205 206 def bootstrap_modulator(self, protocol: ProtocolAnalyzer): 207 """ 208 Set initial parameters for default modulator if it was not edited by user previously 209 :return: 210 """ 211 if len(self.modulators) != 1 or len(self.table_model.protocol.messages) == 0: 212 return 213 214 modulator = self.modulators[0] 215 modulator.samples_per_symbol = protocol.messages[0].samples_per_symbol 216 modulator.bits_per_symbol = protocol.messages[0].bits_per_symbol 217 218 if protocol.signal: 219 modulator.sample_rate = protocol.signal.sample_rate 220 modulator.modulation_type = protocol.signal.modulation_type 221 auto_freq = modulator.estimate_carrier_frequency(protocol.signal, protocol) 222 if auto_freq is not None and auto_freq != 0: 223 modulator.carrier_freq_hz = auto_freq 224 225 modulator.parameters = modulator.get_default_parameters() 226 self.show_modulation_info() 227 228 def show_modulation_info(self): 229 cur_ind = self.ui.cBoxModulations.currentIndex() 230 mod = self.modulators[cur_ind] 231 self.ui.lCarrierFreqValue.setText(mod.carrier_frequency_str) 232 self.ui.lCarrierPhaseValue.setText(mod.carrier_phase_str) 233 self.ui.lBitLenValue.setText(mod.samples_per_symbol_str) 234 self.ui.lSampleRateValue.setText(mod.sample_rate_str) 235 mod_type = mod.modulation_type 236 self.ui.lModTypeValue.setText(mod_type) 237 238 self.ui.lParamCaption.setText(mod.parameter_type_str) 239 self.ui.labelParameterValues.setText(mod.parameters_string) 240 self.ui.labelBitsPerSymbol.setText(str(mod.bits_per_symbol)) 241 242 def prepare_modulation_dialog(self) -> (ModulatorDialog, Message): 243 preselected_index = self.ui.cBoxModulations.currentIndex() 244 245 min_row, max_row, start, end = self.ui.tableMessages.selection_range() 246 if min_row > -1: 247 try: 248 selected_message = self.table_model.protocol.messages[min_row] 249 preselected_index = selected_message.modulator_index 250 except IndexError: 251 selected_message = Message([1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0], 0, [], MessageType("empty")) 252 else: 253 selected_message = Message([1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0], 0, [], MessageType("empty")) 254 if len(self.table_model.protocol.messages) > 0: 255 selected_message.samples_per_symbol = self.table_model.protocol.messages[0].samples_per_symbol 256 257 for m in self.modulators: 258 m.default_sample_rate = self.project_manager.device_conf["sample_rate"] 259 260 modulator_dialog = ModulatorDialog(self.modulators, tree_model=self.tree_model, parent=self.parent()) 261 modulator_dialog.ui.comboBoxCustomModulations.setCurrentIndex(preselected_index) 262 263 modulator_dialog.finished.connect(self.refresh_modulators) 264 modulator_dialog.finished.connect(self.refresh_pause_list) 265 266 return modulator_dialog, selected_message 267 268 def set_modulation_profile_status(self): 269 visible = settings.read("multiple_modulations", False, bool) 270 self.ui.cBoxModulations.setVisible(visible) 271 272 def init_rfcat_plugin(self): 273 self.set_rfcat_button_visibility() 274 self.rfcat_plugin = RfCatPlugin() 275 self.rfcat_plugin.current_send_message_changed.connect(self.on_send_message_changed) 276 self.ui.btnRfCatSend.setEnabled(self.rfcat_plugin.rfcat_is_found) 277 278 @pyqtSlot() 279 def on_undo_stack_index_changed(self): 280 self.refresh_table() 281 self.refresh_pause_list() 282 self.refresh_label_list() 283 self.refresh_estimated_time() 284 self.set_fuzzing_ui_status() 285 286 @pyqtSlot() 287 def show_modulation_dialog(self): 288 modulator_dialog, message = self.prepare_modulation_dialog() 289 modulator_dialog.showMaximized() 290 291 modulator_dialog.initialize(message.encoded_bits_str[0:16]) 292 self.project_manager.modulation_was_edited = True 293 294 @pyqtSlot() 295 def on_table_selection_changed(self): 296 min_row, max_row, start, end = self.ui.tableMessages.selection_range() 297 298 if min_row == -1: 299 self.ui.lEncodingValue.setText("-") # 300 self.ui.lEncodingValue.setToolTip("") 301 self.label_list_model.message = None 302 return 303 304 container = self.table_model.protocol 305 message = container.messages[min_row] 306 self.label_list_model.message = message 307 decoder_name = message.decoder.name 308 metrics = QFontMetrics(self.ui.lEncodingValue.font()) 309 elidedName = metrics.elidedText(decoder_name, Qt.ElideRight, self.ui.lEncodingValue.width()) 310 self.ui.lEncodingValue.setText(elidedName) 311 self.ui.lEncodingValue.setToolTip(decoder_name) 312 self.ui.cBoxModulations.blockSignals(True) 313 self.ui.cBoxModulations.setCurrentIndex(message.modulator_index) 314 self.show_modulation_info() 315 self.ui.cBoxModulations.blockSignals(False) 316 317 @pyqtSlot(int) 318 def edit_pause_item(self, index: int): 319 message = self.table_model.protocol.messages[index] 320 cur_len = message.pause 321 new_len, ok = QInputDialog.getInt(self, self.tr("Enter new Pause Length"), 322 self.tr("Pause Length:"), cur_len, 0) 323 if ok: 324 message.pause = new_len 325 self.refresh_pause_list() 326 327 @pyqtSlot() 328 def edit_all_pause_items(self): 329 message = self.table_model.protocol.messages[0] 330 cur_len = message.pause 331 new_len, ok = QInputDialog.getInt(self, self.tr("Enter new Pause Length"), 332 self.tr("Pause Length:"), cur_len, 0) 333 if ok: 334 for message in self.table_model.protocol.messages: 335 message.pause = new_len 336 337 self.refresh_pause_list() 338 339 @pyqtSlot() 340 def on_lWPauses_double_clicked(self): 341 sel_indexes = [index.row() for index in self.ui.lWPauses.selectedIndexes()] 342 if len(sel_indexes) > 0: 343 self.edit_pause_item(sel_indexes[0]) 344 345 @pyqtSlot() 346 def refresh_pause_list(self): 347 self.ui.lWPauses.clear() 348 349 fmt_str = "Pause ({1:d}-{2:d}) <{0:d} samples ({3})>" 350 for i, pause in enumerate(self.table_model.protocol.pauses): 351 sr = self.__get_modulator_of_message(self.table_model.protocol.messages[i]).sample_rate 352 item = fmt_str.format(pause, i + 1, i + 2, Formatter.science_time(pause / sr)) 353 self.ui.lWPauses.addItem(item) 354 355 self.refresh_estimated_time() 356 357 @pyqtSlot() 358 def on_lWpauses_selection_changed(self): 359 rows = [index.row() for index in self.ui.lWPauses.selectedIndexes()] 360 if len(rows) == 0: 361 return 362 self.ui.tableMessages.show_pause_active = True 363 self.ui.tableMessages.pause_row = rows[0] 364 self.ui.tableMessages.viewport().update() 365 self.ui.tableMessages.scrollTo(self.table_model.index(rows[0], 0)) 366 367 @pyqtSlot() 368 def on_lWPauses_lost_focus(self): 369 self.ui.tableMessages.show_pause_active = False 370 self.ui.tableMessages.viewport().update() 371 372 @pyqtSlot() 373 def generate_file(self): 374 try: 375 total_samples = self.total_modulated_samples 376 buffer = self.prepare_modulation_buffer(total_samples, show_error=False) 377 if buffer is None: 378 Errors.generic_error(self.tr("File too big"), self.tr("This file would get too big to save.")) 379 self.unsetCursor() 380 return 381 modulated_samples = self.modulate_data(buffer) 382 try: 383 sample_rate = self.modulators[0].sample_rate 384 except Exception as e: 385 logger.exception(e) 386 sample_rate = 1e6 387 FileOperator.ask_signal_file_name_and_save("generated", modulated_samples, sample_rate=sample_rate, parent=self) 388 except Exception as e: 389 Errors.exception(e) 390 self.unsetCursor() 391 392 def prepare_modulation_buffer(self, total_samples: int, show_error=True) -> IQArray: 393 dtype = Modulator.get_dtype() 394 n = 2 if dtype == np.int8 else 4 if dtype == np.int16 else 8 395 396 memory_size_for_buffer = total_samples * n 397 logger.debug("Allocating {0:.2f}MB for modulated samples".format(memory_size_for_buffer / (1024 ** 2))) 398 try: 399 # allocate it three times as we need the same amount for the sending process 400 IQArray(None, dtype=dtype, n=3*total_samples) 401 except MemoryError: 402 # will go into continuous mode in this case 403 if show_error: 404 Errors.not_enough_ram_for_sending_precache(3*memory_size_for_buffer) 405 return None 406 407 return IQArray(None, dtype=dtype, n=total_samples) 408 409 def modulate_data(self, buffer: IQArray) -> IQArray: 410 """ 411 412 :param buffer: Buffer in which the modulated data shall be written, initialized with zeros 413 :return: 414 """ 415 self.ui.prBarGeneration.show() 416 self.ui.prBarGeneration.setValue(0) 417 self.ui.prBarGeneration.setMaximum(self.table_model.row_count) 418 self.modulation_msg_indices.clear() 419 420 pos = 0 421 for i in range(0, self.table_model.row_count): 422 message = self.table_model.protocol.messages[i] 423 modulator = self.__get_modulator_of_message(message) 424 # We do not need to modulate the pause extra, as result is already initialized with zeros 425 modulated = modulator.modulate(start=0, data=message.encoded_bits, pause=0) 426 buffer[pos:pos + len(modulated)] = modulated 427 pos += len(modulated) + message.pause 428 self.modulation_msg_indices.append(pos) 429 self.ui.prBarGeneration.setValue(i + 1) 430 QApplication.instance().processEvents() 431 432 self.ui.prBarGeneration.hide() 433 return buffer 434 435 @pyqtSlot(int) 436 def show_fuzzing_dialog(self, label_index: int): 437 view = self.ui.cbViewType.currentIndex() 438 439 if self.label_list_model.message is not None: 440 msg_index = self.table_model.protocol.messages.index(self.label_list_model.message) 441 fdc = FuzzingDialog(protocol=self.table_model.protocol, label_index=label_index, 442 msg_index=msg_index, proto_view=view, parent=self) 443 fdc.show() 444 fdc.finished.connect(self.on_fuzzing_dialog_finished) 445 446 @pyqtSlot() 447 def on_fuzzing_dialog_finished(self): 448 self.refresh_label_list() 449 self.refresh_table() 450 self.set_fuzzing_ui_status() 451 self.ui.tabWidget.setCurrentIndex(2) 452 453 @pyqtSlot() 454 def handle_plabel_fuzzing_state_changed(self): 455 self.refresh_table() 456 self.label_list_model.update() 457 458 @pyqtSlot(ProtocolLabel) 459 def handle_proto_label_removed(self, plabel: ProtocolLabel): 460 self.refresh_label_list() 461 self.refresh_table() 462 self.set_fuzzing_ui_status() 463 464 @pyqtSlot() 465 def on_btn_fuzzing_clicked(self): 466 fuz_mode = "Successive" 467 if self.ui.rbConcurrent.isChecked(): 468 fuz_mode = "Concurrent" 469 elif self.ui.rBExhaustive.isChecked(): 470 fuz_mode = "Exhaustive" 471 472 self.setCursor(Qt.WaitCursor) 473 fuzz_action = Fuzz(self.table_model.protocol, fuz_mode) 474 self.table_model.undo_stack.push(fuzz_action) 475 for row in fuzz_action.added_message_indices: 476 self.table_model.update_checksums_for_row(row) 477 self.unsetCursor() 478 self.ui.tableMessages.setFocus() 479 480 @pyqtSlot() 481 def set_fuzzing_ui_status(self): 482 btn_was_enabled = self.ui.btnFuzz.isEnabled() 483 fuzz_active = any(lbl.active_fuzzing for msg in self.table_model.protocol.messages for lbl in msg.message_type) 484 self.ui.btnFuzz.setEnabled(fuzz_active) 485 if self.ui.btnFuzz.isEnabled() and not btn_was_enabled: 486 font = self.ui.btnFuzz.font() 487 font.setBold(True) 488 self.ui.btnFuzz.setFont(font) 489 else: 490 font = self.ui.btnFuzz.font() 491 font.setBold(False) 492 self.ui.btnFuzz.setFont(font) 493 self.ui.btnFuzz.setStyleSheet("") 494 495 has_same_message = self.table_model.protocol.multiple_fuzz_labels_per_message 496 self.ui.rBSuccessive.setEnabled(has_same_message) 497 self.ui.rBExhaustive.setEnabled(has_same_message) 498 self.ui.rbConcurrent.setEnabled(has_same_message) 499 500 def refresh_existing_encodings(self, encodings_from_file): 501 """ 502 Refresh existing encodings for messages, when encoding was changed by user in dialog 503 504 :return: 505 """ 506 update = False 507 508 for msg in self.table_model.protocol.messages: 509 i = next((i for i, d in enumerate(encodings_from_file) if d.name == msg.decoder.name), 0) 510 if msg.decoder != encodings_from_file[i]: 511 update = True 512 msg.decoder = encodings_from_file[i] 513 msg.clear_decoded_bits() 514 msg.clear_encoded_bits() 515 516 if update: 517 self.refresh_table() 518 self.refresh_estimated_time() 519 520 @pyqtSlot() 521 def refresh_estimated_time(self): 522 c = self.table_model.protocol 523 if c.num_messages == 0: 524 self.ui.lEstimatedTime.setText("Estimated Time: ") 525 return 526 527 avg_msg_len = numpy.mean([len(msg.encoded_bits) for msg in c.messages]) 528 avg_samples_per_symbol = numpy.mean([m.samples_per_symbol for m in self.modulators]) 529 avg_sample_rate = numpy.mean([m.sample_rate for m in self.modulators]) 530 pause_samples = sum(c.pauses) 531 nsamples = c.num_messages * avg_msg_len * avg_samples_per_symbol + pause_samples 532 533 self.ui.lEstimatedTime.setText( 534 locale.format_string("Estimated Time: %.04f seconds", nsamples / avg_sample_rate)) 535 536 @pyqtSlot(int, int, int) 537 def create_fuzzing_label(self, msg_index: int, start: int, end: int): 538 con = self.table_model.protocol 539 start, end = con.convert_range(start, end - 1, self.ui.cbViewType.currentIndex(), 0, False, msg_index) 540 lbl = con.create_fuzzing_label(start, end, msg_index) 541 self.show_fuzzing_dialog(con.protocol_labels.index(lbl)) 542 543 @pyqtSlot() 544 def handle_label_selection_changed(self): 545 rows = [index.row() for index in self.ui.listViewProtoLabels.selectedIndexes()] 546 if len(rows) == 0: 547 return 548 549 maxrow = numpy.max(rows) 550 551 try: 552 label = self.table_model.protocol.protocol_labels[maxrow] 553 except IndexError: 554 return 555 if label.show and self.selected_message: 556 start, end = self.selected_message.get_label_range(lbl=label, view=self.table_model.proto_view, 557 decode=False) 558 indx = self.table_model.index(0, int((start + end) / 2)) 559 self.ui.tableMessages.scrollTo(indx) 560 561 @pyqtSlot() 562 def on_view_type_changed(self): 563 self.setCursor(Qt.WaitCursor) 564 self.table_model.proto_view = self.ui.cbViewType.currentIndex() 565 self.ui.tableMessages.resize_columns() 566 self.unsetCursor() 567 568 @pyqtSlot() 569 def on_btn_send_clicked(self): 570 try: 571 total_samples = self.total_modulated_samples 572 buffer = self.prepare_modulation_buffer(total_samples) 573 if buffer is not None: 574 modulated_data = self.modulate_data(buffer) 575 else: 576 # Enter continuous mode 577 modulated_data = None 578 579 try: 580 if modulated_data is not None: 581 try: 582 dialog = SendDialog(self.project_manager, modulated_data=modulated_data, 583 modulation_msg_indices=self.modulation_msg_indices, parent=self) 584 except MemoryError: 585 # Not enough memory for device buffer so we need to create a continuous send dialog 586 del modulated_data 587 Errors.not_enough_ram_for_sending_precache(None) 588 dialog = ContinuousSendDialog(self.project_manager, 589 self.table_model.protocol.messages, 590 self.modulators, total_samples, parent=self) 591 else: 592 dialog = ContinuousSendDialog(self.project_manager, self.table_model.protocol.messages, 593 self.modulators, total_samples, parent=self) 594 except OSError as e: 595 logger.exception(e) 596 return 597 if dialog.has_empty_device_list: 598 Errors.no_device() 599 dialog.close() 600 return 601 602 dialog.device_parameters_changed.connect(self.project_manager.set_device_parameters) 603 dialog.show() 604 dialog.graphics_view.show_full_scene(reinitialize=True) 605 except Exception as e: 606 Errors.exception(e) 607 self.unsetCursor() 608 609 @pyqtSlot() 610 def on_btn_save_clicked(self): 611 filename = FileOperator.ask_save_file_name("profile.fuzz.xml", caption="Save fuzzing profile") 612 if filename: 613 self.table_model.protocol.to_xml_file(filename, 614 decoders=self.project_manager.decodings, 615 participants=self.project_manager.participants, 616 modulators=self.modulators) 617 618 @pyqtSlot() 619 def on_btn_open_clicked(self): 620 dialog = FileOperator.get_open_dialog(directory_mode=False, parent=self, name_filter="fuzz") 621 if dialog.exec_(): 622 for filename in dialog.selectedFiles(): 623 self.load_from_file(filename) 624 625 def load_from_file(self, filename: str): 626 try: 627 self.modulators = ProjectManager.read_modulators_from_file(filename) 628 self.table_model.protocol.from_xml_file(filename) 629 self.refresh_pause_list() 630 self.refresh_estimated_time() 631 self.refresh_modulators() 632 self.show_modulation_info() 633 self.refresh_table() 634 self.set_fuzzing_ui_status() 635 except: 636 logger.error("You done something wrong to the xml fuzzing profile.") 637 638 @pyqtSlot() 639 def on_project_updated(self): 640 self.table_model.refresh_vertical_header() 641 642 def set_network_sdr_send_button_visibility(self): 643 is_plugin_enabled = PluginManager().is_plugin_enabled("NetworkSDRInterface") 644 self.ui.btnNetworkSDRSend.setVisible(is_plugin_enabled) 645 646 def set_rfcat_button_visibility(self): 647 is_plugin_enabled = PluginManager().is_plugin_enabled("RfCat") 648 self.ui.btnRfCatSend.setVisible(is_plugin_enabled) 649 650 @pyqtSlot() 651 def on_btn_network_sdr_clicked(self): 652 if not self.network_sdr_plugin.is_sending: 653 messages = self.table_model.protocol.messages 654 sample_rates = [self.__get_modulator_of_message(msg).sample_rate for msg in messages] 655 self.network_sdr_plugin.start_message_sending_thread(messages, sample_rates) 656 else: 657 self.network_sdr_plugin.stop_sending_thread() 658 659 @pyqtSlot(bool) 660 def on_network_sdr_sending_status_changed(self, is_sending: bool): 661 self.ui.btnNetworkSDRSend.setChecked(is_sending) 662 self.ui.btnNetworkSDRSend.setEnabled(True) 663 self.ui.btnNetworkSDRSend.setToolTip( 664 "Sending in progress" if is_sending else self.network_sdr_button_orig_tooltip) 665 if not is_sending: 666 self.ui.tableMessages.clearSelection() 667 668 @pyqtSlot() 669 def on_network_sdr_sending_stop_requested(self): 670 self.ui.btnNetworkSDRSend.setToolTip("Stopping sending") 671 self.ui.btnNetworkSDRSend.setEnabled(False) 672 673 @pyqtSlot(int) 674 def on_send_message_changed(self, message_index: int): 675 self.ui.tableMessages.selectRow(message_index) 676 677 @pyqtSlot() 678 def on_btn_rfcat_clicked(self): 679 if not self.rfcat_plugin.is_sending: 680 messages = self.table_model.protocol.messages 681 sample_rates = [self.__get_modulator_of_message(msg).sample_rate for msg in messages] 682 self.rfcat_plugin.start_message_sending_thread(messages, sample_rates, self.modulators, 683 self.project_manager) 684 else: 685 self.rfcat_plugin.stop_sending_thread() 686 687 @pyqtSlot(int) 688 def on_fuzzing_started(self, num_values: int): 689 self.ui.stackedWidgetFuzzing.setCurrentWidget(self.ui.pageFuzzingProgressBar) 690 self.ui.progressBarFuzzing.setMaximum(num_values) 691 self.ui.progressBarFuzzing.setValue(0) 692 QApplication.instance().processEvents() 693 694 @pyqtSlot() 695 def on_fuzzing_finished(self): 696 self.ui.stackedWidgetFuzzing.setCurrentWidget(self.ui.pageFuzzingUI) 697 # Calculate Checksums for Fuzzed Messages 698 self.setCursor(Qt.WaitCursor) 699 700 self.unsetCursor() 701 702 @pyqtSlot(int) 703 def on_current_fuzzing_message_changed(self, current_message: int): 704 self.ui.progressBarFuzzing.setValue(current_message) 705 QApplication.instance().processEvents() 706 707 @pyqtSlot(ProtocolAnalyzer) 708 def on_first_protocol_added(self, protocol: ProtocolAnalyzer): 709 if not self.project_manager.modulation_was_edited: 710 self.bootstrap_modulator(protocol) 711