Skip to content

Commit

Permalink
saving work
Browse files Browse the repository at this point in the history
  • Loading branch information
smarek committed Nov 12, 2023
1 parent 5299068 commit 5ed0bba
Show file tree
Hide file tree
Showing 17 changed files with 250 additions and 1,176 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.PHONY: test
test:
python -m coverage erase
PYTHONPATH=. pytest -vrP --cov-report=term-missing --cov=okdmr.hhb --cov-report=xml
pytest -vrP --cov-report=term-missing --cov=okdmr.hhb --cov-report=xml

clean:
git clean -xdff
Expand Down
5 changes: 4 additions & 1 deletion okdmr/hhb/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def main():
)

loop = asyncio.new_event_loop()
loop.set_debug(True)
asyncio.set_event_loop(loop=loop)
# order is IMPORTANT, various asyncio object are created at bridge init
# and those must be created after the main loop is created
Expand All @@ -63,9 +64,11 @@ def main():
loop.add_signal_handler(signal, bridge.stop_running)

try:
loop.run_until_complete(future=bridge.go())
loop.run_until_complete(bridge.go())

loop.run_forever()
except BaseException as e:
mainlog.exception(msg="HHB Main caught")
mainlog.exception(msg="", exc_info=e)
finally:
mainlog.info("Hytera Homebrew Bridge Ended")
Expand Down
7 changes: 0 additions & 7 deletions okdmr/hhb/callback_interface.py

This file was deleted.

43 changes: 0 additions & 43 deletions okdmr/hhb/custom_bridge_datagram_protocol.py

This file was deleted.

23 changes: 23 additions & 0 deletions okdmr/hhb/hhb_repeater_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from okdmr.dmrlib.storage import ADDRESS_TYPE, ADDRESS_EMPTY
from okdmr.dmrlib.storage.repeater import Repeater
from okdmr.dmrlib.storage.repeater_storage import RepeaterStorage

from okdmr.hhb.hytera_repeater import HyteraRepeater


class HHBRepeaterStorage(RepeaterStorage):
""" """

def create_repeater(
self,
dmr_id: int = None,
address_in: ADDRESS_TYPE = ADDRESS_EMPTY,
address_out: ADDRESS_TYPE = ADDRESS_EMPTY,
address_nat: ADDRESS_TYPE = ADDRESS_EMPTY,
) -> Repeater:
return HyteraRepeater(
address_in=address_in,
address_out=address_out,
dmr_id=dmr_id,
address_nat=address_nat,
)
139 changes: 25 additions & 114 deletions okdmr/hhb/hytera_homebrew_bridge.py
Original file line number Diff line number Diff line change
@@ -1,118 +1,30 @@
#!/usr/bin/env python3
import asyncio
import socket
from asyncio import AbstractEventLoop, Queue
from typing import Optional, Dict
from asyncio import AbstractEventLoop
from typing import Optional
from uuid import UUID

from okdmr.hhb.callback_interface import CallbackInterface
from okdmr.hhb.hytera_mmdvm_translator import HyteraMmdvmTranslator
from okdmr.hhb.hytera_protocols import (
HyteraP2PProtocol,
HyteraDMRProtocol,
HyteraRDACProtocol,
)
from okdmr.hhb.mmdvm_protocol import MMDVMProtocol
from okdmr.hhb.settings import BridgeSettings


class HyteraRepeater(CallbackInterface):
def __init__(self, ip: str, settings: BridgeSettings, asyncloop: AbstractEventLoop):
# ip of hytera repeater
self.ip: str = ip
self.dmr_port: int = 0
self.settings: BridgeSettings = settings
self.loop: AbstractEventLoop = asyncloop
# message queues for translator
self.queue_mmdvm_outgoing: Queue = Queue()
self.queue_hytera_incoming: Queue = Queue()
self.queue_hytera_outgoing: Queue = Queue()
self.queue_mmdvm_incoming: Queue = Queue()
# homebrew / mmdvm
self.homebrew_protocol: MMDVMProtocol = MMDVMProtocol(
settings=self.settings,
connection_lost_callback=self.homebrew_connection_lost,
queue_outgoing=self.queue_mmdvm_outgoing,
queue_incoming=self.queue_mmdvm_incoming,
hytera_repeater_ip=ip,
)
self.hytera_dmr_protocol: HyteraDMRProtocol = HyteraDMRProtocol(
settings=self.settings,
queue_incoming=self.queue_hytera_incoming,
queue_outgoing=self.queue_hytera_outgoing,
hytera_repeater_ip=ip,
)
self.hytera_mmdvm_translator: HyteraMmdvmTranslator = HyteraMmdvmTranslator(
settings=self.settings,
mmdvm_incoming=self.queue_mmdvm_incoming,
hytera_incoming=self.queue_hytera_incoming,
mmdvm_outgoing=self.queue_mmdvm_outgoing,
hytera_outgoing=self.queue_hytera_outgoing,
hytera_repeater_ip=self.ip,
)

def homebrew_connection_lost(self, ip: str, port: int) -> None:
asyncio.run(self.homebrew_connect(ip=ip, port=port))

async def hytera_dmr_connect(self) -> None:
(transport, _) = await self.loop.create_datagram_endpoint(
lambda: self.hytera_dmr_protocol,
sock=self.settings.hytera_repeater_data[self.ip].dmr_socket,
)
from okdmr.dmrlib.protocols.hytera.p2p_datagram_protocol import P2PDatagramProtocol
from okdmr.dmrlib.protocols.hytera.rdac_datagram_protocol import RDACDatagramProtocol
from okdmr.dmrlib.storage.repeater_storage import RepeaterStorage
from okdmr.dmrlib.utils.logging_trait import LoggingTrait

async def homebrew_connect(self, ip: str, port: int) -> None:
incorrect_config_params = self.settings.get_incorrect_configurations(ip)
if len(incorrect_config_params) > 0:
self.homebrew_protocol.log_error(
"Current configuration is not valid for connection"
)
self.settings.print_settings()
for param, current_value, error_message in incorrect_config_params:
self.homebrew_protocol.log_error(
f"PARAM: {param} CURRENT_VALUE: {current_value} MESSAGE: {error_message}"
)
return

# target address
hb_target_address = (self.settings.hb_master_host, self.settings.hb_master_port)
# Create Homebrew protocol handler
hb_transport, _ = await self.loop.create_datagram_endpoint(
lambda: self.homebrew_protocol,
local_addr=(self.settings.hb_local_ip, self.settings.hb_local_port),
remote_addr=hb_target_address,
reuse_port=True,
)
hb_local_socket = hb_transport.get_extra_info("socket")
if isinstance(hb_local_socket, socket.socket):
# Extract bound socket port
self.settings.hb_local_port = hb_local_socket.getsockname()[1]

async def go(self):
# start DMR protocol
await self.hytera_dmr_connect()

# start translator tasks
self.loop.create_task(self.hytera_mmdvm_translator.translate_from_mmdvm())
self.loop.create_task(self.hytera_mmdvm_translator.translate_from_hytera())

# mmdvm maintenance (auto login, auth, ping/pong)
self.loop.create_task(self.homebrew_protocol.periodic_maintenance())

# send translated or protocol generated packets to respective upstreams
self.loop.create_task(self.hytera_dmr_protocol.send_hytera_from_queue())
self.loop.create_task(self.homebrew_protocol.send_mmdvm_from_queue())
from okdmr.hhb.hhb_repeater_storage import HHBRepeaterStorage
from okdmr.hhb.hytera_repeater import HyteraRepeater
from okdmr.hhb.settings import BridgeSettings


class HyteraHomebrewBridge(CallbackInterface):
class HyteraHomebrewBridge(LoggingTrait):
def __init__(self, settings_ini_path: str):
self.loop: Optional[AbstractEventLoop] = None
self.settings: BridgeSettings = BridgeSettings(filepath=settings_ini_path)
self.repeaters: Dict[str, HyteraRepeater] = {}
self.storage: RepeaterStorage = HHBRepeaterStorage()
# hytera ipsc: p2p dmr and rdac
self.hytera_p2p_protocol: HyteraP2PProtocol = HyteraP2PProtocol(
settings=self.settings, repeater_accepted_callback=self
self.hytera_p2p_protocol: P2PDatagramProtocol = P2PDatagramProtocol(
storage=self.storage,
)
self.hytera_rdac_protocol: HyteraRDACProtocol = HyteraRDACProtocol(
settings=self.settings, rdac_completed_callback=self
self.hytera_rdac_protocol: RDACDatagramProtocol = RDACDatagramProtocol(
storage=self.storage, callback=lambda u: self.homebrew_connect(u)
)
# prepare translator

Expand All @@ -125,14 +37,10 @@ async def go(self) -> None:
if not self.settings.hytera_disable_rdac:
await self.hytera_rdac_connect()

async def homebrew_connect(self, ip: str, port: int) -> None:
if not self.repeaters.get(ip):
self.repeaters[ip] = HyteraRepeater(
ip=ip, settings=self.settings, asyncloop=self.loop
)
await self.repeaters[ip].go()

await self.repeaters[ip].homebrew_connect(ip=ip, port=port)
async def homebrew_connect(self, repeater_id: UUID) -> None:
rpt: HyteraRepeater = self.storage.match_uuid(repeater_id)
await rpt.go()
await rpt.homebrew_connect()

async def hytera_p2p_connect(self) -> None:
"""
Expand All @@ -155,8 +63,11 @@ async def hytera_rdac_connect(self) -> None:
)

def stop_running(self) -> None:
for ip, repeater in self.repeaters.items():
repeater.homebrew_protocol.disconnect()
self.log_info("stop_running called")

for rpt in self.storage.all():
if isinstance(rpt, HyteraRepeater):
rpt.homebrew_protocol.disconnect()

self.hytera_p2p_protocol.disconnect()
self.loop.stop()
Expand Down
11 changes: 8 additions & 3 deletions okdmr/hhb/hytera_mmdvm_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
import asyncio
from asyncio import Queue, CancelledError
from typing import Optional
from uuid import UUID

from kaitaistruct import KaitaiStruct
from okdmr.dmrlib.etsi.layer2.burst import Burst
from okdmr.dmrlib.storage.repeater_storage import RepeaterStorage
from okdmr.dmrlib.transmission.transmission_watcher import TransmissionWatcher
from okdmr.dmrlib.utils.bits_bytes import byteswap_bytes
from okdmr.dmrlib.utils.logging_trait import LoggingTrait
Expand All @@ -29,15 +31,18 @@ def __init__(
hytera_outgoing: Queue,
mmdvm_incoming: Queue,
mmdvm_outgoing: Queue,
hytera_repeater_ip: str,
repeater_id: UUID,
storage: RepeaterStorage,
):
self.transmission_watcher: TransmissionWatcher = TransmissionWatcher()
self.settings = settings
self.queue_hytera_to_translate = hytera_incoming
self.queue_hytera_output = hytera_outgoing
self.queue_mmdvm_to_translate = mmdvm_incoming
self.queue_mmdvm_output = mmdvm_outgoing
self.hytera_ip: str = hytera_repeater_ip
rpt = storage.match_uuid(repeater_id)

self.hytera_ip: str = rpt.address_out[0]

async def translate_from_hytera(self):
loop = asyncio.get_running_loop()
Expand Down Expand Up @@ -81,7 +86,7 @@ async def translate_from_hytera(self):
+ byteswap_bytes(packet.ipsc_payload)
)

self.queue_mmdvm_output.put_nowait((self.hytera_ip, mmdvm_out))
self.queue_mmdvm_output.put_nowait((self.ip, mmdvm_out))
else:
print(
"Hytera BurstInfo not available",
Expand Down
Loading

0 comments on commit 5ed0bba

Please sign in to comment.