WIP: Fixed archictectural flaw with Interfaces

Fixed packet transmit issue caused by fundamental issue of attempting to
send on a copy of an interface which is not properly associated with the
actual method overrides of the derived class (the interface
implementation).
Fix was to use methods in the Object data to callback to the original
(implementation) instance of the object.
General clean-up to remove some early testing code.
This commit is contained in:
attermann 2023-12-03 16:43:53 -07:00
parent 9f0e71bb81
commit c39e9e7fee
11 changed files with 155 additions and 153 deletions

View File

@ -47,7 +47,6 @@ Destination::Destination(const Identity& identity, const directions direction, c
_object->_hash = hash(_object->_identity, app_name, fullaspects.c_str());
_object->_hexhash = _object->_hash.toHex();
debug("Destination::Destination: hash: " + _object->_hash.toHex());
// CBA TEST CRASH
debug("Destination::Destination: creating name hash...");
//p self.name_hash = RNS.Identity.full_hash(self.expand_name(None, app_name, *aspects).encode("utf-8"))[:(RNS.Identity.NAME_HASH_LENGTH//8)]
_object->_name_hash = name_hash(app_name, aspects);
@ -59,6 +58,31 @@ Destination::Destination(const Identity& identity, const directions direction, c
mem("Destination object created, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
}
Destination::Destination(const Identity& identity, const Type::Destination::directions direction, const Type::Destination::types type, const Bytes& hash) : _object(new Object(identity)) {
assert(_object);
mem("Destination object creating..., this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
_object->_type = type;
_object->_direction = direction;
if (_object->_identity && _object->_type == PLAIN) {
throw std::invalid_argument("Selected destination type PLAIN cannot hold an identity");
}
_object->_hash = hash;
_object->_hexhash = _object->_hash.toHex();
debug("Destination::Destination: hash: " + _object->_hash.toHex());
debug("Destination::Destination: creating name hash...");
//p self.name_hash = RNS.Identity.full_hash(self.expand_name(None, app_name, *aspects).encode("utf-8"))[:(RNS.Identity.NAME_HASH_LENGTH//8)]
_object->_name_hash = name_hash("unknown", "unknown");
debug("Destination::Destination: name hash: " + _object->_name_hash.toHex());
debug("Destination::Destination: calling register_destination");
Transport::register_destination(*this);
mem("Destination object created, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
}
/*virtual*/ Destination::~Destination() {
mem("Destination object destroyed, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
if (_object && _object.use_count() == 1) {

View File

@ -66,6 +66,12 @@ namespace RNS {
const char* app_name,
const char* aspects
);
Destination(
const Identity& identity,
const Type::Destination::directions direction,
const Type::Destination::types type,
const Bytes& hash
);
virtual ~Destination();
inline Destination& operator = (const Destination& destination) {
@ -158,7 +164,8 @@ namespace RNS {
inline Type::Destination::directions direction() const { assert(_object); return _object->_direction; }
inline Type::Destination::proof_strategies proof_strategy() const { assert(_object); return _object->_proof_strategy; }
inline const Bytes& hash() const { assert(_object); return _object->_hash; }
inline void hash(const Bytes& hash) { assert(_object); _object->_hash = hash; _object->_hexhash = _object->_hash.toHex(); }
// CBA Don't allow changing destination after construction since it's used as key in collections
//inline void hash(const Bytes& hash) { assert(_object); _object->_hash = hash; _object->_hexhash = _object->_hash.toHex(); }
inline const Bytes& link_id() const { assert(_object); return _object->_link_id; }
inline uint16_t mtu() const { assert(_object); return _object->_mtu; }
inline void mtu(uint16_t mtu) { assert(_object); _object->_mtu = mtu; }

View File

@ -8,8 +8,8 @@ using namespace RNS::Type::Interface;
/*static*/ uint8_t Interface::DISCOVER_PATHS_FOR = MODE_ACCESS_POINT | MODE_GATEWAY;
/*virtual*/ inline void Interface::processIncoming(const Bytes& data) {
extreme("Interface::processIncoming: data: " + data.toHex());
/*virtual*/ inline void Interface::on_incoming(const Bytes& data) {
extreme("Interface::on_incoming: data: " + data.toHex());
assert(_object);
_object->_rxb += data.size();
// CBA TODO implement concept of owner or a callback mechanism for incoming data
@ -17,8 +17,8 @@ using namespace RNS::Type::Interface;
Transport::inbound(data, *this);
}
/*virtual*/ inline void Interface::processOutgoing(const Bytes& data) {
extreme("Interface::processOutgoing: data: " + data.toHex());
/*virtual*/ inline void Interface::on_outgoing(const Bytes& data) {
extreme("Interface::on_outgoing: data: " + data.toHex());
assert(_object);
_object->_txb += data.size();
}
@ -58,7 +58,7 @@ void Interface::process_announce_queue() {
}
_object->_announce_allowed_at = now + wait_time;
self.processOutgoing(selected["raw"])
self.on_outgoing(selected["raw"])
if selected in self.announce_queue:
self.announce_queue.remove(selected)

View File

@ -45,13 +45,18 @@ namespace RNS {
Interface(const Interface& interface) : _object(interface._object) {
mem("Interface object copy created, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
}
Interface() : _object(new Object()) {
Interface() : _object(new Object(*this)) {
mem("Interface object created, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
}
Interface(const char* name) : _object(new Object(name)) {
Interface(const char* name) : _object(new Object(*this, name)), _creator(true) {
mem("Interface object created, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
}
virtual ~Interface() {
if (_creator) {
// clear reference to parent in object foir safety
assert(_object);
_object->_parent = {Type::NONE};
}
mem("Interface object destroyed, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
}
@ -61,22 +66,42 @@ namespace RNS {
return *this;
}
inline operator bool() const {
extreme("Interface object bool, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
return _object.get() != nullptr;
}
inline bool operator < (const Interface& interface) const {
extreme("Interface object <, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
return _object.get() < interface._object.get();
}
inline bool operator > (const Interface& interface) const {
extreme("Interface object <, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
return _object.get() > interface._object.get();
}
inline bool operator == (const Interface& interface) const {
extreme("Interface object ==, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
return _object.get() == interface._object.get();
}
inline bool operator != (const Interface& interface) const {
extreme("Interface object !=, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((uintptr_t)_object.get()));
return _object.get() != interface._object.get();
}
public:
const Bytes get_hash() const;
void process_announce_queue();
inline void detach() {}
virtual void processIncoming(const Bytes& data);
virtual void processOutgoing(const Bytes& data);
inline void add_announce(AnnounceEntry& entry) { assert(_object); _object->_announce_queue.push_back(entry); }
protected:
// CBA TODO these should NOT called internally, and should likely be protected (only to be overridden and called by Object)
virtual void on_incoming(const Bytes& data);
virtual void on_outgoing(const Bytes& data);
private:
//inline void process_incoming(const Bytes& data) { assert(_object); _object->process_incoming(data); }
inline void process_outgoing(const Bytes& data) { assert(_object); _object->process_outgoing(data); }
// getters/setters
protected:
inline void IN(bool IN) { assert(_object); _object->_IN = IN; }
@ -116,10 +141,14 @@ namespace RNS {
private:
class Object {
public:
Object() { mem("Interface::Data object created, this: " + std::to_string((uintptr_t)this)); }
Object(const char* name) : _name(name) { mem("Interface::Data object created, this: " + std::to_string((uintptr_t)this)); }
Object(Interface& parent) : _parent(parent) { mem("Interface::Data object created, this: " + std::to_string((uintptr_t)this)); }
Object(Interface& parent, const char* name) : _parent(parent), _name(name) { mem("Interface::Data object created, this: " + std::to_string((uintptr_t)this)); }
virtual ~Object() { mem("Interface::Data object destroyed, this: " + std::to_string((uintptr_t)this)); }
private:
//virtual inline void process_incoming(const Bytes& data) { if (_parent) { _parent.on_incoming(data); } }
virtual inline void process_outgoing(const Bytes& data) { if (_parent) { _parent.on_outgoing(data); } }
private:
Interface& _parent;
bool _IN = false;
bool _OUT = false;
bool _FWD = false;
@ -141,6 +170,7 @@ namespace RNS {
friend class Interface;
};
std::shared_ptr<Object> _object;
bool _creator = false;
friend class Transport;
};

View File

@ -92,19 +92,19 @@ Serial.println(available);
Serial.println("RSSI: " + String(LoRa.packetRssi()));
Serial.println("Snr: " + String(LoRa.packetSnr()));
processIncoming(buffer);
on_incoming(buffer);
}
#endif
}
}
/*virtual*/ void LoRaInterface::processIncoming(const Bytes& data) {
debug("LoRaInterface.processIncoming: data: " + data.toHex());
Interface::processIncoming(data);
/*virtual*/ void LoRaInterface::on_incoming(const Bytes& data) {
debug("LoRaInterface.on_incoming: data: " + data.toHex());
Interface::on_incoming(data);
}
/*virtual*/ void LoRaInterface::processOutgoing(const Bytes& data) {
debug("LoRaInterface.processOutgoing: data: " + data.toHex());
/*virtual*/ void LoRaInterface::on_outgoing(const Bytes& data) {
debug("LoRaInterface.on_outgoing: data: " + data.toHex());
try {
if (online()) {
extreme("LoRaInterface: sending " + std::to_string(data.size()) + " bytes...");
@ -124,7 +124,7 @@ Serial.println(available);
#endif
extreme("LoRaInterface: sent bytes");
}
Interface::processOutgoing(data);
Interface::on_outgoing(data);
}
catch (std::exception& e) {
error("Could not transmit on " + toString() + ". The contained exception was: " + e.what());

View File

@ -38,11 +38,12 @@ namespace RNS { namespace Interfaces {
void stop();
void loop();
virtual void processIncoming(const Bytes& data);
virtual void processOutgoing(const Bytes& data);
virtual inline std::string toString() const { return "LoRaInterface[" + name() + "]"; }
private:
virtual void on_incoming(const Bytes& data);
virtual void on_outgoing(const Bytes& data);
private:
const uint16_t HW_MTU = 508;
//uint8_t buffer[Type::Reticulum::MTU] = {0};

View File

@ -200,7 +200,7 @@ void UDPInterface::loop() {
size_t len = udp.read(_buffer.writable(Type::Reticulum::MTU), Type::Reticulum::MTU);
if (len > 0) {
_buffer.resize(len);
processIncoming(_buffer);
on_incoming(_buffer);
}
#else
size_t available = 0;
@ -211,20 +211,20 @@ void UDPInterface::loop() {
if (len < available) {
_buffer.resize(len);
}
processIncoming(_buffer);
on_incoming(_buffer);
}
}
#endif
}
}
/*virtual*/ void UDPInterface::processIncoming(const Bytes& data) {
debug("UDPInterface.processIncoming: data: " + data.toHex());
Interface::processIncoming(data);
/*virtual*/ void UDPInterface::on_incoming(const Bytes& data) {
debug("UDPInterface.on_incoming: data: " + data.toHex());
Interface::on_incoming(data);
}
/*virtual*/ void UDPInterface::processOutgoing(const Bytes& data) {
debug("UDPInterface.processOutgoing: data: " + data.toHex());
/*virtual*/ void UDPInterface::on_outgoing(const Bytes& data) {
debug("UDPInterface.on_outgoing: data: " + data.toHex());
try {
if (online()) {
// Send packet
@ -243,7 +243,7 @@ void UDPInterface::loop() {
#endif
}
Interface::processOutgoing(data);
Interface::on_outgoing(data);
}
catch (std::exception& e) {
error("Could not transmit on " + toString() + ". The contained exception was: " + e.what());

View File

@ -37,12 +37,13 @@ namespace RNS { namespace Interfaces {
void stop();
void loop();
virtual void processIncoming(const Bytes& data);
virtual void processOutgoing(const Bytes& data);
//virtual inline std::string toString() const { return "UDPInterface[" + name() + "/" + bind_ip + ":" + bind_port + "]"; }
virtual inline std::string toString() const { return "UDPInterface[" + name() + "]"; }
private:
virtual void on_incoming(const Bytes& data);
virtual void on_outgoing(const Bytes& data);
private:
const uint16_t HW_MTU = 1064;
//uint8_t buffer[Type::Reticulum::MTU] = {0};

View File

@ -30,7 +30,7 @@ void testReference() {
RNS::Interface& interface = (*iter);
RNS::extreme("Found interface: " + interface.toString());
RNS::Bytes data;
const_cast<RNS::Interface&>(interface).processOutgoing(data);
const_cast<RNS::Interface&>(interface).on_outgoing(data);
}
return 0;
*/
@ -43,7 +43,7 @@ void testReference() {
for (auto& interface : interfaces) {
RNS::extreme("Found interface: " + interface.toString());
RNS::Bytes data;
const_cast<RNS::Interface&>(interface).processOutgoing(data);
const_cast<RNS::Interface&>(interface).on_outgoing(data);
}
return 0;
*/
@ -57,7 +57,7 @@ void testReference() {
RNS::Interface& interface = (*iter);
RNS::extreme("Found interface: " + interface.toString());
RNS::Bytes data;
const_cast<RNS::Interface&>(interface).processOutgoing(data);
const_cast<RNS::Interface&>(interface).on_outgoing(data);
}
return 0;
*/
@ -71,7 +71,7 @@ void testReference() {
for (RNS::Interface& interface : interfaces) {
RNS::extreme("Found interface: " + interface.toString());
RNS::Bytes data;
const_cast<RNS::Interface&>(interface).processOutgoing(data);
const_cast<RNS::Interface&>(interface).on_outgoing(data);
}
return 0;
*/
@ -85,14 +85,14 @@ void testReference() {
RNS::Interface& interface = (*iter);
RNS::extreme("1 Found interface: " + interface.toString());
RNS::Bytes data;
const_cast<RNS::Interface&>(interface).processOutgoing(data);
const_cast<RNS::Interface&>(interface).on_outgoing(data);
}
}
for (auto iter = interfaces.begin(); iter != interfaces.end(); ++iter) {
RNS::Interface& interface = (*iter);
RNS::extreme("2 Found interface: " + interface.toString());
RNS::Bytes data;
const_cast<RNS::Interface&>(interface).processOutgoing(data);
const_cast<RNS::Interface&>(interface).on_outgoing(data);
}
return 0;
*/

View File

@ -368,8 +368,9 @@ using namespace RNS::Utilities;
}
//p announce_data = packet.data
Identity announce_identity(Identity::recall(announce_entry._packet.destination_hash()));
Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, "unknown", "unknown");
announce_destination.hash(announce_entry._packet.destination_hash());
//Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, "unknown", "unknown");
//announce_destination.hash(announce_entry._packet.destination_hash());
Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, announce_entry._packet.destination_hash());
//P announce_destination.hexhash = announce_destination.hash.hex()
//if (announce_entry._attached_interface) {
@ -686,11 +687,12 @@ using namespace RNS::Utilities;
i += 1
// Send it
interface.processOutgoing(masked_raw)
interface.on_outgoing(masked_raw)
*/
}
else {
interface.processOutgoing(raw);
//interface.on_outgoing(raw);
interface.process_outgoing(raw);
}
}
catch (std::exception& e) {
@ -1463,6 +1465,7 @@ using namespace RNS::Utilities;
);
_reverse_table.insert({packet.getTruncatedHash(), reverse_entry});
}
extreme("Transport::outbound: Sending packet to next hop...");
#if defined(INTERFACES_SET)
transmit(const_cast<Interface&>(outbound_interface), new_raw);
#else
@ -1812,8 +1815,9 @@ using namespace RNS::Utilities;
// transmit the announce to them immediately
if (_local_client_interfaces.size() > 0) {
Identity announce_identity(Identity::recall(packet.destination_hash()));
Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, "unknown", "unknown");
announce_destination.hash(packet.destination_hash());
//Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, "unknown", "unknown");
//announce_destination.hash(packet.destination_hash());
Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, packet.destination_hash());
//announce_destination.hexhash(announce_destination.hash().toHex());
Type::Packet::context_types announce_context = Type::Packet::CONTEXT_NONE;
Bytes announce_data = packet.data();
@ -1869,8 +1873,9 @@ using namespace RNS::Utilities;
debug("Got matching announce, answering waiting discovery path request for " + packet.destination_hash().toHex() + " on " + attached_interface.toString());
Identity announce_identity(Identity::recall(packet.destination_hash()));
Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, "unknown", "unknown");
announce_destination.hash(packet.destination_hash());
//Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, "unknown", "unknown");
//announce_destination.hash(packet.destination_hash());
Destination announce_destination(announce_identity, Type::Destination::OUT, Type::Destination::SINGLE, packet.destination_hash());
//announce_destination.hexhash(announce_destination.hash().toHex());
Type::Packet::context_types announce_context = Type::Packet::CONTEXT_NONE;
Bytes announce_data = packet.data();
@ -2327,6 +2332,9 @@ using namespace RNS::Utilities;
}
}
}
else {
extreme("Transport:register_destination: Skipping registraion (not direction IN) of destination " + destination.toString());
}
/*
#if defined(DESTINATIONS_SET)
@ -2923,6 +2931,7 @@ will announce it.
for (auto& [hash, interface] : _interfaces) {
#endif
if (interface != attached_interface) {
extreme("Transport::path_request: requesting path on interface " + interface.toString());
// Use the previously extracted tag from this path request
// on the new path requests as well, to avoid potential loops
request_path(destination_hash, interface, tag, true);

View File

@ -17,6 +17,10 @@
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <termios.h>
#include <fcntl.h>
#include <stdio.h>
#endif
#include <stdlib.h>
@ -52,94 +56,6 @@ const char* noble_gases[] = {"Helium", "Neon", "Argon", "Krypton", "Xenon", "Rad
double last_announce = 0.0;
bool send_announce = false;
class TestInterface : public RNS::Interface {
public:
TestInterface() : RNS::Interface("TestInterface") {
IN(true);
OUT(true);
}
TestInterface(const char* name) : RNS::Interface(name) {
IN(true);
OUT(true);
}
virtual ~TestInterface() {
name("deleted");
}
virtual void processIncoming(const RNS::Bytes& data) {
RNS::extreme("TestInterface.processIncoming: data: " + data.toHex());
}
virtual void processOutgoing(const RNS::Bytes& data) {
RNS::extreme("TestInterface.processOutgoing: data: " + data.toHex());
}
virtual inline std::string toString() const { return "TestInterface[" + name() + "]"; }
};
class TestLoopbackInterface : public RNS::Interface {
public:
TestLoopbackInterface(RNS::Interface& loopback_interface) : RNS::Interface("TestLoopbackInterface"), _loopback_interface(loopback_interface) {
IN(true);
OUT(true);
}
TestLoopbackInterface(RNS::Interface& loopback_interface, const char* name) : RNS::Interface(name), _loopback_interface(loopback_interface) {
IN(true);
OUT(true);
}
virtual ~TestLoopbackInterface() {
name("deleted");
}
virtual void processIncoming(const RNS::Bytes& data) {
RNS::extreme("TestLoopbackInterface.processIncoming: data: " + data.toHex());
_loopback_interface.processOutgoing(data);
}
virtual void processOutgoing(const RNS::Bytes& data) {
RNS::extreme("TestLoopbackInterface.processOutgoing: data: " + data.toHex());
_loopback_interface.processIncoming(data);
}
virtual inline std::string toString() const { return "TestLoopbackInterface[" + name() + "]"; }
private:
RNS::Interface& _loopback_interface;
};
class TestOutInterface : public RNS::Interface {
public:
TestOutInterface() : RNS::Interface("TestOutInterface") {
OUT(true);
IN(false);
}
TestOutInterface(const char* name) : RNS::Interface(name) {
OUT(true);
IN(false);
}
virtual ~TestOutInterface() {
name("(deleted)");
}
virtual void processOutgoing(const RNS::Bytes& data) {
RNS::head("TestOutInterface.processOutgoing: data: " + data.toHex(), RNS::LOG_EXTREME);
RNS::Interface::processOutgoing(data);
}
virtual inline std::string toString() const { return "TestOutInterface[" + name() + "]"; }
};
class TestInInterface : public RNS::Interface {
public:
TestInInterface() : RNS::Interface("TestInInterface") {
OUT(false);
IN(true);
}
TestInInterface(const char* name) : RNS::Interface(name) {
OUT(false);
IN(true);
}
virtual ~TestInInterface() {
name("(deleted)");
}
virtual void processIncoming(const RNS::Bytes& data) {
RNS::head("TestInInterface.processIncoming: data: " + data.toHex(), RNS::LOG_INFO);
RNS::Interface::processIncoming(data);
}
virtual inline std::string toString() const { return "TestInInterface[" + name() + "]"; }
};
// Test AnnounceHandler
class ExampleAnnounceHandler : public RNS::AnnounceHandler {
public:
@ -183,10 +99,6 @@ RNS::Reticulum reticulum({RNS::Type::NONE});
RNS::Identity identity({RNS::Type::NONE});
RNS::Destination destination({RNS::Type::NONE});
//TestInterface interface;
TestOutInterface outinterface;
TestInInterface ininterface;
TestLoopbackInterface loopinterface(ininterface);
#ifdef UDP_INTERFACE
RNS::Interfaces::UDPInterface udp_interface;
#endif
@ -254,10 +166,6 @@ void setup_reticulum() {
// 21.9% (+0.1%)
RNS::head("Registering Interface instances with Transport...", RNS::LOG_EXTREME);
//RNS::Transport::register_interface(interface);
RNS::Transport::register_interface(outinterface);
RNS::Transport::register_interface(ininterface);
RNS::Transport::register_interface(loopinterface);
#ifdef UDP_INTERFACE
udp_interface.mode(RNS::Type::Interface::MODE_GATEWAY);
RNS::Transport::register_interface(udp_interface);
@ -324,7 +232,6 @@ void setup_reticulum() {
destination.announce(RNS::bytesFromString(fruits[RNS::Cryptography::randomnum() % 7]));
// 23.9% (+0.8%)
*/
reticulum_announce();
#if defined (RETICULUM_PACKET_TEST)
// test data send packet
@ -365,10 +272,6 @@ void teardown_reticulum() {
#ifdef LORA_INTERFACE
RNS::Transport::deregister_interface(lora_interface);
#endif
RNS::Transport::deregister_interface(loopinterface);
RNS::Transport::deregister_interface(ininterface);
RNS::Transport::deregister_interface(outinterface);
//RNS::Transport::deregister_interface(interface);
}
catch (std::exception& e) {
@ -451,15 +354,42 @@ void loop() {
}
#ifndef ARDUINO
int getch( ) {
termios oldt;
termios newt;
tcgetattr( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
int ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}
int main(void) {
printf("Hello from Native on PlatformIO!\n");
setup();
while (true) {
bool run = true;
while (run) {
loop();
int ch = getch();
if (ch > 0) {
switch (ch) {
case 'a':
reticulum_announce();
break;
case 'q':
run = false;
break;
}
}
RNS::Utilities::OS::sleep(0.01);
}
printf("Goodbye from Native on PlatformIO!\n");
}
#endif