mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
first mesh ping tx part
This commit is contained in:
parent
415ff0c777
commit
35576bb760
|
@ -2983,6 +2983,12 @@ ipcRenderer.on("run-tnc-command", (event, arg) => {
|
||||||
if (arg.command == "responseSharedFile") {
|
if (arg.command == "responseSharedFile") {
|
||||||
sock.sendResponseSharedFile(arg.dxcallsign, arg.file, arg.filedata);
|
sock.sendResponseSharedFile(arg.dxcallsign, arg.file, arg.filedata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg.command == "mesh_ping") {
|
||||||
|
sock.sendMeshPing(arg.dxcallsign);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// IPC ACTION FOR AUTO UPDATER
|
// IPC ACTION FOR AUTO UPDATER
|
||||||
|
|
|
@ -13,6 +13,21 @@ const config = require(configPath);
|
||||||
|
|
||||||
// WINDOW LISTENER
|
// WINDOW LISTENER
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|
||||||
|
// startPing button clicked
|
||||||
|
document.getElementById("transmit_mesh_ping").addEventListener("click", () => {
|
||||||
|
var dxcallsign = document.getElementById("dxCallMesh").value.toUpperCase();
|
||||||
|
if (dxcallsign == "" || dxcallsign == null || dxcallsign == undefined)
|
||||||
|
return;
|
||||||
|
//pauseButton(document.getElementById("transmit_mesh_ping"), 2000);
|
||||||
|
ipcRenderer.send("run-tnc-command", {
|
||||||
|
command: "mesh_ping",
|
||||||
|
dxcallsign: dxcallsign,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
document
|
document
|
||||||
.getElementById("enable_mesh")
|
.getElementById("enable_mesh")
|
||||||
.addEventListener("click", () => {
|
.addEventListener("click", () => {
|
||||||
|
@ -42,9 +57,18 @@ let Data = {
|
||||||
|
|
||||||
ipcRenderer.on("action-update-mesh-table", (event, arg) => {
|
ipcRenderer.on("action-update-mesh-table", (event, arg) => {
|
||||||
var routes = arg.routing_table;
|
var routes = arg.routing_table;
|
||||||
|
|
||||||
|
if (typeof routes == "undefined") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var tbl = document.getElementById("mesh-table");
|
var tbl = document.getElementById("mesh-table");
|
||||||
|
if (tbl !== null) {
|
||||||
tbl.innerHTML = "";
|
tbl.innerHTML = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < routes.length; i++) {
|
for (i = 0; i < routes.length; i++) {
|
||||||
|
|
||||||
|
@ -119,7 +143,10 @@ for (i = 0; i < routes.length; i++) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (tbl !== null) {
|
||||||
|
|
||||||
// scroll to bottom of page
|
// scroll to bottom of page
|
||||||
// https://stackoverflow.com/a/11715670
|
// https://stackoverflow.com/a/11715670
|
||||||
window.scrollTo(0, document.body.scrollHeight);
|
window.scrollTo(0, document.body.scrollHeight);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -587,6 +587,15 @@ exports.sendPing = function (dxcallsign) {
|
||||||
writeTncCommand(command);
|
writeTncCommand(command);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Send Mesh Ping
|
||||||
|
exports.sendMeshPing = function (dxcallsign) {
|
||||||
|
command =
|
||||||
|
'{"type" : "mesh", "command" : "ping", "dxcallsign" : "' +
|
||||||
|
dxcallsign +
|
||||||
|
'"}';
|
||||||
|
writeTncCommand(command);
|
||||||
|
};
|
||||||
|
|
||||||
// Send CQ
|
// Send CQ
|
||||||
exports.sendCQ = function () {
|
exports.sendCQ = function () {
|
||||||
command = '{"type" : "broadcast", "command" : "cqcqcq"}';
|
command = '{"type" : "broadcast", "command" : "cqcqcq"}';
|
||||||
|
|
|
@ -31,8 +31,21 @@
|
||||||
<label class="btn btn-outline-info" for="enable_mesh"
|
<label class="btn btn-outline-info" for="enable_mesh"
|
||||||
>Enable / Disable Mesh</label
|
>Enable / Disable Mesh</label
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
style="max-width: 6rem; text-transform: uppercase"
|
||||||
|
placeholder="DXcall"
|
||||||
|
pattern="[A-Z]*"
|
||||||
|
id="dxCallMesh"
|
||||||
|
maxlength="11"
|
||||||
|
aria-label="Input group"
|
||||||
|
aria-describedby="btnGroupAddon"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<button id="transmit_mesh_ping" type="button" class="btn btn-primary">mesh ping</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
134
tnc/mesh.py
134
tnc/mesh.py
|
@ -44,8 +44,11 @@ import threading
|
||||||
import modem
|
import modem
|
||||||
import helpers
|
import helpers
|
||||||
import structlog
|
import structlog
|
||||||
|
import ujson as json
|
||||||
|
|
||||||
from queues import MESH_RECEIVED_QUEUE
|
from queues import MESH_RECEIVED_QUEUE, MESH_QUEUE_TRANSMIT
|
||||||
|
|
||||||
|
MESH_SIGNALLING_TABLE = []
|
||||||
|
|
||||||
class MeshRouter():
|
class MeshRouter():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -63,6 +66,15 @@ class MeshRouter():
|
||||||
)
|
)
|
||||||
self.mesh_rx_dispatcher_thread.start()
|
self.mesh_rx_dispatcher_thread.start()
|
||||||
|
|
||||||
|
self.mesh_tx_dispatcher_thread = threading.Thread(
|
||||||
|
target=self.mesh_tx_dispatcher, name="worker thread receive", daemon=True
|
||||||
|
)
|
||||||
|
self.mesh_tx_dispatcher_thread.start()
|
||||||
|
|
||||||
|
self.mesh_signalling_dispatcher_thread = threading.Thread(
|
||||||
|
target=self.mesh_signalling_dispatcher, name="worker thread receive", daemon=True
|
||||||
|
)
|
||||||
|
self.mesh_signalling_dispatcher_thread.start()
|
||||||
|
|
||||||
def get_from_heard_stations(self):
|
def get_from_heard_stations(self):
|
||||||
"""
|
"""
|
||||||
|
@ -123,7 +135,7 @@ class MeshRouter():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.warning("[MESH] error adding data to routing table", e=e, router=new_router)
|
self.log.warning("[MESH] error adding data to routing table", e=e, router=new_router)
|
||||||
|
|
||||||
def broadcast_routing_table(self, interval=180):
|
def broadcast_routing_table(self, interval=600):
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# always enable receiving for datac4 if broadcasting
|
# always enable receiving for datac4 if broadcasting
|
||||||
|
@ -192,9 +204,50 @@ class MeshRouter():
|
||||||
data_in = MESH_RECEIVED_QUEUE.get()
|
data_in = MESH_RECEIVED_QUEUE.get()
|
||||||
if int.from_bytes(data_in[:1], "big") in [FRAME_TYPE.MESH_BROADCAST.value]:
|
if int.from_bytes(data_in[:1], "big") in [FRAME_TYPE.MESH_BROADCAST.value]:
|
||||||
self.received_routing_table(data_in[:-2])
|
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])
|
||||||
else:
|
else:
|
||||||
print("wrong mesh data received")
|
print("wrong mesh data received")
|
||||||
print(data_in)
|
print(data_in)
|
||||||
|
|
||||||
|
def mesh_tx_dispatcher(self):
|
||||||
|
while True:
|
||||||
|
data = MESH_QUEUE_TRANSMIT.get()
|
||||||
|
print(data)
|
||||||
|
if data[0] == "PING":
|
||||||
|
self.add_mesh_ping_to_signalling_table(data[2])
|
||||||
|
else:
|
||||||
|
print("wrong mesh command")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def mesh_signalling_dispatcher(self):
|
||||||
|
# [timestamp, destination, router, frametype, payload, attempt]
|
||||||
|
# --------------0------------1---------2---------3--------4---------5---- #
|
||||||
|
while True:
|
||||||
|
threading.Event().wait(1.0)
|
||||||
|
for entry in MESH_SIGNALLING_TABLE:
|
||||||
|
print(entry)
|
||||||
|
timestamp = entry[0]
|
||||||
|
attempt = entry[5]
|
||||||
|
# check for PING cases
|
||||||
|
if entry[3] == "PING" and attempt < 10:
|
||||||
|
|
||||||
|
|
||||||
|
# Calculate the transmission time with exponential increase
|
||||||
|
transmission_time = timestamp + (2 ** attempt) * 10
|
||||||
|
|
||||||
|
# check if it is time to transmit
|
||||||
|
if time.time() >= transmission_time:
|
||||||
|
entry[5] += 1
|
||||||
|
print(attempt)
|
||||||
|
print("transmit mesh ping")
|
||||||
|
self.transmit_mesh_signalling(entry)
|
||||||
|
else:
|
||||||
|
print("wait some more time")
|
||||||
|
else:
|
||||||
|
print("...")
|
||||||
|
|
||||||
def received_routing_table(self, data_in):
|
def received_routing_table(self, data_in):
|
||||||
try:
|
try:
|
||||||
print("data received........")
|
print("data received........")
|
||||||
|
@ -266,3 +319,80 @@ class MeshRouter():
|
||||||
|
|
||||||
def calculate_new_avg_score(self, value_old, value):
|
def calculate_new_avg_score(self, value_old, value):
|
||||||
return int((value_old + value) / 2)
|
return int((value_old + value) / 2)
|
||||||
|
|
||||||
|
def received_mesh_ping(self, data_in):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_mesh_ping_to_signalling_table(self, dxcallsign):
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
# add new routing entry if not exists
|
||||||
|
if new_entry not in MESH_SIGNALLING_TABLE:
|
||||||
|
print(f"INSERT {new_entry} >>> SIGNALLING TABLE")
|
||||||
|
MESH_SIGNALLING_TABLE.append(new_entry)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def enqueue_frame_for_tx(
|
||||||
|
self,
|
||||||
|
frame_to_tx, # : list[bytearray], # this causes a crash on python 3.7
|
||||||
|
c2_mode=FREEDV_MODE.sig0.value,
|
||||||
|
copies=1,
|
||||||
|
repeat_delay=0,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Send (transmit) supplied frame to TNC
|
||||||
|
|
||||||
|
:param frame_to_tx: Frame data to send
|
||||||
|
:type frame_to_tx: list of bytearrays
|
||||||
|
:param c2_mode: Codec2 mode to use, defaults to datac13
|
||||||
|
:type c2_mode: int, optional
|
||||||
|
:param copies: Number of frame copies to send, defaults to 1
|
||||||
|
:type copies: int, optional
|
||||||
|
:param repeat_delay: Delay time before sending repeat frame, defaults to 0
|
||||||
|
:type repeat_delay: int, optional
|
||||||
|
"""
|
||||||
|
#print(frame_to_tx[0])
|
||||||
|
#print(frame_to_tx)
|
||||||
|
frame_type = FRAME_TYPE(int.from_bytes(frame_to_tx[0][:1], byteorder="big")).name
|
||||||
|
self.log.debug("[TNC] enqueue_frame_for_tx", c2_mode=FREEDV_MODE(c2_mode).name, data=frame_to_tx,
|
||||||
|
type=frame_type)
|
||||||
|
|
||||||
|
# Set the TRANSMITTING flag before adding an object to the transmit queue
|
||||||
|
# TODO: This is not that nice, we could improve this somehow
|
||||||
|
TNC.transmitting = True
|
||||||
|
modem.MODEM_TRANSMIT_QUEUE.put([c2_mode, copies, repeat_delay, frame_to_tx])
|
||||||
|
|
||||||
|
# Wait while transmitting
|
||||||
|
while TNC.transmitting:
|
||||||
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
|
|
||||||
|
def transmit_mesh_signalling(self, data):
|
||||||
|
dxcallsign_crc = bytes.fromhex(data[1])
|
||||||
|
|
||||||
|
frame_type = bytes([FRAME_TYPE.MESH_SIGNALLING_PING.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)
|
|
@ -14,6 +14,9 @@ MODEM_TRANSMIT_QUEUE = queue.Queue()
|
||||||
|
|
||||||
# Initialize FIFO queue to store received frames
|
# Initialize FIFO queue to store received frames
|
||||||
MESH_RECEIVED_QUEUE = queue.Queue()
|
MESH_RECEIVED_QUEUE = queue.Queue()
|
||||||
|
MESH_QUEUE_TRANSMIT = queue.Queue()
|
||||||
|
MESH_COMMAND_STATES = {}
|
||||||
|
|
||||||
|
|
||||||
# Initialize FIFO queue to store audio frames
|
# Initialize FIFO queue to store audio frames
|
||||||
AUDIO_RECEIVED_QUEUE = queue.Queue()
|
AUDIO_RECEIVED_QUEUE = queue.Queue()
|
||||||
|
|
42
tnc/sock.py
42
tnc/sock.py
|
@ -32,7 +32,7 @@ import structlog
|
||||||
from random import randrange
|
from random import randrange
|
||||||
import ujson as json
|
import ujson as json
|
||||||
from exceptions import NoCallsign
|
from exceptions import NoCallsign
|
||||||
from queues import DATA_QUEUE_TRANSMIT, RX_BUFFER, RIGCTLD_COMMAND_QUEUE
|
from queues import DATA_QUEUE_TRANSMIT, RX_BUFFER, RIGCTLD_COMMAND_QUEUE, MESH_QUEUE_TRANSMIT
|
||||||
|
|
||||||
SOCKET_QUEUE = queue.Queue()
|
SOCKET_QUEUE = queue.Queue()
|
||||||
DAEMON_QUEUE = queue.Queue()
|
DAEMON_QUEUE = queue.Queue()
|
||||||
|
@ -402,6 +402,10 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
||||||
# DISABLE MESH
|
# DISABLE MESH
|
||||||
if received_json["type"] == "set" and received_json["command"] == "disable_mesh":
|
if received_json["type"] == "set" and received_json["command"] == "disable_mesh":
|
||||||
MeshParam.enable_protocol = False
|
MeshParam.enable_protocol = False
|
||||||
|
# -------------- MESH ---------------- #
|
||||||
|
# MESH PING
|
||||||
|
if received_json["type"] == "mesh" and received_json["command"] == "ping":
|
||||||
|
self.tnc_mesh_ping(received_json)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
log.error("[SCK] JSON decoding error", e=err)
|
log.error("[SCK] JSON decoding error", e=err)
|
||||||
|
@ -418,6 +422,8 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
||||||
ARQ.arq_session_state = "disconnecting"
|
ARQ.arq_session_state = "disconnecting"
|
||||||
command_response("disconnect", True)
|
command_response("disconnect", True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
command_response("listen", False)
|
command_response("listen", False)
|
||||||
log.warning(
|
log.warning(
|
||||||
|
@ -571,6 +577,40 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
||||||
command=received_json,
|
command=received_json,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def tnc_mesh_ping(self, received_json):
|
||||||
|
# send ping frame and wait for ACK
|
||||||
|
try:
|
||||||
|
dxcallsign = received_json["dxcallsign"]
|
||||||
|
if not str(dxcallsign).strip():
|
||||||
|
raise NoCallsign
|
||||||
|
|
||||||
|
# additional step for being sure our callsign is correctly
|
||||||
|
# in case we are not getting a station ssid
|
||||||
|
# then we are forcing a station ssid = 0
|
||||||
|
dxcallsign = helpers.callsign_to_bytes(dxcallsign)
|
||||||
|
dxcallsign = helpers.bytes_to_callsign(dxcallsign)
|
||||||
|
|
||||||
|
# check if specific callsign is set with different SSID than the TNC is initialized
|
||||||
|
try:
|
||||||
|
mycallsign = received_json["mycallsign"]
|
||||||
|
mycallsign = helpers.callsign_to_bytes(mycallsign)
|
||||||
|
mycallsign = helpers.bytes_to_callsign(mycallsign)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
mycallsign = Station.mycallsign
|
||||||
|
|
||||||
|
MESH_QUEUE_TRANSMIT.put(["PING", mycallsign, dxcallsign])
|
||||||
|
command_response("ping", True)
|
||||||
|
except NoCallsign:
|
||||||
|
command_response("ping", False)
|
||||||
|
log.warning("[SCK] callsign required for ping", command=received_json)
|
||||||
|
except Exception as err:
|
||||||
|
command_response("ping", False)
|
||||||
|
log.warning(
|
||||||
|
"[SCK] PING command execution error", e=err, command=received_json
|
||||||
|
)
|
||||||
|
|
||||||
def tnc_ping_ping(self, received_json):
|
def tnc_ping_ping(self, received_json):
|
||||||
# send ping frame and wait for ACK
|
# send ping frame and wait for ACK
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,7 @@ class TCIParam:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TNC:
|
class TNC:
|
||||||
version = "0.10.0-alpha.1-mesh-exp8"
|
version = "0.10.0-alpha.1-mesh-exp9"
|
||||||
host: str = "0.0.0.0"
|
host: str = "0.0.0.0"
|
||||||
port: int = 3000
|
port: int = 3000
|
||||||
SOCKET_TIMEOUT: int = 1 # seconds
|
SOCKET_TIMEOUT: int = 1 # seconds
|
||||||
|
@ -162,6 +162,7 @@ class FRAME_TYPE(Enum):
|
||||||
FR_NACK = 63
|
FR_NACK = 63
|
||||||
BURST_NACK = 64
|
BURST_NACK = 64
|
||||||
MESH_BROADCAST = 100
|
MESH_BROADCAST = 100
|
||||||
|
MESH_SIGNALLING_PING = 101
|
||||||
CQ = 200
|
CQ = 200
|
||||||
QRV = 201
|
QRV = 201
|
||||||
PING = 210
|
PING = 210
|
||||||
|
|
Loading…
Reference in a new issue