mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-03-15 20:34:31 +00:00
101 lines
4.3 KiB
Python
101 lines
4.3 KiB
Python
import logging
|
|
import re
|
|
from datetime import datetime
|
|
from threading import Thread
|
|
from time import sleep
|
|
|
|
import pytz
|
|
import telnetlib3
|
|
|
|
from core.config import SERVER_OWNER_CALLSIGN
|
|
from data.spot import Spot
|
|
from spotproviders.spot_provider import SpotProvider
|
|
|
|
|
|
class DXCluster(SpotProvider):
|
|
"""Spot provider for a DX Cluster. Hostname, port, login_prompt, login_callsign and allow_rbn_spots are provided in config.
|
|
See config-example.yml for examples."""
|
|
|
|
_LINE_PATTERN_EXCLUDE_RBN = re.compile(
|
|
r"^DX de ([a-z0-9/]+):\s+([0-9.]+)\s+([a-z0-9/]+)\s+(.*)\s+(\d{4}Z)",
|
|
re.IGNORECASE)
|
|
_LINE_PATTERN_ALLOW_RBN = re.compile(
|
|
r"^DX de ([a-z0-9/]+)-?#?:\s+([0-9.]+)\s+([a-z0-9/]+)\s+(.*)\s+(\d{4}Z)",
|
|
re.IGNORECASE)
|
|
|
|
def __init__(self, provider_config):
|
|
"""Constructor requires hostname and port"""
|
|
|
|
super().__init__(provider_config)
|
|
self._hostname = provider_config["host"]
|
|
self._port = provider_config["port"]
|
|
self._login_prompt = provider_config["login_prompt"] if "login_prompt" in provider_config else "login:"
|
|
self._login_callsign = provider_config[
|
|
"login_callsign"] if "login_callsign" in provider_config else SERVER_OWNER_CALLSIGN
|
|
self._allow_rbn_spots = provider_config["allow_rbn_spots"] if "allow_rbn_spots" in provider_config else False
|
|
self._spot_line_pattern = self._LINE_PATTERN_ALLOW_RBN if self._allow_rbn_spots else self._LINE_PATTERN_EXCLUDE_RBN
|
|
self._telnet = None
|
|
self._thread = Thread(target=self._handle)
|
|
self._thread.daemon = True
|
|
self._running = True
|
|
|
|
def start(self):
|
|
self._thread.start()
|
|
|
|
def stop(self):
|
|
self._running = False
|
|
self._telnet.close()
|
|
self._thread.join()
|
|
|
|
def _handle(self):
|
|
while self._running:
|
|
connected = False
|
|
while not connected and self._running:
|
|
try:
|
|
self.status = "Connecting"
|
|
logging.info("DX Cluster " + self._hostname + " connecting...")
|
|
self._telnet = telnetlib3.Telnet(self._hostname, self._port)
|
|
self._telnet.read_until(self._login_prompt.encode("latin-1"))
|
|
self._telnet.write((self._login_callsign + "\n").encode("latin-1"))
|
|
connected = True
|
|
logging.info("DX Cluster " + self._hostname + " connected.")
|
|
except Exception:
|
|
self.status = "Error"
|
|
logging.exception("Exception while connecting to DX Cluster Provider (" + self._hostname + ").")
|
|
sleep(5)
|
|
|
|
self.status = "Waiting for Data"
|
|
while connected and self._running:
|
|
try:
|
|
# Check new telnet info against regular expression
|
|
telnet_output = self._telnet.read_until("\n".encode("latin-1"))
|
|
match = self._spot_line_pattern.match(telnet_output.decode("latin-1"))
|
|
if match:
|
|
spot_time = datetime.strptime(match.group(5), "%H%MZ")
|
|
spot_datetime = datetime.combine(datetime.now(pytz.UTC).date(), spot_time.time(), tzinfo=pytz.UTC)
|
|
spot = Spot(source=self.name,
|
|
dx_call=match.group(3),
|
|
de_call=match.group(1),
|
|
freq=float(match.group(2)) * 1000,
|
|
comment=match.group(4).strip(),
|
|
time=spot_datetime.timestamp())
|
|
|
|
# Add to our list
|
|
self._submit(spot)
|
|
|
|
self.status = "OK"
|
|
self.last_update_time = datetime.now(pytz.UTC)
|
|
logging.debug("Data received from DX Cluster " + self._hostname + ".")
|
|
|
|
except Exception:
|
|
connected = False
|
|
if self._running:
|
|
self.status = "Error"
|
|
logging.exception("Exception in DX Cluster Provider (" + self._hostname + ")")
|
|
sleep(5)
|
|
else:
|
|
logging.info("DX Cluster " + self._hostname + " shutting down...")
|
|
self.status = "Shutting down"
|
|
|
|
self.status = "Disconnected"
|