From be1de0bb68c5c4acf2edd6f04c5ea8e40d9f31fb Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Thu, 28 Jul 2022 17:54:04 +0200 Subject: [PATCH] rectifier: lookaside based on rectified objects --- loctrkd/beesure.py | 42 +++++++++++++++++++++--------------------- loctrkd/common.py | 9 +++++++++ loctrkd/protomodule.py | 3 +++ loctrkd/rectifier.py | 42 +++++++++++++++++++++++++----------------- loctrkd/zmsg.py | 25 +++++++++++++++++++++++++ loctrkd/zx303proto.py | 37 +++++++++++++++++++------------------ 6 files changed, 102 insertions(+), 56 deletions(-) diff --git a/loctrkd/beesure.py b/loctrkd/beesure.py index 93216c9..baf14db 100755 --- a/loctrkd/beesure.py +++ b/loctrkd/beesure.py @@ -351,29 +351,29 @@ class _LOC_DATA(BeeSurePkt): self.latitude = p.lat * p.nors self.longitude = p.lon * p.eorw - def rectified(self) -> Dict[str, Any]: # JSON-able dict + def rectified(self) -> SimpleNamespace: # JSON-able dict if self.gps_valid: - return { - "type": "location", - "devtime": str(self.devtime), - "battery_percentage": self.battery_percentage, - "accuracy": self.positioning_accuracy, - "altitude": self.altitude, - "speed": self.speed, - "direction": self.direction, - "latitude": self.latitude, - "longitude": self.longitude, - } + return SimpleNamespace( + type="location", + devtime=str(self.devtime), + battery_percentage=self.battery_percentage, + accuracy=self.positioning_accuracy, + altitude=self.altitude, + speed=self.speed, + direction=self.direction, + latitude=self.latitude, + longitude=self.longitude, + ) else: - return { - "type": "approximate_location", - "devtime": str(self.devtime), - "battery_percentage": self.battery_percentage, - "mcc": self.mcc, - "mnc": self.mnc, - "base_stations": self.base_stations, - "wifi_aps": self.wifi_aps, - } + return SimpleNamespace( + type="approximate_location", + devtime=str(self.devtime), + battery_percentage=self.battery_percentage, + mcc=self.mcc, + mnc=self.mnc, + base_stations=self.base_stations, + wifi_aps=self.wifi_aps, + ) class AL(_LOC_DATA): diff --git a/loctrkd/common.py b/loctrkd/common.py index 02b520f..f112fbd 100644 --- a/loctrkd/common.py +++ b/loctrkd/common.py @@ -67,3 +67,12 @@ def pmod_for_proto(proto: str) -> Optional[ProtoModule]: if pmod.proto_handled(proto): return pmod return None + + +def parse_message(proto: str, packet: bytes, is_incoming: bool = True) -> Any: + pmod = pmod_for_proto(proto) + return pmod.parse_message(packet, is_incoming) if pmod else None + + +def exposed_protos() -> List[Tuple[str, bool]]: + return [item for pmod in pmods for item in pmod.exposed_protos()] diff --git a/loctrkd/protomodule.py b/loctrkd/protomodule.py index 1a6f0fc..446fec6 100644 --- a/loctrkd/protomodule.py +++ b/loctrkd/protomodule.py @@ -129,6 +129,9 @@ class ProtoModule: def enframe(buffer: bytes, imei: Optional[str] = None) -> bytes: ... + class DecodeError(Exception): + ... + @staticmethod def exposed_protos() -> List[Tuple[str, bool]]: ... diff --git a/loctrkd/rectifier.py b/loctrkd/rectifier.py index 8ee7597..ec8baa1 100644 --- a/loctrkd/rectifier.py +++ b/loctrkd/rectifier.py @@ -9,8 +9,7 @@ from struct import pack import zmq from . import common -from .zx303proto import parse_message, proto_name, WIFI_POSITIONING -from .zmsg import Bcast, Resp, topic +from .zmsg import Bcast, Report, Resp, topic log = getLogger("loctrkd/rectifier") @@ -18,11 +17,13 @@ log = getLogger("loctrkd/rectifier") def runserver(conf: ConfigParser) -> None: qry = import_module("." + conf.get("rectifier", "lookaside"), __package__) qry.init(conf) + proto_needanswer = dict(common.exposed_protos()) # Is this https://github.com/zeromq/pyzmq/issues/1627 still not fixed?! zctx = zmq.Context() # type: ignore zsub = zctx.socket(zmq.SUB) # type: ignore zsub.connect(conf.get("collector", "publishurl")) - zsub.setsockopt(zmq.SUBSCRIBE, topic(proto_name(WIFI_POSITIONING))) + for proto in proto_needanswer.keys(): + zsub.setsockopt(zmq.SUBSCRIBE, topic(proto)) zpush = zctx.socket(zmq.PUSH) # type: ignore zpush.connect(conf.get("collector", "listenurl")) zpub = zctx.socket(zmq.PUB) # type: ignore @@ -31,7 +32,9 @@ def runserver(conf: ConfigParser) -> None: try: while True: zmsg = Bcast(zsub.recv()) - msg = parse_message(zmsg.packet) + msg = common.parse_message( + zmsg.proto, zmsg.packet, is_incoming=zmsg.is_incoming + ) log.debug( "IMEI %s from %s at %s: %s", zmsg.imei, @@ -39,19 +42,24 @@ def runserver(conf: ConfigParser) -> None: datetime.fromtimestamp(zmsg.when).astimezone(tz=timezone.utc), msg, ) - try: - lat, lon = qry.lookup( - msg.mcc, msg.mnc, msg.gsm_cells, msg.wifi_aps - ) - resp = Resp( - imei=zmsg.imei, - when=zmsg.when, # not the current time, but the original! - packet=msg.Out(latitude=lat, longitude=lon).packed, - ) - log.debug("Response for lat=%s, lon=%s: %s", lat, lon, resp) - zpush.send(resp.packed) - except Exception as e: - log.warning("Lookup for %s resulted in %s", msg, e) + rect = msg.rectified() + log.debug("rectified: %s", rect) + if rect.type == "approximate_location": + try: + lat, lon = qry.lookup( + rect.mcc, rect.mnc, rect.base_stations, rect.wifi_aps + ) + resp = Resp( + imei=zmsg.imei, + when=zmsg.when, # not the current time, but the original! + packet=msg.Out(latitude=lat, longitude=lon).packed, + ) + log.debug( + "Response for lat=%s, lon=%s: %s", lat, lon, resp + ) + zpush.send(resp.packed) + except Exception as e: + log.warning("Lookup for %s resulted in %s", msg, e) except KeyboardInterrupt: zsub.close() diff --git a/loctrkd/zmsg.py b/loctrkd/zmsg.py index ae469a2..9dae605 100644 --- a/loctrkd/zmsg.py +++ b/loctrkd/zmsg.py @@ -166,3 +166,28 @@ class Resp(_Zmsg): self.when = when self.packet = buffer[24:] + + +class Report(_Zmsg): + """Broadcast Zzmq message with "rectified" proto-agnostic json data""" + + KWARGS = (("imei", None), ("payload", "")) + + @property + def packed(self) -> bytes: + return ( + pack( + "16s", + "0000000000000000" + if self.imei is None + else self.imei.encode(), + ) + + self.payload.encode() + ) + + def decode(self, buffer: bytes) -> None: + imei = buffer[:16] + self.imei = ( + None if imei == b"0000000000000000" else imei.decode().strip("\0") + ) + self.payload = buffer[16:].decode() diff --git a/loctrkd/zx303proto.py b/loctrkd/zx303proto.py index e27eb5f..236f5da 100755 --- a/loctrkd/zx303proto.py +++ b/loctrkd/zx303proto.py @@ -19,6 +19,7 @@ from enum import Enum from inspect import isclass from struct import error, pack, unpack from time import time +from types import SimpleNamespace from typing import ( Any, Callable, @@ -363,15 +364,15 @@ class _GPS_POSITIONING(GPS303Pkt): ttup = (tup[0] % 100,) + tup[1:6] return pack("BBBBBB", *ttup) - def rectified(self) -> Dict[str, Any]: # JSON-able dict - return { - "type": "location", - "devtime": str(self.devtime), - "speed": self.speed, - "direction": self.heading, - "latitude": self.latitude, - "longitude": self.longitude, - } + def rectified(self) -> SimpleNamespace: # JSON-able dict + return SimpleNamespace( + type="location", + devtime=str(self.devtime), + speed=self.speed, + direction=self.heading, + latitude=self.latitude, + longitude=self.longitude, + ) class GPS_POSITIONING(_GPS_POSITIONING): @@ -489,15 +490,15 @@ class _WIFI_POSITIONING(GPS303Pkt): ] ) - def rectified(self) -> Dict[str, Any]: # JSON-able dict - return { - "type": "approximate_location", - "devtime": str(self.devtime), - "mcc": self.mcc, - "mnc": self.mnc, - "base_stations": self.gsm_cells, - "wifi_aps": self.wifi_aps, - } + def rectified(self) -> SimpleNamespace: # JSON-able dict + return SimpleNamespace( + type="approximate_location", + devtime=str(self.devtime), + mcc=self.mcc, + mnc=self.mnc, + base_stations=self.gsm_cells, + wifi_aps=self.wifi_aps, + ) class WIFI_OFFLINE_POSITIONING(_WIFI_POSITIONING): -- 2.43.0