from owrx.config import PropertyManager from urllib import request import json from datetime import datetime, timedelta import logging import threading from owrx.map import Map, LatLngLocation from owrx.parser import Parser logger = logging.getLogger(__name__) class DmrCache(object): sharedInstance = None @staticmethod def getSharedInstance(): if DmrCache.sharedInstance is None: DmrCache.sharedInstance = DmrCache() return DmrCache.sharedInstance def __init__(self): self.cache = {} self.cacheTimeout = timedelta(seconds=86400) def isValid(self, key): if not key in self.cache: return False entry = self.cache[key] return entry["timestamp"] + self.cacheTimeout > datetime.now() def put(self, key, value): self.cache[key] = {"timestamp": datetime.now(), "data": value} def get(self, key): if not self.isValid(key): return None return self.cache[key]["data"] class DmrMetaEnricher(object): def __init__(self): self.threads = {} def downloadRadioIdData(self, id): cache = DmrCache.getSharedInstance() try: logger.debug("requesting DMR metadata for id=%s", id) res = request.urlopen("https://www.radioid.net/api/dmr/user/?id={0}".format(id), timeout=30).read() data = json.loads(res.decode("utf-8")) cache.put(id, data) except json.JSONDecodeError: cache.put(id, None) del self.threads[id] def enrich(self, meta): if not PropertyManager.getSharedInstance()["digital_voice_dmr_id_lookup"]: return None if not "source" in meta: return None id = meta["source"] cache = DmrCache.getSharedInstance() if not cache.isValid(id): if not id in self.threads: self.threads[id] = threading.Thread(target=self.downloadRadioIdData, args=[id], daemon=True) self.threads[id].start() return None data = cache.get(id) if "count" in data and data["count"] > 0 and "results" in data: return data["results"][0] return None class YsfMetaEnricher(object): def __init__(self, parser): self.parser = parser def enrich(self, meta): if "source" in meta and "lat" in meta and "lon" in meta: # TODO parsing the float values should probably happen earlier loc = LatLngLocation(float(meta["lat"]), float(meta["lon"])) Map.getSharedInstance().updateLocation(meta["source"], loc, "YSF", self.parser.getBand()) return None class MetaParser(Parser): def __init__(self, handler): super().__init__(handler) self.enrichers = {"DMR": DmrMetaEnricher(), "YSF": YsfMetaEnricher(self)} def parse(self, meta): fields = meta.split(";") meta = {v[0]: "".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""} if "protocol" in meta: protocol = meta["protocol"] if protocol in self.enrichers: additional_data = self.enrichers[protocol].enrich(meta) if additional_data is not None: meta["additional"] = additional_data self.handler.write_metadata(meta)