]> www.average.org Git - loctrkd.git/commitdiff
Convert recitifier to multiprotocol support
authorEugene Crosser <crosser@average.org>
Thu, 28 Jul 2022 22:04:57 +0000 (00:04 +0200)
committerEugene Crosser <crosser@average.org>
Thu, 28 Jul 2022 22:04:57 +0000 (00:04 +0200)
loctrkd/beesure.py
loctrkd/common.py
loctrkd/rectifier.py
loctrkd/zmsg.py
loctrkd/zx303proto.py

index baf14dbee9600eb25252f9d44ea38c80fac0890a..ad351cb0126b3c3616169802e36150d39b1a2371 100755 (executable)
@@ -23,6 +23,12 @@ from typing import (
 from types import SimpleNamespace
 
 from .protomodule import ProtoClass
+from .common import (
+    CoordReport,
+    HintReport,
+    StatusReport,
+    Report,
+)
 
 __all__ = (
     "Stream",
@@ -313,7 +319,7 @@ class _LOC_DATA(BeeSurePkt):
                 (self, "pedometer", int),
                 (self, "tubmling_times", int),
                 (self, "device_status", lambda x: int(x, 16)),
-                (self, "base_stations_number", int),
+                (self, "gsm_cells_number", int),
                 (self, "connect_base_station_number", int),
                 (self, "mcc", int),
                 (self, "mnc", int),
@@ -323,11 +329,11 @@ class _LOC_DATA(BeeSurePkt):
             setattr(obj, attr, func(val))  # type: ignore
         rest_args = args[20:]
         # (area_id, cell_id, strength)*
-        self.base_stations = [
-            tuple(int(el) for el in rest_args[i * 3 : 3 + i * 3])
-            for i in range(self.base_stations_number)
+        self.gsm_cells: List[Tuple[int, int, int]] = [
+            tuple(int(el) for el in rest_args[i * 3 : 3 + i * 3])  # type: ignore
+            for i in range(self.gsm_cells_number)
         ]
-        rest_args = rest_args[3 * self.base_stations_number :]
+        rest_args = rest_args[3 * self.gsm_cells_number :]
         self.wifi_aps_number = int(rest_args[0])
         # (SSID, MAC, strength)*
         self.wifi_aps = [
@@ -351,10 +357,9 @@ class _LOC_DATA(BeeSurePkt):
         self.latitude = p.lat * p.nors
         self.longitude = p.lon * p.eorw
 
-    def rectified(self) -> SimpleNamespace:  # JSON-able dict
+    def rectified(self) -> Report:
         if self.gps_valid:
-            return SimpleNamespace(
-                type="location",
+            return CoordReport(
                 devtime=str(self.devtime),
                 battery_percentage=self.battery_percentage,
                 accuracy=self.positioning_accuracy,
@@ -365,13 +370,12 @@ class _LOC_DATA(BeeSurePkt):
                 longitude=self.longitude,
             )
         else:
-            return SimpleNamespace(
-                type="approximate_location",
+            return HintReport(
                 devtime=str(self.devtime),
                 battery_percentage=self.battery_percentage,
                 mcc=self.mcc,
                 mnc=self.mnc,
-                base_stations=self.base_stations,
+                gsm_cells=self.gsm_cells,
                 wifi_aps=self.wifi_aps,
             )
 
@@ -618,7 +622,7 @@ def parse_message(packet: bytes, is_incoming: bool = True) -> BeeSurePkt:
 
 def exposed_protos() -> List[Tuple[str, bool]]:
     return [
-        (proto_name(cls), False)
+        (proto_name(cls)[:16], False)
         for cls in CLASSES.values()
         if hasattr(cls, "rectified")
     ]
index f112fbd53a51176e279b77c01cc05e6f32529bd2..30a17194ffcb5f645bc01d43cf265261a718e339 100644 (file)
@@ -3,11 +3,13 @@
 from configparser import ConfigParser
 from importlib import import_module
 from getopt import getopt
+from json import dumps
 from logging import Formatter, getLogger, Logger, StreamHandler, DEBUG, INFO
 from logging.handlers import SysLogHandler
 from pkg_resources import get_distribution, DistributionNotFound
 from sys import argv, stderr, stdout
 from typing import Any, cast, Dict, List, Optional, Tuple, Union
+from types import SimpleNamespace
 
 from .protomodule import ProtoModule
 
@@ -76,3 +78,69 @@ def parse_message(proto: str, packet: bytes, is_incoming: bool = True) -> Any:
 
 def exposed_protos() -> List[Tuple[str, bool]]:
     return [item for pmod in pmods for item in pmod.exposed_protos()]
+
+
+class Report(SimpleNamespace):
+    TYPE: str
+
+    @property
+    def json(self) -> str:
+        self.type = self.TYPE
+        return dumps(self.__dict__)
+
+
+class CoordReport(Report):
+    TYPE = "location"
+
+    def __init__(
+        self,
+        *,
+        devtime: str,
+        battery_percentage: int,
+        accuracy: float,
+        altitude: float,
+        speed: float,
+        direction: float,
+        latitude: float,
+        longitude: float
+    ) -> None:
+        super().__init__(
+            devtime=devtime,
+            battery_percentage=battery_percentage,
+            accuracy=accuracy,
+            altitude=altitude,
+            speed=speed,
+            direction=direction,
+            latitude=latitude,
+            longitude=longitude,
+        )
+
+
+class HintReport(Report):
+    TYPE = "approximate_location"
+
+    def __init__(
+        self,
+        *,
+        devtime: str,
+        battery_percentage: int,
+        mcc: int,
+        mnc: int,
+        gsm_cells: List[Tuple[int, int, int]],
+        wifi_aps: List[Tuple[str, str, int]]
+    ) -> None:
+        super().__init__(
+            devtime=devtime,
+            battery_percentage=battery_percentage,
+            mcc=mcc,
+            mnc=mnc,
+            gsm_cells=gsm_cells,
+            wifi_aps=wifi_aps,
+        )
+
+
+class StatusReport(Report):
+    TYPE = "status"
+
+    def __init__(self, *, battery_percentage: int) -> None:
+        super().__init__(battery_percentage=battery_percentage)
index ec8baa1a5ab33afaed9fc64e56959cc4e185cd71..e4f29b2936a40258d664521ba85fb4267ff38da8 100644 (file)
@@ -9,7 +9,8 @@ from struct import pack
 import zmq
 
 from . import common
-from .zmsg import Bcast, Report, Resp, topic
+from .common import CoordReport, HintReport, StatusReport, Report
+from .zmsg import Bcast, Rept, Resp, topic
 
 log = getLogger("loctrkd/rectifier")
 
@@ -42,24 +43,48 @@ def runserver(conf: ConfigParser) -> None:
                 datetime.fromtimestamp(zmsg.when).astimezone(tz=timezone.utc),
                 msg,
             )
-            rect = msg.rectified()
+            rect: Report = msg.rectified()
             log.debug("rectified: %s", rect)
-            if rect.type == "approximate_location":
+            if isinstance(rect, (CoordReport, StatusReport)):
+                zpub.send(Rept(imei=zmsg.imei, payload=rect.json).packed)
+            elif isinstance(rect, HintReport):
                 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,
+                        rect.mcc, rect.mnc, rect.gsm_cells, rect.wifi_aps
                     )
                     log.debug(
-                        "Response for lat=%s, lon=%s: %s", lat, lon, resp
+                        "Approximated lat=%s, lon=%s for %s", lat, lon, rect
+                    )
+                    if proto_needanswer.get(zmsg.proto, False):
+                        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("Sending reponse %s", resp)
+                        zpush.send(resp.packed)
+                    zpub.send(
+                        Rept(
+                            imei=zmsg.imei,
+                            payload=CoordReport(
+                                devtime=rect.devtime,
+                                battery_percentage=rect.battery_percentage,
+                                accuracy=-1,
+                                altitude=-1,
+                                speed=-1,
+                                direction=-1,
+                                latitude=lat,
+                                longitude=lon,
+                            ).json,
+                        ).packed
                     )
-                    zpush.send(resp.packed)
                 except Exception as e:
-                    log.warning("Lookup for %s resulted in %s", msg, e)
+                    log.warning(
+                        "Lookup for %s rectified as %s resulted in %s",
+                        msg,
+                        rect,
+                        e,
+                    )
 
     except KeyboardInterrupt:
         zsub.close()
index 9dae60540f46019957a72121700c6ada5df081de..4da88d2f6754e7e78db97ef8ecc9e989dcd1475c 100644 (file)
@@ -168,7 +168,7 @@ class Resp(_Zmsg):
         self.packet = buffer[24:]
 
 
-class Report(_Zmsg):
+class Rept(_Zmsg):
     """Broadcast Zzmq message with "rectified" proto-agnostic json data"""
 
     KWARGS = (("imei", None), ("payload", ""))
index 236f5daa16a1252f3203e660e1718370148465a5..2a0ede6991c89b9c46ef56fe3ad344d5c7b506a3 100755 (executable)
@@ -32,6 +32,7 @@ from typing import (
     Union,
 )
 
+from .common import CoordReport, HintReport, StatusReport
 from .protomodule import ProtoClass
 
 __all__ = (
@@ -364,10 +365,12 @@ class _GPS_POSITIONING(GPS303Pkt):
         ttup = (tup[0] % 100,) + tup[1:6]
         return pack("BBBBBB", *ttup)
 
-    def rectified(self) -> SimpleNamespace:  # JSON-able dict
-        return SimpleNamespace(
-            type="location",
+    def rectified(self) -> CoordReport:  # JSON-able dict
+        return CoordReport(
             devtime=str(self.devtime),
+            battery_percentage=-1,
+            accuracy=-1.0,
+            altitude=-1.0,
             speed=self.speed,
             direction=self.heading,
             latitude=self.latitude,
@@ -412,6 +415,9 @@ class STATUS(GPS303Pkt):
     def out_encode(self) -> bytes:  # Set interval in minutes
         return pack("B", self.upload_interval)
 
+    def rectified(self) -> StatusReport:
+        return StatusReport(battery_percentage=self.batt)
+
 
 class HIBERNATION(GPS303Pkt):  # Server can send to send devicee to sleep
     PROTO = 0x14
@@ -490,14 +496,14 @@ class _WIFI_POSITIONING(GPS303Pkt):
             ]
         )
 
-    def rectified(self) -> SimpleNamespace:  # JSON-able dict
-        return SimpleNamespace(
-            type="approximate_location",
+    def rectified(self) -> HintReport:
+        return HintReport(
             devtime=str(self.devtime),
+            battery_percentage=-1,
             mcc=self.mcc,
             mnc=self.mnc,
-            base_stations=self.gsm_cells,
-            wifi_aps=self.wifi_aps,
+            gsm_cells=self.gsm_cells,
+            wifi_aps=[("<UNKNOWN>", mac, sig) for mac, sig in self.wifi_aps],
         )
 
 
@@ -887,7 +893,7 @@ def parse_message(packet: bytes, is_incoming: bool = True) -> GPS303Pkt:
 
 def exposed_protos() -> List[Tuple[str, bool]]:
     return [
-        (proto_name(cls), cls.RESPOND is Respond.EXT)
+        (proto_name(cls)[:16], cls.RESPOND is Respond.EXT)
         for cls in CLASSES.values()
         if hasattr(cls, "rectified")
     ]