Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Teplov_Andrey 5030102.10201/Teplov_lab2/Book.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <string>

struct Book
{
std::string name;
int count;
};
73 changes: 73 additions & 0 deletions Teplov_Andrey 5030102.10201/Teplov_lab2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
cmake_minimum_required(VERSION 3.5)

project(DamirAsanov)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")

find_package(Qt5 COMPONENTS Widgets REQUIRED)

set(CMAKE_AUTOMOC ON) # for Q_OBJECT

############ sockets ############
set(SOURCES_SOCK
host_sock.cpp
HostUtils/HostWindow.cpp
ClientUtils/ClientWindow.cpp
conn_sock.cpp
semaphore.cpp
)

set(HEADERS_SOCK
HostUtils/HostWindow.hpp
ClientUtils/ClientWindow.hpp
conn_sock.hpp
semaphore.hpp
logger.hpp
)

add_executable(host_sock ${SOURCES_SOCK} ${HEADERS_SOCK})
target_link_libraries(host_sock Qt5::Widgets)
target_include_directories(host_sock PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

############ queue ############
set(SOURCES_MQ
host_mq.cpp
HostUtils/HostWindow.cpp
ClientUtils/ClientWindow.cpp
conn_mq.cpp
semaphore.cpp
)

set(HEADERS_MQ
HostUtils/HostWindow.hpp
ClientUtils/ClientWindow.hpp
conn_mq.hpp
semaphore.hpp
logger.hpp
)

add_executable(host_mq ${SOURCES_MQ} ${HEADERS_MQ})
target_link_libraries(host_mq Qt5::Widgets)
target_include_directories(host_mq PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

############ fifo ############
set(SOURCES_FIFO
host_fifo.cpp
HostUtils/HostWindow.cpp
ClientUtils/ClientWindow.cpp
conn_fifo.cpp
semaphore.cpp
)

set(HEADERS_FIFO
HostUtils/HostWindow.hpp
ClientUtils/ClientWindow.hpp
conn_fifo.hpp
semaphore.hpp
logger.hpp
)

add_executable(host_fifo ${SOURCES_FIFO} ${HEADERS_FIFO})
target_link_libraries(host_fifo Qt5::Widgets)
target_include_directories(host_fifo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

119 changes: 119 additions & 0 deletions Teplov_Andrey 5030102.10201/Teplov_lab2/ClientUtils/ClientWindow.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include "ClientWindow.hpp"
#include <QMessageBox>
#include <QDockWidget>
#include <QDateTime>
#include <iostream>
#include "logger.hpp"

ClientWindow::ClientWindow(const std::vector<Book>& books, QWidget* parent)
: QMainWindow(parent) {
stackedWidget = new QStackedWidget(this);

createBookView(books);
createReadingView();
createHistoryView();

setCentralWidget(stackedWidget);
setWindowTitle("Client Window");
resize(400, 300);

// Set started window
stackedWidget->setCurrentIndex(0);
}

ClientWindow::~ClientWindow() {}

void ClientWindow::createBookView(const std::vector<Book>& books) {
QWidget* bookView = new QWidget(this);
QVBoxLayout* layout = new QVBoxLayout(bookView);

bookList = new QListWidget(this);
for (const auto& book : books) {
bookList->addItem(QString::fromStdString(book.name) + " - " + QString::number(book.count) + " copies");
}
layout->addWidget(bookList);

selectButton = new QPushButton("Select Book", this);
selectButton->setEnabled(false);
layout->addWidget(selectButton);

//terminateClientButton = new QPushButton("Terminate Client", this);
//layout->addWidget(terminateClientButton);

// Enable button if book is choosed
connect(bookList, &QListWidget::itemSelectionChanged, [this]() {
selectButton->setEnabled(bookList->currentItem() != nullptr);
});
connect(selectButton, &QPushButton::clicked, this, &ClientWindow::selectBook);
//connect(terminateClientButton, &QPushButton::clicked, this, &ClientWindow::terminateClient);

stackedWidget->addWidget(bookView);
}

void ClientWindow::createReadingView() {
QWidget* readingView = new QWidget(this);
QVBoxLayout* layout = new QVBoxLayout(readingView);

readingLabel = new QLabel("Reading book: ", this);
layout->addWidget(readingLabel);

cancelReadingButton = new QPushButton("Cancel Reading", this);
layout->addWidget(cancelReadingButton);

connect(cancelReadingButton, &QPushButton::clicked, this, &ClientWindow::cancelReading);

stackedWidget->addWidget(readingView);
}

void ClientWindow::createHistoryView() {
historyList = new QListWidget(this);
historyList->setFixedWidth(350);
auto* widget = new QDockWidget("History", this);
widget->setWidget(historyList);
addDockWidget(Qt::LeftDockWidgetArea, widget);
}

void ClientWindow::selectBook() {
if (bookList->currentItem()) {
QString bookName = bookList->currentItem()->text().split(" - ").first();
readingLabel->setText("Reading book: " + bookName);

emit bookSelected(bookName);
}
}

void ClientWindow::cancelReading() {
QString bookName = readingLabel->text().split(": ").last();
emit bookReturned(bookName);

// Set started window
stackedWidget->setCurrentIndex(0);

addHistory("Cancelled reading: ", bookName, true);
}

void ClientWindow::addHistory(const QString& action, const QString& bookName, bool success) {
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
QString status = success ? "SUCCESS" : "FAIL";
historyList->addItem(QString("[%1] %2 \"%3\": %4").arg(timestamp, action, bookName, status));
}

void ClientWindow::onSuccessTakeBook() {
// Set reading window
stackedWidget->setCurrentIndex(1);

addHistory("TAKE book: ", bookList->currentItem()->text().split(" - ").first(), true);
}

void ClientWindow::onFailedTakeBook() {
// Cannot display this window....
// QMessageBox::warning(nullptr, "FAIL", "Failed to take book");
LoggerClient::get_instance().log(Status::ERROR, "Failed to take book");

addHistory("TAKE book: ", bookList->currentItem()->text().split(" - ").first(), false);
}

void ClientWindow::terminateClient() {
QMessageBox::information(this, "Terminate Client", "Client terminated.");
std::exit(0); // TODO: exit only qapp, but don't whole process
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

#include <QMainWindow>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QScrollArea>
#include <QListWidget>
#include <QStackedWidget>
#include <QString>
#include <vector>
#include <string>
#include <QWidget>
#include "Book.hpp"

class ClientWindow : public QMainWindow {
Q_OBJECT

public:
ClientWindow(const std::vector<Book>& books, QWidget* parent = nullptr);
~ClientWindow();

void onSuccessTakeBook();
void onFailedTakeBook();
void addHistory(const QString& action, const QString& bookName, bool success);

signals:
void bookSelected(const QString& bookName);
void bookReturned(const QString& bookName);

private slots:
void selectBook();
void cancelReading();
void terminateClient();

private:
void createBookView(const std::vector<Book>& books);
void createReadingView();
void createHistoryView();

QStackedWidget* stackedWidget; // For switch windows (started <-> reading)

QListWidget* historyList;
QListWidget* bookList;
QPushButton* selectButton;

QLabel* readingLabel;
QPushButton* cancelReadingButton;

// QPushButton* terminateClientButton;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include <QApplication>
#include "ClientWindow.hpp"
#include "conn_sock.hpp"
#include "logger.hpp"
#include "semaphore.hpp"
#include <sstream>
#include <thread>
#include <signal.h>

void listenForHostMessages(conn& conn, Semaphore& semaphore, ClientWindow& window, std::atomic<bool>& is_ranning) {
auto& logger = LoggerClient::get_instance();

while (is_ranning) {

semaphore.Wait(); // Start critical section
char buffer[64] = {0};
if (conn.Read(buffer, sizeof(buffer))) {
std::string response(buffer);

if (response == "YES") {
window.onSuccessTakeBook();
logger.log(Status::INFO, "Host gives the book");
}
else if (response == "NO") {
window.onFailedTakeBook();
logger.log(Status::INFO, "Host rejected the request: no such book");
}
else {
logger.log(Status::ERROR, "Unexpected response from host: " + response);
}
}

semaphore.Post(); // End critical section
sleep(0.01);
}
}

int processClient(Semaphore& semaphore, conn& conn, const std::vector<Book>& books) {
auto& logger = LoggerClient::get_instance();

int argc = 0;
char** argv = nullptr;
QApplication app(argc, argv);
ClientWindow window(books);

std::atomic<bool> is_running(true);

// Start thread with listenning socket
std::thread listenerThread(listenForHostMessages, std::ref(conn), std::ref(semaphore), std::ref(window), std::ref(is_running));

QObject::connect(&window, &ClientWindow::bookSelected, [&semaphore, &conn, &logger](const QString& bookName) {
std::string request = "TAKE " + bookName.toStdString();
if (conn.Write(request.c_str(), request.size())) {
logger.log(Status::INFO, "Requested book: " + bookName.toStdString());
}
else {
logger.log(Status::ERROR, "Failed to request the book: " + bookName.toStdString());
}
});

QObject::connect(&window, &ClientWindow::bookReturned, [&conn, &logger](const QString& bookName) {
std::string request = "RETURN " + bookName.toStdString();
if (conn.Write(request.c_str(), request.size())) {
logger.log(Status::INFO, "Returned book: " + bookName.toStdString());
}
else {
logger.log(Status::ERROR, "Failed to return the book: " + bookName.toStdString());
}
});

window.show();
int result = app.exec();

// Complete thread with listenning socket
is_running = false;
if (listenerThread.joinable()) {
listenerThread.join();
}

return result;
}
Loading