91 lines
2.5 KiB
Python
91 lines
2.5 KiB
Python
"""LRU table of ESP-NOW peer MACs seen on uplink."""
|
|
|
|
from espnow_wire import BROADCAST_MAC
|
|
|
|
try:
|
|
from settings import Settings
|
|
except ImportError:
|
|
Settings = None
|
|
|
|
# ESP32 counts the broadcast peer toward the ~20 peer limit.
|
|
_RESERVED_FOR_BROADCAST = 1
|
|
|
|
|
|
class PeerTable:
|
|
def __init__(self, max_peers=20):
|
|
limit = max(1, int(max_peers) - _RESERVED_FOR_BROADCAST)
|
|
self._max = limit
|
|
self._order = []
|
|
self._names = {}
|
|
|
|
def _evict_lru(self, esp):
|
|
if not self._order:
|
|
return
|
|
old = self._order.pop(0)
|
|
self._names.pop(old, None)
|
|
if esp is not None:
|
|
try:
|
|
esp.del_peer(old)
|
|
except OSError:
|
|
pass
|
|
|
|
def touch(self, mac_bytes, name=None, esp=None):
|
|
"""Note a peer from uplink (LRU). Pass ``esp`` so evictions free ESP-NOW slots."""
|
|
if not mac_bytes or len(mac_bytes) != 6:
|
|
return
|
|
if mac_bytes == BROADCAST_MAC:
|
|
return
|
|
if mac_bytes in self._order:
|
|
self._order.remove(mac_bytes)
|
|
elif len(self._order) >= self._max:
|
|
self._evict_lru(esp)
|
|
self._order.append(mac_bytes)
|
|
if name:
|
|
self._names[mac_bytes] = str(name)
|
|
if esp is not None:
|
|
try:
|
|
esp.add_peer(mac_bytes)
|
|
except OSError:
|
|
pass
|
|
|
|
def ensure_peer(self, esp, mac_bytes):
|
|
"""Register ``mac_bytes`` on ESP-NOW, evicting LRU peers when the table is full."""
|
|
if not mac_bytes or len(mac_bytes) != 6:
|
|
return False
|
|
if mac_bytes == BROADCAST_MAC:
|
|
try:
|
|
esp.add_peer(mac_bytes)
|
|
except OSError:
|
|
pass
|
|
return True
|
|
if mac_bytes in self._order:
|
|
self._order.remove(mac_bytes)
|
|
self._order.append(mac_bytes)
|
|
else:
|
|
while len(self._order) >= self._max:
|
|
self._evict_lru(esp)
|
|
self._order.append(mac_bytes)
|
|
# Uplink touch() only updates LRU; always add_peer before unicast send.
|
|
try:
|
|
esp.add_peer(mac_bytes)
|
|
except OSError as err:
|
|
print("add_peer failed", err)
|
|
return False
|
|
return True
|
|
|
|
def peers(self):
|
|
return list(self._order)
|
|
|
|
def is_broadcast_mac(self, mac_bytes):
|
|
return mac_bytes == BROADCAST_MAC
|
|
|
|
|
|
def load_max_peers():
|
|
if Settings is None:
|
|
return 20
|
|
try:
|
|
s = Settings()
|
|
return int(s.get("max_peers", 20))
|
|
except Exception:
|
|
return 20
|