From 45ef1a1116f914266dbbd5bc6a3864d71ebee940 Mon Sep 17 00:00:00 2001 From: sophy-korp <108880258+sophy-korp@users.noreply.github.com> Date: Sun, 24 Nov 2024 16:29:49 +0300 Subject: [PATCH] Add files via upload --- SofiKorp/lab2/CMakeLists.txt | 39 +++++ SofiKorp/lab2/build.sh | 17 ++ SofiKorp/lab2/src/books_for_start.txt | 15 ++ SofiKorp/lab2/src/client/ClientWindow.hpp | 144 ++++++++++++++++ SofiKorp/lab2/src/client/client.cpp | 83 +++++++++ SofiKorp/lab2/src/client/client.hpp | 115 +++++++++++++ SofiKorp/lab2/src/client/fifo_names.hpp | 5 + SofiKorp/lab2/src/client/mq_names.hpp | 5 + SofiKorp/lab2/src/client/shm_names.hpp | 5 + .../lab2/src/connections/all_connections.hpp | 5 + SofiKorp/lab2/src/connections/conn.hpp | 15 ++ SofiKorp/lab2/src/connections/conn_fifo.cpp | 88 ++++++++++ SofiKorp/lab2/src/connections/conn_fifo.hpp | 28 +++ SofiKorp/lab2/src/connections/conn_mq.cpp | 78 +++++++++ SofiKorp/lab2/src/connections/conn_mq.hpp | 26 +++ SofiKorp/lab2/src/connections/conn_shm.cpp | 54 ++++++ SofiKorp/lab2/src/connections/conn_shm.hpp | 28 +++ SofiKorp/lab2/src/host/ConnectClient.hpp | 55 ++++++ SofiKorp/lab2/src/host/HostWindow.hpp | 162 ++++++++++++++++++ SofiKorp/lab2/src/host/fifo_names.hpp | 5 + SofiKorp/lab2/src/host/host.cpp | 75 ++++++++ SofiKorp/lab2/src/host/host.hpp | 59 +++++++ SofiKorp/lab2/src/host/mq_names.hpp | 5 + SofiKorp/lab2/src/host/shm_names.hpp | 5 + SofiKorp/lab2/src/includes/includes.hpp | 39 +++++ 25 files changed, 1155 insertions(+) create mode 100644 SofiKorp/lab2/CMakeLists.txt create mode 100644 SofiKorp/lab2/build.sh create mode 100644 SofiKorp/lab2/src/books_for_start.txt create mode 100644 SofiKorp/lab2/src/client/ClientWindow.hpp create mode 100644 SofiKorp/lab2/src/client/client.cpp create mode 100644 SofiKorp/lab2/src/client/client.hpp create mode 100644 SofiKorp/lab2/src/client/fifo_names.hpp create mode 100644 SofiKorp/lab2/src/client/mq_names.hpp create mode 100644 SofiKorp/lab2/src/client/shm_names.hpp create mode 100644 SofiKorp/lab2/src/connections/all_connections.hpp create mode 100644 SofiKorp/lab2/src/connections/conn.hpp create mode 100644 SofiKorp/lab2/src/connections/conn_fifo.cpp create mode 100644 SofiKorp/lab2/src/connections/conn_fifo.hpp create mode 100644 SofiKorp/lab2/src/connections/conn_mq.cpp create mode 100644 SofiKorp/lab2/src/connections/conn_mq.hpp create mode 100644 SofiKorp/lab2/src/connections/conn_shm.cpp create mode 100644 SofiKorp/lab2/src/connections/conn_shm.hpp create mode 100644 SofiKorp/lab2/src/host/ConnectClient.hpp create mode 100644 SofiKorp/lab2/src/host/HostWindow.hpp create mode 100644 SofiKorp/lab2/src/host/fifo_names.hpp create mode 100644 SofiKorp/lab2/src/host/host.cpp create mode 100644 SofiKorp/lab2/src/host/host.hpp create mode 100644 SofiKorp/lab2/src/host/mq_names.hpp create mode 100644 SofiKorp/lab2/src/host/shm_names.hpp create mode 100644 SofiKorp/lab2/src/includes/includes.hpp diff --git a/SofiKorp/lab2/CMakeLists.txt b/SofiKorp/lab2/CMakeLists.txt new file mode 100644 index 0000000..63d8e48 --- /dev/null +++ b/SofiKorp/lab2/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.23) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(library_system VERSION 0.1.0 LANGUAGES C CXX) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -Wall -Werror") +set(QT6_PATH "/usr/lib/x86_64-linux-gnu/cmake/Qt6") +set(CMAKE_PREFIX_PATH ${QT6_PATH}) + +find_package(Qt6 REQUIRED COMPONENTS Widgets) + +qt6_wrap_cpp(MOC_HEADERS_SERVER src/host/HostWindow.hpp) +qt6_wrap_cpp(MOC_HEADERS_CLIENT src/client/ClientWindow.hpp) + +add_executable(host_mq src/connections/conn_mq.cpp src/host/host.cpp ${MOC_HEADERS_SERVER}) +target_compile_definitions(host_mq PRIVATE MQ_COMMUNICATION) +target_link_libraries(host_mq Qt6::Widgets) + +add_executable(host_fifo src/connections/conn_fifo.cpp src/host/host.cpp ${MOC_HEADERS_SERVER}) +target_compile_definitions(host_fifo PRIVATE FIFO_COMMUNICATION) +target_link_libraries(host_fifo Qt6::Widgets) + +add_executable(host_shm src/connections/conn_shm.cpp src/host/host.cpp ${MOC_HEADERS_SERVER}) +target_compile_definitions(host_shm PRIVATE SHM_COMMUNICATION) +target_link_libraries(host_shm Qt6::Widgets) + +add_executable(client_mq src/connections/conn_mq.cpp src/client/client.cpp ${MOC_HEADERS_CLIENT}) +target_compile_definitions(client_mq PRIVATE MQ_COMMUNICATION) +target_link_libraries(client_mq Qt6::Widgets) + +add_executable(client_fifo src/connections/conn_fifo.cpp src/client/client.cpp ${MOC_HEADERS_CLIENT}) +target_compile_definitions(client_fifo PRIVATE FIFO_COMMUNICATION) +target_link_libraries(client_fifo Qt6::Widgets) + +add_executable(client_shm src/connections/conn_shm.cpp src/client/client.cpp ${MOC_HEADERS_CLIENT}) +target_compile_definitions(client_shm PRIVATE SHM_COMMUNICATION) +target_link_libraries(client_shm Qt6::Widgets) \ No newline at end of file diff --git a/SofiKorp/lab2/build.sh b/SofiKorp/lab2/build.sh new file mode 100644 index 0000000..c5f58a7 --- /dev/null +++ b/SofiKorp/lab2/build.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +BUILD_DIR="build" + +echo "Cleaning..." +rm -rf $BUILD_DIR +mkdir -p $BUILD_DIR +cd $BUILD_DIR + +echo "Generating Makefile using CMake..." +cmake .. + +echo "Compiling..." +make + +echo "Compiling completed successfully!" diff --git a/SofiKorp/lab2/src/books_for_start.txt b/SofiKorp/lab2/src/books_for_start.txt new file mode 100644 index 0000000..d42ddb9 --- /dev/null +++ b/SofiKorp/lab2/src/books_for_start.txt @@ -0,0 +1,15 @@ +# Формат: название книги : количество экземпляров +Дерево-людоед с тёмного холма: 3 +Токийский Зодиак: 2 +Дом кривых стен: 3 +Двойник с лунной дамбы: 5 +Хрустальная пирамида: 1 +Магистр дьявольского культа: 7 +Гоблин: 2 +Тим Таллер, или проданный смех: 4 +Голос монстра: 8 +Профайлер: 1 +Тэянг: 9 +Синий шёпот: 6 +Страшные сказки дядюшки Монтегю: 2 +Грокаем алгоритмы: 100 diff --git a/SofiKorp/lab2/src/client/ClientWindow.hpp b/SofiKorp/lab2/src/client/ClientWindow.hpp new file mode 100644 index 0000000..0ea6f81 --- /dev/null +++ b/SofiKorp/lab2/src/client/ClientWindow.hpp @@ -0,0 +1,144 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +class QLabel; + +class ClientWindow : public QMainWindow { + Q_OBJECT + +public: + explicit ClientWindow(int client_pid, QWidget *parent = nullptr) : ClientWindow(parent) + { + pid = client_pid; + } + + explicit ClientWindow(QWidget *parent = nullptr) : QMainWindow(parent) { + // Установка главного виджета + QWidget *centralWidget = new QWidget(this); + setCentralWidget(centralWidget); + + // Основной вертикальный макет + QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); + + // Таблица запросов + requestTable = new QTableWidget(this); + requestTable->setColumnCount(4); + requestTable->setHorizontalHeaderLabels({"Тип запроса", "Книга", "Дата и время", "Статус"}); + requestTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + requestTable->setSelectionMode(QAbstractItemView::NoSelection); + mainLayout->addWidget(requestTable); + + // Список книг клиента + QHBoxLayout *bookListLayout = new QHBoxLayout(); + QLabel *bookListLabel = new QLabel("Список книг клиента:", this); + bookList = new QListWidget(this); + bookListLayout->addWidget(bookListLabel); + bookListLayout->addWidget(bookList); + mainLayout->addLayout(bookListLayout); + + // Поле ввода для названия книги + QHBoxLayout *inputLayout = new QHBoxLayout(); + QLabel *inputLabel = new QLabel("Название книги:", this); + bookInput = new QLineEdit(this); + inputLayout->addWidget(inputLabel); + inputLayout->addWidget(bookInput); + mainLayout->addLayout(inputLayout); + + // Кнопки для отправки запросов + QHBoxLayout *buttonLayout = new QHBoxLayout(); + QPushButton *getBookButton = new QPushButton("Получить книгу", this); + QPushButton *returnBookButton = new QPushButton("Вернуть книгу", this); + buttonLayout->addWidget(getBookButton); + buttonLayout->addWidget(returnBookButton); + mainLayout->addLayout(buttonLayout); + + // Подключение сигналов и слотов + connect(getBookButton, &QPushButton::clicked, this, &ClientWindow::onGetBookClicked); + connect(returnBookButton, &QPushButton::clicked, this, &ClientWindow::onReturnBookClicked); + } + + void send_return_request_to_host(const std::string &request); + void send_take_request_to_host(const std::string &request); + + void addRequestToTable(const QString &type, const QString &book, const QString &status) const { + const int row = requestTable->rowCount(); + requestTable->insertRow(row); + + requestTable->setItem(row, 0, new QTableWidgetItem(type)); + requestTable->setItem(row, 1, new QTableWidgetItem(book)); + requestTable->setItem(row, 2, new QTableWidgetItem(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"))); + requestTable->setItem(row, 3, new QTableWidgetItem(status)); + } + void addBookToClientList(std::string& bookName) { + if (bookName.length() == 0) return; // Защита от добавления пустых строк + QString QbookName = QString::fromStdString(strip(bookName)); + bookList->addItem(QbookName); + } + + void deleteBookFromClientList(const QString &bookName) const { + if (bookName.isEmpty()) return; // Защита от пустого ввода + + // Поиск книги в списке клиента + for (int i = 0; i < bookList->count(); ++i) { + QListWidgetItem *item = bookList->item(i); + if (item->text() == bookName) { + delete bookList->takeItem(i); // Удаляем книгу из списка + return; // Завершаем после удаления + } + } + } + +private slots: + void onGetBookClicked() { + QString bookName = bookInput->text().trimmed(); + if (bookName.isEmpty()) { + addRequestToTable("Take", bookName, "Ошибка: Название книги не указано"); + return; + } + + std::string str_bookName = strip(bookName.toUtf8().constData()); + send_take_request_to_host(str_bookName); + bookInput->clear(); + } + + void onReturnBookClicked() { + QListWidgetItem *selectedItem = bookList->currentItem(); + if (!selectedItem) { + addRequestToTable("Return", "", "Ошибка: Книга не выбрана"); + return; + } + + std::string bookName = strip(selectedItem->text().toUtf8().constData()); + send_return_request_to_host(bookName); + delete selectedItem; + } + + +private: + int pid{}; + + std::string strip(std::string str) { + size_t first = str.find_first_not_of(' '); + if (first == std::string::npos) return ""; + size_t last = str.find_last_not_of(' '); + str = str.substr(first, last - first + 1); + + while (std::iscntrl(str[str.size() - 1])) { + str.erase(str.size() - 1); + } + + return str; + } + + QTableWidget *requestTable; + QListWidget *bookList; + QLineEdit *bookInput; +}; diff --git a/SofiKorp/lab2/src/client/client.cpp b/SofiKorp/lab2/src/client/client.cpp new file mode 100644 index 0000000..4e33029 --- /dev/null +++ b/SofiKorp/lab2/src/client/client.cpp @@ -0,0 +1,83 @@ +#ifdef FIFO_COMMUNICATION +#include "fifo_names.hpp" +#elif defined(MQ_COMMUNICATION) +#include "mq_names.hpp" +#elif defined(SHM_COMMUNICATION) +#include "shm_names.hpp" +#endif + +#include +#include "client.hpp" + +#include "ClientWindow.hpp" + +using namespace client_config; + +namespace +{ + TempClient& client = TempClient::getInstance(pid_file_path, identifier); + ClientWindow *mainwindow_pointer; +} + +void client_signal_handler(int sig, siginfo_t *info, void *context) +{ + std::cerr<<"client_signal_handler"<addBookToClientList(bookName); + } + mainwindow_pointer->addRequestToTable("Take", bookName.data(), status.data()); + + answer.clear(); + break; + case SIGUSR1: + std::cerr<<"read return answer" << std::endl; + client.readResponse(answer, true); + status = answer.substr(0, answer.find(' ')); + bookName = answer.substr(answer.find(' ') + 1); + std::cerr<< status << ' ' << bookName << std::endl; + if (status == "OK"){ + mainwindow_pointer->deleteBookFromClientList(bookName.data()); + } + std::cerr<<"remove book from client list"<addRequestToTable("Return", bookName.data(), status.data()); + + answer.clear(); + break; + default: + break; + } +} + + +void ClientWindow::send_return_request_to_host(const std::string &request){ + std::cerr << "send_return_request_to_host" << std::endl; + client.sendRequest(SIGUSR1, request); +} + +void ClientWindow::send_take_request_to_host(const std::string &request) { + std::cerr << "send_take_request_to_host" << std::endl; + client.sendRequest(SIGUSR2, request); +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + ClientWindow window; + mainwindow_pointer = &window; + + window.show(); + + return app.exec(); +} \ No newline at end of file diff --git a/SofiKorp/lab2/src/client/client.hpp b/SofiKorp/lab2/src/client/client.hpp new file mode 100644 index 0000000..6b2affd --- /dev/null +++ b/SofiKorp/lab2/src/client/client.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include "../includes/includes.hpp" +#include "../connections/all_connections.hpp" + +void client_signal_handler(int, siginfo_t*, void*); +void processIncomingRequests(int signal, const std::string& request); + +template +class ClientHandler +{ + friend class ClientWindow; + + int server_pid{}; + int client_pid{}; + std::vector connection_channels{}; + struct sigaction signal_handler{}; + friend void client_signal_handler(int, siginfo_t*, void*); + + ClientHandler(const std::string& pid_file_path, bool initialize) + { + std::fstream pid_file(pid_file_path); + if (!pid_file) + { + throw std::runtime_error( + "Configuration file not found: " + std::filesystem::absolute(pid_file_path).string()); + } + + pid_file >> server_pid; + pid_file.close(); + std::cerr << "Server PID: " << server_pid << std::endl; + + client_pid = getpid(); + std::cerr << "Client PID: " << client_pid << std::endl; + + signal_handler.sa_sigaction = client_signal_handler; + signal_handler.sa_flags = SA_SIGINFO; + + if (sigaction(SIGUSR1, &signal_handler, nullptr) == -1 || + sigaction(SIGUSR2, &signal_handler, nullptr) == -1) + { + throw std::runtime_error("Failed to register signal handlers."); + } + + kill(server_pid, SIGUSR1); + + sem_t* semaphore = sem_open( + (ConnectionType::make_filename(server_pid, client_pid) + "_init").c_str(), + O_CREAT, 0777, 0); + + timespec timeout{}; + if (clock_gettime(CLOCK_REALTIME, &timeout) == -1) + { + throw std::runtime_error("Failed to retrieve current time."); + } + + timeout.tv_sec += 5; + + int sem_wait_result; + while ((sem_wait_result = sem_timedwait(semaphore, &timeout)) == -1 && errno == EINTR) + { + continue; + } + + if (sem_wait_result == -1) + { + throw std::runtime_error("Semaphore wait timed out."); + } + + + connection_channels.emplace_back(ConnectionType::make_filename(server_pid, client_pid) + "r", initialize); + connection_channels.emplace_back(ConnectionType::make_filename(client_pid, server_pid) + "r", initialize); + connection_channels.emplace_back(ConnectionType::make_filename(server_pid, client_pid) + "ans", initialize); + connection_channels.emplace_back(ConnectionType::make_filename(client_pid, server_pid) + "ans", initialize); + + sem_unlink((ConnectionType::make_filename(server_pid, client_pid) + "_init").c_str()); + } + +public: + sem_t* client_semaphore{}; + std::string semaphore_name{}; + + static ClientHandler& getInstance(const std::string& pid_file_path, bool initialize = false) + { + static ClientHandler instance(pid_file_path, initialize); + return instance; + } + + bool sendRequest(int signal, const std::string& request) + { + int channel_index = (signal == SIGUSR1) ? 1 : 3; + std::cerr << "Sending request on channel " << channel_index << ": " << request << std::endl; + + bool result = connection_channels[channel_index].Write(request); + kill(server_pid, signal); + return result; + } + + bool readResponse(std::string& response, bool is_return_answer = true) + { + int channel_index = is_return_answer ? 0 : 2; + return connection_channels[channel_index].Read(response); + } + + ~ClientHandler() + { + sem_close(client_semaphore); + } +}; + +namespace client_config +{ + constexpr bool identifier = false; + const std::filesystem::path pid_file_path = std::filesystem::current_path() / "host.pid"; +} \ No newline at end of file diff --git a/SofiKorp/lab2/src/client/fifo_names.hpp b/SofiKorp/lab2/src/client/fifo_names.hpp new file mode 100644 index 0000000..1377d7a --- /dev/null +++ b/SofiKorp/lab2/src/client/fifo_names.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "client.hpp" + +using TempClient = ClientHandler; \ No newline at end of file diff --git a/SofiKorp/lab2/src/client/mq_names.hpp b/SofiKorp/lab2/src/client/mq_names.hpp new file mode 100644 index 0000000..669d91f --- /dev/null +++ b/SofiKorp/lab2/src/client/mq_names.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "client.hpp" + +using TempClient = ClientHandler; \ No newline at end of file diff --git a/SofiKorp/lab2/src/client/shm_names.hpp b/SofiKorp/lab2/src/client/shm_names.hpp new file mode 100644 index 0000000..efe53d6 --- /dev/null +++ b/SofiKorp/lab2/src/client/shm_names.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "client.hpp" + +using TempClient = ClientHandler; \ No newline at end of file diff --git a/SofiKorp/lab2/src/connections/all_connections.hpp b/SofiKorp/lab2/src/connections/all_connections.hpp new file mode 100644 index 0000000..9b1f46a --- /dev/null +++ b/SofiKorp/lab2/src/connections/all_connections.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "conn_fifo.hpp" +#include "conn_mq.hpp" +#include "conn_shm.hpp" \ No newline at end of file diff --git a/SofiKorp/lab2/src/connections/conn.hpp b/SofiKorp/lab2/src/connections/conn.hpp new file mode 100644 index 0000000..cfa5b42 --- /dev/null +++ b/SofiKorp/lab2/src/connections/conn.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "../includes/includes.hpp" + +class Connection +{ +public: + virtual bool Read(std::string&) = 0; + virtual bool Write(const std::string&) = 0; + virtual ~Connection() = default; + Connection() = default; +}; + +template +concept Conn = std::is_base_of_v; \ No newline at end of file diff --git a/SofiKorp/lab2/src/connections/conn_fifo.cpp b/SofiKorp/lab2/src/connections/conn_fifo.cpp new file mode 100644 index 0000000..44df43b --- /dev/null +++ b/SofiKorp/lab2/src/connections/conn_fifo.cpp @@ -0,0 +1,88 @@ +#include "conn_fifo.hpp" + +FIFOConnection::FIFOConnection(const std::string &name, bool create) +{ + // Указываем путь к FIFO файлу + std::cerr << name << std::endl; + pathname = std::filesystem::current_path() / name; + std::cerr << pathname << std::endl; + + // Создание FIFO файла, если флаг create установлен + if (create) + { + std::cerr << "Creating " << pathname << std::endl; + if (mkfifo(pathname.c_str(), 0777) == -1) + { + std::cerr << "Already created " << pathname << std::endl; + // Если файл уже существует, игнорируем ошибку + if (errno != EEXIST) + { + std::cerr << std::strerror(errno) << std::endl; + throw std::runtime_error("Failed to create FIFO channel"); + } + } + std::cerr << "Created: " << pathname << std::endl; + + } + + // Открытие FIFO канала + fd = open(pathname.c_str(), O_RDWR); + if (fd == -1) + { + std::cout << pathname << " " << std::strerror(errno) << std::endl; + throw std::runtime_error("Failed to open FIFO channel"); + } +} + +bool FIFOConnection::Read(std::string &message) +{ + char buffer[max_msg_size]; + memset(buffer, '\0', max_msg_size); + + // Открытие FIFO канала для чтения + fd = open(pathname.c_str(), O_RDWR); + if (fd == -1) + { + std::cout << pathname << " " << std::strerror(errno) << std::endl; + throw std::runtime_error("Failed to open FIFO channel"); + } + + // Чтение данных из FIFO канала + ssize_t bytes_read = read(fd, buffer, max_msg_size - 1); + if (bytes_read == -1) + { + return false; // Ошибка чтения + } + + // Запись прочитанного сообщения в строку + message.assign(buffer, bytes_read); + return true; +} + +bool FIFOConnection::Write(const std::string &message) +{ + // Открытие FIFO канала для записи + fd = open(pathname.c_str(), O_RDWR); + if (fd == -1) + { + std::cout << pathname << " " << std::strerror(errno) << std::endl; + throw std::runtime_error("Failed to open FIFO channel"); + } + + // Запись сообщения в FIFO канал + ssize_t bytesWritten = write(fd, message.c_str(), message.size()); + if (bytesWritten == -1) + { + std::cout << "Error writing to FIFO: " << strerror(errno) << "\n"; + return false; // Ошибка записи + } + return true; +} + +FIFOConnection::~FIFOConnection() +{ + // Закрытие канала и удаление FIFO файла + close(fd); + unlink(pathname.c_str()); + std::cerr<<"removed " << pathname << std::endl; +} \ No newline at end of file diff --git a/SofiKorp/lab2/src/connections/conn_fifo.hpp b/SofiKorp/lab2/src/connections/conn_fifo.hpp new file mode 100644 index 0000000..7a98181 --- /dev/null +++ b/SofiKorp/lab2/src/connections/conn_fifo.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "conn.hpp" + +class FIFOConnection final : public Connection +{ + std::string pathname; + const int max_msg_size = 1024; + int fd; + +public: + + FIFOConnection(const std::string &pathname, bool create); + bool Read(std::string & message) override; + bool Write(const std::string & message) override; + static std::string to_string() { return "fifo"; } + static std::string make_filename(int pid1, int pid2) + { + return to_string() + '_' + std::to_string(pid1) + '_' + std::to_string(pid2); + } + ~FIFOConnection() override; + + FIFOConnection() = default; + FIFOConnection(const FIFOConnection&) = default; + FIFOConnection& operator = (const FIFOConnection&) = default; + FIFOConnection(FIFOConnection &&) = default; + FIFOConnection &operator=(FIFOConnection &&) = default; +}; \ No newline at end of file diff --git a/SofiKorp/lab2/src/connections/conn_mq.cpp b/SofiKorp/lab2/src/connections/conn_mq.cpp new file mode 100644 index 0000000..2666832 --- /dev/null +++ b/SofiKorp/lab2/src/connections/conn_mq.cpp @@ -0,0 +1,78 @@ +#include "conn_mq.hpp" + +MQConnection::MQConnection(const std::string &id, bool create) + : name("/queue_" + id), attr() +{ + // Настройка атрибутов очереди сообщений + attr.mq_flags = 0; + attr.mq_curmsgs = 0; + attr.mq_maxmsg = max_msg_count; + attr.mq_msgsize = max_msg_size; + + + // Открытие очереди сообщений с флагом создания или без + if (create) + { + mq = mq_open(name.c_str(), O_CREAT | O_RDWR | O_NONBLOCK, 0777, &attr); + } + else + { + mq = mq_open(name.c_str(), O_RDWR | O_NONBLOCK); + } + + if (mq == (mqd_t)-1) + { + std::cerr << "Error opening message queue: " << strerror(errno) << std::endl; + throw std::runtime_error("Error opening message queue"); + } +} + +bool MQConnection::Read(std::string &message) +{ + char buffer[max_msg_size]; + memset(buffer, 0, sizeof(buffer)); + + // Открытие очереди сообщений для чтения + mq = mq_open(name.c_str(), O_RDWR | O_NONBLOCK); + if (mq == (mqd_t)-1) + { + std::cerr << "Error opening message queue: " << strerror(errno) << std::endl; + throw std::runtime_error("Error opening message queue"); + } + + // Чтение сообщения из очереди + ssize_t bytesReceived = mq_receive(mq, buffer, sizeof(buffer), nullptr); + if (bytesReceived == -1) + { + return false; // Ошибка чтения + } + + // Присваивание прочитанного сообщения + message.assign(buffer, bytesReceived); + return true; +} + +bool MQConnection::Write(const std::string &message) +{ + // Открытие очереди сообщений для записи + mq = mq_open(name.c_str(), O_RDWR | O_NONBLOCK); + if (mq == (mqd_t)-1) + { + std::cerr << "Error opening message queue: " << strerror(errno) << std::endl; + throw std::runtime_error("Error opening message queue"); + } + + // Отправка сообщения в очередь + if (mq_send(mq, message.c_str(), message.size(), 0) == -1) + { + return false; // Ошибка отправки + } + + return true; +} + +MQConnection::~MQConnection() +{ + // Закрытие очереди сообщений + mq_close(mq); +} \ No newline at end of file diff --git a/SofiKorp/lab2/src/connections/conn_mq.hpp b/SofiKorp/lab2/src/connections/conn_mq.hpp new file mode 100644 index 0000000..12e3e40 --- /dev/null +++ b/SofiKorp/lab2/src/connections/conn_mq.hpp @@ -0,0 +1,26 @@ +#pragma once +#include "conn.hpp" + +class MQConnection final : public Connection +{ + std::string name; + mq_attr attr; + const int max_msg_size = 1024; + const int max_msg_count = 10; + mqd_t mq; +public: + MQConnection(const std::string &id, bool create); + bool Read(std::string &) override; + bool Write(const std::string &) override; + static std::string to_string() { return "mq"; } + static std::string make_filename(int pid1, int pid2) + { + return to_string() + '_' + std::to_string(pid1) + '_' + std::to_string(pid2); + } + ~MQConnection() override; + MQConnection() = default; + MQConnection(const MQConnection &) = default; + MQConnection &operator=(const MQConnection &) = default; + MQConnection(MQConnection &&) = default; + MQConnection &operator=(MQConnection &&) = default; +}; \ No newline at end of file diff --git a/SofiKorp/lab2/src/connections/conn_shm.cpp b/SofiKorp/lab2/src/connections/conn_shm.cpp new file mode 100644 index 0000000..e63a83e --- /dev/null +++ b/SofiKorp/lab2/src/connections/conn_shm.cpp @@ -0,0 +1,54 @@ +#include "conn_shm.hpp" + +SHMConnection::SHMConnection(const std::string &id, bool create) + : name("/shm_" + id) +{ + // Открытие разделяемой памяти + fd = shm_open(name.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) + { + std::cerr << "Error shm opening: " << strerror(errno) << std::endl; + throw std::runtime_error("Error opening shm"); + } + + // Установка размера разделяемой памяти + res = ftruncate(fd, max_msg_size); + if (res == -1) + { + std::cerr << "Error shm ftruncate: " << strerror(errno) << std::endl; + throw std::runtime_error("Error opening ftruncate"); + } + + // Отображение разделяемой памяти в адресное пространство + addr = mmap(NULL, max_msg_size, PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) + { + std::cerr << "Error shm mmap: " << strerror(errno) << std::endl; + throw std::runtime_error("Error opening mmap"); + } +} + +bool SHMConnection::Read(std::string &message) +{ + char buffer[max_msg_size]; + + // Копирование данных из разделяемой памяти в буфер + memcpy(buffer, addr, max_msg_size); + + // Присваивание строки с учетом длины данных + message.assign(buffer, strnlen(buffer, max_msg_size)); + return true; +} + +bool SHMConnection::Write(const std::string &message) +{ + // Копирование данных в разделяемую память + memcpy(addr, message.c_str(), message.size() + 1); + return true; +} + +SHMConnection::~SHMConnection() +{ + // Удаление разделяемой памяти + shm_unlink(name.c_str()); +} \ No newline at end of file diff --git a/SofiKorp/lab2/src/connections/conn_shm.hpp b/SofiKorp/lab2/src/connections/conn_shm.hpp new file mode 100644 index 0000000..6835005 --- /dev/null +++ b/SofiKorp/lab2/src/connections/conn_shm.hpp @@ -0,0 +1,28 @@ +#pragma once +#include "conn.hpp" + +class SHMConnection final : public Connection +{ + std::string name; + const int max_msg_size = 1024; + int res; + int fd; + void* addr; + +public: + SHMConnection(const std::string &id, bool create); + bool Read(std::string &) override; + bool Write(const std::string &) override; + static std::string to_string() { return "shm"; } + static std::string make_filename(int pid1, int pid2) + { + return to_string() + '_' + std::to_string(pid1) + '_' + std::to_string(pid2); + } + ~SHMConnection() override; + + SHMConnection() = default; + SHMConnection(const SHMConnection &) = default; + SHMConnection &operator=(const SHMConnection &) = default; + SHMConnection(SHMConnection &&) = default; + SHMConnection &operator=(SHMConnection &&) = default; +}; \ No newline at end of file diff --git a/SofiKorp/lab2/src/host/ConnectClient.hpp b/SofiKorp/lab2/src/host/ConnectClient.hpp new file mode 100644 index 0000000..5fc0f92 --- /dev/null +++ b/SofiKorp/lab2/src/host/ConnectClient.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include "../includes/includes.hpp" +#include "../connections/all_connections.hpp" + +template +class ConnectClient +{ + int host_pid; + int pid; + std::vector connections; + +public: + ConnectClient(int host_pid, int pid, bool create = true) : host_pid(host_pid), pid(pid) + { + sem_t* semaphore = sem_open((T::make_filename(host_pid, pid) + "_init").c_str(), O_CREAT, 0777, 0); + if (semaphore == SEM_FAILED) + throw std::runtime_error("Error to create a semaphore!"); + + connections.emplace_back(T::make_filename(host_pid, pid) + "r", create); // send return answer to client + connections.emplace_back(T::make_filename(pid, host_pid) + "r", create) ; // read return request from client + connections.emplace_back(T::make_filename(host_pid, pid) + "ans", create); // send take answer to client + connections.emplace_back(T::make_filename(pid, host_pid) + "ans", create); // read take request from client + + std::cerr< +#include +#include +#include +#include +#include +#include + +class HostWindow final : public QMainWindow { + Q_OBJECT + +public: + explicit HostWindow(QWidget *parent = nullptr) : QMainWindow(parent) { + // Установка центрального виджета + auto *centralWidget = new QWidget(this); + this->resize(1200, 600); + setCentralWidget(centralWidget); + + // Основной вертикальный макет + auto *mainLayout = new QVBoxLayout(centralWidget); + + // Таблица книг + bookTable = new QTableWidget(this); + bookTable->setColumnCount(4); + bookTable->setHorizontalHeaderLabels({"Название", "Доступно", "Всего", "История выдачи"}); + bookTable->horizontalHeader()->setStretchLastSection(true); + bookTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + mainLayout->addWidget(bookTable); + + // Заполнение начальных данных + ReadStartBooksList(); + } + + void ReturnBook(int client_id, std::string book_name) { + std::cerr<<"return book script"<rowCount(); ++row) { + if (bookTable->item(row, 0)->text().toUtf8().data() == book_name) { + // Ищем запись о данном клиенте + std::cerr<<"book found"<cellWidget(row, 3); + if (auto *listWidget = qobject_cast(cellWidget)) { + // Перебираем все элементы QListWidget + for (int i = 0; i < listWidget->count(); ++i) { + QListWidgetItem *item = listWidget->item(i); + if (item && item->text().contains(QString("Client%1 ").arg(client_id))) { + // Элемент найден, выделяем его + QListWidgetItem *removedItem = listWidget->takeItem(i); + delete removedItem; + break; + } + } + + // Увеличиваем доступное количество экземпляров + int available = bookTable->item(row, 1)->text().toInt(); + bookTable->item(row, 1)->setText(QString::number(available + 1)); + + answer = "OK " + book_name; + break; + } + } + } + send_return_answer_to_client(client_id, answer); + } + void GiveBook(int client_id, std::string book_name) { + // TODO: отдать книгу. Доступных экземпляров меньше, клиент в списке должников + сообщение в конце + std::lock_guard lock(mtx); + std::cerr<<"Try to give book"<rowCount()<rowCount(); ++i) { + std::string curBookName = bookTable->item(i, 0)->text().toUtf8().constData(); + std::cerr<item(i, 1)->text().toUtf8().constData()); + std::cerr<cellWidget(i, 3); + if (auto *listWidget = qobject_cast(cellWidget)) { + // Перебираем все элементы QListWidget + bool f = false; + for (int i = 0; i < listWidget->count(); ++i) { + QListWidgetItem *item = listWidget->item(i); + if (item && item->text().contains(QString("Client%1 ").arg(client_id))) { + // Элемент найден + answer = "ALREADY " + book_name; + f = true; + break; + } + } + if (f) break; + } + + if (countAvailable > 0) { + answer = "OK " + curBookName; + countAvailable --; + bookTable -> setItem(i, 1, new QTableWidgetItem(QString::number(countAvailable))); + // Добавляем запись в историю выдачи + QString newEntry = "Client" + QString::number(client_id) + " " + QString(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")); + std::cerr<cellWidget(i, 3); + if (auto *listWidget = qobject_cast(cellWidget)) { + // Изменяем содержимое + listWidget->addItem(newEntry); + } + } + else { + answer = "NO " + curBookName; + } + break; + } + } + send_take_answer_to_client(client_id, answer); + } + + void send_take_answer_to_client(int client_id, std::string answer); + void send_return_answer_to_client(int client_id, std::string answer); + +private: + std::mutex mtx; + void ReadStartBooksList() const { + std::ifstream file("../src/books_for_start.txt"); + if (!file.is_open()) { + std::cerr<<"M"; + return; + } + std::string line; + while (std::getline(file, line)){ + if (line[0] == '#') + continue; + std::string bookName = line.substr(0, line.find(':')); + + int count ; + try{ + count = std::stoi(line.substr(line.find(':') + 1, line.length())); + } + catch(...){ + count = 0; + } + addBook(bookName.data(), count, count); + } + } + + void addBook(const QString &title, const int available, const int total) const { + const int row = bookTable->rowCount(); + bookTable->insertRow(row); + + bookTable->setItem(row, 0, new QTableWidgetItem(title)); + bookTable->setItem(row, 1, new QTableWidgetItem(QString::number(available))); + bookTable->setItem(row, 2, new QTableWidgetItem(QString::number(total))); + QListWidget *historyList = new QListWidget(); + bookTable->setCellWidget(row, 3, historyList); // История выдачи пустая изначально + } + + QTableWidget *bookTable; +}; diff --git a/SofiKorp/lab2/src/host/fifo_names.hpp b/SofiKorp/lab2/src/host/fifo_names.hpp new file mode 100644 index 0000000..24850cf --- /dev/null +++ b/SofiKorp/lab2/src/host/fifo_names.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "host.hpp" +using TempHost = Host; +using TempClientInfo = ConnectClient; \ No newline at end of file diff --git a/SofiKorp/lab2/src/host/host.cpp b/SofiKorp/lab2/src/host/host.cpp new file mode 100644 index 0000000..65eeeca --- /dev/null +++ b/SofiKorp/lab2/src/host/host.cpp @@ -0,0 +1,75 @@ +#ifdef FIFO_COMMUNICATION +#include "fifo_names.hpp" +#elif defined(MQ_COMMUNICATION) +#include "mq_names.hpp" +#elif defined(SHM_COMMUNICATION) +#include "shm_names.hpp" +#endif +#include + +#include "HostWindow.hpp" +#include "host.hpp" + +using namespace host_namespace; + +namespace +{ + TempHost& host = TempHost::get_instance(host_pid_path, identifier); + HostWindow *window_ptr; +} + +void host_signal_handler(int sig, siginfo_t *info, void *context) +{ + std::cerr<<"host_signal_handler"<si_pid)) + { + host.table.emplace(info->si_pid, TempClientInfo{getpid(), info->si_pid, identifier}); + return; + } + std::string request = " "; + + switch (sig) + { + case SIGUSR1: + std::cerr << "read return request from client" << std::endl; + host.table[info->si_pid].read_return_request_from_client(request); + std::cerr< ReturnBook(info->si_pid, request); + request.clear(); + break; + case SIGUSR2: + std::cerr << "read take request from client" << std::endl; + host.table[info->si_pid].read_take_request_from_client(request); + std::cerr< GiveBook(info->si_pid, request); + request.clear(); + break; + default: + break; + } +} + +void HostWindow::send_take_answer_to_client(int client_id, std::string answer) { + std::cerr<<"send_take_answer_to_client"< +class Host +{ + int pid; + std::string pid_path; + struct sigaction signal_handler; + std::unordered_map> table; + friend void host_signal_handler(int, siginfo_t *, void *); + friend class HostWindow; + + + + Host(const std::string &pid_path, bool create) : pid(getpid()), pid_path(pid_path), table() + { + std::ofstream file(pid_path); + if (!file) + throw std::runtime_error("No config file: " + std::string(std::filesystem::absolute(pid_path))); + file << pid; + std::cerr << pid << std::endl; + file.close(); + signal_handler.sa_sigaction = host_signal_handler; + signal_handler.sa_flags = SA_SIGINFO; + if (sigaction(SIGUSR1, &signal_handler, nullptr) == -1) + { + throw std::runtime_error("Failed to register signal handler"); + } + if (sigaction(SIGUSR2, &signal_handler, nullptr) == -1) + { + throw std::runtime_error("Failed to register signal handler"); + } + } + +public: + + static Host &get_instance(const std::string &pid_path, bool create) + { + static Host instance(pid_path, create); + return instance; + } + + ~Host() + { + remove(pid_path.c_str()); + } + +}; + +namespace host_namespace +{ + const bool identifier = true; + const std::filesystem::path host_pid_path = std::filesystem::current_path() / "host.pid"; +} \ No newline at end of file diff --git a/SofiKorp/lab2/src/host/mq_names.hpp b/SofiKorp/lab2/src/host/mq_names.hpp new file mode 100644 index 0000000..bca9ab8 --- /dev/null +++ b/SofiKorp/lab2/src/host/mq_names.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "host.hpp" +using TempHost = Host; +using TempClientInfo = ConnectClient; \ No newline at end of file diff --git a/SofiKorp/lab2/src/host/shm_names.hpp b/SofiKorp/lab2/src/host/shm_names.hpp new file mode 100644 index 0000000..6f57bb6 --- /dev/null +++ b/SofiKorp/lab2/src/host/shm_names.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "host.hpp" +using TempHost = Host; +using TempClientInfo = ConnectClient; \ No newline at end of file diff --git a/SofiKorp/lab2/src/includes/includes.hpp b/SofiKorp/lab2/src/includes/includes.hpp new file mode 100644 index 0000000..12d7ea5 --- /dev/null +++ b/SofiKorp/lab2/src/includes/includes.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include \ No newline at end of file