mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-02 10:51:57 +00:00
* added region load/save support
* SUBSCRIBE now with timeout param (default 30 mins, max ~18 hours)
This commit is contained in:
@@ -322,8 +322,10 @@ uint8_t SensorMesh::handleRequest(ClientInfo* from, uint32_t sender_timestamp, u
|
|||||||
return ofs;
|
return ofs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (req_type == REQ_TYPE_SUBSCRIBE && payload_len >= 2 && (perms & PERM_ACL_ROLE_MASK) >= PERM_ACL_READ_ONLY) {
|
if (req_type == REQ_TYPE_SUBSCRIBE && payload_len >= 4 && (perms & PERM_ACL_ROLE_MASK) >= PERM_ACL_READ_ONLY) {
|
||||||
uint8_t reserved = payload[0];
|
uint16_t timeout_secs;
|
||||||
|
memcpy(&timeout_secs, &payload[0], 2);
|
||||||
|
uint8_t reserved = payload[2];
|
||||||
RegionEntry* r;
|
RegionEntry* r;
|
||||||
if (recv_pkt_region && !recv_pkt_region->isWildcard()) { // use request scope
|
if (recv_pkt_region && !recv_pkt_region->isWildcard()) { // use request scope
|
||||||
r = recv_pkt_region;
|
r = recv_pkt_region;
|
||||||
@@ -331,16 +333,19 @@ uint8_t SensorMesh::handleRequest(ClientInfo* from, uint32_t sender_timestamp, u
|
|||||||
r = region_map.getDefaultRegion();
|
r = region_map.getDefaultRegion();
|
||||||
}
|
}
|
||||||
from->extra.sensor.scope_region_id = r ? r->id : 0;
|
from->extra.sensor.scope_region_id = r ? r->id : 0;
|
||||||
from->extra.sensor.min_deltas_len = payload[1];
|
from->extra.sensor.expiry_timestamp = r ? getRTCClock()->getCurrentTime() + timeout_secs : 0;
|
||||||
|
from->extra.sensor.min_deltas_len = payload[3];
|
||||||
// NOTE: curr impl truncates LPP min_diffs spec (re-do if better impl is needed)
|
// NOTE: curr impl truncates LPP min_diffs spec (re-do if better impl is needed)
|
||||||
memcpy(from->extra.sensor.min_deltas, &payload[2], min(sizeof(from->extra.sensor.min_deltas), (size_t)payload[1]));
|
memcpy(from->extra.sensor.min_deltas, &payload[4], min(sizeof(from->extra.sensor.min_deltas), (size_t)payload[3]));
|
||||||
|
|
||||||
getRNG()->random(&reply_data[4], 2); // just some entropy for better packet-hash uniqueness
|
memcpy(&reply_data[4], &from->extra.sensor.expiry_timestamp, 4); // reply with actual expiry timestamp (or 0 for error)
|
||||||
strcpy((char *)&reply_data[6], r ? r->name : ""); // reply with name of scope that will be used
|
strcpy((char *)&reply_data[8], r ? r->name : ""); // reply with name of scope that will be used
|
||||||
return 6 + strlen((char *)&reply_data[6]);
|
return 6 + strlen((char *)&reply_data[8]);
|
||||||
}
|
}
|
||||||
if (req_type == REQ_TYPE_UNSUBSCRIBE && (perms & PERM_ACL_ROLE_MASK) >= PERM_ACL_READ_ONLY) {
|
if (req_type == REQ_TYPE_UNSUBSCRIBE && (perms & PERM_ACL_ROLE_MASK) >= PERM_ACL_READ_ONLY) {
|
||||||
from->extra.sensor.scope_region_id = 0;
|
from->extra.sensor.scope_region_id = 0;
|
||||||
|
from->extra.sensor.expiry_timestamp = 0;
|
||||||
|
// REVISIT: maybe return some stats, eg total number of telemetry pushes since SUBSCRIBE?
|
||||||
reply_data[4] = 0; // success
|
reply_data[4] = 0; // success
|
||||||
getRNG()->random(&reply_data[5], 3); // just some entropy for better packet-hash uniqueness
|
getRNG()->random(&reply_data[5], 3); // just some entropy for better packet-hash uniqueness
|
||||||
return 8;
|
return 8;
|
||||||
@@ -441,6 +446,25 @@ int SensorMesh::getAGCResetInterval() const {
|
|||||||
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
|
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SensorMesh::startRegionsLoad() {
|
||||||
|
temp_map.resetFrom(region_map); // rebuild regions in a temp instance
|
||||||
|
memset(load_stack, 0, sizeof(load_stack));
|
||||||
|
load_stack[0] = &temp_map.getWildcard();
|
||||||
|
region_load_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SensorMesh::saveRegions() {
|
||||||
|
return region_map.save(_fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SensorMesh::onDefaultRegionChanged(const RegionEntry* r) {
|
||||||
|
if (r) {
|
||||||
|
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||||
|
} else {
|
||||||
|
memset(default_scope.key, 0, sizeof(default_scope.key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood) {
|
uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood) {
|
||||||
ClientInfo* client;
|
ClientInfo* client;
|
||||||
if (data[0] == 0) { // blank password, just check if sender is in ACL
|
if (data[0] == 0) { // blank password, just check if sender is in ACL
|
||||||
@@ -454,7 +478,7 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t*
|
|||||||
} else {
|
} else {
|
||||||
if (strcmp((char *) data, _prefs.password) != 0) { // check for valid admin password
|
if (strcmp((char *) data, _prefs.password) != 0) { // check for valid admin password
|
||||||
#if MESH_DEBUG
|
#if MESH_DEBUG
|
||||||
MESH_DEBUG_PRINTLN("Invalid password: %s", &data[4]);
|
MESH_DEBUG_PRINTLN("Invalid password: %s", &data[0]);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -491,6 +515,40 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t*
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SensorMesh::handleCommand(ClientInfo* from, uint32_t sender_timestamp, char* command, char* reply) {
|
void SensorMesh::handleCommand(ClientInfo* from, uint32_t sender_timestamp, char* command, char* reply) {
|
||||||
|
if (region_load_active) {
|
||||||
|
if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation
|
||||||
|
region_map = temp_map; // copy over the temp instance as new current map
|
||||||
|
region_load_active = false;
|
||||||
|
|
||||||
|
sprintf(reply, "OK - loaded %d regions", region_map.getCount());
|
||||||
|
} else {
|
||||||
|
char *np = command;
|
||||||
|
while (*np == ' ') np++; // skip indent
|
||||||
|
int indent = np - command;
|
||||||
|
|
||||||
|
char *ep = np;
|
||||||
|
while (RegionMap::is_name_char(*ep)) ep++;
|
||||||
|
if (*ep) { *ep++ = 0; } // set null terminator for end of name
|
||||||
|
|
||||||
|
while (*ep && *ep != 'F') ep++; // look for (optional) flags
|
||||||
|
|
||||||
|
if (indent > 0 && indent < 8 && strlen(np) > 0) {
|
||||||
|
auto parent = load_stack[indent - 1];
|
||||||
|
if (parent) {
|
||||||
|
auto old = region_map.findByName(np);
|
||||||
|
auto nw = temp_map.putRegion(np, parent->id, old ? old->id : 0); // carry-over the current ID (if name already exists)
|
||||||
|
if (nw) {
|
||||||
|
nw->flags = old ? old->flags : (*ep == 'F' ? 0 : REGION_DENY_FLOOD); // carry-over flags from curr
|
||||||
|
|
||||||
|
load_stack[indent] = nw; // keep pointers to parent regions, to resolve parent_id's
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply[0] = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (*command == ' ') command++; // skip leading spaces
|
while (*command == ' ') command++; // skip leading spaces
|
||||||
|
|
||||||
if (strlen(command) > 4 && command[2] == '|') { // optional prefix (for companion radio CLI)
|
if (strlen(command) > 4 && command[2] == '|') { // optional prefix (for companion radio CLI)
|
||||||
@@ -548,15 +606,25 @@ void SensorMesh::handleCommand(ClientInfo* from, uint32_t sender_timestamp, char
|
|||||||
} else { // use default scope
|
} else { // use default scope
|
||||||
r = region_map.getDefaultRegion();
|
r = region_map.getDefaultRegion();
|
||||||
}
|
}
|
||||||
if (command[3] == ' ') {
|
// defaults:
|
||||||
// compile params as LPP data, eg. "sub 1:0.2V"
|
from->extra.sensor.min_deltas_len = 0; // no minimums (telemetry just needs to CHANGE)
|
||||||
from->extra.sensor.min_deltas_len = compileLPPSpec(&command[4], from->extra.sensor.min_deltas, sizeof(from->extra.sensor.min_deltas));
|
uint16_t timeout_secs = 30*60; // expires after 30 mins
|
||||||
} else {
|
if (command[3] == ' ') { // eg. "sub 300 1:0.2V"
|
||||||
from->extra.sensor.min_deltas_len = 0; // no minimums (telemetry just needs to CHANGE)
|
char* cp = &command[4];
|
||||||
|
while (*cp >= '0' && *cp <= '9') cp++;
|
||||||
|
if (cp > &command[4]) {
|
||||||
|
timeout_secs = atoi(&command[4]);
|
||||||
|
if (*cp == ' ') {
|
||||||
|
cp++; // skip the space
|
||||||
|
from->extra.sensor.min_deltas_len = compileLPPSpec(cp, from->extra.sensor.min_deltas, sizeof(from->extra.sensor.min_deltas));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
from->extra.sensor.scope_region_id = r ? r->id : 0;
|
from->extra.sensor.scope_region_id = r ? r->id : 0;
|
||||||
if (from->extra.sensor.scope_region_id) {
|
from->extra.sensor.expiry_timestamp = r ? getRTCClock()->getCurrentTime() + timeout_secs : 0;
|
||||||
strcpy(reply, "OK - subscribed");
|
if (from->extra.sensor.expiry_timestamp) {
|
||||||
|
DateTime dt = DateTime(from->extra.sensor.expiry_timestamp);
|
||||||
|
sprintf(reply, "OK - sub expires: %02d:%02d (UTC)", dt.hour(), dt.minute());
|
||||||
} else {
|
} else {
|
||||||
strcpy(reply, "Err - region scope needed");
|
strcpy(reply, "Err - region scope needed");
|
||||||
}
|
}
|
||||||
@@ -853,7 +921,7 @@ void SensorMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) {
|
|||||||
|
|
||||||
SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
|
SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
|
||||||
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
|
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
|
||||||
region_map(key_store),
|
region_map(key_store), temp_map(key_store),
|
||||||
_cli(board, rtc, sensors, region_map, acl, &_prefs, this),
|
_cli(board, rtc, sensors, region_map, acl, &_prefs, this),
|
||||||
telemetry(MAX_PACKET_PAYLOAD - 4)
|
telemetry(MAX_PACKET_PAYLOAD - 4)
|
||||||
{
|
{
|
||||||
@@ -864,6 +932,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
|
|||||||
set_radio_at = revert_radio_at = 0;
|
set_radio_at = revert_radio_at = 0;
|
||||||
recv_pkt_region = NULL;
|
recv_pkt_region = NULL;
|
||||||
prev_telem_size = 0;
|
prev_telem_size = 0;
|
||||||
|
region_load_active = false;
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
_prefs.airtime_factor = 1.0;
|
_prefs.airtime_factor = 1.0;
|
||||||
@@ -1110,6 +1179,7 @@ void SensorMesh::loop() {
|
|||||||
if (c->permissions == 0 || c->extra.sensor.scope_region_id == 0) continue; // skip deleted entries, or Not subscribed to deltas
|
if (c->permissions == 0 || c->extra.sensor.scope_region_id == 0) continue; // skip deleted entries, or Not subscribed to deltas
|
||||||
RegionEntry* r = region_map.findById(c->extra.sensor.scope_region_id);
|
RegionEntry* r = region_map.findById(c->extra.sensor.scope_region_id);
|
||||||
if (r == NULL) continue; // unknown region scope
|
if (r == NULL) continue; // unknown region scope
|
||||||
|
if (curr > c->extra.sensor.expiry_timestamp) continue; // subscription now expired
|
||||||
if (telemHasChanged(c->extra.sensor.min_deltas, c->extra.sensor.min_deltas_len)) {
|
if (telemHasChanged(c->extra.sensor.min_deltas, c->extra.sensor.min_deltas_len)) {
|
||||||
TransportKey scope;
|
TransportKey scope;
|
||||||
if (region_map.getTransportKeysFor(*r, &scope, 1) > 0) {
|
if (region_map.getTransportKeysFor(*r, &scope, 1) > 0) {
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ public:
|
|||||||
void saveIdentity(const mesh::LocalIdentity& new_id) override;
|
void saveIdentity(const mesh::LocalIdentity& new_id) override;
|
||||||
void clearStats() override { }
|
void clearStats() override { }
|
||||||
void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override;
|
void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override;
|
||||||
|
void startRegionsLoad() override;
|
||||||
|
bool saveRegions() override;
|
||||||
|
void onDefaultRegionChanged(const RegionEntry* r) override;
|
||||||
|
|
||||||
float getTelemValue(uint8_t channel, uint8_t type);
|
float getTelemValue(uint8_t channel, uint8_t type);
|
||||||
|
|
||||||
@@ -148,8 +151,9 @@ private:
|
|||||||
uint8_t prev_telem_size;
|
uint8_t prev_telem_size;
|
||||||
uint8_t prev_telem[MAX_PACKET_PAYLOAD - 4];
|
uint8_t prev_telem[MAX_PACKET_PAYLOAD - 4];
|
||||||
TransportKeyStore key_store;
|
TransportKeyStore key_store;
|
||||||
RegionMap region_map;
|
RegionMap region_map, temp_map;
|
||||||
RegionEntry* recv_pkt_region;
|
RegionEntry* recv_pkt_region;
|
||||||
|
RegionEntry* load_stack[8];
|
||||||
TransportKey default_scope;
|
TransportKey default_scope;
|
||||||
uint32_t last_read_time;
|
uint32_t last_read_time;
|
||||||
int matching_peer_indexes[MAX_SEARCH_RESULTS];
|
int matching_peer_indexes[MAX_SEARCH_RESULTS];
|
||||||
@@ -160,6 +164,7 @@ private:
|
|||||||
float pending_bw;
|
float pending_bw;
|
||||||
uint8_t pending_sf;
|
uint8_t pending_sf;
|
||||||
uint8_t pending_cr;
|
uint8_t pending_cr;
|
||||||
|
bool region_load_active;
|
||||||
|
|
||||||
bool telemHasChanged(const uint8_t* min_deltas, uint8_t min_deltas_len);
|
bool telemHasChanged(const uint8_t* min_deltas, uint8_t min_deltas_len);
|
||||||
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
|
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ struct ClientInfo {
|
|||||||
uint8_t push_failures;
|
uint8_t push_failures;
|
||||||
} room;
|
} room;
|
||||||
struct {
|
struct {
|
||||||
|
uint32_t expiry_timestamp; // epoch seconds
|
||||||
uint16_t scope_region_id; // scope to use when sending telemetry to this client/subscriber
|
uint16_t scope_region_id; // scope to use when sending telemetry to this client/subscriber
|
||||||
uint8_t min_deltas_len;
|
uint8_t min_deltas_len;
|
||||||
uint8_t min_deltas[14]; // LPP encoded
|
uint8_t min_deltas[14]; // LPP encoded
|
||||||
|
|||||||
Reference in New Issue
Block a user