mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
Harden contact path failover and simplify routing flow
This commit is contained in:
parent
cdd3d5f34e
commit
b63a0e0b67
3 changed files with 271 additions and 72 deletions
|
|
@ -8,6 +8,47 @@
|
|||
#ifndef TXT_ACK_DELAY
|
||||
#define TXT_ACK_DELAY 200
|
||||
#endif
|
||||
#ifndef PATH_FAIL_THRESHOLD
|
||||
#define PATH_FAIL_THRESHOLD 2
|
||||
#endif
|
||||
#ifndef PATH_SWITCH_COOLDOWN_MILLIS
|
||||
#define PATH_SWITCH_COOLDOWN_MILLIS 10000
|
||||
#endif
|
||||
#ifndef BACKUP_PATH_MAX_AGE_SECS
|
||||
#define BACKUP_PATH_MAX_AGE_SECS 1800
|
||||
#endif
|
||||
|
||||
uint8_t BaseChatMesh::getPathByteLen(uint8_t path_len) {
|
||||
uint8_t hash_count = path_len & 63;
|
||||
uint8_t hash_size = (path_len >> 6) + 1;
|
||||
return hash_count * hash_size;
|
||||
}
|
||||
|
||||
uint8_t BaseChatMesh::getPathHashCount(uint8_t path_len) {
|
||||
return path_len & 63;
|
||||
}
|
||||
|
||||
bool BaseChatMesh::isPathBetter(uint8_t candidate_len, uint8_t current_len) {
|
||||
uint8_t candidate_hops = getPathHashCount(candidate_len);
|
||||
uint8_t current_hops = getPathHashCount(current_len);
|
||||
if (candidate_hops != current_hops) {
|
||||
return candidate_hops < current_hops;
|
||||
}
|
||||
return getPathByteLen(candidate_len) < getPathByteLen(current_len);
|
||||
}
|
||||
|
||||
void BaseChatMesh::resetRouteFailoverState(ContactInfo& contact) {
|
||||
contact.backup_out_path_len = OUT_PATH_UNKNOWN;
|
||||
contact.path_failures = 0;
|
||||
contact.path_switch_cooldown_until = 0;
|
||||
contact.direct_block_until = 0;
|
||||
contact.backup_lastmod = 0;
|
||||
}
|
||||
|
||||
void BaseChatMesh::resetAllRouteState(ContactInfo& contact) {
|
||||
contact.out_path_len = OUT_PATH_UNKNOWN;
|
||||
resetRouteFailoverState(contact);
|
||||
}
|
||||
|
||||
void BaseChatMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis) {
|
||||
sendFlood(pkt, delay_millis);
|
||||
|
|
@ -38,8 +79,34 @@ mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name, double lat, doubl
|
|||
return createAdvert(self_id, app_data, app_data_len);
|
||||
}
|
||||
|
||||
int BaseChatMesh::sendUsingBestRouteWithTxtAck(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t pkt_airtime_millis, uint32_t& est_timeout) {
|
||||
if (!canUseDirectNow(recipient)) {
|
||||
clearPendingDirectContact();
|
||||
txt_send_timeout = futureMillis(est_timeout = calcFloodTimeoutMillisFor(pkt_airtime_millis));
|
||||
sendFloodScoped(recipient, pkt);
|
||||
return MSG_SEND_SENT_FLOOD;
|
||||
}
|
||||
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
setPendingDirectContact(recipient);
|
||||
txt_send_timeout = futureMillis(est_timeout = calcDirectTimeoutMillisFor(pkt_airtime_millis, recipient.out_path_len));
|
||||
return MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
|
||||
int BaseChatMesh::sendUsingBestRouteNoTxtAck(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t pkt_airtime_millis, uint32_t& est_timeout) {
|
||||
if (!canUseDirectNow(recipient)) {
|
||||
est_timeout = calcFloodTimeoutMillisFor(pkt_airtime_millis);
|
||||
sendFloodScoped(recipient, pkt);
|
||||
return MSG_SEND_SENT_FLOOD;
|
||||
}
|
||||
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
est_timeout = calcDirectTimeoutMillisFor(pkt_airtime_millis, recipient.out_path_len);
|
||||
return MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
|
||||
void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) {
|
||||
if (dest.out_path_len == OUT_PATH_UNKNOWN) {
|
||||
if (!canUseDirectNow(dest)) {
|
||||
mesh::Packet* ack = createAck(ack_hash);
|
||||
if (ack) sendFloodScoped(dest, ack, TXT_ACK_DELAY);
|
||||
} else {
|
||||
|
|
@ -55,6 +122,143 @@ void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) {
|
|||
}
|
||||
}
|
||||
|
||||
void BaseChatMesh::clearPendingDirectContact() {
|
||||
pending_direct_contact_set = false;
|
||||
}
|
||||
|
||||
void BaseChatMesh::setPendingDirectContact(const ContactInfo& contact) {
|
||||
memcpy(pending_direct_contact_pub_key, contact.id.pub_key, PUB_KEY_SIZE);
|
||||
pending_direct_contact_set = true;
|
||||
}
|
||||
|
||||
bool BaseChatMesh::isPendingDirectContact(const ContactInfo& contact) const {
|
||||
return pending_direct_contact_set && memcmp(pending_direct_contact_pub_key, contact.id.pub_key, PUB_KEY_SIZE) == 0;
|
||||
}
|
||||
|
||||
bool BaseChatMesh::hasUsableBackupPath(ContactInfo& contact, uint32_t now) {
|
||||
if (!mesh::Packet::isValidPathLen(contact.backup_out_path_len)) {
|
||||
contact.backup_out_path_len = OUT_PATH_UNKNOWN;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (contact.backup_lastmod > 0 && now > contact.backup_lastmod + BACKUP_PATH_MAX_AGE_SECS) {
|
||||
contact.backup_out_path_len = OUT_PATH_UNKNOWN;
|
||||
contact.backup_lastmod = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseChatMesh::canUseDirectNow(const ContactInfo& contact) const {
|
||||
return mesh::Packet::isValidPathLen(contact.out_path_len) && millisHasNowPassed(contact.direct_block_until);
|
||||
}
|
||||
|
||||
void BaseChatMesh::activateBackupPath(ContactInfo& contact, uint32_t now) {
|
||||
uint8_t old_len = contact.out_path_len;
|
||||
uint8_t old_path[MAX_PATH_SIZE];
|
||||
uint8_t old_path_bytes = 0;
|
||||
if (mesh::Packet::isValidPathLen(old_len)) {
|
||||
old_path_bytes = getPathByteLen(old_len);
|
||||
memcpy(old_path, contact.out_path, old_path_bytes);
|
||||
}
|
||||
|
||||
uint8_t new_active_len = contact.backup_out_path_len;
|
||||
uint8_t new_active_bytes = getPathByteLen(new_active_len);
|
||||
memcpy(contact.out_path, contact.backup_out_path, new_active_bytes);
|
||||
contact.out_path_len = new_active_len;
|
||||
|
||||
if (mesh::Packet::isValidPathLen(old_len)) {
|
||||
memcpy(contact.backup_out_path, old_path, old_path_bytes);
|
||||
contact.backup_out_path_len = old_len;
|
||||
contact.backup_lastmod = now;
|
||||
} else {
|
||||
contact.backup_out_path_len = OUT_PATH_UNKNOWN;
|
||||
contact.backup_lastmod = 0;
|
||||
}
|
||||
|
||||
contact.path_failures = 0;
|
||||
contact.path_switch_cooldown_until = futureMillis(PATH_SWITCH_COOLDOWN_MILLIS);
|
||||
contact.direct_block_until = 0;
|
||||
}
|
||||
|
||||
void BaseChatMesh::noteDirectPathFailure(ContactInfo& contact) {
|
||||
uint32_t now = getRTCClock()->getCurrentTime();
|
||||
if (!mesh::Packet::isValidPathLen(contact.out_path_len)) return;
|
||||
if (contact.path_failures < 0xFF) contact.path_failures++;
|
||||
if (contact.path_failures < PATH_FAIL_THRESHOLD) return;
|
||||
if (!millisHasNowPassed(contact.path_switch_cooldown_until)) return;
|
||||
|
||||
if (hasUsableBackupPath(contact, now)) {
|
||||
activateBackupPath(contact, now);
|
||||
onContactPathUpdated(contact);
|
||||
} else {
|
||||
resetPathTo(contact);
|
||||
contact.direct_block_until = futureMillis(PATH_DIRECT_BLOCK_MILLIS);
|
||||
onContactPathUpdated(contact);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseChatMesh::noteDirectPathSuccess(ContactInfo& contact) {
|
||||
contact.path_failures = 0;
|
||||
contact.direct_block_until = 0;
|
||||
}
|
||||
|
||||
bool BaseChatMesh::updatePathForContact(ContactInfo& from, const uint8_t* out_path, uint8_t out_path_len) {
|
||||
if (!mesh::Packet::isValidPathLen(out_path_len)) return false;
|
||||
|
||||
uint32_t now = getRTCClock()->getCurrentTime();
|
||||
uint8_t out_bytes = getPathByteLen(out_path_len);
|
||||
|
||||
if (!mesh::Packet::isValidPathLen(from.out_path_len)) {
|
||||
memcpy(from.out_path, out_path, out_bytes);
|
||||
from.out_path_len = out_path_len;
|
||||
from.path_failures = 0;
|
||||
from.path_switch_cooldown_until = 0;
|
||||
if (millisHasNowPassed(from.direct_block_until)) {
|
||||
from.direct_block_until = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t active_bytes = getPathByteLen(from.out_path_len);
|
||||
if (from.out_path_len == out_path_len && memcmp(from.out_path, out_path, active_bytes) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool better_path = isPathBetter(out_path_len, from.out_path_len);
|
||||
bool allow_switch = millisHasNowPassed(from.path_switch_cooldown_until) || from.path_failures >= PATH_FAIL_THRESHOLD;
|
||||
if (better_path && allow_switch) {
|
||||
memcpy(from.backup_out_path, from.out_path, active_bytes);
|
||||
from.backup_out_path_len = from.out_path_len;
|
||||
from.backup_lastmod = now;
|
||||
|
||||
memcpy(from.out_path, out_path, out_bytes);
|
||||
from.out_path_len = out_path_len;
|
||||
from.path_failures = 0;
|
||||
from.path_switch_cooldown_until = futureMillis(PATH_SWITCH_COOLDOWN_MILLIS);
|
||||
from.direct_block_until = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool backup_usable = hasUsableBackupPath(from, now);
|
||||
if (backup_usable) {
|
||||
uint8_t backup_bytes = getPathByteLen(from.backup_out_path_len);
|
||||
if (from.backup_out_path_len == out_path_len && memcmp(from.backup_out_path, out_path, backup_bytes) == 0) {
|
||||
from.backup_lastmod = now;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool replace_backup = !backup_usable || isPathBetter(out_path_len, from.backup_out_path_len);
|
||||
|
||||
if (replace_backup) {
|
||||
memcpy(from.backup_out_path, out_path, out_bytes);
|
||||
from.backup_out_path_len = out_path_len;
|
||||
from.backup_lastmod = now;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseChatMesh::bootstrapRTCfromContacts() {
|
||||
uint32_t latest = 0;
|
||||
for (int i = 0; i < num_contacts; i++) {
|
||||
|
|
@ -92,7 +296,7 @@ ContactInfo* BaseChatMesh::allocateContactSlot() {
|
|||
void BaseChatMesh::populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp) {
|
||||
memset(&ci, 0, sizeof(ci));
|
||||
ci.id = id;
|
||||
ci.out_path_len = OUT_PATH_UNKNOWN;
|
||||
resetAllRouteState(ci);
|
||||
StrHelper::strncpy(ci.name, parser.getName(), sizeof(ci.name));
|
||||
ci.type = parser.getType();
|
||||
if (parser.hasLatLon()) {
|
||||
|
|
@ -272,7 +476,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
|
|||
} else {
|
||||
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, from.id, secret, temp_buf, reply_len);
|
||||
if (reply) {
|
||||
if (from.out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT
|
||||
if (canUseDirectNow(from)) { // we have an out_path, so send DIRECT
|
||||
sendDirect(reply, from.out_path, from.out_path_len, SERVER_RESPONSE_DELAY);
|
||||
} else {
|
||||
sendFloodScoped(from, reply, SERVER_RESPONSE_DELAY);
|
||||
|
|
@ -282,7 +486,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
|
|||
}
|
||||
} else if (type == PAYLOAD_TYPE_RESPONSE && len > 0) {
|
||||
onContactResponse(from, data, len);
|
||||
if (packet->isRouteFlood() && from.out_path_len != OUT_PATH_UNKNOWN) {
|
||||
if (packet->isRouteFlood() && canUseDirectNow(from)) {
|
||||
// we have direct path, but other node is still sending flood response, so maybe they didn't receive reciprocal path properly(?)
|
||||
handleReturnPathRetry(from, packet->path, packet->path_len);
|
||||
}
|
||||
|
|
@ -302,17 +506,23 @@ bool BaseChatMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const ui
|
|||
}
|
||||
|
||||
bool BaseChatMesh::onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) {
|
||||
// NOTE: default impl, we just replace the current 'out_path' regardless, whenever sender sends us a new out_path.
|
||||
// FUTURE: could store multiple out_paths per contact, and try to find which is the 'best'(?)
|
||||
from.out_path_len = mesh::Packet::copyPath(from.out_path, out_path, out_path_len); // store a copy of path, for sendDirect()
|
||||
if (!mesh::Packet::isValidPathLen(out_path_len)) return false;
|
||||
bool active_changed = updatePathForContact(from, out_path, out_path_len);
|
||||
from.lastmod = getRTCClock()->getCurrentTime();
|
||||
|
||||
onContactPathUpdated(from);
|
||||
if (active_changed) {
|
||||
onContactPathUpdated(from);
|
||||
}
|
||||
|
||||
if (extra_type == PAYLOAD_TYPE_ACK && extra_len >= 4) {
|
||||
// also got an encoded ACK!
|
||||
if (processAck(extra) != NULL) {
|
||||
ContactInfo* ack_from = processAck(extra);
|
||||
if (ack_from != NULL) {
|
||||
txt_send_timeout = 0; // matched one we're waiting for, cancel timeout timer
|
||||
if (isPendingDirectContact(*ack_from)) {
|
||||
noteDirectPathSuccess(*ack_from);
|
||||
}
|
||||
clearPendingDirectContact();
|
||||
}
|
||||
} else if (extra_type == PAYLOAD_TYPE_RESPONSE && extra_len > 0) {
|
||||
onContactResponse(from, extra, extra_len);
|
||||
|
|
@ -324,9 +534,13 @@ void BaseChatMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) {
|
|||
ContactInfo* from;
|
||||
if ((from = processAck((uint8_t *)&ack_crc)) != NULL) {
|
||||
txt_send_timeout = 0; // matched one we're waiting for, cancel timeout timer
|
||||
if (isPendingDirectContact(*from)) {
|
||||
noteDirectPathSuccess(*from);
|
||||
}
|
||||
clearPendingDirectContact();
|
||||
packet->markDoNotRetransmit(); // ACK was for this node, so don't retransmit
|
||||
|
||||
if (packet->isRouteFlood() && from->out_path_len != OUT_PATH_UNKNOWN) {
|
||||
if (packet->isRouteFlood() && canUseDirectNow(*from)) {
|
||||
// we have direct path, but other node is still sending flood, so maybe they didn't receive reciprocal path properly(?)
|
||||
handleReturnPathRetry(*from, packet->path, packet->path_len);
|
||||
}
|
||||
|
|
@ -393,18 +607,7 @@ int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp,
|
|||
if (pkt == NULL) return MSG_SEND_FAILED;
|
||||
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
|
||||
int rc;
|
||||
if (recipient.out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFloodScoped(recipient, pkt);
|
||||
txt_send_timeout = futureMillis(est_timeout = calcFloodTimeoutMillisFor(t));
|
||||
rc = MSG_SEND_SENT_FLOOD;
|
||||
} else {
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
txt_send_timeout = futureMillis(est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len));
|
||||
rc = MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
return rc;
|
||||
return sendUsingBestRouteWithTxtAck(recipient, pkt, t, est_timeout);
|
||||
}
|
||||
|
||||
int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout) {
|
||||
|
|
@ -420,17 +623,7 @@ int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timest
|
|||
if (pkt == NULL) return MSG_SEND_FAILED;
|
||||
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
int rc;
|
||||
if (recipient.out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFloodScoped(recipient, pkt);
|
||||
txt_send_timeout = futureMillis(est_timeout = calcFloodTimeoutMillisFor(t));
|
||||
rc = MSG_SEND_SENT_FLOOD;
|
||||
} else {
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
txt_send_timeout = futureMillis(est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len));
|
||||
rc = MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
return rc;
|
||||
return sendUsingBestRouteWithTxtAck(recipient, pkt, t, est_timeout);
|
||||
}
|
||||
|
||||
bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len) {
|
||||
|
|
@ -509,15 +702,7 @@ int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password,
|
|||
}
|
||||
if (pkt) {
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
if (recipient.out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFloodScoped(recipient, pkt);
|
||||
est_timeout = calcFloodTimeoutMillisFor(t);
|
||||
return MSG_SEND_SENT_FLOOD;
|
||||
} else {
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len);
|
||||
return MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
return sendUsingBestRouteNoTxtAck(recipient, pkt, t, est_timeout);
|
||||
}
|
||||
return MSG_SEND_FAILED;
|
||||
}
|
||||
|
|
@ -534,15 +719,7 @@ int BaseChatMesh::sendAnonReq(const ContactInfo& recipient, const uint8_t* data,
|
|||
}
|
||||
if (pkt) {
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
if (recipient.out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFloodScoped(recipient, pkt);
|
||||
est_timeout = calcFloodTimeoutMillisFor(t);
|
||||
return MSG_SEND_SENT_FLOOD;
|
||||
} else {
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len);
|
||||
return MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
return sendUsingBestRouteNoTxtAck(recipient, pkt, t, est_timeout);
|
||||
}
|
||||
return MSG_SEND_FAILED;
|
||||
}
|
||||
|
|
@ -561,15 +738,7 @@ int BaseChatMesh::sendRequest(const ContactInfo& recipient, const uint8_t* req_
|
|||
}
|
||||
if (pkt) {
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
if (recipient.out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFloodScoped(recipient, pkt);
|
||||
est_timeout = calcFloodTimeoutMillisFor(t);
|
||||
return MSG_SEND_SENT_FLOOD;
|
||||
} else {
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len);
|
||||
return MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
return sendUsingBestRouteNoTxtAck(recipient, pkt, t, est_timeout);
|
||||
}
|
||||
return MSG_SEND_FAILED;
|
||||
}
|
||||
|
|
@ -588,15 +757,7 @@ int BaseChatMesh::sendRequest(const ContactInfo& recipient, uint8_t req_type, u
|
|||
}
|
||||
if (pkt) {
|
||||
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
|
||||
if (recipient.out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFloodScoped(recipient, pkt);
|
||||
est_timeout = calcFloodTimeoutMillisFor(t);
|
||||
return MSG_SEND_SENT_FLOOD;
|
||||
} else {
|
||||
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
|
||||
est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len);
|
||||
return MSG_SEND_SENT_DIRECT;
|
||||
}
|
||||
return sendUsingBestRouteNoTxtAck(recipient, pkt, t, est_timeout);
|
||||
}
|
||||
return MSG_SEND_FAILED;
|
||||
}
|
||||
|
|
@ -692,7 +853,7 @@ void BaseChatMesh::checkConnections() {
|
|||
MESH_DEBUG_PRINTLN("checkConnections(): Keep_alive contact not found!");
|
||||
continue;
|
||||
}
|
||||
if (contact->out_path_len == OUT_PATH_UNKNOWN) {
|
||||
if (!canUseDirectNow(*contact)) {
|
||||
MESH_DEBUG_PRINTLN("checkConnections(): Keep_alive contact, no out_path!");
|
||||
continue;
|
||||
}
|
||||
|
|
@ -719,7 +880,7 @@ void BaseChatMesh::checkConnections() {
|
|||
}
|
||||
|
||||
void BaseChatMesh::resetPathTo(ContactInfo& recipient) {
|
||||
recipient.out_path_len = OUT_PATH_UNKNOWN;
|
||||
resetAllRouteState(recipient);
|
||||
}
|
||||
|
||||
static ContactInfo* table; // pass via global :-(
|
||||
|
|
@ -770,6 +931,10 @@ bool BaseChatMesh::addContact(const ContactInfo& contact) {
|
|||
ContactInfo* dest = allocateContactSlot();
|
||||
if (dest) {
|
||||
*dest = contact;
|
||||
if (!mesh::Packet::isValidPathLen(dest->out_path_len)) {
|
||||
dest->out_path_len = OUT_PATH_UNKNOWN;
|
||||
}
|
||||
resetRouteFailoverState(*dest);
|
||||
dest->shared_secret_valid = false; // mark shared_secret as needing calculation
|
||||
return true; // success
|
||||
}
|
||||
|
|
@ -875,6 +1040,13 @@ void BaseChatMesh::loop() {
|
|||
|
||||
if (txt_send_timeout && millisHasNowPassed(txt_send_timeout)) {
|
||||
// failed to get an ACK
|
||||
if (pending_direct_contact_set) {
|
||||
ContactInfo* pending = lookupContactByPubKey(pending_direct_contact_pub_key, PUB_KEY_SIZE);
|
||||
if (pending != NULL) {
|
||||
noteDirectPathFailure(*pending);
|
||||
}
|
||||
clearPendingDirectContact();
|
||||
}
|
||||
onSendTimeout();
|
||||
txt_send_timeout = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#define MSG_SEND_FAILED 0
|
||||
#define MSG_SEND_SENT_FLOOD 1
|
||||
#define MSG_SEND_SENT_DIRECT 2
|
||||
#define PATH_DIRECT_BLOCK_MILLIS 15000
|
||||
|
||||
#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS
|
||||
#define REQ_TYPE_KEEP_ALIVE 0x02
|
||||
|
|
@ -70,8 +71,26 @@ class BaseChatMesh : public mesh::Mesh {
|
|||
mesh::Packet* _pendingLoopback;
|
||||
uint8_t temp_buf[MAX_TRANS_UNIT];
|
||||
ConnectionInfo connections[MAX_CONNECTIONS];
|
||||
uint8_t pending_direct_contact_pub_key[PUB_KEY_SIZE];
|
||||
bool pending_direct_contact_set;
|
||||
|
||||
mesh::Packet* composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack);
|
||||
int sendUsingBestRouteWithTxtAck(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t pkt_airtime_millis, uint32_t& est_timeout);
|
||||
int sendUsingBestRouteNoTxtAck(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t pkt_airtime_millis, uint32_t& est_timeout);
|
||||
static uint8_t getPathByteLen(uint8_t path_len);
|
||||
static uint8_t getPathHashCount(uint8_t path_len);
|
||||
static bool isPathBetter(uint8_t candidate_len, uint8_t current_len);
|
||||
void resetRouteFailoverState(ContactInfo& contact);
|
||||
void resetAllRouteState(ContactInfo& contact);
|
||||
bool hasUsableBackupPath(ContactInfo& contact, uint32_t now);
|
||||
bool canUseDirectNow(const ContactInfo& contact) const;
|
||||
void activateBackupPath(ContactInfo& contact, uint32_t now);
|
||||
void noteDirectPathFailure(ContactInfo& contact);
|
||||
void noteDirectPathSuccess(ContactInfo& contact);
|
||||
bool updatePathForContact(ContactInfo& from, const uint8_t* out_path, uint8_t out_path_len);
|
||||
void clearPendingDirectContact();
|
||||
void setPendingDirectContact(const ContactInfo& contact);
|
||||
bool isPendingDirectContact(const ContactInfo& contact) const;
|
||||
void sendAckTo(const ContactInfo& dest, uint32_t ack_hash);
|
||||
|
||||
protected:
|
||||
|
|
@ -85,11 +104,13 @@ protected:
|
|||
#endif
|
||||
txt_send_timeout = 0;
|
||||
_pendingLoopback = NULL;
|
||||
pending_direct_contact_set = false;
|
||||
memset(pending_direct_contact_pub_key, 0, sizeof(pending_direct_contact_pub_key));
|
||||
memset(connections, 0, sizeof(connections));
|
||||
}
|
||||
|
||||
void bootstrapRTCfromContacts();
|
||||
void resetContacts() { num_contacts = 0; }
|
||||
void resetContacts() { num_contacts = 0; clearPendingDirectContact(); }
|
||||
void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp);
|
||||
ContactInfo* allocateContactSlot(); // helper to find slot for new contact
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,16 @@ struct ContactInfo {
|
|||
uint8_t type; // on of ADV_TYPE_*
|
||||
uint8_t flags;
|
||||
uint8_t out_path_len;
|
||||
uint8_t backup_out_path_len;
|
||||
mutable bool shared_secret_valid; // flag to indicate if shared_secret has been calculated
|
||||
uint8_t out_path[MAX_PATH_SIZE];
|
||||
uint8_t backup_out_path[MAX_PATH_SIZE];
|
||||
uint8_t path_failures; // consecutive direct-send timeouts on active path
|
||||
unsigned long path_switch_cooldown_until; // millis() guard to prevent rapid path flapping
|
||||
unsigned long direct_block_until; // millis() until direct sends are temporarily blocked
|
||||
uint32_t last_advert_timestamp; // by THEIR clock
|
||||
uint32_t lastmod; // by OUR clock
|
||||
uint32_t backup_lastmod; // RTC timestamp of last backup path refresh
|
||||
int32_t gps_lat, gps_lon; // 6 dec places
|
||||
uint32_t sync_since;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue