Compare commits

31 Commits

Author SHA1 Message Date
92a87a0c64 picloud add "-w" option for watchdog 2024-10-15 15:04:39 +03:00
cd7e053fc5 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)
2024-10-15 12:02:18 +03:00
9eecbbab6e new method PIClientServer::Server::closeAll()
PISignals::releaseSignals()
2024-09-24 18:57:50 +03:00
3641e636d2 new PIClientServer::ClientBase::stopAndWait() method for blocking stop read.
PIClientServer::ClientBase::close() now non-blocking
2024-09-21 19:56:39 +03:00
4acab04895 PILog ready to use 2024-09-19 17:26:58 +03:00
aa963a4bda PIEthernet on error close disconnect 2024-09-17 16:50:22 +03:00
bdd18b614f PIEthernet more accuracy construct
PIThread windows fix
2024-09-17 16:11:18 +03:00
e186e0adff shorter thread names 2024-09-17 15:58:06 +03:00
f105f616f6 PIThread more accurate end, PIEthernet tcpserver client no reinit 2024-09-17 13:22:20 +03:00
andrey.bychkov
b99c51181d fix test 2024-09-17 12:31:03 +03:00
andrey.bychkov
bc6b584480 fix waitLoop 2024-09-17 11:28:34 +03:00
eb97de1413 close 2024-09-17 11:21:23 +03:00
97f1c25ff8 close 2024-09-17 11:15:50 +03:00
97aad47a21 some fixes 2024-09-16 23:32:01 +03:00
43bd1d8550 Merge remote-tracking branch 'remotes/origin/tests_client_server' into test 2024-09-16 19:58:26 +03:00
andrey.bychkov
3255199b3f Unit tests for PIClientServer 2024-09-16 19:54:44 +03:00
224412e20a PIDir::temporary fix 2024-09-16 16:24:11 +03:00
000ce2a54d 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
2024-09-16 16:06:07 +03:00
andrey.bychkov
f992bf4cbb build fix 2024-09-16 10:43:22 +03:00
andrey.bychkov
96625bd93d minor clean 2024-09-16 10:11:31 +03:00
9d4357c066 PIVector2D =() fix 2024-09-15 16:50:15 +03:00
7a945f47b1 PILog works 2024-09-13 23:05:24 +03:00
9a352bfc83 add PISystemTime::toFrequency(), add PILog 2024-09-13 18:00:48 +03:00
aa2f117075 finalize module 2024-09-13 13:37:09 +03:00
bf5bb45771 decompose, add new main group "Application"
PICLI code brush
2024-09-13 13:31:31 +03:00
cdc966bd8c Merge pull request 'client_server' (#181) from client_server into master
Reviewed-on: #181
2024-09-13 12:12:10 +03:00
17b902ebcc add PIClientServer::Config, common configuration type for both sides, stream packer and encryption settings 2024-09-13 11:08:32 +03:00
996b7ea403 important:
* PIThread::~PIThread() now unregister itself from introspection, if terminates than show warning
 * PISystemMonitor now correctly stops
 * PIPeer now can correctly stopAndWait
 * PIPeer::destroy(), protected method for close all eths and threads
 * new PIINTROSPECTION_STOP macro
 * Introspection now can be correctly stopped by macro, more safety

ClientServer:
 * ClientBase::close() stop and disconnect channel
 * Server clients clean-up now event-based
 * No warnings on client destructor
2024-09-12 17:07:48 +03:00
da4b09be9e PIEthernet fix tcp-server close (properly delete all clients)
PIEthernet::stopThreadedListen() method
decompose client to 2 implementations - server-side and client-side
2024-09-11 21:41:55 +03:00
b24b5a1346 add encryption 2024-09-11 15:44:02 +03:00
0d94699206 first try, works 2024-09-11 10:18:45 +03:00
72 changed files with 2495 additions and 703 deletions

3
.gitignore vendored
View File

@@ -3,4 +3,5 @@
/doc/rtf
_unsused
CMakeLists.txt.user*
/include
/include
/release

View File

@@ -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)
@@ -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)
@@ -92,7 +96,7 @@ set(PIP_UTILS_LIST)
set(PIP_TESTS_LIST)
set(PIP_EXPORTS)
set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;cloud;lua")
set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua")
foreach(_m ${PIP_SRC_MODULES})
set(PIP_MSG_${_m} "no")
endforeach()
@@ -392,6 +396,7 @@ if (NOT CROSSTOOLS)
pip_find_lib(sodium)
if(sodium_FOUND)
pip_module(crypt "sodium" "PIP crypt support" "" "" "")
pip_module(client_server "pip_io_utils" "PIP client-server helper" "" "" "")
pip_module(cloud "pip_io_utils" "PIP cloud support" "" "" "")
endif()
@@ -480,7 +485,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)
target_link_libraries(pip_test pip pip_io_utils pip_client_server)
if(sodium_FOUND)
add_executable(pip_cloud_test "main_picloud_test.cpp")
target_link_libraries(pip_cloud_test pip_cloud)
@@ -585,7 +590,7 @@ if ((NOT PIP_FREERTOS) AND (NOT CROSSTOOLS))
find_package(Doxygen)
if(DOXYGEN_FOUND)
set(DOXY_DEFINES "${PIP_EXPORTS}")
foreach (_m "console" "usb" "compress" "crypt" "cloud" "fftw" "opencl" "io_utils" "lua")
foreach (_m "console" "usb" "compress" "crypt" "client_server" "cloud" "fftw" "opencl" "io_utils" "lua")
string(TOUPPER "${_m}" _mdef)
list(APPEND DOXY_DEFINES "PIP_${_mdef}_EXPORT")
endforeach()

View File

@@ -9,6 +9,7 @@ Create imported targets:
* PIP::FFTW
* PIP::OpenCL
* PIP::IOUtils
* PIP::ClientServer
* PIP::Cloud
* PIP::Lua
@@ -22,7 +23,7 @@ include(SHSTKMacros)
shstk_set_find_dirs(PIP)
set(__libs "usb;crypt;console;fftw;compress;io_utils;opencl;cloud;lua")
set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua")
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")
@@ -83,15 +84,16 @@ if(PIP_FIND_VERSION VERSION_GREATER PIP_VERSION)
message(FATAL_ERROR "PIP version ${PIP_VERSION} is available, but ${PIP_FIND_VERSION} requested!")
endif()
set(__module_usb USB )
set(__module_console Console )
set(__module_crypt Crypt )
set(__module_fftw FFTW )
set(__module_compress Compress )
set(__module_opencl OpenCL )
set(__module_io_utils IOUtils )
set(__module_cloud Cloud )
set(__module_lua Lua )
set(__module_usb USB )
set(__module_console Console )
set(__module_crypt Crypt )
set(__module_fftw FFTW )
set(__module_compress Compress )
set(__module_opencl OpenCL )
set(__module_io_utils IOUtils )
set(__module_client_server ClientServer)
set(__module_cloud Cloud )
set(__module_lua Lua )
foreach (_l ${__libs})
set( __inc_${_l} "")
@@ -99,8 +101,9 @@ foreach (_l ${__libs})
set(__libs_${_l} "")
endforeach()
set(__deps_io_utils "PIP::Crypt")
set(__deps_cloud "PIP::IOUtils")
set(__deps_io_utils "PIP::Crypt" )
set(__deps_client_server "PIP::IOUtils")
set(__deps_cloud "PIP::IOUtils")
if (BUILDING_PIP)

View File

@@ -0,0 +1,54 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "piclientserver_client.h"
#include "piclientserver_server.h"
#include "piethernet.h"
void PIClientServer::ServerClient::createForServer(Server * parent, PIEthernet * tcp_) {
tcp = tcp_;
tcp->setParameter(PIEthernet::KeepConnection, false);
init();
CONNECTL(tcp, disconnected, ([this, parent](bool) { parent->clientDisconnected(this); }));
}
PIClientServer::Client::Client() {
tcp = new PIEthernet(PIEthernet::TCP_Client);
tcp->setParameter(PIEthernet::KeepConnection, true);
own_tcp = true;
init();
}
PIClientServer::Client::~Client() {
if (tcp) tcp->setDebug(false);
close();
stopAndWait();
}
void PIClientServer::Client::connect(PINetworkAddress addr) {
if (!tcp || !own_tcp) return;
close();
config.apply(this);
tcp->connect(addr, true);
tcp->startThreadedRead();
}

View File

@@ -0,0 +1,117 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "piclientserver_client_base.h"
#include "piethernet.h"
#include "piliterals_time.h"
PIClientServer::ClientBase::ClientBase() {}
PIClientServer::ClientBase::~ClientBase() {
close();
stopAndWait();
if (own_tcp) piDeleteSafety(tcp);
piDeleteSafety(diag);
}
void PIClientServer::ClientBase::close() {
if (!tcp) return;
can_write = false;
tcp->stop();
stream.clear();
}
void PIClientServer::ClientBase::stopAndWait() {
if (!tcp) return;
tcp->stopAndWait(10_s);
if (tcp->isThreadedRead()) tcp->terminateThreadedRead();
tcp->close();
stream.clear();
}
int PIClientServer::ClientBase::write(const void * d, const size_t s) {
if (!tcp) return -1;
if (!can_write) return 0;
PIMutexLocker guard(write_mutex);
// piCout << "... send ...";
stream.send(PIByteArray(d, s));
// piCout << "... send ok";
return 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;
// piCout << "Connected";
connected();
});
CONNECTL(tcp, disconnected, [this](bool) {
can_write = false;
stream.clear();
// piCout << "Disconnected";
disconnected();
});
}
void PIClientServer::ClientBase::destroy() {
write_mutex.lock();
close();
piDeleteSafety(tcp);
write_mutex.unlock();
// piCout << "Destroyed";
}

View File

@@ -0,0 +1,50 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "piclientserver_config.h"
#include "piclientserver_client_base.h"
void PIClientServer::Config::setPacketSign(ushort sign) {
packet_sign = sign;
}
void PIClientServer::Config::setPacketSize(int bytes) {
packet_size = bytes;
}
void PIClientServer::Config::enableSymmetricEncryption(const PIByteArray & key) {
crypt_key = key;
}
void PIClientServer::Config::apply(ClientBase * client) {
auto & s(client->stream);
s.setPacketSign(packet_sign);
s.setMaxPacketSize(packet_size);
if (crypt_key.isNotEmpty()) {
s.setCryptEnabled(true);
s.setCryptKey(crypt_key);
} else
s.setCryptEnabled(false);
}

View File

@@ -0,0 +1,143 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "piclientserver_server.h"
#include "piclientserver_client.h"
#include "piethernet.h"
#include "piliterals_time.h"
PIClientServer::Server::Server() {
tcp_server = new PIEthernet(PIEthernet::TCP_Server);
clean_thread = new PIThread();
client_factory = [] { return new ServerClient(); };
CONNECTL(tcp_server, newConnection, [this](PIEthernet * c) {
PIMutexLocker guard(clients_mutex);
if (clients.size_s() >= max_clients) {
piCout << "Server::newConnection overflow clients count";
delete c;
return;
}
auto sc = client_factory();
if (!sc) {
piCout << "ClientFactory returns nullptr!";
return;
}
sc->createForServer(this, c);
newClient(sc);
});
clean_thread->start([this]() {
clean_notifier.wait();
PIVector<ServerClient *> to_delete;
clients_mutex.lock();
for (auto c: clients) {
const PIEthernet * eth = c->getTCP();
if (!eth) continue;
if (eth->isConnected()) continue;
c->can_write = false;
to_delete << c;
}
for (auto c: to_delete)
clients.removeOne(c);
clients_mutex.unlock();
for (auto c: to_delete) {
c->aboutDelete();
c->destroy();
delete c;
}
});
}
PIClientServer::Server::~Server() {
clean_thread->stop();
clean_notifier.notify();
clean_thread->waitForFinish();
piDeleteSafety(clean_thread);
stopServer();
for (auto c: clients) {
c->aboutDelete();
c->destroy();
delete c;
}
piDeleteSafety(tcp_server);
}
void PIClientServer::Server::listen(PINetworkAddress addr) {
if (!tcp_server) return;
stopServer();
is_closing = false;
tcp_server->listen(addr, true);
// piCout << "Listen on" << addr.toString();
}
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;
}
int PIClientServer::Server::clientsCount() const {
PIMutexLocker guard(clients_mutex);
return clients.size_s();
}
void PIClientServer::Server::forEachClient(std::function<void(ServerClient *)> func) {
PIMutexLocker guard(clients_mutex);
for (auto * c: clients) {
func(c);
if (is_closing) break;
}
}
void PIClientServer::Server::stopServer() {
if (!tcp_server) return;
is_closing = true;
tcp_server->stopThreadedListen();
tcp_server->stopAndWait();
}
void PIClientServer::Server::newClient(ServerClient * c) {
clients << c;
config.apply(c);
c->tcp->startThreadedRead();
c->connected();
}
void PIClientServer::Server::clientDisconnected(ServerClient * c) {
clean_notifier.notify();
}

View File

@@ -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));
}
@@ -166,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());
@@ -195,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);
}

View File

@@ -0,0 +1,58 @@
/*
PIP - Platform Independent Primitives
Module includes
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
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 <http://www.gnu.org/licenses/>.
*/
//! \defgroup Application Application
//! \~\brief
//! \~english Application-level classes.
//! \~russian Классы уровня "приложение".
//!
//! \~\details
//! \~english \section cmake_module_Application Building with CMake
//! \~russian \section cmake_module_Application Сборка с использованием CMake
//!
//! \~\code
//! find_package(PIP REQUIRED)
//! target_link_libraries([target] PIP)
//! \endcode
//!
//! \~english \par Common
//! \~russian \par Общее
//!
//! \~english
//! These files provides some classes for help to create application
//!
//! \~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 PIAPPLICATIONMODULE_H
#define PIAPPLICATIONMODULE_H
#include "picli.h"
#include "pisingleapplication.h"
#include "pisystemmonitor.h"
#endif

View File

@@ -69,11 +69,6 @@
PICLI::PICLI(int argc, char * argv[]) {
needParse = debug_ = true;
_prefix_short = "-";
_prefix_full = "--";
_count_opt = 0;
_count_mand = 0;
for (int i = 0; i < argc; ++i)
_args_raw << argv[i];
if (argc > 0) PISystemInfo::instance()->execCommand = argv[0];
@@ -89,7 +84,7 @@ void PICLI::parse() {
if (cra.left(2) == _prefix_full) {
last = 0;
full = cra.right(cra.length() - 2);
piForeach(Argument & a, _args) {
for (auto & a: _args) {
if (a.full_key == full) {
a.found = true;
last = &a;
@@ -101,7 +96,7 @@ void PICLI::parse() {
last = 0;
for (int j = 1; j < cra.length(); ++j) {
bool found = false;
piForeach(Argument & a, _args) {
for (auto & a: _args) {
if ((a.short_key != '\0') && (a.short_key == cra[j])) {
a.found = true;
last = &a;
@@ -121,7 +116,7 @@ void PICLI::parse() {
_args_opt << cra;
continue;
}
piCoutObj << "[PICli] Arguments overflow, \"" << cra << "\" ignored";
piCoutObj << "Arguments overflow, \"" << cra << "\" ignored";
}
if (last == 0 ? false : last->has_value) {
last->value = cra;
@@ -132,3 +127,129 @@ void PICLI::parse() {
}
needParse = false;
}
void PICLI::addArgument(const PIString & name, bool value) {
_args << Argument(name, name[0], name, value);
needParse = true;
}
void PICLI::addArgument(const PIString & name, const PIChar & shortKey, bool value) {
_args << Argument(name, shortKey, name, value);
needParse = true;
}
void PICLI::addArgument(const PIString & name, const char * shortKey, bool value) {
_args << Argument(name, PIChar::fromUTF8(shortKey), name, value);
needParse = true;
}
void PICLI::addArgument(const PIString & name, const PIChar & shortKey, const PIString & fullKey, bool value) {
_args << Argument(name, shortKey, fullKey, value);
needParse = true;
}
void PICLI::addArgument(const PIString & name, const char * shortKey, const PIString & fullKey, bool value) {
_args << Argument(name, PIChar::fromUTF8(shortKey), fullKey, value);
needParse = true;
}
PIString PICLI::rawArgument(int index) {
parse();
return _args_raw[index];
}
PIString PICLI::mandatoryArgument(int index) {
parse();
return _args_mand[index];
}
PIString PICLI::optionalArgument(int index) {
parse();
return _args_opt[index];
}
const PIStringList & PICLI::rawArguments() {
parse();
return _args_raw;
}
const PIStringList & PICLI::mandatoryArguments() {
parse();
return _args_mand;
}
const PIStringList & PICLI::optionalArguments() {
parse();
return _args_opt;
}
PIString PICLI::programCommand() {
parse();
return _args_raw.isNotEmpty() ? _args_raw.front() : PIString();
}
bool PICLI::hasArgument(const PIString & name) {
parse();
for (const auto & i: _args)
if (i.name == name && i.found) return true;
return false;
}
PIString PICLI::argumentValue(const PIString & name) {
parse();
for (const auto & i: _args)
if (i.name == name && i.found) return i.value;
return PIString();
}
PIString PICLI::argumentShortKey(const PIString & name) {
for (const auto & i: _args)
if (i.name == name) return PIString(i.short_key);
return PIString();
}
PIString PICLI::argumentFullKey(const PIString & name) {
for (const auto & i: _args)
if (i.name == name) return i.full_key;
return PIString();
}
void PICLI::setShortKeyPrefix(const PIString & prefix) {
_prefix_short = prefix;
needParse = true;
}
void PICLI::setFullKeyPrefix(const PIString & prefix) {
_prefix_full = prefix;
needParse = true;
}
void PICLI::setMandatoryArgumentsCount(const int count) {
_count_mand = count;
needParse = true;
}
void PICLI::setOptionalArgumentsCount(const int count) {
_count_opt = count;
needParse = true;
}

View File

@@ -1,5 +1,5 @@
/*! \file picli.h
* \ingroup Core
* \ingroup Application
* \~\brief
* \~english Command-Line parser
* \~russian Парсер командной строки
@@ -29,7 +29,7 @@
#include "piset.h"
#include "pistringlist.h"
//! \ingroup Core
//! \ingroup Application
//! \~\brief
//! \~english Command-Line parser.
//! \~russian Парсер командной строки.
@@ -42,131 +42,65 @@ public:
//! \~english Add argument with name "name", short key = name first letter and full key = name.
//! \~russian Добавляет аргумент с именем "name", коротким ключом = первой букве имени и полным ключом = имени.
void addArgument(const PIString & name, bool value = false) {
_args << Argument(name, name[0], name, value);
needParse = true;
}
void addArgument(const PIString & name, bool value = false);
//! \~english Add argument with name "name", short key = "shortKey" and full key = name.
//! \~russian Добавляет аргумент с именем "name", коротким ключом = "shortKey" и полным ключом = имени.
void addArgument(const PIString & name, const PIChar & shortKey, bool value = false) {
_args << Argument(name, shortKey, name, value);
needParse = true;
}
void addArgument(const PIString & name, const PIChar & shortKey, bool value = false);
//! \~english Add argument with name "name", short key = "shortKey" and full key = name.
//! \~russian Добавляет аргумент с именем "name", коротким ключом = "shortKey" и полным ключом = имени.
void addArgument(const PIString & name, const char * shortKey, bool value = false) {
_args << Argument(name, PIChar::fromUTF8(shortKey), name, value);
needParse = true;
}
void addArgument(const PIString & name, const char * shortKey, bool value = false);
//! \~english Add argument with name "name", short key = "shortKey" and full key = "fullKey".
//! \~russian Добавляет аргумент с именем "name", коротким ключом = "shortKey" и полным ключом = "fullKey".
void addArgument(const PIString & name, const PIChar & shortKey, const PIString & fullKey, bool value = false) {
_args << Argument(name, shortKey, fullKey, value);
needParse = true;
}
void addArgument(const PIString & name, const PIChar & shortKey, const PIString & fullKey, bool value = false);
//! \~english Add argument with name "name", short key = "shortKey" and full key = "fullKey".
//! \~russian Добавляет аргумент с именем "name", коротким ключом = "shortKey" и полным ключом = "fullKey".
void addArgument(const PIString & name, const char * shortKey, const PIString & fullKey, bool value = false) {
_args << Argument(name, PIChar::fromUTF8(shortKey), fullKey, value);
needParse = true;
}
void addArgument(const PIString & name, const char * shortKey, const PIString & fullKey, bool value = false);
//! \~english Returns unparsed command-line argument by index "index". Index 0 is program execute command.
//! \~russian Возвращает исходный аргумент командной строки по индексу "index". Индекс 0 это команда вызова программы.
PIString rawArgument(int index) {
parse();
return _args_raw[index];
}
PIString mandatoryArgument(int index) {
parse();
return _args_mand[index];
}
PIString optionalArgument(int index) {
parse();
return _args_opt[index];
}
PIString rawArgument(int index);
PIString mandatoryArgument(int index);
PIString optionalArgument(int index);
//! \~english Returns unparsed command-line arguments.
//! \~russian Возвращает исходные аргументы командной строки.
const PIStringList & rawArguments() {
parse();
return _args_raw;
}
const PIStringList & mandatoryArguments() {
parse();
return _args_mand;
}
const PIStringList & optionalArguments() {
parse();
return _args_opt;
}
const PIStringList & rawArguments();
const PIStringList & mandatoryArguments();
const PIStringList & optionalArguments();
//! \~english Returns program execute command without arguments.
//! \~russian Возвращает команду вызова программы без аргументов.
PIString programCommand() {
parse();
return _args_raw.size() > 0 ? _args_raw.front() : PIString();
}
PIString programCommand();
//! \~english Returns if argument "name" found.
//! \~russian Возвращает найден ли аргумент "name".
bool hasArgument(const PIString & name) {
parse();
piForeach(Argument & i, _args)
if (i.name == name && i.found) return true;
return false;
}
bool hasArgument(const PIString & name);
//! \~english Returns argument "name" value, or empty string if this is no value.
//! \~russian Возвращает значение аргумента "name" или пустую строку, если значения нет.
PIString argumentValue(const PIString & name) {
parse();
piForeach(Argument & i, _args)
if (i.name == name && i.found) return i.value;
return PIString();
}
PIString argumentValue(const PIString & name);
//! \~english Returns short key of argument "name", or empty string if this is no argument.
//! \~russian Возвращает короткий ключ аргумента "name" или пустую строку, если аргумента нет.
PIString argumentShortKey(const PIString & name) {
piForeach(Argument & i, _args)
if (i.name == name) return PIString(i.short_key);
return PIString();
}
PIString argumentShortKey(const PIString & name);
//! \~english Returns full key of argument "name", or empty string if this is no argument.
//! \~russian Возвращает полный ключ аргумента "name" или пустую строку, если аргумента нет.
PIString argumentFullKey(const PIString & name) {
piForeach(Argument & i, _args)
if (i.name == name) return i.full_key;
return PIString();
}
PIString argumentFullKey(const PIString & name);
const PIString & shortKeyPrefix() const { return _prefix_short; }
const PIString & fullKeyPrefix() const { return _prefix_full; }
int mandatoryArgumentsCount() const { return _count_mand; }
int optionalArgumentsCount() const { return _count_opt; }
void setShortKeyPrefix(const PIString & prefix) {
_prefix_short = prefix;
needParse = true;
}
void setFullKeyPrefix(const PIString & prefix) {
_prefix_full = prefix;
needParse = true;
}
void setMandatoryArgumentsCount(const int count) {
_count_mand = count;
needParse = true;
}
void setOptionalArgumentsCount(const int count) {
_count_opt = count;
needParse = true;
}
void setShortKeyPrefix(const PIString & prefix);
void setFullKeyPrefix(const PIString & prefix);
void setMandatoryArgumentsCount(const int count);
void setOptionalArgumentsCount(const int count);
bool debug() const { return debug_; }
void setDebug(bool debug) { debug_ = debug; }
@@ -175,29 +109,28 @@ public:
private:
struct Argument {
Argument() { has_value = found = false; }
Argument() {}
Argument(const PIString & n, const PIChar & s, const PIString & f, bool v) {
name = n;
short_key = s;
full_key = f;
has_value = v;
found = false;
}
PIString name;
PIChar short_key;
PIString full_key;
PIString value;
bool has_value, found;
bool has_value = false, found = false;
};
void parse();
PIString _prefix_short, _prefix_full;
PIString _prefix_short = "-", _prefix_full = "--";
PIStringList _args_raw, _args_mand, _args_opt;
PISet<PIString> keys_full, keys_short;
PIVector<Argument> _args;
int _count_mand, _count_opt;
bool needParse, debug_;
int _count_mand = 0, _count_opt = 0;
bool needParse = true, debug_ = true;
};
#endif // PICLI_H

View File

@@ -0,0 +1,229 @@
/*
PIP - Platform Independent Primitives
High-level log
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 <http://www.gnu.org/licenses/>.
*/
#include "pilog.h"
#include "pidir.h"
#include "piliterals_string.h"
#include "piliterals_time.h"
#include "pitime.h"
//! \class PILog pilog.h
//! \details
//! \~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().
//!
//! \~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
//!
PILog::PILog(): PIThread(), log_ts(&log_file) {
setName("PILog");
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);
}
PILog::~PILog() {
stop();
}
void PILog::setDir(const PIString & d) {
stopAndWait();
log_dir = d;
if (output[File]) {
PIDir::make(log_dir);
newFile();
}
start();
}
void PILog::setLineFormat(const PIString & f) {
line_format = f;
line_format_p = line_format;
line_format_p.replace("t", "${t}").replace("c", "${c}").replace("m", "${m}");
}
void PILog::setLevel(Level l) {
max_level = l;
}
PICout PILog::error(PIObject * context) {
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);
}
void PILog::stop() {
while (true) {
log_mutex.lock();
bool done = queue.isEmpty();
log_mutex.unlock();
if (done) break;
piMinSleep();
}
PIThread::stopAndWait();
}
void PILog::coutDone(int id, PIString * buffer) {
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, 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_by_cat.value(cat), PICoutManipulators::AddSpaces);
}
void PILog::enqueue(const PIString & msg, Level cat) {
auto t = PIDateTime::fromSystemTime(PISystemTime::current());
PIMutexLocker ml(log_mutex);
queue.enqueue({cat, t, msg});
}
PIString PILog::entryToString(const Entry & e) const {
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<int>(e.cat)]).replace("${m}", e.msg);
return ret;
}
void PILog::newFile() {
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),
PIIODevice::ReadWrite);
}
void PILog::run() {
if (output[File]) {
if (split_tm.elapsed() >= split_time) {
split_tm.reset();
newFile();
}
}
log_mutex.lock();
if (queue.isEmpty()) {
log_mutex.unlock();
piMSleep(20);
return;
}
log_mutex.unlock();
while (true) {
log_mutex.lock();
if (queue.isEmpty()) {
log_mutex.unlock();
return;
}
auto qi = queue.dequeue();
log_mutex.unlock();
auto str = entryToString(qi);
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;
}
}
}

View File

@@ -0,0 +1,152 @@
/*! \file pilog.h
* \ingroup Application
* \~\brief
* \~english High-level log
* \~russian Высокоуровневый лог
*/
/*
PIP - Platform Independent Primitives
High-level log
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 <http://www.gnu.org/licenses/>.
*/
#ifndef PIlog_H
#define PIlog_H
#include "pifile.h"
#include "piiostream.h"
#include "pithread.h"
//! \ingroup Application
//! \~\brief
//! \~english High-level log
//! \~russian Высокоуровневый лог
class PIP_EXPORT PILog: public PIThread {
PIOBJECT_SUBCLASS(PILog, PIThread)
public:
PILog();
~PILog();
enum class Level {
Error,
Warning,
Info,
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; }
//! \~english Set directory for log files. Should be set \b after \a setApplicationName()!
void setDir(const PIString & d);
//! \~english Returns lifetime for file.
PISystemTime fileSplitTime() const { return split_time; }
//! \~english Set lifetime for file. Each "st" interval new file will be created.
void setFileSplitTime(PISystemTime st) { split_time = st; }
//! \~english Returns timestamp format for line.
PIString timestampFormat() const { return timestamp_format; }
//! \~english Set timestamp format for line. Default is "yyyy-MM-dd hh:mm:ss.zzz".
void setTimestampFormat(const PIString & f) { timestamp_format = f; }
//! \~english Returns line format.
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".
void setLineFormat(const PIString & f);
//! \~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);
//! \~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.
void stop();
private:
EVENT_HANDLER2(void, coutDone, int, id, PIString *, buff);
PICout makePICout(PIObject * context, Level cat);
void enqueue(const PIString & msg, Level cat = Level::Debug);
struct Entry {
Level cat;
PIDateTime time;
PIString msg;
};
PIString entryToString(const Entry & e) const;
void newFile();
void run() override;
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, log_name;
PIQueue<Entry> queue;
PIMap<Level, int> id_by_cat;
Level max_level = Level::Debug;
PIFlags<Output> output = All;
bool color_console = true;
int part_number = -1, cout_id = -1;
};
#endif

View File

@@ -136,7 +136,7 @@ void PISingleApplication::run() {
shm->read(readed.data(), readed.size(), hdr_sz);
PIByteArray msg;
readed >> msg;
if (!msg.isEmpty()) {
if (msg.isNotEmpty()) {
messageReceived(msg);
// piCoutObj << "message" << msg;
}

View File

@@ -1,5 +1,5 @@
/*! \file pisingleapplication.h
* \ingroup System
* \ingroup Application
* \~\brief
* \~english Single-instance application control
* \~russian Контроль одного экземпляра приложения
@@ -30,7 +30,7 @@
class PISharedMemory;
//! \ingroup System
//! \ingroup Application
//! \~\brief
//! \~english Single-instance application control.
//! \~russian Контроль одного экземпляра приложения.

View File

@@ -19,9 +19,10 @@
#include "pisystemmonitor.h"
#include <unistd.h>
#include "pidir.h"
#include "piincludes_p.h"
#include "piliterals.h"
#include "piliterals_string.h"
#include "piprocess.h"
#include "pisysteminfo.h"
#include "pitime_win.h"
@@ -150,7 +151,7 @@ void PISystemMonitor::setStatistic(const PISystemMonitor::ProcessStats & s) {
void PISystemMonitor::stop() {
PIThread::stop();
PIThread::stopAndWait();
#ifdef WINDOWS
if (PRIVATE->hProc != 0) {
CloseHandle(PRIVATE->hProc);

View File

@@ -1,5 +1,5 @@
/*! \file pisystemmonitor.h
* \ingroup System
* \ingroup Application
* \~\brief
* \~english System resources monitoring
* \~russian Мониторинг ресурсов системы
@@ -30,7 +30,7 @@
#include "pithread.h"
//! \ingroup System
//! \ingroup Application
//! \~\brief
//! \~english Process monitoring.
//! \~russian Мониторинг процесса.
@@ -46,7 +46,7 @@ public:
~PISystemMonitor();
#pragma pack(push, 1)
//! \ingroup System
//! \ingroup Application
//! \~\brief
//! \~english Process statistics (fixed-size fields).
//! \~russian Статистика процесса (фиксированные поля).
@@ -116,7 +116,7 @@ public:
float cpu_load_user = 0.f;
};
//! \ingroup System
//! \ingroup Application
//! \~\brief
//! \~english Thread statistics (fixed-size fields).
//! \~russian Статистика потока (фиксированные поля).
@@ -151,7 +151,7 @@ public:
};
#pragma pack(pop)
//! \ingroup System
//! \ingroup Application
//! \~\brief
//! \~english Process statistics.
//! \~russian Статистика процесса.
@@ -189,7 +189,7 @@ public:
PIString data_memsize_readable;
};
//! \ingroup System
//! \ingroup Application
//! \~\brief
//! \~english Thread statistics.
//! \~russian Статистика потока.

View File

@@ -0,0 +1,69 @@
/*! \file piclientserver_client.h
* \ingroup ClientServer
* \~\brief
* \~english
* \~russian
*/
/*
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 <http://www.gnu.org/licenses/>.
*/
#ifndef piclientserver_client_H
#define piclientserver_client_H
#include "piclientserver_client_base.h"
namespace PIClientServer {
// ServerClient
class PIP_CLIENT_SERVER_EXPORT ServerClient: public ClientBase {
friend class Server;
NO_COPY_CLASS(ServerClient);
public:
ServerClient() {}
protected:
virtual void aboutDelete() {}
private:
void createForServer(Server * parent, PIEthernet * tcp_);
};
// Client
class PIP_CLIENT_SERVER_EXPORT Client: public ClientBase {
NO_COPY_CLASS(Client);
public:
Client();
~Client();
void connect(PINetworkAddress addr);
protected:
private:
};
} // namespace PIClientServer
#endif

View File

@@ -0,0 +1,86 @@
/*! \file piclientserver_client_base.h
* \ingroup ClientServer
* \~\brief
* \~english
* \~russian
*/
/*
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 <http://www.gnu.org/licenses/>.
*/
#ifndef piclientserver_client_base_H
#define piclientserver_client_base_H
#include "piclientserver_config.h"
#include "pidiagnostics.h"
#include "pip_client_server_export.h"
#include "pistreampacker.h"
class PIEthernet;
namespace PIClientServer {
class Server;
class PIP_CLIENT_SERVER_EXPORT ClientBase {
friend class Config;
friend class Server;
NO_COPY_CLASS(ClientBase);
public:
ClientBase();
virtual ~ClientBase();
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()); }
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();
bool own_tcp = false;
std::atomic_bool can_write = {true};
PIEthernet * tcp = nullptr;
Config config;
private:
void destroy();
PIStreamPacker stream;
mutable PIMutex write_mutex;
PIDiagnostics * diag = nullptr;
};
} // namespace PIClientServer
#endif

View File

@@ -0,0 +1,60 @@
/*! \file piclientserver_config.h
* \ingroup ClientServer
* \~\brief
* \~english
* \~russian
*/
/*
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 <http://www.gnu.org/licenses/>.
*/
#ifndef piclientserver_config_H
#define piclientserver_config_H
#include "pibytearray.h"
#include "pip_client_server_export.h"
namespace PIClientServer {
class Server;
class Client;
class ClientBase;
class PIP_CLIENT_SERVER_EXPORT Config {
friend class Server;
friend class Client;
public:
void setPacketSign(ushort sign);
void setPacketSize(int bytes);
void enableSymmetricEncryption(const PIByteArray & key);
protected:
void apply(ClientBase * client);
PIByteArray crypt_key;
ushort packet_sign = 0xAFBE;
int packet_size = 1400;
private:
};
} // namespace PIClientServer
#endif

View File

@@ -0,0 +1,82 @@
/*! \file piclientserver_server.h
* \ingroup ClientServer
* \~\brief
* \~english
* \~russian
*/
/*
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 <http://www.gnu.org/licenses/>.
*/
#ifndef piclientserver_server_H
#define piclientserver_server_H
#include "piclientserver_config.h"
#include "pimutex.h"
#include "pinetworkaddress.h"
#include "pip_client_server_export.h"
#include "pithreadnotifier.h"
class PIEthernet;
class PIThread;
namespace PIClientServer {
class ServerClient;
class PIP_CLIENT_SERVER_EXPORT Server {
friend class ServerClient;
NO_COPY_CLASS(Server);
public:
Server();
virtual ~Server();
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;
void forEachClient(std::function<void(ServerClient *)> func);
void setClientFactory(std::function<ServerClient *()> f) { client_factory = f; }
Config & configuration() { return config; }
private:
void stopServer();
void newClient(ServerClient * c);
void clientDisconnected(ServerClient * c);
std::function<ServerClient *()> client_factory;
std::atomic_bool is_closing = {false};
PIEthernet * tcp_server = nullptr;
PIThread * clean_thread = nullptr;
PIThreadNotifier clean_notifier;
Config config;
PIVector<ServerClient *> clients;
mutable PIMutex clients_mutex;
int max_clients = 1000;
};
} // namespace PIClientServer
#endif

View File

@@ -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

View File

@@ -29,9 +29,10 @@
#include "pithread.h"
#include "pitime.h"
#define WAIT_FOR_EXIT \
while (!PIKbdListener::exiting) \
piMSleep(PIP_MIN_MSLEEP * 5); // TODO: rewrite with condvar
#define WAIT_FOR_EXIT \
while (!PIKbdListener::exiting) \
piMSleep(PIP_MIN_MSLEEP * 5); \
if (PIKbdListener::instance()) PIKbdListener::instance()->stopAndWait();
class PIP_EXPORT PIKbdListener: public PIThread {
@@ -179,6 +180,8 @@ public:
void readKeyboard();
void stop();
//! Returns if keyboard listening is active (not running!)
bool isActive() { return is_active; }

View File

@@ -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<T *>(realloc(reinterpret_cast<void *>(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);

View File

@@ -355,7 +355,7 @@ public:
inline PIMap<Key, T> & operator<<(const PIMap<Key, T> & 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);

View File

@@ -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<PIVector<T>> 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<T *>(realloc(reinterpret_cast<void *>(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);

View File

@@ -103,7 +103,7 @@ public:
return *this;
}
inline Row & operator=(const PIVector<T> & other) {
const size_t sz = piMin<size_t>(sz, other.size());
const size_t sz = piMin<size_t>(sz_, other.size());
p_->_copyRaw(p_->data(st_), other.data(), sz);
return *this;
}

View File

@@ -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)
@@ -707,4 +720,20 @@ struct PIP_EXPORT PINonTriviallyCopyable {
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default;
template<typename T>
struct FunctionType {
using Type = void;
};
template<typename Ret, typename Class, typename... Args>
struct FunctionType<Ret (Class::*)(Args...) const> {
using Type = std::function<Ret(Args...)>;
};
template<typename L>
typename FunctionType<decltype(&L::operator())>::Type toStdFunction(L const & func) {
return func;
}
#endif // PIBASE_H

View File

@@ -328,7 +328,7 @@ typedef long long ssize_t;
__PrivateInitializer__(const __PrivateInitializer__ & o); \
~__PrivateInitializer__(); \
__PrivateInitializer__ & operator=(const __PrivateInitializer__ & o); \
__Private__ * p; \
__Private__ * p = nullptr; \
}; \
__PrivateInitializer__ __privateinitializer__;
@@ -343,11 +343,10 @@ typedef long long ssize_t;
p = new c::__Private__(); \
} \
c::__PrivateInitializer__::~__PrivateInitializer__() { \
delete p; \
p = 0; \
piDeleteSafety(p); \
} \
c::__PrivateInitializer__ & c::__PrivateInitializer__::operator=(const c::__PrivateInitializer__ &) { \
if (p) delete p; \
piDeleteSafety(p); \
p = new c::__Private__(); \
return *this; \
}

View File

@@ -52,7 +52,6 @@
#define PICOREMODULE_H
#include "pichunkstream.h"
#include "picli.h"
#include "picollection.h"
#include "pijson.h"
#include "piobject.h"

View File

@@ -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<PICoutControls> cos_;
@@ -158,45 +158,65 @@ 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(PICoutStdStream s) {
switch (s) {
case PICoutStdStream::StdOut: return std::cout;
case PICoutStdStream::StdErr: return std::cerr;
default: break;
}
return std::cout;
}
std::wostream & getStdWStream(PICoutStdStream s) {
switch (s) {
case PICoutStdStream::StdOut: return std::wcout;
case PICoutStdStream::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_) {
((NotifierObject *)Notifier::object())->finished(id_, buffer_);
} else {
getStdStream(stream_).flush();
}
}
PICout & PICout::operator<<(PICoutAction v) {
if (!act_) return *this;
if (!actve_) return *this;
#ifdef WINDOWS
CONSOLE_SCREEN_BUFFER_INFO sbi;
COORD coord;
@@ -204,12 +224,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 +243,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 +254,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 +265,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 +286,7 @@ PICout & PICout::operator<<(PICoutAction v) {
}
break;
case PICoutManipulators::ClearScreen:
if (isOutputDeviceActive(StdOut)) {
if (isOutputDeviceActive(Console)) {
#ifdef WINDOWS
/// TODO : wondows ClearScreen !!!
#else
@@ -282,12 +302,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 +334,10 @@ PICout & PICout::operator<<(PICoutManipulators::PICoutFormat v) {
PICout & PICout::operator<<(PIFlags<PICoutManipulators::PICoutFormat> 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 +363,78 @@ PICout & PICout::operator<<(PIFlags<PICoutManipulators::PICoutFormat> 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<std::codecvt_utf8<char16_t>, char16_t> utf8conv;
getStdStream(s) << utf8conv.to_bytes((char16_t *)&(const_cast<PIString &>(str).front()),
(char16_t *)&(const_cast<PIString &>(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 +447,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 +457,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 +467,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 +492,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 +529,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 +574,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 +589,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 +604,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<std::codecvt_utf8<char16_t>, char16_t> utf8conv;
std::cout << utf8conv.to_bytes((char16_t *)&(const_cast<PIString &>(s).front()),
(char16_t *)&(const_cast<PIString &>(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 +634,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 +654,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 +754,8 @@ PICout PICout::withExternalBuffer(PIString * buffer, int id, PIFlags<PICoutManip
c.id_ = id;
return c;
}
int PICout::registerExternalBufferID() {
return Notifier::instance()->new_id.fetch_add(1);
}

View File

@@ -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, PICoutStdStream::StdOut)
# define piCoutObj \
PICout(piDebug && debug(), PICoutStdStream::StdOut) \
<< (PIStringAscii("[") + className() + (name().isEmpty() ? "]" : PIStringAscii(" \"") + name() + PIStringAscii("\"]")))
# define piCerr PICout(piDebug, PICoutStdStream::StdErr)
# define piCerrObj \
PICout(piDebug && debug(), PICoutStdStream::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,10 +130,20 @@ enum PICoutFormat {
Default /*! \~english Default format \~russian Формат по умолчанию */ = 0x4000000
};
typedef PIFlags<PICoutControl> 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.
@@ -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, 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);
PICout(bool active, PICoutStdStream stream = PICoutStdStream::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<OutputDevice> 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, PICoutStdStream s = PICoutStdStream::StdOut);
//! \~english Returns internal PIString buffer
//! \~russian Возвращает внутренний PIString буфер
@@ -368,19 +380,25 @@ public:
PIFlags<PICoutManipulators::PICoutControl> 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;
PICoutStdStream stream_ = PICoutStdStream::StdOut;
};
#endif // PICOUT_H

View File

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

View File

@@ -33,15 +33,11 @@ PRIVATE_DEFINITION_END(PIIntrospectionServer)
PIIntrospectionServer::PIIntrospectionServer(): PIPeer(genName()) {
PRIVATE->process_info = PIIntrospection::getInfo();
sysmon = 0;
}
PIIntrospectionServer::~PIIntrospectionServer() {
PIPeer::stop();
if (sysmon)
if (sysmon->property("__iserver__").toBool()) delete sysmon;
sysmon = 0;
// stop();
}
@@ -69,6 +65,15 @@ void PIIntrospectionServer::start(const PIString & server_name) {
}
void PIIntrospectionServer::stop() {
PIPeer::stopAndWait();
PIPeer::destroy();
if (sysmon)
if (sysmon->property("__iserver__").toBool()) delete sysmon;
sysmon = nullptr;
}
PIString PIIntrospectionServer::genName() {
randomize();
return "__introspection__server_" + PIString::fromNumber(randomi() % 1000);
@@ -102,7 +107,7 @@ void PIIntrospectionServer::dataReceived(const PIString & from, const PIByteArra
void PIIntrospectionServer::sysmonDeleted() {
PIMutexLocker _ml(sysmon_mutex);
sysmon = 0;
sysmon = nullptr;
}
#endif // PIP_INTROSPECTION

View File

@@ -33,6 +33,11 @@
//! \~russian Запускает сервер интроспекции с именем "name"
# define PIINTROSPECTION_START(name)
//! \ingroup Introspection
//! \~english Stop introspection server
//! \~russian Останавливает сервер интроспекции
# define PIINTROSPECTION_STOP
#else
# if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)
@@ -44,6 +49,7 @@ class PISystemMonitor;
# define PIINTROSPECTION_SERVER (PIIntrospectionServer::instance())
# define PIINTROSPECTION_START(name) PIINTROSPECTION_SERVER->start(#name);
# define PIINTROSPECTION_STOP PIINTROSPECTION_SERVER->stop();
class PIP_EXPORT PIIntrospectionServer: public PIPeer {
PIOBJECT_SUBCLASS(PIIntrospectionServer, PIPeer);
@@ -52,6 +58,7 @@ public:
static PIIntrospectionServer * instance();
void start(const PIString & server_name);
void stop();
private:
PIIntrospectionServer();
@@ -59,17 +66,17 @@ private:
NO_COPY_CLASS(PIIntrospectionServer);
PIString genName();
virtual void dataReceived(const PIString & from, const PIByteArray & data);
void dataReceived(const PIString & from, const PIByteArray & data) override;
EVENT_HANDLER(void, sysmonDeleted);
PRIVATE_DECLARATION(PIP_EXPORT)
PITimer itimer;
PISystemMonitor * sysmon;
PISystemMonitor * sysmon = nullptr;
PIMutex sysmon_mutex;
};
# else
# define PIINTROSPECTION_START(name)
# define PIINTROSPECTION_STOP
# endif
#endif // DOXYGEN

View File

@@ -139,8 +139,10 @@ PIByteArray PIIntrospection::packThreads() {
PIMap<PIThread *, PIIntrospectionThreads::ThreadInfo> & tm(p->threads);
auto it = tm.makeIterator();
while (it.next()) {
it.value().classname = PIStringAscii(it.key()->className());
it.value().name = it.key()->name();
if (it.key()->isPIObject()) {
it.value().classname = PIStringAscii(it.key()->className());
it.value().name = it.key()->name();
}
}
ret << tm.values();
p->mutex.unlock();

View File

@@ -79,7 +79,7 @@ PIBinaryLog::PIBinaryLog() {
setLogDir(PIString());
setFilePrefix(PIString());
setRapidStart(false);
file.setName("__S__PIBinaryLog::file");
file.setName("_S.PIBinLog.file");
}

View File

@@ -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,9 @@ PIDir PIDir::temporary() {
s.prepend(separator);
return PIDir(s);
#else
rc = tmpnam(0);
if (rc == 0) return PIDir();
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)));
#endif

View File

@@ -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();
}
@@ -139,9 +141,11 @@ PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) {
addr_s.set(ip_port);
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();
@@ -161,14 +165,12 @@ 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);
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
@@ -178,9 +180,9 @@ void PIEthernet::construct() {
}
bool PIEthernet::init() {
if (isOpened()) 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;
@@ -203,13 +205,13 @@ bool PIEthernet::init() {
sock_s = sock;
if (sock == -1 || sock_s == -1) {
piCoutObj << "Can`t create socket," << ethErrorString();
return false;
connected_ = connecting_ = opened_ = false;
return;
}
applyParameters();
applyTimeouts();
applyOptInt(IPPROTO_IP, IP_TTL, TTL());
// piCoutObj << "inited" << path();
return true;
}
@@ -303,18 +305,12 @@ bool PIEthernet::closeDevice() {
// piCoutObj << "close";
bool ned = connected_;
connected_ = connecting_ = false;
server_thread_.stop();
PRIVATE->event.interrupt();
if (server_thread_.isRunning()) {
if (!server_thread_.waitForFinish(1_s)) server_thread_.terminate();
}
PRIVATE->event.destroy();
if (sock_s == sock) sock_s = -1;
closeSocket(sock);
closeSocket(sock_s);
stopThreadedListen();
clients_mutex.lock();
piDeleteAllAndClear(clients_);
auto cl = clients_;
clients_.clear();
clients_mutex.unlock();
piDeleteAll(cl);
if (ned) {
// piCoutObj << "Disconnect on close";
disconnected(false);
@@ -528,16 +524,32 @@ bool PIEthernet::listen(const PINetworkAddress & addr, bool threaded) {
return listen(threaded);
}
void PIEthernet::stopThreadedListen() {
server_thread_.stop();
PRIVATE->event.interrupt();
if (server_thread_.isRunning()) {
if (!server_thread_.waitForFinish(1_s)) server_thread_.terminate();
}
PRIVATE->event.destroy();
if (sock_s == sock) sock_s = -1;
closeSocket(sock);
closeSocket(sock_s);
}
PIEthernet * PIEthernet::client(int index) {
PIMutexLocker locker(clients_mutex);
return clients_[index];
}
int PIEthernet::clientsCount() const {
PIMutexLocker locker(clients_mutex);
return clients_.size_s();
}
PIVector<PIEthernet *> PIEthernet::clients() const {
PIMutexLocker locker(clients_mutex);
return clients_;
@@ -777,6 +789,9 @@ ssize_t PIEthernet::writeDevice(const void * data, ssize_t max_size) {
closeSocket(sock);
init();
disconnected(true);
if (params[KeepConnection]) {
connect();
}
}
};
if (!isOptionSet(BlockingWrite)) {
@@ -883,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));

View File

@@ -251,6 +251,8 @@ public:
//! Start listen for incoming TCP connections on address "addr". Use only for TCP_Server
bool listen(const PINetworkAddress & addr, bool threaded = false);
void stopThreadedListen();
PIEthernet * client(int index);
int clientsCount() const;
PIVector<PIEthernet *> clients() const;
@@ -470,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);
@@ -479,8 +481,9 @@ 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;
PIThread server_thread_;

View File

@@ -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;
@@ -528,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) {
@@ -616,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;
}

View File

@@ -243,6 +243,10 @@ public:
//! \~russian Возвращает запущен ли поток чтения
bool isThreadedRead() const;
//! \~english Returns if threaded read is stopping
//! \~russian Возвращает останавливается ли поток чтения
bool isThreadedReadStopping() const { return read_thread.isStopping(); }
//! \~english Start threaded read
//! \~russian Запускает потоковое чтение
void startThreadedRead();
@@ -565,8 +569,6 @@ protected:
static PIIODevice * newDeviceByPrefix(const char * prefix);
bool isThreadedReadStopping() const { return read_thread.isStopping(); }
DeviceMode mode_ = ReadOnly;
DeviceOptions options_;
ReadRetFunc func_read = nullptr;

View File

@@ -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);
@@ -201,33 +201,7 @@ PIPeer::~PIPeer() {
stop();
if (destroyed) return;
destroyed = true;
sync_timer.stopAndWait();
diag_s.stopAndWait();
diag_d.stopAndWait();
PIMutexLocker ml(peers_mutex);
piForeach(PeerInfo & p, peers)
if (p._data) {
p._data->dt_in.stop();
p._data->dt_out.stop();
p._data->t.stopAndWait();
}
destroyEths();
piForeach(PIEthernet * i, eths_mcast) {
if (!i) continue;
i->stopAndWait();
}
piForeach(PIEthernet * i, eths_bcast) {
if (!i) continue;
i->stopAndWait();
}
eth_lo.stopAndWait();
eth_tcp_srv.stopAndWait();
eth_tcp_cli.stopAndWait();
sendSelfRemove();
destroyMBcasts();
eth_send.close();
piForeach(PeerInfo & p, peers)
p.destroy();
destroy();
}
@@ -255,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) {
@@ -275,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";
}
@@ -291,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);
@@ -311,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;
@@ -328,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");
@@ -343,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 *, &eth_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();
@@ -363,6 +337,42 @@ void PIPeer::initMBcasts(PIStringList al) {
}
void PIPeer::destroy() {
sync_timer.stopAndWait();
diag_s.stopAndWait();
diag_d.stopAndWait();
PIMutexLocker ml(peers_mutex);
for (auto & p: peers)
if (p._data) {
p._data->dt_in.stop();
p._data->dt_out.stop();
p._data->t.stopAndWait();
}
destroyEths();
for (auto * i: eths_mcast) {
if (!i) continue;
i->stopAndWait();
}
for (auto * i: eths_bcast) {
if (!i) continue;
i->stopAndWait();
}
eth_lo.stopAndWait();
eth_tcp_srv.stopAndWait();
eth_tcp_cli.stopAndWait();
sendSelfRemove();
eth_lo.close();
eth_tcp_srv.close();
eth_tcp_cli.close();
destroyMBcasts();
eth_send.close();
for (auto & p: peers)
p.destroy();
peers.clear();
destroyed = true;
}
void PIPeer::destroyEths() {
for (auto * i: eths_traffic) {
if (!i) continue;
@@ -930,6 +940,7 @@ ssize_t PIPeer::bytesAvailable() const {
ssize_t PIPeer::readDevice(void * read_to, ssize_t max_size) {
iterrupted = false;
read_buffer_mutex.lock();
bool empty = read_buffer.isEmpty();
read_buffer_mutex.unlock();
@@ -937,6 +948,9 @@ ssize_t PIPeer::readDevice(void * read_to, ssize_t max_size) {
read_buffer_mutex.lock();
empty = read_buffer.isEmpty();
read_buffer_mutex.unlock();
if (iterrupted) {
return 0;
}
piMSleep(10);
}
read_buffer_mutex.lock();
@@ -945,6 +959,9 @@ ssize_t PIPeer::readDevice(void * read_to, ssize_t max_size) {
read_buffer_mutex.unlock();
ssize_t sz = piMini(ba.size_s(), max_size);
memcpy(read_to, ba.data(), sz);
if (iterrupted) {
return 0;
}
return sz;
}
read_buffer_mutex.unlock();
@@ -964,8 +981,13 @@ ssize_t PIPeer::writeDevice(const void * data, ssize_t size) {
}
void PIPeer::interrupt() {
iterrupted = true;
}
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();

View File

@@ -171,6 +171,8 @@ protected:
EVENT_HANDLER2(bool, dataRead, const uchar *, readed, ssize_t, size);
EVENT_HANDLER2(bool, mbcastRead, const uchar *, readed, ssize_t, size);
void destroy();
private:
EVENT_HANDLER1(void, timerEvent, int, delim);
EVENT_HANDLER2(bool, sendInternal, const PIString &, to, const PIByteArray &, data);
@@ -212,6 +214,7 @@ private:
void configureFromVariantDevice(const PIPropertyStorage & d) override;
ssize_t readDevice(void * read_to, ssize_t max_size) override;
ssize_t writeDevice(const void * data, ssize_t size) override;
void interrupt() override;
DeviceInfoFlags deviceInfoFlags() const override { return PIIODevice::Reliable; }
PeerInfo * quickestPeer(const PIString & to);
@@ -243,6 +246,7 @@ private:
mutable PIMutex read_buffer_mutex;
PIQueue<PIByteArray> read_buffer;
int read_buffer_size;
std::atomic_bool iterrupted = {false};
PIMutex mc_mutex, eth_mutex, peers_mutex, send_mutex, send_mc_mutex;
};

View File

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

View File

@@ -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<void(SomeStruct)>([this](SomeStruct s){})) \endcode
* \code assign<SomeStruct>(1, [this](SomeStruct s){}) \endcode
* \code assign(1, [this](const SomeStruct & s){}) \endcode
*
*
* \section PIParseHelper_examples Examples
@@ -87,56 +72,21 @@
template<typename Key>
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<typename T, typename Ret>
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<typename Ret>
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<void()> 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<typename T>
void assign(Key key, std::function<void(T)> func) {
if (!parent) return;
auto lf = [func](PIObject *, PIByteArray data) {
void assign(Key key, std::function<void(const T &)> 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<void()> func) {
if (!parent) return;
auto lf = [func](PIObject *, PIByteArray) { func(); };
//! \brief Assign key \"key\" to member function of object \"obj\" without arguments
template<typename O>
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<typename T, typename O>
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<typename L>
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<Key, PIVector<std::function<void(PIObject *, PIByteArray)>>> functions;
PIObject * parent;
PIMap<Key, PIVector<std::function<void(PIByteArray)>>> functions;
};

View File

@@ -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

View File

@@ -20,6 +20,7 @@
#ifndef PIP_H
#define PIP_H
#include "piapplicationmodule.h"
#include "picloudmodule.h"
#include "piconsolemodule.h"
#include "picontainersmodule.h"

View File

@@ -70,7 +70,7 @@ public:
bool binaryStreamAppend(const void * d, size_t s) {
if (!static_cast<P *>(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<P *>(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<typename P,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0>
inline PIBinaryStreamTrivialRef<P> operator>>(PIBinaryStream<P> & 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<P> & operator>>(PIBinaryStream<P> & s, PIVector<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIVector<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIVector<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIDeque<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIDeque<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIDeque<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIVector2D<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIVector2D<T> & 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<typename P, typename T, typename std::enable_if<!std::is_trivially_copy
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIVector<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIVector<T> & 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<typename P, typename T, typename std::enable_if<!std::is_trivially_copy
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIDeque<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIDeque<T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIVector2D<T> & 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<typename P, typename Key, typename T>
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIMap<Key, T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIMap<Key, T> & 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<P> & operator>>(PIBinaryStream<P> & s, PIMap<Key, T> & 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;
}

View File

@@ -31,21 +31,6 @@
namespace PIStateMachineHelpers {
template<typename T>
struct FunctionType {
using Type = void;
};
template<typename Ret, typename Class, typename... Args>
struct FunctionType<Ret (Class::*)(Args...) const> {
using Type = std::function<Ret(Args...)>;
};
template<typename L>
typename FunctionType<decltype(&L::operator())>::Type toStdFunction(L const & func) {
return func;
}
class FunctionBase {
public:
virtual ~FunctionBase() {}

View File

@@ -56,7 +56,7 @@ public:
template<typename L>
PITransitionBase * addGuard(L f) {
return addGuard(PIStateMachineHelpers::toStdFunction(f));
return addGuard(toStdFunction(f));
}
template<typename... Args>

View File

@@ -60,6 +60,33 @@ void PISignals::grabSignals(PIFlags<PISignals::Signal> signals_) {
}
void PISignals::releaseSignals(PIFlags<Signal> 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));
}

View File

@@ -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<PISignals::Signal> signals_);
static void releaseSignals(PIFlags<PISignals::Signal> signals_);
static void raiseSignal(PISignals::Signal signal);
private:

View File

@@ -54,10 +54,10 @@
#define PISYSTEMMODULE_H
#include "pilibrary.h"
#include "piplugin.h"
#include "piprocess.h"
#include "pisignals.h"
#include "pisingleapplication.h"
#include "pisysteminfo.h"
#include "pisystemmonitor.h"
#include "pisystemtests.h"
#endif // PISYSTEMMODULE_H

View File

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

View File

@@ -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".

View File

@@ -568,6 +568,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!";
#ifdef FREERTOS
// void * ret(0);
// PICout(PICoutManipulators::DefaultControls) << "~PIThread" << PRIVATE->thread;
@@ -587,6 +588,8 @@ PIThread::~PIThread() {
CloseHandle(PRIVATE->thread);
# endif
#endif
UNREGISTER_THREAD(this);
PIINTROSPECTION_THREAD_STOP(this);
terminating = running_ = false;
}
@@ -916,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";
@@ -924,14 +931,13 @@ 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;
UNREGISTER_THREAD(this);
PIINTROSPECTION_THREAD_STOP(this);
#if defined(WINDOWS)
ec.callAndCancel();
# ifdef CC_GCC
_endthreadex(0);
# else
@@ -940,7 +946,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

View File

@@ -72,9 +72,9 @@
//! w->startOnce();
//!
//! piMSleep(500);
//! notifier.notifyOnce(); // notify one of them after 500 ms
//! notifier.notify(); // notify one of them after 500 ms
//! piMSleep(500);
//! notifier.notifyOnce(); // notify one of them after 1000 ms
//! notifier.notify(); // notify one of them after 1000 ms
//!
//! for (auto * w: workers)
//! w->waitForFinish();
@@ -93,17 +93,17 @@ PIThreadNotifier::PIThreadNotifier(): cnt(0) {}
//! \~\details
//! \~english
//! If \a notifyOnce() has been called before, then returns immediately.\n
//! If \a notifyOnce() has been called "n" times, then returns immediately "n" times,
//! If \a notify() has been called before, then returns immediately.\n
//! If \a notify() has been called "n" times, then returns immediately "n" times,
//! but only if wait in one thread.\n
//! If many threads waiting, then if \a notifyOnce() has been called "n" times,
//! If many threads waiting, then if \a notify() has been called "n" times,
//! all threads total returns "n" times in undefined sequence.
//!
//! \~russian
//! Если ранее был вызван \a notifyOnce(), то возвращает управление немедленно.\n
//! Если ранее был вызван \a notifyOnce() "n" раз, то возвращает управление немедленно "n" раз,
//! Если ранее был вызван \a notify(), то возвращает управление немедленно.\n
//! Если ранее был вызван \a notify() "n" раз, то возвращает управление немедленно "n" раз,
//! но только если ожидать одним потоком.\n
//! Если ожидают несколько потоков, и \a notifyOnce() был вызван "n" раз,
//! Если ожидают несколько потоков, и \a notify() был вызван "n" раз,
//! то все потоки суммарно вернут управление "n" раз в неопределенной последовательности.
//!
void PIThreadNotifier::wait() {

View File

@@ -33,8 +33,8 @@ class PIP_EXPORT PIThreadNotifier {
public:
PIThreadNotifier();
//! \~english Start waiting, return if other thread call \a notifyOnce()
//! \~russian Начать ожидание, продолжает когда другой поток вызовет \a notifyOnce()
//! \~english Start waiting, return if other thread call \a notify()
//! \~russian Начать ожидание, продолжает когда другой поток вызовет \a notify()
void wait();
//! \~english Notify one waiting thread, which waiting on \a wait() function

View File

@@ -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());
}

View File

@@ -117,6 +117,11 @@ void PISystemTime::toTimespec(void * ts) {
}
PISystemTime::Frequency PISystemTime::toFrequency() {
return PISystemTime::Frequency::fromSystemTime(*this);
}
PIString PISystemTime::toString() const {
static const PISystemTime is_abs_thr = 3650._d; // 10 years
if ((*this) >= is_abs_thr) return PIDateTime::fromSystemTime((*this)).toString("yyyy-MM-dd hh:mm:ss.zzz");

View File

@@ -260,6 +260,10 @@ public:
//! \~russian На *nix системах присваивает время к timespec структуре
void toTimespec(void * ts);
//! \~english Returns \a Frequency that corresponds this time interval
//! \~russian Возвращает \a Frequency соответствующую этому временному интервалу
PISystemTime::Frequency toFrequency();
//! \~english Returns "yyyy-MM-dd hh:mm:ss.zzz" for absolute time and "<V> <d|h|m|s|ms|us|ns> ..." for relative
//! \~russian Возвращает "yyyy-MM-dd hh:mm:ss.zzz" для абсолютного времени и "<V> <d|h|m|s|ms|us|ns> ..." для относительного
PIString toString() const;

168
main.cpp
View File

@@ -1,7 +1,11 @@
#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 "pip.h"
#include "pivaluetree_conversions.h"
@@ -16,22 +20,162 @@ enum MyEvent {
meIntString,
};
PITimeMeasurer tm;
std::atomic_int cnt = {0};
void tfunc(int delim) {
// piCout << "tick with delimiter" << delim;
++cnt;
};
void tfunc4(int delim) {
piCout << "tick4 with delimiter" << delim;
PIKbdListener kbd;
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 <iostream>
int main(int argc, char * argv[]) {
uint v = 0xaabbccdd;
piCout << Hex << v << piChangedEndian(v);
piChangeEndianBinary(&v, sizeof(v));
piCout << Hex << v << piChangedEndian(v);
/*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);
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();
return 0;
/*PIPeer p("123");
piCout << "start ...";
p.start();
piCout << "start ok";
piSleep(1.);
piCout << "stop ...";
p.stopAndWait();
piCout << "stop ok";
piSleep(1.);
piCout << "exit";
return 0;*/
if (argc > 1) {
PIINTROSPECTION_START(server);
} else {
PIINTROSPECTION_START(client);
}
kbd.enableExitCapture();
PIClientServer::Server * s = nullptr;
PIThread * s_thread = new PIThread();
PIVector<PIClientServer::Client *> 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<PIPackedTCP>(); // new PIPackedTCP(PIPackedTCP::Server, {"0.0.0.0:8000"});

View File

@@ -34,3 +34,4 @@ endmacro()
pip_test(math)
pip_test(core)
pip_test(piobject)
pip_test(client_server pip_client_server)

View File

@@ -0,0 +1,222 @@
#include "pitime.h"
#include "test_client_helper.h"
#include "gtest/gtest.h"
template<bool WithConnectPing = false, bool WithPong = false, ullong WriteSize = 64_KiB>
PIClientServer::Server * createServer() {
auto s = new PIClientServer::Server();
s->setClientFactory([] { return new TestServerClient<WithConnectPing, WithPong, WriteSize>(); });
s->listenAll(12345);
return s;
}
bool waitLoop(std::function<bool()> exit_loop, const PISystemTime & timeout) {
PITimeMeasurer tm;
while (tm.elapsed() < timeout) {
if (exit_loop()) {
return true;
}
piMinSleep();
}
return false;
}
template<typename Client>
Client * createAndConnectClient() {
auto c = new Client();
c->connect(PINetworkAddress::resolve("127.0.0.1", 12345));
return c;
}
TEST(ClientServer, OneClient) {
auto const loop_timeout = 1000_ms;
auto s = createServer<false, true>();
auto c = createAndConnectClient<TestClient<false, false>>();
waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout);
EXPECT_EQ(1, s->clientsCount());
c->ping();
waitLoop([c]() { return c->pongCnt() > 0; }, loop_timeout);
EXPECT_EQ(1, c->pongCnt());
s->forEachClient([](PIClientServer::ServerClient * sc) { static_cast<TestServerClient<> *>(sc)->ping(); });
waitLoop([c]() { return c->pongCnt() > 1; }, loop_timeout);
EXPECT_EQ(2, c->pongCnt());
EXPECT_TRUE(c->getTCP()->isConnected());
delete c;
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
EXPECT_EQ(0, s->clientsCount());
delete s;
}
class ClientSendThread {
using ClientType = TestClient<false, false, 10_KiB>;
public:
ClientSendThread() {
client = createAndConnectClient<ClientType>();
sendThread.setName("clSend");
}
~ClientSendThread() {
sendThread.stopAndWait();
delete client;
}
void startSend() {
sendThread.start([this] { client->ping(); }, 100._Hz);
}
void sendOnce() { client->ping(); }
ClientType * client = nullptr;
PIThread sendThread;
};
int getServerPongs(PIClientServer::Server * s) {
int pongs = 0;
s->forEachClient([&pongs](PIClientServer::ServerClient * sc) {
const auto c = static_cast<TestServerClient<> *>(sc);
pongs += c->pongCnt();
});
return pongs;
}
int getClientsPongs(const PIVector<ClientSendThread *> & clients) {
int pongs = 0;
clients.forEach([&pongs](ClientSendThread * c) { pongs += c->client->pongCnt(); });
return pongs;
}
int getClientsPings(const PIVector<ClientSendThread *> & clients) {
int pings = 0;
clients.forEach([&pings](ClientSendThread * c) { pings += c->client->pingCnt(); });
return pings;
}
TEST(ClientServer, ManyClients) {
auto const loop_timeout = 1000_ms;
constexpr int clients_count = 20;
PIVector<ClientSendThread *> clients;
auto s = createServer<false, false, 100_KiB>();
piForTimes(clients_count) {
clients.append(new ClientSendThread());
}
EXPECT_EQ(clients_count, clients.size_s());
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<TestServerClient<> *>(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;
constexpr int clients_count = 20;
PIVector<ClientSendThread *> clients;
PIMutex clients_mutex;
auto s = createServer<true, true, 10_KiB>();
const auto spawnClient = [&clients, &clients_mutex]() {
// if (clients.size() > 100) return;
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.setName("spawn");
deleteThread.setName("delete");
spawnThread.start(
[&spawnClient]() {
const int new_cnt = 7;
piForTimes(new_cnt) {
spawnClient();
}
piCout << "+++++++";
},
12_Hz);
deleteThread.start(
[&clients, &clients_mutex]() {
const int rm_cnt = 8;
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();
}
}
piCout << "----------";
},
13_Hz);
(2_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;
}

View File

@@ -0,0 +1,89 @@
#ifndef TEST_CLIENT_HELPER_H
#define TEST_CLIENT_HELPER_H
#include "piclientserver_client.h"
#include "piclientserver_server.h"
#include "piethernet.h"
#include "piliterals.h"
#include "pithread.h"
template<ullong WriteSize = 64_KiB, bool WithPong = false>
class TestClientBase {
public:
int pongCnt() const { return pong_cnt; }
int pingCnt() const { return ping_cnt; }
void ping() {
const auto data = PIByteArray(WriteSize);
writeInternal(data);
ping_cnt++;
}
protected:
virtual void writeInternal(const PIByteArray & ba) = 0;
void readInternal(const PIByteArray & ba) {
last_read_size = ba.size();
pong_cnt++;
if (WithPong) ping();
}
private:
int pong_cnt = 0;
int ping_cnt = 0;
ullong last_read_size = 0;
};
template<bool WithConnectPing = false, bool WithPong = false, ullong WriteSize = 64_KiB>
class TestServerClient
: public PIClientServer::ServerClient
, public TestClientBase<WriteSize, WithPong> {
using Base = TestClientBase<WriteSize, WithPong>;
public:
~TestServerClient() {
close();
stopAndWait();
}
private:
void readed(PIByteArray data) override { Base::readInternal(data); }
void connected() override {
if (WithConnectPing) {
Base::ping();
}
}
void writeInternal(const PIByteArray & ba) override { write(ba); }
};
template<bool WithConnectPing = false, bool WithPong = false, ullong WriteSize = 64_KiB>
class TestClient
: public PIClientServer::Client
, public TestClientBase<WriteSize, WithPong> {
using Base = TestClientBase<WriteSize, WithPong>;
public:
~TestClient() {
close();
stopAndWait();
}
private:
void readed(PIByteArray data) override { Base::readInternal(data); }
void connected() override {
if (WithConnectPing) {
Base::ping();
}
}
void writeInternal(const PIByteArray & ba) override { write(ba); }
};
#endif // TEST_CLIENT_HELPER_H

View File

@@ -31,23 +31,23 @@ private:
TEST(PiobjectConnections, CONNECT0) {
Object a;
Object b;
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
CONNECT0(void, &a, event_test, &b, handler_test);
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
// piCout << "================";
}
@@ -57,105 +57,105 @@ TEST(PiobjectConnections, CONNECT0_DISCONNECT) {
Object b;
a.setName("A");
b.setName("B");
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
CONNECT0(void, &a, event_test, &b, handler_test);
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
PIObject::piDisconnect(&a, "event_test");
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
// piCout << "================";
}
TEST(PiobjectConnections, CONNECTU) {
Object a;
Object b;
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
CONNECTU(&a, event_test, &b, handler_test);
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
// piCout << "================";
}
TEST(PiobjectConnections, CONNECTU_DISCONNECT) {
Object a;
Object b;
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
CONNECTU(&a, event_test, &b, handler_test);
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 0);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 0);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
PIObject::piDisconnect(&a, "event_test");
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
a.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
b.test();
ASSERT_EQ(a.getX(), 0);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 0);
EXPECT_EQ(b.getX(), 1);
a.handler_val(99);
ASSERT_EQ(a.getX(), 99);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 99);
EXPECT_EQ(b.getX(), 1);
CONNECTU(&a, event_val, &b, handler_val);
ASSERT_EQ(a.getX(), 99);
ASSERT_EQ(b.getX(), 1);
EXPECT_EQ(a.getX(), 99);
EXPECT_EQ(b.getX(), 1);
a.test_val();
ASSERT_EQ(a.getX(), 99);
ASSERT_EQ(b.getX(), 99);
EXPECT_EQ(a.getX(), 99);
EXPECT_EQ(b.getX(), 99);
a.handler_val(-1);
ASSERT_EQ(a.getX(), -1);
ASSERT_EQ(b.getX(), 99);
EXPECT_EQ(a.getX(), -1);
EXPECT_EQ(b.getX(), 99);
PIObject::piDisconnect(&a, "event_val", &b);
ASSERT_EQ(a.getX(), -1);
ASSERT_EQ(b.getX(), 99);
EXPECT_EQ(a.getX(), -1);
EXPECT_EQ(b.getX(), 99);
a.test_val();
ASSERT_EQ(a.getX(), -1);
ASSERT_EQ(b.getX(), 99);
EXPECT_EQ(a.getX(), -1);
EXPECT_EQ(b.getX(), 99);
// piCout << "================";
}

View File

@@ -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)) {

View File

@@ -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<CloudServer *> rmrf_servers;
PITimer timeout_timer;
PIMutex map_mutex;
std::atomic_int wd_cnt = {0};
uint client_gid;
uint max_connections;
};

View File

@@ -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 <ip>] [-p <port>]\"" << NewLine;
piCout << Green << Bold << "Usage:" << Default << "\"picloud [-hswv] [-i <ip>] [-p <port>]\"" << 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);

View File

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

View File

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