From 7afd5f1076505f7aac2cc6b03e77ef3a30d2da5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sat, 28 Feb 2026 12:43:15 +0100 Subject: [PATCH 01/18] Fixed copy pointers leading with possible missalignment --- Inc/HALAL/Models/Packets/PacketValue.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Models/Packets/PacketValue.hpp b/Inc/HALAL/Models/Packets/PacketValue.hpp index 529000d9..1e9d373c 100644 --- a/Inc/HALAL/Models/Packets/PacketValue.hpp +++ b/Inc/HALAL/Models/Packets/PacketValue.hpp @@ -33,8 +33,8 @@ class PacketValue : public PacketValue<> { void set_pointer(void* pointer) { src = (Type*)pointer; } size_t get_size() override { return sizeof(Type); } - void parse(uint8_t* data) override { *src = *((Type*)data); } - void copy_to(uint8_t* data) override { *((Type*)data) = *src; } + void parse(uint8_t* data) override { memcpy(src, data, get_size()); } + void copy_to(uint8_t* data) override { memcpy(data, src, get_size()); } }; template <> class PacketValue : public PacketValue<> { @@ -49,7 +49,7 @@ template <> class PacketValue : public PacketValue<> { void set_pointer(void* pointer) { src = (double*)pointer; } size_t get_size() override { return sizeof(double); } - void parse(uint8_t* data) override { *src = *((double*)data); } + void parse(uint8_t* data) override { memcpy(src, data, get_size()); } void copy_to(uint8_t* data) override { memcpy(data, src, get_size()); } }; From aff5df0082f90932a5b9f29f9bdfb555bcc91462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sat, 28 Feb 2026 12:45:43 +0100 Subject: [PATCH 02/18] fix(tcp): handle fragmented order streams and queue backpressure --- Inc/HALAL/Models/Packets/OrderProtocol.hpp | 1 + .../Ethernet/LWIP/TCP/ServerSocket.hpp | 31 +- .../Ethernet/LWIP/TCP/Socket.hpp | 35 +- .../Ethernet/LWIP/TCP/ServerSocket.cpp | 413 +++++++++---- .../Ethernet/LWIP/TCP/Socket.cpp | 551 +++++++++++++----- 5 files changed, 733 insertions(+), 298 deletions(-) diff --git a/Inc/HALAL/Models/Packets/OrderProtocol.hpp b/Inc/HALAL/Models/Packets/OrderProtocol.hpp index 5e07d39f..4ea3f1d6 100644 --- a/Inc/HALAL/Models/Packets/OrderProtocol.hpp +++ b/Inc/HALAL/Models/Packets/OrderProtocol.hpp @@ -5,6 +5,7 @@ class Order; class OrderProtocol { public: + virtual ~OrderProtocol() = default; virtual bool send_order(Order& order) = 0; static vector sockets; diff --git a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp index daa2742a..70f656cb 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp @@ -41,7 +41,7 @@ class ServerSocket : public OrderProtocol { public: enum ServerState { INACTIVE, LISTENING, ACCEPTED, CLOSING, CLOSED }; - static constexpr size_t MAX_TX_QUEUE_DEPTH = 24; + static constexpr size_t MAX_TX_QUEUE_DEPTH = 64; static unordered_map listening_sockets; IPV4 local_ip; @@ -114,25 +114,13 @@ class ServerSocket : public OrderProtocol { if (state != ACCEPTED || client_control_block == nullptr) { return false; } - send(); - if (tx_packet_buffer.size() >= MAX_TX_QUEUE_DEPTH) { - return false; - } - - uint8_t* order_buffer = order.build(); - if (order.get_size() > tcp_sndbuf(client_control_block)) { - return false; - } - - struct pbuf* packet = pbuf_alloc(PBUF_TRANSPORT, order.get_size(), PBUF_RAM); - if (packet == nullptr) { - return false; - } - if (pbuf_take(packet, order_buffer, order.get_size()) != ERR_OK) { - pbuf_free(packet); - return false; + if (!add_order_to_queue(order)) { + // One opportunistic flush avoids false negatives when TX queue is momentarily full. + send(); + if (!add_order_to_queue(order)) { + return false; + } } - tx_packet_buffer.push(packet); send(); return true; } @@ -160,12 +148,15 @@ class ServerSocket : public OrderProtocol { * otherwise */ bool is_connected(); + bool is_listening() const; private: struct tcp_pcb* server_control_block = nullptr; queue tx_packet_buffer; queue rx_packet_buffer; - struct tcp_pcb* client_control_block; + vector rx_stream_buffer; + struct tcp_pcb* client_control_block = nullptr; + void clear_packet_queues(); /** * @brief process the data received by the client orders. It is meant to be diff --git a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp index 8cb38a1e..14fa9e4e 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp @@ -17,10 +17,12 @@ class Socket : public OrderProtocol { private: - tcp_pcb* connection_control_block; - tcp_pcb* socket_control_block; + tcp_pcb* connection_control_block = nullptr; + tcp_pcb* socket_control_block = nullptr; queue tx_packet_buffer; queue rx_packet_buffer; + vector rx_stream_buffer; + void clear_packet_queues(); void process_data(); static err_t connect_callback(void* arg, struct tcp_pcb* client_control_block, err_t error); static err_t receive_callback( @@ -39,6 +41,7 @@ class Socket : public OrderProtocol { public: enum SocketState { INACTIVE, CONNECTED, CLOSING }; + static constexpr size_t MAX_TX_QUEUE_DEPTH = 64; IPV4 local_ip; uint32_t local_port; @@ -49,6 +52,7 @@ class Socket : public OrderProtocol { static unordered_map connecting_sockets; bool pending_connection_reset = false; + uint16_t connect_poll_ticks = 0; bool use_keep_alives{true}; struct KeepaliveConfig { uint32_t inactivity_time_until_keepalive_ms = TCP_INACTIVITY_TIME_UNTIL_KEEPALIVE_MS; @@ -96,32 +100,17 @@ class Socket : public OrderProtocol { */ bool send_order(Order& order) override { - if (state != CONNECTED) { + if (state != CONNECTED || socket_control_block == nullptr) { reconnect(); return false; } - struct memp* next_memory_pointer_in_packet_buffer_pool = - (*(memp_pools[PBUF_POOL_MEMORY_DESC_POSITION]->tab))->next; - if (next_memory_pointer_in_packet_buffer_pool == nullptr) { - if (socket_control_block->unsent != nullptr) { - tcp_output(socket_control_block); - } else { - memp_free_pool( - memp_pools[PBUF_POOL_MEMORY_DESC_POSITION], - next_memory_pointer_in_packet_buffer_pool - ); + if (!add_order_to_queue(order)) { + // One opportunistic flush avoids false negatives when TX queue is momentarily full. + send(); + if (!add_order_to_queue(order)) { + return false; } - return false; } - - uint8_t* order_buffer = order.build(); - if (order.get_size() > tcp_sndbuf(socket_control_block)) { - return false; - } - - struct pbuf* packet = pbuf_alloc(PBUF_TRANSPORT, order.get_size(), PBUF_POOL); - pbuf_take(packet, order_buffer, order.get_size()); - tx_packet_buffer.push(packet); send(); return true; } diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp index 2e35aba6..6d5b4f45 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp @@ -13,32 +13,95 @@ uint8_t ServerSocket::priority = 1; unordered_map ServerSocket::listening_sockets = {}; +namespace { +constexpr size_t MAX_RX_STREAM_BUFFER_BYTES = 8192; + +void process_order_stream( + OrderProtocol* protocol, + IPV4& remote_ip, + vector& stream_buffer +) { + if (stream_buffer.empty()) { + return; + } + size_t parsed_bytes = 0; + + while (stream_buffer.size() - parsed_bytes >= sizeof(uint16_t)) { + uint8_t* packet_ptr = stream_buffer.data() + parsed_bytes; + uint16_t order_id = Packet::get_id(packet_ptr); + auto order_it = Order::orders.find(order_id); + if (order_it == Order::orders.end()) { + parsed_bytes += 1; + continue; + } + + const size_t order_size = order_it->second->get_size(); + if (order_size < sizeof(uint16_t)) { + parsed_bytes += 1; + continue; + } + if (stream_buffer.size() - parsed_bytes < order_size) { + break; + } + + order_it->second->store_ip_order(remote_ip.string_address); + Order::process_data(protocol, packet_ptr); + parsed_bytes += order_size; + } + + if (parsed_bytes > 0) { + stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + parsed_bytes); + } + + if (stream_buffer.size() > MAX_RX_STREAM_BUFFER_BYTES) { + const size_t trim_count = stream_buffer.size() - MAX_RX_STREAM_BUFFER_BYTES; + stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + trim_count); + } +} +} // namespace + ServerSocket::ServerSocket() = default; ServerSocket::ServerSocket(IPV4 local_ip, uint32_t local_port) : local_ip(local_ip), local_port(local_port) { if (not Ethernet::is_running) { - ErrorHandler("Cannot declare UDP socket before Ethernet::start()"); + ErrorHandler("Cannot declare TCP server socket before Ethernet::start()"); return; } tx_packet_buffer = {}; rx_packet_buffer = {}; + rx_stream_buffer = {}; + rx_stream_buffer.reserve(MAX_RX_STREAM_BUFFER_BYTES); state = INACTIVE; server_control_block = tcp_new(); + if (server_control_block == nullptr) { + ErrorHandler("Cannot allocate TCP server control block"); + return; + } tcp_nagle_disable(server_control_block); ip_set_option(server_control_block, SOF_REUSEADDR); err_t error = tcp_bind(server_control_block, &local_ip.address, local_port); if (error == ERR_OK) { server_control_block = tcp_listen(server_control_block); + if (server_control_block == nullptr) { + ErrorHandler("Cannot switch TCP server socket into LISTEN mode"); + return; + } state = LISTENING; listening_sockets[local_port] = this; + tcp_arg(server_control_block, this); tcp_accept(server_control_block, accept_callback); } else { - memp_free(MEMP_TCP_PCB, server_control_block); + tcp_abort(server_control_block); + server_control_block = nullptr; ErrorHandler("Cannot bind server socket, error %d", (int16_t)error); + return; + } + if (std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this) == + OrderProtocol::sockets.end()) { + OrderProtocol::sockets.push_back(this); } - OrderProtocol::sockets.push_back(this); } ServerSocket::ServerSocket( @@ -55,48 +118,100 @@ ServerSocket::ServerSocket( } ServerSocket::ServerSocket(ServerSocket&& other) - : local_ip(move(other.local_ip)), local_port(move(other.local_port)), state(other.state), - server_control_block(move(other.server_control_block)) { - listening_sockets[local_port] = this; - tx_packet_buffer = {}; - rx_packet_buffer = {}; + : local_ip(move(other.local_ip)), local_port(other.local_port), remote_ip(move(other.remote_ip)), + state(other.state), keepalive_config(other.keepalive_config), + server_control_block(other.server_control_block), + tx_packet_buffer(move(other.tx_packet_buffer)), rx_packet_buffer(move(other.rx_packet_buffer)), + rx_stream_buffer(move(other.rx_stream_buffer)), client_control_block(other.client_control_block) { + other.server_control_block = nullptr; + other.client_control_block = nullptr; + other.state = INACTIVE; + + if (server_control_block != nullptr) { + tcp_arg(server_control_block, this); + } + if (client_control_block != nullptr) { + tcp_arg(client_control_block, this); + } + + auto it = listening_sockets.find(local_port); + if (it != listening_sockets.end() && it->second == &other) { + it->second = this; + } + + if (std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this) == + OrderProtocol::sockets.end()) { + OrderProtocol::sockets.push_back(this); + } } void ServerSocket::operator=(ServerSocket&& other) { + if (this == &other) { + return; + } + close(); + local_ip = move(other.local_ip); - local_port = move(other.local_port); - server_control_block = move(other.server_control_block); + local_port = other.local_port; + remote_ip = move(other.remote_ip); + server_control_block = other.server_control_block; + client_control_block = other.client_control_block; + tx_packet_buffer = move(other.tx_packet_buffer); + rx_packet_buffer = move(other.rx_packet_buffer); + rx_stream_buffer = move(other.rx_stream_buffer); + keepalive_config = other.keepalive_config; state = other.state; - listening_sockets[local_port] = this; - tx_packet_buffer = {}; - rx_packet_buffer = {}; - if (not(std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this) != - OrderProtocol::sockets.end())) - OrderProtocol::sockets.push_back(this); -} -ServerSocket::~ServerSocket() { - // el destructor no destruye - auto it = std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this); - if (it == OrderProtocol::sockets.end()) - return; - else - OrderProtocol::sockets.erase(it); + other.server_control_block = nullptr; + other.client_control_block = nullptr; + other.state = INACTIVE; + + if (server_control_block != nullptr) { + tcp_arg(server_control_block, this); + } if (client_control_block != nullptr) { - tcp_abort(client_control_block); - client_control_block = nullptr; + tcp_arg(client_control_block, this); } - if (server_control_block != nullptr) { - tcp_abort(server_control_block); - server_control_block = nullptr; + + auto it = listening_sockets.find(local_port); + if (it != listening_sockets.end() && it->second == &other) { + it->second = this; } + + if (std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this) == + OrderProtocol::sockets.end()) { + OrderProtocol::sockets.push_back(this); + } +} + +void ServerSocket::clear_packet_queues() { while (!tx_packet_buffer.empty()) { - pbuf_free(tx_packet_buffer.front()); + pbuf* packet = tx_packet_buffer.front(); tx_packet_buffer.pop(); + if (packet != nullptr) { + pbuf_free(packet); + } } while (!rx_packet_buffer.empty()) { - pbuf_free(rx_packet_buffer.front()); + pbuf* packet = rx_packet_buffer.front(); rx_packet_buffer.pop(); + if (packet != nullptr) { + pbuf_free(packet); + } + } +} + +ServerSocket::~ServerSocket() { + close(); + + auto listener_it = listening_sockets.find(local_port); + if (listener_it != listening_sockets.end() && listener_it->second == this) { + listening_sockets.erase(listener_it); + } + + auto it = std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this); + if (it != OrderProtocol::sockets.end()) { + OrderProtocol::sockets.erase(it); } } @@ -118,31 +233,49 @@ void ServerSocket::close() { } client_control_block = nullptr; } - while (!tx_packet_buffer.empty()) { - pbuf_free(tx_packet_buffer.front()); - tx_packet_buffer.pop(); - } - while (!rx_packet_buffer.empty()) { - pbuf_free(rx_packet_buffer.front()); - rx_packet_buffer.pop(); + if (server_control_block != nullptr) { + tcp_arg(server_control_block, nullptr); + tcp_accept(server_control_block, nullptr); + err_t close_error = tcp_close(server_control_block); + if (close_error != ERR_OK) { + tcp_abort(server_control_block); + } + server_control_block = nullptr; } - - listening_sockets[local_port] = this; + clear_packet_queues(); + rx_stream_buffer.clear(); state = CLOSED; - priority--; + auto listener_it = listening_sockets.find(local_port); + if (listener_it != listening_sockets.end() && listener_it->second == this) { + listening_sockets.erase(listener_it); + } + + if (priority > 1) { + priority--; + } } void ServerSocket::process_data() { while (!rx_packet_buffer.empty()) { - struct pbuf* packet = rx_packet_buffer.front(); + pbuf* packet = rx_packet_buffer.front(); rx_packet_buffer.pop(); - uint8_t* new_data = (uint8_t*)(packet->payload); - tcp_recved(client_control_block, packet->tot_len); - uint16_t id = Packet::get_id(new_data); - if (Order::orders.contains(id)) { - Order::orders[id]->store_ip_order(remote_ip.string_address); - Order::process_data(this, new_data); + if (packet == nullptr) { + continue; + } + + if (client_control_block != nullptr) { + tcp_recved(client_control_block, packet->tot_len); + } + + const size_t previous_size = rx_stream_buffer.size(); + const size_t append_size = packet->tot_len; + rx_stream_buffer.resize(previous_size + append_size); + if (pbuf_copy_partial(packet, rx_stream_buffer.data() + previous_size, packet->tot_len, 0) == + static_cast(packet->tot_len)) { + process_order_stream(this, remote_ip, rx_stream_buffer); + } else { + rx_stream_buffer.resize(previous_size); } pbuf_free(packet); } @@ -152,23 +285,22 @@ bool ServerSocket::add_order_to_queue(Order& order) { if (state != ACCEPTED || client_control_block == nullptr) { return false; } - send(); if (tx_packet_buffer.size() >= MAX_TX_QUEUE_DEPTH) { return false; } - struct memp* next_memory_pointer_in_packet_buffer_pool = - (*(memp_pools[PBUF_POOL_MEMORY_DESC_POSITION]->tab))->next; - if (next_memory_pointer_in_packet_buffer_pool == nullptr) { + + const size_t order_size = order.get_size(); + if (order_size == 0 || order_size > tcp_sndbuf(client_control_block)) { return false; } uint8_t* order_buffer = order.build(); - struct pbuf* packet = pbuf_alloc(PBUF_TRANSPORT, order.get_size(), PBUF_RAM); + pbuf* packet = pbuf_alloc(PBUF_TRANSPORT, order_size, PBUF_RAM); if (packet == nullptr) { return false; } - if (pbuf_take(packet, order_buffer, order.get_size()) != ERR_OK) { + if (pbuf_take(packet, order_buffer, order_size) != ERR_OK) { pbuf_free(packet); return false; } @@ -181,68 +313,97 @@ void ServerSocket::send() { return; } - if (tx_packet_buffer.empty()) { - return; - } + while (!tx_packet_buffer.empty()) { + pbuf* temporal_packet_buffer = tx_packet_buffer.front(); + if (temporal_packet_buffer == nullptr) { + tx_packet_buffer.pop(); + continue; + } - pbuf* temporal_packet_buffer = tx_packet_buffer.front(); - if (temporal_packet_buffer->tot_len > tcp_sndbuf(client_control_block)) { - return; - } + if (temporal_packet_buffer->tot_len > tcp_sndbuf(client_control_block)) { + break; + } - err_t error = tcp_write( - client_control_block, - temporal_packet_buffer->payload, - temporal_packet_buffer->tot_len, - TCP_WRITE_FLAG_COPY - ); - if (error == ERR_OK) { - tx_packet_buffer.pop(); - tcp_output(client_control_block); - pbuf_free(temporal_packet_buffer); - } else if (error == ERR_MEM) { - // TX queue full on lwIP side: keep packet enqueued and retry later. + err_t error = tcp_write( + client_control_block, + temporal_packet_buffer->payload, + temporal_packet_buffer->tot_len, + TCP_WRITE_FLAG_COPY + ); + if (error == ERR_OK) { + tx_packet_buffer.pop(); + pbuf_free(temporal_packet_buffer); + } else if (error == ERR_MEM) { + break; + } else { + state = CLOSING; + break; + } + } + if (client_control_block != nullptr) { tcp_output(client_control_block); - return; - } else { - // Connection/state error: request graceful close instead of hard-stopping firmware. - state = CLOSING; - return; } } bool ServerSocket::is_connected() { return state == ServerSocket::ServerState::ACCEPTED; } +bool ServerSocket::is_listening() const { return state == ServerSocket::ServerState::LISTENING; } + err_t ServerSocket::accept_callback( void* arg, struct tcp_pcb* incomming_control_block, err_t error ) { - if (listening_sockets.contains(incomming_control_block->local_port)) { - ServerSocket* server_socket = listening_sockets[incomming_control_block->local_port]; - - server_socket->state = ACCEPTED; - server_socket->client_control_block = incomming_control_block; - server_socket->remote_ip = IPV4(incomming_control_block->remote_ip); - server_socket->rx_packet_buffer = {}; - - tcp_setprio(incomming_control_block, priority); - tcp_nagle_disable(incomming_control_block); - ip_set_option(incomming_control_block, SOF_REUSEADDR); - - tcp_arg(incomming_control_block, server_socket); - tcp_recv(incomming_control_block, receive_callback); - tcp_sent(incomming_control_block, send_callback); - tcp_err(incomming_control_block, error_callback); - tcp_poll(incomming_control_block, poll_callback, 1); - config_keepalive(incomming_control_block, server_socket); - - tcp_close(server_socket->server_control_block); + if (error != ERR_OK || incomming_control_block == nullptr) { + if (incomming_control_block != nullptr) { + tcp_abort(incomming_control_block); + } + return error; + } + + ServerSocket* server_socket = static_cast(arg); + if (server_socket == nullptr) { + auto it = listening_sockets.find(incomming_control_block->local_port); + if (it == listening_sockets.end()) { + tcp_abort(incomming_control_block); + return ERR_ABRT; + } + server_socket = it->second; + } + + server_socket->state = ACCEPTED; + server_socket->client_control_block = incomming_control_block; + server_socket->remote_ip = IPV4(incomming_control_block->remote_ip); + server_socket->rx_packet_buffer = {}; + server_socket->rx_stream_buffer.clear(); + server_socket->rx_stream_buffer.reserve(MAX_RX_STREAM_BUFFER_BYTES); + + tcp_setprio(incomming_control_block, priority); + tcp_nagle_disable(incomming_control_block); + ip_set_option(incomming_control_block, SOF_REUSEADDR); + + tcp_arg(incomming_control_block, server_socket); + tcp_recv(incomming_control_block, receive_callback); + tcp_sent(incomming_control_block, send_callback); + tcp_err(incomming_control_block, error_callback); + tcp_poll(incomming_control_block, poll_callback, 1); + config_keepalive(incomming_control_block, server_socket); + + if (server_socket->server_control_block != nullptr) { + tcp_arg(server_socket->server_control_block, nullptr); + tcp_accept(server_socket->server_control_block, nullptr); + err_t close_error = tcp_close(server_socket->server_control_block); + if (close_error != ERR_OK) { + tcp_abort(server_socket->server_control_block); + } + server_socket->server_control_block = nullptr; + } + + if (priority < 255) { priority++; + } - return ERR_OK; - } else - return ERROR; + return ERR_OK; } err_t ServerSocket::receive_callback( @@ -251,7 +412,13 @@ err_t ServerSocket::receive_callback( struct pbuf* packet_buffer, err_t error ) { - ServerSocket* server_socket = (ServerSocket*)arg; + ServerSocket* server_socket = static_cast(arg); + if (server_socket == nullptr) { + if (packet_buffer != nullptr) { + pbuf_free(packet_buffer); + } + return ERR_VAL; + } server_socket->client_control_block = client_control_block; if (packet_buffer == nullptr) { // FIN has been received @@ -263,39 +430,40 @@ err_t ServerSocket::receive_callback( if (packet_buffer != nullptr) { pbuf_free(packet_buffer); } - return error; - } else if (server_socket->state == ACCEPTED) { + // Keep the socket alive on transient lwIP receive errors. + return ERR_OK; + } + if (server_socket->state == ACCEPTED) { server_socket->rx_packet_buffer.push(packet_buffer); server_socket->process_data(); return ERR_OK; } - else if (server_socket->state == CLOSING) { // Socket is already closed - while (not server_socket->rx_packet_buffer.empty()) { - pbuf_free(server_socket->rx_packet_buffer.front()); - server_socket->rx_packet_buffer.pop(); - } - server_socket->rx_packet_buffer = {}; - pbuf_free(packet_buffer); - return ERR_OK; - } + pbuf_free(packet_buffer); return ERR_OK; } void ServerSocket::error_callback(void* arg, err_t error) { - ServerSocket* server_socket = (ServerSocket*)arg; - server_socket->close(); - ErrorHandler("Socket error: %d. Socket closed", error); + (void)error; + ServerSocket* server_socket = static_cast(arg); + if (server_socket == nullptr) { + return; + } + + // tcp_err is called once the pcb is already freed by lwIP. + server_socket->client_control_block = nullptr; + server_socket->state = CLOSED; + server_socket->clear_packet_queues(); + server_socket->rx_stream_buffer.clear(); } err_t ServerSocket::poll_callback(void* arg, struct tcp_pcb* client_control_block) { - ServerSocket* server_socket = (ServerSocket*)arg; - server_socket->client_control_block = client_control_block; - + ServerSocket* server_socket = static_cast(arg); if (server_socket == nullptr) { // Polling non existing pcb, fatal error tcp_abort(client_control_block); return ERR_ABRT; } + server_socket->client_control_block = client_control_block; if (!server_socket->tx_packet_buffer.empty()) { // TX FIFO is not empty server_socket->send(); @@ -313,7 +481,12 @@ err_t ServerSocket::poll_callback(void* arg, struct tcp_pcb* client_control_bloc } err_t ServerSocket::send_callback(void* arg, struct tcp_pcb* client_control_block, u16_t len) { - ServerSocket* server_socket = (ServerSocket*)arg; + (void)len; + ServerSocket* server_socket = static_cast(arg); + if (server_socket == nullptr) { + tcp_abort(client_control_block); + return ERR_ABRT; + } server_socket->client_control_block = client_control_block; if (!server_socket->tx_packet_buffer.empty()) { server_socket->send(); diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp index 1666cde5..bdb1d09f 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp @@ -10,32 +10,151 @@ unordered_map Socket::connecting_sockets = {}; +namespace { +constexpr size_t MAX_RX_STREAM_BUFFER_BYTES = 8192; + +void process_order_stream( + OrderProtocol* protocol, + IPV4& remote_ip, + vector& stream_buffer +) { + if (stream_buffer.empty()) { + return; + } + size_t parsed_bytes = 0; + + while (stream_buffer.size() - parsed_bytes >= sizeof(uint16_t)) { + uint8_t* packet_ptr = stream_buffer.data() + parsed_bytes; + uint16_t order_id = Packet::get_id(packet_ptr); + auto order_it = Order::orders.find(order_id); + if (order_it == Order::orders.end()) { + parsed_bytes += 1; + continue; + } + + const size_t order_size = order_it->second->get_size(); + if (order_size < sizeof(uint16_t)) { + parsed_bytes += 1; + continue; + } + if (stream_buffer.size() - parsed_bytes < order_size) { + break; + } + + order_it->second->store_ip_order(remote_ip.string_address); + Order::process_data(protocol, packet_ptr); + parsed_bytes += order_size; + } + + if (parsed_bytes > 0) { + stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + parsed_bytes); + } + + if (stream_buffer.size() > MAX_RX_STREAM_BUFFER_BYTES) { + const size_t trim_count = stream_buffer.size() - MAX_RX_STREAM_BUFFER_BYTES; + stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + trim_count); + } +} +} // namespace + Socket::Socket() = default; Socket::Socket(Socket&& other) - : connection_control_block(move(other.connection_control_block)), - remote_port(move(remote_port)), state(other.state) { - EthernetNode remote_node(other.remote_ip, other.remote_port); - connecting_sockets[remote_node] = this; + : connection_control_block(other.connection_control_block), + socket_control_block(other.socket_control_block), + tx_packet_buffer(move(other.tx_packet_buffer)), rx_packet_buffer(move(other.rx_packet_buffer)), + rx_stream_buffer(move(other.rx_stream_buffer)), + local_ip(move(other.local_ip)), local_port(other.local_port), remote_ip(move(other.remote_ip)), + remote_port(other.remote_port), state(other.state), + pending_connection_reset(other.pending_connection_reset), + connect_poll_ticks(other.connect_poll_ticks), use_keep_alives(other.use_keep_alives), + keepalive_config(other.keepalive_config) { + other.connection_control_block = nullptr; + other.socket_control_block = nullptr; + other.state = INACTIVE; + other.pending_connection_reset = false; + other.connect_poll_ticks = 0; + + if (connection_control_block != nullptr) { + tcp_arg(connection_control_block, this); + } + if (socket_control_block != nullptr) { + tcp_arg(socket_control_block, this); + } + + EthernetNode remote_node(remote_ip, remote_port); + auto it = connecting_sockets.find(remote_node); + if (it != connecting_sockets.end() && it->second == &other) { + it->second = this; + } + + if (std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this) == + OrderProtocol::sockets.end()) { + OrderProtocol::sockets.push_back(this); + } } void Socket::operator=(Socket&& other) { - connection_control_block = move(other.connection_control_block); - remote_port = move(other.remote_port); + if (this == &other) { + return; + } + close(); + + connection_control_block = other.connection_control_block; + socket_control_block = other.socket_control_block; + tx_packet_buffer = move(other.tx_packet_buffer); + rx_packet_buffer = move(other.rx_packet_buffer); + rx_stream_buffer = move(other.rx_stream_buffer); + local_ip = move(other.local_ip); + local_port = other.local_port; + remote_ip = move(other.remote_ip); + remote_port = other.remote_port; state = other.state; - EthernetNode remote_node(other.remote_ip, other.remote_port); - connecting_sockets[remote_node] = this; + pending_connection_reset = other.pending_connection_reset; + connect_poll_ticks = other.connect_poll_ticks; + use_keep_alives = other.use_keep_alives; + keepalive_config = other.keepalive_config; + + other.connection_control_block = nullptr; + other.socket_control_block = nullptr; + other.state = INACTIVE; + other.pending_connection_reset = false; + other.connect_poll_ticks = 0; + + if (connection_control_block != nullptr) { + tcp_arg(connection_control_block, this); + } + if (socket_control_block != nullptr) { + tcp_arg(socket_control_block, this); + } + + EthernetNode remote_node(remote_ip, remote_port); + auto it = connecting_sockets.find(remote_node); + if (it != connecting_sockets.end() && it->second == &other) { + it->second = this; + } + if (std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this) == - OrderProtocol::sockets.end()) + OrderProtocol::sockets.end()) { OrderProtocol::sockets.push_back(this); + } } Socket::~Socket() { + close(); + + for (auto it = connecting_sockets.begin(); it != connecting_sockets.end();) { + if (it->second == this) { + it = connecting_sockets.erase(it); + } else { + ++it; + } + } + auto it = std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this); - if (it == OrderProtocol::sockets.end()) - return; - else + if (it != OrderProtocol::sockets.end()) { OrderProtocol::sockets.erase(it); + } } Socket::Socket( @@ -54,18 +173,44 @@ Socket::Socket( state = INACTIVE; tx_packet_buffer = {}; rx_packet_buffer = {}; + rx_stream_buffer = {}; + rx_stream_buffer.reserve(MAX_RX_STREAM_BUFFER_BYTES); EthernetNode remote_node(remote_ip, remote_port); connection_control_block = tcp_new(); - tcp_bind(connection_control_block, &local_ip.address, local_port); + if (connection_control_block == nullptr) { + ErrorHandler("Cannot allocate TCP control block"); + return; + } + ip_set_option(connection_control_block, SOF_REUSEADDR); + + err_t bind_error = tcp_bind(connection_control_block, &local_ip.address, local_port); + if (bind_error != ERR_OK) { + tcp_abort(connection_control_block); + connection_control_block = nullptr; + ErrorHandler("Cannot bind TCP socket. Error code: %d", bind_error); + return; + } tcp_nagle_disable(connection_control_block); tcp_arg(connection_control_block, this); tcp_poll(connection_control_block, connection_poll_callback, 1); tcp_err(connection_control_block, connection_error_callback); connecting_sockets[remote_node] = this; - tcp_connect(connection_control_block, &remote_ip.address, remote_port, connect_callback); - OrderProtocol::sockets.push_back(this); + err_t connect_error = + tcp_connect(connection_control_block, &remote_ip.address, remote_port, connect_callback); + if (connect_error != ERR_OK && connect_error != ERR_ISCONN) { + connecting_sockets.erase(remote_node); + tcp_abort(connection_control_block); + connection_control_block = nullptr; + ErrorHandler("Cannot connect TCP socket. Error code: %d", connect_error); + return; + } + + if (std::find(OrderProtocol::sockets.begin(), OrderProtocol::sockets.end(), this) == + OrderProtocol::sockets.end()) { + OrderProtocol::sockets.push_back(this); + } } Socket::Socket( @@ -86,113 +231,205 @@ Socket::Socket( Socket::Socket(EthernetNode local_node, EthernetNode remote_node) : Socket(local_node.ip, local_node.port, remote_node.ip, remote_node.port) {} -void Socket::close() { - tcp_arg(socket_control_block, nullptr); - tcp_sent(socket_control_block, nullptr); - tcp_recv(socket_control_block, nullptr); - tcp_err(socket_control_block, nullptr); - tcp_poll(socket_control_block, nullptr, 0); - +void Socket::clear_packet_queues() { while (!tx_packet_buffer.empty()) { - pbuf_free(tx_packet_buffer.front()); + pbuf* packet = tx_packet_buffer.front(); tx_packet_buffer.pop(); + if (packet != nullptr) { + pbuf_free(packet); + } } while (!rx_packet_buffer.empty()) { - pbuf_free(rx_packet_buffer.front()); + pbuf* packet = rx_packet_buffer.front(); rx_packet_buffer.pop(); + if (packet != nullptr) { + pbuf_free(packet); + } } +} - tcp_close(socket_control_block); +void Socket::close() { + if (socket_control_block != nullptr) { + tcp_arg(socket_control_block, nullptr); + tcp_sent(socket_control_block, nullptr); + tcp_recv(socket_control_block, nullptr); + tcp_err(socket_control_block, nullptr); + tcp_poll(socket_control_block, nullptr, 0); + + err_t close_error = tcp_close(socket_control_block); + if (close_error != ERR_OK) { + tcp_abort(socket_control_block); + } + } + if (connection_control_block != nullptr && connection_control_block != socket_control_block) { + tcp_arg(connection_control_block, nullptr); + tcp_poll(connection_control_block, nullptr, 0); + tcp_err(connection_control_block, nullptr); + tcp_abort(connection_control_block); + } + socket_control_block = nullptr; + connection_control_block = nullptr; + clear_packet_queues(); + rx_stream_buffer.clear(); + connect_poll_ticks = 0; + pending_connection_reset = false; state = INACTIVE; + + EthernetNode remote_node(remote_ip, remote_port); + auto it = connecting_sockets.find(remote_node); + if (it != connecting_sockets.end() && it->second == this) { + connecting_sockets.erase(it); + } } void Socket::reconnect() { + if (state == CONNECTED) { + return; + } + if (pending_connection_reset || connection_control_block == nullptr) { + reset(); + return; + } + + if (connection_control_block->state == SYN_SENT) { + return; + } + EthernetNode remote_node(remote_ip, remote_port); - if (!connecting_sockets.contains(remote_node)) { - connecting_sockets[remote_node] = this; + connecting_sockets[remote_node] = this; + + err_t connect_error = + tcp_connect(connection_control_block, &remote_ip.address, remote_port, connect_callback); + if (connect_error != ERR_OK && connect_error != ERR_ISCONN) { + pending_connection_reset = true; } - tcp_connect(connection_control_block, &remote_ip.address, remote_port, connect_callback); } void Socket::reset() { - EthernetNode remote_node(remote_ip, remote_port); - if (!connecting_sockets.contains(remote_node)) { - connecting_sockets[remote_node] = this; - } state = INACTIVE; - tcp_abort(connection_control_block); + socket_control_block = nullptr; + clear_packet_queues(); + rx_stream_buffer.clear(); + connect_poll_ticks = 0; + + if (connection_control_block != nullptr) { + tcp_arg(connection_control_block, nullptr); + tcp_poll(connection_control_block, nullptr, 0); + tcp_err(connection_control_block, nullptr); + tcp_abort(connection_control_block); + } connection_control_block = tcp_new(); + if (connection_control_block == nullptr) { + pending_connection_reset = true; + return; + } + ip_set_option(connection_control_block, SOF_REUSEADDR); - tcp_bind(connection_control_block, &local_ip.address, local_port); + err_t bind_error = tcp_bind(connection_control_block, &local_ip.address, local_port); + if (bind_error != ERR_OK) { + tcp_abort(connection_control_block); + connection_control_block = nullptr; + pending_connection_reset = true; + return; + } tcp_nagle_disable(connection_control_block); tcp_arg(connection_control_block, this); tcp_poll(connection_control_block, connection_poll_callback, 1); tcp_err(connection_control_block, connection_error_callback); - tcp_connect(connection_control_block, &remote_ip.address, remote_port, connect_callback); + EthernetNode remote_node(remote_ip, remote_port); + connecting_sockets[remote_node] = this; + + err_t connect_error = + tcp_connect(connection_control_block, &remote_ip.address, remote_port, connect_callback); + pending_connection_reset = !(connect_error == ERR_OK || connect_error == ERR_ISCONN); } void Socket::send() { - pbuf* temporal_packet_buffer; - err_t error = ERR_OK; - while (error == ERR_OK && !tx_packet_buffer.empty() && - tx_packet_buffer.front()->len <= tcp_sndbuf(socket_control_block)) { - temporal_packet_buffer = tx_packet_buffer.front(); - error = tcp_write( + if (state != CONNECTED || socket_control_block == nullptr) { + return; + } + + while (!tx_packet_buffer.empty()) { + pbuf* temporal_packet_buffer = tx_packet_buffer.front(); + if (temporal_packet_buffer == nullptr) { + tx_packet_buffer.pop(); + continue; + } + + if (temporal_packet_buffer->tot_len > tcp_sndbuf(socket_control_block)) { + break; + } + + err_t error = tcp_write( socket_control_block, temporal_packet_buffer->payload, - temporal_packet_buffer->len, + temporal_packet_buffer->tot_len, TCP_WRITE_FLAG_COPY ); if (error == ERR_OK) { tx_packet_buffer.pop(); - tcp_output(socket_control_block); - memp_free_pool(memp_pools[PBUF_POOL_MEMORY_DESC_POSITION], temporal_packet_buffer); + pbuf_free(temporal_packet_buffer); + } else if (error == ERR_MEM) { + break; } else { - if (error == ERR_MEM) { - close(); - ErrorHandler("Too many unacked messages on client socket, disconnecting..."); - } else { - ErrorHandler("Cannot write to client socket. Error code: %d", error); - } + state = CLOSING; + break; } } + if (socket_control_block != nullptr) { + tcp_output(socket_control_block); + } } void Socket::process_data() { while (!rx_packet_buffer.empty()) { - struct pbuf* packet = rx_packet_buffer.front(); + pbuf* packet = rx_packet_buffer.front(); rx_packet_buffer.pop(); - uint8_t* new_data = (uint8_t*)(packet->payload); - tcp_recved(socket_control_block, packet->tot_len); - uint16_t id = Packet::get_id(new_data); - if (Order::orders.contains(id)) { - Order::orders[id]->store_ip_order(remote_ip.string_address); - Order::process_data(this, new_data); + if (packet == nullptr) { + continue; } + if (socket_control_block != nullptr) { + tcp_recved(socket_control_block, packet->tot_len); + } + + const size_t previous_size = rx_stream_buffer.size(); + const size_t append_size = packet->tot_len; + rx_stream_buffer.resize(previous_size + append_size); + if (pbuf_copy_partial(packet, rx_stream_buffer.data() + previous_size, packet->tot_len, 0) == + static_cast(packet->tot_len)) { + process_order_stream(this, remote_ip, rx_stream_buffer); + } else { + rx_stream_buffer.resize(previous_size); + } pbuf_free(packet); } } bool Socket::add_order_to_queue(Order& order) { - if (state == Socket::SocketState::CONNECTED) { + if (state != Socket::SocketState::CONNECTED || socket_control_block == nullptr) { return false; } - struct memp* next_memory_pointer_in_packet_buffer_pool = - (*(memp_pools[PBUF_POOL_MEMORY_DESC_POSITION]->tab))->next; - if (next_memory_pointer_in_packet_buffer_pool == nullptr) { - memp_free_pool( - memp_pools[PBUF_POOL_MEMORY_DESC_POSITION], - next_memory_pointer_in_packet_buffer_pool - ); + + if (tx_packet_buffer.size() >= MAX_TX_QUEUE_DEPTH) { return false; } - uint8_t* order_buffer = order.build(); + const size_t order_size = order.get_size(); + if (order_size == 0 || order_size > tcp_sndbuf(socket_control_block)) { + return false; + } - struct pbuf* packet = pbuf_alloc(PBUF_TRANSPORT, order.get_size(), PBUF_POOL); - pbuf_take(packet, order_buffer, order.get_size()); + uint8_t* order_buffer = order.build(); + pbuf* packet = pbuf_alloc(PBUF_TRANSPORT, order_size, PBUF_RAM); + if (packet == nullptr) { + return false; + } + if (pbuf_take(packet, order_buffer, order_size) != ERR_OK) { + pbuf_free(packet); + return false; + } Socket::tx_packet_buffer.push(packet); return true; } @@ -200,29 +437,43 @@ bool Socket::add_order_to_queue(Order& order) { bool Socket::is_connected() { return state == Socket::SocketState::CONNECTED; } err_t Socket::connect_callback(void* arg, struct tcp_pcb* client_control_block, err_t error) { - IPV4 remote_ip; - remote_ip.address = client_control_block->remote_ip; - EthernetNode remote_node(remote_ip, client_control_block->remote_port); - - if (connecting_sockets.contains(remote_node)) { - Socket* socket = connecting_sockets[remote_node]; - connecting_sockets.erase(remote_node); + Socket* socket = static_cast(arg); + if (socket == nullptr || client_control_block == nullptr) { + return ERR_VAL; + } + if (error != ERR_OK) { + socket->state = INACTIVE; + socket->pending_connection_reset = true; + return error; + } - socket->socket_control_block = client_control_block; - socket->state = CONNECTED; + socket->connection_control_block = client_control_block; + socket->socket_control_block = client_control_block; + socket->rx_stream_buffer.clear(); + socket->state = CONNECTED; + socket->pending_connection_reset = false; + socket->connect_poll_ticks = 0; + + tcp_nagle_disable(client_control_block); + tcp_arg(client_control_block, socket); + tcp_recv(client_control_block, receive_callback); + tcp_poll(client_control_block, poll_callback, 1); + tcp_sent(client_control_block, send_callback); + tcp_err(client_control_block, error_callback); + if (socket->use_keep_alives) { + config_keepalive(client_control_block, socket); + } - tcp_nagle_disable(client_control_block); - tcp_arg(client_control_block, socket); - tcp_recv(client_control_block, receive_callback); - tcp_poll(client_control_block, poll_callback, 0); - tcp_sent(client_control_block, send_callback); - tcp_err(client_control_block, error_callback); - if (socket->use_keep_alives) - config_keepalive(client_control_block, socket); + EthernetNode remote_node(socket->remote_ip, socket->remote_port); + auto it = connecting_sockets.find(remote_node); + if (it != connecting_sockets.end() && it->second == socket) { + connecting_sockets.erase(it); + } - return ERR_OK; - } else - return ERROR; + if (!socket->tx_packet_buffer.empty()) { + socket->send(); + } + return ERR_OK; } err_t Socket::receive_callback( @@ -231,7 +482,13 @@ err_t Socket::receive_callback( struct pbuf* packet_buffer, err_t error ) { - Socket* socket = (Socket*)arg; + Socket* socket = static_cast(arg); + if (socket == nullptr) { + if (packet_buffer != nullptr) { + pbuf_free(packet_buffer); + } + return ERR_VAL; + } socket->socket_control_block = client_control_block; if (packet_buffer == nullptr) { // FIN is received socket->state = CLOSING; @@ -241,47 +498,45 @@ err_t Socket::receive_callback( if (packet_buffer != nullptr) { pbuf_free(packet_buffer); } - return error; - } else if (socket->state == CONNECTED) { + // Keep the socket alive on transient lwIP receive errors. + return ERR_OK; + } + if (socket->state == CONNECTED) { socket->rx_packet_buffer.push(packet_buffer); - tcp_recved(client_control_block, packet_buffer->tot_len); socket->process_data(); - pbuf_free(packet_buffer); - return ERR_OK; - } else { - tcp_recved(client_control_block, packet_buffer->tot_len); - socket->tx_packet_buffer = {}; - pbuf_free(packet_buffer); return ERR_OK; } + pbuf_free(packet_buffer); + return ERR_OK; } err_t Socket::poll_callback(void* arg, struct tcp_pcb* client_control_block) { - Socket* socket = (Socket*)arg; - socket->socket_control_block = client_control_block; - if (socket != nullptr) { - while (not socket->tx_packet_buffer.empty()) { - socket->send(); - } - if (socket->state == CLOSING) { - socket->close(); - } else if (socket->state == INACTIVE) { - tcp_connect( - socket->connection_control_block, - &socket->remote_ip.address, - socket->remote_port, - connect_callback - ); - } - return ERR_OK; - } else { + Socket* socket = static_cast(arg); + if (socket == nullptr) { tcp_abort(client_control_block); return ERR_ABRT; } + socket->socket_control_block = client_control_block; + + if (!socket->tx_packet_buffer.empty()) { + socket->send(); + } + if (!socket->rx_packet_buffer.empty()) { + socket->process_data(); + } + if (socket->state == CLOSING) { + socket->close(); + } + return ERR_OK; } err_t Socket::send_callback(void* arg, struct tcp_pcb* client_control_block, uint16_t length) { - Socket* socket = (Socket*)arg; + (void)length; + Socket* socket = static_cast(arg); + if (socket == nullptr) { + tcp_abort(client_control_block); + return ERR_ABRT; + } socket->socket_control_block = client_control_block; if (not socket->tx_packet_buffer.empty()) { socket->send(); @@ -292,35 +547,61 @@ err_t Socket::send_callback(void* arg, struct tcp_pcb* client_control_block, uin } void Socket::error_callback(void* arg, err_t error) { - Socket* socket = (Socket*)arg; - socket->close(); - ErrorHandler( - "Client socket error: %d. Socket closed, remote ip: %s", - error, - socket->remote_ip.string_address.c_str() - ); + (void)error; + Socket* socket = static_cast(arg); + if (socket == nullptr) { + return; + } + + // tcp_err is called once the pcb is already freed by lwIP. + socket->socket_control_block = nullptr; + socket->connection_control_block = nullptr; + socket->state = INACTIVE; + socket->pending_connection_reset = true; + socket->connect_poll_ticks = 0; + socket->clear_packet_queues(); + socket->rx_stream_buffer.clear(); } void Socket::connection_error_callback(void* arg, err_t error) { - if (error == ERR_RST) { - Socket* socket = (Socket*)arg; - - socket->pending_connection_reset = true; - return; - } else if (error == ERR_ABRT) { + (void)error; + Socket* socket = static_cast(arg); + if (socket == nullptr) { return; } - ErrorHandler("Connection socket error: %d. Couldn t start client socket ", error); + + socket->connection_control_block = nullptr; + socket->socket_control_block = nullptr; + socket->state = INACTIVE; + socket->pending_connection_reset = true; + socket->connect_poll_ticks = 0; + socket->rx_stream_buffer.clear(); } err_t Socket::connection_poll_callback(void* arg, struct tcp_pcb* connection_control_block) { - Socket* socket = (Socket*)arg; + Socket* socket = static_cast(arg); + if (socket == nullptr) { + if (connection_control_block != nullptr) { + tcp_abort(connection_control_block); + } + return ERR_ABRT; + } + + socket->connection_control_block = connection_control_block; if (socket->pending_connection_reset) { - socket->reset(); socket->pending_connection_reset = false; + socket->reset(); return ERR_ABRT; - } else if (socket->connection_control_block->state == SYN_SENT) { - socket->pending_connection_reset = true; + } + + if (socket->state != CONNECTED && connection_control_block != nullptr && + connection_control_block->state == SYN_SENT) { + socket->connect_poll_ticks++; + if (socket->connect_poll_ticks >= 20) { + socket->pending_connection_reset = true; + } + } else { + socket->connect_poll_ticks = 0; } return ERR_OK; } From 12e3e1c67e4bbac6a0b8de7ac15fb6047a62910a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sat, 28 Feb 2026 12:45:50 +0100 Subject: [PATCH 03/18] fix(server): own and recycle ServerSocket instances safely --- .../Communication/Server/Server.hpp | 2 +- .../Communication/Server/Server.cpp | 89 ++++++++++++++----- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/Inc/ST-LIB_LOW/Communication/Server/Server.hpp b/Inc/ST-LIB_LOW/Communication/Server/Server.hpp index e1cf1e7a..1cfc07d5 100644 --- a/Inc/ST-LIB_LOW/Communication/Server/Server.hpp +++ b/Inc/ST-LIB_LOW/Communication/Server/Server.hpp @@ -36,7 +36,7 @@ class Server { Server(IPV4 local_ip, uint32_t local_port); ~Server(); void update(); - void broadcast_order(Order& order); + bool broadcast_order(Order& order); void close_all(); uint32_t connections_count(); diff --git a/Src/ST-LIB_LOW/Communication/Server/Server.cpp b/Src/ST-LIB_LOW/Communication/Server/Server.cpp index 800c54af..8d58d439 100644 --- a/Src/ST-LIB_LOW/Communication/Server/Server.cpp +++ b/Src/ST-LIB_LOW/Communication/Server/Server.cpp @@ -18,57 +18,98 @@ Server::Server(IPV4 local_ip, uint32_t local_port) } Server::~Server() { - open_connection->~ServerSocket(); + if (open_connection != nullptr) { + delete open_connection; + open_connection = nullptr; + } - for (ServerSocket* s : running_connections) { - s->~ServerSocket(); + for (uint16_t s = 0; s < running_connections_count; s++) { + if (running_connections[s] != nullptr) { + delete running_connections[s]; + running_connections[s] = nullptr; + } } - running_servers.erase(find(running_servers.begin(), running_servers.end(), this)); + auto it = find(running_servers.begin(), running_servers.end(), this); + if (it != running_servers.end()) { + running_servers.erase(it); + } } void Server::update() { - if (open_connection->is_connected()) { - running_connections[running_connections_count] = open_connection; - running_connections_count++; + if (status == CLOSED) { + return; + } + + if (open_connection == nullptr) { + open_connection = new ServerSocket(local_ip, local_port); + } else if (!open_connection->is_connected() && !open_connection->is_listening()) { + // Recover from startup/driver races where listener was not created successfully. + delete open_connection; open_connection = new ServerSocket(local_ip, local_port); } + if (open_connection->is_connected()) { + if (running_connections_count < MAX_CONNECTIONS_TCP_SERVER) { + running_connections[running_connections_count] = open_connection; + running_connections_count++; + open_connection = new ServerSocket(local_ip, local_port); + } else { + // Capacity reached: close the new connection and keep current sessions untouched. + open_connection->close(); + delete open_connection; + open_connection = new ServerSocket(local_ip, local_port); + } + } + + uint16_t write_index = 0; for (uint16_t s = 0; s < running_connections_count; s++) { - if (status == RUNNING && !running_connections[s]->is_connected()) { - ErrorHandler( - "ip %s disconnected, going to FAULT", - running_connections[s]->remote_ip.string_address.c_str() - ); - status = CLOSING; - break; + ServerSocket* current = running_connections[s]; + if (current != nullptr && current->is_connected()) { + running_connections[write_index++] = current; + } else { + if (current != nullptr) { + current->close(); + delete current; + } } } + for (uint16_t s = write_index; s < running_connections_count; s++) { + running_connections[s] = nullptr; + } + running_connections_count = write_index; if (status == CLOSING) { close_all(); } } -void Server::broadcast_order(Order& order) { +bool Server::broadcast_order(Order& order) { + bool sent = false; for (uint16_t s = 0; s < running_connections_count; s++) { - if (!running_connections[s]->send_order(order)) { - ErrorHandler( - "Couldn t put Order %d into buffer of ip's %s ServerSocket, buffer may be full or " - "the ServerSocket may be ill formed", - order.get_id(), - running_connections[s]->remote_ip.string_address.c_str() - ); + ServerSocket* connection = running_connections[s]; + if (connection != nullptr) { + sent = connection->send_order(order) || sent; } } + return sent; } void Server::close_all() { for (uint16_t s = 0; s < running_connections_count; s++) { + if (running_connections[s] == nullptr) { + continue; + } running_connections[s]->close(); + delete running_connections[s]; running_connections[s] = nullptr; } running_connections_count = 0; + if (open_connection != nullptr) { + open_connection->close(); + delete open_connection; + open_connection = nullptr; + } status = CLOSED; } @@ -76,7 +117,9 @@ uint32_t Server::connections_count() { return running_connections_count; } void Server::update_servers() { for (Server* s : running_servers) { - s->update(); + if (s != nullptr) { + s->update(); + } } } From 20d9940b134bc7cbf531561fba2e1c91e576021e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sat, 28 Feb 2026 12:45:55 +0100 Subject: [PATCH 04/18] fix(udp): harden DatagramSocket lifecycle and pbuf parsing --- .../Ethernet/LWIP/UDP/DatagramSocket.hpp | 2 +- .../Ethernet/LWIP/UDP/DatagramSocket.cpp | 77 +++++++++++++++---- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/Inc/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.hpp b/Inc/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.hpp index f0eb78a7..0b8dc0f0 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.hpp @@ -8,7 +8,7 @@ class DatagramSocket { public: - struct udp_pcb* udp_control_block; + struct udp_pcb* udp_control_block = nullptr; IPV4 local_ip; uint32_t local_port; diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp index eac56249..8cdd25e5 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp @@ -13,9 +13,12 @@ DatagramSocket::DatagramSocket() = default; DatagramSocket::DatagramSocket(DatagramSocket&& other) - : udp_control_block(move(other.udp_control_block)), local_ip(move(other.local_ip)), - local_port(move(other.local_port)), remote_ip(move(other.remote_ip)), - remote_port(move(remote_port)) {} + : udp_control_block(other.udp_control_block), local_ip(move(other.local_ip)), + local_port(other.local_port), remote_ip(move(other.remote_ip)), remote_port(other.remote_port), + is_disconnected(other.is_disconnected) { + other.udp_control_block = nullptr; + other.is_disconnected = true; +} DatagramSocket::DatagramSocket( IPV4 local_ip, @@ -29,15 +32,20 @@ DatagramSocket::DatagramSocket( return; } udp_control_block = udp_new(); + if (udp_control_block == nullptr) { + ErrorHandler("Cannot allocate UDP control block"); + return; + } err_t error = udp_bind(udp_control_block, &local_ip.address, local_port); if (error == ERR_OK) { - udp_recv(udp_control_block, receive_callback, nullptr); + udp_recv(udp_control_block, receive_callback, this); udp_connect(udp_control_block, &remote_ip.address, remote_port); is_disconnected = false; Ethernet::update(); } else { udp_remove(udp_control_block); + udp_control_block = nullptr; is_disconnected = true; ErrorHandler("Error binding UDP socket"); } @@ -52,34 +60,61 @@ DatagramSocket::~DatagramSocket() { } void DatagramSocket::operator=(DatagramSocket&& other) { - udp_control_block = move(other.udp_control_block); + if (this == &other) { + return; + } + close(); + + udp_control_block = other.udp_control_block; local_ip = move(other.local_ip); - local_port = move(other.local_port); - remote_ip = other.remote_ip; + local_port = other.local_port; + remote_ip = move(other.remote_ip); remote_port = other.remote_port; + is_disconnected = other.is_disconnected; + + other.udp_control_block = nullptr; other.is_disconnected = true; } void DatagramSocket::reconnect() { - udp_disconnect(udp_control_block); + if (not Ethernet::is_running) { + is_disconnected = true; + return; + } + + if (udp_control_block != nullptr) { + udp_disconnect(udp_control_block); + udp_remove(udp_control_block); + udp_control_block = nullptr; + } + + udp_control_block = udp_new(); + if (udp_control_block == nullptr) { + is_disconnected = true; + return; + } + is_disconnected = true; err_t error = udp_bind(udp_control_block, &local_ip.address, local_port); if (error == ERR_OK) { - udp_recv(udp_control_block, receive_callback, nullptr); + udp_recv(udp_control_block, receive_callback, this); udp_connect(udp_control_block, &remote_ip.address, remote_port); is_disconnected = false; Ethernet::update(); } else { udp_remove(udp_control_block); + udp_control_block = nullptr; is_disconnected = true; - ErrorHandler("Error binding UDP socket"); } } void DatagramSocket::close() { - udp_disconnect(udp_control_block); - udp_remove(udp_control_block); + if (udp_control_block != nullptr) { + udp_disconnect(udp_control_block); + udp_remove(udp_control_block); + udp_control_block = nullptr; + } is_disconnected = true; } @@ -90,8 +125,22 @@ void DatagramSocket::receive_callback( const ip_addr_t* remote_address, u16_t port ) { - uint8_t* received_data = (uint8_t*)packet_buffer->payload; - Packet::parse_data(received_data); + (void)args; + (void)udp_control_block; + (void)remote_address; + (void)port; + + if (packet_buffer == nullptr) { + return; + } + + if (packet_buffer->tot_len >= sizeof(uint16_t)) { + vector data(packet_buffer->tot_len); + if (pbuf_copy_partial(packet_buffer, data.data(), packet_buffer->tot_len, 0) == + static_cast(packet_buffer->tot_len)) { + Packet::parse_data(data.data()); + } + } pbuf_free(packet_buffer); } From ef1f2561da687b84581e5f5b61cea1edc5f5d9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sat, 28 Feb 2026 12:46:02 +0100 Subject: [PATCH 05/18] fix(lwip): align ICMP checksum settings with hw offload --- LWIP/Target/lwipopts.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/LWIP/Target/lwipopts.h b/LWIP/Target/lwipopts.h index e319dd6a..39010d39 100644 --- a/LWIP/Target/lwipopts.h +++ b/LWIP/Target/lwipopts.h @@ -93,6 +93,8 @@ #define CHECKSUM_GEN_UDP 0 /*----- Value in opt.h for CHECKSUM_GEN_TCP: 1 -----*/ #define CHECKSUM_GEN_TCP 0 +/*----- Value in opt.h for CHECKSUM_GEN_ICMP: 1 -----*/ +#define CHECKSUM_GEN_ICMP 0 /*----- Value in opt.h for CHECKSUM_GEN_ICMP6: 1 -----*/ #define CHECKSUM_GEN_ICMP6 0 /*----- Value in opt.h for CHECKSUM_CHECK_IP: 1 -----*/ @@ -101,6 +103,8 @@ #define CHECKSUM_CHECK_UDP 0 /*----- Value in opt.h for CHECKSUM_CHECK_TCP: 1 -----*/ #define CHECKSUM_CHECK_TCP 0 +/*----- Value in opt.h for CHECKSUM_CHECK_ICMP: 1 -----*/ +#define CHECKSUM_CHECK_ICMP 0 /*----- Value in opt.h for CHECKSUM_CHECK_ICMP6: 1 -----*/ #define CHECKSUM_CHECK_ICMP6 0 /*-----------------------------------------------------------------------------*/ From 7a5a1c35faa32f8c5ae78b7859fd81fdca996efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sat, 28 Feb 2026 14:39:01 +0100 Subject: [PATCH 06/18] fix(net): harden socket teardown and unify TCP order parsing --- .../LWIP/TCP/TcpOrderStreamParser.hpp | 53 ++++++++++++++++++ .../Ethernet/LWIP/TCP/ServerSocket.cpp | 54 ++----------------- .../Ethernet/LWIP/TCP/Socket.cpp | 52 ++---------------- .../Ethernet/LWIP/UDP/DatagramSocket.cpp | 5 +- .../Communication/Server/Server.cpp | 4 -- 5 files changed, 63 insertions(+), 105 deletions(-) create mode 100644 Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/TcpOrderStreamParser.hpp diff --git a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/TcpOrderStreamParser.hpp b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/TcpOrderStreamParser.hpp new file mode 100644 index 00000000..bb5e0808 --- /dev/null +++ b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/TcpOrderStreamParser.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "HALAL/Models/IPV4/IPV4.hpp" +#include "HALAL/Models/Packets/Order.hpp" + +#ifdef HAL_ETH_MODULE_ENABLED + +namespace TcpOrderStreamParser { + +inline constexpr size_t MAX_RX_STREAM_BUFFER_BYTES = 8192; + +inline void process(OrderProtocol* protocol, IPV4& remote_ip, vector& stream_buffer) { + if (stream_buffer.empty()) { + return; + } + size_t parsed_bytes = 0; + + while (stream_buffer.size() - parsed_bytes >= sizeof(uint16_t)) { + uint8_t* packet_ptr = stream_buffer.data() + parsed_bytes; + uint16_t order_id = Packet::get_id(packet_ptr); + auto order_it = Order::orders.find(order_id); + if (order_it == Order::orders.end()) { + parsed_bytes += 1; + continue; + } + + const size_t order_size = order_it->second->get_size(); + if (order_size < sizeof(uint16_t)) { + parsed_bytes += 1; + continue; + } + if (stream_buffer.size() - parsed_bytes < order_size) { + break; + } + + order_it->second->store_ip_order(remote_ip.string_address); + Order::process_data(protocol, packet_ptr); + parsed_bytes += order_size; + } + + if (parsed_bytes > 0) { + stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + parsed_bytes); + } + + if (stream_buffer.size() > MAX_RX_STREAM_BUFFER_BYTES) { + const size_t trim_count = stream_buffer.size() - MAX_RX_STREAM_BUFFER_BYTES; + stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + trim_count); + } +} + +} // namespace TcpOrderStreamParser + +#endif diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp index 6d5b4f45..5ada0be3 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp @@ -6,6 +6,7 @@ */ #ifdef STLIB_ETH #include "HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp" +#include "HALAL/Services/Communication/Ethernet/LWIP/TCP/TcpOrderStreamParser.hpp" #include "ErrorHandler/ErrorHandler.hpp" #include "lwip/priv/tcp_priv.h" #ifdef HAL_ETH_MODULE_ENABLED @@ -13,53 +14,6 @@ uint8_t ServerSocket::priority = 1; unordered_map ServerSocket::listening_sockets = {}; -namespace { -constexpr size_t MAX_RX_STREAM_BUFFER_BYTES = 8192; - -void process_order_stream( - OrderProtocol* protocol, - IPV4& remote_ip, - vector& stream_buffer -) { - if (stream_buffer.empty()) { - return; - } - size_t parsed_bytes = 0; - - while (stream_buffer.size() - parsed_bytes >= sizeof(uint16_t)) { - uint8_t* packet_ptr = stream_buffer.data() + parsed_bytes; - uint16_t order_id = Packet::get_id(packet_ptr); - auto order_it = Order::orders.find(order_id); - if (order_it == Order::orders.end()) { - parsed_bytes += 1; - continue; - } - - const size_t order_size = order_it->second->get_size(); - if (order_size < sizeof(uint16_t)) { - parsed_bytes += 1; - continue; - } - if (stream_buffer.size() - parsed_bytes < order_size) { - break; - } - - order_it->second->store_ip_order(remote_ip.string_address); - Order::process_data(protocol, packet_ptr); - parsed_bytes += order_size; - } - - if (parsed_bytes > 0) { - stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + parsed_bytes); - } - - if (stream_buffer.size() > MAX_RX_STREAM_BUFFER_BYTES) { - const size_t trim_count = stream_buffer.size() - MAX_RX_STREAM_BUFFER_BYTES; - stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + trim_count); - } -} -} // namespace - ServerSocket::ServerSocket() = default; ServerSocket::ServerSocket(IPV4 local_ip, uint32_t local_port) @@ -71,7 +25,7 @@ ServerSocket::ServerSocket(IPV4 local_ip, uint32_t local_port) tx_packet_buffer = {}; rx_packet_buffer = {}; rx_stream_buffer = {}; - rx_stream_buffer.reserve(MAX_RX_STREAM_BUFFER_BYTES); + rx_stream_buffer.reserve(TcpOrderStreamParser::MAX_RX_STREAM_BUFFER_BYTES); state = INACTIVE; server_control_block = tcp_new(); if (server_control_block == nullptr) { @@ -273,7 +227,7 @@ void ServerSocket::process_data() { rx_stream_buffer.resize(previous_size + append_size); if (pbuf_copy_partial(packet, rx_stream_buffer.data() + previous_size, packet->tot_len, 0) == static_cast(packet->tot_len)) { - process_order_stream(this, remote_ip, rx_stream_buffer); + TcpOrderStreamParser::process(this, remote_ip, rx_stream_buffer); } else { rx_stream_buffer.resize(previous_size); } @@ -376,7 +330,7 @@ err_t ServerSocket::accept_callback( server_socket->remote_ip = IPV4(incomming_control_block->remote_ip); server_socket->rx_packet_buffer = {}; server_socket->rx_stream_buffer.clear(); - server_socket->rx_stream_buffer.reserve(MAX_RX_STREAM_BUFFER_BYTES); + server_socket->rx_stream_buffer.reserve(TcpOrderStreamParser::MAX_RX_STREAM_BUFFER_BYTES); tcp_setprio(incomming_control_block, priority); tcp_nagle_disable(incomming_control_block); diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp index bdb1d09f..61759739 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp @@ -5,58 +5,12 @@ * Author: stefa */ #include "HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp" +#include "HALAL/Services/Communication/Ethernet/LWIP/TCP/TcpOrderStreamParser.hpp" #include "ErrorHandler/ErrorHandler.hpp" #ifdef HAL_ETH_MODULE_ENABLED unordered_map Socket::connecting_sockets = {}; -namespace { -constexpr size_t MAX_RX_STREAM_BUFFER_BYTES = 8192; - -void process_order_stream( - OrderProtocol* protocol, - IPV4& remote_ip, - vector& stream_buffer -) { - if (stream_buffer.empty()) { - return; - } - size_t parsed_bytes = 0; - - while (stream_buffer.size() - parsed_bytes >= sizeof(uint16_t)) { - uint8_t* packet_ptr = stream_buffer.data() + parsed_bytes; - uint16_t order_id = Packet::get_id(packet_ptr); - auto order_it = Order::orders.find(order_id); - if (order_it == Order::orders.end()) { - parsed_bytes += 1; - continue; - } - - const size_t order_size = order_it->second->get_size(); - if (order_size < sizeof(uint16_t)) { - parsed_bytes += 1; - continue; - } - if (stream_buffer.size() - parsed_bytes < order_size) { - break; - } - - order_it->second->store_ip_order(remote_ip.string_address); - Order::process_data(protocol, packet_ptr); - parsed_bytes += order_size; - } - - if (parsed_bytes > 0) { - stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + parsed_bytes); - } - - if (stream_buffer.size() > MAX_RX_STREAM_BUFFER_BYTES) { - const size_t trim_count = stream_buffer.size() - MAX_RX_STREAM_BUFFER_BYTES; - stream_buffer.erase(stream_buffer.begin(), stream_buffer.begin() + trim_count); - } -} -} // namespace - Socket::Socket() = default; Socket::Socket(Socket&& other) @@ -174,7 +128,7 @@ Socket::Socket( tx_packet_buffer = {}; rx_packet_buffer = {}; rx_stream_buffer = {}; - rx_stream_buffer.reserve(MAX_RX_STREAM_BUFFER_BYTES); + rx_stream_buffer.reserve(TcpOrderStreamParser::MAX_RX_STREAM_BUFFER_BYTES); EthernetNode remote_node(remote_ip, remote_port); connection_control_block = tcp_new(); @@ -399,7 +353,7 @@ void Socket::process_data() { rx_stream_buffer.resize(previous_size + append_size); if (pbuf_copy_partial(packet, rx_stream_buffer.data() + previous_size, packet->tot_len, 0) == static_cast(packet->tot_len)) { - process_order_stream(this, remote_ip, rx_stream_buffer); + TcpOrderStreamParser::process(this, remote_ip, rx_stream_buffer); } else { rx_stream_buffer.resize(previous_size); } diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp index 8cdd25e5..11cb5621 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp @@ -39,7 +39,7 @@ DatagramSocket::DatagramSocket( err_t error = udp_bind(udp_control_block, &local_ip.address, local_port); if (error == ERR_OK) { - udp_recv(udp_control_block, receive_callback, this); + udp_recv(udp_control_block, receive_callback, nullptr); udp_connect(udp_control_block, &remote_ip.address, remote_port); is_disconnected = false; Ethernet::update(); @@ -98,7 +98,7 @@ void DatagramSocket::reconnect() { err_t error = udp_bind(udp_control_block, &local_ip.address, local_port); if (error == ERR_OK) { - udp_recv(udp_control_block, receive_callback, this); + udp_recv(udp_control_block, receive_callback, nullptr); udp_connect(udp_control_block, &remote_ip.address, remote_port); is_disconnected = false; Ethernet::update(); @@ -106,6 +106,7 @@ void DatagramSocket::reconnect() { udp_remove(udp_control_block); udp_control_block = nullptr; is_disconnected = true; + ErrorHandler("Error binding UDP socket"); } } diff --git a/Src/ST-LIB_LOW/Communication/Server/Server.cpp b/Src/ST-LIB_LOW/Communication/Server/Server.cpp index 8d58d439..0a54b2eb 100644 --- a/Src/ST-LIB_LOW/Communication/Server/Server.cpp +++ b/Src/ST-LIB_LOW/Communication/Server/Server.cpp @@ -56,7 +56,6 @@ void Server::update() { open_connection = new ServerSocket(local_ip, local_port); } else { // Capacity reached: close the new connection and keep current sessions untouched. - open_connection->close(); delete open_connection; open_connection = new ServerSocket(local_ip, local_port); } @@ -69,7 +68,6 @@ void Server::update() { running_connections[write_index++] = current; } else { if (current != nullptr) { - current->close(); delete current; } } @@ -100,13 +98,11 @@ void Server::close_all() { if (running_connections[s] == nullptr) { continue; } - running_connections[s]->close(); delete running_connections[s]; running_connections[s] = nullptr; } running_connections_count = 0; if (open_connection != nullptr) { - open_connection->close(); delete open_connection; open_connection = nullptr; } From 40d92ac54ed05e8000dc9ec0b251e27a65e14783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sat, 28 Feb 2026 14:41:02 +0100 Subject: [PATCH 07/18] Applied formatter --- .../Ethernet/LWIP/TCP/ServerSocket.cpp | 20 ++++++++++++------- .../Ethernet/LWIP/TCP/Socket.cpp | 15 +++++++++----- .../Ethernet/LWIP/UDP/DatagramSocket.cpp | 4 ++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp index 5ada0be3..95bface8 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp @@ -72,11 +72,13 @@ ServerSocket::ServerSocket( } ServerSocket::ServerSocket(ServerSocket&& other) - : local_ip(move(other.local_ip)), local_port(other.local_port), remote_ip(move(other.remote_ip)), - state(other.state), keepalive_config(other.keepalive_config), - server_control_block(other.server_control_block), - tx_packet_buffer(move(other.tx_packet_buffer)), rx_packet_buffer(move(other.rx_packet_buffer)), - rx_stream_buffer(move(other.rx_stream_buffer)), client_control_block(other.client_control_block) { + : local_ip(move(other.local_ip)), local_port(other.local_port), + remote_ip(move(other.remote_ip)), state(other.state), + keepalive_config(other.keepalive_config), server_control_block(other.server_control_block), + tx_packet_buffer(move(other.tx_packet_buffer)), + rx_packet_buffer(move(other.rx_packet_buffer)), + rx_stream_buffer(move(other.rx_stream_buffer)), + client_control_block(other.client_control_block) { other.server_control_block = nullptr; other.client_control_block = nullptr; other.state = INACTIVE; @@ -225,8 +227,12 @@ void ServerSocket::process_data() { const size_t previous_size = rx_stream_buffer.size(); const size_t append_size = packet->tot_len; rx_stream_buffer.resize(previous_size + append_size); - if (pbuf_copy_partial(packet, rx_stream_buffer.data() + previous_size, packet->tot_len, 0) == - static_cast(packet->tot_len)) { + if (pbuf_copy_partial( + packet, + rx_stream_buffer.data() + previous_size, + packet->tot_len, + 0 + ) == static_cast(packet->tot_len)) { TcpOrderStreamParser::process(this, remote_ip, rx_stream_buffer); } else { rx_stream_buffer.resize(previous_size); diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp index 61759739..20fb259e 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp @@ -16,9 +16,10 @@ Socket::Socket() = default; Socket::Socket(Socket&& other) : connection_control_block(other.connection_control_block), socket_control_block(other.socket_control_block), - tx_packet_buffer(move(other.tx_packet_buffer)), rx_packet_buffer(move(other.rx_packet_buffer)), - rx_stream_buffer(move(other.rx_stream_buffer)), - local_ip(move(other.local_ip)), local_port(other.local_port), remote_ip(move(other.remote_ip)), + tx_packet_buffer(move(other.tx_packet_buffer)), + rx_packet_buffer(move(other.rx_packet_buffer)), + rx_stream_buffer(move(other.rx_stream_buffer)), local_ip(move(other.local_ip)), + local_port(other.local_port), remote_ip(move(other.remote_ip)), remote_port(other.remote_port), state(other.state), pending_connection_reset(other.pending_connection_reset), connect_poll_ticks(other.connect_poll_ticks), use_keep_alives(other.use_keep_alives), @@ -351,8 +352,12 @@ void Socket::process_data() { const size_t previous_size = rx_stream_buffer.size(); const size_t append_size = packet->tot_len; rx_stream_buffer.resize(previous_size + append_size); - if (pbuf_copy_partial(packet, rx_stream_buffer.data() + previous_size, packet->tot_len, 0) == - static_cast(packet->tot_len)) { + if (pbuf_copy_partial( + packet, + rx_stream_buffer.data() + previous_size, + packet->tot_len, + 0 + ) == static_cast(packet->tot_len)) { TcpOrderStreamParser::process(this, remote_ip, rx_stream_buffer); } else { rx_stream_buffer.resize(previous_size); diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp index 11cb5621..4266a39e 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp @@ -14,8 +14,8 @@ DatagramSocket::DatagramSocket() = default; DatagramSocket::DatagramSocket(DatagramSocket&& other) : udp_control_block(other.udp_control_block), local_ip(move(other.local_ip)), - local_port(other.local_port), remote_ip(move(other.remote_ip)), remote_port(other.remote_port), - is_disconnected(other.is_disconnected) { + local_port(other.local_port), remote_ip(move(other.remote_ip)), + remote_port(other.remote_port), is_disconnected(other.is_disconnected) { other.udp_control_block = nullptr; other.is_disconnected = true; } From 566fc892941c59adeef6c5ab0d3c26406ca3392c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sun, 1 Mar 2026 12:54:28 +0100 Subject: [PATCH 08/18] perf(net): benchmark-driven TCP TX fast path --- .../Ethernet/LWIP/TCP/ServerSocket.hpp | 16 +----- .../Ethernet/LWIP/TCP/Socket.hpp | 17 +----- .../Ethernet/LWIP/UDP/DatagramSocket.hpp | 7 ++- .../Ethernet/LWIP/TCP/ServerSocket.cpp | 54 +++++++++++++++++- .../Ethernet/LWIP/TCP/Socket.cpp | 55 ++++++++++++++++++- .../Ethernet/LWIP/UDP/DatagramSocket.cpp | 12 ++-- .../Services/Communication/UART/UART.cpp | 20 ++++++- Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp | 5 +- 8 files changed, 141 insertions(+), 45 deletions(-) diff --git a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp index 70f656cb..cf6be593 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.hpp @@ -110,20 +110,7 @@ class ServerSocket : public OrderProtocol { * message * @return true if the data was sent successfully, false otherwise */ - bool send_order(Order& order) override { - if (state != ACCEPTED || client_control_block == nullptr) { - return false; - } - if (!add_order_to_queue(order)) { - // One opportunistic flush avoids false negatives when TX queue is momentarily full. - send(); - if (!add_order_to_queue(order)) { - return false; - } - } - send(); - return true; - } + bool send_order(Order& order) override; /** * @brief sends all the binary data saved in the tx_packet_buffer to the @@ -157,6 +144,7 @@ class ServerSocket : public OrderProtocol { vector rx_stream_buffer; struct tcp_pcb* client_control_block = nullptr; void clear_packet_queues(); + bool try_send_immediately(Order& order); /** * @brief process the data received by the client orders. It is meant to be diff --git a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp index 14fa9e4e..df18693f 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.hpp @@ -24,6 +24,7 @@ class Socket : public OrderProtocol { vector rx_stream_buffer; void clear_packet_queues(); void process_data(); + bool try_send_immediately(Order& order); static err_t connect_callback(void* arg, struct tcp_pcb* client_control_block, err_t error); static err_t receive_callback( void* arg, @@ -99,21 +100,7 @@ class Socket : public OrderProtocol { * @return true if the data was sent successfully, false otherwise */ - bool send_order(Order& order) override { - if (state != CONNECTED || socket_control_block == nullptr) { - reconnect(); - return false; - } - if (!add_order_to_queue(order)) { - // One opportunistic flush avoids false negatives when TX queue is momentarily full. - send(); - if (!add_order_to_queue(order)) { - return false; - } - } - send(); - return true; - } + bool send_order(Order& order) override; void send(); bool is_connected(); }; diff --git a/Inc/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.hpp b/Inc/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.hpp index 0b8dc0f0..9473bcb0 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.hpp @@ -36,16 +36,17 @@ class DatagramSocket { if (is_disconnected || udp_control_block == nullptr) { return false; } + const size_t packet_size = packet.get_size(); uint8_t* packet_buffer = packet.build(); - if (packet_buffer == nullptr || packet.size == 0) { + if (packet_buffer == nullptr || packet_size == 0) { return false; } - struct pbuf* tx_buffer = pbuf_alloc(PBUF_TRANSPORT, packet.size, PBUF_RAM); + struct pbuf* tx_buffer = pbuf_alloc(PBUF_TRANSPORT, packet_size, PBUF_RAM); if (tx_buffer == nullptr) { return false; } - if (pbuf_take(tx_buffer, packet_buffer, packet.size) != ERR_OK) { + if (pbuf_take(tx_buffer, packet_buffer, packet_size) != ERR_OK) { pbuf_free(tx_buffer); return false; } diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp index 95bface8..750f7da6 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp @@ -250,13 +250,13 @@ bool ServerSocket::add_order_to_queue(Order& order) { } const size_t order_size = order.get_size(); - if (order_size == 0 || order_size > tcp_sndbuf(client_control_block)) { + if (order_size == 0 || order_size > TCP_SND_BUF) { return false; } uint8_t* order_buffer = order.build(); - pbuf* packet = pbuf_alloc(PBUF_TRANSPORT, order_size, PBUF_RAM); + pbuf* packet = pbuf_alloc(PBUF_RAW, order_size, PBUF_RAM); if (packet == nullptr) { return false; } @@ -268,6 +268,56 @@ bool ServerSocket::add_order_to_queue(Order& order) { return true; } +bool ServerSocket::try_send_immediately(Order& order) { + if (state != ACCEPTED || client_control_block == nullptr || !tx_packet_buffer.empty()) { + return false; + } + + const size_t order_size = order.get_size(); + if (order_size == 0 || order_size > TCP_SND_BUF || order_size > tcp_sndbuf(client_control_block)) { + return false; + } + + uint8_t* order_buffer = order.build(); + if (order_buffer == nullptr) { + return false; + } + + err_t error = tcp_write(client_control_block, order_buffer, order_size, TCP_WRITE_FLAG_COPY); + if (error == ERR_OK) { + if (client_control_block != nullptr) { + tcp_output(client_control_block); + } + return true; + } + if (error == ERR_MEM) { + return false; + } + + state = CLOSING; + return false; +} + +bool ServerSocket::send_order(Order& order) { + if (state != ACCEPTED || client_control_block == nullptr) { + return false; + } + + if (try_send_immediately(order)) { + return true; + } + + if (!add_order_to_queue(order)) { + // One opportunistic flush avoids false negatives when TX queue is momentarily full. + send(); + if (!add_order_to_queue(order)) { + return false; + } + } + send(); + return true; +} + void ServerSocket::send() { if (client_control_block == nullptr || state != ACCEPTED) { return; diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp index 20fb259e..b3a50cab 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp @@ -366,6 +366,57 @@ void Socket::process_data() { } } +bool Socket::try_send_immediately(Order& order) { + if (state != CONNECTED || socket_control_block == nullptr || !tx_packet_buffer.empty()) { + return false; + } + + const size_t order_size = order.get_size(); + if (order_size == 0 || order_size > TCP_SND_BUF || order_size > tcp_sndbuf(socket_control_block)) { + return false; + } + + uint8_t* order_buffer = order.build(); + if (order_buffer == nullptr) { + return false; + } + + err_t error = tcp_write(socket_control_block, order_buffer, order_size, TCP_WRITE_FLAG_COPY); + if (error == ERR_OK) { + if (socket_control_block != nullptr) { + tcp_output(socket_control_block); + } + return true; + } + if (error == ERR_MEM) { + return false; + } + + state = CLOSING; + return false; +} + +bool Socket::send_order(Order& order) { + if (state != CONNECTED || socket_control_block == nullptr) { + reconnect(); + return false; + } + + if (try_send_immediately(order)) { + return true; + } + + if (!add_order_to_queue(order)) { + // One opportunistic flush avoids false negatives when TX queue is momentarily full. + send(); + if (!add_order_to_queue(order)) { + return false; + } + } + send(); + return true; +} + bool Socket::add_order_to_queue(Order& order) { if (state != Socket::SocketState::CONNECTED || socket_control_block == nullptr) { return false; @@ -376,12 +427,12 @@ bool Socket::add_order_to_queue(Order& order) { } const size_t order_size = order.get_size(); - if (order_size == 0 || order_size > tcp_sndbuf(socket_control_block)) { + if (order_size == 0 || order_size > TCP_SND_BUF) { return false; } uint8_t* order_buffer = order.build(); - pbuf* packet = pbuf_alloc(PBUF_TRANSPORT, order_size, PBUF_RAM); + pbuf* packet = pbuf_alloc(PBUF_RAW, order_size, PBUF_RAM); if (packet == nullptr) { return false; } diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp index 4266a39e..a433d480 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/UDP/DatagramSocket.cpp @@ -136,10 +136,14 @@ void DatagramSocket::receive_callback( } if (packet_buffer->tot_len >= sizeof(uint16_t)) { - vector data(packet_buffer->tot_len); - if (pbuf_copy_partial(packet_buffer, data.data(), packet_buffer->tot_len, 0) == - static_cast(packet_buffer->tot_len)) { - Packet::parse_data(data.data()); + if (packet_buffer->len == packet_buffer->tot_len && packet_buffer->payload != nullptr) { + Packet::parse_data(static_cast(packet_buffer->payload)); + } else { + vector data(packet_buffer->tot_len); + if (pbuf_copy_partial(packet_buffer, data.data(), packet_buffer->tot_len, 0) == + static_cast(packet_buffer->tot_len)) { + Packet::parse_data(data.data()); + } } } diff --git a/Src/HALAL/Services/Communication/UART/UART.cpp b/Src/HALAL/Services/Communication/UART/UART.cpp index ba613e89..c820b875 100644 --- a/Src/HALAL/Services/Communication/UART/UART.cpp +++ b/Src/HALAL/Services/Communication/UART/UART.cpp @@ -152,6 +152,10 @@ bool UART::set_up_printf(UART::Peripheral& uart) { return false; } + if (!UART::available_uarts.contains(uart)) { + return false; + } + UART::printf_uart = UART::inscribe(uart); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); @@ -162,12 +166,11 @@ bool UART::set_up_printf(UART::Peripheral& uart) { } void UART::print_by_uart(char* ptr, int len) { - if (!UART::printf_ready) { + if (!UART::printf_ready || ptr == nullptr || len <= 0) { return; } - vector data(ptr, ptr + len); - + span data(reinterpret_cast(ptr), static_cast(len)); UART::transmit_polling(UART::printf_uart, data); } @@ -220,6 +223,17 @@ UART_HandleTypeDef* UART::get_handle(uint8_t id) { return registered_uart[id]->h extern "C" { #endif +int __io_putchar(int ch) { + if (!UART::printf_ready) { + return ch; + } + + UART::transmit_polling(UART::printf_uart, static_cast(ch)); + return ch; +} + +int __io_getchar(void) { return -1; } + #ifdef __cplusplus } #endif diff --git a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp index 9b7b8282..83420ad4 100644 --- a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp +++ b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp @@ -57,7 +57,7 @@ void ErrorHandlerModel::ErrorHandlerTrigger(string format, ...) { } void ErrorHandlerModel::ErrorHandlerUpdate() { - if (!ErrorHandlerModel::error_triggered) { + if (!ErrorHandlerModel::error_triggered || !ErrorHandlerModel::error_to_communicate) { return; } @@ -66,6 +66,7 @@ void ErrorHandlerModel::ErrorHandlerUpdate() { return; } - // printf("Error: %s%s", ErrorHandlerModel::description.c_str(), endl); + printf("Error: %s%s", ErrorHandlerModel::description.c_str(), endl); + ErrorHandlerModel::error_to_communicate = false; #endif } From 3b1ae1d4e48aa9577b88b17857f78fb3d88e0ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 18:45:58 +0100 Subject: [PATCH 09/18] feat(error): decouple transport from legacy update --- .../Communication/Ethernet/NewEthernet.hpp | 2 + Inc/HALAL/Services/Time/RTC.hpp | 1 + Inc/ST-LIB.hpp | 4 + Src/HALAL/Services/Time/RTC.cpp | 33 +++- Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp | 184 +++++++++++++++++- 5 files changed, 216 insertions(+), 8 deletions(-) diff --git a/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp b/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp index dbc3b9d6..a9842abe 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp @@ -10,6 +10,7 @@ #include "HALAL/Services/Communication/Ethernet/LWIP/Ethernet.hpp" #include "HALAL/Services/Communication/Ethernet/LWIP/EthernetHelper.hpp" #include "HALAL/Services/Communication/Ethernet/LWIP/EthernetNode.hpp" +#include "ErrorHandler/ErrorHandler.hpp" extern "C" { #include "ethernetif.h" #include "lwip.h" @@ -224,6 +225,7 @@ struct EthernetDomain { void update() { ethernetif_input(&gnetif); sys_check_timeouts(); + ErrorHandlerModel::ErrorHandlerUpdate(); if (HAL_GetTick() - EthernetLinkTimer >= 100) { EthernetLinkTimer = HAL_GetTick(); diff --git a/Inc/HALAL/Services/Time/RTC.hpp b/Inc/HALAL/Services/Time/RTC.hpp index f912fc09..9a8dea03 100644 --- a/Inc/HALAL/Services/Time/RTC.hpp +++ b/Inc/HALAL/Services/Time/RTC.hpp @@ -21,6 +21,7 @@ class Global_RTC { public: static RTCData global_RTC; static void start_rtc(); + static bool ensure_started(); static void update_rtc_data(); static RTCData get_rtc_timestamp(); static void set_rtc_data( diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 726e225f..20828d92 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -186,6 +186,10 @@ template struct Board { HALconfig::system_clock(); HALconfig::peripheral_clock(); +#ifdef HAL_RTC_MODULE_ENABLED + (void)Global_RTC::ensure_started(); +#endif + MPUDomain::Init::init(); GPIODomain::Init::init(cfg.gpio_cfgs); TimerDomain::Init::init(cfg.tim_cfgs); diff --git a/Src/HALAL/Services/Time/RTC.cpp b/Src/HALAL/Services/Time/RTC.cpp index a5700a97..ed8961d5 100644 --- a/Src/HALAL/Services/Time/RTC.cpp +++ b/Src/HALAL/Services/Time/RTC.cpp @@ -3,7 +3,17 @@ RTC_HandleTypeDef hrtc; RTCData Global_RTC::global_RTC; +namespace { +bool rtc_started = false; +bool rtc_start_in_progress = false; +} + void Global_RTC::start_rtc() { + if (rtc_started || rtc_start_in_progress) { + return; + } + + rtc_start_in_progress = true; RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef sDate = {0}; @@ -17,7 +27,9 @@ void Global_RTC::start_rtc() { hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; if (HAL_RTC_Init(&hrtc) != HAL_OK) { + rtc_start_in_progress = false; ErrorHandler("Error on RTC Init"); + return; } sTime.Hours = 0x0; sTime.Minutes = 0x0; @@ -26,7 +38,9 @@ void Global_RTC::start_rtc() { sTime.StoreOperation = RTC_STOREOPERATION_RESET; if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) { + rtc_start_in_progress = false; ErrorHandler("Error while setting time at RTC start"); + return; } sDate.WeekDay = RTC_WEEKDAY_MONDAY; @@ -35,8 +49,20 @@ void Global_RTC::start_rtc() { sDate.Year = 23; if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) { + rtc_start_in_progress = false; ErrorHandler("Error while setting date at RTC start"); + return; + } + + rtc_started = true; + rtc_start_in_progress = false; +} + +bool Global_RTC::ensure_started() { + if (!rtc_started && !rtc_start_in_progress) { + start_rtc(); } + return rtc_started; } RTCData Global_RTC::get_rtc_timestamp() { @@ -83,4 +109,9 @@ void Global_RTC::set_rtc_data( ErrorHandler("Error on writing Date on the RTC"); } } -void Global_RTC::update_rtc_data() { global_RTC = get_rtc_timestamp(); } +void Global_RTC::update_rtc_data() { + if (!ensure_started()) { + return; + } + global_RTC = get_rtc_timestamp(); +} diff --git a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp index 83420ad4..20696d3e 100644 --- a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp +++ b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp @@ -7,6 +7,164 @@ #include "ErrorHandler/ErrorHandler.hpp" #include "HALAL/Services/Time/Scheduler.hpp" +#include "HALAL/Models/Packets/Order.hpp" +#include "HALAL/Services/Time/RTC.hpp" + +namespace { + +constexpr uint16_t ERROR_HANDLER_TCP_ORDER_ID = 1555; +constexpr uint8_t ERROR_HANDLER_BOUNDARY_TYPE_ID = 5; + +bool error_sent_via_tcp = false; +bool error_sent_via_uart = false; +bool tcp_delivery_required = false; +bool uart_delivery_required = false; + +uint8_t error_handler_padding = 0; +uint8_t error_handler_boundary_type = ERROR_HANDLER_BOUNDARY_TYPE_ID; +string error_handler_name = "error_handler"; +string error_handler_message = "Error-No-Description-Found"; +uint16_t error_handler_counter = 0; +uint8_t error_handler_second = 0; +uint8_t error_handler_minute = 0; +uint8_t error_handler_hour = 0; +uint8_t error_handler_day = 0; +uint8_t error_handler_month = 0; +uint16_t error_handler_year = 0; + +class ErrorHandlerOrder final : public Order { +public: + void set_callback(void (*)(void)) override {} + + void process() override {} + + void parse(OrderProtocol* socket, uint8_t* data) override { + (void)socket; + (void)data; + } + + uint8_t* build() override { + const size_t total_size = get_size(); + if (buffer.size() != total_size) { + buffer.resize(total_size); + } + + uint8_t* data = buffer.data(); + append(data, &id, sizeof(id)); + append(data, &error_handler_padding, sizeof(error_handler_padding)); + append(data, &error_handler_boundary_type, sizeof(error_handler_boundary_type)); + append(data, error_handler_name.c_str(), error_handler_name.size() + 1); + append(data, error_handler_message.c_str(), error_handler_message.size() + 1); + append(data, &error_handler_counter, sizeof(error_handler_counter)); + append(data, &error_handler_second, sizeof(error_handler_second)); + append(data, &error_handler_minute, sizeof(error_handler_minute)); + append(data, &error_handler_hour, sizeof(error_handler_hour)); + append(data, &error_handler_day, sizeof(error_handler_day)); + append(data, &error_handler_month, sizeof(error_handler_month)); + append(data, &error_handler_year, sizeof(error_handler_year)); + return buffer.data(); + } + + size_t get_size() override { + size = sizeof(id) + sizeof(error_handler_padding) + sizeof(error_handler_boundary_type) + + error_handler_name.size() + 1 + error_handler_message.size() + 1 + + sizeof(error_handler_counter) + sizeof(error_handler_second) + + sizeof(error_handler_minute) + sizeof(error_handler_hour) + + sizeof(error_handler_day) + sizeof(error_handler_month) + sizeof(error_handler_year); + return size; + } + + uint16_t get_id() override { return id; } + + void set_pointer(size_t index, void* pointer) override { + (void)index; + (void)pointer; + } + +private: + static void append(uint8_t*& dst, const void* src, size_t count) { + memcpy(dst, src, count); + dst += count; + } + + static constexpr uint16_t id = ERROR_HANDLER_TCP_ORDER_ID; + vector buffer{}; +}; + +ErrorHandlerOrder error_handler_order; + +void refresh_error_handler_transport_state(const string& description) { + error_handler_message = description; + +#ifdef HAL_RTC_MODULE_ENABLED + if (Global_RTC::ensure_started()) { + Global_RTC::update_rtc_data(); + error_handler_counter = Global_RTC::global_RTC.counter; + error_handler_second = Global_RTC::global_RTC.second; + error_handler_minute = Global_RTC::global_RTC.minute; + error_handler_hour = Global_RTC::global_RTC.hour; + error_handler_day = Global_RTC::global_RTC.day; + error_handler_month = Global_RTC::global_RTC.month; + error_handler_year = Global_RTC::global_RTC.year; + return; + } +#endif + + error_handler_counter = 0; + error_handler_second = 0; + error_handler_minute = 0; + error_handler_hour = 0; + error_handler_day = 0; + error_handler_month = 0; + error_handler_year = 0; +} + +bool try_send_error_via_tcp(const string& description) { +#ifdef STLIB_ETH + if (!tcp_delivery_required || error_sent_via_tcp) { + return true; + } + + refresh_error_handler_transport_state(description); + + bool delivered = false; + for (OrderProtocol* socket : OrderProtocol::sockets) { + if (socket == nullptr) { + continue; + } + delivered = socket->send_order(error_handler_order) || delivered; + } + + if (delivered) { + error_sent_via_tcp = true; + } + return error_sent_via_tcp; +#else + (void)description; + return true; +#endif +} + +bool try_send_error_via_uart(const string& description) { +#ifdef HAL_UART_MODULE_ENABLED + if (!uart_delivery_required || error_sent_via_uart) { + return true; + } + + if (!UART::printf_ready) { + return false; + } + + printf("Error: %s%s", description.c_str(), endl); + error_sent_via_uart = true; + return true; +#else + (void)description; + return true; +#endif +} + +} // namespace string ErrorHandlerModel::description = "Error-No-Description-Found"; string ErrorHandlerModel::line = "Error-No-Line-Found"; @@ -30,6 +188,18 @@ void ErrorHandlerModel::ErrorHandlerTrigger(string format, ...) { ErrorHandlerModel::error_to_communicate = true; // This flag is marked so the ProtectionManager can know if it already consumed the // error in question. + error_sent_via_tcp = false; + error_sent_via_uart = false; +#ifdef STLIB_ETH + tcp_delivery_required = !OrderProtocol::sockets.empty(); +#else + tcp_delivery_required = false; +#endif +#ifdef HAL_UART_MODULE_ENABLED + uart_delivery_required = UART::printf_ready; +#else + uart_delivery_required = false; +#endif if (format.length() != 0) { description = ""; @@ -54,6 +224,8 @@ void ErrorHandlerModel::ErrorHandlerTrigger(string format, ...) { #ifdef HAL_TIM_MODULE_ENABLED description += " | TimeStamp: " + to_string(Scheduler::get_global_tick()); #endif + + ErrorHandlerModel::ErrorHandlerUpdate(); } void ErrorHandlerModel::ErrorHandlerUpdate() { @@ -61,12 +233,10 @@ void ErrorHandlerModel::ErrorHandlerUpdate() { return; } -#ifdef HAL_UART_MODULE_ENABLED - if (!UART::printf_ready) { - return; - } + const bool tcp_done = try_send_error_via_tcp(ErrorHandlerModel::description); + const bool uart_done = try_send_error_via_uart(ErrorHandlerModel::description); - printf("Error: %s%s", ErrorHandlerModel::description.c_str(), endl); - ErrorHandlerModel::error_to_communicate = false; -#endif + if (tcp_done && uart_done) { + ErrorHandlerModel::error_to_communicate = false; + } } From d88b7f0730fdd45f2ecfb85c1415b7c2704de499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 19:02:14 +0100 Subject: [PATCH 10/18] Modified warning to decouple from protections --- .../Communication/Ethernet/NewEthernet.hpp | 2 + .../Services/InfoWarning/InfoWarning.hpp | 1 + .../Services/InfoWarning/InfoWarning.cpp | 193 +++++++++++++++++- .../Protections/ProtectionManager.cpp | 1 + 4 files changed, 191 insertions(+), 6 deletions(-) diff --git a/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp b/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp index a9842abe..4c5424fa 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp @@ -11,6 +11,7 @@ #include "HALAL/Services/Communication/Ethernet/LWIP/EthernetHelper.hpp" #include "HALAL/Services/Communication/Ethernet/LWIP/EthernetNode.hpp" #include "ErrorHandler/ErrorHandler.hpp" +#include "HALAL/Services/InfoWarning/InfoWarning.hpp" extern "C" { #include "ethernetif.h" #include "lwip.h" @@ -226,6 +227,7 @@ struct EthernetDomain { ethernetif_input(&gnetif); sys_check_timeouts(); ErrorHandlerModel::ErrorHandlerUpdate(); + InfoWarning::InfoWarningUpdate(); if (HAL_GetTick() - EthernetLinkTimer >= 100) { EthernetLinkTimer = HAL_GetTick(); diff --git a/Inc/HALAL/Services/InfoWarning/InfoWarning.hpp b/Inc/HALAL/Services/InfoWarning/InfoWarning.hpp index 60bf49bf..ad00091f 100644 --- a/Inc/HALAL/Services/InfoWarning/InfoWarning.hpp +++ b/Inc/HALAL/Services/InfoWarning/InfoWarning.hpp @@ -18,6 +18,7 @@ class InfoWarning { public: static bool warning_triggered; + static bool warning_to_communicate; /** * @brief Triggers WarningHandler and format the warning message. The format works diff --git a/Src/HALAL/Services/InfoWarning/InfoWarning.cpp b/Src/HALAL/Services/InfoWarning/InfoWarning.cpp index fb3e8cbc..f3879235 100644 --- a/Src/HALAL/Services/InfoWarning/InfoWarning.cpp +++ b/Src/HALAL/Services/InfoWarning/InfoWarning.cpp @@ -6,13 +6,172 @@ */ #include "HALAL/Services/InfoWarning/InfoWarning.hpp" +#include "HALAL/Models/Packets/Order.hpp" +#include "HALAL/Services/Communication/UART/UART.hpp" +#include "HALAL/Services/Time/RTC.hpp" #include "HALAL/Services/Time/Scheduler.hpp" -string InfoWarning::description = "Error-No-Description-Found"; -string InfoWarning::line = "Error-No-Line-Found"; -string InfoWarning::func = "Error-No-Func-Found"; -string InfoWarning::file = "Error-No-File-Found"; +namespace { + +constexpr uint16_t INFO_WARNING_TCP_ORDER_ID = 2555; +constexpr uint8_t INFO_WARNING_BOUNDARY_TYPE_ID = 5; + +bool warning_sent_via_tcp = false; +bool warning_sent_via_uart = false; +bool tcp_delivery_required = false; +bool uart_delivery_required = false; + +uint8_t warning_padding = 0; +uint8_t warning_boundary_type = INFO_WARNING_BOUNDARY_TYPE_ID; +string warning_name = "info_warning"; +string warning_message = "Warning-No-Description-Found"; +uint16_t warning_counter = 0; +uint8_t warning_second = 0; +uint8_t warning_minute = 0; +uint8_t warning_hour = 0; +uint8_t warning_day = 0; +uint8_t warning_month = 0; +uint16_t warning_year = 0; + +class InfoWarningOrder final : public Order { +public: + void set_callback(void (*)(void)) override {} + + void process() override {} + + void parse(OrderProtocol* socket, uint8_t* data) override { + (void)socket; + (void)data; + } + + uint8_t* build() override { + const size_t total_size = get_size(); + if (buffer.size() != total_size) { + buffer.resize(total_size); + } + + uint8_t* data = buffer.data(); + append(data, &id, sizeof(id)); + append(data, &warning_padding, sizeof(warning_padding)); + append(data, &warning_boundary_type, sizeof(warning_boundary_type)); + append(data, warning_name.c_str(), warning_name.size() + 1); + append(data, warning_message.c_str(), warning_message.size() + 1); + append(data, &warning_counter, sizeof(warning_counter)); + append(data, &warning_second, sizeof(warning_second)); + append(data, &warning_minute, sizeof(warning_minute)); + append(data, &warning_hour, sizeof(warning_hour)); + append(data, &warning_day, sizeof(warning_day)); + append(data, &warning_month, sizeof(warning_month)); + append(data, &warning_year, sizeof(warning_year)); + return buffer.data(); + } + + size_t get_size() override { + size = sizeof(id) + sizeof(warning_padding) + sizeof(warning_boundary_type) + + warning_name.size() + 1 + warning_message.size() + 1 + sizeof(warning_counter) + + sizeof(warning_second) + sizeof(warning_minute) + sizeof(warning_hour) + + sizeof(warning_day) + sizeof(warning_month) + sizeof(warning_year); + return size; + } + + uint16_t get_id() override { return id; } + + void set_pointer(size_t index, void* pointer) override { + (void)index; + (void)pointer; + } + +private: + static void append(uint8_t*& dst, const void* src, size_t count) { + memcpy(dst, src, count); + dst += count; + } + + static constexpr uint16_t id = INFO_WARNING_TCP_ORDER_ID; + vector buffer{}; +}; + +InfoWarningOrder info_warning_order; + +void refresh_warning_transport_state(const string& description) { + warning_message = description; + +#ifdef HAL_RTC_MODULE_ENABLED + if (Global_RTC::ensure_started()) { + Global_RTC::update_rtc_data(); + warning_counter = Global_RTC::global_RTC.counter; + warning_second = Global_RTC::global_RTC.second; + warning_minute = Global_RTC::global_RTC.minute; + warning_hour = Global_RTC::global_RTC.hour; + warning_day = Global_RTC::global_RTC.day; + warning_month = Global_RTC::global_RTC.month; + warning_year = Global_RTC::global_RTC.year; + return; + } +#endif + + warning_counter = 0; + warning_second = 0; + warning_minute = 0; + warning_hour = 0; + warning_day = 0; + warning_month = 0; + warning_year = 0; +} + +bool try_send_warning_via_tcp(const string& description) { +#ifdef STLIB_ETH + if (!tcp_delivery_required || warning_sent_via_tcp) { + return true; + } + + refresh_warning_transport_state(description); + + bool delivered = false; + for (OrderProtocol* socket : OrderProtocol::sockets) { + if (socket == nullptr) { + continue; + } + delivered = socket->send_order(info_warning_order) || delivered; + } + + if (delivered) { + warning_sent_via_tcp = true; + } + return warning_sent_via_tcp; +#else + (void)description; + return true; +#endif +} + +bool try_send_warning_via_uart(const string& description) { +#ifdef HAL_UART_MODULE_ENABLED + if (!uart_delivery_required || warning_sent_via_uart) { + return true; + } + + if (!UART::printf_ready) { + return false; + } + + printf("Warning: %s%s", description.c_str(), endl); + warning_sent_via_uart = true; + return true; +#else + (void)description; + return true; +#endif +} + +} // namespace + +string InfoWarning::description = "Warning-No-Description-Found"; +string InfoWarning::line = "Warning-No-Line-Found"; +string InfoWarning::func = "Warning-No-Func-Found"; +string InfoWarning::file = "Warning-No-File-Found"; bool InfoWarning::warning_triggered = false; +bool InfoWarning::warning_to_communicate = false; void InfoWarning::SetMetaData(int line, const char* func, const char* file) { InfoWarning::line = to_string(line); @@ -26,6 +185,19 @@ void InfoWarning::InfoWarningTrigger(string format, ...) { } InfoWarning::warning_triggered = true; + InfoWarning::warning_to_communicate = true; + warning_sent_via_tcp = false; + warning_sent_via_uart = false; +#ifdef STLIB_ETH + tcp_delivery_required = !OrderProtocol::sockets.empty(); +#else + tcp_delivery_required = false; +#endif +#ifdef HAL_UART_MODULE_ENABLED + uart_delivery_required = UART::printf_ready; +#else + uart_delivery_required = false; +#endif if (format.length() != 0) { description = ""; @@ -49,11 +221,20 @@ void InfoWarning::InfoWarningTrigger(string format, ...) { #ifdef HAL_TIM_MODULE_ENABLED description += " | TimeStamp: " + to_string(Scheduler::get_global_tick()); #endif + + InfoWarning::InfoWarningUpdate(); } void InfoWarning::InfoWarningUpdate() { - if (!InfoWarning::warning_triggered) { + if (!InfoWarning::warning_to_communicate) { return; } - printf("Warning: %s\n", InfoWarning::description.c_str()); + + const bool tcp_done = try_send_warning_via_tcp(InfoWarning::description); + const bool uart_done = try_send_warning_via_uart(InfoWarning::description); + + if (tcp_done && uart_done) { + InfoWarning::warning_to_communicate = false; + InfoWarning::warning_triggered = false; + } } diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp index 45dfccf3..6fce4eaf 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp @@ -128,6 +128,7 @@ void ProtectionManager::notify(Protection& protection) { if (warning->boundary_type_id == INFO_WARNING - 2) { warning->update_warning_message(warning->get_warning_string()); InfoWarning::warning_triggered = false; + InfoWarning::warning_to_communicate = false; } socket->send_order(*warning->warn_message); } From 3720091721f13e5d796e024fdeda84ae1e097904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 19:19:36 +0100 Subject: [PATCH 11/18] Better timestamp in error and warning --- .../Services/InfoWarning/InfoWarning.cpp | 32 +++++++++++++++++-- Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp | 32 +++++++++++++++++-- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/Src/HALAL/Services/InfoWarning/InfoWarning.cpp b/Src/HALAL/Services/InfoWarning/InfoWarning.cpp index f3879235..782beb54 100644 --- a/Src/HALAL/Services/InfoWarning/InfoWarning.cpp +++ b/Src/HALAL/Services/InfoWarning/InfoWarning.cpp @@ -164,6 +164,34 @@ bool try_send_warning_via_uart(const string& description) { #endif } +void append_readable_timestamp(string& message) { +#ifdef HAL_RTC_MODULE_ENABLED + if (Global_RTC::ensure_started()) { + Global_RTC::update_rtc_data(); + const RTCData& timestamp = Global_RTC::global_RTC; + char buffer[80]{}; + snprintf( + buffer, + sizeof(buffer), + " | Timestamp: %04u-%02u-%02u %02u:%02u:%02u.%05u", + static_cast(timestamp.year), + static_cast(timestamp.month), + static_cast(timestamp.day), + static_cast(timestamp.hour), + static_cast(timestamp.minute), + static_cast(timestamp.second), + static_cast(timestamp.counter) + ); + message += buffer; + return; + } +#endif + +#ifdef HAL_TIM_MODULE_ENABLED + message += " | Timestamp(ns): " + to_string(Scheduler::get_global_tick()); +#endif +} + } // namespace string InfoWarning::description = "Warning-No-Description-Found"; @@ -218,9 +246,7 @@ void InfoWarning::InfoWarningTrigger(string format, ...) { description += string(buffer.get(), buffer.get() + size - 1) + " | Line: " + InfoWarning::line + " Function: '" + InfoWarning::func + "' File: " + InfoWarning::file; -#ifdef HAL_TIM_MODULE_ENABLED - description += " | TimeStamp: " + to_string(Scheduler::get_global_tick()); -#endif + append_readable_timestamp(description); InfoWarning::InfoWarningUpdate(); } diff --git a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp index 20696d3e..6617b8dc 100644 --- a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp +++ b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp @@ -164,6 +164,34 @@ bool try_send_error_via_uart(const string& description) { #endif } +void append_readable_timestamp(string& message) { +#ifdef HAL_RTC_MODULE_ENABLED + if (Global_RTC::ensure_started()) { + Global_RTC::update_rtc_data(); + const RTCData& timestamp = Global_RTC::global_RTC; + char buffer[80]{}; + snprintf( + buffer, + sizeof(buffer), + " | Timestamp: %04u-%02u-%02u %02u:%02u:%02u.%05u", + static_cast(timestamp.year), + static_cast(timestamp.month), + static_cast(timestamp.day), + static_cast(timestamp.hour), + static_cast(timestamp.minute), + static_cast(timestamp.second), + static_cast(timestamp.counter) + ); + message += buffer; + return; + } +#endif + +#ifdef HAL_TIM_MODULE_ENABLED + message += " | Timestamp(ns): " + to_string(Scheduler::get_global_tick()); +#endif +} + } // namespace string ErrorHandlerModel::description = "Error-No-Description-Found"; @@ -221,9 +249,7 @@ void ErrorHandlerModel::ErrorHandlerTrigger(string format, ...) { " | Line: " + ErrorHandlerModel::line + " Function: '" + ErrorHandlerModel::func + "' File: " + ErrorHandlerModel::file; -#ifdef HAL_TIM_MODULE_ENABLED - description += " | TimeStamp: " + to_string(Scheduler::get_global_tick()); -#endif + append_readable_timestamp(description); ErrorHandlerModel::ErrorHandlerUpdate(); } From 1dd7b68f5ac09dd5b48fd5ccb293e3253113dcde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 19:19:48 +0100 Subject: [PATCH 12/18] Protection manager more robust --- .../Protections/ProtectionManager.cpp | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp index 6fce4eaf..4b310c0f 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp @@ -16,7 +16,7 @@ void* error_handler; void* info_warning; void ProtectionManager::initialize() { - Global_RTC::start_rtc(); + Global_RTC::ensure_started(); for (Protection& protection : low_frequency_protections) { for (auto& boundary : protection.boundaries) { boundary->update_name(protection.get_name()); @@ -93,13 +93,16 @@ void ProtectionManager::check_protections() { void ProtectionManager::check_high_frequency_protections() { for (Protection& protection : high_frequency_protections) { + auto protection_status = protection.check_state(); + if (general_state_machine == nullptr) { ErrorHandler("Protection Manager does not have General State Machine " "Linked"); return; } - if (protection.fault_type == Protections::FAULT) { + if (protection.fault_type == Protections::FAULT && + protection_status == Protections::FAULT) { ProtectionManager::to_fault(); } Global_RTC::update_rtc_data(); @@ -114,21 +117,37 @@ void ProtectionManager::check_high_frequency_protections() { void ProtectionManager::warn(string message) { warning_notification.notify(message); } void ProtectionManager::notify(Protection& protection) { + const bool is_error_handler_fault = + protection.fault_protection != nullptr && + protection.fault_protection->boundary_type_id == ERROR_HANDLER; + const bool should_send_fault = + protection.fault_protection != nullptr && + (!is_error_handler_fault || ErrorHandlerModel::error_to_communicate); + bool error_handler_delivered = false; + bool info_warning_delivered = false; + for (OrderProtocol* socket : OrderProtocol::sockets) { - if (protection.fault_protection) { - if (protection.fault_protection->boundary_type_id == ERROR_HANDLER) { + if (should_send_fault) { + if (is_error_handler_fault) { protection.fault_protection->update_error_handler_message( protection.fault_protection->get_error_handler_string() ); + error_handler_delivered = + socket->send_order(*protection.fault_protection->fault_message) || + error_handler_delivered; + } else { + socket->send_order(*protection.fault_protection->fault_message); } - socket->send_order(*protection.fault_protection->fault_message); - ErrorHandlerModel::error_to_communicate = false; } for (auto& warning : protection.warnings_triggered) { if (warning->boundary_type_id == INFO_WARNING - 2) { + if (!InfoWarning::warning_to_communicate) { + continue; + } warning->update_warning_message(warning->get_warning_string()); - InfoWarning::warning_triggered = false; - InfoWarning::warning_to_communicate = false; + info_warning_delivered = + socket->send_order(*warning->warn_message) || info_warning_delivered; + continue; } socket->send_order(*warning->warn_message); } @@ -136,6 +155,16 @@ void ProtectionManager::notify(Protection& protection) { socket->send_order(*ok->ok_message); } } + + if (error_handler_delivered) { + ErrorHandlerModel::error_to_communicate = false; + } + + if (info_warning_delivered) { + InfoWarning::warning_triggered = false; + InfoWarning::warning_to_communicate = false; + } + protection.oks_triggered.clear(); protection.warnings_triggered.clear(); } From 6796ea22ba9d5a4acec602aacbf817f978f4b48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 19:56:39 +0100 Subject: [PATCH 13/18] Always enqueuing msgs even when there are no sockets --- Inc/HALAL/Services/Time/RTC.hpp | 1 + .../Services/InfoWarning/InfoWarning.cpp | 37 +++++++++++++++++-- Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp | 37 +++++++++++++++++-- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/Inc/HALAL/Services/Time/RTC.hpp b/Inc/HALAL/Services/Time/RTC.hpp index 9a8dea03..baf02c63 100644 --- a/Inc/HALAL/Services/Time/RTC.hpp +++ b/Inc/HALAL/Services/Time/RTC.hpp @@ -22,6 +22,7 @@ class Global_RTC { static RTCData global_RTC; static void start_rtc(); static bool ensure_started(); + static bool has_valid_time(); static void update_rtc_data(); static RTCData get_rtc_timestamp(); static void set_rtc_data( diff --git a/Src/HALAL/Services/InfoWarning/InfoWarning.cpp b/Src/HALAL/Services/InfoWarning/InfoWarning.cpp index 782beb54..1309f190 100644 --- a/Src/HALAL/Services/InfoWarning/InfoWarning.cpp +++ b/Src/HALAL/Services/InfoWarning/InfoWarning.cpp @@ -166,7 +166,7 @@ bool try_send_warning_via_uart(const string& description) { void append_readable_timestamp(string& message) { #ifdef HAL_RTC_MODULE_ENABLED - if (Global_RTC::ensure_started()) { + if (Global_RTC::ensure_started() && Global_RTC::has_valid_time()) { Global_RTC::update_rtc_data(); const RTCData& timestamp = Global_RTC::global_RTC; char buffer[80]{}; @@ -188,7 +188,38 @@ void append_readable_timestamp(string& message) { #endif #ifdef HAL_TIM_MODULE_ENABLED - message += " | Timestamp(ns): " + to_string(Scheduler::get_global_tick()); + const uint64_t uptime_us = Scheduler::get_global_tick(); + const uint64_t total_seconds = uptime_us / 1'000'000ULL; + const uint64_t days = total_seconds / 86'400ULL; + const unsigned hours = static_cast((total_seconds / 3'600ULL) % 24ULL); + const unsigned minutes = static_cast((total_seconds / 60ULL) % 60ULL); + const unsigned seconds = static_cast(total_seconds % 60ULL); + const unsigned micros = static_cast(uptime_us % 1'000'000ULL); + + char buffer[80]{}; + if (days > 0) { + snprintf( + buffer, + sizeof(buffer), + " | Uptime: %llud %02u:%02u:%02u.%06u", + static_cast(days), + hours, + minutes, + seconds, + micros + ); + } else { + snprintf( + buffer, + sizeof(buffer), + " | Uptime: %02u:%02u:%02u.%06u", + hours, + minutes, + seconds, + micros + ); + } + message += buffer; #endif } @@ -217,7 +248,7 @@ void InfoWarning::InfoWarningTrigger(string format, ...) { warning_sent_via_tcp = false; warning_sent_via_uart = false; #ifdef STLIB_ETH - tcp_delivery_required = !OrderProtocol::sockets.empty(); + tcp_delivery_required = true; #else tcp_delivery_required = false; #endif diff --git a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp index 6617b8dc..ef044da0 100644 --- a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp +++ b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp @@ -166,7 +166,7 @@ bool try_send_error_via_uart(const string& description) { void append_readable_timestamp(string& message) { #ifdef HAL_RTC_MODULE_ENABLED - if (Global_RTC::ensure_started()) { + if (Global_RTC::ensure_started() && Global_RTC::has_valid_time()) { Global_RTC::update_rtc_data(); const RTCData& timestamp = Global_RTC::global_RTC; char buffer[80]{}; @@ -188,7 +188,38 @@ void append_readable_timestamp(string& message) { #endif #ifdef HAL_TIM_MODULE_ENABLED - message += " | Timestamp(ns): " + to_string(Scheduler::get_global_tick()); + const uint64_t uptime_us = Scheduler::get_global_tick(); + const uint64_t total_seconds = uptime_us / 1'000'000ULL; + const uint64_t days = total_seconds / 86'400ULL; + const unsigned hours = static_cast((total_seconds / 3'600ULL) % 24ULL); + const unsigned minutes = static_cast((total_seconds / 60ULL) % 60ULL); + const unsigned seconds = static_cast(total_seconds % 60ULL); + const unsigned micros = static_cast(uptime_us % 1'000'000ULL); + + char buffer[80]{}; + if (days > 0) { + snprintf( + buffer, + sizeof(buffer), + " | Uptime: %llud %02u:%02u:%02u.%06u", + static_cast(days), + hours, + minutes, + seconds, + micros + ); + } else { + snprintf( + buffer, + sizeof(buffer), + " | Uptime: %02u:%02u:%02u.%06u", + hours, + minutes, + seconds, + micros + ); + } + message += buffer; #endif } @@ -219,7 +250,7 @@ void ErrorHandlerModel::ErrorHandlerTrigger(string format, ...) { error_sent_via_tcp = false; error_sent_via_uart = false; #ifdef STLIB_ETH - tcp_delivery_required = !OrderProtocol::sockets.empty(); + tcp_delivery_required = true; #else tcp_delivery_required = false; #endif From 2c8670bec009bfb767e32796089dc7a4170d650d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 19:58:06 +0100 Subject: [PATCH 14/18] sorry, I forgot one file --- Src/HALAL/Services/Time/RTC.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Src/HALAL/Services/Time/RTC.cpp b/Src/HALAL/Services/Time/RTC.cpp index ed8961d5..6fbe0edb 100644 --- a/Src/HALAL/Services/Time/RTC.cpp +++ b/Src/HALAL/Services/Time/RTC.cpp @@ -6,7 +6,8 @@ RTCData Global_RTC::global_RTC; namespace { bool rtc_started = false; bool rtc_start_in_progress = false; -} +bool rtc_time_valid = false; +} // namespace void Global_RTC::start_rtc() { if (rtc_started || rtc_start_in_progress) { @@ -54,6 +55,7 @@ void Global_RTC::start_rtc() { return; } + rtc_time_valid = false; rtc_started = true; rtc_start_in_progress = false; } @@ -65,6 +67,8 @@ bool Global_RTC::ensure_started() { return rtc_started; } +bool Global_RTC::has_valid_time() { return rtc_time_valid; } + RTCData Global_RTC::get_rtc_timestamp() { RTCData ret; RTC_TimeTypeDef gTime; @@ -90,6 +94,10 @@ void Global_RTC::set_rtc_data( uint8_t month, uint16_t year ) { + if (!ensure_started()) { + return; + } + RTC_TimeTypeDef gTime; RTC_DateTypeDef gDate; gTime.SubSeconds = counter; @@ -102,16 +110,27 @@ void Global_RTC::set_rtc_data( gDate.Date = day; gDate.Month = month; gDate.Year = year - 2000; + bool write_ok = true; if (HAL_RTC_SetTime(&hrtc, &gTime, RTC_FORMAT_BIN) != HAL_OK) { + write_ok = false; ErrorHandler("Error on writing Time on the RTC"); } if (HAL_RTC_SetDate(&hrtc, &gDate, RTC_FORMAT_BIN) != HAL_OK) { + write_ok = false; ErrorHandler("Error on writing Date on the RTC"); } + rtc_time_valid = write_ok; + if (write_ok) { + global_RTC = get_rtc_timestamp(); + } } void Global_RTC::update_rtc_data() { if (!ensure_started()) { return; } + if (!rtc_time_valid) { + global_RTC = {}; + return; + } global_RTC = get_rtc_timestamp(); } From 4cf4641497dbca25b7d5e836edaa95aa3eb99a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 20:21:23 +0100 Subject: [PATCH 15/18] SNTP set --- CMakeLists.txt | 1 + .../Communication/Ethernet/NewEthernet.hpp | 20 ++++++- .../Services/Communication/SNTP/SNTP.hpp | 4 +- Inc/ST-LIB_HIGH/Protections/Boundary.hpp | 22 ++++--- Inc/ST-LIB_HIGH/Protections/Protection.hpp | 3 +- LWIP/Target/lwipopts.h | 12 ++++ .../Services/Communication/SNTP/SNTP.cpp | 58 +++++++++++-------- .../Protections/ProtectionManager.cpp | 2 +- 8 files changed, 87 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dfdc9a5..7335ca05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,6 +243,7 @@ if(CMAKE_CROSSCOMPILING) ${CMAKE_CURRENT_LIST_DIR}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/core/ipv4/ip4.c ${CMAKE_CURRENT_LIST_DIR}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/core/ipv4/ip4_addr.c ${CMAKE_CURRENT_LIST_DIR}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/core/ipv4/ip4_frag.c + ${CMAKE_CURRENT_LIST_DIR}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/apps/sntp/sntp.c ${CMAKE_CURRENT_LIST_DIR}/STM32CubeH7/Middlewares/Third_Party/LwIP/src/netif/ethernet.c ) diff --git a/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp b/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp index 4c5424fa..16a91a37 100644 --- a/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp +++ b/Inc/HALAL/Services/Communication/Ethernet/NewEthernet.hpp @@ -10,6 +10,7 @@ #include "HALAL/Services/Communication/Ethernet/LWIP/Ethernet.hpp" #include "HALAL/Services/Communication/Ethernet/LWIP/EthernetHelper.hpp" #include "HALAL/Services/Communication/Ethernet/LWIP/EthernetNode.hpp" +#include "HALAL/Services/Communication/SNTP/SNTP.hpp" #include "ErrorHandler/ErrorHandler.hpp" #include "HALAL/Services/InfoWarning/InfoWarning.hpp" extern "C" { @@ -81,6 +82,7 @@ struct EthernetDomain { const char* local_ip; const char* subnet_mask; const char* gateway; + const char* sntp_server; size_t phy_reset_id; }; @@ -99,9 +101,10 @@ struct EthernetDomain { const char* local_mac, const char* local_ip, const char* subnet_mask = "255.255.0.0", - const char* gateway = "192.168.1.1" + const char* gateway = "192.168.1.1", + const char* sntp_server = SNTP::DEFAULT_SERVER_IP ) - : pins{pins}, e{local_mac, local_ip, subnet_mask, gateway}, + : pins{pins}, e{local_mac, local_ip, subnet_mask, gateway, sntp_server}, rmii_gpios{ GPIODomain::GPIO( pins.MDC, @@ -187,6 +190,7 @@ struct EthernetDomain { .local_ip = this->e.local_ip, .subnet_mask = this->e.subnet_mask, .gateway = this->e.gateway, + .sntp_server = this->e.sntp_server, .phy_reset_id = phy_reset_id, }; @@ -201,6 +205,7 @@ struct EthernetDomain { const char* local_ip; const char* subnet_mask; const char* gateway; + const char* sntp_server; size_t phy_reset_id; }; @@ -216,12 +221,16 @@ struct EthernetDomain { cfgs[0].local_ip = e.local_ip; cfgs[0].subnet_mask = e.subnet_mask; cfgs[0].gateway = e.gateway; + cfgs[0].sntp_server = e.sntp_server; cfgs[0].phy_reset_id = e.phy_reset_id; return cfgs; } // Runtime object struct Instance { + const char* sntp_server{nullptr}; + bool sntp_started{false}; + constexpr Instance() {} void update() { ethernetif_input(&gnetif); @@ -237,6 +246,12 @@ struct EthernetDomain { netif_set_up(&gnetif); } } + + if (!sntp_started && sntp_server != nullptr && sntp_server[0] != '\0' && + netif_is_link_up(&gnetif)) { + SNTP::sntp_update(sntp_server); + sntp_started = true; + } }; }; @@ -320,6 +335,7 @@ struct EthernetDomain { ::Ethernet::is_running = true; instances[0] = Instance{}; + instances[0].sntp_server = e.sntp_server; } }; }; diff --git a/Inc/HALAL/Services/Communication/SNTP/SNTP.hpp b/Inc/HALAL/Services/Communication/SNTP/SNTP.hpp index 16e79391..a8759201 100644 --- a/Inc/HALAL/Services/Communication/SNTP/SNTP.hpp +++ b/Inc/HALAL/Services/Communication/SNTP/SNTP.hpp @@ -7,12 +7,14 @@ #pragma once -#include "sntp.h" +#include "lwip/apps/sntp.h" #include "HALAL/Models/IPV4/IPV4.hpp" #include "C++Utilities/CppUtils.hpp" class SNTP { public: + static constexpr const char* DEFAULT_SERVER_IP = "192.168.0.9"; + static void sntp_update( uint8_t address_head, uint8_t address_second, diff --git a/Inc/ST-LIB_HIGH/Protections/Boundary.hpp b/Inc/ST-LIB_HIGH/Protections/Boundary.hpp index d2477981..1f6539dc 100644 --- a/Inc/ST-LIB_HIGH/Protections/Boundary.hpp +++ b/Inc/ST-LIB_HIGH/Protections/Boundary.hpp @@ -27,16 +27,24 @@ enum ProtectionType : uint8_t { struct BoundaryInterface { public: + static constexpr uint8_t ERROR_HANDLER_BOUNDARY_TYPE_ID = ERROR_HANDLER; + static constexpr uint8_t INFO_WARNING_BOUNDARY_TYPE_ID = INFO_WARNING - 2; + virtual Protections::FaultType check_bounds() = 0; HeapOrder* fault_message{nullptr}; HeapOrder* warn_message{nullptr}; HeapOrder* ok_message{nullptr}; void update_name(char* n) { - name = n; - if (strlen(n) > NAME_MAX_LEN) { - ErrorHandler("Variable name is too long, max length is %d", NAME_MAX_LEN); + if (n == nullptr) { + name.clear(); + string_len = 0; return; } + + name = n; + if (name.size() > NAME_MAX_LEN) { + name.resize(NAME_MAX_LEN); + } string_len = name.size(); } virtual void update_error_handler_message([[maybe_unused]] const char* err_message) {} @@ -490,7 +498,7 @@ template struct Boundary : public BoundaryInter template <> struct Boundary : public BoundaryInterface { static constexpr ProtectionType Protector = ERROR_HANDLER; Boundary(void*) { - boundary_type_id = Protector; + boundary_type_id = ERROR_HANDLER_BOUNDARY_TYPE_ID; error_handler_string.reserve(ERROR_HANDLER_MSG_MAX_LEN); fault_message = new HeapOrder( uint16_t{1555}, @@ -509,7 +517,7 @@ template <> struct Boundary : public BoundaryInterface { } uint8_t padding{}; Boundary(void*, Boundary) { - boundary_type_id = Protector; + boundary_type_id = ERROR_HANDLER_BOUNDARY_TYPE_ID; error_handler_string.reserve(ERROR_HANDLER_MSG_MAX_LEN); fault_message = new HeapOrder( uint16_t{1555}, @@ -551,7 +559,7 @@ template <> struct Boundary : public BoundaryInterface { template <> struct Boundary : public BoundaryInterface { static constexpr ProtectionType Protector = INFO_WARNING; Boundary(void*) { - boundary_type_id = Protector - 2; + boundary_type_id = INFO_WARNING_BOUNDARY_TYPE_ID; warning_string.reserve(WARNING_HANDLER_MSG_MAX_LEN); warn_message = new HeapOrder( uint16_t{2555}, @@ -571,7 +579,7 @@ template <> struct Boundary : public BoundaryInterface { uint8_t padding{}; Boundary(void*, Boundary) { // SW are crybabies - boundary_type_id = Protector - 2; + boundary_type_id = INFO_WARNING_BOUNDARY_TYPE_ID; warning_string.reserve(WARNING_HANDLER_MSG_MAX_LEN); warn_message = new HeapOrder( uint16_t{2555}, diff --git a/Inc/ST-LIB_HIGH/Protections/Protection.hpp b/Inc/ST-LIB_HIGH/Protections/Protection.hpp index 089b3f4f..b8371ce4 100644 --- a/Inc/ST-LIB_HIGH/Protections/Protection.hpp +++ b/Inc/ST-LIB_HIGH/Protections/Protection.hpp @@ -69,7 +69,8 @@ class Protection { bound->back_to_normal = true; } bound->warning_already_triggered = false; - if (bound->back_to_normal && bound->boundary_type_id != INFO_WARNING - 2) { + if (bound->back_to_normal && + bound->boundary_type_id != BoundaryInterface::INFO_WARNING_BOUNDARY_TYPE_ID) { triggered_oks_idx[oks_count] = idx - 1; oks_count++; bound->back_to_normal = false; diff --git a/LWIP/Target/lwipopts.h b/LWIP/Target/lwipopts.h index 39010d39..e29c5608 100644 --- a/LWIP/Target/lwipopts.h +++ b/LWIP/Target/lwipopts.h @@ -113,12 +113,24 @@ #define LWIP_DHCP 0 #define LWIP_AUTOIP 0 #define LWIP_DNS 0 +#define LWIP_SNTP 1 #define LWIP_IGMP 0 #define LWIP_IPV6_MLD 0 #define LWIP_IPV6_DHCP6 0 #define LWIP_IPV6_REASS 0 #define LWIP_IPV6_FRAG 0 +void stlib_sntp_set_time(uint32_t sec, uint32_t us); +uint32_t stlib_sntp_get_rtc_seconds(void); +uint32_t stlib_sntp_get_rtc_microseconds(void); + +#define SNTP_SET_SYSTEM_TIME_US(sec, us) stlib_sntp_set_time((sec), (us)) +#define SNTP_GET_SYSTEM_TIME(sec, us) \ + do { \ + (sec) = stlib_sntp_get_rtc_seconds(); \ + (us) = stlib_sntp_get_rtc_microseconds(); \ + } while (0) + /* USER CODE END 1 */ #ifdef __cplusplus diff --git a/Src/HALAL/Services/Communication/SNTP/SNTP.cpp b/Src/HALAL/Services/Communication/SNTP/SNTP.cpp index 3c37b415..4b531be4 100644 --- a/Src/HALAL/Services/Communication/SNTP/SNTP.cpp +++ b/Src/HALAL/Services/Communication/SNTP/SNTP.cpp @@ -10,7 +10,20 @@ #define SUBSECONDS_PER_SECOND 32767 #define TRANSFORMATION_FACTOR (SUBSECONDS_PER_SECOND / 999999.0) -#define TARGET_IP "192.168.0.9" + +namespace { + +void configure_sntp_server(const ip_addr_t& address) { + if (sntp_enabled()) { + sntp_stop(); + } + + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setserver(0, &address); + sntp_init(); +} + +} // namespace void SNTP::sntp_update( uint8_t address_head, @@ -18,28 +31,19 @@ void SNTP::sntp_update( uint8_t address_third, uint8_t address_last ) { - // sntp_setoperatingmode(SNTP_OPMODE_POLL); - // ip4_addr_t* address; - // IP_ADDR4(address,address_head,address_second,address_third,address_last); - // sntp_setserver(0,address); - // sntp_init(); + ip_addr_t address; + IP_ADDR4(&address, address_head, address_second, address_third, address_last); + configure_sntp_server(address); } void SNTP::sntp_update(string ip) { - // sntp_setoperatingmode(SNTP_OPMODE_POLL); - // IPV4 target(ip); - // sntp_setserver(0,&target.address); - // sntp_init(); + IPV4 target(ip.c_str()); + configure_sntp_server(target.address); } -void SNTP::sntp_update() { - // sntp_setoperatingmode(SNTP_OPMODE_POLL); - // IPV4 target(TARGET_IP); - // sntp_setserver(0,&target.address); - // sntp_init(); -} +void SNTP::sntp_update() { sntp_update(DEFAULT_SERVER_IP); } -void set_rtc( +extern "C" void stlib_sntp_set_rtc( uint16_t counter, uint8_t second, uint8_t minute, @@ -51,8 +55,12 @@ void set_rtc( Global_RTC::set_rtc_data(counter, second, minute, hour, day, month, year); } -u32_t get_rtc_s() { - RTCData rtc_time = Global_RTC::get_rtc_timestamp(); +extern "C" u32_t stlib_sntp_get_rtc_seconds() { + if (!Global_RTC::has_valid_time()) { + return 0; + } + + const RTCData rtc_time = Global_RTC::get_rtc_timestamp(); time_t nowtime = 0; struct tm* nowtm; nowtm = gmtime(&nowtime); @@ -66,19 +74,23 @@ u32_t get_rtc_s() { return sec; } -u32_t get_rtc_us() { - RTCData rtc_time = Global_RTC::get_rtc_timestamp(); +extern "C" u32_t stlib_sntp_get_rtc_microseconds() { + if (!Global_RTC::has_valid_time()) { + return 0; + } + + const RTCData rtc_time = Global_RTC::get_rtc_timestamp(); return rtc_time.counter / TRANSFORMATION_FACTOR; } -void set_time(uint32_t sec, uint32_t us) { +extern "C" void stlib_sntp_set_time(uint32_t sec, uint32_t us) { struct timeval tv; tv.tv_sec = sec; tv.tv_usec = us; time_t nowtime = sec; struct tm* nowtm = localtime(&nowtime); uint32_t subsecond = (uint32_t)(TRANSFORMATION_FACTOR * tv.tv_usec); - set_rtc( + stlib_sntp_set_rtc( subsecond, nowtm->tm_sec, nowtm->tm_min, diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp index 4b310c0f..c6cb5f17 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp @@ -140,7 +140,7 @@ void ProtectionManager::notify(Protection& protection) { } } for (auto& warning : protection.warnings_triggered) { - if (warning->boundary_type_id == INFO_WARNING - 2) { + if (warning->boundary_type_id == BoundaryInterface::INFO_WARNING_BOUNDARY_TYPE_ID) { if (!InfoWarning::warning_to_communicate) { continue; } From 09ebf8b11044b1dba4f6e19642599e59bdf51393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 21:28:26 +0100 Subject: [PATCH 16/18] minor fix on protections --- Inc/ST-LIB_HIGH/Protections/ProtectionManager.hpp | 2 +- Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionManager.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionManager.hpp index 5a69cf23..ec045907 100644 --- a/Inc/ST-LIB_HIGH/Protections/ProtectionManager.hpp +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionManager.hpp @@ -39,7 +39,7 @@ class ProtectionManager { typedef uint8_t state_id; static bool external_trigger; - static const uint64_t notify_delay_in_nanoseconds = 2000'000'000; + static const uint64_t notify_delay_in_microseconds = 2'000'000; static uint64_t last_notify; static void set_id(Boards::ID id); diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp index c6cb5f17..6ca063ff 100644 --- a/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp +++ b/Src/ST-LIB_HIGH/Protections/ProtectionManager.cpp @@ -84,7 +84,7 @@ void ProtectionManager::check_protections() { } Global_RTC::update_rtc_data(); if (Scheduler::get_global_tick() > - protection.get_last_notify_tick() + notify_delay_in_nanoseconds) { + protection.get_last_notify_tick() + notify_delay_in_microseconds) { ProtectionManager::notify(protection); protection.update_last_notify_tick(Scheduler::get_global_tick()); } @@ -107,7 +107,7 @@ void ProtectionManager::check_high_frequency_protections() { } Global_RTC::update_rtc_data(); if (Scheduler::get_global_tick() > - protection.get_last_notify_tick() + notify_delay_in_nanoseconds) { + protection.get_last_notify_tick() + notify_delay_in_microseconds) { ProtectionManager::notify(protection); protection.update_last_notify_tick(Scheduler::get_global_tick()); } From 927f914143de924fd80bfcf2d66d93d693c67d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 21:50:32 +0100 Subject: [PATCH 17/18] Being able to compile without ehternet --- Src/HALAL/Services/InfoWarning/InfoWarning.cpp | 8 +++++++- Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Src/HALAL/Services/InfoWarning/InfoWarning.cpp b/Src/HALAL/Services/InfoWarning/InfoWarning.cpp index 1309f190..ad7da2ca 100644 --- a/Src/HALAL/Services/InfoWarning/InfoWarning.cpp +++ b/Src/HALAL/Services/InfoWarning/InfoWarning.cpp @@ -6,21 +6,26 @@ */ #include "HALAL/Services/InfoWarning/InfoWarning.hpp" -#include "HALAL/Models/Packets/Order.hpp" #include "HALAL/Services/Communication/UART/UART.hpp" #include "HALAL/Services/Time/RTC.hpp" #include "HALAL/Services/Time/Scheduler.hpp" +#ifdef STLIB_ETH +#include "HALAL/Models/Packets/Order.hpp" +#endif namespace { +#ifdef STLIB_ETH constexpr uint16_t INFO_WARNING_TCP_ORDER_ID = 2555; constexpr uint8_t INFO_WARNING_BOUNDARY_TYPE_ID = 5; +#endif bool warning_sent_via_tcp = false; bool warning_sent_via_uart = false; bool tcp_delivery_required = false; bool uart_delivery_required = false; +#ifdef STLIB_ETH uint8_t warning_padding = 0; uint8_t warning_boundary_type = INFO_WARNING_BOUNDARY_TYPE_ID; string warning_name = "info_warning"; @@ -118,6 +123,7 @@ void refresh_warning_transport_state(const string& description) { warning_month = 0; warning_year = 0; } +#endif bool try_send_warning_via_tcp(const string& description) { #ifdef STLIB_ETH diff --git a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp index ef044da0..b0c4897d 100644 --- a/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp +++ b/Src/ST-LIB_LOW/ErrorHandler/ErrorHandler.cpp @@ -7,19 +7,24 @@ #include "ErrorHandler/ErrorHandler.hpp" #include "HALAL/Services/Time/Scheduler.hpp" -#include "HALAL/Models/Packets/Order.hpp" #include "HALAL/Services/Time/RTC.hpp" +#ifdef STLIB_ETH +#include "HALAL/Models/Packets/Order.hpp" +#endif namespace { +#ifdef STLIB_ETH constexpr uint16_t ERROR_HANDLER_TCP_ORDER_ID = 1555; constexpr uint8_t ERROR_HANDLER_BOUNDARY_TYPE_ID = 5; +#endif bool error_sent_via_tcp = false; bool error_sent_via_uart = false; bool tcp_delivery_required = false; bool uart_delivery_required = false; +#ifdef STLIB_ETH uint8_t error_handler_padding = 0; uint8_t error_handler_boundary_type = ERROR_HANDLER_BOUNDARY_TYPE_ID; string error_handler_name = "error_handler"; @@ -118,6 +123,7 @@ void refresh_error_handler_transport_state(const string& description) { error_handler_month = 0; error_handler_year = 0; } +#endif bool try_send_error_via_tcp(const string& description) { #ifdef STLIB_ETH From ac84fbcd6b212351e0c22bde9fd3728032a4f406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 2 Mar 2026 21:52:49 +0100 Subject: [PATCH 18/18] formatter --- .../Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp | 3 ++- Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp index 750f7da6..344d8807 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/ServerSocket.cpp @@ -274,7 +274,8 @@ bool ServerSocket::try_send_immediately(Order& order) { } const size_t order_size = order.get_size(); - if (order_size == 0 || order_size > TCP_SND_BUF || order_size > tcp_sndbuf(client_control_block)) { + if (order_size == 0 || order_size > TCP_SND_BUF || + order_size > tcp_sndbuf(client_control_block)) { return false; } diff --git a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp index b3a50cab..8adb232c 100644 --- a/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp +++ b/Src/HALAL/Services/Communication/Ethernet/LWIP/TCP/Socket.cpp @@ -372,7 +372,8 @@ bool Socket::try_send_immediately(Order& order) { } const size_t order_size = order.get_size(); - if (order_size == 0 || order_size > TCP_SND_BUF || order_size > tcp_sndbuf(socket_control_block)) { + if (order_size == 0 || order_size > TCP_SND_BUF || + order_size > tcp_sndbuf(socket_control_block)) { return false; }