2019-05-30 15:19:46 +00:00
|
|
|
from owrx.config import PropertyManager
|
|
|
|
from urllib import request
|
|
|
|
import json
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
import logging
|
2019-05-30 16:32:08 +00:00
|
|
|
import threading
|
2019-07-01 19:20:53 +00:00
|
|
|
from owrx.map import Map, LatLngLocation
|
2020-01-09 14:11:53 +00:00
|
|
|
from owrx.parser import Parser
|
2019-05-30 15:19:46 +00:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2019-07-21 17:40:28 +00:00
|
|
|
|
2019-05-30 16:54:45 +00:00
|
|
|
class DmrCache(object):
|
|
|
|
sharedInstance = None
|
2019-07-21 17:40:28 +00:00
|
|
|
|
2019-05-30 16:54:45 +00:00
|
|
|
@staticmethod
|
|
|
|
def getSharedInstance():
|
|
|
|
if DmrCache.sharedInstance is None:
|
|
|
|
DmrCache.sharedInstance = DmrCache()
|
|
|
|
return DmrCache.sharedInstance
|
2019-07-01 19:20:53 +00:00
|
|
|
|
2019-05-30 15:19:46 +00:00
|
|
|
def __init__(self):
|
|
|
|
self.cache = {}
|
2019-07-21 17:40:28 +00:00
|
|
|
self.cacheTimeout = timedelta(seconds=86400)
|
2019-07-01 19:20:53 +00:00
|
|
|
|
2019-05-30 16:54:45 +00:00
|
|
|
def isValid(self, key):
|
2019-07-21 17:40:28 +00:00
|
|
|
if not key in self.cache:
|
|
|
|
return False
|
2019-05-30 16:54:45 +00:00
|
|
|
entry = self.cache[key]
|
2019-05-30 15:19:46 +00:00
|
|
|
return entry["timestamp"] + self.cacheTimeout > datetime.now()
|
2019-07-01 19:20:53 +00:00
|
|
|
|
2019-05-30 16:54:45 +00:00
|
|
|
def put(self, key, value):
|
2019-07-21 17:40:28 +00:00
|
|
|
self.cache[key] = {"timestamp": datetime.now(), "data": value}
|
2019-07-01 19:20:53 +00:00
|
|
|
|
2019-05-30 16:54:45 +00:00
|
|
|
def get(self, key):
|
2019-07-21 17:40:28 +00:00
|
|
|
if not self.isValid(key):
|
|
|
|
return None
|
2019-05-30 16:54:45 +00:00
|
|
|
return self.cache[key]["data"]
|
|
|
|
|
|
|
|
|
|
|
|
class DmrMetaEnricher(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.threads = {}
|
2019-07-01 19:20:53 +00:00
|
|
|
|
2019-05-30 16:32:08 +00:00
|
|
|
def downloadRadioIdData(self, id):
|
2019-05-30 16:54:45 +00:00
|
|
|
cache = DmrCache.getSharedInstance()
|
2019-05-30 16:32:08 +00:00
|
|
|
try:
|
|
|
|
logger.debug("requesting DMR metadata for id=%s", id)
|
2019-06-04 22:13:54 +00:00
|
|
|
res = request.urlopen("https://www.radioid.net/api/dmr/user/?id={0}".format(id), timeout=30).read()
|
2019-05-30 16:32:08 +00:00
|
|
|
data = json.loads(res.decode("utf-8"))
|
2019-05-30 16:54:45 +00:00
|
|
|
cache.put(id, data)
|
2019-05-30 16:32:08 +00:00
|
|
|
except json.JSONDecodeError:
|
2019-05-30 16:54:45 +00:00
|
|
|
cache.put(id, None)
|
2019-05-30 16:32:08 +00:00
|
|
|
del self.threads[id]
|
2019-07-01 19:20:53 +00:00
|
|
|
|
2019-05-30 15:19:46 +00:00
|
|
|
def enrich(self, meta):
|
2019-07-21 17:40:28 +00:00
|
|
|
if not PropertyManager.getSharedInstance()["digital_voice_dmr_id_lookup"]:
|
|
|
|
return None
|
|
|
|
if not "source" in meta:
|
|
|
|
return None
|
2019-05-30 16:32:08 +00:00
|
|
|
id = meta["source"]
|
2019-05-30 16:54:45 +00:00
|
|
|
cache = DmrCache.getSharedInstance()
|
|
|
|
if not cache.isValid(id):
|
2019-05-30 16:32:08 +00:00
|
|
|
if not id in self.threads:
|
2019-10-31 21:24:31 +00:00
|
|
|
self.threads[id] = threading.Thread(target=self.downloadRadioIdData, args=[id], daemon=True)
|
2019-05-30 16:32:08 +00:00
|
|
|
self.threads[id].start()
|
|
|
|
return None
|
2019-05-30 16:54:45 +00:00
|
|
|
data = cache.get(id)
|
2019-05-30 15:19:46 +00:00
|
|
|
if "count" in data and data["count"] > 0 and "results" in data:
|
|
|
|
return data["results"][0]
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2019-07-01 19:20:53 +00:00
|
|
|
class YsfMetaEnricher(object):
|
2019-09-17 16:44:37 +00:00
|
|
|
def __init__(self, parser):
|
|
|
|
self.parser = parser
|
|
|
|
|
2019-07-01 19:20:53 +00:00
|
|
|
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
|
2019-07-11 21:40:09 +00:00
|
|
|
loc = LatLngLocation(float(meta["lat"]), float(meta["lon"]))
|
2019-09-17 16:44:37 +00:00
|
|
|
Map.getSharedInstance().updateLocation(meta["source"], loc, "YSF", self.parser.getBand())
|
2019-07-01 19:20:53 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
2020-01-09 14:11:53 +00:00
|
|
|
class MetaParser(Parser):
|
2019-05-30 14:12:13 +00:00
|
|
|
def __init__(self, handler):
|
2020-01-09 14:11:53 +00:00
|
|
|
super().__init__(handler)
|
2019-09-17 16:44:37 +00:00
|
|
|
self.enrichers = {"DMR": DmrMetaEnricher(), "YSF": YsfMetaEnricher(self)}
|
2019-06-09 17:12:37 +00:00
|
|
|
|
2019-05-30 14:12:13 +00:00
|
|
|
def parse(self, meta):
|
|
|
|
fields = meta.split(";")
|
2019-06-09 17:12:37 +00:00
|
|
|
meta = {v[0]: "".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""}
|
2019-05-30 15:19:46 +00:00
|
|
|
|
|
|
|
if "protocol" in meta:
|
|
|
|
protocol = meta["protocol"]
|
2019-09-17 16:44:37 +00:00
|
|
|
if protocol in self.enrichers:
|
|
|
|
additional_data = self.enrichers[protocol].enrich(meta)
|
2019-07-21 17:40:28 +00:00
|
|
|
if additional_data is not None:
|
|
|
|
meta["additional"] = additional_data
|
2019-05-30 15:19:46 +00:00
|
|
|
self.handler.write_metadata(meta)
|