diff --git a/Software/cyclope/package/cyclope-controller/src/Sources/WebServer.cpp b/Software/cyclope/package/cyclope-controller/src/Sources/WebServer.cpp index 8cda5d2..490b4af 100644 --- a/Software/cyclope/package/cyclope-controller/src/Sources/WebServer.cpp +++ b/Software/cyclope/package/cyclope-controller/src/Sources/WebServer.cpp @@ -4,11 +4,14 @@ */ #include #include +#include #include +#include #include #include #include #include +#include #include #include #include @@ -20,6 +23,11 @@ namespace WebServer { + /** The watchdog will fire if it is not cleared for this amount of time. */ + constexpr unsigned int WATCHDOG_TIMEOUT_SECONDS = 3; + /** The watchdog is cleared at this rate, granting that it will never fire. */ + constexpr unsigned int WATCHDOG_CLEAR_PERIOD = 1; + /** All available commands the user can send. */ typedef enum { @@ -27,8 +35,8 @@ namespace WebServer WEB_SERVER_COMMUNICATION_PROTOCOL_COMMAND_GET_BATTERY_VOLTAGE, WEB_SERVER_COMMUNICATION_PROTOCOL_COMMAND_SET_LIGHT_ENABLED, WEB_SERVER_COMMUNICATION_PROTOCOL_COMMAND_STOP_CURRENT_AI_PROGRAM, - WEB_SERVER_COMMUNICATION_PROTOCOL_COMMAND_SET_STREAMING_CAMERA_ENABLED, - WEB_SERVER_COMMUNICATION_PROTOCOL_COMMANDS_COUNT + WEB_SERVER_COMMUNICATION_PROTOCOL_COMMAND_CLEAR_WATCHDOG, + WEB_SERVER_COMMUNICATION_PROTOCOL_COMMAND_SET_STREAMING_CAMERA_ENABLED } WebServerCommunicationProtocolCommand; /** The server handle. */ @@ -92,7 +100,8 @@ namespace WebServer " COMMUNICATION_PROTOCOL_COMMAND_SET_MOTION: '0',\n" " COMMUNICATION_PROTOCOL_COMMAND_GET_BATTERY_VOLTAGE: '1',\n" " COMMUNICATION_PROTOCOL_COMMAND_SET_LIGHT_ENABLED: '2',\n" - " COMMUNICATION_PROTOCOL_COMMAND_STOP_CURRENT_AI_PROGRAM: '3'\n" + " COMMUNICATION_PROTOCOL_COMMAND_STOP_CURRENT_AI_PROGRAM: '3',\n" + " COMMUNICATION_PROTOCOL_COMMAND_CLEAR_WATCHDOG: '4'\n" " }\n" "\n" " const RobotMotion =\n" @@ -122,6 +131,13 @@ namespace WebServer " userInterfaceControl = document.getElementById(htmlElementId);\n" " userInterfaceControl.innerHTML = text;\n" " }\n" + "\n" + " function feedWatchdog()\n" + " {\n" + " command = CommunicationProtocol.COMMUNICATION_PROTOCOL_COMMAND_CLEAR_WATCHDOG;\n" + " communicationProtocolSendCommand(command);\n" + " }\n" + " setInterval(feedWatchdog, " + std::to_string(WATCHDOG_CLEAR_PERIOD * 1000) + ");\n" " \n"; } @@ -232,6 +248,10 @@ namespace WebServer ArtificialIntelligenceProgramManager::stopRunningProgram(); break; + case WEB_SERVER_COMMUNICATION_PROTOCOL_COMMAND_CLEAR_WATCHDOG: + alarm(WATCHDOG_TIMEOUT_SECONDS); // Reset the watchdog + break; + default: LOG(LOG_ERR, "Unknown command '%c'.", pointerStringCommand[0]); return -1; @@ -394,8 +414,36 @@ namespace WebServer return returnValue; } + /** Called when the watchdog fires. */ + static void _watchogSignalHandler(int) + { + LOG(LOG_WARNING, "The watchdog fired, stopping the robot."); + + // Stop the currently running AI program (if any) + ArtificialIntelligenceProgramManager::stopRunningProgram(); + + // Stop the robot to be sure + Motor::setRobotMotion(Motor::ROBOT_MOTION_STOP); + Lidar::setEnabled(false); + Light::setEnabled(false); + + // Stop the watchdog to avoid it retrigger itself continuously + int result = alarm(0); + if (result < 0) LOG(LOG_ERR, "Failed to disable the watchdog (%s).", strerror(errno)); + } + int initialize() { + // Create the event handler that will be called if the watchdog fires + struct sigaction signalAction{}; + signalAction.sa_handler = _watchogSignalHandler; + int result = sigaction(SIGALRM, &signalAction, nullptr); + if (result < 0) + { + LOG(LOG_ERR, "Error : failed to install the SIGALRM handler (%s).", strerror(errno)); + return -1; + } + // Start the web server daemon and configure it to accept connections from any IP _pointerServerHandle = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 80, NULL, NULL, _accessHandlerCallback, NULL, MHD_OPTION_END); if (_pointerServerHandle == NULL)