diff --git a/.changeset/keepalive-timeout-reconnect.md b/.changeset/keepalive-timeout-reconnect.md new file mode 100644 index 00000000..9b94d190 --- /dev/null +++ b/.changeset/keepalive-timeout-reconnect.md @@ -0,0 +1,5 @@ +--- +'firmware': minor +--- + +feat: add a keepalive ping timeout that forces a reconnect on backend connection loss \ No newline at end of file diff --git a/include/GatewayClient.h b/include/GatewayClient.h index 9d516723..49458438 100644 --- a/include/GatewayClient.h +++ b/include/GatewayClient.h @@ -28,6 +28,8 @@ namespace OpenShock { bool sendMessageTXT(std::string_view data); bool sendMessageBIN(tcb::span data); + void markPingReceived(); + bool loop(); private: @@ -37,5 +39,6 @@ namespace OpenShock { WebSocketsClient m_webSocket; GatewayClientState m_state; + int64_t m_lastPingTimestamp; }; } // namespace OpenShock diff --git a/include/GatewayConnectionManager.h b/include/GatewayConnectionManager.h index fdff763d..060c9228 100644 --- a/include/GatewayConnectionManager.h +++ b/include/GatewayConnectionManager.h @@ -19,5 +19,7 @@ namespace OpenShock::GatewayConnectionManager { bool SendMessageTXT(std::string_view data); bool SendMessageBIN(tcb::span data); + void MarkPingReceived(); + void Update(); } // namespace OpenShock::GatewayConnectionManager diff --git a/src/GatewayClient.cpp b/src/GatewayClient.cpp index 1a656a7f..4eca8c26 100644 --- a/src/GatewayClient.cpp +++ b/src/GatewayClient.cpp @@ -14,11 +14,14 @@ const char* const TAG = "GatewayClient"; using namespace OpenShock; +const int64_t GATEWAY_PING_TIMEOUT = 90'000; + static bool s_bootStatusSent = false; GatewayClient::GatewayClient(const std::string& authToken) : m_webSocket() , m_state(GatewayClientState::Disconnected) + , m_lastPingTimestamp(0) { OS_LOGD(TAG, "Creating GatewayClient"); @@ -90,6 +93,11 @@ bool GatewayClient::sendMessageBIN(tcb::span data) return m_webSocket.sendBIN(data.data(), data.size()); } +void GatewayClient::markPingReceived() +{ + m_lastPingTimestamp = OpenShock::millis(); +} + bool GatewayClient::loop() { if (m_state == GatewayClientState::Disconnected) { @@ -104,6 +112,13 @@ bool GatewayClient::loop() return true; } + if (m_lastPingTimestamp != 0 && (OpenShock::millis() - m_lastPingTimestamp) > GATEWAY_PING_TIMEOUT) { + OS_LOGW(TAG, "No ping received from gateway for %lld ms, forcing reconnect", GATEWAY_PING_TIMEOUT); + m_webSocket.disconnect(); + _setState(GatewayClientState::Disconnected); + return false; + } + return true; } @@ -160,6 +175,7 @@ void GatewayClient::_handleEvent(WStype_t type, uint8_t* payload, std::size_t le _setState(GatewayClientState::Disconnected); break; case WStype_CONNECTED: + m_lastPingTimestamp = 0; _setState(GatewayClientState::Connected); _sendBootStatus(); break; diff --git a/src/GatewayConnectionManager.cpp b/src/GatewayConnectionManager.cpp index bcfa35df..270fd6ea 100644 --- a/src/GatewayConnectionManager.cpp +++ b/src/GatewayConnectionManager.cpp @@ -194,6 +194,16 @@ bool GatewayConnectionManager::SendMessageBIN(tcb::span data) return client->sendMessageBIN(data); } +void GatewayConnectionManager::MarkPingReceived() +{ + auto client = GetClient(); + if (client == nullptr) { + return; + } + + client->markPingReceived(); +} + bool FetchHubInfo(std::string authToken) { // TODO: this function is very slow, should be optimized! diff --git a/src/message_handlers/websocket/gateway/Ping.cpp b/src/message_handlers/websocket/gateway/Ping.cpp index 55d5ddf8..7c1b8e9d 100644 --- a/src/message_handlers/websocket/gateway/Ping.cpp +++ b/src/message_handlers/websocket/gateway/Ping.cpp @@ -18,5 +18,6 @@ void _Private::HandlePing(const OpenShock::Serialization::Gateway::GatewayToHubM return; } + GatewayConnectionManager::MarkPingReceived(); Serialization::Gateway::SerializePongMessage(GatewayConnectionManager::SendMessageBIN); }