Skip to content

Commit

Permalink
add app tcp-client-to-serial
Browse files Browse the repository at this point in the history
  • Loading branch information
RGNagel committed Jan 19, 2024
1 parent fbc116e commit b932056
Show file tree
Hide file tree
Showing 9 changed files with 482 additions and 2 deletions.
4 changes: 4 additions & 0 deletions apps/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
add_subdirectory(tcp-client-to-serial)
endif()

if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
add_subdirectory(leitor-cli)
endif()
Expand Down
17 changes: 17 additions & 0 deletions apps/tcp-client-to-serial/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
set(SOURCE_SERIAL_POLICY ${CMAKE_SOURCE_DIR}/src/serial/serial_policy_unix.cpp)

set(RELAY tcp-client-to-serial)

add_executable(${RELAY} main.cpp ${SOURCE_SERIAL_POLICY})
target_include_directories(${RELAY} PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(${RELAY} PRIVATE ${LIBRARY_NAME})
target_set_warnings(${RELAY} ENABLE ALL ALL DISABLE Annoying) # Set warnings (if needed).
target_enable_lto(${RELAY} optimized) # enable link-time-optimization if available for non-debug configurations

set_target_properties(
${RELAY}
PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED NO
CXX_EXTENSIONS NO
)
268 changes: 268 additions & 0 deletions apps/tcp-client-to-serial/client_tcp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <log_policy.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <unistd.h>

template <class LogPolicy = LogPolicyStdout> class ClientTCP {
private:
int _socket_fd;

bool _set_keep_alive_options(int socket) {
int optval = 1;
if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval,
sizeof(optval))) {
LogPolicy::log("Error on setsockopt: %s\n", strerror(errno));
return false;
}

// Set the keepalive parameters
int keepidle = 15; // Time in seconds before starting keepalive probes
int keepintvl = 10; // Time in seconds between keepalive probes
int keepcnt = 3; // Number of keepalive probes before considering the
// connection dead

if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle,
sizeof(keepidle)) == -1) {
LogPolicy::log("Error on setsockopt: %s\n", strerror(errno));
return false;
}

if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl,
sizeof(keepintvl)) == -1) {
LogPolicy::log("Error on setsockopt: %s\n", strerror(errno));
return false;
}

if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt,
sizeof(keepcnt)) == -1) {
LogPolicy::log("Error on setsockopt: %s\n", strerror(errno));
return false;
}

return true;
}

public:
ClientTCP() : _socket_fd(-1) {}
~ClientTCP() { disconnect(); }

size_t tx(const std::uint8_t* data, const std::size_t data_sz) {
if (!isConnect())
return 0;

ssize_t bytesSent = send(_socket_fd, data, data_sz, 0);
if (bytesSent == -1) {
LogPolicy::log("Error on send: %s\n", strerror(errno));
disconnect();
return 0;
}

return static_cast<size_t>(bytesSent);
}
size_t rx(std::uint8_t* data, const std::size_t max_data_sz) {
if (!isConnect())
return 0;

ssize_t bytesRead = recv(_socket_fd, data, max_data_sz, 0);

if (bytesRead == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// No data was available for reading
return 0;
} else {
// Error on recv
LogPolicy::log("Error on recv: %s\n", strerror(errno));
}
}

return static_cast<size_t>(bytesRead);
}

bool connect(const char* server_host, uint16_t server_port) {
struct addrinfo hints, *server_info;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;

char port_str[6];
snprintf(port_str, sizeof(port_str), "%u", server_port);

int errcode = getaddrinfo(server_host, port_str, &hints, &server_info);
if (errcode != 0) {
LogPolicy::log("Error on getaddrinfo: %s\n", gai_strerror(errcode));
return false;
}

_socket_fd = socket(server_info->ai_family, server_info->ai_socktype,
server_info->ai_protocol);
if (_socket_fd == -1) {
LogPolicy::log("Error on socket: %s\n", strerror(errno));
freeaddrinfo(server_info);
return false;
}

if (!_set_keep_alive_options(_socket_fd)) {
close(_socket_fd);
_socket_fd = -1;
freeaddrinfo(server_info);
return false;
}

// keep-alive
int optval = 1;
if (setsockopt(_socket_fd, SOL_SOCKET, SO_KEEPALIVE, &optval,
sizeof(optval))) {
LogPolicy::log("Error on setsockopt: %s\n", strerror(errno));
close(_socket_fd);
_socket_fd = -1;
freeaddrinfo(server_info);
return false;
}

// Set the keepalive parameters
int keepidle = 60; // Time in seconds before starting keepalive probes
int keepintvl = 10; // Time in seconds between keepalive probes
int keepcnt = 6; // Number of keepalive probes before considering the
// connection dead

if (setsockopt(_socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle,
sizeof(keepidle)) == -1) {
LogPolicy::log("Error on setsockopt: %s\n", strerror(errno));
return 1;
}

if (setsockopt(_socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl,
sizeof(keepintvl)) == -1) {
perror("setsockopt TCP_KEEPINTVL");
return 1;
}

if (setsockopt(_socket_fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt,
sizeof(keepcnt)) == -1) {
perror("setsockopt TCP_KEEPCNT");
return 1;
}

// set socket nonblock
int flags = fcntl(_socket_fd, F_GETFL, 0);
if (flags == -1) {
LogPolicy::log("Error on fcntl: %s\n", strerror(errno));
freeaddrinfo(server_info);
return false;
}
fcntl(_socket_fd, F_SETFL, flags | O_NONBLOCK);

if (::connect(_socket_fd, server_info->ai_addr,
server_info->ai_addrlen) == -1) {
if (errno == EINPROGRESS) {
fd_set write_set;
FD_ZERO(&write_set);
FD_SET(_socket_fd, &write_set);

struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;

int retval =
select(_socket_fd + 1, NULL, &write_set, NULL, &tv);
if (retval == -1) {
LogPolicy::log("Error on select: %s\n", strerror(errno));
close(_socket_fd);
_socket_fd = -1;
freeaddrinfo(server_info);
return false;
} else if (retval == 0) {
LogPolicy::log("Timeout on select\n");
close(_socket_fd);
_socket_fd = -1;
freeaddrinfo(server_info);
return false;
} else {
int error = 0;
socklen_t len = sizeof(error);
int retval = getsockopt(_socket_fd, SOL_SOCKET, SO_ERROR,
&error, &len);
if (retval != 0 || error != 0) {
// LogPolicy::log("Error on getsockopt: %s\n",
// strerror(errno));
LogPolicy::log("Couldn't connect to server\n");
close(_socket_fd);
_socket_fd = -1;
freeaddrinfo(server_info);
return false;
}
}
} else {
LogPolicy::log("Error on connect: %s\n", strerror(errno));
close(_socket_fd);
_socket_fd = -1;
freeaddrinfo(server_info);
return false;
}
}

freeaddrinfo(server_info);
return true;
}

void disconnect() {
if (isConnect()) {
close(_socket_fd);
_socket_fd = -1;
}
}

bool isConnect() {
if (_socket_fd == -1) {
return false;
} else {
int error = 0;
socklen_t len = sizeof(error);
int retval =
getsockopt(_socket_fd, SOL_SOCKET, SO_ERROR, &error, &len);
if (retval != 0 || error != 0) {
LogPolicy::log("Error on getsockopt: %s\n", strerror(errno));
return false;
}
}

// check if my tcp connection is still alive
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;

fd_set read_set;
FD_ZERO(&read_set);
FD_SET(_socket_fd, &read_set);

int retval = select(_socket_fd + 1, &read_set, NULL, NULL, &tv);
if (retval == -1) {
LogPolicy::log("Error on select: %s\n", strerror(errno));
return false;
} else if (retval == 0) {
// LogPolicy::log("Timeout on select\n");
return true;
} else {
char buf[1];
ssize_t bytesRead = recv(_socket_fd, buf, sizeof(buf), MSG_PEEK);
if (bytesRead == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// No data was available for reading
return true;
} else {
// Error on recv
LogPolicy::log("Error on recv: %s\n", strerror(errno));
return false;
}
}
}

return true;
}
};
93 changes: 93 additions & 0 deletions apps/tcp-client-to-serial/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "NBR14522.h"
#include "client_tcp.h"
#include "serial/serial_policy_unix.h"
#include <cstring>
#include <hexstr2bytes.h>
#include <leitor.h>
#include <log_policy.h>
#include <timer/timer_policy_generic_os.h>
#include <vector>
#include <get_comandos_from_data.h>

using namespace NBR14522;


template <class LogPolicy = LogPolicyStdout>
bool readRespostasFromMeter(
std::vector<comando_t>& cmds, const char* serial_port,
std::function<void(const NBR14522::resposta_t& rsp)> callback) {

std::shared_ptr<SerialPolicyUnix> porta =
std::make_shared<SerialPolicyUnix>();

if (!porta->openSerial(serial_port)) {
LogPolicy::log("Não foi possível abrir a porta serial\n\n");
return false;
}

Leitor<TimerPolicyWinUnix, SerialPolicyUnix, LogPolicy> leitor(porta);

bool retval = true;

for (auto& cmd : cmds) {
retval = leitor.leitura(cmd, callback);
if (!retval)
break;
}

porta->closeSerial();
return retval;
}

using LogPolicy = LogPolicyStdout;

int main(int argc, char* argv[]) {
if (argc < 4) {
std::cerr << "Invalid number of arguments." << std::endl;
std::cerr << "Usage: " << argv[0]
<< " <server_host> <server_port> <serial_port>" << std::endl;
return EXIT_FAILURE;
}

ClientTCP<LogPolicyStdout> client;

std::shared_ptr<SerialPolicyUnix> porta =
std::make_shared<SerialPolicyUnix>();

while (true) {

if (!client.isConnect()) {
if (client.connect(argv[1], atoi(argv[2]))) {
LogPolicy::log("Connected.\n");
uint8_t data[3] = {0x05, 0x05, 0x05};
client.tx(data, 3);
} else {
sleep(5);
continue;
}
}

std::uint8_t rx_data[1024];
size_t rx_sz = client.rx(rx_data, sizeof(rx_data));

if (rx_sz > 0) {
// convert the buffer rx_data to a string
std::string data_str(reinterpret_cast<char*>(rx_data), rx_sz);

LogPolicy::log("Received data: %s\n", data_str.c_str());

std::vector<NBR14522::comando_t> comandos =
getComandosFromData(data_str);

readRespostasFromMeter<LogPolicyStdout>(comandos, argv[3], [&](const resposta_t& rsp) {
client.tx(rsp.data(), rsp.size());
});
}

sleep(1);
}

client.disconnect();

return EXIT_SUCCESS;
}
Loading

0 comments on commit b932056

Please sign in to comment.