diff --git a/Makefile b/Makefile index 957fcde..419516d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ MAKE_FLAGS += -j -SRC := $(wildcard src/*.cpp) $(wildcard src/Pizza/*.cpp) $(wildcard src/Pizza/Pizzas/*.cpp) +SRC := $(wildcard src/*.cpp) $(wildcard src/Pizza/*.cpp) $(wildcard src/Pizza/Pizzas/*.cpp) $(wildcard src/IPC/*.cpp) $(wildcard src/Logger/*.cpp) BUILD_DIR := .build @@ -13,6 +13,8 @@ CXXFLAGS += -Wwrite-strings -Werror=format-nonliteral -Werror=return-type CXXFLAGS += -std=c++20 -iquote src -iquote src/Server -iquote src/Client -iquote libs/myteams CXXFLAGS += -Isrc/Pizza CXXFLAGS += -Isrc/Pizza/Pizzas +CXXFLAGS += -Isrc/IPC +CXXFLAGS += -Isrc/Logger include utils.mk diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..8761ac4 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1778869304, + "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..519e888 --- /dev/null +++ b/flake.nix @@ -0,0 +1,48 @@ +{ + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + outputs = { self, nixpkgs }: let + forAllSystems = function: + nixpkgs.lib.genAttrs [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + ] (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + + isDarwinAarch64 = + pkgs.stdenv.hostPlatform.isDarwin && + pkgs.stdenv.hostPlatform.isAarch64; + in + function pkgs isDarwinAarch64 + ); + in { + devShells = forAllSystems (pkgs: isDarwinAarch64: { + default = pkgs.mkShell { + hardeningDisable = [ "fortify" ]; + packages = with pkgs; + [ + compiledb + clang + llvm_21 + gnumake + criterion + valgrind + ]; + }; + bonus = pkgs.mkShell { + packages = [ + (pkgs.python3.withPackages (ps: [ ps.matplotlib ps.numpy ])) + ]; + }; + }); + + formatter = forAllSystems (pkgs: _: pkgs.alejandra); + + packages = forAllSystems (pkgs: _: { + default = self.packages.${pkgs.system}._scom; + _scom = pkgs.callPackage ./nix/package.nix { }; + }); + }; +} diff --git a/src/IPC/Channel.cpp b/src/IPC/Channel.cpp new file mode 100644 index 0000000..dbfa840 --- /dev/null +++ b/src/IPC/Channel.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include + +#include "Logger/Logger.hpp" +#include "Channel.hpp" + +namespace plazza { + +static std::string errstr(const char *prefix) { + return std::string(prefix) + ": " + std::strerror(errno); +} + +static const char *msgTypeName(MsgType t) { + switch (t) { + case MsgType::ORDER: + return "ORDER"; + case MsgType::READY: + return "READY"; + case MsgType::STATUS_REQ: + return "STATUS_REQ"; + case MsgType::STATUS_RES: + return "STATUS_RES"; + case MsgType::CLOSE: + return "CLOSE"; + case MsgType::CLOSED: + return "CLOSED"; + } + return "UNKNOWN"; +} + +Channel::Channel() : _rfd(-1), _wfd(-1) { + if (::pipe(_toChild) == -1 || ::pipe(_toParent) == -1) + throw std::runtime_error(errstr("pipe")); + LOG_DEBUG("Channel created"); +} + +Channel::~Channel() { + closeFd(_toChild[0]); + closeFd(_toChild[1]); + closeFd(_toParent[0]); + closeFd(_toParent[1]); +} + +void Channel::closeFd(int &fd) { + if (fd != -1) { + ::close(fd); + fd = -1; + } +} + +void Channel::parentSide() { + closeFd(_toChild[0]); + closeFd(_toParent[1]); + _wfd = _toChild[1]; + _rfd = _toParent[0]; + LOG_DEBUG("Channel configured as parent"); +} + +void Channel::childSide() { + closeFd(_toChild[1]); + closeFd(_toParent[0]); + _rfd = _toChild[0]; + _wfd = _toParent[1]; + LOG_DEBUG("Channel configured as child"); +} + +Channel &Channel::operator<<(const Message &msg) { + LOG_DEBUG(std::string("Channel send: ") + msgTypeName(msg.type)); + if (::write(_wfd, &msg, sizeof(msg)) == -1) + throw std::runtime_error(errstr("write")); + return *this; +} + +Channel &Channel::operator>>(Message &msg) { + ssize_t n = ::read(_rfd, &msg, sizeof(msg)); + if (n == 0) { + LOG_WARN("Channel closed by remote"); + throw std::runtime_error("channel closed"); + } + if (n == -1) + throw std::runtime_error(errstr("read")); + LOG_DEBUG(std::string("Channel recv: ") + msgTypeName(msg.type)); + return *this; +} + +bool Channel::tryRead(Message &msg) { + struct pollfd pfd{_rfd, POLLIN, 0}; + if (::poll(&pfd, 1, 0) <= 0) + return false; + *this >> msg; + return true; +} + +} // namespace plazza diff --git a/src/IPC/Channel.hpp b/src/IPC/Channel.hpp new file mode 100644 index 0000000..87741d0 --- /dev/null +++ b/src/IPC/Channel.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "IPizza.hpp" + +namespace plazza { + +static constexpr int INGREDIENT_COUNT = 9; + +enum class MsgType { + ORDER = 1, + READY = 2, + STATUS_REQ = 3, + STATUS_RES = 4, + CLOSE = 5, + CLOSED = 6, +}; + +struct Message { + MsgType type; + PizzaType pizzaType; + PizzaSize pizzaSize; + int cooksBusy; + int cooksTotal; + int pizzasQueued; + int stock[INGREDIENT_COUNT]; +}; + +class Channel { +public: + Channel(); + ~Channel(); + + Channel(const Channel &) = delete; + Channel &operator=(const Channel &) = delete; + + void parentSide(); + void childSide(); + + Channel &operator<<(const Message &msg); + Channel &operator>>(Message &msg); + bool tryRead(Message &msg); + +private: + int _toChild[2]; + int _toParent[2]; + int _rfd; + int _wfd; + + static void closeFd(int &fd); +}; + +} // namespace plazza diff --git a/src/Logger/Logger.hpp b/src/Logger/Logger.hpp new file mode 100644 index 0000000..2911f71 --- /dev/null +++ b/src/Logger/Logger.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Logger/Sequences.hpp" + +enum level_t : uint8_t { L_DEBUG, L_LOG, L_WARN, L_ERROR, L_FATAL }; + +namespace Logger { + +inline level_t &minLevel() { + static level_t level = L_DEBUG; + return level; +} + +inline void setLevel(level_t level) { minLevel() = level; } + +inline std::string timestamp() { + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast( + now.time_since_epoch()) % + 1000; + std::ostringstream oss; + oss << std::put_time(std::localtime(&time), "%H:%M:%S"); + oss << '.' << std::setfill('0') << std::setw(3) << ms.count(); + return oss.str(); +} + +inline const char *levelTag(level_t level) { + switch (level) { + case L_DEBUG: + return PURPLE "DBG" RESET; + case L_LOG: + return BLUE "LOG" RESET; + case L_WARN: + return YELLOW "WRN" RESET; + case L_ERROR: + return RED "ERR" RESET; + case L_FATAL: + return BOLD RED "FTL" RESET; + } + return "???"; +} + +inline void print(level_t level, const char *file, int line, const char *msg) { + if (level < minLevel()) + return; + const char *filename = file; + for (const char *p = file; *p; ++p) + if (*p == '/') + filename = p + 1; + std::cout << BLUE << timestamp() << RESET " [" << levelTag(level) << "] " + << PURPLE << filename << ":" << line << RESET BOLD " - " RESET + << msg << std::endl; +} + +inline void print(level_t level, const char *file, int line, + const std::string &msg) { + print(level, file, line, msg.c_str()); +} + +} // namespace Logger + +#define LOG_DEBUG(msg) Logger::print(L_DEBUG, __FILE__, __LINE__, msg) +#define LOG_INFO(msg) Logger::print(L_LOG, __FILE__, __LINE__, msg) +#define LOG_WARN(msg) Logger::print(L_WARN, __FILE__, __LINE__, msg) +#define LOG_ERROR(msg) Logger::print(L_ERROR, __FILE__, __LINE__, msg) +#define LOG_FATAL(msg) Logger::print(L_FATAL, __FILE__, __LINE__, msg) diff --git a/src/Logger/Sequences.hpp b/src/Logger/Sequences.hpp new file mode 100644 index 0000000..ec2eb23 --- /dev/null +++ b/src/Logger/Sequences.hpp @@ -0,0 +1,14 @@ +#pragma once + +#define ESC "\033" +#define CFMT(n) ESC "[" #n "m" + +#define RESET CFMT(0) +#define BOLD CFMT(1) + +#define RED CFMT(31) +#define GREEN CFMT(32) +#define YELLOW CFMT(33) +#define BLUE CFMT(34) +#define PURPLE CFMT(35) +#define CYAN CFMT(36) diff --git a/src/Pizza/IPizza.hpp b/src/Pizza/IPizza.hpp index d2d814a..df487b9 100644 --- a/src/Pizza/IPizza.hpp +++ b/src/Pizza/IPizza.hpp @@ -48,7 +48,7 @@ static inline std::map ingredientsToString = { namespace plazza { class IPizza { public: - IPizza(PizzaSize size){}; + IPizza(PizzaSize size) {}; virtual ~IPizza() = default; virtual PizzaSize getSize() const = 0;