Skip to content

Commit

Permalink
Merge pull request #7 from janekbaraniewski/basic-functionality-testing
Browse files Browse the repository at this point in the history
Basic functionality testing
  • Loading branch information
janekbaraniewski authored May 1, 2024
2 parents 697df9f + 7b487e8 commit 6d5a4ff
Show file tree
Hide file tree
Showing 11 changed files with 510 additions and 95 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ser2net2ser

> This still needs testing
> WIP: server seems to work fine, client still has some issues
## Requirements

Expand Down
2 changes: 1 addition & 1 deletion include/RealSerialPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class RealSerialPort : public ISerialPort {
}

void async_read_some(const boost::asio::mutable_buffer& buffer, std::function<void(const boost::system::error_code&, std::size_t)> handler) override {
BOOST_LOG_TRIVIAL(info) << "Read some.";
// BOOST_LOG_TRIVIAL(info) << "Read some.";
if (!port.is_open()) {
BOOST_LOG_TRIVIAL(error) << "Attempt to read from a closed serial port.";
return;
Expand Down
8 changes: 3 additions & 5 deletions include/SerialClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,21 @@

#include "common.hpp"
#include "VirtualSerialPort.h"
#include "SocketClient.h"

using namespace boost::asio;
using ip::tcp;
using std::string;

class SerialClient {
private:
boost::asio::io_service& io_service_;
boost::asio::ip::tcp::socket socket_;
std::array<char, 256> buffer_;
VirtualSerialPort vsp_; // Declare the VirtualSerialPort object
VirtualSerialPort vsp_;

public:
SerialClient(boost::asio::io_service& io_service, const std::string& server_ip, unsigned short server_port, const std::string& vsp_name);
SocketClient socketClient_;
void run();
void do_read_vsp();
void do_read_socket();
};

#endif // CLIENT_H
55 changes: 55 additions & 0 deletions include/SocketClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#ifndef SOCKETCLIENT_H
#define SOCKETCLIENT_H

#include <iostream>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>

class SocketClient {
struct sockaddr_in server_addr;

public:
int sock_fd;
SocketClient() : sock_fd(-1) {
memset(&server_addr, 0, sizeof(server_addr));
}

~SocketClient() {
close(sock_fd);
}

bool connectToServer(const char* server_ip, int server_port) {
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd == -1) {
std::cerr << "Error creating socket: " << strerror(errno) << std::endl;
return false;
}

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) {
std::cerr << "Invalid address/ Address not supported" << std::endl;
return false;
}

if (::connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "Connection Failed: " << strerror(errno) << std::endl;
return false;
}

std::cout << "Connected to server at " << server_ip << ":" << server_port << std::endl;
return true;
}

ssize_t sendToServer(const char* buffer, size_t bufferSize) {
return send(sock_fd, buffer, bufferSize, 0);
}

ssize_t receiveFromServer(char* buffer, size_t bufferSize) {
return recv(sock_fd, buffer, bufferSize, 0);
}
};

#endif // CLIENT_H
12 changes: 9 additions & 3 deletions include/VirtualSerialPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@

class VirtualSerialPort {
public:
boost::asio::posix::stream_descriptor master_fd_;

VirtualSerialPort(boost::asio::io_context& io_context, const std::string& device);
~VirtualSerialPort();

void async_read(boost::asio::mutable_buffer buffer, std::function<void(const boost::system::error_code&, std::size_t)> handler);
void async_write(boost::asio::const_buffer buffer, std::function<void(const boost::system::error_code&, std::size_t)> handler);
ssize_t async_read(char* buffer, unsigned int length);
ssize_t async_write(const char* buffer, unsigned int length);
void close();

private:
boost::asio::posix::stream_descriptor master_fd_;
boost::asio::posix::stream_descriptor slave_fd_;
// boost::array<char, 128> buffer_;
// std::array<char, 1024> read_buffer_;
std::string device_name_;
std::mutex mutex_;

void setup_pty(int fd);
};

Expand Down
94 changes: 44 additions & 50 deletions src/SerialClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,61 +9,55 @@ using std::cerr;
using std::endl;

SerialClient::SerialClient(boost::asio::io_service& io_service, const std::string& server_ip, unsigned short server_port, const std::string& vsp_name)
: io_service_(io_service), socket_(io_service), vsp_(io_service, vsp_name) {
BOOST_LOG_TRIVIAL(info) << "Initializing client...";
boost::asio::ip::tcp::resolver resolver(io_service);
auto endpoint_iterator = resolver.resolve({server_ip, std::to_string(server_port)});
BOOST_LOG_TRIVIAL(info) << "Connecting to server at " << server_ip << ":" << server_port;
connect(socket_, endpoint_iterator);
BOOST_LOG_TRIVIAL(info) << "Connected to server.";
BOOST_LOG_TRIVIAL(info) << "Opening virtual serial port: " << vsp_name;
: vsp_(io_service, vsp_name) {
std::cout << "Initializing client...";
std::cout << "Connecting to server at " << server_ip << ":" << server_port;
socketClient_.connectToServer(server_ip.c_str(), server_port);
std::cout << "Connected to server.";
std::cout << "Opening virtual serial port: " << vsp_name;
}

void SerialClient::run() {
BOOST_LOG_TRIVIAL(info) << "Starting client I/O operations.";
do_read_socket();
do_read_vsp();
io_service_.run();
}

void SerialClient::do_read_socket() {
socket_.async_read_some(boost::asio::buffer(buffer_), [this](boost::system::error_code ec, std::size_t length) {
if (!ec && length > 0) {
std::string data(buffer_.data(), length);
std::fill(std::begin(buffer_), std::end(buffer_), 0);

BOOST_LOG_TRIVIAL(debug) << "Received from server: " << data;

vsp_.async_write(boost::asio::buffer(data), [this](boost::system::error_code ec, std::size_t) {
if (!ec) {
do_read_socket();
} else {
BOOST_LOG_TRIVIAL(error) << "Write to VSP failed: " << ec.message();
}
});
} else if (ec) {
BOOST_LOG_TRIVIAL(error) << "Read error on socket: " << ec.message();
fd_set read_fds;
char buffer[256];
int max_fd = std::max(vsp_.master_fd_.native_handle(), socketClient_.sock_fd) + 1;

while (true) {
FD_ZERO(&read_fds);
FD_SET(vsp_.master_fd_.native_handle(), &read_fds);
FD_SET(socketClient_.sock_fd, &read_fds);

if (select(max_fd, &read_fds, NULL, NULL, NULL) < 0 && errno != EINTR) {
std::cerr << "Select error: " << strerror(errno) << std::endl;
break;
}
});
}

void SerialClient::do_read_vsp() {
vsp_.async_read(boost::asio::buffer(buffer_), [this](boost::system::error_code ec, std::size_t length) {
if (!ec && length > 0) {
std::string data(buffer_.data(), length);
std::fill(std::begin(buffer_), std::end(buffer_), 0);

BOOST_LOG_TRIVIAL(debug) << "Received from VSP: " << data;
if (FD_ISSET(vsp_.master_fd_.native_handle(), &read_fds)) {
ssize_t bytes_read = vsp_.async_read(buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
std::cout << "From PTY: " << buffer;
socketClient_.sendToServer(buffer, bytes_read);
} else if (bytes_read == 0) {
std::cout << "PTY closed." << std::endl;
break;
} else {
std::cerr << "Error reading from PTY: " << strerror(errno) << std::endl;
}
}

async_write(socket_, boost::asio::buffer(data), [this](boost::system::error_code ec, std::size_t) {
if (!ec) {
do_read_vsp();
} else {
BOOST_LOG_TRIVIAL(error) << "Write to socket failed: " << ec.message();
}
});
} else if (ec) {
BOOST_LOG_TRIVIAL(error) << "Read error on VSP: " << ec.message();
if (FD_ISSET(socketClient_.sock_fd, &read_fds)) {
ssize_t bytes_read = socketClient_.receiveFromServer(buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
std::cout << "From Server: " << buffer;
vsp_.async_write(buffer, bytes_read);
} else if (bytes_read == 0) {
std::cout << "Server closed connection." << std::endl;
break;
} else {
std::cerr << "Error reading from socket: " << strerror(errno) << std::endl;
}
}
});
}
}
6 changes: 2 additions & 4 deletions src/SerialServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ void SerialServer::run() {
}

void SerialServer::start_accept() {
BOOST_LOG_TRIVIAL(info) << "SerialServer::start_accept";

acceptor_.async_accept(socket_, [this](boost::system::error_code ec) {
BOOST_LOG_TRIVIAL(info) << "SerialServer::acceptor::async_accept";
if (!ec) {
handle_session();
}
Expand Down Expand Up @@ -55,7 +52,8 @@ void SerialServer::async_read_socket() {
}

void SerialServer::async_read_serial() {
BOOST_LOG_TRIVIAL(info) << "SerialServer::async_read_serial";
// WIP: this works fine
// BOOST_LOG_TRIVIAL(info) << "SerialServer::async_read_serial";

serial_port_.async_read_some(boost::asio::buffer(buffer_), [this](boost::system::error_code ec, std::size_t length) {
if (!ec) {
Expand Down
88 changes: 57 additions & 31 deletions src/VirtualSerialPort.cpp
Original file line number Diff line number Diff line change
@@ -1,74 +1,100 @@
#include "VirtualSerialPort.h"

VirtualSerialPort::VirtualSerialPort(boost::asio::io_context& io_context, const std::string& device)
: master_fd_(io_context), device_name_("/dev/" + device) {
int master_fd = posix_openpt(O_RDWR | O_NOCTTY);
if (master_fd == -1 || grantpt(master_fd) != 0 || unlockpt(master_fd) != 0) {
: master_fd_(io_context), slave_fd_(io_context), device_name_("/dev/" + device) {
std::lock_guard<std::mutex> lock(mutex_);
int master_fd, slave_fd;
char* slave_name;
master_fd = posix_openpt(O_RDWR | O_NOCTTY);
if (master_fd == -1) {
BOOST_LOG_TRIVIAL(error) << "Failed to open PTY master: " << strerror(errno);
throw std::runtime_error("Failed to open PTY master");
}
BOOST_LOG_TRIVIAL(info) << "PTY master opened successfully";

char* slave_name = ptsname(master_fd);
if (!slave_name) {
throw std::runtime_error("Failed to get PTY slave name");
if (grantpt(master_fd) == -1 || unlockpt(master_fd) == -1 || (slave_name = ptsname(master_fd)) == nullptr) {
BOOST_LOG_TRIVIAL(error) << "Failed to grant or unlock PTY: " << strerror(errno);
throw std::runtime_error("Failed to grant or unlock PTY");
}
BOOST_LOG_TRIVIAL(info) << "PTY grant and unlock successful";
BOOST_LOG_TRIVIAL(info) << "Slave PTY name: " << slave_name << std::endl;

if (unlink(device_name_.c_str()) == -1 && errno != ENOENT) {
throw std::runtime_error("Failed to remove existing symlink");
// Attempt to create a symbolic link from slave_name to "/dev/ttyUSB0"
if (symlink(slave_name, device_name_.c_str()) == -1) {
BOOST_LOG_TRIVIAL(error) << "Failed to create symlink for PTY slave: " << strerror(errno);
throw std::runtime_error("Failed to create symlink for PTY slave");
}
BOOST_LOG_TRIVIAL(info) << "Symlink for PTY slave created successfully";

if (symlink(slave_name, device_name_.c_str()) != 0) {
throw std::runtime_error("Failed to create symlink for PTY slave");
// Open the slave pseudoterminal
slave_fd = open(slave_name, O_RDWR);
if (slave_fd == -1) {
BOOST_LOG_TRIVIAL(error) << "Failed to create symlink for PTY slave: " << strerror(errno);
throw std::runtime_error("Failed to open the slave pseudoterminal");
}



chmod(device_name_.c_str(), 0660);
struct group* tty_grp = getgrnam("tty");
if (tty_grp) {
chown(device_name_.c_str(), -1, tty_grp->gr_gid);
if (tty_grp && chown(device_name_.c_str(), -1, tty_grp->gr_gid) == -1) {
BOOST_LOG_TRIVIAL(error) << "Failed to change group of device: " << strerror(errno);
throw std::runtime_error("Failed to change group of device");
}
BOOST_LOG_TRIVIAL(info) << "Group changed successfully for the device";

master_fd_.assign(master_fd);
slave_fd_.assign(slave_fd);
setup_pty(master_fd);
}


VirtualSerialPort::~VirtualSerialPort() {
close();
setup_pty(slave_fd);
}

void VirtualSerialPort::setup_pty(int fd) {
struct termios tty;
memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0) {
BOOST_LOG_TRIVIAL(error) << "Error from tcgetattr: " << strerror(errno);
return;
}

cfmakeraw(&tty); // Configure the terminal attributes to raw mode
cfmakeraw(&tty);

tty.c_cflag |= (CLOCAL | CREAD); // Ignore modem controls and enable receiver
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; // 8-bit characters
tty.c_cflag &= ~PARENB; // No parity bit
tty.c_cflag &= ~CSTOPB; // Only need 1 stop bit
tty.c_cflag &= ~CRTSCTS; // No hardware flow control
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Disable canonical mode, echo, and signal chars
tty.c_oflag &= ~OPOST; // No output processing
tty.c_cflag |= CS8;
tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;

tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tty.c_oflag &= ~OPOST;

if (tcsetattr(fd, TCSANOW, &tty) != 0) {
BOOST_LOG_TRIVIAL(error) << "Error from tcsetattr: " << strerror(errno);
} else {
BOOST_LOG_TRIVIAL(info) << "PTY attributes set successfully";
}
}

void VirtualSerialPort::close() {
master_fd_.close();
slave_fd_.close();
unlink(device_name_.c_str());
}

void VirtualSerialPort::async_read(boost::asio::mutable_buffer buffer, std::function<void(const boost::system::error_code&, std::size_t)> handler) {
boost::asio::async_read(master_fd_, buffer, handler);
VirtualSerialPort::~VirtualSerialPort() {
close();
}

ssize_t VirtualSerialPort::async_read(char* buffer, unsigned int length) {
BOOST_LOG_TRIVIAL(info) << "VSP::async_read";
ssize_t bytes_read = read(master_fd_.native_handle(), buffer, length);
BOOST_LOG_TRIVIAL(info) << "READ FROM SERIAL!!!! -> ";
return bytes_read;
}

void VirtualSerialPort::async_write(boost::asio::const_buffer buffer, std::function<void(const boost::system::error_code&, std::size_t)> handler) {
boost::asio::async_write(master_fd_, buffer, handler);

ssize_t VirtualSerialPort::async_write(const char* buffer, unsigned int length) {
return write(master_fd_.native_handle(), buffer, length);
}

Loading

0 comments on commit 6d5a4ff

Please sign in to comment.