diff --git a/include/regilo/controller.hpp b/include/regilo/controller.hpp index 276599a..6bb8edf 100644 --- a/include/regilo/controller.hpp +++ b/include/regilo/controller.hpp @@ -74,7 +74,7 @@ class Controller * @brief Get the current Log (a const variant). * @return The Log or nullptr */ - virtual const std::shared_ptr& getLog() const = 0; + virtual std::shared_ptr getLog() const = 0; /** * @brief Set a Log (it can be shared between more controllers). @@ -105,7 +105,7 @@ class BaseController : public Controller ba::io_service ioService; Stream stream; - std::shared_ptr log; + std::shared_ptr log; virtual std::string sendCommand() final; @@ -141,10 +141,10 @@ class BaseController : public Controller virtual inline bool isConnected() const override { return stream.is_open(); } - virtual std::shared_ptr getLog() override { return log; } - virtual const std::shared_ptr& getLog() const override { return log; } + virtual inline std::shared_ptr getLog() override { return log; } + virtual inline std::shared_ptr getLog() const override { return log; } - virtual void setLog(std::shared_ptr log) override { this->log.swap(log); } + virtual void setLog(std::shared_ptr log) override; /** * @brief Send a command to the device. @@ -189,14 +189,14 @@ BaseController::BaseController(const std::string& logPath) : BaseControl { if(logPath.length() > 0) { - log.reset(new Log(logPath)); + log.reset(new BasicLog(logPath)); } } template BaseController::BaseController(std::iostream& logStream) : BaseController() { - this->log.reset(new Log(logStream)); + this->log.reset(new BasicLog(logStream)); } template @@ -205,6 +205,13 @@ BaseController::~BaseController() if(stream.is_open()) stream.close(); } +template +void BaseController::setLog(std::shared_ptr log) +{ + std::shared_ptr basicLog = std::dynamic_pointer_cast(log); + this->log.swap(basicLog); +} + template std::string BaseController::sendCommand(const std::string& command) { diff --git a/include/regilo/log.hpp b/include/regilo/log.hpp index f6bd824..fc0b6dd 100644 --- a/include/regilo/log.hpp +++ b/include/regilo/log.hpp @@ -22,86 +22,64 @@ #ifndef REGILO_LOG_HPP #define REGILO_LOG_HPP +#include +#include #include +#include +#include +#include + +#include + +#include "regilo/utils.hpp" namespace regilo { -/** - * @brief The Log class is used to log all commands that were send to the device. - */ class Log { -private: - std::string filePath; - std::fstream *fileStream; - - std::iostream& stream; - - long commandTime; - public: - static char MESSAGE_END; - - /** - * @brief Log constructor with logging to a file. - * @param filePath The path of file - */ - Log(const std::string& filePath); - - /** - * @brief Log constructor with logging to a stream. - * @param stream Input/output stream - */ - Log(std::iostream& stream); - /** * @brief Default destructor. */ - virtual ~Log(); + virtual ~Log() = default; /** * @brief Get the path of file if the log was created with a path otherwise the empty string. * @return The path or empty string. */ - inline const std::string& getFilePath() const { return filePath; } + virtual const std::string& getFilePath() const = 0; /** * @brief Get the current underlying stream. * @return The underlying stream */ - inline std::iostream& getStream() { return stream; } + virtual std::iostream& getStream() = 0; /** * @brief Test if the stream is EOF. * @return true/false */ - inline bool isEnd() const { return !stream; } - - /** - * @brief Get the last command time (after reading). - * @return Seconds since epoch - */ - inline long getLastCommandTime() const { return commandTime; } + virtual bool isEnd() const = 0; /** * @brief Read one command from the log. * @return The response of the command. */ - std::string read(); + virtual std::string read() = 0; /** * @brief Read one command from the log. * @param logCommand The input of the command that was read. * @return The response of the command. */ - std::string read(std::string& logCommand); + virtual std::string read(std::string& logCommand) = 0; /** * @brief Read specified command from the log (the others are skipped). * @param command The command to read (The boost::algorithm::starts_with() method is used to compare). * @return The response of the command. */ - std::string readCommand(const std::string& command); + virtual std::string readCommand(const std::string& command) = 0; /** * @brief Read specified command from the log (the others are skipped). @@ -109,16 +87,125 @@ class Log * @param logCommand The input of the command that was read. * @return The response of the command. */ - std::string readCommand(const std::string& command, std::string& logCommand); + virtual std::string readCommand(const std::string& command, std::string& logCommand) = 0; /** * @brief Write a command and response to the log. * @param command The command (with all parameters). * @param response The response of the command. */ - void write(const std::string& command, const std::string& response); + virtual void write(const std::string& command, const std::string& response) = 0; }; +class BasicLog : public Log +{ +private: + std::string filePath; + std::fstream *fileStream; + +protected: + std::iostream& stream; + +public: + char MESSAGE_END = '$'; + + /** + * @brief Log constructor with logging to a file. + * @param filePath The path of file + */ + BasicLog(const std::string& filePath); + + /** + * @brief Log constructor with logging to a stream. + * @param stream Input/output stream + */ + BasicLog(std::iostream& stream); + + virtual ~BasicLog(); + + virtual inline const std::string& getFilePath() const override { return filePath; } + virtual inline std::iostream& getStream() override { return stream; } + virtual inline bool isEnd() const override { return !stream; } + + virtual std::string read() override; + virtual std::string read(std::string& logCommand) override; + virtual std::string readCommand(const std::string& command) override; + virtual std::string readCommand(const std::string& command, std::string& logCommand) override; + + virtual void write(const std::string& command, const std::string& response) override; +}; + +/** + * @brief The Log class is used to log all commands that were send to the device. + */ +template +class TimedLog : public BasicLog +{ +private: + typename Duration::rep commandTime; + + typename Duration::rep firstReadTime; + typename Duration::rep firstCommandTime = std::numeric_limits::max(); + +public: + using BasicLog::BasicLog; + + virtual ~TimedLog() = default; + + /** + * @brief Get last command time (after reading). + * @return Time since epoch in Duration units + */ + inline typename Duration::rep getLastCommandTime() const { return commandTime; } + + /** + * @brief Sync command times with real time. It means that all read methods will block + * its execution until the current time is bigger than the command time. + */ + inline void syncTime(bool sync = true) { firstCommandTime = (sync ? -1 : std::numeric_limits::max()); } + + virtual std::string read(std::string& logCommand) override; + virtual void write(const std::string& command, const std::string& response) override; +}; + +template +std::string TimedLog::read(std::string& logCommand) +{ + std::string epochTime; + std::getline(stream, epochTime, MESSAGE_END); + std::istringstream epochStream(epochTime); + epochStream >> commandTime; + + std::string response = BasicLog::read(logCommand); // TODO: Swap with time + + if(firstCommandTime == -1) + { + firstReadTime = epoch(); + firstCommandTime = commandTime; + } + else + { + typename Duration::rep elapsed = epoch() - firstReadTime; + typename Duration::rep elapsedLog = commandTime - firstCommandTime; + + while(elapsed < elapsedLog) + { + std::this_thread::sleep_for(Duration(elapsedLog - elapsed)); + elapsed = epoch() - firstReadTime; + } + } + + return response; +} + +template +void TimedLog::write(const std::string& command, const std::string& response) +{ + stream << epoch() << MESSAGE_END; + + BasicLog::write(command, response); // TODO: Swap with time +} + } #endif // REGILO_LOG_HPP diff --git a/include/regilo/scancontroller.hpp b/include/regilo/scancontroller.hpp index 1a4dd5b..14e134b 100644 --- a/include/regilo/scancontroller.hpp +++ b/include/regilo/scancontroller.hpp @@ -37,7 +37,7 @@ class ScanController : public Controller virtual bool isConnected() const override = 0; virtual std::string getEndpoint() const override = 0; virtual std::shared_ptr getLog() override = 0; - virtual const std::shared_ptr& getLog() const override = 0; + virtual std::shared_ptr getLog() const override = 0; virtual void setLog(std::shared_ptr log) override = 0; /** @@ -67,7 +67,7 @@ class BaseScanController : public ScanController, public ProtocolController virtual inline bool isConnected() const override { return ProtocolController::isConnected(); } virtual inline std::string getEndpoint() const override { return ProtocolController::getEndpoint(); } virtual inline std::shared_ptr getLog() override { return ProtocolController::getLog(); } - virtual inline const std::shared_ptr& getLog() const override { return ProtocolController::getLog(); } + virtual inline std::shared_ptr getLog() const override { return ProtocolController::getLog(); } virtual inline void setLog(std::shared_ptr log) override { return ProtocolController::setLog(log); } virtual ScanData getScan(bool fromDevice = true) override final; @@ -99,14 +99,14 @@ ScanData BaseScanController::getScan(bool fromDevice) if(fromDevice) { this->sendCommand(getScanCommand()); - data.time = epochSeconds(); + data.time = epoch(); parseScanData(this->deviceOutput, data); } else { std::istringstream response(this->log->readCommand(getScanCommand())); - data.time = this->log->getLastCommandTime(); + // data.time = this->log->getLastCommandTime(); // TODO: Set from log parseScanData(response, data); } diff --git a/include/regilo/utils.hpp b/include/regilo/utils.hpp index 4913fd0..f548012 100644 --- a/include/regilo/utils.hpp +++ b/include/regilo/utils.hpp @@ -22,13 +22,20 @@ #ifndef REGILO_UTILS_HPP #define REGILO_UTILS_HPP +#include + namespace regilo { /** - * @brief Get seconds since epoch. - * @return Seconds + * @brief Get time since epoch. + * @return Time in (s, ms, etc.) */ -long epochSeconds(); +template +typename T::rep epoch() +{ + auto sinceEpoch = std::chrono::system_clock::now().time_since_epoch(); + return std::chrono::duration_cast(sinceEpoch).count(); +} } diff --git a/src/regilo/log.cpp b/src/regilo/log.cpp index 61a2460..6a280e2 100644 --- a/src/regilo/log.cpp +++ b/src/regilo/log.cpp @@ -21,47 +21,35 @@ #include "regilo/log.hpp" -#include -#include - -#include - -#include "regilo/utils.hpp" - namespace regilo { -char Log::MESSAGE_END = '$'; - -Log::Log(const std::string& filePath) : +BasicLog::BasicLog(const std::string& filePath) : filePath(filePath), fileStream(new std::fstream(filePath, std::fstream::in | std::fstream::out | std::fstream::app)), stream(*fileStream) { } -Log::Log(std::iostream& stream) : +BasicLog::BasicLog(std::iostream& stream) : fileStream(nullptr), stream(stream) { } -Log::~Log() +BasicLog::~BasicLog() { delete fileStream; } -std::string Log::read() +std::string BasicLog::read() { std::string command; return read(command); } -std::string Log::read(std::string& logCommand) +std::string BasicLog::read(std::string& logCommand) { - std::string secondsEpoch, response; - - std::getline(stream, secondsEpoch, MESSAGE_END); - commandTime = std::stol(secondsEpoch); + std::string response; std::getline(stream, logCommand, MESSAGE_END); std::getline(stream, response, MESSAGE_END); @@ -69,13 +57,13 @@ std::string Log::read(std::string& logCommand) return response; } -std::string Log::readCommand(const std::string& command) +std::string BasicLog::readCommand(const std::string& command) { std::string logCommand; return readCommand(command, logCommand); } -std::string Log::readCommand(const std::string& command, std::string& logCommand) +std::string BasicLog::readCommand(const std::string& command, std::string& logCommand) { std::string response; do @@ -87,9 +75,8 @@ std::string Log::readCommand(const std::string& command, std::string& logCommand return response; } -void Log::write(const std::string& command, const std::string& response) +void BasicLog::write(const std::string& command, const std::string& response) { - stream << epochSeconds() << MESSAGE_END; stream << command << MESSAGE_END; stream << response << MESSAGE_END; } diff --git a/src/regilo/utils.cpp b/src/regilo/utils.cpp deleted file mode 100644 index f24898a..0000000 --- a/src/regilo/utils.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Regilo - * Copyright (C) 2015-2016 Branislav HolĂ˝ - * - * This file is part of Regilo. - * - * Regilo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Regilo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Regilo. If not, see . - * - */ - -#include "regilo/utils.hpp" - -#include - -namespace regilo { - -long epochSeconds() -{ - auto sinceEpoch = std::chrono::system_clock::now().time_since_epoch(); - return sinceEpoch.count() * std::chrono::system_clock::period::num / std::chrono::system_clock::period::den; -} - -}