From 000ce2a54deced6c39f4f968bcc4db388ff69235 Mon Sep 17 00:00:00 2001 From: peri4 Date: Mon, 16 Sep 2024 16:06:07 +0300 Subject: [PATCH 01/40] PICout improvement: * renamed private members for more clear code * registerExternalBufferID() method to obtain unique ID for withExternalBuffer() * PICoutManipulators::PICoutStdStream enum for select stream (stdout or stderr) * Constructors now accept optional stream * piCerr and piCerrObj macros PIDir::temporary() moved to "mkdtemp" PILog: * now 4 levels * you can set max level * Error writes to piCerr --- libs/main/application/pilog.cpp | 61 ++-- libs/main/application/pilog.h | 43 ++- libs/main/containers/pideque.h | 8 +- libs/main/containers/pimap.h | 2 +- libs/main/containers/pivector.h | 8 +- libs/main/core/picout.cpp | 414 +++++++++++------------ libs/main/core/picout.h | 74 ++-- libs/main/io_devices/pidir.cpp | 10 +- libs/main/serialization/pibinarystream.h | 44 +-- main.cpp | 11 +- utils/system_daemon/main.cpp | 2 +- 11 files changed, 357 insertions(+), 320 deletions(-) diff --git a/libs/main/application/pilog.cpp b/libs/main/application/pilog.cpp index e5c2a4ff..f948d78d 100644 --- a/libs/main/application/pilog.cpp +++ b/libs/main/application/pilog.cpp @@ -76,6 +76,10 @@ PILog::PILog(): PIThread(), log_ts(&log_file) { split_time = 8_h; timestamp_format = "yyyy-MM-dd hh:mm:ss.zzz"; setLineFormat("t - c: m"); + id_by_cat[Level::Info] = PICout::registerExternalBufferID(); + id_by_cat[Level::Debug] = PICout::registerExternalBufferID(); + id_by_cat[Level::Warning] = PICout::registerExternalBufferID(); + id_by_cat[Level::Error] = PICout::registerExternalBufferID(); CONNECTU(PICout::Notifier::object(), finished, this, coutDone); } @@ -101,18 +105,28 @@ void PILog::setLineFormat(const PIString & f) { } -PICout PILog::debug(PIObject * context) { - return makePICout(context, Category::Debug); -} - - -PICout PILog::warning(PIObject * context) { - return makePICout(context, Category::Warning); +void PILog::setLevel(Level l) { + max_level = l; } PICout PILog::error(PIObject * context) { - return makePICout(context, Category::Error); + return makePICout(context, Level::Error); +} + + +PICout PILog::warning(PIObject * context) { + return makePICout(context, Level::Warning); +} + + +PICout PILog::info(PIObject * context) { + return makePICout(context, Level::Info); +} + + +PICout PILog::debug(PIObject * context) { + return makePICout(context, Level::Debug); } @@ -129,35 +143,27 @@ void PILog::stop() { void PILog::coutDone(int id, PIString * buffer) { - cout_mutex.lock(); - if (!cout_cat_by_id.contains(id)) { - cout_mutex.unlock(); - return; - } - auto cat = cout_cat_by_id.take(id, PILog::Category::Debug); - cout_mutex.unlock(); if (!buffer) return; + if (!id_by_cat.containsValue(id)) return; + auto cat = id_by_cat.key(id, PILog::Level::Debug); + if (cat > max_level) return; enqueue(*buffer, cat); delete buffer; } -PICout PILog::makePICout(PIObject * context, Category cat) { - cout_mutex.lock(); - int id = ++cout_id; - auto buffer = new PIString(); - cout_cat_by_id[id] = cat; - cout_mutex.unlock(); +PICout PILog::makePICout(PIObject * context, Level cat) { + auto buffer = new PIString(); if (context) { *buffer = "["_a + context->className(); if (context->name().isNotEmpty()) *buffer += " \"" + context->name() + "\""; *buffer += "] "; } - return PICout::withExternalBuffer(buffer, id, PICoutManipulators::AddSpaces); + return PICout::withExternalBuffer(buffer, id_by_cat.value(cat), PICoutManipulators::AddSpaces); } -void PILog::enqueue(const PIString & msg, Category cat) { +void PILog::enqueue(const PIString & msg, Level cat) { if (log_file.isClosed()) return; auto t = PIDateTime::fromSystemTime(PISystemTime::current()); PIMutexLocker ml(log_mutex); @@ -166,7 +172,7 @@ void PILog::enqueue(const PIString & msg, Category cat) { PIString PILog::entryToString(const Entry & e) const { - static PIStringList categories{"debug", "warn ", "error"}; + static PIStringList categories{"error", "warn ", "info ", "debug"}; PIString t = e.time.toString(timestamp_format); PIString ret = line_format_p; ret.replace("${t}", t).replace("${c}", categories[static_cast(e.cat)]).replace("${m}", e.msg); @@ -175,7 +181,7 @@ PIString PILog::entryToString(const Entry & e) const { void PILog::newFile() { - PIString aname = app_name; + PIString aname = log_name; if (aname.isNotEmpty()) aname += "__"; log_file.open(log_dir + "/" + aname + PIDateTime::current().toString("yyyy_MM_dd__hh_mm_ss") + ".log." + PIString::fromNumber(++part_number), @@ -205,6 +211,9 @@ void PILog::run() { log_mutex.unlock(); auto str = entryToString(qi); log_ts << str << "\n"; - piCout << str; + if (qi.cat == Level::Error) + piCerr << str; + else + piCout << str; } } diff --git a/libs/main/application/pilog.h b/libs/main/application/pilog.h index 968aba00..297fa37d 100644 --- a/libs/main/application/pilog.h +++ b/libs/main/application/pilog.h @@ -41,11 +41,18 @@ public: PILog(); ~PILog(); + enum class Level { + Error, + Warning, + Info, + Debug, + }; + //! \~english Returns prefix for filename. - PIString applicationName() const { return app_name; } + PIString logName() const { return log_name; } //! \~english Set prefix for filename. Should be set \b before \a setDir()! - void setApplicationName(const PIString & n) { app_name = n; } + void setLogName(const PIString & n) { log_name = n; } //! \~english Returns directory for log files. @@ -75,27 +82,30 @@ public: //! \~english Set line format. "t" is timestamp, "c" is category and "m" is message. Default is "t - c: m". void setLineFormat(const PIString & f); - PICout debug(PIObject * context = nullptr); - PICout warning(PIObject * context = nullptr); + + //! \~english Returns maximum level. + Level level() const { return max_level; } + + //! \~english Set maximum level. All levels greater than \"l\" will be ignored. Default if \a Level::Debug. + void setLevel(Level l); + + PICout error(PIObject * context = nullptr); + PICout warning(PIObject * context = nullptr); + PICout info(PIObject * context = nullptr); + PICout debug(PIObject * context = nullptr); //! \~english Write all queued lines and stop. Also called in destructor. void stop(); private: - enum class Category { - Debug, - Warning, - Error - }; - EVENT_HANDLER2(void, coutDone, int, id, PIString *, buff); - PICout makePICout(PIObject * context, Category cat); - void enqueue(const PIString & msg, Category cat = Category::Debug); + PICout makePICout(PIObject * context, Level cat); + void enqueue(const PIString & msg, Level cat = Level::Debug); struct Entry { - Category cat; + Level cat; PIDateTime time; PIString msg; }; @@ -104,14 +114,15 @@ private: void newFile(); void run() override; - PIMutex log_mutex, cout_mutex; + PIMutex log_mutex; PIFile log_file; PIIOTextStream log_ts; PITimeMeasurer split_tm; PISystemTime split_time; - PIString log_dir, timestamp_format, line_format, line_format_p, app_name; + PIString log_dir, timestamp_format, line_format, line_format_p, log_name; PIQueue queue; - PIMap cout_cat_by_id; + PIMap id_by_cat; + Level max_level = Level::Debug; int part_number = -1, cout_id = -1; }; diff --git a/libs/main/containers/pideque.h b/libs/main/containers/pideque.h index 4cd8af5c..03c3ff76 100644 --- a/libs/main/containers/pideque.h +++ b/libs/main/containers/pideque.h @@ -1383,7 +1383,7 @@ public: if (v.isEmpty()) return *this; #ifndef NDEBUG if (&v == this) { - printf("error with PIDeque<%s>::insert\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>::insert\n", __PIP_TYPENAME__(T)); } #endif assert(&v != this); @@ -1738,7 +1738,7 @@ public: if (v.isEmpty()) return *this; #ifndef NDEBUG if (&v == this) { - printf("error with PIDeque<%s>::append\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>::append\n", __PIP_TYPENAME__(T)); } #endif assert(&v != this); @@ -2400,7 +2400,7 @@ public: if (isEmpty()) return ret; #ifndef NDEBUG if (rows * cols != pid_size) { - printf("error with PIDeque<%s>::reshape\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>::reshape\n", __PIP_TYPENAME__(T)); } #endif assert(rows * cols == pid_size); @@ -2689,7 +2689,7 @@ private: T * p_d = reinterpret_cast(realloc(reinterpret_cast(pid_data), as * sizeof(T))); #ifndef NDEBUG if (!p_d) { - printf("error with PIDeque<%s>::alloc\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>::alloc\n", __PIP_TYPENAME__(T)); } #endif assert(p_d); diff --git a/libs/main/containers/pimap.h b/libs/main/containers/pimap.h index 17f9b713..26317296 100644 --- a/libs/main/containers/pimap.h +++ b/libs/main/containers/pimap.h @@ -355,7 +355,7 @@ public: inline PIMap & operator<<(const PIMap & other) { #ifndef NDEBUG if (&other == this) { - printf("error with PIMap<%s, %s>::<<\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIMap<%s, %s>::<<\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); } #endif assert(&other != this); diff --git a/libs/main/containers/pivector.h b/libs/main/containers/pivector.h index 23dee95c..781ec49c 100644 --- a/libs/main/containers/pivector.h +++ b/libs/main/containers/pivector.h @@ -1341,7 +1341,7 @@ public: if (v.isEmpty()) return *this; #ifndef NDEBUG if (&v == this) { - printf("error with PIVector<%s>::insert\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>::insert\n", __PIP_TYPENAME__(T)); } #endif assert(&v != this); @@ -1663,7 +1663,7 @@ public: if (v.isEmpty()) return *this; #ifndef NDEBUG if (&v == this) { - printf("error with PIVector<%s>::push_back\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>::push_back\n", __PIP_TYPENAME__(T)); } #endif assert(&v != this); @@ -2296,7 +2296,7 @@ public: inline PIVector> reshape(size_t rows, size_t cols, ReshapeOrder order = ReshapeByRow) const { #ifndef NDEBUG if (rows * cols != piv_size) { - printf("error with PIVector<%s>::reshape\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>::reshape\n", __PIP_TYPENAME__(T)); } #endif assert(rows * cols == piv_size); @@ -2565,7 +2565,7 @@ private: T * p_d = reinterpret_cast(realloc(reinterpret_cast(piv_data), as * sizeof(T))); #ifndef NDEBUG if (!p_d) { - printf("error with PIVector<%s>::alloc\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>::alloc\n", __PIP_TYPENAME__(T)); } #endif assert(p_d); diff --git a/libs/main/core/picout.cpp b/libs/main/core/picout.cpp index e5a6a8d0..32aad138 100644 --- a/libs/main/core/picout.cpp +++ b/libs/main/core/picout.cpp @@ -141,7 +141,7 @@ PIString & PICout::__string__() { return *ret; } -PICout::OutputDevices PICout::devs = PICout::StdOut; +PICout::OutputDevices PICout::devs = PICout::Console; PRIVATE_DEFINITION_START(PICout) PIStack cos_; @@ -158,35 +158,53 @@ WORD PICout::__Private__::dattr = 0; DWORD PICout::__Private__::smode = 0; #endif -PICout::PICout(int controls): fo_(true), cc_(false), fc_(false), act_(true), cnb_(10), co_(controls) { - buffer_ = nullptr; + +std::ostream & getStdStream(PICoutManipulators::PICoutStdStream s) { + switch (s) { + case PICoutManipulators::StdOut: return std::cout; + case PICoutManipulators::StdErr: return std::cerr; + default: break; + } + return std::cout; +} + +std::wostream & getStdWStream(PICoutManipulators::PICoutStdStream s) { + switch (s) { + case PICoutManipulators::StdOut: return std::wcout; + case PICoutManipulators::StdErr: return std::wcerr; + default: break; + } + return std::wcout; +} + + +PICout::PICout(int controls, PICoutStdStream stream): ctrl_(controls), stream_(stream) { init(); } -PICout::PICout(bool active): fo_(true), cc_(false), fc_(false), act_(active), cnb_(10), co_(PICoutManipulators::DefaultControls) { - buffer_ = nullptr; - if (act_) init(); +PICout::PICout(bool active, PICoutStdStream stream): actve_(active), stream_(stream) { + if (actve_) init(); } PICout::PICout(const PICout & other) - : fo_(other.fo_) - , cc_(true) - , fc_(false) - , act_(other.act_) - , cnb_(other.cnb_) - , attr_(other.attr_) + : first_out_(other.first_out_) + , is_copy_(true) + , actve_(other.actve_) + , int_base_(other.int_base_) + , win_attr_(other.win_attr_) , id_(other.id_) , buffer_(other.buffer_) - , co_(other.co_) {} + , ctrl_(other.ctrl_) + , stream_(other.stream_) {} PICout::~PICout() { - if (!act_) return; - if (fc_) applyFormat(PICoutManipulators::Default); - if (cc_) return; + if (!actve_) return; + if (format_changed_) applyFormat(PICoutManipulators::Default); + if (is_copy_) return; newLine(); - if ((co_ & NoLock) != NoLock) { + if ((ctrl_ & NoLock) != NoLock) { PICout::__mutex__().unlock(); } if (buffer_) { @@ -196,7 +214,7 @@ PICout::~PICout() { PICout & PICout::operator<<(PICoutAction v) { - if (!act_) return *this; + if (!actve_) return *this; #ifdef WINDOWS CONSOLE_SCREEN_BUFFER_INFO sbi; COORD coord; @@ -204,12 +222,12 @@ PICout & PICout::operator<<(PICoutAction v) { #endif switch (v) { case PICoutManipulators::Flush: - if (!buffer_ && isOutputDeviceActive(StdOut)) { - std::cout << std::flush; + if (!buffer_ && isOutputDeviceActive(Console)) { + getStdStream(stream_) << std::flush; } break; case PICoutManipulators::Backspace: - if (isOutputDeviceActive(StdOut)) { + if (isOutputDeviceActive(Console)) { #ifdef WINDOWS GetConsoleScreenBufferInfo(__Private__::hOut, &sbi); coord = sbi.dwCursorPosition; @@ -223,7 +241,7 @@ PICout & PICout::operator<<(PICoutAction v) { } break; case PICoutManipulators::ShowCursor: - if (isOutputDeviceActive(StdOut)) { + if (isOutputDeviceActive(Console)) { #ifdef WINDOWS GetConsoleCursorInfo(__Private__::hOut, &curinfo); curinfo.bVisible = true; @@ -234,7 +252,7 @@ PICout & PICout::operator<<(PICoutAction v) { } break; case PICoutManipulators::HideCursor: - if (isOutputDeviceActive(StdOut)) { + if (isOutputDeviceActive(Console)) { #ifdef WINDOWS GetConsoleCursorInfo(__Private__::hOut, &curinfo); curinfo.bVisible = false; @@ -245,7 +263,7 @@ PICout & PICout::operator<<(PICoutAction v) { } break; case PICoutManipulators::ClearLine: - if (isOutputDeviceActive(StdOut)) { + if (isOutputDeviceActive(Console)) { #ifdef WINDOWS GetConsoleScreenBufferInfo(__Private__::hOut, &sbi); coord = sbi.dwCursorPosition; @@ -266,7 +284,7 @@ PICout & PICout::operator<<(PICoutAction v) { } break; case PICoutManipulators::ClearScreen: - if (isOutputDeviceActive(StdOut)) { + if (isOutputDeviceActive(Console)) { #ifdef WINDOWS /// TODO : wondows ClearScreen !!! #else @@ -282,12 +300,31 @@ PICout & PICout::operator<<(PICoutAction v) { } +PICout & PICout::setControl(PICoutManipulators::PICoutControl c, bool on) { + ctrl_.setFlag(c, on); + return *this; +} + + +PICout & PICout::setControls(PICoutManipulators::PICoutControls c) { + ctrl_ = c; + return *this; +} + + +PICout & PICout::saveAndSetControls(PICoutManipulators::PICoutControls c) { + saveControls(); + ctrl_ = c; + return *this; +} + + PICout & PICout::operator<<(PICoutManipulators::PICoutFormat v) { switch (v) { - case PICoutManipulators::Bin: cnb_ = 2; break; - case PICoutManipulators::Oct: cnb_ = 8; break; - case PICoutManipulators::Dec: cnb_ = 10; break; - case PICoutManipulators::Hex: cnb_ = 16; break; + case PICoutManipulators::Bin: int_base_ = 2; break; + case PICoutManipulators::Oct: int_base_ = 8; break; + case PICoutManipulators::Dec: int_base_ = 10; break; + case PICoutManipulators::Hex: int_base_ = 16; break; default: applyFormat(v); }; return *this; @@ -295,10 +332,10 @@ PICout & PICout::operator<<(PICoutManipulators::PICoutFormat v) { PICout & PICout::operator<<(PIFlags v) { - if (v[PICoutManipulators::Bin]) cnb_ = 2; - if (v[PICoutManipulators::Oct]) cnb_ = 8; - if (v[PICoutManipulators::Dec]) cnb_ = 10; - if (v[PICoutManipulators::Hex]) cnb_ = 16; + if (v[PICoutManipulators::Bin]) int_base_ = 2; + if (v[PICoutManipulators::Oct]) int_base_ = 8; + if (v[PICoutManipulators::Dec]) int_base_ = 10; + if (v[PICoutManipulators::Hex]) int_base_ = 16; if (v[PICoutManipulators::Bold]) applyFormat(PICoutManipulators::Bold); if (v[PICoutManipulators::Faint]) applyFormat(PICoutManipulators::Faint); if (v[PICoutManipulators::Italic]) applyFormat(PICoutManipulators::Italic); @@ -324,31 +361,78 @@ PICout & PICout::operator<<(PIFlags v) { return *this; } -#define PIINTCOUT(v) \ - { \ - if (!act_) return *this; \ - space(); \ - if (cnb_ == 10) { \ - if (buffer_) { \ - (*buffer_) += PIString::fromNumber(v); \ - } else { \ - if (PICout::isOutputDeviceActive(PICout::StdOut)) std::cout << (v); \ - if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__() += PIString::fromNumber(v); \ - } \ - } else \ - write(PIString::fromNumber(v, cnb_)); \ - return *this; \ + +void PICout::stdoutPIString(const PIString & str, PICoutStdStream s) { +#ifdef HAS_LOCALE + std::wstring_convert, char16_t> utf8conv; + getStdStream(s) << utf8conv.to_bytes((char16_t *)&(const_cast(str).front()), + (char16_t *)&(const_cast(str).front()) + str.size()); +#else + for (PIChar c: str) + getStdWStream(s).put(c.toWChar()); +#endif +} + + +PICout & PICout::write(const char * str, int len) { + if (!actve_ || !str) return *this; + if (buffer_) { + buffer_->append(PIString(str, len)); + } else { + if (isOutputDeviceActive(Console)) getStdStream(stream_).write(str, len); + if (isOutputDeviceActive(Buffer)) PICout::__string__().append(PIString(str, len)); + } + return *this; +} + + +PICout & PICout::write(const PIString & s) { + if (!actve_) return *this; + if (buffer_) { + buffer_->append(s); + } else { + if (isOutputDeviceActive(Console)) stdoutPIString(s, stream_); + if (isOutputDeviceActive(Buffer)) PICout::__string__().append(s); + } + return *this; +} + + +void PICout::writeChar(char c) { + if (buffer_) { + buffer_->append(c); + } else { + if (isOutputDeviceActive(Console)) getStdStream(stream_) << c; + if (isOutputDeviceActive(Buffer)) PICout::__string__().append(c); + } +} + + +#define PIINTCOUT(v) \ + { \ + if (!actve_) return *this; \ + space(); \ + if (int_base_ == 10) { \ + if (buffer_) { \ + (*buffer_) += PIString::fromNumber(v); \ + } else { \ + if (isOutputDeviceActive(Console)) getStdStream(stream_) << (v); \ + if (isOutputDeviceActive(Buffer)) PICout::__string__() += PIString::fromNumber(v); \ + } \ + } else \ + write(PIString::fromNumber(v, int_base_)); \ + return *this; \ } -#define PIFLOATCOUT(v) \ - { \ - if (buffer_) { \ - (*buffer_) += PIString::fromNumber(v, 'g'); \ - } else { \ - if (PICout::isOutputDeviceActive(PICout::StdOut)) std::cout << (v); \ - if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__() += PIString::fromNumber(v, 'g'); \ - } \ - } \ +#define PIFLOATCOUT(v) \ + { \ + if (buffer_) { \ + (*buffer_) += PIString::fromNumber(v, 'g'); \ + } else { \ + if (isOutputDeviceActive(Console)) getStdStream(stream_) << (v); \ + if (isOutputDeviceActive(Buffer)) PICout::__string__() += PIString::fromNumber(v, 'g'); \ + } \ + } \ return *this; @@ -361,7 +445,7 @@ PICout & PICout::operator<<(const PIString & v) { } PICout & PICout::operator<<(const char * v) { - if (!act_ || !v) return *this; + if (!actve_ || !v) return *this; if (v[0] == '\0') return *this; space(); quote(); @@ -371,7 +455,7 @@ PICout & PICout::operator<<(const char * v) { } PICout & PICout::operator<<(bool v) { - if (!act_) return *this; + if (!actve_) return *this; space(); if (v) write("true"); @@ -381,7 +465,7 @@ PICout & PICout::operator<<(bool v) { } PICout & PICout::operator<<(char v) { - if (!act_) return *this; + if (!actve_) return *this; space(); write(v); return *this; @@ -406,32 +490,32 @@ PICout & PICout::operator<<(llong v){PIINTCOUT(v)} PICout & PICout::operator<<(ullong v){PIINTCOUT(v)} PICout & PICout::operator<<(float v) { - if (!act_) return *this; + if (!actve_) return *this; space(); PIFLOATCOUT(v) } PICout & PICout::operator<<(double v) { - if (!act_) return *this; + if (!actve_) return *this; space(); PIFLOATCOUT(v) } PICout & PICout::operator<<(ldouble v) { - if (!act_) return *this; + if (!actve_) return *this; space(); PIFLOATCOUT(v) } PICout & PICout::operator<<(const void * v) { - if (!act_) return *this; + if (!actve_) return *this; space(); write("0x" + PIString::fromNumber(ullong(v), 16)); return *this; } PICout & PICout::operator<<(const PIObject * v) { - if (!act_) return *this; + if (!actve_) return *this; space(); if (v == 0) write("PIObject*(0x0)"); @@ -443,74 +527,38 @@ PICout & PICout::operator<<(const PIObject * v) { } PICout & PICout::operator<<(PICoutSpecialChar v) { - if (!act_) return *this; + if (!actve_) return *this; switch (v) { - case Null: - if (buffer_) { - (*buffer_) += PIChar(); - } else { - if (isOutputDeviceActive(StdOut)) std::cout << char(0); - if (isOutputDeviceActive(Buffer)) PICout::__string__() += PIChar(); - } - break; + case Null: writeChar(char(0)); break; case NewLine: - if (buffer_) { - (*buffer_) += "\n"; - } else { - if (isOutputDeviceActive(StdOut)) std::cout << '\n'; - if (isOutputDeviceActive(Buffer)) PICout::__string__() += "\n"; - } - fo_ = true; - break; - case Tab: - if (buffer_) { - (*buffer_) += "\t"; - } else { - if (isOutputDeviceActive(StdOut)) std::cout << '\t'; - if (isOutputDeviceActive(Buffer)) PICout::__string__() += "\t"; - } + first_out_ = true; + writeChar('\n'); break; + case Tab: writeChar('\t'); break; case Esc: #ifdef CC_VC - if (buffer_) { - (*buffer_) += PIChar(27); - } else { - if (isOutputDeviceActive(StdOut)) std::cout << char(27); - if (isOutputDeviceActive(Buffer)) PICout::__string__() += PIChar(27); - } + writeChar(char(27)); #else - if (buffer_) { - (*buffer_) += "\e"; - } else { - if (isOutputDeviceActive(StdOut)) std::cout << '\e'; - if (isOutputDeviceActive(Buffer)) PICout::__string__() += "\e"; - } + writeChar('\e'); #endif break; - case Quote: - if (buffer_) { - (*buffer_) += "\""; - } else { - if (isOutputDeviceActive(StdOut)) std::cout << '"'; - if (isOutputDeviceActive(Buffer)) PICout::__string__() += "\""; - } - break; + case Quote: writeChar('"'); break; }; return *this; } PICout & PICout::saveControls() { - if (!act_) return *this; - PRIVATE->cos_.push(co_); + if (!actve_) return *this; + PRIVATE->cos_.push(ctrl_); return *this; } PICout & PICout::restoreControls() { - if (!act_) return *this; + if (!actve_) return *this; if (!PRIVATE->cos_.isEmpty()) { - co_ = PRIVATE->cos_.top(); + ctrl_ = PRIVATE->cos_.top(); PRIVATE->cos_.pop(); } return *this; @@ -524,16 +572,11 @@ PICout & PICout::restoreControls() { //! Добавляет пробел если это не первый вывод и установлен флаг \a AddSpaces //! \~\sa \a quote(), \a newLine() PICout & PICout::space() { - if (!act_) return *this; - if (!fo_ && co_[AddSpaces]) { - if (buffer_) { - (*buffer_) += " "; - } else { - if (isOutputDeviceActive(StdOut)) std::cout << ' '; - if (isOutputDeviceActive(Buffer)) PICout::__string__() += " "; - } + if (!actve_) return *this; + if (!first_out_ && ctrl_[AddSpaces]) { + writeChar(' '); } - fo_ = false; + first_out_ = false; return *this; } @@ -544,16 +587,11 @@ PICout & PICout::space() { //! Добавляет кавычки если установлен флаг \a AddQuotes //! \~\sa \a space(), \a newLine() PICout & PICout::quote() { - if (!act_) return *this; - if (co_[AddQuotes]) { - if (buffer_) { - (*buffer_) += "\""; - } else { - if (isOutputDeviceActive(StdOut)) std::cout << '"'; - if (isOutputDeviceActive(Buffer)) PICout::__string__() += "\""; - } + if (!actve_) return *this; + if (ctrl_[AddQuotes]) { + writeChar('"'); } - fo_ = false; + first_out_ = false; return *this; } @@ -564,74 +602,28 @@ PICout & PICout::quote() { //! Добавляет новую строку если установлен флаг \a AddNewLine //! \~\sa \a space(), \a quote() PICout & PICout::newLine() { - if (!act_) return *this; - if (co_[AddNewLine]) { - if (buffer_) { - (*buffer_) += "\n"; - } else { - if (isOutputDeviceActive(StdOut)) std::cout << std::endl; - if (isOutputDeviceActive(Buffer)) PICout::__string__() += "\n"; - } + if (!actve_) return *this; + if (ctrl_[AddNewLine]) { + writeChar('\n'); } - fo_ = false; + first_out_ = false; return *this; } PICout & PICout::write(char c) { - if (!act_) return *this; - if (buffer_) { - buffer_->append(c); - } else { - if (PICout::isOutputDeviceActive(PICout::StdOut)) std::cout << c; - if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__().append(c); - } + if (!actve_) return *this; + writeChar(c); return *this; } PICout & PICout::write(const char * str) { - if (!act_ || !str) return *this; + if (!actve_ || !str) return *this; return write(str, strlen(str)); } -PICout & PICout::write(const char * str, int len) { - if (!act_ || !str) return *this; - if (buffer_) { - buffer_->append(PIString(str, len)); - } else { - if (PICout::isOutputDeviceActive(PICout::StdOut)) std::cout.write(str, len); - if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__().append(PIString(str, len)); - } - return *this; -} - - -PICout & PICout::write(const PIString & s) { - if (!act_) return *this; - if (buffer_) { - buffer_->append(s); - } else { - if (PICout::isOutputDeviceActive(PICout::StdOut)) stdoutPIString(s); - if (PICout::isOutputDeviceActive(PICout::Buffer)) PICout::__string__().append(s); - } - return *this; -} - - -void PICout::stdoutPIString(const PIString & s) { -#ifdef HAS_LOCALE - std::wstring_convert, char16_t> utf8conv; - std::cout << utf8conv.to_bytes((char16_t *)&(const_cast(s).front()), - (char16_t *)&(const_cast(s).front()) + s.size()); -#else - for (PIChar c: s) - std::wcout.put(c.toWChar()); -#endif -} - - void PICout::init() { #ifdef WINDOWS if (__Private__::hOut == 0) { @@ -640,19 +632,18 @@ void PICout::init() { GetConsoleScreenBufferInfo(__Private__::hOut, &sbi); __Private__::dattr = sbi.wAttributes; } - attr_ = __Private__::dattr; + win_attr_ = __Private__::dattr; #endif - id_ = 0; - if ((co_ & NoLock) != NoLock) { + if ((ctrl_ & NoLock) != NoLock) { PICout::__mutex__().lock(); } } void PICout::applyFormat(PICoutFormat f) { - if (!act_) return; - if (buffer_ || !isOutputDeviceActive(StdOut)) return; - fc_ = true; + if (!actve_) return; + if (buffer_ || !isOutputDeviceActive(Console)) return; + format_changed_ = true; #ifdef WINDOWS static int mask_fore = ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); static int mask_back = ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); @@ -661,28 +652,28 @@ void PICout::applyFormat(PICoutFormat f) { case Oct: case Dec: case Hex: break; - case PICoutManipulators::Bold: attr_ |= FOREGROUND_INTENSITY; break; - case PICoutManipulators::Underline: attr_ |= COMMON_LVB_UNDERSCORE; break; - case PICoutManipulators::Black: attr_ = (attr_ & mask_fore); break; - case PICoutManipulators::Red: attr_ = (attr_ & mask_fore) | FOREGROUND_RED; break; - case PICoutManipulators::Green: attr_ = (attr_ & mask_fore) | FOREGROUND_GREEN; break; - case PICoutManipulators::Blue: attr_ = (attr_ & mask_fore) | FOREGROUND_BLUE; break; - case PICoutManipulators::Yellow: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN; break; - case PICoutManipulators::Magenta: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_BLUE; break; - case PICoutManipulators::Cyan: attr_ = (attr_ & mask_fore) | FOREGROUND_GREEN | FOREGROUND_BLUE; break; - case PICoutManipulators::White: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; - case PICoutManipulators::BackBlack: attr_ = (attr_ & mask_back); break; - case PICoutManipulators::BackRed: attr_ = (attr_ & mask_back) | BACKGROUND_RED; break; - case PICoutManipulators::BackGreen: attr_ = (attr_ & mask_back) | BACKGROUND_GREEN; break; - case PICoutManipulators::BackBlue: attr_ = (attr_ & mask_back) | BACKGROUND_BLUE; break; - case PICoutManipulators::BackYellow: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN; break; - case PICoutManipulators::BackMagenta: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_BLUE; break; - case PICoutManipulators::BackCyan: attr_ = (attr_ & mask_back) | BACKGROUND_GREEN | BACKGROUND_BLUE; break; - case PICoutManipulators::BackWhite: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; break; - case PICoutManipulators::Default: attr_ = __Private__::dattr; break; + case PICoutManipulators::Bold: win_attr_ |= FOREGROUND_INTENSITY; break; + case PICoutManipulators::Underline: win_attr_ |= COMMON_LVB_UNDERSCORE; break; + case PICoutManipulators::Black: win_attr_ = (win_attr_ & mask_fore); break; + case PICoutManipulators::Red: win_attr_ = (win_attr_ & mask_fore) | FOREGROUND_RED; break; + case PICoutManipulators::Green: win_attr_ = (win_attr_ & mask_fore) | FOREGROUND_GREEN; break; + case PICoutManipulators::Blue: win_attr_ = (win_attr_ & mask_fore) | FOREGROUND_BLUE; break; + case PICoutManipulators::Yellow: win_attr_ = (win_attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN; break; + case PICoutManipulators::Magenta: win_attr_ = (win_attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_BLUE; break; + case PICoutManipulators::Cyan: win_attr_ = (win_attr_ & mask_fore) | FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case PICoutManipulators::White: win_attr_ = (win_attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case PICoutManipulators::BackBlack: win_attr_ = (win_attr_ & mask_back); break; + case PICoutManipulators::BackRed: win_attr_ = (win_attr_ & mask_back) | BACKGROUND_RED; break; + case PICoutManipulators::BackGreen: win_attr_ = (win_attr_ & mask_back) | BACKGROUND_GREEN; break; + case PICoutManipulators::BackBlue: win_attr_ = (win_attr_ & mask_back) | BACKGROUND_BLUE; break; + case PICoutManipulators::BackYellow: win_attr_ = (win_attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN; break; + case PICoutManipulators::BackMagenta: win_attr_ = (win_attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_BLUE; break; + case PICoutManipulators::BackCyan: win_attr_ = (win_attr_ & mask_back) | BACKGROUND_GREEN | BACKGROUND_BLUE; break; + case PICoutManipulators::BackWhite: win_attr_ = (win_attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; break; + case PICoutManipulators::Default: win_attr_ = __Private__::dattr; break; default: break; } - SetConsoleTextAttribute(__Private__::hOut, attr_); + SetConsoleTextAttribute(__Private__::hOut, win_attr_); #else switch (f) { case Bin: @@ -761,3 +752,8 @@ PICout PICout::withExternalBuffer(PIString * buffer, int id, PIFlagsnew_id.fetch_add(1); +} diff --git a/libs/main/core/picout.h b/libs/main/core/picout.h index 0e787d26..92e2fc47 100644 --- a/libs/main/core/picout.h +++ b/libs/main/core/picout.h @@ -40,10 +40,14 @@ # define piCoutObj #else -# define piCout PICout(piDebug) -# define piCoutObj \ - PICout(piDebug && debug()) << (PIStringAscii("[") + className() + \ - (name().isEmpty() ? "]" : PIStringAscii(" \"") + name() + PIStringAscii("\"]"))) +# define piCout PICout(piDebug, PICoutManipulators::StdOut) +# define piCoutObj \ + PICout(piDebug && debug(), PICoutManipulators::StdOut) \ + << (PIStringAscii("[") + className() + (name().isEmpty() ? "]" : PIStringAscii(" \"") + name() + PIStringAscii("\"]"))) +# define piCerr PICout(piDebug, PICoutManipulators::StdErr) +# define piCerrObj \ + PICout(piDebug && debug(), PICoutManipulators::StdErr) \ + << (PIStringAscii("[") + className() + (name().isEmpty() ? "]" : PIStringAscii(" \"") + name() + PIStringAscii("\"]"))) #endif @@ -55,6 +59,7 @@ class PIObject; //! \~russian Пространство имен содержит перечисления для контроля PICout namespace PICoutManipulators { + //! \~english Enum contains special characters //! \~russian Перечисление со спецсимволами enum PICoutSpecialChar { @@ -65,6 +70,7 @@ enum PICoutSpecialChar { Quote /*! \~english Quote character, '\"' \~russian Кавычки, '\"' */ }; + //! \~english Enum contains immediate action //! \~russian Перечисление с немедленными действиями enum PICoutAction { @@ -79,6 +85,7 @@ enum PICoutAction { restoreControl() */ }; + //! \~english Enum contains control of PICout //! \~russian Перечисление с управлением PICout enum PICoutControl { @@ -91,6 +98,7 @@ enum PICoutControl { NoLock /*! \~english Don`t use mutex for output \~russian Не использовать мьютекс при выводе */ = 0x100, }; + //! \~english Enum contains output format //! \~russian Перечисление с форматом вывода enum PICoutFormat { @@ -122,7 +130,17 @@ enum PICoutFormat { Default /*! \~english Default format \~russian Формат по умолчанию */ = 0x4000000 }; + +//! \~english Enum contains console streams +//! \~russian Перечисление с потоками консоли +enum PICoutStdStream { + StdOut /*! \~english Standard output stream \~russian Стандартный поток вывода */ = 0, + StdErr /*! \~english Standard error stream \~russian Стандартный поток ошибок */ = 1, +}; + + typedef PIFlags PICoutControls; + } // namespace PICoutManipulators @@ -134,11 +152,11 @@ class PIP_EXPORT PICout { public: //! \~english Default constructor with default features (AddSpaces and AddNewLine) //! \~russian Конструктор по умолчанию (AddSpaces и AddNewLine) - PICout(int controls = PICoutManipulators::DefaultControls); + PICout(int controls = PICoutManipulators::DefaultControls, PICoutManipulators::PICoutStdStream stream = PICoutManipulators::StdOut); //! \~english Construct with default features (AddSpaces and AddNewLine), but if \"active\" is false does nothing //! \~russian Конструктор по умолчанию (AddSpaces и AddNewLine), но если не \"active\" то будет неактивным - PICout(bool active); + PICout(bool active, PICoutManipulators::PICoutStdStream stream = PICoutManipulators::StdOut); PICout(const PICout & other); @@ -146,6 +164,8 @@ public: class PIP_EXPORT Notifier { + friend class PICout; + public: //! \~english Singleton access to %PICout::Notifier //! \~russian Синглтон класса %PICout::Notifier @@ -158,15 +178,17 @@ public: private: Notifier(); PIObject * o; + std::atomic_int new_id = {1}; }; //! \~english Enum contains output devices of %PICout //! \~russian Перечисление с устройствами вывода для %PICout enum OutputDevice { - NoDevices /** \~english %PICout is disabled \~russian %PICout неактивен */ = 0x0, - StdOut /** \~english Standard console output \~russian Стандартный вывод в консоль */ = 0x1, - Buffer /** \~english Internal buffer \~russian Внутренний буфер */ = 0x2, - AllDevices /** \~english All \~russian Все */ = 0xFFFF, + NoDevices /** \~english %PICout is disabled \~russian %PICout неактивен */ = 0x0, + Console /** \~english Standard console output \~russian Стандартный вывод в консоль */ = 0x1, + StdOut DEPRECATEDM("use PICout::Console") = Console, + Buffer /** \~english Internal buffer \~russian Внутренний буфер */ = 0x2, + AllDevices /** \~english All \~russian Все */ = 0xFFFF, }; typedef PIFlags OutputDevices; @@ -261,25 +283,15 @@ public: //! \~english Set control flag "c" is "on" state //! \~russian Установить флаг "c" в "on" состояние - PICout & setControl(PICoutManipulators::PICoutControl c, bool on = true) { - co_.setFlag(c, on); - return *this; - } + PICout & setControl(PICoutManipulators::PICoutControl c, bool on = true); //! \~english Set control flags "c" //! \~russian Установить флаги "c" - PICout & setControls(PICoutManipulators::PICoutControls c) { - co_ = c; - return *this; - } + PICout & setControls(PICoutManipulators::PICoutControls c); //! \~english Exec \a saveControls() and set control flags to "c" //! \~russian Иыполнить \a saveControls() и Установить флаги "c" - PICout & saveAndSetControls(PICoutManipulators::PICoutControls c) { - saveControls(); - co_ = c; - return *this; - } + PICout & saveAndSetControls(PICoutManipulators::PICoutControls c); //! \~english Save control flags to internal stack //! \~russian Сохраняет состояние флагов во внутренний стек @@ -321,7 +333,7 @@ public: //! \~english Output \a PIString to stdout //! \~russian Вывод \a PIString в stdout - static void stdoutPIString(const PIString & s); + static void stdoutPIString(const PIString & str, PICoutManipulators::PICoutStdStream s = PICoutManipulators::StdOut); //! \~english Returns internal PIString buffer //! \~russian Возвращает внутренний PIString буфер @@ -368,19 +380,25 @@ public: PIFlags controls = PICoutManipulators::AddSpaces | PICoutManipulators::AddNewLine); + //! \~english Returns unique external buffer ID for later use in \a withExternalBuffer() + //! \~russian Возвращает уникальный ID для внешнего буфера для дальнейшего использования в \a withExternalBuffer() + static int registerExternalBufferID(); + static PIMutex & __mutex__(); static PIString & __string__(); private: void init(); void applyFormat(PICoutManipulators::PICoutFormat f); + void writeChar(char c); static OutputDevices devs; PRIVATE_DECLARATION(PIP_EXPORT) - bool fo_, cc_, fc_, act_; - int cnb_, attr_, id_; - PIString * buffer_; - PICoutManipulators::PICoutControls co_; + bool first_out_ = true, is_copy_ = false, format_changed_ = false, actve_ = true; + int int_base_ = 10, win_attr_ = 0, id_ = 0; + PIString * buffer_ = nullptr; + PICoutManipulators::PICoutControls ctrl_ = PICoutManipulators::DefaultControls; + PICoutManipulators::PICoutStdStream stream_ = PICoutManipulators::StdOut; }; #endif // PICOUT_H diff --git a/libs/main/io_devices/pidir.cpp b/libs/main/io_devices/pidir.cpp index 5e370df8..9eda8608 100644 --- a/libs/main/io_devices/pidir.cpp +++ b/libs/main/io_devices/pidir.cpp @@ -459,7 +459,7 @@ PIDir PIDir::current() { PIDir PIDir::home() { #ifndef ESP_PLATFORM - char * rc = 0; + char * rc = nullptr; #endif #ifdef WINDOWS rc = new char[1024]; @@ -482,7 +482,7 @@ PIDir PIDir::home() { #else # ifndef ESP_PLATFORM rc = getenv("HOME"); - if (rc == 0) return PIDir(); + if (!rc) return PIDir(); return PIDir(rc); # else return PIDir(); @@ -492,7 +492,7 @@ PIDir PIDir::home() { PIDir PIDir::temporary() { - char * rc = 0; + char * rc = nullptr; #ifdef WINDOWS rc = new char[1024]; memset(rc, 0, 1024); @@ -507,8 +507,8 @@ PIDir PIDir::temporary() { s.prepend(separator); return PIDir(s); #else - rc = tmpnam(0); - if (rc == 0) return PIDir(); + rc = mkdtemp("/tmp/pidir_tmp_XXXXXX"); + if (!rc) return PIDir(); PIString s(rc); return PIDir(s.left(s.findLast(PIDir::separator))); #endif diff --git a/libs/main/serialization/pibinarystream.h b/libs/main/serialization/pibinarystream.h index 8b1be096..ab294417 100644 --- a/libs/main/serialization/pibinarystream.h +++ b/libs/main/serialization/pibinarystream.h @@ -70,7 +70,7 @@ public: bool binaryStreamAppend(const void * d, size_t s) { if (!static_cast

(this)->binaryStreamAppendImp(d, s)) { return false; - printf("[PIBinaryStream] binaryStreamAppend() error\n"); + fprintf(stderr, "[PIBinaryStream] binaryStreamAppend() error\n"); } return true; } @@ -81,7 +81,7 @@ public: if (!static_cast

(this)->binaryStreamTakeImp(d, s)) { _was_read_error_ = true; return false; - printf("[PIBinaryStream] binaryStreamTake() error\n"); + fprintf(stderr, "[PIBinaryStream] binaryStreamTake() error\n"); } return true; } @@ -323,7 +323,7 @@ template::value, int>::type = 0> inline PIBinaryStreamTrivialRef

operator>>(PIBinaryStream

& s, T & v) { if (!s.binaryStreamTake(&v, sizeof(v))) { - printf("error with %s\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with %s\n", __PIP_TYPENAME__(T)); } return s; } @@ -341,13 +341,13 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector & v) { // piCout << ">> vector trivial default"; int sz = s.binaryStreamTakeInt(); if (s.wasReadError()) { - printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } v._resizeRaw(sz); if (!s.binaryStreamTake(v.data(), sz * sizeof(T))) { - printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T)); v.clear(); } return s; @@ -362,7 +362,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector & v) { // piCout << ">> vector trivial custom"; int sz = s.binaryStreamTakeInt(); if (s.wasReadError()) { - printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -370,7 +370,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector & v) { for (int i = 0; i < sz; ++i) { s >> v[i]; if (s.wasReadError()) { - printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -391,13 +391,13 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & v) { // piCout << ">> deque trivial default"; int sz = s.binaryStreamTakeInt(); if (s.wasReadError()) { - printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } v._resizeRaw(sz); if (!s.binaryStreamTake(v.data(), sz * sizeof(T))) { - printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); v.clear(); } return s; @@ -412,7 +412,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & v) { // piCout << ">> deque trivial custom"; int sz = s.binaryStreamTakeInt(); if (s.wasReadError()) { - printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -420,7 +420,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & v) { for (int i = 0; i < sz; ++i) { s >> v[i]; if (s.wasReadError()) { - printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -443,13 +443,13 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector2D & v) r = s.binaryStreamTakeInt(); c = s.binaryStreamTakeInt(); if (s.wasReadError()) { - printf("error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } v._resizeRaw(r, c); if (!s.binaryStreamTake(v.data(), v.size() * sizeof(T))) { - printf("error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); v.clear(); } return s; @@ -468,7 +468,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector2D & v) c = s.binaryStreamTakeInt(); s >> tmp; if (s.wasReadError()) { - printf("error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -543,7 +543,7 @@ template & operator>>(PIBinaryStream

& s, PIVector & v) { int sz = s.binaryStreamTakeInt(); if (s.wasReadError()) { - printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -551,7 +551,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector & v) { for (size_t i = 0; i < v.size(); ++i) { s >> v[i]; if (s.wasReadError()) { - printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -566,7 +566,7 @@ template & operator>>(PIBinaryStream

& s, PIDeque & v) { int sz = s.binaryStreamTakeInt(); if (s.wasReadError()) { - printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -574,7 +574,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & v) { for (size_t i = 0; i < v.size(); ++i) { s >> v[i]; if (s.wasReadError()) { - printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -593,7 +593,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector2D & v) c = s.binaryStreamTakeInt(); s >> tmp; if (s.wasReadError()) { - printf("error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -625,7 +625,7 @@ template inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIMap & v) { int sz = s.binaryStreamTakeInt(); if (s.wasReadError()) { - printf("error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -635,7 +635,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIMap & v) ind = s.binaryStreamTakeInt(); s >> v.pim_index[i].key; if (s.wasReadError()) { - printf("error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); v.clear(); return s; } @@ -643,7 +643,7 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIMap & v) } s >> v.pim_content; if (s.wasReadError()) { - printf("error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); + fprintf(stderr, "error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); v.clear(); return s; } diff --git a/main.cpp b/main.cpp index de5a652e..39c694b5 100644 --- a/main.cpp +++ b/main.cpp @@ -66,10 +66,10 @@ protected: int counter = 0; }; - +#include int main(int argc, char * argv[]) { PILog log; - log.setApplicationName("test"); + log.setLogName("test"); log.setDir("logs"); // log.setTimestampFormat("hh-mm-ss"); // log.setLineFormat("[c] m (t)"); @@ -78,12 +78,15 @@ int main(int argc, char * argv[]) { // log.enqueue("debug msg"); // log.enqueue("warn msg with ${c}", PILog::Category::Warning); // log.enqueue("ERROR${m}${t}", PILog::Category::Error); + log.setLevel(PILog::Level::Info); - log.debug(&log) << "some msg"; + log.debug() << "some msg"; + piMSleep(50); + log.info() << "information"; piMSleep(50); log.warning() << "another!"; piMSleep(50); - log.error() << "blahblahblag"; + log.error(&log) << "critical!"; // log.stop(); return 0; diff --git a/utils/system_daemon/main.cpp b/utils/system_daemon/main.cpp index 899f8379..f58c556f 100755 --- a/utils/system_daemon/main.cpp +++ b/utils/system_daemon/main.cpp @@ -420,7 +420,7 @@ int main(int argc, char * argv[]) { MainMenu * menu = new MainMenu(*daemon); if (sapp) CONNECTU(sapp, messageReceived, menu, messageFromApp); if (cli.hasArgument("silent")) { - PICout::setOutputDevices(PICout::StdOut); + PICout::setOutputDevices(PICout::Console); PIKbdListener ls; ls.enableExitCapture(PIKbdListener::F10); ls.start(); From 224412e20ad578801a210d845ae873b4d1865743 Mon Sep 17 00:00:00 2001 From: peri4 Date: Mon, 16 Sep 2024 16:24:11 +0300 Subject: [PATCH 02/40] PIDir::temporary fix --- libs/main/io_devices/pidir.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/main/io_devices/pidir.cpp b/libs/main/io_devices/pidir.cpp index 9eda8608..611a3ce2 100644 --- a/libs/main/io_devices/pidir.cpp +++ b/libs/main/io_devices/pidir.cpp @@ -507,7 +507,8 @@ PIDir PIDir::temporary() { s.prepend(separator); return PIDir(s); #else - rc = mkdtemp("/tmp/pidir_tmp_XXXXXX"); + char template_rc[] = "/tmp/pidir_tmp_XXXXXX"; + rc = mkdtemp(template_rc); if (!rc) return PIDir(); PIString s(rc); return PIDir(s.left(s.findLast(PIDir::separator))); From 97aad47a2162cf09bfbad98eacd5eb5f47de1d45 Mon Sep 17 00:00:00 2001 From: peri4 Date: Mon, 16 Sep 2024 23:32:01 +0300 Subject: [PATCH 03/40] some fixes --- CMakeLists.txt | 8 ++++++-- libs/client_server/piclientserver_client_base.cpp | 3 ++- libs/io_utils/pistreampacker.cpp | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 079ae9e4..de871059 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,11 +75,15 @@ option(INTROSPECTION "Build with introspection" OFF) option(TESTS "Build tests and perform their before install step" ${PIP_BUILD_DEBUG}) option(COVERAGE "Build project with coverage info" OFF) set(PIP_UTILS 1) -set(BUILDING_pip 1 PARENT_SCOPE) -set(pip_ROOT_SRC "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_STANDARD 11) +shstk_is_parent_exists(_pe) +if (_pe) + set(BUILDING_pip 1 PARENT_SCOPE) + set(pip_ROOT_SRC "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE) +endif() + # Basic set(PIP_MODULES) diff --git a/libs/client_server/piclientserver_client_base.cpp b/libs/client_server/piclientserver_client_base.cpp index faa99b97..ceb09a16 100644 --- a/libs/client_server/piclientserver_client_base.cpp +++ b/libs/client_server/piclientserver_client_base.cpp @@ -80,8 +80,9 @@ void PIClientServer::ClientBase::init() { void PIClientServer::ClientBase::destroy() { - can_write = false; write_mutex.lock(); + close(); piDeleteSafety(tcp); + write_mutex.unlock(); // piCout << "Destroyed"; } diff --git a/libs/io_utils/pistreampacker.cpp b/libs/io_utils/pistreampacker.cpp index 3df49296..5ab536a0 100644 --- a/libs/io_utils/pistreampacker.cpp +++ b/libs/io_utils/pistreampacker.cpp @@ -120,6 +120,7 @@ void PIStreamPacker::send(const PIByteArray & data) { void PIStreamPacker::received(const uchar * readed, ssize_t size) { + if (size <= 0) return; received(PIByteArray(readed, size)); } From 97f1c25ff88dd0dfd787736f5630a049ea709c64 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 17 Sep 2024 11:15:50 +0300 Subject: [PATCH 04/40] close --- tests/client_server/client_server_test.cpp | 226 +++++++++++---------- 1 file changed, 116 insertions(+), 110 deletions(-) diff --git a/tests/client_server/client_server_test.cpp b/tests/client_server/client_server_test.cpp index 59839512..b701900e 100644 --- a/tests/client_server/client_server_test.cpp +++ b/tests/client_server/client_server_test.cpp @@ -52,10 +52,13 @@ TEST(ClientServer, OneClient) { class ClientSendThread { - using ClientType = TestClient; + using ClientType = TestClient; public: - ClientSendThread() { client = createAndConnectClient(); } + ClientSendThread() { + client = createAndConnectClient(); + sendThread.setName("clSend"); + } ~ClientSendThread() { sendThread.stopAndWait(); @@ -63,11 +66,10 @@ public: } void startSend() { - auto c = client; - sendThread.start([c] { c->ping(); }, 100._Hz); + sendThread.start([this] { client->ping(); }, 100._Hz); } - - void sendOnce() {client->ping();} + + void sendOnce() { client->ping(); } ClientType * client = nullptr; PIThread sendThread; @@ -75,28 +77,24 @@ public: int getServerPongs(PIClientServer::Server * s) { - int pongs = 0; - s->forEachClient([&pongs](PIClientServer::ServerClient * sc){ - const auto c = static_cast *>(sc); - pongs += c->pongCnt(); - }); - return pongs; + int pongs = 0; + s->forEachClient([&pongs](PIClientServer::ServerClient * sc) { + const auto c = static_cast *>(sc); + pongs += c->pongCnt(); + }); + return pongs; } int getClientsPongs(const PIVector & clients) { - int pongs = 0; - clients.forEach([&pongs](ClientSendThread * c){ - pongs += c->client->pongCnt(); - }); - return pongs; + int pongs = 0; + clients.forEach([&pongs](ClientSendThread * c) { pongs += c->client->pongCnt(); }); + return pongs; } int getClientsPings(const PIVector & clients) { - int pings = 0; - clients.forEach([&pings](ClientSendThread * c){ - pings += c->client->pingCnt(); - }); - return pings; + int pings = 0; + clients.forEach([&pings](ClientSendThread * c) { pings += c->client->pingCnt(); }); + return pings; } @@ -113,98 +111,106 @@ TEST(ClientServer, ManyClients) { waitLoop([s]() { return s->clientsCount() == clients_count; }, loop_timeout); EXPECT_EQ(clients_count, s->clientsCount()); - - EXPECT_EQ(0, getServerPongs(s)); - - EXPECT_EQ(getClientsPings(clients), 0); - - for (const auto c : clients) { - c->sendOnce(); - } - - EXPECT_EQ(getClientsPings(clients), clients_count); - EXPECT_TRUE(clients.every([](ClientSendThread * c){return c->client->pingCnt() == 1;})); - EXPECT_TRUE(clients.every([](ClientSendThread * c){return c->client->pongCnt() == 0;})); - waitLoop([s]() { return getServerPongs(s) >= clients_count; }, loop_timeout); - EXPECT_EQ(clients_count, getServerPongs(s)); - - s->forEachClient([](PIClientServer::ServerClient * sc) { static_cast *>(sc)->ping(); }); - const auto clientCheckPong = [&clients](){return clients.every([](ClientSendThread * c){return c->client->pongCnt() == 1;});}; - waitLoop([&clientCheckPong]() { return clientCheckPong(); }, loop_timeout); - EXPECT_TRUE(clientCheckPong()); - - for (const auto c : clients) { - c->startSend(); - } - (100_ms).sleep(); - EXPECT_TRUE(getClientsPings(clients) > clients_count*2); - EXPECT_TRUE(getServerPongs(s) > clients_count*2); - piDeleteAllAndClear(clients); - waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); - EXPECT_EQ(0, s->clientsCount()); - delete s; + + EXPECT_EQ(0, getServerPongs(s)); + + EXPECT_EQ(getClientsPings(clients), 0); + + for (const auto c: clients) { + c->sendOnce(); + } + + EXPECT_EQ(getClientsPings(clients), clients_count); + EXPECT_TRUE(clients.every([](ClientSendThread * c) { return c->client->pingCnt() == 1; })); + EXPECT_TRUE(clients.every([](ClientSendThread * c) { return c->client->pongCnt() == 0; })); + waitLoop([s]() { return getServerPongs(s) >= clients_count; }, loop_timeout); + EXPECT_EQ(clients_count, getServerPongs(s)); + + s->forEachClient([](PIClientServer::ServerClient * sc) { static_cast *>(sc)->ping(); }); + const auto clientCheckPong = [&clients]() { return clients.every([](ClientSendThread * c) { return c->client->pongCnt() == 1; }); }; + waitLoop([&clientCheckPong]() { return clientCheckPong(); }, loop_timeout); + EXPECT_TRUE(clientCheckPong()); + + for (const auto c: clients) { + c->startSend(); + } + (100_ms).sleep(); + EXPECT_TRUE(getClientsPings(clients) > clients_count * 2); + EXPECT_TRUE(getServerPongs(s) > clients_count * 2); + piDeleteAllAndClear(clients); + waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); + EXPECT_EQ(0, s->clientsCount()); + delete s; } TEST(ClientServer, DynamicClients) { - auto const loop_timeout = 1000_ms; + auto const loop_timeout = 1000_ms; constexpr int clients_count = 20; - PIVector clients; - PIMutex clients_mutex; - auto s = createServer(); - - const auto spawnClient = [&clients, &clients_mutex]() { - auto c = new ClientSendThread(); - c->startSend(); - clients_mutex.lock(); - clients << c; - clients_mutex.unlock(); - piCout << "new client" << clients.size(); - }; - - piForTimes(clients_count) { + PIVector clients; + PIMutex clients_mutex; + auto s = createServer(); + + const auto spawnClient = [&clients, &clients_mutex]() { + auto c = new ClientSendThread(); + c->startSend(); + clients_mutex.lock(); + clients << c; + clients_mutex.unlock(); + piCout << "new client" << clients.size(); + }; + + piForTimes(clients_count) { spawnClient(); } - - PIThread spawnThread; - PIThread deleteThread; - - spawnThread.start([&spawnClient](){ - const int new_cnt = randomi() % 10; - piForTimes(new_cnt) { - spawnClient(); - } - }, 12_Hz); - - deleteThread.start([&clients, &clients_mutex](){ - const int rm_cnt = randomi() % 10; - piForTimes(rm_cnt) { - ClientSendThread * c = nullptr; - clients_mutex.lock(); - if (clients.size() > 10) { - c = clients.take_back(); - } - clients_mutex.unlock(); - if (c) { - delete c; - piCout << "remove client" << clients.size(); - } - } - }, 13_Hz); - - (1_s).sleep(); - - EXPECT_GE(s->clientsCount(), 10); - - piCout << "now clients" << clients.size(); - - //delete s; - - deleteThread.stopAndWait(); - spawnThread.stopAndWait(); - - piCout << "total clients" << clients.size(); - - piDeleteAllAndClear(clients); - waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); - EXPECT_EQ(0, s->clientsCount()); + + PIThread spawnThread; + PIThread deleteThread; + spawnThread.setName("spawn"); + deleteThread.setName("delete"); + + spawnThread.start( + [&spawnClient]() { + const int new_cnt = randomi() % 10; + piForTimes(new_cnt) { + spawnClient(); + } + }, + 12_Hz); + + deleteThread.start( + [&clients, &clients_mutex]() { + const int rm_cnt = randomi() % 10; + piForTimes(rm_cnt) { + ClientSendThread * c = nullptr; + clients_mutex.lock(); + if (clients.size() > 10) { + c = clients.take_front(); + } + clients_mutex.unlock(); + if (c) { + delete c; + piCout << "remove client" << clients.size(); + } + } + }, + 13_Hz); + + (10_s).sleep(); + + EXPECT_GE(s->clientsCount(), 10); + + piCout << "now clients" << clients.size(); + + + deleteThread.stopAndWait(); + spawnThread.stopAndWait(); + + + piCout << "total clients" << clients.size(); + + piDeleteAllAndClear(clients); + waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); + EXPECT_EQ(0, s->clientsCount()); + + delete s; } From eb97de14133f2fdc26a9e60ab6e26d90c114cb74 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 17 Sep 2024 11:21:23 +0300 Subject: [PATCH 05/40] close --- tests/client_server/test_client_helper.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/client_server/test_client_helper.h b/tests/client_server/test_client_helper.h index 2c261738..02ab8814 100644 --- a/tests/client_server/test_client_helper.h +++ b/tests/client_server/test_client_helper.h @@ -16,7 +16,7 @@ public: int pingCnt() const { return ping_cnt; } void ping() { - const auto data = PIByteArray(WriteSize); + const auto data = PIByteArray(WriteSize); writeInternal(data); ping_cnt++; } @@ -43,6 +43,9 @@ class TestServerClient , public TestClientBase { using Base = TestClientBase; +public: + ~TestServerClient() { close(); } + private: void readed(PIByteArray data) override { Base::readInternal(data); } @@ -62,6 +65,9 @@ class TestClient , public TestClientBase { using Base = TestClientBase; +public: + ~TestClient() { close(); } + private: void readed(PIByteArray data) override { Base::readInternal(data); } From bc6b5844808ca43cbc560badc1c2224604a24721 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Tue, 17 Sep 2024 11:28:29 +0300 Subject: [PATCH 06/40] fix waitLoop --- tests/client_server/client_server_test.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/client_server/client_server_test.cpp b/tests/client_server/client_server_test.cpp index b701900e..f6906684 100644 --- a/tests/client_server/client_server_test.cpp +++ b/tests/client_server/client_server_test.cpp @@ -13,10 +13,13 @@ PIClientServer::Server * createServer() { bool waitLoop(std::function exit_loop, const PISystemTime & timeout) { PITimeMeasurer tm; - while (!exit_loop() && (tm.elapsed() < timeout)) { + while (tm.elapsed() < timeout) { + if (exit_loop()) { + return true; + } piMinSleep(); } - return tm.elapsed() < timeout; + return false; } template From b99c51181d26e1be911c77487574a9f4f2878393 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Tue, 17 Sep 2024 12:31:03 +0300 Subject: [PATCH 07/40] fix test --- tests/client_server/client_server_test.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/client_server/client_server_test.cpp b/tests/client_server/client_server_test.cpp index f6906684..0535fe47 100644 --- a/tests/client_server/client_server_test.cpp +++ b/tests/client_server/client_server_test.cpp @@ -154,6 +154,7 @@ TEST(ClientServer, DynamicClients) { auto s = createServer(); const auto spawnClient = [&clients, &clients_mutex]() { + if (clients.size() > 100) return; auto c = new ClientSendThread(); c->startSend(); clients_mutex.lock(); @@ -173,16 +174,17 @@ TEST(ClientServer, DynamicClients) { spawnThread.start( [&spawnClient]() { - const int new_cnt = randomi() % 10; + const int new_cnt = 7; piForTimes(new_cnt) { spawnClient(); } + piCout << "+++++++"; }, - 12_Hz); + 52_Hz); deleteThread.start( [&clients, &clients_mutex]() { - const int rm_cnt = randomi() % 10; + const int rm_cnt = 8; piForTimes(rm_cnt) { ClientSendThread * c = nullptr; clients_mutex.lock(); @@ -195,8 +197,9 @@ TEST(ClientServer, DynamicClients) { piCout << "remove client" << clients.size(); } } + piCout << "----------"; }, - 13_Hz); + 53_Hz); (10_s).sleep(); From f105f616f6909e86d2b54cc21500e559f7289d66 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 17 Sep 2024 13:22:20 +0300 Subject: [PATCH 08/40] PIThread more accurate end, PIEthernet tcpserver client no reinit --- libs/main/io_devices/piethernet.cpp | 5 +++-- libs/main/io_devices/piethernet.h | 1 + libs/main/thread/pithread.cpp | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libs/main/io_devices/piethernet.cpp b/libs/main/io_devices/piethernet.cpp index d4d7d04e..d91fe792 100644 --- a/libs/main/io_devices/piethernet.cpp +++ b/libs/main/io_devices/piethernet.cpp @@ -139,6 +139,7 @@ PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) { addr_s.set(ip_port); sock = sock_; opened_ = connected_ = true; + is_server_client = true; init(); setParameters(PIEthernet::ReuseAddress | PIEthernet::MulticastLoop); setType(TCP_Client, false); @@ -168,7 +169,7 @@ void PIEthernet::construct() { setTTL(64); setMulticastTTL(1); server_thread_.setData(this); - server_thread_.setName("__S__server_thread"_a); + server_thread_.setName("_S.tcpserver"_a); #ifdef MICRO_PIP setThreadedReadBufferSize(512); #else @@ -179,7 +180,7 @@ void PIEthernet::construct() { bool PIEthernet::init() { - if (isOpened()) return true; + if (isOpened() || is_server_client) return true; if (sock != -1) return true; // piCout << "init " << type(); PRIVATE->event.destroy(); diff --git a/libs/main/io_devices/piethernet.h b/libs/main/io_devices/piethernet.h index e2a2e5e9..3015fb3d 100644 --- a/libs/main/io_devices/piethernet.h +++ b/libs/main/io_devices/piethernet.h @@ -483,6 +483,7 @@ protected: PRIVATE_DECLARATION(PIP_EXPORT) int sock, sock_s; std::atomic_bool connected_, connecting_, listen_threaded, server_bounded; + bool is_server_client = false; mutable PINetworkAddress addr_r, addr_s, addr_lr; Type eth_type; PIThread server_thread_; diff --git a/libs/main/thread/pithread.cpp b/libs/main/thread/pithread.cpp index 5dd1f287..c1810e68 100644 --- a/libs/main/thread/pithread.cpp +++ b/libs/main/thread/pithread.cpp @@ -919,6 +919,10 @@ void PIThread::_runThread() { void PIThread::_endThread() { + PIScopeExitCall ec([this] { + terminating = running_ = false; + tid_ = -1; + }); // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop" << "..."; stopped(); // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop" << "ok"; @@ -927,8 +931,6 @@ void PIThread::_endThread() { end(); // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop" << "ok"; if (lockRun) thread_mutex.unlock(); - terminating = running_ = false; - tid_ = -1; // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "exit"; // cout << "thread " << t << " exiting ... " << endl; // PICout(PICoutManipulators::DefaultControls) << "pthread_exit" << (__privateinitializer__.p)->thread; @@ -943,7 +945,7 @@ void PIThread::_endThread() { #elif defined(FREERTOS) PRIVATE->thread = 0; #else - pthread_detach(PRIVATE->thread); + // pthread_detach(PRIVATE->thread); PRIVATE->thread = 0; pthread_exit(0); #endif From e186e0adffec1dfed5ed017fa7f745f644c2d272 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 17 Sep 2024 15:58:06 +0300 Subject: [PATCH 09/40] shorter thread names --- libs/main/core/piobject.cpp | 2 +- libs/main/io_devices/pibinarylog.cpp | 2 +- libs/main/io_devices/piiodevice.cpp | 7 ++++--- libs/main/io_devices/pipeer.cpp | 18 +++++++++--------- libs/main/io_utils/piconnection.cpp | 4 ++-- libs/main/thread/pitimer.cpp | 2 +- utils/system_daemon/daemon.cpp | 2 +- 7 files changed, 19 insertions(+), 18 deletions(-) diff --git a/libs/main/core/piobject.cpp b/libs/main/core/piobject.cpp index bc03c919..df348d99 100644 --- a/libs/main/core/piobject.cpp +++ b/libs/main/core/piobject.cpp @@ -763,7 +763,7 @@ void dumpApplication(bool with_objects) { bool dumpApplicationToFile(const PIString & path, bool with_objects) { PIFile f(path + "_tmp"); - f.setName("__S__DumpFile"); + f.setName("_S.DumpFile"); f.clear(); if (!f.open(PIIODevice::WriteOnly)) return false; auto out_devs = PICout::currentOutputDevices(); diff --git a/libs/main/io_devices/pibinarylog.cpp b/libs/main/io_devices/pibinarylog.cpp index 1cb53796..55c2433f 100644 --- a/libs/main/io_devices/pibinarylog.cpp +++ b/libs/main/io_devices/pibinarylog.cpp @@ -79,7 +79,7 @@ PIBinaryLog::PIBinaryLog() { setLogDir(PIString()); setFilePrefix(PIString()); setRapidStart(false); - file.setName("__S__PIBinaryLog::file"); + file.setName("_S.PIBinLog.file"); } diff --git a/libs/main/io_devices/piiodevice.cpp b/libs/main/io_devices/piiodevice.cpp index 654b1a10..83007023 100644 --- a/libs/main/io_devices/piiodevice.cpp +++ b/libs/main/io_devices/piiodevice.cpp @@ -337,8 +337,8 @@ void PIIODevice::_init() { #else threaded_read_buffer_size = 4_KiB; #endif - read_thread.setName("__S__.PIIODevice.read_thread"); - write_thread.setName("__S__.PIIODevice.write_thread"); + read_thread.setName("_S.PIIODev.read"); + write_thread.setName("_S.PIIODev.write"); CONNECT(void, &write_thread, started, this, write_func); CONNECTL(&read_thread, started, [this]() { if (!isOpened()) open(); @@ -384,9 +384,10 @@ void PIIODevice::read_func() { ok = open(); } } - if (!ok) return; + if (!ok || read_thread.isStopping()) return; } ssize_t readed_ = read(buffer_tr.data(), buffer_tr.size_s()); + if (read_thread.isStopping()) return; if (readed_ <= 0) { piMSleep(10); // cout << readed_ << ", " << errno << ", " << errorString() << endl; diff --git a/libs/main/io_devices/pipeer.cpp b/libs/main/io_devices/pipeer.cpp index bbbce3fe..00af59bd 100644 --- a/libs/main/io_devices/pipeer.cpp +++ b/libs/main/io_devices/pipeer.cpp @@ -175,7 +175,7 @@ PIPeer::PIPeer(const PIString & n) , eth_tcp_cli(PIEthernet::TCP_Client) , diag_s(false) , diag_d(false) { - sync_timer.setName("__S__.PIPeer.sync_timer"); + sync_timer.setName("_S.PIPeer.sync"); // piCout << " PIPeer" << uint(this); destroyed = false; setDebug(false); @@ -229,7 +229,7 @@ void PIPeer::initEths(PIStringList al) { piForeachC(PIString & a, al) { ce = new PIEthernet(); ce->setDebug(false); - ce->setName("__S__PIPeer_traffic_eth_rec_" + a); + ce->setName("_S.PIPeer.traf_rec_" + a); ce->setParameters(0); bool ok = false; for (int p = _PIPEER_TRAFFIC_PORT_S; p < _PIPEER_TRAFFIC_PORT_E; ++p) { @@ -249,7 +249,7 @@ void PIPeer::initEths(PIStringList al) { if (!ok) delete ce; } eth_send.setDebug(false); - eth_send.setName("__S__PIPeer_traffic_eth_send"); + eth_send.setName("_S.PIPeer.traf_send"); eth_send.setParameters(0); // piCoutObj << "initEths ok"; } @@ -265,7 +265,7 @@ void PIPeer::initMBcasts(PIStringList al) { // piCout << "mcast try" << a; ce = new PIEthernet(); ce->setDebug(false); - ce->setName("__S__PIPeer_mcast_eth_" + a); + ce->setName("_S.PIPeer.mcast_" + a); ce->setParameters(0); ce->setSendAddress(_PIPEER_MULTICAST_IP, _PIPEER_MULTICAST_PORT); ce->setReadAddress(a, _PIPEER_MULTICAST_PORT); @@ -285,7 +285,7 @@ void PIPeer::initMBcasts(PIStringList al) { piForeachC(PIString & a, al) { ce = new PIEthernet(); ce->setDebug(false); - ce->setName("__S__PIPeer_bcast_eth_" + a); + ce->setName("_S.PIPeer.bcast_" + a); ce->setParameters(PIEthernet::Broadcast); cint = prev_ifaces.getByAddress(a); nm = (cint == 0) ? "255.255.255.0" : cint->netmask; @@ -302,7 +302,7 @@ void PIPeer::initMBcasts(PIStringList al) { // piCoutObj << "invalid address for bcast" << a; } } - eth_lo.setName("__S__PIPeer_eth_loopback"); + eth_lo.setName("_S.PIPeer.lo"); eth_lo.setParameters(PIEthernet::SeparateSockets); eth_lo.init(); cint = prev_ifaces.getByAddress("127.0.0.1"); @@ -317,13 +317,13 @@ void PIPeer::initMBcasts(PIStringList al) { break; } } - eth_tcp_srv.setName("__S__PIPeer_eth_TCP_Server"); + eth_tcp_srv.setName("_S.PIPeer.TCP_Server"); eth_tcp_srv.init(); eth_tcp_srv.listen("0.0.0.0", _PIPEER_TCP_PORT, true); eth_tcp_srv.setDebug(false); CONNECT1(void, PIEthernet *, ð_tcp_srv, newConnection, this, newTcpClient); eth_tcp_srv.startThreadedRead(); - eth_tcp_cli.setName("__S__PIPeer_eth_TCP_Client"); + eth_tcp_cli.setName("_S.PIPeer.TCP_Client"); eth_tcp_cli.init(); eth_tcp_cli.setDebug(false); tcpClientReconnect(); @@ -987,7 +987,7 @@ void PIPeer::interrupt() { void PIPeer::newTcpClient(PIEthernet * client) { - client->setName("__S__PIPeer_eth_TCP_ServerClient" + client->path()); + client->setName("_S.PIPeer.TCP_ServerClient" + client->path()); piCoutObj << "client" << client->path(); CONNECT2(void, const uchar *, ssize_t, client, threadedReadEvent, this, mbcastRead); client->startThreadedRead(); diff --git a/libs/main/io_utils/piconnection.cpp b/libs/main/io_utils/piconnection.cpp index 79758b92..5a4b7763 100644 --- a/libs/main/io_utils/piconnection.cpp +++ b/libs/main/io_utils/piconnection.cpp @@ -998,7 +998,7 @@ PIIODevice * PIConnection::DevicePool::addDevice(PIConnection * parent, const PI dd->started = false; } dd->rthread = new PIThread(dd, __DevicePool_threadReadDP); - dd->rthread->setName("__S__connection_" + fp + "_read_thread"); + dd->rthread->setName("_S.Conn." + fp + ".read"); need_start = true; pmode |= PIIODevice::ReadOnly; } @@ -1211,7 +1211,7 @@ PIConnection::Extractor::~Extractor() { PIConnection::Sender::Sender(PIConnection * parent_): parent(parent_) { - setName("__S__.PIConnection.Sender"); + setName("_S.PIConn.Sender"); needLockRun(true); } diff --git a/libs/main/thread/pitimer.cpp b/libs/main/thread/pitimer.cpp index f22aa04b..84a72e6f 100644 --- a/libs/main/thread/pitimer.cpp +++ b/libs/main/thread/pitimer.cpp @@ -171,7 +171,7 @@ bool PITimer::waitForFinish(PISystemTime timeout) { void PITimer::initFirst() { thread = new PIThread([this] { threadFunc(); }); - thread->setName("__S__.PITimer.thread"); + thread->setName("_S.PITimer.thread"); setProperty("interval", PISystemTime()); } diff --git a/utils/system_daemon/daemon.cpp b/utils/system_daemon/daemon.cpp index 4abe8e3b..0465e559 100644 --- a/utils/system_daemon/daemon.cpp +++ b/utils/system_daemon/daemon.cpp @@ -258,7 +258,7 @@ void Daemon::TileFileProgress::tileEvent(PIScreenTile * t, TileEvent e) { Daemon::Daemon(): PIPeer(pisd_prefix + PISystemInfo::instance()->hostname + "_" + PIString::fromNumber(randomi() % 100)) { // setName("Daemon"); - dtimer.setName("__S__Daemon_timer"); + dtimer.setName("_S.Daemon.timer"); mode = rmNone; offset = cur = height = 0; CONNECTU(screen, keyPressed, this, keyEvent) From bdd18b614f5a97144097c815ba5cfe2077bccbfa Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 17 Sep 2024 16:11:18 +0300 Subject: [PATCH 10/40] PIEthernet more accuracy construct PIThread windows fix --- libs/main/core/pibase.h | 19 ++++++++++++++++--- libs/main/io_devices/piethernet.cpp | 20 ++++++++++---------- libs/main/io_devices/piethernet.h | 6 +++--- libs/main/thread/pithread.cpp | 1 + 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/libs/main/core/pibase.h b/libs/main/core/pibase.h index 6ef208de..ec4491f8 100644 --- a/libs/main/core/pibase.h +++ b/libs/main/core/pibase.h @@ -677,15 +677,28 @@ public: //! \~\brief //! \~english Destructor that executes the function if it exists //! \~russian Деструктор, который выполняет функцию, если она существует - ~PIScopeExitCall() { - if (func) func(); - } + ~PIScopeExitCall() { call(); } //! \~\brief //! \~english Method for canceling the function //! \~russian Метод для отмены функции void cancel() { func = nullptr; } + //! \~\brief + //! \~english Method for call the function + //! \~russian Метод для вызова функции + void call() { + if (func) func(); + } + + //! \~\brief + //! \~english Method for call and canceling the function + //! \~russian Метод для вызова и отмены функции + void callAndCancel() { + call(); + cancel(); + } + private: NO_COPY_CLASS(PIScopeExitCall) diff --git a/libs/main/io_devices/piethernet.cpp b/libs/main/io_devices/piethernet.cpp index d91fe792..78757c26 100644 --- a/libs/main/io_devices/piethernet.cpp +++ b/libs/main/io_devices/piethernet.cpp @@ -119,7 +119,8 @@ PRIVATE_DEFINITION_END(PIEthernet) PIEthernet::PIEthernet(): PIIODevice("", ReadWrite) { construct(); - setType(UDP); + eth_type = UDP; + setProperty("type", (int)UDP); setParameters(PIEthernet::ReuseAddress | PIEthernet::MulticastLoop | PIEthernet::KeepConnection); } @@ -128,7 +129,8 @@ PIEthernet::PIEthernet(PIEthernet::Type type_, const PIString & ip_port, const P : PIIODevice(ip_port, ReadWrite) { construct(); addr_r.set(ip_port); - setType(type_); + eth_type = type_; + setProperty("type", (int)type_); setParameters(params_); if (type_ != UDP) init(); } @@ -140,9 +142,10 @@ PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) { sock = sock_; opened_ = connected_ = true; is_server_client = true; + eth_type = TCP_Client; + setProperty("type", (int)TCP_Client); init(); setParameters(PIEthernet::ReuseAddress | PIEthernet::MulticastLoop); - setType(TCP_Client, false); setPath(ip_port); ethNonblocking(sock); PRIVATE->event.create(); @@ -162,8 +165,6 @@ PIEthernet::~PIEthernet() { void PIEthernet::construct() { // piCout << " PIEthernet" << uint(this); setOption(BlockingWrite); - connected_ = connecting_ = listen_threaded = server_bounded = false; - sock = sock_s = -1; setReadTimeout(10_s); setWriteTimeout(10_s); setTTL(64); @@ -179,9 +180,9 @@ void PIEthernet::construct() { } -bool PIEthernet::init() { - if (isOpened() || is_server_client) return true; - if (sock != -1) return true; +void PIEthernet::init() { + if (isOpened() || is_server_client) return; + if (sock != -1) return; // piCout << "init " << type(); PRIVATE->event.destroy(); if (sock_s == sock) sock_s = -1; @@ -204,13 +205,12 @@ bool PIEthernet::init() { sock_s = sock; if (sock == -1 || sock_s == -1) { piCoutObj << "Can`t create socket," << ethErrorString(); - return false; + return; } applyParameters(); applyTimeouts(); applyOptInt(IPPROTO_IP, IP_TTL, TTL()); // piCoutObj << "inited" << path(); - return true; } diff --git a/libs/main/io_devices/piethernet.h b/libs/main/io_devices/piethernet.h index 3015fb3d..7b16d0b1 100644 --- a/libs/main/io_devices/piethernet.h +++ b/libs/main/io_devices/piethernet.h @@ -472,7 +472,7 @@ protected: virtual void received(const void * data, int size) { ; } void construct(); - bool init(); + void init(); bool openDevice() override; bool closeDevice() override; void closeSocket(int & sd); @@ -481,8 +481,8 @@ protected: void applyOptInt(int level, int opt, int val); PRIVATE_DECLARATION(PIP_EXPORT) - int sock, sock_s; - std::atomic_bool connected_, connecting_, listen_threaded, server_bounded; + int sock = -1, sock_s = -1; + std::atomic_bool connected_ = {false}, connecting_ = {false}, listen_threaded = {false}, server_bounded = {false}; bool is_server_client = false; mutable PINetworkAddress addr_r, addr_s, addr_lr; Type eth_type; diff --git a/libs/main/thread/pithread.cpp b/libs/main/thread/pithread.cpp index c1810e68..afdef1f3 100644 --- a/libs/main/thread/pithread.cpp +++ b/libs/main/thread/pithread.cpp @@ -937,6 +937,7 @@ void PIThread::_endThread() { UNREGISTER_THREAD(this); PIINTROSPECTION_THREAD_STOP(this); #if defined(WINDOWS) + ec.callAndCancel(); # ifdef CC_GCC _endthreadex(0); # else From aa963a4bda8d35da07bcf25afe69ffdeedb35cc0 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 17 Sep 2024 16:50:22 +0300 Subject: [PATCH 11/40] PIEthernet on error close disconnect --- libs/main/io_devices/piethernet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/main/io_devices/piethernet.cpp b/libs/main/io_devices/piethernet.cpp index 78757c26..9f9ac03e 100644 --- a/libs/main/io_devices/piethernet.cpp +++ b/libs/main/io_devices/piethernet.cpp @@ -205,6 +205,7 @@ void PIEthernet::init() { sock_s = sock; if (sock == -1 || sock_s == -1) { piCoutObj << "Can`t create socket," << ethErrorString(); + connected_ = connecting_ = opened_ = false; return; } applyParameters(); @@ -897,7 +898,7 @@ void PIEthernet::server_func(void * eth) { return; } if (ce->debug()) piCout << "[PIEthernet] Can`t accept new connection," << ethErrorString(); - piMSleep(10); + piMSleep(50); return; } PIString ip = PIStringAscii(inet_ntoa(client_addr.sin_addr)); From 4acab04895bf6de37bd2e9037f4aaef3494c5906 Mon Sep 17 00:00:00 2001 From: peri4 Date: Thu, 19 Sep 2024 17:26:58 +0300 Subject: [PATCH 12/40] PILog ready to use --- libs/main/application/pilog.cpp | 32 +++++++++++++++++++---------- libs/main/application/pilog.h | 27 ++++++++++++++++++++++-- libs/main/console/pikbdlistener.cpp | 6 +++++- libs/main/console/pikbdlistener.h | 2 ++ 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/libs/main/application/pilog.cpp b/libs/main/application/pilog.cpp index f948d78d..27fece16 100644 --- a/libs/main/application/pilog.cpp +++ b/libs/main/application/pilog.cpp @@ -92,8 +92,10 @@ PILog::~PILog() { void PILog::setDir(const PIString & d) { stopAndWait(); log_dir = d; - PIDir::make(log_dir); - newFile(); + if (output[File]) { + PIDir::make(log_dir); + newFile(); + } start(); } @@ -164,7 +166,6 @@ PICout PILog::makePICout(PIObject * context, Level cat) { void PILog::enqueue(const PIString & msg, Level cat) { - if (log_file.isClosed()) return; auto t = PIDateTime::fromSystemTime(PISystemTime::current()); PIMutexLocker ml(log_mutex); queue.enqueue({cat, t, msg}); @@ -190,9 +191,11 @@ void PILog::newFile() { void PILog::run() { - if (split_tm.elapsed() >= split_time) { - split_tm.reset(); - newFile(); + if (output[File]) { + if (split_tm.elapsed() >= split_time) { + split_tm.reset(); + newFile(); + } } log_mutex.lock(); if (queue.isEmpty()) { @@ -210,10 +213,17 @@ void PILog::run() { auto qi = queue.dequeue(); log_mutex.unlock(); auto str = entryToString(qi); - log_ts << str << "\n"; - if (qi.cat == Level::Error) - piCerr << str; - else - piCout << str; + if (log_file.isOpened()) log_ts << str << "\n"; + if (output[Console]) { + PICout out(qi.cat == Level::Error ? piCerr : piCout); + if (color_console) { + switch (qi.cat) { + case Level::Error: out << PICoutManipulators::Red; break; + case Level::Warning: out << PICoutManipulators::Yellow; break; + default: break; + } + } + out << str; + } } } diff --git a/libs/main/application/pilog.h b/libs/main/application/pilog.h index 297fa37d..c13f260f 100644 --- a/libs/main/application/pilog.h +++ b/libs/main/application/pilog.h @@ -48,12 +48,27 @@ public: Debug, }; + enum Output { + File = 0x1, + Console = 0x2, + All = 0xFF, + }; + + //! \~english Set output target \"o\" to \"on\". + void setOutput(Output o, bool on = true) { output.setFlag(o, on); } + //! \~english Returns prefix for filename. PIString logName() const { return log_name; } //! \~english Set prefix for filename. Should be set \b before \a setDir()! void setLogName(const PIString & n) { log_name = n; } + //! \~english Returns if color for console output enabled. + bool colorConsole() const { return color_console; } + + //! \~english Set color for console output enabled. True by default. + void setColorConsole(bool yes) { color_console = yes; } + //! \~english Returns directory for log files. PIString dir() const { return log_dir; } @@ -89,10 +104,16 @@ public: //! \~english Set maximum level. All levels greater than \"l\" will be ignored. Default if \a Level::Debug. void setLevel(Level l); - + //! \~english Returns PICout for Level::Error level. PICout error(PIObject * context = nullptr); + + //! \~english Returns PICout for Level::Warning level. PICout warning(PIObject * context = nullptr); + + //! \~english Returns PICout for Level::Info level. PICout info(PIObject * context = nullptr); + + //! \~english Returns PICout for Level::Debug level. PICout debug(PIObject * context = nullptr); //! \~english Write all queued lines and stop. Also called in destructor. @@ -122,7 +143,9 @@ private: PIString log_dir, timestamp_format, line_format, line_format_p, log_name; PIQueue queue; PIMap id_by_cat; - Level max_level = Level::Debug; + Level max_level = Level::Debug; + PIFlags output = All; + bool color_console = true; int part_number = -1, cout_id = -1; }; diff --git a/libs/main/console/pikbdlistener.cpp b/libs/main/console/pikbdlistener.cpp index e959c52f..9e874a64 100644 --- a/libs/main/console/pikbdlistener.cpp +++ b/libs/main/console/pikbdlistener.cpp @@ -220,7 +220,6 @@ void PIKbdListener::readKeyboard() { #ifdef WINDOWS INPUT_RECORD ir; ReadConsoleInput(PRIVATE->hIn, &ir, 1, &(PRIVATE->ret)); - // piCout << ir.EventType; switch (ir.EventType) { case KEY_EVENT: { KEY_EVENT_RECORD ker = ir.Event.KeyEvent; @@ -542,6 +541,11 @@ void PIKbdListener::readKeyboard() { } +void PIKbdListener::stop() { + PIThread::stop(); +} + + void PIKbdListener::end() { // cout << "list end" << endl; #ifdef WINDOWS diff --git a/libs/main/console/pikbdlistener.h b/libs/main/console/pikbdlistener.h index 27080497..fb686304 100644 --- a/libs/main/console/pikbdlistener.h +++ b/libs/main/console/pikbdlistener.h @@ -180,6 +180,8 @@ public: void readKeyboard(); + void stop(); + //! Returns if keyboard listening is active (not running!) bool isActive() { return is_active; } From 3641e636d22ad197cc522d16d4e4d23e99b1682f Mon Sep 17 00:00:00 2001 From: peri4 Date: Sat, 21 Sep 2024 19:56:39 +0300 Subject: [PATCH 13/40] new PIClientServer::ClientBase::stopAndWait() method for blocking stop read. PIClientServer::ClientBase::close() now non-blocking --- libs/client_server/piclientserver_client.cpp | 1 + libs/client_server/piclientserver_client_base.cpp | 9 ++++++++- libs/main/client_server/piclientserver_client_base.h | 1 + main.cpp | 2 ++ tests/client_server/client_server_test.cpp | 8 ++++---- tests/client_server/test_client_helper.h | 10 ++++++++-- 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/libs/client_server/piclientserver_client.cpp b/libs/client_server/piclientserver_client.cpp index 2319584a..2699603e 100644 --- a/libs/client_server/piclientserver_client.cpp +++ b/libs/client_server/piclientserver_client.cpp @@ -41,6 +41,7 @@ PIClientServer::Client::Client() { PIClientServer::Client::~Client() { if (tcp) tcp->setDebug(false); close(); + stopAndWait(); } diff --git a/libs/client_server/piclientserver_client_base.cpp b/libs/client_server/piclientserver_client_base.cpp index ceb09a16..4ee87169 100644 --- a/libs/client_server/piclientserver_client_base.cpp +++ b/libs/client_server/piclientserver_client_base.cpp @@ -27,6 +27,7 @@ PIClientServer::ClientBase::ClientBase() {} PIClientServer::ClientBase::~ClientBase() { close(); + stopAndWait(); if (own_tcp) piDeleteSafety(tcp); } @@ -34,7 +35,13 @@ PIClientServer::ClientBase::~ClientBase() { void PIClientServer::ClientBase::close() { if (!tcp) return; can_write = false; - tcp->interrupt(); + tcp->stop(); + stream.clear(); +} + + +void PIClientServer::ClientBase::stopAndWait() { + if (!tcp) return; tcp->stopAndWait(10_s); if (tcp->isThreadedRead()) tcp->terminateThreadedRead(); tcp->close(); diff --git a/libs/main/client_server/piclientserver_client_base.h b/libs/main/client_server/piclientserver_client_base.h index 3c1105ed..625dad80 100644 --- a/libs/main/client_server/piclientserver_client_base.h +++ b/libs/main/client_server/piclientserver_client_base.h @@ -47,6 +47,7 @@ public: const PIEthernet * getTCP() const { return tcp; } void close(); + void stopAndWait(); int write(const void * d, const size_t s); int write(const PIByteArray & ba) { return write(ba.data(), ba.size()); } diff --git a/main.cpp b/main.cpp index 39c694b5..e58a2374 100644 --- a/main.cpp +++ b/main.cpp @@ -69,6 +69,8 @@ protected: #include int main(int argc, char * argv[]) { PILog log; + log.setColorConsole(false); + log.setOutput(PILog::File, false); log.setLogName("test"); log.setDir("logs"); // log.setTimestampFormat("hh-mm-ss"); diff --git a/tests/client_server/client_server_test.cpp b/tests/client_server/client_server_test.cpp index 0535fe47..e85df95c 100644 --- a/tests/client_server/client_server_test.cpp +++ b/tests/client_server/client_server_test.cpp @@ -154,7 +154,7 @@ TEST(ClientServer, DynamicClients) { auto s = createServer(); const auto spawnClient = [&clients, &clients_mutex]() { - if (clients.size() > 100) return; + // if (clients.size() > 100) return; auto c = new ClientSendThread(); c->startSend(); clients_mutex.lock(); @@ -180,7 +180,7 @@ TEST(ClientServer, DynamicClients) { } piCout << "+++++++"; }, - 52_Hz); + 12_Hz); deleteThread.start( [&clients, &clients_mutex]() { @@ -199,9 +199,9 @@ TEST(ClientServer, DynamicClients) { } piCout << "----------"; }, - 53_Hz); + 13_Hz); - (10_s).sleep(); + (2_s).sleep(); EXPECT_GE(s->clientsCount(), 10); diff --git a/tests/client_server/test_client_helper.h b/tests/client_server/test_client_helper.h index 02ab8814..4a23ad9f 100644 --- a/tests/client_server/test_client_helper.h +++ b/tests/client_server/test_client_helper.h @@ -44,7 +44,10 @@ class TestServerClient using Base = TestClientBase; public: - ~TestServerClient() { close(); } + ~TestServerClient() { + close(); + stopAndWait(); + } private: void readed(PIByteArray data) override { Base::readInternal(data); } @@ -66,7 +69,10 @@ class TestClient using Base = TestClientBase; public: - ~TestClient() { close(); } + ~TestClient() { + close(); + stopAndWait(); + } private: void readed(PIByteArray data) override { Base::readInternal(data); } From 9eecbbab6e63001e14c0c86942b2ef488e9ad85c Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 24 Sep 2024 18:57:50 +0300 Subject: [PATCH 14/40] new method PIClientServer::Server::closeAll() PISignals::releaseSignals() --- libs/client_server/piclientserver_server.cpp | 12 +++++++ .../client_server/piclientserver_server.h | 2 ++ libs/main/system/pisignals.cpp | 33 +++++++++++++++++-- libs/main/system/pisignals.h | 1 + 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/libs/client_server/piclientserver_server.cpp b/libs/client_server/piclientserver_server.cpp index 7984d36f..4a6ec693 100644 --- a/libs/client_server/piclientserver_server.cpp +++ b/libs/client_server/piclientserver_server.cpp @@ -90,6 +90,18 @@ void PIClientServer::Server::listen(PINetworkAddress addr) { } +void PIClientServer::Server::closeAll() { + clients_mutex.lock(); + for (auto c: clients) { + c->aboutDelete(); + c->destroy(); + delete c; + } + clients.clear(); + clients_mutex.unlock(); +} + + void PIClientServer::Server::setMaxClients(int new_max_clients) { max_clients = new_max_clients; } diff --git a/libs/main/client_server/piclientserver_server.h b/libs/main/client_server/piclientserver_server.h index 37a380d1..784fa125 100644 --- a/libs/main/client_server/piclientserver_server.h +++ b/libs/main/client_server/piclientserver_server.h @@ -49,6 +49,8 @@ public: void listen(PINetworkAddress addr); void listenAll(ushort port) { listen({0, port}); } + void closeAll(); + int getMaxClients() const { return max_clients; } void setMaxClients(int new_max_clients); int clientsCount() const; diff --git a/libs/main/system/pisignals.cpp b/libs/main/system/pisignals.cpp index c4518dd8..57fdc146 100644 --- a/libs/main/system/pisignals.cpp +++ b/libs/main/system/pisignals.cpp @@ -60,6 +60,33 @@ void PISignals::grabSignals(PIFlags signals_) { } +void PISignals::releaseSignals(PIFlags signals_) { + if (signals_[PISignals::Interrupt]) signal(signalCode(PISignals::Interrupt), SIG_DFL); + if (signals_[PISignals::Illegal]) signal(signalCode(PISignals::Illegal), SIG_DFL); + if (signals_[PISignals::Abort]) signal(signalCode(PISignals::Abort), SIG_DFL); + if (signals_[PISignals::FPE]) signal(signalCode(PISignals::FPE), SIG_DFL); + if (signals_[PISignals::SegFault]) signal(signalCode(PISignals::SegFault), SIG_DFL); + if (signals_[PISignals::Termination]) signal(signalCode(PISignals::Termination), SIG_DFL); +#ifndef CC_VC + if (signals_[PISignals::UserDefined1]) signal(signalCode(PISignals::UserDefined1), SIG_DFL); + if (signals_[PISignals::UserDefined2]) signal(signalCode(PISignals::UserDefined2), SIG_DFL); +#endif +#ifndef WINDOWS + if (signals_[PISignals::Hangup]) signal(signalCode(PISignals::Hangup), SIG_DFL); + if (signals_[PISignals::Quit]) signal(signalCode(PISignals::Quit), SIG_DFL); + if (signals_[PISignals::Kill]) signal(signalCode(PISignals::Kill), SIG_DFL); + if (signals_[PISignals::BrokenPipe]) signal(signalCode(PISignals::BrokenPipe), SIG_DFL); + if (signals_[PISignals::Timer]) signal(signalCode(PISignals::Timer), SIG_DFL); + if (signals_[PISignals::ChildStopped]) signal(signalCode(PISignals::ChildStopped), SIG_DFL); + if (signals_[PISignals::Continue]) signal(signalCode(PISignals::Continue), SIG_DFL); + if (signals_[PISignals::StopProcess]) signal(signalCode(PISignals::StopProcess), SIG_DFL); + if (signals_[PISignals::StopTTY]) signal(signalCode(PISignals::StopTTY), SIG_DFL); + if (signals_[PISignals::StopTTYInput]) signal(signalCode(PISignals::StopTTYInput), SIG_DFL); + if (signals_[PISignals::StopTTYOutput]) signal(signalCode(PISignals::StopTTYOutput), SIG_DFL); +#endif +} + + void PISignals::raiseSignal(PISignals::Signal signal) { raise(signalCode(signal)); } @@ -90,7 +117,7 @@ int PISignals::signalCode(PISignals::Signal signal) { case PISignals::StopTTYInput: return SIGTTIN; case PISignals::StopTTYOutput: return SIGTTOU; #endif - default:; + default: break; } return 0; } @@ -121,13 +148,13 @@ PISignals::Signal PISignals::signalFromCode(int signal) { case SIGTTIN: return PISignals::StopTTYInput; case SIGTTOU: return PISignals::StopTTYOutput; #endif - default:; + default: break; } return PISignals::Termination; } void PISignals::signal_event(int signal) { - if (PISignals::ret_func == 0) return; + if (!PISignals::ret_func) return; PISignals::ret_func(PISignals::signalFromCode(signal)); } diff --git a/libs/main/system/pisignals.h b/libs/main/system/pisignals.h index d6064587..b9aad0e6 100644 --- a/libs/main/system/pisignals.h +++ b/libs/main/system/pisignals.h @@ -59,6 +59,7 @@ public: // slot is any function format "void(PISignals::Signal)" static void setSlot(SignalEvent slot) { ret_func = slot; } static void grabSignals(PIFlags signals_); + static void releaseSignals(PIFlags signals_); static void raiseSignal(PISignals::Signal signal); private: From cd7e053fc529d9ddbc2310aabf1787968dabf3c0 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 15 Oct 2024 12:02:18 +0300 Subject: [PATCH 15/40] version 4.2.0 move toStdFunction() to pibase.h refactor PIParseHelper, now it much more abstract and useful fix PIIODevice::createFromFullPath() when whitespaces at start or end are presence PIStreamPacker add events for start and end packet receive PIClientServer::ClientBase add virtual methods for start and end packet receive. also one can enable diagnostics with enableDiagnostics() method PICout now call flush() on each end of output add PIString::entries(const PIString & str) --- CMakeLists.txt | 2 +- .../piclientserver_client_base.cpp | 22 ++++ libs/io_utils/pistreampacker.cpp | 6 +- .../piclientserver_client_base.h | 8 ++ libs/main/core/pibase.h | 16 +++ libs/main/core/picout.cpp | 14 ++- libs/main/core/picout.h | 40 +++---- libs/main/io_devices/piiodevice.cpp | 8 +- libs/main/io_utils/piparsehelper.h | 112 +++++++----------- libs/main/io_utils/pistreampacker.h | 11 ++ libs/main/state_machine/pistatemachine_base.h | 15 --- .../state_machine/pistatemachine_transition.h | 2 +- libs/main/text/pistring.cpp | 11 ++ libs/main/text/pistring.h | 10 ++ main.cpp | 16 +++ 15 files changed, 174 insertions(+), 119 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de871059..afb814b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(PIP) set(PIP_MAJOR 4) -set(PIP_MINOR 1) +set(PIP_MINOR 2) set(PIP_REVISION 0) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) diff --git a/libs/client_server/piclientserver_client_base.cpp b/libs/client_server/piclientserver_client_base.cpp index 4ee87169..3fec5e93 100644 --- a/libs/client_server/piclientserver_client_base.cpp +++ b/libs/client_server/piclientserver_client_base.cpp @@ -29,6 +29,7 @@ PIClientServer::ClientBase::~ClientBase() { close(); stopAndWait(); if (own_tcp) piDeleteSafety(tcp); + piDeleteSafety(diag); } @@ -60,17 +61,38 @@ int PIClientServer::ClientBase::write(const void * d, const size_t s) { } +void PIClientServer::ClientBase::enableDiagnostics() { + if (diag) return; + diag = new PIDiagnostics(); +} + + +PIDiagnostics::State PIClientServer::ClientBase::diagnostics() const { + if (!diag) return {}; + return diag->state(); +} + + +int PIClientServer::ClientBase::receivePacketProgress() const { + return stream.receivePacketProgress(); +} + + void PIClientServer::ClientBase::init() { if (!tcp) return; CONNECTL(&stream, sendRequest, [this](const PIByteArray & ba) { if (!can_write) return; tcp->send(ba); + if (diag) diag->sended(ba.size_s()); // piMSleep(1); }); CONNECTL(&stream, packetReceiveEvent, [this](PIByteArray & ba) { readed(ba); }); + CONNECTL(&stream, startPacketReceive, [this](int size) { receivePacketStart(size); }); + CONNECTL(&stream, endPacketReceive, [this]() { receivePacketEnd(); }); CONNECTL(tcp, threadedReadEvent, [this](const uchar * readed, ssize_t size) { if (!can_write) return; stream.received(readed, size); + if (diag) diag->received(size); }); CONNECTL(tcp, connected, [this]() { can_write = true; diff --git a/libs/io_utils/pistreampacker.cpp b/libs/io_utils/pistreampacker.cpp index 5ab536a0..7ec34a18 100644 --- a/libs/io_utils/pistreampacker.cpp +++ b/libs/io_utils/pistreampacker.cpp @@ -167,7 +167,10 @@ void PIStreamPacker::received(const PIByteArray & data) { stream.remove(0, hdr_size); packet.clear(); packet_size = sz; - if (packet_size == 0) packet_size = -1; + if (packet_size == 0) + packet_size = -1; + else + startPacketReceive(packet_size); continue; } else { int ps = piMini(stream.size_s(), packet_size - packet.size_s()); @@ -196,6 +199,7 @@ void PIStreamPacker::received(const PIByteArray & data) { } // piCout << "decrypt" << packet.size() << "->" << cd.size() << key().size(); if (!cd.isEmpty()) { + endPacketReceive(); packetReceived(cd); packetReceiveEvent(cd); } diff --git a/libs/main/client_server/piclientserver_client_base.h b/libs/main/client_server/piclientserver_client_base.h index 625dad80..1ee07290 100644 --- a/libs/main/client_server/piclientserver_client_base.h +++ b/libs/main/client_server/piclientserver_client_base.h @@ -26,6 +26,7 @@ #define piclientserver_client_base_H #include "piclientserver_config.h" +#include "pidiagnostics.h" #include "pip_client_server_export.h" #include "pistreampacker.h" @@ -52,12 +53,18 @@ public: int write(const void * d, const size_t s); int write(const PIByteArray & ba) { return write(ba.data(), ba.size()); } + void enableDiagnostics(); + PIDiagnostics::State diagnostics() const; + int receivePacketProgress() const; + Config & configuration() { return config; } protected: virtual void readed(PIByteArray data) {} virtual void connected() {} virtual void disconnected() {} + virtual void receivePacketStart(int size) {} + virtual void receivePacketEnd() {} void init(); @@ -71,6 +78,7 @@ private: PIStreamPacker stream; mutable PIMutex write_mutex; + PIDiagnostics * diag = nullptr; }; } // namespace PIClientServer diff --git a/libs/main/core/pibase.h b/libs/main/core/pibase.h index ec4491f8..d04dbd04 100644 --- a/libs/main/core/pibase.h +++ b/libs/main/core/pibase.h @@ -720,4 +720,20 @@ struct PIP_EXPORT PINonTriviallyCopyable { inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default; +template +struct FunctionType { + using Type = void; +}; + +template +struct FunctionType { + using Type = std::function; +}; + +template +typename FunctionType::Type toStdFunction(L const & func) { + return func; +} + + #endif // PIBASE_H diff --git a/libs/main/core/picout.cpp b/libs/main/core/picout.cpp index 32aad138..809e3753 100644 --- a/libs/main/core/picout.cpp +++ b/libs/main/core/picout.cpp @@ -159,19 +159,19 @@ DWORD PICout::__Private__::smode = 0; #endif -std::ostream & getStdStream(PICoutManipulators::PICoutStdStream s) { +std::ostream & getStdStream(PICoutStdStream s) { switch (s) { - case PICoutManipulators::StdOut: return std::cout; - case PICoutManipulators::StdErr: return std::cerr; + case PICoutStdStream::StdOut: return std::cout; + case PICoutStdStream::StdErr: return std::cerr; default: break; } return std::cout; } -std::wostream & getStdWStream(PICoutManipulators::PICoutStdStream s) { +std::wostream & getStdWStream(PICoutStdStream s) { switch (s) { - case PICoutManipulators::StdOut: return std::wcout; - case PICoutManipulators::StdErr: return std::wcerr; + case PICoutStdStream::StdOut: return std::wcout; + case PICoutStdStream::StdErr: return std::wcerr; default: break; } return std::wcout; @@ -209,6 +209,8 @@ PICout::~PICout() { } if (buffer_) { ((NotifierObject *)Notifier::object())->finished(id_, buffer_); + } else { + getStdStream(stream_).flush(); } } diff --git a/libs/main/core/picout.h b/libs/main/core/picout.h index 92e2fc47..c228772a 100644 --- a/libs/main/core/picout.h +++ b/libs/main/core/picout.h @@ -40,13 +40,13 @@ # define piCoutObj #else -# define piCout PICout(piDebug, PICoutManipulators::StdOut) -# define piCoutObj \ - PICout(piDebug && debug(), PICoutManipulators::StdOut) \ +# define piCout PICout(piDebug, PICoutStdStream::StdOut) +# define piCoutObj \ + PICout(piDebug && debug(), PICoutStdStream::StdOut) \ << (PIStringAscii("[") + className() + (name().isEmpty() ? "]" : PIStringAscii(" \"") + name() + PIStringAscii("\"]"))) -# define piCerr PICout(piDebug, PICoutManipulators::StdErr) -# define piCerrObj \ - PICout(piDebug && debug(), PICoutManipulators::StdErr) \ +# define piCerr PICout(piDebug, PICoutStdStream::StdErr) +# define piCerrObj \ + PICout(piDebug && debug(), PICoutStdStream::StdErr) \ << (PIStringAscii("[") + className() + (name().isEmpty() ? "]" : PIStringAscii(" \"") + name() + PIStringAscii("\"]"))) #endif @@ -131,19 +131,19 @@ enum PICoutFormat { }; -//! \~english Enum contains console streams -//! \~russian Перечисление с потоками консоли -enum PICoutStdStream { - StdOut /*! \~english Standard output stream \~russian Стандартный поток вывода */ = 0, - StdErr /*! \~english Standard error stream \~russian Стандартный поток ошибок */ = 1, -}; - - typedef PIFlags PICoutControls; } // namespace PICoutManipulators +//! \~english Enum contains console streams +//! \~russian Перечисление с потоками консоли +enum class PICoutStdStream { + StdOut /*! \~english Standard output stream \~russian Стандартный поток вывода */ = 0, + StdErr /*! \~english Standard error stream \~russian Стандартный поток ошибок */ = 1, +}; + + //! \ingroup Core //! \~\brief //! \~english Universal output to console class. @@ -152,11 +152,11 @@ class PIP_EXPORT PICout { public: //! \~english Default constructor with default features (AddSpaces and AddNewLine) //! \~russian Конструктор по умолчанию (AddSpaces и AddNewLine) - PICout(int controls = PICoutManipulators::DefaultControls, PICoutManipulators::PICoutStdStream stream = PICoutManipulators::StdOut); + PICout(int controls = PICoutManipulators::DefaultControls, PICoutStdStream stream = PICoutStdStream::StdOut); //! \~english Construct with default features (AddSpaces and AddNewLine), but if \"active\" is false does nothing //! \~russian Конструктор по умолчанию (AddSpaces и AddNewLine), но если не \"active\" то будет неактивным - PICout(bool active, PICoutManipulators::PICoutStdStream stream = PICoutManipulators::StdOut); + PICout(bool active, PICoutStdStream stream = PICoutStdStream::StdOut); PICout(const PICout & other); @@ -333,7 +333,7 @@ public: //! \~english Output \a PIString to stdout //! \~russian Вывод \a PIString в stdout - static void stdoutPIString(const PIString & str, PICoutManipulators::PICoutStdStream s = PICoutManipulators::StdOut); + static void stdoutPIString(const PIString & str, PICoutStdStream s = PICoutStdStream::StdOut); //! \~english Returns internal PIString buffer //! \~russian Возвращает внутренний PIString буфер @@ -396,9 +396,9 @@ private: PRIVATE_DECLARATION(PIP_EXPORT) bool first_out_ = true, is_copy_ = false, format_changed_ = false, actve_ = true; int int_base_ = 10, win_attr_ = 0, id_ = 0; - PIString * buffer_ = nullptr; - PICoutManipulators::PICoutControls ctrl_ = PICoutManipulators::DefaultControls; - PICoutManipulators::PICoutStdStream stream_ = PICoutManipulators::StdOut; + PIString * buffer_ = nullptr; + PICoutManipulators::PICoutControls ctrl_ = PICoutManipulators::DefaultControls; + PICoutStdStream stream_ = PICoutStdStream::StdOut; }; #endif // PICOUT_H diff --git a/libs/main/io_devices/piiodevice.cpp b/libs/main/io_devices/piiodevice.cpp index 83007023..fa92731a 100644 --- a/libs/main/io_devices/piiodevice.cpp +++ b/libs/main/io_devices/piiodevice.cpp @@ -529,6 +529,7 @@ void PIIODevice::configureFromVariant(const PIVariantTypes::IODevice & d) { void PIIODevice::splitFullPath(PIString fpwm, PIString * full_path, DeviceMode * mode, DeviceOptions * opts) { + fpwm.trim(); int dm = 0; DeviceOptions op = 0; if (fpwm.find('(') > 0 && fpwm.find(')') > 0) { @@ -617,11 +618,12 @@ PIString PIIODevice::fullPathOptions() const { //! В метод \a configureFromFullPath() "full_path" передается без \a fullPathPrefix() и "://". //! См. \ref PIIODevice_sec7 PIIODevice * PIIODevice::createFromFullPath(const PIString & full_path) { - PIString prefix = full_path.left(full_path.find(":")); + PIString fp = full_path.trimmed(); + PIString prefix = fp.left(fp.find(":")); PIIODevice * nd = newDeviceByPrefix(prefix.dataAscii()); if (!nd) return nullptr; - nd->configureFromFullPath(full_path.mid(prefix.length() + 3)); - cacheFullPath(full_path, nd); + nd->configureFromFullPath(fp.mid(prefix.length() + 3)); + cacheFullPath(fp, nd); return nd; } diff --git a/libs/main/io_utils/piparsehelper.h b/libs/main/io_utils/piparsehelper.h index c091184f..4ccda4da 100644 --- a/libs/main/io_utils/piparsehelper.h +++ b/libs/main/io_utils/piparsehelper.h @@ -38,7 +38,7 @@ * This class helps to deserialize and invoke neccessarily methods. * * Data packets with header and various data types can be automated by this class. - * Every key value mapped to event handler or lambda-function. + * Every key value mapped to object member function, lambda-function or functor. * * This class can remove \b switch-case with deserialization code and * replace it with several \a assign() calls, binded to ready-to-use event handlers. @@ -48,30 +48,15 @@ * * * \section PIParseHelper_usage Usage - * There are two variants: subclassing and aggregating. * - * ### Subclass + * Create instance of %PIParseHelper, or subclass. * - * Inherit your class from %PIParseHelper and construct it with \b this. - * - * In \a assign() methods you can use event handlers with 0 or 1 arguments, - * using HANDLER() macro. - * - * ### Aggregate - * - * Create instance of %PIParseHelper and construct it with target object. - * - * In \a assign() methods you can use event handlers with 0 or 1 arguments, - * using HANDLER() macro in target object namespace (e.g. \"o.HANDLER(slot)\"). + * In \a assign() methods you can use object member function, lambda-function + * or functor with 0 or 1 arguments, * * * \section PIParseHelper_lambda Lambda-functions - * Assign methods that receive lambda-functions can`t accept direct lambda (\"[](){}\") - * with template type deducation because of C++ specific. E.g. - * \code assign(1, [this](SomeStruct s){}) \endcode - * doesn`t compile, but these variants allowed: - * \code assign(1, std::function([this](SomeStruct s){})) \endcode - * \code assign(1, [this](SomeStruct s){}) \endcode + * \code assign(1, [this](const SomeStruct & s){}) \endcode * * * \section PIParseHelper_examples Examples @@ -87,56 +72,21 @@ template class PIParseHelper { public: - //! \brief Construct %PIParseHelper with target object \"p\" - PIParseHelper(PIObject * p): parent(p) {} + //! \brief Construct %PIParseHelper + PIParseHelper() {} - //! \brief Assign key \"key\" to event handler \"handler\" with 1 argument - template - void assign(Key key, Ret (*handler)(void *, T)) { - if (!parent) return; - auto mf = PIObject::__meta_data().value(parent->classNameID()); - auto it = mf.eh_func.makeIterator(); - while (it.hasNext()) { - it.next(); - if (it.value().addr == handler) { - void * addr = it.value().addr; - auto func = [addr](PIObject * o, PIByteArray data) { - T v; - data >> v; - ((void (*)(void *, T))addr)(o, v); - }; - functions[key] << func; - break; - } - } - } - - - //! \brief Assign key \"key\" to event handler \"handler\" without arguments - template - void assign(Key key, Ret (*handler)(void *)) { - if (!parent) return; - auto mf = PIObject::__meta_data().value(parent->classNameID()); - auto it = mf.eh_func.makeIterator(); - while (it.hasNext()) { - it.next(); - if (it.value().addr == handler) { - void * addr = it.value().addr; - auto func = [addr](PIObject * o, PIByteArray) { ((void (*)(void *))addr)(o); }; - functions[key] << func; - break; - } - } + //! \brief Assign key \"key\" to lambda-function \"func\" without arguments + void assign(Key key, std::function func) { + auto lf = [func](PIByteArray) { func(); }; + functions[key] << lf; } //! \brief Assign key \"key\" to lambda-function \"func\" with 1 argument - //! \note Important! Direct lambda functions are not allowed, see \ref PIParseHelper_lambda template - void assign(Key key, std::function func) { - if (!parent) return; - auto lf = [func](PIObject *, PIByteArray data) { + void assign(Key key, std::function func) { + auto lf = [func](PIByteArray data) { if (!data.isEmpty()) { T v; data >> v; @@ -147,26 +97,44 @@ public: } - //! \brief Assign key \"key\" to lambda-function \"func\" without arguments - //! \note Important! Direct lambda functions are not allowed, see \ref PIParseHelper_lambda - void assign(Key key, std::function func) { - if (!parent) return; - auto lf = [func](PIObject *, PIByteArray) { func(); }; + //! \brief Assign key \"key\" to member function of object \"obj\" without arguments + template + void assign(Key key, O * obj, void (O::*member_func)()) { + auto lf = [member_func, obj](PIByteArray) { (obj->*member_func)(); }; functions[key] << lf; } + //! \brief Assign key \"key\" to member function of object \"obj\" with 1 argument + template + void assign(Key key, O * obj, void (O::*member_func)(const T &)) { + auto lf = [member_func, obj](PIByteArray data) { + if (!data.isEmpty()) { + T v; + data >> v; + (obj->*member_func)(v); + } + }; + functions[key] << lf; + } + + + //! \brief Assign key \"key\" to functor \"func\" with 0 or 1 argument + template + void assign(Key key, L func) { + return assign(key, toStdFunction(func)); + } + + //! \brief Deserialize data and invoke assigned to \"key\" methods void parse(Key key, PIByteArray ba) { - if (!parent) return; auto fl = functions.value(key); for (auto f: fl) - f(parent, ba); + f(ba); } private: - PIMap>> functions; - PIObject * parent; + PIMap>> functions; }; diff --git a/libs/main/io_utils/pistreampacker.h b/libs/main/io_utils/pistreampacker.h index 0e619407..88a8c35a 100644 --- a/libs/main/io_utils/pistreampacker.h +++ b/libs/main/io_utils/pistreampacker.h @@ -56,6 +56,9 @@ public: //! Returns packet sinature, default 0xAFBE ushort packetSign() const { return packet_sign; } + //! Returns progress of current packet receive in bytes + int receivePacketProgress() const { return packet.size_s(); } + //! Set receive aggressive optimization. If yes then %PIStreamPacker doesn`t //! check every byte in incoming stream but check only begin of each read() @@ -90,6 +93,8 @@ public: void assignDevice(PIIODevice * dev); EVENT1(packetReceiveEvent, PIByteArray &, data); + EVENT1(startPacketReceive, int, size); + EVENT0(endPacketReceive); EVENT1(sendRequest, PIByteArray, data); //! \handlers @@ -107,6 +112,12 @@ public: //! \fn void packetReceiveEvent(PIByteArray data) //! \brief Raise on packet successfully received + //! \fn void startPacketReceive(int size) + //! \brief Raise on start receive packet with overall size \"size\" + + //! \fn void endPacketReceive() + //! \brief Raise on finish receive packet + //! \fn void sendRequest(PIByteArray data) //! \brief Raise from \a send() function. This data should //! be directly sended to your device. You can diff --git a/libs/main/state_machine/pistatemachine_base.h b/libs/main/state_machine/pistatemachine_base.h index 88f2f561..4c1e4c89 100644 --- a/libs/main/state_machine/pistatemachine_base.h +++ b/libs/main/state_machine/pistatemachine_base.h @@ -31,21 +31,6 @@ namespace PIStateMachineHelpers { -template -struct FunctionType { - using Type = void; -}; - -template -struct FunctionType { - using Type = std::function; -}; - -template -typename FunctionType::Type toStdFunction(L const & func) { - return func; -} - class FunctionBase { public: virtual ~FunctionBase() {} diff --git a/libs/main/state_machine/pistatemachine_transition.h b/libs/main/state_machine/pistatemachine_transition.h index d29d9ae9..2cc0dfe8 100644 --- a/libs/main/state_machine/pistatemachine_transition.h +++ b/libs/main/state_machine/pistatemachine_transition.h @@ -56,7 +56,7 @@ public: template PITransitionBase * addGuard(L f) { - return addGuard(PIStateMachineHelpers::toStdFunction(f)); + return addGuard(toStdFunction(f)); } template diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index 2baace77..8707b0d5 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -1197,6 +1197,17 @@ int PIString::entries(const PIChar c) const { } +int PIString::entries(const PIString & str) const { + int sz = size_s(), ssz = str.size_s(); + if (ssz == 0 || sz < ssz) return 0; + int btc = str.size_s() * sizeof(PIChar), ret = 0; + for (int i = 0; i < sz - ssz + 1; ++i) { + if (piCompareBinary(d.data(i), str.d.data(0), btc)) ++ret; + } + return ret; +} + + bool PIString::startsWith(const PIChar c) const { if (isEmpty()) return false; return front() == c; diff --git a/libs/main/text/pistring.h b/libs/main/text/pistring.h index 94170853..b4f5767f 100644 --- a/libs/main/text/pistring.h +++ b/libs/main/text/pistring.h @@ -1271,6 +1271,16 @@ public: //! piCout << PIString(".str.0").entries("0"); // 1 //! \endcode int entries(const PIChar c) const; + int entries(char c) const { return entries(PIChar(c)); } + + //! \~english Returns number of occurrences of string "str". + //! \~russian Возвращает число вхождений строк "str". + //! \~\details + //! \~\code + //! piCout << PIString("hello locale").entries("lo"); // 2 + //! piCout << PIString("hello locale").entries("lo lo"); // 1 + //! \endcode + int entries(const PIString & str) const; //! \~english Returns if string starts with "c". //! \~russian Возвращает начинается ли строка с "c". diff --git a/main.cpp b/main.cpp index e58a2374..21450986 100644 --- a/main.cpp +++ b/main.cpp @@ -68,6 +68,22 @@ protected: #include int main(int argc, char * argv[]) { + /*piCout << PIString("hello locale").entries("lo"); // 2 + piCout << PIString("hello locale").entries("lo lo"); // 1 + piCout << ("3hello4"_u8); + piCout << ("3hello4"_u8).entries("hello1"); + piCout << ("3hello4"_u8).entries("hello"); + piCout << ("3hello4"_u8).entries("l"); + piCout << ("3hello4"_u8).entries("ello"); + piCout << ("3hello4"_u8).entries("hell"); + piCout << ("2hellohello1"_u8); + piCout << ("2hellohello1"_u8).entries("hello1"); + piCout << ("2hellohello1"_u8).entries("hello"); + piCout << ("2hellohello1"_u8).entries("l"); + piCout << ("2hellohello1"_u8).entries("ello"); + piCout << ("2hellohello1"_u8).entries("hell"); + return 0;*/ + PILog log; log.setColorConsole(false); log.setOutput(PILog::File, false); From 92a87a0c64f58a96227827b148cd3a79bc145e39 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 15 Oct 2024 15:04:39 +0300 Subject: [PATCH 16/40] picloud add "-w" option for watchdog --- utils/cloud_dispatcher/dispatcherserver.cpp | 18 ++++++++++++++++++ utils/cloud_dispatcher/dispatcherserver.h | 3 +++ utils/cloud_dispatcher/main.cpp | 15 +++++++++------ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/utils/cloud_dispatcher/dispatcherserver.cpp b/utils/cloud_dispatcher/dispatcherserver.cpp index ad581fbd..3b25990f 100644 --- a/utils/cloud_dispatcher/dispatcherserver.cpp +++ b/utils/cloud_dispatcher/dispatcherserver.cpp @@ -45,6 +45,7 @@ void DispatcherServer::picoutStatus() { void DispatcherServer::cleanClients() { + wd_cnt++; PIMutexLocker locker(map_mutex); for (auto s: rmrf_servers) s->close(); @@ -194,6 +195,23 @@ void DispatcherServer::setMaxConnections(uint max_count) { } +void DispatcherServer::startWatchdog() { + wd_cnt++; + PIThread * thread = new PIThread( + [this]() { + if (wd_cnt == 0) { + piCout << "Deadlock detected, abort"; + std::abort(); + return; + } + wd_cnt = 0; + }, + true, + 10_s); + NO_UNUSED(thread); +} + + void DispatcherServer::disconnectClient(DispatcherClient * client) { PIMutexLocker locker(map_mutex); if (!clients.contains(client)) { diff --git a/utils/cloud_dispatcher/dispatcherserver.h b/utils/cloud_dispatcher/dispatcherserver.h index 28611525..fc4d3a1e 100644 --- a/utils/cloud_dispatcher/dispatcherserver.h +++ b/utils/cloud_dispatcher/dispatcherserver.h @@ -25,6 +25,8 @@ public: uint maxConnections() const { return max_connections; } EVENT_HANDLER0(void, picoutStatus); + void startWatchdog(); + private: EVENT_HANDLER1(void, newConnection, PIEthernet *, cl); EVENT_HANDLER1(void, disconnectClient, DispatcherClient *, client); @@ -40,6 +42,7 @@ private: PIVector rmrf_servers; PITimer timeout_timer; PIMutex map_mutex; + std::atomic_int wd_cnt = {0}; uint client_gid; uint max_connections; }; diff --git a/utils/cloud_dispatcher/main.cpp b/utils/cloud_dispatcher/main.cpp index 273e1893..e210a488 100644 --- a/utils/cloud_dispatcher/main.cpp +++ b/utils/cloud_dispatcher/main.cpp @@ -34,13 +34,14 @@ using namespace PICoutManipulators; void usage() { piCout << Bold << "PIP Cloud Dispatcher"; piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine; - piCout << Green << Bold << "Usage:" << Default << "\"picloud [-hsv] [-i ] [-p ]\"" << NewLine; + piCout << Green << Bold << "Usage:" << Default << "\"picloud [-hswv] [-i ] [-p ]\"" << NewLine; piCout << Green << Bold << "Details:"; - piCout << "-h --help " << Green << "- display this message and exit"; - piCout << "-i --ip " << Green << "- listen address, default \"0.0.0.0\""; - piCout << "-p --port " << Green << "- listen port, default 10101"; - piCout << "-s --screen " << Green << "- start with console UI"; - piCout << "-v --verbose" << Green << "- print state (--screen ignore this flag)"; + piCout << "-h --help " << Green << "- display this message and exit"; + piCout << "-i --ip " << Green << "- listen address, default \"0.0.0.0\""; + piCout << "-p --port " << Green << "- listen port, default 10101"; + piCout << "-s --screen " << Green << "- start with console UI"; + piCout << "-w --watchdog" << Green << "- kill itself on deadlock"; + piCout << "-v --verbose " << Green << "- print state (--screen ignore this flag)"; } @@ -64,6 +65,7 @@ int main(int argc, char * argv[]) { cli.addArgument("ip", true); cli.addArgument("port", true); cli.addArgument("screen"); + cli.addArgument("watchdog"); cli.addArgument("verbose"); if (cli.hasArgument("help")) { @@ -154,6 +156,7 @@ int main(int argc, char * argv[]) { ls.enableExitCapture(PIKbdListener::F10); ls.start(); server.start(); + if (cli.hasArgument("watchdog")) server.startWatchdog(); if (cli.hasArgument("verbose")) { CONNECTU(&status_timer, tickEvent, &server, picoutStatus); status_timer.start(1_Hz); From d3d723533825cee33f1489777fe41c7c93d3c900 Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 16 Oct 2024 22:10:28 +0300 Subject: [PATCH 17/40] enable complex type for PIMathVectorT and PIMathMatrixT TODO: add precision to invert and test vector --- libs/main/math/pimathcomplex.h | 6 + libs/main/math/pimathmatrix.h | 26 +-- libs/main/math/pimathvector.h | 120 ++++++++---- tests/math/testpimathmatrixt.cpp | 313 ++++++++++++++++--------------- 4 files changed, 258 insertions(+), 207 deletions(-) diff --git a/libs/main/math/pimathcomplex.h b/libs/main/math/pimathcomplex.h index a6435666..84cf1ab0 100644 --- a/libs/main/math/pimathcomplex.h +++ b/libs/main/math/pimathcomplex.h @@ -36,6 +36,12 @@ using std::complex; +template +struct is_complex: std::false_type {}; + +template +struct is_complex>: std::true_type {}; + typedef complex complexi; typedef complex complexs; typedef complex complexf; diff --git a/libs/main/math/pimathmatrix.h b/libs/main/math/pimathmatrix.h index 242c9c4f..c853a381 100644 --- a/libs/main/math/pimathmatrix.h +++ b/libs/main/math/pimathmatrix.h @@ -63,7 +63,7 @@ class PIP_EXPORT PIMathMatrixT { typedef PIMathMatrixT _CMatrixI; typedef PIMathVectorT _CMCol; typedef PIMathVectorT _CMRow; - static_assert(std::is_arithmetic::value, "Type must be arithmetic"); + static_assert(std::is_arithmetic::value || is_complex::value, "Type must be arithmetic or complex"); static_assert(Rows > 0, "Row count must be > 0"); static_assert(Cols > 0, "Column count must be > 0"); @@ -386,7 +386,7 @@ public: //! \brief Деление с присваиванием с матрицей `v`. //! \param sm матрица для деления с присваиванием. void operator/=(const Type & v) { - assert(piAbs(v) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(v) > PIMATHVECTOR_ZERO_CMP); PIMM_FOR m[r][c] /= v; } @@ -453,7 +453,7 @@ public: //! \param v делитель. //! \return результат деления. PIMathMatrixT operator/(const Type & v) const { - assert(piAbs(v) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(v) > PIMATHVECTOR_ZERO_CMP); PIMathMatrixT tm = PIMathMatrixT(*this); PIMM_FOR tm.m[r][c] /= v; return tm; @@ -517,7 +517,7 @@ public: for (uint i = 0; i < Cols; ++i) { ndet = true; for (uint j = 0; j < Rows; ++j) - if (smat.m[i][j] != 0) ndet = false; + if (smat.m[i][j] != Type{}) ndet = false; if (ndet) { if (ok != 0) *ok = false; return *this; @@ -525,15 +525,16 @@ public: } for (uint i = 0; i < Cols; ++i) { crow = i; - while (smat.m[i][i] == Type(0)) + while (smat.m[i][i] == Type{}) { smat.swapRows(i, ++crow); + } for (uint j = i + 1; j < Rows; ++j) { mul = smat.m[i][j] / smat.m[i][i]; for (uint k = i; k < Cols; ++k) smat.m[k][j] -= mul * smat.m[k][i]; } if (i < Cols - 1) { - if (piAbs(smat.m[i + 1][i + 1]) < Type(1E-200)) { + if (std::abs(smat.m[i + 1][i + 1]) < PIMATHVECTOR_ZERO_CMP) { if (ok != 0) *ok = false; return *this; } @@ -562,8 +563,9 @@ public: Type mul, iddiv; for (uint i = 0; i < Cols; ++i) { ndet = true; - for (uint j = 0; j < Rows; ++j) - if (smat.m[i][j] != 0) ndet = false; + for (uint j = 0; j < Rows; ++j) { + if (std::abs(smat.m[i][j]) >= PIMATHVECTOR_ZERO_CMP) ndet = false; + } if (ndet) { if (ok != 0) *ok = false; return *this; @@ -571,7 +573,7 @@ public: } for (uint i = 0; i < Cols; ++i) { crow = i; - while (smat.m[i][i] == Type(0)) { + while (std::abs(smat.m[i][i]) < PIMATHVECTOR_ZERO_CMP) { ++crow; smat.swapRows(i, crow); mtmp.swapRows(i, crow); @@ -584,7 +586,7 @@ public: mtmp.m[k][j] -= mul * mtmp.m[k][i]; } if (i < Cols - 1) { - if (piAbs(smat.m[i + 1][i + 1]) < Type(1E-200)) { + if (std::abs(smat.m[i + 1][i + 1]) < PIMATHVECTOR_ZERO_CMP) { if (ok != 0) *ok = false; return *this; } @@ -650,7 +652,7 @@ public: static_assert(Rows == 2 && Cols == 2, "Works only with 2x2 matrix"); Type c = std::cos(angle); Type s = std::sin(angle); - PIMathMatrixT<2u, 2u> tm; + PIMathMatrixT<2u, 2u, Type> tm; tm[0][0] = tm[1][1] = c; tm[0][1] = -s; tm[1][0] = s; @@ -671,7 +673,7 @@ public: PIMathMatrixT outm; Type c = std::cos(angle); Type s = std::sin(angle); - PIMathMatrixT<2u, 2u> tm; + PIMathMatrixT<2u, 2u, Type> tm; tm[0][0] = tm[1][1] = c; tm[0][1] = -s; tm[1][0] = s; diff --git a/libs/main/math/pimathvector.h b/libs/main/math/pimathvector.h index 4f8195ff..41b4daa5 100644 --- a/libs/main/math/pimathvector.h +++ b/libs/main/math/pimathvector.h @@ -27,12 +27,13 @@ #define PIMATHVECTOR_H #include "pimathbase.h" +#include "pimathcomplex.h" template class PIMathMatrixT; -#define PIMATHVECTOR_ZERO_CMP Type(1E-100) +#define PIMATHVECTOR_ZERO_CMP (1E-100) /// Vector templated @@ -42,7 +43,7 @@ class PIMathMatrixT; template class PIP_EXPORT PIMathVectorT { typedef PIMathVectorT _CVector; - static_assert(std::is_arithmetic::value, "Type must be arithmetic"); + static_assert(std::is_arithmetic::value || is_complex::value, "Type must be arithmetic or complex"); static_assert(Size > 0, "Size must be > 0"); public: @@ -83,35 +84,71 @@ public: PIMV_FOR tv += c[i] * c[i]; return tv; } - Type length() const { return std::sqrt(lengthSqr()); } + + Type length() const { + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) return std::sqrt(lengthSqr()); + // if (is_complex::value) return 1000.; // std::sqrt(lengthSqr()); + } + Type manhattanLength() const { - Type tv(0); - PIMV_FOR tv += piAbs(c[i]); - return tv; + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + Type tv(0); + PIMV_FOR tv += piAbs(c[i]); + return tv; + } } Type angleCos(const _CVector & v) const { - Type tv = v.length() * length(); - assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); - return dot(v) / tv; + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + Type tv = v.length() * length(); + assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); + return dot(v) / tv; + } } Type angleSin(const _CVector & v) const { - Type tv = angleCos(v); - return std::sqrt(Type(1) - tv * tv); + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + Type tv = angleCos(v); + return std::sqrt(Type(1) - tv * tv); + } + } + Type angleRad(const _CVector & v) const { + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + return std::acos(angleCos(v)); + } + } + Type angleDeg(const _CVector & v) const { + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + return toDeg(angleRad(v)); + } + } + Type angleElevation(const _CVector & v) const { + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + return 90.0 - angleDeg(v - *this); + } } - Type angleRad(const _CVector & v) const { return std::acos(angleCos(v)); } - Type angleDeg(const _CVector & v) const { return toDeg(angleRad(v)); } - Type angleElevation(const _CVector & v) const { return 90.0 - angleDeg(v - *this); } _CVector projection(const _CVector & v) { - Type tv = v.length(); - assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); - return v * (dot(v) / tv); + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + Type tv = v.length(); + assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); + return v * (dot(v) / tv); + } } _CVector & normalize() { - Type tv = length(); - assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); - if (tv == Type(1)) return *this; - PIMV_FOR c[i] /= tv; - return *this; + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + Type tv = length(); + assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); + if (tv == Type(1)) return *this; + PIMV_FOR c[i] /= tv; + return *this; + } } _CVector normalized() { _CVector tv(*this); @@ -119,10 +156,10 @@ public: return tv; } bool isNull() const { - PIMV_FOR if (c[i] != Type(0)) return false; + PIMV_FOR if (c[i] != Type{}) return false; return true; } - bool isOrtho(const _CVector & v) const { return ((*this) ^ v) == Type(0); } + bool isOrtho(const _CVector & v) const { return ((*this) ^ v) == Type{}; } Type & operator[](uint index) { return c[index]; } const Type & operator[](uint index) const { return c[index]; } @@ -145,7 +182,7 @@ public: void operator-=(const _CVector & v) { PIMV_FOR c[i] -= v[i]; } void operator*=(const Type & v) { PIMV_FOR c[i] *= v; } void operator/=(const Type & v) { - assert(piAbs(v) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(v) > PIMATHVECTOR_ZERO_CMP); PIMV_FOR c[i] /= v; } _CVector operator-() const { @@ -169,7 +206,7 @@ public: return tv; } _CVector operator/(const Type & v) const { - assert(piAbs(v) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(v) > PIMATHVECTOR_ZERO_CMP); _CVector tv = _CVector(*this); PIMV_FOR tv[i] /= v; return tv; @@ -184,7 +221,7 @@ public: return tv; } Type dot(const _CVector & v) const { - Type tv(0); + Type tv{}; PIMV_FOR tv += c[i] * v[i]; return tv; } @@ -197,7 +234,7 @@ public: _CVector div(const _CVector & v) const { _CVector tv(*this); PIMV_FOR { - assert(piAbs(v[i]) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(v[i]) > PIMATHVECTOR_ZERO_CMP); tv[i] /= v[i]; } return tv; @@ -211,11 +248,14 @@ public: } Type distToLine(const _CVector & lp0, const _CVector & lp1) { - _CVector a(lp0, lp1); - Type tv = a.length(); - assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); - _CVector b(lp0, *this); - return piAbs(a[0] * b[1] - a[1] * b[0]) / tv; + static_assert(std::is_arithmetic::value, "Unavailable for complex"); + if (std::is_arithmetic::value) { + _CVector a(lp0, lp1); + Type tv = a.length(); + assert(std::abs(tv) > PIMATHVECTOR_ZERO_CMP); + _CVector b(lp0, *this); + return piAbs(a[0] * b[1] - a[1] * b[0]) / tv; + } } template /// vector {Size, Type} to vector {Size1, Type1} @@ -394,7 +434,7 @@ public: Type angleCos(const _CVector & v) const { assert(c.size() == v.size()); Type tv = v.length() * length(); - assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(tv) > PIMATHVECTOR_ZERO_CMP); return dot(v) / tv; } Type angleSin(const _CVector & v) const { @@ -407,12 +447,12 @@ public: _CVector projection(const _CVector & v) { assert(c.size() == v.size()); Type tv = v.length(); - assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(tv) > PIMATHVECTOR_ZERO_CMP); return v * (dot(v) / tv); } _CVector & normalize() { Type tv = length(); - assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(tv) > PIMATHVECTOR_ZERO_CMP); if (tv == Type(1)) return *this; PIMV_FOR c[i] /= tv; return *this; @@ -451,7 +491,7 @@ public: } void operator*=(const Type & v) { PIMV_FOR c[i] *= v; } void operator/=(const Type & v) { - assert(piAbs(v) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(v) > PIMATHVECTOR_ZERO_CMP); PIMV_FOR c[i] /= v; } _CVector operator-() const { @@ -477,7 +517,7 @@ public: return tv; } _CVector operator/(const Type & v) const { - assert(piAbs(v) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(v) > PIMATHVECTOR_ZERO_CMP); _CVector tv(*this); PIMV_FOR tv[i] /= v; return tv; @@ -508,7 +548,7 @@ public: assert(c.size() == v.size()); _CVector tv(*this); PIMV_FOR { - assert(piAbs(v[i]) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(v[i]) > PIMATHVECTOR_ZERO_CMP); tv[i] /= v[i]; } return tv; @@ -520,7 +560,7 @@ public: assert(c.size() == lp1.size()); _CVector a = _CVector::fromTwoPoints(lp0, lp1); Type tv = a.length(); - assert(piAbs(tv) > PIMATHVECTOR_ZERO_CMP); + assert(std::abs(tv) > PIMATHVECTOR_ZERO_CMP); _CVector b = _CVector::fromTwoPoints(lp0, *this); return piAbs(a[0] * b[1] - a[1] * b[0]) / tv; } diff --git a/tests/math/testpimathmatrixt.cpp b/tests/math/testpimathmatrixt.cpp index d5c643b0..8900aa99 100644 --- a/tests/math/testpimathmatrixt.cpp +++ b/tests/math/testpimathmatrixt.cpp @@ -4,8 +4,9 @@ const uint rows = 3; const uint cols = 3; +using Type = double; -bool cmpSquareMatrixWithValue(PIMathMatrixT matrix, double val, int num) { +bool cmpSquareMatrixWithValue(PIMathMatrixT matrix, Type val, int num) { for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { if (matrix.at(i, j) != val) { @@ -17,50 +18,50 @@ bool cmpSquareMatrixWithValue(PIMathMatrixT matrix, double v } TEST(PIMathMatrixT_Test, identity) { - auto matrix = PIMathMatrixT::identity(); + auto matrix = PIMathMatrixT::identity(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (i != j) { if (matrix[i][j] != 0.0) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } else { if (matrix[i][i] != 1.0) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } } } - ASSERT_TRUE(true); + EXPECT_TRUE(true); } TEST(PIMathMatrixT_Test, at) { - auto matrix1 = PIMathMatrixT::identity(); + auto matrix1 = PIMathMatrixT::identity(); for (uint i = 0; i < rows; i++) { if (matrix1.at(i, i) != 1.0) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } - ASSERT_TRUE(true); + EXPECT_TRUE(true); } TEST(PIMathMatrixT_Test, filled) { - auto matr = PIMathMatrixT(1.0); - ASSERT_TRUE(cmpSquareMatrixWithValue(matr, 1.0, rows)); + auto matr = PIMathMatrixT(1.0); + EXPECT_TRUE(cmpSquareMatrixWithValue(matr, 1.0, rows)); } TEST(PIMathMatrixT_Test, cols) { - PIMathMatrixT matr; - ASSERT_EQ(cols, matr.cols()); + PIMathMatrixT matr; + EXPECT_EQ(cols, matr.cols()); } TEST(PIMathMatrixT_Test, rows) { - PIMathMatrixT matr; - ASSERT_EQ(rows, matr.rows()); + PIMathMatrixT matr; + EXPECT_EQ(rows, matr.rows()); } TEST(PIMathMatrixT_Test, col) { - PIMathMatrixT matr; - PIMathVectorT vect; + PIMathMatrixT matr; + PIMathVectorT vect; uint g = 2; matr.element(0, 0) = 3; matr.element(0, 1) = 6; @@ -74,15 +75,15 @@ TEST(PIMathMatrixT_Test, col) { vect = matr.col(g); for (uint i = 0; i < matr.cols(); i++) { if (matr.element(i, g) != vect[i]) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } - ASSERT_TRUE(true); + EXPECT_TRUE(true); } TEST(PIMathMatrixT_Test, row) { - PIMathMatrixT matr; - PIMathVectorT vect; + PIMathMatrixT matr; + PIMathVectorT vect; uint g = 2; matr.element(0, 0) = 3; matr.element(0, 1) = 6; @@ -96,15 +97,15 @@ TEST(PIMathMatrixT_Test, row) { vect = matr.row(g); for (uint i = 0; i < matr.rows(); i++) { if (matr.element(g, i) != vect[i]) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } - ASSERT_TRUE(true); + EXPECT_TRUE(true); } TEST(PIMathMatrixT_Test, setCol) { - PIMathMatrixT matr; - PIMathVectorT vect; + PIMathMatrixT matr; + PIMathVectorT vect; vect[0] = 1.0; vect[1] = 3.0; vect[2] = 5.0; @@ -112,15 +113,15 @@ TEST(PIMathMatrixT_Test, setCol) { matr.setCol(g, vect); for (uint i = 0; i < vect.size(); i++) { if (matr.element(i, g) != vect[i]) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } - ASSERT_TRUE(true); + EXPECT_TRUE(true); } TEST(PIMathMatrixT_Test, setRow) { - PIMathMatrixT matr; - PIMathVectorT vect; + PIMathMatrixT matr; + PIMathVectorT vect; vect[0] = 1.0; vect[1] = 3.0; vect[2] = 5.0; @@ -128,114 +129,114 @@ TEST(PIMathMatrixT_Test, setRow) { matr.setRow(g, vect); for (uint i = 0; i < vect.size(); i++) { if (matr.element(g, i) != vect[i]) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } - ASSERT_TRUE(true); + EXPECT_TRUE(true); } TEST(PIMathMatrixT_Test, swapCols) { - PIMathMatrixT matr; + PIMathMatrixT matr; int g1 = 1, g2 = 2; - matr.element(0, 0) = 3; - matr.element(0, 1) = 6; - matr.element(0, 2) = 8; - matr.element(1, 0) = 2; - matr.element(1, 1) = 1; - matr.element(1, 2) = 4; - matr.element(2, 0) = 6; - matr.element(2, 1) = 2; - matr.element(2, 2) = 5; - const PIMathVectorT before_Vect1 = matr.col(g1); - const PIMathVectorT before_Vect2 = matr.col(g2); + matr.element(0, 0) = 3; + matr.element(0, 1) = 6; + matr.element(0, 2) = 8; + matr.element(1, 0) = 2; + matr.element(1, 1) = 1; + matr.element(1, 2) = 4; + matr.element(2, 0) = 6; + matr.element(2, 1) = 2; + matr.element(2, 2) = 5; + const PIMathVectorT before_Vect1 = matr.col(g1); + const PIMathVectorT before_Vect2 = matr.col(g2); matr.swapCols(g1, g2); - const PIMathVectorT after_Vect1 = matr.col(g1); - const PIMathVectorT after_Vect2 = matr.col(g2); + const PIMathVectorT after_Vect1 = matr.col(g1); + const PIMathVectorT after_Vect2 = matr.col(g2); if ((before_Vect1 == after_Vect2) && (before_Vect2 == after_Vect1)) { - ASSERT_TRUE(true); + EXPECT_TRUE(true); } else { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } TEST(PIMathMatrixT_Test, swapRows) { - PIMathMatrixT matr; + PIMathMatrixT matr; int g1 = 1, g2 = 2; - matr.element(0, 0) = 3; - matr.element(0, 1) = 6; - matr.element(0, 2) = 8; - matr.element(1, 0) = 2; - matr.element(1, 1) = 1; - matr.element(1, 2) = 4; - matr.element(2, 0) = 6; - matr.element(2, 1) = 2; - matr.element(2, 2) = 5; - const PIMathVectorT before_Vect1 = matr.row(g1); - const PIMathVectorT before_Vect2 = matr.row(g2); + matr.element(0, 0) = 3; + matr.element(0, 1) = 6; + matr.element(0, 2) = 8; + matr.element(1, 0) = 2; + matr.element(1, 1) = 1; + matr.element(1, 2) = 4; + matr.element(2, 0) = 6; + matr.element(2, 1) = 2; + matr.element(2, 2) = 5; + const PIMathVectorT before_Vect1 = matr.row(g1); + const PIMathVectorT before_Vect2 = matr.row(g2); matr.swapRows(g1, g2); - const PIMathVectorT after_Vect1 = matr.row(g1); - const PIMathVectorT after_Vect2 = matr.row(g2); + const PIMathVectorT after_Vect1 = matr.row(g1); + const PIMathVectorT after_Vect2 = matr.row(g2); if ((before_Vect1 == after_Vect2) && (before_Vect2 == after_Vect1)) { - ASSERT_TRUE(true); + EXPECT_TRUE(true); } else { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } TEST(PIMathMatrixT_Test, fill) { - PIMathMatrixT matr; - PIMathMatrixT matrix1; - double g = 1.0; + PIMathMatrixT matr; + PIMathMatrixT matrix1; + Type g = 1.0; matr.fill(g); for (uint i = 0; i < cols; i++) { for (uint j = 0; j < rows; j++) { matrix1.element(j, i) = g; } } - ASSERT_TRUE(matr == matrix1); + EXPECT_TRUE(matr == matrix1); } TEST(PIMathMatrixT_Test, isSquareTrue) { - PIMathMatrixT matrix1; - ASSERT_TRUE(matrix1.isSquare()); + PIMathMatrixT matrix1; + EXPECT_TRUE(matrix1.isSquare()); } TEST(PIMathMatrixT_Test, isSquareFalse) { const uint new_Cols = 4; - PIMathMatrixT matrix2; - ASSERT_FALSE(matrix2.isSquare()); + PIMathMatrixT matrix2; + EXPECT_FALSE(matrix2.isSquare()); } TEST(PIMathMatrixT_Test, isIdentityTrue) { - auto matrix1 = PIMathMatrixT::identity(); - ASSERT_TRUE(matrix1.isIdentity()); + auto matrix1 = PIMathMatrixT::identity(); + EXPECT_TRUE(matrix1.isIdentity()); } TEST(PIMathMatrixT_Test, isIdentityFalse) { - auto matrix1 = PIMathMatrixT(2.5); - ASSERT_FALSE(matrix1.isIdentity()); + auto matrix1 = PIMathMatrixT(2.5); + EXPECT_FALSE(matrix1.isIdentity()); } TEST(PIMathMatrixT_Test, isNullTrue) { - PIMathMatrixT matrix1; - ASSERT_TRUE(matrix1.isNull()); + PIMathMatrixT matrix1; + EXPECT_TRUE(matrix1.isNull()); } TEST(PIMathMatrixT_Test, isNullFalse) { - auto matrix1 = PIMathMatrixT::identity(); - ASSERT_FALSE(matrix1.isNull()); + auto matrix1 = PIMathMatrixT::identity(); + EXPECT_FALSE(matrix1.isNull()); } TEST(PIMathMatrixT_Test, operator_Assignment) { - PIMathMatrixT matrix1; - auto matrix2 = PIMathMatrixT(6.72); + PIMathMatrixT matrix1; + auto matrix2 = PIMathMatrixT(6.72); matrix1 = matrix2; - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1, 6.72, rows)); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1, 6.72, rows)); } TEST(PIMathMatrixT_Test, operator_EqualTrue) { - PIMathMatrixT matrix1; - PIMathMatrixT matrix2; + PIMathMatrixT matrix1; + PIMathMatrixT matrix2; matrix1.element(0, 0) = 5.1; matrix1.element(0, 1) = 1.21; matrix1.element(1, 1) = 0.671; @@ -244,12 +245,12 @@ TEST(PIMathMatrixT_Test, operator_EqualTrue) { matrix2.element(0, 1) = 1.21; matrix2.element(1, 1) = 0.671; matrix2.element(1, 0) = 2.623; - ASSERT_TRUE(matrix1 == matrix2); + EXPECT_TRUE(matrix1 == matrix2); } TEST(PIMathMatrixT_Test, operator_EqualFalse) { - PIMathMatrixT matrix1; - PIMathMatrixT matrix2; + PIMathMatrixT matrix1; + PIMathMatrixT matrix2; matrix1.element(0, 0) = 5.1; matrix1.element(0, 1) = 1.21; matrix1.element(1, 1) = 0.671; @@ -258,12 +259,12 @@ TEST(PIMathMatrixT_Test, operator_EqualFalse) { matrix2.element(0, 1) = 1.21; matrix2.element(1, 1) = 665.671; matrix2.element(1, 0) = 2.623; - ASSERT_FALSE(matrix1 == matrix2); + EXPECT_FALSE(matrix1 == matrix2); } TEST(PIMathMatrixT_Test, operator_Not_EqualTrue) { - PIMathMatrixT matrix1; - PIMathMatrixT matrix2; + PIMathMatrixT matrix1; + PIMathMatrixT matrix2; matrix1.element(0, 0) = 5.1; matrix1.element(0, 1) = 1.21; matrix1.element(1, 1) = 0.671; @@ -272,12 +273,12 @@ TEST(PIMathMatrixT_Test, operator_Not_EqualTrue) { matrix2.element(0, 1) = 1.21; matrix2.element(1, 1) = 665.671; matrix2.element(1, 0) = 2.623; - ASSERT_TRUE(matrix1 != matrix2); + EXPECT_TRUE(matrix1 != matrix2); } TEST(PIMathMatrixT_Test, operator_Not_EqualFalse) { - PIMathMatrixT matrix1; - PIMathMatrixT matrix2; + PIMathMatrixT matrix1; + PIMathMatrixT matrix2; matrix1.element(0, 0) = 5.1; matrix1.element(0, 1) = 1.21; matrix1.element(1, 1) = 0.671; @@ -286,60 +287,60 @@ TEST(PIMathMatrixT_Test, operator_Not_EqualFalse) { matrix2.element(0, 1) = 1.21; matrix2.element(1, 1) = 0.671; matrix2.element(1, 0) = 2.623; - ASSERT_FALSE(matrix1 != matrix2); + EXPECT_FALSE(matrix1 != matrix2); } TEST(PIMathMatrixT_Test, operator_Addition_Assignment) { - auto matrix1 = PIMathMatrixT(6.72); - auto matrix2 = PIMathMatrixT(1.0); + auto matrix1 = PIMathMatrixT(6.72); + auto matrix2 = PIMathMatrixT(1.0); matrix1 += matrix2; - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1, 7.72, rows)); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1, 7.72, rows)); } TEST(PIMathMatrixT_Test, operator_Subtraction_Assignment) { - auto matrix1 = PIMathMatrixT(1.0); - auto matrix2 = PIMathMatrixT(6.72); + auto matrix1 = PIMathMatrixT(1.0); + auto matrix2 = PIMathMatrixT(6.72); matrix1 -= matrix2; - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1, -5.72, rows)); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1, -5.72, rows)); } TEST(PIMathMatrixT_Test, operator_Multiplication_Assignment) { - auto matrix1 = PIMathMatrixT(6.72); + auto matrix1 = PIMathMatrixT(6.72); matrix1 *= 2.0; - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1, 13.44, rows)); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1, 13.44, rows)); } TEST(PIMathMatrixT_Test, operator_Division_Assignment) { - auto matrix1 = PIMathMatrixT(6.72); + auto matrix1 = PIMathMatrixT(6.72); matrix1 /= 2.0; - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1, 3.36, rows)); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1, 3.36, rows)); } TEST(PIMathMatrixT_Test, operator_Addition) { - auto matrix1 = PIMathMatrixT(6.72); - auto matrix2 = PIMathMatrixT(8.28); - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1 + matrix2, 15.0, rows)); + auto matrix1 = PIMathMatrixT(6.72); + auto matrix2 = PIMathMatrixT(8.28); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1 + matrix2, 15.0, rows)); } TEST(PIMathMatrixT_Test, operator_Subtraction) { - auto matrix1 = PIMathMatrixT(6.0); - auto matrix2 = PIMathMatrixT(5.0); - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1 - matrix2, 1.0, rows)); + auto matrix1 = PIMathMatrixT(6.0); + auto matrix2 = PIMathMatrixT(5.0); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1 - matrix2, 1.0, rows)); } TEST(PIMathMatrixT_Test, operator_Multiplication) { - auto matrix1 = PIMathMatrixT(6.72); - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1 * 4.0, 26.88, rows)); + auto matrix1 = PIMathMatrixT(6.72); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1 * 4.0, 26.88, rows)); } TEST(PIMathMatrixT_Test, operator_Division) { - auto matrix1 = PIMathMatrixT(6.72); - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1 / 4.0, 1.68, rows)); + auto matrix1 = PIMathMatrixT(6.72); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1 / 4.0, 1.68, rows)); } TEST(PIMathMatrixT_Test, determinantIfSquare) { - double d; - double i = 59.0; - PIMathMatrixT matr; + Type d; + Type i = 59.0; + PIMathMatrixT matr; matr.element(0, 0) = 3; matr.element(0, 1) = 6; matr.element(0, 2) = 8; @@ -350,15 +351,15 @@ TEST(PIMathMatrixT_Test, determinantIfSquare) { matr.element(2, 1) = 2; matr.element(2, 2) = 5; d = matr.determinant(); - ASSERT_DOUBLE_EQ(i, d); + EXPECT_DOUBLE_EQ(std::abs(i - d), 0.); } TEST(PIMathMatrixT_Test, invert) { - PIMathMatrixT matrix1; - PIMathMatrixT matrix2; - PIMathMatrixT matrix3; - PIMathMatrixT matr; - double d1, d2; + PIMathMatrixT matrix1; + PIMathMatrixT matrix2; + PIMathMatrixT matrix3; + PIMathMatrixT matr; + Type d1, d2; matr.element(0, 0) = 3; matr.element(0, 1) = 6; matr.element(0, 2) = 8; @@ -374,15 +375,16 @@ TEST(PIMathMatrixT_Test, invert) { d2 = matrix2.determinant(); matrix3 = matrix1; matrix1.invert(); - ASSERT_TRUE((matrix1 == matrix3) && piCompare(d1, 1 / d2)); + EXPECT_EQ(matrix1, matrix3); + EXPECT_DOUBLE_EQ(std::abs(d1 - (1. / d2)), 0.); } TEST(PIMathMatrixT_Test, inverted) { - PIMathMatrixT matrix1; - PIMathMatrixT matrix2; - PIMathMatrixT matrix3; - PIMathMatrixT matr; - double d1, d2; + PIMathMatrixT matrix1; + PIMathMatrixT matrix2; + PIMathMatrixT matrix3; + PIMathMatrixT matr; + Type d1, d2; matrix1 = matr.identity(); matr.element(0, 0) = 3; matr.element(0, 1) = 6; @@ -397,13 +399,14 @@ TEST(PIMathMatrixT_Test, inverted) { d1 = matr.determinant(); d2 = matrix2.determinant(); matrix3 = matrix1.inverted(); - ASSERT_TRUE((matrix1 == matrix3) && (round((1 / d1) * 10000) / 10000 == round(d2 * 10000) / 10000)); + EXPECT_EQ(matrix1, matrix3); + EXPECT_DOUBLE_EQ(std::abs(d1 - (1. / d2)), 0.); } TEST(PIMathMatrixT_Test, toUpperTriangular) { - PIMathMatrixT matrix; - double d1, d2 = 1; - PIMathMatrixT matr; + PIMathMatrixT matrix; + Type d1, d2 = 1; + PIMathMatrixT matr; matr.element(0, 0) = 3; matr.element(0, 1) = 6; matr.element(0, 2) = 8; @@ -418,14 +421,14 @@ TEST(PIMathMatrixT_Test, toUpperTriangular) { for (uint i = 0; i < cols; i++) { d2 = d2 * matrix.at(i, i); } - ASSERT_DOUBLE_EQ(d1, d2); + EXPECT_LE(std::abs(d1 - d2), PIMATHVECTOR_ZERO_CMP); } TEST(PIMathMatrixT_Test, transposed) { - PIMathMatrixT matrix1; - PIMathMatrixT matrix2; - PIMathMatrixT matr; - double d1, d2; + PIMathMatrixT matrix1; + PIMathMatrixT matrix2; + PIMathMatrixT matr; + Type d1, d2; matr.element(0, 0) = 3; matr.element(0, 1) = 6; matr.element(0, 2) = 8; @@ -439,46 +442,46 @@ TEST(PIMathMatrixT_Test, transposed) { matrix1 = matr.transposed(); d2 = matrix1.determinant(); matrix2 = matrix1.transposed(); - ASSERT_TRUE((d1 == d2) && (matr == matrix2)); + EXPECT_TRUE((d1 == d2) && (matr == matrix2)); } TEST(PIMathMatrixT_Test, rotation_2x2) { - double angle = 1.0; - auto matrix = PIMathMatrixT<2u, 2u, double>::identity(); + Type angle = 1.0; + auto matrix = PIMathMatrixT<2u, 2u, Type>::identity(); matrix.rotate(angle); - double c = cos(angle); - double s = sin(angle); - ASSERT_TRUE((c == matrix.at(1u, 1u)) && (c == matrix.at(0u, 0u)) && (-s == matrix.at(0u, 1u)) && (s == matrix.at(1u, 0u))); + Type c = cos(angle); + Type s = sin(angle); + EXPECT_TRUE((c == matrix.at(1u, 1u)) && (c == matrix.at(0u, 0u)) && (-s == matrix.at(0u, 1u)) && (s == matrix.at(1u, 0u))); } TEST(PIMathMatrixT_Test, matrixMultiplication) { - auto matrix1 = PIMathMatrixT(1.5); - auto matrix2 = PIMathMatrixT(2.5); - ASSERT_TRUE(cmpSquareMatrixWithValue(matrix1 * matrix2, 11.25, 3)); + auto matrix1 = PIMathMatrixT(1.5); + auto matrix2 = PIMathMatrixT(2.5); + EXPECT_TRUE(cmpSquareMatrixWithValue(matrix1 * matrix2, 11.25, 3)); } TEST(PIMathMatrixT_Test, matrixAndVectorMultiplication) { - auto matrix1 = PIMathMatrixT(1.5); - auto vector = PIMathVectorT(2.5); + auto matrix1 = PIMathMatrixT(1.5); + auto vector = PIMathVectorT(2.5); for (uint i = 0; i < 2; i++) { if ((matrix1 * vector)[i] != 11.25) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } - ASSERT_TRUE(true); + EXPECT_TRUE(true); } TEST(PIMathMatrixT_Test, vectorAndMatrixMultiplication) { - auto matrix1 = PIMathMatrixT(1.5); - auto vector = PIMathVectorT(2.5); + auto matrix1 = PIMathMatrixT(1.5); + auto vector = PIMathVectorT(2.5); for (uint i = 0; i < 2; i++) { if ((vector * matrix1)[i] != 11.25) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } } TEST(PIMathMatrixT_Test, valAndMatrixMultiplication) { - auto matrix1 = PIMathMatrixT(1.5); - ASSERT_TRUE(cmpSquareMatrixWithValue(25.0 * matrix1, 37.5, 3)); + auto matrix1 = PIMathMatrixT(1.5); + EXPECT_TRUE(cmpSquareMatrixWithValue(Type(25.) * matrix1, 37.5, 3)); } From 28f347103694c9e47dc29983eea34b4b45af580a Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 16 Oct 2024 22:29:57 +0300 Subject: [PATCH 18/40] version 4.3.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index afb814b4..6167b409 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(PIP) set(PIP_MAJOR 4) -set(PIP_MINOR 2) +set(PIP_MINOR 3) set(PIP_REVISION 0) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) From 9dbd7210cb8f90e557f8edf45c1d78c609df3c54 Mon Sep 17 00:00:00 2001 From: peri4 Date: Sat, 19 Oct 2024 16:58:12 +0300 Subject: [PATCH 19/40] PILog documentation --- libs/main/application/pilog.cpp | 38 ++---------------------- libs/main/application/pilog.h | 51 ++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 49 deletions(-) diff --git a/libs/main/application/pilog.cpp b/libs/main/application/pilog.cpp index 27fece16..4daba584 100644 --- a/libs/main/application/pilog.cpp +++ b/libs/main/application/pilog.cpp @@ -30,44 +30,10 @@ //! \~english \section PILog_sec0 Synopsis //! \~russian \section PILog_sec0 Краткий обзор //! \~english -//! This class provide handy parsing of command-line arguments. First you should add -//! arguments to %PICLI with function \a addArgument(). Then you can check if there -//! is some argument in application command-line with function \a hasArgument(), -//! or obtain argument value by \a argumentValue(). +//! This class provides log with optional file and console output. //! //! \~russian -//! Этот класс предоставляет удобный механизм для разбора аргументов командной строки. -//! Сперва необходимо добавить аргументы в %PICLI с помощью методов \a addArgument(). -//! Далее можно проверять аргументы на наличие в командной строке методом \a hasArgument(), -//! а также получать их значения при помощи \a argumentValue(). -//! -//! \~english \section PICLI_sec1 Example -//! \~russian \section PICLI_sec1 Пример -//! \~\code -//! int main(int argc, char ** argv) { -//! PICLI cli(argc, argv); -//! cli.addArgument("console"); -//! cli.addArgument("debug"); -//! cli.addArgument("Value", "v", "value", true); -//! if (cli.hasArgument("console")) -//! piCout << "console active"; -//! if (cli.hasArgument("debug")) -//! piCout << "debug active"; -//! piCout << "Value =" << cli.argumentValue("Value"); -//! return 0; -//! } -//! \endcode -//! -//! \~english These executions are similar: -//! \~russian Эти вызовы будут идентичны: -//! -//! \~\code -//! a.out -cd -v 10 -//! a.out --value 10 -dc -//! a.out -c -v 10 -d -//! a.out --console -d -v 10 -//! a.out --debug -c --value 10 -//! \endcode +//! Этот класс предоставляет лог с опциональным выводом в файл и консоль. //! diff --git a/libs/main/application/pilog.h b/libs/main/application/pilog.h index c13f260f..15798a92 100644 --- a/libs/main/application/pilog.h +++ b/libs/main/application/pilog.h @@ -41,82 +41,107 @@ public: PILog(); ~PILog(); + //! \~english Message category + //! \~russian Категория сообщения enum class Level { - Error, - Warning, - Info, - Debug, + Error /** \~english Error \~russian Ошибка */, + Warning /** \~english Warning \~russian Предупреждение */, + Info /** \~english Information \~russian Информация */, + Debug /** \~english Debug \~russian Отладка */, }; + //! \~english Output channel + //! \~russian Канал вывода enum Output { - File = 0x1, - Console = 0x2, - All = 0xFF, + File /** \~english File \~russian Файл */ = 0x1, + Console /** \~english Console \~russian Консоль */ = 0x2, + All /** \~english All \~russian Все */ = 0xFF, }; - //! \~english Set output target \"o\" to \"on\". + //! \~english Set output channel \"o\" to \"on\". + //! \~russian Установить канал вывода \"o\" в \"on\". void setOutput(Output o, bool on = true) { output.setFlag(o, on); } //! \~english Returns prefix for filename. + //! \~russian Возвращает префикс имени файла. PIString logName() const { return log_name; } //! \~english Set prefix for filename. Should be set \b before \a setDir()! + //! \~russian Устанавливает префикс имени файла. Должен быть установлен \b до вызова \a setDir()! void setLogName(const PIString & n) { log_name = n; } //! \~english Returns if color for console output enabled. + //! \~russian Возвращает использовать ли цвет для вывода в консоль. bool colorConsole() const { return color_console; } //! \~english Set color for console output enabled. True by default. + //! \~russian Устанавливает использовать ли цвет для вывода в консоль. Включено по умолчанию. void setColorConsole(bool yes) { color_console = yes; } //! \~english Returns directory for log files. + //! \~russian Возвращает директорию для файлов. PIString dir() const { return log_dir; } //! \~english Set directory for log files. Should be set \b after \a setApplicationName()! + //! \~russian Устанавливает директорию для файлов. Должна быть установлена \b после вызова \a setApplicationName()! void setDir(const PIString & d); //! \~english Returns lifetime for file. + //! \~russian Возвращает время жизни файла. PISystemTime fileSplitTime() const { return split_time; } //! \~english Set lifetime for file. Each "st" interval new file will be created. + //! \~russian Устанавливает время жизни файла. Каждый интервал "st" будет создан новый файл. void setFileSplitTime(PISystemTime st) { split_time = st; } //! \~english Returns timestamp format for line. + //! \~russian Возвращает формат метки времени для строки. PIString timestampFormat() const { return timestamp_format; } //! \~english Set timestamp format for line. Default is "yyyy-MM-dd hh:mm:ss.zzz". + //! \~russian Устанавливает формат метки времени для строки. По умолчанию "yyyy-MM-dd hh:mm:ss.zzz". void setTimestampFormat(const PIString & f) { timestamp_format = f; } //! \~english Returns line format. + //! \~russian Возвращает формат строки. PIString lineFormat() const { return line_format; } //! \~english Set line format. "t" is timestamp, "c" is category and "m" is message. Default is "t - c: m". + //! \~russian Устанавливает формат строки. "t" - метка времени, "c" - категория и "m" - сообщение. По умолчанию "t - c: m". void setLineFormat(const PIString & f); //! \~english Returns maximum level. + //! \~russian Возвращает максимальную категорию. Level level() const { return max_level; } - //! \~english Set maximum level. All levels greater than \"l\" will be ignored. Default if \a Level::Debug. + //! \~english Set maximum level. All levels greater than \"l\" will be ignored. Default is \a Level::Debug. + //! \~russian Устанавливает максимальную категорию. Все сообщения с большей категорией, чем \"l\", будут игнорироваться. По умолчанию \a + //! Level::Debug. void setLevel(Level l); - //! \~english Returns PICout for Level::Error level. + //! \~english Returns \a PICout for \a Level::Error level. + //! \~russian Возвращает \a PICout для категории \a Level::Error. PICout error(PIObject * context = nullptr); - //! \~english Returns PICout for Level::Warning level. + //! \~english Returns \a PICout for \a Level::Warning level. + //! \~russian Возвращает \a PICout для категории \a Level::Warning. PICout warning(PIObject * context = nullptr); - //! \~english Returns PICout for Level::Info level. + //! \~english Returns \a PICout for \a Level::Info level. + //! \~russian Возвращает \a PICout для категории \a Level::Info. PICout info(PIObject * context = nullptr); - //! \~english Returns PICout for Level::Debug level. + //! \~english Returns \a PICout for \a Level::Debug level. + //! \~russian Возвращает \a PICout для категории \a Level::Debug. PICout debug(PIObject * context = nullptr); //! \~english Write all queued lines and stop. Also called in destructor. + //! \~russian Записывает все строки из очереди и останавливается. Также вызывается в деструкторе. void stop(); private: From 992f59904a626e5f9c9eb650c01202caad5b7529 Mon Sep 17 00:00:00 2001 From: peri4 Date: Sat, 19 Oct 2024 17:07:45 +0300 Subject: [PATCH 20/40] more documentation --- libs/main/application/piapplicationmodule.h | 1 + .../main/client_server/piclientservermodule.h | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 libs/main/client_server/piclientservermodule.h diff --git a/libs/main/application/piapplicationmodule.h b/libs/main/application/piapplicationmodule.h index 57d67c81..09c12990 100644 --- a/libs/main/application/piapplicationmodule.h +++ b/libs/main/application/piapplicationmodule.h @@ -52,6 +52,7 @@ #define PIAPPLICATIONMODULE_H #include "picli.h" +#include "pilog.h" #include "pisingleapplication.h" #include "pisystemmonitor.h" diff --git a/libs/main/client_server/piclientservermodule.h b/libs/main/client_server/piclientservermodule.h new file mode 100644 index 00000000..2f1af94e --- /dev/null +++ b/libs/main/client_server/piclientservermodule.h @@ -0,0 +1,57 @@ +/* + PIP - Platform Independent Primitives + Module includes + Andrey Bychkov work.a.b@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ +//! \defgroup ClientServer ClientServer +//! \~\brief +//! \~english TCP Client-Server +//! \~russian TCP Клиент-Сервер +//! +//! \~\details +//! \~english \section cmake_module_ClientServer Building with CMake +//! \~russian \section cmake_module_ClientServer Сборка с использованием CMake +//! +//! \~\code +//! find_package(PIP REQUIRED) +//! target_link_libraries([target] PIP::ClientServer) +//! \endcode +//! +//! \~english \par Common +//! \~russian \par Общее +//! +//! \~english +//! These files provides server with clients dispatching for server-side and client for client-side. +//! +//! \~russian +//! Эти файлы предоставляют сервер с диспетчеризацией клиентов для серверной стороны и клиента для клиентской стороны. +//! +//! \~\authors +//! \~english +//! Ivan Pelipenko peri4ko@yandex.ru; +//! Andrey Bychkov work.a.b@yandex.ru; +//! \~russian +//! Иван Пелипенко peri4ko@yandex.ru; +//! Андрей Бычков work.a.b@yandex.ru; +//! + +#ifndef PICLIENTSERVERMODULE_H +#define PICLIENTSERVERMODULE_H + +#include "piclientserver_client.h" +#include "piclientserver_server.h" + +#endif From 7209eec0127bf839f83aa271b65ec81375d0e42f Mon Sep 17 00:00:00 2001 From: peri4 Date: Sat, 19 Oct 2024 17:08:15 +0300 Subject: [PATCH 21/40] author --- libs/main/client_server/piclientservermodule.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/main/client_server/piclientservermodule.h b/libs/main/client_server/piclientservermodule.h index 2f1af94e..fc3f100e 100644 --- a/libs/main/client_server/piclientservermodule.h +++ b/libs/main/client_server/piclientservermodule.h @@ -1,7 +1,7 @@ /* PIP - Platform Independent Primitives Module includes - Andrey Bychkov work.a.b@yandex.ru + Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by From 7cd2f7a310bf339912a76122704e35a5946672be Mon Sep 17 00:00:00 2001 From: peri4 Date: Mon, 21 Oct 2024 13:47:28 +0300 Subject: [PATCH 22/40] PIBinaryStream supports PISet --- libs/main/containers/piset.h | 4 +++ libs/main/serialization/pibinarystream.h | 41 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/libs/main/containers/piset.h b/libs/main/containers/piset.h index 431f41cf..3b29eac0 100644 --- a/libs/main/containers/piset.h +++ b/libs/main/containers/piset.h @@ -37,6 +37,10 @@ template class PISet: public PIMap { typedef PIMap _CSet; + template + friend PIBinaryStream

& operator<<(PIBinaryStream

& s, const PISet & v); + template + friend PIBinaryStream

& operator>>(PIBinaryStream

& s, PISet & v); public: //! Contructs an empty set diff --git a/libs/main/serialization/pibinarystream.h b/libs/main/serialization/pibinarystream.h index ab294417..bab6a8b0 100644 --- a/libs/main/serialization/pibinarystream.h +++ b/libs/main/serialization/pibinarystream.h @@ -29,6 +29,7 @@ #include "pibitarray.h" #include "pimap.h" #include "pimemoryblock.h" +#include "piset.h" #include "pivector2d.h" #define PIP_BINARY_STREAM @@ -655,6 +656,46 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIMap & v) } +//! \~english Store operator +//! \~russian Оператор сохранения +template +inline PIBinaryStream

& operator<<(PIBinaryStream

& s, const PISet & v) { + s.binaryStreamAppend((int)v.pim_index.size_s()); + for (uint i = 0; i < v.size(); ++i) { + s.binaryStreamAppend((int)v.pim_index[i].index); + s << v.pim_index[i].key; + } + return s; +} + + +//! \~english Restore operator +//! \~russian Оператор извлечения +template +inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PISet & v) { + int sz = s.binaryStreamTakeInt(); + if (s.wasReadError()) { + fprintf(stderr, "error with PISet<%s>\n", __PIP_TYPENAME__(Key)); + v.clear(); + return s; + } + v.pim_index.resize(sz); + v.pim_content.resize(sz, 0); + int ind = 0; + for (int i = 0; i < sz; ++i) { + ind = s.binaryStreamTakeInt(); + s >> v.pim_index[i].key; + if (s.wasReadError()) { + fprintf(stderr, "error with PISet<%s>\n", __PIP_TYPENAME__(Key)); + v.clear(); + return s; + } + v.pim_index[i].index = ind; + } + return s; +} + + // non-defined complex types From 9f1d23ad8ef5ee40ad21fb029e7ddb4bf59929f0 Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 23 Oct 2024 19:11:46 +0300 Subject: [PATCH 23/40] version 4.3.1 PICodeParser now works with multi-line macros --- CMakeLists.txt | 2 +- libs/main/code/picodeparser.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6167b409..8872a989 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(PIP) set(PIP_MAJOR 4) set(PIP_MINOR 3) -set(PIP_REVISION 0) +set(PIP_REVISION 1) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) diff --git a/libs/main/code/picodeparser.cpp b/libs/main/code/picodeparser.cpp index 67e2ccb4..a1c8f58a 100644 --- a/libs/main/code/picodeparser.cpp +++ b/libs/main/code/picodeparser.cpp @@ -343,6 +343,7 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) { static const PIString s_typedef = PIStringAscii("typedef"); static const PIString s_namespace = PIStringAscii("namespace"); static const PIString s_template = PIStringAscii("template"); + static const PIString s_L = PIStringAscii("$L"); bool mlc = false, cc = false; int mls = 0, ole = -1, /*ccs = 0,*/ end = 0; @@ -365,8 +366,9 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) { } if (i > 0) if (c == '\\' && fc[i - 1].toAscii() != '\\') { - fc.cutMid(i, 2); - --i; + fc.cutMid(i, 1); + fc.replace(i, 1, s_L); + ++i; continue; } if (cc) continue; @@ -390,6 +392,7 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) { } } pfc = procMacros(fc); + pfc.removeAll(s_L); if (main) return true; @@ -1248,6 +1251,7 @@ PIString PICodeParser::procMacros(PIString fc) { static const PIString s_elif = PIStringAscii("elif"); static const PIString s_else = PIStringAscii("else"); static const PIString s_endif = PIStringAscii("endif"); + static const PIString s_L = PIStringAscii("$L"); if (fc.isEmpty()) return PIString(); int ifcnt = 0; bool grab = false, skip = false, cond_ok = false; @@ -1337,7 +1341,9 @@ bool PICodeParser::parseDirective(PIString d) { static const PIString s_define = PIStringAscii("define"); static const PIString s_undef = PIStringAscii("undef"); static const PIString s_PIMETA = PIStringAscii("PIMETA"); + static const PIString s_L = PIStringAscii("$L"); if (d.isEmpty()) return true; + d.replaceAll(s_L, '\n'); PIString dname = d.takeCWord(); // piCout << "parseDirective" << d; if (dname == s_include) { From df75efe8810c6a7d0dda9b343ff4c4f2c58ccf08 Mon Sep 17 00:00:00 2001 From: peri4 Date: Sat, 2 Nov 2024 14:15:09 +0300 Subject: [PATCH 24/40] version 4.3.2 fix PIKbdListener wheel on Windows --- CMakeLists.txt | 2 +- libs/main/application/pilog.h | 4 ++-- libs/main/client_server/piclientserver_server.h | 2 +- libs/main/console/pikbdlistener.cpp | 3 +-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8872a989..6264bba8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(PIP) set(PIP_MAJOR 4) set(PIP_MINOR 3) -set(PIP_REVISION 1) +set(PIP_REVISION 2) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) diff --git a/libs/main/application/pilog.h b/libs/main/application/pilog.h index 15798a92..ceb6c60c 100644 --- a/libs/main/application/pilog.h +++ b/libs/main/application/pilog.h @@ -83,8 +83,8 @@ public: //! \~russian Возвращает директорию для файлов. PIString dir() const { return log_dir; } - //! \~english Set directory for log files. Should be set \b after \a setApplicationName()! - //! \~russian Устанавливает директорию для файлов. Должна быть установлена \b после вызова \a setApplicationName()! + //! \~english Set directory for log files. Should be set \b after \a setLogName()! + //! \~russian Устанавливает директорию для файлов. Должна быть установлена \b после вызова \a setLogName()! void setDir(const PIString & d); diff --git a/libs/main/client_server/piclientserver_server.h b/libs/main/client_server/piclientserver_server.h index 784fa125..2827332b 100644 --- a/libs/main/client_server/piclientserver_server.h +++ b/libs/main/client_server/piclientserver_server.h @@ -49,6 +49,7 @@ public: void listen(PINetworkAddress addr); void listenAll(ushort port) { listen({0, port}); } + void stopServer(); void closeAll(); int getMaxClients() const { return max_clients; } @@ -61,7 +62,6 @@ public: Config & configuration() { return config; } private: - void stopServer(); void newClient(ServerClient * c); void clientDisconnected(ServerClient * c); diff --git a/libs/main/console/pikbdlistener.cpp b/libs/main/console/pikbdlistener.cpp index 9e874a64..e5034f57 100644 --- a/libs/main/console/pikbdlistener.cpp +++ b/libs/main/console/pikbdlistener.cpp @@ -360,8 +360,7 @@ void PIKbdListener::readKeyboard() { if (mer.dwEventFlags & MOUSE_WHEELED) { memcpy((void *)(&we), (const void *)(&me), sizeof(me)); we.action = MouseWheel; - we.direction = short((mer.dwButtonState >> 8) & 0xFFFF) > 0; - // piCout << "wheel" << we.direction; + we.direction = short((mer.dwButtonState >> 16) & 0xFFFF) > 0; wheelEvent(we, kbddata_); break; } else { From 9a928f6feb1f095e253ef3a4b5d9eee0bdaf009f Mon Sep 17 00:00:00 2001 From: peri4 Date: Sat, 2 Nov 2024 18:43:30 +0300 Subject: [PATCH 25/40] add PITranslator begin localization "ru" --- libs/main/application/piapplicationmodule.h | 1 + libs/main/application/pitranslator.cpp | 101 +++++ libs/main/application/pitranslator.h | 64 ++++ libs/main/application/tr/pip_all.h | 16 + libs/main/application/tr/pip_ru.h | 25 ++ libs/main/core/piinit.cpp | 8 + libs/main/core/piinit.h | 1 + libs/main/io_utils/pidiagnostics.cpp | 22 +- libs/main/text/pistring.cpp | 56 ++- main.cpp | 403 +++----------------- utils/resources_compiler/generator.cpp | 6 +- utils/resources_compiler/main.cpp | 6 +- utils/resources_compiler/parser.cpp | 2 +- utils/resources_compiler/parser.h | 1 - 14 files changed, 305 insertions(+), 407 deletions(-) create mode 100644 libs/main/application/pitranslator.cpp create mode 100644 libs/main/application/pitranslator.h create mode 100644 libs/main/application/tr/pip_all.h create mode 100644 libs/main/application/tr/pip_ru.h diff --git a/libs/main/application/piapplicationmodule.h b/libs/main/application/piapplicationmodule.h index 09c12990..024b4061 100644 --- a/libs/main/application/piapplicationmodule.h +++ b/libs/main/application/piapplicationmodule.h @@ -55,5 +55,6 @@ #include "pilog.h" #include "pisingleapplication.h" #include "pisystemmonitor.h" +#include "pitranslator.h" #endif diff --git a/libs/main/application/pitranslator.cpp b/libs/main/application/pitranslator.cpp new file mode 100644 index 00000000..874c9a45 --- /dev/null +++ b/libs/main/application/pitranslator.cpp @@ -0,0 +1,101 @@ +/* + PIP - Platform Independent Primitives + Translation support + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "pitranslator.h" + +#include "pivaluetree_conversions.h" +#include "tr/pip_all.h" + + +//! \class PITranslator pitranslator.h +//! \details +//! \~english \section PITranslator_sec0 Synopsis +//! \~russian \section PITranslator_sec0 Краткий обзор +//! \~english +//! +//! \~russian +//! + + +PIString PITranslator::tr(const PIString & in, const PIString & context) { + auto s = instance(); + auto c = s->contexts.value(context.hash()); + if (!c) return in; + return c->strings.value(in.hash(), in); +} + + +void PITranslator::clear() { + instance()->clearInternal(); +} + + +void PITranslator::loadLang(const PIString & short_lang) { + auto s = instance(); + auto vt = PIValueTreeConversions::fromText(getBuiltinConfig()); + auto lang = vt.child(short_lang.toLowerCase().trim()); + for (const auto & cn: lang.children()) { + auto c = s->createContextInternal(cn.name()); + for (const auto & s: cn.children()) + c->add(s.name(), s.value().toString()); + } +} + + +void PITranslator::loadConfig(const PIString & content) { + auto s = instance(); + auto lang = PIValueTreeConversions::fromText(content); + for (const auto & cn: lang.children()) { + auto c = s->createContextInternal(cn.name()); + for (const auto & s: cn.children()) + c->add(s.name(), s.value().toString()); + } +} + + +PITranslator::PITranslator() {} + + +PITranslator::~PITranslator() { + clearInternal(); +} + + +PITranslator * PITranslator::instance() { + static PITranslator ret; + return &ret; +} + + +void PITranslator::clearInternal() { + piDeleteAll(contexts.values()); + contexts.clear(); +} + + +PITranslator::Context * PITranslator::createContextInternal(const PIString & context) { + auto & ret(contexts[context.hash()]); + if (!ret) ret = new Context(); + return ret; +} + + +void PITranslator::Context::add(const PIString & in, const PIString & out) { + strings[in.hash()] = out; +} diff --git a/libs/main/application/pitranslator.h b/libs/main/application/pitranslator.h new file mode 100644 index 00000000..60eea97e --- /dev/null +++ b/libs/main/application/pitranslator.h @@ -0,0 +1,64 @@ +/*! \file pitranslator.h + * \ingroup Application + * \~\brief + * \~english Translation support + * \~russian Поддержка перевода + */ +/* + PIP - Platform Independent Primitives + Translation support + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef pitranslator_H +#define pitranslator_H + +#include "pifile.h" +#include "piiostream.h" +#include "pithread.h" + +//! \ingroup Application +//! \~\brief +//! \~english Translation support +//! \~russian Поддержка перевода +class PIP_EXPORT PITranslator { +public: + static PIString tr(const PIString & in, const PIString & context = {}); + static PIString tr(const char * in, const PIString & context = {}) { return tr(PIString(in), context); } + + static void clear(); + static void loadLang(const PIString & short_lang); + static void loadConfig(const PIString & content); + +private: + PITranslator(); + ~PITranslator(); + NO_COPY_CLASS(PITranslator) + + struct Context { + void add(const PIString & in, const PIString & out); + PIMap strings; + }; + + static PITranslator * instance(); + + void clearInternal(); + Context * createContextInternal(const PIString & context); + + PIMap contexts; +}; + +#endif diff --git a/libs/main/application/tr/pip_all.h b/libs/main/application/tr/pip_all.h new file mode 100644 index 00000000..476ffadb --- /dev/null +++ b/libs/main/application/tr/pip_all.h @@ -0,0 +1,16 @@ +#ifndef pitr_pip_all_H +#define pitr_pip_all_H + +#include "pip_ru.h" +#include "pistring.h" + +PIString getBuiltinConfig() { + // clang-format off + static const PIString ret = + PIString::fromUTF8(config_ru) + ; + // clang-format on + return ret; +} + +#endif diff --git a/libs/main/application/tr/pip_ru.h b/libs/main/application/tr/pip_ru.h new file mode 100644 index 00000000..acc1ae56 --- /dev/null +++ b/libs/main/application/tr/pip_ru.h @@ -0,0 +1,25 @@ +constexpr char config_ru[] = "\ +[ru.PIString] \n\ +B = Б \n\ +KiB = KиБ \n\ +MiB = MиБ \n\ +GiB = ГиБ \n\ +TiB = ТиБ \n\ +PiB = ПиБ \n\ +EiB = ЭиБ \n\ +ZiB = ЗиБ \n\ +YiB = ЙиБ \n\ +\n\ +[ru.PIDiag] \n\ +/s = /сек \n\ +\n\ +[ru.PITime] \n\ +Hz = Гц \n\ +KHz = КГц \n\ +kHz = кГц \n\ +MHz = МГц \n\ +GHz = ГГц \n\ +THz = ТГц \n\ +PHz = ПГц \n\ +\n\ +"; diff --git a/libs/main/core/piinit.cpp b/libs/main/core/piinit.cpp index dd8fcb53..72a65808 100644 --- a/libs/main/core/piinit.cpp +++ b/libs/main/core/piinit.cpp @@ -340,6 +340,13 @@ bool PIInit::isBuildOptionEnabled(PIInit::BuildOption o) { true; # else false; +# endif + case boConsole: + return +# ifdef PIP_CONSOLE + true; +# else + false; # endif default: return false; } @@ -357,6 +364,7 @@ PIStringList PIInit::buildOptions() { if (isBuildOptionEnabled(boCompress)) ret << "Compress"; if (isBuildOptionEnabled(boOpenCL)) ret << "OpenCL"; if (isBuildOptionEnabled(boCloud)) ret << "Cloud"; + if (isBuildOptionEnabled(boConsole)) ret << "Console"; return ret; } diff --git a/libs/main/core/piinit.h b/libs/main/core/piinit.h index b1ba9c2e..87e31489 100644 --- a/libs/main/core/piinit.h +++ b/libs/main/core/piinit.h @@ -67,6 +67,7 @@ public: boCompress /*! \~english Zlib compression support \~russian Поддержка сжатия Zlib */ = 0x80, boOpenCL /*! \~english OpenCL support \~russian Поддержка OpenCL */ = 0x100, boCloud /*! \~english PICloud transport support \~russian Поддержка облачного транспорта PICloud */ = 0x200, + boConsole /*! \~english Console graphics support \~russian Поддержка графики в консоли */ = 0x400, }; static PIInit * instance() { return __PIInit_Initializer__::__instance__; } diff --git a/libs/main/io_utils/pidiagnostics.cpp b/libs/main/io_utils/pidiagnostics.cpp index 9cfe5964..dba39105 100644 --- a/libs/main/io_utils/pidiagnostics.cpp +++ b/libs/main/io_utils/pidiagnostics.cpp @@ -20,6 +20,7 @@ #include "pidiagnostics.h" #include "piliterals_time.h" +#include "pitranslator.h" /** \class PIDiagnostics @@ -40,7 +41,7 @@ PIDiagnostics::State::State() { - receive_speed = send_speed = PIString::readableSize(0) + "/s"; + receive_speed = send_speed = PIString::readableSize(0) + PITranslator::tr("/s", "PIDiag"); } @@ -152,14 +153,15 @@ void PIDiagnostics::sended(int size) { void PIDiagnostics::tick(int) { mutex_state.lock(); + auto speed_sec = PITranslator::tr("/s", "PIDiag"); // piCoutObj << "lock"; - int tcnt_recv = 0; - int tcnt_send = 0; - Entry send = calcHistory(history_send, tcnt_send); - Entry recv = calcHistory(history_rec, tcnt_recv); - float itr = disconn_.toSeconds() * (float(tcnt_recv) / history_rec.size()); - float its = disconn_.toSeconds() * (float(tcnt_send) / history_send.size()); - float hz = 1. / interval().toSeconds(); + int tcnt_recv = 0; + int tcnt_send = 0; + Entry send = calcHistory(history_send, tcnt_send); + Entry recv = calcHistory(history_rec, tcnt_recv); + float itr = disconn_.toSeconds() * (float(tcnt_recv) / history_rec.size()); + float its = disconn_.toSeconds() * (float(tcnt_send) / history_send.size()); + float hz = 1. / interval().toSeconds(); if (tcnt_recv == 0) { cur_state.integral_freq = cur_state.immediate_freq = 0; cur_state.received_packets_per_sec = cur_state.received_bytes_per_sec = 0; @@ -176,8 +178,8 @@ void PIDiagnostics::tick(int) { cur_state.sended_bytes_per_sec = ullong(double(send.bytes_ok) / its); } // piCoutObj << "tick" << recv.cnt_ok << send.cnt_ok; - cur_state.receive_speed = PIString::readableSize(cur_state.received_bytes_per_sec) + "/s"; - cur_state.send_speed = PIString::readableSize(cur_state.sended_bytes_per_sec) + "/s"; + cur_state.receive_speed = PIString::readableSize(cur_state.received_bytes_per_sec) + speed_sec; + cur_state.send_speed = PIString::readableSize(cur_state.sended_bytes_per_sec) + speed_sec; int arc = recv.cnt_ok + recv.cnt_fail; float good_percents = 0.f; if (arc > 0) good_percents = (float)recv.cnt_ok / arc * 100.f; diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index 8707b0d5..dc719ff6 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -23,6 +23,7 @@ #include "piliterals.h" #include "pimathbase.h" #include "pistringlist.h" +#include "pitranslator.h" #ifdef PIP_ICU # define U_NOEXCEPT # include "unicode/ucnv.h" @@ -1732,43 +1733,32 @@ ldouble PIString::toLDouble() const { //! piCout << s; // 47.6 GiB //! \endcode PIString & PIString::setReadableSize(llong bytes) { + static const PIString tr_c = "PIString"_a; clear(); if (bytes < 1024) { - *this += (PIString::fromNumber(bytes) + " B"_a); + *this += (PIString::fromNumber(bytes) + " "_a + PITranslator::tr("B", tr_c)); return *this; } - double fres = bytes / 1024.; - llong res = bytes / 1024; - fres -= res; - if (res < 1024) { - *this += (PIString::fromNumber(res) + "."_a + PIString::fromNumber(llong(fres * 10)).left(1) + " KiB"_a); - return *this; - } - fres = res / 1024.; - res /= 1024; - fres -= res; - if (res < 1024) { - *this += (PIString::fromNumber(res) + "."_a + PIString::fromNumber(llong(fres * 10)).left(1) + " MiB"_a); - return *this; - } - fres = res / 1024.; - res /= 1024; - fres -= res; - if (res < 1024) { - *this += (PIString::fromNumber(res) + "."_a + PIString::fromNumber(llong(fres * 10)).left(1) + " GiB"_a); - return *this; - } - fres = res / 1024.; - res /= 1024; - fres -= res; - if (res < 1024) { - *this += (PIString::fromNumber(res) + "."_a + PIString::fromNumber(llong(fres * 10)).left(1) + " TiB"_a); - return *this; - } - fres = res / 1024.; - res /= 1024; - fres -= res; - *this += (PIString::fromNumber(res) + "."_a + PIString::fromNumber(llong(fres * 10)).left(1) + " PiB"_a); + double fres = bytes; + llong res = bytes; + auto checkRange = [this, &fres, &res](const PIString & unit, bool last = false) { + fres = res / 1024.; + res /= 1024; + fres -= res; + if (res < 1024 || last) { + *this += (PIString::fromNumber(res) + "."_a + PIString::fromNumber(llong(fres * 10)).left(1) + " "_a + unit); + return true; + } + return false; + }; + if (checkRange(PITranslator::tr("KiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("MiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("GiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("TiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("PiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("EiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("ZiB", tr_c))) return *this; + checkRange(PITranslator::tr("YiB", tr_c), true); return *this; } diff --git a/main.cpp b/main.cpp index 21450986..a3a8adb5 100644 --- a/main.cpp +++ b/main.cpp @@ -8,375 +8,66 @@ #include "pilog.h" #include "pimathbase.h" #include "pip.h" +#include "pitranslator.h" #include "pivaluetree_conversions.h" using namespace PICoutManipulators; -enum MyEvent { - meVoid, - meInt, - meString, - meIntString, -}; - -PIKbdListener kbd; +template +void foo() { + static_assert(std::is_arithmetic::value || is_complex::value, "Type must be arithmetic"); +} -class MyServerClient: public PIClientServer::ServerClient { -public: - ~MyServerClient() { send_thread.stopAndWait(); } - -protected: - void readed(PIByteArray data) override { piCout << "readed" << (data.size()); } - // void aboutDelete() override { piCout << "aboutDelete"; } - // void disconnected() override { piCout << "disconnected"; } - void connected() override { - // piCout << "connected"; - send_thread.start( - [this] { - // write((PIString::fromNumber(++counter)).toUTF8()); - PIByteArray ba(64_KiB); - write(ba); - }, - 2_Hz); - } - PIThread send_thread; - int counter = 0; -}; - - -class MyClient: public PIClientServer::Client { -public: - ~MyClient() { send_thread.stopAndWait(); } - -protected: - void readed(PIByteArray data) override { piCout << "readed" << (data.size()); } - void disconnected() override { piCout << "disconnected"; } - void connected() override { - piCout << "connected"; - send_thread.start( - [this] { - // write((PIString::fromNumber(++counter)).toUTF8()); - write(PIByteArray(64_KiB)); - }, - 2_Hz); - } - PIThread send_thread; - int counter = 0; -}; - -#include int main(int argc, char * argv[]) { - /*piCout << PIString("hello locale").entries("lo"); // 2 - piCout << PIString("hello locale").entries("lo lo"); // 1 - piCout << ("3hello4"_u8); - piCout << ("3hello4"_u8).entries("hello1"); - piCout << ("3hello4"_u8).entries("hello"); - piCout << ("3hello4"_u8).entries("l"); - piCout << ("3hello4"_u8).entries("ello"); - piCout << ("3hello4"_u8).entries("hell"); - piCout << ("2hellohello1"_u8); - piCout << ("2hellohello1"_u8).entries("hello1"); - piCout << ("2hellohello1"_u8).entries("hello"); - piCout << ("2hellohello1"_u8).entries("l"); - piCout << ("2hellohello1"_u8).entries("ello"); - piCout << ("2hellohello1"_u8).entries("hell"); - return 0;*/ + PITranslator::loadLang("ru"); + piCout << PIString::readableSize(50_KiB); + piCout << PIString::readableSize(1_GB); + PITranslator::clear(); + piCout << PIString::readableSize(50_KiB); + piCout << PIString::readableSize(1_GB); + // PICodeParser parser; + // parser.parseFile("cmg_test.h"); + /*for (auto m: parser.macros) { + piCout << ""; + piCout << m.name << m.args << m.value; + piCout << m.expand({"hello"}); + } +*/ - PILog log; - log.setColorConsole(false); - log.setOutput(PILog::File, false); - log.setLogName("test"); - log.setDir("logs"); - // log.setTimestampFormat("hh-mm-ss"); - // log.setLineFormat("[c] m (t)"); - log.start(); - - // log.enqueue("debug msg"); - // log.enqueue("warn msg with ${c}", PILog::Category::Warning); - // log.enqueue("ERROR${m}${t}", PILog::Category::Error); - log.setLevel(PILog::Level::Info); - - log.debug() << "some msg"; - piMSleep(50); - log.info() << "information"; - piMSleep(50); - log.warning() << "another!"; - piMSleep(50); - log.error(&log) << "critical!"; - - // log.stop(); + /*PISet set; + piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set)); + set << 1 << 2 << 3; + piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set)); + set << 1 << -2 << 50 << -100; + piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set));*/ return 0; - /*PIPeer p("123"); - piCout << "start ..."; - p.start(); - piCout << "start ok"; + std::numeric_limits::epsilon(); + using cmlp = complexf; + PIMathMatrixT<3, 3, double> v0; + PIMathMatrixT<3, 3, cmlp> v1; + v0[0][1] = 1; + v0[1][1] = 2; + v0[2][1] = 3; + v1[0][1] = cmlp(1., 0); + v1[1][1] = cmlp(2., -1); + v1[2][1] = cmlp(3., 2); + piCout << v0 << v1; + piCout << (v0 * 2.) << (v1 * cmlp(2., 2.)); + piCout << (v0 + 2.) << (v1 + cmlp(2.)); + piCout << (v0 - 2.) << (v1 - cmlp(2.)); + piCout << (v0 / 2.) << (v1 / cmlp(2., 1.)); + // piCout << (v0.length()) << (v1.length()); + // piCout << (v0.lengthSqr()) << (v1.lengthSqr()); + // piCout << (v0.manhattanLength()) << (v1.manhattanLength()); - piSleep(1.); - - piCout << "stop ..."; - p.stopAndWait(); - piCout << "stop ok"; - - piSleep(1.); - - piCout << "exit"; + /*foo(); + foo(); + foo(); + foo(); return 0;*/ - if (argc > 1) { - PIINTROSPECTION_START(server); - } else { - PIINTROSPECTION_START(client); - } - - kbd.enableExitCapture(); - - - PIClientServer::Server * s = nullptr; - PIThread * s_thread = new PIThread(); - PIVector cv; - - if (argc > 1) { - piCout << "Server"; - s = new PIClientServer::Server(); - s->setClientFactory([] { return new MyServerClient(); }); - s->configuration().enableSymmetricEncryption("1122334455667788"_hex); - s->listenAll(12345); - s_thread->start( - [s] { - piCout << "*** clients" << s->clientsCount(); - int i = 0; - s->forEachClient([&i](PIClientServer::ServerClient * c) { - // piCout << "client" << ++i << c; - c->write(PIByteArray(1_KiB)); - // c->close(); - // piMSleep(200); - }); - }, - 1._Hz); - } else { - piCout << "Client"; - piForTimes(2) { - piMSleep(25); - auto c = new MyClient(); - c->configuration().enableSymmetricEncryption("1122334455667788"_hex); - c->connect(PINetworkAddress::resolve("127.0.0.1", 12345)); - cv << c; - } - } - - WAIT_FOR_EXIT; - - s_thread->stopAndWait(); - - piDeleteSafety(s); - piDeleteAllAndClear(cv); - - PIINTROSPECTION_STOP - - return 0; - /*PIPackedTCP * tcp_s = - PIIODevice::createFromFullPath("ptcp://s::8000")->cast(); // new PIPackedTCP(PIPackedTCP::Server, {"0.0.0.0:8000"}); - PIPackedTCP * tcp_c = PIIODevice::createFromFullPath("ptcp://c:127.0.0.1:8000") - ->cast(); // new PIPackedTCP(PIPackedTCP::Client, {"127.0.0.1:8000"}); - piCout << tcp_s << tcp_c; - // CONNECTL(&tcp_s, opened, []() { piCout << "Srv opened"; }); - // CONNECTL(&tcp_c, opened, []() { piCout << "Cli opened"; }); - // CONNECTL(&tcp_s, closed, []() { piCout << "Srv closed"; }); - // CONNECTL(&tcp_c, closed, []() { piCout << "Cli closed"; }); - CONNECTL(tcp_s, connected, []() { piCout << "Srv conn"; }); - CONNECTL(tcp_c, connected, []() { piCout << "Cli conn"; }); - CONNECTL(tcp_s, disconnected, []() { piCout << "Srv disconn"; }); - CONNECTL(tcp_c, disconnected, []() { piCout << "Cli disconn"; }); - CONNECTL(tcp_s, threadedReadEvent, [](const uchar * readed, ssize_t size) { - PIByteArray d(readed, size); - piCout << "Srv readed" << d; - }); - CONNECTL(tcp_c, threadedReadEvent, [](const uchar * readed, ssize_t size) { - PIByteArray d(readed, size); - piCout << "Cli readed" << d; - }); - // tcp_s->open(); - // tcp_c->open(); - tcp_s->startThreadedRead(); - tcp_c->startThreadedRead(); - piForTimes(5) { - piCout << "\n1"; - piMSleep(200); - piCout << "2"; - // tcp_c->close(); - piCout << "2+"; - // tcp_s->close(); - piCout << "3"; - // tcp_c->open(); - piCout << "3+"; - // tcp_s->open(); - piCout << "4"; - piMSleep(500); - // piCout << "5"; - tcp_c->write("1234567890"_a.toByteArray()); - // piCout << "6"; - tcp_s->write("1234567890"_a.toByteArray()); - // piCout << "7"; - piMSleep(200); - } - // piCout << &tcp_s; - // piCout << tcp_s->path(); - // piCout << tcp_s->constructFullPath(); - delete tcp_s; - delete tcp_c; - return 0;*/ - /*PITimer timer(tfunc); - // timer.addDelimiter(2); - timer.addDelimiter(40, tfunc4); - - tm.reset(); - timer.start(0.5_Hz); - piMSleep(2200); - piCout << tm.elapsed_m() << cnt; - - timer.stop(); - timer.waitForFinish(); - return 0;*/ - - - bool posted; - PIStateMachine * root = new PIStateMachine("Machine"); - root->setOnFinish([] { piCout << "finish"; }); - - PIStateLambda * s1 = new PIStateLambda([] { piCout << "+ enter s1"; }, [] { piCout << "- exit s1"; }, "s1"); - PIStateLambda * s2 = new PIStateLambda([] { piCout << "+ enter s2"; }, [] { piCout << "- exit s2"; }, "s2"); - PIStateLambda * s3 = new PIStateLambda([] { piCout << "+ enter s3"; }, [] { piCout << "- exit s3"; }, "s3"); - PIStateLambda * s4 = new PIStateLambda([] { piCout << "+ enter s4"; }, [] { piCout << "- exit s4"; }, "s4"); - PIStateLambda * s11 = new PIStateLambda([] { piCout << " + enter s11"; }, [] { piCout << " - exit s11"; }, "s11"); - PIStateLambda * s12 = new PIStateLambda([] { piCout << " + enter s12"; }, [] { piCout << " - exit s12"; }, "s12"); - PIStateLambda * s13 = new PIStateLambda([] { piCout << " + enter s13"; }, [] { piCout << " - exit s13"; }, "s13"); - PIStateLambda * s21 = new PIStateLambda([] { piCout << " + enter s21"; }, [] { piCout << " - exit s21"; }, "s21"); - PIStateLambda * s22 = new PIStateLambda([] { piCout << " + enter s22"; }, [] { piCout << " - exit s22"; }, "s22"); - PIStateLambda * s23 = new PIStateLambda([] { piCout << " + enter s23"; }, [] { piCout << " - exit s23"; }, "s23"); - PIStateLambda * s211 = new PIStateLambda([] { piCout << " + enter s211"; }, [] { piCout << " - exit s211"; }, "s211"); - PIStateLambda * s212 = new PIStateLambda([] { piCout << " + enter s212"; }, [] { piCout << " - exit s212"; }, "s212"); - PIStateLambda * s213 = new PIStateLambda([] { piCout << " + enter s213"; }, [] { piCout << " - exit s213"; }, "s213"); - PIStateFinal * s214 = new PIStateFinal([] { piCout << " + enter s214 final"; }, "s214f"); - - root->addStates({s1, s2, s3, s4}); - // s1->addStates({s11, s12, s13}); - // s2->addStates({s21, s22, s23}); - // s21->addStates({s211, s212, s213}); - // root->addState(s214); - - s2->setParallel(true); - - root->setInitialState(s1); - s1->setInitialState(s11); - s2->setInitialState(s21); - s21->setInitialState(s213); - - // s213->addTransition(s13, meVoid)->addAction([] { piCout << "action transition s21 -> s22"; }); - // s3->addTransition(s212, meVoid)->addAction([] { piCout << "action transition s1 -> s213"; }); - s1->addTransition(s2, meVoid)->addAction([root] { root->postEvent(meInt, 1); }); - s2->addTransition(s3, meInt)->addGuard([](int i) { return i == 1; })->addAction([root] { root->postEvent(meInt, 2); }); - s3->addTransition(s4, meInt)->addGuard([](int i) { return i == 2; })->addAction([root] { root->postEvent(meInt, 3); }); - // s2->addTimeoutTransition(s3, .5_s); - // s3->addTransition(s1, meIntString)->addGuard([](int i, PIString str) { return i == 2 && str == "hello"; }); - - root->start(); - piCout << "initial" << root->isRunning() << "\n"; - // piCout << "active atomics" << root->activeAtomics(); - root->print(); - - piCout << "\npost event"; - posted = root->postEvent(meVoid); - piCout << "posted" << posted << "\n"; - // piCout << "active atomics" << root->activeAtomics(); - piSleep(1.); - root->print(); - - // piCout << "\npost event"; - // posted = root->postEvent(meVoid); - // piCout << "posted" << posted << "\n"; - // root->print(); - - /*root->addStates({s1, s2, s3}); - root->setInitialState(s1); - root->start(); - - piCout << root->postEvent(meVoid); - piCout << ""; - piCout << root->postEvent(meInt, 0.5f); - piCout << ""; - piCout << root->postEvent(meInt, 0); - piCout << ""; - piCout << root->postEvent(meInt, 1); - piCout << ""; - piCout << root->postEvent(meIntString, 2, "hello"); - piCout << ""; - piCout << root->postEvent(meIntString, 2, PIString("hello")); - piCout << "";*/ - delete root; - - /*PISerial ser; - ser.setSpeed(PISerial::S115200); - piCout << ser.open("COM15", PIIODevice::ReadWrite); - CONNECTL(&ser, threadedReadEvent, ([](const uchar * data, ssize_t size) { piCout << PIByteArray(data, size); })); - ser.startThreadedRead(); - (2_s).sleep(); - ser.stopAndWait();*/ - - /*auto test = [](PISystemTime t) { - piCout << "---------------"; - piCout << t << t.toString(); - piCout << PISystemTime::fromString(t.toString()) << PISystemTime::fromString(t.toString()).toString(); - piCout << PIVariant(t); - piCout << PIVariant(t).toString(); - }; - auto test_str = [](PIString s) { - piCout << "---------------"; - piCout << s; - piCout << PISystemTime::fromString(s) << PISystemTime::fromString(s).toString(); - }; - - test(PISystemTime::current()); - test(1000.123_d); - test(61_s); - test(59_s); - test(1234_us); - test(5000001_ns); - - test_str(""); - test_str("2020-05-04"); - test_str("2020-05-04 11:12:13"); - test_str("11:12:13"); - test_str("11:12:13.405"); - test_str("100"); - test_str("5 s"); - test_str("5s"); - test_str("1000 d 2 h 57 m 7 s 200 ms 2 ns"); - test_str("0.2 d"); - test_str(".5d"); - test_str("3E+2us"); - test_str("0.1d 200millis"); - test_str("2h 15 seconds"); - test_str("4secs 2hours 100 ns 3m 5ms 1 day 6 micros "); - - test_str("1hz"); - test_str("5.123khz"); - test_str("5khz 123 hz"); - test_str("2mhz"); - test_str("1ghz"); - test_str("1hz 1hz");*/ - - // PIPair p = createPIPair(0, ""); - /*PIConfig conf("model.conf"); - piCout << "****** config\n" << conf.allLeaves() << "******\n"; - - PIValueTree vt = PIValueTreeConversions::fromTextFile("model.conf"); - piCout << "****** tree"; - vt.forEachRecursive( - [](const PIValueTree & v, const PIString & fn) { piCout << fn << "=" << v.value().toString() << "#" << v.comment(); }); - piCout << "******";*/ - return 0; } diff --git a/utils/resources_compiler/generator.cpp b/utils/resources_compiler/generator.cpp index 5a11b2bb..9a79a6b4 100644 --- a/utils/resources_compiler/generator.cpp +++ b/utils/resources_compiler/generator.cpp @@ -17,8 +17,8 @@ bool generate(const PIString & init_name, PIFile & file, const PIVector fv; - piForeachC(ParserSection & s, files) { - piForeachC(ParserEntry & p, s.files) { + for (const auto & s: files) { + for (const auto & p: s.files) { PIFile f; if (!f.open(p.path, PIIODevice::ReadOnly)) continue; // piCout << "gen" << p.name << p.alias << p.path; @@ -31,7 +31,7 @@ bool generate(const PIString & init_name, PIFile & file, const PIVector Date: Sun, 3 Nov 2024 14:39:42 +0300 Subject: [PATCH 26/40] add PIString::lineNumber() method add ""_tr literal to translate string by PITranslator add pip_tr util, now useless, only can generate *.ts add qt_support internal lib, now only works with *.ts file pip_vtt migrate to qt_support --- CMakeLists.txt | 2 + libs/main/application/pitranslator.cpp | 5 + libs/main/application/pitranslator.h | 16 +- libs/main/text/pichar.h | 4 +- libs/main/text/pistring.cpp | 27 ++- libs/main/text/pistring.h | 4 + main.cpp | 14 +- utils/qt_support/CMakeLists.txt | 4 + utils/qt_support/ts_file.cpp | 175 ++++++++++++++++ utils/qt_support/ts_file.h | 45 +++++ utils/translator/CMakeLists.txt | 16 ++ utils/translator/main.cpp | 220 +++++++++++++++++++++ utils/value_tree_translator/CMakeLists.txt | 2 +- utils/value_tree_translator/main.cpp | 192 ++---------------- 14 files changed, 532 insertions(+), 194 deletions(-) create mode 100644 utils/qt_support/CMakeLists.txt create mode 100644 utils/qt_support/ts_file.cpp create mode 100644 utils/qt_support/ts_file.h create mode 100644 utils/translator/CMakeLists.txt create mode 100644 utils/translator/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6264bba8..3eb5d1c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -563,6 +563,8 @@ if(NOT PIP_FREERTOS) add_subdirectory("utils/code_model_generator") add_subdirectory("utils/resources_compiler") add_subdirectory("utils/deploy_tool") + add_subdirectory("utils/qt_support") + add_subdirectory("utils/translator") add_subdirectory("utils/value_tree_translator") if(PIP_UTILS AND (NOT CROSSTOOLS)) add_subdirectory("utils/system_test") diff --git a/libs/main/application/pitranslator.cpp b/libs/main/application/pitranslator.cpp index 874c9a45..674fd4d6 100644 --- a/libs/main/application/pitranslator.cpp +++ b/libs/main/application/pitranslator.cpp @@ -66,6 +66,11 @@ void PITranslator::loadConfig(const PIString & content) { for (const auto & s: cn.children()) c->add(s.name(), s.value().toString()); } + auto c = s->createContextInternal(""); + for (const auto & s: lang.children()) { + if (s.hasChildren()) continue; + c->add(s.name(), s.value().toString()); + } } diff --git a/libs/main/application/pitranslator.h b/libs/main/application/pitranslator.h index 60eea97e..6ccdf47a 100644 --- a/libs/main/application/pitranslator.h +++ b/libs/main/application/pitranslator.h @@ -26,9 +26,10 @@ #ifndef pitranslator_H #define pitranslator_H -#include "pifile.h" -#include "piiostream.h" -#include "pithread.h" +#include "pistring.h" + + +#define piTr PITranslator::tr //! \ingroup Application //! \~\brief @@ -37,7 +38,7 @@ class PIP_EXPORT PITranslator { public: static PIString tr(const PIString & in, const PIString & context = {}); - static PIString tr(const char * in, const PIString & context = {}) { return tr(PIString(in), context); } + static PIString tr(const char * in, const PIString & context = {}) { return tr(PIString::fromUTF8(in), context); } static void clear(); static void loadLang(const PIString & short_lang); @@ -61,4 +62,11 @@ private: PIMap contexts; }; +//! \~\brief +//! \~english PIString from UTF-8 +//! \~russian PIString из UTF-8 +inline PIString operator""_tr(const char * v, size_t sz) { + return PITranslator::tr(PIString::fromUTF8(v, sz)); +} + #endif diff --git a/libs/main/text/pichar.h b/libs/main/text/pichar.h index 0fe96237..1e51d89c 100644 --- a/libs/main/text/pichar.h +++ b/libs/main/text/pichar.h @@ -99,8 +99,8 @@ public: //! \~russian Оператор сравнения bool operator<=(const PIChar & o) const; - //! \~english Returns \b true if symbol is digit ('0' to '9') - //! \~russian Возвращает \b true если символ является + //! \~english Returns \b true if symbol is digit (from '0' to '9') + //! \~russian Возвращает \b true если символ является цифрой (от '0' до '9') bool isDigit() const; //! \~english Returns \b true if symbol is HEX digit ('0' to '9', 'a' to 'f', 'A' to 'F') diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index dc719ff6..a8d02619 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -1209,6 +1209,17 @@ int PIString::entries(const PIString & str) const { } +int PIString::lineNumber(int pos) const { + if (isEmpty()) return 0; + if (pos < 0 || pos >= size_s()) pos = size_s() - 1; + int line = 1; + for (int i = 0; i < pos; ++i) { + if (at(i) == '\n') ++line; + } + return line; +} + + bool PIString::startsWith(const PIChar c) const { if (isEmpty()) return false; return front() == c; @@ -1736,7 +1747,7 @@ PIString & PIString::setReadableSize(llong bytes) { static const PIString tr_c = "PIString"_a; clear(); if (bytes < 1024) { - *this += (PIString::fromNumber(bytes) + " "_a + PITranslator::tr("B", tr_c)); + *this += (PIString::fromNumber(bytes) + " "_a + PITranslator::tr("B", "PIString")); return *this; } double fres = bytes; @@ -1751,13 +1762,13 @@ PIString & PIString::setReadableSize(llong bytes) { } return false; }; - if (checkRange(PITranslator::tr("KiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("MiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("GiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("TiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("PiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("EiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("ZiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("KiB", "PIString"))) return *this; + if (checkRange(PITranslator::tr("MiB", "PIString"))) return *this; + if (checkRange(PITranslator::tr("GiB", "PIString"))) return *this; + if (checkRange(PITranslator::tr("TiB", "PIString"))) return *this; + if (checkRange(PITranslator::tr("PiB", "PIString"))) return *this; + if (checkRange(PITranslator::tr("EiB", "PIString"))) return *this; + if (checkRange(PITranslator::tr("ZiB", "PIString"))) return *this; checkRange(PITranslator::tr("YiB", tr_c), true); return *this; } diff --git a/libs/main/text/pistring.h b/libs/main/text/pistring.h index b4f5767f..02bec59f 100644 --- a/libs/main/text/pistring.h +++ b/libs/main/text/pistring.h @@ -1282,6 +1282,10 @@ public: //! \endcode int entries(const PIString & str) const; + //! \~english Returns line number of position "pos". Lines starts from 1. + //! \~russian Возвращает номер строки позиции "pos". Строки начинаются с 1. + int lineNumber(int pos) const; + //! \~english Returns if string starts with "c". //! \~russian Возвращает начинается ли строка с "c". bool startsWith(const char c) const { return startsWith(PIChar(c)); } diff --git a/main.cpp b/main.cpp index a3a8adb5..9634f88f 100644 --- a/main.cpp +++ b/main.cpp @@ -8,7 +8,6 @@ #include "pilog.h" #include "pimathbase.h" #include "pip.h" -#include "pitranslator.h" #include "pivaluetree_conversions.h" using namespace PICoutManipulators; @@ -21,12 +20,17 @@ void foo() { int main(int argc, char * argv[]) { + // piCout << PIString::readableSize(50_KiB); + // piCout << PIString::readableSize(1_GB); PITranslator::loadLang("ru"); - piCout << PIString::readableSize(50_KiB); - piCout << PIString::readableSize(1_GB); + PITranslator::loadConfig("[C]\ntest string=\n"_u8); + piCout << "test string1"; + piCout << "test string2"_tr; + piCout << piTr("test string", "C1"); PITranslator::clear(); - piCout << PIString::readableSize(50_KiB); - piCout << PIString::readableSize(1_GB); + piCout << "test string3"; + piCout << "test string4"_tr; + piCout << piTr("test string", "C2"); // PICodeParser parser; // parser.parseFile("cmg_test.h"); /*for (auto m: parser.macros) { diff --git a/utils/qt_support/CMakeLists.txt b/utils/qt_support/CMakeLists.txt new file mode 100644 index 00000000..2b1c6669 --- /dev/null +++ b/utils/qt_support/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB SRC "*.h" "*.cpp") +add_library(pip_qt_support STATIC ${SRC}) +target_include_directories(pip_qt_support PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(pip_qt_support pip) diff --git a/utils/qt_support/ts_file.cpp b/utils/qt_support/ts_file.cpp new file mode 100644 index 00000000..f3a26192 --- /dev/null +++ b/utils/qt_support/ts_file.cpp @@ -0,0 +1,175 @@ +/* + PIP - Platform Independent Primitives + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "ts_file.h" + +#include "pifile.h" +#include "piiostream.h" + + +void TSFile::Context::confirm(const PIString & msg, const PIString & file, int line) { + if (msg.isEmpty()) return; + bool is_new = !messages.contains(msg); + auto & m(messages[msg]); + m.source = msg; + m.filename = file; + m.line = line >= 0 ? PIString::fromNumber(line) : PIString(); + m.obsolete = false; + if (is_new) m.type = "unfinished"; +} + + +PIString TSFile::mask(const PIString & in) { + static const PIVector> map = { + {"&", "&" }, + {"<", "<" }, + {">", ">" }, + {"'", "'"}, + {"\"", """}, + }; + PIString ret = in; + for (const auto & i: map) + ret.replaceAll(i.first, i.second); + return ret; +} + + +PIString TSFile::unmask(const PIString & in) { + static const PIVector> map = { + {"<", "<" }, + {">", ">" }, + {"'", "'"}, + {"\"", """}, + {"&", "&" }, + }; + PIString ret = in; + for (const auto & i: map) + ret.replaceAll(i.second, i.first); + return ret; +} + + +TSFile::Content TSFile::read(const PIString & path) { + Content ret; + PIFile f(path, PIIODevice::ReadOnly); + if (!f.isOpened()) return ret; + PIIOTextStream ts(&f); + Context * cc = nullptr; + Message msg; + int phase = 0; + while (!ts.isEnd()) { + auto line = ts.readLine().trim(); + switch (phase) { + case 0: + if (line == "") phase = 1; + break; + case 1: + if (line.startsWith("")) { + line.cutLeft(6).cutRight(7); + // if (line == context) phase = 2; + cc = &(ret[line]); + phase = 2; + } + break; + case 2: + if (line == "") { + msg = {}; + phase = 3; + } + break; + case 3: + if (line == "") { + if (cc) cc->messages[msg.source] = msg; + phase = 2; + } else if (line.startsWith("")) { + line.cutLeft(8).cutRight(9); + msg.source = unmask(line); + } else if (line.startsWith("').cutLeft(8); + while (trs.isNotEmpty()) { + PIString t = trs.takeCWord(); + trs.cutLeft(1); + PIString v = trs.takeRange('\"', '\"'); + if (t == "filename") msg.filename = v; + if (t == "line") msg.line = v; + if (trs.size_s() <= 2) break; + } + } else if (line.startsWith("').cutLeft(11); + while (trs.isNotEmpty()) { + PIString t = trs.takeCWord(); + trs.cutLeft(1); + PIString v = trs.takeRange('\"', '\"'); + if (t == "type") msg.type = v; + } + line.cutRight(14); + msg.translation = unmask(line); + } + break; + } + if (line == "") { + cc = nullptr; + phase = 0; + } + } + return ret; +} + + +bool TSFile::write(const PIString & path, const TSFile::Content & content, const PIString & lang, bool no_obsolete) { + PIFile outf(path, PIIODevice::ReadWrite); + if (!outf.isOpened()) { + piCerr << "Can`t open" << outf.path() << "!"; + return false; + } + outf.clear(); + PIIOTextStream ts(&outf); + auto writeMessage = [&ts](const Message & m) { + if (m.source.isEmpty()) return; + ts << " \n"; + if (m.filename.isNotEmpty()) { + ts << " \n"; + } + ts << " " << mask(m.source) << "\n"; + ts << " " << mask(m.translation) << "\n"; + ts << " \n"; + }; + + ts << "\n"; + ts << "\n"; + ts << "\n"; + auto cit = content.makeIterator(); + while (cit.next()) { + if (cit.value().messages.isEmpty()) continue; + ts << "\n"; + ts << " " << cit.key() << "\n"; + auto mit = cit.value().messages.makeIterator(); + while (mit.next()) { + if (mit.value().obsolete && no_obsolete) continue; + writeMessage(mit.value()); + } + ts << "\n"; + }; + ts << "\n"; + + return true; +} diff --git a/utils/qt_support/ts_file.h b/utils/qt_support/ts_file.h new file mode 100644 index 00000000..8ea6c126 --- /dev/null +++ b/utils/qt_support/ts_file.h @@ -0,0 +1,45 @@ +/* + PIP - Platform Independent Primitives + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "pistring.h" + +namespace TSFile { + +struct Message { + PIString source; + PIString translation; + PIString type; + PIString filename; + PIString line; + bool obsolete = true; +}; + +struct Context { + PIMap messages; + void confirm(const PIString & msg, const PIString & file, int line = -1); +}; + +using Content = PIMap; + +PIString mask(const PIString & in); +PIString unmask(const PIString & in); + +Content read(const PIString & path); +bool write(const PIString & path, const Content & content, const PIString & lang, bool no_obsolete); + +} // namespace TSFile diff --git a/utils/translator/CMakeLists.txt b/utils/translator/CMakeLists.txt new file mode 100644 index 00000000..d3ae9f7d --- /dev/null +++ b/utils/translator/CMakeLists.txt @@ -0,0 +1,16 @@ +list(APPEND PIP_UTILS_LIST "pip_tr") +set(PIP_UTILS_LIST ${PIP_UTILS_LIST} PARENT_SCOPE) +import_version(pip_tr PIP) +set_deploy_property(pip_tr + LABEL "PIP Translator" + FULLNAME "${PIP_DOMAIN}.pip_tr" + COMPANY "${PIP_COMPANY}" + INFO "Platform-Independent Primitives") +make_rc(pip_tr _RC) +add_executable(pip_tr "main.cpp" ${_RC}) +target_link_libraries(pip_tr pip_qt_support pip) +if (DEFINED LIB) + install(TARGETS pip_tr DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +else() + install(TARGETS pip_tr DESTINATION bin) +endif() diff --git a/utils/translator/main.cpp b/utils/translator/main.cpp new file mode 100644 index 00000000..819a521d --- /dev/null +++ b/utils/translator/main.cpp @@ -0,0 +1,220 @@ +/* + PIP - Platform Independent Primitives + Translator + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "picli.h" +#include "pidir.h" +#include "pifile.h" +#include "piiostream.h" +#include "pijson.h" +#include "pivaluetree.h" +#include "pivaluetree_conversions.h" +#include "ts_file.h" + +using namespace PICoutManipulators; + +const char help_string[] = "Read input files or entire directories as C++ sources\n" + "and create QtLinguist translation file (*.ts).\n" + "Search for next strings:\n" + " * PITranslator::tr(\"msg\"[, \"ctx\"])\n" + " * piTr(\"msg\"[, \"ctx\"])\n" + " * \"msg\"_tr\n" + "Output file can be translated by QtLinguist and used\n" + "by PIP ?????? to translate strings.\n" + ""; + +void header() { + piCout << Bold << "PIP Translator"; + piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine; + piCout << Green << Bold << "Usage:" << Default + << "\"pip_tr [-hHqn] -l -o [] [] [...]\"" << NewLine; +} + +void usage() { + header(); + piCout << Green << Bold << "Details:"; + piCout << Bold << "Debug control"; + piCout << "-h " << Green << "- display this message and exit"; + piCout << "-H " << Green << "- display details help"; + piCout << "-q " << Green << "- quiet, no debug output to console"; + piCout << ""; + piCout << Bold << "Output control"; + piCout << "-l " << Green << "- translation language (e.g. en_US, ru_RU)"; + piCout << "-n, --no-obsolete " << Green << "- drop unused translations in output file"; + piCout << "-o " << Green << "- output file for translation (QtLinguist *.ts file)"; + piCout << ""; + piCout << Bold << "Input control"; + piCout << " " << Green << "- add file or dir translation"; +} + +void help() { + header(); + piCout << help_string; +} + + +void gatherStrings(TSFile::Content & content, const PIString & file, const PIString & file_loc) { + PIString source, context; + int pos = -1, ppos = 0, line = -1; + int len = 0; + auto isCLetter = [](const PIChar c) { return c.isAlpha() || c.isDigit() || c == '_'; }; + static const PIStringList keyword({"PITranslator::tr", "piTr"}); + for (const auto & kw: keyword) { + for (;;) { + source.clear(); + context.clear(); + + pos = file.find(kw, pos + 1); + if (pos < 0) break; + if (pos > 0) { + auto pc = file[pos - 1]; + if (isCLetter(pc) || pc == ':') { + pos += kw.size_s(); + continue; + } + } + pos += kw.size_s(); + if (pos < file.size_s() - 1) { + auto pc = file[pos]; + if (isCLetter(pc) || pc == ':') { + pos += kw.size_s(); + continue; + } + } + // piCout << "PITranslator" << pos; + + pos = file.find('(', pos); + if (pos < 0) break; + // piCout << "(" << pos; + pos += 1; + + pos = file.findRange('"', '"', '\\', pos, &len); + if (pos < 0) break; + // piCout << "\"" << pos; + source = file.mid(pos, len); + pos += len + 1; + line = file.lineNumber(pos); + ppos = pos; + + while (pos < file.size_s()) { + if (!file[pos].isSpace() && file[pos] != '\n' && file[pos] != '\r') break; + ++pos; + } + if (pos < file.size_s()) { + // piCout << "check comma" << file[pos]; + if (file[pos] == ',') { + pos += 1; + pos = file.findRange('"', '"', '\\', pos, &len); + // piCout << "check range" << file.mid(pos, len); + if (pos >= 0) { + context = file.mid(pos, len); + pos += len + 1; + ppos = pos; + } + } + } + + content[context].confirm(source, file_loc, line); + pos = ppos; + piCout << "Context = \"" << context << "\", message = \"" << source << "\""; + } + } + + auto & ec(content[""]); + content[context].confirm(source, file_loc, line); + pos = -1; + for (;;) { + source.clear(); + + pos = file.find('"', pos + 1); + if (pos < 0) break; + pos = file.findRange('"', '"', '\\', pos, &len); + if (pos < 0) break; + // piCout << "\"" << pos; + source = file.mid(pos, len); + pos += len + 1; + if (file.mid(pos, 3) == "_tr") { + pos += 3; + if (pos < file.size_s() - 1) { + auto pc = file[pos]; + if (isCLetter(pc) || pc == ':') { + continue; + } + } + ec.confirm(source, file_loc, file.lineNumber(pos)); + piCout << "_tr = \"" << source << "\""; + } + } +} + + +int main(int argc, char * argv[]) { + PICLI cli(argc, argv); + // piCout << cli.rawArguments(); + cli.setOptionalArgumentsCount(-1); + cli.addArgument("output", true); + cli.addArgument("language", true); + cli.addArgument("help"); + cli.addArgument("Help"); + cli.addArgument("quiet"); + cli.addArgument("no-obsolete"); + if (cli.hasArgument("Help")) { + help(); + return 0; + } + if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.argumentValue("language").isEmpty() || + cli.optionalArguments().isEmpty()) { + usage(); + return 0; + } + piDebug = !cli.hasArgument("quiet"); + PIString out_path = cli.argumentValue("output"); + + PIStringList files; + const static PIStringList ext({"*.h", "*.hpp", "*.cpp", "*.cxx"}); + for (const PIString & a: cli.optionalArguments()) { + if (PIDir::isExists(a)) { + auto dl = PIDir(a).allEntries(); + for (const auto & i: dl) { + if (!i.isFile()) continue; + if (ext.contains(i.extension().toLowerCase().trim())) files << i.path; + } + } else { + if (PIFile::isExists(a)) files << a; + } + } + + piCout << Cyan << Bold << "Reading ts file ..."; + auto content = TSFile::read(out_path); + piCout << Cyan << Bold << "Reading done"; + + piCout << Cyan << Bold << "Read" << files.size_s() << "files ..."; + PIDir out_dir(PIFile::FileInfo(out_path).dir()); + for (const auto & p: files) { + PIString file_loc = out_dir.relative(p); + PIFile f(p, PIIODevice::ReadOnly); + gatherStrings(content, PIString::fromUTF8(f.readAll()), file_loc); + } + piCout << Cyan << Bold << "Reading done"; + + piCout << Cyan << Bold << "Writing ts file ..."; + if (!TSFile::write(out_path, content, cli.argumentValue("language"), cli.hasArgument("no-obsolete"))) return 1; + piCout << Cyan << Bold << "Writing done"; + + return 0; +} diff --git a/utils/value_tree_translator/CMakeLists.txt b/utils/value_tree_translator/CMakeLists.txt index 22139e2f..906082f5 100644 --- a/utils/value_tree_translator/CMakeLists.txt +++ b/utils/value_tree_translator/CMakeLists.txt @@ -8,7 +8,7 @@ set_deploy_property(pip_vtt INFO "Platform-Independent Primitives") make_rc(pip_vtt _RC) add_executable(pip_vtt "main.cpp" ${_RC}) -target_link_libraries(pip_vtt pip) +target_link_libraries(pip_vtt pip_qt_support pip) if (DEFINED LIB) install(TARGETS pip_vtt DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) else() diff --git a/utils/value_tree_translator/main.cpp b/utils/value_tree_translator/main.cpp index ce5747f1..0a3a0cde 100644 --- a/utils/value_tree_translator/main.cpp +++ b/utils/value_tree_translator/main.cpp @@ -20,12 +20,10 @@ #include "picli.h" #include "pidir.h" #include "pifile.h" -#include "piiostream.h" #include "pijson.h" #include "pivaluetree.h" #include "pivaluetree_conversions.h" - -#include +#include "ts_file.h" using namespace PICoutManipulators; using Attribute = PIValueTree::Attribute; @@ -69,132 +67,24 @@ void help() { piCout << help_string; } -void printError(const PIString & msg) { - std::cerr << msg.data() << std::endl; -} + +const PIString contextName = "QAD::PIValueTreeEdit"; -PISet strings; -PIMap locations; - -PIString mask(const PIString & in) { - static PIVector> map = { - {"&", "&" }, - {"<", "<" }, - {">", ">" }, - {"'", "'"}, - {"\"", """}, - }; - PIString ret = in; - for (const auto & i: map) - ret.replaceAll(i.first, i.second); - return ret; -} - -PIString unmask(const PIString & in) { - static PIVector> map = { - {"<", "<" }, - {">", ">" }, - {"'", "'"}, - {"\"", """}, - {"&", "&" }, - }; - PIString ret = in; - for (const auto & i: map) - ret.replaceAll(i.second, i.first); - return ret; -} - -void addString(const PIString & s, const PIString & loc) { - if (s.isEmpty()) return; - strings << s; - locations[s.hash()] = loc; -} - -void gatherStrings(const PIValueTree & vt, const PIString & loc) { +void gatherStrings(TSFile::Context & context, const PIValueTree & vt, const PIString & loc) { const static PIStringList attrs({Attribute::prefix, Attribute::suffix}); for (const auto & c: vt.children()) { - addString(c.name(), loc); - addString(c.comment(), loc); + context.confirm(c.name(), loc); + context.confirm(c.comment(), loc); for (const auto & a: attrs) { if (c.attributes().contains(a)) { - addString(c.attributes().value(a).toString(), loc); + context.confirm(c.attributes().value(a).toString(), loc); } } - if (!c.isArray()) gatherStrings(c, loc); + if (!c.isArray()) gatherStrings(context, c, loc); } } -const PIString context = "QAD::PIValueTreeEdit"; - -struct TSMessage { - PIString source; - PIString translation; - PIString type; - PIString filename; - PIString line; -}; - -PIMap readTS(const PIString & path) { - PIMap ret; - PIFile f(path, PIIODevice::ReadOnly); - if (!f.isOpened()) return ret; - PIIOTextStream ts(&f); - TSMessage msg; - int phase = 0; - while (!ts.isEnd()) { - auto line = ts.readLine().trim(); - switch (phase) { - case 0: - if (line == "") phase = 1; - break; - case 1: - if (line.startsWith("")) { - line.cutLeft(6).cutRight(7); - if (line == context) phase = 2; - } - break; - case 2: - if (line == "") { - msg = {}; - phase = 3; - } - break; - case 3: - if (line == "") { - ret[msg.source] = msg; - phase = 2; - } else if (line.startsWith("")) { - line.cutLeft(8).cutRight(9); - msg.source = unmask(line); - } else if (line.startsWith("').cutLeft(8); - while (trs.isNotEmpty()) { - PIString t = trs.takeCWord(); - trs.cutLeft(1); - PIString v = trs.takeRange('\"', '\"'); - if (t == "filename") msg.filename = v; - if (t == "line") msg.line = v; - if (trs.size_s() <= 2) break; - } - } else if (line.startsWith("').cutLeft(11); - while (trs.isNotEmpty()) { - PIString t = trs.takeCWord(); - trs.cutLeft(1); - PIString v = trs.takeRange('\"', '\"'); - if (t == "type") msg.type = v; - } - line.cutRight(14); - msg.translation = unmask(line); - } - break; - } - if (line == "") phase = 0; - } - return ret; -} - int main(int argc, char * argv[]) { PICLI cli(argc, argv); @@ -232,74 +122,28 @@ int main(int argc, char * argv[]) { } } + piCout << Cyan << Bold << "Reading ts file ..."; + auto content = TSFile::read(out_path); + piCout << Cyan << Bold << "Reading done"; piCout << Cyan << Bold << "Read" << files.size_s() << "files ..."; + auto & context(content[contextName]); PIDir out_dir(PIFile::FileInfo(out_path).dir()); for (const auto & p: files) { PIString ext = PIFile::FileInfo(p).extension().toLowerCase().trim(); PIFile f(p, PIIODevice::ReadOnly); - if (ext == "conf") gatherStrings(PIValueTreeConversions::fromText(PIString::fromUTF8(f.readAll())), out_dir.relative(p)); + if (ext == "conf") gatherStrings(context, PIValueTreeConversions::fromText(PIString::fromUTF8(f.readAll())), out_dir.relative(p)); if (ext == "json") - gatherStrings(PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(f.readAll()))), out_dir.relative(p)); - if (ext == "bin") gatherStrings(piDeserialize(f.readAll()), PIString()); + gatherStrings(context, + PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(f.readAll()))), + out_dir.relative(p)); + if (ext == "bin") gatherStrings(context, piDeserialize(f.readAll()), PIString()); } piCout << Cyan << Bold << "Reading done"; - - piCout << Cyan << Bold << "Reading ts file ..."; - - auto old = readTS(out_path); - - piCout << Cyan << Bold << "Reading done"; - - piCout << Cyan << Bold << "Writing ts file ..."; - - PIFile outf(out_path, PIIODevice::ReadWrite); - if (!outf.isOpened()) { - printError("Can`t open \"" + outf.path() + "\"!"); - return 1; - } - outf.clear(); - PIIOTextStream ts(&outf); - auto writeTSMessage = [&ts](const PIString & s, const TSMessage & m) { - ts << " \n"; - ts << " " << mask(s) << "\n"; - if (m.filename.isNotEmpty()) { - ts << " \n"; - } - ts << " " << mask(m.translation) << "\n"; - ts << " \n"; - }; - ts << "\n"; - ts << "\n"; - ts << "\n"; - ts << "\n"; - ts << " " << context << "\n"; - for (const auto & s: strings) { - TSMessage m = old.value(s); - m.filename = locations.value(s.hash()); - writeTSMessage(s, m); - old.remove(s); - } - if (!cli.hasArgument("no-obsolete")) { - for (const auto & i: old) { - writeTSMessage(i.first, i.second); - } - } - ts << "\n"; - ts << "\n"; - + if (!TSFile::write(out_path, content, cli.argumentValue("language"), cli.hasArgument("no-obsolete"))) return 1; piCout << Cyan << Bold << "Writing done"; - return 0; } From 1106cde3e47f77e8ed8c2f854beffd154de7520c Mon Sep 17 00:00:00 2001 From: peri4 Date: Sun, 3 Nov 2024 14:41:31 +0300 Subject: [PATCH 27/40] return tr for PIString --- libs/main/text/pistring.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index a8d02619..bae3ae82 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -1747,7 +1747,7 @@ PIString & PIString::setReadableSize(llong bytes) { static const PIString tr_c = "PIString"_a; clear(); if (bytes < 1024) { - *this += (PIString::fromNumber(bytes) + " "_a + PITranslator::tr("B", "PIString")); + *this += (PIString::fromNumber(bytes) + " "_a + PITranslator::tr("B", tr_c)); return *this; } double fres = bytes; @@ -1762,13 +1762,13 @@ PIString & PIString::setReadableSize(llong bytes) { } return false; }; - if (checkRange(PITranslator::tr("KiB", "PIString"))) return *this; - if (checkRange(PITranslator::tr("MiB", "PIString"))) return *this; - if (checkRange(PITranslator::tr("GiB", "PIString"))) return *this; - if (checkRange(PITranslator::tr("TiB", "PIString"))) return *this; - if (checkRange(PITranslator::tr("PiB", "PIString"))) return *this; - if (checkRange(PITranslator::tr("EiB", "PIString"))) return *this; - if (checkRange(PITranslator::tr("ZiB", "PIString"))) return *this; + if (checkRange(PITranslator::tr("KiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("MiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("GiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("TiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("PiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("EiB", tr_c))) return *this; + if (checkRange(PITranslator::tr("ZiB", tr_c))) return *this; checkRange(PITranslator::tr("YiB", tr_c), true); return *this; } From 73ed51e3d4cda3144931932df2a55548dfc52908 Mon Sep 17 00:00:00 2001 From: peri4 Date: Sun, 3 Nov 2024 18:12:32 +0300 Subject: [PATCH 28/40] translations: multi-line and special symbols support --- libs/main/application/pisystemmonitor.cpp | 7 +++--- main.cpp | 14 ++++------- utils/qt_support/ts_file.cpp | 30 ++++++++++++++++++++--- utils/translator/main.cpp | 12 ++++++--- 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/libs/main/application/pisystemmonitor.cpp b/libs/main/application/pisystemmonitor.cpp index 522df17b..98a5e428 100644 --- a/libs/main/application/pisystemmonitor.cpp +++ b/libs/main/application/pisystemmonitor.cpp @@ -19,13 +19,14 @@ #include "pisystemmonitor.h" -#include - #include "pidir.h" #include "piliterals_string.h" #include "piprocess.h" #include "pisysteminfo.h" #include "pitime_win.h" +#include "pitranslator.h" + +#include #ifdef WINDOWS # include # include @@ -114,7 +115,7 @@ bool PISystemMonitor::startOnProcess(int pID, PISystemTime interval) { # else PRIVATE->hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pID_); if (PRIVATE->hProc == 0) { - piCoutObj << "Can`t open process with ID = " << pID_ << "," << errorString(); + piCoutObj << "Can`t open process with ID = "_tr << pID_ << "," << errorString(); return false; } PRIVATE->tm.reset(); diff --git a/main.cpp b/main.cpp index 9634f88f..49821cc7 100644 --- a/main.cpp +++ b/main.cpp @@ -23,16 +23,12 @@ int main(int argc, char * argv[]) { // piCout << PIString::readableSize(50_KiB); // piCout << PIString::readableSize(1_GB); PITranslator::loadLang("ru"); - PITranslator::loadConfig("[C]\ntest string=\n"_u8); - piCout << "test string1"; - piCout << "test string2"_tr; - piCout << piTr("test string", "C1"); + PITranslator::loadConfig("[]\ntest \\\nstring=привет\n"_u8); + piCout << "test\nstring"_tr; PITranslator::clear(); - piCout << "test string3"; - piCout << "test string4"_tr; - piCout << piTr("test string", "C2"); - // PICodeParser parser; - // parser.parseFile("cmg_test.h"); + piCout << "test\nstring"_tr; + // PICodeParser parser; + // parser.parseFile("cmg_test.h"); /*for (auto m: parser.macros) { piCout << ""; piCout << m.name << m.args << m.value; diff --git a/utils/qt_support/ts_file.cpp b/utils/qt_support/ts_file.cpp index f3a26192..281bf26e 100644 --- a/utils/qt_support/ts_file.cpp +++ b/utils/qt_support/ts_file.cpp @@ -71,9 +71,26 @@ TSFile::Content TSFile::read(const PIString & path) { PIIOTextStream ts(&f); Context * cc = nullptr; Message msg; - int phase = 0; + int phase = 0; + bool multi_source = false, multi_translation = false; while (!ts.isEnd()) { auto line = ts.readLine().trim(); + if (multi_source) { + if (line.endsWith("")) { + line.cutRight(9); + multi_source = false; + } + msg.source += "\n" + unmask(line); + continue; + } + if (multi_translation) { + if (line.endsWith("")) { + line.cutRight(14); + multi_translation = false; + } + msg.translation += "\n" + unmask(line); + continue; + } switch (phase) { case 0: if (line == "") phase = 1; @@ -97,7 +114,11 @@ TSFile::Content TSFile::read(const PIString & path) { if (cc) cc->messages[msg.source] = msg; phase = 2; } else if (line.startsWith("")) { - line.cutLeft(8).cutRight(9); + line.cutLeft(8); + if (line.endsWith("")) + line.cutRight(9); + else + multi_source = true; msg.source = unmask(line); } else if (line.startsWith("').cutLeft(8); @@ -117,7 +138,10 @@ TSFile::Content TSFile::read(const PIString & path) { PIString v = trs.takeRange('\"', '\"'); if (t == "type") msg.type = v; } - line.cutRight(14); + if (line.endsWith("")) + line.cutRight(14); + else + multi_translation = true; msg.translation = unmask(line); } break; diff --git a/utils/translator/main.cpp b/utils/translator/main.cpp index 819a521d..050dce55 100644 --- a/utils/translator/main.cpp +++ b/utils/translator/main.cpp @@ -68,6 +68,11 @@ void help() { } +PIString fromCode(const PIString & in) { + return in.replacedAll("\\n", '\n').replaceAll("\\r", '\r').replaceAll("\\t", '\t'); +} + + void gatherStrings(TSFile::Content & content, const PIString & file, const PIString & file_loc) { PIString source, context; int pos = -1, ppos = 0, line = -1; @@ -129,14 +134,13 @@ void gatherStrings(TSFile::Content & content, const PIString & file, const PIStr } } - content[context].confirm(source, file_loc, line); + content[context].confirm(fromCode(source), file_loc, line); pos = ppos; piCout << "Context = \"" << context << "\", message = \"" << source << "\""; } } auto & ec(content[""]); - content[context].confirm(source, file_loc, line); pos = -1; for (;;) { source.clear(); @@ -156,7 +160,7 @@ void gatherStrings(TSFile::Content & content, const PIString & file, const PIStr continue; } } - ec.confirm(source, file_loc, file.lineNumber(pos)); + ec.confirm(fromCode(source), file_loc, file.lineNumber(pos)); piCout << "_tr = \"" << source << "\""; } } @@ -186,7 +190,7 @@ int main(int argc, char * argv[]) { PIString out_path = cli.argumentValue("output"); PIStringList files; - const static PIStringList ext({"*.h", "*.hpp", "*.cpp", "*.cxx"}); + const static PIStringList ext({"h", "hpp", "cpp", "cxx"}); for (const PIString & a: cli.optionalArguments()) { if (PIDir::isExists(a)) { auto dl = PIDir(a).allEntries(); From 57f8c1313e6300e87e4193a7ab584d20d728acb6 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 5 Nov 2024 13:49:00 +0300 Subject: [PATCH 29/40] first release of translation facility * runtime - loading and translating * design-time - works with *.ts file (pip_tr utility) * compile-time - CMake macro for compile *.ts --- CMakeLists.txt | 127 +++-- cmake/FindPIP.cmake | 2 + cmake/PIPMacros.cmake | 39 ++ lang/pip_ru.ts | 531 ++++++++++++++++++ lang/update.bat | 1 + libs/client_server/piclientserver_server.cpp | 6 +- libs/cloud/picloudclient.cpp | 3 +- libs/cloud/picloudserver.cpp | 5 +- libs/cloud/picloudtcp.cpp | 5 +- libs/compress/picompress.cpp | 12 +- libs/crypt/picrypt.cpp | 16 +- libs/fftw/pifft_p.h | 3 +- libs/io_utils/piethutilbase.cpp | 5 +- libs/io_utils/pistreampacker.cpp | 3 +- libs/main/application/picli.cpp | 3 +- libs/main/application/pisystemmonitor.cpp | 8 +- libs/main/application/pitranslator.cpp | 72 ++- libs/main/application/pitranslator.h | 58 +- libs/main/application/pitranslator_p.cpp | 124 ++++ libs/main/application/pitranslator_p.h | 53 ++ libs/main/application/tr/pip_all.h | 16 - libs/main/application/tr/pip_ru.h | 25 - libs/main/io_devices/pibinarylog.cpp | 67 +-- libs/main/io_devices/piethernet.cpp | 17 +- libs/main/io_devices/pifile.cpp | 3 +- libs/main/io_devices/piiobytearray.cpp | 8 +- libs/main/io_devices/piiodevice.cpp | 7 +- libs/main/io_devices/piiostring.cpp | 4 +- libs/main/io_devices/piserial.cpp | 21 +- libs/main/io_utils/pibasetransfer.cpp | 21 +- libs/main/io_utils/piconnection.cpp | 23 +- libs/main/io_utils/pidiagnostics.cpp | 20 +- libs/main/resources/piresourcesstorage.cpp | 11 +- .../state_machine/pistatemachine_state.cpp | 4 +- libs/main/system/piprocess.cpp | 3 +- libs/main/text/pistring.cpp | 53 +- libs/main/text/pistring.h | 56 ++ libs/main/thread/pithread.cpp | 7 +- libs/main/types/pisystemtime.cpp | 7 +- libs/main/types/pivariant.cpp | 6 +- libs/opencl/piopencl.cpp | 14 +- main.cpp | 40 +- utils/code_model_generator/main.cpp | 15 +- utils/deploy_tool/main.cpp | 2 +- utils/qt_support/ts_file.cpp | 103 +++- utils/qt_support/ts_file.h | 18 +- utils/resources_compiler/main.cpp | 7 +- utils/translator/CMakeLists.txt | 9 +- utils/translator/main.cpp | 234 ++++---- utils/translator/parser.cpp | 139 +++++ utils/translator/parser.h | 9 + utils/value_tree_translator/main.cpp | 6 +- 52 files changed, 1571 insertions(+), 480 deletions(-) create mode 100644 lang/pip_ru.ts create mode 100644 lang/update.bat create mode 100644 libs/main/application/pitranslator_p.cpp create mode 100644 libs/main/application/pitranslator_p.h delete mode 100644 libs/main/application/tr/pip_all.h delete mode 100644 libs/main/application/tr/pip_ru.h create mode 100644 utils/translator/parser.cpp create mode 100644 utils/translator/parser.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eb5d1c5..6e248eaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(PIP) set(PIP_MAJOR 4) -set(PIP_MINOR 3) -set(PIP_REVISION 2) +set(PIP_MINOR 4) +set(PIP_REVISION 0) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) @@ -82,6 +82,7 @@ shstk_is_parent_exists(_pe) if (_pe) set(BUILDING_pip 1 PARENT_SCOPE) set(pip_ROOT_SRC "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE) + set(pip_ROOT_SRC "${CMAKE_CURRENT_SOURCE_DIR}") endif() @@ -188,10 +189,12 @@ if (NOT DEFINED PIP_CMG) if (CMAKE_CROSSCOMPILING OR (DEFINED ANDROID_PLATFORM)) set(PIP_CMG "pip_cmg") set(PIP_RC "pip_rc") + set(PIP_TR "pip_tr") set(PIP_DEPLOY_TOOL "deploy_tool") else() set(PIP_CMG "${CMAKE_CURRENT_BINARY_DIR}/utils/code_model_generator/pip_cmg") set(PIP_RC "${CMAKE_CURRENT_BINARY_DIR}/utils/resources_compiler/pip_rc") + set(PIP_TR "${CMAKE_CURRENT_BINARY_DIR}/utils/translator/pip_tr") set(PIP_DEPLOY_TOOL "${CMAKE_CURRENT_BINARY_DIR}/utils/deploy_tool/deploy_tool") endif() endif() @@ -502,55 +505,6 @@ endif() string(REPLACE ";" "," PIP_EXPORTS_STR "${PIP_EXPORTS}") target_compile_definitions(pip PRIVATE "PICODE_DEFINES=\"${PIP_EXPORTS_STR}\"") -# Install -# Check if system or local install will be used (to system install use "-DLIB=" argument of cmake) -if(NOT LOCAL) - if(WIN32) - if(MINGW) - if (NOT CROSSTOOLS) - install(FILES ${HDRS} DESTINATION ${MINGW_INCLUDE}/pip) - if(HDR_DIRS) - install(DIRECTORY ${HDR_DIRS} DESTINATION ${MINGW_INCLUDE}/pip) - endif() - install(TARGETS ${PIP_MODULES} ARCHIVE DESTINATION ${MINGW_LIB}) - endif() - install(TARGETS ${PIP_MODULES} RUNTIME DESTINATION ${MINGW_BIN}) - find_library(STDLIB "stdc++-6" PATHS ${MINGW_BIN} NO_DEFAULT_PATH) - find_library(STDLIB "stdc++-6") - #message("${STDLIB}") - if (STDLIB) - file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/code_model_generator") - file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/resources_compiler") - file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/deploy_tool") - endif() - else() - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pip_export.h DESTINATION include) - endif() - else() - if (NOT CROSSTOOLS) - install(FILES ${HDRS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/pip) - if(HDR_DIRS) - install(DIRECTORY ${HDR_DIRS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/pip) - endif() - endif() - install(TARGETS ${PIP_MODULES} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) - endif() -else() - if(NOT PIP_FREERTOS) - if(WIN32) - install(TARGETS ${PIP_MODULES} RUNTIME DESTINATION bin) - install(TARGETS ${PIP_MODULES} ARCHIVE DESTINATION lib) - else() - install(TARGETS ${PIP_MODULES} DESTINATION lib) - endif() - install(FILES ${HDRS} DESTINATION include/pip) - if(HDR_DIRS) - install(DIRECTORY ${HDR_DIRS} DESTINATION include/pip) - endif() - endif() -endif() -file(GLOB CMAKES "cmake/*.cmake" "cmake/*.in") -install(FILES ${CMAKES} DESTINATION ${CMAKE_ROOT}/Modules) if(NOT PIP_FREERTOS) @@ -575,10 +529,79 @@ if(NOT PIP_FREERTOS) add_subdirectory("utils/cloud_dispatcher") endif() endif() - + endif() +# Translations +set(PIP_LANG) +if (NOT CROSSTOOLS) + pip_translation(PIP_LANG lang/pip_ru.ts) + add_custom_target(pip_lang SOURCES "${PIP_LANG}") +endif() + + +# Install +# Check if system or local install will be used (to system install use "-DLIB=" argument of cmake) +if(NOT LOCAL) + if(WIN32) + if(MINGW) + if (NOT CROSSTOOLS) + install(FILES ${HDRS} DESTINATION ${MINGW_INCLUDE}/pip) + if(PIP_LANG) + install(FILES ${PIP_LANG} DESTINATION ${MINGW_INCLUDE}/../share/pip/lang) + endif() + if(HDR_DIRS) + install(DIRECTORY ${HDR_DIRS} DESTINATION ${MINGW_INCLUDE}/pip) + endif() + install(TARGETS ${PIP_MODULES} ARCHIVE DESTINATION ${MINGW_LIB}) + endif() + install(TARGETS ${PIP_MODULES} RUNTIME DESTINATION ${MINGW_BIN}) + find_library(STDLIB "stdc++-6" PATHS ${MINGW_BIN} NO_DEFAULT_PATH) + find_library(STDLIB "stdc++-6") + #message("${STDLIB}") + if (STDLIB) + file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/code_model_generator") + file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/resources_compiler") + file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/deploy_tool") + file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/translator") + endif() + else() + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pip_export.h DESTINATION include) + endif() + else() + if (NOT CROSSTOOLS) + install(FILES ${HDRS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/pip) + if(PIP_LANG) + install(FILES ${PIP_LANG} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pip/lang) + endif() + if(HDR_DIRS) + install(DIRECTORY ${HDR_DIRS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/pip) + endif() + endif() + install(TARGETS ${PIP_MODULES} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() +else() + if(NOT PIP_FREERTOS) + if(WIN32) + install(TARGETS ${PIP_MODULES} RUNTIME DESTINATION bin) + install(TARGETS ${PIP_MODULES} ARCHIVE DESTINATION lib) + else() + install(TARGETS ${PIP_MODULES} DESTINATION lib) + endif() + install(FILES ${HDRS} DESTINATION include/pip) + if(PIP_LANG) + install(FILES ${PIP_LANG} DESTINATION share/pip/lang) + endif() + if(HDR_DIRS) + install(DIRECTORY ${HDR_DIRS} DESTINATION include/pip) + endif() + endif() +endif() +file(GLOB CMAKES "cmake/*.cmake" "cmake/*.in") +install(FILES ${CMAKES} DESTINATION ${CMAKE_ROOT}/Modules) + + shstk_is_parent_exists(_pe) if (_pe) set(PIP_MODULES ${PIP_MODULES} PARENT_SCOPE) diff --git a/cmake/FindPIP.cmake b/cmake/FindPIP.cmake index 002c92c7..1730ae9f 100644 --- a/cmake/FindPIP.cmake +++ b/cmake/FindPIP.cmake @@ -45,10 +45,12 @@ endif() if (BUILDING_PIP AND (NOT CMAKE_CROSSCOMPILING)) set(PIP_CMG "$/$" CACHE STRING "") set(PIP_RC "$/$" CACHE STRING "") + set(PIP_TR "$/$" CACHE STRING "") set(PIP_DEPLOY_TOOL "$/$" CACHE STRING "") else() find_program(PIP_CMG pip_cmg${PIP_BINEXT} HINTS ${PIP_BINDIR} ${PIP_FIND_PROGRAM_ARG}) find_program(PIP_RC pip_rc${PIP_BINEXT} HINTS ${PIP_BINDIR} ${PIP_FIND_PROGRAM_ARG}) + find_program(PIP_TR pip_tr${PIP_BINEXT} HINTS ${PIP_BINDIR} ${PIP_FIND_PROGRAM_ARG}) find_program(PIP_DEPLOY_TOOL deploy_tool${PIP_BINEXT} HINTS ${PIP_BINDIR} ${PIP_FIND_PROGRAM_ARG}) endif() if (NOT PIP_LIBRARY) diff --git a/cmake/PIPMacros.cmake b/cmake/PIPMacros.cmake index 81a1972d..5e1aa81b 100644 --- a/cmake/PIPMacros.cmake +++ b/cmake/PIPMacros.cmake @@ -18,6 +18,14 @@ Generate C++ files for resource file You should add ${} to your target + + + pip_translation( ts_file) + + Generate *.btf (binary translation file) from *.ts file + You should add ${} to your target and then install it to somewhere + for later loading in runtime by PITranslator + ]] @@ -119,3 +127,34 @@ macro(pip_resources RESULT INPUT) VERBATIM) endmacro() + +macro(pip_translation RESULT INPUT) + #message(STATUS "src = ${CCM_SRC}") + #message(STATUS "result = ${RESULT}") + #message(STATUS "options = \"${CCM_OPTS}\"") + get_filename_component(BTF_FILENAME "${INPUT}" NAME_WE) + set(BTF_FILENAME "${BTF_FILENAME}.btf") + set(BTF_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${BTF_FILENAME}) + list(APPEND ${RESULT} "${BTF_OUTPUT}") + if(IS_ABSOLUTE "${INPUT}") + set(IN_FILES "${INPUT}") + else() + set(IN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}") + endif() + #message(STATUS "CCM = ${RESULT}") + if(NOT DEFINED PIP_DLL_DIR) + set(PIP_DLL_DIR ${CMAKE_CURRENT_BINARY_DIR}) + endif() + #message("PIP_TR: ${PIP_TR}") + #message("BTF_OUTPUT: ${BTF_OUTPUT}") + #message("IN_FILES: ${IN_FILES}") + #message("PIP_DLL_DIR: ${PIP_DLL_DIR}") + add_custom_command(OUTPUT ${BTF_OUTPUT} + COMMAND ${PIP_TR} + ARGS -C -o "${BTF_OUTPUT}" "${IN_FILES}" + DEPENDS ${IN_FILES} + WORKING_DIRECTORY ${PIP_DLL_DIR} + COMMENT "Generating ${BTF_FILENAME}" + VERBATIM) +endmacro() + diff --git a/lang/pip_ru.ts b/lang/pip_ru.ts new file mode 100644 index 00000000..ab6ce5ce --- /dev/null +++ b/lang/pip_ru.ts @@ -0,0 +1,531 @@ + + + + + PICLI + + + Arguments overflow, "%1" ignored + Переизбыток аргументов, "%1" проигнорирован + + + + PIDiag + + + /s + /сек + + + + PIFile + + + Downsize is not supported yet :-( + Уменьшение размера не поддерживается + + + + PICloud + + + Invalid PICloud::TCP version! + Неверная версия PICloud::TCP! + + + + Error: buffer overflow, drop %1 bytes + Ошибка: переполнение буфера, отброшено %1 байт + + + + Warning: reject client with duplicated ID + Предупреждение: отклонен клиент с дублирующимся ID + + + + PICrypt + + + internal error: bad hash size + внутренняя ошибка: плохой размер хэша + + + + Error while initialize sodium! + Ошибка инициализации sodium! + + + + invalid key size %1, should be %2, filled with zeros + неверный размер ключа %1, должен быть %2, заполненный нулями + + + + Warning: PICrypt is disabled, to enable install sodium library and rebuild pip + Предупреждение: PICrypt неактивен, для активации установите библиотеку sodium и пересоберите PIP + + + + PIBinLog + + + Read record error + Ошибка чтения записи + + + + End of BinLog file + Конец BinLog файла + + + + Error, can't open "%1" + Ошибка, невозможно открыть "%1" + + + + Creating directory "%1" + Создание директории "%1" + + + + Error, can't create "%1" + Ошибка, невозможно создать "%1" + + + + Error: File is null "%1" + Ошибка, Файл пуст "%1" + + + + Start join binlogs to "%1" + Начало слияния логов в "%1" + + + + BinLogFile has too old verion + BinLogFile очень старой версии + + + + BinLogFile has invalid version + BinLogFile неверной версии + + + + BinLogFile has too new version + BinLogFile очень новой версии + + + + Can't find record with id = %1 + Невозможно найти запись с ID = %1 + + + + Error, can't write to file "%1" + Ошибка, невозможно записать в "%1" + + + + Error: Can't open file "%1": %2 + Ошибка: Невозможно открыть файл "%1": %2 + + + + Warning: Empty BinLog file "%1" + Предупреждение: Пустой BinLog файл "%1" + + + + Finish join binlogs, total time %1 + Завершение слияния логов, общее время %1 + + + + too small read buffer: %1, data size: %2 + слишком маленький буфер: %1, размер данных: %2 + + + + Error: Can't write binlog file header "%1" + Ошибка: Невозможно записать заголовок в "%1" + + + + Error: can`t write with id = 0! ID must be > 0 + Ошибка: Невозможно записать с ID = 0! ID должен быть > 0 + + + + BinLogFile signature is corrupted or invalid file + Неверный заголовок BinLogFile, либо файл поврежден + + + + Can't create new file, maybe path "%1" is invalid + Невозможно создать новый файл, возможно путь "%1" неверен + + + + Can't create new file, maybe LogDir "%1" is invalid + Невозможно создать новый файл, возможно LogDir "%1" неверен + + + + Error: ReadWrite mode not supported, use WriteOnly or ReadOnly + Ошибка: Режим ReadWrite не поддерживается, используйте WriteOnly или ReadOnly + + + + PIOpenCL + + + Error: empty range + Ошибка: пустой диапазон + + + + setArgValue invalid index %1 + setArgValue неверный индекс %1 + + + + bindArgValue invalid index %1 + bindArgValue неверный индекс %1 + + + + setArgValue set scalar to "%1 %2" + setArgValue устанавливается скаляр в "%1 %2" + + + + Error: OpenCL platforms not found! + Ошибка: Платформы OpenCL не найдены! + + + + bindArgValue set buffer to "%1 %2" + bindArgValue устанавливается буфер в "%1 %2" + + + + PISerial + + + Read error: %1 + Ошибка чтения: %1 + + + + Unable to open "%1": %2 + Невозможно открыть "%1": %2 + + + + Warning: Custom speed %1 + Предупреждение: Нестандартная скорость %1 + + + + Unable to find device "%1" + Невозможно найти устройство "%1" + + + + Can`t set attributes for "%1" + Невозможно установить атрибуты для "%1" + + + + Unable to set comm state for "%1" + Невозможно установить comm state для "%1" + + + + Warning: Unknown speed %1, using 115200 + Предупреждение: Неизвестная скорость %1, используется 115200 + + + + PIString + + + B + Б + + + + EiB + ЭиБ + + + + GiB + ГиБ + + + + KiB + КиБ + + + + MiB + МиБ + + + + PiB + ПиБ + + + + TiB + ТиБ + + + + YiB + ЙиБ + + + + ZiB + ЗиБ + + + + PIThread + + + Warning, terminate! + Предупреждение, прекращение потока! + + + + Error: Can`t start new thread: %1 + Ошибка: Невозможно начать новый поток: %1 + + + + [PIThread "%1"] Warning, terminate on destructor! + [PIThread "%1"] Предупреждение, прекращение в деструкторе! + + + + PIProcess + + + "CreateProcess" error: %1 + Ошибка "CreateProcess": %1 + + + + PIVariant + + + Can`t initialize PIVariant from unregistered type "%1"! + Невозможно инициализировать PIVariant из незарегистрированного типа "%1"! + + + + Can`t initialize PIVariant from unregistered typeID "%1"! + Невозможно инициализировать PIVariant из незарегистрированного ID типа "%1"! + + + + PICompress + + + Error: invalid input + Ошибка: неверный вход + + + + Error: invalid input or not enought memory + Ошибка: неверный вход или недостаточно памяти + + + + Warning: PICompress is disabled, to enable install zlib library and build pip_compress library + Предупреждение: PICompress неактивен, для активации установите библиотеку zlib и пересоберите PIP + + + + PIEthernet + + + Can`t get interfaces: %1 + Невозможно получить интерфейсы: %1 + + + + Can`t accept new connection, %1 + Невозможно принять новое соединение, %1 + + + + Error allocating memory needed to call GetAdaptersInfo + Ошибка выделения памяти для вызова GetAdaptersInfo + + + + PIIODevice + + + Error: Device is running after destructor! + Ошибка: Устройство в поточном выполнении после деструктора! + + + + PIIOString + + + Error: ReadWrite mode not supported, use WriteOnly or ReadOnly + Ошибка: Режим ReadWrite не поддерживается, используйте WriteOnly или ReadOnly + + + + PIConnection + + + Error, + Ошибка, + + + + Null Device! + Нет Устройства! + + + + No such device "%1"! + Нет устройства "%1"! + + + + No such full path "%1"! + Нет полного пути "%1"! + + + + Device "%1" can`t write! + Устройство "%1" не может писать! + + + + Error: can`t create device "%1"! + Ошибка: Невозможно создать устройство "%1"! + + + + "addFilter" error: no such device "%1"! + ошибка "addFilter": нет устройства "%1"! + + + + "addSender" error: no such device "%1"! + ошибка "addSender": нет устройства "%1"! + + + + names assigned to both devices and filters! + имена назначены одновременно устройствам и фильтрам! + + + + "addFilter" error: no such device or filter "%1"! + ошибка "addFilter": нет устройства или фильтра "%1"! + + + + PISystemTime + + + fromSystemTime() Warning: null frequency + fromSystemTime() Предупреждение: нулевая частота + + + + toSystemTime() Warning: invalid hertz: %1 + toSystemTime() Предупреждение: неверная частота: %1 + + + + PIEthUtilBase + + + PICrypt wasn`t built! + PICrypt не был собран! + + + + PIBaseTransfer + + + invalid CRC + неверная CRC + + + + restart receive + перезапуск приема + + + + invalid reply id + неверный ID ответа + + + + invalid packet signature + неверная подпись пакета + + + + PIClientServer + + + ClientFactory returns nullptr! + ClientFactory вернул nullptr! + + + + Server::newConnection overflow clients count + Server::newConnection переполнение количества клиентов + + + + PIStateMachine + + + Error: "%1" no initial state! + Ошибка: "%1" без стартового состояния! + + + + PIStreamPacker + + + Warning! Not recommended to use with non-reliable device + Предупреждение! Не рекомендуется использовать с ненадежными устройствами + + + + PISystemMonitor + + + Can`t find process with ID = %1! + Невозможно найти процесс с ID = %1! + + + + Can`t open process with ID = %1, %2! + Невозможно открыть процесс с ID = %1, %2! + + + diff --git a/lang/update.bat b/lang/update.bat new file mode 100644 index 00000000..2acb1aec --- /dev/null +++ b/lang/update.bat @@ -0,0 +1 @@ +pip_tr --Parse -r -l ru -o pip_ru.ts ../libs \ No newline at end of file diff --git a/libs/client_server/piclientserver_server.cpp b/libs/client_server/piclientserver_server.cpp index 4a6ec693..d31087bb 100644 --- a/libs/client_server/piclientserver_server.cpp +++ b/libs/client_server/piclientserver_server.cpp @@ -20,7 +20,7 @@ #include "piclientserver_client.h" #include "piethernet.h" -#include "piliterals_time.h" +#include "pitranslator.h" PIClientServer::Server::Server() { @@ -30,13 +30,13 @@ PIClientServer::Server::Server() { CONNECTL(tcp_server, newConnection, [this](PIEthernet * c) { PIMutexLocker guard(clients_mutex); if (clients.size_s() >= max_clients) { - piCout << "Server::newConnection overflow clients count"; + piCout << "Server::newConnection overflow clients count"_tr("PIClientServer"); delete c; return; } auto sc = client_factory(); if (!sc) { - piCout << "ClientFactory returns nullptr!"; + piCout << "ClientFactory returns nullptr!"_tr("PIClientServer"); return; } sc->createForServer(this, c); diff --git a/libs/cloud/picloudclient.cpp b/libs/cloud/picloudclient.cpp index 30ccab2c..dbed44ca 100644 --- a/libs/cloud/picloudclient.cpp +++ b/libs/cloud/picloudclient.cpp @@ -20,6 +20,7 @@ #include "picloudclient.h" #include "picloudtcp.h" +#include "pitranslator.h" PICloudClient::PICloudClient(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(path, mode), PICloudBase() { @@ -174,7 +175,7 @@ void PICloudClient::_readed(PIByteArray & ba) { if (is_connected) { mutex_buff.lock(); if (buff.size_s() > threadedReadBufferSize()) { - piCoutObj << "Error: buffer overflow, drop" << ba.size() << "bytes"; + piCoutObj << "Error: buffer overflow, drop %1 bytes"_tr("PICloud").arg(ba.size()); mutex_buff.unlock(); return; } diff --git a/libs/cloud/picloudserver.cpp b/libs/cloud/picloudserver.cpp index 14f519bf..709f94ef 100644 --- a/libs/cloud/picloudserver.cpp +++ b/libs/cloud/picloudserver.cpp @@ -20,6 +20,7 @@ #include "picloudserver.h" #include "piliterals_time.h" +#include "pitranslator.h" PICloudServer::PICloudServer(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(path, mode), PICloudBase() { @@ -226,7 +227,7 @@ void PICloudServer::Client::pushBuffer(const PIByteArray & ba) { if (!is_connected) return; mutex_buff.lock(); if (buff.size_s() > threadedReadBufferSize()) { - piCoutObj << "Error: buffer overflow, drop" << ba.size() << "bytes"; + piCoutObj << "Error: buffer overflow, drop %1 bytes"_tr("PICloud").arg(ba.size()); mutex_buff.unlock(); return; } @@ -247,7 +248,7 @@ void PICloudServer::_readed(PIByteArray & ba) { Client * oc = index_clients.value(id, nullptr); clients_mutex.unlock(); if (oc) { - piCoutObj << "Warning: reject client with duplicated ID"; + piCoutObj << "Warning: reject client with duplicated ID"_tr("PICloud"); tcp.sendDisconnected(id); } else { Client * c = new Client(this, id); diff --git a/libs/cloud/picloudtcp.cpp b/libs/cloud/picloudtcp.cpp index 861a8069..e64d177c 100644 --- a/libs/cloud/picloudtcp.cpp +++ b/libs/cloud/picloudtcp.cpp @@ -23,6 +23,7 @@ #include "picrypt.h" #include "piethernet.h" #include "pistreampacker.h" +#include "pitranslator.h" const char hash_cloud_key[] = "_picloud_"; @@ -95,7 +96,7 @@ int PICloud::TCP::sendData(const PIByteArray & data) { PIByteArray ba; ba << header; ba.append(data); - // piCout << "[PICloud::TCP] sendData" << ba.toHex(); + // piCout << "[PICloud::TCP] sendData" << ba.toHex(); mutex_send.lock(); streampacker->send(ba); mutex_send.unlock(); @@ -135,7 +136,7 @@ PIPair PICloud::TCP::parseHeader(PIByteA ba >> hdr; if (hdr.version != header.version) { piCout << "[PICloud]" - << "invalid PICloud::TCP version!"; + << "Invalid PICloud::TCP version!"_tr("PICloud"); return ret; } ret.first = (Type)hdr.type; diff --git a/libs/compress/picompress.cpp b/libs/compress/picompress.cpp index b82f6208..90e915e9 100644 --- a/libs/compress/picompress.cpp +++ b/libs/compress/picompress.cpp @@ -18,6 +18,8 @@ */ #include "picompress.h" + +#include "pitranslator.h" #ifdef PIP_COMPRESS # ifdef ESP_PLATFORM # include "esp32/rom/miniz.h" @@ -39,7 +41,7 @@ PIByteArray piCompress(const PIByteArray & ba, int level) { ret = compress2(zba.data(), &sz, ba.data(), ba.size(), level); if (ret != Z_OK) { piCout << "[PICompress]" - << "Error: invalid input or not enought memory"; + << "Error: invalid input or not enought memory"_tr("PICompress"); return ba; } zba.resize(sz); @@ -47,7 +49,7 @@ PIByteArray piCompress(const PIByteArray & ba, int level) { return zba; #else piCout << "[PICompress]" - << "Warning: PICompress is disabled, to enable install zlib library and build pip_compress library"; + << "Warning: PICompress is disabled, to enable install zlib library and build pip_compress library"_tr("PICompress"); #endif return ba; } @@ -58,7 +60,7 @@ PIByteArray piDecompress(const PIByteArray & zba) { ullong sz = 0; if (zba.size() < sizeof(ullong)) { piCout << "[PICompress]" - << "Error: invalid input"; + << "Error: invalid input"_tr("PICompress"); return zba; } PIByteArray ba(zba.data(zba.size() - sizeof(ullong)), sizeof(ullong)); @@ -69,13 +71,13 @@ PIByteArray piDecompress(const PIByteArray & zba) { ret = uncompress(ba.data(), &s, zba.data(), zba.size()); if (ret != Z_OK) { piCout << "[PICompress]" - << "Error: invalid input or not enought memory"; + << "Error: invalid input or not enought memory"_tr("PICompress"); return zba; } return ba; #else piCout << "[PICompress]" - << "Warning: PICompress is disabled, to enable install zlib library and build pip_compress library"; + << "Warning: PICompress is disabled, to enable install zlib library and build pip_compress library"_tr("PICompress"); #endif return zba; } diff --git a/libs/crypt/picrypt.cpp b/libs/crypt/picrypt.cpp index 38e55853..0220c155 100644 --- a/libs/crypt/picrypt.cpp +++ b/libs/crypt/picrypt.cpp @@ -18,13 +18,15 @@ */ #include "picrypt.h" + +#include "pitranslator.h" #ifdef PIP_CRYPT # include #endif #define PICRYPT_DISABLED_WARNING \ piCout << "[PICrypt]" \ - << "Warning: PICrypt is disabled, to enable install sodium library and rebuild pip"; + << "Warning: PICrypt is disabled, to enable install sodium library and rebuild pip"_tr("PICrypt"); const char hash_def_key[] = "_picrypt_\0\0\0\0\0\0\0"; const int hash_def_key_size = 9; @@ -34,7 +36,7 @@ PICrypt::PICrypt() { #ifdef PIP_CRYPT if (!init()) piCout << "[PICrypt]" - << "Error while initialize sodium!"; + << "Error while initialize sodium!"_tr("PICrypt"); nonce_.resize(crypto_secretbox_NONCEBYTES); key_.resize(crypto_secretbox_KEYBYTES); randombytes_buf(key_.data(), key_.size()); @@ -107,7 +109,7 @@ PIByteArray PICrypt::decrypt(const PIByteArray & crypt_data, bool * ok) { memcpy(nonce_.data(), crypt_data.data(crypt_data.size() - nonce_.size()), nonce_.size()); if (crypto_secretbox_open_easy(ret.data(), crypt_data.data(), crypt_data.size() - nonce_.size(), nonce_.data(), key_.data()) != 0) { if (ok) *ok = false; - // piCout << "[PICrypt]" << "bad key_"; + // piCout << "[PICrypt]" << "bad key_"; return PIByteArray(); } #endif @@ -134,7 +136,7 @@ PIByteArray PICrypt::decrypt(const PIByteArray & crypt_data, PIByteArray key, bo memcpy(n.data(), crypt_data.data(crypt_data.size() - n.size()), n.size()); if (crypto_secretbox_open_easy(ret.data(), crypt_data.data(), crypt_data.size() - n.size(), n.data(), key.data()) != 0) { if (ok) *ok = false; - // piCout << "[PICrypt]" << "bad key_"; + // piCout << "[PICrypt]" << "bad key_"; return PIByteArray(); } else if (ok) *ok = true; @@ -200,11 +202,11 @@ ullong PICrypt::shorthash(const PIString & s, PIByteArray key) { #ifdef PIP_CRYPT if (crypto_shorthash_BYTES != sizeof(hash)) piCout << "[PICrypt]" - << "internal error: bad hash size"; + << "internal error: bad hash size"_tr("PICrypt"); if (!init()) return hash; if (key.size() != crypto_shorthash_KEYBYTES) { piCout << "[PICrypt]" - << "invalid key size" << key.size() << ", shoud be" << crypto_shorthash_KEYBYTES << ", filled zeros"; + << "invalid key size %1, should be %2, filled with zeros"_tr("PICrypt").arg(key.size()).arg(crypto_shorthash_KEYBYTES); key.resize(crypto_shorthash_KEYBYTES, 0); } PIByteArray in(s.data(), s.size()); @@ -389,7 +391,7 @@ PIByteArray PICrypt::decrypt(const PIByteArray & crypt_data, const PIByteArray & if (crypto_box_open_easy(ret.data(), crypt_data.data(), crypt_data.size() - n.size(), n.data(), public_key.data(), secret_key.data()) != 0) { if (ok) *ok = false; - // piCout << "[PICrypt]" << "bad key_"; + // piCout << "[PICrypt]" << "bad key_"; return PIByteArray(); } else if (ok) *ok = true; diff --git a/libs/fftw/pifft_p.h b/libs/fftw/pifft_p.h index 45795601..39aa3a75 100644 --- a/libs/fftw/pifft_p.h +++ b/libs/fftw/pifft_p.h @@ -24,6 +24,7 @@ #define PIFFT_P_H #include "picout.h" +#include "pimathcomplex.h" #include "pivector.h" #if defined(PIP_FFTW) || defined(PIP_FFTWf) || defined(PIP_FFTWl) || defined(PIP_FFTWq) # include "fftw3.h" @@ -41,7 +42,7 @@ public: explicit PIFFTW_Private() { plan = 0; // #ifndef PIP_FFTW - // piCout << "[PIFFTW]" << "Warning: PIFFTW is disabled, to enable install libfftw3-dev library and build pip with -DFFTW=1"; + // piCout << "[PIFFTW]" << "Warning: PIFFTW is disabled, to enable install libfftw3-dev library and build pip with -DFFTW=1"; // #endif p_makeThreadSafe(); } diff --git a/libs/io_utils/piethutilbase.cpp b/libs/io_utils/piethutilbase.cpp index 5121239d..dfa24d02 100644 --- a/libs/io_utils/piethutilbase.cpp +++ b/libs/io_utils/piethutilbase.cpp @@ -18,6 +18,8 @@ */ #include "piethutilbase.h" + +#include "pitranslator.h" #ifdef PIP_CRYPT # include "picrypt.h" #endif @@ -85,7 +87,8 @@ void PIEthUtilBase::createCryptKey(const PIString & k) { _key = PICrypt::hash("sodium_bug"); _key = PICrypt::hash(k); #else - piCout << "[PIEthUtilBase] PICrypt wasn`t built!"; + piCout << "[PIEthUtilBase]" + << "PICrypt wasn`t built!"_tr("PIEthUtilBase"); #endif _crypt = true; } diff --git a/libs/io_utils/pistreampacker.cpp b/libs/io_utils/pistreampacker.cpp index 7ec34a18..a062a149 100644 --- a/libs/io_utils/pistreampacker.cpp +++ b/libs/io_utils/pistreampacker.cpp @@ -25,6 +25,7 @@ #include "piiodevice.h" #include "piliterals_bytes.h" +#include "pitranslator.h" #ifdef __GNUC__ # pragma GCC diagnostic pop #endif @@ -214,7 +215,7 @@ void PIStreamPacker::received(const PIByteArray & data) { void PIStreamPacker::assignDevice(PIIODevice * dev) { if (!dev) return; if (!dev->infoFlags()[PIIODevice::Reliable]) { - piCoutObj << "Warning! Not recommended to use with non-reliable" << dev; + piCoutObj << "Warning! Not recommended to use with non-reliable device"_tr("PIStreamPacker") << dev; } CONNECT2(void, const uchar *, ssize_t, dev, threadedReadEvent, this, received); CONNECT1(void, PIByteArray, this, sendRequest, dev, write); diff --git a/libs/main/application/picli.cpp b/libs/main/application/picli.cpp index 57553cb9..3f16ec78 100644 --- a/libs/main/application/picli.cpp +++ b/libs/main/application/picli.cpp @@ -20,6 +20,7 @@ #include "picli.h" #include "pisysteminfo.h" +#include "pitranslator.h" //! \class PICLI picli.h @@ -116,7 +117,7 @@ void PICLI::parse() { _args_opt << cra; continue; } - piCoutObj << "Arguments overflow, \"" << cra << "\" ignored"; + piCoutObj << "Arguments overflow, \"%1\" ignored"_tr("PICLI").arg(cra); } if (last == 0 ? false : last->has_value) { last->value = cra; diff --git a/libs/main/application/pisystemmonitor.cpp b/libs/main/application/pisystemmonitor.cpp index 98a5e428..2f51237e 100644 --- a/libs/main/application/pisystemmonitor.cpp +++ b/libs/main/application/pisystemmonitor.cpp @@ -108,14 +108,14 @@ bool PISystemMonitor::startOnProcess(int pID, PISystemTime interval) { PRIVATE->file.open(PRIVATE->proc_dir + "stat", PIIODevice::ReadOnly); PRIVATE->filem.open(PRIVATE->proc_dir + "statm", PIIODevice::ReadOnly); if (!PRIVATE->file.isOpened()) { - piCoutObj << "Can`t find process with ID = " << pID_ << "!"; + piCoutObj << "Can`t find process with ID = %1!"_tr("PISystemMonitor").arg(pID_); return false; } # endif # else PRIVATE->hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pID_); if (PRIVATE->hProc == 0) { - piCoutObj << "Can`t open process with ID = "_tr << pID_ << "," << errorString(); + piCoutObj << "Can`t open process with ID = %1, %2!"_tr("PISystemMonitor").arg(pID_).arg(errorString()); return false; } PRIVATE->tm.reset(); @@ -377,12 +377,12 @@ void PISystemMonitor::gatherThread(llong id) { FILETIME times[4]; HANDLE thdl = OpenThread(THREAD_QUERY_INFORMATION, FALSE, DWORD(id)); if (!thdl) { - piCout << "[PISystemMonitor] gatherThread(" << id << "):: OpenThread() error:" << errorString(); + piCoutObj << "GatherThread(" << id << "):: OpenThread() error:" << errorString(); return; } if (GetThreadTimes(thdl, &(times[0]), &(times[1]), &(times[2]), &(times[3])) == 0) { CloseHandle(thdl); - piCout << "[PISystemMonitor] gatherThread(" << id << "):: GetThreadTimes() error:" << errorString(); + piCoutObj << "GatherThread(" << id << "):: GetThreadTimes() error:" << errorString(); return; } CloseHandle(thdl); diff --git a/libs/main/application/pitranslator.cpp b/libs/main/application/pitranslator.cpp index 674fd4d6..b2d1c009 100644 --- a/libs/main/application/pitranslator.cpp +++ b/libs/main/application/pitranslator.cpp @@ -19,8 +19,11 @@ #include "pitranslator.h" +#include "pidir.h" +#include "pifile.h" +#include "piliterals_string.h" +#include "pitranslator_p.h" #include "pivaluetree_conversions.h" -#include "tr/pip_all.h" //! \class PITranslator pitranslator.h @@ -33,28 +36,38 @@ //! +PRIVATE_DEFINITION_START(PITranslator) + PITranslatorPrivate::Translation content; +PRIVATE_DEFINITION_END(PITranslator) + + PIString PITranslator::tr(const PIString & in, const PIString & context) { - auto s = instance(); - auto c = s->contexts.value(context.hash()); - if (!c) return in; - return c->strings.value(in.hash(), in); + return instance()->PRIVATEWB->content.translate(in, context); } void PITranslator::clear() { - instance()->clearInternal(); + instance()->PRIVATEWB->content.clear(); } -void PITranslator::loadLang(const PIString & short_lang) { - auto s = instance(); +void PITranslator::loadLang(const PIString & short_lang, PIString dir) { + if (dir.isEmpty()) dir = PIDir::current().absolute("lang"); + clear(); + auto files = PIDir(dir).entries(); + for (const auto & f: files) { + if (!f.baseName().endsWith(short_lang)) continue; + loadFile(f.path); + } + piCout << "Loaded %1 string for lang \"%2\""_a.arg(instance()->PRIVATEWB->content.count()).arg(short_lang); + /*auto s = instance(); auto vt = PIValueTreeConversions::fromText(getBuiltinConfig()); auto lang = vt.child(short_lang.toLowerCase().trim()); for (const auto & cn: lang.children()) { - auto c = s->createContextInternal(cn.name()); + auto c = s->PRIVATEWB->content.createContext(cn.name()); for (const auto & s: cn.children()) c->add(s.name(), s.value().toString()); - } + }*/ } @@ -62,11 +75,11 @@ void PITranslator::loadConfig(const PIString & content) { auto s = instance(); auto lang = PIValueTreeConversions::fromText(content); for (const auto & cn: lang.children()) { - auto c = s->createContextInternal(cn.name()); + auto c = s->PRIVATEWB->content.createContext(cn.name()); for (const auto & s: cn.children()) c->add(s.name(), s.value().toString()); } - auto c = s->createContextInternal(""); + auto c = s->PRIVATEWB->content.createContext(""); for (const auto & s: lang.children()) { if (s.hasChildren()) continue; c->add(s.name(), s.value().toString()); @@ -74,11 +87,26 @@ void PITranslator::loadConfig(const PIString & content) { } +bool PITranslator::load(const PIByteArray & content) { + return instance()->PRIVATEWB->content.load(content); +} + + +bool PITranslator::loadFile(const PIString & path) { + PIFile f(path, PIIODevice::ReadOnly); + if (f.isClosed()) return false; + if (!PITranslatorPrivate::checkHeader(&f)) return false; + auto data = f.readAll(); + data.remove(0, PITranslatorPrivate::headerSize()); + return load(data); +} + + PITranslator::PITranslator() {} PITranslator::~PITranslator() { - clearInternal(); + PRIVATE->content.clear(); } @@ -86,21 +114,3 @@ PITranslator * PITranslator::instance() { static PITranslator ret; return &ret; } - - -void PITranslator::clearInternal() { - piDeleteAll(contexts.values()); - contexts.clear(); -} - - -PITranslator::Context * PITranslator::createContextInternal(const PIString & context) { - auto & ret(contexts[context.hash()]); - if (!ret) ret = new Context(); - return ret; -} - - -void PITranslator::Context::add(const PIString & in, const PIString & out) { - strings[in.hash()] = out; -} diff --git a/libs/main/application/pitranslator.h b/libs/main/application/pitranslator.h index 6ccdf47a..c4435a80 100644 --- a/libs/main/application/pitranslator.h +++ b/libs/main/application/pitranslator.h @@ -29,7 +29,8 @@ #include "pistring.h" -#define piTr PITranslator::tr +#define piTr PITranslator::tr +#define piTrNoOp PITranslator::trNoOp //! \ingroup Application //! \~\brief @@ -39,34 +40,59 @@ class PIP_EXPORT PITranslator { public: static PIString tr(const PIString & in, const PIString & context = {}); static PIString tr(const char * in, const PIString & context = {}) { return tr(PIString::fromUTF8(in), context); } + static PIString trNoOp(const PIString & in, const PIString & context = {}) { return in; } + static PIString trNoOp(const char * in, const PIString & context = {}) { return trNoOp(PIString::fromUTF8(in), context); } static void clear(); - static void loadLang(const PIString & short_lang); + static void loadLang(const PIString & short_lang, PIString dir = {}); static void loadConfig(const PIString & content); + static bool load(const PIByteArray & content); + static bool loadFile(const PIString & path); private: PITranslator(); ~PITranslator(); NO_COPY_CLASS(PITranslator) - - struct Context { - void add(const PIString & in, const PIString & out); - PIMap strings; - }; + PRIVATE_DECLARATION(PIP_EXPORT) static PITranslator * instance(); - - void clearInternal(); - Context * createContextInternal(const PIString & context); - - PIMap contexts; }; + +class PIStringContextTr { +public: + PIStringContextTr(PIString && s): _s(s) {} + operator PIString() const { return PITranslator::tr(_s); } + PIString operator()(const PIString & ctx = {}) const { return PITranslator::tr(_s, ctx); } + +private: + PIString _s; +}; + + +class PIStringContextTrNoOp { +public: + PIStringContextTrNoOp(PIString && s): _s(s) {} + operator PIString() const { return _s; } + PIString operator()(const PIString & ctx = {}) const { return _s; } + +private: + PIString _s; +}; + + //! \~\brief -//! \~english PIString from UTF-8 -//! \~russian PIString из UTF-8 -inline PIString operator""_tr(const char * v, size_t sz) { - return PITranslator::tr(PIString::fromUTF8(v, sz)); +//! \~english Translate string with \a PITranslator::tr() +//! \~russian Перевести строку с помощью \a PITranslator::tr() +inline PIStringContextTr operator""_tr(const char * v, size_t sz) { + return PIStringContextTr(PIString::fromUTF8(v, sz)); +} + +//! \~\brief +//! \~english Translate string with \a PITranslator::tr() +//! \~russian Перевести строку с помощью \a PITranslator::tr() +inline PIStringContextTrNoOp operator""_trNoOp(const char * v, size_t sz) { + return PIStringContextTrNoOp(PIString::fromUTF8(v, sz)); } #endif diff --git a/libs/main/application/pitranslator_p.cpp b/libs/main/application/pitranslator_p.cpp new file mode 100644 index 00000000..8d28a5d3 --- /dev/null +++ b/libs/main/application/pitranslator_p.cpp @@ -0,0 +1,124 @@ +/* + PIP - Platform Independent Primitives + Translation private + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "pitranslator_p.h" + +#include "pichunkstream.h" +#include "piiodevice.h" + +constexpr int currentVersion = 1; +const PIByteArray fileHeader("PIPBTF", 6); + + +void PITranslatorPrivate::Context::add(const PIMap & sm) { + auto it = sm.makeIterator(); + while (it.next()) + strings[it.key()] = it.value(); +} + + +void PITranslatorPrivate::Translation::clear() { + piDeleteAll(contexts.values()); + contexts.clear(); + lang.clear(); +} + + +PITranslatorPrivate::Context * PITranslatorPrivate::Translation::createContext(const PIString & context) { + return createContext(context.hash()); +} + + +PITranslatorPrivate::Context * PITranslatorPrivate::Translation::createContext(uint hash) { + auto & ret(contexts[hash]); + if (!ret) ret = new Context(); + return ret; +} + + +PIString PITranslatorPrivate::Translation::translate(const PIString & in, const PIString & context) { + auto c = contexts.value(context.hash()); + if (!c) return in; + return c->strings.value(in.hash(), in); +} + + +bool PITranslatorPrivate::Translation::load(const PIByteArray & data) { + if (data.size_s() <= 4) return false; + PIChunkStream cs(data); + Context * ctx = nullptr; + PIMap strings; + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: { + int version = cs.getData(); + if (version != currentVersion) { + piCout << "Invalid translation version!"; + return false; + } + } break; + case 3: { + uint ctx_hash = cs.getData(); + ctx = createContext(ctx_hash); + } break; + case 4: { + cs.get(strings); + if (ctx) ctx->add(strings); + } break; + } + } + return true; +} + + +int PITranslatorPrivate::Translation::count() const { + int ret = 0; + auto cit = contexts.makeIterator(); + while (cit.next()) + ret += cit.value()->strings.size_s(); + return ret; +} + + +PIByteArray PITranslatorPrivate::Translation::save() { + PIChunkStream cs; + cs.add(1, currentVersion).add(2, lang); + auto cit = contexts.makeIterator(); + while (cit.next()) { + cs.add(3, cit.key()).add(4, cit.value()->strings); + } + return cs.data(); +} + + +int PITranslatorPrivate::headerSize() { + return fileHeader.size_s(); +} + + +bool PITranslatorPrivate::checkHeader(PIIODevice * d) { + if (!d) return false; + return d->read(fileHeader.size_s()) == fileHeader; +} + + +void PITranslatorPrivate::writeHeader(PIIODevice * d) { + if (!d) return; + d->write(fileHeader); +} diff --git a/libs/main/application/pitranslator_p.h b/libs/main/application/pitranslator_p.h new file mode 100644 index 00000000..8479b0ae --- /dev/null +++ b/libs/main/application/pitranslator_p.h @@ -0,0 +1,53 @@ +/* + PIP - Platform Independent Primitives + Translation private + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef pitranslator_p_H +#define pitranslator_p_H + +#include "pistring.h" + +class PIIODevice; + +namespace PITranslatorPrivate { + +struct PIP_EXPORT Context { + void add(const PIString & in, const PIString & out) { strings[in.hash()] = out; } + void add(const PIMap & sm); + PIMap strings; +}; + +struct PIP_EXPORT Translation { + void clear(); + Context * createContext(const PIString & context); + Context * createContext(uint hash); + PIString translate(const PIString & in, const PIString & context); + bool load(const PIByteArray & data); + int count() const; + PIByteArray save(); + PIString lang; + PIMap contexts; +}; + +PIP_EXPORT int headerSize(); +PIP_EXPORT bool checkHeader(PIIODevice * d); +PIP_EXPORT void writeHeader(PIIODevice * d); + +} // namespace PITranslatorPrivate + +#endif diff --git a/libs/main/application/tr/pip_all.h b/libs/main/application/tr/pip_all.h deleted file mode 100644 index 476ffadb..00000000 --- a/libs/main/application/tr/pip_all.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef pitr_pip_all_H -#define pitr_pip_all_H - -#include "pip_ru.h" -#include "pistring.h" - -PIString getBuiltinConfig() { - // clang-format off - static const PIString ret = - PIString::fromUTF8(config_ru) - ; - // clang-format on - return ret; -} - -#endif diff --git a/libs/main/application/tr/pip_ru.h b/libs/main/application/tr/pip_ru.h deleted file mode 100644 index acc1ae56..00000000 --- a/libs/main/application/tr/pip_ru.h +++ /dev/null @@ -1,25 +0,0 @@ -constexpr char config_ru[] = "\ -[ru.PIString] \n\ -B = Б \n\ -KiB = KиБ \n\ -MiB = MиБ \n\ -GiB = ГиБ \n\ -TiB = ТиБ \n\ -PiB = ПиБ \n\ -EiB = ЭиБ \n\ -ZiB = ЗиБ \n\ -YiB = ЙиБ \n\ -\n\ -[ru.PIDiag] \n\ -/s = /сек \n\ -\n\ -[ru.PITime] \n\ -Hz = Гц \n\ -KHz = КГц \n\ -kHz = кГц \n\ -MHz = МГц \n\ -GHz = ГГц \n\ -THz = ТГц \n\ -PHz = ПГц \n\ -\n\ -"; diff --git a/libs/main/io_devices/pibinarylog.cpp b/libs/main/io_devices/pibinarylog.cpp index 55c2433f..eea1fd7a 100644 --- a/libs/main/io_devices/pibinarylog.cpp +++ b/libs/main/io_devices/pibinarylog.cpp @@ -24,6 +24,7 @@ #include "piliterals_time.h" #include "pipropertystorage.h" #include "pitime.h" +#include "pitranslator.h" #define PIBINARYLOG_VERSION_OLD 0x31 @@ -100,7 +101,7 @@ bool PIBinaryLog::openDevice() { index.clear(); log_size = 0; if (mode_ == ReadWrite) { - piCoutObj << "Error: ReadWrite mode not supported, use WriteOnly or ReadOnly"; + piCoutObj << "Error: ReadWrite mode not supported, use WriteOnly or ReadOnly"_tr("PIBinLog"); return false; } if (path().isEmpty() && mode_ == WriteOnly) { @@ -122,21 +123,21 @@ bool PIBinaryLog::openDevice() { } } if (!file.open(path(), mode_)) { - piCoutObj << "Error: Can't open file" << path(); + piCoutObj << "Error: Can't open file \"%1\": %2"_tr("PIBinLog").arg(path()).arg(errorString()); return false; } setName(path()); if (mode_ == WriteOnly) { file.clear(); if (!writeFileHeader()) { - piCoutObj << "Error: Can't write binlog file header" << path(); + piCoutObj << "Error: Can't write binlog file header \"%1\""_tr("PIBinLog").arg(path()); return false; } is_started = true; } if (mode_ == ReadOnly) { if (file.isEmpty()) { - piCoutObj << "Error: File is null" << path(); + piCoutObj << "Error: File is null \"%1\""_tr("PIBinLog").arg(path()); fileError(); return false; } @@ -145,7 +146,7 @@ bool PIBinaryLog::openDevice() { return false; } if (isEmpty()) { - piCoutObj << "Warning: Empty BinLog file" << path(); + piCoutObj << "Warning: Empty BinLog file \"%1\""_tr("PIBinLog").arg(path()); fileEnd(); } play_time = 0; @@ -175,7 +176,7 @@ bool PIBinaryLog::closeDevice() { bool PIBinaryLog::threadedRead(const uchar * readed, ssize_t size) { - // piCout << "binlog threaded read"; + // piCout << "binlog threaded read"; if (!canRead() || isEnd()) return PIIODevice::threadedRead(readed, size); is_thread_ok = false; logmutex.lock(); @@ -258,7 +259,7 @@ PIString PIBinaryLog::getLogfilePath(const PIString & log_dir, const PIString & dir.setDir(dir.absolutePath()); if (!dir.isExists()) { piCout << "[PIBinaryLog]" - << "Creating directory" << dir.path(); + << "Creating directory \"%1\""_tr("PIBinLog").arg(dir.path()); dir.make(true); } const PIString npath = log_dir + PIDir::separator + prefix + PIDateTime::current().toString("yyyy_MM_dd__hh_mm_ss"); @@ -282,7 +283,7 @@ PIString PIBinaryLog::createNewFile() { newFile(file.path()); return file.path(); } - piCoutObj << "Can't create new file, maybe LogDir" << ("\"" + logDir() + "\"") << "is invalid."; + piCoutObj << "Can't create new file, maybe LogDir \"%1\" is invalid"_tr("PIBinLog").arg(logDir()); return PIString(); } @@ -291,7 +292,7 @@ void PIBinaryLog::createNewFile(const PIString & path) { if (open(path, PIIODevice::WriteOnly)) { newFile(file.path()); } else - piCoutObj << "Can't create new file, maybe path" << ("\"" + path + "\"") << "is invalid."; + piCoutObj << "Can't create new file, maybe path \"%1\" is invalid"_tr("PIBinLog").arg(path); } @@ -315,7 +316,7 @@ void PIBinaryLog::setPause(bool pause) { int PIBinaryLog::writeBinLog(int id, const void * data, int size) { if (size <= 0 || !canWrite()) return -1; if (id == 0) { - piCoutObj << "Error: can`t write with id = 0! Id must be > 0"; + piCoutObj << "Error: can`t write with id = 0! ID must be > 0"_tr("PIBinLog"); return -1; } if (is_pause) return 0; @@ -359,7 +360,7 @@ PIByteArray PIBinaryLog::readBinLog(int id, PISystemTime * time, int * readed_id Record br = readRecord(); logmutex.unlock(); if (br.id == -1) { - piCoutObj << "End of BinLog file"; + piCoutObj << "End of BinLog file"_tr("PIBinLog"); fileEnd(); return PIByteArray(); } @@ -373,7 +374,7 @@ PIByteArray PIBinaryLog::readBinLog(int id, PISystemTime * time, int * readed_id br = readRecord(); logmutex.unlock(); if (br.id == -1) { - piCoutObj << "End of BinLog file"; + piCoutObj << "End of BinLog file"_tr("PIBinLog"); fileEnd(); return PIByteArray(); } @@ -382,7 +383,7 @@ PIByteArray PIBinaryLog::readBinLog(int id, PISystemTime * time, int * readed_id if (readed_id) *readed_id = br.id; return br.data; } - piCoutObj << "Can't find record with id =" << id; + piCoutObj << "Can't find record with id = %1"_tr("PIBinLog").arg(id); return PIByteArray(); } @@ -428,15 +429,15 @@ ssize_t PIBinaryLog::readDevice(void * read_to, ssize_t max_size) { } if (br.id == -1) { fileEnd(); - piCoutObj << "End of BinLog file"; + piCoutObj << "End of BinLog file"_tr("PIBinLog"); return 0; } if (br.id == 0) { - piCoutObj << "Read record error"; + piCoutObj << "Read record error"_tr("PIBinLog"); return -1; } const ssize_t sz = piMini(max_size, br.data.size()); - if (sz < br.data.size_s()) piCoutObj << "too small read buffer:" << max_size << ", data size:" << br.data.size(); + if (sz < br.data.size_s()) piCoutObj << "too small read buffer: %1, data size: %2"_tr("PIBinLog").arg(max_size).arg(br.data.size()); memcpy(read_to, br.data.data(), sz); return sz; } @@ -491,7 +492,7 @@ bool PIBinaryLog::checkFileHeader() { for (uint i = 0; i < PIBINARYLOG_SIGNATURE_SIZE; i++) if (read_sig[i] != binlog_sig[i]) correct = false; if (!correct) { - piCoutObj << "BinLogFile signature is corrupted or invalid file"; + piCoutObj << "BinLogFile signature is corrupted or invalid file"_tr("PIBinLog"); return false; } uchar read_version = 0; @@ -509,9 +510,9 @@ bool PIBinaryLog::checkFileHeader() { } return true; } - if (read_version == 0) piCoutObj << "BinLogFile has invalid version"; - if (read_version < PIBINARYLOG_VERSION) piCoutObj << "BinLogFile has too old verion"; - if (read_version > PIBINARYLOG_VERSION) piCoutObj << "BinLogFile has too newest version"; + if (read_version == 0) piCoutObj << "BinLogFile has invalid version"_tr("PIBinLog"); + if (read_version < PIBINARYLOG_VERSION) piCoutObj << "BinLogFile has too old verion"_tr("PIBinLog"); + if (read_version > PIBINARYLOG_VERSION) piCoutObj << "BinLogFile has too new version"_tr("PIBinLog"); return false; } @@ -545,7 +546,7 @@ PIBinaryLog::Record PIBinaryLog::readRecord() { if (br.id == 0) fileError(); moveIndex(index.index_pos.value(file.pos(), -1)); logmutex.unlock(); - // piCoutObj << "readRecord done"; + // piCoutObj << "readRecord done"; return br; } @@ -721,7 +722,7 @@ bool PIBinaryLog::cutBinLog(const PIBinaryLog::BinLogInfo & src, const PIString PIBinaryLog slog; if (!slog.open(src.path, PIIODevice::ReadOnly)) { piCout << "[PIBinaryLog]" - << "Error, can't open" << src.path; + << "Error, can't open \"%1\""_tr("PIBinLog").arg(src.path); return false; } const PIVector ids = src.records.keys(); @@ -730,7 +731,7 @@ bool PIBinaryLog::cutBinLog(const PIBinaryLog::BinLogInfo & src, const PIString dlog.createNewFile(dst); if (!dlog.isOpened()) { piCout << "[PIBinaryLog]" - << "Error, can't create" << dst; + << "Error, can't create \"%1\""_tr("PIBinLog").arg(dst); return false; } bool first = true; @@ -746,7 +747,7 @@ bool PIBinaryLog::cutBinLog(const PIBinaryLog::BinLogInfo & src, const PIString if (ids.contains(br.id)) { if (dlog.writeBinLog_raw(br.id, br.timestamp - st, br.data) <= 0) { piCout << "[PIBinaryLog]" - << "Error, can't write to file" << dst; + << "Error, can't write to file \"%1\""_tr("PIBinLog").arg(dst); return false; } } @@ -772,7 +773,7 @@ bool PIBinaryLog::joinBinLogsSerial(const PIStringList & src, for (const PIString & fn: src) { if (!slog.open(fn, PIIODevice::ReadOnly)) { piCout << "[PIBinaryLog]" - << "Error, can't open" << fn; + << "Error, can't open \"%1\""_tr("PIBinLog").arg(fn); return false; } if (first) { @@ -781,11 +782,11 @@ bool PIBinaryLog::joinBinLogsSerial(const PIStringList & src, dlog.createNewFile(dst); if (!dlog.isOpened()) { piCout << "[PIBinaryLog]" - << "Error, can't create" << dst; + << "Error, can't create \"%1\""_tr("PIBinLog").arg(dst); return false; } piCout << "[PIBinaryLog]" - << "Start join binlogs to" << dst; + << "Start join binlogs to \"%1\""_tr("PIBinLog").arg(dst); } else { dtime = lt; } @@ -799,7 +800,7 @@ bool PIBinaryLog::joinBinLogsSerial(const PIStringList & src, lt = dtime + br.timestamp; if (dlog.writeBinLog_raw(br.id, lt, br.data) <= 0) { piCout << "[PIBinaryLog]" - << "Error, can't write to file" << dst; + << "Error, can't write to file \"%1\""_tr("PIBinLog").arg(dst); return false; } if (tm.elapsed_s() > 0.1) { @@ -821,7 +822,7 @@ bool PIBinaryLog::joinBinLogsSerial(const PIStringList & src, // piCout << "[PIBinaryLog]" << "complete" << fn; } piCout << "[PIBinaryLog]" - << "Finish join binlogs, total time" << PITime::fromSystemTime(lt).toString(); + << "Finish join binlogs, total time %1"_tr("PIBinLog").arg(PITime::fromSystemTime(lt).toString()); return true; } @@ -853,7 +854,7 @@ int PIBinaryLog::posForTime(const PISystemTime & time) { void PIBinaryLog::seekTo(int rindex) { - // piCoutObj << "seekTo"; + // piCoutObj << "seekTo"; logmutex.lock(); pausemutex.lock(); if (rindex < index.index.size_s() && rindex >= 0) { @@ -866,7 +867,7 @@ void PIBinaryLog::seekTo(int rindex) { startlogtime = PISystemTime::current() - lastrecord.timestamp; } } - // piCoutObj << "seekTo done"; + // piCoutObj << "seekTo done"; pausemutex.unlock(); logmutex.unlock(); } @@ -947,7 +948,7 @@ void PIBinaryLog::configureFromFullPathDevice(const PIString & full_path) { break; } } - // piCoutObj << "configured"; + // piCoutObj << "configured"; } @@ -989,7 +990,7 @@ void PIBinaryLog::propertyChanged(const char * s) { split_time = property("splitTime").toSystemTime(); split_size = property("splitFileSize").toLLong(); split_count = property("splitRecordCount").toInt(); - // piCoutObj << "propertyChanged" << s << play_mode; + // piCoutObj << "propertyChanged" << s << play_mode; } diff --git a/libs/main/io_devices/piethernet.cpp b/libs/main/io_devices/piethernet.cpp index 9f9ac03e..cc50a176 100644 --- a/libs/main/io_devices/piethernet.cpp +++ b/libs/main/io_devices/piethernet.cpp @@ -24,6 +24,7 @@ #include "piliterals.h" #include "pipropertystorage.h" #include "pisysteminfo.h" +#include "pitranslator.h" // clang-format off #ifdef QNX # include @@ -897,7 +898,9 @@ void PIEthernet::server_func(void * eth) { piMSleep(10); return; } - if (ce->debug()) piCout << "[PIEthernet] Can`t accept new connection," << ethErrorString(); + if (ce->debug()) + piCout << "[PIEthernet]" + << "Can`t accept new connection, %1"_tr("PIEthernet").arg(ethErrorString()); piMSleep(50); return; } @@ -1081,14 +1084,16 @@ PIEthernet::InterfaceList PIEthernet::interfaces() { ulong ulOutBufLen = sizeof(IP_ADAPTER_INFO); PIP_ADAPTER_INFO pAdapterInfo = (PIP_ADAPTER_INFO)HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO)); if (!pAdapterInfo) { - piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersInfo"; + piCout << "[PIEthernet]" + << "Error allocating memory needed to call GetAdaptersInfo"_tr("PIEthernet"); return il; } if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { HeapFree(GetProcessHeap(), 0, pAdapterInfo); pAdapterInfo = (PIP_ADAPTER_INFO)HeapAlloc(GetProcessHeap(), 0, ulOutBufLen); if (!pAdapterInfo) { - piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersInfo"; + piCout << "[PIEthernet]" + << "Error allocating memory needed to call GetAdaptersInfo"_tr("PIEthernet"); return il; } } @@ -1134,7 +1139,8 @@ PIEthernet::InterfaceList PIEthernet::interfaces() { ifc.ifc_len = 256; ifc.ifc_buf = new char[ifc.ifc_len]; if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { - piCout << "[PIEthernet] Can`t get interfaces:" << errorString(); + piCout << "[PIEthernet]" + << "Can`t get interfaces: %1"_tr("PIEthernet").arg(errorString()); delete[] ifc.ifc_buf; return il; } @@ -1223,7 +1229,8 @@ PIEthernet::InterfaceList PIEthernet::interfaces() { } freeifaddrs(ret); } else - piCout << "[PIEthernet] Can`t get interfaces:" << errorString(); + piCout << "[PIEthernet]" + << "Can`t get interfaces: %1"_tr("PIEthernet").arg(errorString()); if (s != -1) ::close(s); # endif # endif diff --git a/libs/main/io_devices/pifile.cpp b/libs/main/io_devices/pifile.cpp index 0b4f0763..c6a09038 100644 --- a/libs/main/io_devices/pifile.cpp +++ b/libs/main/io_devices/pifile.cpp @@ -23,6 +23,7 @@ #include "piincludes_p.h" #include "piiostream.h" #include "pitime_win.h" +#include "pitranslator.h" #ifdef WINDOWS # undef S_IFDIR # undef S_IFREG @@ -295,7 +296,7 @@ void PIFile::resize(llong new_size, uchar fill_) { clear(); return; } - piCoutObj << "Downsize is not supported yet :-("; + piCoutObj << "Downsize is not supported yet :-("_tr("PIFile"); } diff --git a/libs/main/io_devices/piiobytearray.cpp b/libs/main/io_devices/piiobytearray.cpp index 2859d619..3b6e8374 100644 --- a/libs/main/io_devices/piiobytearray.cpp +++ b/libs/main/io_devices/piiobytearray.cpp @@ -56,12 +56,12 @@ bool PIIOByteArray::open(const PIByteArray & buffer) { ssize_t PIIOByteArray::readDevice(void * read_to, ssize_t size) { - // piCout << "PIIOByteArray::read" << data_ << size << canRead(); + // piCout << "PIIOByteArray::read" << data_ << size << canRead(); if (!canRead() || !data_) return -1; int ret = piMini(size, data_->size_s() - pos); if (ret <= 0) return -1; memcpy(read_to, data_->data(pos), ret); - // piCout << "readed" << ret; + // piCout << "readed" << ret; pos += size; if (pos > data_->size_s()) pos = data_->size_s(); return ret; @@ -69,12 +69,12 @@ ssize_t PIIOByteArray::readDevice(void * read_to, ssize_t size) { ssize_t PIIOByteArray::writeDevice(const void * data, ssize_t size) { - // piCout << "PIIOByteArray::write" << data << size << canWrite(); + // piCout << "PIIOByteArray::write" << data << size << canWrite(); if (!canWrite() || !data) return -1; // piCout << "write" << data; if (pos > data_->size_s()) pos = data_->size_s(); PIByteArray rs = PIByteArray(data, size); - // piCoutObj << rs; + // piCoutObj << rs; data_->insert(pos, rs); pos += rs.size_s(); return rs.size_s(); diff --git a/libs/main/io_devices/piiodevice.cpp b/libs/main/io_devices/piiodevice.cpp index fa92731a..42cd04ca 100644 --- a/libs/main/io_devices/piiodevice.cpp +++ b/libs/main/io_devices/piiodevice.cpp @@ -25,6 +25,7 @@ #include "piliterals_time.h" #include "pipropertystorage.h" #include "pitime.h" +#include "pitranslator.h" //! \class PIIODevice piiodevice.h @@ -222,7 +223,7 @@ void PIIODevice::stopThreadedRead() { if (!destroying) { interrupt(); } else { - piCoutObj << "Error: Device is running after destructor!"; + piCoutObj << "Error: Device is running after destructor!"_tr("PIIODevice"); } #endif } @@ -535,7 +536,7 @@ void PIIODevice::splitFullPath(PIString fpwm, PIString * full_path, DeviceMode * if (fpwm.find('(') > 0 && fpwm.find(')') > 0) { PIString dms(fpwm.right(fpwm.length() - fpwm.findLast('(')).takeRange('(', ')').trim().toLowerCase().removeAll(' ')); PIStringList opts(dms.split(",")); - piForeachC(PIString & o, opts) { + for (const auto & o: opts) { // piCout << dms; if (o == "r"_a || o == "ro"_a || o == "read"_a || o == "readonly"_a) dm |= ReadOnly; if (o == "w"_a || o == "wo"_a || o == "write"_a || o == "writeonly"_a) dm |= WriteOnly; @@ -666,7 +667,7 @@ PIMap & PIIODevice::fabrics() { bool PIIODevice::threadedRead(const uchar * readed, ssize_t size) { - // piCout << "iodevice threaded read"; + // piCout << "iodevice threaded read"; if (func_read) return func_read(readed, size, ret_data_); return true; } diff --git a/libs/main/io_devices/piiostring.cpp b/libs/main/io_devices/piiostring.cpp index 52226865..e2b2c5ac 100644 --- a/libs/main/io_devices/piiostring.cpp +++ b/libs/main/io_devices/piiostring.cpp @@ -19,6 +19,8 @@ #include "piiostring.h" +#include "pitranslator.h" + //! \class PIIOString piiostring.h //! \details @@ -49,7 +51,7 @@ void PIIOString::clear() { bool PIIOString::open(PIString * string, PIIODevice::DeviceMode mode) { if (mode == PIIODevice::ReadWrite) { - piCoutObj << "Error: ReadWrite mode not supported, use WriteOnly or ReadOnly"; + piCoutObj << "Error: ReadWrite mode not supported, use WriteOnly or ReadOnly"_tr("PIIOString"); str = nullptr; return false; } diff --git a/libs/main/io_devices/piserial.cpp b/libs/main/io_devices/piserial.cpp index 28a172f8..31af1b4e 100644 --- a/libs/main/io_devices/piserial.cpp +++ b/libs/main/io_devices/piserial.cpp @@ -24,6 +24,7 @@ #include "piincludes_p.h" #include "pipropertystorage.h" #include "pitime.h" +#include "pitranslator.h" #include "piwaitevent_p.h" #include @@ -463,10 +464,10 @@ int PISerial::convertSpeed(PISerial::Speed speed) { default: break; } #ifdef WINDOWS - piCoutObj << "Warning: Custom speed" << (int)speed; + piCoutObj << "Warning: Custom speed %1"_tr("PISerial").arg((int)speed); return (int)speed; #else - piCoutObj << "Warning: Unknown speed" << (int)speed << ", using 115200"; + piCoutObj << "Warning: Unknown speed %1, using 115200"_tr("PISerial").arg((int)speed); return B115200; #endif } @@ -691,7 +692,7 @@ bool PISerial::openDevice() { } } if (p.isEmpty()) { - piCoutObj << "Unable to find device \"" << pl << "\""; + piCoutObj << "Unable to find device \"%1\""_tr("PISerial").arg(pl); } } if (p.isEmpty()) return false; @@ -708,7 +709,7 @@ bool PISerial::openDevice() { PIString wp = "//./" + p; PRIVATE->hCom = CreateFileA(wp.dataAscii(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); if (PRIVATE->hCom == INVALID_HANDLE_VALUE) { - piCoutObj << "Unable to open \"" << p << "\"" << errorString(); + piCoutObj << "Unable to open \"%1\": %2"_tr("PISerial").arg(p).arg(errorString()); fd = -1; return false; } @@ -722,7 +723,7 @@ bool PISerial::openDevice() { } fd = ::open(p.data(), O_NOCTTY | om); if (fd == -1) { - piCoutObj << "Unable to open \"" << p << "\""; + piCoutObj << "Unable to open \"%1\": %2"_tr("PISerial").arg(p).arg(errorString()); return false; } tcgetattr(fd, &PRIVATE->desc); @@ -747,7 +748,7 @@ bool PISerial::closeDevice() { #ifdef WINDOWS SetCommState(PRIVATE->hCom, &PRIVATE->sdesc); SetCommMask(PRIVATE->hCom, PRIVATE->mask); - // piCoutObj << "close" << + // piCoutObj << "close" << CloseHandle(PRIVATE->hCom); PRIVATE->hCom = 0; #else @@ -788,7 +789,7 @@ void PISerial::applySettings() { } PRIVATE->desc.StopBits = params[PISerial::TwoStopBits] ? TWOSTOPBITS : ONESTOPBIT; if (SetCommState(PRIVATE->hCom, &PRIVATE->desc) == -1) { - piCoutObj << "Unable to set comm state for \"" << path() << "\""; + piCoutObj << "Unable to set comm state for \"%1\""_tr("PISerial").arg(path()); return; } #else @@ -822,7 +823,7 @@ void PISerial::applySettings() { setTimeouts(); if (tcsetattr(fd, TCSANOW, &PRIVATE->desc) < 0) { - piCoutObj << "Can`t set attributes for \"" << path() << "\""; + piCoutObj << "Can`t set attributes for \"%1\""_tr("PISerial").arg(path()); return; } #endif @@ -871,7 +872,7 @@ ssize_t PISerial::readDevice(void * read_to, ssize_t max_size) { // piCoutObj << "read ..." << PRIVATE->hCom << max_size; DWORD mask = 0; if (GetCommMask(PRIVATE->hCom, &mask) == FALSE) { - piCoutObj << "read error" << errorString(); + piCoutObj << "Read error: %1"_tr("PISerial").arg(errorString()); stop(); close(); return 0; @@ -883,7 +884,7 @@ ssize_t PISerial::readDevice(void * read_to, ssize_t max_size) { DWORD err = GetLastError(); // piCoutObj << "read" << err; if (err == ERROR_BAD_COMMAND || err == ERROR_ACCESS_DENIED) { - piCoutObj << "read error" << errorString(); + piCoutObj << "Read error: %1"_tr("PISerial").arg(errorString()); stop(); close(); return 0; diff --git a/libs/main/io_utils/pibasetransfer.cpp b/libs/main/io_utils/pibasetransfer.cpp index 3575a284..1829cd45 100644 --- a/libs/main/io_utils/pibasetransfer.cpp +++ b/libs/main/io_utils/pibasetransfer.cpp @@ -21,9 +21,12 @@ #include "piliterals_time.h" #include "pitime.h" +#include "pitranslator.h" + const uint PIBaseTransfer::signature = 0x54424950; + PIBaseTransfer::PIBaseTransfer(): crc(standardCRC_16()), diag(false) { header.sig = signature; crc_enabled = true; @@ -96,7 +99,7 @@ void PIBaseTransfer::received(PIByteArray data) { data >> h; PacketType pt = (PacketType)h.type; if (!h.check_sig()) { - piCoutObj << "invalid packet signature"; + piCoutObj << "invalid packet signature"_tr("PIBaseTransfer"); diag.received(data.size(), false); return; } else @@ -119,7 +122,7 @@ void PIBaseTransfer::received(PIByteArray data) { ccrc = 0; if (rcrc != ccrc) { header.id = h.id; - piCoutObj << "invalid CRC"; + piCoutObj << "invalid CRC"_tr("PIBaseTransfer"); sendReply(pt_ReplyInvalid); } else { mutex_session.lock(); @@ -170,9 +173,9 @@ void PIBaseTransfer::received(PIByteArray data) { if (send_up > 20 && send_up > packets_count * 2) packets_count += piMaxi(packets_count / 10, 1); // piCoutObj << packets_count; } else - piCoutObj << "invalid reply id"; + piCoutObj << "invalid reply id"_tr("PIBaseTransfer"); mutex_session.unlock(); - // piCoutObj << "Done Packet" << h.id; + // piCoutObj << "Done Packet" << h.id; } if (is_receiving && h.id == 0) { if (pt == pt_ReplySuccess) { @@ -213,7 +216,7 @@ void PIBaseTransfer::received(PIByteArray data) { } if (is_receiving) { if (header.session_id != h.session_id) { - piCoutObj << "restart receive"; + piCoutObj << "restart receive"_tr("PIBaseTransfer"); mutex_header.unlock(); finish_receive(false, true); } else { @@ -344,7 +347,7 @@ bool PIBaseTransfer::send_process() { mutex_send.unlock(); if (break_) return finish_send(false); } - // piCoutObj << "send done, checking"; + // piCoutObj << "send done, checking"; PITimeMeasurer rtm; int prev_chk = 0; mutex_send.lock(); @@ -516,7 +519,7 @@ bool PIBaseTransfer::getStartRequest() { diag.sended(ba.size_s()); sendRequest(ba); if (break_) return false; - // piCoutObj << replies[0]; + // piCoutObj << replies[0]; mutex_session.lock(); if (replies[0] == pt_ReplySuccess) { state_string = "send permited!"; @@ -532,7 +535,7 @@ bool PIBaseTransfer::getStartRequest() { void PIBaseTransfer::processData(int id, PIByteArray & data) { - // piCoutObj << "received packet" << id << ", size" << data.size(); + // piCoutObj << "received packet" << id << ", size" << data.size(); if (id < 1 || id > replies.size_s()) return; if (!session[id - 1].isEmpty()) { header.id = id; @@ -595,7 +598,7 @@ PIByteArray PIBaseTransfer::build_packet(int id) { hdr << header; mutex_header.unlock(); ret.insert(0, hdr); - // piCoutObj << "Send Packet" << header.id << ret.size(); + // piCoutObj << "Send Packet" << header.id << ret.size(); return ret; } diff --git a/libs/main/io_utils/piconnection.cpp b/libs/main/io_utils/piconnection.cpp index 5a4b7763..ee7d0a5b 100644 --- a/libs/main/io_utils/piconnection.cpp +++ b/libs/main/io_utils/piconnection.cpp @@ -23,6 +23,7 @@ #include "piiostream.h" #include "piliterals_time.h" #include "pitime.h" +#include "pitranslator.h" /** \class PIConnection * \brief Complex Input/Output point @@ -142,7 +143,7 @@ bool PIConnection::configure(PIConfig & conf, const PIString & name_) { // piCout << name_list << flt_list << chk_set; chk_set.remove(""); if (!chk_set.isEmpty()) { - piCoutObj << "Error," << chk_set.toVector() << "names assigned to both devices and filters!"; + piCoutObj << "Error,"_tr("PIConnection") << chk_set.toVector() << "names assigned to both devices and filters!"_tr("PIConnection"); return false; } PIMap dev_aliases; @@ -238,7 +239,7 @@ bool PIConnection::configure(PIConfig & conf, const PIString & name_) { } setDebug(pdebug); for (const PIString & f: filter_fails) { - piCoutObj << "\"addFilter\" error: no such device \"" << f << "\"!"; + piCoutObj << "\"addFilter\" error: no such device \"%1\"!"_tr("PIConnection").arg(f); } for (const PIConfig::Entry * e: cb) { PIString f(e->getValue("from").value()), t(e->getValue("to").value()); @@ -469,7 +470,7 @@ PIPacketExtractor * PIConnection::addFilter(const PIString & name_, const PIStri if (extractors.value(full_path, nullptr)) pe = extractors.value(full_path, nullptr)->extractor; if (pe) dev = pe; if (!dev) { - piCoutObj << "\"addFilter\" error: no such device or filter \"" << full_path << "\"!"; + piCoutObj << "\"addFilter\" error: no such device or filter \"%1\"!"_tr("PIConnection").arg(full_path); return nullptr; } if (!e) { @@ -508,7 +509,7 @@ PIPacketExtractor * PIConnection::addFilter(PIPacketExtractor * filter, const PI if (pe) { dev = pe; } else { - piCoutObj << "\"addFilter\" error: no such device or filter \"" << full_path << "\"!"; + piCoutObj << "\"addFilter\" error: no such device or filter \"%1\"!"_tr("PIConnection").arg(full_path); return nullptr; } if (!e) { @@ -739,7 +740,7 @@ void PIConnection::addSender(const PIString & name_, const PIString & full_path_ } PIIODevice * dev = devByString(full_path_name); if (!dev) { - piCoutObj << "\"addSender\" error: no such device \"" << full_path_name << "\"!"; + piCoutObj << "\"addSender\" error: no such device \"%1\"!"_tr("PIConnection").arg(full_path_name); return; } if (!s->isRunning() && start_) { @@ -899,7 +900,7 @@ int PIConnection::writeByFullPath(const PIString & full_path, const PIByteArray PIIODevice * dev = __device_pool__->device(fp); // piCout << "SEND" << full_path << fp; if (!dev) { - piCoutObj << "No such full path \"" << full_path << "\"!"; + piCoutObj << "No such full path \"%1\"!"_tr("PIConnection").arg(full_path); return -1; } return write(dev, data); @@ -909,7 +910,7 @@ int PIConnection::writeByFullPath(const PIString & full_path, const PIByteArray int PIConnection::writeByName(const PIString & name_, const PIByteArray & data) { PIIODevice * dev = deviceByName(name_); if (!dev) { - piCoutObj << "No such device \"" << name_ << "\"!"; + piCoutObj << "No such device \"%1\"!"_tr("PIConnection").arg(name_); return -1; } return write(dev, data); @@ -918,12 +919,12 @@ int PIConnection::writeByName(const PIString & name_, const PIByteArray & data) int PIConnection::write(PIIODevice * dev, const PIByteArray & data) { if (!dev) { - piCoutObj << "Null Device!"; + piCoutObj << "Null Device!"_tr("PIConnection"); return -1; } if (!dev->isOpened()) return -1; if (!dev->canWrite()) { - piCoutObj << "Device \"" << dev->constructFullPath() << "\" can`t write!"; + piCoutObj << "Device \"%1\" can`t write!"_tr("PIConnection").arg(dev->constructFullPath()); return -1; } int ret = dev->write(data); @@ -982,7 +983,7 @@ PIIODevice * PIConnection::DevicePool::addDevice(PIConnection * parent, const PI // piCout << "new device" << fp; dd->dev = PIIODevice::createFromFullPath(fp); if (!dd->dev) { - piCoutObj << "Error: can`t create device \"" << fp << "\"!"; //:" << errorString(); + piCoutObj << "Error: can`t create device \"%1\"!"_tr("PIConnection").arg(fp); return nullptr; } dd->dev->setProperty("__fullPath__", fp); @@ -1258,7 +1259,7 @@ void PIConnection::unboundExtractor(PIPacketExtractor * pe) { void PIConnection::packetExtractorReceived(const uchar * data, int size) { PIString from(emitter() ? emitter()->name() : PIString()); PIIODevice * cd = (PIIODevice *)emitter(); - // piCout << "packetExtractorReceived" << from << cd; + // piCout << "packetExtractorReceived" << from << cd; if (cd) { PIVector be(bounded_extractors.value(cd)); // piCout << be << (void*)data << size; diff --git a/libs/main/io_utils/pidiagnostics.cpp b/libs/main/io_utils/pidiagnostics.cpp index dba39105..d6dd7f25 100644 --- a/libs/main/io_utils/pidiagnostics.cpp +++ b/libs/main/io_utils/pidiagnostics.cpp @@ -152,16 +152,16 @@ void PIDiagnostics::sended(int size) { void PIDiagnostics::tick(int) { + auto speed_sec = "/s"_tr("PIDiag"); mutex_state.lock(); - auto speed_sec = PITranslator::tr("/s", "PIDiag"); // piCoutObj << "lock"; - int tcnt_recv = 0; - int tcnt_send = 0; - Entry send = calcHistory(history_send, tcnt_send); - Entry recv = calcHistory(history_rec, tcnt_recv); - float itr = disconn_.toSeconds() * (float(tcnt_recv) / history_rec.size()); - float its = disconn_.toSeconds() * (float(tcnt_send) / history_send.size()); - float hz = 1. / interval().toSeconds(); + int tcnt_recv = 0; + int tcnt_send = 0; + Entry send = calcHistory(history_send, tcnt_send); + Entry recv = calcHistory(history_rec, tcnt_recv); + float itr = disconn_.toSeconds() * (float(tcnt_recv) / history_rec.size()); + float its = disconn_.toSeconds() * (float(tcnt_send) / history_send.size()); + float hz = 1. / interval().toSeconds(); if (tcnt_recv == 0) { cur_state.integral_freq = cur_state.immediate_freq = 0; cur_state.received_packets_per_sec = cur_state.received_bytes_per_sec = 0; @@ -197,7 +197,7 @@ void PIDiagnostics::tick(int) { diag = PIDiagnostics::Good; } if ((tcnt_send + tcnt_recv) != 0) { - // piCoutObj << tcnt_recv << tcnt_send; + // piCoutObj << tcnt_recv << tcnt_send; history_rec.dequeue(); history_send.dequeue(); Entry e; @@ -225,7 +225,7 @@ PIDiagnostics::Entry PIDiagnostics::calcHistory(PIQueue & hist, int & cnt if (!hist[i].empty) cnt++; } e.empty = false; - // piCoutObj << hist.size() << cnt; + // piCoutObj << hist.size() << cnt; return e; } diff --git a/libs/main/resources/piresourcesstorage.cpp b/libs/main/resources/piresourcesstorage.cpp index fce26322..8623c6f4 100644 --- a/libs/main/resources/piresourcesstorage.cpp +++ b/libs/main/resources/piresourcesstorage.cpp @@ -19,7 +19,6 @@ #include "piresourcesstorage.h" -#include "pichunkstream.h" #include "piset.h" @@ -42,11 +41,11 @@ void PIResourcesStorage::Section::add(const PIResourcesStorage::Section & s) { void PIResourcesStorage::Section::purge() { PIVector bav = entries.values(); PISet bas; - piForeach(PIByteArray * i, bav) { + for (auto i: bav) { if (i) bas << i; } bav = bas.toVector(); - piForeach(PIByteArray * i, bav) + for (auto i: bav) delete i; entries.clear(); } @@ -76,14 +75,14 @@ void PIResourcesStorage::registerSection(const uchar * rc_data, const uchar * rc PIVector el; dba >> el; PIMap> ebs; - piForeachC(PIResourcesStorage::__RCEntry & e, el) { + for (const auto & e: el) { ebs[e.section] << e; } auto it = ebs.makeIterator(); while (it.next()) { PIResourcesStorage::Section s; const PIVector & itv(it.value()); - piForeachC(PIResourcesStorage::__RCEntry & e, itv) { + for (const auto & e: itv) { // piCout << "add" << e.name << e.alias << PIString::readableSize(e.size); PIByteArray * eba = new PIByteArray(&(rc_data[e.offset]), e.size); s.entries[e.name] = eba; @@ -123,7 +122,7 @@ PIByteArray PIResourcesStorage::get(const PIString & entry_name) const { void PIResourcesStorage::clear() { // piCout << "PIResourcesStorage clear"; PIVector

sv = sections.values(); - piForeach(Section * i, sv) { + for (auto i: sv) { if (i) i->purge(); } sections.clear(); diff --git a/libs/main/state_machine/pistatemachine_state.cpp b/libs/main/state_machine/pistatemachine_state.cpp index f23b257b..6a5162d8 100644 --- a/libs/main/state_machine/pistatemachine_state.cpp +++ b/libs/main/state_machine/pistatemachine_state.cpp @@ -20,7 +20,7 @@ #include "pistatemachine_state.h" #include "pistatemachine_transition.h" - +#include "pitranslator.h" PIStateBase::~PIStateBase() { piDeleteAll(transitions); @@ -108,7 +108,7 @@ bool PIStateBase::start(bool force) { setActive(true); return initial_state->start(); } else { - piCout << "error:" << getName() << "no initial state!"; + piCout << "Error: \"%1\" no initial state!"_tr("PIStateMachine").arg(getName()); return false; } } else { diff --git a/libs/main/system/piprocess.cpp b/libs/main/system/piprocess.cpp index b63a0278..3463ab69 100644 --- a/libs/main/system/piprocess.cpp +++ b/libs/main/system/piprocess.cpp @@ -19,6 +19,7 @@ #include "piliterals_time.h" #include "pitime.h" +#include "pitranslator.h" #ifndef MICRO_PIP # include "piincludes_p.h" @@ -196,7 +197,7 @@ void PIProcess::startProc(bool detached) { CloseHandle(PRIVATE->pi.hThread); CloseHandle(PRIVATE->pi.hProcess); } else - piCoutObj << "\"CreateProcess\" error, " << errorString(); + piCoutObj << "\"CreateProcess\" error: %1"_tr("PIProcess").arg(errorString()); # else // cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl; diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index bae3ae82..515d36f0 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -297,7 +297,7 @@ llong PIString::toNumberBase(const PIString & value, int base, bool * ok) { void PIString::appendFromChars(const char * c, int s, const char * codepage) { - // piCout << "appendFromChars"; + // piCout << "appendFromChars"; if (s == 0) return; int old_sz = size_s(); if (s == -1) s = strlen(c); @@ -523,6 +523,31 @@ void PIString::trimsubstr(int & st, int & fn) const { } +PIString PIString::minArgPlaceholder() { + if (size() < 2) return {}; + int min = -1; + PIString ret, tmp; + for (int i = 0; i < size_s(); ++i) { + if (at(i) == '%') { + int j = 0; + for (j = i + 1; j < size_s(); ++j) { + if (!at(j).isDigit()) break; + } + bool ok = false; + tmp = mid(i + 1, j - i - 1); + int cur = tmp.toInt(10, &ok); + if (!ok) continue; + if (min < 0 || min > cur) { + min = cur; + ret = tmp; + } + } + } + if (ret.isEmpty()) return {}; + return "%" + ret; +} + + uint PIString::hash() const { return piHashData((const uchar *)d.data(), d.size() * sizeof(PIChar)); } @@ -1744,10 +1769,9 @@ ldouble PIString::toLDouble() const { //! piCout << s; // 47.6 GiB //! \endcode PIString & PIString::setReadableSize(llong bytes) { - static const PIString tr_c = "PIString"_a; clear(); if (bytes < 1024) { - *this += (PIString::fromNumber(bytes) + " "_a + PITranslator::tr("B", tr_c)); + *this += (PIString::fromNumber(bytes) + " "_a + "B"_tr("PIString")); return *this; } double fres = bytes; @@ -1762,14 +1786,21 @@ PIString & PIString::setReadableSize(llong bytes) { } return false; }; - if (checkRange(PITranslator::tr("KiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("MiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("GiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("TiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("PiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("EiB", tr_c))) return *this; - if (checkRange(PITranslator::tr("ZiB", tr_c))) return *this; - checkRange(PITranslator::tr("YiB", tr_c), true); + if (checkRange("KiB"_tr("PIString"))) return *this; + if (checkRange("MiB"_tr("PIString"))) return *this; + if (checkRange("GiB"_tr("PIString"))) return *this; + if (checkRange("TiB"_tr("PIString"))) return *this; + if (checkRange("PiB"_tr("PIString"))) return *this; + if (checkRange("EiB"_tr("PIString"))) return *this; + if (checkRange("ZiB"_tr("PIString"))) return *this; + checkRange("YiB"_tr("PIString"), true); + return *this; +} + + +PIString & PIString::arg(const PIString & v) { + auto ph = minArgPlaceholder(); + if (!ph.isEmpty()) replaceAll(ph, v); return *this; } diff --git a/libs/main/text/pistring.h b/libs/main/text/pistring.h index 02bec59f..27ffa208 100644 --- a/libs/main/text/pistring.h +++ b/libs/main/text/pistring.h @@ -1682,6 +1682,61 @@ public: //! \~\sa PIString::readableSize() PIString & setReadableSize(llong bytes); + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + //! \~\details + //! \~\code + //! piCout << PIString("color %1 is not %2, but %1").arg("red").arg("green"); // color red is not green, but red + //! \endcode + PIString & arg(const PIString & v); + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(short v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(ushort v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + //! \~\details + //! \~\code + //! piCout << PIString("ten = %1").arg(10); // ten = 10 + //! piCout << PIString("'%1' is max hex letter (%2 in dec)").arg(0xF, 16).arg(0xF); // 'F' is max hex letter (15 in dec) + //! \endcode + PIString & arg(int v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(uint v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(llong v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(ullong v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(float v, char format = 'f', int precision = 8) { return arg(PIString::fromNumber(v, format, precision)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + //! \~\details + //! \~\code + //! piCout << PIString("light speed = %1 %2").arg(M_LIGHT_SPEED, 'g', 4).arg("m/s"); // light speed = 2.998e+08 m/s + //! \endcode + PIString & arg(double v, char format = 'f', int precision = 8) { return arg(PIString::fromNumber(v, format, precision)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(ldouble v, char format = 'f', int precision = 8) { return arg(PIString::fromNumber(v, format, precision)); } + + //! \~english Returns string contains numeric representation of "value" in base "base". //! \~russian Возвращает строковое представление числа "value" по основанию "base". //! \~\details @@ -1867,6 +1922,7 @@ private: void buildData(const char * cp = __syslocname__) const; void deleteData() const; void trimsubstr(int & st, int & fn) const; + PIString minArgPlaceholder(); PIDeque d; mutable bool changed_ = true; diff --git a/libs/main/thread/pithread.cpp b/libs/main/thread/pithread.cpp index afdef1f3..aa00aa21 100644 --- a/libs/main/thread/pithread.cpp +++ b/libs/main/thread/pithread.cpp @@ -22,6 +22,7 @@ #include "piincludes_p.h" #include "piintrospection_threads.h" #include "pitime.h" +#include "pitranslator.h" #ifndef MICRO_PIP # include "pisystemtests.h" #endif @@ -568,7 +569,7 @@ PIThread::PIThread(bool startNow, PISystemTime loop_delay): PIObject() { PIThread::~PIThread() { PIINTROSPECTION_THREAD_DELETE(this); if (!running_ || PRIVATE->thread == 0) return; - piCout << "[PIThread \"" << name() << "\"] Warning, terminate on destructor!"; + piCout << "[PIThread \"%1\"] Warning, terminate on destructor!"_tr("PIThread").arg(name()); #ifdef FREERTOS // void * ret(0); // PICout(PICoutManipulators::DefaultControls) << "~PIThread" << PRIVATE->thread; @@ -663,7 +664,7 @@ void PIThread::stop() { void PIThread::terminate() { - piCoutObj << "Warning, terminate!"; + piCoutObj << "Warning, terminate!"_tr("PIThread"); // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "terminate ..." << running_; #ifdef FREERTOS PICout(PICoutManipulators::DefaultControls) << "FreeRTOS can't terminate pthreads! waiting for stop"; @@ -781,7 +782,7 @@ bool PIThread::_startThread(void * func) { running_ = false; PRIVATE->thread = 0; - piCoutObj << "Error: Can`t start new thread:" << errorString(); + piCoutObj << "Error: Can`t start new thread: %1"_tr("PIThread").arg(errorString()); return false; } diff --git a/libs/main/types/pisystemtime.cpp b/libs/main/types/pisystemtime.cpp index eaa7f8fc..4cbb1e27 100644 --- a/libs/main/types/pisystemtime.cpp +++ b/libs/main/types/pisystemtime.cpp @@ -23,6 +23,7 @@ #include "piiostream.h" #include "piliterals_time.h" #include "pitime.h" +#include "pitranslator.h" #include #ifdef QNX @@ -320,7 +321,8 @@ PISystemTime PITimeMeasurer::elapsed() const { PISystemTime PISystemTime::Frequency::toSystemTime() const { if (value_hz <= 0.) { - piCout << "[PISystemTime::Frequency] toSystemTime() warning: invalid hertz" << value_hz; + piCout << "[PISystemTime::Frequency]" + << "toSystemTime() Warning: invalid hertz: %1"_tr("PISystemTime").arg(value_hz); return PISystemTime(); } return PISystemTime::fromSeconds(1. / value_hz); @@ -329,7 +331,8 @@ PISystemTime PISystemTime::Frequency::toSystemTime() const { PISystemTime::Frequency PISystemTime::Frequency::fromSystemTime(const PISystemTime & st) { if (st == PISystemTime()) { - piCout << "[PISystemTime::Frequency] fromSystemTime() warning: null frequency"; + piCout << "[PISystemTime::Frequency]" + << "fromSystemTime() Warning: null frequency"_tr("PISystemTime"); return Frequency(); } return Frequency(1. / st.toSeconds()); diff --git a/libs/main/types/pivariant.cpp b/libs/main/types/pivariant.cpp index 0130c82f..56e8f506 100644 --- a/libs/main/types/pivariant.cpp +++ b/libs/main/types/pivariant.cpp @@ -19,6 +19,8 @@ #include "pivariant.h" +#include "pitranslator.h" + //! \class PIVariant pivariant.h //! \details @@ -388,7 +390,7 @@ PIVariant PIVariant::fromValue(const PIByteArray & c, uint type_id) { } else #endif { - piCout << "Can`t initialize PIVariant from unregistered typeID \"" << type_id << "\"!"; + piCout << "Can`t initialize PIVariant from unregistered typeID \"%1\"!"_tr("PIVariant").arg(type_id); return ret; } } @@ -410,7 +412,7 @@ PIVariant PIVariant::fromValue(const PIByteArray & c, const PIString & type) { } else #endif { - piCout << "Can`t initialize PIVariant from unregistered type \"" << type << "\"!"; + piCout << "Can`t initialize PIVariant from unregistered type \"%1\"!"_tr("PIVariant").arg(type); return ret; } } diff --git a/libs/opencl/piopencl.cpp b/libs/opencl/piopencl.cpp index 017852e4..b34c99cf 100644 --- a/libs/opencl/piopencl.cpp +++ b/libs/opencl/piopencl.cpp @@ -1,6 +1,7 @@ #include "piopencl.h" #include "piresources.h" +#include "pitranslator.h" #define CL_USE_DEPRECATED_OPENCL_1_2_APIS #define CL_USE_DEPRECATED_OPENCL_2_0_APIS #define CL_TARGET_OPENCL_VERSION 120 @@ -88,7 +89,8 @@ void PIOpenCL::Initializer::init() { cl_uint plat_num = 0; ret = clGetPlatformIDs(max_size, cl_platforms, &plat_num); if (ret != 0) { - piCout << "[PIOpenCL] Error: OpenCL platforms not found!"; + piCout << "[PIOpenCL]" + << "Error: OpenCL platforms not found!"_tr("PIOpenCL"); return; } for (uint i = 0; i < plat_num; i++) { @@ -504,7 +506,7 @@ bool PIOpenCL::Program::initKernels(PIVector kerns) { bool PIOpenCL::Kernel::execute() { if (dims.isEmpty()) { piCout << "[PIOpenCL::Kernel]" - << "Error: empty range"; + << "Error: empty range"_tr("PIOpenCL"); return false; } cl_int ret = clEnqueueNDRangeKernel(context_->PRIVATEWB->queue, PRIVATE->kernel, dims.size(), 0, dims.data(), 0, 0, 0, 0); @@ -581,13 +583,13 @@ void setArgV(cl_kernel k, int index, T v) { bool PIOpenCL::Kernel::setArgValueS(int index, const PIVariant & value) { if (index < 0 || index >= args_.size_s()) { piCout << "[PIOpenCL::Kernel]" - << "setArgValue invalid index" << index; + << "setArgValue invalid index %1"_tr("PIOpenCL").arg(index); return false; } KernelArg & ka(args_[index]); if (ka.dims > 0) { piCout << "[PIOpenCL::Kernel]" - << "setArgValue set scalar to \"" << ka.type_name << ka.arg_name << "\""; + << "setArgValue set scalar to \"%1 %2\""_tr("PIOpenCL").arg(ka.type_name).arg(ka.arg_name); return false; } switch (ka.arg_type) { @@ -611,13 +613,13 @@ bool PIOpenCL::Kernel::bindArgValue(int index, Buffer * buffer) { if (!buffer) return false; if (index < 0 || index >= args_.size_s()) { piCout << "[PIOpenCL::Kernel]" - << "bindArgValue invalid index" << index; + << "bindArgValue invalid index %1"_tr("PIOpenCL").arg(index); return false; } KernelArg & ka(args_[index]); if (ka.dims <= 0) { piCout << "[PIOpenCL::Kernel]" - << "bindArgValue set buffer to \"" << ka.type_name << ka.arg_name << "\""; + << "bindArgValue set buffer to \"%1 %2\""_tr("PIOpenCL").arg(ka.type_name).arg(ka.arg_name); return false; } clSetKernelArg(PRIVATE->kernel, index, sizeof(buffer->PRIVATEWB->buffer), &(buffer->PRIVATEWB->buffer)); diff --git a/main.cpp b/main.cpp index 49821cc7..764a1f9d 100644 --- a/main.cpp +++ b/main.cpp @@ -8,25 +8,45 @@ #include "pilog.h" #include "pimathbase.h" #include "pip.h" +#include "pitranslator_p.h" #include "pivaluetree_conversions.h" using namespace PICoutManipulators; - -template -void foo() { - static_assert(std::is_arithmetic::value || is_complex::value, "Type must be arithmetic"); -} - - int main(int argc, char * argv[]) { - // piCout << PIString::readableSize(50_KiB); - // piCout << PIString::readableSize(1_GB); PITranslator::loadLang("ru"); - PITranslator::loadConfig("[]\ntest \\\nstring=привет\n"_u8); + PISerial f("COM123"); + f.open(); + + /*auto test = [](PIString s, PIString v) { + piCout << " in:" << s; + piCout << "arg:" << minArgPlaceholder(s); + piCout << "out:" << arg(s, v); + piCout << ""; + }; + + test(" %", "asd"); + test("%", "asd"); + test("1", "asd"); + test(" %11", "asd"); + test("%1%2 %0f%0g", "asd"); + test("%01 ", "asd");*/ + + /*piCout << PIString::readableSize(50_KiB); + piCout << PIString::readableSize(1_GB); + PITranslator::loadLang("ru"); + piCout << PIString::readableSize(50_KiB); + piCout << PIString::readableSize(1_GB); piCout << "test\nstring"_tr; PITranslator::clear(); + piCout << PIString::readableSize(50_KiB); + piCout << PIString::readableSize(1_GB); piCout << "test\nstring"_tr; + piCout << "hello!"_tr; + PITranslator::loadConfig("[]\nhello!=привет!\n[Co]\nhello!=привет CO!\n"_u8); + piCout << "hello!"_tr("Co") << "hello!"_tr;*/ + // piCout << "hello!"_trNoOp; + // PICodeParser parser; // parser.parseFile("cmg_test.h"); /*for (auto m: parser.macros) { diff --git a/utils/code_model_generator/main.cpp b/utils/code_model_generator/main.cpp index 5b569b62..fd9eb2ab 100755 --- a/utils/code_model_generator/main.cpp +++ b/utils/code_model_generator/main.cpp @@ -20,8 +20,7 @@ #include "picli.h" #include "picodeparser.h" #include "piiostream.h" - -#include +#include "pitranslator.h" using namespace PICoutManipulators; @@ -107,10 +106,6 @@ void help() { piCout << help_string; } -void printError(const PIString & msg) { - std::cerr << msg.data() << std::endl; -} - PIString toCName(const PIString & s) { PIString ret(s.trimmed()); @@ -307,7 +302,7 @@ bool writeClassStreamMembersOut(PIIOTextStream & ts, const PICodeParser::Entity ++cnt; if (m.meta.contains("id")) cnt = m.meta.value("id").toInt(); if (used_id[cnt]) { - printError("Error with \"" + e->name + "\" stream operator: ID " + PIString::fromNumber(cnt) + " already used"); + piCerr << "Error with \"%1\" stream operator: ID %2 already used"_tr("pip_cmg").arg(e->name).arg(cnt); return false; } if (m.dims.isEmpty()) { @@ -361,7 +356,7 @@ bool writeClassStreamMembersIn(PIIOTextStream & ts, const PICodeParser::Entity * ++cnt; if (m.meta.contains("id")) cnt = m.meta.value("id").toInt(); if (used_id[cnt]) { - printError("Error with \"" + e->name + "\" stream operator: ID " + PIString::fromNumber(cnt) + " already used"); + piCerr << "Error with \"%1\" stream operator: ID %2 already used"_tr("pip_cmg").arg(e->name).arg(cnt); return false; } used_id << cnt; @@ -496,7 +491,7 @@ bool writeModel(PICodeParser & parser, PICLI & cli, const PIString out, bool met PIFile f(out + ".cpp"); f.clear(); if (!f.open(PIIODevice::WriteOnly)) { - piCout << "Error: can`t open out file" << f.path(); + piCerr << "Error: can`t open output file \"%1\""_tr("pip_cmg").arg(f.path()); return false; } PIIOTextStream ts(&f); @@ -585,7 +580,7 @@ bool writeModel(PICodeParser & parser, PICLI & cli, const PIString out, bool met f.setPath(out + ".h"); f.clear(); if (!f.open(PIIODevice::WriteOnly)) { - piCout << "Error: can`t open out file" << f.path(); + piCerr << "Error: can`t open output file \"%1\""_tr("pip_cmg").arg(f.path()); return false; } ts << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n"); diff --git a/utils/deploy_tool/main.cpp b/utils/deploy_tool/main.cpp index 563f6d0a..6c43ab8b 100644 --- a/utils/deploy_tool/main.cpp +++ b/utils/deploy_tool/main.cpp @@ -640,7 +640,7 @@ int main(int argc, char * argv[]) { if (!objdump.isEmpty()) ++etcnt; if (!otool.isEmpty()) ++etcnt; if (etcnt > 1) { - piCout << "Can use only one of \"readelf\", \"objdump\" and \"otool\"!"; + piCerr << "Can use only one of \"readelf\", \"objdump\" and \"otool\"!"; return 1; } if (etcnt > 0) is_ldd = false; diff --git a/utils/qt_support/ts_file.cpp b/utils/qt_support/ts_file.cpp index 281bf26e..6ec9af28 100644 --- a/utils/qt_support/ts_file.cpp +++ b/utils/qt_support/ts_file.cpp @@ -20,17 +20,19 @@ #include "pifile.h" #include "piiostream.h" +#include "pitranslator.h" -void TSFile::Context::confirm(const PIString & msg, const PIString & file, int line) { - if (msg.isEmpty()) return; +bool TSFile::Context::confirm(const PIString & msg, const PIString & file, int line) { + if (msg.isEmpty()) return false; bool is_new = !messages.contains(msg); auto & m(messages[msg]); m.source = msg; m.filename = file; m.line = line >= 0 ? PIString::fromNumber(line) : PIString(); - m.obsolete = false; - if (is_new) m.type = "unfinished"; + m.type.setFlag(Message::Missing, false); + if (is_new) m.type.setFlag(Message::Unfinished); + return is_new; } @@ -65,14 +67,21 @@ PIString TSFile::unmask(const PIString & in) { TSFile::Content TSFile::read(const PIString & path) { + enum Phase { + pHeader, + pContext, + pContextName, + pMessage, + pMessageBody, + }; Content ret; PIFile f(path, PIIODevice::ReadOnly); if (!f.isOpened()) return ret; PIIOTextStream ts(&f); Context * cc = nullptr; Message msg; - int phase = 0; - bool multi_source = false, multi_translation = false; + int phase = pHeader; + bool multi_source = false, multi_translation = false, multi_comment = false; while (!ts.isEnd()) { auto line = ts.readLine().trim(); if (multi_source) { @@ -91,28 +100,45 @@ TSFile::Content TSFile::read(const PIString & path) { msg.translation += "\n" + unmask(line); continue; } + if (multi_comment) { + if (line.endsWith("")) { + line.cutRight(20); + multi_comment = false; + } + msg.comment += "\n" + unmask(line); + continue; + } switch (phase) { - case 0: - if (line == "") phase = 1; + case pHeader: + if (line.startsWith("") phase = pContextName; + break; + case pContextName: if (line.startsWith("")) { line.cutLeft(6).cutRight(7); - // if (line == context) phase = 2; - cc = &(ret[line]); - phase = 2; + cc = &(ret.contexts[line]); + phase = pMessage; } break; - case 2: + case pMessage: if (line == "") { msg = {}; - phase = 3; + phase = pMessageBody; } break; - case 3: + case pMessageBody: if (line == "") { if (cc) cc->messages[msg.source] = msg; - phase = 2; + phase = pMessage; } else if (line.startsWith("")) { line.cutLeft(8); if (line.endsWith("")) @@ -136,29 +162,41 @@ TSFile::Content TSFile::read(const PIString & path) { PIString t = trs.takeCWord(); trs.cutLeft(1); PIString v = trs.takeRange('\"', '\"'); - if (t == "type") msg.type = v; + if (t == "type") { + if (v == "unfinished") + msg.type.setFlag(Message::Unfinished); + else if (v == "vanished") + msg.type.setFlag(Message::Missing); + } } if (line.endsWith("")) line.cutRight(14); else multi_translation = true; msg.translation = unmask(line); + } else if (line.startsWith("")) { + line.cutLeft(19); + if (line.endsWith("")) + line.cutRight(20); + else + multi_comment = true; + msg.comment = unmask(line); } break; } if (line == "") { cc = nullptr; - phase = 0; + phase = pContext; } } return ret; } -bool TSFile::write(const PIString & path, const TSFile::Content & content, const PIString & lang, bool no_obsolete) { +bool TSFile::write(const PIString & path, const TSFile::Content & content, bool no_obsolete) { PIFile outf(path, PIIODevice::ReadWrite); if (!outf.isOpened()) { - piCerr << "Can`t open" << outf.path() << "!"; + piCerr << "Can`t open file \"%1\"!"_tr("TSFile").arg(outf.path()); return false; } outf.clear(); @@ -172,23 +210,29 @@ bool TSFile::write(const PIString & path, const TSFile::Content & content, const ts << "/>\n"; } ts << " " << mask(m.source) << "\n"; + if (m.comment.isNotEmpty()) { + ts << " " << mask(m.comment) << "\n"; + } ts << " " << mask(m.translation) << "\n"; ts << " \n"; }; ts << "\n"; ts << "\n"; - ts << "\n"; - auto cit = content.makeIterator(); + ts << "\n"; + auto cit = content.contexts.makeIterator(); while (cit.next()) { if (cit.value().messages.isEmpty()) continue; ts << "\n"; ts << " " << cit.key() << "\n"; auto mit = cit.value().messages.makeIterator(); while (mit.next()) { - if (mit.value().obsolete && no_obsolete) continue; + if (mit.value().type[Message::Missing] && no_obsolete) continue; writeMessage(mit.value()); } ts << "\n"; @@ -197,3 +241,14 @@ bool TSFile::write(const PIString & path, const TSFile::Content & content, const return true; } + + +void TSFile::Content::markAllMissing() { + auto cit = contexts.makeIterator(); + while (cit.next()) { + auto mit = cit.value().messages.makeIterator(); + while (mit.next()) { + mit.value().type.setFlag(Message::Missing); + } + } +} diff --git a/utils/qt_support/ts_file.h b/utils/qt_support/ts_file.h index 8ea6c126..01b939d3 100644 --- a/utils/qt_support/ts_file.h +++ b/utils/qt_support/ts_file.h @@ -21,25 +21,33 @@ namespace TSFile { struct Message { + enum Type { + Unfinished = 0x1, + Missing = 0x2, + }; PIString source; PIString translation; - PIString type; + PIString comment; PIString filename; PIString line; - bool obsolete = true; + PIFlags type; }; struct Context { PIMap messages; - void confirm(const PIString & msg, const PIString & file, int line = -1); + bool confirm(const PIString & msg, const PIString & file, int line = -1); }; -using Content = PIMap; +struct Content { + void markAllMissing(); + PIString lang; + PIMap contexts; +}; PIString mask(const PIString & in); PIString unmask(const PIString & in); Content read(const PIString & path); -bool write(const PIString & path, const Content & content, const PIString & lang, bool no_obsolete); +bool write(const PIString & path, const Content & content, bool no_obsolete); } // namespace TSFile diff --git a/utils/resources_compiler/main.cpp b/utils/resources_compiler/main.cpp index 8cf65b42..f0ed7675 100644 --- a/utils/resources_compiler/main.cpp +++ b/utils/resources_compiler/main.cpp @@ -21,6 +21,7 @@ #include "parser.h" #include "picli.h" #include "piiostream.h" +#include "pitranslator.h" using namespace PICoutManipulators; @@ -56,7 +57,7 @@ int main(int argc, char * argv[]) { PIVector files = parse(cli.argumentValue("input")); if (files.isEmpty()) { - piCout << "Error: resources description file is empty"; + piCerr << "Error: resources description file is empty"_tr("pip_rc"); return 0; } if (cli.hasArgument("list")) { @@ -76,7 +77,7 @@ int main(int argc, char * argv[]) { if (outf.open(out_file, PIIODevice::ReadWrite)) { outf.clear(); } else { - piCout << "Error: can`t open out file" << out_file; + piCerr << "Error: can`t open output file \"%1\""_tr("pip_rc").arg(out_file); return 1; } PIIOTextStream ts(&outf); @@ -86,7 +87,7 @@ int main(int argc, char * argv[]) { ts << "// \"" << _a << "\"\n"; ts << "\n"; if (!generate(init_name, outf, files)) { - piCout << "Error: generate fail"; + piCerr << "Error: generate fail"_tr("pip_rc"); return 1; } } diff --git a/utils/translator/CMakeLists.txt b/utils/translator/CMakeLists.txt index d3ae9f7d..8559e1a8 100644 --- a/utils/translator/CMakeLists.txt +++ b/utils/translator/CMakeLists.txt @@ -7,10 +7,17 @@ set_deploy_property(pip_tr COMPANY "${PIP_COMPANY}" INFO "Platform-Independent Primitives") make_rc(pip_tr _RC) -add_executable(pip_tr "main.cpp" ${_RC}) +set(TS_LIST) +if (NOT CROSSTOOLS) + file(GLOB TS_LIST "${pip_ROOT_SRC}/lang/*.ts") +endif() +add_executable(pip_tr "main.cpp" "parser.h" "parser.cpp" ${_RC} ${TS_LIST}) target_link_libraries(pip_tr pip_qt_support pip) if (DEFINED LIB) install(TARGETS pip_tr DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) else() install(TARGETS pip_tr DESTINATION bin) endif() +if (NOT CROSSTOOLS) + add_custom_command(TARGET pip_tr POST_BUILD COMMAND "${CMAKE_COMMAND}" --build "${PIP_DLL_DIR}" --target pip_lang) +endif() diff --git a/utils/translator/main.cpp b/utils/translator/main.cpp index 050dce55..f8b548db 100644 --- a/utils/translator/main.cpp +++ b/utils/translator/main.cpp @@ -17,14 +17,13 @@ along with this program. If not, see . */ +#include "parser.h" #include "picli.h" #include "pidir.h" #include "pifile.h" -#include "piiostream.h" -#include "pijson.h" -#include "pivaluetree.h" -#include "pivaluetree_conversions.h" -#include "ts_file.h" +#include "piliterals_string.h" +#include "pitranslator.h" +#include "pitranslator_p.h" using namespace PICoutManipulators; @@ -42,12 +41,17 @@ void header() { piCout << Bold << "PIP Translator"; piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine; piCout << Green << Bold << "Usage:" << Default - << "\"pip_tr [-hHqn] -l -o [] [] [...]\"" << NewLine; + << "\"pip_tr -P [-hHqnr] -l -o [] [] [...]\"" << NewLine + << "\"pip_tr -C [-hHq] -o \"" << NewLine; } void usage() { header(); piCout << Green << Bold << "Details:"; + piCout << Bold << "Mode"; + piCout << "-P --Parse " << Green << "- parse sources and create/update *.ts file"; + piCout << "-C --Compile " << Green << "- compile *.ts file"; + piCout << ""; piCout << Bold << "Debug control"; piCout << "-h " << Green << "- display this message and exit"; piCout << "-H " << Green << "- display details help"; @@ -56,10 +60,12 @@ void usage() { piCout << Bold << "Output control"; piCout << "-l " << Green << "- translation language (e.g. en_US, ru_RU)"; piCout << "-n, --no-obsolete " << Green << "- drop unused translations in output file"; - piCout << "-o " << Green << "- output file for translation (QtLinguist *.ts file)"; + piCout << "-o " << Green + << "- output file for translation (QtLinguist *.ts file for parse mode or binary file for compile mode)"; piCout << ""; piCout << Bold << "Input control"; - piCout << " " << Green << "- add file or dir translation"; + piCout << "-r, --recursive " << Green << "- scan directories recursively"; + piCout << " " << Green << "- add source file/dir for parse mode or *.ts file for compile mode"; } void help() { @@ -67,105 +73,10 @@ void help() { piCout << help_string; } - -PIString fromCode(const PIString & in) { - return in.replacedAll("\\n", '\n').replaceAll("\\r", '\r').replaceAll("\\t", '\t'); -} - - -void gatherStrings(TSFile::Content & content, const PIString & file, const PIString & file_loc) { - PIString source, context; - int pos = -1, ppos = 0, line = -1; - int len = 0; - auto isCLetter = [](const PIChar c) { return c.isAlpha() || c.isDigit() || c == '_'; }; - static const PIStringList keyword({"PITranslator::tr", "piTr"}); - for (const auto & kw: keyword) { - for (;;) { - source.clear(); - context.clear(); - - pos = file.find(kw, pos + 1); - if (pos < 0) break; - if (pos > 0) { - auto pc = file[pos - 1]; - if (isCLetter(pc) || pc == ':') { - pos += kw.size_s(); - continue; - } - } - pos += kw.size_s(); - if (pos < file.size_s() - 1) { - auto pc = file[pos]; - if (isCLetter(pc) || pc == ':') { - pos += kw.size_s(); - continue; - } - } - // piCout << "PITranslator" << pos; - - pos = file.find('(', pos); - if (pos < 0) break; - // piCout << "(" << pos; - pos += 1; - - pos = file.findRange('"', '"', '\\', pos, &len); - if (pos < 0) break; - // piCout << "\"" << pos; - source = file.mid(pos, len); - pos += len + 1; - line = file.lineNumber(pos); - ppos = pos; - - while (pos < file.size_s()) { - if (!file[pos].isSpace() && file[pos] != '\n' && file[pos] != '\r') break; - ++pos; - } - if (pos < file.size_s()) { - // piCout << "check comma" << file[pos]; - if (file[pos] == ',') { - pos += 1; - pos = file.findRange('"', '"', '\\', pos, &len); - // piCout << "check range" << file.mid(pos, len); - if (pos >= 0) { - context = file.mid(pos, len); - pos += len + 1; - ppos = pos; - } - } - } - - content[context].confirm(fromCode(source), file_loc, line); - pos = ppos; - piCout << "Context = \"" << context << "\", message = \"" << source << "\""; - } - } - - auto & ec(content[""]); - pos = -1; - for (;;) { - source.clear(); - - pos = file.find('"', pos + 1); - if (pos < 0) break; - pos = file.findRange('"', '"', '\\', pos, &len); - if (pos < 0) break; - // piCout << "\"" << pos; - source = file.mid(pos, len); - pos += len + 1; - if (file.mid(pos, 3) == "_tr") { - pos += 3; - if (pos < file.size_s() - 1) { - auto pc = file[pos]; - if (isCLetter(pc) || pc == ':') { - continue; - } - } - ec.confirm(fromCode(source), file_loc, file.lineNumber(pos)); - piCout << "_tr = \"" << source << "\""; - } - } -} - +enum Mode { + mParse = 1, + mCompile = 2, +}; int main(int argc, char * argv[]) { PICLI cli(argc, argv); @@ -177,48 +88,99 @@ int main(int argc, char * argv[]) { cli.addArgument("Help"); cli.addArgument("quiet"); cli.addArgument("no-obsolete"); + cli.addArgument("recursive"); + cli.addArgument("Parse"); + cli.addArgument("Compile"); if (cli.hasArgument("Help")) { help(); return 0; } - if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.argumentValue("language").isEmpty() || - cli.optionalArguments().isEmpty()) { + int mode = 0; + if (cli.hasArgument("Parse")) mode |= mParse; + if (cli.hasArgument("Compile")) mode |= mCompile; + if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.optionalArguments().isEmpty() || + (mode != mParse && mode != mCompile)) { + usage(); + return 0; + } + if (mode == mParse && cli.argumentValue("language").isEmpty()) { usage(); return 0; } piDebug = !cli.hasArgument("quiet"); PIString out_path = cli.argumentValue("output"); - PIStringList files; - const static PIStringList ext({"h", "hpp", "cpp", "cxx"}); - for (const PIString & a: cli.optionalArguments()) { - if (PIDir::isExists(a)) { - auto dl = PIDir(a).allEntries(); - for (const auto & i: dl) { - if (!i.isFile()) continue; - if (ext.contains(i.extension().toLowerCase().trim())) files << i.path; + if (mode == mParse) { + piCout << Cyan << Bold << "Reading ts file ..."; + auto content = TSFile::read(out_path); + if (content.lang.isEmpty()) content.lang = cli.argumentValue("language"); + content.markAllMissing(); + piCout << Cyan << Bold << "Reading done"; + + bool recursive = cli.hasArgument("recursive"); + + PIStringList files; + const static PIStringList ext({"h", "hpp", "cpp", "cxx"}); + for (const PIString & a: cli.optionalArguments()) { + if (PIDir::isExists(a)) { + auto dl = recursive ? PIDir(a).allEntries() : PIDir(a).entries(); + for (const auto & i: dl) { + if (!i.isFile()) continue; + if (ext.contains(i.extension().toLowerCase().trim())) files << i.path; + } + } else { + if (PIFile::isExists(a)) files << a; } - } else { - if (PIFile::isExists(a)) files << a; } + + piCout << Cyan << Bold << "Read" << files.size_s() << "files ..."; + PITimeMeasurer tm; + PIDir out_dir(PIFile::FileInfo(out_path).dir()); + int count_all = 0, count_new = 0; + for (const auto & p: files) { + PIString file_loc = out_dir.relative(p); + PIFile f(p, PIIODevice::ReadOnly); + gatherStrings(content, PIString::fromUTF8(f.readAll()), file_loc, count_all, count_new); + } + auto elapsed = tm.elapsed(); + piCout << Cyan << Bold + << "Reading done, found %1 strings (%2 new) in %3 ms"_a.arg(count_all).arg(count_new).arg((int)elapsed.toMilliseconds()); + + piCout << Cyan << Bold << "Writing ts file ..."; + if (!TSFile::write(out_path, content, cli.hasArgument("no-obsolete"))) return 1; + piCout << Cyan << Bold << "Writing done"; + } else if (mode == mCompile) { + piCout << Cyan << Bold << "Reading ts file ..."; + auto content = TSFile::read(cli.optionalArguments().front()); + if (content.lang.isEmpty()) content.lang = cli.argumentValue("language"); + piCout << Cyan << Bold << "Reading done"; + + int wcount = 0; + PITranslatorPrivate::Translation t; + t.lang = content.lang; + auto cit = content.contexts.makeIterator(); + while (cit.next()) { + auto * c = t.createContext(cit.key()); + auto mit = cit.value().messages.makeIterator(); + while (mit.next()) { + const auto & m(mit.value()); + if (m.type[TSFile::Message::Unfinished] || m.type[TSFile::Message::Missing]) continue; + c->add(m.source, m.translation); + ++wcount; + // piCout << "tr" << m.source << "->" << m.translation; + } + } + PIFile outf(out_path, PIIODevice::WriteOnly); + if (!outf.isOpened()) { + piCerr << "Can`t open file \"%1\"!"_tr("TSFile").arg(outf.path()); + return 1; + } + outf.clear(); + piCout << Cyan << Bold << "Writing bin file ..."; + PITranslatorPrivate::writeHeader(&outf); + outf.write(t.save()); + piCout << Cyan << Bold << "Writing done (%1 strings)"_a.arg(wcount); } - piCout << Cyan << Bold << "Reading ts file ..."; - auto content = TSFile::read(out_path); - piCout << Cyan << Bold << "Reading done"; - - piCout << Cyan << Bold << "Read" << files.size_s() << "files ..."; - PIDir out_dir(PIFile::FileInfo(out_path).dir()); - for (const auto & p: files) { - PIString file_loc = out_dir.relative(p); - PIFile f(p, PIIODevice::ReadOnly); - gatherStrings(content, PIString::fromUTF8(f.readAll()), file_loc); - } - piCout << Cyan << Bold << "Reading done"; - - piCout << Cyan << Bold << "Writing ts file ..."; - if (!TSFile::write(out_path, content, cli.argumentValue("language"), cli.hasArgument("no-obsolete"))) return 1; - piCout << Cyan << Bold << "Writing done"; - return 0; } diff --git a/utils/translator/parser.cpp b/utils/translator/parser.cpp new file mode 100644 index 00000000..073c28af --- /dev/null +++ b/utils/translator/parser.cpp @@ -0,0 +1,139 @@ +#include "parser.h" + +#include "piliterals_string.h" +#include "pistringlist.h" + + +PIString fromCode(const PIString & in) { + return in.replacedAll("\\n", '\n') + .replaceAll("\\r", '\r') + .replaceAll("\\t", '\t') + .replaceAll("\\\"", '"') + .replaceAll("\\'", '\'') + .replaceAll("\\\\", '\\'); +} + + +void gatherStrings(TSFile::Content & content, const PIString & file, const PIString & file_loc, int & count_all, int & count_new) { + static const PIStringList methods({"PITranslator::tr", "PITranslator::trNoOp", "piTr", "piTrNoOp"}); + static const PIStringList literals({"_tr", "_trNoOp"}); + PIString source, context; + int pos = -1, ppos = 0, line = -1; + int len = 0; + auto isCLetter = [](const PIChar c) { return c.isAlpha() || c.isDigit() || c == '_'; }; + auto add = [&content, file_loc, &count_all, &count_new](const PIString & source, const PIString & context, int line) { + if (source.isEmpty()) return; + ++count_all; + if (content.contexts[context].confirm(fromCode(source), file_loc, line)) ++count_new; + // piCout << "Context = \"" << context << "\", message = \"" << source << "\" (" << file_loc << ":" << line << ")"; + }; + // piCout << "file" << file_loc; + for (const auto & kw: methods) { + // piCout << "method" << kw; + for (;;) { + source.clear(); + context.clear(); + + pos = file.find(kw, pos + 1); + if (pos < 0) break; + if (pos > 0) { + auto pc = file[pos - 1]; + if (isCLetter(pc) || pc == ':') { + pos += kw.size_s(); + continue; + } + } + pos += kw.size_s(); + if (pos < file.size_s() - 1) { + auto pc = file[pos]; + if (isCLetter(pc) || pc == ':') { + pos += kw.size_s(); + continue; + } + } + // piCout << "PITranslator" << pos; + + pos = file.find('(', pos); + if (pos < 0) break; + // piCout << "(" << pos; + pos += 1; + + pos = file.findRange('"', '"', '\\', pos, &len); + if (pos < 0) break; + // piCout << "\"" << pos; + source = file.mid(pos, len); + pos += len + 1; + line = file.lineNumber(pos); + ppos = pos; + + while (pos < file.size_s()) { + if (!file[pos].isSpace() && file[pos] != '\n' && file[pos] != '\r') break; + ++pos; + } + if (pos < file.size_s()) { + // piCout << "check comma" << file[pos]; + if (file[pos] == ',') { + pos += 1; + pos = file.findRange('"', '"', '\\', pos, &len); + // piCout << "check range" << file.mid(pos, len); + if (pos >= 0) { + context = file.mid(pos, len); + pos += len + 1; + ppos = pos; + } + } + } + + pos = ppos; + add(source, context, line); + } + } + for (const auto & kw: literals) { + // piCout << "literal" << kw; + pos = -1; + for (;;) { + source.clear(); + context.clear(); + + pos = file.find('"', pos + 1); + if (pos < 0) break; + pos = file.findRange('"', '"', '\\', pos, &len); + if (pos < 0) break; + // piCout << "\"" << pos; + source = file.mid(pos, len); + pos += len + 1; + if (file.mid(pos, kw.size_s()) == kw) { + // piCout << "line" << file.lineNumber(pos); + pos += kw.size_s(); + if (pos < file.size_s() - 1) { + auto pc = file[pos]; + if (isCLetter(pc) || pc == ':') { + continue; + } + } + ppos = pos; + while (pos < file.size_s()) { + if (!file[pos].isSpace() && file[pos] != '\n' && file[pos] != '\r') break; + ++pos; + } + if (pos < file.size_s()) { + // piCout << "check comma" << file[pos]; + if (file[pos] == '(') { + pos += 1; + pos = file.findRange('"', '"', '\\', pos, &len); + // piCout << "check range" << file.mid(pos, len); + if (pos >= 0) { + context = file.mid(pos, len); + pos += len + 1; + ppos = pos; + } + } + } + pos = ppos; + if (source.isEmpty()) continue; + line = file.lineNumber(pos); + add(source, context, line); + } + } + } +} diff --git a/utils/translator/parser.h b/utils/translator/parser.h new file mode 100644 index 00000000..5e900d34 --- /dev/null +++ b/utils/translator/parser.h @@ -0,0 +1,9 @@ +#ifndef PITR_PARSER_H +#define PITR_PARSER_H + +#include "ts_file.h" + +PIString fromCode(const PIString & in); +void gatherStrings(TSFile::Content & content, const PIString & file, const PIString & file_loc, int & count_all, int & count_new); + +#endif diff --git a/utils/value_tree_translator/main.cpp b/utils/value_tree_translator/main.cpp index 0a3a0cde..e9c48d98 100644 --- a/utils/value_tree_translator/main.cpp +++ b/utils/value_tree_translator/main.cpp @@ -124,10 +124,12 @@ int main(int argc, char * argv[]) { piCout << Cyan << Bold << "Reading ts file ..."; auto content = TSFile::read(out_path); + if (content.lang.isEmpty()) content.lang = cli.argumentValue("language"); + content.markAllMissing(); piCout << Cyan << Bold << "Reading done"; piCout << Cyan << Bold << "Read" << files.size_s() << "files ..."; - auto & context(content[contextName]); + auto & context(content.contexts[contextName]); PIDir out_dir(PIFile::FileInfo(out_path).dir()); for (const auto & p: files) { PIString ext = PIFile::FileInfo(p).extension().toLowerCase().trim(); @@ -142,7 +144,7 @@ int main(int argc, char * argv[]) { piCout << Cyan << Bold << "Reading done"; piCout << Cyan << Bold << "Writing ts file ..."; - if (!TSFile::write(out_path, content, cli.argumentValue("language"), cli.hasArgument("no-obsolete"))) return 1; + if (!TSFile::write(out_path, content, cli.hasArgument("no-obsolete"))) return 1; piCout << Cyan << Bold << "Writing done"; return 0; From 22474739591e5882c7ee1607db8baccbf8fcd1fd Mon Sep 17 00:00:00 2001 From: peri4 Date: Thu, 7 Nov 2024 15:43:13 +0300 Subject: [PATCH 30/40] PICodeParser namespace fix --- libs/main/code/picodeparser.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/main/code/picodeparser.cpp b/libs/main/code/picodeparser.cpp index a1c8f58a..7badf95b 100644 --- a/libs/main/code/picodeparser.cpp +++ b/libs/main/code/picodeparser.cpp @@ -498,6 +498,7 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) { MetaMap smeta = maybeMeta(pfc); meta << smeta; } + // piCout << "pfc E" << cur_namespace << "," << tmp; parseEnum(0, cur_namespace + tmp, pfc.takeRange('{', '}'), meta); pfc.takeSymbol(); continue; @@ -613,8 +614,8 @@ void PICodeParser::parseClass(Entity * parent, PIString & fc, bool is_namespace) int ps = -1; bool def = false; PIString prev_namespace = cur_namespace, stmp; - if (ce) cur_namespace += ce->name + s_ns; - // piCout << "parse class" << ce->name << "namespace" << cur_namespace; + if (ce) cur_namespace = ce->name + s_ns; + // piCout << "parse class" << (ce ? ce->name : "NULL") << "namespace" << cur_namespace; // piCout << "\nparse class" << ce->name << "namespace" << cur_namespace; while (!fc.isEmpty()) { PIString cw = fc.takeCWord(), tmp; @@ -666,6 +667,7 @@ void PICodeParser::parseClass(Entity * parent, PIString & fc, bool is_namespace) MetaMap smeta = maybeMeta(fc); meta << smeta; } + // piCout << "pc E" << cur_namespace << "," << tmp; parseEnum(ce, cur_namespace + tmp, fc.takeRange('{', '}'), meta); fc.takeSymbol(); continue; From 5b066cbc27c274248d609e0d5c1e80188abac637 Mon Sep 17 00:00:00 2001 From: peri4 Date: Sun, 10 Nov 2024 21:28:59 +0300 Subject: [PATCH 31/40] add PIProtectedVariable --- libs/main/thread/piprotectedvariable.h | 60 ++++++++++++++++++++++++++ libs/main/thread/pithreadmodule.h | 1 + main.cpp | 46 ++++++++++++++++---- 3 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 libs/main/thread/piprotectedvariable.h diff --git a/libs/main/thread/piprotectedvariable.h b/libs/main/thread/piprotectedvariable.h new file mode 100644 index 00000000..c5bc2b01 --- /dev/null +++ b/libs/main/thread/piprotectedvariable.h @@ -0,0 +1,60 @@ +/*! \file piprotectedvariable.h + * \ingroup Thread + * \~\brief + * \~english Thread-safe variable + * \~russian Потокобезопасная переменная + */ +/* + PIP - Platform Independent Primitives + Thread-safe variable + Ivan Pelipenko peri4ko@yandex.ru, Stephan Fomenko, Andrey Bychkov work.a.b@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef PIPROTECTEDVARIABLE_H +#define PIPROTECTEDVARIABLE_H + +#include "pimutex.h" + + +template +class PIP_EXPORT PIProtectedVariable { +public: + void set(const T & v) { + PIMutexLocker _ml(mutex); + var = v; + } + void set(T && v) { + PIMutexLocker _ml(mutex); + var = std::move(v); + } + T get() const { + PIMutexLocker _ml(mutex); + return var; + } + T & lock() { + mutex.lock(); + return var; + } + void unlock() { mutex.unlock(); } + + +private: + mutable PIMutex mutex; + T var; +}; + + +#endif diff --git a/libs/main/thread/pithreadmodule.h b/libs/main/thread/pithreadmodule.h index e4e0733d..b9a98069 100644 --- a/libs/main/thread/pithreadmodule.h +++ b/libs/main/thread/pithreadmodule.h @@ -55,6 +55,7 @@ #include "pigrabberbase.h" #include "pimutex.h" #include "pipipelinethread.h" +#include "piprotectedvariable.h" #include "pispinlock.h" #include "pithread.h" #include "pithreadnotifier.h" diff --git a/main.cpp b/main.cpp index 764a1f9d..38593865 100644 --- a/main.cpp +++ b/main.cpp @@ -8,12 +8,50 @@ #include "pilog.h" #include "pimathbase.h" #include "pip.h" +#include "piprotectedvariable.h" #include "pitranslator_p.h" #include "pivaluetree_conversions.h" using namespace PICoutManipulators; +class MyStr: public PIString { +public: + MyStr(): PIString() {} + MyStr(const char * o): PIString(o) { piCout << "MyStr *"; } + MyStr(const MyStr & o): PIString(o) { piCout << "MyStr &"; } + // MyStr(const MyStr & o): PIString(o) { piCout << "MyStr &"; } + MyStr(MyStr && o): PIString(o) { piCout << "MyStr &&"; } + MyStr & operator=(const MyStr & o) { + *this += o; + piCout << "MyStr =&"; + return *this; + } + MyStr & operator=(MyStr && o) { + *this += o; + piCout << "MyStr =&&"; + return *this; + } +}; + + int main(int argc, char * argv[]) { + PIProtectedVariable var; + // MyStr s("hello"); + var.set("hello"); + auto & v = var.lock(); + // v = "world"; + var.unlock(); + piCout << var.get(); + + /*PICodeParser parser; + parser.parseFile("client_server.h"); + for (auto m: parser.enums) { + piCout << ""; + piCout << m.name; // << m.args << m.value; + // piCout << m.expand({"hello"}); + }*/ + + return 0; PITranslator::loadLang("ru"); PISerial f("COM123"); f.open(); @@ -47,14 +85,6 @@ int main(int argc, char * argv[]) { piCout << "hello!"_tr("Co") << "hello!"_tr;*/ // piCout << "hello!"_trNoOp; - // PICodeParser parser; - // parser.parseFile("cmg_test.h"); - /*for (auto m: parser.macros) { - piCout << ""; - piCout << m.name << m.args << m.value; - piCout << m.expand({"hello"}); - } -*/ /*PISet set; piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set)); From 4655d725546b82331a005cdbfd248145a6cb8e39 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 12 Nov 2024 18:50:22 +0300 Subject: [PATCH 32/40] new class PISemaphore doc for PIProtectedVariable --- libs/main/thread/piprotectedvariable.h | 21 ++++ libs/main/thread/pisemaphore.cpp | 152 +++++++++++++++++++++++++ libs/main/thread/pisemaphore.h | 95 ++++++++++++++++ libs/main/thread/pithreadmodule.h | 1 + main.cpp | 72 ++++++++++-- 5 files changed, 333 insertions(+), 8 deletions(-) create mode 100644 libs/main/thread/pisemaphore.cpp create mode 100644 libs/main/thread/pisemaphore.h diff --git a/libs/main/thread/piprotectedvariable.h b/libs/main/thread/piprotectedvariable.h index c5bc2b01..b7490f95 100644 --- a/libs/main/thread/piprotectedvariable.h +++ b/libs/main/thread/piprotectedvariable.h @@ -32,24 +32,45 @@ template class PIP_EXPORT PIProtectedVariable { public: + //! \~english Sets value to copy of \"v\" + //! \~russian Устанавливает значение как копию \"v\" void set(const T & v) { PIMutexLocker _ml(mutex); var = v; } + + //! \~english Sets value by moving \"v\" + //! \~russian Устанавливает значение перемещением \"v\" void set(T && v) { PIMutexLocker _ml(mutex); var = std::move(v); } + + //! \~english Returns copy of value + //! \~russian Возвращает копию значения T get() const { PIMutexLocker _ml(mutex); return var; } + + //! \~english Lock mutex and returns reference of value + //! \~russian Блокирует мьютекс и возвращает ссылку на значение T & lock() { mutex.lock(); return var; } + + //! \~english Unlock mutex + //! \~russian Разблокирует мьютекс void unlock() { mutex.unlock(); } + //! \~english Sets value to copy of \"v\" + //! \~russian Устанавливает значение как копию \"v\" + PIProtectedVariable & operator=(const T & v) { + set(v); + return *this; + } + private: mutable PIMutex mutex; diff --git a/libs/main/thread/pisemaphore.cpp b/libs/main/thread/pisemaphore.cpp new file mode 100644 index 00000000..3decf3eb --- /dev/null +++ b/libs/main/thread/pisemaphore.cpp @@ -0,0 +1,152 @@ +/* + PIP - Platform Independent Primitives + PISemaphore, PISemaphoreLocker + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +//! \addtogroup Thread +//! \{ +//! \class PISemaphore pisemaphore.h +//! +//! \~\brief +//! \~english Basic semaphore +//! \~russian Простой семафор +//! +//! +//! \~\details +//! \~english \section PISemaphore_sec0 Synopsis +//! \~russian \section PISemaphore_sec0 Краткий обзор +//! +//! \~english +//! %PISemaphore provides critical code section defence between several threads with resource counting. +//! Semaphore contains logic counter and functions to change it: +//! \a release(), \a acquire() and \a tryAcquire(). +//! +//! For automatic acquire-release use \a PISemaphoreLocker. +//! +//! \~russian +//! %PISemaphore предоставляет межпотоковую защиту критических секций кода с подсчетом ресурсов. +//! Семафор состоит из логического счетчика и методов для его изменения: +//! \a release(), \a acquire() and \a tryAcquire(). +//! +//! Для автоматического захвата-освобождения используйте \a PISemaphoreLocker. +//! +//! \~english \section PISemaphore_sec1 Usage +//! \~russian \section PISemaphore_sec1 Использование +//! +//! \~english +//! +//! \~russian +//! +//! \~\code +//! \endcode +//! \} + + +//! \addtogroup Thread +//! \{ +//! \class PISemaphoreLocker pisemaphore.h +//! +//! \~\brief +//! \~english %PISemaphore autolocker +//! \~russian Автоблокировщик %PISemaphore +//! +//! +//! \~\details +//! +//! \~english +//! When a %PISemaphoreLocker object is created, it attempts to acquire the semaphore resources, if "condition" \c true. +//! When control leaves the scope in which the %PISemaphoreLocker object was created, +//! the %PISemaphoreLocker is destructed and the resources are released, if "condition" was \c true. +//! +//! If "condition" \c false this class do nothing. +//! +//! The %PISemaphoreLocker class is non-copyable. +//! +//! \~russian +//! При создании экземпляра %PISemaphoreLocker захватываются ресурсы семафора, если "condition" \c true. +//! Когда выполнение покидает область жизни объекта, вызывается его деструктор и ресурсы +//! освобождаются, если "condition" был \c true. +//! +//! Если "condition" \c false, то этот объект ничего не делает. +//! +//! Класс %PISemaphoreLocker некопируемый. +//! +//! \~\code +//! // critical section start +//! { +//! PISemaphoreLocker locker(mutex, 5); +//! // ... your code here +//! } +//! // critical section end +//! \endcode +//! \} + + +#include "pisemaphore.h" + + +PISemaphore::PISemaphore(int initial) { + count = initial; +} + + +PISemaphore::~PISemaphore() { + var.notifyAll(); +} + + +void PISemaphore::acquire(int cnt) { + PIMutexLocker _ml(mutex); + while (count < cnt) { + var.wait(mutex); + } + count -= cnt; +} + + +bool PISemaphore::tryAcquire(int cnt) { + PIMutexLocker _ml(mutex); + if (count < cnt) return false; + count -= cnt; + return true; +} + + +bool PISemaphore::tryAcquire(int cnt, PISystemTime timeout) { + PITimeMeasurer tm; + PIMutexLocker _ml(mutex); + while (count < cnt) { + PISystemTime remain = timeout - tm.elapsed(); + if (remain.isNegative()) return false; + var.waitFor(mutex, remain); + } + count -= cnt; + return true; +} + + +void PISemaphore::release(int cnt) { + PIMutexLocker _ml(mutex); + count += cnt; + var.notifyAll(); +} + + +int PISemaphore::available() const { + PIMutexLocker _ml(mutex); + return count; +} diff --git a/libs/main/thread/pisemaphore.h b/libs/main/thread/pisemaphore.h new file mode 100644 index 00000000..5f964408 --- /dev/null +++ b/libs/main/thread/pisemaphore.h @@ -0,0 +1,95 @@ +/*! \file pisemaphore.h + * \ingroup Semaphore + * \~\brief + * \~english Basic semaphore + * \~russian Простой семафор + */ +/* + PIP - Platform Independent Primitives + PISemaphore, PISemaphoreLocker + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef PISEMAPHORE_H +#define PISEMAPHORE_H + +#include "piconditionvar.h" + + +class PIP_EXPORT PISemaphore { +public: + NO_COPY_CLASS(PISemaphore) + + //! \~english Constructs semaphore with \"initial\" available resources. + //! \~russian Создает семафор с \"initial\" начальными свободными ресурсами. + PISemaphore(int initial = 0); + + //! \~english Destroy semaphore. + //! \~russian Деструктор семафора. + ~PISemaphore(); + + + //! \~english Acquire \"cnt\" resources. If no available resources, than blocks until they freed. + //! \~russian Захватывает \"cnt\" ресурсов. Если свободных ресурсов недостаточно, то блокирует до их появления. + void acquire(int cnt = 1); + + //! \~english Try to acquire \"cnt\" resources. Returns if operation was successfull. + //! \~russian Пробует захватывает \"cnt\" ресурсов. Возвращает успех захвата. + bool tryAcquire(int cnt = 1); + + //! \~english Try to acquire \"cnt\" resources for \"timeout\". Returns if operation was successfull (timeout has not expired). + //! \~russian Пробует захватывает \"cnt\" ресурсов в течении \"timeout\". Возвращает успех захвата (не истек ли тайм-аут). + bool tryAcquire(int cnt, PISystemTime timeout); + + //! \~english Release \"cnt\" resources. + //! \~russian Освобождает \"cnt\" ресурсов. + void release(int cnt = 1); + + //! \~english Returns available resources count. + //! \~russian Возвращает количество свободных ресурсов. + int available() const; + +private: + int count = 0; + mutable PIMutex mutex; + PIConditionVariable var; +}; + + +class PIP_EXPORT PISemaphoreLocker { +public: + NO_COPY_CLASS(PISemaphoreLocker); + + //! \~english Constructs and acquire \"cnt\" resources on semaphore "s" if "condition" is \c true. + //! \~russian Создается и захватывает \"cnt\" ресурсов у семафора "s" если "condition" \c true. + PISemaphoreLocker(PISemaphore & s, int cnt = 1, bool condition = true): sem(s), count(cnt), cond(condition) { + if (cond) sem.acquire(count); + } + + //! \~english Release "cnt" resources on semaphore if "condition" was \c true. + //! \~russian Освобождает "cnt" ресурсов у семафора если "condition" был \c true. + ~PISemaphoreLocker() { + if (cond) sem.release(count); + } + +private: + PISemaphore & sem; + int count = 1; + bool cond = true; +}; + + +#endif // PIMUTEX_H diff --git a/libs/main/thread/pithreadmodule.h b/libs/main/thread/pithreadmodule.h index b9a98069..f5e7c7e3 100644 --- a/libs/main/thread/pithreadmodule.h +++ b/libs/main/thread/pithreadmodule.h @@ -56,6 +56,7 @@ #include "pimutex.h" #include "pipipelinethread.h" #include "piprotectedvariable.h" +#include "pisemaphore.h" #include "pispinlock.h" #include "pithread.h" #include "pithreadnotifier.h" diff --git a/main.cpp b/main.cpp index 38593865..34c01013 100644 --- a/main.cpp +++ b/main.cpp @@ -12,8 +12,10 @@ #include "pitranslator_p.h" #include "pivaluetree_conversions.h" -using namespace PICoutManipulators; +#include +using namespace PICoutManipulators; +/* class MyStr: public PIString { public: MyStr(): PIString() {} @@ -32,16 +34,70 @@ public: return *this; } }; +*/ +class RWL { +public: + void lockWrite() { mutex.lock(); } + void unlockWrite() { mutex.unlock(); } + void lockRead() {} + void unlockRead() {} + PIMutex mutex; +}; + +PIMutex mutex; +PISemaphore sem(10); + int main(int argc, char * argv[]) { - PIProtectedVariable var; - // MyStr s("hello"); - var.set("hello"); - auto & v = var.lock(); - // v = "world"; - var.unlock(); - piCout << var.get(); + /*sem.acquire(2); + piCout << sem.tryAcquire(2); + piCout << sem.available(); + return 0;*/ + PIThread t_w( + [] { + // PIMutexLocker _ml(mutex); + piCout << "write start ..."; + piMSleep(500); + sem.release(11); + piCout << "write end" << sem.available() << "\n"; + }, + true, + 1_Hz); + + int cnt0 = 0, cnt1 = 0; + PIThread t_r0( + [&cnt0] { + // PIMutexLocker _ml(mutex); + piCout << "read0 start ..."; + piMSleep(50); + sem.acquire(2); + bool ok = true; // sem.tryAcquire(2, 100_ms); + if (ok) ++cnt0; + piCout << "read0 end" << sem.available() << ok; + }, + true, + 10_Hz); + PIThread t_r1( + [&cnt1] { + // PIMutexLocker _ml(mutex); + piCout << "read1 start ..."; + piMSleep(50); + sem.acquire(2); + bool ok = true; // sem.tryAcquire(2, 100_ms); + if (ok) ++cnt1; + piCout << "read1 end" << sem.available() << ok; + }, + true, + 11_Hz); + + piSleep(5.); + + t_r0.stopAndWait(); + t_r1.stopAndWait(); + t_w.stopAndWait(); + + piCout << cnt0 << cnt1; /*PICodeParser parser; parser.parseFile("client_server.h"); From 0840d807a0514fa1b6805e0906a0712e88817b5c Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 13 Nov 2024 13:56:16 +0300 Subject: [PATCH 33/40] add PIReadWriteLock, PIReadLocker and PIWriteLocker --- libs/main/thread/pireadwritelock.cpp | 234 +++++++++++++++++++++++++++ libs/main/thread/pireadwritelock.h | 130 +++++++++++++++ libs/main/thread/pisemaphore.h | 2 +- libs/main/thread/pithreadmodule.h | 1 + main.cpp | 75 +++++++-- 5 files changed, 424 insertions(+), 18 deletions(-) create mode 100644 libs/main/thread/pireadwritelock.cpp create mode 100644 libs/main/thread/pireadwritelock.h diff --git a/libs/main/thread/pireadwritelock.cpp b/libs/main/thread/pireadwritelock.cpp new file mode 100644 index 00000000..99ebb209 --- /dev/null +++ b/libs/main/thread/pireadwritelock.cpp @@ -0,0 +1,234 @@ +/* + PIP - Platform Independent Primitives + PIReadWriteLock, PIReadLocker, PIWriteLocker + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +//! \addtogroup Thread +//! \{ +//! \class PIReadWriteLock pireadwritelock.h +//! +//! \~\brief +//! \~english Read/Write lock +//! \~russian Блокировка чтения/записи +//! +//! +//! \~\details +//! \~english \section PIReadWriteLock_sec0 Synopsis +//! \~russian \section PIReadWriteLock_sec0 Краткий обзор +//! +//! \~english +//! %PIReadWriteLock provides more complex critical code section defence between several threads. +//! %PIReadWriteLock permit only one thread to modify data, besides multiple thread can read data +//! simultaneously. Conatains methods: +//! \a lockWrite(), \a tryLockWrite(), \a unlockWrite(), \a lockRead(), \a tryLockRead() and \a unlockRead(). +//! +//! For automatic read and write locks use \a PIReadLocker and \a PIWriteLocker. +//! +//! \~russian +//! %PIReadWriteLock предоставляет более сложную межпотоковую защиту критических секций кода. +//! %PIReadWriteLock разрешает только одному потоку изменять данные, в то время как читать данные +//! могут одновременно несколько потоков. +//! Содержит методы: +//! \a lockWrite(), \a tryLockWrite(), \a unlockWrite(), \a lockRead(), \a tryLockRead() и \a unlockRead(). +//! +//! Для автоматической блокировки на чтение и запись используйте \a PIReadLocker и \a PIWriteLocker. +//! +//! \~english \section PIReadWriteLock_sec1 Usage +//! \~russian \section PIReadWriteLock_sec1 Использование +//! +//! \~english +//! When one thread lock for write with \a lockWrite(), all other threads (read and write) can`t access data until this thread +//! call \a unlockWrite(). In this way, write lock works similar to mutex. +//! +//! On the other hand, several threads can simultaneously read data. In other words, \a lockRead() increase read threads counter +//! and \a unlockRead() decrease. But thread can`t read data while other thread locked for write. +//! +//! \~russian +//! Когда один поток блокирует запись с помощью функции \a lockWrite(), все остальные потоки (чтение и запись) не смогут получить +//! доступ к данным, пока этот поток не вызовет \a unlockWrite(). Таким образом, блокировка записи работает аналогично мьютексу. +//! +//! С другой стороны, несколько потоков могут одновременно считывать данные. Другими словами, функция \a lockRead() увеличивает счетчик +//! потоков чтения, а функция \a unlockRead() уменьшает. Но поток не может считывать данные, пока другой поток заблокирован для записи. +//! +//! \~\code +//! \endcode +//! \} + + +//! \addtogroup Thread +//! \{ +//! \class PIReadLocker pireadwritelock.h +//! +//! \~\brief +//! \~english %PIReadWriteLock read autolocker +//! \~russian Автоблокировщик на чтение %PIReadWriteLock +//! +//! +//! \~\details +//! +//! \~english +//! When a %PIReadLocker object is created, it lock %PIReadWriteLock for read, if "condition" \c true. +//! When control leaves the scope in which the %PIReadLocker object was created, +//! the %PIReadLocker is destructed and unlock for read, if "condition" was \c true. +//! +//! If "condition" \c false this class do nothing. +//! +//! The %PIReadLocker class is non-copyable. +//! +//! \~russian +//! При создании экземпляра %PIReadLocker он блокирует %PIReadWriteLock на чтение, если "condition" \c true. +//! Когда выполнение покидает область жизни объекта, вызывается его деструктор и освобождается блокировка на чтение, +//! если "condition" был \c true. +//! +//! Если "condition" \c false, то этот объект ничего не делает. +//! +//! Класс %PIReadLocker некопируемый. +//! +//! \~\code +//! // critical section start +//! { +//! PIReadLocker locker(rw_lock); +//! // ... your read code here +//! } +//! // critical section end +//! \endcode +//! \} + + +//! \addtogroup Thread +//! \{ +//! \class PIWriteLocker pireadwritelock.h +//! +//! \~\brief +//! \~english %PIReadWriteLock write autolocker +//! \~russian Автоблокировщик на запись %PIReadWriteLock +//! +//! +//! \~\details +//! +//! \~english +//! When a %PIWriteLocker object is created, it lock %PIReadWriteLock for write, if "condition" \c true. +//! When control leaves the scope in which the %PIWriteLocker object was created, +//! the %PIWriteLocker is destructed and unlock for write, if "condition" was \c true. +//! +//! If "condition" \c false this class do nothing. +//! +//! The %PIWriteLocker class is non-copyable. +//! +//! \~russian +//! При создании экземпляра %PIWriteLocker он блокирует %PIReadWriteLock на запись, если "condition" \c true. +//! Когда выполнение покидает область жизни объекта, вызывается его деструктор и освобождается блокировка на запись, +//! если "condition" был \c true. +//! +//! Если "condition" \c false, то этот объект ничего не делает. +//! +//! Класс %PIWriteLocker некопируемый. +//! +//! \~\code +//! // critical section start +//! { +//! PIWriteLocker locker(rw_lock); +//! // ... your write code here +//! } +//! // critical section end +//! \endcode +//! \} + + +#include "pireadwritelock.h" + + +PIReadWriteLock::PIReadWriteLock() {} + + +PIReadWriteLock::~PIReadWriteLock() { + var.notifyAll(); +} + + +void PIReadWriteLock::lockWrite() { + PIMutexLocker _ml(mutex); + while (reading > 0 || writing) { + var.wait(mutex); + } + writing = true; +} + + +bool PIReadWriteLock::tryLockWrite() { + PIMutexLocker _ml(mutex); + if (reading > 0 || writing) return false; + writing = true; + return true; +} + + +bool PIReadWriteLock::tryLockWrite(PISystemTime timeout) { + PITimeMeasurer tm; + PIMutexLocker _ml(mutex); + while (reading > 0 || writing) { + PISystemTime remain = timeout - tm.elapsed(); + if (remain.isNegative()) return false; + var.waitFor(mutex, remain); + } + writing = true; + return true; +} + + +void PIReadWriteLock::unlockWrite() { + PIMutexLocker _ml(mutex); + writing = false; + var.notifyAll(); +} + + +void PIReadWriteLock::lockRead() { + PIMutexLocker _ml(mutex); + while (writing) { + var.wait(mutex); + } + ++reading; +} + + +bool PIReadWriteLock::tryLockRead() { + PIMutexLocker _ml(mutex); + if (writing) return false; + ++reading; + return true; +} + + +bool PIReadWriteLock::tryLockRead(PISystemTime timeout) { + PITimeMeasurer tm; + PIMutexLocker _ml(mutex); + while (writing) { + PISystemTime remain = timeout - tm.elapsed(); + if (remain.isNegative()) return false; + var.waitFor(mutex, remain); + } + ++reading; + return true; +} + + +void PIReadWriteLock::unlockRead() { + PIMutexLocker _ml(mutex); + --reading; + var.notifyAll(); +} diff --git a/libs/main/thread/pireadwritelock.h b/libs/main/thread/pireadwritelock.h new file mode 100644 index 00000000..24b08a37 --- /dev/null +++ b/libs/main/thread/pireadwritelock.h @@ -0,0 +1,130 @@ +/*! \file pireadwritelock.h + * \ingroup Read/Write lock + * \~\brief + * \~english Read/Write lock + * \~russian Блокировка чтения/записи + */ +/* + PIP - Platform Independent Primitives + PIReadWriteLock, PIReadLocker, PIWriteLocker + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef PIREADWRITELOCK_H +#define PIREADWRITELOCK_H + +#include "piconditionvar.h" + + +class PIP_EXPORT PIReadWriteLock { +public: + NO_COPY_CLASS(PIReadWriteLock) + + //! \~english Constructs %PIReadWriteLock. + //! \~russian Создает %PIReadWriteLock. + PIReadWriteLock(); + + //! \~english Destroy %PIReadWriteLock. + //! \~russian Деструктор %PIReadWriteLock. + ~PIReadWriteLock(); + + + //! \~english Lock for write. If already locked for write or read, than wait until all locks released. + //! \~russian Заблокировать на запись. Если уже заблокировано на запись или чтение, то ждёт освобождения блокировок. + void lockWrite(); + + //! \~english Try to lock for write. Returns if operation was successfull. + //! \~russian Пробует заблокировать на запись. Возвращает успех операции. + bool tryLockWrite(); + + //! \~english Try to lock for write for \"timeout\". Returns if operation was successfull (timeout has not expired). + //! \~russian Пробует заблокировать на запись в течении \"timeout\". Возвращает успех операции (не истек ли тайм-аут). + bool tryLockWrite(PISystemTime timeout); + + //! \~english Release lock for write. + //! \~russian Освобождает блокировку на запись. + void unlockWrite(); + + + //! \~english Lock for read. If already locked for write, than wait until write lock released. + //! \~russian Заблокировать на чтение. Если уже заблокировано на запись, то ждёт освобождения записывающей блокировки. + void lockRead(); + + //! \~english Try to lock for read. Returns if operation was successfull. + //! \~russian Пробует заблокировать на чтение. Возвращает успех операции. + bool tryLockRead(); + + //! \~english Try to lock for read for \"timeout\". Returns if operation was successfull (timeout has not expired). + //! \~russian Пробует заблокировать на чтение в течении \"timeout\". Возвращает успех операции (не истек ли тайм-аут). + bool tryLockRead(PISystemTime timeout); + + //! \~english Release lock for read. + //! \~russian Освобождает блокировку на чтение. + void unlockRead(); + +private: + int reading = 0; + bool writing = false; + PIMutex mutex; + PIConditionVariable var; +}; + + +class PIP_EXPORT PIReadLocker { +public: + NO_COPY_CLASS(PIReadLocker); + + //! \~english Constructs and lock for read %PIReadWriteLock "l" if "condition" is \c true. + //! \~russian Создается и блокирует на чтение %PIReadWriteLock "l" если "condition" \c true. + PIReadLocker(PIReadWriteLock & l, bool condition = true): rwl(l), cond(condition) { + if (cond) rwl.lockRead(); + } + + //! \~english Release read lock on %PIReadWriteLock if "condition" was \c true. + //! \~russian Освобождает блокировку на чтение %PIReadWriteLock если "condition" был \c true. + ~PIReadLocker() { + if (cond) rwl.unlockRead(); + } + +private: + PIReadWriteLock & rwl; + bool cond = true; +}; + + +class PIP_EXPORT PIWriteLocker { +public: + NO_COPY_CLASS(PIWriteLocker); + + //! \~english Constructs and lock for write %PIReadWriteLock "l" if "condition" is \c true. + //! \~russian Создается и блокирует на запись %PIReadWriteLock "l" если "condition" \c true. + PIWriteLocker(PIReadWriteLock & l, bool condition = true): rwl(l), cond(condition) { + if (cond) rwl.lockWrite(); + } + + //! \~english Release write lock on %PIReadWriteLock if "condition" was \c true. + //! \~russian Освобождает блокировку на запись %PIReadWriteLock если "condition" был \c true. + ~PIWriteLocker() { + if (cond) rwl.unlockWrite(); + } + +private: + PIReadWriteLock & rwl; + bool cond = true; +}; + + +#endif diff --git a/libs/main/thread/pisemaphore.h b/libs/main/thread/pisemaphore.h index 5f964408..7bacbecc 100644 --- a/libs/main/thread/pisemaphore.h +++ b/libs/main/thread/pisemaphore.h @@ -92,4 +92,4 @@ private: }; -#endif // PIMUTEX_H +#endif diff --git a/libs/main/thread/pithreadmodule.h b/libs/main/thread/pithreadmodule.h index f5e7c7e3..eba1b509 100644 --- a/libs/main/thread/pithreadmodule.h +++ b/libs/main/thread/pithreadmodule.h @@ -56,6 +56,7 @@ #include "pimutex.h" #include "pipipelinethread.h" #include "piprotectedvariable.h" +#include "pireadwritelock.h" #include "pisemaphore.h" #include "pispinlock.h" #include "pithread.h" diff --git a/main.cpp b/main.cpp index 34c01013..338791b2 100644 --- a/main.cpp +++ b/main.cpp @@ -39,28 +39,66 @@ public: class RWL { public: - void lockWrite() { mutex.lock(); } - void unlockWrite() { mutex.unlock(); } - void lockRead() {} - void unlockRead() {} + void lockWrite() { + PIMutexLocker _ml(mutex); + while (reading > 0 || writing) { + var.wait(mutex); + } + writing = true; + } + void unlockWrite() { + PIMutexLocker _ml(mutex); + writing = false; + var.notifyAll(); + } + void lockRead() { + PIMutexLocker _ml(mutex); + while (writing) { + var.wait(mutex); + } + ++reading; + } + void unlockRead() { + PIMutexLocker _ml(mutex); + --reading; + var.notifyAll(); + } + +private: + PIConditionVariable var; + int reading = 0; + bool writing = false; PIMutex mutex; }; PIMutex mutex; PISemaphore sem(10); +PIReadWriteLock rwl; int main(int argc, char * argv[]) { /*sem.acquire(2); piCout << sem.tryAcquire(2); piCout << sem.available(); return 0;*/ - PIThread t_w( + PIThread t_w0( [] { // PIMutexLocker _ml(mutex); - piCout << "write start ..."; + PIWriteLocker rl(rwl); + piCout << "write0 start ..."; piMSleep(500); - sem.release(11); - piCout << "write end" << sem.available() << "\n"; + piCout << "write0 end" + << "\n"; + }, + true, + 1_Hz); + PIThread t_w1( + [] { + // PIMutexLocker _ml(mutex); + PIWriteLocker rl(rwl); + piCout << "write1 start ..."; + piMSleep(500); + piCout << "write1 end" + << "\n"; }, true, 1_Hz); @@ -69,33 +107,36 @@ int main(int argc, char * argv[]) { PIThread t_r0( [&cnt0] { // PIMutexLocker _ml(mutex); + PIReadLocker rl(rwl); piCout << "read0 start ..."; piMSleep(50); - sem.acquire(2); - bool ok = true; // sem.tryAcquire(2, 100_ms); - if (ok) ++cnt0; - piCout << "read0 end" << sem.available() << ok; + // bool ok = rwl.tryLockRead(100_ms); + // if (ok) ++cnt0; + piCout << "read0 end"; + // if (ok) rwl.unlockRead(); }, true, 10_Hz); PIThread t_r1( [&cnt1] { // PIMutexLocker _ml(mutex); + // PIReadLocker rl(rwl); piCout << "read1 start ..."; piMSleep(50); - sem.acquire(2); - bool ok = true; // sem.tryAcquire(2, 100_ms); + bool ok = rwl.tryLockRead(100_ms); if (ok) ++cnt1; - piCout << "read1 end" << sem.available() << ok; + piCout << "read1 end" << ok; + if (ok) rwl.unlockRead(); }, true, 11_Hz); - piSleep(5.); + piSleep(8.); t_r0.stopAndWait(); t_r1.stopAndWait(); - t_w.stopAndWait(); + t_w0.stopAndWait(); + t_w1.stopAndWait(); piCout << cnt0 << cnt1; From ee34e8a72edd13ca34dfd6e78c1c60b61069e4fc Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 13 Nov 2024 16:32:21 +0300 Subject: [PATCH 34/40] doc --- doc/pages/summary.md | 80 +++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/doc/pages/summary.md b/doc/pages/summary.md index 0a4adc5a..c7a0dce4 100644 --- a/doc/pages/summary.md +++ b/doc/pages/summary.md @@ -3,19 +3,19 @@ \~english -* direct output to console (\a PICout) -* containers (\a PIVector, \a PIDeque, \a PIVector2D, \a PIStack, \a PIQueue, \a PIMap, \a PISet) -* byte array (\a PIByteArray) -* serialization (\a PIBinaryStream, \a PITextStream, \a PIIOBinaryStream, \a PIIOTextStream, \a PIChunkStream, \a PIJSON) -* string (\a PIConstChars, \a PIString, \a PIStringList) -* base object (events and handlers) (\a PIObject) -* multithreading +* Direct output to console (\a PICout) +* Containers (\a PIVector, \a PIDeque, \a PIVector2D, \a PIStack, \a PIQueue, \a PIMap, \a PISet) +* Byte array (\a PIByteArray) +* Serialization (\a PIBinaryStream, \a PITextStream, \a PIIOBinaryStream, \a PIIOTextStream, \a PIChunkStream, \a PIJSON) +* String (\a PIConstChars, \a PIString, \a PIStringList) +* Base object (events and handlers) (\a PIObject) +* Multithreading * thread (\a PIThread) - * blocking (\a PIMutex, \a PISpinlock) - * executor (\a PIThreadPoolExecutor) + * blocking (\a PIMutex, \a PISpinlock, \a PIConditionVariable, \a PISemaphore, \a PIReadWriteLock) + * executor (\a PIThreadPoolExecutor, \a PIThreadPoolLoop) * blocking dequeue (\a PIBlockingDequeue) -* timer (\a PITimer) -* tiling console (with widgets) (\a PIScreen) + * timer (\a PITimer) +* Tiling console (with widgets) (\a PIScreen) * simple text rows * scroll bar * list @@ -37,7 +37,8 @@ * peering net node (\a PIPeer) * connection quality diagnotic (\a PIDiagnostics) * Run-time libraries - * abstract (\a PILibrary) + * external process (\a PIProcess) + * external library (\a PILibrary) * plugin (\a PIPluginLoader) * Mathematics * complex numbers @@ -49,24 +50,33 @@ * CRC checksum (\a PICRC) * Fourier transform (\a PIFFTW, \a PIFFT) * expression evaluator (\a PIEvaluator) -* command-line arguments parser (\a PICLI) -* process (\a PIProcess) +* Application-level + * command-line arguments parser (\a PICLI) + * system resources monitoring (\a PISystemMonitor) + * single-instance application control (\a PISingleApplication) + * high-level log (\a PILog) + * translation support (\a PITranslator) +* State machine ([By stantard](https://www.w3.org/TR/scxml/)) (\a PIStateMachine) +* High-level TCP client-server + * server (\a PIClientServer::Server, \a PIClientServer::ServerClient) + * client (\a PIClientServer::Client) +* Crypt support (\a PICrypt, \a PIAuth) \~russian -* общение с консолью (\a PICout) -* контейнеры (\a PIVector, \a PIDeque, \a PIVector2D, \a PIStack, \a PIQueue, \a PIMap, \a PISet) -* байтовый массив (\a PIByteArray) -* сериализация (\a PIBinaryStream, \a PITextStream, \a PIIOBinaryStream, \a PIIOTextStream, \a PIChunkStream) -* строка (\a PIConstChars, \a PIString, \a PIStringList) -* базовый объект (события и обработчики) (\a PIObject) -* многопоточность +* Общение с консолью (\a PICout) +* Контейнеры (\a PIVector, \a PIDeque, \a PIVector2D, \a PIStack, \a PIQueue, \a PIMap, \a PISet) +* Байтовый массив (\a PIByteArray) +* Сериализация (\a PIBinaryStream, \a PITextStream, \a PIIOBinaryStream, \a PIIOTextStream, \a PIChunkStream, \a PIJSON) +* Строка (\a PIConstChars, \a PIString, \a PIStringList) +* Базовый объект (события и обработчики) (\a PIObject) +* Многопоточность * поток (\a PIThread) - * блокировки (\a PIMutex, \a PISpinlock) - * исполнитель (\a PIThreadPoolExecutor) + * блокировки (\a PIMutex, \a PISpinlock, \a PIConditionVariable, \a PISemaphore, \a PIReadWriteLock) + * исполнитель (\a PIThreadPoolExecutor, \a PIThreadPoolLoop) * блокирующая очередь (\a PIBlockingDequeue) -* таймер (\a PITimer) -* тайлинговая консоль (с виджетами) (\a PIScreen) + * таймер (\a PITimer) +* Тайлинговая консоль (с виджетами) (\a PIScreen) * простой вывод строк * скроллбар * лист @@ -76,7 +86,7 @@ * прогрессбар * вывод PICout * текстовый ввод -* устройства ввода/вывода +* Устройства ввода/вывода * базовый класс (\a PIIODevice) * файл (\a PIFile) * последовательный порт (\a PISerial) @@ -87,8 +97,9 @@ * сложное составное устройство (\a PIConnection) * пиринговая сеть (\a PIPeer) * диагностика качества связи (\a PIDiagnostics) -* поддержка библиотек времени выполнения - * базовая функциональность (\a PILibrary) +* Поддержка библиотек времени выполнения + * внешний процесс (\a PIProcess) + * внешняя библиотека (\a PILibrary) * плагин (\a PIPluginLoader) * Математика * комплексные числа @@ -100,5 +111,14 @@ * CRC контрольная сумма (\a PICRC) * преобразования Фурье (\a PIFFTW, \a PIFFT) * вычислитель выражений (\a PIEvaluator) -* парсер аргументов командной строки (\a PICLI) -* процесс (\a PIProcess) +* Уровень приложения + * парсер аргументов командной строки (\a PICLI) + * мониторинг ресурсов системы (\a PISystemMonitor) + * контроль одного экземпляра приложения (\a PISingleApplication) + * высокоуровневый лог (\a PILog) + * поддержка перевода (\a PITranslator) +* Машина состояний ([По стандарту](https://www.w3.org/TR/scxml/)) (\a PIStateMachine) +* Высокоуровневый TCP клиент-сервер + * сервер (\a PIClientServer::Server, \a PIClientServer::ServerClient) + * клиент (\a PIClientServer::Client) +* Поддержка шифрования (\a PICrypt, \a PIAuth) From cdde340efe45c4aa0cebf94559bd7e7eeb1cfde8 Mon Sep 17 00:00:00 2001 From: peri4 Date: Thu, 14 Nov 2024 18:15:27 +0300 Subject: [PATCH 35/40] add microhttpd server --- CMakeLists.txt | 31 +- cmake/FindPIP.cmake | 6 +- libs/http_server/microhttpd_server_p.cpp | 308 ++++++++++++++++++++ libs/http_server/pihttpserver.cpp | 50 ++++ libs/main/http_server/microhttpd_server_p.h | 75 +++++ libs/main/http_server/pihttpserver.h | 33 +++ libs/main/text/pistring.cpp | 11 + libs/main/text/pistring.h | 4 + libs/main/thread/piprotectedvariable.h | 21 +- libs/main/types/pibytearray.cpp | 25 ++ libs/main/types/pibytearray.h | 6 + main.cpp | 250 +++------------- 12 files changed, 599 insertions(+), 221 deletions(-) create mode 100644 libs/http_server/microhttpd_server_p.cpp create mode 100644 libs/http_server/pihttpserver.cpp create mode 100644 libs/main/http_server/microhttpd_server_p.h create mode 100644 libs/main/http_server/pihttpserver.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e248eaf..b214b66a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,7 +97,7 @@ set(PIP_UTILS_LIST) set(PIP_TESTS_LIST) set(PIP_EXPORTS) -set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua") +set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua;http_server") foreach(_m ${PIP_SRC_MODULES}) set(PIP_MSG_${_m} "no") endforeach() @@ -481,6 +481,33 @@ if (NOT CROSSTOOLS) list(APPEND HDR_DIRS "${_lua_bri_dir}/LuaBridge") list(APPEND HDRS ${_lua_src_hdr}) + # libmicrohttpd + find_library(microhttpd_LIBRARIES microhttpd HINTS "${MINGW_LIB}") + if (microhttpd_LIBRARIES) + set(_microhttpd_add_libs) + if(WIN32) + if("${C_COMPILER}" STREQUAL "cl.exe") + else() + list(APPEND _microhttpd_add_libs ws2_32) + endif() + else() + list(APPEND _microhttpd_add_libs dl) + find_library(tls_lib gnutls) + if (tls_lib) + list(APPEND _microhttpd_add_libs ${tls_lib}) + endif() + if(DEFINED ENV{QNX_HOST}) + list(APPEND _microhttpd_add_libs socket) + else() + if (NOT DEFINED ANDROID_PLATFORM) + list(APPEND _microhttpd_add_libs pthread util) + endif() + endif() + endif() + list(APPEND microhttpd_LIBRARIES "${_microhttpd_add_libs}") + pip_module(http_server "${microhttpd_LIBRARIES}" "PIP HTTP server" "" "" "") + endif() + # Test program if(PIP_UTILS) @@ -488,7 +515,7 @@ if (NOT CROSSTOOLS) #target_link_libraries(pip_plugin pip) add_executable(pip_test "main.cpp") - target_link_libraries(pip_test pip pip_io_utils pip_client_server) + target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server) if(sodium_FOUND) add_executable(pip_cloud_test "main_picloud_test.cpp") target_link_libraries(pip_cloud_test pip_cloud) diff --git a/cmake/FindPIP.cmake b/cmake/FindPIP.cmake index 1730ae9f..223c2655 100644 --- a/cmake/FindPIP.cmake +++ b/cmake/FindPIP.cmake @@ -12,6 +12,7 @@ Create imported targets: * PIP::ClientServer * PIP::Cloud * PIP::Lua + * PIP::HTTPServer These targets include directories and depends on main library @@ -23,10 +24,10 @@ include(SHSTKMacros) shstk_set_find_dirs(PIP) -set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua") +set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua;http_server") if (BUILDING_PIP) - #set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua") + #set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua;pip_http_server") #set(_bins "pip_cmg;pip_rc;deploy_tool") #get_target_property(_path pip BINARY_DIR) #get_target_property(_path pip LIBRARY_OUTPUT_NAME) @@ -96,6 +97,7 @@ set(__module_io_utils IOUtils ) set(__module_client_server ClientServer) set(__module_cloud Cloud ) set(__module_lua Lua ) +set(__module_http_server HTTPServer ) foreach (_l ${__libs}) set( __inc_${_l} "") diff --git a/libs/http_server/microhttpd_server_p.cpp b/libs/http_server/microhttpd_server_p.cpp new file mode 100644 index 00000000..fb89d1a3 --- /dev/null +++ b/libs/http_server/microhttpd_server_p.cpp @@ -0,0 +1,308 @@ +#include "microhttpd_server_p.h" + +#include "pidir.h" +#include "pifile.h" +#include "piliterals_string.h" + +#include + + +struct MicrohttpdServerConnection { + bool ready(); + int send_reply(const MicrohttpdServer::Reply & r); + int send_error(); + + bool done = false; + MicrohttpdServer::Method method = MicrohttpdServer::Method::Unknown; + PIString path; + PIByteArray body; + PIMap headers, args, post; + MHD_Connection * connection = nullptr; + MicrohttpdServer * server = nullptr; + MHD_PostProcessor * postprocessor = nullptr; +}; + + +bool MicrohttpdServerConnection::ready() { + if (!server) return false; + if (done) return true; + done = true; + MicrohttpdServer::Reply rep; + if (method == MicrohttpdServer::Method::Get) { + PIByteArray file = server->getFile(path); + if (!file.isEmpty()) { + rep.addHeader(MHD_HTTP_HEADER_CACHE_CONTROL, "public"); + rep.addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/octet-stream"); + rep.setBody(file); + send_reply(rep); + return true; + } + if (path == "/favicon.ico"_a) { + piCout << "send favicon" << server->favicon.size() << "bytes"; + rep.setBody(server->favicon); + send_reply(rep); + return true; + } + } + piCout << "ready" << (int)method << path; + MicrohttpdServer::Request req; + req.method = method; + req.path = path; + req.body = body; + req.headers = headers; + req.args = args; + // rep.headers = headers; + if (server->callback) rep = server->callback(req); + rep.addFixedHeaders(); + send_reply(rep); + return true; +} + + +int MicrohttpdServerConnection::send_reply(const MicrohttpdServer::Reply & r) { + MHD_Response * response = MHD_create_response_from_buffer(r.body.size(), (void *)r.body.data(), MHD_RESPMEM_MUST_COPY); + if (!response) { + piCout << "null response" << r.body.size() << (void *)r.body.data(); + return MHD_NO; + } + auto it = r.headers.makeIterator(); + while (it.next()) + MHD_add_response_header(response, it.key().dataAscii(), it.value().dataUTF8()); + piCout << "status" << r.code; + int ret = MHD_queue_response(connection, r.code, response); + MHD_destroy_response(response); + return ret; +} + + +int MicrohttpdServerConnection::send_error() { + return MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY)); +} + + +int iterate_post(void * conn_cls, + MHD_ValueKind kind, + const char * key, + const char * filename, + const char * content_type, + const char * transfer_encoding, + const char * data, + uint64_t off, + size_t size) { + MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)conn_cls; + if (!conn) return MHD_NO; + conn->post[PIString::fromUTF8(key)] = PIString::fromUTF8(data); + return MHD_YES; +} + + +void request_completed(void * cls, MHD_Connection * connection, void ** con_cls, MHD_RequestTerminationCode toe) { + MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls)); + // qDebug() << "request_completed" << conn << conn->headers << conn->post << '"' << conn->body << '"'; + if (!conn) return; + if (conn->method == MicrohttpdServer::Method::Post && conn->postprocessor) MHD_destroy_post_processor(conn->postprocessor); + conn->ready(); + piDeleteSafety(conn); +} + +int header_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * value) { + MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)cls; + if (!conn) return MHD_NO; + conn->headers[PIString::fromUTF8(key)] = PIString::fromUTF8(value); + return MHD_YES; +} + +int args_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * value) { + MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)cls; + if (!conn) return MHD_NO; + conn->args[PIString::fromUTF8(key)] = PIString::fromUTF8(value); + return MHD_YES; +} + + +int answer_to_connection(void * cls, + MHD_Connection * connection, + const char * url, + const char * method, + const char * version, + const char * upload_data, + size_t * upload_data_size, + void ** con_cls) { + MicrohttpdServer * server = (MicrohttpdServer *)cls; + + MicrohttpdServer::Method m = MicrohttpdServer::Method::Unknown; + + if (0 == strcmp(method, "GET")) + m = MicrohttpdServer::Method::Get; + else if (0 == strcmp(method, "POST")) + m = MicrohttpdServer::Method::Post; + else if (0 == strcmp(method, "HEAD")) + m = MicrohttpdServer::Method::Head; + else if (0 == strcmp(method, "PUT")) + m = MicrohttpdServer::Method::Put; + else if (0 == strcmp(method, "DELETE")) + m = MicrohttpdServer::Method::Delete; + else if (0 == strcmp(method, "CONNECT")) + m = MicrohttpdServer::Method::Connect; + else if (0 == strcmp(method, "OPTIONS")) + m = MicrohttpdServer::Method::Options; + else if (0 == strcmp(method, "TRACE")) + m = MicrohttpdServer::Method::Trace; + else if (0 == strcmp(method, "PATCH")) + m = MicrohttpdServer::Method::Patch; + + if (m == MicrohttpdServer::Method::Unknown) { + piCout << "[MicrohttpdServer] Unknown method!"; + return MHD_NO; + } + + // piCout << "answer" << url << method << server; + MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls)); + if (!conn) { + conn = new MicrohttpdServerConnection(); + conn->connection = connection; + conn->server = server; + conn->path = PIString::fromUTF8(url); + conn->method = m; + MHD_get_connection_values(connection, MHD_HEADER_KIND, (MHD_KeyValueIterator)header_iterate, *con_cls); + MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, (MHD_KeyValueIterator)args_iterate, *con_cls); + + if (m == MicrohttpdServer::Method::Post) { + // qDebug() << "new POST" << *upload_data_size; + conn->postprocessor = MHD_create_post_processor(connection, 65536, (MHD_PostDataIterator)iterate_post, (void *)conn); + } + return MHD_YES; + } + + switch (m) { + case MicrohttpdServer::Method::Get: + if (!conn->ready()) return conn->send_error(); + return MHD_YES; + case MicrohttpdServer::Method::Post: + // qDebug() << "add POST" << *upload_data_size << PIString::fromUtf8(upload_data, *upload_data_size); + if (*upload_data_size) { + conn->body.append(upload_data, *upload_data_size); + if (conn->postprocessor) MHD_post_process(conn->postprocessor, upload_data, *upload_data_size); + *upload_data_size = 0; + return MHD_YES; + } else { + // qDebug() << "answer ok"; + if (!conn->ready()) return conn->send_error(); + return MHD_YES; + } + break; + default: break; + } + + return conn->send_error(); +} + + +PRIVATE_DEFINITION_START(MicrohttpdServer) + MHD_Daemon * daemon; +PRIVATE_DEFINITION_END(MicrohttpdServer) + + +MicrohttpdServer::MicrohttpdServer() { + PRIVATE->daemon = nullptr; +} + + +MicrohttpdServer::~MicrohttpdServer() { + stop(); +} + + +void MicrohttpdServer::setWWWRoot(const PIString & path) { + www_root = path; + if (www_root.isEmpty()) return; + www_root = PIDir(www_root).absolutePath(); +} + + +void MicrohttpdServer::setFavicon(const PIByteArray & im) { + favicon = im; +} + + +bool MicrohttpdServer::listen(PINetworkAddress addr) { + stop(); + uint flags = 0; +#if MHD_VERSION <= 0x00095100 + flags = MHD_USE_POLL_INTERNALLY; +#else + flags = MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD; +#endif + sockaddr_in sa_addr; + memset(&sa_addr, 0, sizeof(sa_addr)); + sa_addr.sin_port = htons(addr.port()); + sa_addr.sin_addr.s_addr = addr.ip(); + sa_addr.sin_family = AF_INET; + PRIVATE->daemon = MHD_start_daemon(flags, + addr.port(), + nullptr, + nullptr, + (MHD_AccessHandlerCallback)answer_to_connection, + this, + MHD_OPTION_NOTIFY_COMPLETED, + request_completed, + nullptr, + MHD_OPTION_SOCK_ADDR, + &sa_addr, + MHD_OPTION_END); + return isListen(); +} + + +bool MicrohttpdServer::isListen() const { + return PRIVATE->daemon; +} + + +PIByteArray MicrohttpdServer::getFile(PIString name) { + if (www_root.isEmpty()) return PIByteArray(); + if (name == "/") name += "index.html"; + PIString path = PIDir(www_root).relative(name); + // qDebug() << "getFile" << path; + if (path.isEmpty() || !path.startsWith(www_root)) return PIByteArray(); + return PIFile::readAll(PIDir(www_root).absolute(name)); +} + + +void MicrohttpdServer::stop() { + if (PRIVATE->daemon) { + MHD_stop_daemon(PRIVATE->daemon); + PRIVATE->daemon = nullptr; + } +} + + +void MicrohttpdServer::Reply::addHeader(const PIString & header, const PIString & value) { + headers[header] = value; +} + + +void MicrohttpdServer::Reply::removeHeader(const PIString & header) { + headers.remove(header); +} + + +void MicrohttpdServer::Reply::setBody(const PIByteArray & b) { + body = b; +} + + +void MicrohttpdServer::Reply::setCode(int c) { + code = c; +} + + +void MicrohttpdServer::Reply::addFixedHeaders() { + if (!headers.contains(MHD_HTTP_HEADER_CONTENT_TYPE)) { + if (body.startsWith(PIByteArray::fromAscii(""))) + addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html; charset=utf-8"); + else + addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/json; charset=utf-8"); + } + addHeader(MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*"); +} diff --git a/libs/http_server/pihttpserver.cpp b/libs/http_server/pihttpserver.cpp new file mode 100644 index 00000000..f32a653e --- /dev/null +++ b/libs/http_server/pihttpserver.cpp @@ -0,0 +1,50 @@ +#include "pihttpserver.h" + +#include "piliterals_string.h" + + +PIHTTPServer::PIHTTPServer() { + setRequestCallback([this](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply rep; + rep.setCode(404); + auto in_path = r.path.split("/"); + in_path.removeAll(""); + auto it = functions.makeReverseIterator(); + while (it.next()) { + if (it.value().function) { + if (it.value().match(in_path)) { + rep.setCode(200); + rep = it.value().function(r); + break; + } + } + } + auto hit = reply_headers.makeIterator(); + while (hit.next()) + rep.addHeader(hit.key(), hit.value()); + return rep; + }); +} + + +PIHTTPServer::~PIHTTPServer() { + stop(); +} + + +void PIHTTPServer::registerPath(const PIString & path, RequestFunction functor) { + auto & ep(functions[path]); + ep.path = path.split("/"); + ep.function = functor; + ep.path.removeAll(""); +} + + +bool PIHTTPServer::Endpoint::match(const PIStringList & in_path) const { + if (in_path.size() != path.size()) return false; + for (int i = 0; i < path.size_s(); ++i) { + if (path[i] == "*"_a) continue; + if (path[i] != in_path[i]) return false; + } + return true; +} diff --git a/libs/main/http_server/microhttpd_server_p.h b/libs/main/http_server/microhttpd_server_p.h new file mode 100644 index 00000000..c26c31bf --- /dev/null +++ b/libs/main/http_server/microhttpd_server_p.h @@ -0,0 +1,75 @@ +#ifndef MICROHTTPD_SERVER_P_H +#define MICROHTTPD_SERVER_P_H + +#include "pibase.h" +#include "piobject.h" +#include "pip_http_server_export.h" + +struct MicrohttpdServerConnection; + +class PIP_HTTP_SERVER_EXPORT MicrohttpdServer: public PIObject { + PIOBJECT(MicrohttpdServer) + friend struct MicrohttpdServerConnection; + +public: + MicrohttpdServer(); + virtual ~MicrohttpdServer(); + + enum class Method { + Unknown, + Get, + Head, + Post, + Put, + Delete, + Connect, + Options, + Trace, + Patch + }; + + struct PIP_HTTP_SERVER_EXPORT Request { + MicrohttpdServer::Method method; + PIString path; + PIByteArray body; + PIMap headers; + PIMap args; + }; + + class PIP_HTTP_SERVER_EXPORT Reply { + friend struct MicrohttpdServerConnection; + + public: + void addHeader(const PIString & header, const PIString & value); + void removeHeader(const PIString & header); + void setBody(const PIByteArray & b); + void setCode(int c); + + private: + void addFixedHeaders(); + int code = 200; + PIByteArray body; + PIMap headers; + }; + + void setWWWRoot(const PIString & path); + void setFavicon(const PIByteArray & im); + + bool listen(PINetworkAddress addr); + bool listenAll(ushort port) { return listen({0, port}); } + bool isListen() const; + void stop(); + + void setRequestCallback(std::function c) { callback = c; } + +private: + PIByteArray getFile(PIString name); + + PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT) + PIString www_root; + PIByteArray favicon; + std::function callback; +}; + + +#endif diff --git a/libs/main/http_server/pihttpserver.h b/libs/main/http_server/pihttpserver.h new file mode 100644 index 00000000..e8b3d44f --- /dev/null +++ b/libs/main/http_server/pihttpserver.h @@ -0,0 +1,33 @@ +#ifndef PIHTTPSERVER_H +#define PIHTTPSERVER_H + +#include "microhttpd_server_p.h" +#include "pip_http_server_export.h" + +class PIP_HTTP_SERVER_EXPORT PIHTTPServer: public MicrohttpdServer { + PIOBJECT_SUBCLASS(PIHTTPServer, MicrohttpdServer) + +public: + PIHTTPServer(); + virtual ~PIHTTPServer(); + + using RequestFunction = std::function; + + void registerPath(const PIString & path, RequestFunction functor); + + void addReplyHeader(const PIString & name, const PIString & value) { reply_headers[name] = value; } + void removeReplyHeader(const PIString & name) { reply_headers.remove(name); } + void clearReplyHeaders() { reply_headers.clear(); } + +private: + struct Endpoint { + bool match(const PIStringList & in_path) const; + PIStringList path; + RequestFunction function; + }; + PIMap reply_headers; + PIMap functions; +}; + + +#endif diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index 515d36f0..6c8dbb6f 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -553,6 +553,17 @@ uint PIString::hash() const { } +PIByteArray PIString::toAscii() const { + if (isEmpty()) return PIByteArray(); + PIByteArray ret; + ret.resize(size()); + for (int i = 0; i < size_s(); ++i) { + ret[i] = uchar(at(i).ch); + } + return ret; +} + + PIByteArray PIString::toSystem() const { if (isEmpty()) return PIByteArray(); buildData(__syslocname__); diff --git a/libs/main/text/pistring.h b/libs/main/text/pistring.h index 27ffa208..ab573d31 100644 --- a/libs/main/text/pistring.h +++ b/libs/main/text/pistring.h @@ -1107,6 +1107,10 @@ public: //! \~russian Тоже самое, что \a toUTF8(). PIByteArray toByteArray() const { return toUTF8(); } + //! \~english Returns \a PIByteArray contains \a dataAscii() of this string without terminating null-char. + //! \~russian Возвращает \a PIByteArray содержащий \a dataAscii() строки без завершающего нулевого байта. + PIByteArray toAscii() const; + //! \~english Returns \a PIByteArray contains \a data() of this string without terminating null-char. //! \~russian Возвращает \a PIByteArray содержащий \a data() строки без завершающего нулевого байта. PIByteArray toSystem() const; diff --git a/libs/main/thread/piprotectedvariable.h b/libs/main/thread/piprotectedvariable.h index b7490f95..b1646764 100644 --- a/libs/main/thread/piprotectedvariable.h +++ b/libs/main/thread/piprotectedvariable.h @@ -32,16 +32,9 @@ template class PIP_EXPORT PIProtectedVariable { public: - //! \~english Sets value to copy of \"v\" - //! \~russian Устанавливает значение как копию \"v\" - void set(const T & v) { - PIMutexLocker _ml(mutex); - var = v; - } - - //! \~english Sets value by moving \"v\" - //! \~russian Устанавливает значение перемещением \"v\" - void set(T && v) { + //! \~english Sets value to \"v\" + //! \~russian Устанавливает значение как \"v\" + void set(T v) { PIMutexLocker _ml(mutex); var = std::move(v); } @@ -64,10 +57,10 @@ public: //! \~russian Разблокирует мьютекс void unlock() { mutex.unlock(); } - //! \~english Sets value to copy of \"v\" - //! \~russian Устанавливает значение как копию \"v\" - PIProtectedVariable & operator=(const T & v) { - set(v); + //! \~english Sets value to \"v\" + //! \~russian Устанавливает значение как \"v\" + PIProtectedVariable & operator=(T v) { + set(std::move(v)); return *this; } diff --git a/libs/main/types/pibytearray.cpp b/libs/main/types/pibytearray.cpp index 6207c9ea..f3c5359d 100644 --- a/libs/main/types/pibytearray.cpp +++ b/libs/main/types/pibytearray.cpp @@ -129,6 +129,20 @@ struct base64HelpStruct { // clang-format on +bool PIByteArray::startsWith(const PIByteArray & o) const { + if (o.isEmpty()) return false; + if (size() < o.size()) return false; + return piCompareBinary(data(), o.data(), o.size()); +} + + +bool PIByteArray::endsWith(const PIByteArray & o) const { + if (o.isEmpty()) return false; + if (size() < o.size()) return false; + return piCompareBinary(data(size() - o.size()), o.data(), o.size()); +} + + PIByteArray & PIByteArray::convertToBase64() { return *this = toBase64(); } @@ -396,6 +410,17 @@ PIByteArray PIByteArray::fromHex(PIString str) { } +PIByteArray PIByteArray::fromAscii(const char * str) { + PIByteArray ret; + int ind = 0; + while (str[ind] != 0) { + ret.append(str[ind]); + ++ind; + } + return ret; +} + + PICout operator<<(PICout s, const PIByteArray & ba) { s.space(); s.saveAndSetControls(0); diff --git a/libs/main/types/pibytearray.h b/libs/main/types/pibytearray.h index f7f434aa..9cd02076 100644 --- a/libs/main/types/pibytearray.h +++ b/libs/main/types/pibytearray.h @@ -340,6 +340,10 @@ public: //! \~\sa \a every(), \a any(), \a contains(), \a indexWhere() inline int entries(std::function test, ssize_t start = 0) const { return d.entries(test, start); } + bool startsWith(const PIByteArray & o) const; + + bool endsWith(const PIByteArray & o) const; + //! \~english Returns the first index at which a given element `e` //! can be found in the array, or `-1` if it is not present. //! \~russian Возвращает первый индекс, по которому данный элемент `e` @@ -1165,6 +1169,8 @@ public: static PIByteArray fromHex(PIString str); + static PIByteArray fromAscii(const char * str); + //! \~english Return converted from Base 64 data //! \~russian Возвращает массив из Base 64 представления static PIByteArray fromBase64(const PIByteArray & base64); diff --git a/main.cpp b/main.cpp index 338791b2..4952e4d3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,220 +1,64 @@ -#include "pibytearray.h" -#include "piclientserver_client.h" -#include "piclientserver_server.h" -#include "picodeparser.h" -#include "piintrospection_server.h" -#include "piiostream.h" -#include "pijson.h" -#include "pilog.h" -#include "pimathbase.h" +#include "pihttpserver.h" #include "pip.h" -#include "piprotectedvariable.h" -#include "pitranslator_p.h" -#include "pivaluetree_conversions.h" - -#include using namespace PICoutManipulators; -/* -class MyStr: public PIString { -public: - MyStr(): PIString() {} - MyStr(const char * o): PIString(o) { piCout << "MyStr *"; } - MyStr(const MyStr & o): PIString(o) { piCout << "MyStr &"; } - // MyStr(const MyStr & o): PIString(o) { piCout << "MyStr &"; } - MyStr(MyStr && o): PIString(o) { piCout << "MyStr &&"; } - MyStr & operator=(const MyStr & o) { - *this += o; - piCout << "MyStr =&"; - return *this; - } - MyStr & operator=(MyStr && o) { - *this += o; - piCout << "MyStr =&&"; - return *this; - } -}; -*/ +PIKbdListener kbd; +const char * pageTitle = "" + "" + "" + "

Title

" + "" + ""; -class RWL { -public: - void lockWrite() { - PIMutexLocker _ml(mutex); - while (reading > 0 || writing) { - var.wait(mutex); - } - writing = true; - } - void unlockWrite() { - PIMutexLocker _ml(mutex); - writing = false; - var.notifyAll(); - } - void lockRead() { - PIMutexLocker _ml(mutex); - while (writing) { - var.wait(mutex); - } - ++reading; - } - void unlockRead() { - PIMutexLocker _ml(mutex); - --reading; - var.notifyAll(); - } - -private: - PIConditionVariable var; - int reading = 0; - bool writing = false; - PIMutex mutex; -}; - -PIMutex mutex; -PISemaphore sem(10); -PIReadWriteLock rwl; int main(int argc, char * argv[]) { - /*sem.acquire(2); - piCout << sem.tryAcquire(2); - piCout << sem.available(); - return 0;*/ - PIThread t_w0( - [] { - // PIMutexLocker _ml(mutex); - PIWriteLocker rl(rwl); - piCout << "write0 start ..."; - piMSleep(500); - piCout << "write0 end" - << "\n"; - }, - true, - 1_Hz); - PIThread t_w1( - [] { - // PIMutexLocker _ml(mutex); - PIWriteLocker rl(rwl); - piCout << "write1 start ..."; - piMSleep(500); - piCout << "write1 end" - << "\n"; - }, - true, - 1_Hz); + kbd.enableExitCapture(); - int cnt0 = 0, cnt1 = 0; - PIThread t_r0( - [&cnt0] { - // PIMutexLocker _ml(mutex); - PIReadLocker rl(rwl); - piCout << "read0 start ..."; - piMSleep(50); - // bool ok = rwl.tryLockRead(100_ms); - // if (ok) ++cnt0; - piCout << "read0 end"; - // if (ok) rwl.unlockRead(); - }, - true, - 10_Hz); - PIThread t_r1( - [&cnt1] { - // PIMutexLocker _ml(mutex); - // PIReadLocker rl(rwl); - piCout << "read1 start ..."; - piMSleep(50); - bool ok = rwl.tryLockRead(100_ms); - if (ok) ++cnt1; - piCout << "read1 end" << ok; - if (ok) rwl.unlockRead(); - }, - true, - 11_Hz); + PIHTTPServer server; - piSleep(8.); + server.setFavicon(PIFile::readAll("logo.png", false)); + server.listen({"127.0.0.1", 7777}); - t_r0.stopAndWait(); - t_r1.stopAndWait(); - t_w0.stopAndWait(); - t_w1.stopAndWait(); + server.registerPath("/", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply ret; + ret.setBody(PIByteArray::fromAscii(pageTitle)); + return ret; + }); - piCout << cnt0 << cnt1; + server.registerPath("/html", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply ret; + ret.setBody("

arg=%1

"_a.arg(r.args.value("a0")).toUTF8()); + return ret; + }); - /*PICodeParser parser; - parser.parseFile("client_server.h"); - for (auto m: parser.enums) { + server.registerPath("/api", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply ret; + ret.setBody(PIByteArray::fromAscii("API")); + return ret; + }); + + server.registerPath("/api/*", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply ret; + ret.setBody("API etry %1"_a.arg(r.path).toUTF8()); + ret.setCode(405); + return ret; + }); + + /*server.setRequestCallback([](MicrohttpdServer::Request r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply rep; + piCout << "request" << r.path; + piCout << " header" << r.headers; + piCout << " args" << r.args; + piCout << " body" << r.body; piCout << ""; - piCout << m.name; // << m.args << m.value; - // piCout << m.expand({"hello"}); - }*/ + rep.setBody(PIByteArray::fromAscii("[{\"value1\": true, \"value2\": \"ыекштп\"}]")); + return rep; + });*/ + piCout << "start" << server.isListen(); - return 0; - PITranslator::loadLang("ru"); - PISerial f("COM123"); - f.open(); - - /*auto test = [](PIString s, PIString v) { - piCout << " in:" << s; - piCout << "arg:" << minArgPlaceholder(s); - piCout << "out:" << arg(s, v); - piCout << ""; - }; - - test(" %", "asd"); - test("%", "asd"); - test("1", "asd"); - test(" %11", "asd"); - test("%1%2 %0f%0g", "asd"); - test("%01 ", "asd");*/ - - /*piCout << PIString::readableSize(50_KiB); - piCout << PIString::readableSize(1_GB); - PITranslator::loadLang("ru"); - piCout << PIString::readableSize(50_KiB); - piCout << PIString::readableSize(1_GB); - piCout << "test\nstring"_tr; - PITranslator::clear(); - piCout << PIString::readableSize(50_KiB); - piCout << PIString::readableSize(1_GB); - piCout << "test\nstring"_tr; - piCout << "hello!"_tr; - PITranslator::loadConfig("[]\nhello!=привет!\n[Co]\nhello!=привет CO!\n"_u8); - piCout << "hello!"_tr("Co") << "hello!"_tr;*/ - // piCout << "hello!"_trNoOp; - - - /*PISet set; - piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set)); - set << 1 << 2 << 3; - piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set)); - set << 1 << -2 << 50 << -100; - piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set));*/ - return 0; - - std::numeric_limits::epsilon(); - using cmlp = complexf; - PIMathMatrixT<3, 3, double> v0; - PIMathMatrixT<3, 3, cmlp> v1; - v0[0][1] = 1; - v0[1][1] = 2; - v0[2][1] = 3; - v1[0][1] = cmlp(1., 0); - v1[1][1] = cmlp(2., -1); - v1[2][1] = cmlp(3., 2); - piCout << v0 << v1; - piCout << (v0 * 2.) << (v1 * cmlp(2., 2.)); - piCout << (v0 + 2.) << (v1 + cmlp(2.)); - piCout << (v0 - 2.) << (v1 - cmlp(2.)); - piCout << (v0 / 2.) << (v1 / cmlp(2., 1.)); - // piCout << (v0.length()) << (v1.length()); - // piCout << (v0.lengthSqr()) << (v1.lengthSqr()); - // piCout << (v0.manhattanLength()) << (v1.manhattanLength()); - - /*foo(); - foo(); - foo(); - foo(); - return 0;*/ + WAIT_FOR_EXIT + server.stop(); return 0; } From ee137b2820a8ca39a41031a893c904725ca21cfc Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 15 Nov 2024 14:18:49 +0300 Subject: [PATCH 36/40] http server options, remove old --- libs/http_server/microhttpd_server_p.cpp | 99 ++++++++++++--------- libs/http_server/pihttpserver.cpp | 1 - libs/main/core/pibase.h | 4 +- libs/main/http_server/microhttpd_server_p.h | 16 +++- main.cpp | 2 + 5 files changed, 70 insertions(+), 52 deletions(-) diff --git a/libs/http_server/microhttpd_server_p.cpp b/libs/http_server/microhttpd_server_p.cpp index fb89d1a3..d77577bc 100644 --- a/libs/http_server/microhttpd_server_p.cpp +++ b/libs/http_server/microhttpd_server_p.cpp @@ -1,8 +1,7 @@ #include "microhttpd_server_p.h" -#include "pidir.h" -#include "pifile.h" #include "piliterals_string.h" +#include "piliterals_time.h" #include @@ -29,29 +28,21 @@ bool MicrohttpdServerConnection::ready() { done = true; MicrohttpdServer::Reply rep; if (method == MicrohttpdServer::Method::Get) { - PIByteArray file = server->getFile(path); - if (!file.isEmpty()) { - rep.addHeader(MHD_HTTP_HEADER_CACHE_CONTROL, "public"); - rep.addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/octet-stream"); - rep.setBody(file); - send_reply(rep); - return true; - } if (path == "/favicon.ico"_a) { - piCout << "send favicon" << server->favicon.size() << "bytes"; + // piCout << "send favicon" << server->favicon.size() << "bytes"; rep.setBody(server->favicon); send_reply(rep); return true; } } - piCout << "ready" << (int)method << path; + // piCout << "ready" << (int)method << path; MicrohttpdServer::Request req; req.method = method; req.path = path; req.body = body; req.headers = headers; req.args = args; - // rep.headers = headers; + rep.setCode(MHD_HTTP_BAD_REQUEST); if (server->callback) rep = server->callback(req); rep.addFixedHeaders(); send_reply(rep); @@ -62,13 +53,13 @@ bool MicrohttpdServerConnection::ready() { int MicrohttpdServerConnection::send_reply(const MicrohttpdServer::Reply & r) { MHD_Response * response = MHD_create_response_from_buffer(r.body.size(), (void *)r.body.data(), MHD_RESPMEM_MUST_COPY); if (!response) { - piCout << "null response" << r.body.size() << (void *)r.body.data(); + // piCout << "null response" << r.body.size() << (void *)r.body.data(); return MHD_NO; } auto it = r.headers.makeIterator(); while (it.next()) MHD_add_response_header(response, it.key().dataAscii(), it.value().dataUTF8()); - piCout << "status" << r.code; + // piCout << "status" << r.code; int ret = MHD_queue_response(connection, r.code, response); MHD_destroy_response(response); return ret; @@ -80,6 +71,14 @@ int MicrohttpdServerConnection::send_error() { } +void log_callback(void * cls, const char * fm, va_list ap) { + MicrohttpdServer * server = (MicrohttpdServer *)cls; + piCout << "log" << server; + if (!server) return; + piCout << server << fm << ap; +} + + int iterate_post(void * conn_cls, MHD_ValueKind kind, const char * key, @@ -98,13 +97,14 @@ int iterate_post(void * conn_cls, void request_completed(void * cls, MHD_Connection * connection, void ** con_cls, MHD_RequestTerminationCode toe) { MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls)); - // qDebug() << "request_completed" << conn << conn->headers << conn->post << '"' << conn->body << '"'; + // piCout << "request_completed" << conn << conn->headers << conn->post << '"' << conn->body << '"'; if (!conn) return; if (conn->method == MicrohttpdServer::Method::Post && conn->postprocessor) MHD_destroy_post_processor(conn->postprocessor); conn->ready(); piDeleteSafety(conn); } + int header_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * value) { MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)cls; if (!conn) return MHD_NO; @@ -112,6 +112,7 @@ int header_iterate(void * cls, MHD_ValueKind kind, const char * key, const char return MHD_YES; } + int args_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * value) { MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)cls; if (!conn) return MHD_NO; @@ -152,7 +153,9 @@ int answer_to_connection(void * cls, m = MicrohttpdServer::Method::Patch; if (m == MicrohttpdServer::Method::Unknown) { - piCout << "[MicrohttpdServer] Unknown method!"; + piCout << "[MicrohttpdServer]" + << "Warning:" + << "Unknown method!"; return MHD_NO; } @@ -204,7 +207,9 @@ PRIVATE_DEFINITION_END(MicrohttpdServer) MicrohttpdServer::MicrohttpdServer() { - PRIVATE->daemon = nullptr; + PRIVATE->daemon = nullptr; + opts[Option::ConnectionLimit] = FD_SETSIZE - 4; + opts[Option::ConnectionTimeout] = 0_s; } @@ -213,10 +218,8 @@ MicrohttpdServer::~MicrohttpdServer() { } -void MicrohttpdServer::setWWWRoot(const PIString & path) { - www_root = path; - if (www_root.isEmpty()) return; - www_root = PIDir(www_root).absolutePath(); +void MicrohttpdServer::setOption(Option o, PIVariant v) { + opts[o] = std::move(v); } @@ -229,26 +232,42 @@ bool MicrohttpdServer::listen(PINetworkAddress addr) { stop(); uint flags = 0; #if MHD_VERSION <= 0x00095100 - flags = MHD_USE_POLL_INTERNALLY; + flags |= MHD_USE_POLL_INTERNALLY; #else - flags = MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD; + flags |= MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD; #endif + if (opts.value(Option::HTTPSEnabled).toBool()) flags |= MHD_USE_TLS; + mem_key = opts.value(Option::HTTPSMemKey).toByteArray(); + if (mem_key.isNotEmpty()) mem_key.append(0); + mem_cert = opts.value(Option::HTTPSMemCert).toByteArray(); + if (mem_cert.isNotEmpty()) mem_cert.append(0); + key_pass = opts.value(Option::HTTPSKeyPassword).toByteArray(); + if (key_pass.isNotEmpty()) key_pass.append(0); sockaddr_in sa_addr; memset(&sa_addr, 0, sizeof(sa_addr)); sa_addr.sin_port = htons(addr.port()); sa_addr.sin_addr.s_addr = addr.ip(); sa_addr.sin_family = AF_INET; - PRIVATE->daemon = MHD_start_daemon(flags, + PIVector options; + options.append({MHD_OPTION_EXTERNAL_LOGGER, (intptr_t)log_callback, this}); + options.append({MHD_OPTION_NOTIFY_COMPLETED, (intptr_t)request_completed, nullptr}); + options.append({MHD_OPTION_CONNECTION_LIMIT, opts.value(Option::ConnectionLimit).toInt(), nullptr}); + options.append({MHD_OPTION_CONNECTION_TIMEOUT, piRound(opts.value(Option::ConnectionTimeout).toSystemTime().toSeconds()), nullptr}); + options.append({MHD_OPTION_SOCK_ADDR, 0, &sa_addr}); + if (opts.value(Option::HTTPSEnabled).toBool()) { + options.append({MHD_OPTION_HTTPS_MEM_KEY, 0, mem_key.data()}); + options.append({MHD_OPTION_HTTPS_MEM_CERT, 0, mem_cert.data()}); + options.append({MHD_OPTION_HTTPS_KEY_PASSWORD, 0, key_pass.data()}); + } + options.append({MHD_OPTION_END, 0, nullptr}); + PRIVATE->daemon = MHD_start_daemon(flags, addr.port(), nullptr, nullptr, (MHD_AccessHandlerCallback)answer_to_connection, this, - MHD_OPTION_NOTIFY_COMPLETED, - request_completed, - nullptr, - MHD_OPTION_SOCK_ADDR, - &sa_addr, + MHD_OPTION_ARRAY, + options.data(), MHD_OPTION_END); return isListen(); } @@ -259,16 +278,6 @@ bool MicrohttpdServer::isListen() const { } -PIByteArray MicrohttpdServer::getFile(PIString name) { - if (www_root.isEmpty()) return PIByteArray(); - if (name == "/") name += "index.html"; - PIString path = PIDir(www_root).relative(name); - // qDebug() << "getFile" << path; - if (path.isEmpty() || !path.startsWith(www_root)) return PIByteArray(); - return PIFile::readAll(PIDir(www_root).absolute(name)); -} - - void MicrohttpdServer::stop() { if (PRIVATE->daemon) { MHD_stop_daemon(PRIVATE->daemon); @@ -299,10 +308,12 @@ void MicrohttpdServer::Reply::setCode(int c) { void MicrohttpdServer::Reply::addFixedHeaders() { if (!headers.contains(MHD_HTTP_HEADER_CONTENT_TYPE)) { - if (body.startsWith(PIByteArray::fromAscii(""))) - addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html; charset=utf-8"); - else - addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/json; charset=utf-8"); + if (body.isNotEmpty()) { + if (body.startsWith(PIByteArray::fromAscii(""))) + addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html; charset=utf-8"); + else if (body[0] == '[' || body[0] == '{') + addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/json; charset=utf-8"); + } } addHeader(MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*"); } diff --git a/libs/http_server/pihttpserver.cpp b/libs/http_server/pihttpserver.cpp index f32a653e..3419ed42 100644 --- a/libs/http_server/pihttpserver.cpp +++ b/libs/http_server/pihttpserver.cpp @@ -13,7 +13,6 @@ PIHTTPServer::PIHTTPServer() { while (it.next()) { if (it.value().function) { if (it.value().match(in_path)) { - rep.setCode(200); rep = it.value().function(r); break; } diff --git a/libs/main/core/pibase.h b/libs/main/core/pibase.h index d04dbd04..8269137c 100644 --- a/libs/main/core/pibase.h +++ b/libs/main/core/pibase.h @@ -132,9 +132,7 @@ inline void piSwapBinary(const void *& f, const void *& s) { //! \~english Example:\n \snippet piincludes.cpp compareBinary //! \~russian Пример:\n \snippet piincludes.cpp compareBinary inline bool piCompareBinary(const void * f, const void * s, size_t size) { - for (size_t i = 0; i < size; ++i) - if (((const uchar *)f)[i] != ((const uchar *)s)[i]) return false; - return true; + return 0 == memcmp(f, s, size); } //! \~\brief diff --git a/libs/main/http_server/microhttpd_server_p.h b/libs/main/http_server/microhttpd_server_p.h index c26c31bf..5e8c526a 100644 --- a/libs/main/http_server/microhttpd_server_p.h +++ b/libs/main/http_server/microhttpd_server_p.h @@ -27,6 +27,14 @@ public: Trace, Patch }; + enum class Option { + ConnectionLimit, // uint + ConnectionTimeout, // uint, sec + HTTPSEnabled, // bool + HTTPSMemKey, // const char * to key.pem data + HTTPSMemCert, // const char * to cert.pem data + HTTPSKeyPassword // const char * to passwd for key.pem + }; struct PIP_HTTP_SERVER_EXPORT Request { MicrohttpdServer::Method method; @@ -52,7 +60,7 @@ public: PIMap headers; }; - void setWWWRoot(const PIString & path); + void setOption(Option o, PIVariant v); void setFavicon(const PIByteArray & im); bool listen(PINetworkAddress addr); @@ -63,12 +71,12 @@ public: void setRequestCallback(std::function c) { callback = c; } private: - PIByteArray getFile(PIString name); - PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT) - PIString www_root; + PIByteArray favicon; + PIMap opts; std::function callback; + PIByteArray mem_key, mem_cert, key_pass; }; diff --git a/main.cpp b/main.cpp index 4952e4d3..df656680 100644 --- a/main.cpp +++ b/main.cpp @@ -18,7 +18,9 @@ int main(int argc, char * argv[]) { PIHTTPServer server; server.setFavicon(PIFile::readAll("logo.png", false)); + // server.setOption(MicrohttpdServer::Option::HTTPSEnabled, true); server.listen({"127.0.0.1", 7777}); + // server.listen({"192.168.1.10", 7778}); server.registerPath("/", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { MicrohttpdServer::Reply ret; From 3d077955159247549a139a323956772a31c158eb Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 15 Nov 2024 15:43:03 +0300 Subject: [PATCH 37/40] add_custom_command(... pip_lang) on ninja brings to resursive cmake and ninja call ( --- CMakeLists.txt | 16 ++++++++++------ lang/compile.bat | 1 + lang/pip_ru.btf | Bin 0 -> 8049 bytes libs/main/text/pistring.h | 8 ++++++++ utils/translator/CMakeLists.txt | 3 --- 5 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 lang/compile.bat create mode 100644 lang/pip_ru.btf diff --git a/CMakeLists.txt b/CMakeLists.txt index b214b66a..94ffcb73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -484,7 +484,8 @@ if (NOT CROSSTOOLS) # libmicrohttpd find_library(microhttpd_LIBRARIES microhttpd HINTS "${MINGW_LIB}") if (microhttpd_LIBRARIES) - set(_microhttpd_add_libs) + set(microhttpd_FOUND TRUE) + set(_microhttpd_add_libs microhttpd) if(WIN32) if("${C_COMPILER}" STREQUAL "cl.exe") else() @@ -494,7 +495,9 @@ if (NOT CROSSTOOLS) list(APPEND _microhttpd_add_libs dl) find_library(tls_lib gnutls) if (tls_lib) - list(APPEND _microhttpd_add_libs ${tls_lib}) + set(gnutls_FOUND TRUE) + set(gnutls_LIBRARIES "${tls_lib}") + list(APPEND _microhttpd_add_libs gnutls) endif() if(DEFINED ENV{QNX_HOST}) list(APPEND _microhttpd_add_libs socket) @@ -504,8 +507,8 @@ if (NOT CROSSTOOLS) endif() endif() endif() - list(APPEND microhttpd_LIBRARIES "${_microhttpd_add_libs}") - pip_module(http_server "${microhttpd_LIBRARIES}" "PIP HTTP server" "" "" "") + #list(APPEND microhttpd_LIBRARIES "${_microhttpd_add_libs}") + pip_module(http_server "${_microhttpd_add_libs}" "PIP HTTP server" "" "" "") endif() # Test program @@ -563,8 +566,9 @@ endif() # Translations set(PIP_LANG) if (NOT CROSSTOOLS) - pip_translation(PIP_LANG lang/pip_ru.ts) - add_custom_target(pip_lang SOURCES "${PIP_LANG}") + # pip_translation(PIP_LANG lang/pip_ru.ts) + # add_custom_target(pip_lang SOURCES "${PIP_LANG}") + file(GLOB PIP_LANG "lang/*.btf") endif() diff --git a/lang/compile.bat b/lang/compile.bat new file mode 100644 index 00000000..6a09314a --- /dev/null +++ b/lang/compile.bat @@ -0,0 +1 @@ +pip_tr --Compile -o pip_ru.btf pip_ru.ts \ No newline at end of file diff --git a/lang/pip_ru.btf b/lang/pip_ru.btf new file mode 100644 index 0000000000000000000000000000000000000000..230085c6f9a857790ed1ebefd1c2fec73505b209 GIT binary patch literal 8049 zcmd6s3vAWZ9mh|(2PeJ|@WspC+X{*gxVP;sm)rLC3G%44N;kJL3>8E$l$sWF61TND z9dnFjb2u{MG?|K#41*)a_#kmXjYiZsGvgdFQK#Y~z9M3by6@+l{`>#;_O{?-*>3ai zIsM;re&_f2{vPMo)_JWBZF4ph*&<`ih@(g7cb8dbV)ou8hmNwn?;pi$Ju^nEm|``) zv~5a;rSB}dt9hix_wJo=g9bD2oxkslX}oA;=Pve~s^@e&*Jf5(Qq|oG%rlx{Nc_OewQg^Ljn-Gr@r`ZRbe&4Guodrc>~XnKjB^)$V&{) z8`a(%X>zr5-JGbQeYAn|Cncv30N$aoL&i=Ra(~qI2k)CaxMx<`7fQe&>xu`2<){@y9|$SK<*Blwx87XQ2+%wHdGXw43`5N|*{$j1^{$A74g%r~H2FIpj%k|rT({NEMs#7d`K5Qm_Yn_Bqgv{zcCm+fA5yyL&Pt5@uIcG3-DFOyYsc3q~ z-c|pFQ+-TtQdH^^S3y+HKly?Jwv0>9mC_cNyhstPUFF5aqOwYbN>G{*OLOunQgoeL z9W4&f1NnwERr30o(!ea~08a=hwJ7PYHrKedSSLI!_$Fp&nEOWC>y9E#l9E4P`Op|r z?uNzBwU5;JhlggbBUR>J_;>>;G0LeSl4vb3`L5)6lM;59z_uL{Cl_0%0yv;ldT* zSZ=(d0go=d%m^S%thZK31nI_X5E<4u9TdSlL;7*}WZR9U8)H3BdW35j3 zCiKHNUg(4%)5Ywl4G)}Wdu~TWJ}YKl*~EOVp5cVRkMKGYC8Z|m6)aE?g!h+$0g6-? zlqe)A@C$ATaFn_WL<8#-Mn}fR>@kySYHiQXfLU$}%>Umb?2_vG3vAD00i-_Kc0&bh zpK^QoQr=N+HY!Z7a^ARBp5AVjIRJFI&>h@SflOTfx=w3X>qfU$Fie<@5r)JEdw#xlQOeCG8`x1w@fxkFlMf?K~4q|cKWZgR~#dV@gR;Yce;zHY- zAzAQwu=#_rh_QRo?-wH0-JO;H;JZ>i!$F7)=4(gpHc3~<1yvt`oa4CUC*)B_8g79T zzT|6xX#EgU46vUS)y1Y&wNaZ{DfRg*90r3W;_vJ7PX!LFeR6A*9R4nrmNYT@$h&WT z*G@uLd>VVN8_%4iIztsJ?i90|#>bY~9|ddP{Bq&*%(?KF_PJWK(gbGl#*5agJ|N?q zNIg^q5wo}6^TaaS+l8?pe)iZ2Ct=8^|MW<&26OEPyI$ivF6Q~!4%`H|^aWa(N@ET? z?<=h)=KGioMG90@h5@;1`D{Tqhq9hb-Y)9H83p5FQn244AHl5M`ti|59V15t&TA|H ztkCHM;P9KIF75ux?BfuHKe*3mCn`;8mU27;QH<57x9gq%UMh-2u)xR2{wb~D67F}y zTdN?jc!aICtvNjH`bU`?EoxY|gQ0WJ^ zNbfs^Ddpey@WtV&p)hVfV0i1RJwLV^E}^=E@%O&C##N6S-hOKa0xaI!u|tEo^{tk@ zBQ@6FxaSqB!e8(C?P~yi^Tb!)V=qmdD7FSRRsvV`Erdeq$QL50;rz}r7dj4y7u4fX zNB*Lf&>)-pv`Gh!CW^-;mus$;adBN(sZsL`&STM9@5~`-48|Sj;uGMA>uQ5?Wr*EC zX2N7)t_o9J=6s|>(on>->$yzxz#{^x)uMVN8qBEv!>`cf@Y*YMbqQ;Rl=Pbx$ddYr z*=^$<{)z1^2}0EL{aZHyuO%Dnw`(xhZ@Fk6-<9YoEV{oEG%A?jf+&(EFV0x!8kSr; z7WCceQ?9cy6djab$ygwkI3FvQtnng5a4AxkK920$6CTqG{l7_ zU?pvA$|gC6%tIS{xlBd!C*^l5oUBnj+9_3ix_#%*ZST}TRc&9cx2US<<&Q4bU=BX_ z=&gK5wc$lX1%ah}ET?S-J3fN07TI+=j*6m95w%s0Pg5l-4t^FyJFn_c1<}?@(nVXm zHRZg?r!M@#`%|hftkHxx9NN6k(>n2I4Xjao7^DvMQMyjEg(@z%t@y4w&5)SA??bcM z))nbIKBJF&`fBFfCcX63UZ~k;4&N@J{_-)MbX((+`5Akzzr)TA=yZSjvtKc%^%&I? zWkq9Ok%8%2HSbhi=ChN(&!LH!-T&&|yX?|{-i~oE?qxnTj~;ptJQUv20t&qOR;aJk zf?F^g^G!~+^?)OQFTeb6_t@U~K!neeyK4T$L0hWVf6BXgdOS?>yELFYvCB_FL!{?!nr;43lDB3=Sz5W)Y=qU-!gzBu%q4MG~q&i3ty+2i9= zpR-rMH=oL#_v~R#=c8C_Fd!!NO+??o#H{7=Hh&ZJ7$gjaIED9|_LVy`4>Wwtrmr1w z!1mlZN=Kl+lEzEVrV&*->+D&m>ri^*5=w|iHy>IFEAy||+BH(+jzy>4L@BZT%V~c^ z-P`YcXpaUndQ$oTv{*aF^38cV+Rw)I&nEQGCiTyX`)4Knv+@12Q~k`APW<=Bw&zC3 z^Lc6B?sn!h8l#?%rG290^fgW5mYmFsm?xJ$|;l*!Yc!mJvZOZWg@DZBfC)i E2fwK#O8@`> literal 0 HcmV?d00001 diff --git a/libs/main/text/pistring.h b/libs/main/text/pistring.h index ab573d31..efcbe031 100644 --- a/libs/main/text/pistring.h +++ b/libs/main/text/pistring.h @@ -1716,6 +1716,14 @@ public: //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. PIString & arg(uint v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(long v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. + //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. + PIString & arg(ulong v, int base = 10) { return arg(PIString::fromNumber(v, base)); } + //! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string. //! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку. PIString & arg(llong v, int base = 10) { return arg(PIString::fromNumber(v, base)); } diff --git a/utils/translator/CMakeLists.txt b/utils/translator/CMakeLists.txt index 8559e1a8..2b4268db 100644 --- a/utils/translator/CMakeLists.txt +++ b/utils/translator/CMakeLists.txt @@ -18,6 +18,3 @@ if (DEFINED LIB) else() install(TARGETS pip_tr DESTINATION bin) endif() -if (NOT CROSSTOOLS) - add_custom_command(TARGET pip_tr POST_BUILD COMMAND "${CMAKE_COMMAND}" --build "${PIP_DLL_DIR}" --target pip_lang) -endif() From 047d38ea594fc4751ce32df4c9f28ae20bfeaa67 Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 15 Nov 2024 15:47:56 +0300 Subject: [PATCH 38/40] http includes --- libs/http_server/microhttpd_server_p.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/libs/http_server/microhttpd_server_p.cpp b/libs/http_server/microhttpd_server_p.cpp index d77577bc..17039f09 100644 --- a/libs/http_server/microhttpd_server_p.cpp +++ b/libs/http_server/microhttpd_server_p.cpp @@ -4,6 +4,30 @@ #include "piliterals_time.h" #include +// clang-format off +#ifdef QNX +# include +# include +# include +# ifdef BLACKBERRY +# include +# else +# include +# endif +#else +# ifdef WINDOWS +# include +# include +# include +# else +# include +# include +# ifdef LWIP +# include +# endif +# endif +#endif +// clang-format on struct MicrohttpdServerConnection { From c67f7a2b64aa32c7790e965826f82f126988af93 Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 15 Nov 2024 16:23:48 +0300 Subject: [PATCH 39/40] log fix --- libs/http_server/microhttpd_server_p.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libs/http_server/microhttpd_server_p.cpp b/libs/http_server/microhttpd_server_p.cpp index 17039f09..13d5c0ea 100644 --- a/libs/http_server/microhttpd_server_p.cpp +++ b/libs/http_server/microhttpd_server_p.cpp @@ -95,11 +95,14 @@ int MicrohttpdServerConnection::send_error() { } -void log_callback(void * cls, const char * fm, va_list ap) { +void log_callback(void * cls, const char * fmt, va_list ap) { MicrohttpdServer * server = (MicrohttpdServer *)cls; piCout << "log" << server; if (!server) return; - piCout << server << fm << ap; + char buffer[1024]; + memset(buffer, 0, 1024); + std::vsnprintf(buffer, 1024, fmt, ap); + piCout << buffer; } From f6b9131f4a4e915f1ede9221ba4857333daf8199 Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 15 Nov 2024 17:14:19 +0300 Subject: [PATCH 40/40] test excluded for android --- CMakeLists.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94ffcb73..6e618ec8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -483,6 +483,7 @@ if (NOT CROSSTOOLS) # libmicrohttpd find_library(microhttpd_LIBRARIES microhttpd HINTS "${MINGW_LIB}") + set(microhttpd_FOUND FALSE) if (microhttpd_LIBRARIES) set(microhttpd_FOUND TRUE) set(_microhttpd_add_libs microhttpd) @@ -517,11 +518,15 @@ if (NOT CROSSTOOLS) #add_library(pip_plugin SHARED "test_plugin.h" "test_plugin.cpp" "ccm.h" "ccm.cpp") #target_link_libraries(pip_plugin pip) - add_executable(pip_test "main.cpp") - target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server) - if(sodium_FOUND) - add_executable(pip_cloud_test "main_picloud_test.cpp") - target_link_libraries(pip_cloud_test pip_cloud) + if (NOT DEFINED ANDROID_PLATFORM) + if(microhttpd_FOUND) + add_executable(pip_test "main.cpp") + target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server) + if(sodium_FOUND) + add_executable(pip_cloud_test "main_picloud_test.cpp") + target_link_libraries(pip_cloud_test pip_cloud) + endif() + endif() endif() endif()