224 lines
7.2 KiB
Python
224 lines
7.2 KiB
Python
from sys import getsizeof
|
|
import meshtastic
|
|
import meshtastic.serial_interface
|
|
from pubsub import pub
|
|
from Packets.Packets import Packets as Packets
|
|
from Packets.SinglePacket import SinglePacket
|
|
import time
|
|
from threading import Thread
|
|
from Components.daisy import Catch, Cache
|
|
import sys
|
|
import logging
|
|
|
|
# from Filters.base import Filter
|
|
import msgpack
|
|
|
|
import asyncio
|
|
import random
|
|
|
|
|
|
class Transmitter:
|
|
def __init__(self, device, filter, onodeID, cache, catch, cryptographyInfo, cLog):
|
|
self.cLog = cLog
|
|
self.cryptographyInfo = cryptographyInfo
|
|
self.filter = filter
|
|
self.tcache = cache
|
|
self.tcatch = catch
|
|
self.html = False
|
|
self.notConnected = True
|
|
self.messages = {}
|
|
self.acks = {}
|
|
# self.threads = {}
|
|
self.onodeID = onodeID
|
|
# Be careful with this
|
|
self.cpid = 0
|
|
self.tasks = {}
|
|
# TODO: use node id to deliver directly
|
|
pub.subscribe(self.onReceive, "meshtastic.receive")
|
|
pub.subscribe(self.onConnection, "meshtastic.connection.established")
|
|
self.interface = meshtastic.serial_interface.SerialInterface(device)
|
|
i = 0
|
|
while self.notConnected:
|
|
if i % 5000000 == 0:
|
|
self.cLog(20, "Waiting for node initialization...")
|
|
i += 1
|
|
self.cLog(20, "Initialized")
|
|
|
|
# TODO: Sending packets across multiple nodes/load balancing/distributed packet transmission/reception
|
|
def onReceive(self, packet, interface):
|
|
asyncio.new_event_loop().run_until_complete(self.filter.sieve(packet))
|
|
self.tcache.refresh()
|
|
|
|
async def sendAnnounce(self):
|
|
await self.addPackets(
|
|
msgpack.dumps(
|
|
{
|
|
"onodeID": self.onodeID,
|
|
"mnodeID": self.interface.localNode.nodeNum,
|
|
}
|
|
),
|
|
self.onodeID,
|
|
None,
|
|
True,
|
|
None,
|
|
packetsClass=0,
|
|
)
|
|
|
|
def onConnection(self, interface, topic=pub.AUTO_TOPIC):
|
|
# self.send("connect".encode("utf-8"))
|
|
# time.sleep(3)
|
|
asyncio.run(self.sendAnnounce())
|
|
self.notConnected = False
|
|
|
|
def responseCheck(self, packet):
|
|
rid = packet["decoded"]["requestId"]
|
|
if packet["decoded"]["routing"]["errorReason"] == "MAX_RETRANSMIT":
|
|
self.cLog(20, "Got ack error")
|
|
self.acks[str(rid)] = False
|
|
else:
|
|
self.acks[str(rid)] = True
|
|
|
|
# TODO: Threaded send method
|
|
|
|
def send(self, packet, recipientNode=False):
|
|
interface = self.interface
|
|
if recipientNode == False:
|
|
pid = interface.sendData(
|
|
packet, wantAck=True, onResponse=self.responseCheck
|
|
)
|
|
else:
|
|
pid = interface.sendData(
|
|
packet,
|
|
destinationId=recipientNode,
|
|
wantAck=True,
|
|
onResponse=self.responseCheck,
|
|
)
|
|
|
|
# Can I use waitForAckNak on cpid?
|
|
self.cpid = pid.id
|
|
return True
|
|
|
|
async def awaitResponse(self, pid):
|
|
for i in range(120):
|
|
await asyncio.sleep(1)
|
|
if str(pid) in self.acks:
|
|
break
|
|
return True
|
|
|
|
async def initNodeDH(self, dhefOb, recipientNode, onodeID):
|
|
await self.addPackets(
|
|
msgpack.dumps(
|
|
{"params": dhefOb.getParamsBytes(), "publicKey": dhefOb.publicKey}
|
|
),
|
|
self.onodeID,
|
|
000000,
|
|
000000,
|
|
onodeID,
|
|
directID=recipientNode,
|
|
packetsClass=3,
|
|
)
|
|
|
|
def awaitFullResponse(self, pid):
|
|
for i in range(1_000_000_000):
|
|
time.sleep(5)
|
|
if pid in self.messages.keys():
|
|
if self.messages[pid]["finished"]:
|
|
break
|
|
return True
|
|
|
|
async def addPackets(
|
|
self,
|
|
data,
|
|
sender,
|
|
senderName,
|
|
recipient,
|
|
recipientNode,
|
|
directID=False,
|
|
packetsClass=None,
|
|
encrypt=False,
|
|
):
|
|
interface = self.interface
|
|
tp = Packets(
|
|
data,
|
|
sender,
|
|
senderName,
|
|
recipient,
|
|
recipientNode,
|
|
packetsClass=packetsClass,
|
|
)
|
|
# print(sys.getsizeof(tp.packets[0]))
|
|
# print(tp.packets[0])
|
|
for p in tp.packets:
|
|
# time.sleep(5)
|
|
if recipientNode == None:
|
|
# print("sending none")
|
|
# print(p)
|
|
self.send(p)
|
|
else:
|
|
# print(p)
|
|
# print(recipientNode)
|
|
self.cLog(10, "Sending target: " + str(directID))
|
|
if directID != False:
|
|
recipientNode = directID
|
|
self.send(p, recipientNode=recipientNode)
|
|
awaitTask = asyncio.create_task(self.awaitResponse(self.cpid))
|
|
await asyncio.sleep(1)
|
|
currentTask = {
|
|
"ob": awaitTask,
|
|
"pid": str(self.cpid),
|
|
"packet": p,
|
|
"retry": False,
|
|
}
|
|
self.tasks[str(self.cpid)] = currentTask
|
|
|
|
async def progressCheck(self):
|
|
# interface = self.interface
|
|
while True:
|
|
await asyncio.sleep(90)
|
|
self.cLog(
|
|
20, "Checking progress of {0} tasks".format(len(self.tasks.keys()))
|
|
)
|
|
doneFlag = True
|
|
dcTasks = [k for k in self.tasks.keys()]
|
|
for task in dcTasks:
|
|
task = self.tasks[task]
|
|
if task["ob"]:
|
|
if task["pid"] in self.acks:
|
|
if not self.acks[task["pid"]]:
|
|
retry = task["retry"]
|
|
remove = False
|
|
if retry == False:
|
|
retry = 1
|
|
elif retry < 3:
|
|
retry += 1
|
|
else:
|
|
self.cLog(30, "Too many retries")
|
|
remove = True
|
|
if remove:
|
|
del self.tasks[task["pid"]]
|
|
else:
|
|
self.cLog(20, "Doing retry")
|
|
doneFlag = False
|
|
# TODO: Resend to specific node
|
|
self.send(task["packet"])
|
|
await_thread = asyncio.create_task(
|
|
self.awaitResponse(task["pid"])
|
|
)
|
|
|
|
await asyncio.sleep(1)
|
|
currentTask = {
|
|
"ob": await_thread,
|
|
"pid": str(self.cpid),
|
|
"packet": task["packet"],
|
|
}
|
|
currentTask["retry"] = retry
|
|
self.tasks[task["pid"]] = currentTask
|
|
else:
|
|
del self.tasks[task["pid"]]
|
|
|
|
async def announce(self):
|
|
while True:
|
|
self.cLog(10, "Announce")
|
|
await self.sendAnnounce()
|
|
await asyncio.sleep(180)
|