diff --git a/core/include/aare/core/DType.hpp b/core/include/aare/core/DType.hpp index b7fbebe..71ab9c3 100644 --- a/core/include/aare/core/DType.hpp +++ b/core/include/aare/core/DType.hpp @@ -5,6 +5,9 @@ namespace aare { +/** + * @brief enum class to define the endianess of the system + */ enum class endian { #ifdef _WIN32 little = 0, @@ -17,6 +20,10 @@ enum class endian { #endif }; +/** + * @brief class to define the data type of the pixels + * @note only native endianess is supported + */ class DType { // TODO! support for non native endianess? static_assert(sizeof(long) == sizeof(int64_t), "long should be 64bits"); diff --git a/core/include/aare/core/Frame.hpp b/core/include/aare/core/Frame.hpp index ed8a4de..9ef404e 100644 --- a/core/include/aare/core/Frame.hpp +++ b/core/include/aare/core/Frame.hpp @@ -7,14 +7,13 @@ #include #include +namespace aare { + /** * @brief Frame class to represent a single frame of data * model class * should be able to work with streams coming from files or network */ - -namespace aare { - class Frame { ssize_t m_rows; ssize_t m_cols; diff --git a/core/include/aare/core/defs.hpp b/core/include/aare/core/defs.hpp index 0f177ea..18594d4 100644 --- a/core/include/aare/core/defs.hpp +++ b/core/include/aare/core/defs.hpp @@ -11,6 +11,9 @@ namespace aare { +/** + * @brief header contained in parts of frames + */ struct sls_detector_header { uint64_t frameNumber; uint32_t expLength; @@ -35,7 +38,6 @@ struct xy { bool operator!=(const xy &other) const { return !(*this == other); } }; -// using image_shape = std::array; using dynamic_shape = std::vector; enum class DetectorType { Jungfrau, Eiger, Mythen3, Moench, ChipTestBoard }; diff --git a/core/src/DType.cpp b/core/src/DType.cpp index ca9849e..4e172ab 100644 --- a/core/src/DType.cpp +++ b/core/src/DType.cpp @@ -6,6 +6,13 @@ namespace aare { +/** + * @brief Construct a DType object from a type_info object + * @param t type_info object + * @throw runtime_error if the type is not supported + * @note supported types are: int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double + * @note the type_info object is obtained using typeid (e.g. typeid(int)) + */ DType::DType(const std::type_info &t) { if (t == typeid(int8_t)) m_type = TypeIndex::INT8; @@ -33,6 +40,10 @@ DType::DType(const std::type_info &t) { throw std::runtime_error("Could not construct data type. Type not supported."); } +/** + * @brief Get the bitdepth of the data type + * @return bitdepth + */ uint8_t DType::bitdepth() const { switch (m_type) { case TypeIndex::INT8: @@ -58,8 +69,20 @@ uint8_t DType::bitdepth() const { } } +/** + * @brief Construct a DType object from a TypeIndex + * @param ti TypeIndex + * + */ DType::DType(DType::TypeIndex ti) : m_type(ti) {} +/** + * @brief Construct a DType object from a string + * @param sv string_view + * @throw runtime_error if the type is not supported + * @note example strings: "= m_rows || col < 0 || col >= m_cols) { std::cerr << "Invalid row or column index" << std::endl; diff --git a/core/src/defs.cpp b/core/src/defs.cpp index 8f77439..3ce6c07 100644 --- a/core/src/defs.cpp +++ b/core/src/defs.cpp @@ -2,6 +2,11 @@ namespace aare { +/** + * @brief Convert a DetectorType to a string + * @param type DetectorType + * @return string representation of the DetectorType + */ template <> std::string toString(DetectorType type) { switch (type) { case DetectorType::Jungfrau: @@ -19,6 +24,12 @@ template <> std::string toString(DetectorType type) { } } +/** + * @brief Convert a string to a DetectorType + * @param name string representation of the DetectorType + * @return DetectorType + * @throw runtime_error if the string does not match any DetectorType + */ template <> DetectorType StringTo(std::string name) { if (name == "Jungfrau") return DetectorType::Jungfrau; @@ -35,6 +46,12 @@ template <> DetectorType StringTo(std::string name) { } } +/** + * @brief Convert a string to a TimingMode + * @param mode string representation of the TimingMode + * @return TimingMode + * @throw runtime_error if the string does not match any TimingMode + */ template <> TimingMode StringTo(std::string mode) { if (mode == "auto") return TimingMode::Auto; diff --git a/etc/multimodule_virtual_jf.config b/etc/multimodule_virtual_jf.config new file mode 100644 index 0000000..b377fd7 --- /dev/null +++ b/etc/multimodule_virtual_jf.config @@ -0,0 +1,14 @@ +hostname localhost +rx_hostname localhost + +udp_dstip auto +powerchip 1 +frames 1 +exptime 5us +period 1ms + + + +rx_zmqip 127.0.0.1 +rx_zmqport 5555 +rx_zmqstream 1 diff --git a/etc/virtual_jf.config b/etc/virtual_jf.config index 96ac0db..23725c7 100644 --- a/etc/virtual_jf.config +++ b/etc/virtual_jf.config @@ -5,7 +5,7 @@ udp_dstip auto powerchip 1 frames 1 exptime 5us -period 100ms +period 1ms rx_zmqip 127.0.0.1 rx_zmqport 5555 diff --git a/examples/zmq_receiver_example.cpp b/examples/zmq_receiver_example.cpp index ea893b1..6189ef2 100644 --- a/examples/zmq_receiver_example.cpp +++ b/examples/zmq_receiver_example.cpp @@ -1,14 +1,41 @@ #include "aare/network_io/ZmqSocketReceiver.hpp" #include "aare/network_io/defs.hpp" +#include #include #include #include using namespace aare; +namespace po = boost::program_options; +using namespace std; -int main() { +int main(int argc, char **argv) { aare::logger::set_verbosity(aare::logger::DEBUG); - std::string endpoint = "tcp://localhost:5555"; + + po::options_description desc("options"); + desc.add_options()("help", "produce help message")("port,p", po::value()->default_value(5555), + "port number"); + po::positional_options_description pd; + pd.add("port", 1); + po::variables_map vm; + try { + auto parsed = po::command_line_parser(argc, argv).options(desc).positional(pd).run(); + po::store(parsed, vm); + po::notify(vm); + + } catch (const boost::program_options::error &e) { + cout << e.what() << "\n"; + cout << desc << "\n"; + return 1; + } + if (vm.count("help")) { + cout << desc << "\n"; + return 1; + } + + auto port = vm["port"].as(); + + std::string endpoint = "udp://127.0.0.1:" + std::to_string(port); aare::ZmqSocketReceiver socket(endpoint); socket.connect(); while (true) { diff --git a/examples/zmq_restream_example.cpp b/examples/zmq_restream_example.cpp index 07bb996..1d3f496 100644 --- a/examples/zmq_restream_example.cpp +++ b/examples/zmq_restream_example.cpp @@ -79,7 +79,7 @@ int main(int argc, char **argv) { header.npixelsx = frame.rows(); header.npixelsy = frame.cols(); header.dynamicRange = frame.bitdepth(); - header.imageSize = frame.size(); + header.size = frame.size(); sender.send({header, frame}); std::this_thread::sleep_for(d); diff --git a/examples/zmq_sender_example.cpp b/examples/zmq_sender_example.cpp index 5799130..b0083c6 100644 --- a/examples/zmq_sender_example.cpp +++ b/examples/zmq_sender_example.cpp @@ -24,7 +24,7 @@ int main() { aare::ZmqHeader header; header.npixelsx = 1024; header.npixelsy = 1024; - header.imageSize = sizeof(uint32_t) * 1024 * 1024; + header.size = sizeof(uint32_t) * 1024 * 1024; header.dynamicRange = 32; std::vector zmq_frames; diff --git a/network_io/include/aare/network_io/ZmqHeader.hpp b/network_io/include/aare/network_io/ZmqHeader.hpp index 085f922..4c4b6bf 100644 --- a/network_io/include/aare/network_io/ZmqHeader.hpp +++ b/network_io/include/aare/network_io/ZmqHeader.hpp @@ -98,7 +98,7 @@ struct ZmqHeader { /** number of pixels/channels in y axis for this zmq socket */ uint32_t npixelsy{0}; /** number of bytes for an image in this socket */ - uint32_t imageSize{0}; + uint32_t size{0}; /** frame number from detector */ uint64_t acqIndex{0}; /** frame index (starting at 0 for each acquisition) */ diff --git a/network_io/include/aare/network_io/ZmqSocket.hpp b/network_io/include/aare/network_io/ZmqSocket.hpp index 1338658..96ea923 100644 --- a/network_io/include/aare/network_io/ZmqSocket.hpp +++ b/network_io/include/aare/network_io/ZmqSocket.hpp @@ -10,7 +10,22 @@ class zmq_msg_t; namespace aare { +/** + * @brief parent class for ZmqSocketReceiver and ZmqSocketSender + * contains common functions and variables + */ class ZmqSocket { + public: + ZmqSocket() = default; + ~ZmqSocket(); + ZmqSocket(const ZmqSocket &) = delete; + ZmqSocket operator=(const ZmqSocket &) = delete; + ZmqSocket(ZmqSocket &&) = delete; + void disconnect(); + void set_zmq_hwm(int hwm); + void set_timeout_ms(int n); + void set_potential_frame_size(size_t size); + protected: void *m_context{nullptr}; void *m_socket{nullptr}; @@ -20,19 +35,6 @@ class ZmqSocket { size_t m_potential_frame_size{1024 * 1024}; constexpr static size_t m_max_header_size = 1024; char *m_header_buffer = new char[m_max_header_size]; - - public: - ZmqSocket() = default; - ~ZmqSocket(); - - ZmqSocket(const ZmqSocket &) = delete; - ZmqSocket operator=(const ZmqSocket &) = delete; - ZmqSocket(ZmqSocket &&) = delete; - - void disconnect(); - void set_zmq_hwm(int hwm); - void set_timeout_ms(int n); - void set_potential_frame_size(size_t size); }; } // namespace aare \ No newline at end of file diff --git a/network_io/include/aare/network_io/ZmqSocketReceiver.hpp b/network_io/include/aare/network_io/ZmqSocketReceiver.hpp index 4562114..7d207a5 100644 --- a/network_io/include/aare/network_io/ZmqSocketReceiver.hpp +++ b/network_io/include/aare/network_io/ZmqSocketReceiver.hpp @@ -8,14 +8,15 @@ #include #include -// Socket to receive data from a ZMQ publisher -// needs to be in sync with the main library (or maybe better use the versioning in the header) - // forward declare zmq_msg_t to avoid including zmq.h in the header class zmq_msg_t; namespace aare { +/** + * @brief Socket to receive data from a ZMQ publisher + * @note needs to be in sync with the main library (or maybe better use the versioning in the header) + */ class ZmqSocketReceiver : public ZmqSocket { public: ZmqSocketReceiver(const std::string &endpoint); diff --git a/network_io/include/aare/network_io/ZmqSocketSender.hpp b/network_io/include/aare/network_io/ZmqSocketSender.hpp index 1a7f9fb..2ef379f 100644 --- a/network_io/include/aare/network_io/ZmqSocketSender.hpp +++ b/network_io/include/aare/network_io/ZmqSocketSender.hpp @@ -5,6 +5,11 @@ #include "aare/network_io/defs.hpp" namespace aare { + +/** + * @brief Socket to send data to a ZMQ subscriber + * @note needs to be in sync with the main library (or maybe better use the versioning in the header) + */ class ZmqSocketSender : public ZmqSocket { public: ZmqSocketSender(const std::string &endpoint); diff --git a/network_io/src/ZmqHeader.cpp b/network_io/src/ZmqHeader.cpp index fdc67a2..76bf34f 100644 --- a/network_io/src/ZmqHeader.cpp +++ b/network_io/src/ZmqHeader.cpp @@ -77,7 +77,7 @@ std::string ZmqHeader::to_string() const { write_digit(s, "ndety", ndety); write_digit(s, "npixelsx", npixelsx); write_digit(s, "npixelsy", npixelsy); - write_digit(s, "imageSize", imageSize); + write_digit(s, "size", size); write_digit(s, "acqIndex", acqIndex); write_digit(s, "frameIndex", frameIndex); write_digit(s, "progress", progress); @@ -117,7 +117,6 @@ void ZmqHeader::from_string(std::string &s) { for (auto field : object) { std::string_view key = field.unescaped_key(); - if (key == "data") { data = uint64_t(field.value()) ? true : false; } else if (key == "jsonversion") { @@ -134,8 +133,8 @@ void ZmqHeader::from_string(std::string &s) { npixelsx = uint32_t(field.value()); } else if (key == "npixelsy") { npixelsy = uint32_t(field.value()); - } else if (key == "imageSize") { - imageSize = uint32_t(field.value()); + } else if (key == "size") { + size = uint32_t(field.value()); } else if (key == "acqIndex") { acqIndex = uint64_t(field.value()); } else if (key == "frameIndex") { @@ -187,7 +186,7 @@ void ZmqHeader::from_string(std::string &s) { bool ZmqHeader::operator==(const ZmqHeader &other) const { return data == other.data && jsonversion == other.jsonversion && dynamicRange == other.dynamicRange && fileIndex == other.fileIndex && ndetx == other.ndetx && ndety == other.ndety && npixelsx == other.npixelsx && - npixelsy == other.npixelsy && imageSize == other.imageSize && acqIndex == other.acqIndex && + npixelsy == other.npixelsy && size == other.size && acqIndex == other.acqIndex && frameIndex == other.frameIndex && progress == other.progress && fname == other.fname && frameNumber == other.frameNumber && expLength == other.expLength && packetNumber == other.packetNumber && detSpec1 == other.detSpec1 && timestamp == other.timestamp && modId == other.modId && row == other.row && diff --git a/network_io/src/ZmqSocket.cpp b/network_io/src/ZmqSocket.cpp index 6200aac..e3d6b3f 100644 --- a/network_io/src/ZmqSocket.cpp +++ b/network_io/src/ZmqSocket.cpp @@ -3,6 +3,11 @@ namespace aare { +/** + * @brief closes the socket and destroys the context + * @return void + * @note this function is called by the destructor + */ void ZmqSocket::disconnect() { zmq_close(m_socket); zmq_ctx_destroy(m_context); @@ -10,6 +15,10 @@ void ZmqSocket::disconnect() { m_context = nullptr; } +/** + * @brief destructor + * @note called from child classes (ZmqSocketReceiver and ZmqSocketSender) + */ ZmqSocket::~ZmqSocket() { if (m_socket) disconnect(); diff --git a/network_io/src/ZmqSocketReceiver.cpp b/network_io/src/ZmqSocketReceiver.cpp index 2235def..60d191b 100644 --- a/network_io/src/ZmqSocketReceiver.cpp +++ b/network_io/src/ZmqSocketReceiver.cpp @@ -79,6 +79,10 @@ int ZmqSocketReceiver::receive_data(std::byte *data, size_t size) { return data_bytes_received; } +/** + * @brief receive a ZmqFrame (header and data) + * @return ZmqFrame + */ ZmqFrame ZmqSocketReceiver::receive_zmqframe() { // receive header from zmq and parse it ZmqHeader header = receive_header(); @@ -94,13 +98,17 @@ ZmqFrame ZmqSocketReceiver::receive_zmqframe() { if (bytes_received == -1) { throw network_io::NetworkError(LOCATION + "Error receiving frame"); } - if ((uint32_t)bytes_received != header.imageSize) { + if ((uint32_t)bytes_received != header.size) { throw network_io::NetworkError( - fmt::format("{} Expected {} bytes but received {}", LOCATION, header.imageSize, bytes_received)); + fmt::format("{} Expected {} bytes but received {}", LOCATION, header.size, bytes_received)); } return {header, std::move(frame)}; } +/** + * @brief receive multiple ZmqFrames (header and data) + * @return std::vector + */ std::vector ZmqSocketReceiver::receive_n() { std::vector frames; while (true) { diff --git a/network_io/test/ZmqHeader.test.cpp b/network_io/test/ZmqHeader.test.cpp index c7769ff..de5b8b9 100644 --- a/network_io/test/ZmqHeader.test.cpp +++ b/network_io/test/ZmqHeader.test.cpp @@ -13,7 +13,7 @@ TEST_CASE("Test ZmqHeader") { header.fileIndex = 4; header.ndetx = 5; header.ndety = 6; - header.imageSize = 4800; + header.size = 4800; header.acqIndex = 8; header.frameIndex = 9; header.progress = 0.1; @@ -46,7 +46,7 @@ TEST_CASE("Test ZmqHeader") { "\"ndety\": 6, " "\"npixelsx\": 10, " "\"npixelsy\": 15, " - "\"imageSize\": 4800, " + "\"size\": 4800, " "\"acqIndex\": 8, " "\"frameIndex\": 9, " "\"progress\": 0.100000, " diff --git a/utils/include/aare/utils/logger.hpp b/utils/include/aare/utils/logger.hpp index 87928bf..3091971 100644 --- a/utils/include/aare/utils/logger.hpp +++ b/utils/include/aare/utils/logger.hpp @@ -5,10 +5,19 @@ #include #include +/** + * @brief LOCATION macro to get the current location in the code + */ #define LOCATION std::string(__FILE__) + std::string(":") + std::to_string(__LINE__) + ":" + std::string(__func__) + ":" -// operator overload to print vectors -// typename T must be printable (i.e. have the << operator) +/** + * @brief operator overload for std::vector + * @tparam T type of the vector. T should have operator<< defined + * @param out output stream + * @param v vector to print + * @return std::ostream& output stream + * @note this is used to print vectors in the logger (or anywhere else) + */ template std::ostream &operator<<(std::ostream &out, const std::vector &v) { out << "["; size_t last = v.size() - 1; @@ -21,7 +30,15 @@ template std::ostream &operator<<(std::ostream &out, const std::vec return out; } -// operator overload for std::array +/** + * @brief operator overload for std::array + * @tparam T type of the array. T should have operator<< defined + * @tparam N size of the array + * @param out output stream + * @param v array to print + * @return std::ostream& output stream + * + */ template std::ostream &operator<<(std::ostream &out, const std::array &v) { out << "["; size_t last = N - 1; @@ -33,7 +50,16 @@ template std::ostream &operator<<(std::ostream &out, cons out << "]"; return out; } -// operator overlaod for std::map + +/** + * @brief operator overload for std::map + * @tparam K type of the key in the map. K should have operator<< defined + * @tparam V type of the value in the map. V should have operator<< defined + * @param out output stream + * @param v map to print + * @return std::ostream& output stream + * + */ template std::ostream &operator<<(std::ostream &out, const std::map &v) { out << "{"; size_t i = 0; @@ -48,6 +74,9 @@ template std::ostream &operator<<(std::ostream &out, co namespace aare { namespace logger { +/** + * @brief enum to define the logging level + */ enum LOGGING_LEVEL { DEBUG = 0, INFO = 1, @@ -56,36 +85,64 @@ enum LOGGING_LEVEL { }; +/** + * @brief Logger class to log messages + * @note can be used to log to file or to a std::streambuf (like std::cout) + * @note by default logs to std::cout and std::cerr with INFO verbosity + */ class Logger { - std::streambuf *standard_buf = std::cout.rdbuf(); - std::streambuf *error_buf = std::cerr.rdbuf(); - std::ostream *standard_output; - std::ostream *error_output; - LOGGING_LEVEL VERBOSITY_LEVEL = LOGGING_LEVEL::INFO; - - std::ofstream out_file; - public: + /** + * @brief get the instance of the logger + */ + Logger() { + standard_output = new std::ostream(standard_buf); + error_output = new std::ostream(error_buf); + } + + /** + * @brief set the output file for the logger by filename + * @param filename name of the file to log to + * @return void + */ void set_output_file(std::string filename) { if (out_file.is_open()) out_file.close(); out_file.open(filename); set_streams(out_file.rdbuf()); } + + /** + * @brief set the output streams for the logger + * @param out output stream for standard output + * @param err output stream for error output + * @return void + */ void set_streams(std::streambuf *out, std::streambuf *err) { delete standard_output; delete error_output; standard_output = new std::ostream(out); error_output = new std::ostream(err); } + /** + * @brief set the output streams for the logger + * @param out output stream for both standard and error output + * @return void + */ void set_streams(std::streambuf *out) { set_streams(out, out); } - void set_verbosity(LOGGING_LEVEL level) { VERBOSITY_LEVEL = level; } - Logger() { - standard_output = new std::ostream(standard_buf); - error_output = new std::ostream(error_buf); - } + /** + * @brief set the verbosity level of the logger + * @param level verbosity level + */ + void set_verbosity(LOGGING_LEVEL level) { VERBOSITY_LEVEL = level; } + + /** + * @brief destructor for the logger + * @note closes the file if it is open + * @note flushes the output streams + */ ~Logger() { if (out_file.is_open()) out_file.close(); @@ -95,16 +152,51 @@ class Logger { delete standard_output; delete error_output; } + + /** + * @brief log a message + * @tparam level logging level + * @tparam Strings variadic template for inferring the types of the arguments (not necessarily strings but can be + * any printable type) + * @param s arguments to log + * @return void + */ template void log(const Strings... s) { if (level >= VERBOSITY_LEVEL) log_(s...); } + /** + * @brief log a message with DEBUG level + */ template void debug(const Strings... s) { log("[DEBUG]", s...); } + /** + * @brief log a message with INFO level + */ template void info(const Strings... s) { log("[INFO]", s...); } + /** + * @brief log a message with WARNING level + */ template void warn(const Strings... s) { log("[WARN]", s...); } + /** + * @brief log a message with ERROR level + */ template void error(const Strings... s) { log("[ERROR]", s...); } private: + std::streambuf *standard_buf = std::cout.rdbuf(); + std::streambuf *error_buf = std::cerr.rdbuf(); + std::ostream *standard_output; + std::ostream *error_output; + LOGGING_LEVEL VERBOSITY_LEVEL = LOGGING_LEVEL::INFO; + + std::ofstream out_file; + /** + * @brief log_ function private function to log messages + * @tparam level logging level + * @note this is the final function in the recursive template function log_ + * @note this function is called when there are no more arguments to log + * @note adds a newline at the end of the log message + */ template void log_() { if (level == LOGGING_LEVEL::ERROR) { *error_output << std::endl; @@ -112,6 +204,16 @@ class Logger { *standard_output << std::endl; } } + + /** + * @brief log_ recursive function private function to log messages + * @tparam level logging level + * @tparam First type of the first argument + * @tparam Strings variadic template for inferring the types of the arguments + * @param arg first argument to log + * @param s rest of the arguments to log + * @note called at first from the public log function + */ template void log_(First arg, const Strings... s) { if (level == LOGGING_LEVEL::ERROR) { *error_output << (arg) << ' '; @@ -125,16 +227,44 @@ class Logger { }; namespace internal { - +/** + * @brief global instance of the logger + */ extern aare::logger::Logger logger_instance; } // namespace internal +/** + * functions below are the public interface to the logger. + * These functions call the corresponding functions in the logger_instance + * @note this is done to avoid having to pass the logger_instance around and allow users to assign their own + * logger_instance + */ + +/** + * @brief log a message with the given level + * @tparam level logging level + * @tparam Strings variadic template for inferring the types of the arguments + * @param s arguments to log + * @return void + */ template void log(const Strings... s) { internal::logger_instance.log(s...); } +/** + * @brief log a message with DEBUG level + */ template void debug(const Strings... s) { internal::logger_instance.debug(s...); } +/** + * @brief log a message with INFO level + */ template void info(const Strings... s) { internal::logger_instance.info(s...); } +/** + * @brief log a message with WARNING level + */ template void warn(const Strings... s) { internal::logger_instance.warn(s...); } +/** + * @brief log a message with ERROR level + */ template void error(const Strings... s) { internal::logger_instance.error(s...); } extern void set_streams(std::streambuf *out, std::streambuf *err);