diff --git a/serviceinterface/ServiceInterfaceBuffer.cpp b/serviceinterface/ServiceInterfaceBuffer.cpp index 58065994a..5c80862c2 100644 --- a/serviceinterface/ServiceInterfaceBuffer.cpp +++ b/serviceinterface/ServiceInterfaceBuffer.cpp @@ -1,11 +1,58 @@ #include #include #include +#include // to be implemented by bsp -extern "C" void printChar(const char*); +extern "C" void printChar(const char*, bool errStream); + +#ifndef UT699 + +ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string setMessage, + bool addCrToPreamble, bool buffered , bool errStream, uint16_t port): + isActive(true), logMessage(setMessage), + addCrToPreamble(addCrToPreamble), buffered(buffered), + errStream(errStream) { + if(buffered) { + // Set pointers if the stream is buffered. + setp( buf, buf + BUF_SIZE ); + } + preamble.reserve(MAX_PREAMBLE_SIZE); + preamble.resize(MAX_PREAMBLE_SIZE); +} + +void ServiceInterfaceBuffer::putChars(char const* begin, char const* end) { + char array[BUF_SIZE]; + uint32_t length = end - begin; + if (length > sizeof(array)) { + length = sizeof(array); + } + memcpy(array, begin, length); + + for(; begin != end; begin++){ + if(errStream) { + printChar(begin, true); + } + else { + printChar(begin, false); + } + } +} + +#endif int ServiceInterfaceBuffer::overflow(int c) { + if(not buffered and this->isActive) { + if (c != Traits::eof()) { + if(errStream) { + printChar(reinterpret_cast(&c), true); + } + else { + printChar(reinterpret_cast(&c), false); + } + } + return 0; + } // Handle output putChars(pbase(), pptr()); if (c != Traits::eof()) { @@ -20,52 +67,70 @@ int ServiceInterfaceBuffer::overflow(int c) { } int ServiceInterfaceBuffer::sync(void) { - if (this->isActive) { - Clock::TimeOfDay_t loggerTime; - Clock::getDateAndTime(&loggerTime); - char preamble[96] = { 0 }; - sprintf(preamble, "%s: | %lu:%02lu:%02lu.%03lu | ", - this->log_message.c_str(), (unsigned long) loggerTime.hour, - (unsigned long) loggerTime.minute, - (unsigned long) loggerTime.second, - (unsigned long) loggerTime.usecond /1000); - // Write log_message and time - this->putChars(preamble, preamble + sizeof(preamble)); - // Handle output - this->putChars(pbase(), pptr()); + if(not this->isActive and not buffered) { + if(not buffered) { + setp(buf, buf + BUF_SIZE - 1); + } + return 0; } + if(not buffered) { + return 0; + } + + size_t preambleSize = 0; + auto preamble = getPreamble(&preambleSize); + // Write logMessage and time + this->putChars(preamble.data(), preamble.data() + preambleSize); + // Handle output + this->putChars(pbase(), pptr()); // This tells that buffer is empty again setp(buf, buf + BUF_SIZE - 1); return 0; } -#ifndef UT699 - -ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string set_message, uint16_t port) { - this->log_message = set_message; - this->isActive = true; - setp( buf, buf + BUF_SIZE ); +bool ServiceInterfaceBuffer::isBuffered() const { + return buffered; } -void ServiceInterfaceBuffer::putChars(char const* begin, char const* end) { - char array[BUF_SIZE]; - uint32_t length = end - begin; - if (length > sizeof(array)) { - length = sizeof(array); +std::string ServiceInterfaceBuffer::getPreamble(size_t * preambleSize) { + Clock::TimeOfDay_t loggerTime; + Clock::getDateAndTime(&loggerTime); + size_t currentSize = 0; + char* parsePosition = &preamble[0]; + if(addCrToPreamble) { + preamble[0] = '\r'; + currentSize += 1; + parsePosition += 1; } - memcpy(array, begin, length); - - for( ; begin != end; begin++){ - printChar(begin); + int32_t charCount = sprintf(parsePosition, + "%s: | %02" SCNu32 ":%02" SCNu32 ":%02" SCNu32 ".%03" SCNu32 " | ", + this->logMessage.c_str(), loggerTime.hour, + loggerTime.minute, + loggerTime.second, + loggerTime.usecond /1000); + if(charCount < 0) { + printf("ServiceInterfaceBuffer: Failure parsing preamble\r\n"); + return ""; } - + if(charCount > MAX_PREAMBLE_SIZE) { + printf("ServiceInterfaceBuffer: Char count too large for maximum " + "preamble size"); + return ""; + } + currentSize += charCount; + if(preambleSize != nullptr) { + *preambleSize = currentSize; + } + return preamble; } -#endif + + #ifdef UT699 #include -ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string set_message, uint16_t port) { +ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string set_message, + uint16_t port) { this->log_message = set_message; this->isActive = true; setp( buf, buf + BUF_SIZE ); diff --git a/serviceinterface/ServiceInterfaceBuffer.h b/serviceinterface/ServiceInterfaceBuffer.h index b42c8a197..39ea25c2d 100644 --- a/serviceinterface/ServiceInterfaceBuffer.h +++ b/serviceinterface/ServiceInterfaceBuffer.h @@ -1,51 +1,71 @@ #ifndef FRAMEWORK_SERVICEINTERFACE_SERVICEINTERFACEBUFFER_H_ #define FRAMEWORK_SERVICEINTERFACE_SERVICEINTERFACEBUFFER_H_ +#include #include -#include #include -#include +#include #ifndef UT699 -class ServiceInterfaceBuffer: public std::basic_streambuf > { + +/** + * @brief This is the underlying stream buffer which implements the + * streambuf class and overloads the overflow() and sync() methods + * @details + * This class is used to modify the output of the stream, for example by adding. + * It also calls the char printing function which is implemented in the + * board supply package (BSP). + */ +class ServiceInterfaceBuffer: + public std::streambuf { friend class ServiceInterfaceStream; public: - ServiceInterfaceBuffer(std::string set_message, uint16_t port); + static constexpr uint8_t MAX_PREAMBLE_SIZE = 40; + + ServiceInterfaceBuffer(std::string setMessage, bool addCrToPreamble, + bool buffered, bool errStream, uint16_t port); + protected: bool isActive; - // This is called when buffer becomes full. If - // buffer is not used, then this is called every - // time when characters are put to stream. - virtual int overflow(int c = Traits::eof()); + //! This is called when buffer becomes full. If + //! buffer is not used, then this is called every + //! time when characters are put to stream. + int overflow(int c = Traits::eof()) override; - // This function is called when stream is flushed, - // for example when std::endl is put to stream. - virtual int sync(void); + //! This function is called when stream is flushed, + //! for example when std::endl is put to stream. + int sync(void) override; + bool isBuffered() const; private: - // For additional message information - std::string log_message; + //! For additional message information + std::string logMessage; + std::string preamble; // For EOF detection typedef std::char_traits Traits; - // Work in buffer mode. It is also possible to work without buffer. + //! This is useful for some terminal programs which do not have + //! implicit carriage return with newline characters. + bool addCrToPreamble; + + //! Specifies whether the stream operates in buffered or unbuffered mode. + bool buffered; + //! This specifies to print to stderr and work in unbuffered mode. + bool errStream; + + //! Needed for buffered mode. static size_t const BUF_SIZE = 128; char buf[BUF_SIZE]; - // In this function, the characters are parsed. + //! In this function, the characters are parsed. void putChars(char const* begin, char const* end); + + std::string getPreamble(size_t * preambleSize = nullptr); }; + #endif - - - - - - - #ifdef UT699 class ServiceInterfaceBuffer: public std::basic_streambuf > { diff --git a/serviceinterface/ServiceInterfaceStream.cpp b/serviceinterface/ServiceInterfaceStream.cpp index c2979f369..76481ed12 100644 --- a/serviceinterface/ServiceInterfaceStream.cpp +++ b/serviceinterface/ServiceInterfaceStream.cpp @@ -1,11 +1,32 @@ #include +ServiceInterfaceStream::ServiceInterfaceStream(std::string setMessage, + bool addCrToPreamble, bool buffered, bool errStream, uint16_t port) : + std::ostream(&streambuf), + streambuf(setMessage, addCrToPreamble, buffered, errStream, port) {} + void ServiceInterfaceStream::setActive( bool myActive) { - this->buf.isActive = myActive; + this->streambuf.isActive = myActive; } -ServiceInterfaceStream::ServiceInterfaceStream(std::string set_message, - uint16_t port) : - std::basic_ostream >(&buf), buf( - set_message, port) { +std::string ServiceInterfaceStream::getPreamble() { + return streambuf.getPreamble(); +} + +void ServiceInterfaceStream::print(std::string error, + bool withPreamble, bool withNewline, bool flush) { + if(not streambuf.isBuffered() and withPreamble) { + *this << getPreamble() << error; + } + else { + *this << error; + } + + if(withNewline) { + *this << "\n"; + } + // if mode is non-buffered, no need to flush. + if(flush and streambuf.isBuffered()) { + this->flush(); + } } diff --git a/serviceinterface/ServiceInterfaceStream.h b/serviceinterface/ServiceInterfaceStream.h index df736a1b6..9e19c228c 100644 --- a/serviceinterface/ServiceInterfaceStream.h +++ b/serviceinterface/ServiceInterfaceStream.h @@ -3,28 +3,56 @@ #include #include -#include -#include #include -// Unfortunately, there must be a forward declaration of log_fe -// (MUST be defined in main), to let the system know where to write to. -namespace sif { -extern std::ostream debug; -extern std::ostream info; -extern std::ostream warning; -extern std::ostream error; -} - - -class ServiceInterfaceStream : public std::basic_ostream< char, std::char_traits< char > > { -protected: - ServiceInterfaceBuffer buf; +/** + * Generic service interface stream which can be used like std::cout or + * std::cerr but has additional capability. Add preamble and timestamp + * to output. Can be run in buffered or unbuffered mode. + */ +class ServiceInterfaceStream : public std::ostream { public: - ServiceInterfaceStream( std::string set_message, uint16_t port = 1234 ); + /** + * This constructor is used by specifying the preamble message. + * Optionally, the output can be directed to stderr and a CR character + * can be prepended to the preamble. + * @param setMessage message of preamble. + * @param addCrToPreamble Useful for applications like Puttty. + * @param buffered specify whether to use buffered mode. + * @param errStream specify which output stream to use (stderr or stdout). + */ + ServiceInterfaceStream(std::string setMessage, + bool addCrToPreamble = false, bool buffered = true, + bool errStream = false, uint16_t port = 1234); + + //! An inactive stream will not print anything. void setActive( bool ); + + /** + * This can be used to retrieve the preamble in case it should be printed in + * the unbuffered mode. + * @return Preamle consisting of log message and timestamp. + */ + std::string getPreamble(); + + /** + * This prints an error with a preamble. Useful if using the unbuffered + * mode. Flushes in default mode (prints immediately). + */ + void print(std::string error, bool withPreamble = true, + bool withNewline = true, bool flush = true); + +protected: + ServiceInterfaceBuffer streambuf; }; - +// Forward declaration of interface streams. These should be instantiated in +// main. They can then be used like std::cout or std::cerr. +namespace sif { +extern ServiceInterfaceStream debug; +extern ServiceInterfaceStream info; +extern ServiceInterfaceStream warning; +extern ServiceInterfaceStream error; +} #endif /* FRAMEWORK_SERVICEINTERFACE_SERVICEINTERFACESTREAM_H_ */