Source code for juham.simulation.rtracker

import json
import math
import time
from typing import Any, Optional, cast
from typing_extensions import override
from influxdb_client_3 import Point

from juham.base import Base
from juham.base import JMqtt, MqttMsg
from juham.base.time import epoc2utc
from juham.web.rthread import RThread, IWorkerThread


class RTrackerThread(IWorkerThread):
    """A tracker simulation thread generating and publishing geographic
    coordinates.

    """

    def __init__(self, client: Optional[JMqtt] = None) -> None:
        super().__init__(client)
        self._sensor_topic: str = ""
        self._topic: str = ""
        self._interval: float = 60
        self._lon: float = 25.63
        self._lat: float = 60.95
        self._rad: float = 3

    @override
    def update(self) -> bool:
        rc: bool = super().update()
        self.update_track(1, "fixed", 0.5, 1.0, 0, 0)
        self.update_track(2, "rotary", 0.7, 1.8, -0.9, 0)
        return rc

    def init(
        self, lon: float, lat: float, rad: float, interval: float, topic: str
    ) -> None:
        """Initialize the thread

        Args:
            lon (float): longitude coordinate
            lat (float): latitude
            rad (float): radius
            interval (float): update interval in seconds
            topic (str): Mqtt topic
        """
        self._lon = lon
        self._lat = lat
        self._rad = rad
        self._interval = interval
        self._topic = topic

    def update_track(
        self,
        id: int,
        type: str,
        radLon: float,
        radLat: float,
        offLon: float,
        offLat: float,
    ) -> None:
        epoc = time.time()
        rec = {
            "ts": epoc,
            "lon": math.sin(epoc / 360000.0) * math.sin(epoc * 0.001) * radLon
            + self._lon
            + offLon,
            "lat": 0.5 * math.cos(epoc / 360000.0) * math.sin(epoc * 0.001) * radLat
            + self._lat
            + offLat,
            "alt": math.cos(epoc / 360000.0) * (10 * id) + 100,
            "fom": math.cos(epoc / 360000.0) * (0.1 * id) * 10 + 100,
            "type": type,
            "id": str(id),
        }
        self.publish(self._topic, json.dumps(rec), qos=1, retain=False)
        self.debug("Track " + str(id) + " moved")


[docs] class RTracker(RThread): """A tracker automation object. Spawns async thread to generate geographic coordinates at specific rate, and writes them to time series database. Args: RThread (class): super class """ workerThreadId = RTrackerThread.get_class_id() lon = 25.636786 lat = 60.968117 rad = 3 update_interval = 60 def __init__(self, name: str = "rtracker") -> None: super().__init__(name) self.topic = self.make_topic_name("tracks")
[docs] @override def on_connect(self, client: object, userdata: Any, flags: int, rc: int) -> None: super().on_connect(client, userdata, flags, rc) if rc == 0: self.subscribe(self.topic)
[docs] @override def on_message(self, client: object, userdata: Any, msg: MqttMsg) -> None: if msg.topic == self.topic: em = json.loads(msg.payload.decode()) self.on_sensor(em) else: super().on_message(client, userdata, msg)
[docs] def on_sensor(self, msg: dict[str, Any]) -> None: point = ( Point("track") .tag("id", msg["id"]) .field("lon", msg["lon"]) .field("lat", msg["lat"]) .field("alt", msg["alt"]) .field("type", msg["type"]) .field("fom", msg["fom"]) .time(epoc2utc(msg["ts"])) ) self.write(point) self.debug( f"Track {msg['type']} {msg['lat']} {msg['lon']} recorded to timeseries" )
[docs] @override def run(self) -> None: self.worker = cast(RTrackerThread, Base.instantiate(RTracker.workerThreadId)) self.worker.init(self.lon, self.lat, self.rad, self.update_interval, self.topic) super().run()