diff --git a/CMakeLists.txt b/CMakeLists.txt index 987b4f53..69f070b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ if (POLICY CMP0177) endif() project(PIP) set(PIP_MAJOR 5) -set(PIP_MINOR 7) -set(PIP_REVISION 0) +set(PIP_MINOR 8) +set(PIP_REVISION 1) set(PIP_SUFFIX _beta) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) diff --git a/libs/client_server/piclientserver_server.cpp b/libs/client_server/piclientserver_server.cpp index edfd2083..aaea5d21 100644 --- a/libs/client_server/piclientserver_server.cpp +++ b/libs/client_server/piclientserver_server.cpp @@ -37,6 +37,7 @@ PIClientServer::Server::Server() { auto sc = client_factory(); if (!sc) { piCout << "ClientFactory returns nullptr!"_tr("PIClientServer"); + delete c; return; } sc->createForServer(this, c); diff --git a/libs/console/piterminal.cpp b/libs/console/piterminal.cpp index 795d9af3..aeaffd9a 100644 --- a/libs/console/piterminal.cpp +++ b/libs/console/piterminal.cpp @@ -880,7 +880,7 @@ bool PITerminal::initialize() { execvp(argv[0], argv); delete[] argv[0]; delete[] argv; - exit(0); + exit(errno); } else { if (fr < 0 || PRIVATE->fd < 0) { piCoutObj << "forkpty error," << errorString(); diff --git a/libs/http_server/pihttpserver.cpp b/libs/http_server/pihttpserver.cpp index 796f88c4..23d8f463 100644 --- a/libs/http_server/pihttpserver.cpp +++ b/libs/http_server/pihttpserver.cpp @@ -1,14 +1,25 @@ #include "pihttpserver.h" -#include "piliterals_string.h" +#include "piserverendpoint_p.h" + + +struct Endpoint: public PIHTTP::ServerEndpoint { + PIHTTP::Method method = PIHTTP::Method::Unknown; + PIHTTPServer::RequestFunction function; +}; + + +PRIVATE_DEFINITION_START(PIHTTPServer) + PIMap> endpoints; +PRIVATE_DEFINITION_END(PIHTTPServer) PIHTTPServer::PIHTTPServer() { setRequestCallback([this](const PIHTTP::MessageConst & r) -> PIHTTP::MessageMutable { PIHTTP::MessageMutable reply; reply.setCode(PIHTTP::Code::NotFound); - auto in_path = splitPath(r.path()); - auto it = endpoints.makeReverseIterator(); + auto in_path = PIHTTP::ServerEndpoint::splitPath(r.path()); + auto it = PRIVATE->endpoints.makeReverseIterator(); bool found = false; while (it.next()) { for (const auto & ep: it.value()) { @@ -46,7 +57,7 @@ bool PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, Re if (!ep.create(path)) return false; ep.method = method; ep.function = std::move(functor); - endpoints[ep.priority] << ep; + PRIVATE->endpoints[ep.priority] << ep; return true; } @@ -57,143 +68,20 @@ void PIHTTPServer::registerUnhandled(RequestFunction functor) { void PIHTTPServer::unregisterPath(const PIString & path, PIHTTP::Method method) { - auto pl = splitPath(path); - auto it = endpoints.makeIterator(); + auto pl = PIHTTP::ServerEndpoint::splitPath(path); + auto it = PRIVATE->endpoints.makeIterator(); while (it.next()) { it.value().removeWhere([&pl, method](const Endpoint & ep) { return ep.path == pl && ep.method == method; }); } - endpoints.removeWhere([](uint, const PIVector & epl) { return epl.isEmpty(); }); + PRIVATE->endpoints.removeWhere([](uint, const PIVector & epl) { return epl.isEmpty(); }); } void PIHTTPServer::unregisterPath(const PIString & path) { - auto pl = splitPath(path); - auto it = endpoints.makeIterator(); + auto pl = PIHTTP::ServerEndpoint::splitPath(path); + auto it = PRIVATE->endpoints.makeIterator(); while (it.next()) { it.value().removeWhere([&pl](const Endpoint & ep) { return ep.path == pl; }); } - endpoints.removeWhere([](uint, const PIVector & epl) { return epl.isEmpty(); }); -} - - -PIStringList PIHTTPServer::splitPath(const PIString & path) { - auto ret = path.split("/"); - ret.removeAll({}); - return ret; -} - - -PIHTTPServer::PathElement::PathElement(const PIString & reg) { - source = reg; - if (reg == "*"_a) { - type = Type::AnyOne; - } else if (reg == "**"_a) { - type = Type::AnyMany; - } else if (reg.contains('*')) { - type = Type::AnyPart; - parts = reg.split('*'); - } else if (reg.contains('{')) { - type = Type::Arguments; - int ind = 0, eind = 0, pind = 0; - for (;;) { - ind = reg.find('{', ind); - if (ind < 0) break; - eind = reg.find('}', ind + 1); - if (eind < 0) break; - arguments.insert(arguments.size_s(), reg.mid(ind + 1, eind - ind - 1)); - if (ind == 0) - parts << PIString(); - else { - if (ind > pind) - parts << reg.mid(pind, ind - pind); - else if (parts.isNotEmpty()) { - piCout << "[PIHTTPServer] Warning: sequential arguments, ignoring this path!"; - type = Type::Invalid; - return; - } - } - ind = pind = eind + 1; - } - if (eind < reg.size_s() - 1) parts << reg.mid(eind + 1); - } -} - - -bool PIHTTPServer::PathElement::match(const PIString & in, PIMap & ext_args) const { - // piCout << "match" << source << "with" << in; - if (type == Type::AnyOne) return true; - if (type == Type::AnyPart) { - int ind = 0; - for (const auto & m: parts) { - ind = in.find(m, ind); - if (ind < 0) return false; - } - return true; - } - if (type == Type::Arguments) { - int ind = 0, eind = 0; - for (int i = 0; i < parts.size_s(); ++i) { - const auto & m(parts[i]); - if (m.isNotEmpty()) { - ind = in.find(m, eind); - if (ind < 0) return false; - } - if (i > 0) { - ext_args[arguments.value(i - 1)] = in.mid(eind, ind - eind); - } - eind = ind + m.size_s(); - } - if (parts.size() == arguments.size()) { - ext_args[arguments.value(arguments.size_s() - 1)] = in.mid(eind); - } - return true; - } - return source == in; -} - - -uint PIHTTPServer::PathElement::priority() const { - switch (type) { - case Type::Fixed: return 0x10000; break; - case Type::Arguments: return 0x1000; break; - case Type::AnyPart: return 0x100; break; - case Type::AnyOne: return 0x10; break; - case Type::AnyMany: return 0x1; break; - default: break; - } - return 0; -} - - -bool PIHTTPServer::Endpoint::create(const PIString & p) { - path = splitPath(p); - prepared_path.clear(); - priority = 0; - for (const auto & i: path) { - PathElement pe(i); - prepared_path << pe; - path_types |= pe.type; - priority += pe.priority(); - } - return !path_types[PathElement::Type::Invalid]; -} - - -bool PIHTTPServer::Endpoint::match(const PIStringList & in_path, PIMap & ext_args) const { - if (path_types[PathElement::Type::AnyMany]) { - int any_ind = path.indexOf("**"_a); - for (int i = 0; i < any_ind; ++i) { - if (!prepared_path[i].match(in_path[i], ext_args)) return false; - } - int si = prepared_path.size_s() - 1, ii = in_path.size_s() - 1; - for (; si > any_ind && ii >= 0; --si, --ii) { - if (!prepared_path[si].match(in_path[ii], ext_args)) return false; - } - } else { - if (in_path.size() != prepared_path.size()) return false; - for (int i = 0; i < prepared_path.size_s(); ++i) { - if (!prepared_path[i].match(in_path[i], ext_args)) return false; - } - } - return true; + PRIVATE->endpoints.removeWhere([](uint, const PIVector & epl) { return epl.isEmpty(); }); } diff --git a/libs/main/code/picodeparser.cpp b/libs/main/code/picodeparser.cpp index 545059e3..a7a5a071 100644 --- a/libs/main/code/picodeparser.cpp +++ b/libs/main/code/picodeparser.cpp @@ -577,28 +577,36 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) { PICodeParser::Entity * PICodeParser::parseClassDeclaration(const PIString & fc) { - static const PIString s_ss = PIStringAscii(" "); - static const PIString s_M = PIStringAscii("$M"); - static const PIString s_class = PIStringAscii("class"); - PIString cd = fc.trimmed().removeAll('\n').replaceAll('\t', ' ').replaceAll(s_ss, ' '), pn; + static const PIString s_ss = PIStringAscii(" "); + static const PIString s_M = PIStringAscii("$M"); + static const PIString s_class = PIStringAscii("class"); + static const PIString s_public = PIStringAscii("public"); + PIString cd = fc.trimmed().removeAll('\n').replaceAll('\t', ' ').replaceAll(s_ss, ' '), pn; MetaMap meta; int ind = cd.find(s_M); if (ind >= 0) { meta = tmp_meta.value(cd.takeMid(ind, 5)); cd.replaceAll(s_ss, ' '); } + PIString typename_ = cd.left(6).trim(); + bool is_class = typename_ == s_class; + ind = cd.find(':'); // piCout << "found class <****\n" << cd << "\n****>"; - ind = cd.find(':'); PIVector parents; if (ind > 0) { PIStringList pl = cd.takeMid(ind + 1).trim().split(','); cd.cutRight(1); Entity * pe = 0; for (const auto & p: pl) { - if (p.contains(' ')) - pn = p.mid(p.find(' ') + 1); - else + PIString access; + if (p.contains(' ')) { + access = p.left(p.find(' ')).trim(); + pn = p.mid(p.find(' ') + 1); + } else { pn = p; + } + bool is_public = access.isEmpty() ? !is_class : access == s_public; + if (!is_public) continue; pe = findEntityByName(pn); if (pe == 0) ; //{piCout << "Error: can`t find" << pn;} @@ -606,12 +614,10 @@ PICodeParser::Entity * PICodeParser::parseClassDeclaration(const PIString & fc) parents << pe; } } - PIString typename_ = cd.left(6).trim(); - bool is_class = typename_ == s_class; - Visibility vis = cur_def_vis; - cur_def_vis = (is_class ? Private : Public); - PIString cn = cd.mid(6).trim(); - bool is_anonymous = cn.isEmpty(); + Visibility vis = cur_def_vis; + cur_def_vis = (is_class ? Private : Public); + PIString cn = cd.mid(6).trim(); + bool is_anonymous = cn.isEmpty(); if (cn.isEmpty()) cn = PIStringAscii("'; // piCout << "found " << typename_ << cn; Entity * e = new Entity(); diff --git a/libs/main/core/piwaitevent_p.cpp b/libs/main/core/piwaitevent_p.cpp index d04564cf..85873e7b 100644 --- a/libs/main/core/piwaitevent_p.cpp +++ b/libs/main/core/piwaitevent_p.cpp @@ -66,9 +66,9 @@ void PIWaitEvent::destroy() { } # else for (int i = 0; i < 2; ++i) { - if (pipe_fd[i] != 0) { + if (pipe_fd[i] != -1) { ::close(pipe_fd[i]); - pipe_fd[i] = 0; + pipe_fd[i] = -1; } } # endif @@ -90,12 +90,13 @@ bool PIWaitEvent::wait(int fd, CheckRole role) { FD_SET(pipe_fd[ReadEnd], &(fds[CheckRead])); FD_SET(fd, &(fds[CheckExeption])); if (fd_index != CheckExeption) FD_SET(fd, &(fds[fd_index])); - int sr = ::select(nfds, &(fds[CheckRead]), &(fds[CheckWrite]), &(fds[CheckExeption]), nullptr); + int sr = ::select(nfds, &(fds[CheckRead]), &(fds[CheckWrite]), &(fds[CheckExeption]), nullptr); + if (sr < 0) return false; + errorClear(); int buf = 0; - while (::read(pipe_fd[ReadEnd], &buf, sizeof(buf)) > 0) - ; + while (::read(pipe_fd[ReadEnd], &buf, sizeof(buf)) > 0) {} // piCout << "wait result" << sr << FD_ISSET(fd, &(fds[CheckExeption])) << FD_ISSET(fd, &(fds[fd_index])); - if (sr == EBADF || sr == EINTR) return false; + if (errno == EBADF || errno == EINTR) return false; if (FD_ISSET(fd, &(fds[CheckExeption]))) return true; return FD_ISSET(fd, &(fds[fd_index])); # endif @@ -140,7 +141,7 @@ bool PIWaitEvent::isCreate() const { # ifdef WINDOWS return event; # else - return pipe_fd[ReadEnd] != 0; + return pipe_fd[ReadEnd] != -1; # endif } diff --git a/libs/main/core/piwaitevent_p.h b/libs/main/core/piwaitevent_p.h index 1a3dfde8..3bba7caf 100644 --- a/libs/main/core/piwaitevent_p.h +++ b/libs/main/core/piwaitevent_p.h @@ -57,7 +57,7 @@ private: # ifdef WINDOWS void * event = nullptr; # else - int pipe_fd[2] = {0, 0}; + int pipe_fd[2] = {-1, -1}; fd_set fds[3]; enum { ReadEnd = 0, diff --git a/libs/main/http_common/piserverendpoint.cpp b/libs/main/http_common/piserverendpoint.cpp new file mode 100644 index 00000000..cfcd253a --- /dev/null +++ b/libs/main/http_common/piserverendpoint.cpp @@ -0,0 +1,125 @@ +#include "piliterals_string.h" +#include "piserverendpoint_p.h" + + +PIStringList PIHTTP::ServerEndpoint::splitPath(const PIString & path) { + auto ret = path.split("/"); + ret.removeAll({}); + return ret; +} + + +PIHTTP::ServerEndpoint::PathElement::PathElement(const PIString & reg) { + source = reg; + if (reg == "*"_a) { + type = Type::AnyOne; + } else if (reg == "**"_a) { + type = Type::AnyMany; + } else if (reg.contains('*')) { + type = Type::AnyPart; + parts = reg.split('*'); + } else if (reg.contains('{')) { + type = Type::Arguments; + int ind = 0, eind = 0, pind = 0; + for (;;) { + ind = reg.find('{', ind); + if (ind < 0) break; + eind = reg.find('}', ind + 1); + if (eind < 0) break; + arguments.insert(arguments.size_s(), reg.mid(ind + 1, eind - ind - 1)); + if (ind == 0) + parts << PIString(); + else { + if (ind > pind) + parts << reg.mid(pind, ind - pind); + else if (parts.isNotEmpty()) { + piCout << "[PIHTTP::ServerEndpoint] Warning: sequential arguments, ignoring this path!"; + type = Type::Invalid; + return; + } + } + ind = pind = eind + 1; + } + if (eind < reg.size_s() - 1) parts << reg.mid(eind + 1); + } +} + + +bool PIHTTP::ServerEndpoint::PathElement::match(const PIString & in, PIMap & ext_args) const { + // piCout << "match" << source << "with" << in; + if (type == Type::AnyOne) return true; + if (type == Type::AnyPart) { + int ind = 0; + for (const auto & m: parts) { + ind = in.find(m, ind); + if (ind < 0) return false; + } + return true; + } + if (type == Type::Arguments) { + int ind = 0, eind = 0; + for (int i = 0; i < parts.size_s(); ++i) { + const auto & m(parts[i]); + if (m.isNotEmpty()) { + ind = in.find(m, eind); + if (ind < 0) return false; + } + if (i > 0) { + ext_args[arguments.value(i - 1)] = in.mid(eind, ind - eind); + } + eind = ind + m.size_s(); + } + if (parts.size() == arguments.size()) { + ext_args[arguments.value(arguments.size_s() - 1)] = in.mid(eind); + } + return true; + } + return source == in; +} + + +uint PIHTTP::ServerEndpoint::PathElement::priority() const { + switch (type) { + case Type::Fixed: return 0x10000; break; + case Type::Arguments: return 0x1000; break; + case Type::AnyPart: return 0x100; break; + case Type::AnyOne: return 0x10; break; + case Type::AnyMany: return 0x1; break; + default: break; + } + return 0; +} + + +bool PIHTTP::ServerEndpoint::create(const PIString & p) { + path = splitPath(p); + prepared_path.clear(); + priority = 0; + for (const auto & i: path) { + PathElement pe(i); + prepared_path << pe; + path_types |= pe.type; + priority += pe.priority(); + } + return !path_types[PathElement::Type::Invalid]; +} + + +bool PIHTTP::ServerEndpoint::match(const PIStringList & in_path, PIMap & ext_args) const { + if (path_types[PathElement::Type::AnyMany]) { + int any_ind = path.indexOf("**"_a); + for (int i = 0; i < any_ind; ++i) { + if (!prepared_path[i].match(in_path[i], ext_args)) return false; + } + int si = prepared_path.size_s() - 1, ii = in_path.size_s() - 1; + for (; si > any_ind && ii >= 0; --si, --ii) { + if (!prepared_path[si].match(in_path[ii], ext_args)) return false; + } + } else { + if (in_path.size() != prepared_path.size()) return false; + for (int i = 0; i < prepared_path.size_s(); ++i) { + if (!prepared_path[i].match(in_path[i], ext_args)) return false; + } + } + return true; +} diff --git a/libs/main/http_common/piserverendpoint_p.h b/libs/main/http_common/piserverendpoint_p.h new file mode 100644 index 00000000..22e4a181 --- /dev/null +++ b/libs/main/http_common/piserverendpoint_p.h @@ -0,0 +1,72 @@ +//! \~\file piserverendpoint_p.h +//! \~\ingroup HTTP +//! \~\brief +//! \~english Shared HTTP message container types +//! \~russian Общие типы контейнеров HTTP-сообщений +/* + PIP - Platform Independent Primitives + Shared HTTP message container types + 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 piserverendpoint_p_h +#define piserverendpoint_p_h + +#include "pip_export.h" +#include "pistringlist.h" + + +namespace PIHTTP { + + +struct PIP_EXPORT ServerEndpoint { + struct PIP_EXPORT PathElement { + enum class Type { + Invalid = 0x01, + Fixed = 0x02, + Arguments = 0x04, + AnyOne = 0x08, + AnyPart = 0x10, + AnyMany = 0x20 + }; + + Type type = Type::Fixed; + PIString source; + PIStringList parts; + PIMap arguments; + + PathElement(const PIString & reg = {}); + + bool match(const PIString & in, PIMap & ext_args) const; + uint priority() const; + }; + + PIStringList path; + PIFlags path_types; + PIVector prepared_path; + uint priority = 0; + + bool create(const PIString & p); + bool match(const PIStringList & in_path, PIMap & ext_args) const; + + static PIStringList splitPath(const PIString & path); +}; + + +}; // namespace PIHTTP + + +#endif diff --git a/libs/main/http_server/pihttpserver.h b/libs/main/http_server/pihttpserver.h index 68fa2fe4..ca3abea1 100644 --- a/libs/main/http_server/pihttpserver.h +++ b/libs/main/http_server/pihttpserver.h @@ -102,43 +102,9 @@ public: void clearReplyHeaders() { reply_headers.clear(); } private: - struct PathElement { - enum class Type { - Invalid = 0x01, - Fixed = 0x02, - Arguments = 0x04, - AnyOne = 0x08, - AnyPart = 0x10, - AnyMany = 0x20 - }; - - Type type = Type::Fixed; - PIString source; - PIStringList parts; - PIMap arguments; - - PathElement(const PIString & reg = {}); - - bool match(const PIString & in, PIMap & ext_args) const; - uint priority() const; - }; - - struct Endpoint { - PIStringList path; - PIHTTP::Method method = PIHTTP::Method::Unknown; - RequestFunction function; - PIFlags path_types; - PIVector prepared_path; - uint priority = 0; - - bool create(const PIString & p); - bool match(const PIStringList & in_path, PIMap & ext_args) const; - }; - - static PIStringList splitPath(const PIString & path); + PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT) PIMap reply_headers; - PIMap> endpoints; RequestFunction unhandled; }; diff --git a/libs/main/io_devices/pican.cpp b/libs/main/io_devices/pican.cpp index 2f06894c..8848abf4 100644 --- a/libs/main/io_devices/pican.cpp +++ b/libs/main/io_devices/pican.cpp @@ -24,6 +24,7 @@ # define PIP_CAN #endif #ifdef PIP_CAN +# include # include # include # include @@ -50,7 +51,7 @@ PICAN::PICAN(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(pat setPath(path); #ifdef PIP_CAN can_id = 0; - sock = 0; + sock = -1; PRIVATE->event.create(); #endif } @@ -71,19 +72,25 @@ bool PICAN::openDevice() { sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (sock < 0) { piCoutObj << "Error! while opening socket"; + sock = -1; return false; } + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); ifreq ifr; strcpy(ifr.ifr_name, path().dataAscii()); piCout << "PICAN try to get interface index..."; if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) { piCoutObj << "Error! while determin the interface ioctl"; + ::close(sock); + sock = -1; return false; } struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv); + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) < 0) { + piCoutObj << "Error! while setting socket receive timeout"; + } // bind socket to all CAN interface sockaddr_can addr; addr.can_family = AF_CAN; @@ -91,6 +98,8 @@ bool PICAN::openDevice() { piCout << "PICAN try to bind socket to interface" << ifr.ifr_ifindex; if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { piCoutObj << "Error! while binding socket"; + ::close(sock); + sock = -1; return false; } piCout << "PICAN Open OK!"; @@ -105,7 +114,11 @@ bool PICAN::openDevice() { bool PICAN::closeDevice() { #ifdef PIP_CAN interrupt(); - if (sock > 0) ::close(sock); + if (sock != -1) { + ::shutdown(sock, SHUT_RDWR); + ::close(sock); + sock = -1; + } #endif return true; } @@ -113,6 +126,7 @@ bool PICAN::closeDevice() { ssize_t PICAN::readDevice(void * read_to, ssize_t max_size) { #ifdef PIP_CAN + if (sock == -1) return -1; // piCout << "PICAN read"; can_frame frame; ssize_t ret = 0; @@ -131,6 +145,7 @@ ssize_t PICAN::readDevice(void * read_to, ssize_t max_size) { ssize_t PICAN::writeDevice(const void * data, ssize_t max_size) { #ifdef PIP_CAN + if (sock == -1) return -1; // piCout << "PICAN write" << can_id << max_size; if (max_size > 8) { piCoutObj << "Can't send CAN frame bigger than 8 bytes (requested " << max_size << ")!"; diff --git a/libs/main/io_devices/pican.h b/libs/main/io_devices/pican.h index 93bef921..37938818 100644 --- a/libs/main/io_devices/pican.h +++ b/libs/main/io_devices/pican.h @@ -73,7 +73,7 @@ protected: private: PRIVATE_DECLARATION(PIP_EXPORT) - int sock; + int sock = -1; int can_id, readed_id; }; diff --git a/libs/main/io_devices/piethernet.cpp b/libs/main/io_devices/piethernet.cpp index b4c9730a..32c3a3e8 100644 --- a/libs/main/io_devices/piethernet.cpp +++ b/libs/main/io_devices/piethernet.cpp @@ -103,7 +103,11 @@ # ifndef WINDOWS PIString getSockAddr(sockaddr * s) { - return s == 0 ? PIString() : PIStringAscii(inet_ntoa(((sockaddr_in *)s)->sin_addr)); + if (!s) return PIString(); + char buf[INET_ADDRSTRLEN]; + piZeroMemory(buf, sizeof(buf)); + const char * r = inet_ntop(AF_INET, &((sockaddr_in *)s)->sin_addr, buf, sizeof(buf)); + return r ? PIStringAscii(r) : PIString(); } # endif @@ -862,6 +866,7 @@ ssize_t PIEthernet::writeDevice(const void * data, ssize_t max_size) { return -1; } } + ret += sr; remain_data += sr; remain_size -= sr; } @@ -1176,13 +1181,19 @@ PIEthernet::InterfaceList PIEthernet::interfaces() { # else # ifdef ANDROID struct ifconf ifc; - int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (s == -1) { + piCout << "[PIEthernet]" + << "Can`t create socket: %1"_tr("PIEthernet").arg(errorString()); + return il; + } ifc.ifc_len = 256; ifc.ifc_buf = new char[ifc.ifc_len]; if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { piCout << "[PIEthernet]" << "Can`t get interfaces: %1"_tr("PIEthernet").arg(errorString()); delete[] ifc.ifc_buf; + ::close(s); return il; } int icnt = ifc.ifc_len / sizeof(ifreq); @@ -1201,7 +1212,8 @@ PIEthernet::InterfaceList PIEthernet::interfaces() { if (ci.address == "127.0.0.1") ci.flags |= PIEthernet::ifLoopback; il << ci; } - delete ifc.ifc_buf; + delete[] ifc.ifc_buf; + ::close(s); # else struct ifaddrs *ret, *cif = 0; int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); @@ -1223,7 +1235,7 @@ PIEthernet::InterfaceList PIEthernet::interfaces() { # ifdef QNX # ifndef BLACKBERRY int fd = ::open((PIString("/dev/io-net/") + ci.name).dataAscii(), O_RDONLY); - if (fd != 0) { + if (fd >= 0) { nic_config_t nic; devctl(fd, DCMD_IO_NET_GET_CONFIG, &nic, sizeof(nic), 0); ::close(fd); @@ -1292,8 +1304,10 @@ PINetworkAddress PIEthernet::interfaceAddress(const PIString & interface_) { piZeroMemory(ifr); strcpy(ifr.ifr_name, interface_.dataAscii()); int s = ::socket(AF_INET, SOCK_DGRAM, 0); - ioctl(s, SIOCGIFADDR, &ifr); - ::close(s); + if (s != -1) { + ioctl(s, SIOCGIFADDR, &ifr); + ::close(s); + } struct sockaddr_in * sa = (struct sockaddr_in *)&ifr.ifr_addr; return PINetworkAddress(uint(sa->sin_addr.s_addr)); # endif diff --git a/libs/main/io_devices/piiobytearray.cpp b/libs/main/io_devices/piiobytearray.cpp index 3b6e8374..699bd1e9 100644 --- a/libs/main/io_devices/piiobytearray.cpp +++ b/libs/main/io_devices/piiobytearray.cpp @@ -62,8 +62,7 @@ ssize_t PIIOByteArray::readDevice(void * read_to, ssize_t size) { if (ret <= 0) return -1; memcpy(read_to, data_->data(pos), ret); // piCout << "readed" << ret; - pos += size; - if (pos > data_->size_s()) pos = data_->size_s(); + pos += ret; return ret; } diff --git a/libs/main/io_devices/pipeer.cpp b/libs/main/io_devices/pipeer.cpp index c121dfea..0cf8bd6a 100644 --- a/libs/main/io_devices/pipeer.cpp +++ b/libs/main/io_devices/pipeer.cpp @@ -623,7 +623,10 @@ bool PIPeer::dataRead(const uchar * readed, ssize_t size) { return true; } cnt++; - if (cnt > _PIPEER_MSG_TTL || from == dp->name) return true; + if (cnt > _PIPEER_MSG_TTL || from == dp->name) { + eth_mutex.unlock(); + return true; + } sba << type << from << to << cnt << pba; // piCout << "translate packet" << from << "->" << to << ", ttl =" << cnt; sendToNeighbour(dp, sba); diff --git a/libs/main/io_devices/piserial.cpp b/libs/main/io_devices/piserial.cpp index 83bcc9ac..fb50d6e0 100644 --- a/libs/main/io_devices/piserial.cpp +++ b/libs/main/io_devices/piserial.cpp @@ -512,7 +512,10 @@ bool PISerial::read(void * data, int size, double timeout_ms) { all = readDevice(data, 1); while (all < size) { ret = readDevice(&((uchar *)data)[all], size - all); - if (ret > 0) all += ret; + if (ret > 0) + all += ret; + else + break; } setOption(BlockingRead, br); received(data, all); diff --git a/libs/main/io_utils/pifiletransfer.cpp b/libs/main/io_utils/pifiletransfer.cpp index 7389a769..acdeac73 100644 --- a/libs/main/io_utils/pifiletransfer.cpp +++ b/libs/main/io_utils/pifiletransfer.cpp @@ -131,6 +131,12 @@ bool PIFileTransfer::sendFiles(const PIVector & files) { void PIFileTransfer::processFile(int id, ullong start, PIByteArray & data) { // piCout << "processFile" << id << files_.size(); + if (id <= 0 || id > files_.size_s()) { + cur_file_string = "Error: Invalid file id " + PIString::fromNumber(id); + piCoutObj << cur_file_string; + stopReceive(); + return; + } PFTFileInfo fi = files_[id - 1]; bytes_file_all = fi.size; bytes_file_cur = start; diff --git a/libs/main/math/pimathbase.h b/libs/main/math/pimathbase.h index 79252451..9aeb126e 100644 --- a/libs/main/math/pimathbase.h +++ b/libs/main/math/pimathbase.h @@ -308,10 +308,12 @@ inline PIVector piAbs(const PIVector & v) { //! \~russian Нормализует угол к диапазону `[0; 360]` градусов на месте. template void normalizeAngleDeg360(T & a) { - while (a < 0.) - a += 360.; - while (a > 360.) - a -= 360.; + if (std::isnan(a) || std::isinf(a)) { + a = 0.; + return; + } + a -= std::floor(a / 360.) * 360.; + if (a < 0) a += 360; } //! \~english Returns an angle normalized to the `[0; 360]` degree range. @@ -327,10 +329,13 @@ double normalizedAngleDeg360(T a) { //! \~russian Нормализует угол к диапазону `[-180; 180]` градусов на месте. template void normalizeAngleDeg180(T & a) { - while (a < -180.) - a += 360.; - while (a > 180.) - a -= 360.; + if (std::isnan(a) || std::isinf(a)) { + a = 0.; + return; + } + a -= std::floor(a / 360.) * 360.; + if (a < -180) a += 360; + if (a >= 180) a -= 360; } //! \~english Returns an angle normalized to the `[-180; 180]` degree range. diff --git a/libs/main/math/pipoint.h b/libs/main/math/pipoint.h index 20e88e8e..820c1d38 100644 --- a/libs/main/math/pipoint.h +++ b/libs/main/math/pipoint.h @@ -167,6 +167,22 @@ public: return *this; } + //! \~english Multiplies by-coordinates by `v`. + //! \~russian Умножает по-координатно на `v`. + PIPoint & operator*=(const PIPoint & v) { + x *= v.x; + y *= v.y; + return *this; + } + + //! \~english Divides by-coordinates by `v`. + //! \~russian Делит по-координатно на `v`. + PIPoint & operator/=(const PIPoint & v) { + x /= v.x; + y /= v.y; + return *this; + } + //! \~english Returns sum of two points. //! \~russian Возвращает сумму двух точек. PIPoint operator+(const PIPoint & p) const { return PIPoint(x + p.x, y + p.y); } @@ -195,6 +211,14 @@ public: //! \~russian Возвращает точку, деленную на `v`. PIPoint operator/(Type v) const { return PIPoint(x / v, y / v); } + //! \~english Returns point multiplied by `v`. + //! \~russian Возвращает точку, умноженную на `v`. + PIPoint operator*(const PIPoint & v) const { return PIPoint(x * v.x, y * v.y); } + + //! \~english Returns point divided by `v`. + //! \~russian Возвращает точку, деленную на `v`. + PIPoint operator/(const PIPoint & v) const { return PIPoint(x / v.x, y / v.y); } + //! \~english Checks whether point coordinates are equal. //! \~russian Проверяет равенство координат точек. bool operator==(const PIPoint & p) const { return (x == p.x && y == p.y); } diff --git a/libs/main/mqtt_client/pimqttclient.h b/libs/main/mqtt_client/pimqttclient.h index 1e12c47d..6d32bf27 100644 --- a/libs/main/mqtt_client/pimqttclient.h +++ b/libs/main/mqtt_client/pimqttclient.h @@ -41,24 +41,39 @@ public: Client(); virtual ~Client(); + //! \~english Request handler used by registered routes and fallback processing. + //! \~russian Обработчик запроса, используемый зарегистрированными маршрутами и fallback-обработкой. + using MessageFunction = std::function; + void setConnectTimeout(PISystemTime time) { connect_timeout = time; } void connect(const PIString & address, const PIString & client, const PIString & username = {}, const PIString & password = {}); void disconnect(); - void subscribe(const PIString & topic, QoS qos = QoS::Level1); + void subscribe(const PIString & topic, MessageFunction functor, QoS qos = QoS::Level1); + + template + void + subscribe(const PIString & topic, T * o, PIMQTT::MessageMutable (T::*function)(const PIMQTT::MessageConst &), QoS qos = QoS::Level1) { + subscribe(topic, [o, function](const PIMQTT::MessageConst & m) { return (o->*function)(m); }, qos); + } + void unsubscribe(const PIString & topic); + void unsubscribeAll(); void publish(const PIString & topic, const PIByteArray & msg, QoS qos = QoS::Level0); void publish(const MessageConst & msg); - void unsubscribeAll() { unsubscribe("#"); } bool isConnecting() const { return m_status == Connecting; } bool isConnected() const { return m_status == Connected; } + PIStringList usedTopics() const; + EVENT0(connected); EVENT1(disconnected, PIMQTT::Error, code); - EVENT1(received, PIMQTT::MessageConst, message); + EVENT1(receivedUnhandled, PIMQTT::MessageConst, message); + + struct Endpoint; private: NO_COPY_CLASS(Client) @@ -79,23 +94,34 @@ private: }; struct Subscribe { PIString topic; + MessageFunction functor; QoS qos; }; void mqtt_connectionLost(); void mqtt_deliveryComplete(int token); - void mqtt_messageArrived(const MessageConst & msg); + void mqtt_messageArrived(MessageMutable & msg); + + PIString registerSubscribe(const Subscribe & sub); + PIString unregisterSubscribe(const PIString & mqtt_topic); + void unregisterAll(); void connectInternal(const ConnectInfo & ci); void disconnectInternal(); void publishInternal(const MessageConst & m); void subscribeInternal(const Subscribe & sub); - void unsubscribeInternal(const PIString & topic); + void unsubscribeInternal(const PIString & mqtt_topic); void destroy(); void changeStatus(Status s); void run(); + // from HTTP format + static PIString convertTopic2MQTT(const PIString & topic); + + // from MQTT format + static PIString convertTopic2HTTP(const PIString & topic); + std::atomic_int m_status = {Idle}; std::atomic_bool is_destoying = {false}; PISystemTime connect_timeout = 10_s; diff --git a/libs/main/serialization/pibinarystream.h b/libs/main/serialization/pibinarystream.h index 80dcbe25..0d4d1ab9 100644 --- a/libs/main/serialization/pibinarystream.h +++ b/libs/main/serialization/pibinarystream.h @@ -56,17 +56,17 @@ #else -# define BINARY_STREAM_FRIEND(T) \ - template \ - friend PIBinaryStream

& operator<<(PIBinaryStream

& s, const T & v); \ - template \ - friend PIBinaryStream

& operator>>(PIBinaryStream

& s, T & v); +# define BINARY_STREAM_FRIEND(T) \ + template \ + friend PIBinaryStream

& operator<<(PIBinaryStream

& s, const T & v); \ + template \ + friend PIBinaryStream

& operator>>(PIBinaryStream

& s, T & v); # define BINARY_STREAM_WRITE(T) \ - template \ - inline PIBinaryStream

& operator<<(PIBinaryStream

& s, const T & v) + template \ + inline PIBinaryStream

& operator<<(PIBinaryStream

& s, const T & v) # define BINARY_STREAM_READ(T) \ - template \ - inline PIBinaryStream

& operator>>(PIBinaryStream

& s, T & v) + template \ + inline PIBinaryStream

& operator>>(PIBinaryStream

& s, T & v) #endif @@ -410,7 +410,7 @@ template & operator>>(PIBinaryStream

& s, PIVector & v) { // piCout << ">> vector trivial default"; int sz = s.binaryStreamTakeInt(); - if (s.wasReadError()) { + if (s.wasReadError() || sz < 0) { fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; @@ -433,7 +433,7 @@ template & operator>>(PIBinaryStream

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

& s, PIDeque & v) { // piCout << ">> deque trivial default"; int sz = s.binaryStreamTakeInt(); - if (s.wasReadError()) { + if (s.wasReadError() || sz < 0) { fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; @@ -485,7 +485,7 @@ template & operator>>(PIBinaryStream

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

& operator>>(PIBinaryStream

& s, PIVector2D & v) int r, c; r = s.binaryStreamTakeInt(); c = s.binaryStreamTakeInt(); - if (s.wasReadError()) { + if (s.wasReadError() || r < 0 || c < 0) { fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; @@ -542,6 +542,11 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

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

& s, PIVector & v) { int sz = s.binaryStreamTakeInt(); - if (s.wasReadError()) { + if (s.wasReadError() || sz < 0) { fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; @@ -641,7 +646,7 @@ template & operator>>(PIBinaryStream

& s, PIDeque & v) { int sz = s.binaryStreamTakeInt(); - if (s.wasReadError()) { + if (s.wasReadError() || sz < 0) { fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); v.clear(); return s; @@ -667,6 +672,11 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector2D & v) PIVector tmp; r = s.binaryStreamTakeInt(); c = s.binaryStreamTakeInt(); + if (s.wasReadError() || r < 0 || c < 0) { + fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } s >> tmp; if (s.wasReadError()) { fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); @@ -700,7 +710,7 @@ template //! \~russian Восстанавливает ключи и значения %PIMap. inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIMap & v) { int sz = s.binaryStreamTakeInt(); - if (s.wasReadError()) { + if (s.wasReadError() || sz < 0) { fprintf(stderr, "error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); v.clear(); return s; @@ -749,7 +759,7 @@ template //! \~russian Восстанавливает ключи %PISet. inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PISet & v) { int sz = s.binaryStreamTakeInt(); - if (s.wasReadError()) { + if (s.wasReadError() || sz < 0) { fprintf(stderr, "error with PISet<%s>\n", __PIP_TYPENAME__(Key)); v.clear(); return s; diff --git a/libs/main/serialization/pijson.cpp b/libs/main/serialization/pijson.cpp index 8ef17798..b58085ee 100644 --- a/libs/main/serialization/pijson.cpp +++ b/libs/main/serialization/pijson.cpp @@ -547,7 +547,7 @@ void PIJSON::print(PIString & s, const PIJSON & v, PIString tab, bool spaces, bo if (spaces) s += ' '; } switch (v.c_type) { - case PIJSON::Invalid: break; + case PIJSON::Invalid: case PIJSON::Null: s += "null"; break; case PIJSON::Boolean: s += PIString::fromBool(v.c_value.toBool()); break; case PIJSON::Number: s += v.c_value.toString(); break; diff --git a/libs/main/serialization/pijsonserialization.h b/libs/main/serialization/pijsonserialization.h index dd8bcc22..9401feaf 100644 --- a/libs/main/serialization/pijsonserialization.h +++ b/libs/main/serialization/pijsonserialization.h @@ -291,6 +291,13 @@ inline PIJSON piSerializeJSON(const PIMathVectorT & v) { return ret; } +template<> +//! \~english Serializes %PIStringList as JSON array. +//! \~russian Сериализует %PIStringList как JSON-массив. +inline PIJSON piSerializeJSON(const PIStringList & v) { + return piSerializeJSON((const PIDeque &)v); +} + // --- // deserialize, piDeserializeJSON(T, PIJSON) @@ -538,6 +545,13 @@ inline void piDeserializeJSON(PIMathVectorT & v, const PIJSON & js) { piDeserializeJSON(v[i], js[i]); } +template<> +//! \~english Deserializes %PIStringList from JSON array. +//! \~russian Десериализует %PIStringList из JSON-массива. +inline void piDeserializeJSON(PIStringList & v, const PIJSON & js) { + piDeserializeJSON((PIDeque &)v, js); +} + // --- // PIJSON static wrapper diff --git a/libs/main/system/piprocess.cpp b/libs/main/system/piprocess.cpp index aa4c4713..115b9a69 100644 --- a/libs/main/system/piprocess.cpp +++ b/libs/main/system/piprocess.cpp @@ -215,10 +215,10 @@ PRIVATE_DEFINITION_START(PIProcess) PeekNamedPipe(pipes[pipe_type][PipeRead], nullptr, 0, nullptr, &available, nullptr); if (available > 0) { BOOL ok = ReadFile(pipes[pipe_type][PipeRead], - read_buffer.data(offset), - piMini(available, read_buffer.size() - offset), - &bytes_read, - nullptr); + read_buffer.data(offset), + piMini(available, read_buffer.size() - offset), + &bytes_read, + nullptr); if (!ok) bytes_read = 0; } # else @@ -293,10 +293,10 @@ void PIProcess::startProc(bool detached) { si.dwFlags |= STARTF_USESTDHANDLES; const auto cmd = convertWindowsCmd(args); if (CreateProcessA(0, // No module name (use command line) - (LPSTR)cmd.data(), // Command line + (LPSTR)cmd.data(), // Command line 0, // Process handle not inheritable 0, // Thread handle not inheritable - true, // Set handle inheritance to FALSE + true, // Set handle inheritance to FALSE detached ? DETACHED_PROCESS /*CREATE_NEW_CONSOLE*/ : 0, // Creation flags 0, // Use environment wd.isEmpty() ? 0 : wd.data(), // Use working directory @@ -319,10 +319,17 @@ void PIProcess::startProc(bool detached) { auto largs = convertToCharArrays(args); auto lenv = convertToCharArrays(env); int pid_ = fork(); + if (pid_ < 0) { + piCoutObj << "\"fork\" error: " << errorString(); + PRIVATE->closeAllPipes(); + delete[] largs; + delete[] lenv; + return; + } if (!detached) PRIVATE->pid = pid_; if (pid_ == 0) { if (!wd.isEmpty()) { - if (!chdir(wd.data())) piCoutObj << "Error while set working directory"; + if (chdir(wd.data()) != 0) piCoutObj << "Error while set working directory"; } PRIVATE->closePipe(StdIn, PipeWrite); PRIVATE->closePipe(StdOut, PipeRead); diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index db3b9da8..65bf8035 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -671,7 +671,7 @@ PIString & PIString::operator+=(const PIConstChars & str) { if (!str.isEmpty()) { size_t os = d.size(); d.enlarge(str.size()); - for (size_t l = 0; l < d.size(); ++l) { + for (size_t l = 0; l < str.size(); ++l) { d[os + l] = str[l]; } } @@ -1763,7 +1763,7 @@ PIString PIString::toLowerCase() const { char PIString::toChar() const { - char v; + char v = 0; sscanf(dataAscii(), "%c", &v); return v; } diff --git a/libs/main/types/pibytearray.h b/libs/main/types/pibytearray.h index 5d0ada7f..cf90ff53 100644 --- a/libs/main/types/pibytearray.h +++ b/libs/main/types/pibytearray.h @@ -1107,9 +1107,11 @@ public: PIByteArray & append(const PIByteArray & data_) { #ifdef CC_GCC # pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wstringop-overflow" # pragma GCC diagnostic ignored "-Warray-bounds" -# pragma GCC diagnostic ignored "-Wrestrict" +# ifndef ANDROID +# pragma GCC diagnostic ignored "-Wstringop-overflow" +# pragma GCC diagnostic ignored "-Wrestrict" +# endif #endif const size_t ps = size(); enlarge(data_.size_s()); diff --git a/libs/main/types/pivaluetree.cpp b/libs/main/types/pivaluetree.cpp index a02ce13c..a87303ad 100644 --- a/libs/main/types/pivaluetree.cpp +++ b/libs/main/types/pivaluetree.cpp @@ -217,7 +217,7 @@ PIValueTree & PIValueTree::child(const PIStringList & path) { if (_is_null || path.isEmpty()) return *this; PIValueTree * ret = &child(path[0]); for (int i = 1; i < path.size_s(); ++i) - ret = &child(path[i]); + ret = &(ret->child(path[i])); return *ret; } diff --git a/libs/mqtt_client/pimqttclient.cpp b/libs/mqtt_client/pimqttclient.cpp index 6518d7b2..133639e5 100644 --- a/libs/mqtt_client/pimqttclient.cpp +++ b/libs/mqtt_client/pimqttclient.cpp @@ -20,6 +20,36 @@ #include "pimqttclient.h" #include "MQTTClient.h" +#include "piliterals_string.h" +#include "piserverendpoint_p.h" + + +struct PIMQTT::Client::Endpoint: public PIHTTP::ServerEndpoint { + PIMQTT::Client::MessageFunction function; +}; + +struct TopicUsage { + int counter = 0; + PIMQTT::QoS qos = PIMQTT::QoS::Level1; +}; + +struct EndpointsStorage { + PIMap>> prepared; // [priority][topic] -> endpoints + PIMap topics_binded; // [topic] -> TopicUsage +}; + + +PIString topicFromEndpoint(const PIMQTT::Client::Endpoint & e) { + PIStringList ret; + for (const auto & i: e.prepared_path) { + switch (i.type) { + case PIHTTP::ServerEndpoint::PathElement::Type::Fixed: ret << i.source; break; + case PIHTTP::ServerEndpoint::PathElement::Type::AnyMany: ret << "#"_a; break; + default: ret << "+"_a; break; + } + } + return ret.join('/'); +} STATIC_INITIALIZER_BEGIN @@ -30,7 +60,9 @@ STATIC_INITIALIZER_END PRIVATE_DEFINITION_START(PIMQTT::Client) MQTTClient client = nullptr; - bool connected = false; + std::atomic connected{false}; + + PIProtectedVariable endpoints; static void connectionLost_callback(void * context, char *) { ((PIMQTT::Client *)context)->mqtt_connectionLost(); @@ -88,15 +120,23 @@ void PIMQTT::Client::disconnect() { } -void PIMQTT::Client::subscribe(const PIString & topic, QoS qos) { +void PIMQTT::Client::subscribe(const PIString & topic, MessageFunction functor, QoS qos) { if (is_destoying) return; - worker->enqueueTask([this, topic, qos] { subscribeInternal({topic, qos}); }); + Subscribe sub{topic, functor, qos}; + // piCout << "subscribe" << topic; + PIString mqtt_topic = registerSubscribe(sub); + if (mqtt_topic.isEmpty()) return; + sub.topic = mqtt_topic; + worker->enqueueTask([this, sub] { subscribeInternal(sub); }); } void PIMQTT::Client::unsubscribe(const PIString & topic) { if (is_destoying) return; - worker->enqueueTask([this, topic] { unsubscribeInternal(topic); }); + // piCout << "unsubscribe" << topic; + PIString mqtt_topic = unregisterSubscribe(topic); + if (mqtt_topic.isEmpty()) return; + worker->enqueueTask([this, mqtt_topic] { unsubscribeInternal(mqtt_topic); }); } @@ -115,8 +155,27 @@ void PIMQTT::Client::publish(const MessageConst & msg) { } +PIStringList PIMQTT::Client::usedTopics() const { + return PRIVATE->endpoints.getRef()->topics_binded.keys(); +} + + +void PIMQTT::Client::unsubscribeAll() { + { + auto ref = PRIVATE->endpoints.getRef(); + auto tit = ref->topics_binded.makeIterator(); + while (tit.next()) { + if (tit.value().counter <= 0) continue; + PIString mqtt_topic = tit.key(); + worker->enqueueTask([this, mqtt_topic] { unsubscribeInternal(mqtt_topic); }); + } + } + unregisterAll(); +} + + void PIMQTT::Client::mqtt_connectionLost() { - piCoutObj << "mqtt_connectionLost"; + // piCoutObj << "mqtt_connectionLost"; PRIVATE->connected = false; changeStatus(Idle); disconnected(Error::ServerUnavailable); @@ -126,9 +185,114 @@ void PIMQTT::Client::mqtt_connectionLost() { void PIMQTT::Client::mqtt_deliveryComplete(int token) {} -void PIMQTT::Client::mqtt_messageArrived(const MessageConst & msg) { - piCoutObj << "mqtt_messageArrived"; - received(msg); +void PIMQTT::Client::mqtt_messageArrived(MessageMutable & msg) { + PIStringList in_path = msg.topicList(); + PIMQTT::Client::MessageFunction function; + // piCoutObj << "mqtt_messageArrived"; + { + bool found = false; + auto ref = PRIVATE->endpoints.getRef(); + auto pit = ref->prepared.makeReverseIterator(); + while (pit.next()) { // by priority + + auto tit = pit.value().makeIterator(); + while (tit.next()) { // by MQTT topic + + for (const auto & ep: tit.value()) { + PIMap ext_args; + if (ep.match(in_path, ext_args)) { + msg.pathArguments() = ext_args; + function = ep.function; + found = true; + break; + } + } + if (found) break; + } + if (found) break; + } + } + if (function) + function(msg); + else + receivedUnhandled(msg); +} + + +PIString PIMQTT::Client::registerSubscribe(const Subscribe & sub) { + Endpoint ep; + ep.create(convertTopic2HTTP(sub.topic)); + ep.function = sub.functor; + PIString topic = topicFromEndpoint(ep); + if (topic.isEmpty()) { + piCoutObj << "Warning: subscribe to empty topic, ignore"; + return {}; + } + // piCout << sub.topic << "->" << topic << ep.priority; + bool is_new_topic = false; + auto ref = PRIVATE->endpoints.getRef(); + auto & eps_by_topic(ref->prepared[ep.priority][topic]); + for (const auto & i: eps_by_topic) { + if (i.path == ep.path) { + piCoutObj << "Warning: subscribe duplicate path, ignore"; + return {}; + } + } + eps_by_topic << ep; + auto & usage(ref->topics_binded[topic]); + is_new_topic = usage.counter == 0; + ++usage.counter; + if (!is_new_topic) return {}; + usage.qos = sub.qos; + return topic; +} + + +PIString PIMQTT::Client::unregisterSubscribe(const PIString & mqtt_topic) { + Endpoint ep; + ep.create(convertTopic2HTTP(mqtt_topic)); + PIString topic = topicFromEndpoint(ep); + if (topic.isEmpty()) { + piCoutObj << "Warning: unsubscribe from empty topic, ignore"; + return {}; + } + // piCout << mqtt_topic << "->" << topic << ep.priority; + auto ref = PRIVATE->endpoints.getRef(); + auto pit = ref->prepared.makeIterator(); + while (pit.next()) { // by priority + + auto tit = pit.value().makeIterator(); + while (tit.next()) { // by MQTT topic + + auto & eps(tit.value()); + for (int i = 0; i < eps.size_s(); ++i) { + if (eps[i].path == ep.path) { + eps.remove(i); + auto & usage(ref->topics_binded[tit.key()]); + --usage.counter; + PIString ret; + if (usage.counter <= 0) { + ret = tit.key(); + ref->topics_binded.remove(tit.key()); + } + if (eps.isEmpty()) { + // piCout << "remove topics" << tit.key(); + pit.value().remove(tit.key()); + } + return ret; + } + } + } + } + piCoutObj << "Warning: unsubscribe from" << mqtt_topic << ", topic not found"; + return {}; +} + + +void PIMQTT::Client::unregisterAll() { + auto ref = PRIVATE->endpoints.getRef(); + ref->prepared.clear(); + ref->topics_binded.clear(); } @@ -160,6 +324,12 @@ void PIMQTT::Client::connectInternal(const ConnectInfo & ci) { return; } PRIVATE->connected = true; + PIMap topics_binded; + { topics_binded = PRIVATE->endpoints.getRef()->topics_binded; } + auto it = topics_binded.makeIterator(); + while (it.next()) { + if (it.value().counter > 0) subscribeInternal({it.key(), nullptr, it.value().qos}); + } changeStatus(Connected); connected(); } @@ -189,18 +359,21 @@ void PIMQTT::Client::publishInternal(const MessageConst & m) { void PIMQTT::Client::subscribeInternal(const Subscribe & sub) { if (!PRIVATE->client) return; + // piCout << "subscribeInternal" << sub.topic; int ret = MQTTClient_subscribe(PRIVATE->client, sub.topic.dataUTF8(), static_cast(sub.qos)); if (ret != MQTTCLIENT_SUCCESS) { piCoutObj << "Failed to subscribe" << sub.topic << ", code" << ret; + return; } } -void PIMQTT::Client::unsubscribeInternal(const PIString & topic) { +void PIMQTT::Client::unsubscribeInternal(const PIString & mqtt_topic) { if (!PRIVATE->client) return; - int ret = MQTTClient_unsubscribe(PRIVATE->client, topic.dataUTF8()); + // piCout << "unsubscribeInternal" << mqtt_topic; + int ret = MQTTClient_unsubscribe(PRIVATE->client, mqtt_topic.dataUTF8()); if (ret != MQTTCLIENT_SUCCESS) { - piCoutObj << "Failed to unsubscribe" << topic << ", code" << ret; + piCoutObj << "Failed to unsubscribe" << mqtt_topic << ", code" << ret; } } @@ -217,3 +390,12 @@ void PIMQTT::Client::destroy() { void PIMQTT::Client::changeStatus(Status s) { m_status = s; } + +PIString PIMQTT::Client::convertTopic2MQTT(const PIString & topic) { + return topic.replacedAll("**", '#').replacedAll('*', '+'); +} + + +PIString PIMQTT::Client::convertTopic2HTTP(const PIString & topic) { + return topic.replacedAll('#', "**").replacedAll('+', '*'); +} diff --git a/main.cpp b/main.cpp index fa83d4df..6550c43a 100644 --- a/main.cpp +++ b/main.cpp @@ -10,161 +10,100 @@ using namespace PICoutManipulators; using namespace PIHTTP; -using namespace PIUnits::Class; -int rcnt = 0, scnt = 0; - -inline PIByteArray SMBusTypeInfo_genHash(PIString n) { - PICrypt c; - return piSerialize(c.shorthash(n.removeAll(" "), PIString("SMBusDataHashKey").toByteArray())); -} PIKbdListener kbd; +MessageMutable createMessage(Code c, const char * path, const MessageConst & msg) { + piCout << "path" << path << "args" << msg.pathArguments(); + return MessageMutable().setCode(c); +}; int main(int argc, char * argv[]) { + // piCout << "start ..."; + // PIHTTPServer server; + // server.registerUnhandled([](const MessageConst & msg) { return createMessage(Code::BadRequest, "unhadled", msg); }); + // server.registerPath("api/v1/status", Method::Get, [](const MessageConst & msg) { + // return createMessage(Code::Accepted, "api/v1/status", msg); + // }); + // server.registerPath("api/v1/plugins", Method::Get, [](const MessageConst & msg) { + // return createMessage(Code::Accepted, "api/v1/plugins", msg); + // }); + // server.registerPath("api/v1/task-status", Method::Get, [](const MessageConst & msg) { + // return createMessage(Code::Accepted, "api/v1/task-status", msg); + // }); + // server.registerPath("api/v1/task/{taskID}/status", Method::Get, [](const MessageConst & msg) { + // return createMessage(Code::Accepted, "api/v1/task/{taskID}/status", msg); + // }); + // server.registerPath("api/v1/bort/list", Method::Get, [](const MessageConst & msg) { + // return createMessage(Code::Accepted, "api/v1/bort/list", msg); + // }); + // server.registerPath("api/v1/all", Method::Get, [](const MessageConst & msg) { + // return createMessage(Code::Accepted, "api/v1/all", msg); + // }); + // server.registerPath("api/v1/all/bort{A}/f", Method::Get, [](const MessageConst & msg) { + // return createMessage(Code::Accepted, "api/v1/all/*/f", msg); + // }); + // server.registerPath("api/v1/all2/**", Method::Get, [](const MessageConst & msg) { + // return createMessage(Code::Accepted, "api/v1/all2/**", msg); + // }); + // server.listenAll(12345); + + // kbd.enableExitCapture('Q'); + // WAIT_FOR_EXIT + // piCout << "exiting ..."; + // server.stop(); + + // return 0; + + // PISystemMonitor mon; + // mon.startOnSelf(); + // PISystemMonitor::totalRAM(); + // 2_s .sleep(); + + // return 0; PIMQTT::Client cl; cl.setConnectTimeout(2_s); + + cl.subscribe("api/v1/all/bort{A}/f", + [](const PIMQTT::MessageConst & msg) { piCout << "1" << msg.topicList() << msg.pathArguments() << msg.body().size(); }); + cl.subscribe("api/v1/all/task{T}/f", + [](const PIMQTT::MessageConst & msg) { piCout << "2" << msg.topicList() << msg.pathArguments() << msg.body().size(); }); + cl.subscribe("api/v1/all/*/f", + [](const PIMQTT::MessageConst & msg) { piCout << "3" << msg.topicList() << msg.pathArguments() << msg.body().size(); }); + cl.subscribe("api/v1/all2/**", + [](const PIMQTT::MessageConst & msg) { piCout << "4" << msg.topicList() << msg.pathArguments() << msg.body().size(); }); CONNECTL(&cl, connected, [&cl] { piCout << "connected"; - cl.subscribe("/zigbee2mqtt"); - cl.subscribe("/zigbee2mqtt/+"); - cl.unsubscribe("/zigbee2mqtt"); - cl.publish("/zigbee2mqtt/abc", "hello from PIP"_a.toAscii()); + // cl.subscribe("api/v1/plugins"); + // cl.subscribe("api/v1/task-status"); + // cl.subscribe("api/v1/*/{taskID}/status"); + // cl.subscribe("api/v1/bort/list"); + // cl.subscribe("api/v1/all"); + // cl.subscribe("/zigbee2mqtt"); + // cl.subscribe("/zigbee2mqtt/+/status/"); + // cl.subscribe("/zigbee2mqtt/*/status/"); + // cl.subscribe("test/#"); + // cl.subscribe("#"); + // cl.unsubscribe("/zigbee2mqtt"); + // cl.unsubscribe("api/v1/all/bort{A}/f"); + // cl.unsubscribe("api/v1/all/*/f"); + // cl.publish("/zigbee2mqtt/abc", "hello from PIP"_a.toAscii()); }); CONNECTL(&cl, disconnected, [&cl](PIMQTT::Error code) { piCout << "disconnected code" << (int)code; cl.connect("localhost", "PIP"); }); - CONNECTL(&cl, received, [](const PIMQTT::MessageConst & message) { - piCout << "received" << message.topic() << message.payload().size(); + CONNECTL(&cl, receivedUnhandled, [](const PIMQTT::MessageConst & message) { + piCout << "receivedUnhandled" << message.topic() << message.pathArguments() << message.payload().size(); }); cl.connect("localhost", "PIP"); + piSleep(6.); + cl.unsubscribeAll(); + kbd.enableExitCapture('Q'); WAIT_FOR_EXIT piCout << "exiting ..."; - return 0; - - PICrypt _crypt; - // auto ba = PIFile::readAll("logo.png"); - PIString str = "hello!"_a; - PIByteArray ba = str.toAscii(); - PIByteArray key = PIString("SMBusDataHashKey").toByteArray(); - - const int times = 1000000; - PITimeMeasurer tm; - PISystemTime el; - - tm.reset(); - piForTimes(times) { - PIDigest::calculateWithKey(ba, key, PIDigest::Type::SipHash_2_4_128); - } - el = tm.elapsed(); - piCout << "PIDigest" << el.toString(); - - tm.reset(); - piForTimes(times) { - _crypt.shorthash(str, key); - } - el = tm.elapsed(); - piCout << " sodium" << el.toString(); - - tm.reset(); - piForTimes(times) { - PIDigest::calculateWithKey(ba, key, PIDigest::Type::BLAKE2b_128); - } - el = tm.elapsed(); - piCout << " blake" << el.toString(); - - return 0; - - - PIEthernet *eth_r, *eth_s; - eth_r = PIIODevice::createFromFullPath("eth://udp: 192.168.1.25 :10000")->cast(); - eth_s = PIIODevice::createFromFullPath("eth://udp: : : 192.168.1.25:10000")->cast(); - - eth_r->setReadBufferSize(1_MiB); - CONNECTL(eth_r, threadedReadEvent, [](const uchar * readed, ssize_t size) { - // piCout << "rec"; - piMSleep(1); - ++rcnt; - }); - eth_r->startThreadedRead(); - - PIByteArray _ba(1400); - for (int i = 0; i < 100; ++i) { - eth_s->write(_ba); - ++scnt; - } - - 0.2_s .sleep(); - - piCout << "snd" << scnt; - piCout << "rec" << rcnt; - - piDeleteSafety(eth_r); - piDeleteSafety(eth_s); - return 0; - - PITranslator::loadLang("ru"); - /*auto ucl = PIUnits::allClasses(); - for (auto c: ucl) { - piCout << (c->className() + ":"); - for (auto t: c->allTypes()) { - piCout << " " << c->name(t) << "->" << c->unit(t); - } - }*/ - - // PIUnits::Value(1); - // piCout << PIUnits::name(PIUnits::Class::Information::Bit); - // piCout << PIUnits::name(PIUnits::Class::Information::Byte); - // piCout << PIUnits::name(PIUnits::Class::Information::_LastType); - // piCout << PIUnits::name((int)PIUnits::Class::Angle::Degree); - - // piCout << PIUnits::unit(PIUnits::Class::Information::Bit); - // piCout << PIUnits::unit(PIUnits::Class::Information::Byte); - // piCout << PIUnits::unit(PIUnits::Class::Information::_LastType); - // piCout << PIUnits::unit((int)PIUnits::Class::Angle::Degree); - - // for (int i = -10; i < 10; ++i) - // piCout << PIUnits::Value(pow10(i * 0.99), PIUnits::Class::Distance::Meter).toString(); - - auto v = PIUnits::Value(M_PI, Angle::Radian); - piCout << v << "=" << v.converted(Angle::Degree); - - v = PIUnits::Value(45, Angle::Degree); - piCout << v << "=" << v.converted(Angle::Radian); - - piCout << PIUnits::Value(5E-5, Time::Second); - piCout << PIUnits::Value(3E-3, Time::Second); - piCout << PIUnits::Value(0.8, Time::Second); - piCout << PIUnits::Value(1.2, Time::Second); - piCout << PIUnits::Value(1001, Time::Second); - piCout << PIUnits::Value(1000001, Time::Second); - - piCout << PIUnits::Value(1_KB, Information::Byte); - piCout << PIUnits::Value(1_MB, Information::Byte); - piCout << PIUnits::Value(1_MiB, Information::Byte); - piCout << PIUnits::Value(1_MB, Information::Byte).converted(Information::Bit); - piCout << PIUnits::Value(1_MiB, Information::Byte).converted(Information::Bit); - - piCout << PIUnits::Value(0., Temperature::Celsius).converted(Temperature::Kelvin); - piCout << PIUnits::Value(0., Temperature::Celsius).converted(Temperature::Fahrenheit); - piCout << PIUnits::Value(100., Temperature::Celsius).converted(Temperature::Fahrenheit); - - piCout << PIUnits::Value(1., Pressure::Atmosphere).converted(Pressure::Pascal); - piCout << PIUnits::Value(1., Pressure::Atmosphere).converted(Pressure::MillimetreOfMercury); - piCout << PIUnits::Value(766., Pressure::MillimetreOfMercury).converted(Pressure::Atmosphere); - - piCout << PIUnits::Value(5E-5, Time::Second).converted(Time::Hertz); - piCout << PIUnits::Value(3E-3, Time::Second).converted(Time::Hertz); - piCout << PIUnits::Value(0.8, Time::Second).converted(Time::Hertz); - piCout << PIUnits::Value(1.2, Time::Second).converted(Time::Hertz); - piCout << PIUnits::Value(1001, Time::Second).converted(Time::Hertz); - piCout << PIUnits::Value(1000001, Time::Second).converted(Time::Hertz); - // piCout << PIUnits::Value(0.2, Time::Second).converted(Time::Hertz); - // piCout << PIUnits::Value(5E-5, Time::Second).converted(Time::Hertz); - return 0; } diff --git a/utils/code_model_generator/getter.cpp b/utils/code_model_generator/getter.cpp index 5f76f16c..c553e0f4 100644 --- a/utils/code_model_generator/getter.cpp +++ b/utils/code_model_generator/getter.cpp @@ -24,6 +24,10 @@ void writeGetterTypeMembers(Runtime & rt, const PICodeParser::Entity * e, PIString var_prefix) { if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += "."; + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + writeGetterTypeMembers(rt, p, var_prefix); + } PISet used_id; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue; @@ -43,6 +47,10 @@ void writeGetterTypeMembers(Runtime & rt, const PICodeParser::Entity * e, PIStri void writeGetterValueMembers(Runtime & rt, const PICodeParser::Entity * e, PIString var_prefix) { if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += "."; + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + writeGetterValueMembers(rt, p, var_prefix); + } PISet used_id; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue; @@ -65,6 +73,10 @@ void writeGetterValueMembers(Runtime & rt, const PICodeParser::Entity * e, PIStr void writeGetterOffsetMembers(Runtime & rt, const PICodeParser::Entity * e, PIString entity_name, PIString var_prefix) { if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += "."; + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + writeGetterOffsetMembers(rt, p, entity_name, var_prefix); + } PISet used_id; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || !m.dims.isEmpty() || m.isBitfield() || (m.visibility != PICodeParser::Public)) continue; @@ -117,5 +129,9 @@ bool needClassGetter(const PICodeParser::Entity * e) { if (m.attributes[PICodeParser::Static]) continue; return true; } + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + if (needClassGetter(p)) return true; + } return false; } diff --git a/utils/code_model_generator/json.cpp b/utils/code_model_generator/json.cpp index 16ae6b97..07f547e9 100644 --- a/utils/code_model_generator/json.cpp +++ b/utils/code_model_generator/json.cpp @@ -24,6 +24,10 @@ bool writeClassJSONMembersOut(Runtime & rt, const PICodeParser::Entity * e, PIString var_prefix) { if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += "."; + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + writeClassJSONMembersOut(rt, p, var_prefix); + } PIVector ml; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue; @@ -57,16 +61,16 @@ bool writeClassJSONMembersOut(Runtime & rt, const PICodeParser::Entity * e, PISt if (is_union) break; } if (is_union) return true; - /*for (const PICodeParser::Entity * ce: e->children) { - if (!ce->is_anonymous) continue; - if (!writeClassJSONMembersOut(rt, ce)) return false; - }*/ return true; } bool writeClassJSONMembersIn(Runtime & rt, const PICodeParser::Entity * e, PIString var_prefix) { if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += "."; + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + writeClassJSONMembersIn(rt, p, var_prefix); + } PIVector ml; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue; @@ -105,10 +109,6 @@ bool writeClassJSONMembersIn(Runtime & rt, const PICodeParser::Entity * e, PIStr if (is_union) break; } if (is_union) return true; - /*for (const PICodeParser::Entity * ce: e->children) { - if (!ce->is_anonymous) continue; - if (!writeClassJSONMembersIn(rt, ce)) return false; - }*/ return true; } @@ -121,6 +121,10 @@ bool needClassJSON(const PICodeParser::Entity * e) { if (m.meta.value("id") == "-") continue; return true; } + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + if (needClassJSON(p)) return true; + } return false; } diff --git a/utils/code_model_generator/metainfo.cpp b/utils/code_model_generator/metainfo.cpp index c7664d7c..54b1efe4 100644 --- a/utils/code_model_generator/metainfo.cpp +++ b/utils/code_model_generator/metainfo.cpp @@ -22,6 +22,10 @@ void writeClassInfoMembers(Runtime & rt, const PICodeParser::Entity * e, PIString var_prefix) { if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += "."; + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + writeClassInfoMembers(rt, p, var_prefix); + } for (const PICodeParser::Member & m: e->members) { auto type = findEntity(rt, m.type); if (type) { diff --git a/utils/code_model_generator/stream.cpp b/utils/code_model_generator/stream.cpp index f560d29e..b04e15cc 100644 --- a/utils/code_model_generator/stream.cpp +++ b/utils/code_model_generator/stream.cpp @@ -24,6 +24,10 @@ bool writeClassStreamMembersOut(Runtime & rt, const PICodeParser::Entity * e, int & cnt, bool simple, PIString var_prefix) { if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += "."; + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + if (!writeClassStreamMembersOut(rt, p, cnt, simple, var_prefix)) return false; + } PIVector ml; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue; @@ -83,6 +87,10 @@ bool writeClassStreamMembersOut(Runtime & rt, const PICodeParser::Entity * e, in bool writeClassStreamMembersIn(Runtime & rt, const PICodeParser::Entity * e, int & cnt, bool simple, PIString var_prefix) { if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += "."; + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + if (!writeClassStreamMembersIn(rt, p, cnt, simple, var_prefix)) return false; + } PIVector ml; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue; @@ -166,6 +174,10 @@ bool needClassStream(const PICodeParser::Entity * e) { if (m.meta.value("id") == "-") continue; return true; } + for (const PICodeParser::Entity * p: e->parents) { + if (p->is_anonymous) continue; + if (needClassStream(p)) return true; + } return false; }