From 225b40b34f9be748a38c50d2892af00dcddd9ce4 Mon Sep 17 00:00:00 2001 From: Florian Griffon Date: Fri, 23 May 2025 20:33:37 +0300 Subject: [PATCH] feat: gsm_sim generates log | removed redundant code from python_ui and bugfixes --- protocol_prototype/DryBox/UI/python_ui.py | 261 ++++++++---------- .../DryBox/input_8k_mono.wav.gsm | Bin 14949 -> 0 bytes .../DryBox/{ => simulator}/Dockerfile | 0 .../DryBox/{ => simulator}/gsm_simulator.py | 0 .../{ => simulator}/launch_gsm_simulator.sh | 34 +-- .../DryBox/{ => unused}/external_caller.py | 2 +- .../DryBox/{ => unused}/external_receiver.py | 2 +- .../DryBox/{ => unused}/protocol.py | 4 +- protocol_prototype/DryBox/{ => wav}/input.wav | Bin .../DryBox/{ => wav}/input_8k_mono.wav | Bin .../DryBox/{ => wav}/received.wav | Bin protocol_prototype/requirements.txt | 6 + 12 files changed, 137 insertions(+), 172 deletions(-) delete mode 100644 protocol_prototype/DryBox/input_8k_mono.wav.gsm rename protocol_prototype/DryBox/{ => simulator}/Dockerfile (100%) rename protocol_prototype/DryBox/{ => simulator}/gsm_simulator.py (100%) rename protocol_prototype/DryBox/{ => simulator}/launch_gsm_simulator.sh (68%) rename protocol_prototype/DryBox/{ => unused}/external_caller.py (92%) rename protocol_prototype/DryBox/{ => unused}/external_receiver.py (95%) rename protocol_prototype/DryBox/{ => unused}/protocol.py (98%) rename protocol_prototype/DryBox/{ => wav}/input.wav (100%) rename protocol_prototype/DryBox/{ => wav}/input_8k_mono.wav (100%) rename protocol_prototype/DryBox/{ => wav}/received.wav (100%) create mode 100644 protocol_prototype/requirements.txt diff --git a/protocol_prototype/DryBox/UI/python_ui.py b/protocol_prototype/DryBox/UI/python_ui.py index 97d8aa0..67e5a7c 100644 --- a/protocol_prototype/DryBox/UI/python_ui.py +++ b/protocol_prototype/DryBox/UI/python_ui.py @@ -1,8 +1,6 @@ import sys import random import socket -import threading -import time from PyQt5.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFrame, QSizePolicy, QStyle @@ -155,20 +153,6 @@ class PhoneUI(QMainWindow): } """) - # Phone states - self.phone1_state = PhoneState.IDLE - self.phone2_state = PhoneState.IDLE - - # Phone clients - self.phone1_client = PhoneClient("localhost", 12345, 0) - self.phone2_client = PhoneClient("localhost", 12345, 1) - self.phone1_client.data_received.connect(lambda data, cid: self.update_waveform(cid, data)) - self.phone2_client.data_received.connect(lambda data, cid: self.update_waveform(cid, data)) - self.phone1_client.state_changed.connect(lambda state, num, cid: self.set_phone_state(cid, self.map_state(state), num)) - self.phone2_client.state_changed.connect(lambda state, num, cid: self.set_phone_state(cid, self.map_state(state), num)) - self.phone1_client.start() - self.phone2_client.start() - # Main widget and layout main_widget = QWidget() self.setCentralWidget(main_widget) @@ -190,13 +174,29 @@ class PhoneUI(QMainWindow): phone_controls_layout.setAlignment(Qt.AlignCenter) main_layout.addLayout(phone_controls_layout) - # Phone 1 - phone1_widget_container, self.phone1_display, self.phone1_button, self.phone1_waveform = self._create_phone_ui("Phone 1", self.phone1_action) - phone_controls_layout.addWidget(phone1_widget_container) + # Initialize phones + self.phones = [] + for i in range(2): + client = PhoneClient("localhost", 12345, i) + client.data_received.connect(lambda data, cid=i: self.update_waveform(cid, data)) + client.state_changed.connect(lambda state, num, cid=i: self.set_phone_state(cid, self.map_state(state), num)) + client.start() - # Phone 2 - phone2_widget_container, self.phone2_display, self.phone2_button, self.phone2_waveform = self._create_phone_ui("Phone 2", self.phone2_action) - phone_controls_layout.addWidget(phone2_widget_container) + # Corrected lambda to handle 'checked' and capture phone_id + phone_widget_container, phone_display, phone_button, phone_waveform, phone_status_label = self._create_phone_ui( + f"Phone {i+1}", lambda checked, phone_id=i: self.phone_action(phone_id) + ) + self.phones.append({ + 'id': i, + 'client': client, + 'state': PhoneState.IDLE, + 'button': phone_button, + 'waveform': phone_waveform, + 'number': "123-4567" if i == 0 else "987-6543", + 'audio_timer': None, + 'status_label': phone_status_label + }) + phone_controls_layout.addWidget(phone_widget_container) # Spacer main_layout.addStretch(1) @@ -215,8 +215,8 @@ class PhoneUI(QMainWindow): main_layout.addLayout(settings_layout) # Initialize button states - self._update_phone_button_ui(self.phone1_button, self.phone1_state) - self._update_phone_button_ui(self.phone2_button, self.phone2_state) + for phone in self.phones: + self._update_phone_button_ui(phone['button'], phone['status_label'], phone['state']) def _create_phone_ui(self, title, action_slot): phone_container_widget = QWidget() @@ -257,46 +257,78 @@ class PhoneUI(QMainWindow): waveform_widget = WaveformWidget(dynamic=False) phone_layout.addWidget(waveform_widget, alignment=Qt.AlignCenter) - phone_display_frame.setProperty("statusLabel", phone_status_label) - return phone_container_widget, phone_display_frame, phone_button, waveform_widget + return phone_container_widget, phone_display_frame, phone_button, waveform_widget, phone_status_label - def _update_phone_button_ui(self, button, state, phone_number=""): - parent_widget = button.parentWidget() - if parent_widget: - frame = parent_widget.findChild(QFrame, "phoneDisplay") - if frame: - status_label = frame.property("statusLabel") - if status_label: - if state == PhoneState.IDLE: - button.setText("Call") - button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) - status_label.setText("Idle") - button.setStyleSheet("background-color: #0078D4;") - elif state == PhoneState.CALLING: - button.setText("Cancel") - button.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) - status_label.setText(f"Calling {phone_number}...") - button.setStyleSheet("background-color: #E81123;") - elif state == PhoneState.IN_CALL: - button.setText("Hang Up") - button.setIcon(self.style().standardIcon(QStyle.SP_DialogCancelButton)) - status_label.setText(f"In Call with {phone_number}") - button.setStyleSheet("background-color: #E81123;") - elif state == PhoneState.RINGING: - button.setText("Answer") - button.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) - status_label.setText(f"Incoming Call from {phone_number}") - button.setStyleSheet("background-color: #107C10;") - else: - print("Warning: statusLabel property not found") - else: - print("Warning: QFrame not found") - else: - print("Warning: Parent widget not found") + def _update_phone_button_ui(self, button, status_label, state, phone_number=""): + if state == PhoneState.IDLE: + button.setText("Call") + button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) + status_label.setText("Idle") + button.setStyleSheet("background-color: #0078D4;") + elif state == PhoneState.CALLING: + button.setText("Cancel") + button.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) + status_label.setText(f"Calling {phone_number}...") + button.setStyleSheet("background-color: #E81123;") + elif state == PhoneState.IN_CALL: + button.setText("Hang Up") + button.setIcon(self.style().standardIcon(QStyle.SP_DialogCancelButton)) + status_label.setText(f"In Call with {phone_number}") + button.setStyleSheet("background-color: #E81123;") + elif state == PhoneState.RINGING: + button.setText("Answer") + button.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + status_label.setText(f"Incoming Call from {phone_number}") + button.setStyleSheet("background-color: #107C10;") + + def phone_action(self, phone_id): + phone = self.phones[phone_id] + other_phone = self.phones[1 - phone_id] + print(f"Phone {phone_id + 1} Action, current state: {phone['state']}") + + if phone['state'] == PhoneState.IDLE: + # Initiate a call + phone['state'] = PhoneState.CALLING + other_phone['state'] = PhoneState.RINGING + self._update_phone_button_ui(phone['button'], phone['status_label'], phone['state'], other_phone['number']) + self._update_phone_button_ui(other_phone['button'], other_phone['status_label'], other_phone['state'], phone['number']) + phone['client'].send("RINGING") + + elif phone['state'] == PhoneState.RINGING: + # Answer the call + phone['state'] = PhoneState.IN_CALL + other_phone['state'] = PhoneState.IN_CALL + self._update_phone_button_ui(phone['button'], phone['status_label'], phone['state'], other_phone['number']) + self._update_phone_button_ui(other_phone['button'], other_phone['status_label'], other_phone['state'], phone['number']) + phone['client'].send("IN_CALL") + # Start audio timers for both phones + for p in [phone, other_phone]: + if not p['audio_timer'] or not p['audio_timer'].isActive(): + p['audio_timer'] = QTimer(self) + p['audio_timer'].timeout.connect(lambda pid=p['id']: self.send_audio(pid)) + p['audio_timer'].start(1000) + + elif phone['state'] == PhoneState.IN_CALL or phone['state'] == PhoneState.CALLING: + # Hang up or cancel + phone['state'] = PhoneState.IDLE + other_phone['state'] = PhoneState.IDLE + self._update_phone_button_ui(phone['button'], phone['status_label'], phone['state'], "") + self._update_phone_button_ui(other_phone['button'], other_phone['status_label'], other_phone['state'], "") + phone['client'].send("CALL_END") + # Stop audio timers for both phones + for p in [phone, other_phone]: + if p['audio_timer']: + p['audio_timer'].stop() + + def send_audio(self, phone_id): + phone = self.phones[phone_id] + if phone['state'] == PhoneState.IN_CALL: + message = f"Audio packet {random.randint(1, 1000)}" + phone['client'].send(message) def update_waveform(self, client_id, data): print(f"Updating waveform for client_id {client_id}") - waveform = self.phone1_waveform if client_id == 0 else self.phone2_waveform + waveform = self.phones[client_id]['waveform'] waveform.set_data(data) def map_state(self, state_str): @@ -306,106 +338,31 @@ class PhoneUI(QMainWindow): return PhoneState.IDLE elif state_str == "IN_CALL": return PhoneState.IN_CALL - return PhoneState.IDLE # Default to IDLE + return PhoneState.IDLE def set_phone_state(self, client_id, state, number=""): - if client_id == 0: - self.phone1_state = state - self._update_phone_button_ui(self.phone1_button, self.phone1_state, number if number else "123-4567") - if state == PhoneState.IDLE and hasattr(self, 'phone1_audio_timer'): - self.phone1_audio_timer.stop() - elif state == PhoneState.IN_CALL and (not hasattr(self, 'phone1_audio_timer') or not self.phone1_audio_timer.isActive()): - self.phone1_audio_timer = QTimer(self) - self.phone1_audio_timer.timeout.connect(self.send_phone1_audio) - self.phone1_audio_timer.start(1000) + phone = self.phones[client_id] + other_phone = self.phones[1 - client_id] + phone['state'] = state + if state == PhoneState.RINGING: + self._update_phone_button_ui(phone['button'], phone['status_label'], state, other_phone['number']) + elif state == PhoneState.IN_CALL: + self._update_phone_button_ui(phone['button'], phone['status_label'], state, other_phone['number']) else: - self.phone2_state = state - self._update_phone_button_ui(self.phone2_button, self.phone2_state, number if number else "987-6543") - if state == PhoneState.IDLE and hasattr(self, 'phone2_audio_timer'): - self.phone2_audio_timer.stop() - elif state == PhoneState.IN_CALL and (not hasattr(self, 'phone2_audio_timer') or not self.phone2_audio_timer.isActive()): - self.phone2_audio_timer = QTimer(self) - self.phone2_audio_timer.timeout.connect(self.send_phone2_audio) - self.phone2_audio_timer.start(1000) - - def phone1_action(self): - print("Phone 1 Action") - if self.phone1_state == PhoneState.IDLE: - self.phone1_state = PhoneState.CALLING - self.phone1_client.send("RINGING") - self._update_phone_button_ui(self.phone1_button, self.phone1_state, "123-4567") - elif self.phone1_state == PhoneState.CALLING: - self.phone1_state = PhoneState.IDLE - self.phone1_client.send("CALL_END") - self._update_phone_button_ui(self.phone1_button, self.phone1_state) - if hasattr(self, 'phone1_audio_timer'): - self.phone1_audio_timer.stop() - elif self.phone1_state == PhoneState.RINGING: - self.phone1_state = PhoneState.IN_CALL - self.phone2_state = PhoneState.IN_CALL # Sync both phones - self.phone1_client.send("IN_CALL") - self._update_phone_button_ui(self.phone1_button, self.phone1_state, "123-4567") - self._update_phone_button_ui(self.phone2_button, self.phone2_state, "987-6543") - # Start audio timer - self.phone1_audio_timer = QTimer(self) - self.phone1_audio_timer.timeout.connect(self.send_phone1_audio) - self.phone1_audio_timer.start(1000) - elif self.phone1_state == PhoneState.IN_CALL: - self.phone1_state = PhoneState.IDLE - self.phone2_state = PhoneState.IDLE # Sync both phones - self.phone1_client.send("CALL_END") - self._update_phone_button_ui(self.phone1_button, self.phone1_state) - self._update_phone_button_ui(self.phone2_button, self.phone2_state) - if hasattr(self, 'phone1_audio_timer'): - self.phone1_audio_timer.stop() - - def send_phone1_audio(self): - if self.phone1_state == PhoneState.IN_CALL: - message = f"Audio packet {random.randint(1, 1000)}" - self.phone1_client.send(message) - - def phone2_action(self): - print("Phone 2 Action") - if self.phone2_state == PhoneState.IDLE: - self.phone2_state = PhoneState.CALLING - self.phone2_client.send("RINGING") - self._update_phone_button_ui(self.phone2_button, self.phone2_state, "987-6543") - elif self.phone2_state == PhoneState.CALLING: - self.phone2_state = PhoneState.IDLE - self.phone2_client.send("CALL_END") - self._update_phone_button_ui(self.phone2_button, self.phone2_state) - if hasattr(self, 'phone2_audio_timer'): - self.phone2_audio_timer.stop() - elif self.phone2_state == PhoneState.RINGING: - self.phone2_state = PhoneState.IN_CALL - self.phone1_state = PhoneState.IN_CALL # Sync both phones - self.phone2_client.send("IN_CALL") - self._update_phone_button_ui(self.phone2_button, self.phone2_state, "987-6543") - self._update_phone_button_ui(self.phone1_button, self.phone1_state, "123-4567") - # Start audio timer - self.phone2_audio_timer = QTimer(self) - self.phone2_audio_timer.timeout.connect(self.send_phone2_audio) - self.phone2_audio_timer.start(1000) - elif self.phone2_state == PhoneState.IN_CALL: - self.phone2_state = PhoneState.IDLE - self.phone1_state = PhoneState.IDLE # Sync both phones - self.phone2_client.send("CALL_END") - self._update_phone_button_ui(self.phone2_button, self.phone2_state) - self._update_phone_button_ui(self.phone1_button, self.phone1_state) - if hasattr(self, 'phone2_audio_timer'): - self.phone2_audio_timer.stop() - - def send_phone2_audio(self): - if self.phone2_state == PhoneState.IN_CALL: - message = f"Audio packet {random.randint(1, 1000)}" - self.phone2_client.send(message) + self._update_phone_button_ui(phone['button'], phone['status_label'], state, "") + if state == PhoneState.IDLE and phone['audio_timer']: + phone['audio_timer'].stop() + elif state == PhoneState.IN_CALL and (not phone['audio_timer'] or not phone['audio_timer'].isActive()): + phone['audio_timer'] = QTimer(self) + phone['audio_timer'].timeout.connect(lambda: self.send_audio(client_id)) + phone['audio_timer'].start(1000) def settings_action(self): print("Settings clicked") def closeEvent(self, event): - self.phone1_client.stop() - self.phone2_client.stop() + for phone in self.phones: + phone['client'].stop() event.accept() if __name__ == "__main__": diff --git a/protocol_prototype/DryBox/input_8k_mono.wav.gsm b/protocol_prototype/DryBox/input_8k_mono.wav.gsm deleted file mode 100644 index 4ffd55e4220e7ce1113c08cc7ad0c40c6c29af1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14949 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 wOEKIrvgAJACq?VG+?kjZC{?KZ0SCpx1uGd~ /dev/null; then @@ -13,7 +14,7 @@ if ! command -v docker &> /dev/null; then exit 1 fi -# Check if the gsm_simulator.py file exists in the current directory +# Check if gsm_simulator.py exists if [ ! -f "gsm_simulator.py" ]; then echo "Error: gsm_simulator.py not found in the current directory." echo "Please ensure gsm_simulator.py is present and try again." @@ -26,11 +27,16 @@ if [ ! -f "Dockerfile" ]; then cat < Dockerfile FROM python:3.9-slim WORKDIR /app -COPY gsm_simulator.py /app +COPY gsm_simulator.py . +EXPOSE 12345 CMD ["python", "gsm_simulator.py"] EOF fi +# Ensure log file is writable +touch $LOG_FILE +chmod 666 $LOG_FILE + # Build the Docker image echo "Building Docker image: $IMAGE_NAME..." docker build -t $IMAGE_NAME . @@ -41,7 +47,7 @@ if [ $? -ne 0 ]; then exit 1 fi -# Stop and remove any existing container with the same name +# Stop and remove any existing container if [ "$(docker ps -q -f name=$CONTAINER_NAME)" ]; then echo "Stopping existing container: $CONTAINER_NAME..." docker stop $CONTAINER_NAME @@ -51,16 +57,12 @@ if [ "$(docker ps -aq -f name=$CONTAINER_NAME)" ]; then docker rm $CONTAINER_NAME fi -# Run the Docker container -echo "Launching GSM Simulator in Docker container: $CONTAINER_NAME..." -docker run -d -p $PORT:$PORT --name $CONTAINER_NAME $IMAGE_NAME +# Clean up dangling images +docker image prune -f -# Check if the container is running -if [ $? -eq 0 ]; then - echo "GSM Simulator is running on port $PORT." - echo "Container ID: $(docker ps -q -f name=$CONTAINER_NAME)" - echo "You can now connect your external Python programs to localhost:$PORT." -else - echo "Error: Failed to launch the container." - exit 1 -fi \ No newline at end of file +# Run the Docker container interactively +echo "Launching GSM Simulator in Docker container: $CONTAINER_NAME..." +docker run -it --rm -p $PORT:$PORT --name $CONTAINER_NAME $IMAGE_NAME | tee $LOG_FILE + +# Note: Script will block here until container exits +echo "GSM Simulator stopped. Logs saved to $LOG_FILE." \ No newline at end of file diff --git a/protocol_prototype/DryBox/external_caller.py b/protocol_prototype/DryBox/unused/external_caller.py similarity index 92% rename from protocol_prototype/DryBox/external_caller.py rename to protocol_prototype/DryBox/unused/external_caller.py index f96da67..9b06026 100644 --- a/protocol_prototype/DryBox/external_caller.py +++ b/protocol_prototype/DryBox/unused/external_caller.py @@ -5,7 +5,7 @@ import time def connect(): caller_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - caller_socket.connect(('localhost', 5555)) + caller_socket.connect(('localhost', 12345)) caller_socket.send("CALLER".encode()) print("Connected to GSM simulator as CALLER") time.sleep(2) # Wait 2 seconds for receiver to connect diff --git a/protocol_prototype/DryBox/external_receiver.py b/protocol_prototype/DryBox/unused/external_receiver.py similarity index 95% rename from protocol_prototype/DryBox/external_receiver.py rename to protocol_prototype/DryBox/unused/external_receiver.py index 3c5f8cd..20c02de 100644 --- a/protocol_prototype/DryBox/external_receiver.py +++ b/protocol_prototype/DryBox/unused/external_receiver.py @@ -4,7 +4,7 @@ import socket def connect(): receiver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) receiver_socket.settimeout(15) # Increase timeout to 15 seconds - receiver_socket.connect(('localhost', 5555)) + receiver_socket.connect(('localhost', 12345)) receiver_socket.send("RECEIVER".encode()) print("Connected to GSM simulator as RECEIVER") diff --git a/protocol_prototype/DryBox/protocol.py b/protocol_prototype/DryBox/unused/protocol.py similarity index 98% rename from protocol_prototype/DryBox/protocol.py rename to protocol_prototype/DryBox/unused/protocol.py index ee4d82e..4c3cc79 100644 --- a/protocol_prototype/DryBox/protocol.py +++ b/protocol_prototype/DryBox/unused/protocol.py @@ -6,8 +6,8 @@ import subprocess # Configuration HOST = "localhost" PORT = 12345 -INPUT_FILE = "input.wav" -OUTPUT_FILE = "received.wav" +INPUT_FILE = "wav/input.wav" +OUTPUT_FILE = "wav/received.wav" def encrypt_data(data): diff --git a/protocol_prototype/DryBox/input.wav b/protocol_prototype/DryBox/wav/input.wav similarity index 100% rename from protocol_prototype/DryBox/input.wav rename to protocol_prototype/DryBox/wav/input.wav diff --git a/protocol_prototype/DryBox/input_8k_mono.wav b/protocol_prototype/DryBox/wav/input_8k_mono.wav similarity index 100% rename from protocol_prototype/DryBox/input_8k_mono.wav rename to protocol_prototype/DryBox/wav/input_8k_mono.wav diff --git a/protocol_prototype/DryBox/received.wav b/protocol_prototype/DryBox/wav/received.wav similarity index 100% rename from protocol_prototype/DryBox/received.wav rename to protocol_prototype/DryBox/wav/received.wav diff --git a/protocol_prototype/requirements.txt b/protocol_prototype/requirements.txt new file mode 100644 index 0000000..731ba4c --- /dev/null +++ b/protocol_prototype/requirements.txt @@ -0,0 +1,6 @@ +# System install +Docker +Python3 + +# Venv install +PyQt5 \ No newline at end of file