From 4c556e61fd6fae65e2a3b93011224f40144ed6ea Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Tue, 13 Jun 2023 14:03:36 +0200 Subject: [PATCH] introduced mesh state table --- gui/preload-mesh.js | 94 +++++++++++++++++++++++++------ gui/sock.js | 1 + gui/src/mesh-module.html | 44 +++++++++++---- tnc/data_handler.py | 2 +- tnc/mesh.py | 118 ++++++++++++++++++++++++++++++++------- tnc/modem.py | 13 +++-- tnc/queues.py | 2 +- tnc/sock.py | 19 ++++++- tnc/static.py | 1 + 9 files changed, 239 insertions(+), 55 deletions(-) diff --git a/gui/preload-mesh.js b/gui/preload-mesh.js index f8e8cc50..00657660 100644 --- a/gui/preload-mesh.js +++ b/gui/preload-mesh.js @@ -72,22 +72,6 @@ if (tbl !== null) { for (i = 0; i < routes.length; i++) { - - /* - var myGrid = document.getElementById("myGrid").value; - try { - var dist = parseInt(distance(myGrid, dxGrid)) + " km"; - document.getElementById("dataModalPingDistance").textContent = dist; - } catch { - document.getElementById("dataModalPingDistance").textContent = "---"; - } - document.getElementById("dataModalPingDB").textContent = - arg.stations[i]["snr"]; - } - */ - - - var row = document.createElement("tr"); var datetime = new Date(routes[i]["timestamp"] * 1000).toLocaleString( navigator.language,{ @@ -139,6 +123,82 @@ for (i = 0; i < routes.length; i++) { + tbl.appendChild(row); +} + /*-------------------------------------------*/ + var routes = arg.mesh_signalling_table; + +console.log(routes) +if (typeof routes == "undefined") { + return; + } + + + var tbl = document.getElementById("mesh-signalling-table"); +if (tbl !== null) { + tbl.innerHTML = ""; + + } + + +for (i = 0; i < routes.length; i++) { + + var row = document.createElement("tr"); + var datetime = new Date(routes[i]["timestamp"] * 1000).toLocaleString( + navigator.language,{ + hourCycle: 'h23', + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit" + } + ); + var timestamp = document.createElement("td"); + var timestampText = document.createElement("span"); + timestampText.innerText = datetime; + timestamp.appendChild(timestampText); + + var destination = document.createElement("td"); + var destinationText = document.createElement("span"); + destinationText.innerText = routes[i]["destination"]; + destination.appendChild(destinationText); + + var router = document.createElement("td"); + var routerText = document.createElement("span"); + routerText.innerText = routes[i]["router"]; + router.appendChild(routerText); + + var frametype = document.createElement("td"); + var frametypeText = document.createElement("span"); + frametypeText.innerText = routes[i]["frametype"]; + frametype.appendChild(frametypeText); + + var payload = document.createElement("td"); + var payloadText = document.createElement("span"); + payloadText.innerText = routes[i]["payload"]; + payload.appendChild(payloadText); + + var attempt = document.createElement("td"); + var attemptText = document.createElement("span"); + attemptText.innerText = routes[i]["attempt"]; + attempt.appendChild(attemptText); + + var status = document.createElement("td"); + var statusText = document.createElement("span"); + statusText.innerText = routes[i]["status"]; + status.appendChild(statusText); + + + row.appendChild(timestamp); + row.appendChild(destination); + row.appendChild(router); + row.appendChild(frametype); + row.appendChild(payload); + row.appendChild(attempt); + row.appendChild(status); + tbl.appendChild(row); } @@ -147,6 +207,6 @@ if (tbl !== null) { // scroll to bottom of page // https://stackoverflow.com/a/11715670 - window.scrollTo(0, document.body.scrollHeight); + //window.scrollTo(0, document.body.scrollHeight); } }); diff --git a/gui/sock.js b/gui/sock.js index 182802dd..7b7454b2 100644 --- a/gui/sock.js +++ b/gui/sock.js @@ -219,6 +219,7 @@ client.on("data", function (socketdata) { arq_transmission_percent: data["arq_transmission_percent"], stations: data["stations"], routing_table: data["routing_table"], + mesh_signalling_table: data["mesh_signalling_table"], beacon_state: data["beacon_state"], hamlib_status: data["hamlib_status"], listen: data["listen"], diff --git a/gui/src/mesh-module.html b/gui/src/mesh-module.html index 3b1982f5..ffea72d9 100644 --- a/gui/src/mesh-module.html +++ b/gui/src/mesh-module.html @@ -49,8 +49,10 @@ -
-
+
+ + +
@@ -64,17 +66,39 @@ - +
+ + + + +
+ + + + + + + + + + + + + + +
TimestampDestinationRouterFrametypePayloadAttemptStatus
+
+ + +
+ + + + +
diff --git a/tnc/data_handler.py b/tnc/data_handler.py index 71ec13a4..fba1a137 100644 --- a/tnc/data_handler.py +++ b/tnc/data_handler.py @@ -26,7 +26,7 @@ import structlog import stats import ujson as json from codec2 import FREEDV_MODE, FREEDV_MODE_USED_SLOTS -from queues import DATA_QUEUE_RECEIVED, DATA_QUEUE_TRANSMIT, RX_BUFFER +from queues import DATA_QUEUE_RECEIVED, DATA_QUEUE_TRANSMIT, RX_BUFFER, MESH_RECEIVED_QUEUE from static import FRAME_TYPE as FR_TYPE import broadcast diff --git a/tnc/mesh.py b/tnc/mesh.py index b4243932..85251428 100644 --- a/tnc/mesh.py +++ b/tnc/mesh.py @@ -46,9 +46,7 @@ import helpers import structlog import ujson as json -from queues import MESH_RECEIVED_QUEUE, MESH_QUEUE_TRANSMIT - -MESH_SIGNALLING_TABLE = [] +from queues import MESH_RECEIVED_QUEUE, MESH_QUEUE_TRANSMIT, MESH_SIGNALLING_TABLE class MeshRouter(): def __init__(self): @@ -206,6 +204,9 @@ class MeshRouter(): self.received_routing_table(data_in[:-2]) elif int.from_bytes(data_in[:1], "big") in [FRAME_TYPE.MESH_SIGNALLING_PING.value]: self.received_mesh_ping(data_in[:-2]) + elif int.from_bytes(data_in[:1], "big") in [FRAME_TYPE.MESH_SIGNALLING_PING_ACK.value]: + self.received_mesh_ping_ack(data_in[:-2]) + else: print("wrong mesh data received") print(data_in) @@ -215,23 +216,24 @@ class MeshRouter(): data = MESH_QUEUE_TRANSMIT.get() print(data) if data[0] == "PING": - self.add_mesh_ping_to_signalling_table(data[2]) + self.add_mesh_ping_to_signalling_table(helpers.get_crc_24(data[2]).hex(), status="awaiting_ack") else: print("wrong mesh command") def mesh_signalling_dispatcher(self): - # [timestamp, destination, router, frametype, payload, attempt] - # --------------0------------1---------2---------3--------4---------5---- # + # [timestamp, destination, router, frametype, payload, attempt, status] + # --------------0------------1---------2---------3--------4---------5--------6 # while True: threading.Event().wait(1.0) for entry in MESH_SIGNALLING_TABLE: print(entry) timestamp = entry[0] attempt = entry[5] + status = entry[6] # check for PING cases - if entry[3] == "PING" and attempt < 10: + if entry[3] in ["PING", "PING-ACK"] and attempt < 10 and status not in ["acknowledged"]: # Calculate the transmission time with exponential increase @@ -242,7 +244,7 @@ class MeshRouter(): entry[5] += 1 print(attempt) print("transmit mesh ping") - self.transmit_mesh_signalling(entry) + self.transmit_mesh_signalling_ping(entry) else: print("wait some more time") else: @@ -321,25 +323,88 @@ class MeshRouter(): return int((value_old + value) / 2) def received_mesh_ping(self, data_in): - pass + destination = data_in[1:4].hex() + if destination == Station.mycallsign_crc.hex(): + print("its a ping for us") + # use case 1: set status to acknowleding if we are the receiver of a PING + self.add_mesh_ping_to_signalling_table(destination, status="acknowledging") + dxcallsign_crc = Station.mycallsign_crc + self.transmit_mesh_signalling_ping_ack(dxcallsign_crc) + # use case 2: set status to acknowledged if we are out of retries + #self.add_mesh_ping_to_signalling_table(destination, status="acknowledged") + else: + print(f"its a ping for {destination}") + # use case 1: set status to forwarding if we are not hte receiver of a PING + self.add_mesh_ping_to_signalling_table(destination, status="forwarding") + dxcallsign_crc = bytes.fromhex(destination) + self.transmit_mesh_signalling_ping_ack(dxcallsign_crc) + # use case 2: set status to forwarded if we are not the receiver of a PING and out of retries + #self.add_mesh_ping_to_signalling_table(destination, status="forwarded") - def add_mesh_ping_to_signalling_table(self, dxcallsign): + def received_mesh_ping_ack(self, data_in): + # TODO: + # Check if we have a ping callsign already in signalling table + # if PING, then override and make it a PING-ACK + # if not, then add to table + + destination = data_in[1:4].hex() + timestamp = time.time() + router = "" + frametype = "PING-ACK" + payload = "" + attempt = 0 + status = "forwarding" + new_entry = [timestamp, destination, router, frametype, payload, attempt, status] + + print(MESH_SIGNALLING_TABLE) + for _, item in enumerate(MESH_SIGNALLING_TABLE): + + # use case 3: PING ACK sets state to processed if we are the initiator of a PING + if destination == Station.mycallsign_crc.hex(): + update_entry = [time.time(), destination, "", "PING-ACK", "", 0, "acknowledged"] + print(f"UPDATE AND CHANGE {MESH_SIGNALLING_TABLE[_]} >>> {update_entry}") + MESH_SIGNALLING_TABLE[_] = update_entry + return + + + # use case 1: PING-ACK updates PING-ACK, but stay at attempts + if destination in item[1] and "PING-ACK" in item[3]: + update_entry = [item[0], destination, "", "PING-ACK", "", item[5], "forwarding"] + print(f"UPDATE {MESH_SIGNALLING_TABLE[_]} >>> {update_entry}") + MESH_SIGNALLING_TABLE[_] = update_entry + return + + # use case 2: PING-ACK overwrites PING + # this avoids possible packet loops + if destination in item[1] and "PING" in item[3]: + update_entry = [time.time(), destination, "", "PING-ACK", "", 0, "forwarding"] + print(f"UPDATE AND CHANGE {MESH_SIGNALLING_TABLE[_]} >>> {update_entry}") + MESH_SIGNALLING_TABLE[_] = update_entry + return + + + + if new_entry not in MESH_SIGNALLING_TABLE: + print(f"INSERT {new_entry} >>> SIGNALLING TABLE") + MESH_SIGNALLING_TABLE.append(new_entry) + + def add_mesh_ping_to_signalling_table(self, destination, status): timestamp = time.time() - destination = helpers.get_crc_24(dxcallsign).hex() router = "" frametype = "PING" payload = "" attempt = 0 - # [timestamp, destination, router, frametype, payload, attempt] - # --------------0------------1---------2---------3--------4---------5---- # - new_entry = [timestamp, destination, router, frametype, payload, attempt] - print(MESH_SIGNALLING_TABLE) + # [timestamp, destination, router, frametype, payload, attempt, status] + # --------------0------------1---------2---------3--------4---------5--------6-----# + new_entry = [timestamp, destination, router, frametype, payload, attempt, status] for _, item in enumerate(MESH_SIGNALLING_TABLE): - # update routing entry if exists - if new_entry[0] in item[0] and new_entry[1] in item[1]: - print(f"UPDATE {MESH_SIGNALLING_TABLE[_]} >>> {new_entry}") - MESH_SIGNALLING_TABLE[_] = new_entry + # update entry if exists + if destination in item[1] and "PING" in item[3]: + update_entry = [item[0], destination, "", "PING", "", item[5], status] + print(f"UPDATE {MESH_SIGNALLING_TABLE[_]} >>> {update_entry}") + MESH_SIGNALLING_TABLE[_] = update_entry + return # add new routing entry if not exists if new_entry not in MESH_SIGNALLING_TABLE: @@ -384,7 +449,7 @@ class MeshRouter(): threading.Event().wait(0.01) - def transmit_mesh_signalling(self, data): + def transmit_mesh_signalling_ping(self, data): dxcallsign_crc = bytes.fromhex(data[1]) frame_type = bytes([FRAME_TYPE.MESH_SIGNALLING_PING.value]) @@ -395,4 +460,17 @@ class MeshRouter(): ping_frame[4:7] = helpers.get_crc_24(Station.mycallsign) ping_frame[7:13] = helpers.callsign_to_bytes(Station.mycallsign) + self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.sig0.value) + + def transmit_mesh_signalling_ping_ack(self, dxcallsign_crc): + #dxcallsign_crc = bytes.fromhex(data[1]) + + frame_type = bytes([FRAME_TYPE.MESH_SIGNALLING_PING_ACK.value]) + + ping_frame = bytearray(14) + ping_frame[:1] = frame_type + ping_frame[1:4] = dxcallsign_crc + #ping_frame[4:7] = helpers.get_crc_24(Station.mycallsign) + #ping_frame[7:13] = helpers.callsign_to_bytes(Station.mycallsign) + self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.sig0.value) \ No newline at end of file diff --git a/tnc/modem.py b/tnc/modem.py index ba95ed6c..182222e3 100644 --- a/tnc/modem.py +++ b/tnc/modem.py @@ -907,7 +907,9 @@ class RF: ]: print("dropp") elif int.from_bytes(bytes(bytes_out[:1]), "big") in [ - FRAME_TYPE.MESH_BROADCAST.value + FRAME_TYPE.MESH_BROADCAST.value, + FRAME_TYPE.MESH_SIGNALLING_PING.value, + FRAME_TYPE.MESH_SIGNALLING_PING_ACK.value, ]: self.log.debug( "[MDM] [demod_audio] moving data to mesh dispatcher", nbytes=nbytes @@ -1308,10 +1310,11 @@ class RF: raise ZeroDivisionError AudioParam.audio_dbfs = 20 * np.log10(rms / 32768) except Exception as e: - self.log.warning( - "[MDM] fft calculation error - please check your audio setup", - e=e, - ) + # FIXME: Disabled for cli cleanup + #self.log.warning( + # "[MDM] fft calculation error - please check your audio setup", + # e=e, + #) AudioParam.audio_dbfs = -100 rms_counter = 0 diff --git a/tnc/queues.py b/tnc/queues.py index 6a9d9f88..14c907a9 100644 --- a/tnc/queues.py +++ b/tnc/queues.py @@ -15,7 +15,7 @@ MODEM_TRANSMIT_QUEUE = queue.Queue() # Initialize FIFO queue to store received frames MESH_RECEIVED_QUEUE = queue.Queue() MESH_QUEUE_TRANSMIT = queue.Queue() -MESH_COMMAND_STATES = {} +MESH_SIGNALLING_TABLE = [] # Initialize FIFO queue to store audio frames diff --git a/tnc/sock.py b/tnc/sock.py index 613ce36e..f4999310 100644 --- a/tnc/sock.py +++ b/tnc/sock.py @@ -32,7 +32,7 @@ import structlog from random import randrange import ujson as json from exceptions import NoCallsign -from queues import DATA_QUEUE_TRANSMIT, RX_BUFFER, RIGCTLD_COMMAND_QUEUE, MESH_QUEUE_TRANSMIT +from queues import DATA_QUEUE_TRANSMIT, RX_BUFFER, RIGCTLD_COMMAND_QUEUE, MESH_QUEUE_TRANSMIT, MESH_SIGNALLING_TABLE SOCKET_QUEUE = queue.Queue() DAEMON_QUEUE = queue.Queue() @@ -1177,6 +1177,7 @@ def send_tnc_state(): "beacon_state": str(Beacon.beacon_state), "stations": [], "routing_table": [], + "mesh_signalling_table" : [], "mycallsign": str(Station.mycallsign, encoding), "mygrid": str(Station.mygrid, encoding), "dxcallsign": str(Station.dxcallsign, encoding), @@ -1215,6 +1216,22 @@ def send_tnc_state(): "timestamp": MeshParam.routing_table[_][5], } ) + + for _, entry in enumerate(MESH_SIGNALLING_TABLE): + + output["mesh_signalling_table"].append( + { + "timestamp": MESH_SIGNALLING_TABLE[_][0], + "destination": MESH_SIGNALLING_TABLE[_][1], + "router": MESH_SIGNALLING_TABLE[_][2], + "frametype": MESH_SIGNALLING_TABLE[_][3], + "payload": MESH_SIGNALLING_TABLE[_][4], + "attempt": MESH_SIGNALLING_TABLE[_][5], + "status": MESH_SIGNALLING_TABLE[_][6], + + } + ) + #print(output) return json.dumps(output) diff --git a/tnc/static.py b/tnc/static.py index 6e4b134c..84cbb921 100644 --- a/tnc/static.py +++ b/tnc/static.py @@ -163,6 +163,7 @@ class FRAME_TYPE(Enum): BURST_NACK = 64 MESH_BROADCAST = 100 MESH_SIGNALLING_PING = 101 + MESH_SIGNALLING_PING_ACK = 102 CQ = 200 QRV = 201 PING = 210