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);