diff --git a/CMakeLists.txt b/CMakeLists.txt index 61a32303..b05912a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,7 +480,7 @@ if (NOT CROSSTOOLS) #target_link_libraries(pip_plugin pip) add_executable(pip_test "main.cpp") - target_link_libraries(pip_test pip) + target_link_libraries(pip_test pip pip_io_utils) if(sodium_FOUND) add_executable(pip_cloud_test "main_picloud_test.cpp") target_link_libraries(pip_cloud_test pip_cloud) diff --git a/libs/io_utils/pipackedtcp.cpp b/libs/io_utils/pipackedtcp.cpp new file mode 100644 index 00000000..d9efac7b --- /dev/null +++ b/libs/io_utils/pipackedtcp.cpp @@ -0,0 +1,199 @@ +/* + PIP - Platform Independent Primitives + PIPackedTCP + 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 . +*/ + +#include "pipackedtcp.h" + +#include "piethernet.h" +#include "piliterals.h" + + +/** \class PIPackedTCP pipackedtcp.h + * \brief + * TCP packed channel + * + * \details + * \section PITCP_sec0 Synopsis + * %PIEthernet designed to work with IPv4 network via two protocols: + * UDP and TCP. This class allow you send and receive packets to/from + * another computer through network. Also it supports broadcast and + * multicast extensions. + * + * */ + + +PIPackedTCP::PIPackedTCP(Role role, const PINetworkAddress & addr): m_role(role) { + setMode(PIIODevice::ReadWrite); + packer.setCryptEnabled(false); + CONNECTL(&packer, packetReceiveEvent, [this](PIByteArray & data) { + PIMutexLocker ml(rec_mutex); + rec_queue.enqueue(data); + }); + init(); + setAddress(addr); +} + + +PIPackedTCP::~PIPackedTCP() { + stopAndWait(); + if (client) client->stopAndWait(); + if (eth) eth->stopAndWait(); + piDeleteSafety(eth); +} + + +void PIPackedTCP::setAddress(const PINetworkAddress & addr) { + m_addr = addr; + setPath(m_addr.toString()); +} + + +bool PIPackedTCP::isConnected() const { + if (m_role == Client) { + return eth->isConnected(); + } else { + if (client) return client->isConnected(); + } + return false; +} + + +bool PIPackedTCP::isConnecting() const { + if (m_role == Client) { + return eth->isConnecting(); + } else { + if (client) return client->isConnecting(); + } + return false; +} + + +void PIPackedTCP::init() { + if (client) client->stopAndWait(); + if (eth) eth->stopAndWait(); + piDeleteSafety(eth); + eth = new PIEthernet(m_role == Client ? PIEthernet::TCP_Client : PIEthernet::TCP_Server); + if (m_role == Client) { + eth->setReopenTimeout(100_ms); + packer.assignDevice(eth); + CONNECTL(eth, connected, [this]() { + packer.clear(); + connected(); + }); + CONNECTL(eth, disconnected, [this](bool) { + packer.clear(); + eth->connect(path(), true); + disconnected(); + }); + } else { + CONNECTL(eth, newConnection, [this](PIEthernet * c) { + if (client) client->stopAndWait(); + piDeleteSafety(client); + client = c; + // piCout << "Server connected" << client; + packer.assignDevice(client); + CONNECTL(client, disconnected, [this](bool) { + packer.assignDevice(nullptr); + packer.clear(); + disconnected(); + }); + client->startThreadedRead(); + connected(); + }); + } +} + + +PIIODevice::DeviceInfoFlags PIPackedTCP::deviceInfoFlags() const { + return PIIODevice::Reliable; +} + + +PIString PIPackedTCP::constructFullPathDevice() const { + return PIString(m_role == Client ? "client" : "server") + ":" + path(); +} + + +void PIPackedTCP::configureFromFullPathDevice(const PIString & full_path) { + PIStringList pl = full_path.split(":"); + if (pl.size() >= 1) { + PIString p = pl[0].toLowerCase().left(1); + if (p == "c") m_role = Client; + if (p == "s") m_role = Server; + init(); + } + PINetworkAddress addr("0.0.0.0", 13362); + if (pl.size() >= 2) { + if (pl[1].isNotEmpty()) addr.setIP(pl[1]); + } + if (pl.size() >= 3) { + if (pl[2].isNotEmpty()) addr.setPort(pl[2].toInt()); + } + setAddress(addr); +} + + +ssize_t PIPackedTCP::readDevice(void * read_to, ssize_t max_size) { + PIMutexLocker ml(rec_mutex); + if (rec_queue.isNotEmpty()) { + auto d = rec_queue.dequeue(); + auto sz = piMin(max_size, d.size_s()); + if (read_to) memcpy(read_to, d.data(), sz); + return sz; + } + return 0; +} + + +ssize_t PIPackedTCP::writeDevice(const void * data, ssize_t max_size) { + if (!isConnected()) return 0; + packer.send(PIByteArray(data, max_size)); + // piCout << m_role << "write" << eth; + return max_size; + /*if (m_role == Client) { + return eth->write(data, max_size); + } else { + if (client) return client->write(data, max_size); + }*/ +} + + +bool PIPackedTCP::openDevice() { + if (m_role == Client) { + if (eth->isConnected()) return true; + if (eth->isConnecting()) return false; + packer.clear(); + bool ret = eth->connect(path(), false); + eth->startThreadedRead(); + return ret; + } else { + return eth->listen(path(), false); + } + return false; +} + + +bool PIPackedTCP::closeDevice() { + if (client) { + client->close(); + client->stopAndWait(); + piDeleteSafety(client); + packer.assignDevice(nullptr); + } + return eth->close(); +} diff --git a/libs/main/io_devices/piiodevice.cpp b/libs/main/io_devices/piiodevice.cpp index 7c06b4a6..654b1a10 100644 --- a/libs/main/io_devices/piiodevice.cpp +++ b/libs/main/io_devices/piiodevice.cpp @@ -569,15 +569,14 @@ PIStringList PIIODevice::availableClasses() { void PIIODevice::registerDevice(PIConstChars prefix, PIConstChars classname, PIIODevice * (*fabric)()) { + // printf("registerDevice %s %s %d\n", prefix.data(), classname.data(), fabrics().size()); if (prefix.isEmpty()) return; - // printf("registerDevice %s %d %d\n", prefix, p.isEmpty(), fabrics().size()); - if (!fabrics().contains(prefix)) { - FabricInfo fi; - fi.prefix = prefix; - fi.classname = classname; - fi.fabricator = fabric; - fabrics()[prefix] = fi; - } + if (fabrics().contains(prefix)) return; + FabricInfo fi; + fi.prefix = prefix; + fi.classname = classname; + fi.fabricator = fabric; + fabrics()[prefix] = fi; } diff --git a/libs/main/io_utils/piioutilsmodule.h b/libs/main/io_utils/piioutilsmodule.h index 7baee2d3..462e09e8 100644 --- a/libs/main/io_utils/piioutilsmodule.h +++ b/libs/main/io_utils/piioutilsmodule.h @@ -57,6 +57,7 @@ #include "pidatatransfer.h" #include "pidiagnostics.h" #include "pifiletransfer.h" +#include "pipackedtcp.h" #include "pipacketextractor.h" #include "piparsehelper.h" diff --git a/libs/main/io_utils/pipackedtcp.h b/libs/main/io_utils/pipackedtcp.h new file mode 100644 index 00000000..d5d66d3a --- /dev/null +++ b/libs/main/io_utils/pipackedtcp.h @@ -0,0 +1,90 @@ +/*! \file pipackedtcp.h + * \ingroup IO-Utils + * \~\brief + * \~english Ethernet device + * \~russian Устройство Ethernet + */ +/* + PIP - Platform Independent Primitives + Ethernet, UDP/TCP Broadcast/Multicast + 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 . +*/ + +#ifndef pipackedtcp_H +#define pipackedtcp_H + +#include "piiodevice.h" +#include "pinetworkaddress.h" +#include "pip_io_utils_export.h" +#include "pistreampacker.h" + +class PIEthernet; + + +class PIP_IO_UTILS_EXPORT PIPackedTCP: public PIIODevice { + PIIODEVICE(PIPackedTCP, "ptcp"); + +public: + //! \brief Role of %PIPackedTCP + enum Role { + Client /** TCP client */, + Server /** TCP server for one client */ + }; + + //! Contructs %PIPackedTCP with "role" and "addr" address + explicit PIPackedTCP(Role role = Client, const PINetworkAddress & addr = {}); + + virtual ~PIPackedTCP(); + + //! Set server address for Server role or connect address for Client + void setAddress(const PINetworkAddress & addr); + + bool isConnected() const; + bool isConnecting() const; + + //! Returns read address in format "i.i.i.i:p" + PINetworkAddress address() const { return m_addr; } + + //! Returns %PIEthernet type + Role role() const { return m_role; } + + EVENT0(connected); + EVENT0(disconnected); + +protected: + void init(); + + DeviceInfoFlags deviceInfoFlags() const override; + PIString constructFullPathDevice() const override; + void configureFromFullPathDevice(const PIString & full_path) override; + ssize_t readDevice(void * read_to, ssize_t max_size) override; + ssize_t writeDevice(const void * data, ssize_t max_size) override; + bool openDevice() override; + bool closeDevice() override; + + mutable PINetworkAddress m_addr; + Role m_role = Client; + PIEthernet *eth = nullptr, *client = nullptr; + PIStreamPacker packer; + PIMutex rec_mutex; + PIQueue rec_queue; + +private: +}; + +REGISTER_DEVICE(PIPackedTCP) + +#endif diff --git a/main.cpp b/main.cpp index 8c575fc2..9c5d61ba 100644 --- a/main.cpp +++ b/main.cpp @@ -2,7 +2,6 @@ #include "picodeparser.h" #include "piiostream.h" #include "pijson.h" -#include "piliterals_time.h" #include "pimathbase.h" #include "pip.h" #include "pivaluetree_conversions.h" @@ -17,7 +16,83 @@ 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; +}; + + int main(int argc, char * argv[]) { + PIPackedTCP * tcp_s = + PIIODevice::createFromFullPath("ptcp://s::8000")->cast(); // new PIPackedTCP(PIPackedTCP::Server, {"0.0.0.0:8000"}); + PIPackedTCP * tcp_c = PIIODevice::createFromFullPath("ptcp://c:127.0.0.1:8000") + ->cast(); // new PIPackedTCP(PIPackedTCP::Client, {"127.0.0.1:8000"}); + piCout << tcp_s << tcp_c; + // CONNECTL(&tcp_s, opened, []() { piCout << "Srv opened"; }); + // CONNECTL(&tcp_c, opened, []() { piCout << "Cli opened"; }); + // CONNECTL(&tcp_s, closed, []() { piCout << "Srv closed"; }); + // CONNECTL(&tcp_c, closed, []() { piCout << "Cli closed"; }); + CONNECTL(tcp_s, connected, []() { piCout << "Srv conn"; }); + CONNECTL(tcp_c, connected, []() { piCout << "Cli conn"; }); + CONNECTL(tcp_s, disconnected, []() { piCout << "Srv disconn"; }); + CONNECTL(tcp_c, disconnected, []() { piCout << "Cli disconn"; }); + CONNECTL(tcp_s, threadedReadEvent, [](const uchar * readed, ssize_t size) { + PIByteArray d(readed, size); + piCout << "Srv readed" << d; + }); + CONNECTL(tcp_c, threadedReadEvent, [](const uchar * readed, ssize_t size) { + PIByteArray d(readed, size); + piCout << "Cli readed" << d; + }); + // tcp_s->open(); + // tcp_c->open(); + tcp_s->startThreadedRead(); + tcp_c->startThreadedRead(); + piForTimes(5) { + piCout << "\n1"; + piMSleep(200); + piCout << "2"; + // tcp_c->close(); + piCout << "2+"; + // tcp_s->close(); + piCout << "3"; + // tcp_c->open(); + piCout << "3+"; + // tcp_s->open(); + piCout << "4"; + piMSleep(500); + // piCout << "5"; + tcp_c->write("1234567890"_a.toByteArray()); + // piCout << "6"; + tcp_s->write("1234567890"_a.toByteArray()); + // piCout << "7"; + piMSleep(200); + } + // piCout << &tcp_s; + // piCout << tcp_s->path(); + // piCout << tcp_s->constructFullPath(); + delete tcp_s; + delete tcp_c; + return 0; + /*PITimer timer(tfunc); + // timer.addDelimiter(2); + timer.addDelimiter(40, tfunc4); + + tm.reset(); + timer.start(0.5_Hz); + piMSleep(2200); + piCout << tm.elapsed_m() << cnt; + + timer.stop(); + timer.waitForFinish(); + return 0;*/ + + bool posted; PIStateMachine * root = new PIStateMachine("Machine"); root->addOnFinish([] { piCout << "finish"; }); @@ -49,9 +124,10 @@ int main(int argc, char * argv[]) { s2->setInitialState(s21); s21->setInitialState(s213); - s213->addTransition(s13, meVoid)->addAction([] { piCout << "action transition s21 -> s22"; }); - s3->addTransition(s212, meVoid)->addAction([] { piCout << "action transition s1 -> s213"; }); - // s2->addTransition(s3, meInt)->addGuard([](int i) { return i == 1; }); + // s213->addTransition(s13, meVoid)->addAction([] { piCout << "action transition s21 -> s22"; }); + // s3->addTransition(s212, meVoid)->addAction([] { piCout << "action transition s1 -> s213"; }); + s2->addTransition(s3, meVoid); + s2->addTimeoutTransition(s3, .5_s); // s3->addTransition(s1, meIntString)->addGuard([](int i, PIString str) { return i == 2 && str == "hello"; }); root->start(); @@ -59,10 +135,11 @@ int main(int argc, char * argv[]) { piCout << "active atomics" << root->activeAtomics(); root->print(); - piCout << "\npost event"; - posted = root->postEvent(meVoid); - piCout << "posted" << posted << "\n"; - piCout << "active atomics" << root->activeAtomics(); + // piCout << "\npost event"; + // posted = root->postEvent(meVoid); + // piCout << "posted" << posted << "\n"; + // piCout << "active atomics" << root->activeAtomics(); + piSleep(1.); root->print(); // piCout << "\npost event"; diff --git a/utils/crypt_tool/main.cpp b/utils/crypt_tool/main.cpp index 8d4ec3b7..57fdb8b0 100644 --- a/utils/crypt_tool/main.cpp +++ b/utils/crypt_tool/main.cpp @@ -17,8 +17,9 @@ along with this program. If not, see . */ +#include "picli.h" #include "picrypt.h" -#include "pip.h" +#include "pifile.h" #include diff --git a/utils/system_test/main.cpp b/utils/system_test/main.cpp index e62ec337..59ab539a 100755 --- a/utils/system_test/main.cpp +++ b/utils/system_test/main.cpp @@ -17,8 +17,9 @@ along with this program. If not, see . */ -#include "pip.h" +#include "piconfig.h" #include "pisystemtests.h" +#include "pisystemtime.h" #ifdef CC_GCC # include #endif