* various changes for CLI support via companion radio

This commit is contained in:
Scott Powell
2025-02-27 12:51:00 +11:00
parent 189ed79d46
commit 1209d54d2e
6 changed files with 79 additions and 11 deletions

View File

@@ -438,12 +438,12 @@ protected:
return false;
}
void onMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
void queueMessage(const ContactInfo& from, uint8_t txt_type, uint8_t path_len, uint32_t sender_timestamp, const char *text) {
int i = 0;
out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV;
memcpy(&out_frame[i], from.id.pub_key, 6); i += 6; // just 6-byte prefix
out_frame[i++] = path_len;
out_frame[i++] = TXT_TYPE_PLAIN;
out_frame[i++] = txt_type;
memcpy(&out_frame[i], &sender_timestamp, 4); i += 4;
int tlen = strlen(text); // TODO: UTF-8 ??
if (i + tlen > MAX_FRAME_SIZE) {
@@ -461,6 +461,14 @@ protected:
}
}
void onMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
queueMessage(from, TXT_TYPE_PLAIN, path_len, sender_timestamp, text);
}
void onCommandDataRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
queueMessage(from, TXT_TYPE_CLI_DATA, path_len, sender_timestamp, text);
}
void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) override {
int i = 0;
out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV;
@@ -677,12 +685,18 @@ public:
memcpy(&msg_timestamp, &cmd_frame[i], 4); i += 4;
uint8_t* pub_key_prefix = &cmd_frame[i]; i += 6;
ContactInfo* recipient = lookupContactByPubKey(pub_key_prefix, 6);
if (recipient && attempt < 4 && txt_type == TXT_TYPE_PLAIN) {
if (recipient && attempt < 4 && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) {
char *text = (char *) &cmd_frame[i];
int tlen = len - i;
uint32_t est_timeout;
text[tlen] = 0; // ensure null
int result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack_crc, est_timeout);
int result;
if (txt_type == TXT_TYPE_CLI_DATA) {
result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout);
expected_ack_crc = 0; // no Ack expected
} else {
result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack_crc, est_timeout);
}
// TODO: add expected ACK to table
if (result == MSG_SEND_FAILED) {
writeErrFrame();

View File

@@ -469,7 +469,7 @@ protected:
timestamp++;
}
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
temp[4] = (TXT_TYPE_PLAIN << 2); // TODO: change this to TXT_TYPE_CLI_DATA soon
temp[4] = (TXT_TYPE_CLI_DATA << 2); // NOTE: legacy was: TXT_TYPE_PLAIN
// calc expected ACK reply
//mesh::Utils::sha256((uint8_t *)&expected_ack_crc, 4, temp, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
@@ -581,9 +581,15 @@ public:
}
}
void handleCommand(uint32_t sender_timestamp, const char* command, char reply[]) {
void handleCommand(uint32_t sender_timestamp, const char* command, char* reply) {
while (*command == ' ') command++; // skip leading spaces
if (strlen(command) > 4 && command[2] == '|') { // optional prefix (for companion radio CLI)
memcpy(reply, command, 3); // reflect the prefix back
reply += 3;
command += 3;
}
if (memcmp(command, "reboot", 6) == 0) {
board.reboot(); // doesn't return
} else if (memcmp(command, "advert", 6) == 0) {

View File

@@ -423,6 +423,7 @@ protected:
if (flags == TXT_TYPE_CLI_DATA) {
if (client->is_admin) {
handleAdminCommand(sender_timestamp, (const char *) &data[5], (char *) &temp[5]);
temp[4] = (TXT_TYPE_CLI_DATA << 2); // attempt and flags, (NOTE: legacy was: TXT_TYPE_PLAIN)
send_ack = true;
} else {
temp[5] = 0; // no reply
@@ -452,7 +453,6 @@ protected:
now++;
}
memcpy(temp, &now, 4); // mostly an extra blob to help make packet_hash unique
temp[4] = (TXT_TYPE_PLAIN << 2); // attempt and flags
// calc expected ACK reply
//mesh::Utils::sha256((uint8_t *)&expected_ack_crc, 4, temp, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
@@ -604,9 +604,15 @@ public:
}
}
void handleAdminCommand(uint32_t sender_timestamp, const char* command, char reply[]) {
void handleAdminCommand(uint32_t sender_timestamp, const char* command, char* reply) {
while (*command == ' ') command++; // skip leading spaces
if (strlen(command) > 4 && command[2] == '|') { // optional prefix (for companion radio CLI)
memcpy(reply, command, 3); // reflect the prefix back
reply += 3;
command += 3;
}
if (memcmp(command, "reboot", 6) == 0) {
board.reboot(); // doesn't return
} else if (memcmp(command, "advert", 6) == 0) {

View File

@@ -259,6 +259,9 @@ protected:
}
}
void onCommandDataRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
}
void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) override {
if (in_path_len < 0) {
Serial.printf("PUBLIC CHANNEL MSG -> (Direct!)\n");

View File

@@ -99,13 +99,13 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
if (type == PAYLOAD_TYPE_TXT_MSG && len > 5) {
uint32_t timestamp;
memcpy(&timestamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong)
uint flags = data[4]; // message attempt number, and other flags
uint flags = data[4] >> 2; // message attempt number, and other flags
// len can be > original length, but 'text' will be padded with zeroes
data[len] = 0; // need to make a C string again, with null terminator
//if ( ! alreadyReceived timestamp ) {
if ((flags >> 2) == TXT_TYPE_PLAIN) {
if (flags == TXT_TYPE_PLAIN) {
onMessageRecv(from, packet->isRouteFlood() ? packet->path_len : 0xFF, timestamp, (const char *) &data[5]); // let UI know
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
@@ -126,8 +126,19 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
}
}
}
} else if (flags == TXT_TYPE_CLI_DATA) {
onCommandDataRecv(from, packet->isRouteFlood() ? packet->path_len : 0xFF, timestamp, (const char *) &data[5]); // let UI know
// NOTE: no ack expected for CLI_DATA replies
if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK
mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, 0, NULL, 0);
if (path) sendFlood(path);
}
} else if (flags == TXT_TYPE_SIGNED_PLAIN) {
// TODO
} else {
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported message type: %u", (uint32_t) (flags >> 2));
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported message type: %u", (uint32_t) flags);
}
} else if (type == PAYLOAD_TYPE_RESPONSE && len > 0) {
onContactResponse(from, data, len);
@@ -228,6 +239,32 @@ int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp,
return rc;
}
int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout) {
int text_len = strlen(text);
if (text_len > MAX_TEXT_LEN) return MSG_SEND_FAILED;
uint8_t temp[5+MAX_TEXT_LEN+1];
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
temp[4] = (attempt & 3) | (TXT_TYPE_CLI_DATA << 2);
memcpy(&temp[5], text, text_len + 1);
auto pkt = createDatagram(PAYLOAD_TYPE_TXT_MSG, recipient.id, recipient.shared_secret, temp, 5 + text_len);
if (pkt == NULL) return MSG_SEND_FAILED;
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
int rc;
if (recipient.out_path_len < 0) {
sendFlood(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;
}
bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len) {
uint8_t temp[5+MAX_TEXT_LEN+32];
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique

View File

@@ -86,6 +86,7 @@ protected:
virtual bool processAck(const uint8_t *data) = 0;
virtual void onContactPathUpdated(const ContactInfo& contact) = 0;
virtual void onMessageRecv(const ContactInfo& contact, uint8_t path_len, uint32_t sender_timestamp, const char *text) = 0;
virtual void onCommandDataRecv(const ContactInfo& contact, uint8_t path_len, uint32_t sender_timestamp, const char *text) = 0;
virtual uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const = 0;
virtual uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const = 0;
virtual void onSendTimeout() = 0;
@@ -111,6 +112,7 @@ protected:
public:
mesh::Packet* createSelfAdvert(const char* name, double lat=0.0, double lon=0.0);
int sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout);
int sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout);
bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len);
int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout);
int sendStatusRequest(const ContactInfo& recipient, uint32_t& est_timeout);