diff --git a/Software/LMSourceCode/ImageProcessing/gs_fsm.cpp b/Software/LMSourceCode/ImageProcessing/gs_fsm.cpp index adafdc4e..25014f93 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_fsm.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_fsm.cpp @@ -165,6 +165,7 @@ namespace golf_sim { // TBD - see if we need to move this back to the initializating state if (!waitingForBallState.already_sent_waiting_ipc_message) { GsUISystem::SendIPCStatusMessage(GsIPCResultType::kWaitingForBallToAppear); + GsSimInterface::SendHeartbeat(false); } // This check will be called repeatedly by re-queuing events. @@ -182,7 +183,8 @@ namespace golf_sim { } if (found) { - + // Inform connected sims that the ball is on the tee. + GsSimInterface::SendHeartbeat(true); if (GolfSimOptions::GetCommandLineOptions().system_mode_ == SystemMode::kCamera1Calibrate || GolfSimOptions::GetCommandLineOptions().system_mode_ == SystemMode::kCamera2Calibrate) { @@ -983,4 +985,3 @@ namespace golf_sim { } // GolfSim namespace #endif // #ifdef __unix__ // Ignore in Windows environment - diff --git a/Software/LMSourceCode/ImageProcessing/gs_gspro_results.cpp b/Software/LMSourceCode/ImageProcessing/gs_gspro_results.cpp index 2e0ff22e..2709fb14 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_gspro_results.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_gspro_results.cpp @@ -76,14 +76,14 @@ namespace golf_sim { shot_data_options_child.put("ContainsClubData", false); // TBD - Consider if we want to send the next two values in a heartbeat? shot_data_options_child.put("LaunchMonitorIsReady", true); - shot_data_options_child.put("LaunchMonitorBallDetected", true); + shot_data_options_child.put("LaunchMonitorBallDetected", heartbeat_ball_detected_); shot_data_options_child.put("IsHeartBeat", false); } else { shot_data_options_child.put("ContainsBallData", false); shot_data_options_child.put("ContainsClubData", false); shot_data_options_child.put("LaunchMonitorIsReady", true); - shot_data_options_child.put("LaunchMonitorBallDetected", true); + shot_data_options_child.put("LaunchMonitorBallDetected", heartbeat_ball_detected_); shot_data_options_child.put("IsHeartBeat", true); } diff --git a/Software/LMSourceCode/ImageProcessing/gs_results.cpp b/Software/LMSourceCode/ImageProcessing/gs_results.cpp index 39036bdc..a94b5b3b 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_results.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_results.cpp @@ -32,6 +32,9 @@ namespace golf_sim { // but this is a reasonable default for now club_type_ = GolfSimClubs::GetCurrentClubType(); + // Real shot data implies a ball was definitely detected. + heartbeat_ball_detected_ = true; + // TBD - Even though this is a constructor, it might be a reasonable // place to calculate the Carry yardarge. } diff --git a/Software/LMSourceCode/ImageProcessing/gs_results.h b/Software/LMSourceCode/ImageProcessing/gs_results.h index 010687e8..ac2e9ee8 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_results.h +++ b/Software/LMSourceCode/ImageProcessing/gs_results.h @@ -52,6 +52,8 @@ namespace golf_sim { // Some systems need a keep-alive bool result_message_is_keepalive_ = false; + bool heartbeat_launch_monitor_ready_ = true; + bool heartbeat_ball_detected_ = false; }; diff --git a/Software/LMSourceCode/ImageProcessing/gs_sim_interface.cpp b/Software/LMSourceCode/ImageProcessing/gs_sim_interface.cpp index 18cda70a..c1250bb6 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_sim_interface.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_sim_interface.cpp @@ -237,6 +237,26 @@ namespace golf_sim { shot_counter_++; } + void GsSimInterface::SendHeartbeat(bool ball_detected) { +#ifdef __unix__ // Ignore in Windows environment + if (!sims_initialized_) { + return; + } + + GsResults heartbeat; + heartbeat.result_message_is_keepalive_ = true; + heartbeat.heartbeat_ball_detected_ = ball_detected; + heartbeat.heartbeat_launch_monitor_ready_ = true; + + for (auto interface : interfaces_) { + if (interface == nullptr) { + continue; + } + interface->SendResults(heartbeat); + } +#endif + } + bool GsSimInterface::InterfaceIsPresent() { // The base interface isn't a real interface, so cannot be 'present' return false; diff --git a/Software/LMSourceCode/ImageProcessing/gs_sim_interface.h b/Software/LMSourceCode/ImageProcessing/gs_sim_interface.h index 6c969578..7e12e237 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_sim_interface.h +++ b/Software/LMSourceCode/ImageProcessing/gs_sim_interface.h @@ -80,6 +80,10 @@ namespace golf_sim { // Returns true only if each of the available interfaces is armed static bool GetAllSystemsArmed(); + // Heartbeat support for external simulators + static void SendHeartbeat(bool ball_detected); + static inline void ResetHeartbeatState() {} + protected: // Typical derived-class behavior will be to convert the results into a diff --git a/Software/LMSourceCode/ImageProcessing/gs_sim_socket_interface.cpp b/Software/LMSourceCode/ImageProcessing/gs_sim_socket_interface.cpp index 14083607..5e960c1a 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_sim_socket_interface.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_sim_socket_interface.cpp @@ -18,6 +18,7 @@ #include "gs_events.h" #include "gs_ipc_control_msg.h" +#include "gs_sim_socket_interface.h" #include "gs_gspro_interface.h" #include "gs_gspro_response.h" #include "gs_gspro_results.h" @@ -95,6 +96,10 @@ namespace golf_sim { initialized_ = true; + // Connection just came up – make sure the first heartbeat reports no ball detected. + GsSimInterface::ResetHeartbeatState(); + GsSimInterface::SendHeartbeat(false); + // Derived classes will need to deal with any initial messaging after the socket is established. return true;