mirror of
https://github.com/agessaman/MeshCore.git
synced 2026-07-19 02:56:33 +00:00
Keep Halo branch focused on direct retry
This commit is contained in:
+127
-363
@@ -28,25 +28,12 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
**Usage:**
|
||||
- `reboot`
|
||||
|
||||
**Note:** No reply is sent.
|
||||
|
||||
---
|
||||
|
||||
### Power-off the node
|
||||
**Usage:**
|
||||
- `poweroff`, or
|
||||
- `shutdown`
|
||||
|
||||
**Note:** No reply is sent.
|
||||
|
||||
---
|
||||
|
||||
### Reset the clock and reboot
|
||||
**Usage:**
|
||||
- `clkreboot`
|
||||
|
||||
**Note:** No reply is sent.
|
||||
|
||||
---
|
||||
|
||||
### Sync the clock with the remote device
|
||||
@@ -128,66 +115,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
### Send flood text to `#repeaters` channel
|
||||
|
||||
**Usage:**
|
||||
- `send text.flood <message>`
|
||||
|
||||
**Notes:**
|
||||
- Sends a `PAYLOAD_TYPE_GRP_TXT` flood message using the built-in `#repeaters` channel key.
|
||||
- Message format is `<node_name>: <message>`.
|
||||
|
||||
---
|
||||
|
||||
### View or change automatic low-battery alerts to `#repeaters`
|
||||
|
||||
**Usage:**
|
||||
- `get battery.alert`
|
||||
- `set battery.alert <state>`
|
||||
- `get battery.alert.low`
|
||||
- `set battery.alert.low <percent>`
|
||||
- `get battery.alert.critical`
|
||||
- `set battery.alert.critical <percent>`
|
||||
|
||||
**Parameters:**
|
||||
- `state`: `on` (enable) or `off` (disable)
|
||||
- `percent`: Battery percentage threshold
|
||||
|
||||
**Default:** `off`
|
||||
|
||||
**Default thresholds:** `20` for `battery.alert.low`, `10` for `battery.alert.critical`
|
||||
|
||||
**Notes:**
|
||||
- When enabled, sends a `#repeaters` flood text warning if voltage is above `1 V` and the battery estimate is below `battery.alert.low`.
|
||||
- Warnings repeat every `24` hours, or every `12` hours below `battery.alert.critical`.
|
||||
- `battery.alert.critical` must be lower than `battery.alert.low`.
|
||||
|
||||
---
|
||||
|
||||
### Get or set recent repeater prefix/SNR
|
||||
**Usage:**
|
||||
- `get recent.repeater`
|
||||
- `get recent.repeater <page>`
|
||||
- `get recent.repeater page <page>`
|
||||
- `set recent.repeater <prefix_hex_6> <snr_db>`
|
||||
|
||||
**Parameters:**
|
||||
- `prefix_hex_6`: Exactly 3 bytes of next-hop prefix in hex (6 chars)
|
||||
- `snr_db`: SNR in dB (supports decimals; stored at x4 precision)
|
||||
- `page`: 1-based page number
|
||||
|
||||
**Notes:**
|
||||
- `set` stores or updates the prefix in the recent repeater table.
|
||||
- Output rows are `prefix,snr` with optional `,l` for locked manual entries.
|
||||
- Rows are sorted by prefix width (3-byte, 2-byte, 1-byte), then SNR descending.
|
||||
- A full direct retry failure lowers the stored SNR by `0.25 dB`.
|
||||
- If a full failure has no row yet, it first seeds the row at the active retry cutoff + `2.5 dB`, then applies the `0.25 dB` penalty.
|
||||
- Serial CLI page size is fixed at `128` rows; choose page with `get recent.repeater <page>`.
|
||||
- Over LoRa remote CLI, page size is fixed at `7` rows; choose page with `get recent.repeater <page>`.
|
||||
- Repeaters can use adjacent entries in this table to short-circuit non-TRACE direct packets when this node appears later in the direct path.
|
||||
|
||||
---
|
||||
|
||||
## Statistics
|
||||
|
||||
### Clear Stats
|
||||
@@ -292,20 +219,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
#### View or change the boosted receive gain mode
|
||||
**Usage:**
|
||||
- `get radio.rxgain`
|
||||
- `set radio.rxgain <state>`
|
||||
|
||||
**Parameters:**
|
||||
- `state`: `on`|`off`
|
||||
|
||||
**Default:** `off`
|
||||
|
||||
**Note:** Only available on SX1262 and SX1268 based boards.
|
||||
|
||||
---
|
||||
|
||||
#### Change the radio parameters for a set duration
|
||||
**Usage:**
|
||||
- `tempradio <freq>,<bw>,<sf>,<cr>,<timeout_mins>`
|
||||
@@ -336,7 +249,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
#### View or change this node's rx boosted gain mode (SX12xx only, v1.14.1+)
|
||||
#### View or change this node's rx boosted gain mode (SX12xx and LR1110, v1.14.1+)
|
||||
**Usage:**
|
||||
- `get radio.rxgain`
|
||||
- `set radio.rxgain <state>`
|
||||
@@ -469,7 +382,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
**Note:** `|` characters are translated to newlines
|
||||
|
||||
**Note:** Requires firmware 1.12.+
|
||||
**Note:** Requires firmware 1.12+
|
||||
|
||||
---
|
||||
|
||||
@@ -518,18 +431,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
#### View or set reboot interval (Repeater and room server)
|
||||
**Usage:**
|
||||
- `get reboot.interval`
|
||||
- `set reboot.interval <hours>`
|
||||
|
||||
**Parameters:**
|
||||
- `hours`: 0-255. 0 is disabled
|
||||
|
||||
**Default:** `0` (disabled)
|
||||
|
||||
---
|
||||
|
||||
### Routing
|
||||
|
||||
#### View or change this node's repeat flag
|
||||
@@ -560,7 +461,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
**Note:** the 'path.hash.mode' sets the low-level ID/hash encoding size used when the repeater adverts. This setting has no impact on what packet ID/hash size this repeater forwards, all sizes should be forwarded on firmware >= 1.14. This feature was added in firmware 1.14
|
||||
|
||||
**Temporary Note:** adverts with ID/hash sizes of 2 or 3 bytes may have limited flood propogation in your network while this feature is new as v1.13.0 firmware and older will drop packets with multibyte path ID/hashes as only 1-byte hashes are suppored. Consider your install base of firmware >=1.14 has reached a criticality for effective network flooding before implementing higher ID/hash sizes.
|
||||
**Temporary Note:** adverts with ID/hash sizes of 2 or 3 bytes may have limited flood propagation in your network while this feature is new as v1.13.0 firmware and older will drop packets with multibyte path ID/hashes as only 1-byte hashes are supported. Consider your install base of firmware >=1.14 has reached a criticality for effective network flooding before implementing higher ID/hash sizes.
|
||||
|
||||
---
|
||||
|
||||
@@ -578,7 +479,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
**Default:** `off`
|
||||
|
||||
**Note:** When it is enabled, repeaters will now reject flood packets which look like they are in a loop. This has been happening recently in some meshes when there is just a single 'bad' repeater firmware out there (prob some forked or custom firmware). If the payload is messed with, then forwarded, the same packet ends up causing a packet storm, repeated up to the max 64 hops. This feature was added in firmware 1.14
|
||||
**Note:** When it is enabled, repeaters will now reject flood packets which look like they are in a loop. This has been happening recently in some meshes when there is just a single 'bad' repeater firmware out there (probably some forked or custom firmware). If the payload is messed with, then forwarded, the same packet ends up causing a packet storm, repeated up to the max 64 hops. This feature was added in firmware 1.14
|
||||
|
||||
**Example:** If preference is `loop.detect minimal`, and a 1-byte path size packet is received, the repeater will see if its own ID/hash is already in the path. If it's already encoded 4 times, it will reject the packet. If the packet uses 2-byte path size, and repeater's own ID/hash is already encoded 2 times, it rejects. If the packet uses 3-byte path size, and the repeater's own ID/hash is already encoded 1 time, it rejects.
|
||||
|
||||
@@ -594,6 +495,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
**Default:** `0.5`
|
||||
|
||||
**Note:** When multiple nearby repeaters all hear the same flood packet, each waits a random amount of time before retransmitting to avoid simultaneous collisions. This factor scales the size of that random window. Higher values reduce collision risk at the cost of added latency. `0` disables the window entirely.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the retransmit delay factor for direct traffic
|
||||
@@ -604,116 +507,9 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
**Parameters:**
|
||||
- `value`: Direct transmit delay factor (0-2)
|
||||
|
||||
**Default:** `0.3`
|
||||
**Default:** `0.2`
|
||||
|
||||
**Note:** Direct retry waits include the same airtime-based randomized delay calculation as direct retransmits, so this factor also controls retry echo windows.
|
||||
|
||||
---
|
||||
|
||||
#### View or change whether direct retries use the recent repeater SNR gate
|
||||
**Usage:**
|
||||
- `get direct.retry.heard`
|
||||
- `set direct.retry.heard <state>`
|
||||
|
||||
**Parameters:**
|
||||
- `state`: `on`|`off`
|
||||
|
||||
**Default:** `on`
|
||||
|
||||
**Note:** When enabled, the recent repeater table is the only direct retry eligibility gate. Prefixes missing from the table are assumed reachable; prefixes in the table below the active SNR gate are blocked. Neighbor data is not used.
|
||||
|
||||
---
|
||||
|
||||
#### View or change adaptive coding rate for direct retry packets
|
||||
**Usage:**
|
||||
- `get direct.retry.cr`
|
||||
- `set direct.retry.cr <cr4_min>,<cr5_min>,<cr7_min>,<cr8_max>`
|
||||
- `set direct.retry.cr off`
|
||||
|
||||
**Parameters:**
|
||||
- `cr4_min`: SNR in dB where retry packets use `CR4`
|
||||
- `cr5_min`: SNR in dB where retry packets use `CR5`
|
||||
- `cr7_min`: SNR in dB where retry packets use `CR7`
|
||||
- `cr8_max`: SNR in dB where retry packets use `CR8`
|
||||
|
||||
**Default:** `10.0,7.5,2.5,2.5`
|
||||
|
||||
**Note:** DM retry packets use the next-hop SNR from a recent repeater table entry to pick a local transmit coding rate; if no recent entry is available, retry packets use `CR5`. With the default, SNR `10.0 dB` and up uses `CR4`, SNR `7.5 dB` and up uses `CR5`, SNR `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. `CR6` is never selected. Use `set direct.retry.cr off` to disable adaptive coding-rate overrides. If adaptive selection chooses `CR4`, retries after the third attempt use `CR5`.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the SNR margin used for direct retry eligibility
|
||||
**Usage:**
|
||||
- `get direct.retry.margin`
|
||||
- `set direct.retry.margin <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Rooftop preset margin in dB above the SF-specific receive floor (minimum `0`, maximum `40`, quarter-dB precision, default `5.0`)
|
||||
|
||||
**Default:** `5.0`
|
||||
|
||||
**Note:** `get direct.retry.margin` returns the active preset's effective margin. The retry gate uses the active SF floor of `SF5=-2.5`, `SF6=-5`, `SF7=-7.5`, `SF8=-10`, `SF9=-12.5`, `SF10=-15`, `SF11=-17.5`, `SF12=-20`, then adds this margin.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the retry preset
|
||||
**Usage:**
|
||||
- `get retry.preset`
|
||||
- `set retry.preset <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2`
|
||||
|
||||
**Default:** `rooftop` (`1`)
|
||||
|
||||
**Presets:**
|
||||
- `infra` (`0`): `275 ms` direct base wait, `4` direct retries, `150 ms` added per direct retry, SNR gate is SF floor + `15 dB`; flood retry defaults to `1` retry and path gate `1`
|
||||
- `rooftop` (`1`): `175 ms` direct base wait, `15` direct retries, `100 ms` added per direct retry, SNR gate is SF floor + `5 dB`; flood retry defaults to `3` retries and path gate `2`
|
||||
- `mobile` (`2`): `175 ms` direct base wait, `15` direct retries, `50 ms` added per direct retry, SNR gate is the SF floor; flood retry defaults to `3` retries and path gate `1`
|
||||
|
||||
**Note:** Selecting a preset copies those values into the direct retry settings and resets flood retry defaults. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, `flood.retry.count`, or `flood.retry.path` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the number of direct retry attempts
|
||||
**Usage:**
|
||||
- `get direct.retry.count`
|
||||
- `set direct.retry.count <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Maximum retry attempts after initial TX (`1`-`15`)
|
||||
|
||||
**Default:** `15`
|
||||
|
||||
**Note:** The effective value is capped by total direct path length: paths of `3` hops or less use at most `8` retries, `4` hops use at most `12`, and `5+` hops use at most `15`. A queued resend is canceled early when the next-hop echo is heard.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the base direct retry wait (milliseconds)
|
||||
**Usage:**
|
||||
- `get direct.retry.base`
|
||||
- `set direct.retry.base <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Base wait in milliseconds (`10`-`5000`)
|
||||
|
||||
**Default:** `175`
|
||||
|
||||
**Note:** The configured base is added to packet-length airtime and `direct.txdelay` jitter. Preset defaults are already reduced to account for the added `direct.txdelay` component.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the direct retry per-attempt add time (milliseconds)
|
||||
**Usage:**
|
||||
- `get direct.retry.step`
|
||||
- `set direct.retry.step <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Milliseconds added per retry attempt (`0`-`5000`)
|
||||
|
||||
**Default:** `100`
|
||||
|
||||
**Note:** This controls the linear add after the first retry wait. For example, `base=300` and `step=150` adds `0`, `150`, `300`, ... ms across retry attempts.
|
||||
**Note:** Same collision-avoidance random window as `txdelay`, but applied to direct (non-flood, routed) traffic. The default is lower because direct packets are addressed to a specific next hop, so far fewer nodes compete to retransmit them.
|
||||
|
||||
---
|
||||
|
||||
@@ -727,6 +523,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
**Default:** `0.0`
|
||||
|
||||
**Note:** When enabled, repeaters that received a flood packet with a weak signal are held in a delay queue before processing, while those that received it with a strong signal process it immediately. This gives strong-signal paths forwarding priority. By the time weak-signal nodes process their copy, the packet may have already propagated and will be suppressed as a duplicate, reducing redundant retransmissions.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the duty cycle limit
|
||||
@@ -854,114 +652,18 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
#### View or change the number of flood retry attempts
|
||||
#### Limit the number of hops for an unscoped flood message
|
||||
**Usage:**
|
||||
- `get flood.retry.count`
|
||||
- `set flood.retry.count <value>`
|
||||
- `get flood.max.unscoped`
|
||||
- `set flood.max.unscoped <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Maximum retry attempts after initial flood TX (`0`-`15`)
|
||||
- `value`: Maximum flood hop count (0-64) for a packet without a scope (no region set)
|
||||
|
||||
**Default:** `3` for `rooftop` and `mobile`, `1` for `infra`
|
||||
**Default:** `0xFF` - indicates it hasn't been set, will track flood.max until it is.
|
||||
|
||||
**Note:** `0` disables flood retry.
|
||||
**Note:** An alternative to `region denyf *`, setting `flood.max.unscoped` to a lower value such as `3` would allow for local unscoped messages to propagate, while preventing noisy neighbors from flooding a local region.
|
||||
|
||||
---
|
||||
|
||||
#### View or change the flood retry path gate
|
||||
**Usage:**
|
||||
- `get flood.retry.path`
|
||||
- `set flood.retry.path <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Maximum flood path length eligible for retry (`0`-`63`), or `off` to disable the gate
|
||||
|
||||
**Default:** `2` for `rooftop`, `1` for `infra` and `mobile`
|
||||
|
||||
**Note:** Prefixes in `flood.retry.ignore` do not count toward this path length.
|
||||
|
||||
---
|
||||
|
||||
#### View or change whether advert packets are flood-retried
|
||||
**Usage:**
|
||||
- `get flood.retry.advert`
|
||||
- `set flood.retry.advert <state>`
|
||||
|
||||
**Parameters:**
|
||||
- `state`: `on` or `off`
|
||||
|
||||
**Default:** `off`
|
||||
|
||||
**Note:** When this is `off`, node advert packets (`PAYLOAD_TYPE_ADVERT`, type `4`) are not queued for flood retry.
|
||||
|
||||
---
|
||||
|
||||
#### View or change flood retry target prefixes
|
||||
**Usage:**
|
||||
- `get flood.retry.prefixes`
|
||||
- `set flood.retry.prefixes <prefixes>`
|
||||
|
||||
**Parameters:**
|
||||
- `prefixes`: Comma-separated 3-byte hex prefixes, such as `A1B2C3,D4E5F6`; use `none` or `off` to clear
|
||||
|
||||
**Default:** `none`
|
||||
|
||||
**Note:** Prefixes are stored as 3 bytes. Flood retry skips packets whose path already contains a matching target prefix. When prefixes are configured, only a downstream echo from one of those target prefixes cancels a queued retry; when no prefixes are configured, any downstream echo cancels it. Matching works with 3-byte, 2-byte, or 1-byte flood paths by comparing the matching leading bytes.
|
||||
|
||||
---
|
||||
|
||||
#### View or change flood retry bridge mode
|
||||
**Usage:**
|
||||
- `get flood.retry.bridge`
|
||||
- `set flood.retry.bridge <state>`
|
||||
|
||||
**Parameters:**
|
||||
- `state`: `on` or `off`
|
||||
|
||||
**Default:** `off`
|
||||
|
||||
**Note:** Bridge mode uses bucket definitions instead of the single `flood.retry.prefixes` target list. It also has an implicit unconfigured catch-all bucket. If a flood comes from one fresh configured bucket, retry continues until every other fresh configured bucket plus the catch-all bucket has been heard or `flood.retry.count` is exhausted. If a flood comes from an unconfigured or pathless source, retry targets every fresh configured bucket. This means one configured bucket bridges between that bucket and everything else. Prefixes in `flood.retry.ignore` never count as heard bridge targets.
|
||||
|
||||
---
|
||||
|
||||
#### View or change flood retry bridge buckets
|
||||
**Usage:**
|
||||
- `get flood.retry.bucket.<bucket>`
|
||||
- `set flood.retry.bucket <bucket> <prefixes>`
|
||||
|
||||
**Parameters:**
|
||||
- `bucket`: Bucket number (`1`-`6`)
|
||||
- `prefixes`: Up to 17 comma-separated 3-byte hex prefixes, such as `AABBCC,223344`; use `none` or `off` to clear
|
||||
|
||||
**Default:** all buckets empty
|
||||
|
||||
**Note:** Prefixes are stored as 3 bytes but match 3-byte, 2-byte, and 1-byte flood paths by comparing leading bytes. Bucket prefixes are included in bridge retry logic only if they were heard in the recent repeater table within the last hour.
|
||||
|
||||
---
|
||||
|
||||
#### View or change flood retry ignored prefixes
|
||||
**Usage:**
|
||||
- `get flood.retry.ignore`
|
||||
- `set flood.retry.ignore <prefixes>`
|
||||
|
||||
**Parameters:**
|
||||
- `prefixes`: Up to 8 comma-separated 3-byte hex prefixes, such as `AABBCC,223344`; use `none` or `off` to clear
|
||||
|
||||
**Default:** empty
|
||||
|
||||
**Note:** In non-bridge retry, an echo whose last hop matches an ignored prefix does not cancel a queued retry as successful. In bridge mode, ignored prefixes do not count as a heard bridge bucket or as the implicit catch-all bucket when bridge retry decides whether every target has repeated the flood.
|
||||
|
||||
---
|
||||
|
||||
#### Limit the number of hops for an advert flood message
|
||||
**Usage:**
|
||||
- `get flood.max.advert`
|
||||
- `set flood.max.advert <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Maximum flood hop count (0-64) for an advert packet
|
||||
|
||||
**Default:** `8`
|
||||
|
||||
---
|
||||
|
||||
@@ -991,32 +693,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
#### View or set direct path overrides for the current remote client
|
||||
**Usage:**
|
||||
- `get outpath`
|
||||
- `set outpath <hop1_hex,hop2_hex,...>`
|
||||
- `set outpath direct`
|
||||
- `set outpath clear`
|
||||
- `set outpath flood`
|
||||
- `get altpath`
|
||||
- `set altpath <hop1_hex,hop2_hex,...>`
|
||||
- `set altpath clear`
|
||||
|
||||
**Parameters:**
|
||||
- `hopN_hex`: Hop hash, `2`, `4`, or `6` hex characters. All hops must use the same width.
|
||||
|
||||
**Notes:**
|
||||
- These commands require remote client context; they target the caller's ACL entry.
|
||||
- The path hash size is inferred from the hop hash width.
|
||||
- `outpath` overrides the primary direct route used for replies to the caller.
|
||||
- `direct` sets a zero-hop direct route for a caller reachable without repeaters.
|
||||
- `clear` forgets the current direct path and allows normal path discovery to repopulate it.
|
||||
- `flood` forces replies to use flood packets until the client logs in again.
|
||||
- `altpath` is an optional second direct route used for duplicate response attempts.
|
||||
- `set altpath clear` removes the duplicate route so only one reply is sent.
|
||||
|
||||
---
|
||||
|
||||
#### View or change this room server's 'read-only' flag
|
||||
**Usage:**
|
||||
- `get allow.read.only`
|
||||
@@ -1104,27 +780,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
#### View or set the direct path override for the current remote client
|
||||
**Usage:**
|
||||
- `get outpath`
|
||||
- `set outpath <hop1_hex,hop2_hex,...>`
|
||||
- `set outpath direct`
|
||||
- `set outpath clear`
|
||||
- `set outpath flood`
|
||||
|
||||
**Parameters:**
|
||||
- `hopN_hex`: Hop hash, `2`, `4`, or `6` hex characters. All hops must use the same width.
|
||||
|
||||
**Notes:**
|
||||
- These commands require remote client context (they target the caller's ACL entry).
|
||||
- The path hash size is inferred from the hop hash width.
|
||||
- `outpath` overrides the primary direct route used for replies to the caller.
|
||||
- `direct` sets a zero-hop direct route for a caller reachable without repeaters.
|
||||
- `clear` forgets the current direct path and allows normal path discovery to repopulate it.
|
||||
- `flood` forces replies to use flood packets until the client logs in again.
|
||||
|
||||
---
|
||||
|
||||
#### Create a new region
|
||||
**Usage:**
|
||||
- `region put <name> [parent_name]`
|
||||
@@ -1135,6 +790,47 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
#### Define region hierarchy (single line)
|
||||
**Usage:**
|
||||
- `region def <token> [<token> ...]`
|
||||
|
||||
**Parameters (tokens):** Space-separated. A logical **cursor** starts at the wildcard `*`.
|
||||
|
||||
- **`name`** — Create `name` as a child of the current cursor (equivalent to `region put name` with the cursor as parent). Cursor moves to `name`.
|
||||
- **`name|jump`** *(or `name,jump`)* — Create `name` as a child of the current cursor, then move the cursor to `jump` (must already exist on the node, or have been created earlier in this command). `jump` is **not** the parent of `name`; use this form to pop back up and start another branch.
|
||||
|
||||
**Behavior:** Each created region defaults to flood-allowed (same as `region put`). The reply is the resulting region tree (same format as bare `region`); review it before running `region save` to persist. On error, the reply is `Err - ...` and any regions placed before the failure remain on the node, just like a partial chain of `region put`.
|
||||
|
||||
**Existing regions:** `region def` does not clear the existing tree — if a name already exists, its parent is updated to the current cursor; otherwise a new region is created. To start from scratch, `region remove` the unwanted regions first.
|
||||
|
||||
**Limits:** Repeater serial accepts one line up to **160 characters**. For larger trees, split across multiple `region def` commands; the cursor resets to `*` between commands, so lead the next command with `child|ancestor` to reposition. Each token splits at most once on `|` — `region def a|b|c|d` is not a flat-list shorthand; see the flat-list example below.
|
||||
|
||||
**Example — linear chain** (each token becomes a child of the previous):
|
||||
```
|
||||
region def a b c d e
|
||||
region save
|
||||
```
|
||||
|
||||
**Example — branched tree** (equivalent to `region put a`, `region put b a`, `region put c b`, `region put d c`, `region put e b`, `region put f e`):
|
||||
```
|
||||
region def a b c d|b e f
|
||||
region save
|
||||
```
|
||||
|
||||
**Example — error and partial state:**
|
||||
```
|
||||
region def a b c|nope d
|
||||
```
|
||||
The reply is `Err - unknown jump: nope`. `a`, `b`, and `c` were placed before the failure; `d` was not. Run `region` to inspect, then re-run with a corrected jump or repair with `region remove` / `region put`.
|
||||
|
||||
**Example — flat list** (each region a child of `*`). Use `|*` after each token to pop the cursor back to the root before the next token:
|
||||
```
|
||||
region def a|* b|* c|* d|* e|* f
|
||||
region save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Remove a region
|
||||
**Usage:**
|
||||
- `region remove <name>`
|
||||
@@ -1155,7 +851,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
**Parameters:**
|
||||
- `filter`: `allowed`|`denied`
|
||||
|
||||
**Note:** Requires firmware 1.12.+
|
||||
**Note:** Requires firmware 1.12+
|
||||
|
||||
---
|
||||
|
||||
@@ -1312,7 +1008,7 @@ region save
|
||||
|
||||
---
|
||||
|
||||
#### View or change thevalue of a sensor
|
||||
#### View or change the value of a sensor
|
||||
**Usage:**
|
||||
- `sensor get <key>`
|
||||
- `sensor set <key> <value>`
|
||||
@@ -1432,3 +1128,71 @@ region save
|
||||
**Note:** Returns an error on boards without power management support.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Halo Direct Retry Commands
|
||||
|
||||
These commands are available on the Halo direct-message retry branch. See `docs/halo_settings.md` for operating guidance and examples.
|
||||
|
||||
### View or change the retry preset
|
||||
**Usage:**
|
||||
- `get retry.preset`
|
||||
- `set retry.preset <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2`
|
||||
|
||||
---
|
||||
|
||||
### View or change whether direct retries use the recent repeater SNR gate
|
||||
**Usage:**
|
||||
- `get direct.retry.heard`
|
||||
- `set direct.retry.heard <state>`
|
||||
|
||||
**Parameters:**
|
||||
- `state`: `on`|`off`
|
||||
|
||||
---
|
||||
|
||||
### View or change direct retry timing and count
|
||||
**Usage:**
|
||||
- `get direct.retry.margin`
|
||||
- `set direct.retry.margin <value>`
|
||||
- `get direct.retry.count`
|
||||
- `set direct.retry.count <1-15>`
|
||||
- `get direct.retry.base`
|
||||
- `set direct.retry.base <10-5000>`
|
||||
- `get direct.retry.step`
|
||||
- `set direct.retry.step <0-5000>`
|
||||
|
||||
---
|
||||
|
||||
### View or change adaptive coding rate for direct retry packets
|
||||
**Usage:**
|
||||
- `get direct.retry.cr`
|
||||
- `set direct.retry.cr <cr4_min>,<cr5_min>,<cr7_min>,<cr8_max>`
|
||||
- `set direct.retry.cr off`
|
||||
|
||||
---
|
||||
|
||||
### Get or set recent repeater prefix/SNR
|
||||
**Usage:**
|
||||
- `get recent.repeater`
|
||||
- `get recent.repeater <page>`
|
||||
- `get recent.repeater page <page>`
|
||||
- `set recent.repeater <prefix_hex_6> <snr_db>`
|
||||
- `clear recent.repeater`
|
||||
|
||||
---
|
||||
|
||||
### View or change direct reply path overrides
|
||||
**Usage:**
|
||||
- `get outpath`
|
||||
- `set outpath <hops>`
|
||||
- `set outpath direct`
|
||||
- `set outpath clear`
|
||||
- `set outpath flood`
|
||||
- `get altpath`
|
||||
- `set altpath <hops>`
|
||||
- `set altpath clear`
|
||||
|
||||
@@ -85,9 +85,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
uint32_t last_millis;
|
||||
uint64_t uptime_millis;
|
||||
unsigned long next_local_advert, next_flood_advert;
|
||||
unsigned long next_battery_alert_check;
|
||||
unsigned long last_battery_alert_sent;
|
||||
bool battery_alert_sent;
|
||||
bool _logging;
|
||||
NodePrefs _prefs;
|
||||
ClientACL acl;
|
||||
@@ -102,15 +99,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
RegionEntry* recv_pkt_region;
|
||||
TransportKey default_scope;
|
||||
RateLimiter discover_limiter, anon_limiter;
|
||||
struct FloodRetryBridgeState {
|
||||
uint8_t key[MAX_HASH_SIZE];
|
||||
uint8_t source_bucket;
|
||||
uint8_t target_mask;
|
||||
uint8_t heard_mask;
|
||||
uint8_t progress_marker;
|
||||
bool active;
|
||||
};
|
||||
mutable FloodRetryBridgeState flood_retry_bridge_states[MAX_FLOOD_RETRY_SLOTS];
|
||||
uint32_t pending_discover_tag;
|
||||
unsigned long pending_discover_until;
|
||||
bool region_load_active;
|
||||
@@ -140,25 +128,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
uint8_t getDirectRetryPreset() const;
|
||||
uint8_t getDirectRetryConfiguredMaxAttempts() const;
|
||||
uint32_t getDirectRetryAttemptStepMillis() const;
|
||||
bool hasFloodRetryPrefixes() const;
|
||||
bool floodRetryPrefixMatches(const mesh::Packet* packet) const;
|
||||
bool floodRetryLastHopMatches(const mesh::Packet* packet) const;
|
||||
bool floodRetryPrefixIgnored(const uint8_t* prefix, uint8_t prefix_len) const;
|
||||
uint8_t floodRetryEffectivePathLength(const mesh::Packet* packet, uint8_t max_hops = 0xFF) const;
|
||||
bool floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const;
|
||||
int floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh,
|
||||
bool include_other) const;
|
||||
int floodRetryBucketForPathHop(const uint8_t* prefix, uint8_t prefix_len, uint8_t hop,
|
||||
uint8_t progress_marker) const;
|
||||
int floodRetrySourceBucket(const mesh::Packet* packet) const;
|
||||
uint8_t floodRetryBridgeTargetMask(uint8_t source_bucket) const;
|
||||
uint8_t floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket,
|
||||
uint8_t progress_marker) const;
|
||||
FloodRetryBridgeState* floodRetryBridgeStateFor(const mesh::Packet* packet, bool create) const;
|
||||
void clearFloodRetryBridgeState(const mesh::Packet* packet);
|
||||
void refreshFloodRetryHeardRecent(const mesh::Packet* packet);
|
||||
void formatFloodRetryPath(char* dest, size_t dest_len, const mesh::Packet* packet) const;
|
||||
bool formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Packet* packet) const;
|
||||
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
|
||||
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
|
||||
uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
|
||||
@@ -166,8 +135,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
uint8_t handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
|
||||
int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len);
|
||||
mesh::Packet* createSelfAdvert();
|
||||
bool sendRepeatersFloodText(const char* text);
|
||||
void checkBatteryAlert();
|
||||
|
||||
File openAppend(const char* fname);
|
||||
bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]);
|
||||
@@ -196,12 +163,6 @@ protected:
|
||||
uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override;
|
||||
uint32_t getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) override;
|
||||
void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override;
|
||||
bool allowFloodRetry(const mesh::Packet* packet) const override;
|
||||
void onFloodRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override;
|
||||
bool hasFloodRetryTargetPrefix(const mesh::Packet* packet) const override;
|
||||
uint8_t getFloodRetryMaxPathLength(const mesh::Packet* packet) const override;
|
||||
uint8_t getFloodRetryMaxAttempts(const mesh::Packet* packet) const override;
|
||||
bool isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress_marker) const override;
|
||||
|
||||
int getInterferenceThreshold() const override {
|
||||
return _prefs.interference_threshold;
|
||||
|
||||
@@ -159,9 +159,4 @@ void loop() {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (the_mesh.getNodePrefs()->reboot_interval > 0 &&
|
||||
the_mesh.millisHasNowPassed(the_mesh.getNodePrefs()->reboot_interval * 3600000)) {
|
||||
board.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
-308
@@ -5,8 +5,6 @@ namespace mesh {
|
||||
|
||||
static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15;
|
||||
static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15;
|
||||
static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT = 3;
|
||||
static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX = 15;
|
||||
|
||||
static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) {
|
||||
uint8_t code = flags & 0x03;
|
||||
@@ -44,19 +42,6 @@ void Mesh::begin() {
|
||||
_direct_retries[i].queued = false;
|
||||
_direct_retries[i].active = false;
|
||||
}
|
||||
for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) {
|
||||
_flood_retries[i].packet = NULL;
|
||||
_flood_retries[i].trigger_packet = NULL;
|
||||
_flood_retries[i].retry_started_at = 0;
|
||||
_flood_retries[i].retry_at = 0;
|
||||
_flood_retries[i].retry_delay = 0;
|
||||
_flood_retries[i].retry_attempts_sent = 0;
|
||||
_flood_retries[i].priority = 0;
|
||||
_flood_retries[i].progress_marker = 0;
|
||||
_flood_retries[i].waiting_final_echo = false;
|
||||
_flood_retries[i].queued = false;
|
||||
_flood_retries[i].active = false;
|
||||
}
|
||||
Dispatcher::begin();
|
||||
}
|
||||
|
||||
@@ -93,37 +78,6 @@ void Mesh::loop() {
|
||||
clearDirectRetrySlot(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) {
|
||||
if (!_flood_retries[i].active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_flood_retries[i].waiting_final_echo) {
|
||||
if (!millisHasNowPassed(_flood_retries[i].retry_at)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t elapsed_millis = _flood_retries[i].retry_started_at == 0
|
||||
? 0
|
||||
: (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at);
|
||||
onFloodRetryEvent("failed_all_tries", _flood_retries[i].packet, elapsed_millis, _flood_retries[i].retry_attempts_sent);
|
||||
onFloodRetryEvent("failure", _flood_retries[i].packet, elapsed_millis, _flood_retries[i].retry_attempts_sent);
|
||||
clearFloodRetrySlot(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_flood_retries[i].queued || !millisHasNowPassed(_flood_retries[i].retry_at)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isFloodRetryQueued(_flood_retries[i].packet)) {
|
||||
if (_flood_retries[i].packet == getOutboundInFlight()) {
|
||||
continue;
|
||||
}
|
||||
clearFloodRetrySlot(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Mesh::allowPacketForward(const mesh::Packet* packet) {
|
||||
@@ -152,39 +106,16 @@ uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_
|
||||
// Keep the historical linear spacing while allowing the base wait to vary by platform/profile.
|
||||
return base + ((uint32_t)attempt_idx * 100UL);
|
||||
}
|
||||
bool Mesh::allowFloodRetry(const Packet* packet) const {
|
||||
return true;
|
||||
}
|
||||
bool Mesh::hasFloodRetryTargetPrefix(const Packet* packet) const {
|
||||
return false;
|
||||
}
|
||||
uint8_t Mesh::getFloodRetryMaxPathLength(const Packet* packet) const {
|
||||
return 2;
|
||||
}
|
||||
uint8_t Mesh::getFloodRetryMaxAttempts(const Packet* packet) const {
|
||||
return FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT;
|
||||
}
|
||||
uint32_t Mesh::getFloodRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) {
|
||||
if (packet == NULL) {
|
||||
return _radio->getEstAirtimeFor(MAX_TRANS_UNIT);
|
||||
}
|
||||
|
||||
uint32_t max_packet_airtime = _radio->getEstAirtimeFor(MAX_TRANS_UNIT);
|
||||
uint32_t packet_airtime = _radio->getEstAirtimeFor(packet->getRawLength());
|
||||
return max_packet_airtime + (20UL * packet_airtime);
|
||||
}
|
||||
uint8_t Mesh::getExtraAckTransmitCount() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Mesh::onSendComplete(Packet* packet) {
|
||||
armDirectRetryOnSendComplete(packet);
|
||||
armFloodRetryOnSendComplete(packet);
|
||||
}
|
||||
|
||||
void Mesh::onSendFail(Packet* packet) {
|
||||
clearPendingDirectRetryOnSendFail(packet);
|
||||
clearPendingFloodRetryOnSendFail(packet);
|
||||
}
|
||||
|
||||
uint32_t Mesh::getCADFailRetryDelay() const {
|
||||
@@ -202,8 +133,6 @@ int Mesh::searchChannelsByHash(const uint8_t* hash, GroupChannel channels[], int
|
||||
DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
|
||||
if (pkt->isRouteDirect()) {
|
||||
cancelDirectRetryOnEcho(pkt);
|
||||
} else if (pkt->isRouteFlood()) {
|
||||
cancelFloodRetryOnEcho(pkt);
|
||||
}
|
||||
|
||||
if (pkt->isRouteDirect() && pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) {
|
||||
@@ -523,7 +452,6 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) {
|
||||
|
||||
uint32_t d = getRetransmitDelay(packet);
|
||||
uint8_t priority = packet->getPathHashCount();
|
||||
maybeScheduleFloodRetry(packet, priority);
|
||||
// as this propagates outwards, give it lower and lower priority
|
||||
return ACTION_RETRANSMIT_DELAYED(priority, d); // give priority to closer sources, than ones further away
|
||||
}
|
||||
@@ -952,242 +880,6 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) {
|
||||
_direct_retries[slot_idx].active = true;
|
||||
}
|
||||
|
||||
void Mesh::clearFloodRetrySlot(int idx) {
|
||||
if (_flood_retries[idx].waiting_final_echo && _flood_retries[idx].packet != NULL) {
|
||||
releasePacket(_flood_retries[idx].packet);
|
||||
}
|
||||
_flood_retries[idx].packet = NULL;
|
||||
_flood_retries[idx].trigger_packet = NULL;
|
||||
_flood_retries[idx].retry_started_at = 0;
|
||||
_flood_retries[idx].retry_at = 0;
|
||||
_flood_retries[idx].retry_delay = 0;
|
||||
_flood_retries[idx].retry_attempts_sent = 0;
|
||||
_flood_retries[idx].priority = 0;
|
||||
_flood_retries[idx].progress_marker = 0;
|
||||
_flood_retries[idx].waiting_final_echo = false;
|
||||
_flood_retries[idx].queued = false;
|
||||
_flood_retries[idx].active = false;
|
||||
}
|
||||
|
||||
bool Mesh::isFloodRetryQueued(const Packet* packet) const {
|
||||
for (int i = 0; i < _mgr->getOutboundTotal(); i++) {
|
||||
if (_mgr->getOutboundByIdx(i) == packet) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mesh::isFloodRetryEchoTarget(const Packet* packet, uint8_t progress_marker) const {
|
||||
return packet->isRouteFlood() && packet->getPathHashCount() > progress_marker;
|
||||
}
|
||||
|
||||
bool Mesh::cancelFloodRetryOnEcho(const Packet* packet) {
|
||||
uint8_t recv_key[MAX_HASH_SIZE];
|
||||
packet->calculatePacketHash(recv_key);
|
||||
|
||||
bool cleared = false;
|
||||
for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) {
|
||||
if (!_flood_retries[i].active || memcmp(recv_key, _flood_retries[i].retry_key, MAX_HASH_SIZE) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (!isFloodRetryEchoTarget(packet, _flood_retries[i].progress_marker)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t echo_millis = _flood_retries[i].retry_started_at == 0
|
||||
? 0
|
||||
: (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at);
|
||||
uint8_t retry_attempt = _flood_retries[i].waiting_final_echo
|
||||
? _flood_retries[i].retry_attempts_sent
|
||||
: _flood_retries[i].retry_attempts_sent + 1;
|
||||
onFloodRetryEvent("good", packet, echo_millis, retry_attempt);
|
||||
|
||||
if (_flood_retries[i].queued) {
|
||||
for (int j = 0; j < _mgr->getOutboundTotal(); j++) {
|
||||
if (_mgr->getOutboundByIdx(j) == _flood_retries[i].packet) {
|
||||
Packet* pending = _mgr->removeOutboundByIdx(j);
|
||||
if (pending) {
|
||||
releasePacket(pending);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
clearFloodRetrySlot(i);
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
return cleared;
|
||||
}
|
||||
|
||||
void Mesh::armFloodRetryOnSendComplete(const Packet* packet) {
|
||||
for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) {
|
||||
if (!_flood_retries[i].active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_flood_retries[i].queued) {
|
||||
if (_flood_retries[i].packet != packet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t elapsed_millis = _flood_retries[i].retry_started_at == 0
|
||||
? 0
|
||||
: (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at);
|
||||
onFloodRetryEvent("resent", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1);
|
||||
_flood_retries[i].retry_attempts_sent++;
|
||||
|
||||
uint8_t max_attempts = getFloodRetryMaxAttempts(packet);
|
||||
if (max_attempts < 1) {
|
||||
max_attempts = 1;
|
||||
} else if (max_attempts > FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX) {
|
||||
max_attempts = FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX;
|
||||
}
|
||||
if (_flood_retries[i].retry_attempts_sent >= max_attempts) {
|
||||
Packet* final_wait = obtainNewPacket();
|
||||
if (final_wait == NULL) {
|
||||
onFloodRetryEvent("dropped_no_packet", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent);
|
||||
onFloodRetryEvent("failure", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent);
|
||||
clearFloodRetrySlot(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
*final_wait = *packet;
|
||||
_flood_retries[i].packet = final_wait;
|
||||
_flood_retries[i].retry_at = futureMillis(_flood_retries[i].retry_delay);
|
||||
_flood_retries[i].waiting_final_echo = true;
|
||||
_flood_retries[i].queued = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
Packet* retry = obtainNewPacket();
|
||||
if (retry == NULL) {
|
||||
onFloodRetryEvent("dropped_no_packet", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1);
|
||||
onFloodRetryEvent("failure", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1);
|
||||
clearFloodRetrySlot(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
*retry = *packet;
|
||||
uint32_t retry_delay = getFloodRetryAttemptDelay(packet, _flood_retries[i].retry_attempts_sent);
|
||||
if (queueOutboundPacket(retry, _flood_retries[i].priority, retry_delay)) {
|
||||
_flood_retries[i].packet = retry;
|
||||
_flood_retries[i].retry_delay = retry_delay;
|
||||
_flood_retries[i].retry_at = futureMillis(retry_delay);
|
||||
_flood_retries[i].retry_started_at = _ms->getMillis();
|
||||
_flood_retries[i].waiting_final_echo = false;
|
||||
onFloodRetryEvent("queued", retry, retry_delay, _flood_retries[i].retry_attempts_sent + 1);
|
||||
} else {
|
||||
onFloodRetryEvent("dropped_queue_full", retry, retry_delay, _flood_retries[i].retry_attempts_sent + 1);
|
||||
onFloodRetryEvent("failure", retry, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1);
|
||||
releasePacket(retry);
|
||||
clearFloodRetrySlot(i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_flood_retries[i].trigger_packet != packet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Packet* retry = obtainNewPacket();
|
||||
if (retry == NULL) {
|
||||
onFloodRetryEvent("dropped_no_packet", packet, _flood_retries[i].retry_delay, 1);
|
||||
onFloodRetryEvent("failure", packet, 0, 1);
|
||||
clearFloodRetrySlot(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
*retry = *packet;
|
||||
if (queueOutboundPacket(retry, _flood_retries[i].priority, _flood_retries[i].retry_delay)) {
|
||||
unsigned long now = _ms->getMillis();
|
||||
_flood_retries[i].packet = retry;
|
||||
_flood_retries[i].trigger_packet = NULL;
|
||||
_flood_retries[i].queued = true;
|
||||
_flood_retries[i].waiting_final_echo = false;
|
||||
_flood_retries[i].retry_at = futureMillis(_flood_retries[i].retry_delay);
|
||||
_flood_retries[i].retry_started_at = now;
|
||||
onFloodRetryEvent("queued", retry, _flood_retries[i].retry_delay, 1);
|
||||
} else {
|
||||
onFloodRetryEvent("dropped_queue_full", retry, _flood_retries[i].retry_delay, 1);
|
||||
onFloodRetryEvent("failure", retry, 0, 1);
|
||||
releasePacket(retry);
|
||||
clearFloodRetrySlot(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::clearPendingFloodRetryOnSendFail(const Packet* packet) {
|
||||
for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) {
|
||||
if (!_flood_retries[i].active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_flood_retries[i].queued) {
|
||||
if (_flood_retries[i].packet == packet) {
|
||||
onFloodRetryEvent("dropped_send_fail", packet, 0, _flood_retries[i].retry_attempts_sent + 1);
|
||||
onFloodRetryEvent("failure", packet, 0, _flood_retries[i].retry_attempts_sent + 1);
|
||||
clearFloodRetrySlot(i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_flood_retries[i].trigger_packet == packet) {
|
||||
onFloodRetryEvent("dropped_send_fail", packet, 0, 1);
|
||||
onFloodRetryEvent("failure", packet, 0, 1);
|
||||
clearFloodRetrySlot(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::maybeScheduleFloodRetry(const Packet* packet, uint8_t priority) {
|
||||
if (packet == NULL || !packet->isRouteFlood() || hasFloodRetryTargetPrefix(packet)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t max_path_len = getFloodRetryMaxPathLength(packet);
|
||||
if (max_path_len != FLOOD_RETRY_PATH_GATE_DISABLED && packet->getPathHashCount() > max_path_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t max_attempts = getFloodRetryMaxAttempts(packet);
|
||||
if (max_attempts == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int slot_idx = -1;
|
||||
for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) {
|
||||
if (!_flood_retries[i].active) {
|
||||
slot_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot_idx < 0) {
|
||||
onFloodRetryEvent("dropped_no_slot", packet, 0, 0);
|
||||
onFloodRetryEvent("failure", packet, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allowFloodRetry(packet)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t retry_delay = getFloodRetryAttemptDelay(packet, 0);
|
||||
packet->calculatePacketHash(_flood_retries[slot_idx].retry_key);
|
||||
_flood_retries[slot_idx].packet = NULL;
|
||||
_flood_retries[slot_idx].trigger_packet = const_cast<Packet*>(packet);
|
||||
_flood_retries[slot_idx].retry_started_at = 0;
|
||||
_flood_retries[slot_idx].retry_at = 0;
|
||||
_flood_retries[slot_idx].retry_delay = retry_delay;
|
||||
_flood_retries[slot_idx].retry_attempts_sent = 0;
|
||||
_flood_retries[slot_idx].priority = priority;
|
||||
_flood_retries[slot_idx].progress_marker = packet->getPathHashCount();
|
||||
_flood_retries[slot_idx].waiting_final_echo = false;
|
||||
_flood_retries[slot_idx].queued = false;
|
||||
_flood_retries[slot_idx].active = true;
|
||||
}
|
||||
|
||||
Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) {
|
||||
if (app_data_len > MAX_ADVERT_DATA_SIZE) return NULL;
|
||||
|
||||
|
||||
-68
@@ -8,14 +8,6 @@ namespace mesh {
|
||||
#define MAX_DIRECT_RETRY_SLOTS 6
|
||||
#endif
|
||||
|
||||
#ifndef MAX_FLOOD_RETRY_SLOTS
|
||||
#define MAX_FLOOD_RETRY_SLOTS 6
|
||||
#endif
|
||||
|
||||
#ifndef FLOOD_RETRY_PATH_GATE_DISABLED
|
||||
#define FLOOD_RETRY_PATH_GATE_DISABLED 0xFF
|
||||
#endif
|
||||
|
||||
class GroupChannel {
|
||||
public:
|
||||
uint8_t hash[PATH_HASH_SIZE];
|
||||
@@ -54,26 +46,7 @@ class Mesh : public Dispatcher {
|
||||
bool active;
|
||||
};
|
||||
|
||||
struct FloodRetryEntry {
|
||||
Packet* packet;
|
||||
Packet* trigger_packet;
|
||||
unsigned long retry_started_at;
|
||||
unsigned long retry_at;
|
||||
uint32_t retry_delay;
|
||||
uint8_t retry_attempts_sent;
|
||||
uint8_t retry_key[MAX_HASH_SIZE];
|
||||
uint8_t priority;
|
||||
uint8_t progress_marker;
|
||||
bool waiting_final_echo;
|
||||
bool queued;
|
||||
bool active;
|
||||
};
|
||||
|
||||
RTCClock* _rtc;
|
||||
RNG* _rng;
|
||||
MeshTables* _tables;
|
||||
DirectRetryEntry _direct_retries[MAX_DIRECT_RETRY_SLOTS];
|
||||
FloodRetryEntry _flood_retries[MAX_FLOOD_RETRY_SLOTS];
|
||||
|
||||
void removePathPrefix(Packet* packet, uint8_t prefix_count);
|
||||
void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis);
|
||||
@@ -87,12 +60,6 @@ class Mesh : public Dispatcher {
|
||||
uint8_t& progress_marker, bool& expect_path_growth) const;
|
||||
bool canDecodeDirectPayloadForSelf(const Packet* packet);
|
||||
void maybeScheduleDirectRetry(const Packet* packet, uint8_t priority);
|
||||
void clearFloodRetrySlot(int idx);
|
||||
bool isFloodRetryQueued(const Packet* packet) const;
|
||||
bool cancelFloodRetryOnEcho(const Packet* packet);
|
||||
void armFloodRetryOnSendComplete(const Packet* packet);
|
||||
void clearPendingFloodRetryOnSendFail(const Packet* packet);
|
||||
void maybeScheduleFloodRetry(const Packet* packet, uint8_t priority);
|
||||
//void routeRecvAcks(Packet* packet, uint32_t delay_millis);
|
||||
DispatcherAction forwardMultipartDirect(Packet* pkt);
|
||||
|
||||
@@ -156,41 +123,6 @@ protected:
|
||||
*/
|
||||
virtual uint32_t getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx);
|
||||
|
||||
/**
|
||||
* \brief Decide whether a FLOOD packet should retry when no downstream echo is overheard.
|
||||
*/
|
||||
virtual bool allowFloodRetry(const Packet* packet) const;
|
||||
|
||||
/**
|
||||
* \brief Return true when this FLOOD packet already carries an application-defined target prefix.
|
||||
*/
|
||||
virtual bool hasFloodRetryTargetPrefix(const Packet* packet) const;
|
||||
|
||||
/**
|
||||
* \returns maximum flood path hash count eligible for retry, or FLOOD_RETRY_PATH_GATE_DISABLED.
|
||||
*/
|
||||
virtual uint8_t getFloodRetryMaxPathLength(const Packet* packet) const;
|
||||
|
||||
/**
|
||||
* \returns maximum number of FLOOD retry transmissions after the initial TX.
|
||||
*/
|
||||
virtual uint8_t getFloodRetryMaxAttempts(const Packet* packet) const;
|
||||
|
||||
/**
|
||||
* \brief Return true when a received FLOOD echo is enough to cancel a pending retry.
|
||||
*/
|
||||
virtual bool isFloodRetryEchoTarget(const Packet* packet, uint8_t progress_marker) const;
|
||||
|
||||
/**
|
||||
* \returns delay before a specific flood retry attempt, where attempt_idx=0 is the first retry.
|
||||
*/
|
||||
virtual uint32_t getFloodRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx);
|
||||
|
||||
/**
|
||||
* \brief Optional hook for logging flood-retry lifecycle events.
|
||||
*/
|
||||
virtual void onFloodRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { }
|
||||
|
||||
/**
|
||||
* \returns number of extra (Direct) ACK transmissions wanted.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user