The IP address of the computer running OpenGolfSim
", None)) +#endif // QT_CONFIG(tooltip) + self.ogs_ipaddress.setPlaceholderText(QCoreApplication.translate("SettingsForm", u"OpenGolfSim IP Address", None)) +#if QT_CONFIG(tooltip) + self.ogs_port.setToolTip(QCoreApplication.translate("SettingsForm", u"The port of the OpenGolfSim API
", None)) +#endif // QT_CONFIG(tooltip) + self.ogs_port.setPlaceholderText(QCoreApplication.translate("SettingsForm", u"OpenGolfSim Port", None)) self.label_6.setText(QCoreApplication.translate("SettingsForm", u"Launch Monitor", None)) self.launch_monitor_combo.setItemText(0, QCoreApplication.translate("SettingsForm", u"None", None)) self.launch_monitor_combo.setItemText(1, QCoreApplication.translate("SettingsForm", u"New Item", None)) diff --git a/src/ball_data.py b/src/ball_data.py index bfdfbeb..0495287 100644 --- a/src/ball_data.py +++ b/src/ball_data.py @@ -163,6 +163,29 @@ def ballcolor_as_list(): keys.append(getattr(BallColor, key)) return keys + def to_opengolfsim(self): + payload = { + "shot": { + "ballSpeed": self.speed, + "spinAxis": self.spin_axis, + "spinSpeed": self.total_spin, + "horizontalLaunchAngle": self.hla, + "verticalLaunchAngle": self.vla + # "Backspin": self.back_spin, + # "SideSpin": self.side_spin, + # "CarryDistance": 0 + }, + "club": { + "angleOfAttack": self.angle_of_attack, + "clubPath": self.path, + "clubSpeed": self.club_speed, + "faceToPath": self.face_to_path, + "faceToTarget": self.face_to_target, + "speedAtImpact": self.speed_at_impact + } + } + return payload + def to_gspro(self): payload = { "BallData": { diff --git a/src/bluetooth/mlm2pro_secret.py b/src/bluetooth/mlm2pro_secret.py new file mode 100644 index 0000000..d3366ff --- /dev/null +++ b/src/bluetooth/mlm2pro_secret.py @@ -0,0 +1,5 @@ +class MLM2PROSecret: + + @staticmethod + def decrypt(data: str): + return "" \ No newline at end of file diff --git a/src/connect_base.py b/src/connect_base.py new file mode 100644 index 0000000..960a05c --- /dev/null +++ b/src/connect_base.py @@ -0,0 +1,84 @@ +import socket +import logging +import json +from threading import Event + +import select +from PySide6.QtCore import QObject + +from src.ball_data import BallData +from src.custom_exception import SimTCPConnectionTimeout, SimTCPConnectionUnknownError, \ + SimTCPConnectionClientClosedConnection, SimTCPConnectionSocketError + + +class ConnectBase(QObject): + + successful_send = 200 + + def __init__(self, device_id, units, api_version) -> None: + self._socket = None + self._device_id = device_id + self._units = units + self._api_version = api_version + self._shot_number = 1 + self._connected = False + super(ConnectBase, self).__init__() + + def init_socket(self, ip_address: str, port: int) -> None: + self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._socket.connect((ip_address, port)) + self._socket.settimeout(2) + self._connected = True + + def connected(self): + return self._connected + + def send_msg(self, payload, attempts=2): + if self._connected: + for attempt in range(attempts): + try: + logging.info(f"Sending to GSPro data: {payload}") + self._socket.sendall(payload) + msg = self._socket.recv(2048) + except socket.timeout: + logging.info('Timed out. Retrying...') + if attempt >= attempts-1: + raise SimTCPConnectionTimeout(f'Failed to send shot to simulator after {attempts} attempts.') + Event().wait(0.5) + continue + except socket.error as e: + msg = f'Socket error when trying to send shot to simulator, Exception: {format(e)}' + logging.debug(msg) + raise SimTCPConnectionSocketError(msg) + except Exception as e: + msg = f"Unknown error when trying to send shot to simulator, Exception: {format(e)}" + logging.debug(msg) + raise SimTCPConnectionUnknownError(msg) + else: + if len(msg) == 0: + msg = f"Simulator closed the connection" + logging.debug(msg) + raise SimTCPConnectionClientClosedConnection(msg) + else: + logging.debug(f"Response from simulator: {msg}") + return msg + + def launch_ball(self, ball_data: BallData) -> None: + # logging.debug(f"---- launch_ball -----") + print("---------base_launch_ball") + logging.debug(ball_data) + return + + def check_for_message(self): + message = bytes(0) + if self._connected: + read_socket, write_socket, error_socket = select.select([self._socket], [], [], 0) + while read_socket: + message = message + self._socket.recv(1024) + read_socket, write_socket, error_socket = select.select([self._socket], [], [], 0) + return message + + def terminate_session(self): + if self._socket: + self._socket.close() + self._connected = False diff --git a/src/gspro_connect.py b/src/connect_gspro.py similarity index 83% rename from src/gspro_connect.py rename to src/connect_gspro.py index d8b7f89..ff25a8a 100644 --- a/src/gspro_connect.py +++ b/src/connect_gspro.py @@ -6,12 +6,13 @@ import select from PySide6.QtCore import QObject +from src.connect_base import ConnectBase from src.ball_data import BallData -from src.custom_exception import GSProConnectionTimeout, GSProConnectionUknownError, \ - GSProConnectionGSProClosedConnection, GSProConnectionSocketError +from src.custom_exception import SimTCPConnectionTimeout, SimTCPConnectionUnknownError, \ + SimTCPConnectionClientClosedConnection, SimTCPConnectionSocketError -class GSProConnect(QObject): +class GSProConnect(ConnectBase): successful_send = 200 @@ -22,7 +23,7 @@ def __init__(self, device_id, units, api_version) -> None: self._api_version = api_version self._shot_number = 1 self._connected = False - super(GSProConnect, self).__init__() + super(ConnectBase, self).__init__() def init_socket(self, ip_address: str, port: int) -> None: self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -43,22 +44,22 @@ def send_msg(self, payload, attempts=2): except socket.timeout: logging.info('Timed out. Retrying...') if attempt >= attempts-1: - raise GSProConnectionTimeout(f'Failed to send shot to GSPro after {attempts} attempts.') + raise SimTCPConnectionTimeout(f'Failed to send shot to GSPro after {attempts} attempts.') Event().wait(0.5) continue except socket.error as e: msg = f'GSPro Connector socket error when trying to send shot, Exception: {format(e)}' logging.debug(msg) - raise GSProConnectionSocketError(msg) + raise SimTCPConnectionSocketError(msg) except Exception as e: msg = f"GSPro Connector unknown error when trying to send shot, Exception: {format(e)}" logging.debug(msg) - raise GSProConnectionUknownError(msg) + raise SimTCPConnectionUnknownError(msg) else: if len(msg) == 0: msg = f"GSPro closed the connection" logging.debug(msg) - raise GSProConnectionGSProClosedConnection(msg) + raise SimTCPConnectionClientClosedConnection(msg) else: logging.debug(f"Response from GSPro: {msg}") return msg diff --git a/src/connect_ogs.py b/src/connect_ogs.py new file mode 100644 index 0000000..cd05cb3 --- /dev/null +++ b/src/connect_ogs.py @@ -0,0 +1,75 @@ +import socket +import logging +import json +from threading import Event + +import select +from PySide6.QtCore import QObject + +from src.connect_base import ConnectBase +from src.ball_data import BallData +from src.custom_exception import SimTCPConnectionTimeout, SimTCPConnectionUnknownError, \ + SimTCPConnectionClientClosedConnection, SimTCPConnectionSocketError + + +class OpenGolfSimConnect(ConnectBase): + + successful_send = 200 + + def __init__(self) -> None: + self._socket = None + self._shot_number = 1 + self._connected = False + super(ConnectBase, self).__init__() + + def send_msg(self, payload, attempts=2): + if self._connected: + for attempt in range(attempts): + try: + logging.info(f"Sending data: {payload}") + self._socket.sendall(payload) + msg = self._socket.recv(2048) + except socket.timeout: + logging.info('Timed out. Retrying...') + if attempt >= attempts-1: + raise SimTCPConnectionTimeout(f'Failed to send shot to OpenGolfSim after {attempts} attempts.') + Event().wait(0.5) + continue + except socket.error as e: + msg = f'Socket error when trying to send shot to OpenGolfSim, Exception: {format(e)}' + logging.debug(msg) + raise SimTCPConnectionSocketError(msg) + except Exception as e: + msg = f"Unknown error when trying to send shot to OpenGolfSim, Exception: {format(e)}" + logging.debug(msg) + raise SimTCPConnectionUnknownError(msg) + else: + if len(msg) == 0: + msg = f"OpenGolfSim closed the connection" + logging.debug(msg) + raise SimTCPConnectionClientClosedConnection(msg) + else: + logging.debug(f"Response from OpenGolfSim: {msg}") + return msg + + def launch_ball(self, ball_data: BallData) -> None: + if self._connected: + device = { + "shotId": self._shot_number, + "type": "shot" + } + payload = device | ball_data.to_opengolfsim() + logging.debug(f'Launch Ball payload: {payload} ball_data.to_opengolfsim(): {ball_data.to_opengolfsim()}') + self.send_msg(json.dumps(payload).encode("utf-8")) + self._shot_number += 1 + + def check_for_message(self): + message = bytes(0) + if self._connected: + read_socket, write_socket, error_socket = select.select([self._socket], [], [], 0) + while read_socket: + message = message + self._socket.recv(1024) + read_socket, write_socket, error_socket = select.select([self._socket], [], [], 0) + return message + + \ No newline at end of file diff --git a/src/custom_exception.py b/src/custom_exception.py index d183f34..31d804f 100644 --- a/src/custom_exception.py +++ b/src/custom_exception.py @@ -5,19 +5,19 @@ class CameraWindowNotFoundException(Exception): pass -class GSProConnectionTimeout(Exception): +class SimTCPConnectionTimeout(Exception): pass -class GSProConnectionSocketError(Exception): +class SimTCPConnectionSocketError(Exception): pass -class GSProConnectionUknownError(Exception): +class SimTCPConnectionUnknownError(Exception): pass -class GSProConnectionGSProClosedConnection(Exception): +class SimTCPConnectionClientClosedConnection(Exception): pass diff --git a/src/device_base.py b/src/device_base.py index 2aa7cb9..1b3bc7d 100644 --- a/src/device_base.py +++ b/src/device_base.py @@ -13,7 +13,7 @@ def __init__(self, main_window): def resume(self): self.running = True - if self.device_worker is not None and self.running and self.main_window.gspro_connection.connected: + if self.device_worker is not None and self.running and self.main_window.sim_connection.connected: self.device_worker.resume() def pause(self): diff --git a/src/device_launch_monitor_bluetooth_base.py b/src/device_launch_monitor_bluetooth_base.py index 16ab1d4..8f76da3 100644 --- a/src/device_launch_monitor_bluetooth_base.py +++ b/src/device_launch_monitor_bluetooth_base.py @@ -32,7 +32,7 @@ def setup_device_thread(self) -> None: def __setup_signals(self) -> None: self.main_window.start_server_button.clicked.connect(self.server_start_stop) - self.main_window.gspro_connection.club_selected.connect(self.__club_selected) + self.main_window.sim_connection.club_selected.connect(self.__club_selected) # Scanner signals self._scanner.status_update.connect(self.__status_update) self._scanner.device_found.connect(self.device_found) @@ -87,7 +87,7 @@ def _setup_device_signals(self) -> None: self._device.error.connect(self.__device_error) self._device.connected.connect(self.__device_connected) self._device.update_battery.connect(self.__update_battery) - self._device.shot.connect(self.main_window.gspro_connection.send_shot_worker.run) + self._device.shot.connect(self.main_window.sim_connection.send_shot_worker.run) self._device.launch_monitor_event.connect(self.__launch_monitor_event) def __launch_monitor_event(self, event: str) -> None: @@ -96,7 +96,7 @@ def __launch_monitor_event(self, event: str) -> None: def __shot_sent(self, ball_data: BallData) -> None: print(f"Shot sent: {json.dumps(ball_data.to_json())}") - if self.main_window.gspro_connection.connected: + if self.main_window.sim_connection.connected: self.main_window.shot_sent(ball_data) def __update_battery(self, battery: int) -> None: diff --git a/src/device_launch_monitor_relay_server.py b/src/device_launch_monitor_relay_server.py index c56ca59..4c6cd42 100644 --- a/src/device_launch_monitor_relay_server.py +++ b/src/device_launch_monitor_relay_server.py @@ -32,32 +32,32 @@ def setup_device_thread(self): def __setup_signals(self): self.main_window.start_server_button.clicked.connect(self.__server_start_stop) - self.main_window.gspro_connection.club_selected.connect(self.__club_selected) - self.main_window.gspro_connection.disconnected_from_gspro.connect(self.pause) - self.main_window.gspro_connection.connected_to_gspro.connect(self.resume) - self.main_window.gspro_connection.gspro_message.connect(self.__gspro_message) + self.main_window.sim_connection.club_selected.connect(self.__club_selected) + self.main_window.sim_connection.disconnected_from_sim.connect(self.pause) + self.main_window.sim_connection.connected_to_sim.connect(self.resume) + self.main_window.sim_connection.sim_message.connect(self.__sim_message) def __shot_sent(self, shot_data): data = json.loads(shot_data.decode("utf-8")) balldata = BallData() balldata.from_gspro(data) - balldata.club = self.main_window.gspro_connection.current_club - print(f'balldata: {balldata.to_json()} club: {self.main_window.gspro_connection.current_club}') + balldata.club = self.main_window.sim_connection.current_club + print(f'balldata: {balldata.to_json()} club: {self.main_window.sim_connection.current_club}') balldata.good_shot = True if self.prev_shot is None or self.prev_shot.eq(balldata) > 0: self.main_window.shot_sent(balldata) self.prev_shot = balldata - def __gspro_message(self, message): + def __sim_message(self, message): self.device_worker.send_msg(message) def __server_start_stop(self): if self.device_worker is None: #QMessageBox.warning(self.main_window, "Starting ELM connector", 'Before starting the relay server ensure your launch monitor is turned on and ready for connection.') - self.device_worker = WorkerDeviceLaunchMonitorRelayServer(self.main_window.settings, self.main_window.gspro_connection.gspro_connect) + self.device_worker = WorkerDeviceLaunchMonitorRelayServer(self.main_window.settings, self.main_window.sim_connection.sim_connect) self.setup_device_thread() self.device_worker.start() - self.device_worker.club_selected(self.main_window.gspro_connection.current_club) + self.device_worker.club_selected(self.main_window.sim_connection.current_club) #self.__start_app() else: self.device_worker.stop() @@ -86,7 +86,7 @@ def device_worker_paused(self): button = 'Start' if self.is_running(): button = 'Stop' - if self.main_window.gspro_connection.connected: + if self.main_window.sim_connection.connected: color = 'orange' status = 'Paused' else: @@ -103,7 +103,7 @@ def device_worker_resumed(self): self.main_window.start_server_button.setText('Stop') msg = 'Running' color = 'green' - if not self.main_window.gspro_connection.connected: + if not self.main_window.sim_connection.connected: msg = 'Waiting GSPro' color = 'red' self.main_window.server_status_label.setText(msg) diff --git a/src/device_launch_monitor_relay_server_base.py b/src/device_launch_monitor_relay_server_base.py index a559a2e..58d509a 100644 --- a/src/device_launch_monitor_relay_server_base.py +++ b/src/device_launch_monitor_relay_server_base.py @@ -34,32 +34,33 @@ def setup_device_thread(self): def __setup_signals(self): self.main_window.start_server_button.clicked.connect(self.__server_start_stop) - self.main_window.gspro_connection.club_selected.connect(self.__club_selected) - self.main_window.gspro_connection.disconnected_from_gspro.connect(self.pause) - self.main_window.gspro_connection.connected_to_gspro.connect(self.resume) - self.main_window.gspro_connection.gspro_message.connect(self.__gspro_message) + self.main_window.sim_connection.club_selected.connect(self.__club_selected) + self.main_window.sim_connection.disconnected_from_sim.connect(self.pause) + self.main_window.sim_connection.connected_to_sim.connect(self.resume) + self.main_window.sim_connection.sim_message.connect(self.__post_sim_message) def __shot_sent(self, shot_data): data = json.loads(shot_data.decode("utf-8")) balldata = BallData() balldata.from_gspro(data) - balldata.club = self.main_window.gspro_connection.current_club - print(f'balldata: {balldata.to_json()} club: {self.main_window.gspro_connection.current_club}') + balldata.club = self.main_window.sim_connection.current_club + print(f'balldata: {balldata.to_json()} club: {self.main_window.sim_connection.current_club}') balldata.good_shot = True if self.prev_shot is None or self.prev_shot.eq(balldata) > 0: self.main_window.shot_sent(balldata) self.prev_shot = balldata - def __gspro_message(self, message): + def __post_sim_message(self, message): + print(f'__post_sim_message: {message}') self.device_worker.send_msg(message) def __server_start_stop(self): if self.device_worker is None: #QMessageBox.warning(self.main_window, "Starting LM connector", 'Before starting the relay server ensure your launch monitor is turned on and ready for connection.') - self.device_worker = WorkerDeviceLaunchMonitorRelayServer(self.main_window.settings, self.main_window.gspro_connection.gspro_connect) + self.device_worker = WorkerDeviceLaunchMonitorRelayServer(self.main_window.settings, self.main_window.sim_connection.sim_connect) self.setup_device_thread() self.device_worker.start() - self.device_worker.club_selected(self.main_window.gspro_connection.current_club) + self.device_worker.club_selected(self.main_window.sim_connection.current_club) self.__start_app() else: self.device_worker.stop() @@ -111,7 +112,7 @@ def device_worker_paused(self): button = 'Start' if self.is_running(): button = 'Stop' - if self.main_window.gspro_connection.connected: + if self.main_window.sim_connection.connected: color = 'orange' status = 'Paused' else: @@ -128,7 +129,7 @@ def device_worker_resumed(self): self.main_window.start_server_button.setText('Stop') msg = 'Running' color = 'green' - if not self.main_window.gspro_connection.connected: + if not self.main_window.sim_connection.connected: msg = 'Waiting GSPro' color = 'red' self.main_window.server_status_label.setText(msg) diff --git a/src/device_launch_monitor_screenshot.py b/src/device_launch_monitor_screenshot.py index 9b80e32..a96ae03 100644 --- a/src/device_launch_monitor_screenshot.py +++ b/src/device_launch_monitor_screenshot.py @@ -27,7 +27,7 @@ def __init__(self, main_window): def setup_device_thread(self): super().setup_device_thread() - self.device_worker.shot.connect(self.main_window.gspro_connection.send_shot_worker.run) + self.device_worker.shot.connect(self.main_window.sim_connection.send_shot_worker.run) self.device_worker.bad_shot.connect(self.__bad_shot) self.device_worker.too_many_ghost_shots.connect(self.__too_many_ghost_shots) @@ -38,9 +38,9 @@ def __setup_signals(self): self.select_device.selected.connect(self.__device_selected) self.select_device.cancel.connect(self.__device_select_cancelled) self.main_window.select_device_button.clicked.connect(self.__select_device) - self.main_window.gspro_connection.club_selected.connect(self.__club_selected) - self.main_window.gspro_connection.disconnected_from_gspro.connect(self.pause) - self.main_window.gspro_connection.connected_to_gspro.connect(self.resume) + self.main_window.sim_connection.club_selected.connect(self.__club_selected) + self.main_window.sim_connection.disconnected_from_sim.connect(self.pause) + self.main_window.sim_connection.connected_to_sim.connect(self.resume) self.main_window.putting_settings_form.cancel.connect(self.resume) self.main_window.actionPuttingSettings.triggered.connect(self.pause) self.main_window.actionDevices.triggered.connect(self.__devices) @@ -67,7 +67,7 @@ def device_worker_paused(self): color = 'red' restart = False if self.is_running(): - if self.main_window.gspro_connection.connected: + if self.main_window.sim_connection.connected: color = 'orange' status = 'Paused' if self.device_worker.selected_club() != "PT": @@ -126,7 +126,7 @@ def device_worker_resumed(self): color = 'green' restart = False pause = True - if not self.main_window.gspro_connection.connected: + if not self.main_window.sim_connection.connected: msg = 'Waiting GSPro' color = 'red' pause = False @@ -137,7 +137,7 @@ def device_worker_resumed(self): self.main_window.pause_button.setEnabled(pause) def __device_select_cancelled(self): - if not self.current_device is None and self.main_window.gspro_connection.connected: + if not self.current_device is None and self.main_window.sim_connection.connected: self.device_worker.change_device(self.current_device) self.resume() @@ -150,7 +150,7 @@ def __device_selected(self, device): self.resume() else: self.start() - self.device_worker.club_selected(self.main_window.gspro_connection.current_club) + self.device_worker.club_selected(self.main_window.sim_connection.current_club) def __update_selected_mirror_app(self): diff --git a/src/device_putting_base.py b/src/device_putting_base.py index 182e32b..9febca1 100644 --- a/src/device_putting_base.py +++ b/src/device_putting_base.py @@ -20,12 +20,12 @@ def start_app(self): return def setup_signals(self): - self.main_window.gspro_connection.club_selected.connect(self.club_selected) + self.main_window.sim_connection.club_selected.connect(self.club_selected) self.main_window.putting_settings_form.cancel.connect(self.resume) self.main_window.actionPuttingSettings.triggered.connect(self.pause) - self.main_window.gspro_connection.disconnected_from_gspro.connect(self.pause) - self.main_window.gspro_connection.connected_to_gspro.connect(self.resume) - self.device_worker.shot.connect(self.main_window.gspro_connection.send_shot_worker.run) + self.main_window.sim_connection.disconnected_from_sim.connect(self.pause) + self.main_window.sim_connection.connected_to_sim.connect(self.resume) + self.device_worker.shot.connect(self.main_window.sim_connection.send_shot_worker.run) def club_selected(self, club_data): if self.device_worker is not None: @@ -39,7 +39,7 @@ def device_worker_paused(self): enabled = True if self.is_running(): msg = 'Stop' - if self.main_window.gspro_connection.connected: + if self.main_window.sim_connection.connected: color = 'orange' status = 'Paused' else: @@ -56,7 +56,7 @@ def device_worker_resumed(self): self.main_window.putting_server_button.setText('Stop') msg = 'Running' color = 'green' - if not self.main_window.gspro_connection.connected: + if not self.main_window.sim_connection.connected: msg = 'Waiting GSPro' color = 'red' self.main_window.putting_server_status_label.setText(msg) diff --git a/src/log_message.py b/src/log_message.py index 5b456d2..3160180 100644 --- a/src/log_message.py +++ b/src/log_message.py @@ -6,6 +6,7 @@ class LogMessageSystems: CONNECTOR = 'Connector' GSPRO_CONNECT = 'GSProConnect' + OPENGOLFSIM = 'OpenGolfSim' WEBCAM_PUTTING = "Webcam Putting" EXPUTT_PUTTING = "ExPutt" RELAY_SERVER = "Relay Server" diff --git a/src/settings.py b/src/settings.py index c70afc6..f242435 100644 --- a/src/settings.py +++ b/src/settings.py @@ -3,6 +3,11 @@ from src.settings_base import SettingsBase +@dataclass +class Simulator: + GSPRO = "GSPro" + OPENGOLFSIM = 'OpenGolfSim' + @dataclass class LaunchMonitor: MLM2PRO = "Rapsodo MLM2PRO" @@ -30,6 +35,7 @@ def __init__(self, app_paths): name='settings', ext='.json' ), { + "simulator_api": Simulator.GSPRO, "ip_address": "127.0.0.1", "port": 921, "api_version": "1", @@ -47,7 +53,9 @@ def __init__(self, app_paths): "relay_server_ip_address": "127.0.0.1", "relay_server_port": 9234, 'auto_start_all_apps': 'No', - 'keep_log_history': 'No' + 'keep_log_history': 'No', + "ogs_ip_address": "127.0.0.1", + "ogs_port": 3111 } ) # Removed this from the settings file, specifies the @@ -115,6 +123,9 @@ def load(self): "offline_mode": "No" } save = True + if not hasattr(self, 'simulator_api'): + self.simulator_api = Simulator.GSPRO + save = True if save: super().save() diff --git a/src/gspro_connection.py b/src/sim_connection.py similarity index 67% rename from src/gspro_connection.py rename to src/sim_connection.py index d73ce0f..c8370ee 100644 --- a/src/gspro_connection.py +++ b/src/sim_connection.py @@ -4,55 +4,69 @@ from PySide6.QtCore import QThread, Signal, QObject from PySide6.QtWidgets import QMessageBox from src.ctype_screenshot import ScreenMirrorWindow -from src.gspro_connect import GSProConnect -from src.worker_gspro_messages import WorkerGSProMessages +from src.settings import Simulator +from src.connect_base import ConnectBase +from src.connect_gspro import GSProConnect +from src.connect_ogs import OpenGolfSimConnect from src.worker_gspro_start import WorkerGSProStart -from src.worker_gspro import WorkerGspro +from src.worker_sim_messages import WorkerSimMessages +from src.worker_sim import WorkerSim from src.log_message import LogMessageSystems, LogMessageTypes from src.worker_thread import WorkerThread from PySide6.QtCore import QProcess -class GSProConnection(QObject): +class SimConnection(QObject): + + # simulator_api = Simulator.GSPRO + disconnected_from_sim = Signal() + connected_to_sim = Signal() - connected_to_gspro = Signal() - disconnected_from_gspro = Signal() club_selected = Signal(object) - gspro_message = Signal(object) + sim_message = Signal(object) + # ogs_message = Signal(object) - def __init__(self, main_window): - super(GSProConnection, self).__init__() + def __init__(self, main_window, simulator_api: Simulator): + super(SimConnection, self).__init__() self.main_window = main_window + # self.simulator_api == Simulator.GSPRO + self.simulator_api = simulator_api self.current_club = None self.worker = None self.thread = None - self.gspro_messages_thread = None - self.gspro_messages_worker = None + self.sim_messages_thread = None + self.sim_messages_worker = None self.send_shot_thread = None self.send_shot_worker = None self.gspro_start_worker = None self.gspro_start_thread = None self.connected = False self.settings = main_window.settings - self.gspro_connect = GSProConnect( - self.settings.device_id, - self.settings.units, - self.settings.api_version - ) - self.__gspro_disconnected() + + + if self.simulator_api == Simulator.OPENGOLFSIM: + self.sim_connect = OpenGolfSimConnect() + else: + self.sim_connect = GSProConnect( + self.settings.device_id, + self.settings.units, + self.settings.api_version + ) + + self.__sim_disconnected() self.__setup_send_shot_thread() - self.__setup_gspro_messages_thread() + self.__setup_sim_messages_thread() - def __gspro_disconnected(self): + def __sim_disconnected(self): self.main_window.gspro_connect_button.setEnabled(True) - self.main_window.log_message(LogMessageTypes.ALL, LogMessageSystems.GSPRO_CONNECT, 'Disconnected from GSPro') + self.main_window.log_message(LogMessageTypes.ALL, LogMessageSystems.GSPRO_CONNECT, f'Disconnected from {self.simulator_api}') self.main_window.gspro_connect_button.setText('Connect') self.main_window.gspro_status_label.setText('Not Connected') self.main_window.gspro_status_label.setStyleSheet(f"QLabel {{ background-color : red; color : white; }}") def __setup_send_shot_thread(self): self.send_shot_thread = QThread() - self.send_shot_worker = WorkerGspro(self.gspro_connect) + self.send_shot_worker = WorkerSim(self.sim_connect) self.send_shot_worker.moveToThread(self.send_shot_thread) self.send_shot_worker.started.connect(self.__sending_shot) self.send_shot_worker.sent.connect(self.main_window.shot_sent) @@ -60,23 +74,26 @@ def __setup_send_shot_thread(self): self.send_shot_thread.started.connect(self.send_shot_worker.run) self.send_shot_thread.start() - def __setup_gspro_messages_thread(self): - self.gspro_messages_thread = QThread() - self.gspro_messages_worker = WorkerGSProMessages(self.gspro_connect) - self.gspro_messages_worker.moveToThread(self.gspro_messages_thread) - self.gspro_messages_worker.club_selected.connect(self.__club_selected) - self.gspro_messages_worker.error.connect(self.__gspro_messages_error) - self.gspro_messages_worker.gspro_message.connect(self.__gspro_message) - self.gspro_messages_thread.started.connect(self.gspro_messages_worker.run) - self.gspro_messages_thread.start() + def __setup_sim_messages_thread(self): + self.sim_messages_thread = QThread() + self.sim_messages_worker = WorkerSimMessages(self.sim_connect) + self.sim_messages_worker.moveToThread(self.sim_messages_thread) + self.sim_messages_worker.club_selected.connect(self.__club_selected) + self.sim_messages_worker.error.connect(self.__sim_messages_error) + self.sim_messages_worker.sim_message.connect(self.__sim_message) + self.sim_messages_thread.started.connect(self.sim_messages_worker.run) + self.sim_messages_thread.start() - def __gspro_message(self, message): - self.gspro_message.emit(message) + def __sim_message(self, message): + self.sim_message.emit(message) def __setup_connection_thread(self): self.thread = QThread() - self.worker = WorkerThread( - self.gspro_connect.init_socket, self.settings.ip_address, self.settings.port) + if self.simulator_api == Simulator.OPENGOLFSIM: + self.worker = WorkerThread(self.sim_connect.init_socket, self.settings.ogs_ip_address, self.settings.ogs_port) + else: + self.worker = WorkerThread(self.sim_connect.init_socket, self.settings.ip_address, self.settings.port) + self.worker.moveToThread(self.thread) self.worker.started.connect(self.__in_progress) self.worker.result.connect(self.__connected) @@ -86,10 +103,10 @@ def __setup_connection_thread(self): self.thread.start() def __club_selecion_error(self, error): - self.disconnect_from_gspro() + self.disconnect_sim() msg = f"Error while trying to check for club selection messages from GSPro.\nMake sure GSPro API Connect is running.\nStart/restart API Connect from GSPro.\nPress 'Connect' to reconnect to GSPro." self.__log_message(LogMessageTypes.LOGS, f'{msg}\nException: {format(error)}') - QMessageBox.warning(self.main_window, "GSPro Receive Error", msg) + QMessageBox.warning(self.main_window, f"{self.simulator_api} Receive Error", msg) def __club_selected(self, club_data): logging.debug(f"{self.__class__.__name__} Club selected: {club_data['Player']['Club']}") @@ -105,48 +122,48 @@ def __club_selected(self, club_data): self.current_club = club_data['Player']['Club'] def __send_shot_error(self, error): - self.disconnect_from_gspro() - msg = f"Error while trying to send shot to GSPro.\nMake sure GSPro API Connect is running.\nStart/restart API Connect from GSPro.\nPress 'Connect' to reconnect to GSPro." + self.disconnect_sim() + msg = f"Error while trying to send shot to {self.simulator_api}" self.__log_message(LogMessageTypes.LOGS, f'{msg}\nException: {format(error)}') - QMessageBox.warning(self.main_window, "GSPro Send Error", msg) + QMessageBox.warning(self.main_window, f"{self.simulator_api} Send Error", msg) - def __gspro_messages_error(self, error): - self.disconnect_from_gspro() + def __sim_messages_error(self, error): + self.disconnect_sim() msg = f"Error while trying to check for new messages from GSPro.\nStart/restart API Connect from GSPro.\nPress 'Connect' to reconnect to GSPro." self.__log_message(LogMessageTypes.LOGS, f'{msg}\nException: {format(error)}') - QMessageBox.warning(self.main_window, "GSPro Message Receive Error", msg) + QMessageBox.warning(self.main_window, f"{self.simulator_api} Message Receive Error", msg) - def connect_to_gspro(self): + def connect_sim(self): if not self.connected: if self.__find_gspro_api_app(): if self.thread is None: self.__setup_connection_thread() else: - if not self.gspro_connect.connected(): + if not self.sim_connect.connected(): self.worker.run() - if self.gspro_messages_thread is None: - self.__setup_gspro_messages_thread() - self.gspro_messages_worker.start() + if self.sim_messages_thread is None: + self.__setup_sim_messages_thread() + self.sim_messages_worker.start() if self.send_shot_thread is None: self.__setup_send_shot_thread() self.send_shot_worker.start() self.__shutdown_gspro_start_thread() - def disconnect_from_gspro(self): + def disconnect_sim(self): if self.connected: self.connected = False - self.__gspro_disconnected() + self.__sim_disconnected() self.send_shot_worker.stop() - self.gspro_messages_worker.stop() - self.gspro_connect.terminate_session() - self.disconnected_from_gspro.emit() + self.sim_messages_worker.stop() + self.sim_connect.terminate_session() + self.disconnected_from_sim.emit() def __sending_shot(self): - self.__log_message(LogMessageTypes.ALL, 'Sending shot to GSPro') + self.__log_message(LogMessageTypes.ALL, 'Sending shot to {self.simulator_api}') def __in_progress(self): msg = 'Connecting...' - self.__log_message(LogMessageTypes.ALL, f'Connecting to GSPro...') + self.__log_message(LogMessageTypes.ALL, f'Connecting to {self.simulator_api}...') self.__log_message(LogMessageTypes.LOGS, f'Connection settings: {self.settings.to_json(True)}') self.main_window.gspro_status_label.setText(msg) self.main_window.gspro_status_label.setStyleSheet("QLabel { background-color : orange; color : white; }") @@ -155,37 +172,46 @@ def __in_progress(self): def __connected(self): self.connected = True self.main_window.gspro_connect_button.setEnabled(True) - self.main_window.log_message(LogMessageTypes.ALL, LogMessageSystems.GSPRO_CONNECT, f'Connected to GSPro') + self.main_window.log_message(LogMessageTypes.ALL, LogMessageSystems.GSPRO_CONNECT, f'Connected to {self.simulator_api}') self.main_window.gspro_connect_button.setText('Disconnect') self.main_window.gspro_status_label.setText('Connected') self.main_window.gspro_status_label.setStyleSheet(f"QLabel {{ background-color : green; color : white; }}") - self.connected_to_gspro.emit() + self.connected_to_sim.emit() def __error(self, error): - self.disconnect_from_gspro() - msg = "Error while trying to connect to GSPro.\nMake sure GSPro API Connect is running.\nStart/restart API Connect from GSPro.\nPress 'Connect' to reconnect to GSPro." + self.disconnect_sim() + if self.simulator_api == Simulator.OPENGOLFSIM: + msg = f"Error while trying to connect to {self.simulator_api}" + else: + msg = "Error while trying to connect to GSPro.\nMake sure GSPro API Connect is running.\nStart/restart API Connect from GSPro.\nPress 'Connect' to reconnect to GSPro." self.__log_message(LogMessageTypes.LOGS, f'{msg} Exception: {format(error)}') - QMessageBox.warning(self.main_window, "GSPro Connect Error", msg) + QMessageBox.warning(self.main_window, f"{self.simulator_api} Connect Error", msg) def shutdown(self): - self.gspro_connect.terminate_session() + self.sim_connect.terminate_session() self.connected = False self.__shutdown_threads() def __log_message(self, types, message): self.main_window.log_message(types, LogMessageSystems.GSPRO_CONNECT, message) + def __find_gspro_api_app(self): + # assume opengolfsim is already running + if self.simulator_api == Simulator.OPENGOLFSIM: + running = True + return running + try: if self.settings.local_gspro(): ScreenMirrorWindow.find_window(self.settings.gspro_api_window_name) running = True except Exception: - self.main_window.gspro_connection.disconnect_from_gspro() + self.disconnect_sim() msg = f"GSPro API window '{self.main_window.settings.gspro_api_window_name}' does not seem to be running.\nStart GSPro or reset the API connector.\nPress 'Connect' to reconnect to GSPro." self.main_window.log_message(LogMessageTypes.LOGS, - LogMessageSystems.GSPRO_CONNECT, - msg) + LogMessageSystems.GSPRO_CONNECT, + msg) QMessageBox.warning(self.main_window, "GSPRO API Window Not Found", msg) running = False return running @@ -224,12 +250,12 @@ def __gspro_start_error(self, error): QMessageBox.warning(self.main_window, "GSPro Start Error", msg) def __shutdown_threads(self): - if self.gspro_messages_thread is not None: - self.gspro_messages_worker.shutdown() - self.gspro_messages_thread.quit() - self.gspro_messages_thread.wait() - self.gspro_messages_thread = None - self.gspro_messages_worker = None + if self.sim_messages_thread is not None: + self.sim_messages_worker.shutdown() + self.sim_messages_thread.quit() + self.sim_messages_thread.wait() + self.sim_messages_thread = None + self.sim_messages_worker = None if self.send_shot_thread is not None: self.send_shot_thread.quit() self.send_shot_thread.wait() diff --git a/src/worker_device_launch_monitor_relay_server.py b/src/worker_device_launch_monitor_relay_server.py index 71a9ec3..6f58af6 100644 --- a/src/worker_device_launch_monitor_relay_server.py +++ b/src/worker_device_launch_monitor_relay_server.py @@ -5,7 +5,7 @@ from PySide6.QtCore import Signal -from src.gspro_connect import GSProConnect +from src.connect_base import ConnectBase from src.settings import Settings from src.worker_base import WorkerBase @@ -19,10 +19,10 @@ class WorkerDeviceLaunchMonitorRelayServer(WorkerBase): shot_error = Signal(tuple) disconnected = Signal() - def __init__(self, settings: Settings, gspro_connection: GSProConnect): + def __init__(self, settings: Settings, sim_connect: ConnectBase): WorkerBase.__init__(self) self.settings = settings - self.gspro_connection = gspro_connection + self.sim_connect = sim_connect self.name = 'WorkerDeviceLaunchMonitorRelayServer' self.connection = None self._shutdown = Event() @@ -56,9 +56,9 @@ def run(self) -> None: data = self.connection.recv(1024) if data is not None and len(data) > 0: logging.debug(f'{self.name}: connector received data: {data.decode()}') - if self.gspro_connection.connected(): + if self.sim_connect.connected(): try: - msg = self.gspro_connection.send_msg(data) + msg = self.sim_connect.send_msg(data) self.send_msg(msg) self.relay_server_shot.emit(data) logging.debug(f'{self.name}: connector sent data to GSPro result: {msg.decode()}') diff --git a/src/worker_gspro.py b/src/worker_sim.py similarity index 71% rename from src/worker_gspro.py rename to src/worker_sim.py index 079d247..c07bbe6 100644 --- a/src/worker_gspro.py +++ b/src/worker_sim.py @@ -1,22 +1,22 @@ import traceback from PySide6.QtCore import Signal -from src.gspro_connect import GSProConnect from src.worker_base import WorkerBase -class WorkerGspro(WorkerBase): +class WorkerSim(WorkerBase): sent = Signal(object or None) - def __init__(self, gspro_connection: GSProConnect): + def __init__(self, sim_connect): super().__init__() - self.gspro_connection = gspro_connection + self.sim_connect = sim_connect def run(self, balldata=None): if balldata is not None: try: + print(f'WorkerSim: {balldata.to_json()}') self.started.emit() - self.gspro_connection.launch_ball(balldata) + self.sim_connect.launch_ball(balldata) except Exception as e: traceback.print_exc() self.error.emit((e, traceback.format_exc())) diff --git a/src/worker_gspro_messages.py b/src/worker_sim_messages.py similarity index 71% rename from src/worker_gspro_messages.py rename to src/worker_sim_messages.py index 0dbdc3a..1d13b64 100644 --- a/src/worker_gspro_messages.py +++ b/src/worker_sim_messages.py @@ -4,20 +4,19 @@ import traceback from threading import Event from PySide6.QtCore import Signal -from src.gspro_connect import GSProConnect from src.worker_base import WorkerBase from src.worker_screenshot_device_base import WorkerScreenshotBase -class WorkerGSProMessages(WorkerBase): +class WorkerSimMessages(WorkerBase): player_info = 201 club_selected = Signal(object) - gspro_message = Signal(object) + sim_message = Signal(object) - def __init__(self, gspro_connection: GSProConnect): + def __init__(self, sim_connect): super().__init__() - self.gspro_connection = gspro_connection - self.name = 'WorkerGSProMessages' + self.sim_connect = sim_connect + self.name = 'WorkerSimMessages' def run(self): self.started.emit() @@ -27,12 +26,12 @@ def run(self): Event().wait(250/1000) # When _pause is clear we wait(suspended) if set we process self._pause.wait() - if not self._shutdown.is_set() and self.gspro_connection is not None and self.gspro_connection.connected(): + if not self._shutdown.is_set() and self.sim_connect is not None and self.sim_connect.connected(): try: - message = self.gspro_connection.check_for_message() + message = self.sim_connect.check_for_message() if len(message) > 0: - logging.debug(f'{self.name}: GSPro received data: {message}') - self.gspro_message.emit(message) + logging.debug(f'{self.name}: Received data: {message}') + self.sim_message.emit(message) self.__process_message(message) except Exception as e: if not isinstance(e, ValueError): @@ -51,6 +50,6 @@ def __process_message(self, message): msg = json.loads(json_message) messages[str(msg['Code'])] = msg # Check if club selection message - if msg['Code'] == WorkerGSProMessages.player_info: + if msg['Code'] == WorkerSimMessages.player_info: self.club_selected.emit(msg) return messages