diff --git a/.changesets/adjv3-protections-tcp.md b/.changesets/adjv3-protections-tcp.md new file mode 100644 index 000000000..afd5a0688 --- /dev/null +++ b/.changesets/adjv3-protections-tcp.md @@ -0,0 +1,2 @@ +release: minor +summary: Add ProtectionADJV3Packet TCP client with IP passed as an argument, plus improved error handling diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b20398d1..1766293ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -384,7 +384,9 @@ set(STLIB_LOW_CPP_NO_ETH set(STLIB_HIGH_C_ETH) -set(STLIB_HIGH_CPP_ETH) +set(STLIB_HIGH_CPP_ETH + ${CMAKE_CURRENT_LIST_DIR}/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp +) set(STLIB_HIGH_C_NO_ETH) diff --git a/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp new file mode 100644 index 000000000..ac8341431 --- /dev/null +++ b/Inc/ST-LIB_HIGH/Protections/ProtectionMessages.hpp @@ -0,0 +1,48 @@ +#pragma once +/* + * ProtectionMessages.hpp + * "Hello there" + * Created on: Oct 22, 2026 + * Author: Aleks + */ +#include "ST-LIB_HIGH/Protections/SampleSource.hpp" +#include "HALAL/Services/Diagnostics/Diagnostics.hpp" +#include "ST-LIB_HIGH/Protections/FaultController.hpp" +#include "ST-LIB_HIGH/Protections/Protection.hpp" +#include "ST-LIB_HIGH/Protections/ProtectionTypes.hpp" +#include +#include +#include + +struct tcp_pcb; + +namespace ST_LIB { +struct ProtectionADJv3Packet { + + struct Data { + uint16_t id; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; + uint8_t subseconds; + uint16_t reserved; // Just to not forget about it exists + }; + + struct Package { + size_t packageSize; + Data data; + // Maximum is 8 bytes, not all will be sent always + uint8_t value[8]; + }; + +public: + static Package + make(uint16_t measurement_id, Protections::RuleKind protection, size_t value_size); + static bool tcp_client_init(const char* ip, uint16_t port); + static bool tcp_client_send(Package& pack, const void* value); + + static struct tcp_pcb* tpcb; + static bool is_connected; +}; + +} // namespace ST_LIB diff --git a/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp new file mode 100644 index 000000000..f07f79dec --- /dev/null +++ b/Src/ST-LIB_HIGH/Protections/ProtectionMessages.cpp @@ -0,0 +1,88 @@ +/* + * ProtectionMessages.cpp + * "Hello there" + * Created on: Oct 22, 2026 + * Author: Aleks + */ +#include "ST-LIB_HIGH/Protections/ProtectionMessages.hpp" +#include "HALAL/Services/Time/RTC.hpp" +#include "lwip/tcp.h" + +namespace ST_LIB { + +struct tcp_pcb* ProtectionADJv3Packet::tpcb = nullptr; +bool ProtectionADJv3Packet::is_connected = false; + +ProtectionADJv3Packet::Package +ProtectionADJv3Packet::make(uint16_t measurement_id, Protections::RuleKind prot, size_t valueSize) { + Package pack = {.packageSize = sizeof(Data) + valueSize, .data = {}}; + // Bit packing of protection into 3 most significant bits of id (Look adjv3 format) + pack.data.id = (measurement_id & 0x1FFF) | (static_cast(prot) << 13); + pack.data.hours = 0; + pack.data.minutes = 0; + pack.data.seconds = 0; + pack.data.subseconds = 0; + pack.data.reserved = 0; + return pack; +} + +static err_t tcp_client_connected_callback(void* arg, struct tcp_pcb* pcb, err_t err) { + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + if (err == ERR_OK) { + ProtectionADJv3Packet::is_connected = true; + } + return ERR_OK; +} +static void tcp_client_err_callback(void* arg, err_t err) { + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + ProtectionADJv3Packet::tpcb = nullptr; // lwip already has freed the tpcb, only pointer is left + ProtectionADJv3Packet::is_connected = false; +} + +bool ProtectionADJv3Packet::tcp_client_init(const char* ip, uint16_t port) { + if (tpcb != nullptr) { // it means we already have one tpcb initialized + return false; + } + ip_addr_t destIPADDR; + if (!ip4addr_aton(ip, &destIPADDR)) { + return false; // ip was not converted + } + tpcb = tcp_new(); + if (tpcb == nullptr) { + return false; // tpcb was not initialized + } + tcp_err(tpcb, tcp_client_err_callback); + err_t err = tcp_connect(tpcb, &destIPADDR, port, tcp_client_connected_callback); + if (err != ERR_OK) { // we got an error while connecting + tcp_abort(tpcb); + tpcb = nullptr; + return false; + } + return true; +} +bool ProtectionADJv3Packet::tcp_client_send( + ProtectionADJv3Packet::Package& pack, + const void* value +) { + if (tpcb == nullptr || !is_connected) { + return false; + } + + memcpy(pack.value, value, pack.packageSize - sizeof(Data)); +#if defined(HAL_RTC_MODULE_ENABLED) && !defined(SIM_ON) + RTCData rtc = Global_RTC::get_rtc_timestamp(); + pack.data.hours = rtc.hour; + pack.data.minutes = rtc.minute; + pack.data.seconds = rtc.second; + pack.data.subseconds = static_cast((rtc.counter * 256) / (RTC_MAX_COUNTER + 1)); +#endif // if HAL_RTC was not enabled all values were initialized before to 0 + err_t err = tcp_write(tpcb, &pack.data, pack.packageSize, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + return false; + } + return tcp_output(tpcb) == ERR_OK; +} + +} // namespace ST_LIB