Wait for the radio from the repeater's own loop

This commit is contained in:
mikecarper committed 2026-10-26 19:46:05 -07:00
1 parent 435c740e5a
commit c313658b88
3 files changed
+21 -8

No files matched your search

+1 -1
View File
@@ -13393,7 +13393,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
void MyMesh::loop() {
#if MESH_ENABLE_DIRECT_OTA_ADMIN
direct_ota_admin.service(*this, millis());
direct_ota_admin.service(*this, millis(), !hasPendingRadioWork());
#endif
#if defined(WITH_WEBCONFIG) || defined(ETHERNET_ENABLED)
_local_cli_output.service();
+3 -2
View File
@@ -146,14 +146,15 @@ public:
if (phase_ != Idle) finish("admin session expired", true);
}
}
void service(Mesh& mesh, uint32_t now) {
void service(Mesh& mesh, uint32_t now, bool radio_idle) {
tick(now);
if (phase_ == Proof && expired(now, proof_retry_at_) && proof_attempts_ < 3) {
if (!queueReturnPath(mesh)) { clearSession(); finish("login return path queue failed", true); }
return; // radio_idle was sampled before this new PATH was queued
}
// PATH has lower queue priority than CLI. Wait until its transmission and
// retries finish before admitting the proof, rather than relying on delay.
if (phase_ == ReturnPath && !mesh.hasPendingRadioWork()) {
if (phase_ == ReturnPath && radio_idle) {
phase_ = Proof;
if (!queueCommand(mesh, "get public.key", now)) { clearSession(); finish("login proof queue failed", true); }
else proof_retry_at_ = now + 8000;
+17 -5
View File
@@ -80,6 +80,7 @@ struct Mesh {
Packet* createPathReturn(const uint8_t*,const uint8_t*,const uint8_t*,uint8_t,uint8_t,const uint8_t*,size_t);
Packet* createPathReturn(const Identity& dest,const uint8_t* secret,const uint8_t* path,uint8_t length,uint8_t type,const uint8_t* extra,size_t size){uint8_t hash[1];dest.copyHashTo(hash);return createPathReturn(hash,secret,path,length,type,extra,size);}
bool sendDirect(Packet* p,const uint8_t* path,uint8_t length){assert(p&&path==nullptr&&length==0&&p->tx_radio==RADIO_TX_PRIMARY);if(type==PAYLOAD_TYPE_PATH)sent.assign(p->payload,p->payload+p->payload_len);pending=send_ok;return send_ok;}
protected:
bool hasPendingRadioWork()const{return pending;}
};
@PATH_BUILDER@
@@ -92,6 +93,7 @@ using Client=mesh::DirectOtaAdminClient;
static uint32_t millis(){return 100;}
struct UsbRole:mesh::Mesh {
Client direct_ota_admin;
void loop(){ @SERVICE_CALL@ }
void dispatch(uint32_t sender_timestamp,void* sender,char* command,char* reply,bool usb_origin){
@USB_GATE@
strcpy(reply,"not a peer command");
@@ -153,8 +155,8 @@ static void authenticate(Client& c,mesh::Mesh& radio,uint32_t now=100){
// by authenticated ciphertext containing empty return path + dummy payload.
assert(radio.sent.size()==10&&radio.sent[0]==0x11&&radio.sent[1]==0x55);
assert(radio.sent[2]==0xCA&&radio.sent[3]==0xFE&&radio.sent[4]==0&&radio.sent[5]==0xFF);
c.service(radio,now+3);assert(radio.type==PAYLOAD_TYPE_PATH); // do not overtake the PATH
radio.pending=false;c.service(radio,now+4);
c.service(radio,now+3,!radio.pending);assert(radio.type==PAYLOAD_TYPE_PATH); // do not overtake the PATH
radio.pending=false;c.service(radio,now+4,!radio.pending);
assert(radio.type==PAYLOAD_TYPE_TXT_MSG&&radio.sent[4]==12);
std::string proof(radio.sent.begin()+5,radio.sent.begin()+8);
assert(std::string(radio.sent.begin()+8,radio.sent.end())=="get public.key");
@@ -247,10 +249,17 @@ int main(){
Client retries;discover(retries,radio,10);command(retries,radio,"ota.peer login "+key+" password",6011);
mesh::Packet direct;uint8_t accepted_login[13]{};uint32_t stamp=60000;memcpy(accepted_login,&stamp,4);accepted_login[7]=3;
retries.onData(radio,&direct,PAYLOAD_TYPE_RESPONSE,accepted_login,sizeof(accepted_login),6012);
radio.pending=false;retries.service(radio,6013);assert(radio.type==PAYLOAD_TYPE_TXT_MSG);
for(uint32_t now:{14013U,22014U}){retries.service(radio,now);assert(radio.type==PAYLOAD_TYPE_PATH);radio.pending=false;retries.service(radio,now+1);}
const int attempts=radio.calls;retries.service(radio,30015);assert(radio.calls==attempts);
radio.pending=false;retries.service(radio,6013,!radio.pending);assert(radio.type==PAYLOAD_TYPE_TXT_MSG);
for(uint32_t now:{14013U,22014U}){radio.pending=false;retries.service(radio,now,!radio.pending);assert(radio.type==PAYLOAD_TYPE_PATH);radio.pending=false;retries.service(radio,now+1,!radio.pending);}
const int attempts=radio.calls;retries.service(radio,30015,!radio.pending);assert(radio.calls==attempts);
retries.tick(36011);assert(!retries.hasSession());
// Execute the production MyMesh loop call with Dispatcher-like protection.
// Only the derived mesh may inspect its protected radio-work state.
UsbRole integration;discover(integration.direct_ota_admin,integration,10);
command(integration.direct_ota_admin,integration,"ota.peer login "+key+" password",6011);
integration.direct_ota_admin.onData(integration,&direct,PAYLOAD_TYPE_RESPONSE,accepted_login,sizeof(accepted_login),6012);
integration.loop();assert(integration.type==PAYLOAD_TYPE_PATH);
integration.pending=false;integration.loop();assert(integration.type==PAYLOAD_TYPE_TXT_MSG);
}
'''
@@ -266,6 +275,9 @@ class DirectOtaAdminClientTest(unittest.TestCase):
end += 1
code = HARNESS.replace('@ECHO_LOOP@',source[start:end])
mesh_source = (ROOT/'examples/simple_repeater/MyMesh.cpp').read_text()
start = mesh_source.index('direct_ota_admin.service(')
end = mesh_source.index(';',start)+1
code = code.replace('@SERVICE_CALL@', mesh_source[start:end])
start = mesh_source.index('if (mesh::DirectOtaAdminClient::isCommand(command))')
opening = mesh_source.index('{',start)
end,depth=opening+1,1